diff --git a/AndroidKernel.mk b/AndroidKernel.mk index beefecad0ab03b0df9cd870391f215d24618cbde..9931150b2b9a00df72e667d6ae34354ac2036cb5 100644 --- a/AndroidKernel.mk +++ b/AndroidKernel.mk @@ -100,7 +100,10 @@ endif KERNEL_HEADERS_INSTALL := $(KERNEL_OUT)/usr KERNEL_MODULES_INSTALL ?= system KERNEL_MODULES_OUT ?= $(PRODUCT_OUT)/$(KERNEL_MODULES_INSTALL)/lib/modules - +ifneq ($(SOMC_PLATFORM),) +KERNEL_DIFFCONFIG ?= $(TARGET_PRODUCT)_diffconfig +endif +KERNEL_SRC_DIR := $(TARGET_KERNEL_SOURCE) TARGET_PREBUILT_KERNEL := $(TARGET_PREBUILT_INT_KERNEL) define mv-modules @@ -119,6 +122,7 @@ mpath=`dirname $$mdpath`; rm -rf $$mpath;\ fi endef +FORCE: ifneq ($(KERNEL_LEGACY_DIR),true) $(KERNEL_USR): $(KERNEL_HEADERS_INSTALL) rm -rf $(KERNEL_SYMLINK) @@ -130,12 +134,9 @@ endif $(KERNEL_OUT): mkdir -p $(KERNEL_OUT) -$(KERNEL_CONFIG): $(KERNEL_OUT) - $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(KERNEL_DEFCONFIG) - $(hide) if [ ! -z "$(KERNEL_CONFIG_OVERRIDE)" ]; then \ - echo "Overriding kernel config with '$(KERNEL_CONFIG_OVERRIDE)'"; \ - echo $(KERNEL_CONFIG_OVERRIDE) >> $(KERNEL_OUT)/.config; \ - $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) oldconfig; fi +$(KERNEL_CONFIG): $(KERNEL_OUT) FORCE + env KBUILD_DIFFCONFIG=$(KERNEL_DIFFCONFIG) \ + $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(KERNEL_DEFCONFIG) $(TARGET_PREBUILT_INT_KERNEL): $(KERNEL_OUT) $(KERNEL_HEADERS_INSTALL) $(hide) echo "Building kernel..." @@ -149,12 +150,14 @@ $(TARGET_PREBUILT_INT_KERNEL): $(KERNEL_OUT) $(KERNEL_HEADERS_INSTALL) $(KERNEL_HEADERS_INSTALL): $(KERNEL_OUT) $(hide) if [ ! -z "$(KERNEL_HEADER_DEFCONFIG)" ]; then \ rm -f $(BUILD_ROOT_LOC)$(KERNEL_CONFIG); \ - $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_HEADER_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(KERNEL_HEADER_DEFCONFIG); \ + env KBUILD_DIFFCONFIG=$(KERNEL_DIFFCONFIG) \ + $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_HEADER_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(KERNEL_HEADER_DEFCONFIG); \ $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_HEADER_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) headers_install; fi $(hide) if [ "$(KERNEL_HEADER_DEFCONFIG)" != "$(KERNEL_DEFCONFIG)" ]; then \ echo "Used a different defconfig for header generation"; \ rm -f $(BUILD_ROOT_LOC)$(KERNEL_CONFIG); \ - $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(KERNEL_DEFCONFIG); fi + env KBUILD_DIFFCONFIG=$(KERNEL_DIFFCONFIG) \ + $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(KERNEL_DEFCONFIG); fi $(hide) if [ ! -z "$(KERNEL_CONFIG_OVERRIDE)" ]; then \ echo "Overriding kernel config with '$(KERNEL_CONFIG_OVERRIDE)'"; \ echo $(KERNEL_CONFIG_OVERRIDE) >> $(KERNEL_OUT)/.config; \ @@ -162,13 +165,33 @@ $(KERNEL_HEADERS_INSTALL): $(KERNEL_OUT) kerneltags: $(KERNEL_OUT) $(KERNEL_CONFIG) $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) tags + @if [ ! -z "$(KERNEL_CONFIG_OVERRIDE)" ]; then \ + echo "Overriding kernel config with '$(KERNEL_CONFIG_OVERRIDE)'"; \ + echo $(KERNEL_CONFIG_OVERRIDE) >> $(KERNEL_OUT)/.config; \ + $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) oldconfig; \ + fi + +platformconfig: KERNEL_DIFFCONFIG="" +platformconfig: kernelconfig kernelconfig: $(KERNEL_OUT) $(KERNEL_CONFIG) - env KCONFIG_NOTIMESTAMP=true \ + @env KCONFIG_NOTIMESTAMP=true \ $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) menuconfig - env KCONFIG_NOTIMESTAMP=true \ + @env KCONFIG_NOTIMESTAMP=true \ $(MAKE) -C $(TARGET_KERNEL_SOURCE) O=$(BUILD_ROOT_LOC)$(KERNEL_OUT) $(KERNEL_MAKE_ENV) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) savedefconfig - cp $(KERNEL_OUT)/defconfig $(TARGET_KERNEL_SOURCE)/arch/$(KERNEL_ARCH)/configs/$(KERNEL_DEFCONFIG) + @env KCONFIG_NOTIMESTAMP=true KBUILD_DIFFCONFIG=$(KERNEL_DIFFCONFIG) \ + $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) savediffconfig + @if [ ! -z "$(KERNEL_CONFIG_OVERRIDE)" ]; then \ + echo "Overriding kernel config with '$(KERNEL_CONFIG_OVERRIDE)'"; \ + echo $(KERNEL_CONFIG_OVERRIDE) >> $(KERNEL_OUT)/.config; \ + $(MAKE) -C kernel O=../$(KERNEL_OUT) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) oldconfig; \ + fi + @if [ ! $(KERNEL_DIFFCONFIG) ]; then \ + cp -f $(KERNEL_OUT)/defconfig $(KERNEL_SRC_DIR)/arch/$(KERNEL_ARCH)/configs/$(KERNEL_DEFCONFIG); \ + echo ===========; \ + echo $(KERNEL_DEFCONFIG) has been modified !; \ + echo ===========; \ + fi endif endif diff --git a/Documentation/arm/SH-Mobile/.gitignore b/Documentation/arm/SH-Mobile/.gitignore deleted file mode 100644 index c928dbf3cc8806e28a65d3302e3e21b654ac39e1..0000000000000000000000000000000000000000 --- a/Documentation/arm/SH-Mobile/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vrl4 diff --git a/Documentation/blockdev/zram.txt b/Documentation/blockdev/zram.txt index 5bda5031c83de2b6e85ff2a6fe8bb3a8107a4aa5..d88f0c70cd7fc470875960d07a63fb883e75d48b 100644 --- a/Documentation/blockdev/zram.txt +++ b/Documentation/blockdev/zram.txt @@ -59,27 +59,16 @@ num_devices parameter is optional and tells zram how many devices should be pre-created. Default: 1. 2) Set max number of compression streams - Compression backend may use up to max_comp_streams compression streams, - thus allowing up to max_comp_streams concurrent compression operations. - By default, compression backend uses single compression stream. - - Examples: - #show max compression streams number + Regardless the value passed to this attribute, ZRAM will always + allocate multiple compression streams - one per online CPUs - thus + allowing several concurrent compression operations. The number of + allocated compression streams goes down when some of the CPUs + become offline. There is no single-compression-stream mode anymore, + unless you are running a UP system or has only 1 CPU online. + + To find out how many streams are currently available: cat /sys/block/zram0/max_comp_streams - #set max compression streams number to 3 - echo 3 > /sys/block/zram0/max_comp_streams - -Note: -In order to enable compression backend's multi stream support max_comp_streams -must be initially set to desired concurrency level before ZRAM device -initialisation. Once the device initialised as a single stream compression -backend (max_comp_streams equals to 1), you will see error if you try to change -the value of max_comp_streams because single stream compression backend -implemented as a special case by lock overhead issue and does not support -dynamic max_comp_streams. Only multi stream backend supports dynamic -max_comp_streams adjustment. - 3) Select compression algorithm Using comp_algorithm device attribute one can see available and currently selected (shown in square brackets) compression algorithms, diff --git a/Documentation/devicetree/bindings/debug_memory/debug_memory.txt b/Documentation/devicetree/bindings/debug_memory/debug_memory.txt new file mode 100644 index 0000000000000000000000000000000000000000..e7db6efd11c1262e70a548c69b814c1f092eadb4 --- /dev/null +++ b/Documentation/devicetree/bindings/debug_memory/debug_memory.txt @@ -0,0 +1,22 @@ +Ramdump Debug Memory + +Debug memory contains necessary information for ramdump to dump +the memory.This memory is used as a bridge between Android and +ramdump. + +This memory is used by the modules mentioned below: +a) Rdtags +b) Ramdump Memory Descriptors + + +Required Properties: +-compatible: "removed-dma-pool", "qcom,debug_memory" +-reg: Specifies the physical address of the debug memory region and its size + +Example: + debug_region: debug_region@57e00000 { + compatible = "removed-dma-pool", "qcom,debug_memory"; + no-map; + reg = <0 0x57e00000 0 0x100000>; + label = "debug_mem"; + }; diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt index 176f9e115b4231328a72def913f72ca46b1d4e9c..abb15556037d7b41de90f31ea144498e471d3a51 100644 --- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt @@ -78,6 +78,7 @@ Optional properties: - qcom,thermal-derate-current : Array of currrent limits for thermal mitigation. Required if qcom,thermal-derate-en is specified. Unit is mA. Format is qcom,thermal-derate-current = . + 0, 15, 30, 45 for pmi8998. - qcom,otst-ramp-back-up-dis : Boolean property to disable current ramp backup after thermal derate trigger is deasserted. diff --git a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt index a5e607de69d8d258b53b2fa49c88f98cf0204339..d5e179bc528839bef5b82c74be8968f0743386c3 100644 --- a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt @@ -88,11 +88,11 @@ LAB subnode required properties: 50, 60, 70 and 80. - interrupts: Specify the interrupts as per the interrupt encoding. - Currently "lab-vreg-ok" is required and "lab-sc_err" - is optional for LCD mode in pmi8998. - For AMOLED mode, "lab-vreg-ok" is required - only when SWIRE control is enabled and skipping - 2nd SWIRE pulse is required in pmi8952/8996. + Currently "lab-vreg-ok" is required for + LCD mode in pmi8998. For AMOLED mode, + "lab-vreg-ok" is required only when SWIRE + control is enabled and skipping 2nd SWIRE + pulse is required in pmi8952/8996. - interrupt-names: Interrupt names to match up 1-to-1 with the interrupts specified in 'interrupts' property. @@ -213,14 +213,6 @@ IBB subnode required properties: IBB subnode optional properties: -- interrupts: Specify the interrupts as per the interrupt - encoding. - Currently "ibb-sc-err" could be used for LCD mode - in pmi8998 to detect the short circuit fault. -- interrupt-names: Interrupt names to match up 1-to-1 with - the interrupts specified in 'interrupts' - property. - - qcom,qpnp-ibb-discharge-resistor: The discharge resistor in Kilo Ohms which controls the soft start time. Supported values are 300, 64, 32 and 16. diff --git a/Documentation/vm/z3fold.txt b/Documentation/vm/z3fold.txt new file mode 100644 index 0000000000000000000000000000000000000000..38e4dac810b62b2092f845d2a436ae15455ae9a4 --- /dev/null +++ b/Documentation/vm/z3fold.txt @@ -0,0 +1,26 @@ +z3fold +------ + +z3fold is a special purpose allocator for storing compressed pages. +It is designed to store up to three compressed pages per physical page. +It is a zbud derivative which allows for higher compression +ratio keeping the simplicity and determinism of its predecessor. + +The main differences between z3fold and zbud are: +* unlike zbud, z3fold allows for up to PAGE_SIZE allocations +* z3fold can hold up to 3 compressed pages in its page +* z3fold doesn't export any API itself and is thus intended to be used + via the zpool API. + +To keep the determinism and simplicity, z3fold, just like zbud, always +stores an integral number of compressed pages per page, but it can store +up to 3 pages unlike zbud which can store at most 2. Therefore the +compression ratio goes to around 2.7x while zbud's one is around 1.7x. + +Unlike zbud (but like zsmalloc for that matter) z3fold_alloc() does not +return a dereferenceable pointer. Instead, it returns an unsigned long +handle which encodes actual location of the allocated object. + +Keeping effective compression ratio close to zsmalloc's, z3fold doesn't +depend on MMU enabled and provides more predictable reclaim behavior +which makes it a better fit for small and response-critical systems. diff --git a/README_Xperia b/README_Xperia new file mode 100644 index 0000000000000000000000000000000000000000..dc46f203fedfb47d18591834f262c2f2621a05b5 --- /dev/null +++ b/README_Xperia @@ -0,0 +1,65 @@ +Configuration files can be found in arch/arm64/configs. + + defconfig using in common: + msmcortex-perf_defconfig + + diffconfigs for each product: + Xperia XZ Premium G8141 => maple_diffconfig + Xperia XZ Premium Dual G8142 => maple_dsds_diffconfig + Xperia XZ1 G8341/G8343 => poplar_diffconfig + Xperia XZ1 G8342 => poplar_dsds_diffconfig + Xperia XZ1 Compact G8441 => lilac_diffconfig + + +How to build your kernel: + + Prerequisites: + + * ramdisk.img - root fs + + * mkbootimg - boot.img generator + + * The ARM cross-compiler + You can use prebuild executable binary which is included in + standard Android repository. Please visit to external site. + In case of this platform, we recommend to use gcc 4.9 or later + such as aarch64-linux-android-4.9 to avoid known issues. + + + Step 1: Build Your Kernel + $ cd kernel/msm-4.4 + + $ export ARCH=arm64 + + $ export PATH=:$PATH + NOTE: Please set the location of the ARM cross-compiler. + + $ export CROSS_COMPILE= + NOTE: Please set the prefix of the ARM cross-compiler. + ex) aarch64-linux-android- + + $ export KBUILD_DIFFCONFIG=maple_diffconfig + NOTE: Please set a configuration file you want to build. + + $ make msmcortex-perf_defconfig O=./out + + $ make O=./out + + You can see arch/arm64/boot/Image-dtb if you succeed in building. + + + Step 2: Assembling the boot.img + (In the Linux Kernel directory) + $ mkbootimg \ + --kernel out/arch/arm64/boot/Image.gz-dtb \ + --ramdisk ramdisk.img \ + --cmdline "androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x237 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 sched_enable_hmp=1 sched_enable_power_aware=1 service_locator.enable=1 swiotlb=2048 androidboot.configfs=true androidboot.usbcontroller=a800000.dwc3 zram.backend=z3fold buildvariant=userdebug" \ + --os_version 9 \ + --os_patch_level 2018-10-01 \ + --base 0x00000000 \ + --kernel_offset 0x00008000 \ + --ramdisk_offset 0x01000000 \ + --tags_offset 0x00000100 \ + --pagesize 4096 \ + -o boot.img + diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 162bd82dbf51603d8da138a13476fbf71536b6ca..94cf29bc62feb8ef1342549115d8ea40af7ae216 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -3,6 +3,7 @@ config ARM default y select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_ELF_RANDOMIZE + select ARCH_HAS_CRASH_NOTES if CRASH_NOTES select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAVE_CUSTOM_GPIO_H select ARCH_HAS_GCOV_PROFILE_ALL @@ -197,6 +198,9 @@ config ARCH_HAS_BANDGAP config FIX_EARLYCON_MEM def_bool y if MMU +config ARCH_HAS_CRASH_NOTES + bool + config GENERIC_HWEIGHT bool default y @@ -2119,6 +2123,14 @@ config CRASH_DUMP For more details see Documentation/kdump/kdump.txt +config CRASH_NOTES + bool "Support storing crash notes at panic" + depends on !KEXEC + help + Generate kdump style crash notes at the time of a panic and fill it + with the crashed context to support analysis of the memory dump with + tools like Redhat CRASH. + config AUTO_ZRELADDR bool "Auto calculation of the decompressed kernel image address" help diff --git a/arch/arm/boot/dts/qcom-msm8660.dtsi b/arch/arm/boot/dts/qcom-msm8660.dtsi index e5f7f33aa4677739f9bc1e6d57d969db9b856cf9..576211b74e2fc0a0d0fd69214e4d5bf1b28f9729 100644 --- a/arch/arm/boot/dts/qcom-msm8660.dtsi +++ b/arch/arm/boot/dts/qcom-msm8660.dtsi @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /dts-v1/; /include/ "skeleton.dtsi" diff --git a/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts b/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts index 016f9ad9392a9cbad2702834aff6648add614b7b..5b785e6fbae9726df05d06dd51f92fdd578f3b25 100644 --- a/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts +++ b/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "qcom-msm8974.dtsi" #include "qcom-pm8841.dtsi" #include "qcom-pm8941.dtsi" diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi index 753bdfddd46ea5d503c8409cc5c035b239efe47c..76f5d68acf66df50ed787d83d82170fbebac15fb 100644 --- a/arch/arm/boot/dts/qcom-msm8974.dtsi +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /dts-v1/; #include diff --git a/arch/arm/boot/dts/qcom-pm8941.dtsi b/arch/arm/boot/dts/qcom-pm8941.dtsi index b0d443999fcccb84d3301e7787c292830a596e86..60b565335ffa16a1db10e3aa8526c9e3c5e99409 100644 --- a/arch/arm/boot/dts/qcom-pm8941.dtsi +++ b/arch/arm/boot/dts/qcom-pm8941.dtsi @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 9a7be59ef113a43c738822fe8ee97d35d3b40002..fe4f232035f2df2b07e0b73a36b6d8f2df02ca22 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -122,6 +122,7 @@ dtb-$(CONFIG_MSM_GVM_QUIN) += vplatform-lfv-msm8996-telematics.dtb \ vplatform-lfv-msm8996-ivi-la.dtb \ vplatform-lfv-msm8996-ivi-lv-mt.dtb +ifneq ($(CONFIG_ARCH_SONY_YOSHINO),y) ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y) dtbo-$(CONFIG_ARCH_MSM8998) += \ msm8998-cdp-overlay.dtbo \ @@ -179,6 +180,23 @@ dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \ msm8998-v2.1-interposer-sdm660-mtp.dtb \ msm8998-v2.1-interposer-sdm660-qrd.dtb endif +else +dtb-$(CONFIG_MACH_SONY_MAPLE) += msm8998-yoshino-maple_generic.dtb \ + msm8998-v2-yoshino-maple_generic.dtb \ + msm8998-v2.1-yoshino-maple_generic.dtb +dtb-$(CONFIG_MACH_SONY_MAPLE_DSDS) += msm8998-yoshino-maple_dsds.dtb \ + msm8998-v2-yoshino-maple_dsds.dtb \ + msm8998-v2.1-yoshino-maple_dsds.dtb +dtb-$(CONFIG_MACH_SONY_POPLAR) += msm8998-yoshino-poplar_generic.dtb \ + msm8998-v2-yoshino-poplar_generic.dtb \ + msm8998-v2.1-yoshino-poplar_generic.dtb +dtb-$(CONFIG_MACH_SONY_POPLAR_DSDS) += msm8998-yoshino-poplar_dsds.dtb \ + msm8998-v2-yoshino-poplar_dsds.dtb \ + msm8998-v2.1-yoshino-poplar_dsds.dtb +dtb-$(CONFIG_MACH_SONY_LILAC) += msm8998-yoshino-lilac_generic.dtb \ + msm8998-v2-yoshino-lilac_generic.dtb \ + msm8998-v2.1-yoshino-lilac_generic.dtb +endif dtb-$(CONFIG_ARCH_MSMHAMSTER) += msmhamster-rumi.dtb diff --git a/arch/arm/boot/dts/qcom/clearpad-ic-default-regoffset.dtsi b/arch/arm/boot/dts/qcom/clearpad-ic-default-regoffset.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..1bffd1fe8e2fbb71ed5c97a2b3c7d5b64cdb49f2 --- /dev/null +++ b/arch/arm/boot/dts/qcom/clearpad-ic-default-regoffset.dtsi @@ -0,0 +1,45 @@ +/* + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +synaptics_clearpad@2c { +/* Write area------------------------------------------ */ + + somc,clearpad-f01-rmi-cmd00 = <0x00>; + somc,clearpad-f01-rmi-ctrl00 = <0x00>; + somc,clearpad-f01-rmi-ctrl01 = <0x01>; + somc,clearpad-f01-rmi-data00 = <0x00>; + somc,clearpad-f01-rmi-data01 = <0x01>; + somc,clearpad-f01-rmi-query11 = <0x0B>; + + somc,clearpad-f34-flash-ctrl00 = <0x00>; + somc,clearpad-f34-flash-data00 = <0x00>; + somc,clearpad-f34-flash-data01 = <0x01>; + somc,clearpad-f34-flash-data02 = <0x02>; + somc,clearpad-f34-flash-data03 = <0x03>; + somc,clearpad-f34-flash-data04 = <0x04>; + somc,clearpad-f34-flash-data05 = <0x05>; + somc,clearpad-f34-flash-query00 = <0x00>; + somc,clearpad-f34-flash-query01 = <0x01>; + somc,clearpad-f34-flash-query03 = <0x03>; + +/* Write area------------------------------------------ */ +}; + +#include "clearpad-ic-s332u-regoffset.dtsi" +#include "clearpad-ic-s3330-regoffset.dtsi" +#include "clearpad-ic-s3500-regoffset.dtsi" diff --git a/arch/arm/boot/dts/qcom/clearpad-ic-s332u-regoffset.dtsi b/arch/arm/boot/dts/qcom/clearpad-ic-s332u-regoffset.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..9b41541db1e4465df4a694cf59b759ac839f63b3 --- /dev/null +++ b/arch/arm/boot/dts/qcom/clearpad-ic-s332u-regoffset.dtsi @@ -0,0 +1,88 @@ +/* + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +synaptics_clearpad@2c { + S332U { + /* Write area------------------------------------------ */ + + EXTRA_0x05 { + somc,clearpad-f01-rmi-cmd00 = <0x00>; + somc,clearpad-f01-rmi-ctrl00 = <0x00>; + somc,clearpad-f01-rmi-ctrl01 = <0x01>; + somc,clearpad-f01-rmi-ctrl05 = <0x02>; + somc,clearpad-f01-rmi-ctrl18 = <0x04>; + somc,clearpad-f01-rmi-data00 = <0x00>; + somc,clearpad-f01-rmi-data01 = <0x01>; + somc,clearpad-f01-rmi-query11 = <0x0B>; + + somc,clearpad-f12-2d-ctrl08 = <0x00>; + + somc,clearpad-f34-flash-ctrl00 = <0x00>; + somc,clearpad-f34-flash-data00 = <0x00>; + somc,clearpad-f34-flash-data01 = <0x01>; + somc,clearpad-f34-flash-data02 = <0x02>; + somc,clearpad-f34-flash-data03 = <0x03>; + somc,clearpad-f34-flash-data04 = <0x04>; + somc,clearpad-f34-flash-data05 = <0x05>; + somc,clearpad-f34-flash-query01 = <0x01>; + somc,clearpad-f34-flash-query03 = <0x03>; + + somc,clearpad-f54-analog-cmd00 = <0x00>; + somc,clearpad-f54-analog-ctrl188 = <0x1D>; + somc,clearpad-f54-analog-data00 = <0x00>; + somc,clearpad-f54-analog-data01 = <0x01>; + somc,clearpad-f54-analog-data02 = <0x02>; + somc,clearpad-f54-analog-data03 = <0x03>; + somc,clearpad-f54-analog-data31 = <0x0E>; + }; + + EXTRA_0x06 { + somc,clearpad-f01-rmi-cmd00 = <0x00>; + somc,clearpad-f01-rmi-ctrl00 = <0x00>; + somc,clearpad-f01-rmi-ctrl01 = <0x01>; + somc,clearpad-f01-rmi-ctrl05 = <0x02>; + somc,clearpad-f01-rmi-ctrl18 = <0x04>; + somc,clearpad-f01-rmi-data00 = <0x00>; + somc,clearpad-f01-rmi-data01 = <0x01>; + somc,clearpad-f01-rmi-query11 = <0x0B>; + + somc,clearpad-f12-2d-ctrl08 = <0x00>; + + somc,clearpad-f34-flash-ctrl00 = <0x00>; + somc,clearpad-f34-flash-data00 = <0x00>; + somc,clearpad-f34-flash-data01 = <0x01>; + somc,clearpad-f34-flash-data02 = <0x02>; + somc,clearpad-f34-flash-data03 = <0x03>; + somc,clearpad-f34-flash-data04 = <0x04>; + somc,clearpad-f34-flash-data05 = <0x05>; + somc,clearpad-f34-flash-query01 = <0x01>; + somc,clearpad-f34-flash-query03 = <0x03>; + + somc,clearpad-f54-analog-cmd00 = <0x00>; + somc,clearpad-f54-analog-ctrl188 = <0x1F>; + somc,clearpad-f54-analog-data00 = <0x00>; + somc,clearpad-f54-analog-data01 = <0x01>; + somc,clearpad-f54-analog-data02 = <0x02>; + somc,clearpad-f54-analog-data03 = <0x03>; + somc,clearpad-f54-analog-data31 = <0x0E>; + }; + + /* Write area------------------------------------------ */ + }; +}; + diff --git a/arch/arm/boot/dts/qcom/clearpad-ic-s3330-regoffset.dtsi b/arch/arm/boot/dts/qcom/clearpad-ic-s3330-regoffset.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..4fa6045a327185ccc3eb94b25a995bb8d6d4c424 --- /dev/null +++ b/arch/arm/boot/dts/qcom/clearpad-ic-s3330-regoffset.dtsi @@ -0,0 +1,94 @@ +/* + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +synaptics_clearpad@2c { + S3330 { + /* Write area------------------------------------------ */ + + EXTRA_0x05 { + somc,clearpad-f01-rmi-cmd00 = <0x00>; + somc,clearpad-f01-rmi-ctrl00 = <0x00>; + somc,clearpad-f01-rmi-ctrl01 = <0x01>; + somc,clearpad-f01-rmi-ctrl05 = <0x04>; + somc,clearpad-f01-rmi-data00 = <0x00>; + somc,clearpad-f01-rmi-data01 = <0x01>; + somc,clearpad-f01-rmi-query11 = <0x0B>; + + somc,clearpad-f12-2d-ctrl08 = <0x00>; + + somc,clearpad-f34-flash-ctrl00 = <0x00>; + somc,clearpad-f34-flash-data00 = <0x00>; + somc,clearpad-f34-flash-data01 = <0x01>; + somc,clearpad-f34-flash-data02 = <0x02>; + somc,clearpad-f34-flash-data03 = <0x03>; + somc,clearpad-f34-flash-data04 = <0x04>; + somc,clearpad-f34-flash-data05 = <0x05>; + somc,clearpad-f34-flash-query01 = <0x01>; + somc,clearpad-f34-flash-query03 = <0x03>; + + somc,clearpad-f54-analog-cmd00 = <0x00>; + somc,clearpad-f54-analog-ctrl109 = <0x3a>; + somc,clearpad-f54-analog-ctrl113 = <0x3b>; + somc,clearpad-f54-analog-ctrl147 = <0x42>; + somc,clearpad-f54-analog-ctrl214 = <0x53>; + somc,clearpad-f54-analog-data00 = <0x00>; + somc,clearpad-f54-analog-data01 = <0x01>; + somc,clearpad-f54-analog-data02 = <0x02>; + somc,clearpad-f54-analog-data03 = <0x03>; + somc,clearpad-f54-analog-data31 = <0x0E>; + somc,clearpad-f54-analog-query38 = <0x1E>; + }; + + EXTRA_0x06 { + somc,clearpad-f01-rmi-cmd00 = <0x00>; + somc,clearpad-f01-rmi-ctrl00 = <0x00>; + somc,clearpad-f01-rmi-ctrl01 = <0x01>; + somc,clearpad-f01-rmi-ctrl05 = <0x04>; + somc,clearpad-f01-rmi-data00 = <0x00>; + somc,clearpad-f01-rmi-data01 = <0x01>; + somc,clearpad-f01-rmi-query11 = <0x0B>; + + somc,clearpad-f12-2d-ctrl08 = <0x00>; + + somc,clearpad-f34-flash-ctrl00 = <0x00>; + somc,clearpad-f34-flash-data00 = <0x00>; + somc,clearpad-f34-flash-data01 = <0x01>; + somc,clearpad-f34-flash-data02 = <0x02>; + somc,clearpad-f34-flash-data03 = <0x03>; + somc,clearpad-f34-flash-data04 = <0x04>; + somc,clearpad-f34-flash-data05 = <0x05>; + somc,clearpad-f34-flash-query01 = <0x01>; + somc,clearpad-f34-flash-query03 = <0x03>; + + somc,clearpad-f54-analog-cmd00 = <0x00>; + somc,clearpad-f54-analog-ctrl88 = <0x26>; + somc,clearpad-f54-analog-ctrl109 = <0x38>; + somc,clearpad-f54-analog-ctrl113 = <0x39>; + somc,clearpad-f54-analog-ctrl147 = <0x40>; + somc,clearpad-f54-analog-ctrl214 = <0x53>; + somc,clearpad-f54-analog-data00 = <0x00>; + somc,clearpad-f54-analog-data01 = <0x01>; + somc,clearpad-f54-analog-data02 = <0x02>; + somc,clearpad-f54-analog-data03 = <0x03>; + somc,clearpad-f54-analog-query38 = <0x1E>; + }; + + /* Write area------------------------------------------ */ + }; +}; + diff --git a/arch/arm/boot/dts/qcom/clearpad-ic-s3500-regoffset.dtsi b/arch/arm/boot/dts/qcom/clearpad-ic-s3500-regoffset.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..678b4fba7b7eeffe5348fcc821eb9dee4d89293d --- /dev/null +++ b/arch/arm/boot/dts/qcom/clearpad-ic-s3500-regoffset.dtsi @@ -0,0 +1,63 @@ +/* + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +synaptics_clearpad@2c { + S3500 { + /* Write area------------------------------------------ */ + + EXTRA_0x03 { + somc,clearpad-f01-rmi-cmd00 = <0x00>; + somc,clearpad-f01-rmi-ctrl00 = <0x00>; + somc,clearpad-f01-rmi-ctrl01 = <0x01>; + somc,clearpad-f01-rmi-ctrl05 = <0x04>; + somc,clearpad-f01-rmi-data00 = <0x00>; + somc,clearpad-f01-rmi-data01 = <0x01>; + somc,clearpad-f01-rmi-query11 = <0x0B>; + + somc,clearpad-f12-2d-ctrl08 = <0x00>; + + somc,clearpad-f34-flash-ctrl00 = <0x00>; + somc,clearpad-f34-flash-data00 = <0x00>; + somc,clearpad-f34-flash-data01 = <0x01>; + somc,clearpad-f34-flash-data02 = <0x02>; + somc,clearpad-f34-flash-data03 = <0x03>; + somc,clearpad-f34-flash-query00 = <0x00>; + somc,clearpad-f34-flash-query01 = <0x01>; + somc,clearpad-f34-flash-query03 = <0x03>; + + somc,clearpad-f51-custom-ctrl05 = <0x00>; + somc,clearpad-f51-custom-ctrl30 = <0x86>; + + somc,clearpad-f54-analog-cmd00 = <0x00>; + somc,clearpad-f54-analog-ctrl113 = <0x25>; + somc,clearpad-f54-analog-ctrl147 = <0x2F>; + somc,clearpad-f54-analog-ctrl149 = <0x30>; + somc,clearpad-f54-analog-ctrl41 = <0x14>; + somc,clearpad-f54-analog-ctrl57 = <0x17>; + somc,clearpad-f54-analog-ctrl88 = <0x19>; + somc,clearpad-f54-analog-data00 = <0x00>; + somc,clearpad-f54-analog-data01 = <0x01>; + somc,clearpad-f54-analog-data02 = <0x02>; + somc,clearpad-f54-analog-data03 = <0x03>; + somc,clearpad-f54-analog-query38 = <0x1E>; + }; + + /* Write area------------------------------------------ */ + }; +}; + diff --git a/arch/arm/boot/dts/qcom/dsi-panel-lilac-id5_pcc.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-lilac-id5_pcc.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..826d0286142fad0fa35b41b53330aff3e00c74ff --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-lilac-id5_pcc.dtsi @@ -0,0 +1,717 @@ +/* arch/arm64/boot/dts/qcom/dsi-panel-lilac-id5_pcc.dtsi + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +/* pcc-table for Lilac LGD */ +&mdss_mdp { + dsi_5: somc,5_panel { + somc,mdss-dsi-pcc-enable; + somc,mdss-dsi-uv-command = [ + 06 01 00 00 00 00 01 DA + 06 01 00 00 00 00 01 DB]; + somc,mdss-dsi-uv-param-type = <4>; + somc,mdss-dsi-pcc-table-size = <226>; + somc,mdss-dsi-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x2E00 0x5800 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x3180 0x5780 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x3500 0x5700 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x3800 0x5700 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x3C00 0x5680 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x3E80 0x5600 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x4280 0x5580 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x4600 0x5500 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x4A00 0x5500 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x4E00 0x5480 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x5280 0x5400 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x5800 0x5400 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x5D00 0x5380 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x6280 0x5380 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x6800 0x5300 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x3400 0x5E80 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x3700 0x5E00 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x3A80 0x5D00 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x3D80 0x5C80 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x4080 0x5C00 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x4400 0x5B80 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x4780 0x5B00 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x4B80 0x5A80 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x4F80 0x5A00 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x5400 0x5A00 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x5880 0x5980 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x5D80 0x5900 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x6300 0x5880 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x6800 0x5800 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x6F80 0x5800 0x8000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x3900 0x6380 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x3C80 0x6300 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x3F00 0x6280 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x4200 0x6200 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x4580 0x6100 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x4900 0x6100 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x4C80 0x6080 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x5000 0x5F80 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x5500 0x5F00 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x5980 0x5E80 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x5E80 0x5E80 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x6300 0x5E00 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x6880 0x5D80 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x6F80 0x5D00 0x8000 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x7500 0x5C80 0x8000 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x3E00 0x6A00 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x4080 0x6900 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x4380 0x6880 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x4700 0x6700 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x4A00 0x6680 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x4D80 0x6600 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x5180 0x6500 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x5600 0x6480 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x5A80 0x6380 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x5F00 0x6300 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x6380 0x6280 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x6880 0x6280 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x6F80 0x6180 0x8000 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x7500 0x6100 0x8000 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x7980 0x6100 0x8000 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x4200 0x7180 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x4500 0x7080 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x4800 0x7000 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x4B80 0x6E80 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x4E80 0x6D00 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x5280 0x6B80 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x5700 0x6B00 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x5B00 0x6A00 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x5F80 0x6900 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x6380 0x6880 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x6880 0x6700 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x6F80 0x6700 0x8000 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x7500 0x6680 0x8000 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x7900 0x6580 0x8000 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x6400 0x7D80 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x4680 0x7680 0x8000 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x4980 0x7600 0x8000 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x4C80 0x7500 0x8000 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x5000 0x7480 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x5380 0x7380 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x5800 0x7280 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x5B80 0x7200 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x6000 0x7100 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x6400 0x7000 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x6900 0x6F00 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x6F80 0x6D80 0x8000 + 0x00 0x57 0x11 0x13 0x23 0x25 0x7500 0x6C80 0x8000 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x7900 0x6B80 0x8000 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x6A80 0x8000 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x6380 0x7900 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x4A80 0x7B00 0x8000 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x4D80 0x7A80 0x8000 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x5080 0x7980 0x8000 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x5480 0x7880 0x8000 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x5800 0x7800 0x8000 + 0x00 0x60 0x23 0x25 0x20 0x22 0x5C00 0x7700 0x8000 + 0x00 0x61 0x20 0x22 0x20 0x22 0x6080 0x7680 0x8000 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x6400 0x7600 0x8000 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x6900 0x7500 0x8000 + 0x00 0x64 0x17 0x19 0x20 0x22 0x6F80 0x7480 0x8000 + 0x00 0x65 0x14 0x16 0x20 0x22 0x7480 0x7380 0x8000 + 0x00 0x66 0x11 0x13 0x20 0x22 0x7880 0x7280 0x8000 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x7200 0x8000 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x6A00 0x7A00 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x6300 0x7500 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x4B00 0x8000 0x7B00 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x4F00 0x8000 0x7B80 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x5380 0x8000 0x7D80 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x5880 0x8000 0x8000 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x5C80 0x7D80 0x8000 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x6100 0x7B00 0x8000 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x6480 0x7A80 0x8000 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x6900 0x7980 0x8000 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x6F80 0x7900 0x8000 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x7480 0x7800 0x8000 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x7800 0x7780 0x8000 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x7D80 0x7680 0x8000 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x7200 0x7A80 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x6980 0x7680 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x6280 0x6F00 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x4980 0x8000 0x7700 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x4D80 0x8000 0x7800 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x5280 0x8000 0x7900 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x5800 0x8000 0x7A00 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x5C80 0x8000 0x7A80 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x6180 0x8000 0x7B80 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x6680 0x8000 0x7C00 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x6D00 0x8000 0x7D80 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7400 0x8000 0x8000 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x7800 0x7D00 0x8000 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x8000 0x8000 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7800 0x7B00 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x7200 0x7780 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x6900 0x7100 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x6200 0x6A00 + 0x00 0x88 0x32 0x34 0x17 0x19 0x4800 0x8000 0x7100 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x4C80 0x8000 0x7380 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x5100 0x8000 0x7500 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x5680 0x8000 0x7600 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x5B00 0x8000 0x7700 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x6100 0x8000 0x7880 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x6600 0x8000 0x7900 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x6C80 0x8000 0x7980 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7380 0x8000 0x7A00 + 0x00 0x91 0x17 0x19 0x17 0x19 0x7800 0x8000 0x7B00 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x8000 0x7B80 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7800 0x7800 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x7200 0x7380 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x6900 0x6C00 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x6180 0x6600 + 0x00 0x97 0x32 0x34 0x14 0x16 0x4680 0x8000 0x6B80 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x4B80 0x8000 0x6D00 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x5000 0x8000 0x6E80 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x5580 0x8000 0x7080 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x5A80 0x8000 0x7200 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x6000 0x8000 0x7400 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x6500 0x8000 0x7500 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x6B00 0x8000 0x7600 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7300 0x8000 0x7700 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x7780 0x8000 0x7800 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x8000 0x7880 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7800 0x7480 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x7180 0x6E00 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x6800 0x6800 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x6100 0x6280 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x4580 0x8000 0x6700 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x4980 0x8000 0x6800 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x4E80 0x8000 0x6980 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x5400 0x8000 0x6B00 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x5980 0x8000 0x6C80 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x5F00 0x8000 0x6E00 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x6400 0x8000 0x7000 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x6A80 0x8000 0x7100 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7200 0x8000 0x7280 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x7780 0x8000 0x7400 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x7D80 0x8000 0x7580 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7800 0x7000 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x7180 0x6980 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x6780 0x6500 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x6080 0x5F80 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x4380 0x8000 0x6380 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x4880 0x8000 0x6500 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x4D80 0x8000 0x6600 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x5280 0x8000 0x6780 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x5880 0x8000 0x6800 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x5E00 0x8000 0x6980 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x6380 0x8000 0x6B00 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x6980 0x8000 0x6C00 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7180 0x8000 0x6E00 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x7700 0x8000 0x6F00 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x7D80 0x8000 0x7080 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7800 0x6B80 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x7180 0x6680 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x6780 0x6200 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x5F80 0x5C80 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x4200 0x8000 0x5F80 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x4780 0x8000 0x6100 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x4C00 0x8000 0x6300 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x5100 0x8000 0x6400 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x5780 0x8000 0x6580 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x5D00 0x8000 0x6600 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x6300 0x8000 0x6780 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x6900 0x8000 0x6880 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7100 0x8000 0x6980 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x7700 0x8000 0x6B00 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x7B80 0x8000 0x6C00 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7800 0x6800 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x7100 0x6400 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x6700 0x5E80 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x5F00 0x5980 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x4080 0x8000 0x5C00 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x4580 0x8000 0x5E00 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x4B00 0x8000 0x5F00 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x5000 0x8000 0x6080 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x5680 0x8000 0x6200 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x5C00 0x8000 0x6300 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x6200 0x8000 0x6480 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x6800 0x8000 0x6600 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7080 0x8000 0x6680 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x7680 0x8000 0x6800 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x7B80 0x8000 0x6880 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7800 0x6600 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x7100 0x6100 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x6700 0x5C00 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x5E80 0x5700 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-srgb-pcc-enable; + somc,mdss-dsi-srgb-pcc-table-size = <226>; + somc,mdss-dsi-srgb-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x2E80 0x5A80 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x3180 0x5A00 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x3500 0x5980 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x3880 0x5900 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x3C00 0x5880 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x4000 0x5800 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x4400 0x5780 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x4800 0x5780 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x4B80 0x5700 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x4F80 0x5700 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x5480 0x5700 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x5900 0x5680 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x5F00 0x5680 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x6480 0x5600 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x6A00 0x5580 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x3380 0x6180 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x3780 0x6080 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x3A80 0x6000 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x3E00 0x5F80 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x4180 0x5E80 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x4580 0x5E00 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x4980 0x5D80 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x4D00 0x5D00 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x5080 0x5C80 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x5580 0x5C00 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x5A00 0x5B80 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x6000 0x5B00 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x6500 0x5B00 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x6A00 0x5A80 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x7180 0x5A00 0x8000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x3980 0x6700 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x3D00 0x6600 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x4080 0x6580 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x4380 0x6500 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x4780 0x6400 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x4A80 0x6400 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x4E00 0x6380 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x5200 0x6300 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x5600 0x6280 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x5B00 0x6200 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x6080 0x6180 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x6580 0x6100 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x6A80 0x6080 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x7180 0x6000 0x8000 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x7780 0x5F80 0x8000 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x3E80 0x6D00 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x4200 0x6B80 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x4500 0x6B00 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x4880 0x6A00 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x4B80 0x6980 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x4F00 0x6880 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x5300 0x6800 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x5700 0x6700 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x5C00 0x6700 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x6180 0x6680 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x6600 0x6600 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x6A80 0x6580 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x7180 0x6480 0x8000 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x7700 0x6480 0x8000 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x7B80 0x6400 0x8000 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x4380 0x7480 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x4700 0x7300 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x4980 0x7280 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x4D00 0x7100 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x5000 0x6F80 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x5400 0x6E80 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x5800 0x6D80 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x5C80 0x6D00 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x6200 0x6B80 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x6600 0x6B00 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x6A80 0x6A80 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x7180 0x6980 0x8000 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x7700 0x6900 0x8000 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x7B80 0x6880 0x8000 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x6700 0x7F00 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x4800 0x7900 0x8000 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x4B00 0x7800 0x8000 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x4D80 0x7780 0x8000 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x5100 0x7700 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x5500 0x7600 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x5900 0x7500 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x5D80 0x7480 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x6280 0x7400 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x6680 0x7280 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x6B00 0x7180 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x7180 0x7000 0x8000 + 0x00 0x57 0x11 0x13 0x23 0x25 0x7680 0x6F80 0x8000 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x7B00 0x6E00 0x8000 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x6D80 0x8000 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x6680 0x7A80 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x4C00 0x7E00 0x8000 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x4F00 0x7D00 0x8000 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x5200 0x7C00 0x8000 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x5580 0x7B00 0x8000 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x5980 0x7A00 0x8000 + 0x00 0x60 0x23 0x25 0x20 0x22 0x5E00 0x7980 0x8000 + 0x00 0x61 0x20 0x22 0x20 0x22 0x6300 0x7900 0x8000 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x6700 0x7800 0x8000 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x6B00 0x7780 0x8000 + 0x00 0x64 0x17 0x19 0x20 0x22 0x7180 0x7680 0x8000 + 0x00 0x65 0x14 0x16 0x20 0x22 0x7680 0x7600 0x8000 + 0x00 0x66 0x11 0x13 0x20 0x22 0x7A80 0x7500 0x8000 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x7480 0x8000 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x6D00 0x7B00 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x6600 0x7600 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x4C00 0x8000 0x7C00 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x5000 0x8000 0x7D80 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x5580 0x8000 0x7E80 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x5A00 0x8000 0x8000 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x5E80 0x7F80 0x8000 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x6300 0x7E00 0x8000 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x6700 0x7D00 0x8000 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x6B00 0x7C80 0x8000 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x7180 0x7B80 0x8000 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x7600 0x7A80 0x8000 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x7A00 0x7A00 0x8000 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x7F00 0x7980 0x8000 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x7480 0x7C00 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x6C00 0x7700 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x6580 0x7000 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x4B00 0x8000 0x7800 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x4F80 0x8000 0x7900 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x5400 0x8000 0x7A00 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x5900 0x8000 0x7A80 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x5E00 0x8000 0x7C00 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x6400 0x8000 0x7C80 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x6880 0x8000 0x7E00 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x6F80 0x8000 0x7F00 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7600 0x8000 0x8000 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x7A00 0x7F80 0x8000 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x8000 0x8000 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7A00 0x7C00 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x7480 0x7880 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x6B80 0x7200 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x6500 0x6B00 + 0x00 0x88 0x32 0x34 0x17 0x19 0x4980 0x8000 0x7280 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x4D80 0x8000 0x7400 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x5280 0x8000 0x7580 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x5780 0x8000 0x7680 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x5D00 0x8000 0x7800 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x6300 0x8000 0x7880 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x6800 0x8000 0x7980 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x6E00 0x8000 0x7A80 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7580 0x8000 0x7B80 + 0x00 0x91 0x17 0x19 0x17 0x19 0x7A00 0x8000 0x7C00 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x8000 0x7D80 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7A00 0x7900 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x7480 0x7400 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x6B80 0x6D00 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x6480 0x6700 + 0x00 0x97 0x32 0x34 0x14 0x16 0x4800 0x8000 0x6C00 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x4C80 0x8000 0x6E00 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x5100 0x8000 0x6F80 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x5680 0x8000 0x7100 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x5C00 0x8000 0x7380 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x6280 0x8000 0x7480 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x6780 0x8000 0x7580 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x6D80 0x8000 0x7700 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7500 0x8000 0x7800 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x7A00 0x8000 0x7880 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x8000 0x7980 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7A00 0x7580 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x7480 0x6E80 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x6B00 0x6900 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x6400 0x6380 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x4700 0x8000 0x6800 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x4B80 0x8000 0x6900 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x5000 0x8000 0x6A80 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x5580 0x8000 0x6C00 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x5B00 0x8000 0x6D80 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x6180 0x8000 0x6E80 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x6700 0x8000 0x7080 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x6D00 0x8000 0x7200 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7480 0x8000 0x7400 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x7A00 0x8000 0x7500 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x7F00 0x8000 0x7600 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7A00 0x7080 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x7480 0x6A80 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x6A80 0x6600 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x6380 0x5F80 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x4500 0x8000 0x6400 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x4A00 0x8000 0x6580 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x4F00 0x8000 0x6680 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x5400 0x8000 0x6800 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x5A00 0x8000 0x6900 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x6080 0x8000 0x6A80 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x6600 0x8000 0x6C00 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x6B80 0x8000 0x6D00 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7400 0x8000 0x6E80 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x7980 0x8000 0x7000 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x7F00 0x8000 0x7100 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7A00 0x6C00 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x7400 0x6780 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x6A00 0x6280 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x6300 0x5C00 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x4380 0x8000 0x5F80 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x4880 0x8000 0x6180 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x4D80 0x8000 0x6300 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x5300 0x8000 0x6480 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x5880 0x8000 0x6600 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x5F00 0x8000 0x6700 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x6500 0x8000 0x6880 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x6B00 0x8000 0x6980 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7380 0x8000 0x6A80 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x7900 0x8000 0x6C00 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x7E80 0x8000 0x6D00 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7A00 0x6900 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x7400 0x6480 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x6A00 0x5F00 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x6280 0x5900 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x4180 0x8000 0x5B80 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x4780 0x8000 0x5D80 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x4C00 0x8000 0x5F00 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x5180 0x8000 0x6080 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x5780 0x8000 0x6280 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x5D80 0x8000 0x6400 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x6480 0x8000 0x6580 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x6A80 0x8000 0x6600 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7300 0x8000 0x6780 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x7880 0x8000 0x6900 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x7E80 0x8000 0x6980 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7A00 0x6680 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x7400 0x6180 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x6980 0x5B80 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x6180 0x5700 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-vivid-pcc-enable; + somc,mdss-dsi-vivid-pcc-table-size = <226>; + somc,mdss-dsi-vivid-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x5180 0x6E80 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x5400 0x6E00 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x5680 0x6E00 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x5900 0x6D00 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x5B80 0x6D00 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x5E00 0x6C80 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x6080 0x6C80 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x6300 0x6C80 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x6500 0x6C00 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x6800 0x6C00 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x6A80 0x6C00 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x6D00 0x6B80 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x7000 0x6B80 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x7300 0x6B80 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x7600 0x6B80 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x5600 0x7180 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x5880 0x7100 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x5B00 0x7100 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x5D00 0x7080 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x5F80 0x7080 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x6180 0x7000 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x6380 0x7000 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x6600 0x6F80 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x6880 0x6F80 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x6B00 0x6F00 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x6D80 0x6F00 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x7080 0x6E80 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x7300 0x6E80 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x7600 0x6E80 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x7900 0x6E00 0x8000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x5980 0x7480 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x5C00 0x7400 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x5E80 0x7400 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x6080 0x7380 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x6280 0x7300 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x6480 0x7300 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x6700 0x7280 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x6900 0x7200 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x6B80 0x7200 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x6E00 0x7180 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x7080 0x7180 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x7380 0x7100 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x7600 0x7100 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x7900 0x7080 0x8000 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x7C80 0x7080 0x8000 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x5D80 0x7780 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x5F80 0x7700 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x6180 0x7680 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x6380 0x7600 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x6580 0x7600 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x6780 0x7580 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x6A00 0x7500 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x6C00 0x7500 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x6E80 0x7480 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x7100 0x7400 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x7380 0x7400 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x7680 0x7380 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x7900 0x7380 0x8000 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x7C80 0x7300 0x8000 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x7E80 0x7300 0x8000 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x6080 0x7B00 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x6200 0x7A00 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x6400 0x7A00 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x6600 0x7980 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x6800 0x7880 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x6A80 0x7800 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x6C80 0x7780 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x6F00 0x7780 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x7180 0x7700 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x7400 0x7680 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x7680 0x7680 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x7900 0x7600 0x8000 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x7C80 0x7580 0x8000 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x7E80 0x7580 0x8000 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x7480 0x8000 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x6300 0x7D80 0x8000 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x6500 0x7D00 0x8000 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x6700 0x7C80 0x8000 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x6900 0x7C80 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x6B00 0x7C00 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x6D00 0x7B80 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x6F80 0x7B00 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x7180 0x7A80 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x7400 0x7A00 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x7680 0x7980 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x7900 0x7880 0x8000 + 0x00 0x57 0x11 0x13 0x23 0x25 0x7C80 0x7880 0x8000 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x7E00 0x7800 0x8000 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x7780 0x8000 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x7480 0x7E00 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x6580 0x7F80 0x8000 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x6780 0x7F00 0x8000 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x6980 0x7E80 0x8000 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x6B80 0x7E00 0x8000 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x6D80 0x7E00 0x8000 + 0x00 0x60 0x23 0x25 0x20 0x22 0x6F80 0x7D80 0x8000 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7200 0x7D00 0x8000 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x7400 0x7C80 0x8000 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x7680 0x7C80 0x8000 + 0x00 0x64 0x17 0x19 0x20 0x22 0x7900 0x7C80 0x8000 + 0x00 0x65 0x14 0x16 0x20 0x22 0x7C00 0x7C00 0x8000 + 0x00 0x66 0x11 0x13 0x20 0x22 0x7E00 0x7B80 0x8000 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x7B00 0x8000 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x7780 0x7E80 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x7400 0x7C00 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x6580 0x8000 0x7F00 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x6880 0x8000 0x7F80 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x6B00 0x8000 0x8000 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x6D80 0x8000 0x8000 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x7000 0x8000 0x8000 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x7200 0x7F80 0x8000 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7480 0x7F00 0x8000 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x7680 0x7E80 0x8000 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x7900 0x7E80 0x8000 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x7C00 0x7E00 0x8000 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x7E00 0x7E00 0x8000 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x7D80 0x8000 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x7B80 0x7F00 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x7700 0x7C80 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x7380 0x7900 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x6500 0x8000 0x7D00 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x6780 0x8000 0x7D80 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x6A00 0x8000 0x7E00 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x6D00 0x8000 0x7E00 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x6F80 0x8000 0x7E80 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x7280 0x8000 0x7F00 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7580 0x8000 0x7F80 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x7880 0x8000 0x8000 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7C00 0x8000 0x8000 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x7E00 0x8000 0x8000 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x7F80 0x7F80 0x8000 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7E00 0x7F00 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x7B00 0x7D80 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x7700 0x7A00 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x7380 0x7700 + 0x00 0x88 0x32 0x34 0x17 0x19 0x6400 0x8000 0x7A00 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x6700 0x8000 0x7B80 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x6980 0x8000 0x7C00 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x6C80 0x8000 0x7C80 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x6F00 0x8000 0x7D00 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x7200 0x8000 0x7D80 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7500 0x8000 0x7E00 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7800 0x8000 0x7E00 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7C00 0x8000 0x7E80 + 0x00 0x91 0x17 0x19 0x17 0x19 0x7E00 0x8000 0x7F00 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x8000 0x7F80 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7E00 0x7D80 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x7B00 0x7B80 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x7700 0x7800 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x7300 0x7500 + 0x00 0x97 0x32 0x34 0x14 0x16 0x6300 0x8000 0x7780 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x6600 0x8000 0x7800 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x6900 0x8000 0x7900 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x6B80 0x8000 0x7A00 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x6E80 0x8000 0x7A80 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x7180 0x8000 0x7B80 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7480 0x8000 0x7C00 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7780 0x8000 0x7C80 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7B00 0x8000 0x7D00 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x7E00 0x8000 0x7D80 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x8000 0x7E00 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7E00 0x7C00 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x7B00 0x7880 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x7680 0x7600 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x7300 0x7300 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x6200 0x8000 0x7580 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x6500 0x8000 0x7600 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x6800 0x8000 0x7680 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x6B00 0x8000 0x7780 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x6E00 0x8000 0x7800 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x7100 0x8000 0x7880 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x7400 0x8000 0x7900 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7780 0x8000 0x7A00 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7B00 0x8000 0x7A80 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x7D80 0x8000 0x7B80 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x8000 0x7C00 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7E00 0x7900 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x7B00 0x7700 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x7680 0x7400 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x7280 0x7180 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x6180 0x8000 0x7380 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x6480 0x8000 0x7400 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x6780 0x8000 0x7480 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x6A80 0x8000 0x7580 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x6D80 0x8000 0x7600 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x7080 0x8000 0x7680 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x7380 0x8000 0x7780 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7700 0x8000 0x7800 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7A80 0x8000 0x7880 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x7D80 0x8000 0x7900 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x8000 0x7A00 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7E00 0x7780 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x7B00 0x7500 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x7680 0x7280 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x7280 0x6F80 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x6080 0x8000 0x7180 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x6380 0x8000 0x7200 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x6680 0x8000 0x7300 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x6980 0x8000 0x7380 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x6D00 0x8000 0x7480 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x7000 0x8000 0x7500 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x7380 0x8000 0x7580 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7680 0x8000 0x7600 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7A80 0x8000 0x7680 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x7D00 0x8000 0x7780 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x7F80 0x8000 0x7800 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7E00 0x7600 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x7B00 0x7380 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x7600 0x7100 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x7200 0x6D80 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x5F80 0x8000 0x6F00 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x6280 0x8000 0x7000 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x6580 0x8000 0x7100 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x6900 0x8000 0x7200 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x6C00 0x8000 0x7280 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x6F80 0x8000 0x7300 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x7300 0x8000 0x7400 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7600 0x8000 0x7480 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7A00 0x8000 0x7500 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x7D00 0x8000 0x7580 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x7F80 0x8000 0x7680 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7E00 0x7480 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x7B00 0x7200 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x7600 0x6F00 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x7180 0x6C80 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-lilac-id8_pcc.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-lilac-id8_pcc.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..8684538a72221d4df1b0b4defbbf7a984ee826d5 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-lilac-id8_pcc.dtsi @@ -0,0 +1,717 @@ +/* arch/arm64/boot/dts/qcom/dsi-panel-lilac-id8_pcc.dtsi + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +/* pcc-table for Lilac JDI */ +&mdss_mdp { + dsi_8: somc,8_panel { + somc,mdss-dsi-pcc-enable; + somc,mdss-dsi-uv-command = [ + 06 01 00 00 00 00 01 DA + 06 01 00 00 00 00 01 DB]; + somc,mdss-dsi-uv-param-type = <4>; + somc,mdss-dsi-pcc-table-size = <226>; + somc,mdss-dsi-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x3100 0x5580 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x3380 0x5500 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x3680 0x5480 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x3900 0x5400 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x3B80 0x5380 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x3E00 0x5380 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x4100 0x5380 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x4380 0x5300 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x4700 0x5300 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x4B80 0x5280 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x4F80 0x5200 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x5480 0x5200 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x5980 0x5200 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x5E80 0x5180 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x6300 0x5180 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x3680 0x5C00 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x3900 0x5B80 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x3B80 0x5B00 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x3D80 0x5A80 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x4000 0x5A00 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x4280 0x5980 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x4600 0x5900 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x4980 0x5880 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x4D00 0x5800 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x5180 0x5800 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x5680 0x5780 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x5B00 0x5700 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x6000 0x5700 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x6480 0x5700 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x6900 0x5680 0x8000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x3B80 0x6200 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x3D80 0x6180 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x3F80 0x6100 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x4200 0x6080 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x4500 0x6000 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x4800 0x5F00 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x4B80 0x5E80 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x4F80 0x5E80 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x5380 0x5E00 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x5800 0x5D80 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x5C80 0x5D00 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x6100 0x5C80 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x6500 0x5C00 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x6A00 0x5B80 0x8000 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x6F80 0x5B00 0x8000 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x3F00 0x6780 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x4180 0x6680 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x4380 0x6600 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x4700 0x6580 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x4980 0x6480 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x4D00 0x6400 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x5100 0x6400 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x5580 0x6300 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x5980 0x6280 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x5D80 0x6200 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x6200 0x6180 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x6600 0x6100 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x6A80 0x6100 0x8000 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x7000 0x6080 0x8000 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x7580 0x6000 0x8000 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x4300 0x6E00 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x4580 0x6C80 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x4880 0x6B80 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x4C00 0x6B00 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x4F00 0x6A80 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x5280 0x6980 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x5700 0x6900 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x5B00 0x6800 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x5F00 0x6700 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x6280 0x6680 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x6700 0x6600 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x6B80 0x6580 0x8000 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x7080 0x6500 0x8000 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x7600 0x6480 0x8000 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x7B80 0x6400 0x8000 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x4700 0x7480 0x8000 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x4A00 0x7380 0x8000 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x4D80 0x7280 0x8000 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x5080 0x7180 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x5480 0x7080 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x5800 0x6F80 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x5C00 0x6E80 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x6000 0x6D80 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x6380 0x6D00 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x6700 0x6B80 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x6C00 0x6B00 0x8000 + 0x00 0x57 0x11 0x13 0x23 0x25 0x7100 0x6A00 0x8000 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x7600 0x6A00 0x8000 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x7B80 0x6900 0x8000 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x6500 0x7A80 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x4C00 0x7B00 0x8000 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x4F00 0x7980 0x8000 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x5200 0x7880 0x8000 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x5600 0x7780 0x8000 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x5980 0x7680 0x8000 + 0x00 0x60 0x23 0x25 0x20 0x22 0x5D00 0x7580 0x8000 + 0x00 0x61 0x20 0x22 0x20 0x22 0x6100 0x7480 0x8000 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x6480 0x7380 0x8000 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x6800 0x7280 0x8000 + 0x00 0x64 0x17 0x19 0x20 0x22 0x6C80 0x7180 0x8000 + 0x00 0x65 0x14 0x16 0x20 0x22 0x7200 0x7080 0x8000 + 0x00 0x66 0x11 0x13 0x20 0x22 0x7680 0x7000 0x8000 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x7B80 0x6F00 0x8000 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x6A00 0x7A80 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x6400 0x7500 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x4C80 0x8000 0x7A80 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x5100 0x8000 0x7B80 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x5600 0x8000 0x7E00 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x5A80 0x8000 0x8000 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x5E00 0x7E00 0x8000 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x6180 0x7B80 0x8000 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x6500 0x7A80 0x8000 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x6880 0x7900 0x8000 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x6D00 0x7800 0x8000 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x7280 0x7700 0x8000 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x7680 0x7600 0x8000 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x7B80 0x7500 0x8000 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x7080 0x7B00 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x6A00 0x7580 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x6400 0x6F80 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x4B80 0x8000 0x7500 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x4F80 0x8000 0x7600 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x5480 0x8000 0x7780 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x5980 0x8000 0x7880 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x5E00 0x8000 0x7980 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x6280 0x8000 0x7B00 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x6700 0x8000 0x7C00 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x6C80 0x8000 0x7E00 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7280 0x8000 0x8000 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x7700 0x7E00 0x8000 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x8000 0x8000 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7680 0x7B00 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x7000 0x7600 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x6980 0x6F80 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x6300 0x6A80 + 0x00 0x88 0x32 0x34 0x17 0x19 0x4A00 0x8000 0x6F00 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x4E80 0x8000 0x7000 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x5380 0x8000 0x7180 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x5800 0x8000 0x7380 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x5D00 0x8000 0x7480 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x6200 0x8000 0x7600 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x6680 0x8000 0x7700 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x6B80 0x8000 0x7800 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7200 0x8000 0x7900 + 0x00 0x91 0x17 0x19 0x17 0x19 0x7700 0x8000 0x7A00 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x8000 0x7B00 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7680 0x7680 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x7000 0x7100 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x6900 0x6B80 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x6280 0x6700 + 0x00 0x97 0x32 0x34 0x14 0x16 0x4900 0x8000 0x6A00 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x4D00 0x8000 0x6B80 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x5200 0x8000 0x6D00 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x5780 0x8000 0x6E00 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x5C80 0x8000 0x6F80 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x6100 0x8000 0x7000 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x6600 0x8000 0x7200 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x6B00 0x8000 0x7380 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7100 0x8000 0x7480 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x7700 0x8000 0x7580 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x8000 0x7700 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7680 0x7200 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x6F80 0x6C80 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x6880 0x6800 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x6200 0x6300 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x4700 0x8000 0x6600 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x4C00 0x8000 0x6780 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x5100 0x8000 0x6880 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x5680 0x8000 0x6A00 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x5B80 0x8000 0x6B00 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x6100 0x8000 0x6C00 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x6500 0x8000 0x6D80 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x6A80 0x8000 0x6E80 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7080 0x8000 0x7000 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x7680 0x8000 0x7100 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x8000 0x7200 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7680 0x6D80 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x6F80 0x6900 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x6800 0x6480 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x6180 0x5F80 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x4600 0x8000 0x6280 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x4A80 0x8000 0x6400 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x4F80 0x8000 0x6500 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x5500 0x8000 0x6600 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x5A80 0x8000 0x6780 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x6000 0x8000 0x6880 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x6500 0x8000 0x6980 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x6A00 0x8000 0x6A80 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7000 0x8000 0x6C00 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x7680 0x8000 0x6D00 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x7E00 0x8000 0x6E00 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7680 0x6A00 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x6F80 0x6600 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x6780 0x6180 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x6100 0x5C00 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x4500 0x8000 0x5E80 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x4980 0x8000 0x5F80 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x4E80 0x8000 0x6100 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x5400 0x8000 0x6300 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x5980 0x8000 0x6400 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x5F00 0x8000 0x6580 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x6400 0x8000 0x6600 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x6980 0x8000 0x6780 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x6F80 0x8000 0x6880 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x7600 0x8000 0x6980 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x7E00 0x8000 0x6A80 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7680 0x6700 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x6F00 0x6300 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x6700 0x5E00 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x6080 0x5900 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x4380 0x8000 0x5A80 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x4800 0x8000 0x5C00 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x4D00 0x8000 0x5D80 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x5280 0x8000 0x5F00 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x5880 0x8000 0x6080 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x5E00 0x8000 0x6200 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x6380 0x8000 0x6380 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x6900 0x8000 0x6480 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x6F00 0x8000 0x6580 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x7580 0x8000 0x6680 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x7E00 0x8000 0x6780 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7680 0x6480 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x6F00 0x5F80 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x6680 0x5B00 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x6000 0x5600 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-srgb-pcc-enable; + somc,mdss-dsi-srgb-pcc-table-size = <226>; + somc,mdss-dsi-srgb-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x3080 0x5600 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x3300 0x5580 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x3580 0x5500 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x3880 0x5500 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x3B80 0x5480 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x3E00 0x5480 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x4100 0x5400 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x4480 0x5380 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x4780 0x5380 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x4B80 0x5300 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x5000 0x5300 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x5480 0x5280 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x5980 0x5300 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x5F00 0x5280 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x6400 0x5280 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x3580 0x5D00 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x3880 0x5C80 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x3B00 0x5B80 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x3D80 0x5B00 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x4080 0x5A80 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x4300 0x5A00 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x4600 0x5980 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x4A00 0x5900 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x4D80 0x5880 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x5200 0x5880 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x5680 0x5800 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x5B80 0x5800 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x6000 0x5780 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x6500 0x5780 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x6980 0x5700 0x8000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x3B00 0x6300 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x3D80 0x6280 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x3F80 0x6200 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x4200 0x6180 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x4500 0x6080 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x4800 0x6000 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x4C00 0x5F80 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x4F80 0x5F00 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x5400 0x5F00 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x5800 0x5E80 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x5D00 0x5E00 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x6180 0x5D80 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x6600 0x5D00 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x6A80 0x5C80 0x8000 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x7000 0x5C00 0x8000 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x3F80 0x6880 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x4200 0x6780 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x4480 0x6700 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x4700 0x6680 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x4A00 0x6600 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x4D80 0x6580 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x5100 0x6480 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x5500 0x6400 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x5980 0x6380 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x5E00 0x6300 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x6280 0x6280 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x6680 0x6200 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x6B00 0x6180 0x8000 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x7080 0x6180 0x8000 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x7600 0x6100 0x8000 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x4380 0x6E80 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x4600 0x6D80 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x4900 0x6C80 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x4C00 0x6B80 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x4F80 0x6A80 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x5300 0x6A00 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x5700 0x6980 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x5B00 0x6880 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x5F00 0x6800 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x6380 0x6780 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x6780 0x6700 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x6B80 0x6680 0x8000 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x7100 0x6600 0x8000 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x7680 0x6580 0x8000 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x7B80 0x6500 0x8000 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x4780 0x7500 0x8000 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x4A80 0x7400 0x8000 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x4D80 0x7300 0x8000 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x5100 0x7200 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x5480 0x7100 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x5880 0x7000 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x5C80 0x6F80 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x6080 0x6E00 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x6480 0x6D00 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x6800 0x6C80 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x6C00 0x6B80 0x8000 + 0x00 0x57 0x11 0x13 0x23 0x25 0x7180 0x6A80 0x8000 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x7680 0x6A00 0x8000 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x7B80 0x6A00 0x8000 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x6580 0x7900 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x4C80 0x7B00 0x8000 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x4F80 0x7A00 0x8000 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x5280 0x7900 0x8000 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x5600 0x7780 0x8000 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x5A00 0x7700 0x8000 + 0x00 0x60 0x23 0x25 0x20 0x22 0x5D80 0x7600 0x8000 + 0x00 0x61 0x20 0x22 0x20 0x22 0x6180 0x7500 0x8000 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x6500 0x7400 0x8000 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x6880 0x7300 0x8000 + 0x00 0x64 0x17 0x19 0x20 0x22 0x6D00 0x7200 0x8000 + 0x00 0x65 0x14 0x16 0x20 0x22 0x7200 0x7180 0x8000 + 0x00 0x66 0x11 0x13 0x20 0x22 0x7700 0x7080 0x8000 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x7B80 0x6F80 0x8000 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x6A80 0x7980 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x6580 0x7380 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x4D80 0x8000 0x7980 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x5100 0x8000 0x7A80 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x5580 0x8000 0x7C00 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x5A80 0x8000 0x8000 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x5E80 0x7E00 0x8000 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x6200 0x7B80 0x8000 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x6600 0x7A80 0x8000 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x6900 0x7980 0x8000 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x6D80 0x7880 0x8000 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x7280 0x7780 0x8000 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x7700 0x7680 0x8000 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x7B80 0x7580 0x8000 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x7100 0x7980 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x6A00 0x7400 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x6480 0x6D80 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x4C00 0x8000 0x7380 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x5080 0x8000 0x7500 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x5480 0x8000 0x7600 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x5980 0x8000 0x7780 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x5E80 0x8000 0x7880 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x6300 0x8000 0x7980 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x6780 0x8000 0x7B00 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x6C00 0x8000 0x7C00 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7280 0x8000 0x8000 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x7780 0x7E00 0x8000 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x8000 0x8000 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7700 0x7A00 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x7100 0x7480 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x6980 0x6E80 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x6400 0x6900 + 0x00 0x88 0x32 0x34 0x17 0x19 0x4A80 0x8000 0x6D80 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x4F00 0x8000 0x6F80 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x5380 0x8000 0x7080 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x5880 0x8000 0x7200 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x5D80 0x8000 0x7380 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x6280 0x8000 0x7480 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x6700 0x8000 0x7580 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x6B80 0x8000 0x7700 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7200 0x8000 0x7800 + 0x00 0x91 0x17 0x19 0x17 0x19 0x7780 0x8000 0x7900 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x8000 0x7A00 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7700 0x7500 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x7080 0x7000 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x6980 0x6A80 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x6400 0x6580 + 0x00 0x97 0x32 0x34 0x14 0x16 0x4900 0x8000 0x6880 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x4D80 0x8000 0x6A00 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x5280 0x8000 0x6B80 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x5780 0x8000 0x6C80 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x5D00 0x8000 0x6E00 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x6180 0x8000 0x6F80 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x6680 0x8000 0x7100 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x6B80 0x8000 0x7200 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7180 0x8000 0x7300 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x7780 0x8000 0x7480 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x8000 0x7580 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7700 0x7100 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x7080 0x6B00 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x6900 0x6700 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x6300 0x6200 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x4780 0x8000 0x6500 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x4C80 0x8000 0x6600 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x5100 0x8000 0x6700 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x5680 0x8000 0x6800 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x5C00 0x8000 0x6980 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x6100 0x8000 0x6B00 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x6600 0x8000 0x6B80 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x6B00 0x8000 0x6D00 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7100 0x8000 0x6E00 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x7700 0x8000 0x7000 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x8000 0x7100 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7700 0x6C00 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x7000 0x6780 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x6900 0x6380 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x6280 0x5E00 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x4680 0x8000 0x6100 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x4B00 0x8000 0x6200 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x5080 0x8000 0x6400 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x5500 0x8000 0x6500 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x5B00 0x8000 0x6600 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x6080 0x8000 0x6700 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x6580 0x8000 0x6800 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x6A80 0x8000 0x6900 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7080 0x8000 0x6A80 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x7700 0x8000 0x6B80 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x7E00 0x8000 0x6C80 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7700 0x6880 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x7000 0x6480 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x6880 0x6000 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x6200 0x5A80 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x4500 0x8000 0x5C80 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x4A00 0x8000 0x5E00 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x4F00 0x8000 0x6000 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x5480 0x8000 0x6100 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x5A00 0x8000 0x6280 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x5F80 0x8000 0x6400 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x6480 0x8000 0x6500 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x6A00 0x8000 0x6600 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7000 0x8000 0x6700 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x7680 0x8000 0x6800 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x7E00 0x8000 0x6900 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7700 0x6580 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x6F80 0x6180 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x6800 0x5C80 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x6180 0x5700 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x4400 0x8000 0x5880 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x4880 0x8000 0x5A80 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x4D80 0x8000 0x5C00 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x5300 0x8000 0x5D80 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x5900 0x8000 0x5F00 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x5E80 0x8000 0x6080 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x6400 0x8000 0x6180 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x6900 0x8000 0x6300 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x6F80 0x8000 0x6400 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x7600 0x8000 0x6580 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x7E00 0x8000 0x6600 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7700 0x6300 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x6F80 0x5E80 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x6780 0x5900 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x6080 0x5480 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-vivid-pcc-enable; + somc,mdss-dsi-vivid-pcc-table-size = <226>; + somc,mdss-dsi-vivid-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x5380 0x6D00 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x5600 0x6C80 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x5800 0x6C80 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x5A00 0x6C80 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x5B80 0x6C00 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x5D80 0x6C00 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x5F80 0x6C00 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x6180 0x6B80 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x6400 0x6B80 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x6600 0x6B80 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x6880 0x6B80 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x6B80 0x6B00 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x6E00 0x6B00 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x7180 0x6B00 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x7380 0x6B00 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x5800 0x7080 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x5A00 0x7000 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x5B80 0x6F80 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x5D80 0x6F80 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x5F00 0x6F00 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x6100 0x6F00 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x6300 0x6F00 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x6500 0x6E80 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x6780 0x6E80 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x6A00 0x6E00 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x6C80 0x6E00 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x6F00 0x6E00 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x7180 0x6D80 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x7400 0x6D80 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x7680 0x6D80 0x8000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x5B80 0x7400 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x5D00 0x7380 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x5E80 0x7300 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x6080 0x7280 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x6280 0x7200 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x6480 0x7200 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x6680 0x7180 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x6880 0x7180 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x6B00 0x7100 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x6D80 0x7100 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x7080 0x7080 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x7200 0x7080 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x7500 0x7080 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x7700 0x7000 0x8000 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x7980 0x7000 0x8000 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x5E80 0x7680 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x6000 0x7600 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x6200 0x7580 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x6380 0x7580 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x6580 0x7500 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x6780 0x7480 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x6A00 0x7480 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x6C00 0x7400 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x6E00 0x7380 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x7100 0x7400 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x7300 0x7380 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x7580 0x7300 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x7780 0x7300 0x8000 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x7A80 0x7280 0x8000 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x7C80 0x7200 0x8000 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x6180 0x7980 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x6300 0x7900 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x6480 0x7880 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x6680 0x7800 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x6880 0x7780 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x6B00 0x7780 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x6D00 0x7700 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x6F00 0x7680 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x7180 0x7680 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x7380 0x7600 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x7580 0x7580 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x7780 0x7580 0x8000 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x7A00 0x7500 0x8000 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x7C80 0x7500 0x8000 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x7F80 0x7480 0x8000 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x6400 0x7C80 0x8000 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x6580 0x7C00 0x8000 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x6780 0x7B80 0x8000 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x6A00 0x7B80 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x6B80 0x7A80 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x6D80 0x7A00 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x7000 0x7980 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x7180 0x7900 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x7400 0x7900 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x7600 0x7880 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x7800 0x7800 0x8000 + 0x00 0x57 0x11 0x13 0x23 0x25 0x7B00 0x7780 0x8000 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x7D00 0x7780 0x8000 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x7F80 0x7700 0x8000 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x7500 0x7E80 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x6680 0x7F80 0x8000 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x6880 0x7F00 0x8000 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x6A80 0x7E80 0x8000 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x6C80 0x7E00 0x8000 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x6E80 0x7D80 0x8000 + 0x00 0x60 0x23 0x25 0x20 0x22 0x7080 0x7D00 0x8000 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7200 0x7C80 0x8000 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x7480 0x7C00 0x8000 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x7680 0x7B80 0x8000 + 0x00 0x64 0x17 0x19 0x20 0x22 0x7800 0x7B80 0x8000 + 0x00 0x65 0x14 0x16 0x20 0x22 0x7B00 0x7A80 0x8000 + 0x00 0x66 0x11 0x13 0x20 0x22 0x7D00 0x7A00 0x8000 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x7F80 0x7980 0x8000 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x7780 0x7E80 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x7480 0x7C00 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x6700 0x8000 0x7E80 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x6A00 0x8000 0x7F00 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x6C80 0x8000 0x7F80 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x6E80 0x8000 0x8000 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x7180 0x8000 0x8000 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x7280 0x7F80 0x8000 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7500 0x7F00 0x8000 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x7680 0x7E80 0x8000 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x7880 0x7E00 0x8000 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x7B00 0x7D80 0x8000 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x7D00 0x7D00 0x8000 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x7F80 0x7D00 0x8000 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x7A80 0x7E80 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x7780 0x7C80 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x7480 0x7980 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x6680 0x8000 0x7C00 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x6900 0x8000 0x7C80 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x6B80 0x8000 0x7D00 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x6E00 0x8000 0x7D80 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x7100 0x8000 0x7E80 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x7380 0x8000 0x7E80 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7600 0x8000 0x7F00 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x7800 0x8000 0x7F80 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7B80 0x8000 0x8000 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x7D00 0x8000 0x8000 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x7F80 0x7F80 0x8000 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7D80 0x7F00 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x7A80 0x7C80 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x7780 0x7A00 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x7400 0x7700 + 0x00 0x88 0x32 0x34 0x17 0x19 0x6580 0x8000 0x7900 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x6800 0x8000 0x7A00 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x6B00 0x8000 0x7B00 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x6D80 0x8000 0x7B80 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x7100 0x8000 0x7C00 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x7300 0x8000 0x7C80 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7580 0x8000 0x7D00 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7800 0x8000 0x7D80 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7B00 0x8000 0x7E00 + 0x00 0x91 0x17 0x19 0x17 0x19 0x7D80 0x8000 0x7E80 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x8000 0x7F00 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7D80 0x7D00 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x7A00 0x7A00 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x7700 0x7780 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x7400 0x7500 + 0x00 0x97 0x32 0x34 0x14 0x16 0x6500 0x8000 0x7700 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x6780 0x8000 0x7780 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x6A80 0x8000 0x7800 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x6D00 0x8000 0x7900 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x7080 0x8000 0x7980 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x7280 0x8000 0x7A00 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7580 0x8000 0x7A80 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7780 0x8000 0x7B80 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7B00 0x8000 0x7C00 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x7D00 0x8000 0x7C80 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x8000 0x7D00 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7D80 0x7B00 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x7A00 0x7800 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x7700 0x7600 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x7400 0x7380 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x6400 0x8000 0x7500 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x6680 0x8000 0x7580 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x6A00 0x8000 0x7600 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x6C80 0x8000 0x7700 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x7000 0x8000 0x7780 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x7200 0x8000 0x7800 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x7500 0x8000 0x7800 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7780 0x8000 0x7900 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7A80 0x8000 0x7980 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x7D00 0x8000 0x7A00 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x8000 0x7B00 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7D80 0x7880 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x7A00 0x7680 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x7680 0x7400 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x7380 0x7180 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x6380 0x8000 0x7280 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x6600 0x8000 0x7380 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x6880 0x8000 0x7400 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x6C00 0x8000 0x7500 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x6F00 0x8000 0x7580 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x7180 0x8000 0x7600 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x7480 0x8000 0x7680 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7700 0x8000 0x7700 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7A00 0x8000 0x7800 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x7D00 0x8000 0x7880 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x8000 0x7900 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7D80 0x7700 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x7A00 0x7480 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x7680 0x7280 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x7300 0x6F80 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x6280 0x8000 0x7080 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x6500 0x8000 0x7180 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x6800 0x8000 0x7200 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x6B80 0x8000 0x7300 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x6E80 0x8000 0x7380 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x7180 0x8000 0x7480 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x7400 0x8000 0x7500 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7680 0x8000 0x7580 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7980 0x8000 0x7600 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x7D00 0x8000 0x7680 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x8000 0x7700 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7D80 0x7580 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x7980 0x7300 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x7600 0x7080 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x7300 0x6E00 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x6180 0x8000 0x6F00 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x6480 0x8000 0x6F80 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x6780 0x8000 0x7080 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x6B00 0x8000 0x7100 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x6E00 0x8000 0x7200 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x7180 0x8000 0x7280 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x7400 0x8000 0x7300 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7680 0x8000 0x7400 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7980 0x8000 0x7480 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x7C80 0x8000 0x7500 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x8000 0x7580 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7D80 0x7400 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x7980 0x7180 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x7600 0x6F00 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x7200 0x6C00 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-lilac.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-lilac.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..77e9bfae880cda3816a0b09d885609f1feb407b1 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-lilac.dtsi @@ -0,0 +1,640 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include "dsi-panel-lilac-id5_pcc.dtsi" +#include "dsi-panel-lilac-id8_pcc.dtsi" + +&mdss_mdp { + /* LGD ID5 */ + dsi_5: somc,5_panel { + qcom,mdss-dsi-panel-name = "5"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <1280>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-h-back-porch = <8>; + qcom,mdss-dsi-h-pulse-width = <8>; + qcom,mdss-dsi-h-front-porch = <280>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <8>; + qcom,mdss-dsi-v-front-porch = <1162>; + qcom,mdss-pan-physical-width-dimension = <56>; + qcom,mdss-pan-physical-height-dimension = <100>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-on-command = [ + 23 01 00 00 00 00 02 B0 04 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 20 C1 84 01 10 FF D3 29 E3 3B 66 D5 EF 1A 63 CD FD BA CC 01 62 90 F2 3F 00 AA 40 02 C2 11 08 00 01 + 29 01 00 00 00 00 0A CB FC F7 FB 0F 00 00 00 00 00 + 39 01 00 00 00 00 05 2A 00 00 02 CF + 39 01 00 00 00 00 05 2B 00 00 04 FF + 05 01 00 00 64 00 01 11]; + qcom,mdss-dsi-post-panel-on-command = [ + 05 01 00 00 00 00 01 29]; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 14 00 01 28 + 05 01 00 00 64 00 01 10]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1E 06 06 0E 0F 07 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x2C>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_full_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + + somc,lcd-id-adc = <801000 917000>; + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 15>, <1 20>; + somc,pw-off-rst-b-seq = <0 11>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <0>; + somc,pw-wait-after-on-vsp = <8>; + somc,pw-wait-after-on-vsn = <0>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <1>; + somc,pw-wait-after-off-vsp = <8>; + somc,pw-wait-after-off-vsn = <8>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <11>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <300>; + + somc,lab-output-voltage = <5500000>; + somc,ibb-output-voltage = <5500000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,ewu-rst-seq = <0 2>, <1 5>; + somc,ewu-wait-after-touch-reset = <0>; + + somc,change-fps-enable; + somc,change-fps-panel-type = "full_incell_type"; + somc,change-fps-panel-mode = "dynamic_mode"; + somc,change-fps-command = + [29 01 00 00 00 00 02 B0 04 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 02 C6 59]; + somc,driver-ic-total-porch = <11>; + somc,driver-ic-vdisp = <1280>; + somc,driver-ic-rclk = <14000000>; + somc,driver-ic-vtp = <1330>; + somc,change-fps-rtn-pos = <2 1>; + + somc,mdss-dsi-disp-on-in-hs = <0>; + somc,mdss-dsi-wait-time-before-post-on-cmd = <0>; + }; + + /* Kugo AUO ID6 */ + dsi_6: somc,6_panel { + qcom,mdss-dsi-panel-name = "6"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <1280>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-h-back-porch = <8>; + qcom,mdss-dsi-h-pulse-width = <8>; + qcom,mdss-dsi-h-front-porch = <280>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <8>; + qcom,mdss-dsi-v-front-porch = <1162>; + qcom,mdss-pan-physical-width-dimension = <56>; + qcom,mdss-pan-physical-height-dimension = <100>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-on-command = [ + 29 01 00 00 00 00 02 B0 04 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 20 C1 84 01 00 FF 47 99 80 39 EB FF CF 9A 73 8D FD BF D6 31 2F 89 F1 3F 00 00 40 22 82 03 08 00 01 + 29 01 00 00 00 00 0F C6 7A 02 7A 05 78 01 64 01 02 01 02 0B 1C 07 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 05 2A 00 00 02 CF + 39 01 00 00 00 00 05 2B 00 00 04 FF + 05 01 00 00 78 00 01 11]; + qcom,mdss-dsi-post-panel-on-command = [ + 05 01 00 00 00 00 01 29]; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 28 00 01 28 + 29 01 00 00 00 00 1D D3 13 3B BB B3 A5 33 33 33 00 80 A1 AA 4F 4F 33 33 33 F7 F2 0F 7D 7C FF 0F 99 00 FF FF + 29 01 00 00 28 00 04 D4 00 00 00 + 05 01 00 00 78 00 01 10]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1E 06 06 0E 0F 07 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x2C>; + + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_full_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + + somc,lcd-id-adc = <565000 653000>; + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 10>, <1 10>; + somc,pw-off-rst-seq = <0 0>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <1>; + somc,pw-wait-after-on-vsp = <1>; + somc,pw-wait-after-on-vsn = <20>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <0>; + somc,pw-wait-after-off-vsp = <70>; + somc,pw-wait-after-off-vsn = <0>; + somc,pw-wait-after-on-touch-avdd = <1>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-avdd = <0>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <11>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <100>; + + somc,lab-output-voltage = <5600000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + + somc,ewu-wait-after-touch-reset = <40>; + + somc,mdss-dsi-disp-on-in-hs = <0>; + somc,mdss-dsi-wait-time-before-post-on-cmd = <0>; + }; + + /* JDI ID8 */ + dsi_8: somc,8_panel { + qcom,mdss-dsi-panel-name = "8"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <1280>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-h-back-porch = <8>; + qcom,mdss-dsi-h-pulse-width = <8>; + qcom,mdss-dsi-h-front-porch = <280>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <8>; + qcom,mdss-dsi-v-front-porch = <1162>; + qcom,mdss-pan-physical-width-dimension = <56>; + qcom,mdss-pan-physical-height-dimension = <100>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-on-command = [ + 29 01 00 00 00 00 02 B0 00 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 08 C2 01 05 00 04 00 04 F0 + 29 01 00 00 00 00 0E EC 64 DC 7A 7A 3D 00 0B 0B 13 15 68 0B B5 + 29 01 00 00 00 00 02 B0 03 + 39 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 02 36 00 + 39 01 00 00 00 00 02 3A 77 + 39 01 00 00 00 00 05 2A 00 00 02 CF + 39 01 00 00 00 00 05 2B 00 00 04 FF + 39 01 00 00 00 00 03 44 00 00 + 39 01 00 00 78 00 01 11]; + qcom,mdss-dsi-post-panel-on-command = [ + 39 01 00 00 00 00 01 29]; + qcom,mdss-dsi-off-command = [ + 39 01 00 00 00 00 01 28 + 29 01 00 00 00 00 02 B0 00 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 0E EC 64 DC 7A 7A 3D 00 0B 0B 13 15 68 0B 95 + 29 01 00 00 00 00 02 B0 03 + 39 01 00 00 78 00 01 10]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1E 06 06 0E 0F 07 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x2C>; + + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_hybrid_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + + somc,lcd-id-adc = <215000 256000>; + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 15>, <1 16>, <0 15>, <1 20>; + somc,pw-off-rst-b-seq = <0 11>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <1>; + somc,pw-wait-after-on-vsp = <1>; + somc,pw-wait-after-on-vsn = <0>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <1>; + somc,pw-wait-after-off-vsp = <8>; + somc,pw-wait-after-off-vsn = <8>; + somc,pw-wait-after-on-touch-avdd = <1>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-avdd = <0>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <11>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <300>; + + somc,lab-output-voltage = <5600000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,ewu-wait-after-touch-reset = <40>; + + somc,change-fps-enable; + somc,change-fps-panel-type = "hybrid_incell_type"; + somc,change-fps-panel-mode = "dynamic_mode"; + somc,change-fps-command = + [29 01 00 00 00 00 02 B0 04 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 08 C2 01 05 00 04 00 04 F0]; + somc,driver-ic-rtn = <122>; + somc,driver-ic-vdisp = <1280>; + somc,driver-ic-vtouch = <6993970>; + somc,driver-ic-mclk = <61539>; + somc,change-fps-send-pos = <2 4>; + somc,change-fps-send-byte = <4>; + somc,change-fps-porch-mask-pos = <3>; + somc,change-fps-porch-mask = <0xF0>; + somc,change-fps-porch-range = <4 511>; + + somc,mdss-dsi-disp-on-in-hs = <0>; + somc,mdss-dsi-wait-time-before-post-on-cmd = <0>; + }; + + /* Kagura Sharp ID9 */ + dsi_9: somc,9_panel { + qcom,mdss-dsi-panel-name = "9"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <1280>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-h-back-porch = <8>; + qcom,mdss-dsi-h-pulse-width = <8>; + qcom,mdss-dsi-h-front-porch = <280>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <8>; + qcom,mdss-dsi-v-front-porch = <1162>; + qcom,mdss-pan-physical-width-dimension = <56>; + qcom,mdss-pan-physical-height-dimension = <100>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-on-command = [ + 39 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 05 2A 00 00 02 CF + 39 01 00 00 00 00 05 2B 00 00 04 FF + 05 01 00 00 78 00 01 29]; + qcom,mdss-dsi-post-panel-on-command = [ + 05 01 00 00 00 00 01 11]; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 00 00 01 28 + 05 01 00 00 78 00 01 10]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1E 06 06 0E 0F 07 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x2C>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_full_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + + somc,lcd-id-adc = <0 57000>; + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 10>, <1 10>; + somc,pw-off-rst-b-seq = <0 5>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <10>; + somc,pw-wait-after-on-vsp = <10>; + somc,pw-wait-after-on-vsn = <0>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <0>; + somc,pw-wait-after-off-vsp = <10>; + somc,pw-wait-after-off-vsn = <10>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <6>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <300>; + + somc,lab-output-voltage = <5600000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,ewu-rst-seq = <0 2>, <1 5>; + somc,ewu-wait-after-touch-reset = <0>; + }; + + /* Default */ + dsi_default_panel: somc,default_cmd_panel { + qcom,mdss-dsi-panel-name = "default"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <1280>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-h-back-porch = <8>; + qcom,mdss-dsi-h-pulse-width = <8>; + qcom,mdss-dsi-h-front-porch = <280>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <8>; + qcom,mdss-dsi-v-front-porch = <1162>; + qcom,mdss-pan-physical-width-dimension = <56>; + qcom,mdss-pan-physical-height-dimension = <100>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-on-command = [ + 29 01 00 00 00 00 02 B0 00 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 08 C2 01 05 00 04 00 04 F0 + 29 01 00 00 00 00 0E EC 64 DC 7A 7A 3D 00 0B 0B 13 15 68 0B B5 + 29 01 00 00 00 00 02 B0 03 + 39 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 02 36 00 + 39 01 00 00 00 00 02 3A 77 + 39 01 00 00 00 00 05 2A 00 00 02 CF + 39 01 00 00 00 00 05 2B 00 00 04 FF + 39 01 00 00 00 00 03 44 00 00 + 39 01 00 00 78 00 01 11]; + qcom,mdss-dsi-post-panel-on-command = [ + 39 01 00 00 00 00 01 29]; + qcom,mdss-dsi-off-command = [ + 39 01 00 00 00 00 01 28 + 29 01 00 00 00 00 02 B0 00 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 0E EC 64 DC 7A 7A 3D 00 0B 0B 13 15 68 0B 95 + 29 01 00 00 00 00 02 B0 03 + 39 01 00 00 78 00 01 10]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1E 06 06 0E 0F 07 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x2C>; + + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_hybrid_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + + somc,lcd-id-adc = <0 0x7fffffff>; + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 15>, <1 16>, <0 15>, <1 20>; + somc,pw-off-rst-b-seq = <0 11>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <1>; + somc,pw-wait-after-on-vsp = <1>; + somc,pw-wait-after-on-vsn = <0>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <1>; + somc,pw-wait-after-off-vsp = <8>; + somc,pw-wait-after-off-vsn = <8>; + somc,pw-wait-after-on-touch-avdd = <1>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-avdd = <0>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <11>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <300>; + + somc,lab-output-voltage = <5600000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,ewu-wait-after-touch-reset = <40>; + + somc,mdss-dsi-disp-on-in-hs = <0>; + somc,mdss-dsi-wait-time-before-post-on-cmd = <0>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-maple-id3_pcc.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-maple-id3_pcc.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..280c2d9920ed051cea713719ae534e97f5044dd7 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-maple-id3_pcc.dtsi @@ -0,0 +1,256 @@ +/* arch/arm64/boot/dts/qcom/dsi-panel-maple-id3_pcc.dtsi + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +/* pcc-table for Satsuki Sharp */ +&mdss_mdp { + dsi_dual0_3_panel: somc,dual0_3_panel { + somc,mdss-dsi-pcc-enable; + somc,mdss-dsi-uv-command = [ + 06 01 00 00 00 00 01 DA + 06 01 00 00 00 00 01 DB]; + somc,mdss-dsi-uv-param-type = <4>; + somc,mdss-dsi-pcc-table-size = <225>; + somc,mdss-dsi-pcc-table = < + 0x00 0x01 0x39 0x3C 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x02 0x35 0x38 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x03 0x31 0x34 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x04 0x2D 0x30 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x05 0x29 0x2C 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x06 0x25 0x28 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x07 0x21 0x24 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x08 0x1D 0x20 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x09 0x18 0x1B 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x0A 0x14 0x17 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x0B 0x10 0x13 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x0C 0x0C 0x0F 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x0D 0x08 0x0B 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x0E 0x04 0x07 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x0F 0x00 0x03 0x38 0x3B 0x8000 0x8000 0x8000 + 0x00 0x10 0x39 0x3C 0x34 0x37 0x8000 0x8000 0x8000 + 0x00 0x11 0x35 0x38 0x34 0x37 0x8000 0x8000 0x8000 + 0x00 0x12 0x31 0x34 0x34 0x37 0x8000 0x8000 0x8000 + 0x00 0x13 0x2D 0x30 0x34 0x37 0x5D00 0x7000 0x8000 + 0x00 0x14 0x29 0x2C 0x34 0x37 0x6100 0x6F80 0x8000 + 0x00 0x15 0x25 0x28 0x34 0x37 0x6500 0x6F00 0x8000 + 0x00 0x16 0x21 0x24 0x34 0x37 0x6980 0x6E80 0x8000 + 0x00 0x17 0x1D 0x20 0x34 0x37 0x6E00 0x6E00 0x8000 + 0x00 0x18 0x18 0x1B 0x34 0x37 0x7100 0x7100 0x8000 + 0x00 0x19 0x14 0x17 0x34 0x37 0x7480 0x7380 0x8000 + 0x00 0x1A 0x10 0x13 0x34 0x37 0x7980 0x7380 0x8000 + 0x00 0x1B 0x0C 0x0F 0x34 0x37 0x7E80 0x7300 0x8000 + 0x00 0x1C 0x08 0x0B 0x34 0x37 0x8000 0x8000 0x8000 + 0x00 0x1D 0x04 0x07 0x34 0x37 0x8000 0x8000 0x8000 + 0x00 0x1E 0x00 0x03 0x34 0x37 0x8000 0x8000 0x8000 + 0x00 0x1F 0x39 0x3C 0x30 0x33 0x8000 0x8000 0x8000 + 0x00 0x20 0x35 0x38 0x30 0x33 0x8000 0x8000 0x8000 + 0x00 0x21 0x31 0x34 0x30 0x33 0x8000 0x8000 0x8000 + 0x00 0x22 0x2D 0x30 0x30 0x33 0x6080 0x7300 0x8000 + 0x00 0x23 0x29 0x2C 0x30 0x33 0x6480 0x7280 0x8000 + 0x00 0x24 0x25 0x28 0x30 0x33 0x6880 0x7200 0x8000 + 0x00 0x25 0x21 0x24 0x30 0x33 0x6D00 0x7180 0x8000 + 0x00 0x26 0x1D 0x20 0x30 0x33 0x7180 0x7100 0x8000 + 0x00 0x27 0x18 0x1B 0x30 0x33 0x7480 0x7400 0x8000 + 0x00 0x28 0x14 0x17 0x30 0x33 0x7780 0x7700 0x8000 + 0x00 0x29 0x10 0x13 0x30 0x33 0x7C80 0x7680 0x8000 + 0x00 0x2A 0x0C 0x0F 0x30 0x33 0x8000 0x7400 0x7E80 + 0x00 0x2B 0x08 0x0B 0x30 0x33 0x8000 0x8000 0x8000 + 0x00 0x2C 0x04 0x07 0x30 0x33 0x8000 0x8000 0x8000 + 0x00 0x2D 0x00 0x03 0x30 0x33 0x8000 0x8000 0x8000 + 0x00 0x2E 0x39 0x3C 0x2C 0x2F 0x8000 0x8000 0x8000 + 0x00 0x2F 0x35 0x38 0x2C 0x2F 0x8000 0x8000 0x8000 + 0x00 0x30 0x31 0x34 0x2C 0x2F 0x8000 0x8000 0x8000 + 0x00 0x31 0x2D 0x30 0x2C 0x2F 0x6400 0x7680 0x8000 + 0x00 0x32 0x29 0x2C 0x2C 0x2F 0x6780 0x7600 0x8000 + 0x00 0x33 0x25 0x28 0x2C 0x2F 0x6C00 0x7580 0x8000 + 0x00 0x34 0x21 0x24 0x2C 0x2F 0x7000 0x7480 0x8000 + 0x00 0x35 0x1D 0x20 0x2C 0x2F 0x7480 0x7400 0x8000 + 0x00 0x36 0x18 0x1B 0x2C 0x2F 0x7780 0x7700 0x8000 + 0x00 0x37 0x14 0x17 0x2C 0x2F 0x7800 0x7700 0x8000 + 0x00 0x38 0x10 0x13 0x2C 0x2F 0x7D00 0x7680 0x8000 + 0x00 0x39 0x0C 0x0F 0x2C 0x2F 0x7D00 0x7680 0x8000 + 0x00 0x3A 0x08 0x0B 0x2C 0x2F 0x8000 0x8000 0x8000 + 0x00 0x3B 0x04 0x07 0x2C 0x2F 0x8000 0x8000 0x8000 + 0x00 0x3C 0x00 0x03 0x2C 0x2F 0x8000 0x8000 0x8000 + 0x00 0x3D 0x39 0x3C 0x28 0x2B 0x8000 0x8000 0x8000 + 0x00 0x3E 0x35 0x38 0x28 0x2B 0x8000 0x8000 0x8000 + 0x00 0x3F 0x31 0x34 0x28 0x2B 0x8000 0x8000 0x8000 + 0x00 0x40 0x2D 0x30 0x28 0x2B 0x6700 0x7A00 0x8000 + 0x00 0x41 0x29 0x2C 0x28 0x2B 0x6B00 0x7900 0x8000 + 0x00 0x42 0x25 0x28 0x28 0x2B 0x6F00 0x7880 0x8000 + 0x00 0x43 0x21 0x24 0x28 0x2B 0x7300 0x7800 0x8000 + 0x00 0x44 0x1D 0x20 0x28 0x2B 0x7780 0x7700 0x8000 + 0x00 0x45 0x18 0x1B 0x28 0x2B 0x7A80 0x7A00 0x8000 + 0x00 0x46 0x14 0x17 0x28 0x2B 0x7B00 0x7A00 0x8000 + 0x00 0x47 0x10 0x13 0x28 0x2B 0x7F80 0x7980 0x8000 + 0x00 0x48 0x0C 0x0F 0x28 0x2B 0x8000 0x7980 0x8000 + 0x00 0x49 0x08 0x0B 0x28 0x2B 0x8000 0x8000 0x8000 + 0x00 0x4A 0x04 0x07 0x28 0x2B 0x8000 0x8000 0x8000 + 0x00 0x4B 0x00 0x03 0x28 0x2B 0x8000 0x8000 0x8000 + 0x00 0x4C 0x39 0x3C 0x24 0x27 0x8000 0x8000 0x8000 + 0x00 0x4D 0x35 0x38 0x24 0x27 0x8000 0x8000 0x8000 + 0x00 0x4E 0x31 0x34 0x24 0x27 0x8000 0x8000 0x8000 + 0x00 0x4F 0x2D 0x30 0x24 0x27 0x6A00 0x7D00 0x8000 + 0x00 0x50 0x29 0x2C 0x24 0x27 0x6E00 0x7C80 0x8000 + 0x00 0x51 0x25 0x28 0x24 0x27 0x7200 0x7B80 0x8000 + 0x00 0x52 0x21 0x24 0x24 0x27 0x7600 0x7B00 0x8000 + 0x00 0x53 0x1D 0x20 0x24 0x27 0x7A80 0x7A00 0x8000 + 0x00 0x54 0x18 0x1B 0x24 0x27 0x7D80 0x7D00 0x8000 + 0x00 0x55 0x14 0x17 0x24 0x27 0x7D80 0x7D00 0x8000 + 0x00 0x56 0x10 0x13 0x24 0x27 0x8000 0x7A00 0x7E00 + 0x00 0x57 0x0C 0x0F 0x24 0x27 0x8000 0x7980 0x7D80 + 0x00 0x58 0x08 0x0B 0x24 0x27 0x8000 0x8000 0x8000 + 0x00 0x59 0x04 0x07 0x24 0x27 0x8000 0x8000 0x8000 + 0x00 0x5A 0x00 0x03 0x24 0x27 0x8000 0x8000 0x8000 + 0x00 0x5B 0x39 0x3C 0x20 0x23 0x8000 0x8000 0x8000 + 0x00 0x5C 0x35 0x38 0x20 0x23 0x8000 0x8000 0x8000 + 0x00 0x5D 0x31 0x34 0x20 0x23 0x8000 0x8000 0x8000 + 0x00 0x5E 0x2D 0x30 0x20 0x23 0x6D00 0x8000 0x8000 + 0x00 0x5F 0x29 0x2C 0x20 0x23 0x7100 0x7F80 0x8000 + 0x00 0x60 0x25 0x28 0x20 0x23 0x7500 0x7E80 0x8000 + 0x00 0x61 0x21 0x24 0x20 0x23 0x7900 0x7E00 0x8000 + 0x00 0x62 0x1D 0x20 0x20 0x23 0x7D80 0x7D00 0x8000 + 0x00 0x63 0x18 0x1B 0x20 0x23 0x8000 0x8000 0x8000 + 0x00 0x64 0x14 0x17 0x20 0x23 0x8000 0x8000 0x8000 + 0x00 0x65 0x10 0x13 0x20 0x23 0x8000 0x7980 0x7B00 + 0x00 0x66 0x0C 0x0F 0x20 0x23 0x8000 0x7900 0x7A80 + 0x00 0x67 0x08 0x0B 0x20 0x23 0x8000 0x8000 0x8000 + 0x00 0x68 0x04 0x07 0x20 0x23 0x8000 0x8000 0x8000 + 0x00 0x69 0x00 0x03 0x20 0x23 0x8000 0x8000 0x8000 + 0x00 0x6A 0x39 0x3C 0x1C 0x1F 0x8000 0x8000 0x8000 + 0x00 0x6B 0x35 0x38 0x1C 0x1F 0x8000 0x8000 0x8000 + 0x00 0x6C 0x31 0x34 0x1C 0x1F 0x8000 0x8000 0x8000 + 0x00 0x6D 0x2D 0x30 0x1C 0x1F 0x6C80 0x8000 0x7D80 + 0x00 0x6E 0x29 0x2C 0x1C 0x1F 0x7100 0x8000 0x7E00 + 0x00 0x6F 0x25 0x28 0x1C 0x1F 0x7600 0x8000 0x7F00 + 0x00 0x70 0x21 0x24 0x1C 0x1F 0x7B00 0x8000 0x7F80 + 0x00 0x71 0x18 0x1B 0x1C 0x1F 0x8000 0x8000 0x8000 + 0x00 0x72 0x14 0x17 0x1C 0x1F 0x8000 0x7A00 0x7B80 + 0x00 0x73 0x10 0x13 0x1C 0x1F 0x8000 0x7980 0x7B00 + 0x00 0x74 0x0C 0x0F 0x1C 0x1F 0x8000 0x7880 0x7A80 + 0x00 0x75 0x08 0x0B 0x1C 0x1F 0x8000 0x8000 0x8000 + 0x00 0x76 0x04 0x07 0x1C 0x1F 0x8000 0x8000 0x8000 + 0x00 0x77 0x00 0x03 0x1C 0x1F 0x8000 0x8000 0x8000 + 0x00 0x78 0x39 0x3C 0x18 0x1B 0x8000 0x8000 0x8000 + 0x00 0x79 0x35 0x38 0x18 0x1B 0x8000 0x8000 0x8000 + 0x00 0x7A 0x31 0x34 0x18 0x1B 0x8000 0x8000 0x8000 + 0x00 0x7B 0x2D 0x30 0x18 0x1B 0x6B80 0x8000 0x7A80 + 0x00 0x7C 0x29 0x2C 0x18 0x1B 0x7080 0x8000 0x7B80 + 0x00 0x7D 0x25 0x28 0x18 0x1B 0x7580 0x8000 0x7C00 + 0x00 0x7E 0x21 0x24 0x18 0x1B 0x7B00 0x8000 0x7D00 + 0x00 0x7F 0x1D 0x20 0x18 0x1B 0x8000 0x8000 0x7D80 + 0x00 0x80 0x18 0x1B 0x18 0x1B 0x8000 0x8000 0x7D80 + 0x00 0x81 0x14 0x17 0x18 0x1B 0x8000 0x8000 0x7D80 + 0x00 0x82 0x10 0x13 0x18 0x1B 0x8000 0x7900 0x7800 + 0x00 0x83 0x0C 0x0F 0x18 0x1B 0x8000 0x7800 0x7780 + 0x00 0x84 0x08 0x0B 0x18 0x1B 0x8000 0x8000 0x8000 + 0x00 0x85 0x04 0x07 0x18 0x1B 0x8000 0x8000 0x8000 + 0x00 0x86 0x00 0x03 0x18 0x1B 0x8000 0x8000 0x8000 + 0x00 0x87 0x39 0x3C 0x14 0x17 0x8000 0x8000 0x8000 + 0x00 0x88 0x35 0x38 0x14 0x17 0x8000 0x8000 0x8000 + 0x00 0x89 0x31 0x34 0x14 0x17 0x8000 0x8000 0x8000 + 0x00 0x8A 0x2D 0x30 0x14 0x17 0x6A80 0x8000 0x7800 + 0x00 0x8B 0x29 0x2C 0x14 0x17 0x7000 0x8000 0x7880 + 0x00 0x8C 0x25 0x28 0x14 0x17 0x7500 0x8000 0x7980 + 0x00 0x8D 0x21 0x24 0x14 0x17 0x7A80 0x8000 0x7A80 + 0x00 0x8E 0x1D 0x20 0x14 0x17 0x8000 0x8000 0x7B00 + 0x00 0x8F 0x18 0x1B 0x14 0x17 0x8000 0x8000 0x7B00 + 0x00 0x90 0x14 0x17 0x14 0x17 0x8000 0x8000 0x7B00 + 0x00 0x91 0x10 0x13 0x14 0x17 0x8000 0x7880 0x7580 + 0x00 0x92 0x0C 0x0F 0x14 0x17 0x8000 0x7780 0x7500 + 0x00 0x93 0x08 0x0B 0x14 0x17 0x8000 0x8000 0x8000 + 0x00 0x94 0x04 0x07 0x14 0x17 0x8000 0x8000 0x8000 + 0x00 0x95 0x00 0x03 0x14 0x17 0x8000 0x8000 0x8000 + 0x00 0x96 0x39 0x3C 0x10 0x13 0x8000 0x8000 0x8000 + 0x00 0x97 0x35 0x38 0x10 0x13 0x8000 0x8000 0x8000 + 0x00 0x98 0x31 0x34 0x10 0x13 0x8000 0x8000 0x8000 + 0x00 0x99 0x2D 0x30 0x10 0x13 0x6A00 0x8000 0x7580 + 0x00 0x9A 0x29 0x2C 0x10 0x13 0x6F80 0x8000 0x7680 + 0x00 0x9B 0x25 0x28 0x10 0x13 0x7500 0x8000 0x7700 + 0x00 0x9C 0x21 0x24 0x10 0x13 0x7A80 0x8000 0x7800 + 0x00 0x9D 0x1D 0x20 0x10 0x13 0x8000 0x8000 0x7900 + 0x00 0x9E 0x18 0x1C 0x10 0x13 0x8000 0x8000 0x7900 + 0x00 0x9F 0x14 0x17 0x10 0x13 0x8000 0x8000 0x7900 + 0x00 0xA0 0x10 0x13 0x10 0x13 0x8000 0x7800 0x7380 + 0x00 0xA1 0x0C 0x0F 0x10 0x13 0x8000 0x7700 0x7280 + 0x00 0xA2 0x08 0x0B 0x10 0x13 0x8000 0x8000 0x8000 + 0x00 0xA3 0x04 0x07 0x10 0x13 0x8000 0x8000 0x8000 + 0x00 0xA4 0x00 0x03 0x10 0x13 0x8000 0x8000 0x8000 + 0x00 0xA5 0x39 0x3C 0x0C 0x0F 0x8000 0x8000 0x8000 + 0x00 0xA6 0x35 0x38 0x0C 0x0F 0x8000 0x8000 0x8000 + 0x00 0xA7 0x31 0x34 0x0C 0x0F 0x8000 0x8000 0x8000 + 0x00 0xA8 0x2D 0x30 0x0C 0x0F 0x6900 0x8000 0x7300 + 0x00 0xA9 0x29 0x2C 0x0C 0x0F 0x6E80 0x8000 0x7400 + 0x00 0xAA 0x25 0x28 0x0C 0x0F 0x7480 0x8000 0x7500 + 0x00 0xAB 0x21 0x24 0x0C 0x0F 0x7A00 0x8000 0x7600 + 0x00 0xAC 0x1D 0x20 0x0C 0x0F 0x8000 0x8000 0x7700 + 0x00 0xAD 0x18 0x1C 0x0C 0x0F 0x8000 0x8000 0x7700 + 0x00 0xAE 0x14 0x17 0x0C 0x0F 0x8000 0x8000 0x7700 + 0x00 0xAF 0x10 0x13 0x0C 0x0F 0x8000 0x7780 0x7180 + 0x00 0xB0 0x0C 0x0F 0x0C 0x0F 0x8000 0x7680 0x7080 + 0x00 0xB1 0x08 0x0B 0x0C 0x0F 0x8000 0x8000 0x8000 + 0x00 0xB2 0x04 0x07 0x0C 0x0F 0x8000 0x8000 0x8000 + 0x00 0xB3 0x00 0x03 0x0C 0x0F 0x8000 0x8000 0x8000 + 0x00 0xB4 0x39 0x3C 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xB5 0x35 0x38 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xB6 0x31 0x34 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xB7 0x2D 0x30 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xB8 0x29 0x2C 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xB9 0x25 0x28 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xBA 0x21 0x24 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xBB 0x1D 0x20 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xBC 0x18 0x1C 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xBD 0x14 0x17 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xBE 0x10 0x13 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xBF 0x0C 0x0F 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xC0 0x08 0x0B 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xC1 0x04 0x07 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xC2 0x00 0x03 0x08 0x0B 0x8000 0x8000 0x8000 + 0x00 0xC3 0x39 0x3C 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xC4 0x35 0x38 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xC5 0x31 0x34 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xC6 0x2D 0x30 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xC7 0x29 0x2C 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xC8 0x25 0x28 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xC9 0x21 0x24 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xCA 0x1D 0x20 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xCB 0x19 0x1C 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xCC 0x14 0x17 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xCD 0x10 0x13 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xCE 0x0C 0x0F 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xCF 0x08 0x0B 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xD0 0x04 0x07 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xD1 0x00 0x03 0x04 0x07 0x8000 0x8000 0x8000 + 0x00 0xD2 0x39 0x3C 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xD3 0x35 0x38 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xD4 0x31 0x34 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xD5 0x2D 0x30 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xD6 0x29 0x2C 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xD7 0x25 0x28 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xD8 0x21 0x24 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xD9 0x1D 0x20 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xDA 0x19 0x1C 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xDB 0x14 0x17 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xDC 0x10 0x13 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xDD 0x0C 0x0F 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xDE 0x08 0x0B 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xDF 0x04 0x07 0x00 0x03 0x8000 0x8000 0x8000 + 0x00 0xE0 0x00 0x03 0x00 0x03 0x8000 0x8000 0x8000 + 0xFF 0x00 0x0C 0x30 0x0C 0x2F 0x8000 0x8000 0x8000>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-maple-id6_pcc.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-maple-id6_pcc.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..ac32c463bf4cd84da6f597abff6871d7f09a3948 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-maple-id6_pcc.dtsi @@ -0,0 +1,947 @@ +/* arch/arm64/boot/dts/qcom/dsi-panel-maple-id6_pcc.dtsi + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +/* pcc-table for Maple Sharp */ +&mdss_mdp { + dsi_dual0_6_panel: somc,dual0_6_panel { + somc,mdss-dsi-pcc-enable; + somc,mdss-dsi-uv-command = [ + 06 01 00 00 00 00 01 DA + 06 01 00 00 00 00 01 DB]; + somc,mdss-dsi-uv-param-type = <4>; + somc,mdss-dsi-pcc-table-size = <226>; + somc,mdss-dsi-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x4580 0x6280 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x4900 0x6200 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x4C80 0x6200 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x4F80 0x6180 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x5400 0x6180 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x5800 0x6100 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x5C00 0x6100 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x6100 0x6080 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x6600 0x6080 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x6A80 0x6000 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x6F80 0x6000 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x7600 0x6000 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x7C80 0x5B80 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x8000 0x5A80 0x7D00 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x8000 0x5380 0x7480 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x4A80 0x6980 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x4E00 0x6900 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x5180 0x6880 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x5580 0x6880 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x5980 0x6800 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x5D80 0x6380 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x6200 0x6380 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x6680 0x6300 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x6B80 0x6300 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x7080 0x6280 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x7680 0x6280 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x7C80 0x6280 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x8000 0x6100 0x7D80 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x8000 0x5A00 0x7600 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x8000 0x5280 0x6E80 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x4F80 0x7080 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x5300 0x7000 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x5700 0x6B80 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x5B00 0x6B00 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x5E80 0x6B00 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x6300 0x6A80 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x6700 0x6A00 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x6C00 0x6A00 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x7100 0x6980 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x7680 0x6900 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x7C80 0x6900 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x8000 0x6380 0x7D80 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x8000 0x6080 0x7680 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x8000 0x5980 0x6F80 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x5200 0x6900 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x5500 0x7380 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x5800 0x7300 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x5B80 0x7280 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x6000 0x7200 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x6400 0x7180 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x6800 0x7100 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x6C80 0x7080 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x7180 0x7080 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x7680 0x7000 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x7C80 0x6B80 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x8000 0x6A80 0x7E00 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x8000 0x6300 0x7700 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x8000 0x6000 0x7080 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x5900 0x6A80 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x5180 0x6380 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x5980 0x7A80 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x5D00 0x7A00 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x6100 0x7980 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x6500 0x7900 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x6900 0x7880 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x6D80 0x7800 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x7280 0x7380 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x7680 0x7300 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x7C80 0x7280 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x8000 0x7100 0x7E80 + 0x00 0x47 0x14 0x16 0x26 0x28 0x8000 0x6A00 0x7780 + 0x00 0x48 0x11 0x13 0x26 0x28 0x8000 0x6300 0x7180 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x5B80 0x6B00 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x5880 0x6500 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x5080 0x5E80 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x5B00 0x8000 0x7C80 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x5F80 0x8000 0x7E00 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x6480 0x8000 0x7F00 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x6900 0x8000 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x6E00 0x7B80 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x7280 0x7B00 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x7700 0x7A80 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x7C80 0x7A00 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x8000 0x7880 0x7E80 + 0x00 0x55 0x17 0x19 0x23 0x25 0x8000 0x7100 0x7800 + 0x00 0x56 0x14 0x16 0x23 0x25 0x8000 0x6A00 0x7280 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x6280 0x6C80 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x5B00 0x6680 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x5800 0x6080 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x5000 0x5A00 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x5A00 0x8000 0x7600 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x5E80 0x8000 0x7780 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x6380 0x8000 0x7880 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x6880 0x8000 0x7980 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x6E00 0x8000 0x7B00 + 0x00 0x60 0x23 0x25 0x20 0x22 0x7400 0x8000 0x7C80 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7980 0x8000 0x7D80 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x8000 0x8000 0x7E80 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x8000 0x7880 0x7880 + 0x00 0x64 0x17 0x19 0x20 0x22 0x8000 0x7100 0x7300 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x6A00 0x6D80 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x6280 0x6800 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x5B00 0x6280 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x5380 0x5C80 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x4B80 0x5600 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x5880 0x8000 0x7080 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x5D80 0x8000 0x7280 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x6300 0x8000 0x7300 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x6780 0x8000 0x7400 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x6D80 0x8000 0x7580 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x7380 0x8000 0x7680 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7900 0x8000 0x7780 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x8000 0x8000 0x7880 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x8000 0x7880 0x7380 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7100 0x6E80 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x6980 0x6980 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x6200 0x6400 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x5A80 0x5E80 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x5280 0x5800 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x4A80 0x5200 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x5800 0x8000 0x6C00 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x5C80 0x8000 0x6D00 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x6200 0x8000 0x6E80 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x6700 0x8000 0x6F80 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x6C80 0x8000 0x7100 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x7300 0x8000 0x7180 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7900 0x8000 0x7300 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x8000 0x8000 0x7400 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x8000 0x7880 0x6F80 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7100 0x6B00 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x6980 0x6580 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x6200 0x6000 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x5A00 0x5B00 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x5200 0x5500 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x4A00 0x4F00 + 0x00 0x88 0x32 0x34 0x17 0x19 0x5680 0x8000 0x6800 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x5B80 0x8000 0x6900 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x6100 0x8000 0x6A80 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x6600 0x8000 0x6B00 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x6C00 0x8000 0x6C80 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x7280 0x8000 0x6D80 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7800 0x8000 0x6E80 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7F00 0x8000 0x6F80 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x8000 0x7880 0x6B80 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x7100 0x6700 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x6900 0x6200 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x6180 0x5D00 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x5980 0x5780 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x5200 0x5200 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x4980 0x4C00 + 0x00 0x97 0x32 0x34 0x14 0x16 0x5580 0x8000 0x6380 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x5A80 0x8000 0x6500 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x6000 0x8000 0x6600 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x6600 0x8000 0x6780 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x6B80 0x8000 0x6900 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x7180 0x8000 0x6A00 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7780 0x8000 0x6A80 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7F00 0x8000 0x6C00 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x8000 0x7880 0x6880 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x7080 0x6400 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x6900 0x5F00 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x6180 0x5980 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x5980 0x5400 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x5100 0x4F00 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x4900 0x4880 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x5400 0x8000 0x5F80 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x5980 0x8000 0x6100 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x5F00 0x8000 0x6280 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x6500 0x8000 0x6400 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x6A80 0x8000 0x6500 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x7100 0x8000 0x6600 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x7700 0x8000 0x6780 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7E80 0x8000 0x6880 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x8000 0x7900 0x6500 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x7080 0x6080 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x6900 0x5C00 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x6100 0x5680 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x5900 0x5180 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x5080 0x4C80 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x4800 0x4600 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x5300 0x8000 0x5C80 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x5880 0x8000 0x5E00 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x5E00 0x8000 0x5F00 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x6480 0x8000 0x6080 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x6A00 0x8000 0x6180 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x7080 0x8000 0x6300 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x7700 0x8000 0x6400 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7E80 0x8000 0x6500 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x8000 0x7900 0x6200 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x7080 0x5D80 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x6880 0x5880 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x6080 0x5380 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x5880 0x4F00 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x5000 0x4900 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x4380 0x4480 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x5180 0x8000 0x5900 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x5780 0x8000 0x5A80 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x5D00 0x8000 0x5C00 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x6380 0x8000 0x5D80 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x6980 0x8000 0x5E80 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x6F80 0x8000 0x5F80 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x7680 0x8000 0x6100 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7E00 0x8000 0x6280 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x8000 0x7900 0x5F80 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x7080 0x5B00 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x6880 0x5600 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x6080 0x5180 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x5800 0x4C80 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x4B80 0x4700 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x4300 0x4200 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x5000 0x8000 0x5600 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x5680 0x8000 0x5780 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x5C00 0x8000 0x5880 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x6280 0x8000 0x5A80 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x6880 0x8000 0x5C00 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x6E80 0x8000 0x5D80 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x7680 0x8000 0x5E00 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7D80 0x8000 0x5F00 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x8000 0x7900 0x5D00 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x8000 0x7080 0x5800 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x6880 0x5380 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x6000 0x4F00 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x5380 0x4A00 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x4B00 0x4500 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x4200 0x3F80 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-srgb-pcc-enable; + somc,mdss-dsi-srgb-pcc-table-size = <226>; + somc,mdss-dsi-srgb-pcc-table = < + 0x00 0x01 0x38 0x3B 0x38 0x3B 0x4580 0x6200 0x8000 + 0x00 0x02 0x34 0x37 0x38 0x3B 0x4900 0x6200 0x8000 + 0x00 0x03 0x30 0x33 0x38 0x3B 0x4D00 0x6180 0x8000 + 0x00 0x04 0x2C 0x2F 0x38 0x3B 0x4F80 0x6180 0x8000 + 0x00 0x05 0x28 0x2B 0x38 0x3B 0x5400 0x6100 0x8000 + 0x00 0x06 0x24 0x27 0x38 0x3B 0x5800 0x6080 0x8000 + 0x00 0x07 0x20 0x23 0x38 0x3B 0x5D00 0x6080 0x8000 + 0x00 0x08 0x1C 0x1F 0x38 0x3B 0x6100 0x6080 0x8000 + 0x00 0x09 0x18 0x1B 0x38 0x3B 0x6600 0x6000 0x8000 + 0x00 0x0A 0x14 0x17 0x38 0x3B 0x6B80 0x6000 0x8000 + 0x00 0x0B 0x10 0x13 0x38 0x3B 0x7100 0x5B80 0x8000 + 0x00 0x0C 0x0C 0x0F 0x38 0x3B 0x7680 0x5B80 0x8000 + 0x00 0x0D 0x08 0x0B 0x38 0x3B 0x7C80 0x5B00 0x8000 + 0x00 0x0E 0x04 0x07 0x38 0x3B 0x8000 0x5A00 0x7D00 + 0x00 0x0F 0x00 0x03 0x38 0x3B 0x8000 0x5300 0x7580 + 0x00 0x10 0x38 0x3B 0x34 0x37 0x4B80 0x6980 0x8000 + 0x00 0x11 0x34 0x37 0x34 0x37 0x4E00 0x6900 0x8000 + 0x00 0x12 0x30 0x33 0x34 0x37 0x5180 0x6880 0x8000 + 0x00 0x13 0x2C 0x2F 0x34 0x37 0x5580 0x6880 0x8000 + 0x00 0x14 0x28 0x2B 0x34 0x37 0x5A00 0x6800 0x8000 + 0x00 0x15 0x24 0x27 0x34 0x37 0x5E00 0x6380 0x8000 + 0x00 0x16 0x20 0x23 0x34 0x37 0x6200 0x6380 0x8000 + 0x00 0x17 0x1C 0x1F 0x34 0x37 0x6680 0x6300 0x8000 + 0x00 0x18 0x18 0x1B 0x34 0x37 0x6C80 0x6300 0x8000 + 0x00 0x19 0x14 0x17 0x34 0x37 0x7180 0x6280 0x8000 + 0x00 0x1A 0x10 0x13 0x34 0x37 0x7700 0x6280 0x8000 + 0x00 0x1B 0x0C 0x0F 0x34 0x37 0x7C80 0x6200 0x8000 + 0x00 0x1C 0x08 0x0B 0x34 0x37 0x8000 0x6080 0x7E00 + 0x00 0x1D 0x04 0x07 0x34 0x37 0x8000 0x5980 0x7680 + 0x00 0x1E 0x00 0x03 0x34 0x37 0x8000 0x5280 0x6F80 + 0x00 0x1F 0x38 0x3B 0x30 0x33 0x4F80 0x7080 0x8000 + 0x00 0x20 0x34 0x37 0x30 0x33 0x5300 0x7000 0x8000 + 0x00 0x21 0x30 0x33 0x30 0x33 0x5700 0x6B80 0x8000 + 0x00 0x22 0x2C 0x2F 0x30 0x33 0x5B80 0x6B00 0x8000 + 0x00 0x23 0x28 0x2B 0x30 0x33 0x5F00 0x6B00 0x8000 + 0x00 0x24 0x24 0x27 0x30 0x33 0x6300 0x6A80 0x8000 + 0x00 0x25 0x20 0x23 0x30 0x33 0x6700 0x6A00 0x8000 + 0x00 0x26 0x1C 0x1F 0x30 0x33 0x6D00 0x6A00 0x8000 + 0x00 0x27 0x18 0x1B 0x30 0x33 0x7200 0x6980 0x8000 + 0x00 0x28 0x14 0x17 0x30 0x33 0x7700 0x6900 0x8000 + 0x00 0x29 0x10 0x13 0x30 0x33 0x7C80 0x6900 0x8000 + 0x00 0x2A 0x0C 0x0F 0x30 0x33 0x8000 0x6380 0x7D80 + 0x00 0x2B 0x08 0x0B 0x30 0x33 0x8000 0x6000 0x7700 + 0x00 0x2C 0x04 0x07 0x30 0x33 0x8000 0x5900 0x7080 + 0x00 0x2D 0x00 0x03 0x30 0x33 0x8000 0x5180 0x6900 + 0x00 0x2E 0x38 0x3B 0x2C 0x2F 0x5500 0x7380 0x8000 + 0x00 0x2F 0x34 0x37 0x2C 0x2F 0x5880 0x7300 0x8000 + 0x00 0x30 0x30 0x33 0x2C 0x2F 0x5C80 0x7280 0x8000 + 0x00 0x31 0x2C 0x2F 0x2C 0x2F 0x6000 0x7200 0x8000 + 0x00 0x32 0x28 0x2B 0x2C 0x2F 0x6400 0x7180 0x8000 + 0x00 0x33 0x24 0x27 0x2C 0x2F 0x6800 0x7100 0x8000 + 0x00 0x34 0x20 0x23 0x2C 0x2F 0x6D80 0x7100 0x8000 + 0x00 0x35 0x1C 0x1F 0x2C 0x2F 0x7200 0x7080 0x8000 + 0x00 0x36 0x18 0x1B 0x2C 0x2F 0x7800 0x7000 0x8000 + 0x00 0x37 0x14 0x17 0x2C 0x2F 0x7C80 0x6B80 0x8000 + 0x00 0x38 0x10 0x13 0x2C 0x2F 0x8000 0x6A80 0x7E80 + 0x00 0x39 0x0C 0x0F 0x2C 0x2F 0x8000 0x6300 0x7780 + 0x00 0x3A 0x08 0x0B 0x2C 0x2F 0x8000 0x5B80 0x7180 + 0x00 0x3B 0x04 0x07 0x2C 0x2F 0x8000 0x5880 0x6B00 + 0x00 0x3C 0x00 0x03 0x2C 0x2F 0x8000 0x5100 0x6400 + 0x00 0x3D 0x38 0x3B 0x28 0x2B 0x5A00 0x7A80 0x8000 + 0x00 0x3E 0x34 0x37 0x28 0x2B 0x5D80 0x7A00 0x8000 + 0x00 0x3F 0x30 0x33 0x28 0x2B 0x6100 0x7980 0x8000 + 0x00 0x40 0x2C 0x2F 0x28 0x2B 0x6500 0x7900 0x8000 + 0x00 0x41 0x28 0x2B 0x28 0x2B 0x6980 0x7880 0x8000 + 0x00 0x42 0x24 0x27 0x28 0x2B 0x6E00 0x7800 0x8000 + 0x00 0x43 0x20 0x23 0x28 0x2B 0x7280 0x7380 0x8000 + 0x00 0x44 0x1C 0x1F 0x28 0x2B 0x7800 0x7300 0x8000 + 0x00 0x45 0x18 0x1B 0x28 0x2B 0x7C80 0x7280 0x8000 + 0x00 0x46 0x14 0x17 0x28 0x2B 0x8000 0x7180 0x7E80 + 0x00 0x47 0x10 0x13 0x28 0x2B 0x8000 0x6A00 0x7800 + 0x00 0x48 0x0C 0x0F 0x28 0x2B 0x8000 0x6300 0x7280 + 0x00 0x49 0x08 0x0B 0x28 0x2B 0x8000 0x5B80 0x6C00 + 0x00 0x4A 0x04 0x07 0x28 0x2B 0x8000 0x5800 0x6580 + 0x00 0x4B 0x00 0x03 0x28 0x2B 0x8000 0x5080 0x5F00 + 0x00 0x4C 0x38 0x3B 0x24 0x27 0x5B80 0x8000 0x7C80 + 0x00 0x4D 0x34 0x37 0x24 0x27 0x5F80 0x8000 0x7E00 + 0x00 0x4E 0x30 0x33 0x24 0x27 0x6480 0x8000 0x7F00 + 0x00 0x4F 0x2C 0x2F 0x24 0x27 0x6980 0x8000 0x8000 + 0x00 0x50 0x28 0x2B 0x24 0x27 0x6F00 0x7B80 0x8000 + 0x00 0x51 0x24 0x27 0x24 0x27 0x7280 0x7B00 0x8000 + 0x00 0x52 0x20 0x23 0x24 0x27 0x7800 0x7A80 0x8000 + 0x00 0x53 0x1C 0x1F 0x24 0x27 0x7C80 0x7A00 0x8000 + 0x00 0x54 0x18 0x1B 0x24 0x27 0x8000 0x7880 0x7E80 + 0x00 0x55 0x14 0x17 0x24 0x27 0x8000 0x7100 0x7900 + 0x00 0x56 0x10 0x13 0x24 0x27 0x8000 0x6A00 0x7300 + 0x00 0x57 0x0C 0x0F 0x24 0x27 0x8000 0x6280 0x6D80 + 0x00 0x58 0x08 0x0B 0x24 0x27 0x8000 0x5B00 0x6780 + 0x00 0x59 0x04 0x07 0x24 0x27 0x8000 0x5380 0x6080 + 0x00 0x5A 0x00 0x03 0x24 0x27 0x8000 0x4B80 0x5A80 + 0x00 0x5B 0x38 0x3B 0x20 0x23 0x5A00 0x8000 0x7700 + 0x00 0x5C 0x34 0x37 0x20 0x23 0x5F00 0x8000 0x7800 + 0x00 0x5D 0x30 0x33 0x20 0x23 0x6380 0x8000 0x7980 + 0x00 0x5E 0x2C 0x2F 0x20 0x23 0x6880 0x8000 0x7A80 + 0x00 0x5F 0x28 0x2B 0x20 0x23 0x6F00 0x8000 0x7B80 + 0x00 0x60 0x24 0x27 0x20 0x23 0x7400 0x8000 0x7C80 + 0x00 0x61 0x20 0x23 0x20 0x23 0x7A00 0x8000 0x7E00 + 0x00 0x62 0x1C 0x1F 0x20 0x23 0x8000 0x8000 0x7E80 + 0x00 0x63 0x18 0x1B 0x20 0x23 0x8000 0x7880 0x7980 + 0x00 0x64 0x14 0x17 0x20 0x23 0x8000 0x7100 0x7400 + 0x00 0x65 0x10 0x13 0x20 0x23 0x8000 0x6A00 0x6E80 + 0x00 0x66 0x0C 0x0F 0x20 0x23 0x8000 0x6200 0x6880 + 0x00 0x67 0x08 0x0B 0x20 0x23 0x8000 0x5A80 0x6300 + 0x00 0x68 0x04 0x07 0x20 0x23 0x8000 0x5300 0x5D00 + 0x00 0x69 0x00 0x03 0x20 0x23 0x8000 0x4B00 0x5600 + 0x00 0x6A 0x38 0x3B 0x1C 0x1F 0x5900 0x8000 0x7180 + 0x00 0x6B 0x34 0x37 0x1C 0x1F 0x5E00 0x8000 0x7300 + 0x00 0x6C 0x30 0x33 0x1C 0x1F 0x6280 0x8000 0x7400 + 0x00 0x6D 0x2C 0x2F 0x1C 0x1F 0x6780 0x8000 0x7500 + 0x00 0x6E 0x28 0x2B 0x1C 0x1F 0x6E80 0x8000 0x7600 + 0x00 0x6F 0x24 0x27 0x1C 0x1F 0x7380 0x8000 0x7780 + 0x00 0x70 0x20 0x23 0x1C 0x1F 0x7980 0x8000 0x7800 + 0x00 0x71 0x1C 0x1F 0x1C 0x1F 0x8000 0x8000 0x7980 + 0x00 0x72 0x18 0x1B 0x1C 0x1F 0x8000 0x7880 0x7480 + 0x00 0x73 0x14 0x17 0x1C 0x1F 0x8000 0x7100 0x6F80 + 0x00 0x74 0x10 0x13 0x1C 0x1F 0x8000 0x6980 0x6980 + 0x00 0x75 0x0C 0x0F 0x1C 0x1F 0x8000 0x6200 0x6500 + 0x00 0x76 0x08 0x0B 0x1C 0x1F 0x8000 0x5A00 0x5E80 + 0x00 0x77 0x04 0x07 0x1C 0x1F 0x8000 0x5280 0x5880 + 0x00 0x78 0x00 0x03 0x1C 0x1F 0x8000 0x4A80 0x5280 + 0x00 0x79 0x38 0x3B 0x18 0x1B 0x5800 0x8000 0x6D00 + 0x00 0x7A 0x34 0x37 0x18 0x1B 0x5D00 0x8000 0x6E00 + 0x00 0x7B 0x30 0x33 0x18 0x1B 0x6200 0x8000 0x6F80 + 0x00 0x7C 0x2C 0x2F 0x18 0x1B 0x6700 0x8000 0x7080 + 0x00 0x7D 0x28 0x2B 0x18 0x1B 0x6D80 0x8000 0x7180 + 0x00 0x7E 0x24 0x27 0x18 0x1B 0x7300 0x8000 0x7300 + 0x00 0x7F 0x20 0x23 0x18 0x1B 0x7980 0x8000 0x7400 + 0x00 0x80 0x1C 0x1F 0x18 0x1B 0x8000 0x8000 0x7500 + 0x00 0x81 0x18 0x1B 0x18 0x1B 0x8000 0x7880 0x7080 + 0x00 0x82 0x14 0x17 0x18 0x1B 0x8000 0x7100 0x6B80 + 0x00 0x83 0x10 0x13 0x18 0x1B 0x8000 0x6980 0x6600 + 0x00 0x84 0x0C 0x0F 0x18 0x1B 0x8000 0x6180 0x6000 + 0x00 0x85 0x08 0x0B 0x18 0x1B 0x8000 0x5980 0x5B00 + 0x00 0x86 0x04 0x07 0x18 0x1B 0x8000 0x5200 0x5500 + 0x00 0x87 0x00 0x03 0x18 0x1B 0x8000 0x4980 0x4F00 + 0x00 0x88 0x38 0x3B 0x14 0x17 0x5680 0x8000 0x6800 + 0x00 0x89 0x34 0x37 0x14 0x17 0x5C80 0x8000 0x6900 + 0x00 0x8A 0x30 0x33 0x14 0x17 0x6100 0x8000 0x6B00 + 0x00 0x8B 0x2C 0x2F 0x14 0x17 0x6600 0x8000 0x6C00 + 0x00 0x8C 0x28 0x2B 0x14 0x17 0x6D00 0x8000 0x6D80 + 0x00 0x8D 0x24 0x27 0x14 0x17 0x7280 0x8000 0x6E80 + 0x00 0x8E 0x20 0x23 0x14 0x17 0x7900 0x8000 0x6F80 + 0x00 0x8F 0x1C 0x1F 0x14 0x17 0x7F00 0x8000 0x7100 + 0x00 0x90 0x18 0x1B 0x14 0x17 0x8000 0x7880 0x6C80 + 0x00 0x91 0x14 0x17 0x14 0x17 0x8000 0x7100 0x6700 + 0x00 0x92 0x10 0x13 0x14 0x17 0x8000 0x6900 0x6280 + 0x00 0x93 0x0C 0x0F 0x14 0x17 0x8000 0x6180 0x5D00 + 0x00 0x94 0x08 0x0B 0x14 0x17 0x8000 0x5980 0x5780 + 0x00 0x95 0x04 0x07 0x14 0x17 0x8000 0x5100 0x5180 + 0x00 0x96 0x00 0x03 0x14 0x17 0x8000 0x4900 0x4C80 + 0x00 0x97 0x38 0x3B 0x10 0x13 0x5580 0x8000 0x6480 + 0x00 0x98 0x34 0x37 0x10 0x13 0x5B00 0x8000 0x6580 + 0x00 0x99 0x30 0x33 0x10 0x13 0x6000 0x8000 0x6700 + 0x00 0x9A 0x2C 0x2F 0x10 0x13 0x6600 0x8000 0x6780 + 0x00 0x9B 0x28 0x2B 0x10 0x13 0x6C80 0x8000 0x6900 + 0x00 0x9C 0x24 0x27 0x10 0x13 0x7280 0x8000 0x6A80 + 0x00 0x9D 0x20 0x23 0x10 0x13 0x7880 0x8000 0x6C00 + 0x00 0x9E 0x1C 0x1F 0x10 0x13 0x7F00 0x8000 0x6D00 + 0x00 0x9F 0x18 0x1B 0x10 0x13 0x8000 0x7880 0x6880 + 0x00 0xA0 0x14 0x17 0x10 0x13 0x8000 0x7100 0x6480 + 0x00 0xA1 0x10 0x13 0x10 0x13 0x8000 0x6900 0x5F00 + 0x00 0xA2 0x0C 0x0F 0x10 0x13 0x8000 0x6100 0x5980 + 0x00 0xA3 0x08 0x0B 0x10 0x13 0x8000 0x5900 0x5400 + 0x00 0xA4 0x04 0x07 0x10 0x13 0x8000 0x5100 0x4F00 + 0x00 0xA5 0x00 0x03 0x10 0x13 0x8000 0x4880 0x4880 + 0x00 0xA6 0x38 0x3B 0x0C 0x0F 0x5400 0x8000 0x6000 + 0x00 0xA7 0x34 0x37 0x0C 0x0F 0x5A00 0x8000 0x6180 + 0x00 0xA8 0x30 0x33 0x0C 0x0F 0x5F00 0x8000 0x6380 + 0x00 0xA9 0x2C 0x2F 0x0C 0x0F 0x6500 0x8000 0x6480 + 0x00 0xAA 0x28 0x2B 0x0C 0x0F 0x6B80 0x8000 0x6580 + 0x00 0xAB 0x24 0x27 0x0C 0x0F 0x7200 0x8000 0x6700 + 0x00 0xAC 0x20 0x23 0x0C 0x0F 0x7880 0x8000 0x6780 + 0x00 0xAD 0x1C 0x1F 0x0C 0x0F 0x7E80 0x8000 0x6880 + 0x00 0xAE 0x18 0x1B 0x0C 0x0F 0x8000 0x7880 0x6580 + 0x00 0xAF 0x14 0x17 0x0C 0x0F 0x8000 0x7100 0x6080 + 0x00 0xB0 0x10 0x13 0x0C 0x0F 0x8000 0x6900 0x5C00 + 0x00 0xB1 0x0C 0x0F 0x0C 0x0F 0x8000 0x6080 0x5680 + 0x00 0xB2 0x08 0x0B 0x0C 0x0F 0x8000 0x5880 0x5180 + 0x00 0xB3 0x04 0x07 0x0C 0x0F 0x8000 0x5000 0x4C80 + 0x00 0xB4 0x00 0x03 0x0C 0x0F 0x8000 0x4800 0x4680 + 0x00 0xB5 0x38 0x3B 0x08 0x0B 0x5300 0x8000 0x5D00 + 0x00 0xB6 0x34 0x37 0x08 0x0B 0x5900 0x8000 0x5E00 + 0x00 0xB7 0x30 0x33 0x08 0x0B 0x5E80 0x8000 0x5F00 + 0x00 0xB8 0x2C 0x2F 0x08 0x0B 0x6400 0x8000 0x6080 + 0x00 0xB9 0x28 0x2B 0x08 0x0B 0x6A80 0x8000 0x6280 + 0x00 0xBA 0x24 0x27 0x08 0x0B 0x7180 0x8000 0x6380 + 0x00 0xBB 0x20 0x23 0x08 0x0B 0x7800 0x8000 0x6500 + 0x00 0xBC 0x1C 0x1F 0x08 0x0B 0x7E80 0x8000 0x6580 + 0x00 0xBD 0x18 0x1B 0x08 0x0B 0x8000 0x7880 0x6280 + 0x00 0xBE 0x14 0x17 0x08 0x0B 0x8000 0x7080 0x5D80 + 0x00 0xBF 0x10 0x13 0x08 0x0B 0x8000 0x6880 0x5880 + 0x00 0xC0 0x0C 0x0F 0x08 0x0B 0x8000 0x6080 0x5380 + 0x00 0xC1 0x08 0x0B 0x08 0x0B 0x8000 0x5800 0x4F00 + 0x00 0xC2 0x04 0x07 0x08 0x0B 0x8000 0x4B80 0x4900 + 0x00 0xC3 0x00 0x03 0x08 0x0B 0x8000 0x4300 0x4400 + 0x00 0xC4 0x38 0x3B 0x04 0x07 0x5180 0x8000 0x5900 + 0x00 0xC5 0x34 0x37 0x04 0x07 0x5780 0x8000 0x5B00 + 0x00 0xC6 0x30 0x33 0x04 0x07 0x5D80 0x8000 0x5C80 + 0x00 0xC7 0x2C 0x2F 0x04 0x07 0x6380 0x8000 0x5D80 + 0x00 0xC8 0x28 0x2B 0x04 0x07 0x6A00 0x8000 0x5F00 + 0x00 0xC9 0x24 0x27 0x04 0x07 0x7100 0x8000 0x5F80 + 0x00 0xCA 0x20 0x23 0x04 0x07 0x7800 0x8000 0x6100 + 0x00 0xCB 0x1C 0x1F 0x04 0x07 0x7E00 0x8000 0x6300 + 0x00 0xCC 0x18 0x1B 0x04 0x07 0x8000 0x7900 0x5F00 + 0x00 0xCD 0x14 0x17 0x04 0x07 0x8000 0x7080 0x5B00 + 0x00 0xCE 0x10 0x13 0x04 0x07 0x8000 0x6880 0x5600 + 0x00 0xCF 0x0C 0x0F 0x04 0x07 0x8000 0x6000 0x5180 + 0x00 0xD0 0x08 0x0B 0x04 0x07 0x8000 0x5380 0x4C80 + 0x00 0xD1 0x04 0x07 0x04 0x07 0x8000 0x4B00 0x4700 + 0x00 0xD2 0x00 0x03 0x04 0x07 0x8000 0x4280 0x4180 + 0x00 0xD3 0x38 0x3B 0x00 0x03 0x5000 0x8000 0x5600 + 0x00 0xD4 0x34 0x37 0x00 0x03 0x5680 0x8000 0x5780 + 0x00 0xD5 0x30 0x33 0x00 0x03 0x5D00 0x8000 0x5900 + 0x00 0xD6 0x2C 0x2F 0x00 0x03 0x6280 0x8000 0x5A80 + 0x00 0xD7 0x28 0x2B 0x00 0x03 0x6880 0x8000 0x5C00 + 0x00 0xD8 0x24 0x27 0x00 0x03 0x7000 0x8000 0x5D00 + 0x00 0xD9 0x20 0x23 0x00 0x03 0x7700 0x8000 0x5E80 + 0x00 0xDA 0x1C 0x1F 0x00 0x03 0x7D80 0x8000 0x5F00 + 0x00 0xDB 0x18 0x1B 0x00 0x03 0x8000 0x7900 0x5D00 + 0x00 0xDC 0x14 0x17 0x00 0x03 0x8000 0x7080 0x5800 + 0x00 0xDD 0x10 0x13 0x00 0x03 0x8000 0x6880 0x5380 + 0x00 0xDE 0x0C 0x0F 0x00 0x03 0x8000 0x5B80 0x4F00 + 0x00 0xDF 0x08 0x0B 0x00 0x03 0x8000 0x5300 0x4A00 + 0x00 0xE0 0x04 0x07 0x00 0x03 0x8000 0x4A80 0x4500 + 0x00 0xE1 0x00 0x03 0x00 0x03 0x8000 0x4200 0x3F80 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-vivid-pcc-enable; + somc,mdss-dsi-vivid-pcc-table-size = <226>; + somc,mdss-dsi-vivid-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x4F00 0x6900 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x5200 0x6900 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x5580 0x6880 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x5800 0x6880 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x5B80 0x6880 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x5F80 0x6800 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x6300 0x6800 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x6700 0x6500 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x6A80 0x6380 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x6F00 0x6380 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x7380 0x6380 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x7780 0x6300 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x7D00 0x6300 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x8000 0x6200 0x7D80 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x8000 0x5B00 0x7600 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x5400 0x6B80 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x5700 0x6B00 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x5A00 0x6B00 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x5D00 0x6A80 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x6080 0x6A80 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x6480 0x6A00 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x6800 0x6A00 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x6B80 0x6980 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x6F00 0x6980 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x7400 0x6980 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x7800 0x6900 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x7D00 0x6900 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x8000 0x6800 0x7D80 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x8000 0x6180 0x7700 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x8000 0x5A80 0x7000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x5800 0x7200 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x5B00 0x7180 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x5E80 0x7100 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x6200 0x7100 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x6580 0x7080 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x6880 0x7080 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x6C00 0x7000 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x6F80 0x7000 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x7480 0x6B80 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x7800 0x6B80 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x7D00 0x6B00 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x8000 0x6A00 0x7E00 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x8000 0x6380 0x7780 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x8000 0x6100 0x7180 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x5A00 0x6C00 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x5C80 0x7880 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x5F80 0x7800 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x6300 0x7380 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x6600 0x7380 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x6900 0x7300 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x6D00 0x7280 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x7080 0x7200 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x7500 0x7200 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x7880 0x7180 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x7D00 0x7180 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x8000 0x7080 0x7E00 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x8000 0x6A00 0x7800 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x8000 0x6380 0x7200 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x6080 0x6C80 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x5980 0x6700 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x6100 0x7B00 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x6400 0x7A80 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x6700 0x7A00 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x6A00 0x7A00 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x6D80 0x7900 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x7100 0x7900 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x7580 0x7880 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x7880 0x7800 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x7D00 0x7800 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x8000 0x7280 0x7E00 + 0x00 0x47 0x14 0x16 0x26 0x28 0x8000 0x7000 0x7880 + 0x00 0x48 0x11 0x13 0x26 0x28 0x8000 0x6980 0x7380 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x6300 0x6E00 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x6000 0x6880 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x5900 0x6300 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x6200 0x8000 0x7C80 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x6600 0x8000 0x7E00 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x6980 0x8000 0x7F00 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x6D80 0x8000 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x7180 0x8000 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x7580 0x7B00 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x7980 0x7B00 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x7D00 0x7A80 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x8000 0x7900 0x7E80 + 0x00 0x55 0x17 0x19 0x23 0x25 0x8000 0x7280 0x7880 + 0x00 0x56 0x14 0x16 0x23 0x25 0x8000 0x6B80 0x7400 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x6980 0x6F00 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x6280 0x6A00 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x5B80 0x6500 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x5880 0x5F80 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x6100 0x8000 0x7700 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x6500 0x8000 0x7800 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x6900 0x8000 0x7980 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x6D00 0x8000 0x7A80 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x7180 0x8000 0x7B00 + 0x00 0x60 0x23 0x25 0x20 0x22 0x7600 0x8000 0x7C80 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7B00 0x8000 0x7D00 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x8000 0x8000 0x7E80 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x8000 0x7900 0x7980 + 0x00 0x64 0x17 0x19 0x20 0x22 0x8000 0x7280 0x7500 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x6B80 0x7000 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x6900 0x6B80 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x6280 0x6680 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x5B00 0x6100 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x5800 0x5C00 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x5F80 0x8000 0x7300 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x6480 0x8000 0x7400 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x6800 0x8000 0x7500 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x6C80 0x8000 0x7600 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x7100 0x8000 0x7680 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x7600 0x8000 0x7780 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7B00 0x8000 0x7880 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x8000 0x8000 0x7980 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x8000 0x7900 0x7580 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7280 0x7080 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x6B80 0x6C80 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x6900 0x6800 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x6200 0x6300 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x5A80 0x5E00 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x5380 0x5880 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x5F00 0x8000 0x6E80 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x6380 0x8000 0x6F80 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x6800 0x8000 0x7000 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x6C00 0x8000 0x7180 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x7080 0x8000 0x7300 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x7600 0x8000 0x7400 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7A80 0x8000 0x7480 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x8000 0x8000 0x7580 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x8000 0x7900 0x7180 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7280 0x6D00 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x6B80 0x6900 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x6880 0x6500 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x6180 0x5F80 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x5A00 0x5A80 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x5280 0x5600 + 0x00 0x88 0x32 0x34 0x17 0x19 0x5E00 0x8000 0x6B00 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x6280 0x8000 0x6C00 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x6700 0x8000 0x6D00 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x6B80 0x8000 0x6E00 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x6F80 0x8000 0x6F00 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x7580 0x8000 0x7000 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7A00 0x8000 0x7000 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7F00 0x8000 0x7180 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x8000 0x7900 0x6E00 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x7280 0x6A80 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x6B80 0x6680 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x6880 0x6180 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x6100 0x5D00 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x5980 0x5800 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x5200 0x5300 + 0x00 0x97 0x32 0x34 0x14 0x16 0x5D00 0x8000 0x6780 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x6180 0x8000 0x6880 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x6680 0x8000 0x6980 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x6A80 0x8000 0x6B00 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x6F00 0x8000 0x6C00 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x7500 0x8000 0x6C80 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7A00 0x8000 0x6D80 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7F00 0x8000 0x6E80 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x8000 0x7980 0x6B80 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x7200 0x6780 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x6B80 0x6380 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x6880 0x5F00 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x6100 0x5A80 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x5900 0x5580 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x5180 0x5080 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x5C00 0x8000 0x6480 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x6100 0x8000 0x6580 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x6580 0x8000 0x6680 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x6A00 0x8000 0x6780 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x6F00 0x8000 0x6880 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x7480 0x8000 0x6980 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x7980 0x8000 0x6B00 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7F00 0x8000 0x6C00 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x8000 0x7980 0x6880 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x7200 0x6500 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x6B00 0x6080 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x6800 0x5C80 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x6080 0x5800 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x5900 0x5300 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x5100 0x4E00 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x5B00 0x8000 0x6100 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x5F80 0x8000 0x6280 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x6500 0x8000 0x6380 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x6980 0x8000 0x6500 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x6E80 0x8000 0x6600 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x7400 0x8000 0x6700 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x7980 0x8000 0x6800 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7E80 0x8000 0x6900 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x8000 0x7980 0x6680 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x7200 0x6200 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x6B00 0x5E00 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x6800 0x5A00 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x6000 0x5580 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x5880 0x5100 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x5080 0x4C80 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x5A00 0x8000 0x5E80 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x5F00 0x8000 0x5F80 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x6400 0x8000 0x6080 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x6880 0x8000 0x6200 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x6E00 0x8000 0x6300 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x7380 0x8000 0x6480 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x7880 0x8000 0x6580 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7E80 0x8000 0x6680 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x8000 0x7980 0x6400 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x7200 0x5F80 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x6B00 0x5C00 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x6380 0x5800 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x6000 0x5380 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x5800 0x4F00 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x4B80 0x4980 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x5880 0x8000 0x5C00 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x5E00 0x8000 0x5D00 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x6300 0x8000 0x5E80 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x6880 0x8000 0x5F80 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x6D00 0x8000 0x6080 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x7300 0x8000 0x6180 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x7800 0x8000 0x6300 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7E00 0x8000 0x6400 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x8000 0x7980 0x6180 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x8000 0x7200 0x5D80 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x6A80 0x5A00 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x6300 0x5600 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x5B80 0x5180 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x5380 0x4D00 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x4B00 0x4780 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-hdr-pcc-enable; + somc,mdss-dsi-hdr-pcc-table-size = <226>; + somc,mdss-dsi-hdr-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x6500 0x7380 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x6700 0x7300 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x6980 0x7300 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x6B80 0x7300 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x6E00 0x7280 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x7100 0x7280 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x7380 0x7280 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x7680 0x7280 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x7900 0x7200 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x7C00 0x7200 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x7F80 0x7200 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x8000 0x7080 0x7D80 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x8000 0x6B00 0x7A00 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x8000 0x6900 0x7680 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x8000 0x6380 0x7300 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x6880 0x7900 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x6A80 0x7900 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x6C80 0x7880 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x6F80 0x7880 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x7180 0x7800 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x7480 0x7800 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x7700 0x7800 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x7980 0x7380 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x7C80 0x7380 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x7F80 0x7380 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x8000 0x7200 0x7D80 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x8000 0x7080 0x7A00 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x8000 0x6A80 0x7700 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x8000 0x6900 0x7380 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x8000 0x6300 0x7000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x6B80 0x7A80 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x6D80 0x7A80 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x7080 0x7A00 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x7280 0x7A00 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x7500 0x7980 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x7800 0x7980 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x7A00 0x7980 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x7D00 0x7900 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x8000 0x7900 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x8000 0x7380 0x7D80 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x8000 0x7200 0x7A80 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x8000 0x7080 0x7780 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x8000 0x6A80 0x7480 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x8000 0x6900 0x7100 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x6280 0x6D00 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x6F00 0x8000 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x7100 0x8000 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x7300 0x7B80 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x7580 0x7B80 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x7880 0x7B00 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x7A80 0x7B00 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x7D00 0x7A80 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x8000 0x7A80 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x8000 0x7900 0x7D80 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x8000 0x7380 0x7A80 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x8000 0x7200 0x7780 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x8000 0x7000 0x7500 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x8000 0x6A00 0x7180 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x6800 0x6E00 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x6280 0x6A80 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x6E00 0x8000 0x7D00 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x7100 0x8000 0x7D80 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x7400 0x8000 0x7E00 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x7700 0x8000 0x7F00 + 0x00 0x41 0x26 0x28 0x26 0x28 0x7980 0x8000 0x7F80 + 0x00 0x42 0x23 0x25 0x26 0x28 0x7D00 0x8000 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x8000 0x8000 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x8000 0x7A80 0x7D80 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x8000 0x7900 0x7A80 + 0x00 0x46 0x17 0x19 0x26 0x28 0x8000 0x7380 0x7800 + 0x00 0x47 0x14 0x16 0x26 0x28 0x8000 0x7180 0x7500 + 0x00 0x48 0x11 0x13 0x26 0x28 0x8000 0x7000 0x7280 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x6A00 0x6F00 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x6800 0x6B80 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x6200 0x6880 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x6D80 0x8000 0x7A00 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x7100 0x8000 0x7A80 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x7380 0x8000 0x7B00 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x7680 0x8000 0x7B80 + 0x00 0x50 0x26 0x28 0x23 0x25 0x7980 0x8000 0x7C00 + 0x00 0x51 0x23 0x25 0x23 0x25 0x7D00 0x8000 0x7D00 + 0x00 0x52 0x20 0x22 0x23 0x25 0x8000 0x8000 0x7D80 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x8000 0x7A80 0x7A80 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x8000 0x7900 0x7800 + 0x00 0x55 0x17 0x19 0x23 0x25 0x8000 0x7380 0x7580 + 0x00 0x56 0x14 0x16 0x23 0x25 0x8000 0x7180 0x7300 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x6B80 0x7000 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x6980 0x6C80 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x6380 0x6980 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x6180 0x6600 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x6C80 0x8000 0x7780 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x7080 0x8000 0x7800 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x7300 0x8000 0x7880 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x7680 0x8000 0x7900 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x7900 0x8000 0x7980 + 0x00 0x60 0x23 0x25 0x20 0x22 0x7C80 0x8000 0x7A00 + 0x00 0x61 0x20 0x22 0x20 0x22 0x8000 0x8000 0x7A80 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x8000 0x7A80 0x7880 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x8000 0x7900 0x7600 + 0x00 0x64 0x17 0x19 0x20 0x22 0x8000 0x7300 0x7380 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x7180 0x7080 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x6B80 0x6D80 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x6980 0x6A80 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x6380 0x6780 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x6100 0x6380 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x6C00 0x8000 0x7500 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x7000 0x8000 0x7580 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x7280 0x8000 0x7680 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x7600 0x8000 0x7700 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x7900 0x8000 0x7780 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x7C80 0x8000 0x7800 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x8000 0x8000 0x7880 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x8000 0x7A80 0x7680 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x8000 0x7900 0x7400 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7300 0x7180 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x7100 0x6E80 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x6B00 0x6B80 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x6900 0x6900 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x6300 0x6580 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x6080 0x6200 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x6B80 0x8000 0x7300 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x6F80 0x8000 0x7380 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x7200 0x8000 0x7400 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x7580 0x8000 0x7480 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x7880 0x8000 0x7580 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x7C00 0x8000 0x7600 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7F80 0x8000 0x7680 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x8000 0x7A80 0x7480 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x8000 0x7900 0x7200 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7300 0x6F80 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x7100 0x6C80 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x6B00 0x6980 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x6880 0x6700 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x6280 0x6300 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x6000 0x6000 + 0x00 0x88 0x32 0x34 0x17 0x19 0x6B00 0x8000 0x7080 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x6E00 0x8000 0x7180 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x7180 0x8000 0x7200 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x7500 0x8000 0x7280 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x7880 0x8000 0x7300 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x7C00 0x8000 0x7400 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7F80 0x8000 0x7480 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x8000 0x7A80 0x7280 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x8000 0x7900 0x7000 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x7300 0x6D80 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x7100 0x6A80 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x6B00 0x6800 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x6880 0x6500 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x6200 0x6200 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x5B80 0x5E00 + 0x00 0x97 0x32 0x34 0x14 0x16 0x6A80 0x8000 0x6E80 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x6D80 0x8000 0x6F80 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x7100 0x8000 0x7000 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x7500 0x8000 0x7080 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x7880 0x8000 0x7180 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x7B80 0x8000 0x7200 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7F80 0x8000 0x7280 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x8000 0x7A80 0x7100 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x8000 0x7900 0x6E80 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x7300 0x6C00 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x7100 0x6980 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x6A80 0x6680 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x6880 0x6300 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x6200 0x6000 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x5A80 0x5C00 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x6A00 0x8000 0x6C80 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x6D00 0x8000 0x6D80 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x7080 0x8000 0x6E00 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x7480 0x8000 0x6F00 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x7800 0x8000 0x6F80 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x7B80 0x8000 0x7000 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x7F00 0x8000 0x7100 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x8000 0x7A80 0x6F00 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x8000 0x7900 0x6D00 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x7300 0x6A00 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x7080 0x6800 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x6A80 0x6480 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x6800 0x6200 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x6180 0x5E80 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x5A00 0x5A80 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x6900 0x8000 0x6A80 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x6C80 0x8000 0x6B80 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x7080 0x8000 0x6C80 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x7400 0x8000 0x6D00 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x7780 0x8000 0x6E00 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x7B00 0x8000 0x6E80 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x7F00 0x8000 0x6F00 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x8000 0x7A80 0x6D80 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x8000 0x7900 0x6B00 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x7280 0x6900 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x7080 0x6600 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x6A00 0x6300 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x6800 0x6080 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x6100 0x5C80 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x5980 0x5900 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x6880 0x8000 0x6980 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x6B80 0x8000 0x6A00 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x7000 0x8000 0x6A80 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x7380 0x8000 0x6B80 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x7780 0x8000 0x6C00 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x7B00 0x8000 0x6D00 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x7F00 0x8000 0x6D80 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x8000 0x7B00 0x6C00 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x8000 0x7900 0x6A00 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x7280 0x6780 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x7080 0x6500 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x6A00 0x6200 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x6380 0x5F00 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x6080 0x5B00 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x5900 0x5780 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x6780 0x8000 0x6800 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x6B00 0x8000 0x6880 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x6F80 0x8000 0x6980 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x7300 0x8000 0x6A00 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x7700 0x8000 0x6A80 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x7A80 0x8000 0x6B80 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x7E80 0x8000 0x6C00 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x8000 0x7B00 0x6B00 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x8000 0x7900 0x6900 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x8000 0x7280 0x6600 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x7000 0x6380 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x6980 0x6100 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x6300 0x5D80 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x6000 0x5A00 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x5880 0x5600 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-maple.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-maple.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..23e2ebff72f0a6b5696df59f998097d1c597af3c --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-maple.dtsi @@ -0,0 +1,1086 @@ +/* arch/arm64/boot/dts/qcom/dsi-panel-maple.dtsi + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include "dsi-panel-maple-id3_pcc.dtsi" +#include "dsi-panel-maple-id6_pcc.dtsi" + +&mdss_mdp { + dsi_dual_default_cmd_config0: config0 { + qcom,split-mode = "dualctl-split"; + }; + + dsi_dual_default_cmd_config1: config1 { + qcom,mdss-dsc-encoders = <1>; + qcom,mdss-dsc-slice-height = <16>; + qcom,mdss-dsc-slice-width = <1080>; + qcom,mdss-dsc-slice-per-pkt = <1>; + qcom,mdss-dsc-bit-per-component = <8>; + qcom,mdss-dsc-bit-per-pixel = <8>; + qcom,mdss-dsc-block-prediction-enable; + }; + + /* Satsuki Sharp ID3 */ + dsi_dual0_3_panel: somc,dual0_3_panel { + qcom,mdss-dsi-panel-name = "3"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-destination = "display_1"; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <121>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 0A 00 01 28 + 05 01 00 00 96 00 01 10]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_hybrid_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + somc,dsi-index = <0>; + somc,lcd-id-adc = <1236000 1395000>; + + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 15>, <1 10>, <0 15>, <1 20>; + somc,pw-off-rst-b-seq = <0 11>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <10>; + somc,pw-wait-after-on-vsp = <0>; + somc,pw-wait-after-on-vsn = <0>; + somc,pw-wait-after-on-dcdc = <10>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <1>; + somc,pw-wait-after-off-vsp = <5>; + somc,pw-wait-after-off-vsn = <5>; + somc,pw-wait-after-off-dcdc = <5>; + somc,pw-wait-after-on-touch-avdd = <1>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-avdd = <0>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <0>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <100>; + + somc,lab-output-voltage = <5800000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,ewu-wait-after-touch-reset = <40>; + somc,lk-on-skip; + + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 01 0A]; + qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; + qcom,mdss-dsi-panel-status-value = <0x9c>; + + somc,change-fps-enable; + somc,change-fps-panel-type = "uhd_4k_type"; + somc,change-fps-panel-mode = "susres_mode"; + somc,change-fps-command = + [39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 39 01 00 00 00 00 10 BD 00 AC 0C 0C 00 01 59 09 09 01 0E 0C 0C 00 B4]; + somc,driver-ic-vdisp = <1920>; + somc,driver-ic-rclk = <40000000>; + somc,driver-ic-total-porch = <12>; + somc,change-fps-rtn-adj; + somc,change-fps-rtn-pos = <1 6>; + + qcom,dynamic-mode-switch-enabled; + qcom,dynamic-mode-switch-type = "dynamic-resolution-switch-immediate"; + qcom,dcs-cmd-by-left; + qcom,mdss-dsi-display-timings { + 1080p { + qcom,mdss-dsi-timing-default; + qcom,mdss-dsi-panel-width = <540>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-back-porch = <300>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <400>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-on-command = [ + 39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 15 01 00 00 00 00 02 C9 01 + 15 01 00 00 00 00 02 90 00 + 15 01 00 00 00 00 02 58 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 07 + 15 01 00 00 00 00 02 EF 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 15 01 00 00 00 00 02 B4 01 + 39 01 00 00 00 00 10 BD 00 AC 0C 0C 00 01 56 09 09 01 01 0C 0C 00 D9 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 44 00 00 + 39 01 00 00 00 00 06 F0 55 AA 52 08 01 + 39 01 00 00 00 00 03 D4 88 88 + 39 01 00 00 00 00 05 FF AA 55 A5 80 + 15 01 00 00 00 00 02 6F 01 + 15 01 00 00 00 00 02 F3 10 + 39 01 00 00 00 00 05 FF AA 55 A5 00]; + qcom,mdss-dsi-post-panel-on-command = [ + 05 01 00 00 00 00 01 29 + 05 01 00 00 43 00 01 11]; + + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-timing-switch-command = [ + 39 00 00 00 00 00 06 F0 55 AA 52 08 00 + 15 00 00 00 00 00 02 C9 01 + 15 00 00 00 00 00 02 90 00 + 15 00 00 00 00 00 02 58 01 + 15 00 00 00 00 00 02 03 00 + 39 00 00 00 00 00 06 F0 55 AA 52 08 04 + 15 01 00 00 00 00 02 C0 03 + ]; + qcom,mdss-dsi-timing-switch-command-state = + "dsi_lp_mode"; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,config-select = <&dsi_dual_default_cmd_config0>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + + 4k { + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-back-porch = <104>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <140>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-on-command = [ + 15 01 00 00 00 00 02 90 03 + 15 01 00 00 00 00 02 03 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 04 + 15 01 00 00 00 00 02 C0 03 + 39 01 00 00 00 00 06 F0 55 AA 52 08 07 + 15 01 00 00 00 00 02 EF 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 15 01 00 00 00 00 02 B4 01 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 44 00 00 + 39 01 00 00 00 00 06 F0 55 AA 52 08 01 + 39 01 00 00 00 00 03 D4 88 88 + 39 01 00 00 00 00 05 FF AA 55 A5 80 + 15 01 00 00 00 00 02 6F 01 + 15 01 00 00 00 00 02 F3 10 + 39 01 00 00 00 00 05 FF AA 55 A5 00]; + qcom,mdss-dsi-post-panel-on-command = [ + 05 01 00 00 00 00 01 29 + 05 01 00 00 43 00 01 11]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-timing-switch-command = [ + 39 00 00 00 00 00 06 F0 55 AA 52 08 00 + 15 00 00 00 00 00 02 C9 01 + 15 00 00 00 00 00 02 90 03 + 15 00 00 00 00 00 02 58 00 + 15 00 00 00 00 00 02 03 01 + 39 00 00 00 00 00 06 F0 55 AA 52 08 04 + 15 01 00 00 00 00 02 C0 03 + ]; + qcom,mdss-dsi-timing-switch-command-state = + "dsi_lp_mode"; + + qcom,compression-mode = "dsc"; + qcom,config-select = <&dsi_dual_default_cmd_config1>; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + }; + }; + + dsi_dual1_3_panel: somc,dual1_3_panel { + qcom,mdss-dsi-panel-name = "3"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi1>; + qcom,mdss-dsi-panel-destination = "display_2"; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <121>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_hybrid_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + somc,dsi-index = <1>; + somc,lcd-id-adc = <1236000 1395000>; + + somc,lab-output-voltage = <5800000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,lk-on-skip; + + qcom,dynamic-mode-switch-enabled; + qcom,dynamic-mode-switch-type = "dynamic-resolution-switch-immediate"; + qcom,dcs-cmd-by-left; + qcom,mdss-dsi-display-timings { + 1080p { + qcom,mdss-dsi-timing-default; + qcom,mdss-dsi-panel-width = <540>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-back-porch = <300>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <400>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,config-select = <&dsi_dual_default_cmd_config0>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + + 4k { + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-back-porch = <104>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <140>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + + qcom,compression-mode = "dsc"; + qcom,config-select = <&dsi_dual_default_cmd_config1>; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + }; + }; + + /* Maple Sharp ID6 */ + dsi_dual0_6_panel: somc,dual0_6_panel { + qcom,mdss-dsi-panel-name = "6"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-destination = "display_1"; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <121>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 0A 00 01 28 + 05 01 00 00 96 00 01 10]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_hybrid_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + somc,dsi-index = <0>; + somc,lcd-id-adc = <565000 653000>; + + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 15>, <1 10>, <0 15>, <1 20>; + somc,pw-off-rst-b-seq = <0 11>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <10>; + somc,pw-wait-after-on-vsp = <0>; + somc,pw-wait-after-on-vsn = <0>; + somc,pw-wait-after-on-dcdc = <10>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <1>; + somc,pw-wait-after-off-vsp = <5>; + somc,pw-wait-after-off-vsn = <5>; + somc,pw-wait-after-off-dcdc = <5>; + somc,pw-wait-after-on-touch-avdd = <1>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-avdd = <0>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <0>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <100>; + + somc,lab-output-voltage = <5800000>; + somc,ibb-output-voltage = <5600000>; + + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,ewu-wait-after-touch-reset = <40>; + somc,lk-on-skip; + + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 01 0A]; + qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; + qcom,mdss-dsi-panel-status-value = <0x9c>; + + somc,change-fps-enable; + somc,change-fps-panel-type = "uhd_4k_type"; + somc,change-fps-panel-mode = "susres_mode"; + somc,change-fps-command = + [39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 39 01 00 00 00 00 10 BD 00 AC 0C 0C 00 01 59 09 09 01 0E 0C 0C 00 B4]; + somc,driver-ic-vdisp = <1920>; + somc,driver-ic-rclk = <40000000>; + somc,driver-ic-total-porch = <12>; + somc,change-fps-rtn-adj; + somc,change-fps-rtn-pos = <1 6>; + + qcom,dynamic-mode-switch-enabled; + qcom,dynamic-mode-switch-type = "dynamic-resolution-switch-immediate"; + qcom,dcs-cmd-by-left; + + qcom,mdss-mdp-kickoff-threshold = <20 3615>; + qcom,mdss-mdp-kickoff-delay = <1500>; + + qcom,mdss-dsi-display-timings { + 1080p { + qcom,mdss-dsi-timing-default; + qcom,mdss-dsi-panel-width = <540>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-back-porch = <300>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <400>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-on-command = [ + 39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 15 01 00 00 00 00 02 C9 01 + 15 01 00 00 00 00 02 90 00 + 15 01 00 00 00 00 02 58 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 07 + 15 01 00 00 00 00 02 EF 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 15 01 00 00 00 00 02 B4 01 + 39 01 00 00 00 00 10 BD 00 AC 0C 0C 00 01 56 09 09 01 01 0C 0C 00 D9 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 44 00 00 + 39 01 00 00 00 00 06 F0 55 AA 52 08 01 + 39 01 00 00 00 00 03 D4 88 88 + 39 01 00 00 00 00 05 FF AA 55 A5 80 + 15 01 00 00 00 00 02 6F 01 + 15 01 00 00 00 00 02 F3 10 + 39 01 00 00 00 00 05 FF AA 55 A5 00]; + qcom,mdss-dsi-post-panel-on-command = [ + 05 01 00 00 00 00 01 29 + 05 01 00 00 43 00 01 11]; + + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-timing-switch-command = [ + 39 00 00 00 00 00 06 F0 55 AA 52 08 00 + 15 00 00 00 00 00 02 C9 01 + 15 00 00 00 00 00 02 90 00 + 15 00 00 00 00 00 02 58 01 + 15 00 00 00 00 00 02 03 00 + 39 00 00 00 00 00 06 F0 55 AA 52 08 04 + 15 01 00 00 00 00 02 C0 03 + ]; + qcom,mdss-dsi-timing-switch-command-state = + "dsi_lp_mode"; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,config-select = <&dsi_dual_default_cmd_config0>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + + 4k { + somc,mdss-mdp-kickoff-threshold-enable; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-back-porch = <104>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <140>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-on-command = [ + 15 01 00 00 00 00 02 90 03 + 15 01 00 00 00 00 02 03 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 04 + 15 01 00 00 00 00 02 C0 03 + 39 01 00 00 00 00 06 F0 55 AA 52 08 07 + 15 01 00 00 00 00 02 EF 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 15 01 00 00 00 00 02 B4 01 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 44 00 00 + 39 01 00 00 00 00 06 F0 55 AA 52 08 01 + 39 01 00 00 00 00 03 D4 88 88 + 39 01 00 00 00 00 05 FF AA 55 A5 80 + 15 01 00 00 00 00 02 6F 01 + 15 01 00 00 00 00 02 F3 10 + 39 01 00 00 00 00 05 FF AA 55 A5 00]; + qcom,mdss-dsi-post-panel-on-command = [ + 05 01 00 00 00 00 01 29 + 05 01 00 00 43 00 01 11]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-timing-switch-command = [ + 39 00 00 00 00 00 06 F0 55 AA 52 08 00 + 15 00 00 00 00 00 02 C9 01 + 15 00 00 00 00 00 02 90 03 + 15 00 00 00 00 00 02 58 00 + 15 00 00 00 00 00 02 03 01 + 39 00 00 00 00 00 06 F0 55 AA 52 08 04 + 15 01 00 00 00 00 02 C0 03 + ]; + qcom,mdss-dsi-timing-switch-command-state = + "dsi_lp_mode"; + + qcom,compression-mode = "dsc"; + qcom,config-select = <&dsi_dual_default_cmd_config1>; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + }; + }; + + dsi_dual1_6_panel: somc,dual1_6_panel { + qcom,mdss-dsi-panel-name = "6"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi1>; + qcom,mdss-dsi-panel-destination = "display_2"; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <121>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_hybrid_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + somc,dsi-index = <1>; + somc,lcd-id-adc = <565000 653000>; + + somc,lab-output-voltage = <5800000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,lk-on-skip; + + qcom,dynamic-mode-switch-enabled; + qcom,dynamic-mode-switch-type = "dynamic-resolution-switch-immediate"; + + qcom,mdss-mdp-kickoff-threshold = <20 3615>; + qcom,mdss-mdp-kickoff-delay = <1500>; + + qcom,dcs-cmd-by-left; + qcom,mdss-dsi-display-timings { + 1080p { + qcom,mdss-dsi-timing-default; + qcom,mdss-dsi-panel-width = <540>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-back-porch = <300>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <400>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,config-select = <&dsi_dual_default_cmd_config0>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + + 4k { + somc,mdss-mdp-kickoff-threshold-enable; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-back-porch = <104>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <140>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + + qcom,compression-mode = "dsc"; + qcom,config-select = <&dsi_dual_default_cmd_config1>; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + }; + }; + + /* Default */ + dsi_dual0_default_panel: somc,dual0_default_panel { + qcom,mdss-dsi-panel-name = "default"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-destination = "display_1"; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <121>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 0A 00 01 28 + 05 01 00 00 96 00 01 10]; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_hybrid_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + somc,dsi-index = <0>; + somc,lcd-id-adc = <0 0x7fffffff>; + + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 15>, <1 10>, <0 15>, <1 20>; + somc,pw-off-rst-b-seq = <0 11>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <10>; + somc,pw-wait-after-on-vsp = <0>; + somc,pw-wait-after-on-vsn = <0>; + somc,pw-wait-after-on-dcdc = <10>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <1>; + somc,pw-wait-after-off-vsp = <5>; + somc,pw-wait-after-off-vsn = <5>; + somc,pw-wait-after-off-dcdc = <5>; + somc,pw-wait-after-on-touch-avdd = <1>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-avdd = <0>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <0>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <100>; + + somc,lab-output-voltage = <5800000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,ewu-wait-after-touch-reset = <40>; + somc,lk-on-skip; + + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 01 0A]; + qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; + qcom,mdss-dsi-panel-status-value = <0x9c>; + + qcom,dynamic-mode-switch-enabled; + qcom,dynamic-mode-switch-type = "dynamic-resolution-switch-immediate"; + qcom,dcs-cmd-by-left; + qcom,mdss-dsi-display-timings { + 1080p { + qcom,mdss-dsi-timing-default; + qcom,mdss-dsi-panel-width = <540>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-back-porch = <300>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <400>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-on-command = [ + 39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 15 01 00 00 00 00 02 C9 01 + 15 01 00 00 00 00 02 90 00 + 15 01 00 00 00 00 02 58 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 07 + 15 01 00 00 00 00 02 EF 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 15 01 00 00 00 00 02 B4 01 + 39 01 00 00 00 00 10 BD 00 AC 0C 0C 00 01 56 09 09 01 01 0C 0C 00 D9 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 44 00 00 + 39 01 00 00 00 00 06 F0 55 AA 52 08 01 + 39 01 00 00 00 00 03 D4 88 88 + 39 01 00 00 00 00 05 FF AA 55 A5 80 + 15 01 00 00 00 00 02 6F 01 + 15 01 00 00 00 00 02 F3 10 + 39 01 00 00 00 00 05 FF AA 55 A5 00]; + qcom,mdss-dsi-post-panel-on-command = [ + 05 01 00 00 00 00 01 29 + 05 01 00 00 43 00 01 11]; + + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-timing-switch-command = [ + 39 00 00 00 00 00 06 F0 55 AA 52 08 00 + 15 00 00 00 00 00 02 C9 01 + 15 00 00 00 00 00 02 90 00 + 15 00 00 00 00 00 02 58 01 + 15 00 00 00 00 00 02 03 00 + 39 00 00 00 00 00 06 F0 55 AA 52 08 04 + 15 01 00 00 00 00 02 C0 03 + ]; + qcom,mdss-dsi-timing-switch-command-state = + "dsi_lp_mode"; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,config-select = <&dsi_dual_default_cmd_config0>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + + 4k { + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-back-porch = <104>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <140>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-on-command = [ + 15 01 00 00 00 00 02 90 03 + 15 01 00 00 00 00 02 03 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 04 + 15 01 00 00 00 00 02 C0 03 + 39 01 00 00 00 00 06 F0 55 AA 52 08 07 + 15 01 00 00 00 00 02 EF 01 + 39 01 00 00 00 00 06 F0 55 AA 52 08 00 + 15 01 00 00 00 00 02 B4 01 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 44 00 00 + 39 01 00 00 00 00 06 F0 55 AA 52 08 01 + 39 01 00 00 00 00 03 D4 88 88 + 39 01 00 00 00 00 05 FF AA 55 A5 80 + 15 01 00 00 00 00 02 6F 01 + 15 01 00 00 00 00 02 F3 10 + 39 01 00 00 00 00 05 FF AA 55 A5 00]; + qcom,mdss-dsi-post-panel-on-command = [ + 05 01 00 00 00 00 01 29 + 05 01 00 00 43 00 01 11]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + + qcom,mdss-dsi-timing-switch-command = [ + 39 00 00 00 00 00 06 F0 55 AA 52 08 00 + 15 00 00 00 00 00 02 C9 01 + 15 00 00 00 00 00 02 90 03 + 15 00 00 00 00 00 02 58 00 + 15 00 00 00 00 00 02 03 01 + 39 00 00 00 00 00 06 F0 55 AA 52 08 04 + 15 01 00 00 00 00 02 C0 03 + ]; + qcom,mdss-dsi-timing-switch-command-state = + "dsi_lp_mode"; + + qcom,compression-mode = "dsc"; + qcom,config-select = <&dsi_dual_default_cmd_config1>; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + }; + }; + + dsi_dual1_default_panel: somc,dual1_default_panel { + qcom,mdss-dsi-panel-name = "default"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi1>; + qcom,mdss-dsi-panel-destination = "display_2"; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <121>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_hybrid_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + somc,dsi-index = <1>; + somc,lcd-id-adc = <0 0x7fffffff>; + + somc,lab-output-voltage = <5800000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,lk-on-skip; + + qcom,dynamic-mode-switch-enabled; + qcom,dynamic-mode-switch-type = "dynamic-resolution-switch-immediate"; + qcom,dcs-cmd-by-left; + qcom,mdss-dsi-display-timings { + 1080p { + qcom,mdss-dsi-timing-default; + qcom,mdss-dsi-panel-width = <540>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-back-porch = <300>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <400>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,config-select = <&dsi_dual_default_cmd_config0>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + + 4k { + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <3840>; + qcom,mdss-dsi-h-back-porch = <104>; + qcom,mdss-dsi-h-pulse-width = <40>; + qcom,mdss-dsi-h-front-porch = <140>; + qcom,mdss-dsi-v-back-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-v-front-porch = <12>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + + qcom,compression-mode = "dsc"; + qcom,config-select = <&dsi_dual_default_cmd_config1>; + + qcom,mdss-tear-check-sync-init-val = <3840>; + qcom,mdss-tear-check-start-pos = <3840>; + qcom,mdss-tear-check-rd-ptr-trigger-intr = <3841>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-poplar-id6_pcc.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-poplar-id6_pcc.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..e34764a0fd65e61c120b3eb9e23beb985f36dd20 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-poplar-id6_pcc.dtsi @@ -0,0 +1,947 @@ +/* arch/arm64/boot/dts/qcom/dsi-panel-poplar-id6_pcc.dtsi + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +/* pcc-table for Poplar JDI */ +&mdss_mdp { + dsi_6: somc,6_panel { + somc,mdss-dsi-pcc-enable; + somc,mdss-dsi-uv-command = [ + 06 01 00 00 00 00 01 DA + 06 01 00 00 00 00 01 DB]; + somc,mdss-dsi-uv-param-type = <4>; + somc,mdss-dsi-pcc-table-size = <226>; + somc,mdss-dsi-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x4380 0x6280 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x4680 0x6280 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x4900 0x6200 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x4B80 0x6200 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x4F00 0x6200 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x5300 0x6180 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x5700 0x6180 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x5A80 0x6100 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x5F00 0x6100 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x6380 0x6080 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x6780 0x6080 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x6B80 0x6080 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x7100 0x6000 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x7680 0x6000 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x7D00 0x6000 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x4900 0x6A00 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x4B80 0x6980 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x4E80 0x6900 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x5180 0x6900 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x5580 0x6880 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x5900 0x6880 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x5C80 0x6800 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x6100 0x6800 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x6500 0x6380 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x6880 0x6380 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x6D00 0x6300 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x7280 0x6300 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x7780 0x6280 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x7D80 0x6280 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x8000 0x6100 0x7E00 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x4E00 0x7100 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x5080 0x7100 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x5480 0x7080 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x5800 0x6B80 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x5A80 0x6B80 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x5F00 0x6B00 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x6300 0x6B00 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x6680 0x6A80 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x6A00 0x6A00 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x6E00 0x6A00 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x7400 0x6980 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x7880 0x6980 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x7E80 0x6900 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x8000 0x6380 0x7D80 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x6080 0x7780 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x5380 0x7900 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x5680 0x7880 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x5980 0x7380 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x5C80 0x7380 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x6100 0x7300 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x6480 0x7280 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x6780 0x7200 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x6B00 0x7180 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x6F80 0x7100 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x7480 0x7080 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x7980 0x7080 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x7F00 0x6B80 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x8000 0x6A00 0x7D80 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x6300 0x7780 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x6080 0x7180 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x5800 0x8000 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x5B00 0x8000 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x5F00 0x7B00 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x6280 0x7A80 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x6580 0x7A80 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x6900 0x7A00 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x6C80 0x7900 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x7100 0x7880 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x7580 0x7880 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x7A80 0x7380 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x7F80 0x7300 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x8000 0x7080 0x7D00 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x6980 0x7680 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x6300 0x7180 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x6000 0x6C80 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x5680 0x8000 0x7900 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x5A80 0x8000 0x7B00 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x5F00 0x8000 0x7C80 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x6380 0x8000 0x7D80 + 0x00 0x50 0x26 0x28 0x23 0x25 0x6780 0x8000 0x7E80 + 0x00 0x51 0x23 0x25 0x23 0x25 0x6C00 0x8000 0x7F00 + 0x00 0x52 0x20 0x22 0x23 0x25 0x7100 0x8000 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x7680 0x8000 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x7B00 0x7B00 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x8000 0x7A80 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x8000 0x7380 0x7C80 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x7080 0x7680 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x6980 0x7200 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x6280 0x6D00 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x5B80 0x6880 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x5500 0x8000 0x7380 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x5980 0x8000 0x7480 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x5E00 0x8000 0x7600 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x6300 0x8000 0x7700 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x6700 0x8000 0x7880 + 0x00 0x60 0x23 0x25 0x20 0x22 0x6B00 0x8000 0x7A00 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7080 0x8000 0x7A80 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x7680 0x8000 0x7C00 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x7C80 0x8000 0x7D00 + 0x00 0x64 0x17 0x19 0x20 0x22 0x8000 0x7B00 0x7C80 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x7800 0x7680 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x7080 0x7200 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x6980 0x6D80 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x6280 0x6900 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x5B80 0x6400 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x5400 0x8000 0x6E80 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x5800 0x8000 0x6F80 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x5C80 0x8000 0x7080 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x6200 0x8000 0x7200 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x6680 0x8000 0x7300 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x6B00 0x8000 0x7400 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x6F80 0x8000 0x7500 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x7600 0x8000 0x7680 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x7C00 0x8000 0x7700 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7B00 0x7680 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x7800 0x7280 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x7080 0x6E00 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x6900 0x6980 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x6200 0x6500 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x5B00 0x5F80 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x5200 0x8000 0x6A00 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x5780 0x8000 0x6B00 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x5B80 0x8000 0x6C00 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x6100 0x8000 0x6D00 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x6580 0x8000 0x6E80 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x6A00 0x8000 0x6F80 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x6F00 0x8000 0x7080 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x7580 0x8000 0x7180 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7C00 0x8000 0x7280 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7B00 0x7280 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x7800 0x6E80 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7080 0x6A80 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x6900 0x6600 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x6200 0x6080 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x5A80 0x5C00 + 0x00 0x88 0x32 0x34 0x17 0x19 0x5080 0x8000 0x6580 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x5600 0x8000 0x6700 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x5A80 0x8000 0x6880 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x6000 0x8000 0x6980 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x6480 0x8000 0x6A80 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x6980 0x8000 0x6B80 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x6E80 0x8000 0x6C80 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7500 0x8000 0x6D80 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7B00 0x8000 0x6E80 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x7B80 0x6F00 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x7800 0x6B00 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7000 0x6700 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x6900 0x6180 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x6180 0x5D00 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x5A00 0x5800 + 0x00 0x97 0x32 0x34 0x14 0x16 0x4F00 0x8000 0x6100 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x5480 0x8000 0x6300 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x5980 0x8000 0x6400 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x5E80 0x8000 0x6580 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x6400 0x8000 0x6700 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x6880 0x8000 0x6800 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x6D80 0x8000 0x6900 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7480 0x8000 0x6A00 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7A80 0x8000 0x6B00 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x7B80 0x6B80 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x7380 0x6800 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7000 0x6380 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x6880 0x5E80 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x6180 0x5A00 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x5980 0x5400 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x4E00 0x8000 0x5D00 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x5300 0x8000 0x5E80 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x5800 0x8000 0x6000 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x5D00 0x8000 0x6180 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x6300 0x8000 0x6300 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x6800 0x8000 0x6480 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x6D00 0x8000 0x6580 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7400 0x8000 0x6700 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7A00 0x8000 0x6800 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x8000 0x6880 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x7800 0x6480 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7000 0x5F80 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x6880 0x5B80 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x6100 0x5680 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x5900 0x5100 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x4C00 0x8000 0x5980 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x5100 0x8000 0x5B00 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x5780 0x8000 0x5C80 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x5C00 0x8000 0x5D80 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x6200 0x8000 0x5F00 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x6780 0x8000 0x6000 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x6C80 0x8000 0x6200 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7400 0x8000 0x6380 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7A00 0x8000 0x6480 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x8000 0x6580 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x7800 0x6100 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7000 0x5D00 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x6880 0x5880 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x6100 0x5300 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x5900 0x4F00 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x4B00 0x8000 0x5580 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x4F80 0x8000 0x5780 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x5600 0x8000 0x5900 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x5B00 0x8000 0x5A80 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x6100 0x8000 0x5C00 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x6680 0x8000 0x5D00 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x6B80 0x8000 0x5E80 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7280 0x8000 0x6000 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7980 0x8000 0x6080 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x8000 0x6280 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x7800 0x5E80 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7000 0x5A80 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x6880 0x5580 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x6080 0x5080 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x5880 0x4C80 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x4980 0x8000 0x5280 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x4E80 0x8000 0x5380 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x5480 0x8000 0x5580 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x5A00 0x8000 0x5700 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x6000 0x8000 0x5880 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x6580 0x8000 0x5A80 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x6B00 0x8000 0x5B80 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7180 0x8000 0x5C80 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7880 0x8000 0x5E00 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x8000 0x8000 0x5F80 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x7800 0x5C00 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7000 0x5780 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x6880 0x5300 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x6080 0x4E80 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x5800 0x4980 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-srgb-pcc-enable; + somc,mdss-dsi-srgb-pcc-table-size = <226>; + somc,mdss-dsi-srgb-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x4580 0x6800 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x4880 0x6800 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x4B00 0x6380 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x4E00 0x6380 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x5180 0x6300 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x5500 0x6300 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x5880 0x6280 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x5D00 0x6280 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x6180 0x6200 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x6580 0x6200 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x6980 0x6180 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x6E00 0x6180 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x7300 0x6180 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x7800 0x6100 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x7E80 0x6100 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x4B00 0x6B80 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x4D80 0x6B00 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x5080 0x6A80 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x5400 0x6A00 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x5700 0x6A00 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x5B00 0x6A00 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x5F80 0x6980 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x6380 0x6900 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x6780 0x6900 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x6B80 0x6880 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x6F80 0x6880 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x7400 0x6800 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x7980 0x6800 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x7F80 0x6800 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x8000 0x6180 0x7B00 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x5000 0x7280 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x5380 0x7200 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x5600 0x7180 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x5980 0x7180 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x5D00 0x7080 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x6180 0x7080 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x6580 0x7000 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x6900 0x7000 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x6C80 0x6B80 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x7100 0x6B00 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x7580 0x6B00 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x7A80 0x6A80 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x8000 0x6A80 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x8000 0x6800 0x7B00 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x6100 0x7480 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x5580 0x7A00 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x5800 0x7980 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x5B80 0x7900 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x5F80 0x7880 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x6380 0x7800 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x6680 0x7380 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x6A00 0x7300 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x6D80 0x7300 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x7200 0x7280 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x7680 0x7200 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x7C00 0x7180 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x8000 0x7100 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x8000 0x6A80 0x7A80 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x6500 0x7480 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x6080 0x6E80 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x5700 0x8000 0x7D80 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x5B80 0x8000 0x7E80 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x6080 0x8000 0x7F80 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x6500 0x8000 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x6800 0x7B00 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x6B80 0x7A80 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x6F00 0x7A00 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x7300 0x7A00 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x7780 0x7900 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x7C80 0x7880 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x8000 0x7380 0x7F80 + 0x00 0x48 0x11 0x13 0x26 0x28 0x8000 0x7100 0x7980 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x6A00 0x7480 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x6380 0x6E80 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x6000 0x6A00 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x5600 0x8000 0x7680 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x5A80 0x8000 0x7780 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x5F80 0x8000 0x7900 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x6480 0x8000 0x7A80 + 0x00 0x50 0x26 0x28 0x23 0x25 0x6880 0x8000 0x7B80 + 0x00 0x51 0x23 0x25 0x23 0x25 0x6C80 0x8000 0x7D00 + 0x00 0x52 0x20 0x22 0x23 0x25 0x7180 0x8000 0x7E00 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x7680 0x8000 0x7F00 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x7D00 0x8000 0x7F80 + 0x00 0x55 0x17 0x19 0x23 0x25 0x8000 0x7B00 0x7F00 + 0x00 0x56 0x14 0x16 0x23 0x25 0x8000 0x7800 0x7980 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x7080 0x7480 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x6A00 0x6F00 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x6300 0x6A80 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x6000 0x6580 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x5500 0x8000 0x7080 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x5980 0x8000 0x7180 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x5E80 0x8000 0x7380 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x6380 0x8000 0x7480 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x6800 0x8000 0x7580 + 0x00 0x60 0x23 0x25 0x20 0x22 0x6C80 0x8000 0x7680 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7100 0x8000 0x7800 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x7600 0x8000 0x7880 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x7C80 0x8000 0x7980 + 0x00 0x64 0x17 0x19 0x20 0x22 0x8000 0x7B00 0x7900 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x7380 0x7480 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x7100 0x6F80 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x6980 0x6B00 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x6300 0x6680 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x5B80 0x6100 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x5400 0x8000 0x6B80 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x5800 0x8000 0x6D00 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x5D00 0x8000 0x6E00 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x6280 0x8000 0x6F00 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x6700 0x8000 0x7000 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x6B80 0x8000 0x7180 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7080 0x8000 0x7300 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x7580 0x8000 0x7400 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x7C00 0x8000 0x7500 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7B00 0x7480 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x7380 0x6F80 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x7080 0x6B80 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x6980 0x6780 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x6280 0x6280 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x5B00 0x5C00 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x5300 0x8000 0x6780 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x5700 0x8000 0x6880 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x5C00 0x8000 0x6A00 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x6180 0x8000 0x6A80 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x6600 0x8000 0x6C00 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x6B00 0x8000 0x6D00 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x6F80 0x8000 0x6E00 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x7500 0x8000 0x6F00 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7B00 0x8000 0x7000 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7B00 0x7000 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x7380 0x6C00 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7080 0x6800 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x6980 0x6380 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x6280 0x5E00 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x5A80 0x5800 + 0x00 0x88 0x32 0x34 0x17 0x19 0x5100 0x8000 0x6300 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x5600 0x8000 0x6480 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x5A80 0x8000 0x6580 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x6080 0x8000 0x6700 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x6580 0x8000 0x6880 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x6A00 0x8000 0x6900 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x6F00 0x8000 0x6A80 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7480 0x8000 0x6B00 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7B00 0x8000 0x6C00 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x7B80 0x6C80 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x7380 0x6880 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7080 0x6480 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x6980 0x5F80 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x6200 0x5A00 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x5A00 0x5500 + 0x00 0x97 0x32 0x34 0x14 0x16 0x4F80 0x8000 0x5E80 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x5480 0x8000 0x6000 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x5980 0x8000 0x6180 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x5F80 0x8000 0x6300 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x6500 0x8000 0x6480 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x6980 0x8000 0x6580 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x6E80 0x8000 0x6680 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7400 0x8000 0x6800 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7A80 0x8000 0x6880 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x7B80 0x6900 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x7800 0x6580 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7080 0x6100 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x6980 0x5B80 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x6180 0x5680 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x5980 0x5180 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x4E00 0x8000 0x5A00 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x5380 0x8000 0x5B80 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x5800 0x8000 0x5D00 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x5D80 0x8000 0x5F00 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x6400 0x8000 0x6080 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x6900 0x8000 0x6180 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x6E00 0x8000 0x6300 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7400 0x8000 0x6480 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7A00 0x8000 0x6580 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x8000 0x6600 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x7800 0x6200 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7080 0x5D80 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x6900 0x5800 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x6180 0x5400 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x5980 0x4E80 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x4C80 0x8000 0x5600 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x5180 0x8000 0x5780 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x5700 0x8000 0x5900 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x5C80 0x8000 0x5B00 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x6300 0x8000 0x5C80 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x6800 0x8000 0x5E00 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x6D00 0x8000 0x5F80 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7300 0x8000 0x6080 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7980 0x8000 0x6200 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x8000 0x6380 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x7800 0x5F00 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7080 0x5A00 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x6900 0x5580 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x6100 0x5080 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x5900 0x4C00 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x4B00 0x8000 0x5300 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x5000 0x8000 0x5500 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x5600 0x8000 0x5600 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x5B80 0x8000 0x5780 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x6200 0x8000 0x5880 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x6780 0x8000 0x5A00 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x6C80 0x8000 0x5B80 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7280 0x8000 0x5D00 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7880 0x8000 0x5E80 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x8000 0x6000 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x7800 0x5B80 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7000 0x5780 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x6900 0x5300 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x6100 0x4E00 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x5880 0x4A00 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x4980 0x8000 0x4F80 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x4E80 0x8000 0x5100 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x5480 0x8000 0x5300 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x5A00 0x8000 0x5480 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x6080 0x8000 0x5600 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x6680 0x8000 0x5700 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x6C00 0x8000 0x5880 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7200 0x8000 0x5A00 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7800 0x8000 0x5B80 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x7F80 0x8000 0x5C80 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x7800 0x5880 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7000 0x5500 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x6880 0x5000 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x6080 0x4C00 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x5380 0x4780 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-vivid-pcc-enable; + somc,mdss-dsi-vivid-pcc-table-size = <226>; + somc,mdss-dsi-vivid-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x6180 0x7280 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x6300 0x7200 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x6500 0x7200 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x6700 0x7200 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x6880 0x7180 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x6A80 0x7180 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x6C80 0x7180 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x6F80 0x7180 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x7180 0x7100 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x7400 0x7100 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x7600 0x7100 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x7800 0x7100 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x7A80 0x7100 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x7C80 0x7100 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x7F80 0x7080 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x6500 0x7800 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x6680 0x7380 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x6800 0x7380 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x6A00 0x7380 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x6C00 0x7300 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x6E80 0x7300 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x7080 0x7300 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x7280 0x7300 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x7480 0x7300 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x7680 0x7280 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x7880 0x7280 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x7B00 0x7280 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x7D00 0x7280 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x8000 0x7200 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x8000 0x7100 0x7E00 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x6800 0x7980 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x6980 0x7980 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x6B80 0x7900 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x6D00 0x7900 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x6F80 0x7880 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x7180 0x7880 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x7380 0x7880 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x7500 0x7800 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x7700 0x7800 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x7900 0x7800 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x7B80 0x7380 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x7D80 0x7380 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x8000 0x7380 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x8000 0x7280 0x7E00 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x7080 0x7B00 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x6B00 0x7B00 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x6C80 0x7B00 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x6F00 0x7A80 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x7080 0x7A80 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x7280 0x7A00 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x7400 0x7A00 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x7600 0x7A00 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x7780 0x7980 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x7A00 0x7980 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x7C00 0x7900 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x7E00 0x7900 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x8000 0x7900 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x8000 0x7380 0x7D80 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x7200 0x7B00 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x7080 0x7880 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x6C00 0x8000 0x7F00 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x6F00 0x8000 0x7F80 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x7100 0x8000 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x7380 0x8000 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x7500 0x7B80 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x7700 0x7B80 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x7880 0x7B00 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x7A80 0x7B00 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x7C80 0x7B00 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x7E80 0x7A80 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x8000 0x7A00 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x8000 0x7880 0x7D80 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x7380 0x7B00 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x7200 0x7880 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x7080 0x7600 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x6B80 0x8000 0x7C00 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x6E80 0x8000 0x7C80 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x7080 0x8000 0x7D80 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x7300 0x8000 0x7E00 + 0x00 0x50 0x26 0x28 0x23 0x25 0x7500 0x8000 0x7E00 + 0x00 0x51 0x23 0x25 0x23 0x25 0x7700 0x8000 0x7E80 + 0x00 0x52 0x20 0x22 0x23 0x25 0x7980 0x8000 0x7F00 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x7C80 0x8000 0x7F80 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x7E80 0x8000 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x8000 0x7B80 0x7F80 + 0x00 0x56 0x14 0x16 0x23 0x25 0x8000 0x7A00 0x7D80 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x7880 0x7B00 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x7300 0x7900 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x7200 0x7600 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x7000 0x7400 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x6A80 0x8000 0x7980 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x6D80 0x8000 0x7A00 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x7000 0x8000 0x7A80 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x7280 0x8000 0x7B00 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x7480 0x8000 0x7B80 + 0x00 0x60 0x23 0x25 0x20 0x22 0x7700 0x8000 0x7C00 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7980 0x8000 0x7C80 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x7C00 0x8000 0x7D00 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x7E80 0x8000 0x7D80 + 0x00 0x64 0x17 0x19 0x20 0x22 0x8000 0x7B80 0x7D80 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x7A00 0x7B00 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x7880 0x7900 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x7300 0x7680 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x7180 0x7480 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x7000 0x7180 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x6A00 0x8000 0x7700 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x6C80 0x8000 0x7780 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x6F80 0x8000 0x7800 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x7200 0x8000 0x7900 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x7400 0x8000 0x7980 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x7700 0x8000 0x7A00 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7900 0x8000 0x7A80 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x7C00 0x8000 0x7B00 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x7E00 0x8000 0x7B80 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7B80 0x7B00 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x7A00 0x7900 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x7880 0x7700 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x7300 0x7500 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x7180 0x7280 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x7000 0x6F80 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x6980 0x8000 0x7500 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x6C00 0x8000 0x7600 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x6F00 0x8000 0x7600 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x7180 0x8000 0x7680 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x7400 0x8000 0x7700 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x7680 0x8000 0x7780 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7900 0x8000 0x7800 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x7B80 0x8000 0x7880 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7E00 0x8000 0x7900 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7B80 0x7900 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x7A00 0x7700 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7880 0x7580 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x7300 0x7300 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x7180 0x7080 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x6B80 0x6D80 + 0x00 0x88 0x32 0x34 0x17 0x19 0x6880 0x8000 0x7300 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x6B00 0x8000 0x7380 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x6E80 0x8000 0x7400 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x7100 0x8000 0x7500 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x7400 0x8000 0x7580 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x7600 0x8000 0x7600 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7880 0x8000 0x7600 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7B80 0x8000 0x7680 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7D80 0x8000 0x7700 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x8000 0x7780 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x7A00 0x7580 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7880 0x7380 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x7300 0x7100 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x7100 0x6E80 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x6B00 0x6B80 + 0x00 0x97 0x32 0x34 0x14 0x16 0x6800 0x8000 0x7080 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x6A80 0x8000 0x7180 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x6D00 0x8000 0x7200 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x7080 0x8000 0x7280 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x7380 0x8000 0x7380 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x7580 0x8000 0x7400 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7800 0x8000 0x7480 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7B00 0x8000 0x7500 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7D80 0x8000 0x7600 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x8000 0x7600 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x7A00 0x7400 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7880 0x7180 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x7300 0x6F00 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x7100 0x6C80 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x6B80 0x6980 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x6700 0x8000 0x6E80 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x6A00 0x8000 0x6F00 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x6C80 0x8000 0x7000 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x7000 0x8000 0x7080 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x7300 0x8000 0x7180 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x7580 0x8000 0x7200 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x7800 0x8000 0x7280 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7B00 0x8000 0x7380 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7D80 0x8000 0x7400 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x8000 0x7480 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x7A00 0x7200 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7880 0x7000 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x7300 0x6D80 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x7100 0x6B00 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x6B00 0x6800 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x6600 0x8000 0x6C80 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x6900 0x8000 0x6D00 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x6C00 0x8000 0x6E00 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x6F80 0x8000 0x6F00 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x7280 0x8000 0x6F80 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x7500 0x8000 0x7000 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x7780 0x8000 0x7100 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7A80 0x8000 0x7180 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7D00 0x8000 0x7200 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x8000 0x7300 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x7A80 0x7080 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7880 0x6E80 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x7300 0x6C00 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x7100 0x6900 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x6B00 0x6680 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x6580 0x8000 0x6A80 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x6880 0x8000 0x6B80 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x6B00 0x8000 0x6C00 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x6F00 0x8000 0x6D00 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x7200 0x8000 0x6E00 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x7480 0x8000 0x6E80 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x7700 0x8000 0x6F00 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7A00 0x8000 0x7000 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7D00 0x8000 0x7080 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x8000 0x7100 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x7A80 0x6F00 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7880 0x6D00 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x7280 0x6A80 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x7080 0x6780 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x6A80 0x6500 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x6480 0x8000 0x6880 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x6780 0x8000 0x6980 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x6A80 0x8000 0x6A80 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x6E00 0x8000 0x6B80 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x7180 0x8000 0x6C00 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x7400 0x8000 0x6D00 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x7700 0x8000 0x6D80 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7A00 0x8000 0x6E80 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7C80 0x8000 0x6F00 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x8000 0x8000 0x6F80 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x7A80 0x6D80 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7880 0x6B80 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x7280 0x6900 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x7080 0x6600 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x6A00 0x6380 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-hdr-pcc-enable; + somc,mdss-dsi-hdr-pcc-table-size = <226>; + somc,mdss-dsi-hdr-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x6100 0x7180 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x6280 0x7200 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x6480 0x7180 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x6600 0x7180 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x6800 0x7180 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x6A00 0x7100 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x6C80 0x7100 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x6E80 0x7100 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x7100 0x7100 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x7380 0x7080 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x7580 0x7080 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x7780 0x7080 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x7A00 0x7080 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x7D00 0x7080 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x8000 0x7080 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x6400 0x7380 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x6600 0x7380 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x6780 0x7300 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x6980 0x7300 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x6B80 0x7280 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x6D80 0x7280 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x6F80 0x7280 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x7200 0x7280 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x7400 0x7200 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x7600 0x7200 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x7800 0x7200 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x7A80 0x7200 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x7D80 0x7200 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x8000 0x7180 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x8000 0x7000 0x7C80 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x6780 0x7900 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x6900 0x7900 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x6B00 0x7880 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x6D00 0x7880 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x6F00 0x7880 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x7100 0x7800 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x7300 0x7800 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x7480 0x7800 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x7700 0x7380 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x7900 0x7380 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x7B00 0x7380 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x7E00 0x7300 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x8000 0x7280 0x7F80 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x8000 0x7180 0x7C80 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x7000 0x7980 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x6A80 0x7B00 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x6C00 0x7A80 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x6E00 0x7A80 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x7000 0x7A00 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x7200 0x7A00 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x7380 0x7980 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x7580 0x7980 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x7700 0x7980 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x7980 0x7900 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x7C00 0x7900 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x7E00 0x7880 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x8000 0x7800 0x7F80 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x8000 0x7280 0x7C80 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x7180 0x7980 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x6B80 0x7700 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x6C00 0x8000 0x7F00 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x6E00 0x8000 0x7F80 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x7080 0x8000 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x7280 0x8000 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x7400 0x7B80 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x7600 0x7B00 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x7800 0x7B00 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x7A00 0x7A80 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x7C80 0x7A80 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x7E80 0x7A80 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x8000 0x7980 0x7F00 + 0x00 0x48 0x11 0x13 0x26 0x28 0x8000 0x7800 0x7C00 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x7280 0x7A00 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x7100 0x7700 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x6B80 0x7480 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x6B00 0x8000 0x7B80 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x6D80 0x8000 0x7C00 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x7000 0x8000 0x7D00 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x7280 0x8000 0x7D80 + 0x00 0x50 0x26 0x28 0x23 0x25 0x7480 0x8000 0x7E80 + 0x00 0x51 0x23 0x25 0x23 0x25 0x7700 0x8000 0x7F00 + 0x00 0x52 0x20 0x22 0x23 0x25 0x7980 0x8000 0x7F00 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x7C00 0x8000 0x7F80 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x7F00 0x8000 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x8000 0x7B00 0x7F00 + 0x00 0x56 0x14 0x16 0x23 0x25 0x8000 0x7980 0x7C00 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x7800 0x7A00 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x7280 0x7780 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x7100 0x7480 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x6B00 0x7280 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x6A80 0x8000 0x7880 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x6D00 0x8000 0x7980 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x6F80 0x8000 0x7A00 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x7200 0x8000 0x7A80 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x7400 0x8000 0x7B00 + 0x00 0x60 0x23 0x25 0x20 0x22 0x7700 0x8000 0x7B80 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7900 0x8000 0x7C00 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x7C00 0x8000 0x7C80 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x7E80 0x8000 0x7D00 + 0x00 0x64 0x17 0x19 0x20 0x22 0x8000 0x7B00 0x7C00 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x7980 0x7A00 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x7800 0x7780 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x7200 0x7500 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x7100 0x7300 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x6B00 0x7000 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x6980 0x8000 0x7680 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x6C00 0x8000 0x7700 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x6F00 0x8000 0x7780 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x7180 0x8000 0x7800 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x7380 0x8000 0x7900 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x7680 0x8000 0x7980 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7900 0x8000 0x7A00 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x7B80 0x8000 0x7A80 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x7E80 0x8000 0x7B00 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7B00 0x7A00 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x7980 0x7780 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x7800 0x7580 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x7200 0x7300 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x7080 0x7080 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x6A80 0x6D80 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x6900 0x8000 0x7400 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x6B80 0x8000 0x7480 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x6E80 0x8000 0x7500 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x7100 0x8000 0x7600 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x7380 0x8000 0x7680 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x7600 0x8000 0x7700 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7880 0x8000 0x7780 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x7B80 0x8000 0x7800 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7E80 0x8000 0x7800 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7B00 0x7800 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x7980 0x7580 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7800 0x7300 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x7200 0x7100 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x7080 0x6E80 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x6A80 0x6B80 + 0x00 0x88 0x32 0x34 0x17 0x19 0x6800 0x8000 0x7200 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x6B00 0x8000 0x7200 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x6D80 0x8000 0x7300 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x7080 0x8000 0x7380 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x7380 0x8000 0x7400 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x7580 0x8000 0x7500 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7800 0x8000 0x7580 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7B00 0x8000 0x7600 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7E00 0x8000 0x7680 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x7B00 0x7600 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x7980 0x7380 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7380 0x7180 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x7200 0x6F00 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x7080 0x6C80 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x6A00 0x6980 + 0x00 0x97 0x32 0x34 0x14 0x16 0x6780 0x8000 0x6F80 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x6A00 0x8000 0x7080 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x6D00 0x8000 0x7100 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x7000 0x8000 0x7200 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x7300 0x8000 0x7280 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x7580 0x8000 0x7300 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7800 0x8000 0x7380 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7B00 0x8000 0x7400 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7E00 0x8000 0x7480 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x7B00 0x7400 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x7980 0x7280 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7380 0x7000 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x7200 0x6D80 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x7000 0x6A80 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x6A00 0x6780 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x6680 0x8000 0x6D80 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x6980 0x8000 0x6E80 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x6C80 0x8000 0x6F00 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x6F80 0x8000 0x7000 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x7280 0x8000 0x7080 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x7500 0x8000 0x7180 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x7780 0x8000 0x7200 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7A80 0x8000 0x7280 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7E00 0x8000 0x7280 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x7B00 0x7280 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x7980 0x7080 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7380 0x6E00 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x7200 0x6B80 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x7000 0x6900 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x6A00 0x6600 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x6580 0x8000 0x6B80 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x6880 0x8000 0x6C80 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x6B80 0x8000 0x6D00 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x6F00 0x8000 0x6E00 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x7200 0x8000 0x6E80 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x7480 0x8000 0x6F80 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x7700 0x8000 0x7000 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7A80 0x8000 0x7100 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7D80 0x8000 0x7180 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x7B00 0x7100 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x7980 0x6F00 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7380 0x6C80 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x7200 0x6A00 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x7000 0x6700 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x6980 0x6400 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x6500 0x8000 0x6980 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x6800 0x8000 0x6A80 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x6B00 0x8000 0x6B00 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x6E00 0x8000 0x6B80 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x7180 0x8000 0x6C80 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x7400 0x8000 0x6D80 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x7700 0x8000 0x6E80 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7A00 0x8000 0x6F00 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7D80 0x8000 0x6F80 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x7B80 0x6F80 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x7980 0x6D80 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7380 0x6B00 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x7200 0x6880 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x6B80 0x6580 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x6980 0x6280 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x6400 0x8000 0x6780 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x6700 0x8000 0x6880 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x6A80 0x8000 0x6980 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x6D80 0x8000 0x6A80 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x7100 0x8000 0x6B00 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x7380 0x8000 0x6C00 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x7700 0x8000 0x6C00 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7980 0x8000 0x6D80 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7D00 0x8000 0x6E00 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x8000 0x7B80 0x6E00 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x7980 0x6C00 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7380 0x6980 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x7180 0x6700 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x6B80 0x6400 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x6900 0x6180 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-poplar-id9_pcc.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-poplar-id9_pcc.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..c97fa7d3d2ae4eff680d948f2b6cd6aee7327e4c --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-poplar-id9_pcc.dtsi @@ -0,0 +1,947 @@ +/* arch/arm64/boot/dts/qcom/dsi-panel-poplar-id9_pcc.dtsi + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +/* pcc-table for Poplar Sharp */ +&mdss_mdp { + dsi_9: somc,9_panel { + somc,mdss-dsi-pcc-enable; + somc,mdss-dsi-uv-command = [ + 06 01 00 00 00 00 01 DA + 06 01 00 00 00 00 01 DB]; + somc,mdss-dsi-uv-param-type = <4>; + somc,mdss-dsi-pcc-table-size = <226>; + somc,mdss-dsi-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x3A80 0x5A80 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x3D80 0x5A00 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x4080 0x5A00 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x4380 0x5980 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x4700 0x5980 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x4A00 0x5900 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x4D80 0x5900 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x5180 0x5900 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x5580 0x5900 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x5980 0x5900 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x5E00 0x5880 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x6380 0x5880 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x6900 0x5880 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x6E80 0x5880 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x7480 0x5880 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x4000 0x6180 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x4300 0x6100 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x4600 0x6100 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x4900 0x6080 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x4C00 0x6080 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x4F80 0x6000 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x5300 0x6000 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x5780 0x5B80 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x5B00 0x5B80 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x5F80 0x5B00 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x6500 0x5B00 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x6A80 0x5B00 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x6F80 0x5B00 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x7500 0x5B00 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x7B80 0x5A80 0x8000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x4580 0x6880 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x4880 0x6800 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x4B00 0x6800 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x4E00 0x6380 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x5180 0x6300 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x5580 0x6300 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x5900 0x6280 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x5C80 0x6280 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x6180 0x6200 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x6600 0x6200 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x6B80 0x6180 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x7080 0x6180 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x7600 0x6180 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x7C00 0x6100 0x8000 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x5B80 0x7E00 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x4A00 0x6B80 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x4D00 0x6B00 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x5000 0x6A80 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x5300 0x6A80 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x5780 0x6A00 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x5A80 0x6980 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x5E80 0x6900 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x6300 0x6900 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x6780 0x6900 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x6C80 0x6880 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x7180 0x6800 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x7680 0x6800 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x7D00 0x6380 0x8000 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x6200 0x7D80 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x5B00 0x7680 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x4F00 0x7280 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x5200 0x7200 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x5500 0x7180 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x5880 0x7100 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x5B80 0x7080 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x5F80 0x7080 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x6400 0x7000 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x6880 0x6B80 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x6D80 0x6B00 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x7200 0x6B00 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x7700 0x6A80 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x7D00 0x6A00 0x8000 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x6900 0x7D80 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x6200 0x7780 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x5B00 0x7180 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x5380 0x7A00 0x8000 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x5700 0x7980 0x8000 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x5A00 0x7900 0x8000 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x5D00 0x7800 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x6100 0x7800 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x6580 0x7380 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x6980 0x7300 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x6E00 0x7200 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x7300 0x7200 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x7780 0x7180 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x7D80 0x7100 0x8000 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x6B80 0x7D80 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x6880 0x7780 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x6180 0x7200 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x5A80 0x6C80 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x5500 0x8000 0x7D80 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x5980 0x8000 0x7E80 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x5D80 0x8000 0x7F80 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x6280 0x8000 0x8000 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x6680 0x7B00 0x8000 + 0x00 0x60 0x23 0x25 0x20 0x22 0x6B00 0x7A80 0x8000 + 0x00 0x61 0x20 0x22 0x20 0x22 0x6F00 0x7A00 0x8000 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x7380 0x7980 0x8000 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x7800 0x7900 0x8000 + 0x00 0x64 0x17 0x19 0x20 0x22 0x7E00 0x7800 0x8000 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x7200 0x7D80 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x6B00 0x7800 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x6880 0x7280 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x6180 0x6D80 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x5A00 0x6700 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x5400 0x8000 0x7700 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x5880 0x8000 0x7780 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x5C80 0x8000 0x7900 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x6180 0x8000 0x7B00 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x6700 0x8000 0x7C80 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x6C80 0x8000 0x7D80 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7180 0x8000 0x7E00 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x7700 0x8000 0x7F00 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x7E00 0x8000 0x8000 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7980 0x7D80 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x7280 0x7780 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x6B00 0x7300 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x6800 0x6E00 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x6100 0x6880 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x5980 0x6280 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x5280 0x8000 0x7200 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x5780 0x8000 0x7300 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x5B80 0x8000 0x7480 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x6080 0x8000 0x7580 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x6600 0x8000 0x7680 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x6C00 0x8000 0x7780 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7100 0x8000 0x7800 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x7700 0x8000 0x7980 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7D80 0x8000 0x7B00 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7980 0x7780 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x7200 0x7380 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x6B00 0x6F00 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x6800 0x6980 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x6080 0x6400 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x5900 0x5E80 + 0x00 0x88 0x32 0x34 0x17 0x19 0x5180 0x8000 0x6D00 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x5680 0x8000 0x6E80 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x5B00 0x8000 0x6F80 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x5F80 0x8000 0x7100 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x6580 0x8000 0x7200 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x6B80 0x8000 0x7300 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7080 0x8000 0x7400 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7680 0x8000 0x7500 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7D80 0x8000 0x7600 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x7A00 0x7400 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x7200 0x6F80 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x6B00 0x6B00 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x6380 0x6500 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x6080 0x6000 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x5900 0x5B80 + 0x00 0x97 0x32 0x34 0x14 0x16 0x5000 0x8000 0x6800 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x5500 0x8000 0x6980 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x5980 0x8000 0x6B00 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x5E80 0x8000 0x6C80 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x6480 0x8000 0x6D80 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x6A80 0x8000 0x6F00 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7000 0x8000 0x7000 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7600 0x8000 0x7100 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7D00 0x8000 0x7200 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x7A00 0x7080 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x7280 0x6C00 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x6A80 0x6680 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x6380 0x6180 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x6000 0x5D00 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x5880 0x5800 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x4F00 0x8000 0x6380 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x5380 0x8000 0x6500 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x5900 0x8000 0x6680 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x5D80 0x8000 0x6780 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x6380 0x8000 0x6900 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x6980 0x8000 0x6B00 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x6F80 0x8000 0x6C00 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7580 0x8000 0x6D00 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7C00 0x8000 0x6E00 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x7A00 0x6C80 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x7200 0x6780 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x6A80 0x6300 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x6380 0x5E80 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x5B80 0x5A00 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x5800 0x5480 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x4D80 0x8000 0x6000 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x5280 0x8000 0x6100 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x5800 0x8000 0x6280 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x5C80 0x8000 0x6380 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x6280 0x8000 0x6500 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x6880 0x8000 0x6680 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x6F00 0x8000 0x6780 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7500 0x8000 0x6900 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7C00 0x8000 0x6A80 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x7A00 0x6880 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x7200 0x6400 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x6A80 0x6000 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x6300 0x5B80 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x5B80 0x5680 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x5380 0x5200 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x4C80 0x8000 0x5D00 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x5180 0x8000 0x5E00 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x5700 0x8000 0x5F00 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x5B80 0x8000 0x6000 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x6180 0x8000 0x6180 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x6800 0x8000 0x6280 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x6E00 0x8000 0x6400 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7480 0x8000 0x6580 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7B80 0x8000 0x6680 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x7A00 0x6580 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x7200 0x6100 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x6A80 0x5D00 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x6300 0x5900 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x5B00 0x5400 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x5380 0x5000 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x4B80 0x8000 0x5980 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x5000 0x8000 0x5A80 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x5580 0x8000 0x5C00 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x5B00 0x8000 0x5D80 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x6080 0x8000 0x5E80 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x6700 0x8000 0x5F80 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x6D80 0x8000 0x6080 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7400 0x8000 0x6200 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7B00 0x8000 0x6300 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x8000 0x7A80 0x6280 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x7200 0x5E80 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x6A80 0x5A80 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x6280 0x5680 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x5B00 0x5200 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x5300 0x4E00 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-srgb-pcc-enable; + somc,mdss-dsi-srgb-pcc-table-size = <226>; + somc,mdss-dsi-srgb-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x3A80 0x5A80 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x3E00 0x5A80 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x4100 0x5A00 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x4480 0x5A00 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x4780 0x5980 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x4A80 0x5980 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x4E00 0x5980 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x5200 0x5900 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x5600 0x5900 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x5A00 0x5900 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x5F00 0x5880 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x6400 0x5880 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x6980 0x5880 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x6F80 0x5880 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x7480 0x5880 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x4100 0x6200 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x4400 0x6180 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x4700 0x6180 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x4A00 0x6100 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x4D00 0x6100 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x5000 0x6080 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x5400 0x6080 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x5780 0x6000 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x5C00 0x6000 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x6000 0x5B80 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x6600 0x5B80 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x6B80 0x5B80 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x7080 0x5B00 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x7500 0x5B00 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x7B80 0x5B00 0x8000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x4680 0x6900 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x4900 0x6880 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x4B80 0x6880 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x4F00 0x6880 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x5280 0x6800 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x5580 0x6380 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x5900 0x6380 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x5D80 0x6300 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x6280 0x6300 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x6700 0x6280 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x6C80 0x6280 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x7100 0x6200 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x7600 0x6200 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x7C00 0x6180 0x8000 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x6000 0x7D00 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x4B00 0x7000 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x4D80 0x6B80 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x5100 0x6B00 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x5400 0x6B00 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x5700 0x6A80 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x5B00 0x6A00 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x5F00 0x6A00 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x6380 0x6980 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x6880 0x6980 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x6D00 0x6900 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x7200 0x6880 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x7680 0x6880 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x7D00 0x6800 0x8000 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x6300 0x7D00 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x6000 0x7580 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x4F80 0x7300 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x5300 0x7280 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x5580 0x7200 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x5880 0x7180 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x5C80 0x7180 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x6000 0x7100 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x6500 0x7080 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x6980 0x7000 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x6E00 0x6B80 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x7280 0x6B80 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x7700 0x6B00 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x7D00 0x6A80 0x8000 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x6980 0x7D00 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x6280 0x7600 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x5B80 0x7100 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x5480 0x7A00 0x8000 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x5700 0x7980 0x8000 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x5A00 0x7900 0x8000 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x5E00 0x7800 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x6200 0x7800 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x6680 0x7380 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x6B00 0x7300 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x6F00 0x7280 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x7380 0x7200 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x7780 0x7180 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x7D80 0x7180 0x8000 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x7000 0x7D00 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x6900 0x7600 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x6200 0x7180 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x5B00 0x6B80 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x5580 0x8000 0x7C00 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x5980 0x8000 0x7D80 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x5E80 0x8000 0x7F80 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x6380 0x8000 0x8000 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x6780 0x7B00 0x8000 + 0x00 0x60 0x23 0x25 0x20 0x22 0x6C00 0x7A80 0x8000 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7000 0x7A00 0x8000 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x7380 0x7980 0x8000 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x7800 0x7900 0x8000 + 0x00 0x64 0x17 0x19 0x20 0x22 0x7E00 0x7800 0x8000 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x7280 0x7D00 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x6B80 0x7680 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x6900 0x7180 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x6200 0x6C80 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x5A80 0x6680 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x5500 0x8000 0x7580 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x5880 0x8000 0x7680 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x5D80 0x8000 0x7800 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x6280 0x8000 0x7980 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x6800 0x8000 0x7B00 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x6D00 0x8000 0x7C00 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7200 0x8000 0x7D80 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x7700 0x8000 0x7F00 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x7E00 0x8000 0x8000 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7980 0x7D00 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x7280 0x7680 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x6B80 0x7200 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x6880 0x6D80 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x6180 0x6780 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x5A00 0x6180 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x5380 0x8000 0x7100 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x5780 0x8000 0x7200 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x5C80 0x8000 0x7300 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x6100 0x8000 0x7400 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x6700 0x8000 0x7500 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x6D00 0x8000 0x7600 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7180 0x8000 0x7700 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x7700 0x8000 0x7800 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x7D80 0x8000 0x7980 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7980 0x7700 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x7280 0x7280 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x6B80 0x6E00 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x6880 0x6880 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x6100 0x6300 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x5980 0x5E00 + 0x00 0x88 0x32 0x34 0x17 0x19 0x5280 0x8000 0x6C80 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x5680 0x8000 0x6D80 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x5B00 0x8000 0x6F00 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x6000 0x8000 0x7000 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x6600 0x8000 0x7100 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x6C80 0x8000 0x7200 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7100 0x8000 0x7300 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7680 0x8000 0x7400 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x7D00 0x8000 0x7480 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x7A00 0x7300 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x7280 0x6F00 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x6B80 0x6A00 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x6800 0x6480 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x6100 0x5F00 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x5900 0x5980 + 0x00 0x97 0x32 0x34 0x14 0x16 0x5100 0x8000 0x6700 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x5580 0x8000 0x6880 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x5A00 0x8000 0x6A00 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x5F80 0x8000 0x6B80 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x6580 0x8000 0x6D00 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x6B80 0x8000 0x6E00 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7100 0x8000 0x6F00 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7600 0x8000 0x7080 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x7D00 0x8000 0x7180 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x7A00 0x6F80 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x7280 0x6A80 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x6B80 0x6580 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x6800 0x6080 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x6080 0x5B80 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x5880 0x5680 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x4F80 0x8000 0x6280 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x5480 0x8000 0x6400 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x5900 0x8000 0x6580 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x5E80 0x8000 0x6700 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x6480 0x8000 0x6880 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x6B00 0x8000 0x6980 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x7080 0x8000 0x6B00 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7580 0x8000 0x6C00 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7C00 0x8000 0x6D80 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x7A00 0x6B80 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x7280 0x6700 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x6B00 0x6200 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x6800 0x5D80 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x6000 0x5800 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x5880 0x5400 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x4E80 0x8000 0x5E80 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x5300 0x8000 0x6000 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x5800 0x8000 0x6100 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x5D80 0x8000 0x6280 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x6380 0x8000 0x6400 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x6980 0x8000 0x6580 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x6F80 0x8000 0x6700 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7500 0x8000 0x6800 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7B80 0x8000 0x6980 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x7A00 0x6800 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x7280 0x6380 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x6B00 0x5E80 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x6800 0x5A00 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x6000 0x5580 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x5380 0x5100 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x4D00 0x8000 0x5A80 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x5200 0x8000 0x5C80 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x5680 0x8000 0x5D80 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x5C80 0x8000 0x5F00 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x6280 0x8000 0x6080 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x6880 0x8000 0x6180 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x6F00 0x8000 0x6300 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7480 0x8000 0x6480 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7B80 0x8000 0x6580 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x7A00 0x6480 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x7280 0x6000 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x6B00 0x5C00 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x6380 0x5780 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x5B80 0x5300 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x5380 0x4E80 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x4B80 0x8000 0x5780 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x5080 0x8000 0x5900 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x5600 0x8000 0x5A00 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x5B00 0x8000 0x5C00 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x6100 0x8000 0x5D80 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x6800 0x8000 0x5E80 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x6E80 0x8000 0x6000 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7400 0x8000 0x6100 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7B00 0x8000 0x6200 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x8000 0x7A80 0x6180 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x7280 0x5D80 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x6B00 0x5900 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x6380 0x5500 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x5B80 0x5080 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x5300 0x4C80 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-vivid-pcc-enable; + somc,mdss-dsi-vivid-pcc-table-size = <226>; + somc,mdss-dsi-vivid-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x5B80 0x7000 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x5E00 0x7000 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x6000 0x7000 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x6200 0x6B80 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x6400 0x6B80 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x6600 0x6B80 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x6880 0x6B80 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x6A80 0x6B80 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x6C80 0x6B80 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x6F00 0x6B00 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x7180 0x6B00 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x7480 0x6B00 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x7780 0x6B00 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x7A00 0x6B00 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x7C80 0x6B00 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x5F80 0x7180 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x6180 0x7180 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x6400 0x7180 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x6580 0x7100 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x6780 0x7100 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x6980 0x7100 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x6C00 0x7100 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x6D80 0x7100 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x7000 0x7080 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x7280 0x7080 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x7500 0x7080 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x7800 0x7080 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x7A80 0x7080 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x7D00 0x7000 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x8000 0x7000 0x8000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x6300 0x7300 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x6500 0x7300 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x6700 0x7300 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x6900 0x7280 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x6B00 0x7280 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x6C80 0x7280 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x6E80 0x7280 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x7100 0x7200 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x7380 0x7200 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x7600 0x7200 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x7880 0x7180 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x7B00 0x7180 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x7D80 0x7180 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x8000 0x7180 0x8000 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x7000 0x7D80 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x6600 0x7880 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x6800 0x7880 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x6A00 0x7880 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x6C00 0x7800 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x6D80 0x7800 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x6F80 0x7800 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x7200 0x7380 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x7400 0x7380 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x7680 0x7380 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x7900 0x7300 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x7B00 0x7300 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x7D80 0x7300 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x8000 0x7280 0x8000 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x7180 0x7D80 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x7000 0x7B00 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x6900 0x7A00 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x6B00 0x7A00 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x6C80 0x7980 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x6E80 0x7980 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x7080 0x7980 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x7280 0x7900 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x7500 0x7900 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x7700 0x7880 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x7980 0x7880 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x7B80 0x7880 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x7E00 0x7800 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x8000 0x7800 0x8000 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x7280 0x7D80 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x7100 0x7B00 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x6B80 0x7800 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x6C00 0x7B80 0x8000 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x6D80 0x7B80 0x8000 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x6F00 0x7B00 0x8000 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x7100 0x7B00 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x7380 0x7A80 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x7580 0x7A80 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x7780 0x7A00 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x7A00 0x7A00 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x7C00 0x7A00 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x7E80 0x7980 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x8000 0x7900 0x8000 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x7800 0x7D80 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x7280 0x7B00 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x7100 0x7880 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x6B80 0x7580 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x6C00 0x8000 0x7E00 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x6E00 0x8000 0x7E80 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x7080 0x8000 0x7F00 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x7300 0x8000 0x7F80 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x7600 0x8000 0x8000 + 0x00 0x60 0x23 0x25 0x20 0x22 0x7800 0x8000 0x8000 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7A80 0x7B80 0x8000 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x7C00 0x7B80 0x8000 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x7E80 0x7B00 0x8000 + 0x00 0x64 0x17 0x19 0x20 0x22 0x8000 0x7A80 0x7F80 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x7900 0x7D80 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x7800 0x7B00 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x7280 0x7900 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x7100 0x7580 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x6B00 0x7300 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x6B80 0x8000 0x7B80 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x6D80 0x8000 0x7C00 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x7000 0x8000 0x7C80 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x7280 0x8000 0x7D00 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x7580 0x8000 0x7D80 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x7800 0x8000 0x7E00 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7B00 0x8000 0x7E80 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x7D80 0x8000 0x7F00 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x8000 0x8000 0x7F80 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7A80 0x7D80 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x7900 0x7B80 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x7380 0x7900 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x7200 0x7600 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x7080 0x7380 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x6B00 0x7080 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x6A80 0x8000 0x7980 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x6D00 0x8000 0x7A00 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x6F80 0x8000 0x7A80 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x7200 0x8000 0x7B00 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x7500 0x8000 0x7B80 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x7800 0x8000 0x7C00 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7A80 0x8000 0x7C80 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x7D00 0x8000 0x7D00 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x8000 0x8000 0x7D80 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7A80 0x7B80 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x7900 0x7980 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7380 0x7680 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x7200 0x7400 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x7080 0x7180 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x6A80 0x6E80 + 0x00 0x88 0x32 0x34 0x17 0x19 0x6A00 0x8000 0x7700 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x6C00 0x8000 0x7780 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x6F00 0x8000 0x7800 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x7180 0x8000 0x7900 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x7480 0x8000 0x7980 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x7780 0x8000 0x7A00 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7A80 0x8000 0x7A80 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7D00 0x8000 0x7B00 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x8000 0x8000 0x7B80 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x7A80 0x7980 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x7900 0x7780 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7380 0x7480 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x7200 0x7200 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x7080 0x6F80 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x6A80 0x6C80 + 0x00 0x97 0x32 0x34 0x14 0x16 0x6900 0x8000 0x7400 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x6C00 0x8000 0x7500 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x6E80 0x8000 0x7580 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x7100 0x8000 0x7600 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x7400 0x8000 0x7700 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x7700 0x8000 0x7800 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7A00 0x8000 0x7880 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7C80 0x8000 0x7900 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x8000 0x8000 0x7980 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x7B00 0x7780 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x7900 0x7500 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7380 0x7300 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x7200 0x7080 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x7000 0x6D80 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x6A00 0x6B00 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x6880 0x8000 0x7200 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x6B80 0x8000 0x7300 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x6E00 0x8000 0x7380 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x7080 0x8000 0x7400 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x7380 0x8000 0x7500 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x7680 0x8000 0x7580 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x7A00 0x8000 0x7600 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7C80 0x8000 0x7700 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7F80 0x8000 0x7780 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x7A80 0x7580 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x7900 0x7380 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7380 0x7100 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x7180 0x6E80 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x7000 0x6C00 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x6A00 0x6980 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x6780 0x8000 0x7000 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x6A80 0x8000 0x7100 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x6D00 0x8000 0x7180 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x7000 0x8000 0x7280 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x7300 0x8000 0x7300 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x7680 0x8000 0x7380 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x7980 0x8000 0x7400 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7C00 0x8000 0x7480 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7F80 0x8000 0x7580 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x7A80 0x7400 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x7900 0x7180 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7380 0x6F80 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x7180 0x6D00 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x7000 0x6A80 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x6980 0x6800 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x6700 0x8000 0x6E00 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x6A00 0x8000 0x6F00 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x6C80 0x8000 0x6F80 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x6F80 0x8000 0x7080 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x7300 0x8000 0x7100 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x7600 0x8000 0x7200 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x7900 0x8000 0x7280 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7C00 0x8000 0x7300 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7F80 0x8000 0x7380 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x7A80 0x7280 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x7900 0x7000 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7380 0x6E00 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x7180 0x6B80 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x6B80 0x6900 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x6980 0x6700 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x6600 0x8000 0x6C80 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x6900 0x8000 0x6D00 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x6C00 0x8000 0x6E00 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x6F00 0x8000 0x6E80 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x7280 0x8000 0x6F80 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x7580 0x8000 0x7000 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x7900 0x8000 0x7100 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7B80 0x8000 0x7180 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7F00 0x8000 0x7200 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x8000 0x7A80 0x7100 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x7900 0x6E80 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7380 0x6C80 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x7180 0x6A80 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x6B80 0x6780 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x6980 0x6580 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + + somc,mdss-dsi-hdr-pcc-enable; + somc,mdss-dsi-hdr-pcc-table-size = <226>; + somc,mdss-dsi-hdr-pcc-table = < + 0x00 0x01 0x32 0x34 0x32 0x34 0x5A00 0x6B00 0x8000 + 0x00 0x02 0x2F 0x31 0x32 0x34 0x5C80 0x6B00 0x8000 + 0x00 0x03 0x2C 0x2E 0x32 0x34 0x5E00 0x6B00 0x8000 + 0x00 0x04 0x29 0x2B 0x32 0x34 0x6080 0x6B00 0x8000 + 0x00 0x05 0x26 0x28 0x32 0x34 0x6280 0x6B00 0x8000 + 0x00 0x06 0x23 0x25 0x32 0x34 0x6480 0x6A80 0x8000 + 0x00 0x07 0x20 0x22 0x32 0x34 0x6680 0x6A80 0x8000 + 0x00 0x08 0x1D 0x1F 0x32 0x34 0x6880 0x6A80 0x8000 + 0x00 0x09 0x1A 0x1C 0x32 0x34 0x6B00 0x6A80 0x8000 + 0x00 0x0A 0x17 0x19 0x32 0x34 0x6D80 0x6A80 0x8000 + 0x00 0x0B 0x14 0x16 0x32 0x34 0x6F80 0x6A80 0x8000 + 0x00 0x0C 0x11 0x13 0x32 0x34 0x7280 0x6A00 0x8000 + 0x00 0x0D 0x0E 0x10 0x32 0x34 0x7580 0x6A00 0x8000 + 0x00 0x0E 0x0B 0x0D 0x32 0x34 0x7800 0x6A00 0x8000 + 0x00 0x0F 0x08 0x0A 0x32 0x34 0x7B00 0x6A00 0x8000 + 0x00 0x10 0x32 0x34 0x2F 0x31 0x5E00 0x7100 0x8000 + 0x00 0x11 0x2F 0x31 0x2F 0x31 0x6080 0x7080 0x8000 + 0x00 0x12 0x2C 0x2E 0x2F 0x31 0x6200 0x7080 0x8000 + 0x00 0x13 0x29 0x2B 0x2F 0x31 0x6400 0x7080 0x8000 + 0x00 0x14 0x26 0x28 0x2F 0x31 0x6600 0x7080 0x8000 + 0x00 0x15 0x23 0x25 0x2F 0x31 0x6800 0x7000 0x8000 + 0x00 0x16 0x20 0x22 0x2F 0x31 0x6A00 0x7000 0x8000 + 0x00 0x17 0x1D 0x1F 0x2F 0x31 0x6C00 0x7000 0x8000 + 0x00 0x18 0x1A 0x1C 0x2F 0x31 0x6E00 0x7000 0x8000 + 0x00 0x19 0x17 0x19 0x2F 0x31 0x7080 0x6B80 0x8000 + 0x00 0x1A 0x14 0x16 0x2F 0x31 0x7300 0x6B80 0x8000 + 0x00 0x1B 0x11 0x13 0x2F 0x31 0x7600 0x6B80 0x8000 + 0x00 0x1C 0x0E 0x10 0x2F 0x31 0x7880 0x6B80 0x8000 + 0x00 0x1D 0x0B 0x0D 0x2F 0x31 0x7B00 0x6B80 0x8000 + 0x00 0x1E 0x08 0x0A 0x2F 0x31 0x7E80 0x6B80 0x8000 + 0x00 0x1F 0x32 0x34 0x2C 0x2E 0x6180 0x7280 0x8000 + 0x00 0x20 0x2F 0x31 0x2C 0x2E 0x6300 0x7200 0x8000 + 0x00 0x21 0x2C 0x2E 0x2C 0x2E 0x6500 0x7200 0x8000 + 0x00 0x22 0x29 0x2B 0x2C 0x2E 0x6700 0x7200 0x8000 + 0x00 0x23 0x26 0x28 0x2C 0x2E 0x6880 0x7200 0x8000 + 0x00 0x24 0x23 0x25 0x2C 0x2E 0x6B00 0x7180 0x8000 + 0x00 0x25 0x20 0x22 0x2C 0x2E 0x6D00 0x7180 0x8000 + 0x00 0x26 0x1D 0x1F 0x2C 0x2E 0x6F00 0x7180 0x8000 + 0x00 0x27 0x1A 0x1C 0x2C 0x2E 0x7180 0x7100 0x8000 + 0x00 0x28 0x17 0x19 0x2C 0x2E 0x7400 0x7100 0x8000 + 0x00 0x29 0x14 0x16 0x2C 0x2E 0x7680 0x7100 0x8000 + 0x00 0x2A 0x11 0x13 0x2C 0x2E 0x7900 0x7100 0x8000 + 0x00 0x2B 0x0E 0x10 0x2C 0x2E 0x7B80 0x7080 0x8000 + 0x00 0x2C 0x0B 0x0D 0x2C 0x2E 0x7F00 0x7080 0x8000 + 0x00 0x2D 0x08 0x0A 0x2C 0x2E 0x8000 0x6B80 0x7E00 + 0x00 0x2E 0x32 0x34 0x29 0x2B 0x6480 0x7380 0x8000 + 0x00 0x2F 0x2F 0x31 0x29 0x2B 0x6680 0x7380 0x8000 + 0x00 0x30 0x2C 0x2E 0x29 0x2B 0x6880 0x7380 0x8000 + 0x00 0x31 0x29 0x2B 0x29 0x2B 0x6A00 0x7300 0x8000 + 0x00 0x32 0x26 0x28 0x29 0x2B 0x6C00 0x7300 0x8000 + 0x00 0x33 0x23 0x25 0x29 0x2B 0x6D80 0x7300 0x8000 + 0x00 0x34 0x20 0x22 0x29 0x2B 0x7000 0x7280 0x8000 + 0x00 0x35 0x1D 0x1F 0x29 0x2B 0x7200 0x7280 0x8000 + 0x00 0x36 0x1A 0x1C 0x29 0x2B 0x7480 0x7280 0x8000 + 0x00 0x37 0x17 0x19 0x29 0x2B 0x7700 0x7200 0x8000 + 0x00 0x38 0x14 0x16 0x29 0x2B 0x7980 0x7200 0x8000 + 0x00 0x39 0x11 0x13 0x29 0x2B 0x7C00 0x7200 0x8000 + 0x00 0x3A 0x0E 0x10 0x29 0x2B 0x7F00 0x7200 0x8000 + 0x00 0x3B 0x0B 0x0D 0x29 0x2B 0x8000 0x7100 0x7E00 + 0x00 0x3C 0x08 0x0A 0x29 0x2B 0x8000 0x6B80 0x7A80 + 0x00 0x3D 0x32 0x34 0x26 0x28 0x6780 0x7980 0x8000 + 0x00 0x3E 0x2F 0x31 0x26 0x28 0x6900 0x7900 0x8000 + 0x00 0x3F 0x2C 0x2E 0x26 0x28 0x6B00 0x7900 0x8000 + 0x00 0x40 0x29 0x2B 0x26 0x28 0x6D00 0x7900 0x8000 + 0x00 0x41 0x26 0x28 0x26 0x28 0x6E80 0x7880 0x8000 + 0x00 0x42 0x23 0x25 0x26 0x28 0x7080 0x7880 0x8000 + 0x00 0x43 0x20 0x22 0x26 0x28 0x7300 0x7800 0x8000 + 0x00 0x44 0x1D 0x1F 0x26 0x28 0x7500 0x7380 0x8000 + 0x00 0x45 0x1A 0x1C 0x26 0x28 0x7780 0x7380 0x8000 + 0x00 0x46 0x17 0x19 0x26 0x28 0x7980 0x7380 0x8000 + 0x00 0x47 0x14 0x16 0x26 0x28 0x7C00 0x7300 0x8000 + 0x00 0x48 0x11 0x13 0x26 0x28 0x8000 0x7300 0x8000 + 0x00 0x49 0x0E 0x10 0x26 0x28 0x8000 0x7200 0x7D80 + 0x00 0x4A 0x0B 0x0D 0x26 0x28 0x8000 0x7080 0x7A80 + 0x00 0x4B 0x08 0x0A 0x26 0x28 0x8000 0x6B00 0x7800 + 0x00 0x4C 0x32 0x34 0x23 0x25 0x6A00 0x7B00 0x8000 + 0x00 0x4D 0x2F 0x31 0x23 0x25 0x6B80 0x7B00 0x8000 + 0x00 0x4E 0x2C 0x2E 0x23 0x25 0x6D80 0x7A80 0x8000 + 0x00 0x4F 0x29 0x2B 0x23 0x25 0x6F00 0x7A80 0x8000 + 0x00 0x50 0x26 0x28 0x23 0x25 0x7180 0x7A00 0x8000 + 0x00 0x51 0x23 0x25 0x23 0x25 0x7380 0x7980 0x8000 + 0x00 0x52 0x20 0x22 0x23 0x25 0x7580 0x7980 0x8000 + 0x00 0x53 0x1D 0x1F 0x23 0x25 0x7800 0x7980 0x8000 + 0x00 0x54 0x1A 0x1C 0x23 0x25 0x7A00 0x7900 0x8000 + 0x00 0x55 0x17 0x19 0x23 0x25 0x7C80 0x7900 0x8000 + 0x00 0x56 0x14 0x16 0x23 0x25 0x8000 0x7880 0x8000 + 0x00 0x57 0x11 0x13 0x23 0x25 0x8000 0x7300 0x7D80 + 0x00 0x58 0x0E 0x10 0x23 0x25 0x8000 0x7200 0x7A80 + 0x00 0x59 0x0B 0x0D 0x23 0x25 0x8000 0x7080 0x7800 + 0x00 0x5A 0x08 0x0A 0x23 0x25 0x8000 0x6B00 0x7580 + 0x00 0x5B 0x32 0x34 0x20 0x22 0x6B00 0x8000 0x7F00 + 0x00 0x5C 0x2F 0x31 0x20 0x22 0x6D80 0x8000 0x7F80 + 0x00 0x5D 0x2C 0x2E 0x20 0x22 0x6F80 0x8000 0x8000 + 0x00 0x5E 0x29 0x2B 0x20 0x22 0x7200 0x8000 0x8000 + 0x00 0x5F 0x26 0x28 0x20 0x22 0x7400 0x7B80 0x8000 + 0x00 0x60 0x23 0x25 0x20 0x22 0x7600 0x7B00 0x8000 + 0x00 0x61 0x20 0x22 0x20 0x22 0x7880 0x7B00 0x8000 + 0x00 0x62 0x1D 0x1F 0x20 0x22 0x7A00 0x7B00 0x8000 + 0x00 0x63 0x1A 0x1C 0x20 0x22 0x7C80 0x7A80 0x8000 + 0x00 0x64 0x17 0x19 0x20 0x22 0x8000 0x7A80 0x8000 + 0x00 0x65 0x14 0x16 0x20 0x22 0x8000 0x7900 0x7D80 + 0x00 0x66 0x11 0x13 0x20 0x22 0x8000 0x7300 0x7B00 + 0x00 0x67 0x0E 0x10 0x20 0x22 0x8000 0x7200 0x7880 + 0x00 0x68 0x0B 0x0D 0x20 0x22 0x8000 0x7080 0x7580 + 0x00 0x69 0x08 0x0A 0x20 0x22 0x8000 0x6A80 0x7280 + 0x00 0x6A 0x32 0x34 0x1D 0x1F 0x6A80 0x8000 0x7B80 + 0x00 0x6B 0x2F 0x31 0x1D 0x1F 0x6D00 0x8000 0x7C00 + 0x00 0x6C 0x2C 0x2E 0x1D 0x1F 0x6F00 0x8000 0x7C80 + 0x00 0x6D 0x29 0x2B 0x1D 0x1F 0x7180 0x8000 0x7D80 + 0x00 0x6E 0x26 0x28 0x1D 0x1F 0x7480 0x8000 0x7E00 + 0x00 0x6F 0x23 0x25 0x1D 0x1F 0x7700 0x8000 0x7E80 + 0x00 0x70 0x20 0x22 0x1D 0x1F 0x7980 0x8000 0x7F80 + 0x00 0x71 0x1D 0x1F 0x1D 0x1F 0x7C80 0x8000 0x8000 + 0x00 0x72 0x1A 0x1C 0x1D 0x1F 0x8000 0x8000 0x8000 + 0x00 0x73 0x17 0x19 0x1D 0x1F 0x8000 0x7A80 0x7D80 + 0x00 0x74 0x14 0x16 0x1D 0x1F 0x8000 0x7900 0x7B00 + 0x00 0x75 0x11 0x13 0x1D 0x1F 0x8000 0x7300 0x7880 + 0x00 0x76 0x0E 0x10 0x1D 0x1F 0x8000 0x7200 0x7600 + 0x00 0x77 0x0B 0x0D 0x1D 0x1F 0x8000 0x7000 0x7300 + 0x00 0x78 0x08 0x0A 0x1D 0x1F 0x8000 0x6A80 0x7000 + 0x00 0x79 0x32 0x34 0x1A 0x1C 0x6980 0x8000 0x7900 + 0x00 0x7A 0x2F 0x31 0x1A 0x1C 0x6C00 0x8000 0x7980 + 0x00 0x7B 0x2C 0x2E 0x1A 0x1C 0x6E80 0x8000 0x7A00 + 0x00 0x7C 0x29 0x2B 0x1A 0x1C 0x7100 0x8000 0x7A80 + 0x00 0x7D 0x26 0x28 0x1A 0x1C 0x7400 0x8000 0x7B00 + 0x00 0x7E 0x23 0x25 0x1A 0x1C 0x7700 0x8000 0x7B80 + 0x00 0x7F 0x20 0x22 0x1A 0x1C 0x7980 0x8000 0x7C00 + 0x00 0x80 0x1D 0x1F 0x1A 0x1C 0x7C00 0x8000 0x7C80 + 0x00 0x81 0x1A 0x1C 0x1A 0x1C 0x8000 0x8000 0x7D80 + 0x00 0x82 0x17 0x19 0x1A 0x1C 0x8000 0x7A80 0x7B00 + 0x00 0x83 0x14 0x16 0x1A 0x1C 0x8000 0x7900 0x7900 + 0x00 0x84 0x11 0x13 0x1A 0x1C 0x8000 0x7300 0x7680 + 0x00 0x85 0x0E 0x10 0x1A 0x1C 0x8000 0x7180 0x7380 + 0x00 0x86 0x0B 0x0D 0x1A 0x1C 0x8000 0x7000 0x7100 + 0x00 0x87 0x08 0x0A 0x1A 0x1C 0x8000 0x6A00 0x6E80 + 0x00 0x88 0x32 0x34 0x17 0x19 0x6900 0x8000 0x7680 + 0x00 0x89 0x2F 0x31 0x17 0x19 0x6B80 0x8000 0x7700 + 0x00 0x8A 0x2C 0x2E 0x17 0x19 0x6E00 0x8000 0x7780 + 0x00 0x8B 0x29 0x2B 0x17 0x19 0x7080 0x8000 0x7880 + 0x00 0x8C 0x26 0x28 0x17 0x19 0x7380 0x8000 0x7900 + 0x00 0x8D 0x23 0x25 0x17 0x19 0x7680 0x8000 0x7980 + 0x00 0x8E 0x20 0x22 0x17 0x19 0x7980 0x8000 0x7A00 + 0x00 0x8F 0x1D 0x1F 0x17 0x19 0x7C00 0x8000 0x7A80 + 0x00 0x90 0x1A 0x1C 0x17 0x19 0x8000 0x8000 0x7B00 + 0x00 0x91 0x17 0x19 0x17 0x19 0x8000 0x7A80 0x7900 + 0x00 0x92 0x14 0x16 0x17 0x19 0x8000 0x7900 0x7700 + 0x00 0x93 0x11 0x13 0x17 0x19 0x8000 0x7300 0x7400 + 0x00 0x94 0x0E 0x10 0x17 0x19 0x8000 0x7180 0x7180 + 0x00 0x95 0x0B 0x0D 0x17 0x19 0x8000 0x7000 0x6F00 + 0x00 0x96 0x08 0x0A 0x17 0x19 0x8000 0x6A00 0x6C80 + 0x00 0x97 0x32 0x34 0x14 0x16 0x6880 0x8000 0x7380 + 0x00 0x98 0x2F 0x31 0x14 0x16 0x6B00 0x8000 0x7500 + 0x00 0x99 0x2C 0x2E 0x14 0x16 0x6D80 0x8000 0x7580 + 0x00 0x9A 0x29 0x2B 0x14 0x16 0x7000 0x8000 0x7600 + 0x00 0x9B 0x26 0x28 0x14 0x16 0x7300 0x8000 0x7680 + 0x00 0x9C 0x23 0x25 0x14 0x16 0x7600 0x8000 0x7780 + 0x00 0x9D 0x20 0x22 0x14 0x16 0x7900 0x8000 0x7800 + 0x00 0x9E 0x1D 0x1F 0x14 0x16 0x7B80 0x8000 0x7880 + 0x00 0x9F 0x1A 0x1C 0x14 0x16 0x8000 0x8000 0x7900 + 0x00 0xA0 0x17 0x19 0x14 0x16 0x8000 0x7A80 0x7700 + 0x00 0xA1 0x14 0x16 0x14 0x16 0x8000 0x7900 0x7500 + 0x00 0xA2 0x11 0x13 0x14 0x16 0x8000 0x7300 0x7200 + 0x00 0xA3 0x0E 0x10 0x14 0x16 0x8000 0x7180 0x6F80 + 0x00 0xA4 0x0B 0x0D 0x14 0x16 0x8000 0x6B80 0x6D80 + 0x00 0xA5 0x08 0x0A 0x14 0x16 0x8000 0x6980 0x6A80 + 0x00 0xA6 0x32 0x34 0x11 0x13 0x6800 0x8000 0x7180 + 0x00 0xA7 0x2F 0x31 0x11 0x13 0x6A00 0x8000 0x7200 + 0x00 0xA8 0x2C 0x2E 0x11 0x13 0x6D00 0x8000 0x7300 + 0x00 0xA9 0x29 0x2B 0x11 0x13 0x6F80 0x8000 0x7380 + 0x00 0xAA 0x26 0x28 0x11 0x13 0x7280 0x8000 0x7400 + 0x00 0xAB 0x23 0x25 0x11 0x13 0x7580 0x8000 0x7580 + 0x00 0xAC 0x20 0x22 0x11 0x13 0x7900 0x8000 0x7600 + 0x00 0xAD 0x1D 0x1F 0x11 0x13 0x7B80 0x8000 0x7680 + 0x00 0xAE 0x1A 0x1C 0x11 0x13 0x7F00 0x8000 0x7700 + 0x00 0xAF 0x17 0x19 0x11 0x13 0x8000 0x7A80 0x7580 + 0x00 0xB0 0x14 0x16 0x11 0x13 0x8000 0x7900 0x7280 + 0x00 0xB1 0x11 0x13 0x11 0x13 0x8000 0x7300 0x7080 + 0x00 0xB2 0x0E 0x10 0x11 0x13 0x8000 0x7180 0x6E80 + 0x00 0xB3 0x0B 0x0D 0x11 0x13 0x8000 0x6B80 0x6B80 + 0x00 0xB4 0x08 0x0A 0x11 0x13 0x8000 0x6980 0x6900 + 0x00 0xB5 0x32 0x34 0x0E 0x10 0x6700 0x8000 0x6F80 + 0x00 0xB6 0x2F 0x31 0x0E 0x10 0x6980 0x8000 0x7000 + 0x00 0xB7 0x2C 0x2E 0x0E 0x10 0x6C80 0x8000 0x7100 + 0x00 0xB8 0x29 0x2B 0x0E 0x10 0x6F00 0x8000 0x7180 + 0x00 0xB9 0x26 0x28 0x0E 0x10 0x7200 0x8000 0x7280 + 0x00 0xBA 0x23 0x25 0x0E 0x10 0x7580 0x8000 0x7300 + 0x00 0xBB 0x20 0x22 0x0E 0x10 0x7880 0x8000 0x7380 + 0x00 0xBC 0x1D 0x1F 0x0E 0x10 0x7B00 0x8000 0x7400 + 0x00 0xBD 0x1A 0x1C 0x0E 0x10 0x7F00 0x8000 0x7500 + 0x00 0xBE 0x17 0x19 0x0E 0x10 0x8000 0x7A80 0x7300 + 0x00 0xBF 0x14 0x16 0x0E 0x10 0x8000 0x7880 0x7100 + 0x00 0xC0 0x11 0x13 0x0E 0x10 0x8000 0x7300 0x6E80 + 0x00 0xC1 0x0E 0x10 0x0E 0x10 0x8000 0x7180 0x6C80 + 0x00 0xC2 0x0B 0x0D 0x0E 0x10 0x8000 0x6B80 0x6A00 + 0x00 0xC3 0x08 0x0A 0x0E 0x10 0x8000 0x6980 0x6780 + 0x00 0xC4 0x32 0x34 0x0B 0x0D 0x6600 0x8000 0x6E00 + 0x00 0xC5 0x2F 0x31 0x0B 0x0D 0x6880 0x8000 0x6E00 + 0x00 0xC6 0x2C 0x2E 0x0B 0x0D 0x6C00 0x8000 0x6F00 + 0x00 0xC7 0x29 0x2B 0x0B 0x0D 0x6E80 0x8000 0x6F80 + 0x00 0xC8 0x26 0x28 0x0B 0x0D 0x7180 0x8000 0x7080 + 0x00 0xC9 0x23 0x25 0x0B 0x0D 0x7500 0x8000 0x7100 + 0x00 0xCA 0x20 0x22 0x0B 0x0D 0x7800 0x8000 0x7200 + 0x00 0xCB 0x1D 0x1F 0x0B 0x0D 0x7B00 0x8000 0x7280 + 0x00 0xCC 0x1A 0x1C 0x0B 0x0D 0x7F00 0x8000 0x7300 + 0x00 0xCD 0x17 0x19 0x0B 0x0D 0x8000 0x7A80 0x7180 + 0x00 0xCE 0x14 0x16 0x0B 0x0D 0x8000 0x7880 0x6F80 + 0x00 0xCF 0x11 0x13 0x0B 0x0D 0x8000 0x7300 0x6D80 + 0x00 0xD0 0x0E 0x10 0x0B 0x0D 0x8000 0x7100 0x6B00 + 0x00 0xD1 0x0B 0x0D 0x0B 0x0D 0x8000 0x6B00 0x6900 + 0x00 0xD2 0x08 0x0A 0x0B 0x0D 0x8000 0x6900 0x6600 + 0x00 0xD3 0x32 0x34 0x08 0x0A 0x6580 0x8000 0x6C00 + 0x00 0xD4 0x2F 0x31 0x08 0x0A 0x6880 0x8000 0x6D00 + 0x00 0xD5 0x2C 0x2E 0x08 0x0A 0x6B00 0x8000 0x6D80 + 0x00 0xD6 0x29 0x2B 0x08 0x0A 0x6E00 0x8000 0x6E00 + 0x00 0xD7 0x26 0x28 0x08 0x0A 0x7100 0x8000 0x6E80 + 0x00 0xD8 0x23 0x25 0x08 0x0A 0x7480 0x8000 0x6F80 + 0x00 0xD9 0x20 0x22 0x08 0x0A 0x7800 0x8000 0x7000 + 0x00 0xDA 0x1D 0x1F 0x08 0x0A 0x7A80 0x8000 0x7080 + 0x00 0xDB 0x1A 0x1C 0x08 0x0A 0x7E80 0x8000 0x7180 + 0x00 0xDC 0x17 0x19 0x08 0x0A 0x8000 0x7A80 0x7000 + 0x00 0xDD 0x14 0x16 0x08 0x0A 0x8000 0x7880 0x6E00 + 0x00 0xDE 0x11 0x13 0x08 0x0A 0x8000 0x7300 0x6C00 + 0x00 0xDF 0x0E 0x10 0x08 0x0A 0x8000 0x7100 0x6980 + 0x00 0xE0 0x0B 0x0D 0x08 0x0A 0x8000 0x6B00 0x6700 + 0x00 0xE1 0x08 0x0A 0x08 0x0A 0x8000 0x6900 0x6500 + 0xFF 0x00 0x0E 0x38 0x0C 0x2F 0x8000 0x8000 0x8000>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-poplar.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-poplar.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..116c41facd4510408cb7e7cd82f0d6174f6cb012 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-poplar.dtsi @@ -0,0 +1,415 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include "dsi-panel-poplar-id6_pcc.dtsi" +#include "dsi-panel-poplar-id9_pcc.dtsi" + +&mdss_mdp { + /* JDI ID6 */ + dsi_6: somc,6_panel { + qcom,mdss-dsi-panel-name = "6"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-h-back-porch = <8>; + qcom,mdss-dsi-h-pulse-width = <8>; + qcom,mdss-dsi-h-front-porch = <56>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <8>; + qcom,mdss-dsi-v-front-porch = <227>; + qcom,mdss-pan-physical-width-dimension = <64>; + qcom,mdss-pan-physical-height-dimension = <114>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-on-command = [ + 29 01 00 00 00 00 02 B0 00 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 08 C2 01 07 80 04 00 04 F0 + 29 01 00 00 00 00 03 C4 70 22 + 29 01 00 00 00 00 15 C6 53 2E 2E 05 45 00 00 00 00 00 00 42 0F 00 00 00 00 04 10 06 + 29 01 00 00 00 00 0E EC 64 DC EC 3B 52 00 0B 0B 13 15 68 0B B5 + 29 01 00 00 00 00 02 B0 03 + 39 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 02 36 00 + 39 01 00 00 00 00 02 3A 77 + 39 01 00 00 00 00 05 2A 00 00 04 37 + 39 01 00 00 00 00 05 2B 00 00 07 7F + 39 01 00 00 00 00 03 44 00 00 + 39 01 00 00 78 00 01 11]; + qcom,mdss-dsi-post-panel-on-command = [ + 39 01 00 00 00 00 01 29]; + qcom,mdss-dsi-off-command = [ + 39 01 00 00 14 00 01 28 + 29 01 00 00 00 00 02 B0 00 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 0E EC 64 DC EC 3B 52 00 0B 0B 13 15 68 0B 95 + 29 01 00 00 00 00 02 B0 03 + 39 01 00 00 78 00 01 10]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_hybrid_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + + qcom,mdss-dsi-panel-clockrate = <899000000>; + + somc,lcd-id-adc = <565000 653000>; + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 15>, <1 16>, <0 15>, <1 20>; + somc,pw-off-rst-b-seq = <0 11>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <1>; + somc,pw-wait-after-on-vsp = <1>; + somc,pw-wait-after-on-vsn = <0>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <1>; + somc,pw-wait-after-off-vsp = <8>; + somc,pw-wait-after-off-vsn = <8>; + somc,pw-wait-after-on-touch-avdd = <1>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-avdd = <0>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <11>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <300>; + + somc,lab-output-voltage = <5600000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,ewu-wait-after-touch-reset = <40>; + + somc,change-fps-enable; + somc,change-fps-panel-type = "hybrid_incell_type"; + somc,change-fps-panel-mode = "dynamic_mode"; + somc,change-fps-command = + [29 01 00 00 00 00 02 B0 04 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 08 C2 01 07 80 04 00 04 F0]; + somc,driver-ic-rtn = <83>; + somc,driver-ic-vdisp = <1920>; + somc,driver-ic-vtouch = <6818000>; + somc,driver-ic-mclk = <61539>; + somc,change-fps-send-pos = <2 4>; + somc,change-fps-send-byte = <4>; + somc,change-fps-porch-mask-pos = <3>; + somc,change-fps-porch-mask = <0xF0>; + somc,change-fps-porch-range = <4 511>; + + somc,mdss-dsi-disp-on-in-hs = <0>; + somc,mdss-dsi-wait-time-before-post-on-cmd = <0>; + }; + + /* Sharp ID9 */ + dsi_9: somc,9_panel { + qcom,mdss-dsi-panel-name = "9"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-h-back-porch = <8>; + qcom,mdss-dsi-h-pulse-width = <8>; + qcom,mdss-dsi-h-front-porch = <56>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <8>; + qcom,mdss-dsi-v-front-porch = <227>; + qcom,mdss-pan-physical-width-dimension = <64>; + qcom,mdss-pan-physical-height-dimension = <114>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-on-command = [ + 39 01 00 00 00 00 02 35 00 + 05 01 00 00 00 00 01 29 + ]; + qcom,mdss-dsi-post-panel-on-command = [ + 05 01 00 00 00 00 01 11 + ]; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 00 00 01 28 + 05 01 00 00 78 00 01 10]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_full_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + + qcom,mdss-dsi-panel-clockrate = <899000000>; + + somc,lcd-id-adc = <0 57000>; + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 15>, <1 20>; + somc,pw-off-rst-b-seq = <0 11>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <10>; + somc,pw-wait-after-on-vsp = <10>; + somc,pw-wait-after-on-vsn = <0>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <0>; + somc,pw-wait-after-off-vsp = <8>; + somc,pw-wait-after-off-vsn = <8>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <11>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <300>; + + somc,lab-output-voltage = <5600000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,ewu-rst-seq = <0 2>, <1 5>; + somc,ewu-wait-after-touch-reset = <0>; + + somc,change-fps-enable; + somc,change-fps-panel-type = "full_incell_type"; + somc,change-fps-panel-mode = "dynamic_mode"; + somc,change-fps-command = + [29 01 00 00 00 00 02 B0 04 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 02 C6 59]; + somc,driver-ic-total-porch = <11>; + somc,driver-ic-vdisp = <1920>; + somc,driver-ic-rclk = <14000000>; + somc,driver-ic-vtp = <682>; + somc,change-fps-rtn-pos = <2 1>; + }; + + /* Default */ + dsi_default_panel: somc,default_cmd_panel { + qcom,mdss-dsi-panel-name = "default"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-h-back-porch = <8>; + qcom,mdss-dsi-h-pulse-width = <8>; + qcom,mdss-dsi-h-front-porch = <56>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-pulse-width = <8>; + qcom,mdss-dsi-v-front-porch = <227>; + qcom,mdss-pan-physical-width-dimension = <64>; + qcom,mdss-pan-physical-height-dimension = <114>; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-underflow-color = <0x0>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-on-command = [ + 29 01 00 00 00 00 02 B0 00 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 08 C2 01 07 80 04 00 04 F0 + 29 01 00 00 00 00 03 C4 70 22 + 29 01 00 00 00 00 15 C6 53 2E 2E 05 45 00 00 00 00 00 00 42 0F 00 00 00 00 04 10 06 + 29 01 00 00 00 00 0E EC 64 DC EC 3B 52 00 0B 0B 13 15 68 0B B5 + 29 01 00 00 00 00 02 B0 03 + 39 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 02 36 00 + 39 01 00 00 00 00 02 3A 77 + 39 01 00 00 00 00 05 2A 00 00 04 37 + 39 01 00 00 00 00 05 2B 00 00 07 7F + 39 01 00 00 00 00 03 44 00 00 + 39 01 00 00 78 00 01 11]; + qcom,mdss-dsi-post-panel-on-command = [ + 39 01 00 00 00 00 01 29]; + qcom,mdss-dsi-off-command = [ + 39 01 00 00 14 00 01 28 + 29 01 00 00 00 00 02 B0 00 + 29 01 00 00 00 00 02 D6 01 + 29 01 00 00 00 00 0E EC 64 DC EC 3B 52 00 0B 0B 13 15 68 0B 95 + 29 01 00 00 00 00 02 B0 03 + 39 01 00 00 78 00 01 10]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-wr-mem-start = <0x2c>; + qcom,mdss-dsi-wr-mem-continue = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1B 06 06 0B 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_hybrid_incell>; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-brightness-max-level = <4095>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + + qcom,mdss-dsi-panel-hdr-enabled; + qcom,mdss-dsi-panel-hdr-color-primaries = <14940 15790 33800 15700 10950 33800 7450 2300>; + qcom,mdss-dsi-panel-peak-brightness = <7000000>; + qcom,mdss-dsi-panel-blackness-level = <4646>; + + qcom,mdss-dsi-panel-clockrate = <899000000>; + + somc,lcd-id-adc = <0 0x7fffffff>; + somc,mdss-dsi-master; + somc,pw-on-rst-seq = <0 15>, <1 16>, <0 15>, <1 20>; + somc,pw-off-rst-b-seq = <0 11>; + somc,pw-wait-after-on-vdd = <0>; + somc,pw-wait-after-on-vddio = <1>; + somc,pw-wait-after-on-vsp = <1>; + somc,pw-wait-after-on-vsn = <0>; + somc,pw-wait-after-off-vdd = <0>; + somc,pw-wait-after-off-vddio = <1>; + somc,pw-wait-after-off-vsp = <8>; + somc,pw-wait-after-off-vsn = <8>; + somc,pw-wait-after-on-touch-avdd = <1>; + somc,pw-wait-after-on-touch-vddio = <0>; + somc,pw-wait-after-on-touch-reset = <0>; + somc,pw-wait-after-on-touch-int-n = <10>; + somc,pw-wait-after-off-touch-avdd = <0>; + somc,pw-wait-after-off-touch-vddio = <0>; + somc,pw-wait-after-off-touch-reset = <11>; + somc,pw-wait-after-off-touch-int-n = <0>; + somc,pw-down-period = <300>; + + somc,lab-output-voltage = <5600000>; + somc,ibb-output-voltage = <5600000>; + somc,qpnp-lab-limit-maximum-current = <200>; + somc,qpnp-ibb-limit-maximum-current = <800>; + somc,qpnp-lab-max-precharge-time = <500>; + somc,qpnp-lab-soft-start = <800>; + somc,qpnp-ibb-discharge-resistor = <300>; + somc,qpnp-lab-pull-down-enable; + somc,qpnp-lab-full-pull-down; + somc,qpnp-ibb-pull-down-enable; + somc,qpnp-ibb-full-pull-down; + + somc,ewu-wait-after-touch-reset = <40>; + + somc,mdss-dsi-disp-on-in-hs = <0>; + somc,mdss-dsi-wait-time-before-post-on-cmd = <0>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4245mv-2425mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4245mv-2425mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..e05d0693a1343ac37906cdd003d339f68ed6276a --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4245mv-2425mah.dtsi @@ -0,0 +1,88 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2018 Sony Mobile Communications 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. + */ + +qcom,send_2425mah { + qcom, = <24>; + qcom,max-voltage-uv = <4245000>; + qcom,fg-cc-cv-threshold-mv = <4235>; + qcom,fastchg-current-ma = <2025>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,battery-type = "1308-1851-4"; + qcom,checksum = <0xB17B>; + qcom,gui-version = "PMI8998GUI - 2.0.0.58"; + qcom,fg-profile-data = [ + AB 20 BA 04 + A8 0A 3E 06 + 6F 1C 48 02 + CE 0D E5 03 + 2B 18 78 22 + 96 3C 4A 4B + 92 00 00 00 + 16 00 00 00 + 00 00 BE C2 + F9 D5 79 CA + 31 00 08 00 + E8 E5 F7 07 + A7 05 87 FA + 7F 05 4D 03 + 65 E5 B1 1A + 43 06 09 20 + 27 00 14 00 + E6 1F B3 05 + 09 0A B7 06 + 79 1C FC 02 + 99 0C 16 0B + 8D 18 AA 22 + E4 45 5A 52 + 7C 00 00 00 + 13 00 00 00 + 00 00 F6 07 + 34 C3 65 CB + 28 00 00 00 + 3A E3 F7 07 + FE 05 D9 F3 + E5 06 97 03 + 1A F4 E7 0B + 9F 33 CC FF + 07 10 00 00 + B2 09 EB 43 + 28 00 40 00 + 99 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4297mv-2539mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4297mv-2539mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..6fb34d0210f4248d722e3c993d34db7532f1e03e --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4297mv-2539mah.dtsi @@ -0,0 +1,88 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2018 Sony Mobile Communications 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. + */ + +qcom,send_2539mah { + qcom, = <24>; + qcom,max-voltage-uv = <4297000>; + qcom,fg-cc-cv-threshold-mv = <4287>; + qcom,fastchg-current-ma = <2025>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,battery-type = "1308-1851-3"; + qcom,checksum = <0x1D5A>; + qcom,gui-version = "PMI8998GUI - 2.0.0.58"; + qcom,fg-profile-data = [ + EC 1F 6C 05 + 4A 0A 23 FD + 6D 1C 6A 02 + 8A 0D 4D 0A + 10 18 A6 22 + 2A 3C DF 4B + 86 00 00 00 + 15 00 00 00 + 00 00 2D B3 + 33 CC 8C CB + 30 00 08 00 + A8 E5 2D CC + C4 05 51 FA + D6 05 3A 03 + DD FD A7 32 + 22 06 09 20 + 27 00 14 00 + BA 20 BB 04 + CC 0A 1D FC + 8C 1C D5 02 + DB 0C E2 0A + 85 18 CB 22 + B8 45 9D 52 + 83 00 00 00 + 12 00 00 00 + 00 00 47 CC + C4 C3 02 00 + 28 00 00 00 + 4E E3 2D CC + 17 FC A3 F3 + F2 06 5F 03 + A4 06 A2 12 + 9C 33 CC FF + 07 10 00 00 + 31 0A C0 44 + 28 00 40 00 + 93 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4335mv-2647mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4335mv-2647mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..56765d2e53b002a7e3d04654a96213333e9efd1a --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4335mv-2647mah.dtsi @@ -0,0 +1,87 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +qcom,send_2647mah { + qcom,max-voltage-uv = <4335000>; + qcom,fg-cc-cv-threshold-mv = <4325>; + qcom,fastchg-current-ma = <2025>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,battery-type = "1308-1851-2"; + qcom,checksum = <0xF252>; + qcom,gui-version = "PMI8998GUI - 2.0.0.55"; + qcom,fg-profile-data = [ + 5A 1F EE 05 + 0D 0A 7B FD + AD 1C 21 FB + E9 06 05 EC + 47 18 95 22 + 33 3C F5 4B + 53 00 00 00 + 0F 00 00 00 + 00 00 62 C2 + F8 07 D8 C2 + 2E 00 08 00 + F1 DC 1B DD + 5D FC 22 F3 + 81 06 AF 02 + 59 FC 13 3A + 18 06 09 20 + 27 00 14 00 + FF 1B 96 01 + 2A 07 96 F3 + 77 1C 25 03 + 50 0C 61 0B + 97 18 D5 22 + 96 45 F8 52 + 81 00 00 00 + 0D 00 00 00 + 00 00 29 C5 + B7 C3 B6 CD + 26 00 00 00 + 37 EA 1B DD + 9B FC C2 F2 + E1 FD 30 03 + 3B D4 8E 12 + 99 33 CC FF + 07 10 00 00 + 9D 0A 5C 45 + 26 00 40 00 + 89 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4357mv-2688mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4357mv-2688mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..65456441f01c2dd8c9d728614d875be2455ae5d0 --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4357mv-2688mah.dtsi @@ -0,0 +1,87 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +qcom,send_2688mah { + qcom,max-voltage-uv = <4357000>; + qcom,fg-cc-cv-threshold-mv = <4347>; + qcom,fastchg-current-ma = <2025>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,battery-type = "1308-1851-1"; + qcom,checksum = <0x98E0>; + qcom,gui-version = "PMI8998GUI - 2.0.0.55"; + qcom,fg-profile-data = [ + 80 1F B7 05 + 40 0A 8F 06 + A5 1C 67 FB + D3 FC D7 CC + 67 18 8C 22 + 3D 3C E3 4B + 56 00 00 00 + 0F 00 00 00 + 00 00 EC BA + 33 CC 17 CA + 2E 00 08 00 + 15 DC 79 DD + 3B 06 F7 F2 + FB FC 89 02 + 23 F3 B3 3B + 15 06 09 20 + 27 00 14 00 + A3 1B F9 01 + 53 FD 27 01 + 6E 1C 4D 03 + FE 15 D0 0B + 9E 18 C5 22 + B4 45 B9 52 + 7D 00 00 00 + 0D 00 00 00 + 00 00 29 C5 + 19 C3 B6 CD + 26 00 00 00 + 3F EA 79 DD + 81 FC F9 F2 + E2 06 06 03 + FB 07 B0 12 + 99 33 CC FF + 07 10 00 00 + CD 0A B6 45 + 26 00 40 00 + 81 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4380mv-2729mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4380mv-2729mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..15a1af1d3d6be6c5692527a1ea134fe52cf3fd79 --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-lilac-send-4380mv-2729mah.dtsi @@ -0,0 +1,87 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +qcom,send_2729mah { + qcom,max-voltage-uv = <4380000>; + qcom,fg-cc-cv-threshold-mv = <4370>; + qcom,fastchg-current-ma = <2025>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,battery-type = "1308-1851-0"; + qcom,checksum = <0x4442>; + qcom,gui-version = "PMI8998GUI - 2.0.0.55"; + qcom,fg-profile-data = [ + 89 1F A5 05 + 5B 0A EB FC + AD 1C AC 01 + E5 FC 77 D3 + 6C 18 92 22 + 30 3C F0 4B + 62 00 00 00 + 0F 00 00 00 + 00 00 E5 AB + B6 CC 58 CA + 2D 00 08 00 + FE DC EC DC + 8B FC D1 F2 + A7 06 8E 02 + 41 EB 56 3A + 16 06 09 20 + 27 00 14 00 + A0 1F C1 05 + 3E 0A 95 06 + 74 1C 45 03 + 0E 0C B0 0B + B3 18 B5 22 + CE 45 85 52 + 5E 00 00 00 + 0C 00 00 00 + 00 00 29 C5 + 27 C3 B6 CD + 26 00 00 00 + 4C EA EC DC + 46 06 F1 F2 + 97 FD 9E 02 + 46 D2 FC 12 + 99 33 CC FF + 07 10 00 00 + 04 0B 14 46 + 26 00 40 00 + 80 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-maple-send-4300mv-3167mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-maple-send-4300mv-3167mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..03c9a95aecee5d765c1fd4537a6e1b3c67b3c329 --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-maple-send-4300mv-3167mah.dtsi @@ -0,0 +1,87 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +qcom,send_3167mah { + qcom,max-voltage-uv = <4300000>; + qcom,fg-cc-cv-threshold-mv = <4290>; + qcom,fastchg-current-ma = <2400>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,battery-type = "1305-3151-1"; + qcom,checksum = <0x0142>; + qcom,gui-version = "PMI8998GUI - 2.0.0.55"; + qcom,fg-profile-data = [ + B6 18 52 0A + 9E 0D 80 02 + 71 1C 55 02 + D0 0D C0 03 + EB 17 D3 22 + D8 45 5A 52 + 89 00 00 00 + 0F 00 00 00 + 00 00 B1 C2 + 88 CC 48 C2 + 25 00 08 00 + 55 D5 06 DD + 69 05 BD FA + B3 05 68 03 + B1 FD 99 3B + 21 06 09 20 + 27 00 14 00 + 65 18 97 0A + 37 0D DE 02 + 86 1C FB 02 + A8 0C 02 0B + 8F 18 D9 22 + 9D 45 EB 52 + 8F 00 00 00 + 0D 00 00 00 + 00 00 2D CC + 03 00 F9 B2 + 1F 00 00 00 + A6 E3 06 DD + AC 05 1F 01 + 17 07 33 0A + B8 06 D1 12 + 99 33 CC FF + 07 10 00 00 + EA 0C CC 44 + 1F 00 40 00 + CA 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-maple-send-4350mv-3230mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-maple-send-4350mv-3230mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..4021d105c758b0935f4c00cdd861ba1be52682f5 --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-maple-send-4350mv-3230mah.dtsi @@ -0,0 +1,87 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +qcom,send_3230mah { + qcom,max-voltage-uv = <4350000>; + qcom,fg-cc-cv-threshold-mv = <4340>; + qcom,fastchg-current-ma = <2400>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,battery-type = "1305-3151-0"; + qcom,checksum = <0x68E8>; + qcom,gui-version = "PMI8998GUI - 2.0.0.54"; + qcom,fg-profile-data = [ + BE 18 55 0A + 9B 0D 94 02 + 6B 1C 85 02 + 70 0D 5A 0A + C9 17 3F 23 + 2D 45 84 53 + 89 00 00 00 + 0E 00 00 00 + 00 00 34 BA + 9C CC 58 CA + 25 00 08 00 + 59 D5 FE DC + 7A 05 9B FA + 33 FC 8F 03 + 25 FD 8B 3B + 1F 06 09 20 + 27 00 14 00 + 81 1A 42 03 + D8 04 F1 01 + 7A 1C 43 03 + 13 0C B6 0B + 87 18 E1 22 + 98 45 E1 52 + 83 00 00 00 + 0D 00 00 00 + 00 00 88 CD + D8 C2 A9 CD + 1F 00 00 00 + AD E3 FE DC + BF 05 0C 01 + 25 F4 25 0A + 67 07 C1 12 + 99 33 CC FF + 07 10 00 00 + 60 0D 99 45 + 1F 00 40 00 + C4 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4245mv-2425mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4245mv-2425mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..76fb549ab5dc7e3f4c0e8fab180e122d110629ae --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4245mv-2425mah.dtsi @@ -0,0 +1,88 @@ +/* Copyright (c) 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. +*/ +/* + * Copyright (C) 2018 Sony Mobile Communications 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. + */ + +qcom,send_2354mah { + qcom, = <24>; + qcom,max-voltage-uv = <4245000>; + qcom,fastchg-current-ma = <2025>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,fg-cc-cv-threshold-mv = <4235>; + qcom,battery-type = "1307-0625-4"; + qcom,checksum = <0x32AB>; + qcom,gui-version = "PMI8998GUI - 2.0.0.58"; + qcom,fg-profile-data = [ + 7C 20 ED 04 + 85 0A 5C 06 + 75 1C 43 02 + D0 0D E7 03 + 2F 18 87 22 + 6B 3C 88 4B + 91 00 00 00 + 15 00 00 00 + 00 00 96 C2 + A3 CC 48 C2 + 27 00 08 00 + 42 E5 33 CC + C3 05 23 01 + 0F 06 08 0A + 1A 07 F1 2B + 24 06 09 20 + 27 00 14 00 + 88 20 03 05 + 80 0A 9F FC + 83 1C D1 02 + EA 0C BE 0A + 8F 18 9C 22 + FB 45 31 52 + 85 00 00 00 + 13 00 00 00 + 00 00 88 CC + C4 C3 7C AB + 22 00 00 00 + B1 E3 33 CC + FE 05 AF F3 + 56 F4 0F 0A + 33 07 0E 12 + A1 33 CC FF + 07 10 00 00 + B3 09 EB 43 + 22 00 40 00 + C1 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4297mv-2465mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4297mv-2465mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..4c800c09b70b8aae3e5dfc221ce37c7d08186170 --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4297mv-2465mah.dtsi @@ -0,0 +1,88 @@ +/* Copyright (c) 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. +*/ +/* + * Copyright (C) 2018 Sony Mobile Communications 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. + */ + +qcom,send_2465mah { + qcom, = <24>; + qcom,max-voltage-uv = <4297000>; + qcom,fastchg-current-ma = <2025>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,fg-cc-cv-threshold-mv = <4287>; + qcom,battery-type = "1307-0625-3"; + qcom,checksum = <0x3337>; + qcom,gui-version = "PMI8998GUI - 2.0.0.58"; + qcom,fg-profile-data = [ + 1C 20 3F 05 + 67 0A 79 06 + 7E 1C 46 02 + BD 0D 1F 0A + 26 18 A1 22 + 2F 3C DB 4B + 8A 00 00 00 + 15 00 00 00 + 00 00 27 B2 + 61 CD 31 CA + 26 00 08 00 + A4 E5 DC D5 + E8 05 FD 00 + 20 06 88 03 + 75 F4 DF 32 + 1E 06 09 20 + 27 00 14 00 + 80 20 F2 04 + A8 0A 5D FC + 86 1C E4 02 + BB 0C 06 0B + 84 18 C8 22 + BB 45 99 52 + 80 00 00 00 + 12 00 00 00 + 00 00 F1 CC + 68 C3 27 B2 + 21 00 00 00 + 6E E3 DC D5 + 09 FC E6 00 + BB F4 12 0A + 8D FD 74 12 + 99 33 CC FF + 07 10 00 00 + 2C 0A C0 44 + 21 00 40 00 + BF 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4335mv-2647mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4335mv-2647mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..eb270db7d6768d61e6b5f2f8757755dfb3c10dac --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4335mv-2647mah.dtsi @@ -0,0 +1,87 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +qcom,send_2647mah { + qcom,max-voltage-uv = <4335000>; + qcom,fg-cc-cv-threshold-mv = <4325>; + qcom,fastchg-current-ma = <2025>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,battery-type = "1307-0625-2"; + qcom,checksum = <0x488B>; + qcom,gui-version = "PMI8998GUI - 2.0.0.55"; + qcom,fg-profile-data = [ + 6B 1F D4 05 + 22 0A A7 06 + C0 1C 15 01 + 77 01 D3 0D + 8C 18 96 22 + 19 3C 12 52 + 58 00 00 00 + 0E 00 00 00 + 00 00 9D C3 + 4F D5 E8 CA + 2E 00 08 00 + 6D E5 C5 D5 + 7F 05 56 01 + DC 04 12 03 + FE 07 40 1B + 44 06 09 20 + 27 00 14 00 + 90 1A 1C 03 + 11 05 D6 01 + 80 1C 16 03 + 70 0C 40 0B + 94 18 DA 22 + 92 45 FA 52 + 85 00 00 00 + 0D 00 00 00 + 00 00 26 CD + 0C C3 D3 C5 + 24 00 00 00 + B1 E3 C5 D5 + D9 05 05 01 + 29 FD F1 03 + AD 06 A9 12 + 99 33 CC FF + 07 10 00 00 + 80 0A 5C 45 + 24 00 40 00 + BC 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4357mv-2688mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4357mv-2688mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..cb9bbfacebd8c0a0b7ea456b3e3cc36590a343da --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4357mv-2688mah.dtsi @@ -0,0 +1,87 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +qcom,send_2688mah { + qcom,max-voltage-uv = <4357000>; + qcom,fg-cc-cv-threshold-mv = <4347>; + qcom,fastchg-current-ma = <2025>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,battery-type = "1307-0625-1"; + qcom,checksum = <0xF300>; + qcom,gui-version = "PMI8998GUI - 2.0.0.55"; + qcom,fg-profile-data = [ + C3 1A 8D 02 + 2B FC AB FA + 71 1C 61 02 + BB 0D FE 03 + 19 18 02 23 + 6F 45 22 53 + 83 00 00 00 + 0E 00 00 00 + 00 00 A4 C2 + 97 D5 2A CB + 2D 00 08 00 + 50 DC E5 DD + 92 05 83 FA + 17 05 3C 03 + 63 DD 15 1B + 43 06 09 20 + 27 00 14 00 + 34 1B 61 02 + 2E 06 54 01 + 82 1C 17 03 + 6A 0C 52 0B + E5 18 72 22 + 53 3C E2 4B + 84 00 00 00 + 0D 00 00 00 + 00 00 40 CC + 75 C3 0D A2 + 25 00 00 00 + F5 E3 E5 DD + D6 05 09 01 + C9 FC 67 03 + 86 06 10 13 + 99 33 CC FF + 07 10 00 00 + AF 0A B6 45 + 25 00 40 00 + C0 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4380mv-2650mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4380mv-2650mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..04d5696797b3b870b0124242235b5d01758a45a3 --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-send-4380mv-2650mah.dtsi @@ -0,0 +1,87 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +qcom,send_2650mah { + qcom,max-voltage-uv = <4380000>; + qcom,fg-cc-cv-threshold-mv = <4370>; + qcom,fastchg-current-ma = <2025>; + qcom,batt-id-kohm = <330>; + qcom,battery-beta = <4050>; + qcom,battery-type = "1307-0625-0"; + qcom,checksum = <0xE818>; + qcom,gui-version = "PMI8998GUI - 2.0.0.55"; + qcom,fg-profile-data = [ + 5F 1F D5 05 + 36 0A 98 06 + AF 1C E9 FA + 6C E4 EF 05 + 61 18 9C 22 + 13 3C 0F 52 + 50 00 00 00 + 0F 00 00 00 + 00 00 04 00 + 4B D5 A0 CA + 2C 00 08 00 + 41 DC 8F DD + 9E 05 6D FA + 4E 05 5B 03 + 17 D4 D0 1A + 43 06 09 20 + 27 00 14 00 + A8 1B E2 01 + D9 FD 02 01 + 7A 1C 44 03 + 07 0C D0 0B + 9F 18 D1 22 + A9 45 C3 52 + 83 00 00 00 + 0D 00 00 00 + 00 00 DF D5 + 89 BB EB C3 + 24 00 00 00 + 29 EA 8F DD + DE 05 FF 00 + 7D 06 96 03 + E1 E2 11 1A + AE 33 CC FF + 07 10 00 00 + E8 0A 14 46 + 24 00 40 00 + BD 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-tdk-4380mv-2650mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-tdk-4380mv-2650mah.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..b318300afc7e977e36ebdd7670536130d0d0e774 --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-poplar-tdk-4380mv-2650mah.dtsi @@ -0,0 +1,87 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +qcom,tdk_2650mah { + qcom,max-voltage-uv = <4000000>; + qcom,fg-cc-cv-threshold-mv = <3990>; + qcom,fastchg-current-ma = <525>; + qcom,batt-id-kohm = <15>; + qcom,battery-beta = <4050>; + qcom,battery-type = "TDK(Restricted)"; + qcom,checksum = <0x2596>; + qcom,gui-version = "PMI8998GUI - 2.0.0.54"; + qcom,fg-profile-data = [ + 92 1F 9E 05 + 6C 0A 52 06 + 4C 1D 93 E2 + 28 0A 2A 0C + 85 18 3C 23 + 1B 45 8A 53 + 5B 00 00 00 + 0E 00 00 00 + 00 00 EE C5 + D7 CD 9D C3 + 23 00 08 00 + CD DB 4A E4 + 97 06 D9 EB + 07 F3 35 12 + 13 07 6C 2B + 19 06 09 20 + 27 00 14 00 + A6 20 8D 04 + 2B 0B A8 05 + 26 1D 0B FB + A9 06 55 F5 + AE 19 55 22 + 53 3C D7 4B + 60 00 00 00 + 0D 00 00 00 + 00 00 74 CD + 4E BA BD BB + 1D 00 00 00 + C4 EA 4A E4 + DA 06 8B EA + D2 E3 66 0A + F6 EC 71 1A + 99 33 CC FF + 07 10 00 00 + DA 0A 14 46 + 1D 00 40 00 + CC 01 0A FA + FF 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 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 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi index ddc367711dab4775633cacc9b04703b4c2d9cdd8..aa51f433201b6b640218746dbaf41f4984659e6d 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi @@ -9,9 +9,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include -#include "msm8998-camera-sensor-mtp.dtsi" +/* #include "msm8998-camera-sensor-mtp.dtsi" */ &vendor { bluetooth: bt_wcn3990 { compatible = "qca,wcn3990"; @@ -240,6 +245,7 @@ }; }; +/* &mdss_hdmi_tx { pinctrl-names = "hdmi_hpd_active", "hdmi_ddc_active", "hdmi_cec_active", "hdmi_active", "hdmi_sleep"; @@ -254,6 +260,7 @@ pinctrl-4 = <&mdss_hdmi_5v_suspend &mdss_hdmi_hpd_suspend &mdss_hdmi_ddc_suspend &mdss_hdmi_cec_suspend>; }; +*/ &mdss_dp_ctrl { pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; @@ -611,6 +618,7 @@ }; }; +/* &vendor { mtp_batterydata: qcom,battery-data { qcom,batt-id-range-pct = <15>; @@ -623,3 +631,4 @@ &pmi8998_fg { qcom,battery-data = <&mtp_batterydata>; }; +*/ diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-lilac_generic.dts b/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-lilac_generic.dts new file mode 100644 index 0000000000000000000000000000000000000000..f03cb1a2cc77967d3f971f6894d10bdae9b38b74 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-lilac_generic.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-lilac_generic.dtsi" + +/ { + model = "SoMC Lilac-ROW(MSM8998 v2)"; + compatible = "somc,lilac-row", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-maple_dsds.dts b/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-maple_dsds.dts new file mode 100644 index 0000000000000000000000000000000000000000..15f037dff6175bbc1be52252b95d19d13ab8c89a --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-maple_dsds.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-maple_dsds.dtsi" + +/ { + model = "SoMC Maple-DSDS(MSM8998 v2)"; + compatible = "somc,maple-dsds", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-maple_generic.dts b/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-maple_generic.dts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e735d6c9fd49bfed21182903bd0efcf032572 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-maple_generic.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-maple_generic.dtsi" + +/ { + model = "SoMC Maple-ROW(MSM8998 v2)"; + compatible = "somc,maple-row", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-poplar_dsds.dts b/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-poplar_dsds.dts new file mode 100644 index 0000000000000000000000000000000000000000..286ac88b42d9fb673a4f437ca65c740eda841084 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-poplar_dsds.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-poplar_dsds.dtsi" + +/ { + model = "SoMC Poplar-DSDS(MSM8998 v2)"; + compatible = "somc,poplar-dsds", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-poplar_generic.dts b/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-poplar_generic.dts new file mode 100644 index 0000000000000000000000000000000000000000..26f8e1d13b74c54db88d8d5a4da293cc1a3b93ee --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2-yoshino-poplar_generic.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-poplar_generic.dtsi" + +/ { + model = "SoMC Poplar-ROW(MSM8998 v2)"; + compatible = "somc,poplar-row", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-lilac_generic.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-lilac_generic.dts new file mode 100644 index 0000000000000000000000000000000000000000..c4ea8ae8ea94f52e75eea5be1d117b7c15acd4b2 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-lilac_generic.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-lilac_generic.dtsi" + +/ { + model = "SoMC Lilac-ROW(MSM8998 v2.1)"; + compatible = "somc,lilac-row", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-maple_dsds.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-maple_dsds.dts new file mode 100644 index 0000000000000000000000000000000000000000..ae3ff0c0ac3dc5d59a6fb8c16cbc60a613b1be32 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-maple_dsds.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-maple_dsds.dtsi" + +/ { + model = "SoMC Maple-DSDS(MSM8998 v2.1)"; + compatible = "somc,maple-dsds", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-maple_generic.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-maple_generic.dts new file mode 100644 index 0000000000000000000000000000000000000000..977176e23e7e007ffa3f2ce0ba4648029fce7da4 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-maple_generic.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-maple_generic.dtsi" + +/ { + model = "SoMC Maple-ROW(MSM8998 v2.1)"; + compatible = "somc,maple-row", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-poplar_dsds.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-poplar_dsds.dts new file mode 100644 index 0000000000000000000000000000000000000000..98a5f0b7e1812e44a425d0f5443e3f775b896a03 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-poplar_dsds.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-poplar_dsds.dtsi" + +/ { + model = "SoMC Poplar-DSDS(MSM8998 v2.1)"; + compatible = "somc,poplar-dsds", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-poplar_generic.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-poplar_generic.dts new file mode 100644 index 0000000000000000000000000000000000000000..3ddda1dbf0bfc6ae800698ea55c4c1a9fae9e543 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-yoshino-poplar_generic.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-poplar_generic.dtsi" + +/ { + model = "SoMC Poplar-ROW(MSM8998 v2.1)"; + compatible = "somc,poplar-row", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-common.dtsi b/arch/arm/boot/dts/qcom/msm8998-yoshino-common.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..752f45a043d76590deee5974ac227d62b879af79 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-common.dtsi @@ -0,0 +1,3721 @@ +/* arch/arm64/boot/dts/qcom/msm8998-yoshino-common.dtsi + * + * 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. + */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ +#include + +/ { + aliases { + i2c8 = &i2c_8; + }; + + reserved-memory { + debug_region: debug_region@ffb00000 { + compatible = "removed-dma-pool", "qcom,debug_memory"; + no-map; + reg = <0 0xffb00000 0 0x100000>; + label = "debug_mem"; + }; + + pstore_reserve_mem: pstore_reserve_mem_region_region@ffc00000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0 0xffc00000 0 0x00100000>; + }; + }; +}; + +&firmware { + android { + fstab { + vendor { + fsmgr_flags = "wait,verify=/dev/block/platform/soc/1da4000.ufshc/by-name/fsmetadata"; + }; + + system { + compatible = "android,system"; + dev = "/dev/block/platform/soc/1da4000.ufshc/by-name/system"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,verify=/dev/block/platform/soc/1da4000.ufshc/by-name/fsmetadata"; + status = "ok"; + }; + }; + }; +}; + +&soc { + pinctrl@03400000 { + disabled-pins = <0 1 2 3 81 82 83 84>; + }; + + fpc1145 { + status = "ok"; + compatible = "fpc,fpc1020", "fpc1145"; + interrupt-parent = <&tlmm>; + interrupts = <121 0x0>; + fpc,gpio_rst = <&tlmm 40 0x0>; + fpc,gpio_irq = <&tlmm 121 0x0>; + vdd_ana-supply = <&pm8998_l6>; + + pinctrl-names = "fpc1145_reset_reset", + "fpc1145_reset_active", + "fpc1145_irq_active"; + + pinctrl-0 = <&msm_gpio_40>; + pinctrl-1 = <&msm_gpio_40_output_high>; + pinctrl-2 = <&msm_gpio_121>; + }; + + somc_pinctrl: somc_pinctrl { + compatible = "somc-pinctrl"; + pinctrl-names = "platform_common_default", + "product_common_default", + "variant_default"; + pinctrl-0 = <&msm_gpio_4 + &msm_gpio_5 &msm_gpio_6 &msm_gpio_7 &msm_gpio_8 &msm_gpio_9 + &msm_gpio_10 &msm_gpio_11_suspend &msm_gpio_12 &msm_gpio_13 &msm_gpio_14 + &msm_gpio_15 &msm_gpio_16 &msm_gpio_17 &msm_gpio_18 &msm_gpio_19 + &msm_gpio_20 &msm_gpio_21 &msm_gpio_22_suspend &msm_gpio_23 &msm_gpio_24 + &msm_gpio_25 &msm_gpio_26 &msm_gpio_27 &msm_gpio_28 &msm_gpio_29 + &msm_gpio_30 &msm_gpio_31 &msm_gpio_32 &msm_gpio_33 &msm_gpio_34 + &msm_gpio_35 &msm_gpio_36 &msm_gpio_37 &msm_gpio_39 &msm_gpio_40 + &msm_gpio_41 &msm_gpio_42 &msm_gpio_43 &msm_gpio_44 &msm_gpio_49 + &msm_gpio_50 &msm_gpio_51 &msm_gpio_52 &msm_gpio_55 &msm_gpio_56 + &msm_gpio_57 &msm_gpio_58 &msm_gpio_59 &msm_gpio_60 &msm_gpio_61 + &msm_gpio_62 &msm_gpio_63 &msm_gpio_65 &msm_gpio_66 &msm_gpio_67 + &msm_gpio_68 &msm_gpio_69 &msm_gpio_75 &msm_gpio_76 &msm_gpio_77 + &msm_gpio_78 &msm_gpio_79 &msm_gpio_80 &msm_gpio_81 &msm_gpio_82 + &msm_gpio_83 &msm_gpio_84 &msm_gpio_85 &msm_gpio_86 &msm_gpio_87 + &msm_gpio_88 &msm_gpio_90 &msm_gpio_91 &msm_gpio_92 + &msm_gpio_93 &msm_gpio_94 &msm_gpio_95 &msm_gpio_96 &msm_gpio_99 + &msm_gpio_100 &msm_gpio_104 &msm_gpio_108 &msm_gpio_112 &msm_gpio_114 + &msm_gpio_116 &msm_gpio_120 &msm_gpio_121 &msm_gpio_122 &msm_gpio_123 + &msm_gpio_124 &msm_gpio_125 &msm_gpio_126 &msm_gpio_128 &msm_gpio_129 + &msm_gpio_133>; + + /* If product common default setting is needed, + fill pinctrl-1 value in _common.dtsi */ + pinctrl-1 = <>; + + /* If variant specific default setting is needed, + fill pinctrl-2 value in .dtsi */ + pinctrl-2 = <>; + + /* If variant specific default setting is needed, + fill pinctrl-3 value in .dtsi */ + pinctrl-3 = <>; + }; + + /* SPI: BLSP1 */ + spi@c175000 { /* BLSP1 QUP1 */ + qcom,clk-freq-out = <4800000>; + status = "disabled"; + }; + + /* UART: BLSP3 */ + uart@c171000 { /* BLSP1 UART3 */ + qcom,clk-freq-out = <4000000>; + status = "okay"; + }; + + /* I2C: BLSP5 */ + i2c@c179000 { /* BLSP1 QUP5 */ + pinctrl-0 = <&msm_gpio_87 &msm_gpio_88>; + pinctrl-1 = <&msm_gpio_87 &msm_gpio_88>; + qcom,clk-freq-out = <355000>; + status = "okay"; + + /delete-node/ synaptics@20; + +#include "clearpad-ic-default-regoffset.dtsi" + + synaptics_clearpad@2c { + compatible = "synaptics,clearpad"; + reg = <0x2c>; + interrupt-parent = <&tlmm>; + interrupts = <125 0x2>; + synaptics,irq_gpio = <&tlmm 125 0x00>; + post_probe_start = <0>; + synaptics,firmware_name = "touch_module_id_0x%02x.img"; + flash_on_post_probe = <1>; + flip_config = <0>; + watchdog_enabled = <1>; + watchdog_delay_ms = <10000>; + charger_supported = <0>; + pen_supported = <0>; + glove_supported = <1>; + cover_supported = <1>; + touch_pressure_enabled = <1>; + touch_size_enabled = <0>; + touch_orientation_enabled = <0>; + preset_x_max = <2159>; + preset_y_max = <3839>; + preset_n_fingers = <10>; + wakeup_gesture { + double_tap { + gesture_code = <0x0003>; + event_00 { + type = <2>; /* LOG */ + message = "=== DOUBLE TAP ==="; + }; + event_01 { + type = <1>; /* KEY */ + code = <531>; /* TOUCHPAD_ON */ + down = <1>; + }; + event_02 { + type = <1>; /* KEY */ + code = <531>; /* TOUCHPAD_ON */ + down = <0>; + }; + event_03 { + type = <99>; /* END */ + }; + }; + }; + /* chip settings */ + clearpad_default { + flash_default_timeout_ms = <20000>; + calibrate_on_fwflash = <0>; + calibration_supported = <0>; + hwreset_delay_for_powerup_ms = <220>; + interrupt_default_wait_ms = <1000>; + charger_only_delay_ms = <200>; + }; + S3330 { + flash_default_timeout_ms = <20000>; + calibrate_on_fwflash = <0>; + calibration_supported = <0>; + hwreset_delay_for_powerup_ms = <220>; + interrupt_default_wait_ms = <1000>; + charger_only_delay_ms = <200>; + }; + S332U { + flash_default_timeout_ms = <20000>; + calibrate_on_fwflash = <0>; + calibration_supported = <0>; + hwreset_delay_for_powerup_ms = <220>; + interrupt_default_wait_ms = <1000>; + charger_only_delay_ms = <200>; + }; + S3500 { + flash_default_timeout_ms = <20000>; + calibrate_on_fwflash = <0>; + calibration_supported = <0>; + hwreset_delay_for_powerup_ms = <220>; + interrupt_default_wait_ms = <1000>; + charger_only_delay_ms = <16>; + }; + }; + }; + + /* I2C: BLSP7 */ + i2c@c1b5000 { /* BLSP2 QUP1 */ + pinctrl-0 = <&msm_gpio_55 &msm_gpio_56>; + pinctrl-1 = <&msm_gpio_55 &msm_gpio_56>; + qcom,clk-freq-out = <355000>; + status = "okay"; + }; + + /* UART: BLSP8 */ + serial@0c1b0000 { /* BLSP2 UART2 */ + pinctrl-names = "uart_active"; + pinctrl-0 = <&msm_gpio_4 &msm_gpio_5>; + status = "okay"; + }; + + /* I2C : BLSP8 */ + i2c_8: i2c@c1b6000 { /* BLSP2 QUP2 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "qup_phys_addr"; + reg = <0xC1B6000 0x600>; + interrupt-names = "qup_irq"; + interrupts = <0 102 0>; + dmas = <&dma_blsp2 8 64 0x20000020 0x20>, + <&dma_blsp2 9 32 0x20000020 0x20>; + dma-names = "tx", "rx"; + qcom,master-id = <84>; + qcom,clk-freq-out = <355000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>, + <&clock_gcc clk_gcc_blsp2_qup2_i2c_apps_clk>; + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&msm_gpio_6 &msm_gpio_7>; + pinctrl-1 = <&msm_gpio_6_suspend &msm_gpio_7_suspend>; + status = "okay"; + tcs3490@72 { + compatible = "ams,tcs3490"; + reg = <0x72>; + interrupt-parent = <&tlmm>; + interrupts = <11 0x0>; + ams,rgbcir-supply_name = "rgbcir_vdd"; + rgbcir-gpio-vdd-supply = <&cam_vio_verg>; + pinctrl-names = "rgbcir_irq_active", "rgbcir_irq_suspend"; + pinctrl-0 = <&msm_gpio_11>; + pinctrl-1 = <&msm_gpio_11_suspend>; + ams,rgbcir-vdd-supply = <0>; + ams,rgbcir-gpio-vdd = <1>; + ams,rgbcir-vio-supply = <0>; + }; + tof_sensor@52 { + compatible = "tof_sensor"; + reg = <0x52>; + interrupt-parent = <&tlmm>; + interrupts = <22 0x0>; + tof-supply_name = "tof_avdd"; + tof-gpio-avdd-supply = <&cam_vio_verg>; + pinctrl-names = "tof_irq_active", "tof_irq_suspend"; + pinctrl-0 = <&msm_gpio_22 &msm_gpio_27>; + pinctrl-1 = <&msm_gpio_22_suspend &msm_gpio_27>; + tof-reset-gpio = <&tlmm 27 0>; + sony,tof-sensor-name = "VL53L0"; + sony,tof-need-cam-on = <1>; + sony,tof-sensor-facing = <0>; + sony,tof-avdd-supply = <0>; + sony,tof-gpio-avdd = <1>; + }; + }; + + bu520x1nvx { + compatible = "rohm,bu520x1nvx"; + + acc_cover { + label = "lid"; + gpios = <&tlmm 124 0x1>; + lid-pin = <1>; + open-debounce-interval = <120>; + close-debounce-interval = <300>; + }; + }; + + mdss_dsi: qcom,mdss_dsi@0 { + mdss_dsi0: qcom,mdss_dsi_ctrl0@c994000 { + vddio-supply = <&pm8998_l14>; + touch-avdd-supply = <&pm8998_l28>; + }; + + mdss_dsi1: qcom,mdss_dsi_ctrl1@c996000 { + vddio-supply = <&pm8998_l14>; + touch-avdd-supply = <&pm8998_l28>; + }; + qcom,core-supply-entries { + qcom,core-supply-entry@0 { + /delete-property/ qcom,supply-lp-mode-disable-allowed; + }; + }; + qcom,phy-supply-entries { + qcom,phy-supply-entry@0 { + /delete-property/ qcom,supply-lp-mode-disable-allowed; + }; + }; + }; + + dsi_panel_pwr_supply_hybrid_incell: dsi_panel_pwr_supply_hybrid_incell { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <62000>; + qcom,supply-disable-load = <80>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "lab"; + qcom,supply-min-voltage = <4600000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@2 { + reg = <2>; + qcom,supply-name = "ibb"; + qcom,supply-min-voltage = <4600000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@3 { + reg = <2>; + qcom,supply-name = "touch-avdd"; + qcom,supply-min-voltage = <3000000>; + qcom,supply-max-voltage = <3000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + }; + + dsi_panel_pwr_supply_full_incell: dsi_panel_pwr_supply_full_incell { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <62000>; + qcom,supply-disable-load = <80>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "lab"; + qcom,supply-min-voltage = <4600000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@2 { + reg = <2>; + qcom,supply-name = "ibb"; + qcom,supply-min-voltage = <4600000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + /delete-node/ qcom,panel-supply-entry@3; + }; + + qcom,cci@ca0c000 { + qcom,i2c_fast_mode { + qcom,hw-thigh = <43>; + qcom,hw-tlow = <64>; + qcom,hw-tsu-sto = <41>; + qcom,hw-tsu-sta = <41>; + qcom,hw-thd-dat = <25>; + qcom,hw-thd-sta = <35>; + qcom,hw-tbuf = <64>; + qcom,hw-scl-stretch-en = <0>; + qcom,hw-trdhld = <6>; + qcom,hw-tsp = <3>; + qcom,cci-clk-src = <37500000>; + status = "ok"; + }; + + qcom,i2c_fast_plus_mode { + qcom,hw-thigh = <16>; + qcom,hw-tlow = <22>; + qcom,hw-tsu-sto = <17>; + qcom,hw-tsu-sta = <18>; + qcom,hw-thd-dat = <16>; + qcom,hw-thd-sta = <15>; + qcom,hw-tbuf = <19>; + qcom,hw-scl-stretch-en = <1>; + qcom,hw-trdhld = <3>; + qcom,hw-tsp = <3>; + qcom,cci-clk-src = <37500000>; + status = "ok"; + }; + qcom,camera@0 { + cell-index = <0>; + compatible = "qcom,sony_camera_0"; + reg = <0x0>; + interrupt-parent = <&tlmm>; + interrupts = <96 0x0>; + status = "ok"; + qcom,slave-id = <0x20 0x0 0x0000>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <0>; + qcom,sensor-name = "sony_camera_0"; + cam_vaf-supply = <&pm8998_l19>; + cam_vio_gpio-supply = <&cam_vio_verg>; + cam_vdig_gpio-supply = <&cam_vdig_rear_verg>; + qcom,cam-vreg-name = "cam_vaf", "cam_vio_gpio", "cam_vdig_gpio"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <2700000 0 0>; + qcom,cam-vreg-max-voltage = <2700000 0 0>; + qcom,cam-vreg-op-mode = <300000 0 0>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk0_active &msm_gpio_30>; + pinctrl-1 = <&cam_sensor_mclk0_suspend &msm_gpio_30>; + gpios = <&tlmm 13 0>, <&tlmm 30 0>; + qcom,gpio-reset = <1>; + qcom,gpio-req-tbl-num = <0 1>; + qcom,gpio-req-tbl-flags = <1 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK0", "CAM_RESET0"; + qcom,csi-lane-assign = <0x4320>; + qcom,csi-lane-mask = <0x1F>; + qcom,sensor-position = <0>; + qcom,sensor-mode = <1>; + qcom,cci-master = <0>; + clocks = <&clock_mmss clk_mclk0_clk_src>, <&clock_mmss clk_mmss_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + sony,i2c_addr = <0x20>; + sony,eeprom_addr = <0xA2>; + sony,eeprom_type = <2>; + sony,eeprom_max_len = <2048>; + sony,gpio_af = <0>; + sony,subdev_code = <0x3007>; + sony_camera_module_0: sony,camera_modules { + status = "disabled"; + }; + }; + qcom,camera@1 { + cell-index = <1>; + compatible = "qcom,sony_camera_1"; + reg = <0x1>; + interrupt-parent = <&tlmm>; + status = "ok"; + qcom,slave-id = <0x6c 0x0 0x0000>; + qcom,csiphy-sd-index = <2>; + qcom,csid-sd-index = <2>; + qcom,mount-angle = <270>; + qcom,sensor-name = "sony_camera_1"; + cam_vio-supply = <&pm8998_lvs1>; + cam_vdig-supply = <&pm8998_s3>; + cam_vaf-supply = <&pm8998_l22>; + cam_vio_gpio-supply = <&cam_vio_verg>; + cam_vdig_gpio-supply = <&cam_vdig_front_verg>; + qcom,cam-vreg-name = "cam_vio", "cam_vaf", "cam_vdig", "cam_vio_gpio", "cam_vdig_gpio"; + qcom,cam-vreg-type = <1 0 0 0 0>; + qcom,cam-vreg-min-voltage = <0 2700000 1352000 0 0>; + qcom,cam-vreg-max-voltage = <0 2700000 1352000 0 0>; + qcom,cam-vreg-op-mode = <0 150000 105000 0 0>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk1_active &msm_gpio_28>; + pinctrl-1 = <&cam_sensor_mclk1_suspend &msm_gpio_28>; + gpios = <&tlmm 14 0>, <&tlmm 28 0>; + qcom,gpio-reset = <1>; + qcom,gpio-req-tbl-num = <0 1>; + qcom,gpio-req-tbl-flags = <1 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK2", "CAM_RESET2"; + qcom,csi-lane-assign = <0x4320>;/* NONE */ + qcom,csi-lane-mask = <0x1F>; /* NONE */ + qcom,sensor-position = <1>; + qcom,sensor-mode = <1>; + qcom,cci-master = <1>; + clocks = <&clock_mmss clk_mclk1_clk_src>, <&clock_mmss clk_mmss_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + sony,i2c_addr = <0x34>; + sony,eeprom_addr = <0xA0>; + sony,eeprom_type = <0>; + sony,eeprom_max_len = <2048>; + sony,gpio_af = <0>; + sony,subdev_code = <0x3007>; + sony_camera_module_1: sony,camera_modules { + status = "disabled"; + }; + }; + }; + + ldo_vibrator { + compatible = "ldo-vibrator"; + gpios = <&pmi8998_gpios 5 1>; + }; + + qcom,sensor-information { + /* msm_therm */ + sensor_information23: qcom,sensor-information-23 { + qcom,scaling-factor = <10>; + }; + + /* quiet_therm */ + sensor_information27: qcom,sensor-information-27 { + qcom,scaling-factor = <10>; + qcom,alias-name = "bl_therm"; + }; + + /* ufs_therm */ + sensor_information100: qcom,sensor-information-100 { + qcom,sensor-type = "adc"; + qcom,scaling-factor = <10>; + qcom,sensor-name = "ufs_therm"; + }; + + /* flash_therm */ + sensor_information101: qcom,sensor-information-101 { + qcom,sensor-type = "adc"; + qcom,sensor-name = "flash_therm"; + qcom,scaling-factor = <10>; + }; + + /* xo_therm */ + sensor_information102: qcom,sensor-information-102 { + qcom,sensor-type = "adc"; + qcom,sensor-name = "xo_therm"; + }; + + /* pa_therm2 */ + sensor_information103: qcom,sensor-information-103 { + qcom,sensor-type = "adc"; + qcom,sensor-name = "pa_therm2"; + }; + /* bms */ + sensor_information104: qcom,sensor-information-104 { + qcom,sensor-type = "adc"; + qcom,sensor-name = "bms"; + qcom,alias-name = "batt_therm"; + qcom,scaling-factor = <1000>; + }; + }; + + sound-9335 { + qcom,msm-mbhc-hphl-swh = <1>; + /delete-property/ qcom,hph-en1-gpio; + /delete-property/ qcom,hph-en0-gpio; + /delete-property/ qcom,us-euro-gpios; + qcom,ear-en-gpios = <&pm8005_gpios 1 0>; + qcom,audio-routing = + "AIF4 VI", "MCLK", + "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", + "MIC BIAS2", "ANCRight Headset Mic", + "AMIC3", "MIC BIAS3", + "MIC BIAS3", "ANCLeft Headset Mic", + "DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "DMIC3", "MIC BIAS4", + "MIC BIAS4", "Digital Mic3", + "SpkrLeft IN", "SPK1 OUT", + "SpkrRight IN", "SPK2 OUT"; + }; + + qcom,wdt@17817000 { + qcom,bark-time = <12000>; + }; + + sim_detect { + compatible = "sim-detect"; + + sim1_det { + label = "sim-detection"; + gpios = <&tlmm 112 0x0>; + debounce-interval = <10>; + }; + }; + + ramoops { + compatible = "ramoops"; + memory-region = <&pstore_reserve_mem>; + record-size = <0x0 0x1000>; + console-size = <0x0 0x40000>; + ftrace-size = <0x0 0x0>; + pmsg-size = <0x0 0x0>; + ecc-size = <0x0 0x0>; + }; + + gpio_keys { + vol_dn { + label = "volume_down"; + gpios = <&pm8998_gpios 5 0x1>; + linux,input-type = <1>; + linux,code = <114>; + gpio-key,wakeup; + debounce-interval = <15>; + }; + }; + + cam_vio_verg: cam_vio_verg { + compatible = "regulator-fixed"; + regulator-name = "cam_vio_verg"; + startup-delay-us = <0>; + enable-active-high; + gpio = <&pmi8998_gpios 1 0>; + }; + + cam_vdig_front_verg: cam_vdig_front_verg { + compatible = "regulator-fixed"; + regulator-name = "cam_vdig_front_verg"; + startup-delay-us = <0>; + enable-active-high; + gpio = <&tlmm 25 0>; + }; + + cam_vdig_rear_verg: cam_vdig_rear_verg { + compatible = "regulator-fixed"; + regulator-name = "cam_vdig_rear_verg"; + startup-delay-us = <0>; + enable-active-high; + gpio = <&tlmm 21 0>; + }; + + qcom,bcl { + qcom,ibat-monitor { + qcom,soc-low-threshold = <5>; + }; + }; +}; + +&sony_camera_module_0 { + module_names = "GENERIC", "SOI20BS2"; + default_module_name = "SOI20BS2"; + + GENERIC { + mount_angle = <90>; + sensor_rotation = <0>; + sensor_facing = <0x00>; + sensor_config_delay_num = <13>; + sensor_config_delay = <4 3 4 4 4 4 4 4 4 4 4 4 4>; + temperature_check_skip_num = <0>; + total_pixel_number_w = <5520>; + total_pixel_number_h = <4032>; + active_pixel_number_x = <8>; + active_pixel_number_y = <176>; + active_pixel_number_w = <5504>; + active_pixel_number_h = <3808>; + min_focus_distance = <120>; + hyper_focal_distance = <4321>; + diagonal_len = "7.73"; + unit_cell_size_w = "1.22"; + unit_cell_size_h = "1.22"; + min_f_number = "2.00"; + max_f_number = "2.00"; + min_focus_pos = <1>; + max_focus_pos = <1024>; + min_focus_dac = <0x0000>; + max_focus_dac = <0x03FF>; + focus_inf_range_offset = <0x19>; + focus_macro_range_offset = <0x38>; + focus_lens_stroke_inf_to_1m = <0x14>; + focus_lens_stroke_1m_to_macro = <0x95>; + focus_lens_stroke_inf_to_macro = <0xA9>; + focus_calc_type = <0x01>; + focus_wob_time = <0x35>; + has_3a = <0>; + has_focus_actuator = <1>; + need_standby_af = <0>; + i2c_freq_mode = <3>; + has_pdaf = <1>; + has_rs = <1>; + has_multi_output = <0>; + has_super_slow = <1>; + has_sub_sensor = <0>; + has_aube = <0>; + has_flicker_detector = <1>; + has_hdr = <1>; + has_seamless_mode_change = <1>; + has_gph = <1>; + pdaf_free_area_num = <8>; + pdaf_fixed_area_size_w = <16>; + pdaf_fixed_area_size_h = <12>; + pll_num = <26>; + pll = <290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290>; + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VAF", + "CAM_VDIG_GPIO", + "CAM_VIO_GPIO", + "EXIT"; + GPIO_RESET = <5 0x0 0 5>; + CAM_CLK = <10 0xFFFFFFFF 0 0>; + CAM_VAF = <3 0xFFFFFFFF 0 0>; + CAM_VDIG_GPIO = <6 0xFFFFFFFF 0 5>; + CAM_VIO_GPIO = <7 0xFFFFFFFF 0 30>; + EXIT = <12 0x0 0 0>; + }; + power_on { + commands = + "CAM_VDIG_GPIO", + "CAM_VIO_GPIO", + "CAM_VAF", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + CAM_VDIG_GPIO = <6 0 0 1>; + CAM_VIO_GPIO = <7 0 0 1>; + CAM_VAF = <3 2700 106500 3>; + CAM_CLK = <10 0 0 1>; + GPIO_RESET = <5 1 0 1>; + EXIT = <12 0x0 0 0>; + }; + }; + SOI20BS2 { + mount_angle = <90>; + sensor_rotation = <0>; + sensor_facing = <0x00>; + sensor_config_delay_num = <13>; + sensor_config_delay = <4 3 4 4 4 4 4 4 4 4 4 4 4>; + temperature_check_skip_num = <0>; + total_pixel_number_w = <5520>; + total_pixel_number_h = <4032>; + active_pixel_number_x = <8>; + active_pixel_number_y = <176>; + active_pixel_number_w = <5504>; + active_pixel_number_h = <3808>; + min_focus_distance = <120>; + hyper_focal_distance = <4321>; + diagonal_len = "7.73"; + unit_cell_size_w = "1.22"; + unit_cell_size_h = "1.22"; + min_f_number = "2.00"; + max_f_number = "2.00"; + min_focus_pos = <1>; + max_focus_pos = <1024>; + min_focus_dac = <0x0000>; + max_focus_dac = <0x03FF>; + focus_inf_range_offset = <0x24>; + focus_macro_range_offset = <0x38>; + focus_lens_stroke_inf_to_1m = <0x14>; + focus_lens_stroke_1m_to_macro = <0x95>; + focus_lens_stroke_inf_to_macro = <0xA9>; + focus_calc_type = <0x01>; + focus_wob_time = <0x35>; + has_3a = <0>; + has_focus_actuator = <1>; + need_standby_af = <0>; + i2c_freq_mode = <3>; + has_pdaf = <1>; + has_rs = <1>; + has_multi_output = <0>; + has_super_slow = <1>; + has_sub_sensor = <0>; + has_aube = <0>; + has_flicker_detector = <1>; + has_hdr = <1>; + has_seamless_mode_change = <1>; + has_gph = <1>; + pdaf_free_area_num = <8>; + pdaf_fixed_area_size_w = <16>; + pdaf_fixed_area_size_h = <12>; + pll_num = <26>; + pll = <290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290 290>; + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VAF", + "CAM_VDIG_GPIO", + "CAM_VIO_GPIO", + "EXIT"; + GPIO_RESET = <5 0x0 0 5>; + CAM_CLK = <10 0xFFFFFFFF 0 0>; + CAM_VAF = <3 0xFFFFFFFF 0 0>; + CAM_VDIG_GPIO = <6 0xFFFFFFFF 0 5>; + CAM_VIO_GPIO = <7 0xFFFFFFFF 0 1>; + EXIT = <12 0x0 0 0>; + }; + power_on { + commands = + "CAM_VDIG_GPIO", + "CAM_VIO_GPIO", + "CAM_VAF", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + CAM_VDIG_GPIO = <6 0 0 1>; + CAM_VIO_GPIO = <7 0 0 1>; + CAM_VAF = <3 2700 106500 3>; + CAM_CLK = <10 0 0 1>; + GPIO_RESET = <5 1 0 1>; + EXIT = <12 0x0 0 0>; + }; + }; +}; + +&sony_camera_module_1 { + module_names = "GENERIC", "SOI13BS1", "SEM13BS1"; + default_module_name = "SOI13BS1"; + + GENERIC { + mount_angle = <270>; + sensor_rotation = <180>; + sensor_facing = <1>; + sensor_config_delay_num = <13>; + sensor_config_delay = <3 3 3 3 3 3 3 3 3 3 3 3 3>; + temperature_check_skip_num = <8>; + total_pixel_number_w = <4224>; + total_pixel_number_h = <3192>; + active_pixel_number_x = <8>; + active_pixel_number_y = <56>; + active_pixel_number_w = <4208>; + active_pixel_number_h = <3120>; + min_focus_distance = <100>; + hyper_focal_distance = <2000>; + diagonal_len = "5.867"; + unit_cell_size_w = "1.12"; + unit_cell_size_h = "1.12"; + min_f_number = "2.00"; + max_f_number = "2.00"; + min_focus_pos = <1>; + max_focus_pos = <1024>; + min_focus_dac = <0x0000>; + max_focus_dac = <0x03FF>; + focus_inf_range_offset = <0x24>; + focus_macro_range_offset = <0x24>; + focus_lens_stroke_inf_to_1m = <0x09>; + focus_lens_stroke_1m_to_macro = <0x55>; + focus_lens_stroke_inf_to_macro = <0x5E>; + focus_calc_type = <0x01>; + focus_wob_time = <0>; + has_3a = <0>; + has_focus_actuator = <1>; + need_standby_af = <0>; + i2c_freq_mode = <1>; + has_pdaf = <0>; + has_rs = <1>; + has_multi_output = <0>; + has_super_slow = <0>; + has_sub_sensor = <0>; + has_aube = <0>; + has_flicker_detector = <0>; + has_hdr = <0>; + has_seamless_mode_change = <0>; + has_gph = <1>; + pdaf_free_area_num = <0>; + pdaf_fixed_area_size_w = <0>; + pdaf_fixed_area_size_h = <0>; + pll_num = <26>; + pll = <1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134>; + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VDIG_GPIO", + "CAM_VDIG", + "CAM_VIO", + "CAM_VIO_GPIO", + "CAM_VAF", + "EXIT"; + CAM_VDIG = <0 0xFFFFFFFF 0 2>; + CAM_VIO = <1 0xFFFFFFFF 0 2>; + CAM_VIO_GPIO = <7 0xFFFFFFFF 0 28>; + CAM_VAF = <3 0xFFFFFFFF 0 2>; + GPIO_RESET = <5 0x0 0 5>; + CAM_VDIG_GPIO = <6 0xFFFFFFFF 0 5>; + CAM_CLK = <10 0xFFFFFFFF 0 0>; + EXIT = <12 0x0 0 0>; + }; + power_on { + commands = + "CAM_VDIG", + "CAM_VIO", + "CAM_VIO_GPIO", + "CAM_VAF", + "CAM_VDIG_GPIO", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + CAM_VDIG = <0 1352 105000 0>; + CAM_VIO = <1 0 0 0>; + CAM_VIO_GPIO = <7 0 0 1>; + CAM_VAF = <3 2700 150000 0>; + GPIO_RESET = <5 1 0 1>; + CAM_VDIG_GPIO = <6 0 0 1>; + CAM_CLK = <10 0 0 1>; + EXIT = <12 0x0 0 0>; + }; + }; + SOI13BS1 { + mount_angle = <270>; + sensor_rotation = <180>; + sensor_facing = <1>; + sensor_config_delay_num = <13>; + sensor_config_delay = <3 3 3 3 3 3 3 3 3 3 3 3 3>; + temperature_check_skip_num = <8>; + total_pixel_number_w = <4224>; + total_pixel_number_h = <3192>; + active_pixel_number_x = <8>; + active_pixel_number_y = <56>; + active_pixel_number_w = <4208>; + active_pixel_number_h = <3120>; + min_focus_distance = <100>; + hyper_focal_distance = <2000>; + diagonal_len = "5.867"; + unit_cell_size_w = "1.12"; + unit_cell_size_h = "1.12"; + min_f_number = "2.00"; + max_f_number = "2.00"; + min_focus_pos = <1>; + max_focus_pos = <1024>; + min_focus_dac = <0x0000>; + max_focus_dac = <0x03FF>; + focus_inf_range_offset = <0x24>; + focus_macro_range_offset = <0x24>; + focus_lens_stroke_inf_to_1m = <0x09>; + focus_lens_stroke_1m_to_macro = <0x55>; + focus_lens_stroke_inf_to_macro = <0x5E>; + focus_calc_type = <0x01>; + focus_wob_time = <0>; + has_3a = <0>; + has_focus_actuator = <1>; + need_standby_af = <0>; + i2c_freq_mode = <1>; + has_pdaf = <0>; + has_rs = <1>; + has_multi_output = <0>; + has_super_slow = <0>; + has_sub_sensor = <0>; + has_aube = <0>; + has_flicker_detector = <0>; + has_hdr = <0>; + has_seamless_mode_change = <0>; + has_gph = <1>; + pdaf_free_area_num = <0>; + pdaf_fixed_area_size_w = <0>; + pdaf_fixed_area_size_h = <0>; + pll_num = <26>; + pll = <1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134>; + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VDIG_GPIO", + "CAM_VDIG", + "CAM_VIO", + "CAM_VIO_GPIO", + "CAM_VAF", + "EXIT"; + CAM_VDIG = <0 0xFFFFFFFF 0 2>; + CAM_VIO = <1 0xFFFFFFFF 0 2>; + CAM_VIO_GPIO = <7 0xFFFFFFFF 0 1>; + CAM_VAF = <3 0xFFFFFFFF 0 2>; + GPIO_RESET = <5 0x0 0 5>; + CAM_VDIG_GPIO = <6 0xFFFFFFFF 0 5>; + CAM_CLK = <10 0xFFFFFFFF 0 0>; + EXIT = <12 0x0 0 0>; + }; + power_on { + commands = + "CAM_VDIG", + "CAM_VIO", + "CAM_VIO_GPIO", + "CAM_VAF", + "CAM_VDIG_GPIO", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + CAM_VDIG = <0 1352 105000 0>; + CAM_VIO = <1 0 0 0>; + CAM_VIO_GPIO = <7 0 0 1>; + CAM_VAF = <3 2700 150000 0>; + GPIO_RESET = <5 1 0 1>; + CAM_VDIG_GPIO = <6 0 0 1>; + CAM_CLK = <10 0 0 1>; + EXIT = <12 0x0 0 0>; + }; + }; + SEM13BS1 { + mount_angle = <270>; + sensor_rotation = <180>; + sensor_facing = <1>; + sensor_config_delay_num = <13>; + sensor_config_delay = <3 3 3 3 3 3 3 3 3 3 3 3 3>; + temperature_check_skip_num = <8>; + total_pixel_number_w = <4224>; + total_pixel_number_h = <3192>; + active_pixel_number_x = <8>; + active_pixel_number_y = <56>; + active_pixel_number_w = <4208>; + active_pixel_number_h = <3120>; + min_focus_distance = <100>; + hyper_focal_distance = <2000>; + diagonal_len = "5.867"; + unit_cell_size_w = "1.12"; + unit_cell_size_h = "1.12"; + min_f_number = "2.00"; + max_f_number = "2.00"; + min_focus_pos = <1>; + max_focus_pos = <1024>; + min_focus_dac = <0x0000>; + max_focus_dac = <0x03FF>; + focus_inf_range_offset = <0x24>; + focus_macro_range_offset = <0x24>; + focus_lens_stroke_inf_to_1m = <0x09>; + focus_lens_stroke_1m_to_macro = <0x53>; + focus_lens_stroke_inf_to_macro = <0x5C>; + focus_calc_type = <0x01>; + focus_wob_time = <0>; + has_3a = <0>; + has_focus_actuator = <1>; + need_standby_af = <0>; + i2c_freq_mode = <1>; + has_pdaf = <0>; + has_rs = <1>; + has_multi_output = <0>; + has_super_slow = <0>; + has_sub_sensor = <0>; + has_aube = <0>; + has_flicker_detector = <0>; + has_hdr = <0>; + has_seamless_mode_change = <0>; + has_gph = <1>; + pdaf_free_area_num = <0>; + pdaf_fixed_area_size_w = <0>; + pdaf_fixed_area_size_h = <0>; + pll_num = <26>; + pll = <1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134>; + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VDIG_GPIO", + "CAM_VDIG", + "CAM_VIO", + "CAM_VIO_GPIO", + "CAM_VAF", + "EXIT"; + CAM_VDIG = <0 0xFFFFFFFF 0 2>; + CAM_VIO = <1 0xFFFFFFFF 0 2>; + CAM_VIO_GPIO = <7 0xFFFFFFFF 0 1>; + CAM_VAF = <3 0xFFFFFFFF 0 2>; + GPIO_RESET = <5 0x0 0 5>; + CAM_VDIG_GPIO = <6 0xFFFFFFFF 0 5>; + CAM_CLK = <10 0xFFFFFFFF 0 0>; + EXIT = <12 0x0 0 0>; + }; + power_on { + commands = + "CAM_VDIG", + "CAM_VIO", + "CAM_VIO_GPIO", + "CAM_VAF", + "CAM_VDIG_GPIO", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + CAM_VDIG = <0 1352 105000 0>; + CAM_VIO = <1 0 0 0>; + CAM_VIO_GPIO = <7 0 0 1>; + CAM_VAF = <3 2700 150000 0>; + GPIO_RESET = <5 1 0 1>; + CAM_VDIG_GPIO = <6 0 0 1>; + CAM_CLK = <10 0 0 1>; + EXIT = <12 0x0 0 0>; + }; + }; +}; + +&qusb_phy0 { + qcom,efuse-offset = <0x00000005>; + qcom,qusb-phy-init-seq = + /* */ + <0x13 0x04 /* analog_controls_two */ + 0x7c 0x18c /* pll_clock_inverter */ + 0x80 0x2c /* pll_cmode */ + 0x0a 0x184 /* pll_lock_delay */ + 0xD0 0x21c /* imp_ctrl1 */ + 0x05 0x23c /* tune1 */ + 0x08 0x240 /* tune2 */ + 0xD4 0x244 /* tune3 */ + 0x04 0x248 /* tune4 */ + 0x19 0xb4>; /* digital_timers_two */ + + qcom,qusb-phy-host-init-seq = + /* */ + <0x63 0x210 /* pwr_ctrl1 */ + 0x13 0x04 /* analog_controls_two */ + 0x7c 0x18c /* pll_clock_inverter */ + 0x80 0x2c /* pll_cmode */ + 0x0a 0x184 /* pll_lock_delay */ + 0x8c 0x21c /* imp_ctrl1 */ + 0x05 0x23c /* tune1 */ + 0x03 0x240 /* tune2 */ + 0xff 0x218 /* rescode_contrpl */ + 0x62 0x210>; /* pwr_ctrl1 */ +}; + +&ssphy { + qcom,qmp-phy-init-seq = + /* */ + <0x138 0x30 0x00 + 0x034 0x04 0x01 + 0x080 0x14 0x00 + 0x03c 0x06 0x00 + 0x08c 0x08 0x00 + 0x15c 0x06 0x00 + 0x164 0x01 0x00 + 0x13c 0x80 0x00 + 0x0b0 0x82 0x00 + 0x0b8 0xab 0x00 + 0x0bc 0xea 0x00 + 0x0c0 0x02 0x00 + 0x060 0x06 0x00 + 0x068 0x16 0x00 + 0x070 0x36 0x00 + 0x0dc 0x00 0x00 + 0x0d8 0x3f 0x00 + 0x0f8 0x01 0x00 + 0x0f4 0xc9 0x00 + 0x148 0x0a 0x00 + 0x0a0 0x00 0x00 + 0x09c 0x34 0x00 + 0x098 0x15 0x00 + 0x090 0x04 0x00 + 0x154 0x00 0x00 + 0x094 0x00 0x00 + 0x0f0 0x00 0x00 + 0x00c 0x0a 0x00 + 0x048 0x07 0x00 + 0x0d0 0x80 0x00 + 0x184 0x01 0x00 + 0x010 0x01 0x00 + 0x01c 0x31 0x00 + 0x020 0x01 0x00 + 0x014 0x00 0x00 + 0x018 0x00 0x00 + 0x024 0x85 0x00 + 0x028 0x07 0x00 + 0x430 0x0b 0x00 + 0x4d4 0x0f 0x00 + 0x4d8 0x4e 0x00 + 0x4dc 0x18 0x00 + 0x4f8 0x07 0x00 + 0x4fc 0x80 0x00 + 0x504 0x43 0x00 + 0x50c 0x1c 0x00 + 0x434 0x75 0x00 + 0x43c 0x00 0x00 + 0x440 0x00 0x00 + 0x444 0x80 0x00 + 0x408 0x0a 0x00 + 0x414 0x06 0x00 + 0x500 0x00 0x00 + 0x4c0 0x03 0x00 + 0x564 0x05 0x00 + 0x830 0x0b 0x00 + 0x8d4 0x0f 0x00 + 0x8d8 0x4e 0x00 + 0x8dc 0x18 0x00 + 0x8f8 0x07 0x00 + 0x8fc 0x80 0x00 + 0x904 0x43 0x00 + 0x90c 0x1c 0x00 + 0x834 0x75 0x00 + 0x83c 0x00 0x00 + 0x840 0x00 0x00 + 0x844 0x80 0x00 + 0x808 0x0a 0x00 + 0x814 0x06 0x00 + 0x900 0x00 0x00 + 0x8c0 0x03 0x00 + 0x964 0x05 0x00 + 0x260 0x10 0x00 + 0x2a4 0x12 0x00 + 0x28c 0x16 0x00 + 0x244 0x00 0x00 + 0x660 0x10 0x00 + 0x6a4 0x12 0x00 + 0x68c 0x16 0x00 + 0x644 0x00 0x00 + 0xcc8 0x83 0x00 + 0xccc 0x09 0x00 + 0xcd0 0xa2 0x00 + 0xcd4 0x40 0x00 + 0xcc4 0x02 0x00 + 0xc80 0xd1 0x00 + 0xc84 0x1f 0x00 + 0xc88 0x47 0x00 + 0xc64 0x1b 0x00 + 0xc0c 0x9f 0x00 + 0xc10 0x9f 0x00 + 0xc14 0xb7 0x00 + 0xc18 0x4e 0x00 + 0xc1c 0x65 0x00 + 0xc20 0x6b 0x00 + 0xc24 0x15 0x00 + 0xc28 0x0d 0x00 + 0xc2c 0x15 0x00 + 0xc30 0x0d 0x00 + 0xc34 0x15 0x00 + 0xc38 0x0d 0x00 + 0xc3c 0x15 0x00 + 0xc40 0x0d 0x00 + 0xc44 0x15 0x00 + 0xc48 0x0d 0x00 + 0xc4c 0x15 0x00 + 0xc50 0x0d 0x00 + 0xc5c 0x02 0x00 + 0xca0 0x04 0x00 + 0xc8c 0x44 0x00 + 0xc70 0xe7 0x00 + 0xc74 0x03 0x00 + 0xc78 0x40 0x00 + 0xc7c 0x00 0x00 + 0xdd8 0x8a 0x00 + 0xcb8 0x75 0x00 + 0xcb0 0x86 0x00 + 0xcbc 0x13 0x00 + 0x21c 0x1f 0x00 + 0x61c 0x1f 0x00 + 0x20c 0x0c 0x00 + 0x60c 0x0c 0x00 + 0xffffffff 0xffffffff 0x00>; +}; + +&pm8998_gpios { + /* GPIO_1: UIM_BATT_ALARM */ + /* Follow QTI */ + + /* GPIO_2: NC */ + gpio@c100 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_3: WLAN_SW_CTRL */ + /* Follow QTI */ + + /* GPIO_4: SSC_PWR_EN */ + /* Follow QTI */ + + /* GPIO_5: VOL_DOWN_N */ + gpio@c400 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <0>; /* In */ + qcom,vin-sel = <0>; /* 1.8V */ + qcom,pull = <0>; /* PU */ + qcom,master-en = <1>; /* Enable */ + status = "ok"; + }; + + /* GPIO_6: VOL_UP_N */ + gpio@c500 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <0>; /* In */ + qcom,vin-sel = <0>; /* 1.8V */ + qcom,pull = <0>; /* PU */ + qcom,master-en = <1>; /* Enable */ + status = "ok"; + }; + + /* GPIO_7: SNAPSHOT_N */ + gpio@c600 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <0>; /* In */ + qcom,vin-sel = <0>; /* 1.8V */ + qcom,pull = <0>; /* PU */ + qcom,master-en = <1>; /* Enable */ + status = "ok"; + }; + + /* GPIO_8: FOCUS_N */ + gpio@c700 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <0>; /* In */ + qcom,vin-sel = <0>; /* 1.8V */ + qcom,pull = <0>; /* PU */ + qcom,master-en = <1>; /* Enable */ + status = "ok"; + }; + + /* GPIO_9: FLASH_THERM */ + gpio@c800 { + status = "ok"; + qcom,master-en = <0>; /* Disable */ + }; + + /* GPIO_13: DIV_CLK1 */ + /* Follow QTI */ + + /* GPIO_14: NC */ + gpio@cd00 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_15: NC */ + gpio@ce00 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_16: DIV_CLK3 */ + /* Follow QTI */ + + /* GPIO_17: NC */ + gpio@d000 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_18: NC */ + gpio@d100 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_19: NC */ + gpio@d200 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_20: NC */ + gpio@d300 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_21: NFC_CLK_REQ */ + gpio@d400 { + qcom,mode = <0>; /* In */ + qcom,vin-sel = <1>; /* 1.8V */ + qcom,pull = <4>; /* PD */ + qcom,master-en = <1>; /* Enable */ + status = "ok"; + }; + + /* GPIO_22: NC */ + gpio@d500 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_23: WCSS_PWR_REQ */ + /* Follow QTI */ + + /* GPIO_24: OPTION_1 */ + /* Follow QTI */ + + /* GPIO_25: OPTION_2 */ + /* Follow QTI */ + + /* GPIO_26: PM_SLB */ + /* Follow QTI */ +}; + +&pmi8998_gpios { + /* GPIO_1: NC */ + gpio@c000 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_2: NC */ + gpio@c100 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_3: NC */ + gpio@c200 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_4: TYPEC_UUSB_SEL */ + /* Follow QTI */ + + /* GPIO_5: VIB_LDO_EN */ + gpio@c400 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <1>; /* Out */ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <0>; /* 1.8V */ + qcom,out-strength = <1>; /* Low */ + qcom,invert = <0>; /* Low */ + qcom,master-en = <1>; /* Enable */ + status = "okay"; + }; + + /* GPIO_6: NC */ + gpio@c500 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_7: DISPLAY_TYPE_SEL */ + /* Follow QTI */ + + /* GPIO_8: USB_SWITCH_SEL */ + gpio@c700 { + status = "okay"; + }; + + /* GPIO_9: NC */ + gpio@c800 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_10: 4K_DISP_DCDC_EN */ + gpio@c900 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <1>; /* Out */ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <0>; /* 1.8V */ + qcom,out-strength = <1>; /* Low */ + qcom,invert = <0>; /* Low */ + qcom,master-en = <1>; /* Enable */ + status = "okay"; + }; + + /* GPIO_11: NC */ + gpio@ca00 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_12: DIV_CLK3 */ + /* Follow QTI */ + + /* GPIO_13: SPMI_I2C_SEL */ + /* Follow QTI */ + + /* GPIO_14: NC */ + gpio@cd00 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; +}; + +&pm8005_gpios { + /* GPIO_1: EAR_EN */ + gpio@c000 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <1>; /* Out */ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <1>; /* 1.8V */ + qcom,out-strength = <1>; /* Low */ + qcom,invert = <0>; /* Low */ + qcom,master-en = <1>; /* Enable */ + status = "okay"; + }; + + /* GPIO_2: NC */ + gpio@c100 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_3: SLB */ + /* Follow QTI */ + + /* GPIO_4: OPTION_1_PM8005 */ + /* Follow QTI */ +}; + +&usb3 { + id_polling_use; + id_polling_up_interval = <2000>; + id_polling_up_period = <0>; + id_polling_pd_gpio = <&tlmm 128 0>; + qcom,usb_detect-vadc = <&pm8998_vadc>; +}; + +&pm8998_vadc { + chan@14 { /* USB_DETECT_ADC */ + label = "usb_detect"; + reg = <0x14>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <5>; + qcom,fast-avg-setup = <0>; + }; + + chan@4d { /* msm_therm */ + qcom,scale-function = <17>; + }; + + chan@4e { + label = "ufs_therm"; + reg = <0x4e>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <17>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,vadc-thermal-node; + }; + + chan@4f { + label = "pa_therm1"; + reg = <0x4f>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,vadc-thermal-node; + }; + + chan@50 { + label = "pa_therm2"; + reg = <0x50>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,vadc-thermal-node; + }; + + chan@51 { /* quiet_therm */ + qcom,scale-function = <17>; + }; + + chan@53 { + label = "flash_therm"; + reg = <0x53>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <17>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,vadc-thermal-node; + }; +}; + +/* Regulator config */ +/* pm8998_s1 */ +/* Follow QTI */ + +/* pm8998_s2 */ +/* Follow QTI */ + +/* pm8998_s3 */ +/* Follow QTI */ + +/* pm8998_s4 */ +/* Follow QTI */ + +/* pm8998_s5 */ +/* Follow QTI */ + +/* pm8998_s6 */ +/* Follow QTI */ + +/* pm8998_s7 */ +/* Follow QTI */ + +/* pm8998_s8 */ +/* Follow QTI */ + +/* pm8998_s9 */ +/* Follow QTI */ + +/* pm8998_s10 */ +/* Follow QTI */ + +/* pm8998_s11 */ +/* Follow QTI */ + +/* pm8998_s12 */ +/* Follow QTI */ + +/* pm8998_s13 */ +/* Follow QTI */ + +/* pm8998_l1 */ +/* Follow QTI */ + +/* pm8998_l2 */ +/* Follow QTI */ + +/* pm8998_l3 */ +/* Follow QTI */ + +/* pm8998_l4 */ +/* Follow QTI */ + +/* pm8998_l5 */ +/* Follow QTI */ + +&pm8998_l6 { + qcom,regulator-type = <0>; /* LDO */ + qcom,init-enable = <0>; + qcom,init-ldo-mode = <1>; + qcom,init-pin-ctrl-enable = <0>; + qcom,init-pin-ctrl-mode = <0>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + status = "okay"; +}; + +/* pm8998_l7 */ +/* Follow QTI */ + +/* pm8998_l8 */ +/* Follow QTI */ + +/* pm8998_l9 */ +/* Follow QTI */ + +/* pm8998_l10 */ +/* Follow QTI */ + +/* pm8998_l11 { +/* Follow QTI */ + +/* pm8998_l12 */ +/* Follow QTI */ + +/* pm8998_l13 */ +/* Follow QTI */ + +&pm8998_l14 { + qcom,regulator-type = <0>; /* LDO */ + qcom,init-enable = <0>; + qcom,init-ldo-mode = <1>; + qcom,init-pin-ctrl-enable = <0>; + qcom,init-pin-ctrl-mode = <0>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + status = "okay"; +}; + +/* pm8998_l15 */ +/* Follow QTI */ + +/* pm8998_l16 */ +/* Follow QTI */ + +/* pm8998_l17 */ +/* Follow QTI */ + +&pm8998_l18 { + status = "disabled"; +}; + +&pm8998_l19 { + qcom,regulator-type = <0>; /* LDO */ + qcom,init-enable = <0>; + qcom,init-ldo-mode = <1>; + qcom,init-pin-ctrl-enable = <0>; + qcom,init-pin-ctrl-mode = <0>; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2700000>; + qcom,init-voltage = <2700000>; + status = "okay"; +}; + +/* pm8998_l20 */ +/* Follow QTI */ + +/* pm8998_l21 */ +/* Follow QTI */ + +&pm8998_l22 { + qcom,regulator-type = <0>; /* LDO */ + qcom,init-enable = <0>; + qcom,init-ldo-mode = <1>; + qcom,init-pin-ctrl-enable = <0>; + qcom,init-pin-ctrl-mode = <0>; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2700000>; + qcom,init-voltage = <2700000>; + status = "okay"; +}; + +/* pm8998_l23 */ +/* Follow QTI */ + +/* pm8998_l24 */ +/* Follow QTI */ + +/* pm8998_l25 */ +/* Follow QTI */ + +/* pm8998_l26 */ +/* Follow QTI */ + +/* pm8998_l27 */ +/* Follow QTI */ + +&pm8998_l28 { + qcom,regulator-type = <0>; /* LDO */ + qcom,init-enable = <0>; + qcom,init-ldo-mode = <1>; + qcom,init-pin-ctrl-enable = <0>; + qcom,init-pin-ctrl-mode = <0>; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + qcom,init-voltage = <3000000>; + status = "okay"; +}; + +&pm8998_lvs1 { + qcom,regulator-type = <2>; /* VS */ + qcom,init-enable = <0>; + qcom,init-pin-ctrl-enable = <0>; + qcom,init-pin-ctrl-mode = <0>; + status = "okay"; +}; + +/* pm8998_lvs2 */ +/* Follow QTI */ + +/* pm8005_s1 */ +/* Follow QTI */ + +/* pm8005_s2 */ +/* Follow QTI */ + +/* pm8005_s3 */ +/* Follow QTI */ + +/* pm8005_s4 */ +/* Follow QTI */ + +/* pmi8998_bob */ +/* Follow QTI */ + +&tlmm { + /* GPIO_4 : DEBUG_UART_TX */ + msm_gpio_4: msm_gpio_4 { + mux { + pins = "gpio4"; + function = "blsp_uart8_a"; + }; + + config { + pins = "gpio4"; + drive-strength = <4>; + bias-disable; + }; + }; + + /* GPIO_5 : DEBUG_UART_RX */ + msm_gpio_5: msm_gpio_5 { + mux { + pins = "gpio5"; + function = "blsp_uart8_a"; + }; + + config { + pins = "gpio5"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + /* GPIO_6 : CAMSENSOR_I2C_SDA */ + msm_gpio_6: msm_gpio_6 { + mux { + pins = "gpio6"; + function = "blsp_i2c8"; + }; + + config { + pins = "gpio6"; + drive-strength = <2>; + bias-disable; + }; + }; + msm_gpio_6_suspend: msm_gpio_6_suspend { + mux { + pins = "gpio6"; + function = "blsp_i2c8"; + }; + + config { + pins = "gpio6"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_7 : CAMSENSOR_I2C_SCL */ + msm_gpio_7: msm_gpio_7 { + mux { + pins = "gpio7"; + function = "blsp_i2c8"; + }; + + config { + pins = "gpio7"; + drive-strength = <2>; + bias-disable; + }; + }; + msm_gpio_7_suspend: msm_gpio_7_suspend { + mux { + pins = "gpio7"; + function = "blsp_i2c8"; + }; + + config { + pins = "gpio7"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_8 : NC */ + msm_gpio_8: msm_gpio_8 { + mux { + pins = "gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio8"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_9 : "NC" */ + msm_gpio_9: msm_gpio_9 { + mux { + pins = "gpio9"; + function = "gpio"; + }; + + config { + pins = "gpio9"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_10 : MDP_VSYNC_P */ + msm_gpio_10: msm_gpio_10 { + mux { + pins = "gpio10"; + function = "mdp_vsync_a"; + }; + + config { + pins = "gpio10"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_11 : RGBC_IR_INT */ + msm_gpio_11: msm_gpio_11 { + mux { + pins = "gpio11"; + function = "gpio"; + }; + + config { + pins = "gpio11"; + drive-strength = <2>; + bias-pull-up; + input-enable; + }; + }; + msm_gpio_11_suspend: msm_gpio_11_suspend { + mux { + pins = "gpio11"; + function = "gpio"; + }; + + config { + pins = "gpio11"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_12 : NFC_VEN */ + msm_gpio_12: msm_gpio_12 { + mux { + pins = "gpio12"; + function = "gpio"; + }; + + config { + pins = "gpio12"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_13 : CAM_MCLK0 */ + msm_gpio_13: msm_gpio_13 { + mux { + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_14 : CAM_MCLK1 */ + msm_gpio_14: msm_gpio_14 { + mux { + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_15 : NC */ + msm_gpio_15: msm_gpio_15 { + mux { + pins = "gpio15"; + function = "gpio"; + }; + + config { + pins = "gpio15"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_16 : NC */ + msm_gpio_16: msm_gpio_16 { + mux { + pins = "gpio16"; + function = "gpio"; + }; + + config { + pins = "gpio16"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_17 : CCI_I2C_SDA0 */ + msm_gpio_17: msm_gpio_17 { + mux { + pins = "gpio17"; + function = "cci_i2c"; + }; + + config { + pins = "gpio17"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_18 : CCI_I2C_SCL0 */ + msm_gpio_18: msm_gpio_18 { + mux { + pins = "gpio18"; + function = "cci_i2c"; + }; + + config { + pins = "gpio18"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_19 : CCI_I2C_SDA1 */ + msm_gpio_19: msm_gpio_19 { + mux { + pins = "gpio19"; + function = "cci_i2c"; + }; + + config { + pins = "gpio19"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_20 : CCI_I2C_SCL1 */ + msm_gpio_20: msm_gpio_20 { + mux { + pins = "gpio20"; + function = "cci_i2c"; + }; + + config { + pins = "gpio20"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_21 : MAIN_CAM_PWR_EN */ + msm_gpio_21: msm_gpio_21 { + /* CAMERA SENSOR 0 EN */ + mux { + pins = "gpio21"; + function = "gpio"; + }; + + config { + pins = "gpio21"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_22 : TOF_INT_N */ + msm_gpio_22: msm_gpio_22 { + mux { + pins = "gpio22"; + function = "gpio"; + }; + + config { + pins = "gpio22"; + drive-strength = <2>; + bias-pull-up; + input-enable; + }; + }; + msm_gpio_22_suspend: msm_gpio_22_suspend { + mux { + pins = "gpio22"; + function = "gpio"; + }; + + config { + pins = "gpio22"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_23 : NC */ + msm_gpio_23: msm_gpio_23 { + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_24 : NC */ + msm_gpio_24: msm_gpio_24 { + mux { + pins = "gpio24"; + function = "gpio"; + }; + + config { + pins = "gpio24"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_25 : CHAT_CAM_PWR_EN */ + msm_gpio_25: msm_gpio_25 { + /* CAMERA SENSOR 2 VDIG */ + mux { + /* CLK, DATA */ + pins = "gpio25"; + function = "gpio"; + }; + + config { + pins = "gpio25"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_26 : NC */ + msm_gpio_26: msm_gpio_26 { + mux { + pins = "gpio26"; + function = "gpio"; + }; + + config { + pins = "gpio26"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_27 : TOF_RESET_N */ + msm_gpio_27: msm_gpio_27 { + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_28 : CAM2_RSTN */ + msm_gpio_28: msm_gpio_28 { + mux { + pins = "gpio28"; + function = "gpio"; + }; + + config { + pins = "gpio28"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_29 : NC */ + msm_gpio_29: msm_gpio_29 { + mux { + pins = "gpio29"; + function = "gpio"; + }; + + config { + pins = "gpio29"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_30 : CAM1_RSTN */ + msm_gpio_30: msm_gpio_30 { + mux { + pins = "gpio30"; + function = "gpio"; + }; + + config { + pins = "gpio30"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_31 : NC */ + msm_gpio_31: msm_gpio_31 { + mux { + pins = "gpio31"; + function = "gpio"; + }; + + config { + pins = "gpio31"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_32 : NC */ + msm_gpio_32: msm_gpio_32 { + mux { + pins = "gpio32"; + function = "gpio"; + }; + + config { + pins = "gpio32"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_33 : NC */ + msm_gpio_33: msm_gpio_33 { + mux { + pins = "gpio33"; + function = "gpio"; + }; + + config { + pins = "gpio33"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_34 : NC */ + msm_gpio_34: msm_gpio_34 { + mux { + pins = "gpio34"; + function = "gpio"; + }; + + config { + pins = "gpio34"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_35 : NC */ + msm_gpio_35: msm_gpio_35 { + mux { + pins = "gpio35"; + function = "gpio"; + }; + + config { + pins = "gpio35"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_36 : NC */ + msm_gpio_36: msm_gpio_36 { + mux { + pins = "gpio36"; + function = "gpio"; + }; + + config { + pins = "gpio36"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_37 : NC */ + msm_gpio_37: msm_gpio_37 { + mux { + pins = "gpio37"; + function = "gpio"; + }; + + config { + pins = "gpio37"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_38 : CC_DIR */ + /* Follow QTI */ + + /* GPIO_39 : UIM2_DETECT_EN */ + msm_gpio_39: msm_gpio_39 { + mux { + pins = "gpio39"; + function = "gpio"; + }; + + config { + pins = "gpio39"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_40 : FP_RESET_N */ + msm_gpio_40: msm_gpio_40 { + mux { + pins = "gpio40"; + function = "gpio"; + }; + + config { + pins = "gpio40"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_40 : FP_RESET_N, state device active */ + msm_gpio_40_output_high: msm_gpio_40_output_high { + mux { + pins = "gpio40"; + function = "gpio"; + }; + + config { + pins = "gpio40"; + drive-strength = <2>; + bias-disable; + output-high; + }; + }; + + /* GPIO_41 : NC */ + msm_gpio_41: msm_gpio_41 { + mux { + pins = "gpio41"; + function = "gpio"; + }; + + config { + pins = "gpio41"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_42 : NC */ + msm_gpio_42: msm_gpio_42 { + mux { + pins = "gpio42"; + function = "gpio"; + }; + + config { + pins = "gpio42"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_43 : NC */ + msm_gpio_43: msm_gpio_43 { + mux { + pins = "gpio43"; + function = "gpio"; + }; + + config { + pins = "gpio43"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_44 : NC */ + msm_gpio_44: msm_gpio_44 { + mux { + pins = "gpio44"; + function = "gpio"; + }; + + config { + pins = "gpio44"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_45 : BT_HCI_UART_TXD */ + /* Follow QTI */ + + /* GPIO_46 : BT_HCI_UART_RXD */ + /* Follow QTI */ + + /* GPIO_47 : BT_HCI_UART_CTS_N */ + /* Follow QTI */ + + /* GPIO_48 : BT_HCI_UART_RFR_N */ + /* Follow QTI */ + + /* GPIO_49 : NC */ + msm_gpio_49: msm_gpio_49 { + mux { + pins = "gpio49"; + function = "gpio"; + }; + + config { + pins = "gpio49"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_50 : NC */ + msm_gpio_50: msm_gpio_50 { + mux { + pins = "gpio50"; + function = "gpio"; + }; + + config { + pins = "gpio50"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_51 : NC */ + msm_gpio_51: msm_gpio_51 { + mux { + pins = "gpio51"; + function = "gpio"; + }; + + config { + pins = "gpio51"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_52 : NC */ + msm_gpio_52: msm_gpio_52 { + mux { + pins = "gpio52"; + function = "gpio"; + }; + + config { + pins = "gpio52"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_53 : CODEC_INT2_N */ + /* Follow QTI */ + + /* GPIO_54 : CODEC_INT1_N */ + /* Follow QTI */ + + /* GPIO_55 : APPS_I2C_SDA */ + msm_gpio_55: msm_gpio_55 { + mux { + pins = "gpio55"; + function = "blsp_i2c7"; + }; + + config { + pins = "gpio55"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_56 : APPS_I2C_SCL */ + msm_gpio_56: msm_gpio_56 { + mux { + pins = "gpio56"; + function = "blsp_i2c7"; + }; + + config { + pins = "gpio56"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_57 : FORCED_USB_BOOT */ + msm_gpio_57: msm_gpio_57 { + mux { + pins = "gpio57"; + function = "gpio"; + }; + + config { + pins = "gpio57"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_58 : NC */ + msm_gpio_58: msm_gpio_58 { + mux { + pins = "gpio58"; + function = "gpio"; + }; + + config { + pins = "gpio58"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_59 : NC */ + msm_gpio_59: msm_gpio_59 { + mux { + pins = "gpio59"; + function = "gpio"; + }; + + config { + pins = "gpio59"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_60 : NC */ + msm_gpio_60: msm_gpio_60 { + mux { + pins = "gpio60"; + function = "gpio"; + }; + + config { + pins = "gpio60"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_61 : NC */ + msm_gpio_61: msm_gpio_61 { + mux { + pins = "gpio61"; + function = "gpio"; + }; + + config { + pins = "gpio61"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_62 : NC */ + msm_gpio_62: msm_gpio_62 { + mux { + pins = "gpio62"; + function = "gpio"; + }; + + config { + pins = "gpio62"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_63 : TRAY2_DET_DS */ + msm_gpio_63: msm_gpio_63 { + mux { + pins = "gpio63"; + function = "gpio"; + }; + + config { + pins = "gpio63"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + /* GPIO_64 : CODEC_RST_N */ + /* Follow QTI */ + + /* GPIO_65 : WSA_L_EN */ + msm_gpio_65: msm_gpio_65 { + mux { + pins = "gpio65"; + function = "gpio"; + }; + + config { + pins = "gpio65"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_66 : WSA_R_EN */ + msm_gpio_66: msm_gpio_66 { + mux { + pins = "gpio66"; + function = "gpio"; + }; + + config { + pins = "gpio66"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_67 : NC */ + msm_gpio_67: msm_gpio_67 { + mux { + pins = "gpio67"; + function = "gpio"; + }; + + config { + pins = "gpio67"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_68 : NC */ + msm_gpio_68: msm_gpio_68 { + mux { + pins = "gpio68"; + function = "gpio"; + }; + + config { + pins = "gpio68"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_69 : NC */ + msm_gpio_69: msm_gpio_69 { + mux { + pins = "gpio69"; + function = "gpio"; + }; + + config { + pins = "gpio69"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_70 : LPASS_SLIMBUS_CLK */ + /* Follow QTI */ + + /* GPIO_71 : LPASS_SLIMBUS_DATA0 */ + /* Follow QTI */ + + /* GPIO_72 : LPASS_SLIMBUS_DATA1 */ + /* Follow QTI */ + + /* GPIO_73 : BT_FM_SLIMBUS_DATA */ + /* Follow QTI */ + + /* GPIO_74 : BT_FM_SLIMBUS_CLK */ + /* Follow QTI */ + + /* GPIO_75 : NC */ + msm_gpio_75: msm_gpio_75 { + mux { + pins = "gpio75"; + function = "gpio"; + }; + + config { + pins = "gpio75"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_76 : RF_LCD_ID_EN */ + msm_gpio_76: msm_gpio_76 { + mux { + pins = "gpio76"; + function = "gpio"; + }; + + config { + pins = "gpio76"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_77 : NC */ + msm_gpio_77: msm_gpio_77 { + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_78 : NC */ + msm_gpio_78: msm_gpio_78 { + mux { + pins = "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio78"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_79 : NC */ + msm_gpio_79: msm_gpio_79 { + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_80 : NC */ + msm_gpio_80: msm_gpio_80 { + mux { + pins = "gpio80"; + function = "gpio"; + }; + + config { + pins = "gpio80"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_81 : SW_SERVICE */ + msm_gpio_81: msm_gpio_81 { + mux { + pins = "gpio81"; + function = "gpio"; + }; + + config { + pins = "gpio81"; + drive-strength = <2>; + bias-pull-up; + input-enable; + }; + }; + + /* GPIO_82 : TX_GTR_THRES_IN */ + msm_gpio_82: msm_gpio_82 { + mux { + pins = "gpio82"; + function = "gpio"; + }; + + config { + pins = "gpio82"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_83 : HW_ID[0] */ + msm_gpio_83: msm_gpio_83 { + mux { + pins = "gpio83"; + function = "gpio"; + }; + + config { + pins = "gpio83"; + drive-strength = <2>; + bias-pull-up; + input-enable; + }; + }; + + /* GPIO_84 : HW_ID[1] */ + msm_gpio_84: msm_gpio_84 { + mux { + pins = "gpio84"; + function = "gpio"; + }; + + config { + pins = "gpio84"; + drive-strength = <2>; + bias-pull-up; + input-enable; + }; + }; + + /* GPIO_85 : NC */ + msm_gpio_85: msm_gpio_85 { + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_86 : NC */ + msm_gpio_86: msm_gpio_86 { + mux { + pins = "gpio86"; + function = "gpio"; + }; + + config { + pins = "gpio86"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_87 : TS_I2C_SDA */ + msm_gpio_87: msm_gpio_87 { + mux { + pins = "gpio87"; + function = "blsp_i2c5"; + }; + + config { + pins = "gpio87"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_88 : TS_I2C_SCL */ + msm_gpio_88: msm_gpio_88 { + mux { + pins = "gpio88"; + function = "blsp_i2c5"; + }; + + config { + pins = "gpio88"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_89 : TS_RESET */ + /* Follow QTI */ + + /* GPIO_90 : NC */ + msm_gpio_90: msm_gpio_90 { + mux { + pins = "gpio90"; + function = "gpio"; + }; + + config { + pins = "gpio90"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_91 : NC */ + msm_gpio_91: msm_gpio_91 { + mux { + pins = "gpio91"; + function = "gpio"; + }; + + config { + pins = "gpio91"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_92 : NFC_IRQ */ + msm_gpio_92: msm_gpio_92 { + mux { + pins = "gpio92"; + function = "gpio"; + }; + + config { + pins = "gpio92"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_93 : NFC_DWLD_EN */ + msm_gpio_93: msm_gpio_93 { + mux { + pins = "gpio93"; + function = "gpio"; + }; + + config { + pins = "gpio93"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_94 : DISP_RESET_N */ + msm_gpio_94: msm_gpio_94 { + mux { + pins = "gpio94"; + function = "gpio"; + }; + + config { + pins = "gpio94"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_95 : TRAY2_DET */ + msm_gpio_95: msm_gpio_95 { + mux { + pins = "gpio95"; + function = "gpio"; + }; + + config { + pins = "gpio95"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + /* GPIO_96 : CAM_SOF */ + msm_gpio_96: msm_gpio_96 { + mux { + pins = "gpio96"; + function = "gpio"; + }; + + config { + pins = "gpio96"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_97 : RFFE6_CLK */ + /* Follow QTI */ + + /* GPIO_98 : RFFE6_DATA */ + /* Follow QTI */ + + /* GPIO_99 : DEBUG_GPIO0 */ + msm_gpio_99: msm_gpio_99 { + mux { + pins = "gpio99"; + function = "gpio"; + }; + + config { + pins = "gpio99"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_100 : DEBUG_GPIO1 */ + msm_gpio_100: msm_gpio_100 { + mux { + pins = "gpio100"; + function = "gpio"; + }; + + config { + pins = "gpio100"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_101 : GRFC4 */ + /* Follow QTI */ + + /* GPIO_102 : GRFC5 */ + /* Follow QTI */ + + /* GPIO_103 : GRFC6 */ + /* Follow QTI */ + + /* GPIO_104 : RSVD (GRFC7) */ + msm_gpio_104: msm_gpio_104 { + mux { + pins = "gpio104"; + function = "gpio"; + }; + + config { + pins = "gpio104"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_105 : UIM2_DATA */ + /* Follow QTI */ + + /* GPIO_106 : UIM2_CLK */ + /* Follow QTI */ + + /* GPIO_107 : UIM2_RESET */ + /* Follow QTI */ + + /* GPIO_108 : UIM2_PRESENT (UIM2_DET) */ + msm_gpio_108: msm_gpio_108 { + mux { + pins = "gpio108"; + function = "gpio"; + }; + + config { + pins = "gpio108"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + /* GPIO_109 : UIM1_DATA */ + /* Follow QTI */ + + /* GPIO_110 : UIM1_CLK */ + /* Follow QTI */ + + /* GPIO_111 : UIM1_RST */ + /* Follow QTI */ + + /* GPIO_112 : UIM1_PRESENT (TRAY1_DET) */ + msm_gpio_112: msm_gpio_112 { + mux { + pins = "gpio112"; + function = "gpio"; + }; + + config { + pins = "gpio112"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + /* GPIO_113 : UIM_BATT_ALARM */ + /* Follow QTI */ + + /* GPIO_114 : RSVD (GRFC8) */ + msm_gpio_114: msm_gpio_114 { + mux { + pins = "gpio114"; + function = "gpio"; + }; + + config { + pins = "gpio114"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_115 : GRFC9 */ + /* Follow QTI */ + + /* GPIO_116 : NC */ + msm_gpio_116: msm_gpio_116 { + mux { + pins = "gpio116"; + function = "gpio"; + }; + + config { + pins = "gpio116"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_117 : ACCEL_INT */ + /* Follow QTI */ + + /* GPIO_118 : GYRO_INT */ + /* Follow QTI */ + + /* GPIO_119 : COMPASS_INT */ + /* Follow QTI */ + + /* GPIO_120 : ALS_PROX_INT_N */ + msm_gpio_120: msm_gpio_120 { + mux { + pins = "gpio120"; + function = "gpio"; + }; + + config { + pins = "gpio120"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + /* GPIO_121 : FP_INT_N */ + msm_gpio_121: msm_gpio_121 { + mux { + pins = "gpio121"; + function = "gpio"; + }; + + config { + pins = "gpio121"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_122 : NC */ + msm_gpio_122: msm_gpio_122 { + mux { + pins = "gpio122"; + function = "gpio"; + }; + + config { + pins = "gpio122"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_123 : BAROMETER_INT */ + msm_gpio_123: msm_gpio_123 { + mux { + pins = "gpio123"; + function = "gpio"; + }; + + config { + pins = "gpio123"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_124 : ACC_COVER_OPEN */ + msm_gpio_124: msm_gpio_124 { + mux { + pins = "gpio124"; + function = "gpio"; + }; + + config { + pins = "gpio124"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + + /* GPIO_125 : TS_INT_N */ + msm_gpio_125: msm_gpio_125 { + mux { + pins = "gpio125"; + function = "gpio"; + }; + + config { + pins = "gpio125"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; + + /* GPIO_126 : NC */ + msm_gpio_126: msm_gpio_126 { + mux { + pins = "gpio126"; + function = "gpio"; + }; + + config { + pins = "gpio126"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_127 : GRFC3 */ + /* Follow QTI */ + + /* GPIO_128 : USB_DETECT_EN */ + msm_gpio_128: msm_gpio_128 { + mux { + pins = "gpio128"; + function = "gpio"; + }; + + config { + pins = "gpio128"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_129 : NC */ + msm_gpio_129: msm_gpio_129 { + mux { + pins = "gpio129"; + function = "gpio"; + }; + + config { + pins = "gpio129"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_130 : QLINK_REQUEST */ + /* Follow QTI */ + + /* GPIO_131 : QLINK_ENABLE */ + /* Follow QTI */ + + /* GPIO_132 : GRFC2 */ + /* Follow QTI */ + + /* GPIO_133 : NC */ + msm_gpio_133: msm_gpio_133 { + mux { + pins = "gpio133"; + function = "gpio"; + }; + + config { + pins = "gpio133"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_134 : WMSS_RESET_N */ + /* Follow QTI */ + + /* GPIO_135 : PA_INDICATOR_OR */ + /* Follow QTI */ + + /* GPIO_136 : GRFC1 */ + /* Follow QTI */ + + /* GPIO_137 : RFFE3_DATA */ + /* Follow QTI */ + + /* GPIO_138 : RFFE3_CLK */ + /* Follow QTI */ + + /* GPIO_139 : RFFE4_DATA */ + /* Follow QTI */ + + /* GPIO_140 : RFFE4_CLK */ + /* Follow QTI */ + + /* GPIO_141 : RFFE5_DATA */ + /* Follow QTI */ + + /* GPIO_142 : RFFE5_CLK */ + /* Follow QTI */ + + /* GPIO_143 : GNSS_EN */ + /* Follow QTI */ + + /* GPIO_144 : MSS_LTE_COXM_TXD */ + /* Follow QTI */ + + /* GPIO_145 : MSS_LTE_COXM_RXD */ + /* Follow QTI */ + + /* GPIO_146 : RFFE2_DATA */ + /* Follow QTI */ + + /* GPIO_147 : RFFE2_CLK */ + /* Follow QTI */ + + /* GPIO_148 : RFFE1_DATA */ + /* Follow QTI */ + + /* GPIO_149 : RFFE1_CLK */ + /* Follow QTI */ + + mdss_touch_active: mdss_touch_active { + mux { + pins = "gpio125"; + function = "gpio"; + }; + config { + pins = "gpio125"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + + mdss_touch_suspend: mdss_touch_suspend { + mux { + pins = "gpio125"; + function = "gpio"; + }; + config { + pins = "gpio125"; + drive-strength = <2>; + bias-pull-down; + }; + }; +}; + +&pmx_mdss { + mdss_dsi_active: mdss_dsi_active { + mux { + pins = "gpio94"; + function = "gpio"; + }; + + config { + pins = "gpio94"; + drive-strength = <2>; /* 2 mA */ + bias-disable = <0>; /* no pull */ + }; + }; + mdss_dsi_suspend: mdss_dsi_suspend { + mux { + pins = "gpio94"; + function = "gpio"; + }; + + config { + pins = "gpio94"; + drive-strength = <2>; /* 2 mA */ + /delete-property/ bias-pull-down; + bias-disable = <0>; /* no pull */ + output-low; + }; + }; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi0 { + pinctrl-names = "mdss_default", "mdss_sleep", "mdss_touch_active", "mdss_touch_suspend"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + pinctrl-2 = <&mdss_touch_active>; + pinctrl-3 = <&mdss_touch_suspend>; + qcom,platform-te-gpio = <&tlmm 10 0>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-touch-reset-gpio = <&tlmm 89 0>; + qcom,platform-touch-int-gpio = <&tlmm 125 0>; + qcom,platform-touch-vddio-gpio = <&tlmm 133 0>; + somc,disp-dcdc-en-gpio = <&pmi8998_gpios 10 0>; + /delete-property/ qcom,platform-bklight-en-gpio; + /delete-property/ qcom,panel-mode-gpio; +}; + +&mdss_dsi1 { + /delete-property/ qcom,panel-mode-gpio; +}; + +&labibb { + qcom,lab@de00 { + interrupts = <0x3 0xde 0x0 IRQ_TYPE_LEVEL_LOW>, + <0x3 0xde 0x1 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "lab_vreg_not_ok_interrupt", "lab-vreg-ok"; + }; + qcom,ibb@dc00 { + interrupts = <0x3 0xdc 0x0 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "ibb_vreg_not_ok_interrupt"; + }; +}; + +&mdss_dp_ctrl { + status = "disabled"; +}; + +&sdhc_2 { + cd-gpios = <&tlmm 95 GPIO_ACTIVE_HIGH>; +}; + +&spmi_bus { + qcom,pm8998@0 { + qcom,power-on@800 { + qcom,s3-src = "resin"; + qcom,pon_1 { + /delete-property/ qcom,support-reset; + /delete-property/ qcom,s1-timer; + /delete-property/ qcom,s2-timer; + /delete-property/ qcom,s2-type; + }; + qcom,pon_2 { + linux,code = <115>; + }; + qcom,pon_3 { + /delete-property/ qcom,support-reset; + /delete-property/ qcom,s1-timer; + /delete-property/ qcom,s2-timer; + /delete-property/ qcom,s2-type; + }; + }; + }; + + qcom,pmi8998@2 { + pmi8998_charger: qcom,qpnp-smb2 { + qcom,otg-cl-ua = <1000000>; + }; + }; + + qcom,pmi8998@3 { + qcom,leds@d000 { + status = "okay"; + qcom,rgb_sync = <1>; + }; + }; +}; + +&red_led { + somc,pwm-channel = <4>; + linux,name = "red"; + linux,default-trigger = "none"; + somc,color_variation_max_num = <4>; + somc,max_current = < + 18 511 511 + 19 511 511 + 20 511 511 + 21 511 511>; +}; +&green_led { + somc,pwm-channel = <3>; + linux,name = "green"; + linux,default-trigger = "none"; + somc,color_variation_max_num = <4>; + somc,max_current = < + 18 511 511 + 19 511 511 + 20 511 511 + 21 511 511>; +}; + +&blue_led { + somc,pwm-channel = <2>; + linux,name = "blue"; + linux,default-trigger = "none"; + somc,color_variation_max_num = <4>; + somc,max_current = < + 18 511 511 + 19 511 511 + 20 511 511 + 21 511 511>; +}; + +&pmi8998_wled { + linux,name = "wled"; + qcom,fs-curr-ua = <24000>; + qcom,led-strings-list = [00 01 02]; + somc,init-br-ua = <10000>; + somc-s1,br-power-save-ua = <800>; + somc,bl-scale-enabled; + somc,area_count_table_size = <0>; +}; + +&flash_led { + qcom,hw-strobe-option = <1>; +}; + +&pmi8998_flash0 { + qcom,current-ma = <625>; + qcom,max-current = <1000>; + qcom,hw-strobe-edge-trigger; + qcom,strobe-sel = <1>; + somc,sw-strobe-init; +}; + +&pmi8998_flash1 { + qcom,current-ma = <625>; + qcom,max-current = <1000>; + qcom,hw-strobe-edge-trigger; + qcom,strobe-sel = <1>; + somc,sw-strobe-init; +}; + +&pmi8998_flash2 { + status = "disabled"; +}; + +&pmi8998_torch0 { + qcom,current-ma = <120>; + qcom,max-current = <200>; + qcom,hw-strobe-edge-trigger; + qcom,strobe-sel = <1>; + somc,sw-strobe-init; +}; + +&pmi8998_torch1 { + qcom,current-ma = <120>; + qcom,max-current = <200>; + qcom,hw-strobe-edge-trigger; + qcom,strobe-sel = <1>; + somc,sw-strobe-init; +}; + +&pmi8998_torch2 { + status = "disabled"; +}; + +&pmi8998_switch0 { + qcom,led-mask = <3>; +}; + +&pmi8998_switch1 { + status = "disabled"; +}; + +&pmi8998_fg { + qcom,fg-delta-soc-thr = <1>; + qcom,fg-recharge-soc-thr = <95>; + qcom,fg-empty-voltage = <3100>; + qcom,fg-rsense-sel = <0>; + qcom,fg-jeita-thresholds = <5 10 45 55>; + qcom,slope-limit-temp-threshold = <100>; + qcom,slope-limit-coeffs = <1 1 1 1>; + qcom,fg-esr-tight-lt-filter-micro-pct = <30000>; + qcom,fg-esr-broad-lt-filter-micro-pct = <30000>; + qcom,cycle-counter-en; + qcom,cl-start-capacity = <15>; + qcom,cl-min-temp = <150>; + qcom,cl-max-temp = <450>; + qcom,fg-jeita-hyst-temp = <2>; + qcom,hold-soc-while-full; + qcom,cl-max-increment = <0>; + qcom,cl-max-decrement = <20>; + somc,therm-coeff-c1 = <199>; + somc,therm-coeff-c2 = <80>; + somc,therm-coeff-c3 = <255>; +}; + +&pmi8998_charger { + clock-names = "xo"; + clocks = <&clock_gcc clk_cxo_clk_src>; + usb_switch_sel = <&pmi8998_gpios 8 0>; + qcom,usb-icl-ua = <1500000>; + io-channels = <&pmi8998_rradc 2>, + <&pmi8998_rradc 3>, + <&pmi8998_rradc 4>, + <&pmi8998_rradc 8>, + <&pmi8998_rradc 10>; + io-channel-names = "skin_temp", + "usbin_i", + "usbin_v", + "charger_temp", + "charger_temp_max"; + somc,jeita-sw-ctrl-en; +}; + +&pmi8998_rradc { + somc,reg-cfg = + /* addr mask value */ + <0x83 0x07 0x04>, /* FG_ADC_RR_AUX_THERM_CFG */ + <0x88 0xFF 0xAF>, /* FG_ADC_RR_AUX_THERM_C1 */ + <0x89 0xFF 0x34>, /* FG_ADC_RR_AUX_THERM_C2 */ + <0x8A 0xFF 0xDF>, /* FG_ADC_RR_AUX_THERM_C3 */ + <0x8B 0xFF 0xA0>; /* FG_ADC_RR_AUX_THERM_HALF_RANGE */ +}; + +&slim_aud { + tasha_codec { + qcom,cdc-micbias1-mv = <1800>; + qcom,cdc-micbias2-mv = <1800>; + qcom,cdc-micbias3-mv = <1800>; + qcom,cdc-micbias4-mv = <1800>; + qcom,cdc-dmic-sample-rate = <2400000>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-lilac_common.dtsi b/arch/arm/boot/dts/qcom/msm8998-yoshino-lilac_common.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..d44a22001e2bd55d0b45cfe29e0e519e68cb6dc6 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-lilac_common.dtsi @@ -0,0 +1,668 @@ +/* arch/arm64/boot/dts/qcom/msm8998-lilac_common.dtsi + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include "msm8998-yoshino-common.dtsi" +#include "dsi-panel-lilac.dtsi" + +&soc { + /* I2C: BLSP5 */ + i2c@c179000 { /* BLSP1 QUP5 */ + synaptics_clearpad@2c { + preset_x_max = <719>; + preset_y_max = <1279>; + + /* Stamina Mode */ + stamina_mode_supported = <0x80000003>; + + /* F01_RMI_CTRL05: Doze Holdoff */ + doze_default_time = <35>; + doze_glove_mode_time = <35>; + doze_cover_mode_time = <35>; + }; + }; + + somc_pinctrl: somc_pinctrl { + pinctrl-1 = <&msm_gpio_89 &msm_gpio_102 &msm_gpio_103 &msm_gpio_105 &msm_gpio_106 + &msm_gpio_107 &msm_gpio_115 &msm_gpio_127 &msm_gpio_132 &msm_gpio_136>; + }; + + qcom,cci@ca0c000 { + qcom,camera@1 { + /delete-property/ cam_vio-supply; + /delete-property/ cam_vaf-supply; + qcom,cam-vreg-name = "cam_vdig", "cam_vio_gpio", "cam_vdig_gpio"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1352000 0 0>; + qcom,cam-vreg-max-voltage = <1352000 0 0>; + qcom,cam-vreg-op-mode = <105000 0 0>; + sony,eeprom_type = <2>; + }; + }; + + mdss_dsi0_pll: qcom,mdss_dsi_pll@c994400 { + qcom,dsi-pll-ssc-en; + qcom,dsi-pll-ssc-mode = "down-spread"; + qcom,ssc-frequency-hz = <31500>; + qcom,ssc-ppm = <5000>; + }; + /* I2C : BLSP8 */ + i2c_8: i2c@c1b6000 { /* BLSP2 QUP2 */ + tcs3490@72 { + rgbcir_vdd-supply = <&pm8998_l22>; + rgbcir-vio-supply = <&cam_vio_verg>; + ams,rgbcir-vdd-supply = <1>; + ams,rgbcir-gpio-vdd = <0>; + ams,rgbcir-vio-supply = <1>; + }; + tof_sensor@52 { + tof_avdd-supply = <&pm8998_l22>; + sony,tof-avdd-supply = <1>; + }; + }; +}; + +&sony_camera_module_1 { + module_names = "GENERIC", "LGI08BS0", "CHI08BS0"; + default_module_name = "LGI08BS0"; + /delete-node/ SOI13BS1; + /delete-node/ SEM13BS1; + + GENERIC { + mount_angle = <90>; + sensor_rotation = <0>; + sensor_facing = <1>; + sensor_config_delay_num = <13>; + sensor_config_delay = <3 3 3 3 3 3 3 3 3 3 3 3 3>; + temperature_check_skip_num = <0>; + total_pixel_number_w = <3296>; + total_pixel_number_h = <2528>; + active_pixel_number_x = <8>; + active_pixel_number_y = <8>; + active_pixel_number_w = <3280>; + active_pixel_number_h = <2464>; + min_focus_distance = <0>; + hyper_focal_distance = <715>; + diagonal_len = "4.595"; + unit_cell_size_w = "1.12"; + unit_cell_size_h = "1.12"; + min_f_number = "2.4"; + max_f_number = "2.4"; + min_focus_pos = <1>; + max_focus_pos = <1024>; + min_focus_dac = <0x0000>; + max_focus_dac = <0x03FF>; + focus_inf_range_offset = <0x24>; + focus_macro_range_offset = <0x24>; + focus_lens_stroke_inf_to_1m = <0x09>; + focus_lens_stroke_1m_to_macro = <0x55>; + focus_lens_stroke_inf_to_macro = <0x5E>; + focus_calc_type = <0x01>; + focus_wob_time = <0>; + has_3a = <0>; + has_focus_actuator = <0>; + need_standby_af = <0>; + i2c_freq_mode = <1>; + has_pdaf = <0>; + has_rs = <1>; + has_multi_output = <0>; + has_super_slow = <0>; + has_sub_sensor = <0>; + has_aube = <0>; + has_flicker_detector = <0>; + has_hdr = <0>; + has_seamless_mode_change = <0>; + has_gph = <0>; + pdaf_free_area_num = <0>; + pdaf_fixed_area_size_w = <0>; + pdaf_fixed_area_size_h = <0>; + pll_num = <26>; + pll = <1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134>; + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VDIG_GPIO", + "CAM_VDIG", + "CAM_VIO_GPIO", + "EXIT"; + CAM_VDIG = <0 0xFFFFFFFF 0 2>; + /delete-property/ CAM_VIO; + /delete-property/ CAM_VAF; + CAM_VIO_GPIO = <7 0xFFFFFFFF 0 1>; + GPIO_RESET = <5 0x0 0 5>; + CAM_VDIG_GPIO = <6 0xFFFFFFFF 0 5>; + CAM_CLK = <10 0xFFFFFFFF 0 0>; + EXIT = <12 0x0 0 0>; + }; + power_on { + commands = + "CAM_VDIG", + "CAM_VIO_GPIO", + "CAM_VDIG_GPIO", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + CAM_VDIG = <0 1352 105000 0>; + /delete-property/ CAM_VIO; + /delete-property/ CAM_VAF; + CAM_VIO_GPIO = <7 0 0 1>; + GPIO_RESET = <5 1 0 1>; + CAM_VDIG_GPIO = <6 0 0 1>; + CAM_CLK = <10 0 0 1>; + EXIT = <12 0x0 0 0>; + }; + }; + LGI08BS0 { + mount_angle = <90>; + sensor_rotation = <0>; + sensor_facing = <1>; + sensor_config_delay_num = <13>; + sensor_config_delay = <3 3 3 3 3 3 3 3 3 3 3 3 3>; + temperature_check_skip_num = <0>; + total_pixel_number_w = <3296>; + total_pixel_number_h = <2528>; + active_pixel_number_x = <8>; + active_pixel_number_y = <8>; + active_pixel_number_w = <3280>; + active_pixel_number_h = <2464>; + min_focus_distance = <0>; + hyper_focal_distance = <715>; + diagonal_len = "4.595"; + unit_cell_size_w = "1.12"; + unit_cell_size_h = "1.12"; + min_f_number = "2.4"; + max_f_number = "2.4"; + min_focus_pos = <1>; + max_focus_pos = <1024>; + min_focus_dac = <0x0000>; + max_focus_dac = <0x03FF>; + focus_inf_range_offset = <0x24>; + focus_macro_range_offset = <0x24>; + focus_lens_stroke_inf_to_1m = <0x09>; + focus_lens_stroke_1m_to_macro = <0x55>; + focus_lens_stroke_inf_to_macro = <0x5E>; + focus_calc_type = <0x01>; + focus_wob_time = <0>; + has_3a = <0>; + has_focus_actuator = <0>; + need_standby_af = <0>; + i2c_freq_mode = <1>; + has_pdaf = <0>; + has_rs = <1>; + has_multi_output = <0>; + has_super_slow = <0>; + has_sub_sensor = <0>; + has_aube = <0>; + has_flicker_detector = <0>; + has_hdr = <0>; + has_seamless_mode_change = <0>; + has_gph = <0>; + pdaf_free_area_num = <0>; + pdaf_fixed_area_size_w = <0>; + pdaf_fixed_area_size_h = <0>; + pll_num = <26>; + pll = <1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134>; + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VDIG_GPIO", + "CAM_VDIG", + "CAM_VIO_GPIO", + "EXIT"; + CAM_VDIG = <0 0xFFFFFFFF 0 2>; + CAM_VIO_GPIO = <7 0xFFFFFFFF 0 1>; + GPIO_RESET = <5 0x0 0 5>; + CAM_VDIG_GPIO = <6 0xFFFFFFFF 0 5>; + CAM_CLK = <10 0xFFFFFFFF 0 0>; + EXIT = <12 0x0 0 0>; + }; + power_on { + commands = + "CAM_VDIG", + "CAM_VIO_GPIO", + "CAM_VDIG_GPIO", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + CAM_VDIG = <0 1352 105000 0>; + CAM_VIO_GPIO = <7 0 0 1>; + GPIO_RESET = <5 1 0 1>; + CAM_VDIG_GPIO = <6 0 0 1>; + CAM_CLK = <10 0 0 1>; + EXIT = <12 0x0 0 0>; + }; + }; + CHI08BS0 { + mount_angle = <90>; + sensor_rotation = <0>; + sensor_facing = <1>; + sensor_config_delay_num = <13>; + sensor_config_delay = <3 3 3 3 3 3 3 3 3 3 3 3 3>; + temperature_check_skip_num = <0>; + total_pixel_number_w = <3296>; + total_pixel_number_h = <2528>; + active_pixel_number_x = <8>; + active_pixel_number_y = <8>; + active_pixel_number_w = <3280>; + active_pixel_number_h = <2464>; + min_focus_distance = <0>; + hyper_focal_distance = <715>; + diagonal_len = "4.595"; + unit_cell_size_w = "1.12"; + unit_cell_size_h = "1.12"; + min_f_number = "2.4"; + max_f_number = "2.4"; + min_focus_pos = <1>; + max_focus_pos = <1024>; + min_focus_dac = <0x0000>; + max_focus_dac = <0x03FF>; + focus_inf_range_offset = <0x24>; + focus_macro_range_offset = <0x24>; + focus_lens_stroke_inf_to_1m = <0x09>; + focus_lens_stroke_1m_to_macro = <0x55>; + focus_lens_stroke_inf_to_macro = <0x5E>; + focus_calc_type = <0x01>; + focus_wob_time = <0>; + has_3a = <0>; + has_focus_actuator = <0>; + need_standby_af = <0>; + i2c_freq_mode = <1>; + has_pdaf = <0>; + has_rs = <1>; + has_multi_output = <0>; + has_super_slow = <0>; + has_sub_sensor = <0>; + has_aube = <0>; + has_flicker_detector = <0>; + has_hdr = <0>; + has_seamless_mode_change = <0>; + has_gph = <0>; + pdaf_free_area_num = <0>; + pdaf_fixed_area_size_w = <0>; + pdaf_fixed_area_size_h = <0>; + pll_num = <26>; + pll = <1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134 1134>; + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VDIG_GPIO", + "CAM_VDIG", + "CAM_VIO_GPIO", + "EXIT"; + CAM_VDIG = <0 0xFFFFFFFF 0 2>; + CAM_VIO_GPIO = <7 0xFFFFFFFF 0 1>; + GPIO_RESET = <5 0x0 0 5>; + CAM_VDIG_GPIO = <6 0xFFFFFFFF 0 5>; + CAM_CLK = <10 0xFFFFFFFF 0 0>; + EXIT = <12 0x0 0 0>; + }; + power_on { + commands = + "CAM_VDIG", + "CAM_VIO_GPIO", + "CAM_VDIG_GPIO", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + CAM_VDIG = <0 1352 105000 0>; + CAM_VIO_GPIO = <7 0 0 1>; + GPIO_RESET = <5 1 0 1>; + CAM_VDIG_GPIO = <6 0 0 1>; + CAM_CLK = <10 0 0 1>; + EXIT = <12 0x0 0 0>; + }; + }; +}; + +&pm8998_gpios { + /* GPIO_20: DTV_PWR_EN */ + gpio@d300 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <1>; /* Out */ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <1>; /* 1.8V */ + qcom,out-strength = <1>; /* Low */ + qcom,invert = <0>; /* Low */ + qcom,master-en = <1>; /* Enable */ + status = "okay"; + }; +}; + +&pmi8998_gpios { + /* GPIO_1: MAIN_CAM_PWR_IO_EN */ + gpio@c000 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <1>; /* Out */ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <1>; /* 1.8V */ + qcom,out-strength = <1>; /* Low */ + qcom,invert = <0>; /* Low */ + qcom,master-en = <1>; /* Enable */ + status = "okay"; + }; + + /* GPIO_8: NC */ + gpio@c700 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_10: NC */ + gpio@c900 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; +}; + +&pm8005_gpios { + /* GPIO_1: NC */ + gpio@c000 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; +}; + +&usb3 { + dwc3@a800000 { + maximum-speed = "high-speed"; + }; +}; + +/* Regulator config */ +&pm8998_l22 { + qcom,regulator-type = <0>; /* LDO */ + qcom,init-enable = <0>; + qcom,init-ldo-mode = <1>; + qcom,init-pin-ctrl-enable = <0>; + qcom,init-pin-ctrl-mode = <0>; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + qcom,init-voltage = <2800000>; + status = "okay"; +}; + +&pm8998_lvs1 { + status = "disabled"; +}; + +&pmi8998_wled { + qcom,led-strings-list = [00 01]; + qcom,fs-curr-ua = <17500>; + somc,area_count_table_size = <21>; + somc,area_count_table = <0 137 273 410 546 683 819 956 + 1092 1229 1365 1638 1911 2184 2457 + 2730 3003 3276 3549 3822 4095>; + somc,init-br-ua = <8500>; +}; + +&red_led { + somc,color_variation_max_num = <4>; + somc,max_current = < + 34 510 419 + 35 498 348 + 36 498 348 + 37 498 348>; +}; + +&green_led { + somc,color_variation_max_num = <4>; + somc,max_current = < + 34 400 500 + 35 360 500 + 36 360 500 + 37 360 500>; +}; + +&blue_led { + somc,color_variation_max_num = <4>; + somc,max_current = < + 34 510 290 + 35 500 228 + 36 500 228 + 37 500 228>; +}; + +&tlmm { + /* GPIO_89 : TS_RESET_N */ + msm_gpio_89: msm_gpio_89 { + mux { + pins = "gpio89"; + function = "gpio"; + }; + + config { + pins = "gpio89"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_102 : NC */ + msm_gpio_102: msm_gpio_102{ + mux { + pins = "gpio102"; + function = "gpio"; + }; + + config { + pins = "gpio102"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_103 : NC */ + msm_gpio_103: msm_gpio_103{ + mux { + pins = "gpio103"; + function = "gpio"; + }; + + config { + pins = "gpio103"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_105 : NC */ + msm_gpio_105: msm_gpio_105{ + mux { + pins = "gpio105"; + function = "gpio"; + }; + + config { + pins = "gpio105"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_106 : NC */ + msm_gpio_106: msm_gpio_106{ + mux { + pins = "gpio106"; + function = "gpio"; + }; + + config { + pins = "gpio106"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_107 : NC */ + msm_gpio_107: msm_gpio_107{ + mux { + pins = "gpio107"; + function = "gpio"; + }; + + config { + pins = "gpio107"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_115 : NC */ + msm_gpio_115: msm_gpio_115{ + mux { + pins = "gpio115"; + function = "gpio"; + }; + + config { + pins = "gpio115"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_127 : NC */ + msm_gpio_127: msm_gpio_127{ + mux { + pins = "gpio127"; + function = "gpio"; + }; + + config { + pins = "gpio127"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_132: NC */ + msm_gpio_132: msm_gpio_132{ + mux { + pins = "gpio132"; + function = "gpio"; + }; + + config { + pins = "gpio132"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_136: NC */ + msm_gpio_136: msm_gpio_136{ + mux { + pins = "gpio136"; + function = "gpio"; + }; + + config { + pins = "gpio136"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_default_panel>; +}; + +&mdss_dsi1 { + status = "disabled"; + /delete-property/ qcom,dsi-pref-prim-pan; +}; + +&vendor { + yoshino_lilac_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <17>; + #include "fg-gen3-batterydata-lilac-send-4380mv-2729mah.dtsi" + #include "fg-gen3-batterydata-lilac-send-4357mv-2688mah.dtsi" + #include "fg-gen3-batterydata-lilac-send-4335mv-2647mah.dtsi" + #include "fg-gen3-batterydata-lilac-send-4297mv-2539mah.dtsi" + #include "fg-gen3-batterydata-lilac-send-4245mv-2425mah.dtsi" + }; +}; + +&pmi8998_fg { + qcom,battery-data = <&yoshino_lilac_batterydata>; + qcom,fg-sys-term-current = <(-160)>; + qcom,fg-chg-term-current = <135>; + somc,rated-capacity-uah = <2650000>; + somc,initial-capacity-uah = <2820000>; +}; + +&pmi8998_charger { + qcom,fv-max-uv = <4380000>; + somc,product-icl-ua = <2000000>; + somc,high-voltage-icl-ua = <1700000>; + somc,thermal-fcc-ua = <2025000 2000000 1950000 + 1800000 1500000 1300000 + 1300000 1000000 800000 + 700000 700000 500000 + 300000 100000 0>; + somc,thermal-lo-volt-icl-ua = <2000000 1500000 1500000 + 1500000 1300000 1100000 + 1100000 1000000 900000 + 900000 900000 500000 + 200000 100000 0>; + somc,thermal-hi-volt-icl-ua = <1700000 1700000 1700000 + 1700000 1500000 1500000 + 1000000 1000000 1000000 + 1000000 600000 500000 + 200000 100000 0>; + somc,jeita-use-aux-therm; + somc,jeita-aux-thresh-hot = <530>; + somc,jeita-aux-thresh-warm = <430>; + somc,jeita-warm-fcc-ua = <800000>; + somc,jeita-cool-fcc-ua = <800000>; +}; + +&pmi8998_pdphy { + qcom,default-sink-caps = <5000 2000>, /* 5V @ 2A */ + <9000 1700>; /* 9V @ 1.7A */ +}; + +&spmi_bus { + qcom,pmi8998@3 { + labibb: qpnp-labibb-regulator { + ibb_regulator: qcom,ibb@dc00 { + qcom,qpnp-ibb-slew-rate = <12000>; + }; + lab_regulator: qcom,lab@de00 { + qcom,qpnp-lab-slew-rate = <5000>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-lilac_generic.dts b/arch/arm/boot/dts/qcom/msm8998-yoshino-lilac_generic.dts new file mode 100644 index 0000000000000000000000000000000000000000..9874839a7bd5fe3ff7c490dd9be8f055f1eeeba3 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-lilac_generic.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-lilac_generic.dtsi" + +/ { + model = "SoMC Lilac-ROW(MSM8998)"; + compatible = "somc,lilac-row", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-lilac_generic.dtsi b/arch/arm/boot/dts/qcom/msm8998-yoshino-lilac_generic.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..9305cca2ac8fa7301d3c5d7adef82fcac0105b96 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-lilac_generic.dtsi @@ -0,0 +1,66 @@ +/* arch/arm64/boot/dts/qcom/msm8998-lilac_generic.dtsi + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include "msm8998-yoshino-lilac_common.dtsi" + +&soc { + /* I2C : BLSP7 */ + i2c@c1b5000 { /* BLSP2 QUP1 */ + nfc@28 { + compatible = "nxp,pn553"; + reg = <0x28>; + interrupt-parent = <&tlmm>; + interrupts = <92 0x1>; + nxp,irq_gpio = <&tlmm 92 0x00>; + nxp,dwld_en = <&tlmm 93 0x00>; + nxp,ven = <&tlmm 12 0x01>; + qcom,clk-src = "BBCLK2"; + qcom,clk-gpio = <&pm8998_gpios 21 0x00>; + clocks = <&clock_gcc clk_ln_bb_clk3_pin>; + clock-names = "nfc_clk"; + }; + }; + + nfc,pm-ops { + compatible = "sony,pn553-pm-ops"; + }; + + somc_pinctrl: somc_pinctrl { + /* If variant specific default setting is needed, + fill pinctrl-2 value in .dtsi */ + pinctrl-2 = <&msm_gpio_92>; + }; +}; + +&tlmm { + /* GPIO_92 : NFC_IRQ_FELICA_INT_N */ + msm_gpio_92: msm_gpio_92 { + mux { + pins = "gpio92"; + function = "gpio"; + }; + + config { + pins = "gpio92"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_common.dtsi b/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_common.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..dd6bd6ef14d28f6137bf8ab6b29a8f8d4b799cf2 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_common.dtsi @@ -0,0 +1,353 @@ +/* arch/arm64/boot/dts/qcom/msm8998-maple_common.dtsi + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include "msm8998-yoshino-common.dtsi" +#include "dsi-panel-maple.dtsi" + +&firmware { + android { + fstab { + vendor { + status = "disabled"; + }; + }; + }; +}; + +&soc { + + somc_pinctrl: somc_pinctrl { + /* If product common default setting is needed, + fill pinctrl-1 value in _common.dtsi */ + pinctrl-1 = <&msm_gpio_102 &msm_gpio_103 &msm_gpio_115 + &msm_gpio_127 &msm_gpio_132 &msm_gpio_133 &msm_gpio_136>; + }; + + /* I2C: BLSP5 */ + i2c@c179000 { /* BLSP1 QUP5 */ + synaptics_clearpad@2c { + chip_id = <0x38>; + + /* Stamina Mode */ + stamina_mode_supported = <0x80000003>; + + /* F01_RMI_CTRL05: Doze Holdoff */ + doze_default_time = <35>; + doze_glove_mode_time = <35>; + doze_cover_mode_time = <35>; + }; + }; + + mdss_dsi0_pll: qcom,mdss_dsi_pll@c994400 { + qcom,dsi-pll-ssc-en; + qcom,dsi-pll-ssc-mode = "down-spread"; + qcom,ssc-frequency-hz = <31500>; + qcom,ssc-ppm = <5000>; + }; + + mdss_dsi1_pll: qcom,mdss_dsi_pll@c996400 { + qcom,dsi-pll-ssc-en; + qcom,dsi-pll-ssc-mode = "down-spread"; + qcom,ssc-frequency-hz = <31500>; + qcom,ssc-ppm = <5000>; + }; + +}; + +&qusb_phy0 { + qcom,efuse-offset = <0x00000005>; + qcom,qusb-phy-init-seq = + /* */ + <0x13 0x04 /* analog_controls_two */ + 0x7c 0x18c /* pll_clock_inverter */ + 0x80 0x2c /* pll_cmode */ + 0x0a 0x184 /* pll_lock_delay */ + 0x05 0x23c /* tune1 */ + 0x0F 0x240 /* tune2 */ + 0xD4 0x244 /* tune3 */ + 0x04 0x248 /* tune4 */ + 0x19 0xb4>; /* digital_timers_two */ + + qcom,qusb-phy-host-init-seq = + /* */ + <0x63 0x210 /* pwr_ctrl1 */ + 0x13 0x04 /* analog_controls_two */ + 0x7c 0x18c /* pll_clock_inverter */ + 0x80 0x2c /* pll_cmode */ + 0x0a 0x184 /* pll_lock_delay */ + 0x8c 0x21c /* imp_ctrl1 */ + 0x05 0x23c /* tune1 */ + 0x0F 0x240 /* tune2 */ + 0xff 0x218 /* rescode_contrpl */ + 0x62 0x210>; /* pwr_ctrl1 */ +}; + +&pmi8998_gpios { + /* GPIO_1: MAIN_CAM_PWR_IO_EN */ + gpio@c000 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <1>; /* Out */ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <1>; /* 1.8V */ + qcom,out-strength = <1>; /* Low */ + qcom,invert = <0>; /* Low */ + qcom,master-en = <1>; /* Enable */ + status = "okay"; + }; + + /* GPIO_10: 4K_DISP_DCDC_EN */ + gpio@c900 { + qcom,invert = <1>; /* High */ + }; +}; + +&tlmm { + /* GPIO_102 : NC */ + msm_gpio_102: msm_gpio_102{ + mux { + pins = "gpio102"; + function = "gpio"; + }; + + config { + pins = "gpio102"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_103 : NC */ + msm_gpio_103: msm_gpio_103{ + mux { + pins = "gpio103"; + function = "gpio"; + }; + + config { + pins = "gpio103"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_115 : NC */ + msm_gpio_115: msm_gpio_115{ + mux { + pins = "gpio115"; + function = "gpio"; + }; + + config { + pins = "gpio115"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_127: NC */ + msm_gpio_127: msm_gpio_127{ + mux { + pins = "gpio127"; + function = "gpio"; + }; + + config { + pins = "gpio127"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_132: NC */ + msm_gpio_132: msm_gpio_132{ + mux { + pins = "gpio132"; + function = "gpio"; + }; + + config { + pins = "gpio132"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_133: TS_VDDIO_EN*/ + msm_gpio_133: msm_gpio_133{ + mux { + pins = "gpio133"; + function = "gpio"; + }; + + config { + pins = "gpio133"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_136: NC */ + msm_gpio_136: msm_gpio_136{ + mux { + pins = "gpio136"; + function = "gpio"; + }; + + config { + pins = "gpio136"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; +}; + +&vendor { + yoshino_maple_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <17>; + #include "fg-gen3-batterydata-maple-send-4350mv-3230mah.dtsi" + #include "fg-gen3-batterydata-maple-send-4300mv-3167mah.dtsi" + }; +}; + +&pm8998_l14 { + qcom,regulator-type = <0>; /* LDO */ + qcom,init-enable = <0>; + qcom,init-ldo-mode = <1>; + qcom,init-pin-ctrl-enable = <0>; + qcom,init-pin-ctrl-mode = <0>; + regulator-min-microvolt = <1850000>; + regulator-max-microvolt = <1850000>; + qcom,init-voltage = <1850000>; + status = "okay"; +}; + +&pmi8998_fg { + qcom,battery-data = <&yoshino_maple_batterydata>; + qcom,fg-sys-term-current = <(-186)>; + qcom,fg-chg-term-current = <161>; + somc,rated-capacity-uah = <3230000>; + somc,initial-capacity-uah = <3424000>; +}; + +&pmi8998_charger { + qcom,fv-max-uv = <4350000>; + somc,product-icl-ua = <1700000>; + somc,high-voltage-icl-ua = <1700000>; + somc,thermal-fcc-ua = <2400000 2200000 1950000 + 1800000 1500000 1300000 + 1300000 1000000 800000 + 700000 700000 500000 + 300000 100000 0>; + somc,thermal-lo-volt-icl-ua = <1500000 1500000 1500000 + 1500000 1300000 1100000 + 1100000 1000000 900000 + 900000 900000 500000 + 200000 100000 0>; + somc,thermal-hi-volt-icl-ua = <1700000 1700000 1700000 + 1700000 1500000 1500000 + 1000000 1000000 1000000 + 1000000 600000 500000 + 200000 100000 0>; + somc,jeita-use-aux-therm; + somc,jeita-aux-thresh-hot = <900>; + somc,jeita-aux-thresh-warm = <700>; + somc,jeita-warm-fcc-ua = <800000>; + somc,jeita-cool-fcc-ua = <800000>; +}; + +&pmi8998_pdphy { + qcom,default-sink-caps = <5000 1500>, /* 5V @ 1.5A */ + <9000 1700>; /* 9V @ 1.7A */ +}; + +&mdss_dsi { + hw-config = "split_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_dual0_default_panel>; +}; + +&mdss_dsi1 { + status = "ok"; + qcom,dsi-pref-prim-pan = <&dsi_dual1_default_panel>; +}; + +&dsi_panel_pwr_supply_hybrid_incell { + qcom,panel-supply-entry@0 { + qcom,supply-min-voltage = <1850000>; + qcom,supply-max-voltage = <1850000>; + }; +}; + +&red_led { + somc,color_variation_max_num = <4>; + somc,max_current = < + 26 126 90 + 27 82 72 + 28 82 72 + 29 110 72>; +}; + +&green_led { + somc,color_variation_max_num = <4>; + somc,max_current = < + 26 86 136 + 27 80 160 + 28 82 160 + 29 78 160>; +}; + +&blue_led { + somc,color_variation_max_num = <4>; + somc,max_current = < + 26 307 100 + 27 305 118 + 28 285 118 + 29 277 118>; +}; + +&pmi8998_wled { + somc,calc-curr; + somc,area_count_table_size = <24>; + somc,area_count_table = <0 117 234 351 468 585 702 819 + 936 1053 1170 1404 1638 1872 2106 + 2340 2574 2808 3042 3276 3510 3744 + 3978 4095>; +}; + +&spmi_bus { + qcom,pmi8998@3 { + labibb: qpnp-labibb-regulator { + ibb_regulator: qcom,ibb@dc00 { + qcom,qpnp-ibb-slew-rate = <12000>; + }; + lab_regulator: qcom,lab@de00 { + qcom,qpnp-lab-slew-rate = <5000>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_dsds.dts b/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_dsds.dts new file mode 100644 index 0000000000000000000000000000000000000000..1f4314454cc960bcb85c15f134d65f9f1b833b93 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_dsds.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-maple_dsds.dtsi" + +/ { + model = "SoMC Maple-DSDS(MSM8998)"; + compatible = "somc,maple-dsds", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_dsds.dtsi b/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_dsds.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..353e1380cad81d118105b271fc4e062aaa3e5614 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_dsds.dtsi @@ -0,0 +1,35 @@ +/* arch/arm64/boot/dts/qcom/msm8998-maple_dsds.dtsi + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include "msm8998-yoshino-maple_common.dtsi" +#include "msm8998-yoshino-maple_generic.dtsi" + +&soc { + sim_detect { + sim2_det { + label = "sim2-detection"; + gpios = <&tlmm 63 0x0>; + debounce-interval = <10>; + }; + }; +}; + +&sdhc_2 { + uim2-gpios = <&tlmm 39 GPIO_ACTIVE_HIGH>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_generic.dts b/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_generic.dts new file mode 100644 index 0000000000000000000000000000000000000000..3339143689ac2e97935e096367650f631795cf18 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_generic.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-maple_generic.dtsi" + +/ { + model = "SoMC Maple-ROW(MSM8998)"; + compatible = "somc,maple-row", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_generic.dtsi b/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_generic.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..676b3f695a4b4a6be6ea566d2ac4b02bd96f1eda --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-maple_generic.dtsi @@ -0,0 +1,43 @@ +/* arch/arm64/boot/dts/qcom/msm8998-maple_generic.dtsi + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include "msm8998-yoshino-maple_common.dtsi" + +&soc { + /* I2C : BLSP7 */ + i2c@c1b5000 { /* BLSP2 QUP1 */ + nfc@28 { + compatible = "nxp,pn553"; + reg = <0x28>; + interrupt-parent = <&tlmm>; + interrupts = <92 0x1>; + nxp,irq_gpio = <&tlmm 92 0x00>; + nxp,dwld_en = <&tlmm 93 0x00>; + nxp,ven = <&tlmm 12 0x01>; + qcom,clk-src = "BBCLK2"; + qcom,clk-gpio = <&pm8998_gpios 21 0x00>; + clocks = <&clock_gcc clk_ln_bb_clk3_pin>; + clock-names = "nfc_clk"; + }; + }; + + nfc,pm-ops { + compatible = "sony,pn553-pm-ops"; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_common.dtsi b/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_common.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..09ade3662043bb536e7bda23d4d1fe0712dcc605 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_common.dtsi @@ -0,0 +1,431 @@ +/* arch/arm64/boot/dts/qcom/msm8998-poplar_common.dtsi + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include "msm8998-yoshino-common.dtsi" +#include "dsi-panel-poplar.dtsi" + +&soc { + /* I2C: BLSP5 */ + i2c@c179000 { /* BLSP1 QUP5 */ + synaptics_clearpad@2c { + preset_x_max = <1079>; + preset_y_max = <1919>; + + /* Stamina Mode */ + stamina_mode_supported = <0x80000003>; + + /* F01_RMI_CTRL05: Doze Holdoff */ + doze_default_time = <35>; + doze_glove_mode_time = <35>; + doze_cover_mode_time = <35>; + }; + }; + + somc_pinctrl: somc_pinctrl { + pinctrl-1 = <&msm_gpio_89 &msm_gpio_102 &msm_gpio_103 &msm_gpio_115 &msm_gpio_132 + &msm_gpio_133>; + }; + qcom,cci@ca0c000 { + qcom,camera@1 { + /delete-property/ cam_vio-supply; + qcom,cam-vreg-name = "cam_vaf", "cam_vdig", "cam_vio_gpio", "cam_vdig_gpio"; + qcom,cam-vreg-type = <0 0 0 0>; + qcom,cam-vreg-min-voltage = <2700000 1352000 0 0>; + qcom,cam-vreg-max-voltage = <2700000 1352000 0 0>; + qcom,cam-vreg-op-mode = <150000 105000 0 0>; + }; + }; + + mdss_dsi0_pll: qcom,mdss_dsi_pll@c994400 { + qcom,dsi-pll-ssc-en; + qcom,dsi-pll-ssc-mode = "down-spread"; + qcom,ssc-frequency-hz = <31500>; + qcom,ssc-ppm = <5000>; + + }; +}; + +&sony_camera_module_1 { + GENERIC { + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VDIG_GPIO", + "CAM_VDIG", + "CAM_VIO_GPIO", + "CAM_VAF", + "EXIT"; + /delete-property/ CAM_VIO; + }; + power_on { + commands = + "CAM_VDIG", + "CAM_VIO_GPIO", + "CAM_VAF", + "CAM_VDIG_GPIO", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + /delete-property/ CAM_VIO; + }; + }; + SOI13BS1 { + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VDIG_GPIO", + "CAM_VDIG", + "CAM_VIO_GPIO", + "CAM_VAF", + "EXIT"; + /delete-property/ CAM_VIO; + }; + power_on { + commands = + "CAM_VDIG", + "CAM_VIO_GPIO", + "CAM_VAF", + "CAM_VDIG_GPIO", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + /delete-property/ CAM_VIO; + }; + }; + SEM13BS1 { + power_off { + commands = + "GPIO_RESET", + "CAM_CLK", + "CAM_VDIG_GPIO", + "CAM_VDIG", + "CAM_VIO_GPIO", + "CAM_VAF", + "EXIT"; + /delete-property/ CAM_VIO; + }; + power_on { + commands = + "CAM_VDIG", + "CAM_VIO_GPIO", + "CAM_VAF", + "CAM_VDIG_GPIO", + "CAM_CLK", + "GPIO_RESET", + "EXIT"; + /delete-property/ CAM_VIO; + }; + }; +}; + +&pmi8998_gpios { + /* GPIO_1: MAIN_CAM_PWR_IO_EN */ + gpio@c000 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <1>; /* Out */ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <1>; /* 1.8V */ + qcom,out-strength = <1>; /* Low */ + qcom,invert = <0>; /* Low */ + qcom,master-en = <1>; /* Enable */ + status = "okay"; + }; +}; + +&pmi8998_gpios { + /* GPIO_1: MAIN_CAM_PWR_IO_EN */ + gpio@c000 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <1>; /* Out */ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <1>; /* 1.8V */ + qcom,out-strength = <1>; /* Low */ + qcom,invert = <0>; /* Low */ + qcom,master-en = <1>; /* Enable */ + status = "okay"; + }; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_default_panel>; +}; + +&mdss_dsi1 { + status = "disabled"; + /delete-property/ qcom,dsi-pref-prim-pan; +}; + +&vendor { + yoshino_poplar_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <17>; + #include "fg-gen3-batterydata-poplar-send-4380mv-2650mah.dtsi" + #include "fg-gen3-batterydata-poplar-tdk-4380mv-2650mah.dtsi" + #include "fg-gen3-batterydata-poplar-send-4357mv-2688mah.dtsi" + #include "fg-gen3-batterydata-poplar-send-4335mv-2647mah.dtsi" + #include "fg-gen3-batterydata-poplar-send-4297mv-2465mah.dtsi" + #include "fg-gen3-batterydata-poplar-send-4245mv-2425mah.dtsi" + }; +}; + +&pmi8998_fg { + qcom,battery-data = <&yoshino_poplar_batterydata>; + qcom,fg-sys-term-current = <(-160)>; + qcom,fg-chg-term-current = <135>; + somc,rated-capacity-uah = <2650000>; + somc,initial-capacity-uah = <2792000>; +}; + +&pmi8998_charger { + qcom,fv-max-uv = <4380000>; + somc,product-icl-ua = <2000000>; + somc,high-voltage-icl-ua = <1700000>; + somc,thermal-fcc-ua = <2025000 2000000 1950000 + 1800000 1500000 1300000 + 1300000 1000000 800000 + 700000 700000 500000 + 300000 100000 0>; + somc,thermal-lo-volt-icl-ua = <2000000 1500000 1500000 + 1500000 1300000 1100000 + 1100000 1000000 900000 + 900000 900000 500000 + 200000 100000 0>; + somc,thermal-hi-volt-icl-ua = <1700000 1700000 1700000 + 1700000 1500000 1500000 + 1000000 1000000 1000000 + 1000000 600000 500000 + 200000 100000 0>; + somc,jeita-use-aux-therm; + somc,jeita-aux-thresh-hot = <900>; + somc,jeita-aux-thresh-warm = <700>; + somc,jeita-warm-fcc-ua = <800000>; + somc,jeita-cool-fcc-ua = <800000>; +}; + +&pmi8998_pdphy { + qcom,default-sink-caps = <5000 2000>, /* 5V @ 2A */ + <9000 1700>; /* 9V @ 1.7A */ +}; + +&pm8998_gpios { + /* GPIO_20: DTV_PWR_EN */ + gpio@d300 { + qcom,src-sel = <0>; /* GPIO */ + qcom,mode = <1>; /* Out */ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <1>; /* 1.8V */ + qcom,out-strength = <1>; /* Low */ + qcom,invert = <0>; /* Low */ + qcom,master-en = <1>; /* Enable */ + status = "okay"; + }; +}; + +&pmi8998_gpios { + /* GPIO_8: NC */ + gpio@c700 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; + + /* GPIO_10: NC */ + gpio@c900 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; +}; + +&pm8005_gpios { + /* GPIO_1: NC */ + gpio@c000 { + qcom,master-en = <0>; /* Disable */ + status = "okay"; + }; +}; + +/* Regulator config */ +&pm8998_l18 { + qcom,regulator-type = <0>; /* LDO */ + qcom,init-enable = <1>; + qcom,init-ldo-mode = <1>; + qcom,init-pin-ctrl-enable = <0>; + qcom,init-pin-ctrl-mode = <0>; + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + qcom,init-voltage = <2850000>; + regulator-always-on; + status = "okay"; +}; + +&pm8998_l22 { + qcom,regulator-type = <0>; /* LDO */ + qcom,init-enable = <0>; + qcom,init-ldo-mode = <1>; + qcom,init-pin-ctrl-enable = <0>; + qcom,init-pin-ctrl-mode = <0>; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2700000>; + qcom,init-voltage = <2700000>; + status = "okay"; +}; + +&tlmm { + /* GPIO_89 : TS_RESET_N */ + msm_gpio_89: msm_gpio_89 { + mux { + pins = "gpio89"; + function = "gpio"; + }; + + config { + pins = "gpio89"; + drive-strength = <2>; + bias-disable; + }; + }; + + /* GPIO_102 : NC */ + msm_gpio_102: msm_gpio_102{ + mux { + pins = "gpio102"; + function = "gpio"; + }; + + config { + pins = "gpio102"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_103 : NC */ + msm_gpio_103: msm_gpio_103{ + mux { + pins = "gpio103"; + function = "gpio"; + }; + + config { + pins = "gpio103"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_115 : NC */ + msm_gpio_115: msm_gpio_115{ + mux { + pins = "gpio115"; + function = "gpio"; + }; + + config { + pins = "gpio115"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_132: NC */ + msm_gpio_132: msm_gpio_132{ + mux { + pins = "gpio132"; + function = "gpio"; + }; + + config { + pins = "gpio132"; + drive-strength = <2>; + bias-disable; + output-low; + }; + }; + + /* GPIO_133: TS_VDDIO_EN*/ + msm_gpio_133: msm_gpio_133{ + mux { + pins = "gpio133"; + function = "gpio"; + }; + + config { + pins = "gpio133"; + drive-strength = <2>; + bias-disable; + }; + }; +}; + +&pmi8998_wled { + qcom,ilim-ma = <620>; + qcom,fs-curr-ua = <20000>; + somc,area_count_table_size = <24>; + somc,area_count_table = <0 117 234 351 468 585 702 819 + 936 1053 1170 1404 1638 1872 2106 + 2340 2574 2808 3042 3276 3510 + 3744 3978 4095>; +}; + +&red_led { + somc,color_variation_max_num = <4>; + somc,max_current = < + 30 50 28 + 31 30 22 + 32 30 22 + 33 30 22>; +}; + +&green_led { + somc,color_variation_max_num = <4>; + somc,max_current = < + 30 18 28 + 31 16 30 + 32 16 30 + 33 16 30>; +}; + +&blue_led { + somc,color_variation_max_num = <4>; + somc,max_current = < + 30 56 12 + 31 44 12 + 32 52 12 + 33 44 12>; +}; + +&spmi_bus { + qcom,pmi8998@3 { + labibb: qpnp-labibb-regulator { + ibb_regulator: qcom,ibb@dc00 { + qcom,qpnp-ibb-slew-rate = <12000>; + }; + lab_regulator: qcom,lab@de00 { + qcom,qpnp-lab-slew-rate = <5000>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_dsds.dts b/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_dsds.dts new file mode 100644 index 0000000000000000000000000000000000000000..c3fda1cd86dcfb2b76072c25a67c081b2fac82f1 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_dsds.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-poplar_dsds.dtsi" + +/ { + model = "SoMC Poplar-DSDS(MSM8998)"; + compatible = "somc,poplar-dsds", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_dsds.dtsi b/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_dsds.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..4f4981f7dce77e0c1e962b8e2d08eb35a1776291 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_dsds.dtsi @@ -0,0 +1,35 @@ +/* arch/arm64/boot/dts/qcom/msm8998-yoshino-poplar_dsds.dtsi + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +#include "msm8998-yoshino-poplar_common.dtsi" +#include "msm8998-yoshino-poplar_generic.dtsi" + +&soc { + sim_detect { + sim2_det { + label = "sim2-detection"; + gpios = <&tlmm 63 0x0>; + debounce-interval = <10>; + }; + }; +}; + +&sdhc_2 { + uim2-gpios = <&tlmm 39 GPIO_ACTIVE_HIGH>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_generic.dts b/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_generic.dts new file mode 100644 index 0000000000000000000000000000000000000000..ec1276fee7ac638ebed588c6b2e6deffd21f9a4e --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_generic.dts @@ -0,0 +1,32 @@ +/* Copyright (c) 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +/dts-v1/; + +#include "msm8998.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "msm8998-yoshino-poplar_generic.dtsi" + +/ { + model = "SoMC Poplar-ROW(MSM8998)"; + compatible = "somc,poplar-row", "qcom,msm8998"; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_generic.dtsi b/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_generic.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..9fee06a36f432ff3b4a5f8ce1695136f8da653bf --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-yoshino-poplar_generic.dtsi @@ -0,0 +1,66 @@ +/* arch/arm64/boot/dts/qcom/msm8998-poplar_generic.dtsi + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include "msm8998-yoshino-poplar_common.dtsi" + +&soc { + /* I2C : BLSP7 */ + i2c@c1b5000 { /* BLSP2 QUP1 */ + nfc@28 { + compatible = "nxp,pn553"; + reg = <0x28>; + interrupt-parent = <&tlmm>; + interrupts = <92 0x1>; + nxp,irq_gpio = <&tlmm 92 0x00>; + nxp,dwld_en = <&tlmm 93 0x00>; + nxp,ven = <&tlmm 12 0x01>; + qcom,clk-src = "BBCLK2"; + qcom,clk-gpio = <&pm8998_gpios 21 0x00>; + clocks = <&clock_gcc clk_ln_bb_clk3_pin>; + clock-names = "nfc_clk"; + }; + }; + + nfc,pm-ops { + compatible = "sony,pn553-pm-ops"; + }; + + somc_pinctrl: somc_pinctrl { + /* If variant specific default setting is needed, + fill pinctrl-2 value in .dtsi */ + pinctrl-2 = <&msm_gpio_92>; + }; +}; + +&tlmm { + /* GPIO_92 : NFC_IRQ_FELICA_INT_N */ + msm_gpio_92: msm_gpio_92 { + mux { + pins = "gpio92"; + function = "gpio"; + }; + + config { + pins = "gpio92"; + drive-strength = <2>; + bias-pull-down; + input-enable; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 6990099978c6eed432e8a1aa97d6d70fe919bb48..bbeaca888fafa58984f91d56efbb44fa226ea9ba 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "skeleton64.dtsi" #include @@ -394,8 +399,8 @@ linux,cma-default; }; - cont_splash_mem: splash_region@9d600000 { - reg = <0x0 0x9d600000 0x0 0x02400000>; + cont_splash_mem: splash_region@9d400000 { + reg = <0x0 0x9d400000 0x0 0x02400000>; label = "cont_splash_mem"; }; }; @@ -1255,6 +1260,7 @@ qcom,msm_fastrpc { compatible = "qcom,msm-fastrpc-adsp"; qcom,fastrpc-glink; + qcom,fastrpc-vmid-heap-shared; qcom,msm_fastrpc_cpz_cb1 { compatible = "qcom,msm-fastrpc-compute-cb"; @@ -1796,7 +1802,6 @@ tx-fifo-resize; snps,nominal-elastic-buffer; snps,disable-clk-gating; - snps,has-lpm-erratum; snps,hird-threshold = /bits/ 8 <0x10>; snps,num-gsi-evt-buffs = <0x3>; }; @@ -2174,6 +2179,7 @@ }; qcom,qbt1000 { + status = "disabled"; compatible = "qcom,qbt1000"; clock-names = "core", "iface"; clocks = <&clock_gcc clk_gcc_blsp2_qup6_spi_apps_clk>, diff --git a/arch/arm/include/asm/bitrev.h b/arch/arm/include/asm/bitrev.h index ec291c350ea3814e4f16e13140a324a0b509a531..2f3c9c488794db50b4a28abfbc1819589888bd9e 100644 --- a/arch/arm/include/asm/bitrev.h +++ b/arch/arm/include/asm/bitrev.h @@ -1,3 +1,10 @@ +/* + * Copyright (C) 2015 Sony Mobile Communications 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 __ASM_BITREV_H #define __ASM_BITREV_H diff --git a/arch/arm/include/asm/crash_notes.h b/arch/arm/include/asm/crash_notes.h new file mode 100644 index 0000000000000000000000000000000000000000..421eed9c0aebbc195b3c5759c4f772056324887d --- /dev/null +++ b/arch/arm/include/asm/crash_notes.h @@ -0,0 +1,37 @@ +/* arch/arm/include/asm/crash_notes.h + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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 __ARM_CRASH_NOTES_H +#define __ARM_CRASH_NOTES_H + +static inline void crash_notes_save_regs(struct pt_regs *regs) +{ + __asm__ __volatile__ ( + "stmia %[regs_base], {r0-r12}\n\t" + "mov %[_ARM_sp], sp\n\t" + "str lr, %[_ARM_lr]\n\t" + "adr %[_ARM_pc], 1f\n\t" + "mrs %[_ARM_cpsr], cpsr\n\t" + "1:" + : [_ARM_pc] "=r" (regs->ARM_pc), + [_ARM_cpsr] "=r" (regs->ARM_cpsr), + [_ARM_sp] "=r" (regs->ARM_sp), + [_ARM_lr] "=o" (regs->ARM_lr) + : [regs_base] "r" (®s->ARM_r0) + : "memory" + ); +} + +#endif diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 7b8f2141427bda172899bfe8ae5113367163af47..c02613ed984981387cf0285f66d2b4fdf5ee6803 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -7,6 +7,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 4d58a6eca48e9b66f0663c1b0609dbb0862da764..2082d28d852f015ec4889e3c310f4c294009e8b5 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -7,6 +7,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b6f3d353e1f9631c22ebc50e86c753e76626e02c..d802070de6ca84652e2753ebaa64d1e8caa27bfa 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -6,6 +6,7 @@ config ARM64 select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_ELF_RANDOMIZE select ARCH_HAS_GCOV_PROFILE_ALL + select ARCH_HAS_CRASH_NOTES if CRASH_NOTES select ARCH_HAS_SG_CHAIN select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_USE_CMPXCHG_LOCKREF @@ -979,6 +980,15 @@ config ARM64_LSE_ATOMICS not support these instructions and requires the kernel to be built with binutils >= 2.25. +config ARM64_FLUSH_CONSOLE_ON_RESTART + bool "Force flush the console on restart" + help + If the console is locked while the system is rebooted, the messages + in the temporary logbuffer would not have propogated to all the + console drivers. This option forces the console lock to be + released if it failed to be acquired, which will cause all the + pending messages to be flushed. + endmenu config ARM64_UAO @@ -1167,6 +1177,17 @@ config BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES Space separated list of names of dtbs to append when building a concatenated Image.gz-dtb. +config ARCH_HAS_CRASH_NOTES + bool + +config CRASH_NOTES + bool "Support storing crash notes at panic" + depends on !KEXEC + help + Generate kdump style crash notes at the time of a panic and fill it + with the crashed context to support analysis of the memory dump with + tools like Redhat CRASH. + config BUILD_ARM64_DT_OVERLAY bool "enable DT overlay compilation support" depends on OF diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 3206dc4aec5311d1c4fba0b7743627e183f3753d..430d566c3880e051e8df5e64175fc2e44d797c45 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -77,6 +77,60 @@ config ARCH_MSM8998 This enables support for the MSM8998 chipset. If you do not wish to build a kernel that runs on this chipset, say 'N' here. +config ARCH_SONY_YOSHINO + bool "Sony Mobile Yoshino platform" + depends on ARCH_MSM8998 + help + Support for the SONY Mobile Yoshino platform + which is based on QCOM MSM8998 chipset. + If you enable this config, + please use SONY Mobile device tree. + +config MACH_SONY_MAPLE + bool "Sony Mobile Maple" + depends on ARCH_SONY_YOSHINO + help + Support for the SONY Mobile Maple device + which is based on SONY Yoshino platform. + If you enable this config, + please use SONY Mobile device tree. + +config MACH_SONY_MAPLE_DSDS + bool "Sony Mobile Maple Dsds" + depends on ARCH_SONY_YOSHINO + help + Support for the SONY Mobile Maple Dsds device + which is based on SONY Yoshino platform. + If you enable this config, + please use SONY Mobile device tree. + +config MACH_SONY_POPLAR + bool "Sony Mobile Poplar" + depends on ARCH_SONY_YOSHINO + help + Support for the SONY Mobile Poplar device + which is based on SONY Yoshino platform. + If you enable this config, + please use SONY Mobile device tree. + +config MACH_SONY_POPLAR_DSDS + bool "Sony Mobile Poplar Dsds" + depends on ARCH_SONY_YOSHINO + help + Support for the SONY Mobile Poplar Dsds device + which is based on SONY Yoshino platform. + If you enable this config, + please use SONY Mobile device tree. + +config MACH_SONY_LILAC + bool "Sony Mobile Lilac" + depends on ARCH_SONY_YOSHINO + help + Support for the SONY Mobile Lilac device + which is based on SONY Yoshino platform. + If you enable this config, + please use SONY Mobile device tree. + config ARCH_MSMHAMSTER bool "Enable Support for Qualcomm Technologies Inc MSMHAMSTER" depends on ARCH_QCOM diff --git a/arch/arm64/configs/diffconfig/common_diffconfig b/arch/arm64/configs/diffconfig/common_diffconfig new file mode 100644 index 0000000000000000000000000000000000000000..0a574fa2064211f2a852cdb17935403164fcf1c9 --- /dev/null +++ b/arch/arm64/configs/diffconfig/common_diffconfig @@ -0,0 +1,201 @@ +CONFIG_AHC=y +CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_STATS=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG=y +CONFIG_ARCH_SONY_YOSHINO=y +CONFIG_ARM64_FLUSH_CONSOLE_ON_RESTART=y +CONFIG_CRASH_NOTES=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_EDAC=y +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL=y +CONFIG_FORCE_24BIT_COPP=y +# CONFIG_GPIO_USB_DETECT is not set +CONFIG_HID_LOGITECH=y +CONFIG_HID_PANTHERLORD=y +CONFIG_HID_SONY=y +# CONFIG_INPUT_HBTP_INPUT is not set +CONFIG_INPUT_JOYSTICK=y +# CONFIG_IOMMU_DEBUG is not set +CONFIG_LAST_LOGS=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0 +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_MEMCG=y +CONFIG_MEMORY_STATE_TIME=y +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +CONFIG_MMC_CMD_DEBUG=y +# CONFIG_MODULE_SIG_FORCE is not set +CONFIG_MSM_FORCE_PANIC_ON_WDOG_BARK=y +# CONFIG_MSM_QBT1000 is not set +CONFIG_NLS_UTF8=y +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_OOM_SCORE_NOTIFIER=y +CONFIG_PANIC_ON_DM_VERITY_ERRORS=y +CONFIG_PINCTRL_SOMC=y +CONFIG_POWERKEY_FORCECRASH=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_PSTORE=y +CONFIG_QCOM_DCC=y +CONFIG_QCOM_RTB=y +CONFIG_QNS_SYSTEM=y +# CONFIG_QPNP_HAPTIC is not set +# CONFIG_QPNP_QNOVO is not set +CONFIG_RAMDUMP_MEMDESC=y +CONFIG_RAMDUMP_TAGS=y +CONFIG_RAS=y +CONFIG_RCU_BOOST=y +CONFIG_RCU_KTHREAD_PRIO=1 +CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y +CONFIG_SCSI_UFS_RESTRICT_TX_LANES=y +CONFIG_SECURITY_SELINUX_AVC_EXTRA_INFO=y +CONFIG_SECURITY_SELINUX_TRAP=y +CONFIG_SECURITY_STATUS=y +CONFIG_SERIAL_MSM=y +CONFIG_SOMC_CHARGER_EXTENSION=y +CONFIG_SOMC_LCD_OCP_ENABLED=y +CONFIG_SONY_CAM_V4L2=y +CONFIG_STRICT_DEVMEM=y +CONFIG_SUBSYS_LAST_ERR_LOG=y +# CONFIG_TRACE_PRINTK is not set +CONFIG_UID_STAT=y +# CONFIG_USB_CONFIGFS_NCM is not set +CONFIG_USB_DWC3_MSM_ID_POLL=y +# CONFIG_USB_DWC3_PCI is not set +CONFIG_USB_HOST_EXTRA_NOTIFICATION=y +# CONFIG_USB_NET_DRIVERS is not set +# CONFIG_USB_OTG_WAKELOCK is not set +CONFIG_VFAT_FS_NO_DUALNAMES=y +CONFIG_WAKEUP_IRQ_DEBUG=y +CONFIG_XFRM_RFC_4868_TRUNCATION=y +CONFIG_Z3FOLD=y +CONFIG_ZRAM_LZ4_COMPRESS=y +CONFIG_ARCH_HAS_CRASH_NOTES=y +# CONFIG_DVB_AS102 is not set +# CONFIG_DVB_B2C2_FLEXCOP_USB is not set +# CONFIG_DVB_TTUSB_BUDGET is not set +# CONFIG_DVB_TTUSB_DEC is not set +# CONFIG_DVB_USB_V2 is not set +CONFIG_EDAC_CORTEX_ARM64=y +# CONFIG_EDAC_CORTEX_ARM64_DBE_IRQ_ONLY is not set +# CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_CE is not set +CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_UE=y +# CONFIG_EDAC_DEBUG is not set +CONFIG_EDAC_LEGACY_SYSFS=y +CONFIG_EDAC_MM_EDAC=y +# CONFIG_EDAC_XGENE is not set +CONFIG_EXT4_USE_FOR_EXT2=y +# CONFIG_HID_LOGITECH_HIDPP is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_AS5011 is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_JOYDUMP is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_TWIDJOY is not set +# CONFIG_JOYSTICK_WARRIOR is not set +CONFIG_JOYSTICK_XPAD=y +# CONFIG_JOYSTICK_XPAD_FF is not set +# CONFIG_JOYSTICK_XPAD_LEDS is not set +# CONFIG_JOYSTICK_ZHENHUA is not set +# CONFIG_LOGIG940_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIWHEELS_FF is not set +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y +# CONFIG_MACH_SONY_LILAC is not set +# CONFIG_MACH_SONY_MAPLE is not set +# CONFIG_MACH_SONY_MAPLE_DSDS is not set +# CONFIG_MACH_SONY_POPLAR is not set +# CONFIG_MACH_SONY_POPLAR_DSDS is not set +# CONFIG_MEMCG_KMEM is not set +CONFIG_MEMCG_SWAP=y +CONFIG_MEMCG_SWAP_ENABLED=y +CONFIG_MMC_CMD_QUEUE_SIZE=256 +CONFIG_PAGE_COUNTER=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_PSTORE_CONSOLE=y +# CONFIG_PSTORE_PMSG is not set +CONFIG_PSTORE_RAM=y +CONFIG_QCOM_RTB_SEPARATE_CPUS=y +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_EARLYCON=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SMS_USB_DRV is not set +# CONFIG_SONY_FF is not set +# CONFIG_TOUCHSCREEN_SUR40 is not set +# CONFIG_USB_GL860 is not set +CONFIG_USB_GSPCA=m +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_DTCS033 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STK1135 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TOUPTEK is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_USB_M5602 is not set +# CONFIG_USB_PWC is not set +# CONFIG_USB_S2255 is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_VIDEO_CLASS is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_VIDEO_AU0828 is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_USBTV is not set diff --git a/arch/arm64/configs/diffconfig/lilac_diffconfig b/arch/arm64/configs/diffconfig/lilac_diffconfig new file mode 100644 index 0000000000000000000000000000000000000000..1f25ef415ad51b8cbf5194b2d4fcbce4fe66c58d --- /dev/null +++ b/arch/arm64/configs/diffconfig/lilac_diffconfig @@ -0,0 +1,9 @@ +CONFIG_LEDS_QPNP_RGB_SCALE=y +CONFIG_MACH_SONY_LILAC=y +CONFIG_NFC_PN547=y +CONFIG_SENSORS_TCS3490=y +CONFIG_TOF_SENSOR=y +CONFIG_TOUCHSCREEN_CLEARPAD=y +CONFIG_FRONT_CAMERA_LED_SCALE=12 +CONFIG_TOUCHSCREEN_CLEARPAD_I2C=y +CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV=y diff --git a/arch/arm64/configs/diffconfig/maple_diffconfig b/arch/arm64/configs/diffconfig/maple_diffconfig new file mode 100644 index 0000000000000000000000000000000000000000..494e5fe6f0c614c39d7f5b99ec75abdf31de9744 --- /dev/null +++ b/arch/arm64/configs/diffconfig/maple_diffconfig @@ -0,0 +1,7 @@ +CONFIG_MACH_SONY_MAPLE=y +CONFIG_NFC_PN547=y +CONFIG_SENSORS_TCS3490=y +CONFIG_TOF_SENSOR=y +CONFIG_TOUCHSCREEN_CLEARPAD=y +CONFIG_TOUCHSCREEN_CLEARPAD_I2C=y +CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV=y diff --git a/arch/arm64/configs/diffconfig/maple_dsds_diffconfig b/arch/arm64/configs/diffconfig/maple_dsds_diffconfig new file mode 100644 index 0000000000000000000000000000000000000000..ded7993b08b156033f005f88587585e85f0af610 --- /dev/null +++ b/arch/arm64/configs/diffconfig/maple_dsds_diffconfig @@ -0,0 +1,7 @@ +CONFIG_MACH_SONY_MAPLE_DSDS=y +CONFIG_NFC_PN547=y +CONFIG_SENSORS_TCS3490=y +CONFIG_TOF_SENSOR=y +CONFIG_TOUCHSCREEN_CLEARPAD=y +CONFIG_TOUCHSCREEN_CLEARPAD_I2C=y +CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV=y diff --git a/arch/arm64/configs/diffconfig/poplar_diffconfig b/arch/arm64/configs/diffconfig/poplar_diffconfig new file mode 100644 index 0000000000000000000000000000000000000000..9e5bdd136b8fa34837991dc165c7ed6ae5c6c6b4 --- /dev/null +++ b/arch/arm64/configs/diffconfig/poplar_diffconfig @@ -0,0 +1,7 @@ +CONFIG_MACH_SONY_POPLAR=y +CONFIG_NFC_PN547=y +CONFIG_SENSORS_TCS3490=y +CONFIG_TOF_SENSOR=y +CONFIG_TOUCHSCREEN_CLEARPAD=y +CONFIG_TOUCHSCREEN_CLEARPAD_I2C=y +CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV=y diff --git a/arch/arm64/configs/diffconfig/poplar_dsds_diffconfig b/arch/arm64/configs/diffconfig/poplar_dsds_diffconfig new file mode 100644 index 0000000000000000000000000000000000000000..990940fad1844545bf690392c26fa64460851346 --- /dev/null +++ b/arch/arm64/configs/diffconfig/poplar_dsds_diffconfig @@ -0,0 +1,7 @@ +CONFIG_MACH_SONY_POPLAR_DSDS=y +CONFIG_NFC_PN547=y +CONFIG_SENSORS_TCS3490=y +CONFIG_TOF_SENSOR=y +CONFIG_TOUCHSCREEN_CLEARPAD=y +CONFIG_TOUCHSCREEN_CLEARPAD_I2C=y +CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 570a21b464ca057466aae71a78350ebaaaff7fa0..a322be3c1137830ce49194f3f31707863e7e0b3f 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -298,7 +298,7 @@ CONFIG_PPP_SYNC_TTY=y CONFIG_USB_USBNET=y CONFIG_WCNSS_MEM_PRE_ALLOC=y CONFIG_ATH_CARDS=y -CONFIG_WIL6210=m +# CONFIG_WIL6210 is not set CONFIG_CLD_LL_CORE=y CONFIG_CNSS_GENL=y CONFIG_INPUT_EVDEV=y diff --git a/arch/arm64/include/asm/bitrev.h b/arch/arm64/include/asm/bitrev.h index a5a0c366013773f980919591c252f0fab5d2ca74..78791d1fea3c8aca314a8cfa7692578e1bb950e2 100644 --- a/arch/arm64/include/asm/bitrev.h +++ b/arch/arm64/include/asm/bitrev.h @@ -1,3 +1,10 @@ +/* + * Copyright (C) 2014 Sony Mobile Communications 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 __ASM_BITREV_H #define __ASM_BITREV_H static __always_inline __attribute_const__ u32 __arch_bitrev32(u32 x) diff --git a/arch/arm64/include/asm/crash_notes.h b/arch/arm64/include/asm/crash_notes.h new file mode 100644 index 0000000000000000000000000000000000000000..289fce346a516058e1c9e255bf2531a3e98aaa51 --- /dev/null +++ b/arch/arm64/include/asm/crash_notes.h @@ -0,0 +1,86 @@ +/* arch/arm64/include/asm/crash_notes.h + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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 __ARM64_CRASH_NOTES_H +#define __ARM64_CRASH_NOTES_H + +#include + +#define PSTATE_NZCV_MASK (PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | PSR_V_BIT) +#define PSTATE_DAIF_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT) + +static inline void crash_notes_save_regs(struct pt_regs *regs) +{ + struct pstate { + u64 nzcv; + u64 daif; + u64 current_el; + u64 sp_sel; + } pstate; + + /* 31 General purpose registers x0-x30 */ + __asm__ __volatile__("str x0, %0" : "=m"(regs->regs[0])); + __asm__ __volatile__("str x1, %0" : "=m"(regs->regs[1])); + __asm__ __volatile__("str x2, %0" : "=m"(regs->regs[2])); + __asm__ __volatile__("str x3, %0" : "=m"(regs->regs[3])); + __asm__ __volatile__("str x4, %0" : "=m"(regs->regs[4])); + __asm__ __volatile__("str x5, %0" : "=m"(regs->regs[5])); + __asm__ __volatile__("str x6, %0" : "=m"(regs->regs[6])); + __asm__ __volatile__("str x7, %0" : "=m"(regs->regs[7])); + __asm__ __volatile__("str x8, %0" : "=m"(regs->regs[8])); + __asm__ __volatile__("str x9, %0" : "=m"(regs->regs[9])); + __asm__ __volatile__("str x10, %0" : "=m"(regs->regs[10])); + __asm__ __volatile__("str x11, %0" : "=m"(regs->regs[11])); + __asm__ __volatile__("str x12, %0" : "=m"(regs->regs[12])); + __asm__ __volatile__("str x13, %0" : "=m"(regs->regs[13])); + __asm__ __volatile__("str x14, %0" : "=m"(regs->regs[14])); + __asm__ __volatile__("str x15, %0" : "=m"(regs->regs[15])); + __asm__ __volatile__("str x16, %0" : "=m"(regs->regs[16])); + __asm__ __volatile__("str x17, %0" : "=m"(regs->regs[17])); + __asm__ __volatile__("str x18, %0" : "=m"(regs->regs[18])); + __asm__ __volatile__("str x19, %0" : "=m"(regs->regs[19])); + __asm__ __volatile__("str x20, %0" : "=m"(regs->regs[20])); + __asm__ __volatile__("str x21, %0" : "=m"(regs->regs[21])); + __asm__ __volatile__("str x22, %0" : "=m"(regs->regs[22])); + __asm__ __volatile__("str x23, %0" : "=m"(regs->regs[23])); + __asm__ __volatile__("str x24, %0" : "=m"(regs->regs[24])); + __asm__ __volatile__("str x25, %0" : "=m"(regs->regs[25])); + __asm__ __volatile__("str x26, %0" : "=m"(regs->regs[26])); + __asm__ __volatile__("str x27, %0" : "=m"(regs->regs[27])); + __asm__ __volatile__("str x28, %0" : "=m"(regs->regs[28])); + __asm__ __volatile__("str x29, %0" : "=m"(regs->regs[29])); + __asm__ __volatile__("str x30, %0" : "=m"(regs->regs[30])); + + /* Save program counter & stack pointer here */ + __asm__ __volatile__( + "mov %[_ARM_sp], sp\n\t" + "adr %[_ARM_pc], 1f\n\t" + "1:" + : [_ARM_pc] "=r" (regs->pc), + [_ARM_sp] "=r" (regs->sp) + ); + + /* Obtain pstate through system registers */ + __asm__ __volatile__("mrs %0, nzcv" : "=&r"(pstate.nzcv)); + __asm__ __volatile__("mrs %0, daif" : "=&r"(pstate.daif)); + __asm__ __volatile__("mrs %0, currentel" : "=&r"(pstate.current_el)); + __asm__ __volatile__("mrs %0, spsel" : "=&r"(pstate.sp_sel)); + regs->pstate = pstate.nzcv & PSTATE_NZCV_MASK; + regs->pstate |= pstate.daif & PSTATE_DAIF_MASK; + regs->pstate |= pstate.current_el & PSR_MODE_MASK; + regs->pstate |= pstate.sp_sel & 1; +} + +#endif diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index d39d8bde42d707da02955d19e33e7ffc9f27cdc1..b2ea5d83f8b74ce736b7a1cedcd0050124b180ad 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -15,6 +15,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __ASM_UACCESS_H #define __ASM_UACCESS_H diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 41649c5095577920fc212550991978d7e99705e3..751662701779121e80b021875372a9867dd483ff 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -153,6 +153,10 @@ int main(void) DEFINE(MPIDR_HASH_SHIFTS, offsetof(struct mpidr_hash, shift_aff)); DEFINE(SLEEP_STACK_DATA_SYSTEM_REGS, offsetof(struct sleep_stack_data, system_regs)); DEFINE(SLEEP_STACK_DATA_CALLEE_REGS, offsetof(struct sleep_stack_data, callee_saved_regs)); +#endif + BLANK(); +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + DEFINE(TRAMP_VALIAS, TRAMP_VALIAS); #endif DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0)); DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 8b326cb13b52cf8b099c7888d8c1379d4e270775..90c2f601e99116904d32f447e732f174e3aaf4c7 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -742,6 +742,12 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = cpufeature_pan_not_uao, }, #endif /* CONFIG_ARM64_PAN */ +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + { + .capability = ARM64_UNMAP_KERNEL_AT_EL0, + .matches = unmap_kernel_at_el0 + }, +#endif { .desc = "Virtualization Host Extensions", .capability = ARM64_HAS_VIRT_HOST_EXTN, diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 5dd9b572259f46d7bb777925ec639b4d5378596d..82e97316e6f49ee491a3d1d78c81b0e381991b07 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -17,6 +17,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include @@ -31,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +70,34 @@ unsigned long __stack_chk_guard __read_mostly; EXPORT_SYMBOL(__stack_chk_guard); #endif +#ifdef CONFIG_ARM64_FLUSH_CONSOLE_ON_RESTART +void arm_machine_flush_console(void) +{ + printk("\n"); + pr_emerg("Restarting %s\n", linux_banner); + if (console_trylock()) { + console_unlock(); + return; + } + + mdelay(50); + + local_irq_disable(); + if (!console_trylock()) + pr_emerg("arm_restart: Console was locked! Busting\n"); + else + pr_emerg("arm_restart: Console was locked!\n"); + if (is_console_suspended()) + resume_console(); + else + console_unlock(); +} +#else +void arm_machine_flush_console(void) +{ +} +#endif + /* * Function pointers to optional machine specific functions */ @@ -166,6 +200,10 @@ void machine_restart(char *cmd) if (efi_enabled(EFI_RUNTIME_SERVICES)) efi_reboot(reboot_mode, NULL); + /* Flush the console to make sure all the relevant messages make it + * out to the console drivers */ + arm_machine_flush_console(); + /* Now call the architecture specific reboot code. */ if (arm_pm_restart) arm_pm_restart(reboot_mode, cmd); @@ -416,7 +454,7 @@ static void entry_task_switch(struct task_struct *next) /* * Thread switching. */ -struct task_struct *__switch_to(struct task_struct *prev, +__notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *next) { struct task_struct *last; diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index 71c8076bbc60b4f1e3b5d1dc6487518edbb65658..a86bca7b4ca547b0497deb089901e42cac58ae23 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -244,6 +244,10 @@ SECTIONS HEAD_SYMBOLS } +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 +ASSERT((__entry_tramp_text_end - __entry_tramp_text_start) == PAGE_SIZE, + "Entry trampoline text too big") +#endif /* * The HYP init code and ID map text can't be longer than a page each, * and should not cross a page boundary. diff --git a/arch/arm64/lib/copy_page.S b/arch/arm64/lib/copy_page.S index 4c1e700840b6ced5a0b2f868bfb4f37dddc8abc0..2534533ceb1d7374b5abfbf70bc3e7c5cce7300e 100644 --- a/arch/arm64/lib/copy_page.S +++ b/arch/arm64/lib/copy_page.S @@ -18,8 +18,6 @@ #include #include #include -#include -#include /* * Copy a page from src to dest (both are page aligned) @@ -29,15 +27,6 @@ * x1 - src */ ENTRY(copy_page) -alternative_if_not ARM64_HAS_NO_HW_PREFETCH - nop - nop -alternative_else - # Prefetch two cache lines ahead. - prfm pldl1strm, [x1, #128] - prfm pldl1strm, [x1, #256] -alternative_endif - ldp x2, x3, [x1] ldp x4, x5, [x1, #16] ldp x6, x7, [x1, #32] @@ -52,12 +41,6 @@ alternative_endif 1: subs x18, x18, #128 -alternative_if_not ARM64_HAS_NO_HW_PREFETCH - nop -alternative_else - prfm pldl1strm, [x1, #384] -alternative_endif - stnp x2, x3, [x0] ldp x2, x3, [x1] stnp x4, x5, [x0, #16] diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 9930190a4929923005c03b9ed3ba9efb9b05a95a..7fd5b3dcf810479a5a2b1ad7582adcae08ebe92f 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -16,6 +16,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 1d9db2ef26bdd8c79bcd45f0b68e18ae8335911b..f90eb2f36f3b6e355c36596329fcf9157c9e0250 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -32,6 +32,7 @@ #include "binder_trace.h" struct list_lru binder_alloc_lru; +#define BINDER_MIN_ALLOC (1 * PAGE_SIZE) static DEFINE_MUTEX(binder_alloc_mmap_lock); @@ -693,6 +694,11 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, } } #endif + if (vma->vm_end - vma->vm_start < BINDER_MIN_ALLOC) { + ret = -EINVAL; + failure_string = "VMA size < BINDER_MIN_ALLOC"; + goto err_vma_too_small; + } alloc->pages = kzalloc(sizeof(alloc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL); @@ -727,6 +733,7 @@ err_alloc_buf_struct_failed: kfree(alloc->pages); alloc->pages = NULL; err_alloc_pages_failed: +err_vma_too_small: mutex_lock(&binder_alloc_mmap_lock); vfree(alloc->buffer); alloc->buffer = NULL; diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index 9ef64e56385667a53abeab2f41f67b198a8ce86e..d62ae5731f83cdf66cafe5549bc38c801bc35485 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -24,6 +24,8 @@ #include extern struct list_lru binder_alloc_lru; +#define BINDER_MIN_ALLOC (1 * PAGE_SIZE) + struct binder_transaction; /** diff --git a/drivers/base/class.c b/drivers/base/class.c index 71059e32bebc989e180bee874372f24582ba10b9..8dec2ed3535547bfab2d45535c1d6cbc59fc2c09 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -9,6 +9,11 @@ * This file is released under the GPLv2 * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -83,7 +88,7 @@ static struct kobj_type class_ktype = { }; /* Hotplug events for classes go to the class subsys */ -static struct kset *class_kset; +struct kset *class_kset; int class_create_file_ns(struct class *cls, const struct class_attribute *attr, diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 6c5bc3fadfcfc5dda58bb4e48935be753b4233ab..0360b1e8d9118eb46ecb55e60b0348c97739c849 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -16,6 +16,11 @@ * domain dependencies may differ from the ancestral dependencies that the * subsystem list maintains. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -34,6 +39,10 @@ #include #include #include +#ifdef CONFIG_PM_WAKEUP_TIMES +#include +#include +#endif #include "../base.h" #include "power.h" @@ -57,6 +66,11 @@ static LIST_HEAD(dpm_late_early_list); static LIST_HEAD(dpm_noirq_list); struct suspend_stats suspend_stats; +#ifdef CONFIG_PM_WAKEUP_TIMES +struct suspend_stats_queue suspend_stats_queue; +static ktime_t suspend_start_time; +static ktime_t resume_start_time; +#endif static DEFINE_MUTEX(dpm_list_mtx); static pm_message_t pm_transition; @@ -376,6 +390,100 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); } +#ifdef CONFIG_PM_WAKEUP_TIMES +void dpm_log_start_time(pm_message_t state) +{ + switch (state.event) { + case PM_EVENT_RESUME: + resume_start_time = ktime_get_boottime(); + break; + case PM_EVENT_SUSPEND: + suspend_start_time = ktime_get_boottime(); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(dpm_log_start_time); + +void dpm_log_wakeup_stats(pm_message_t state) +{ + ktime_t *start_time, *avg_time, end_time, duration, prev_duration, sum; + struct stats_wakeup_time *min_time, *max_time, *last_time, prev; + u64 avg_ns; + char buf[32] = {0}; + unsigned int nr = 0; + + switch (state.event) { + case PM_EVENT_RESUME: + snprintf(buf, sizeof(buf), "%s", "resume time:"); + start_time = &resume_start_time; + min_time = &suspend_stats.resume_min_time; + max_time = &suspend_stats.resume_max_time; + last_time = &suspend_stats.resume_last_time; + avg_time = &suspend_stats.resume_avg_time; + break; + case PM_EVENT_SUSPEND: + snprintf(buf, sizeof(buf), "%s", "suspend time:"); + start_time = &suspend_start_time; + min_time = &suspend_stats.suspend_min_time; + max_time = &suspend_stats.suspend_max_time; + last_time = &suspend_stats.suspend_last_time; + avg_time = &suspend_stats.suspend_avg_time; + break; + default: + return; + } + + if (!ktime_to_ns(*start_time)) + return; + + /* Calculate duration and update last time */ + end_time = ktime_get_boottime(); + prev = *last_time; + prev_duration = ktime_sub(prev.end, prev.start); + last_time->end = end_time; + last_time->start = *start_time; + duration = ktime_sub(end_time, *start_time); + + /* Update max time */ + if (ktime_compare(duration, + ktime_sub(max_time->end, max_time->start)) > 0) + *max_time = *last_time; + + /* Update min time */ + if (!ktime_to_ns(ktime_sub(min_time->end, min_time->start))) + *min_time = *last_time; + + if (ktime_compare(duration, + ktime_sub(min_time->end, min_time->start)) < 0) + *min_time = *last_time; + + /* Compute the avg of current, previous and previous average times */ + if (ktime_to_ns(prev_duration)) + nr++; + + if (ktime_to_ns(*avg_time)) + nr++; + + sum = ktime_add(ktime_add(*avg_time, prev_duration), duration); + avg_ns = div_u64(ktime_to_ns(sum), (nr + 1)); + *avg_time = ktime_set(0, avg_ns); + *start_time = ktime_set(0, 0); + + pr_debug("%s\n%s %llums\n%s %llums\n %s %llums\n%s %llums\n", buf, + " min:", + ktime_to_ms(ktime_sub(min_time->end, min_time->start)), + " max:", + ktime_to_ms(ktime_sub(max_time->end, max_time->start)), + " last:", ktime_to_ms(duration), + " avg:", ktime_to_ms(*avg_time)); + suspend_stats_queue.resume_done = 1; + wake_up(&suspend_stats_queue.wait_queue); +} +EXPORT_SYMBOL_GPL(dpm_log_wakeup_stats); +#endif + static int dpm_run_callback(pm_callback_t cb, struct device *dev, pm_message_t state, char *info) { diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 61cdbaa3c09a5e0d1d10307c16f6be19b52bc812..09a0cade7dbee4f39a49780a59340ad6be930ca5 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -9,6 +9,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _REGMAP_INTERNAL_H #define _REGMAP_INTERNAL_H diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index b3a62e94d1f31af376bf7251678d97897b85f870..fb37f5593b57917ed9a142d0cbada5bd31187d3e 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -9,6 +9,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/block/zram/Kconfig b/drivers/block/zram/Kconfig index 386ba3d1a6ee8aed4a9c9885f07c408dfa3bff4b..4831d0a4f3523740ddd8b96f829c2ba22733f5af 100644 --- a/drivers/block/zram/Kconfig +++ b/drivers/block/zram/Kconfig @@ -1,6 +1,7 @@ config ZRAM tristate "Compressed RAM block device support" - depends on BLOCK && SYSFS && ZSMALLOC + depends on BLOCK && SYSFS + select ZPOOL select LZO_COMPRESS select LZO_DECOMPRESS default n diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c index c53617752b93cc3976f764e19cc436b5cb66cdcf..b51a816d766bb31cf8f8615535fe2435eda49f1e 100644 --- a/drivers/block/zram/zcomp.c +++ b/drivers/block/zram/zcomp.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "zcomp.h" #include "zcomp_lzo.h" @@ -20,29 +21,6 @@ #include "zcomp_lz4.h" #endif -/* - * single zcomp_strm backend - */ -struct zcomp_strm_single { - struct mutex strm_lock; - struct zcomp_strm *zstrm; -}; - -/* - * multi zcomp_strm backend - */ -struct zcomp_strm_multi { - /* protect strm list */ - spinlock_t strm_lock; - /* max possible number of zstrm streams */ - int max_strm; - /* number of available zstrm streams */ - int avail_strm; - /* list of available strms */ - struct list_head idle_strm; - wait_queue_head_t strm_wait; -}; - static struct zcomp_backend *backends[] = { &zcomp_lzo, #ifdef CONFIG_ZRAM_LZ4_COMPRESS @@ -74,18 +52,18 @@ static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm) * allocate new zcomp_strm structure with ->private initialized by * backend, return NULL on error */ -static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp) +static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp, gfp_t flags) { - struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_NOIO); + struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), flags); if (!zstrm) return NULL; - zstrm->private = comp->backend->create(); + zstrm->private = comp->backend->create(flags); /* * 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_NOIO | __GFP_ZERO, 1); + zstrm->buffer = (void *)__get_free_pages(flags | __GFP_ZERO, 1); if (!zstrm->private || !zstrm->buffer) { zcomp_strm_free(comp, zstrm); zstrm = NULL; @@ -93,180 +71,6 @@ static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp) return zstrm; } -/* - * get idle zcomp_strm or wait until other process release - * (zcomp_strm_release()) one for us - */ -static struct zcomp_strm *zcomp_strm_multi_find(struct zcomp *comp) -{ - struct zcomp_strm_multi *zs = comp->stream; - struct zcomp_strm *zstrm; - - while (1) { - spin_lock(&zs->strm_lock); - if (!list_empty(&zs->idle_strm)) { - zstrm = list_entry(zs->idle_strm.next, - struct zcomp_strm, list); - list_del(&zstrm->list); - spin_unlock(&zs->strm_lock); - return zstrm; - } - /* zstrm streams limit reached, wait for idle stream */ - if (zs->avail_strm >= zs->max_strm) { - spin_unlock(&zs->strm_lock); - wait_event(zs->strm_wait, !list_empty(&zs->idle_strm)); - continue; - } - /* allocate new zstrm stream */ - zs->avail_strm++; - spin_unlock(&zs->strm_lock); - - zstrm = zcomp_strm_alloc(comp); - if (!zstrm) { - spin_lock(&zs->strm_lock); - zs->avail_strm--; - spin_unlock(&zs->strm_lock); - wait_event(zs->strm_wait, !list_empty(&zs->idle_strm)); - continue; - } - break; - } - return zstrm; -} - -/* add stream back to idle list and wake up waiter or free the stream */ -static void zcomp_strm_multi_release(struct zcomp *comp, struct zcomp_strm *zstrm) -{ - struct zcomp_strm_multi *zs = comp->stream; - - spin_lock(&zs->strm_lock); - if (zs->avail_strm <= zs->max_strm) { - list_add(&zstrm->list, &zs->idle_strm); - spin_unlock(&zs->strm_lock); - wake_up(&zs->strm_wait); - return; - } - - zs->avail_strm--; - spin_unlock(&zs->strm_lock); - zcomp_strm_free(comp, zstrm); -} - -/* change max_strm limit */ -static bool zcomp_strm_multi_set_max_streams(struct zcomp *comp, int num_strm) -{ - struct zcomp_strm_multi *zs = comp->stream; - struct zcomp_strm *zstrm; - - spin_lock(&zs->strm_lock); - zs->max_strm = num_strm; - /* - * if user has lowered the limit and there are idle streams, - * immediately free as much streams (and memory) as we can. - */ - while (zs->avail_strm > num_strm && !list_empty(&zs->idle_strm)) { - zstrm = list_entry(zs->idle_strm.next, - struct zcomp_strm, list); - list_del(&zstrm->list); - zcomp_strm_free(comp, zstrm); - zs->avail_strm--; - } - spin_unlock(&zs->strm_lock); - return true; -} - -static void zcomp_strm_multi_destroy(struct zcomp *comp) -{ - struct zcomp_strm_multi *zs = comp->stream; - struct zcomp_strm *zstrm; - - while (!list_empty(&zs->idle_strm)) { - zstrm = list_entry(zs->idle_strm.next, - struct zcomp_strm, list); - list_del(&zstrm->list); - zcomp_strm_free(comp, zstrm); - } - kfree(zs); -} - -static int zcomp_strm_multi_create(struct zcomp *comp, int max_strm) -{ - struct zcomp_strm *zstrm; - struct zcomp_strm_multi *zs; - - comp->destroy = zcomp_strm_multi_destroy; - comp->strm_find = zcomp_strm_multi_find; - comp->strm_release = zcomp_strm_multi_release; - comp->set_max_streams = zcomp_strm_multi_set_max_streams; - zs = kmalloc(sizeof(struct zcomp_strm_multi), GFP_KERNEL); - if (!zs) - return -ENOMEM; - - comp->stream = zs; - spin_lock_init(&zs->strm_lock); - INIT_LIST_HEAD(&zs->idle_strm); - init_waitqueue_head(&zs->strm_wait); - zs->max_strm = max_strm; - zs->avail_strm = 1; - - zstrm = zcomp_strm_alloc(comp); - if (!zstrm) { - kfree(zs); - return -ENOMEM; - } - list_add(&zstrm->list, &zs->idle_strm); - return 0; -} - -static struct zcomp_strm *zcomp_strm_single_find(struct zcomp *comp) -{ - struct zcomp_strm_single *zs = comp->stream; - mutex_lock(&zs->strm_lock); - return zs->zstrm; -} - -static void zcomp_strm_single_release(struct zcomp *comp, - struct zcomp_strm *zstrm) -{ - struct zcomp_strm_single *zs = comp->stream; - mutex_unlock(&zs->strm_lock); -} - -static bool zcomp_strm_single_set_max_streams(struct zcomp *comp, int num_strm) -{ - /* zcomp_strm_single support only max_comp_streams == 1 */ - return false; -} - -static void zcomp_strm_single_destroy(struct zcomp *comp) -{ - struct zcomp_strm_single *zs = comp->stream; - zcomp_strm_free(comp, zs->zstrm); - kfree(zs); -} - -static int zcomp_strm_single_create(struct zcomp *comp) -{ - struct zcomp_strm_single *zs; - - comp->destroy = zcomp_strm_single_destroy; - comp->strm_find = zcomp_strm_single_find; - comp->strm_release = zcomp_strm_single_release; - comp->set_max_streams = zcomp_strm_single_set_max_streams; - zs = kmalloc(sizeof(struct zcomp_strm_single), GFP_KERNEL); - if (!zs) - return -ENOMEM; - - comp->stream = zs; - mutex_init(&zs->strm_lock); - zs->zstrm = zcomp_strm_alloc(comp); - if (!zs->zstrm) { - kfree(zs); - return -ENOMEM; - } - return 0; -} - /* show available compressors */ ssize_t zcomp_available_show(const char *comp, char *buf) { @@ -291,19 +95,14 @@ bool zcomp_available_algorithm(const char *comp) return find_backend(comp) != NULL; } -bool zcomp_set_max_streams(struct zcomp *comp, int num_strm) -{ - return comp->set_max_streams(comp, num_strm); -} - struct zcomp_strm *zcomp_strm_find(struct zcomp *comp) { - return comp->strm_find(comp); + return *get_cpu_ptr(comp->stream); } void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm) { - comp->strm_release(comp, zstrm); + put_cpu_ptr(comp->stream); } int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, @@ -319,9 +118,83 @@ int zcomp_decompress(struct zcomp *comp, const unsigned char *src, return comp->backend->decompress(src, src_len, dst); } +static int __zcomp_cpu_notifier(struct zcomp *comp, + unsigned long action, unsigned long cpu) +{ + struct zcomp_strm *zstrm; + + switch (action) { + case CPU_UP_PREPARE: + if (WARN_ON(*per_cpu_ptr(comp->stream, cpu))) + break; + zstrm = zcomp_strm_alloc(comp, GFP_KERNEL); + if (IS_ERR_OR_NULL(zstrm)) { + pr_err("Can't allocate a compression stream\n"); + return NOTIFY_BAD; + } + *per_cpu_ptr(comp->stream, cpu) = zstrm; + break; + case CPU_DEAD: + case CPU_UP_CANCELED: + zstrm = *per_cpu_ptr(comp->stream, cpu); + if (!IS_ERR_OR_NULL(zstrm)) + zcomp_strm_free(comp, zstrm); + *per_cpu_ptr(comp->stream, cpu) = NULL; + break; + default: + break; + } + return NOTIFY_OK; +} + +static int zcomp_cpu_notifier(struct notifier_block *nb, + unsigned long action, void *pcpu) +{ + unsigned long cpu = (unsigned long)pcpu; + struct zcomp *comp = container_of(nb, typeof(*comp), notifier); + + return __zcomp_cpu_notifier(comp, action, cpu); +} + +static int zcomp_init(struct zcomp *comp) +{ + unsigned long cpu; + int ret; + + comp->notifier.notifier_call = zcomp_cpu_notifier; + + comp->stream = alloc_percpu(struct zcomp_strm *); + if (!comp->stream) + return -ENOMEM; + + cpu_notifier_register_begin(); + for_each_online_cpu(cpu) { + ret = __zcomp_cpu_notifier(comp, CPU_UP_PREPARE, cpu); + if (ret == NOTIFY_BAD) + goto cleanup; + } + __register_cpu_notifier(&comp->notifier); + cpu_notifier_register_done(); + return 0; + +cleanup: + for_each_online_cpu(cpu) + __zcomp_cpu_notifier(comp, CPU_UP_CANCELED, cpu); + cpu_notifier_register_done(); + return -ENOMEM; +} + void zcomp_destroy(struct zcomp *comp) { - comp->destroy(comp); + unsigned long cpu; + + cpu_notifier_register_begin(); + for_each_online_cpu(cpu) + __zcomp_cpu_notifier(comp, CPU_UP_CANCELED, cpu); + __unregister_cpu_notifier(&comp->notifier); + cpu_notifier_register_done(); + + free_percpu(comp->stream); kfree(comp); } @@ -331,9 +204,9 @@ void zcomp_destroy(struct zcomp *comp) * 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 functions zcomp_strm_{multi,single}_create. + * returned by zcomp_init(). */ -struct zcomp *zcomp_create(const char *compress, int max_strm) +struct zcomp *zcomp_create(const char *compress) { struct zcomp *comp; struct zcomp_backend *backend; @@ -348,10 +221,7 @@ struct zcomp *zcomp_create(const char *compress, int max_strm) return ERR_PTR(-ENOMEM); comp->backend = backend; - if (max_strm > 1) - error = zcomp_strm_multi_create(comp, max_strm); - else - error = zcomp_strm_single_create(comp); + error = zcomp_init(comp); if (error) { kfree(comp); return ERR_PTR(error); diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h index 46e2b9f8f1f0e3ec684736305c4d8a1ab8de0c97..ffd88cb747feefb7fb04a467e4cd2456e49249b6 100644 --- a/drivers/block/zram/zcomp.h +++ b/drivers/block/zram/zcomp.h @@ -10,8 +10,6 @@ #ifndef _ZCOMP_H_ #define _ZCOMP_H_ -#include - struct zcomp_strm { /* compression/decompression buffer */ void *buffer; @@ -21,8 +19,6 @@ struct zcomp_strm { * working memory) */ void *private; - /* used in multi stream backend, protected by backend strm_lock */ - struct list_head list; }; /* static compression backend */ @@ -33,7 +29,7 @@ struct zcomp_backend { int (*decompress)(const unsigned char *src, size_t src_len, unsigned char *dst); - void *(*create)(void); + void *(*create)(gfp_t flags); void (*destroy)(void *private); const char *name; @@ -41,19 +37,15 @@ struct zcomp_backend { /* dynamic per-device compression frontend */ struct zcomp { - void *stream; + struct zcomp_strm * __percpu *stream; struct zcomp_backend *backend; - - struct zcomp_strm *(*strm_find)(struct zcomp *comp); - void (*strm_release)(struct zcomp *comp, struct zcomp_strm *zstrm); - bool (*set_max_streams)(struct zcomp *comp, int num_strm); - void (*destroy)(struct zcomp *comp); + struct notifier_block notifier; }; ssize_t zcomp_available_show(const char *comp, char *buf); bool zcomp_available_algorithm(const char *comp); -struct zcomp *zcomp_create(const char *comp, int max_strm); +struct zcomp *zcomp_create(const char *comp); void zcomp_destroy(struct zcomp *comp); struct zcomp_strm *zcomp_strm_find(struct zcomp *comp); diff --git a/drivers/block/zram/zcomp_lz4.c b/drivers/block/zram/zcomp_lz4.c index dd6083124276fa107717112cb273648887f1d52e..dc2338d5258c171f55ac5d4d3576fbb9465c4643 100644 --- a/drivers/block/zram/zcomp_lz4.c +++ b/drivers/block/zram/zcomp_lz4.c @@ -15,24 +15,14 @@ #include "zcomp_lz4.h" -static void *zcomp_lz4_create(void) +static void *zcomp_lz4_create(gfp_t flags) { void *ret; - /* - * This function can be called in swapout/fs write path - * so we can't use GFP_FS|IO. And it assumes we already - * have at least one stream in zram initialization so we - * don't do best effort to allocate more stream in here. - * A default stream will work well without further multiple - * streams. That's why we use NORETRY | NOWARN. - */ - ret = kzalloc(LZ4_MEM_COMPRESS, GFP_NOIO | __GFP_NORETRY | - __GFP_NOWARN); + ret = kzalloc(LZ4_MEM_COMPRESS, flags); if (!ret) ret = __vmalloc(LZ4_MEM_COMPRESS, - GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN | - __GFP_ZERO | __GFP_HIGHMEM, + flags | __GFP_ZERO | __GFP_HIGHMEM, PAGE_KERNEL); return ret; } diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c index edc549920fa069478e24edf9fa4e416fdbb8adf6..0ab6fce8abe4782994c7f425300a6cce1e21afaf 100644 --- a/drivers/block/zram/zcomp_lzo.c +++ b/drivers/block/zram/zcomp_lzo.c @@ -15,24 +15,14 @@ #include "zcomp_lzo.h" -static void *lzo_create(void) +static void *lzo_create(gfp_t flags) { void *ret; - /* - * This function can be called in swapout/fs write path - * so we can't use GFP_FS|IO. And it assumes we already - * have at least one stream in zram initialization so we - * don't do best effort to allocate more stream in here. - * A default stream will work well without further multiple - * streams. That's why we use NORETRY | NOWARN. - */ - ret = kzalloc(LZO1X_MEM_COMPRESS, GFP_NOIO | __GFP_NORETRY | - __GFP_NOWARN); + ret = kzalloc(LZO1X_MEM_COMPRESS, flags); if (!ret) ret = __vmalloc(LZO1X_MEM_COMPRESS, - GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN | - __GFP_ZERO | __GFP_HIGHMEM, + flags | __GFP_ZERO | __GFP_HIGHMEM, PAGE_KERNEL); return ret; } diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index bbdf32de1452833d0ab0549138073084ca7950a5..22680ab5140eaf821b06d1e1a17f26b16fd65144 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -11,6 +11,11 @@ * Released under the terms of GNU General Public License Version 2.0 * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define KMSG_COMPONENT "zram" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt @@ -39,6 +44,8 @@ static DEFINE_MUTEX(zram_index_mutex); static int zram_major; static const char *default_compressor = "lzo"; +#define BACKEND_PARAM_BUF_SIZE 32 +static char backend_param_buf[BACKEND_PARAM_BUF_SIZE]; /* * We don't need to see memory allocation errors more than once every 1 @@ -235,11 +242,11 @@ static ssize_t mem_used_total_show(struct device *dev, down_read(&zram->init_lock); if (init_done(zram)) { struct zram_meta *meta = zram->meta; - val = zs_get_total_pages(meta->mem_pool); + val = zpool_get_total_size(meta->mem_pool); } up_read(&zram->init_lock); - return scnprintf(buf, PAGE_SIZE, "%llu\n", val << PAGE_SHIFT); + return scnprintf(buf, PAGE_SIZE, "%llu\n", val); } static ssize_t mem_limit_show(struct device *dev, @@ -304,53 +311,32 @@ static ssize_t mem_used_max_store(struct device *dev, if (init_done(zram)) { struct zram_meta *meta = zram->meta; atomic_long_set(&zram->stats.max_used_pages, - zs_get_total_pages(meta->mem_pool)); + zpool_get_total_size(meta->mem_pool) >> PAGE_SHIFT); } up_read(&zram->init_lock); return len; } +/* + * 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) { - int val; - struct zram *zram = dev_to_zram(dev); - - down_read(&zram->init_lock); - val = zram->max_comp_streams; - up_read(&zram->init_lock); - - return scnprintf(buf, PAGE_SIZE, "%d\n", val); + 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) { - int num; - struct zram *zram = dev_to_zram(dev); - int ret; - - ret = kstrtoint(buf, 0, &num); - if (ret < 0) - return ret; - if (num < 1) - return -EINVAL; - - down_write(&zram->init_lock); - if (init_done(zram)) { - if (!zcomp_set_max_streams(zram->comp, num)) { - pr_info("Cannot change max compression streams\n"); - ret = -EINVAL; - goto out; - } - } - - zram->max_comp_streams = num; - ret = len; -out: - up_write(&zram->init_lock); - return ret; + return len; } static ssize_t comp_algorithm_show(struct device *dev, @@ -405,7 +391,7 @@ static ssize_t compact_store(struct device *dev, } meta = zram->meta; - zs_compact(meta->mem_pool); + zpool_compact(meta->mem_pool); up_read(&zram->init_lock); return len; @@ -433,18 +419,13 @@ 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->meta->mem_pool); - zs_pool_stats(zram->meta->mem_pool, &pool_stats); - } + if (init_done(zram)) + mem_used = zpool_get_total_size(zram->meta->mem_pool); orig_size = atomic64_read(&zram->stats.pages_stored); max_used = atomic_long_read(&zram->stats.max_used_pages); @@ -453,11 +434,11 @@ static ssize_t mm_stat_show(struct device *dev, "%8llu %8llu %8llu %8lu %8ld %8llu %8lu\n", orig_size << PAGE_SHIFT, (u64)atomic64_read(&zram->stats.compr_data_size), - mem_used << PAGE_SHIFT, + mem_used, zram->limit_pages << PAGE_SHIFT, max_used << PAGE_SHIFT, (u64)atomic64_read(&zram->stats.zero_pages), - pool_stats.pages_compacted); + zpool_get_num_compacted(zram->meta->mem_pool)); up_read(&zram->init_lock); return ret; @@ -498,10 +479,10 @@ static void zram_meta_free(struct zram_meta *meta, u64 disksize) if (!handle) continue; - zs_free(meta->mem_pool, handle); + zpool_free(meta->mem_pool, handle); } - zs_destroy_pool(meta->mem_pool); + zpool_destroy_pool(meta->mem_pool); vfree(meta->table); kfree(meta); } @@ -510,6 +491,7 @@ static struct zram_meta *zram_meta_alloc(char *pool_name, u64 disksize) { size_t num_pages; struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL); + char *backend; if (!meta) return NULL; @@ -521,7 +503,9 @@ static struct zram_meta *zram_meta_alloc(char *pool_name, u64 disksize) goto out_error; } - meta->mem_pool = zs_create_pool(pool_name); + backend = strlen(backend_param_buf) ? backend_param_buf : "zsmalloc"; + meta->mem_pool = zpool_create_pool(backend, pool_name, + GFP_NOIO, NULL); if (!meta->mem_pool) { pr_err("Error creating memory pool\n"); goto out_error; @@ -557,7 +541,7 @@ static void zram_free_page(struct zram *zram, size_t index) return; } - zs_free(meta->mem_pool, handle); + zpool_free(meta->mem_pool, handle); atomic64_sub(zram_get_obj_size(meta, index), &zram->stats.compr_data_size); @@ -585,12 +569,12 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) return 0; } - cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO); + cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_RO); if (size == PAGE_SIZE) memcpy(mem, cmem, PAGE_SIZE); else ret = zcomp_decompress(zram->comp, cmem, size, mem); - zs_unmap_object(meta->mem_pool, handle); + zpool_unmap_handle(meta->mem_pool, handle); bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); /* Should NEVER happen. Return bio error if it does. */ @@ -657,13 +641,12 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, { int ret = 0; size_t clen; - unsigned long handle; + unsigned long handle = 0; struct page *page; unsigned char *user_mem, *cmem, *src, *uncmem = NULL; struct zram_meta *meta = zram->meta; struct zcomp_strm *zstrm = NULL; unsigned long alloced_pages; - static unsigned long zram_rs_time; page = bvec->bv_page; if (is_partial_io(bvec)) { @@ -681,9 +664,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } - zstrm = zcomp_strm_find(zram->comp); +compress_again: user_mem = kmap_atomic(page); - if (is_partial_io(bvec)) { memcpy(uncmem + offset, user_mem + bvec->bv_offset, bvec->bv_len); @@ -707,6 +689,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } + zstrm = zcomp_strm_find(zram->comp); ret = zcomp_compress(zram->comp, zstrm, uncmem, &clen); if (!is_partial_io(bvec)) { kunmap_atomic(user_mem); @@ -718,6 +701,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, pr_err("Compression failed! err=%d\n", ret); goto out; } + src = zstrm->buffer; if (unlikely(clen > max_zpage_size)) { clen = PAGE_SIZE; @@ -725,28 +709,46 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, src = uncmem; } - handle = zs_malloc(meta->mem_pool, clen, GFP_NOIO | __GFP_HIGHMEM | - __GFP_MOVABLE); - if (!handle) { - if (printk_timed_ratelimit(&zram_rs_time, - ALLOC_ERROR_LOG_RATE_MS)) - pr_info("Error allocating memory for compressed page: %u, size=%zu\n", - index, clen); + /* + * 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) + ret = zpool_malloc(meta->mem_pool, clen, + __GFP_KSWAPD_RECLAIM | __GFP_NOWARN, &handle); + if (ret < 0) { + zcomp_strm_release(zram->comp, zstrm); + zstrm = NULL; + + ret = zpool_malloc(meta->mem_pool, clen, + GFP_NOIO, &handle); + if (ret == 0) + goto compress_again; + pr_err("Error allocating memory for compressed page: %u, size=%zu\n", + index, clen); ret = -ENOMEM; goto out; } - alloced_pages = zs_get_total_pages(meta->mem_pool); + alloced_pages = zpool_get_total_size(meta->mem_pool) >> PAGE_SHIFT; update_used_max(zram, alloced_pages); - if (zram->limit_pages && alloced_pages > zram->limit_pages) { - zs_free(meta->mem_pool, handle); + zpool_free(meta->mem_pool, handle); ret = -ENOMEM; goto out; } - cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO); + cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_WO); if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) { src = kmap_atomic(page); @@ -758,7 +760,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, zcomp_strm_release(zram->comp, zstrm); zstrm = NULL; - zs_unmap_object(meta->mem_pool, handle); + zpool_unmap_handle(meta->mem_pool, handle); /* * Free memory associated with this sector @@ -1021,7 +1023,6 @@ static void zram_reset_device(struct zram *zram) /* Reset stats */ memset(&zram->stats, 0, sizeof(zram->stats)); zram->disksize = 0; - zram->max_comp_streams = 1; set_capacity(zram->disk, 0); part_stat_set_all(&zram->disk->part0, 0); @@ -1050,7 +1051,7 @@ static ssize_t disksize_store(struct device *dev, if (!meta) return -ENOMEM; - comp = zcomp_create(zram->compressor, zram->max_comp_streams); + comp = zcomp_create(zram->compressor); if (IS_ERR(comp)) { pr_err("Cannot initialise %s compressing backend\n", zram->compressor); @@ -1288,7 +1289,6 @@ static int zram_add(void) } strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor)); zram->meta = NULL; - zram->max_comp_streams = 1; pr_info("Added device: %s\n", zram->disk->disk_name); return device_id; @@ -1468,6 +1468,8 @@ module_exit(zram_exit); module_param(num_devices, uint, 0); MODULE_PARM_DESC(num_devices, "Number of pre-created zram devices"); +module_param_string(backend, backend_param_buf, BACKEND_PARAM_BUF_SIZE, 0); +MODULE_PARM_DESC(backend, "Compression storage (backend) name"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Nitin Gupta "); diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 8e92339686d7467584220ff1805002953d5e6621..f9a4c8b6bedc2f68bce970b35472ccebf6c0553f 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -11,12 +11,17 @@ * Released under the terms of GNU General Public License Version 2.0 * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _ZRAM_DRV_H_ #define _ZRAM_DRV_H_ #include -#include +#include #include "zcomp.h" @@ -89,7 +94,7 @@ struct zram_stats { struct zram_meta { struct zram_table_entry *table; - struct zs_pool *mem_pool; + struct zpool *mem_pool; }; struct zram { @@ -102,7 +107,6 @@ struct zram { * the number of pages zram can consume for storing compressed data */ unsigned long limit_pages; - int max_comp_streams; struct zram_stats stats; atomic_t refcount; /* refcount for zram_meta */ diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index f204fe013d489bae0e4d3d0375cc273dfefab134..9b4cb53a2725780fd4ad51d2cf3ee9b4e934f5b1 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -2915,6 +2915,10 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int ioctl_num, p.init.init.memlen >= 0); if (err) goto bail; + VERIFY(err, p.init.init.filelen >= 0 && + p.init.init.memlen >= 0); + if (err) + goto bail; VERIFY(err, 0 == fastrpc_init_process(fl, &p.init)); if (err) goto bail; diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 4eb1c772ded702406850d93488f0b293c96ac317..334e14c9cd07944c445b264ff3d738d278558ad8 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -25,6 +25,11 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _AGP_BACKEND_PRIV_H #define _AGP_BACKEND_PRIV_H 1 diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index f002fa5d1887a0b183b2532891fd853c2587f239..eccf3fed9378f1fcda175b08e6570e6cbefbb6b3 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -27,6 +27,11 @@ * TODO: * - Allocate more than order 0 pages to avoid too much linear map splitting. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h index 8b097cfc4527584a000bd4c931532f073ce87668..e45585faed3ab8091d464127fe324d426cd89faa 100644 --- a/drivers/char/diag/diagfwd.h +++ b/drivers/char/diag/diagfwd.h @@ -26,7 +26,7 @@ #define GET_PD_CTXT(u) ((u & 0xFF000000) >> 24) #define CHK_OVERFLOW(bufStart, start, end, length) \ - ((((bufStart) <= (start)) && ((end) - (start) >= (length))) ? 1 : 0) +((((bufStart) <= (start)) && (end - start >= (length)) && (length > 0)) ? 1 : 0) int diagfwd_init(void); void diagfwd_exit(void); diff --git a/drivers/clk/msm/clock-gcc-8998.c b/drivers/clk/msm/clock-gcc-8998.c index b1c8cc43769f823896c4b3383b2228b584ad02f9..e11d7e0e480aa1365e14bb305f3d7b934e7a6354 100644 --- a/drivers/clk/msm/clock-gcc-8998.c +++ b/drivers/clk/msm/clock-gcc-8998.c @@ -337,6 +337,18 @@ static struct clk_freq_tbl ftbl_blsp_qup_spi_apps_clk_src[] = { F_END }; +static struct clk_freq_tbl ftbl_blsp2_qup3_spi_apps_clk_src[] = { + F( 960000, cxo_clk_src, 10, 1, 2), + F( 4800000, cxo_clk_src, 4, 0, 0), + F( 9600000, cxo_clk_src, 2, 0, 0), + F( 15000000, gpll0_out_main, 10, 1, 4), + F( 19200000, cxo_clk_src, 1, 0, 0), + F( 25000000, gpll0_out_main, 12, 1, 2), + F( 30000000, gpll0_out_main, 10, 1, 2), + F( 50000000, gpll0_out_main, 12, 0, 0), + F_END +}; + static struct rcg_clk blsp1_qup1_spi_apps_clk_src = { .cmd_rcgr_reg = GCC_BLSP1_QUP1_SPI_APPS_CMD_RCGR, .set_rate = set_rate_mnd, @@ -636,7 +648,7 @@ static struct rcg_clk blsp2_qup3_i2c_apps_clk_src = { static struct rcg_clk blsp2_qup3_spi_apps_clk_src = { .cmd_rcgr_reg = GCC_BLSP2_QUP3_SPI_APPS_CMD_RCGR, .set_rate = set_rate_mnd, - .freq_tbl = ftbl_blsp_qup_spi_apps_clk_src, + .freq_tbl = ftbl_blsp2_qup3_spi_apps_clk_src, .current_freq = &rcg_dummy_freq, .base = &virt_base, .c = { diff --git a/drivers/clk/msm/clock-mmss-8998.c b/drivers/clk/msm/clock-mmss-8998.c index fdaaa723accd83949c2a9104b008aa5212bb7949..cf4b738b3e605e1a2fc9e250d31a060c3896a4f9 100644 --- a/drivers/clk/msm/clock-mmss-8998.c +++ b/drivers/clk/msm/clock-mmss-8998.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -399,6 +404,9 @@ static struct rcg_clk maxi_clk_src = { static struct clk_freq_tbl ftbl_cpp_clk_src[] = { F_MM( 100000000, mmsscc_gpll0, 6, 0, 0), F_MM( 200000000, mmsscc_gpll0, 3, 0, 0), +#if defined(CONFIG_SONY_CAM_V4L2) + F_MM( 384000000, mmpll4_pll_out, 2, 0, 0), +#endif F_MM( 576000000, mmpll10_pll_out, 1, 0, 0), F_MM( 600000000, mmsscc_gpll0, 1, 0, 0), F_END diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 6760f84faf062d487f1357b3e33f8054108d7935..a2f24d65752dd73ebf6d9738a691a3e3760afb9e 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -339,7 +339,7 @@ static void arch_counter_set_user_access(void) cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; else cntkctl &= ~ARCH_TIMER_USR_VCT_ACCESS_EN; - + arch_timer_set_cntkctl(cntkctl); } diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index ae65fbc3ceac83e34b7a83e42ce3a14733d532e7..16e1cccdb4a9f810463f5d6aae41dc6ab25aaffb 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -15,6 +15,11 @@ * Author: Mike Chan (mike@android.com) * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -35,6 +40,37 @@ #define CREATE_TRACE_POINTS #include +#ifdef CONFIG_SCHED_HMP +struct cluster { + struct list_head list; + struct task_struct *speedchange_task; + spinlock_t speedchange_cpumask_lock; + cpumask_t speedchange_cpumask; + cpumask_t cpus; + int id; +}; + +static LIST_HEAD(cluster_list_head); + +#define for_each_cluster(cluster) \ + list_for_each_entry(cluster, &cluster_list_head, list) + +#define for_each_cluster_safe(cluster, tmp) \ + list_for_each_entry_safe(cluster, tmp, \ + &cluster_list_head, list) + +static void assign_cluster_ids(void) +{ + struct cluster *clstr; + int pos = 0; + + for_each_cluster(clstr) { + clstr->id = pos; + pos++; + } +} +#endif + struct cpufreq_interactive_policyinfo { struct timer_list policy_timer; struct timer_list policy_slack_timer; @@ -53,11 +89,14 @@ struct cpufreq_interactive_policyinfo { u64 max_freq_hyst_start_time; struct rw_semaphore enable_sem; bool reject_notification; - bool notif_pending; + atomic_t notif_pending; unsigned long notif_cpu; int governor_enabled; struct cpufreq_interactive_tunables *cached_tunables; struct sched_load *sl; +#ifdef CONFIG_SCHED_HMP + struct cluster *cluster; +#endif }; /* Protected by per-policy load_lock */ @@ -72,16 +111,18 @@ struct cpufreq_interactive_cpuinfo { static DEFINE_PER_CPU(struct cpufreq_interactive_policyinfo *, polinfo); static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); +#ifndef CONFIG_SCHED_HMP /* realtime thread handles frequency scaling */ static struct task_struct *speedchange_task; static cpumask_t speedchange_cpumask; static spinlock_t speedchange_cpumask_lock; -static struct mutex gov_lock; +#endif static int set_window_count; static int migration_register_count; static struct mutex sched_lock; static cpumask_t controlled_cpus; +static struct mutex gov_lock; /* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 @@ -480,6 +521,10 @@ static void cpufreq_interactive_timer(unsigned long data) bool jump_to_max_no_ts = false; bool jump_to_max = false; bool start_hyst = true; + bool notif_pending = false; +#ifdef CONFIG_SCHED_HMP + struct cluster *clstr; +#endif if (!down_read_trylock(&ppol->enable_sem)) return; @@ -491,10 +536,15 @@ static void cpufreq_interactive_timer(unsigned long data) spin_lock_irqsave(&ppol->target_freq_lock, flags); spin_lock(&ppol->load_lock); + if (atomic_read(&ppol->notif_pending)) { + (void) hrtimer_try_to_cancel(&ppol->notif_timer); + atomic_set(&ppol->notif_pending, 0); + notif_pending = true; + } + skip_hispeed_logic = - tunables->ignore_hispeed_on_notif && ppol->notif_pending; - skip_min_sample_time = tunables->fast_ramp_down && ppol->notif_pending; - ppol->notif_pending = false; + tunables->ignore_hispeed_on_notif && notif_pending; + skip_min_sample_time = tunables->fast_ramp_down && notif_pending; now = ktime_to_us(ktime_get()); ppol->last_evaluated_jiffy = get_jiffies_64(); @@ -668,10 +718,24 @@ static void cpufreq_interactive_timer(unsigned long data) ppol->target_freq = new_freq; spin_unlock_irqrestore(&ppol->target_freq_lock, flags); + +#ifdef CONFIG_SCHED_HMP + /* + * per cluster == per policy. Each cluster has own policy. + */ + clstr = ppol->cluster; + if (clstr) { + spin_lock_irqsave(&clstr->speedchange_cpumask_lock, flags); + cpumask_set_cpu(max_cpu, &clstr->speedchange_cpumask); + spin_unlock_irqrestore(&clstr->speedchange_cpumask_lock, flags); + wake_up_process_no_notif(clstr->speedchange_task); + } +#else spin_lock_irqsave(&speedchange_cpumask_lock, flags); cpumask_set_cpu(max_cpu, &speedchange_cpumask); spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); wake_up_process_no_notif(speedchange_task); +#endif rearm: if (!timer_pending(&ppol->policy_timer)) @@ -698,6 +762,69 @@ exit: return; } +#ifdef CONFIG_SCHED_HMP +static void update_cpus_freq_in_mask(cpumask_t *mask) +{ + struct cpufreq_interactive_policyinfo *ppol; + unsigned int cpu; + + for_each_cpu(cpu, mask) { + ppol = per_cpu(polinfo, cpu); + if (!down_read_trylock(&ppol->enable_sem)) + continue; + + if (!ppol->governor_enabled) { + up_read(&ppol->enable_sem); + continue; + } + + if (ppol->target_freq != ppol->policy->cur) { + trace_cpufreq_interactive_setspeed(cpu, + ppol->target_freq, + ppol->policy->cur); + __cpufreq_driver_target(ppol->policy, + ppol->target_freq, + CPUFREQ_RELATION_H); + } + + up_read(&ppol->enable_sem); + } +} + +static int speed_change_task_hmp(void *data) +{ + struct cluster *clstr = (struct cluster *) data; + cpumask_t tmp_mask; + unsigned long flags; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&clstr->speedchange_cpumask_lock, flags); + + if (cpumask_empty(&clstr->speedchange_cpumask)) { + spin_unlock_irqrestore( + &clstr->speedchange_cpumask_lock, flags); + schedule(); + + if (kthread_should_stop()) + break; + + spin_lock_irqsave( + &clstr->speedchange_cpumask_lock, flags); + } + + set_current_state(TASK_RUNNING); + tmp_mask = clstr->speedchange_cpumask; + cpumask_clear(&clstr->speedchange_cpumask); + spin_unlock_irqrestore( + &clstr->speedchange_cpumask_lock, flags); + + update_cpus_freq_in_mask(&tmp_mask); + } + + return 0; +} +#else static int cpufreq_interactive_speedchange_task(void *data) { unsigned int cpu; @@ -747,16 +874,60 @@ static int cpufreq_interactive_speedchange_task(void *data) return 0; } +#endif /* CONFIG_SCHED_HMP */ static void cpufreq_interactive_boost(struct cpufreq_interactive_tunables *tunables) { +#ifdef CONFIG_SCHED_HMP + struct cluster *clstr; +#else int i; +#endif int anyboost = 0; unsigned long flags[2]; struct cpufreq_interactive_policyinfo *ppol; tunables->boosted = true; +#ifdef CONFIG_SCHED_HMP + for_each_cluster(clstr) { + cpumask_t cluster_online_cpus = CPU_MASK_NONE; + int cfcpu; + + cpumask_and(&cluster_online_cpus, &clstr->cpus, cpu_online_mask); + cfcpu = cpumask_first(&cluster_online_cpus); + anyboost = 0; + + ppol = per_cpu(polinfo, cfcpu); + if (!ppol || tunables != ppol->policy->governor_data) + continue; + + spin_lock_irqsave(&clstr->speedchange_cpumask_lock, flags[0]); + spin_lock(&ppol->target_freq_lock); + + if (ppol->target_freq < tunables->hispeed_freq) { + ppol->target_freq = tunables->hispeed_freq; + cpumask_set_cpu(cfcpu, &clstr->speedchange_cpumask); + ppol->hispeed_validate_time = + ktime_to_us(ktime_get()); + anyboost = 1; + } + + /* + * Set floor freq and (re)start timer for when last + * validated. + */ + + ppol->floor_freq = tunables->hispeed_freq; + ppol->floor_validate_time = ktime_to_us(ktime_get()); + + spin_unlock(&ppol->target_freq_lock); + spin_unlock_irqrestore(&clstr->speedchange_cpumask_lock, flags[0]); + + if (anyboost) + wake_up_process_no_notif(clstr->speedchange_task); + } +#else spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]); for_each_online_cpu(i) { @@ -788,6 +959,7 @@ static void cpufreq_interactive_boost(struct cpufreq_interactive_tunables *tunab if (anyboost) wake_up_process_no_notif(speedchange_task); +#endif } static int load_change_callback(struct notifier_block *nb, unsigned long val, @@ -796,7 +968,6 @@ static int load_change_callback(struct notifier_block *nb, unsigned long val, unsigned long cpu = (unsigned long) data; struct cpufreq_interactive_policyinfo *ppol = per_cpu(polinfo, cpu); struct cpufreq_interactive_tunables *tunables; - unsigned long flags; if (!ppol || ppol->reject_notification) return 0; @@ -810,14 +981,13 @@ static int load_change_callback(struct notifier_block *nb, unsigned long val, if (!tunables->use_sched_load || !tunables->use_migration_notif) goto exit; - spin_lock_irqsave(&ppol->target_freq_lock, flags); - ppol->notif_pending = true; - ppol->notif_cpu = cpu; - spin_unlock_irqrestore(&ppol->target_freq_lock, flags); + if (!atomic_cmpxchg(&ppol->notif_pending, 0, 1)) { + ppol->notif_cpu = cpu; + if (!hrtimer_is_queued(&ppol->notif_timer)) + hrtimer_start(&ppol->notif_timer, ms_to_ktime(1), + HRTIMER_MODE_REL); + } - if (!hrtimer_is_queued(&ppol->notif_timer)) - hrtimer_start(&ppol->notif_timer, ms_to_ktime(1), - HRTIMER_MODE_REL); exit: up_read(&ppol->enable_sem); return 0; @@ -830,18 +1000,29 @@ static enum hrtimer_restart cpufreq_interactive_hrtimer(struct hrtimer *timer) int cpu; if (!down_read_trylock(&ppol->enable_sem)) - return 0; - if (!ppol->governor_enabled) { - up_read(&ppol->enable_sem); - return 0; - } + goto no_restart; + + if (!ppol->governor_enabled) + goto up_read_no_restart; + + /* + * We race here with a policy timer, but it is not + * a big issue. Just leave the callback, because + * notification has already been handled by the + * normal periodic timer. + */ + if (!atomic_read(&ppol->notif_pending)) + goto up_read_no_restart; + cpu = ppol->notif_cpu; trace_cpufreq_interactive_load_change(cpu); del_timer(&ppol->policy_timer); del_timer(&ppol->policy_slack_timer); cpufreq_interactive_timer(cpu); +up_read_no_restart: up_read(&ppol->enable_sem); +no_restart: return HRTIMER_NORESTART; } @@ -1649,6 +1830,88 @@ static struct cpufreq_interactive_tunables *get_tunables( return cached_common_tunables; } +#ifdef CONFIG_SCHED_HMP +static int hmp_register_cluster(cpumask_t *assign_mask) +{ + struct cpufreq_interactive_policyinfo *ppol; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + struct cluster *clstr; + int i; + + /* + * check if already exists or not + */ + for_each_cluster(clstr) { + if (cpumask_intersects(assign_mask, &clstr->cpus)) + goto leave; + } + + clstr = kzalloc(sizeof(*clstr), GFP_KERNEL); + if (!clstr) + return -ENOMEM; + + /* + * set cluster's mask cpus it is responsible for + */ + cpumask_copy(&clstr->cpus, assign_mask); + spin_lock_init(&clstr->speedchange_cpumask_lock); + + for_each_cpu(i, assign_mask) { + ppol = per_cpu(polinfo, i); + ppol->cluster = clstr; + } + + clstr->speedchange_cpumask = CPU_MASK_NONE; + clstr->speedchange_task = kthread_create(speed_change_task_hmp, + clstr, "cfinteractive"); + + if (IS_ERR(clstr->speedchange_task)) + return PTR_ERR(clstr->speedchange_task); + + sched_setscheduler_nocheck(clstr->speedchange_task, + SCHED_FIFO, ¶m); + get_task_struct(clstr->speedchange_task); + + /* + * add our new cluster to the list + */ + INIT_LIST_HEAD(&clstr->list); + list_add_tail(&clstr->list, &cluster_list_head); + assign_cluster_ids(); + + wake_up_process_no_notif(clstr->speedchange_task); + + pr_info("-> added cluster_%d with %d CPUs\n", + clstr->id, cpumask_weight(&clstr->cpus)); + +leave: + return 0; +} + +static void hmp_unregister_cluster(struct cluster *clstr) +{ + struct cpufreq_interactive_policyinfo *ppol; + int i; + + if (clstr) { + kthread_stop(clstr->speedchange_task); + put_task_struct(clstr->speedchange_task); + + for_each_cpu(i, &clstr->cpus) { + ppol = per_cpu(polinfo, i); + ppol->cluster = NULL; + } + + list_del(&clstr->list); + + pr_info("-> remove cluster_%d with %d CPUs\n", + clstr->id, cpumask_weight(&clstr->cpus)); + + kzfree(clstr); + } +} +#endif /* CONFIG_SCHED_HMP */ + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event) { @@ -1719,6 +1982,14 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, else cached_common_tunables = tunables; +#ifdef CONFIG_SCHED_HMP + mutex_lock(&gov_lock); + rc = hmp_register_cluster(policy->related_cpus); + mutex_unlock(&gov_lock); + + if (rc) + return rc; +#endif break; case CPUFREQ_GOV_POLICY_EXIT: @@ -1762,7 +2033,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, ppol->hispeed_validate_time = ppol->floor_validate_time; ppol->min_freq = policy->min; ppol->reject_notification = true; - ppol->notif_pending = false; + atomic_set(&ppol->notif_pending, 0); down_write(&ppol->enable_sem); del_timer_sync(&ppol->policy_timer); del_timer_sync(&ppol->policy_slack_timer); @@ -1824,12 +2095,17 @@ struct cpufreq_governor cpufreq_gov_interactive = { static int __init cpufreq_interactive_init(void) { +#ifndef CONFIG_SCHED_HMP struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; +#endif int ret = 0; - spin_lock_init(&speedchange_cpumask_lock); mutex_init(&gov_lock); mutex_init(&sched_lock); + +#ifndef CONFIG_SCHED_HMP + spin_lock_init(&speedchange_cpumask_lock); + speedchange_task = kthread_create(cpufreq_interactive_speedchange_task, NULL, "cfinteractive"); @@ -1841,12 +2117,15 @@ static int __init cpufreq_interactive_init(void) /* NB: wake up so the thread does not look hung to the freezer */ wake_up_process_no_notif(speedchange_task); +#endif ret = cpufreq_register_governor(&cpufreq_gov_interactive); +#ifndef CONFIG_SCHED_HMP if (ret) { kthread_stop(speedchange_task); put_task_struct(speedchange_task); } +#endif return ret; } @@ -1861,8 +2140,18 @@ static void __exit cpufreq_interactive_exit(void) int cpu; cpufreq_unregister_governor(&cpufreq_gov_interactive); +#ifdef CONFIG_SCHED_HMP + { + struct cluster *clstr, *tmp; + + for_each_cluster_safe(clstr, tmp) { + hmp_unregister_cluster(clstr); + } + } +#else kthread_stop(speedchange_task); put_task_struct(speedchange_task); +#endif for_each_possible_cpu(cpu) free_policyinfo(cpu); diff --git a/drivers/devfreq/governor_msm_adreno_tz.c b/drivers/devfreq/governor_msm_adreno_tz.c index f31089d63e0c1fd7aa3fbdbee6156cc003250bb7..baba654315f0542f436c77c872048a161bd8e50e 100644 --- a/drivers/devfreq/governor_msm_adreno_tz.c +++ b/drivers/devfreq/governor_msm_adreno_tz.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -26,6 +31,7 @@ static DEFINE_SPINLOCK(tz_lock); static DEFINE_SPINLOCK(sample_lock); +static DEFINE_SPINLOCK(sample_load_lock); static DEFINE_SPINLOCK(suspend_lock); /* * FLOOR is 5msec to capture up to 3 re-draws @@ -58,9 +64,26 @@ static DEFINE_SPINLOCK(suspend_lock); #define TAG "msm_adreno_tz: " -static u64 suspend_time; -static u64 suspend_start; +#define USEC_PER_MINUTE (1*60*1000*1000) +#define NMAX (15*60*60+1) + +struct gpu_load_data { + unsigned long total_time; + unsigned long busy_time; + u64 update_time; +}; + +struct gpu_load_queue { + struct gpu_load_data *gpu_load; + int head; + int tail; +}; + +static u64 suspend_time, suspend_time_idd; +static u64 suspend_start, suspend_start_idd; static unsigned long acc_total, acc_relative_busy; +static unsigned long gpu_load_total, gpu_load_rel_busy; +static struct gpu_load_queue *gpu_load_infos; static struct msm_adreno_extended_profile *partner_gpu_profile; static void do_partner_start_event(struct work_struct *work); @@ -88,6 +111,21 @@ u64 suspend_time_ms(void) return time_diff; } +u64 suspend_time_ms_idd(void) +{ + u64 suspend_sampling_time; + u64 time_diff = 0; + + if (suspend_start_idd == 0) + return 0; + + suspend_sampling_time = (u64)ktime_to_ms(ktime_get()); + time_diff = suspend_sampling_time - suspend_start_idd; + /* Update the suspend_start sample again */ + suspend_start_idd = suspend_sampling_time; + return time_diff; +} + static ssize_t gpu_load_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -109,6 +147,135 @@ static ssize_t gpu_load_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%lu\n", sysfs_busy_perc); } +int findItem(int head, int tail, u64 refTime) +{ + int low = head, high = tail, middle; + u64 update_time; + + while (low < high) { + middle = (low + high) / 2; + update_time = + gpu_load_infos->gpu_load[middle % NMAX].update_time; + + if (update_time <= refTime) + low = middle + 1; + else if (update_time > refTime) + high = middle - 1; + } + + update_time = gpu_load_infos->gpu_load[low % NMAX].update_time; + if (update_time > refTime) + low = low - 1; + + if (low < tail) { + u64 l = gpu_load_infos->gpu_load[low % NMAX].update_time; + u64 r = gpu_load_infos->gpu_load[(low+1) % NMAX].update_time; + + if (r - refTime < refTime - l) + low = low + 1; + } + return low % NMAX; +} + +unsigned long cal_gpu_load(u64 current_time, u64 update_time, + u64 begin_time, int minutes) +{ + unsigned long sysfs_busy_perc; + unsigned long tmp_act_relative_busy, tmp_act_total; + int tail = gpu_load_infos->tail, head = gpu_load_infos->head; + + if (current_time - update_time > minutes * USEC_PER_MINUTE) { + sysfs_busy_perc = 0; + } else { + int tmpIndex = head; + + if (current_time - begin_time <= minutes * USEC_PER_MINUTE) { + tmpIndex = head; + } else { + u64 begin = current_time - minutes * USEC_PER_MINUTE; + int tmpTail = (tail < head) ? tail + NMAX : tail; + + tmpIndex = findItem(head, tmpTail-1, begin); + } + + tmp_act_relative_busy = + gpu_load_infos->gpu_load[tail-1].busy_time - + gpu_load_infos->gpu_load[tmpIndex].busy_time; + + tmp_act_total = current_time - + gpu_load_infos->gpu_load[tmpIndex].update_time; + + sysfs_busy_perc = (tmp_act_relative_busy * 100 * 10) / + tmp_act_total; + } + return sysfs_busy_perc; +} + +void convert_int_to_string(unsigned long perc, char *str, unsigned int size) +{ + char low[6] = "0"; + char mid[] = "."; + + snprintf(str, sizeof(low), "%lu", (perc / 10)); + snprintf(low, sizeof(low), "%lu", (perc % 10)); + strlcat(str, mid, size); + strlcat(str, low, size); +} + +static ssize_t gpu_period_load_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long sysfs_busy_perc[3]; + int tail, head; + char load[3][6] = {"0", "0", "0"}; + int i; + unsigned int size; + spin_lock(&sample_load_lock); + tail = gpu_load_infos->tail; + head = gpu_load_infos->head; + + if (tail == head) { + sysfs_busy_perc[0] = 0; + sysfs_busy_perc[1] = 0; + sysfs_busy_perc[2] = 0; + } else { + u64 current_time = (u64)ktime_to_us(ktime_get()); + u64 update_time = gpu_load_infos->gpu_load[tail-1].update_time; + u64 begin_time = gpu_load_infos->gpu_load[head].update_time; + + sysfs_busy_perc[0] = cal_gpu_load(current_time, + update_time, begin_time, 1); + sysfs_busy_perc[1] = cal_gpu_load(current_time, + update_time, begin_time, 5); + sysfs_busy_perc[2] = cal_gpu_load(current_time, + update_time, begin_time, 15); + } + for (i = 0; i < 3; i++) { + size = sizeof(load[i]); + convert_int_to_string(sysfs_busy_perc[i], load[i], size); + } + + spin_unlock(&sample_load_lock); + return snprintf(buf, PAGE_SIZE, "%s %s %s\n", + load[0], + load[1], + load[2] + ); +} + +static ssize_t gpu_load_idd_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long busy, total; + spin_lock(&sample_load_lock); + busy = gpu_load_rel_busy; + total = gpu_load_total; + spin_unlock(&sample_load_lock); + return snprintf(buf, PAGE_SIZE, "%lu %lu\n", busy, total); +} + /* * Returns the time in ms for which gpu was in suspend state * since last time the entry is read. @@ -134,7 +301,30 @@ static ssize_t suspend_time_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%llu\n", time_diff); } +static ssize_t suspend_time_idd_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u64 time_diff = 0; + + spin_lock(&suspend_lock); + time_diff = suspend_time_ms_idd(); + /* + * Adding the previous suspend time also as the gpu + * can go and come out of suspend states in between + * reads also and we should have the total suspend + * since last read. + */ + suspend_time_idd += time_diff; + spin_unlock(&suspend_lock); + + return snprintf(buf, PAGE_SIZE, "%llu\n", suspend_time_idd); +} + static DEVICE_ATTR(gpu_load, 0444, gpu_load_show, NULL); +static DEVICE_ATTR(gpu_period_load, 0444, gpu_period_load_show, NULL); +static DEVICE_ATTR(gpu_load_idd, 0444, gpu_load_idd_show, NULL); +static DEVICE_ATTR(gpu_suspend_idd, 0444, suspend_time_idd_show, NULL); static DEVICE_ATTR(suspend_time, 0444, suspend_time_show, @@ -142,10 +332,30 @@ static DEVICE_ATTR(suspend_time, 0444, static const struct device_attribute *adreno_tz_attr_list[] = { &dev_attr_gpu_load, + &dev_attr_gpu_period_load, + &dev_attr_gpu_load_idd, + &dev_attr_gpu_suspend_idd, &dev_attr_suspend_time, NULL }; +void store_work_load(unsigned long gpu_load_total, unsigned long gpu_load_busy) +{ + /* Queue item to gpu_load_queue */ + int index = gpu_load_infos->tail; + int head = gpu_load_infos->head; + + gpu_load_infos->gpu_load[index].total_time = gpu_load_total; + gpu_load_infos->gpu_load[index].busy_time = gpu_load_busy; + gpu_load_infos->gpu_load[index].update_time = + (u64)ktime_to_us(ktime_get()); + + if ((index + 1) % NMAX == head) + gpu_load_infos->head = (head + 1) % NMAX; + + gpu_load_infos->tail = (index + 1) % NMAX; +} + void compute_work_load(struct devfreq_dev_status *stats, struct devfreq_msm_adreno_tz_data *priv, struct devfreq *devfreq) @@ -160,6 +370,15 @@ void compute_work_load(struct devfreq_dev_status *stats, acc_relative_busy += (stats->busy_time * stats->current_frequency) / devfreq->profile->freq_table[0]; spin_unlock(&sample_lock); + + spin_lock(&sample_load_lock); + gpu_load_total += stats->total_time; + gpu_load_rel_busy += (stats->busy_time * stats->current_frequency) / + devfreq->profile->freq_table[0]; + + store_work_load(gpu_load_total, gpu_load_rel_busy); + + spin_unlock(&sample_load_lock); } /* Trap into the TrustZone, and call funcs there. */ @@ -230,6 +449,14 @@ static int __secure_tz_update_entry3(unsigned int *scm_data, u32 size_scm_data, return ret; } +static void tz_init_gpuloadinfos(void) +{ + gpu_load_infos = kzalloc(sizeof(struct gpu_load_queue), GFP_KERNEL); + gpu_load_infos->head = gpu_load_infos->tail = 0; + gpu_load_infos->gpu_load = + kcalloc(NMAX, sizeof(struct gpu_load_data), GFP_KERNEL); +} + static int tz_init_ca(struct devfreq_msm_adreno_tz_data *priv) { unsigned int tz_ca_data[2]; @@ -269,6 +496,8 @@ static int tz_init(struct devfreq_msm_adreno_tz_data *priv, unsigned int *version, u32 size_version) { int ret; + + tz_init_gpuloadinfos(); /* Make sure all CMD IDs are avaialble */ if (scm_is_call_available(SCM_SVC_DCVS, TZ_INIT_ID)) { ret = scm_call(SCM_SVC_DCVS, TZ_INIT_ID, tz_pwrlevels, @@ -556,6 +785,7 @@ static int tz_handler(struct devfreq *devfreq, unsigned int event, void *data) spin_lock(&suspend_lock); /* Collect the start sample for suspend time */ suspend_start = (u64)ktime_to_ms(ktime_get()); + suspend_start_idd = suspend_start; spin_unlock(&suspend_lock); } break; @@ -563,8 +793,10 @@ static int tz_handler(struct devfreq *devfreq, unsigned int event, void *data) case DEVFREQ_GOV_RESUME: spin_lock(&suspend_lock); suspend_time += suspend_time_ms(); + suspend_time_idd += suspend_time_ms_idd(); /* Reset the suspend_start when gpu resumes */ suspend_start = 0; + suspend_start_idd = 0; spin_unlock(&suspend_lock); case DEVFREQ_GOV_INTERVAL: diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index db8f0eeca7c9e0e5c3be66c8b6358fb666c71492..15c362a2be1c51213d097ae552ba2ecdf273f239 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -23,6 +23,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -80,6 +85,8 @@ static const char *extcon_name[] = { [EXTCON_JIG] = "JIG", [EXTCON_MECHANICAL] = "MECHANICAL", + /* Somc Extention */ + [EXTCON_VBUS_DROP] = "VBUS-DROP", NULL, }; diff --git a/drivers/firmware/qcom/tz_log.c b/drivers/firmware/qcom/tz_log.c index c893681f3bf3341b45cdfbb66e9d709419b229bc..4a3afc00f827432c55f793c0404d35dbce302e31 100644 --- a/drivers/firmware/qcom/tz_log.c +++ b/drivers/firmware/qcom/tz_log.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -26,6 +31,11 @@ #include #include +#include + +#ifdef CONFIG_LAST_LOGS +#include +#endif /* QSEE_LOG_BUF_SIZE = 32K */ #define QSEE_LOG_BUF_SIZE 0x8000 @@ -1165,6 +1175,150 @@ err: return -ENXIO; } +#ifdef CONFIG_LAST_LOGS +#define TZBSP_DIAG_MAGIC 0x747a6461 +#define MAX_BANNER_LEN 1024 +#define TZDBG_STATS_COUNT (TZDBG_LOG + 1) +static int (*disp_stat[TZDBG_STATS_MAX])(void); +static char *merge_buf; +static uint32_t merge_buf_len; + +static int _tz_log_stats(void) +{ + static struct tzdbg_log_pos_t log_start = {0}; + struct tzdbg_log_t *log_ptr; + + log_ptr = (struct tzdbg_log_t *)((unsigned char *)tzdbg.diag_buf + + tzdbg.diag_buf->ring_off - + offsetof(struct tzdbg_log_t, log_buf)); + + /* No data in ring buffer, so no need to hang around */ + if (log_ptr && log_ptr->log_pos.offset == 0 && log_ptr->log_pos.wrap == 0) + return 0; + + return _disp_log_stats(log_ptr, &log_start, + tzdbg.diag_buf->ring_len, debug_rw_buf_size, TZDBG_LOG); +} + +static void merge_buffers(void) +{ + int data_len = 0, len = 0, i, status = SECURITY_ON; + + if (get_security_status(&status) < 0) + pr_warn("Unable to get security status.\n"); + + for (i = 0; i < TZDBG_STATS_COUNT; i++) { + if ((status == SECURITY_ON) && (i == TZDBG_LOG)) + continue; + + if ((len + MAX_BANNER_LEN + debug_rw_buf_size) < + merge_buf_len) { + len += snprintf(merge_buf + len, + MAX_BANNER_LEN, "\n\n--------%s--------\n\n", + tzdbg.stat[i].name); + data_len = disp_stat[i](); + memcpy(merge_buf + len, tzdbg.stat[i].data, data_len); + len += data_len; + memset(tzdbg.disp_buf, 0x0, debug_rw_buf_size); + } + } + + merge_buf_len = len; + pr_info("Length of merged buffers %d\n", len); +} + +int format_tzbsp_log(void *src, size_t src_sz, void **dst, uint32_t *dst_sz) +{ + char *save_disp_buf_addr = NULL; + uint32_t save_debug_rw_buf_size = 0; + struct tzdbg_t *dbg = (struct tzdbg_t *)src; + int ret = 0; + + if (debug_rw_buf_size != src_sz) + pr_warn("%s: Diag buffer size does not match - 0x%lx", + __func__, src_sz); + + if (dbg == NULL || !src_sz || !tzdbg.diag_buf) + return -EINVAL; + + /* validate tzdiag area w.r.t magic */ + if (dbg->magic_num != TZBSP_DIAG_MAGIC) { + pr_err("No magic found, magic: 0x%x\n", dbg->magic_num); + return -ENXIO; + } + + /* As we use the tzdbg.disp_buf pointer, backup tzdbg.disp_buf and + restore it before returns */ + save_disp_buf_addr = tzdbg.disp_buf; + save_debug_rw_buf_size = debug_rw_buf_size; + + /* Debug buffer size increased to 48k (12K*4) size. + Because, formatted output buffer size is more than the + unformatted buffer size */ + /* debug_rw_buf_size is modified and will be restored + before the function returns*/ + debug_rw_buf_size = src_sz * 4; + + /* Merge buffer increased to 294k size */ + merge_buf_len = (debug_rw_buf_size + MAX_BANNER_LEN) * + TZDBG_STATS_COUNT; + + merge_buf = kzalloc(merge_buf_len, GFP_KERNEL); + if (merge_buf == NULL) { + pr_err("%s: Can't Allocate memory: merged_buf\n", + __func__); + ret = -ENOMEM; + goto exit; + } + + tzdbg.disp_buf = kzalloc(debug_rw_buf_size, GFP_KERNEL); + if (tzdbg.disp_buf == NULL) { + pr_err("%s: Can't Allocate memory: disp_buf\n", + __func__); + ret = -ENOMEM; + goto exit1; + } + + memcpy(tzdbg.diag_buf, src, src_sz); + + disp_stat[TZDBG_BOOT] = _disp_tz_boot_stats; + disp_stat[TZDBG_RESET] = _disp_tz_reset_stats; + disp_stat[TZDBG_INTERRUPT] = _disp_tz_interrupt_stats; + disp_stat[TZDBG_VMID] = _disp_tz_vmid_stats; + disp_stat[TZDBG_GENERAL] = _disp_tz_general_stats; + if (TZBSP_DIAG_MAJOR_VERSION_LEGACY < + (tzdbg.diag_buf->version >> 16)) { + disp_stat[TZDBG_LOG] = _tz_log_stats; + } else { + disp_stat[TZDBG_LOG] = _disp_tz_log_stats_legacy; + } + + merge_buffers(); + /* Allocate required buffer to store the formatted logs */ + *dst = kzalloc(merge_buf_len, GFP_KERNEL); + if (*dst == NULL) { + pr_err("%s: Can't Allocate memory: buffer\n", + __func__); + ret = -ENOMEM; + goto exit2; + } + + *dst_sz = merge_buf_len; + memcpy(*dst, merge_buf, *dst_sz); + +exit2: + memset(tzdbg.diag_buf, 0x0, src_sz); + kzfree(tzdbg.disp_buf); +exit1: + kzfree(merge_buf); +exit: + /* Restore tzdbg.disp_buf pointer and debug_rw_buf_size */ + tzdbg.disp_buf = save_disp_buf_addr; + debug_rw_buf_size = save_debug_rw_buf_size; + return ret; +} + +#endif static int tz_log_remove(struct platform_device *pdev) { diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index fe89fd56eabfb48c5a7c71b435a988404d9db894..40901080b758731d0b01799798f7120372d6fff1 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/drivers/gpio/qpnp-pin.c b/drivers/gpio/qpnp-pin.c index 62cd78a953031621148ec90bde2871d543275e55..542ebdd9386895092c96d8c9423db16099d8f006 100644 --- a/drivers/gpio/qpnp-pin.c +++ b/drivers/gpio/qpnp-pin.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -718,6 +723,117 @@ gpio_cfg: return rc; } +static int _qpnp_get_pin_config(struct qpnp_pin_chip *q_chip, + struct qpnp_pin_spec *q_spec, struct qpnp_pin_cfg *param) +{ + u8 shift, mask, *reg; + + if (is_gpio_lv_mv(q_spec)) { + shift = Q_REG_LV_MV_MODE_SEL_SHIFT; + mask = Q_REG_LV_MV_MODE_SEL_MASK; + } else { + shift = Q_REG_MODE_SEL_SHIFT; + mask = Q_REG_MODE_SEL_MASK; + } + param->mode = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL], + shift, mask); + + param->output_type = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL], + Q_REG_OUT_TYPE_SHIFT, + Q_REG_OUT_TYPE_MASK); + + if (is_gpio_lv_mv(q_spec)) { + shift = Q_REG_DIG_OUT_SRC_INVERT_SHIFT; + mask = Q_REG_DIG_OUT_SRC_INVERT_MASK; + reg = &q_spec->regs[Q_REG_I_DIG_OUT_SRC_CTL]; + } else { + shift = Q_REG_OUT_INVERT_SHIFT; + mask = Q_REG_OUT_INVERT_MASK; + reg = &q_spec->regs[Q_REG_I_MODE_CTL]; + } + param->invert = q_reg_get(reg, shift, mask); + + param->pull = q_reg_get(&q_spec->regs[Q_REG_I_DIG_PULL_CTL], + Q_REG_PULL_SHIFT, Q_REG_PULL_MASK); + param->vin_sel = q_reg_get(&q_spec->regs[Q_REG_I_DIG_VIN_CTL], + Q_REG_VIN_SHIFT, Q_REG_VIN_MASK); + param->out_strength = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL], + Q_REG_OUT_STRENGTH_SHIFT, + Q_REG_OUT_STRENGTH_MASK); + + if (is_gpio_lv_mv(q_spec)) { + shift = Q_REG_DIG_OUT_SRC_SRC_SEL_SHIFT; + mask = Q_REG_DIG_OUT_SRC_SRC_SEL_MASK; + reg = &q_spec->regs[Q_REG_I_DIG_OUT_SRC_CTL]; + } else { + shift = Q_REG_SRC_SEL_SHIFT; + mask = Q_REG_SRC_SEL_MASK; + reg = &q_spec->regs[Q_REG_I_MODE_CTL]; + } + param->src_sel = q_reg_get(reg, shift, mask); + + param->master_en = q_reg_get(&q_spec->regs[Q_REG_I_EN_CTL], + Q_REG_MASTER_EN_SHIFT, + Q_REG_MASTER_EN_MASK); + param->aout_ref = q_reg_get(&q_spec->regs[Q_REG_I_AOUT_CTL], + Q_REG_AOUT_REF_SHIFT, + Q_REG_AOUT_REF_MASK); + param->ain_route = q_reg_get(&q_spec->regs[Q_REG_I_AIN_CTL], + Q_REG_AIN_ROUTE_SHIFT, + Q_REG_AIN_ROUTE_MASK); + param->cs_out = q_reg_get(&q_spec->regs[Q_REG_I_SINK_CTL], + Q_REG_CS_OUT_SHIFT, + Q_REG_CS_OUT_MASK); + param->apass_sel = q_reg_get(&q_spec->regs[Q_REG_I_APASS_SEL_CTL], + Q_REG_APASS_SEL_SHIFT, + Q_REG_APASS_SEL_MASK); + if (is_gpio_lv_mv(q_spec)) { + param->dtest_sel = q_reg_get(&q_spec->regs[Q_REG_I_DIG_IN_CTL], + Q_REG_LV_MV_DTEST_SEL_CFG_SHIFT, + Q_REG_LV_MV_DTEST_SEL_CFG_MASK); + } else { + param->dtest_sel = q_reg_get(&q_spec->regs[Q_REG_I_DIG_IN_CTL], + Q_REG_DTEST_SEL_SHIFT, + Q_REG_DTEST_SEL_MASK); + } + return 0; +} + +int qpnp_get_pin_config(int gpio, struct qpnp_pin_cfg *param) +{ + int rc, chip_offset; + struct qpnp_pin_chip *q_chip; + struct qpnp_pin_spec *q_spec = NULL; + struct gpio_chip *gpio_chip; + + if (param == NULL) + return -EINVAL; + + mutex_lock(&qpnp_pin_chips_lock); + list_for_each_entry(q_chip, &qpnp_pin_chips, chip_list) { + gpio_chip = &q_chip->gpio_chip; + if (gpio >= gpio_chip->base + && gpio < gpio_chip->base + gpio_chip->ngpio) { + chip_offset = gpio - gpio_chip->base; + q_spec = qpnp_chip_gpio_get_spec(q_chip, chip_offset); + if (WARN_ON(!q_spec)) { + mutex_unlock(&qpnp_pin_chips_lock); + return -ENODEV; + } + break; + } + } + mutex_unlock(&qpnp_pin_chips_lock); + + if (!q_spec) + return -ENODEV; + + rc = _qpnp_get_pin_config(q_chip, q_spec, param); + + return rc; +} +EXPORT_SYMBOL(qpnp_get_pin_config); + int qpnp_pin_config(int gpio, struct qpnp_pin_cfg *param) { int rc, chip_offset; diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 845e4ad7b464c44e63a6aec2d0f1fce2a88e67bd..4f94cdfabbbcc6c5f18afdb141aa060b4bb25a42 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c index f1f955f571fa1fa277da91ffc830726edebe32e8..a87f898707a3d887e19c8b3b414d5f2c298b568a 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 9cb65033ed133db49d678aa223fc16eb000107c3..0d7cc7844748be3b4d49822d1a1140d0770f8cd5 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -2436,7 +2441,7 @@ static void adreno_regwrite(struct kgsl_device *device, /*ensure previous writes post before this one, * i.e. act like normal writel() */ wmb(); - __raw_writel(value, reg); + __raw_writel_no_log(value, reg); } /** diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index d1d399cce06af0631f95e84217adca98f6a09e0e..64946dbde07e404a364a15dfd1d7e0d56f5ad158 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -6,6 +6,11 @@ * Copyright (c) 2005 Michael Haboustak for Concept2, Inc * Copyright (c) 2006-2012 Jiri Kosina */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * This program is free software; you can redistribute it and/or modify it @@ -2012,6 +2017,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 60e2c9faa95ff0d425dfcffba1c276d41c6eb88e..a0351f2f22d2870d8f32b8e13b2af446866add0c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -6,6 +6,11 @@ * Copyright (c) 2005 Michael Haboustak for Concept2, Inc * Copyright (c) 2006-2007 Jiri Kosina */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * This program is free software; you can redistribute it and/or modify it @@ -879,6 +884,7 @@ #define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306 #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 #define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4 +#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 0x09cc #define USB_DEVICE_ID_SONY_MOTION_CONTROLLER 0x03d5 #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f #define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002 diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 21febbb0d84e6e59f9615cd9046d09b0690675f7..8e2bf78b51cc8c039a6347692fbb65b540ad0cc8 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -10,6 +10,11 @@ * Copyright (c) 2013 Colin Leitner * Copyright (c) 2014 Frank Praznik */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * This program is free software; you can redistribute it and/or modify it @@ -2277,6 +2282,11 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) hid_set_drvdata(hdev, sc); sc->hdev = hdev; + if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { + hid_dbg(hdev, "Ignoring DUALSHOCK4 Controller via USB\n"); + return 0; + } + ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); @@ -2404,6 +2414,11 @@ static void sony_remove(struct hid_device *hdev) { struct sony_sc *sc = hid_get_drvdata(hdev); + if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { + hid_dbg(hdev, "Ignoring DUALSHOCK4 Controller via USB\n"); + return; + } + if (sc->quirks & SONY_LED_SUPPORT) sony_leds_remove(sc); @@ -2458,6 +2473,8 @@ static const struct hid_device_id sony_devices[] = { /* Sony Dualshock 4 controllers for PS4 */ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), .driver_data = DUALSHOCK4_CONTROLLER_USB }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), + .driver_data = DUALSHOCK4_CONTROLLER_USB }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), .driver_data = DUALSHOCK4_CONTROLLER_BT }, { } diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 690a9f0fa04293ff8b6ec617109a6e5dd08a34bc..9a32b318b763cace00d3f83d4e8404b4bead18f9 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -2,6 +2,11 @@ * User-space I/O driver support for HID subsystem * Copyright (c) 2012 David Herrmann */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c index 812b96b381e3e448a30c30e72aa75be4742dc1a8..736856d0e1ebe69ee75c3f4ed718a4879f1bae76 100644 --- a/drivers/hwmon/qpnp-adc-common.c +++ b/drivers/hwmon/qpnp-adc-common.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -426,6 +431,44 @@ static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb[] = { {44, 125} }; +/* Voltage to temperature */ +static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb_decidegc[] = { + {1758, -400}, + {1742, -350}, + {1719, -300}, + {1691, -250}, + {1654, -200}, + {1608, -150}, + {1551, -100}, + {1483, -50}, + {1404, 0}, + {1315, 50}, + {1218, 100}, + {1114, 150}, + {1007, 200}, + {900, 250}, + {795, 300}, + {696, 350}, + {605, 400}, + {522, 450}, + {448, 500}, + {383, 550}, + {327, 600}, + {278, 650}, + {237, 700}, + {202, 750}, + {172, 800}, + {146, 850}, + {125, 900}, + {107, 950}, + {92, 1000}, + {79, 1050}, + {68, 1100}, + {59, 1150}, + {51, 1200}, + {44, 1250} +}; + /* Voltage to temperature */ static const struct qpnp_vadc_map_pt adcmap_150k_104ef_104fb[] = { {1738, -40}, @@ -629,6 +672,47 @@ static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = { { 46, 125 }, }; +/* + * Voltage to temperature table for 100k pull up for NTCG104EF104 with + * 1.875V reference. + */ +static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb_1875_vref_decidegc[] = { + { 1831, -400 }, + { 1814, -350 }, + { 1791, -300 }, + { 1761, -250 }, + { 1723, -200 }, + { 1675, -150 }, + { 1616, -100 }, + { 1545, -50 }, + { 1463, 0 }, + { 1370, 50 }, + { 1268, 100 }, + { 1160, 150 }, + { 1049, 200 }, + { 937, 250 }, + { 828, 300 }, + { 726, 350 }, + { 630, 400 }, + { 544, 450 }, + { 467, 500 }, + { 399, 550 }, + { 340, 600 }, + { 290, 650 }, + { 247, 700 }, + { 209, 750 }, + { 179, 800 }, + { 153, 850 }, + { 130, 900 }, + { 112, 950 }, + { 96, 1000 }, + { 82, 1050 }, + { 71, 1100 }, + { 62, 1150 }, + { 53, 1200 }, + { 46, 1250 }, +}; + static int32_t qpnp_adc_map_voltage_temp(const struct qpnp_vadc_map_pt *pts, uint32_t tablesize, int32_t input, int64_t *output) { @@ -1186,6 +1270,161 @@ int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *chip, } EXPORT_SYMBOL(qpnp_adc_tm_scale_therm_voltage_pu2); +int32_t qpnp_adc_scale_therm_pu2_decidegc(struct qpnp_vadc_chip *chip, + int32_t adc_code, + const struct qpnp_adc_properties *adc_properties, + const struct qpnp_vadc_chan_properties *chan_properties, + struct qpnp_vadc_result *adc_chan_result) +{ + int64_t therm_voltage = 0; + int32_t rc = -EINVAL; + + if (!chip || !adc_properties || !chan_properties || !adc_chan_result || + !chan_properties->offset_gain_numerator || + !chan_properties->offset_gain_denominator) + goto error; + + if (adc_properties->adc_hc) { + /* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */ + if (adc_code > QPNP_VADC_HC_MAX_CODE) + adc_code = 0; + therm_voltage = (int64_t) adc_code; + therm_voltage *= (int64_t) (adc_properties->adc_vdd_reference + * 1000); + if (therm_voltage < 0) + goto error; + + therm_voltage = div64_s64(therm_voltage, + (QPNP_VADC_HC_VREF_CODE * 1000)); + + rc = qpnp_adc_map_voltage_temp( + adcmap_100k_104ef_104fb_1875_vref_decidegc, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref_decidegc), + therm_voltage, &adc_chan_result->physical); + } else { + qpnp_adc_scale_with_calib_param(adc_code, + adc_properties, chan_properties, &therm_voltage); + if (therm_voltage < 0) + goto error; + + if (chan_properties->calib_type == CALIB_ABSOLUTE) + do_div(therm_voltage, 1000); + + rc = qpnp_adc_map_voltage_temp( + adcmap_100k_104ef_104fb_decidegc, + ARRAY_SIZE(adcmap_100k_104ef_104fb_decidegc), + therm_voltage, &adc_chan_result->physical); + } + +error: + return rc; +} +EXPORT_SYMBOL(qpnp_adc_scale_therm_pu2_decidegc); + +int32_t qpnp_adc_tm_scale_voltage_therm_pu2_decidegc( + struct qpnp_vadc_chip *chip, + const struct qpnp_adc_properties *adc_properties, + uint32_t reg, int64_t *result) +{ + int64_t adc_voltage = 0; + struct qpnp_vadc_linear_graph param1; + int32_t rc = -EINVAL; + + if (!chip || !result || !adc_properties) + goto error; + + if (adc_properties->adc_hc) { + /* (ADC code * vref_vadc (1.875V)) / 0x4000 */ + if (reg > QPNP_VADC_HC_MAX_CODE) + reg = 0; + adc_voltage = (int64_t) reg; + adc_voltage *= QPNP_VADC_HC_VDD_REFERENCE_MV; + adc_voltage = div64_s64(adc_voltage, + QPNP_VADC_HC_VREF_CODE); + rc = qpnp_adc_map_voltage_temp( + adcmap_100k_104ef_104fb_1875_vref_decidegc, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref_decidegc), + adc_voltage, result); + } else { + rc = qpnp_get_vadc_gain_and_offset(chip, ¶m1, + CALIB_RATIOMETRIC); + if (rc) + goto error; + + adc_voltage = (reg - param1.adc_gnd) * param1.adc_vref; + if (adc_voltage < 0) + adc_voltage = -adc_voltage; + + do_div(adc_voltage, param1.dy); + + rc = qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_decidegc, + ARRAY_SIZE(adcmap_100k_104ef_104fb_decidegc), + adc_voltage, result); + } + +error: + return rc; +} +EXPORT_SYMBOL(qpnp_adc_tm_scale_voltage_therm_pu2_decidegc); + +int32_t qpnp_adc_tm_scale_therm_voltage_pu2_decidegc( + struct qpnp_vadc_chip *chip, + const struct qpnp_adc_properties *adc_properties, + struct qpnp_adc_tm_config *param) +{ + struct qpnp_vadc_linear_graph param1; + int32_t rc = -EINVAL; + + if (!chip || !param || !adc_properties) + goto error; + + if (adc_properties->adc_hc) { + rc = qpnp_adc_map_temp_voltage( + adcmap_100k_104ef_104fb_1875_vref_decidegc, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref_decidegc), + param->low_thr_temp, ¶m->low_thr_voltage); + if (rc) + goto error; + param->low_thr_voltage *= QPNP_VADC_HC_VREF_CODE; + do_div(param->low_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV); + + rc = qpnp_adc_map_temp_voltage( + adcmap_100k_104ef_104fb_1875_vref_decidegc, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref_decidegc), + param->high_thr_temp, ¶m->high_thr_voltage); + if (rc) + goto error; + param->high_thr_voltage *= QPNP_VADC_HC_VREF_CODE; + do_div(param->high_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV); + } else { + qpnp_get_vadc_gain_and_offset(chip, ¶m1, CALIB_RATIOMETRIC); + + rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb_decidegc, + ARRAY_SIZE(adcmap_100k_104ef_104fb_decidegc), + param->low_thr_temp, ¶m->low_thr_voltage); + if (rc) + goto error; + + param->low_thr_voltage *= param1.dy; + do_div(param->low_thr_voltage, param1.adc_vref); + param->low_thr_voltage += param1.adc_gnd; + + rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb_decidegc, + ARRAY_SIZE(adcmap_100k_104ef_104fb_decidegc), + param->high_thr_temp, ¶m->high_thr_voltage); + if (rc) + goto error; + + param->high_thr_voltage *= param1.dy; + do_div(param->high_thr_voltage, param1.adc_vref); + param->high_thr_voltage += param1.adc_gnd; + } + +error: + return rc; +} +EXPORT_SYMBOL(qpnp_adc_tm_scale_therm_voltage_pu2_decidegc); + int32_t qpnp_adc_scale_therm_ncp03(struct qpnp_vadc_chip *chip, int32_t adc_code, const struct qpnp_adc_properties *adc_properties, diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c index 6ed947e5603bb6461edca5e67c82eb4a7944e59a..7b56f58135da0b7ed9b4a7ab1ed892e6de3ae5e6 100644 --- a/drivers/hwmon/qpnp-adc-voltage.c +++ b/drivers/hwmon/qpnp-adc-voltage.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -219,6 +224,7 @@ static struct qpnp_vadc_scale_fn vadc_scale_fn[] = { [SCALE_NCP_03WF683_THERM] = {qpnp_adc_scale_therm_ncp03}, [SCALE_QRD_SKUT1_BATT_THERM] = {qpnp_adc_scale_qrd_skut1_batt_therm}, [SCALE_PMI_CHG_TEMP] = {qpnp_adc_scale_pmi_chg_temp}, + [SCALE_THERM_100K_PULLUP_DECI] = {qpnp_adc_scale_therm_pu2_decidegc}, }; static struct qpnp_vadc_rscale_fn adc_vadc_rscale_fn[] = { diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c index d72953f2df23a14e7086d78baa0b3a17608c8173..1d7f78a97bf69f2ebbfc04658138afd8a9be0bd7 100644 --- a/drivers/i2c/busses/i2c-msm-v2.c +++ b/drivers/i2c/busses/i2c-msm-v2.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * I2C controller driver for Qualcomm Technologies Inc platforms */ @@ -414,6 +419,7 @@ struct i2c_msm_clk_div_fld { */ static struct i2c_msm_clk_div_fld i2c_msm_clk_div_map[] = { {KHz(100), 124, 62}, + {KHz(355), 32, 16}, {KHz(400), 28, 14}, {KHz(1000), 8, 5}, }; diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index b3aa73f1a5a1d6e2914e6bc9e87294f38c7d07f3..aa295e3327cf05d1801676f0f6b6d96db03e0c79 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "RRADC: %s: " fmt, __func__ @@ -224,7 +229,22 @@ enum rradc_channel_id { RR_ADC_MAX }; +enum rradc_reg_data_idx { + RR_ADC_REG_DATA_ADDR = 0, + RR_ADC_REG_DATA_MASK, + RR_ADC_REG_DATA_VALUE, + RR_ADC_REG_DATA_IDX_MAX +}; + +struct rradc_reg_cfg { + uint32_t addr; + uint32_t mask; + uint32_t val; +}; + struct rradc_chip { + const struct rradc_reg_cfg *reg_cfg; + int reg_cfg_num; struct device *dev; struct mutex lock; struct regmap *regmap; @@ -1080,6 +1100,8 @@ static const struct iio_info rradc_info = { static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node) { + const uint32_t *prop_buf; + int reg_cfg_size = 0; const struct rradc_channels *rradc_chan; struct iio_chan_spec *iio_chan; unsigned int i = 0, base; @@ -1123,6 +1145,19 @@ static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node) } } + prop_buf = of_get_property(node, "somc,reg-cfg", ®_cfg_size); + if (prop_buf) { + if ((reg_cfg_size / sizeof(uint32_t)) + % RR_ADC_REG_DATA_IDX_MAX) { + pr_err("Register config data is invalid size\n"); + return -EINVAL; + } + + chip->reg_cfg = (struct rradc_reg_cfg *)prop_buf; + chip->reg_cfg_num = reg_cfg_size / sizeof(uint32_t) + / RR_ADC_REG_DATA_IDX_MAX; + } + iio_chan = chip->iio_chans; for (i = 0; i < RR_ADC_MAX; i++) { @@ -1148,6 +1183,7 @@ static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node) static int rradc_probe(struct platform_device *pdev) { + int i; struct device_node *node = pdev->dev.of_node; struct device *dev = &pdev->dev; struct iio_dev *indio_dev; @@ -1172,6 +1208,23 @@ static int rradc_probe(struct platform_device *pdev) if (rc) return rc; + for (i = 0; i < chip->reg_cfg_num; i++) { + rc = rradc_masked_write(chip, + (u16)be32_to_cpu(chip->reg_cfg[i].addr), + (u8)be32_to_cpu(chip->reg_cfg[i].mask), + (u8)be32_to_cpu(chip->reg_cfg[i].val)); + if (rc < 0) { + pr_err("Failed in register write (addr=0x%02x)\n", + (u16)be32_to_cpu(chip->reg_cfg[i].addr)); + return -EIO; + } + + pr_debug("Write register (addr=0x%02x mask=0x%02x value=0x%02x)\n", + (u16)be32_to_cpu(chip->reg_cfg[i].addr), + (u8)be32_to_cpu(chip->reg_cfg[i].mask), + (u8)be32_to_cpu(chip->reg_cfg[i].val)); + } + indio_dev->dev.parent = dev; indio_dev->dev.of_node = node; indio_dev->name = pdev->name; diff --git a/drivers/input/input.c b/drivers/input/input.c index baaddd16880459160a75491981840b05caaecfde..61d9cb91d0e852952df130432e5f3c19b64d6d98 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -3,6 +3,11 @@ * * Copyright (c) 1999-2002 Vojtech Pavlik */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index c93dd193a496812465ec07b0cfb821e8201ea1e8..deaf0e5bc5252ab5c6b66d000507829d9dd7d4a6 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -9,6 +9,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index dd98d8c8fd1f3577b5de0ebbf12810e30903efc9..59ce5f358bd192bb5c687ae64d44b0c1fa7b3038 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -844,4 +844,26 @@ config INPUT_STMVL53L0 To compile this driver as a module, choose M here: the module will be called stmvl53l0. +config INPUT_ADUX1050 + tristate "ADUX1050 sensor support" + depends on I2C=y + help + Say Y here if you have ADUX1050 hooked to an I2C bus. + To compile this driver as a module, choose M here: + The module will be called adux1050.ko + +config ADUX1050_DEBUG + bool " Debug :To enable the Basic debug support for the driver" + depends on INPUT_ADUX1050 + default n + help + To add the basic debug support of the driver in the build. Used in printing the debug information on the console. + +config ADUX1050_EVAL + bool "Eval : To enable the driver in evaluation mode for test " + depends on INPUT_ADUX1050 + default n + help + To use the generic sysfs path and modifications for the control attributes. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 44c026abcb6fece1154c3ea3fe81bf717cb0f19f..09aaf7030032c0d0fd561e2113105d0f77f797e5 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o +obj-$(CONFIG_INPUT_ADUX1050) += adux1050.o obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o diff --git a/drivers/input/misc/adux1050.c b/drivers/input/misc/adux1050.c new file mode 100644 index 0000000000000000000000000000000000000000..d93e0aef30975c867a9a0532a61ac2a0a431ea40 --- /dev/null +++ b/drivers/input/misc/adux1050.c @@ -0,0 +1,4386 @@ +/* + * ADUX1050 Generic Controller Driver + * + * @copyright 2016 Analog Devices Inc. + * + * Licensed under the GPL version 2 or later. + * date OCT-2016 + * version Driver 1.3.1 + * version Linux 3.18.20 and above + * version Android 6.0.1 [Marshmallow] + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +/* + * drivers/input/misc/adux1050.c + * This file is the core driver part of ADUX1050 Capacitive sensor. + * It also has routines for interrupt handling, + * suspend, resume, initialization routines etc. + * + * ADUX1050 Generic Controller Driver + * + * Copyright 2016 Analog Devices Inc. + * + * Licensed under the GPL version 2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#ifdef CONFIG_ADUX1050_POLL +#include +#endif +#include + +#define ADUX1050_DRIVER_DBG(format, arg...) pr_debug("[ADUX1050]:"format,\ + ## arg) + +#ifdef CONFIG_ADUX1050_AWFUL_LOG +#define ADUX1050_AWFUL_LOG(format, arg...) pr_debug("[ADUX1050]:"format, ## arg) +#else +#define ADUX1050_AWFUL_LOG(format, arg...) do { if (0); } while (0) +#endif + + +#define CHK_AUTO_TH_ENABLED(bs_reg) (bs_reg & AUTO_TH_MASK) + + +/* + * Local platform data used if no platform data is found in board file or + * Device tree. + */ +static struct adux1050_platform_data local_plat_data = { + .init_regs = { + 0x0001010F, 0x00020529, 0x00030000, 0x00050F55, + 0x00069999, 0x000700e8, 0x00080200, 0x00090000, + 0x000a000C, 0x00798000, + 0x000b9999, 0x000c03e8, 0x000d0200, 0x000e0000, + 0x000f000C, 0x007a8000, + 0x00109999, 0x001105e8, 0x00120200, 0x00130000, + 0x0014000C, 0x007b8000, + 0x00159999, 0x001607e8, 0x00170200, 0x00180000, + 0x0019000C, 0x007c8000, + }, + .req_stg0_base = 10000, + .req_stg1_base = 20000, + .req_stg2_base = 30000, + .req_stg3_base = 40000, + .is_set_irq_gpio_no = false, + .proxy_enable = false, + .allowed_proxy_time = 20, + .max_proxy_count = 10, + .intr_err = 0, +}; + +/* + * int adux1050_i2c_write(struct device *dev, u8 reg, u16 *data, u16 data_cnt) + * Writes to the device register through I2C interface. + * Used to write the data to the I2C client's Register through the i2c protocol + * Used i2c_transfer api's for the bus transfer + * @param dev The i2c client's device structure + * @param reg The register address to be written + * @param data The data buffer which holds the data to be written to the device + * @param data_cnt The number of data to be written to the device from buffer. + * @return Number of messages transferred, default 2 + * + * @see adux1050_i2c_read + */ +static int adux1050_i2c_write(struct device *dev, u8 reg, + u16 *data, u16 data_cnt) +{ + + struct i2c_client *client = to_i2c_client(dev); + u8 device_addr = client->addr; + u16 loop_cnt = 0; + u8 tx[(MAX_ADUX1050_WR_LEN*sizeof(short)) + 1] = {0}; + u16 *head; + s32 ret = -EIO; + struct i2c_msg adux1050_wr_msg = { + .addr = device_addr, + .buf = (u8 *)tx, + .len = ((data_cnt*sizeof(data_cnt))+1), + .flags = 0, + }; + if (!data) + return -EINVAL; + tx[0] = reg; + head = (unsigned short *)&tx[1]; + for (loop_cnt = 0; loop_cnt < data_cnt; loop_cnt++) + *(head++) = cpu_to_be16(*(data++)); + + for (loop_cnt = 0; loop_cnt < I2C_RETRY_CNT; loop_cnt++) { + ret = i2c_transfer(client->adapter, &adux1050_wr_msg, 1); + if (unlikely(ret < 1)) + dev_err(&client->dev, "I2C write error %d\n", ret); + else + break; + } + return ret; +} + +/* + * int adux1050_i2c_read(struct device *dev, u8 reg,u16 *data, u16 data_cnt) + * This is used to read the data from the ADUX1050's register through + * I2C interface + * This function uses i2c protocol and its api's to read data from register + * @param dev The i2c client device Structure. + * @param reg The register address to be read. + * @param data The buffer's pointer to store the register's value. + * @param data_cnt The number of registers to be read. + * @return The number of messages transferred as an integer + * + * @see adux1050_i2c_write + */ +static int adux1050_i2c_read(struct device *dev, u8 reg, + u16 *data, u16 data_cnt) +{ + struct i2c_client *client = to_i2c_client(dev); + u16 loop_cnt = 0; + u16 rx[MAX_ADUX1050_WR_LEN] = {}; + s8 device_addr = client->addr; + s32 ret = 0; + struct i2c_msg adux1050_rd_msg[I2C_WRMSG_LEN] = { + { + .addr = device_addr, + .buf = (u8 *)®, + .len = sizeof(reg), + .flags = 0, + }, + { + .addr = device_addr, + .buf = (u8 *)rx, + .len = data_cnt * sizeof(short), + .flags = I2C_M_RD, + } + }; + for (loop_cnt = 0; loop_cnt < I2C_RETRY_CNT; loop_cnt++) { + ret = i2c_transfer(client->adapter, adux1050_rd_msg, + I2C_WRMSG_LEN); + if (unlikely(ret < I2C_WRMSG_LEN)) { + dev_err(dev, "[ADUX1050]: I2C READ error %d\n", ret); + if (loop_cnt >= (I2C_RETRY_CNT - 1)) + memset(data, 0, data_cnt * sizeof(short)); + } else { + for (loop_cnt = 0; loop_cnt < data_cnt; loop_cnt++) + data[loop_cnt] = be16_to_cpu(rx[loop_cnt]); + break; + } + } + return ret; +} + +/* + * inline u16 set_dac_offset(s16 new_offset_value) + * Function to set the DAC positive and negative offset based on given offset + * @param new_offset_value value to be set as the offset. + * @return Combined +ve and -ve value to be set to the DAC_OFFSET_STGx register + */ +static inline u16 set_dac_offset(s16 new_offset_value) +{ + u16 offset_val = 0; + + if (new_offset_value >= 0) + offset_val = ST_POS_DAC_OFFSET(new_offset_value); + else + offset_val = ST_NEG_DAC_OFFSET(new_offset_value); + + return offset_val; +} + +/* + * static inline s16 set_swap_state(struct adux1050_chip *adux1050, u16 stg_num, + * u16 *swap_state, s16 curr_val) + * Function to set the swap bits based on the calibration DAC offset + * @param adux1050 chip structure of ADUX1050 driver. + * @param stg_num The stage to which swap is to be done for DAC offset value. + * @param swap_state The swap state as set in control register + * [ctrl reg holds the swap state] + * @param curr_val Current value of DAC_offest for the stage provided [stg_num] + * @return Zero on success. + */ +static inline s16 set_swap_state(struct adux1050_chip *adux1050, u16 stg_num, + u16 *swap_state, s16 curr_val) { + s16 err = 0; + + if (!swap_state) + return -EINVAL; + if (curr_val > (MAX_OFFSET/2)) { + *swap_state = ((*swap_state & CLR_POS_SWAP) | SET_NEG_SWAP); + err = adux1050->write(adux1050->dev, GET_CONFIG_REG(stg_num), + swap_state, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Error in FC %d\n", err); + return err; + } + dev_dbg(adux1050->dev, "%s - Swap set %x", + __func__, *swap_state); + } else if (curr_val < -(MAX_OFFSET/2)) { + *swap_state = ((*swap_state & CLR_NEG_SWAP) | SET_POS_SWAP); + err = adux1050->write(adux1050->dev, GET_CONFIG_REG(stg_num), + swap_state, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Error in FC %d\n", err); + return err; + } + dev_dbg(adux1050->dev, "%s - Swap set %x", + __func__, *swap_state); + } else { + dev_dbg(adux1050->dev, "%s - No Swap to be set", __func__); + *swap_state = ((*swap_state & CLR_NEG_SWAP) & CLR_POS_SWAP); + err = adux1050->write(adux1050->dev, GET_CONFIG_REG(stg_num), + swap_state, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Error in FC %d\n", err); + return err; + } + } + return err; +} + +/* + * short adux1050_force_cal(struct adux1050_chip *adux1050,int cal_time) + * Internal function to perform force calibration of the Chip. + * @param adux1050 The chip structure of adux1050 driver + * @param cal_time Sleep time required after the force calibration. + * @return 0 on success and -1 on error + */ +static inline s16 adux1050_force_cal(struct adux1050_chip *adux1050, + int cal_time) +{ + u16 data = 0; + s16 err = 0; + + err = adux1050->read(adux1050->dev, BASELINE_CTRL_REG, &data, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Error in FC %d\n", err); + return -EIO; + } + data = data | FORCE_CAL_MASK; + err = adux1050->write(adux1050->dev, BASELINE_CTRL_REG, &data, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Error in FC %d\n", err); + return -EIO; + } + if (cal_time != 0) { + dev_dbg(adux1050->dev, " sleep time in FC:%d\n", cal_time); + msleep(cal_time); + } + return err; +} + +/* + * inline s16 get_conv_time(struct adux1050_chip *adux1050, int mul_flag) + * To get the required conversion time for the current seting + * @param adux1050 Chip structure. + * @param mul_flag Multiplier flag + * @return Returns the conversion time required for an updated configuration + */ +static inline s16 get_conv_time(struct adux1050_chip *adux1050, int mul_flag) +{ + u16 pwr_ctrl_reg = 0; + u16 cv_time_ctrl = 0; + u16 pwr_mode = 0; + u16 stg_num = 0; + u16 delay_in_ctoc = 0; + u16 avg = 0; + u16 osr = 0; + u16 phase = 0; + u16 base_time = 0; + u16 temp_base_time = 0; + u16 stg_cfg = 0; + u16 lp_cnt = 0; + s16 err = 0; + + err = adux1050->read(adux1050->dev, CTRL_REG, &pwr_ctrl_reg, DEF_WR); + if (err < I2C_WRMSG_LEN) + dev_err(adux1050->dev, "i2c RD Err %d\n", err); + err = adux1050->read(adux1050->dev, CONV_TIME_CTRL_REG, &cv_time_ctrl, + DEF_WR); + if (err < I2C_WRMSG_LEN) + dev_err(adux1050->dev, "i2c RD Err %d\n", err); + pwr_mode = GET_PWR_MODE(pwr_ctrl_reg); + stg_num = GET_NUM_STG(pwr_ctrl_reg); + adux1050->tot_stg = stg_num; + + if (pwr_mode == PWR_STAND_BY) { + dev_dbg(adux1050->dev, "Device in Standby mode\n"); + return 1; + } else if (pwr_mode == PWR_FULL_POWER) { + delay_in_ctoc = ZERO_VAL; + } else if (pwr_mode == PWR_TIMED_CONV) { + delay_in_ctoc = GET_TIMED_CONV_TIME(pwr_ctrl_reg); + } else if (pwr_mode == PWR_AUTO_WAKE) { + delay_in_ctoc = GET_TIMED_CONV_TIME(pwr_ctrl_reg); + if (delay_in_ctoc < GET_AUTO_WAKE_TIME(pwr_ctrl_reg)) + delay_in_ctoc = GET_AUTO_WAKE_TIME(pwr_ctrl_reg); + } + avg = GET_AVG_CONV(cv_time_ctrl); + avg = CALC_AVG_CONV(avg); /*AVG based multiplication factor*/ + osr = GET_OSR_CONV(cv_time_ctrl); + osr = CALC_OSR_CONV(osr); /*OSR multipling factor*/ + phase = GET_CONV_TIME(cv_time_ctrl); /*Phase timing factor*/ + /* Calculate base time as a factor of phase */ + temp_base_time = ((phase + (phase / DECIMAL_BASE)) - 3) / 2; + dev_dbg(adux1050->dev, "%s, temp_Basetime(%d), avg (%d), OSR (%d)\n", + __func__, temp_base_time, avg, osr); + temp_base_time *= (avg * osr); /*Set the base_time*/ + for (; lp_cnt < stg_num; lp_cnt++) { + err = adux1050->read(adux1050->dev, GET_CONFIG_REG(lp_cnt), + &stg_cfg, DEF_WR); + if (err < I2C_WRMSG_LEN) + dev_err(adux1050->dev, "i2c RD Err %d\n", err); + if (IS_NOISE_MEASURE_EN(stg_cfg)) + base_time += (temp_base_time * + GET_NOISE_SAMPLE(cv_time_ctrl)); + else + base_time += temp_base_time; + } + if (mul_flag == TWICE_CONV_DELAY_TIME) { + base_time += base_time; /*Return twice the conv time*/ + base_time += delay_in_ctoc; /*Add the timed conv delay*/ + } else if (mul_flag == CONV_DELAY_TIME) { + base_time += delay_in_ctoc; + } + dev_dbg(adux1050->dev, "%s, Basetime(%d), delay_in_ctoc (%d)\n", + __func__, base_time, delay_in_ctoc); + + return base_time; +} + +/* + * int get_intr_mask_info(struct adux1050_chip *adux1050) + * This function is used to get interrupt mask information. + * @param adux1050 The chip structure of ADUX1050 driver + * @return 0 on success. + */ +static int get_intr_mask_info(struct adux1050_chip *adux1050) +{ + u16 temp_reg_val = 0; + s16 err = 0; + + /* Checking whether Conversion complete interrupt is enabled or not */ + err = adux1050->read(adux1050->dev, INT_CTRL_REG, + &temp_reg_val, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", err, __FILE__); + return err; + } + adux1050->conv_enable = CHECK_CONV_EN(temp_reg_val); + ADUX1050_DRIVER_DBG("%s - Checking conv_enable %d temp_reg_val %x\n", + __func__, adux1050->conv_enable, temp_reg_val); + /* Checking whether High threshold interrupt is enabled or not */ + adux1050->high_thresh_enable = CHECK_THRESH_HIGH_EN(temp_reg_val); + + /* Checking whether Low threshold interrupt is enabled or not */ + adux1050->low_thresh_enable = CHECK_THRESH_LOW_EN(temp_reg_val); + return 0; +} + +/* + * int getstageinfo(struct adux1050_chip *adux1050) + * This function is used to get the current stage information. + * @param adux1050 The chip structure of ADUX1050 driver + * @return 0 on success. + */ +static int getstageinfo(struct adux1050_chip *adux1050) +{ + u16 temp_reg_val = 0; + s16 err = 0; + u8 stg_cnt = 0; + u8 cin_cnt = 0; + u8 temp_cin; + + adux1050->conn_stg_cnt = 0; + + /* How many stages to measure CDC */ + err = adux1050->read(adux1050->dev, CTRL_REG, &temp_reg_val, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", err, __FILE__); + return err; + } + adux1050->num_stages = GET_NUM_STG(temp_reg_val); + /* Find whether stage is connected(either +ve or -ve) or not */ + for (stg_cnt = 0 ; stg_cnt < TOTAL_STG ; stg_cnt++) { + if (stg_cnt < adux1050->num_stages) { + err = adux1050->read(adux1050->dev, + GET_CONFIG_REG(stg_cnt), + &temp_reg_val, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", + err, __FILE__); + return err; + } + + adux1050->stg_info[stg_cnt].status = CIN_NOT_CONNECTED; + for (cin_cnt = 0; cin_cnt < TOTAL_CIN ; cin_cnt++) { + temp_cin = (temp_reg_val) & 3; + if ((temp_cin == CIN_NEG_INPUT) || + (temp_cin == CIN_POS_INPUT)) { + adux1050->stg_info[stg_cnt].status = + CIN_CONNECTED; + adux1050->conn_stg_cnt++; + dev_dbg(adux1050->dev, + "STG CONNECTED %d,tot=%d\n", + stg_cnt, + adux1050->conn_stg_cnt); + break; + } + temp_reg_val = temp_reg_val >> 2; + } + } else { + adux1050->stg_info[stg_cnt].status = CIN_NOT_CONNECTED; + pr_debug("%s - CDC not configured for STG_%d\n", + __func__, stg_cnt); + } + } + return 0; +} + +/* + * int update_calib_settings(struct adux1050_chip *adux1050, u16 total_stg, + u16 *data, bool write_to_reg, u8 file_exist) + * This function updates the calibration output to local register array and + registers of ADUX1050. + * @param adux1050 The chip structure of ADUX1050 driver + * @param total_stg Number of stages for which calib settings to be updated + * @param *data Pointer to buffer which contains the calib output + * @param write_to_reg A Flag to specify whether to write to registers or not + * @param file_exist A Flag to update the calib status + * @return 0 on success + */ +inline int update_calib_settings(struct adux1050_chip *adux1050, u16 total_stg, + u16 *data, bool write_to_reg, u8 file_exist) +{ + u16 temp_baseline_ctrl = 0; + u16 int_ctrl_reg = 0; + u16 stg_cnt = 0; + u16 stg_num = 0; + u8 fp = 0; + u16 cal_base_fail_flag = 0; + u16 value = 0; + u16 hys_reg = 0; + s16 err = 0; + + + /* Disable the interrupt */ + err = adux1050->read(adux1050->dev, INT_CTRL_REG, + &int_ctrl_reg, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", err, __FILE__); + goto err_calib; + } + value = int_ctrl_reg | DISABLE_DEV_INT; + err = adux1050->write(adux1050->dev, INT_CTRL_REG, &value, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", err, __FILE__); + goto err_calib; + } + + /* Disabling the Auto threhold & Force calib to */ + /* update baseline registers */ + err = adux1050->read(adux1050->dev, BASELINE_CTRL_REG, + &temp_baseline_ctrl, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", err, __FILE__); + goto err_calib; + } + temp_baseline_ctrl = temp_baseline_ctrl & ANTI_FORCE_CAL_MASK; + if (temp_baseline_ctrl & AUTO_TH_MASK) { + value = temp_baseline_ctrl; + value = value & (~AUTO_TH_MASK); + err = adux1050->write(adux1050->dev, BASELINE_CTRL_REG, + &value, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + goto err_calib; + } + } + msleep(adux1050->slp_time_conv_complete); + + for (stg_cnt = 0; stg_cnt < total_stg; stg_cnt++) { + stg_num = data[fp++]; + if ((stg_num >= STG_ZERO) && (stg_num <= STG_THREE)) { + + adux1050->pdata->cal_fact_base[stg_num] = data[fp++]; + adux1050->pdata->cal_offset[stg_num] = data[fp++]; + adux1050->pdata->digi_offset[stg_num] = data[fp++]; + adux1050->pdata->stg_cfg[stg_num] = data[fp++]; + + adux1050->bs_reg[stg_num].wr_flag = ADUX1050_ENABLE; + adux1050->bs_reg[stg_num].value = + adux1050->pdata->cal_fact_base[stg_num]; + + adux1050->reg[GET_OFFSET_REG(stg_num)].wr_flag = + ADUX1050_ENABLE; + adux1050->reg[GET_OFFSET_REG(stg_num)].value = + adux1050->pdata->cal_offset[stg_num]; + + adux1050->reg[GET_HYS_REG(stg_num)].wr_flag = + ADUX1050_ENABLE; + hys_reg = + ((adux1050->reg[GET_HYS_REG(stg_num)].value) & + HYS_BYTE_MASK) | + (adux1050->pdata->digi_offset[stg_num] << 8); + adux1050->reg[GET_HYS_REG(stg_num)].value = hys_reg; + + adux1050->reg[GET_CONFIG_REG(stg_num)].wr_flag = + ADUX1050_ENABLE; + adux1050->reg[GET_CONFIG_REG(stg_num)].value = + adux1050->pdata->stg_cfg[stg_num]; + cal_base_fail_flag |= + adux1050->pdata->cal_fact_base[stg_num]; + + if (write_to_reg == ADUX1050_ENABLE) { + err = adux1050->write( + adux1050->dev, GET_BASE_LINE_REG(stg_cnt), + &adux1050->pdata->cal_fact_base[stg_cnt], + DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, + "I2C WR Err %d in %s\n", + err, __FILE__); + goto err_calib; + } + + err = adux1050->write( + adux1050->dev, GET_OFFSET_REG(stg_cnt), + &adux1050->pdata->cal_offset[stg_cnt], DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, + "I2C WR Err %d in %s\n", + err, __FILE__); + goto err_calib; + } + + err = adux1050->read(adux1050->dev, + GET_HYS_REG(stg_cnt), + &hys_reg, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, + "I2C RD Err %d in %s\n", + err, __FILE__); + goto err_calib; + } + + hys_reg = + ((hys_reg & HYS_BYTE_MASK) | + (adux1050->pdata->digi_offset[stg_cnt] << 8)); + err = adux1050->write(adux1050->dev, + GET_HYS_REG(stg_cnt), + &hys_reg, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, + "I2C WR Err %d in %s\n", + err, __FILE__); + goto err_calib; + } + + err = adux1050->write( + adux1050->dev, GET_CONFIG_REG(stg_cnt), + &adux1050->pdata->stg_cfg[stg_cnt], DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, + "I2C WR Err %d in %s\n", + err, __FILE__); + goto err_calib; + } + } + + } else { + dev_err(adux1050->dev, + "%s Invalid Stg num in FILP or SYSFS input\n", + __func__); + break; + } + } + + pr_debug("Calib status = %d\n", adux1050->dac_calib.cal_flags); + /*Restoring the Auto threshold mode if enabled previously*/ + if (temp_baseline_ctrl & AUTO_TH_MASK) { + err = adux1050->write(adux1050->dev, BASELINE_CTRL_REG, + &temp_baseline_ctrl, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + goto err_calib; + } + } + /* Reenable the interrupt */ + err = adux1050->write(adux1050->dev, INT_CTRL_REG, + &int_ctrl_reg, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", err, __FILE__); + goto err_calib; + } + + if (cal_base_fail_flag != 0) { + adux1050->dac_calib.cal_flags = CAL_RET_SUCCESS; + #ifdef CONFIG_ADUX1050_EVAL + err = get_intr_mask_info(adux1050); + /* Getting the stage info */ + err = getstageinfo(adux1050); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, "Error in getstageinfo %d\n", + err); + goto err_calib; + } + #endif + } else { + if (file_exist) + adux1050->dac_calib.cal_flags = CAL_RET_EXIST; + else + adux1050->dac_calib.cal_flags = CAL_RET_NONE; + err = -EIO; + } + return err; +err_calib: + if (file_exist) + adux1050->dac_calib.cal_flags = CAL_RET_EXIST; + else + adux1050->dac_calib.cal_flags = CAL_RET_NONE; + err = -EIO; + return err; + +} + + +/* + * int adux1050_store_register_values(struct adux1050_chip *adux1050) + * This is to retrieve the register values from either device tree/local + * platform data and store it in local array + * @param adux1050 The Device structure + * @return Zero on success + */ +static int adux1050_store_register_values(struct adux1050_chip *adux1050) +{ + u32 lcnt = 0; + u32 data_cnt = 0; + const u32 *init_buffer = NULL; + const __be32 *df_regs = NULL; + u32 df_prop_length = 0; +#ifdef CONFIG_OF + + u32 len; + const __be32 *property = NULL; +#endif + u8 of_reg_found = false; + +#ifdef CONFIG_OF + + /* Fetching data from Device tree */ + if (adux1050->dt_device_node) { + df_regs = of_get_property(adux1050->dt_device_node, + "adi,adux1050_reg", &df_prop_length); + /* Fetching required baseline value for STG 0 */ + property = of_get_property(adux1050->dt_device_node, + "adi,adux1050_stg0_base", + &len); + if (property && len == sizeof(int)) { + adux1050->pdata->req_stg0_base = + be32_to_cpu(*property); + + dev_dbg(adux1050->dev, "valid req_base on %s\n", + adux1050->dt_device_node->full_name); + } else { + dev_dbg(adux1050->dev, "Invalid req_base on %s\n", + adux1050->dt_device_node->full_name); + } + /* Fetching required baseline value for STG 1 */ + property = of_get_property(adux1050->dt_device_node, + "adi,adux1050_stg1_base", + &len); + if (property && len == sizeof(int)) { + adux1050->pdata->req_stg1_base = + be32_to_cpu(*property); + + dev_dbg(adux1050->dev, "valid req_base1 on %s\n", + adux1050->dt_device_node->full_name); + } else { + dev_dbg(adux1050->dev, "Invalid req_base1 on %s\n", + adux1050->dt_device_node->full_name); + } + /* Fetching required baseline value for STG 2 */ + property = of_get_property(adux1050->dt_device_node, + "adi,adux1050_stg2_base", + &len); + if (property && len == sizeof(int)) { + adux1050->pdata->req_stg2_base = + be32_to_cpu(*property); + + dev_dbg(adux1050->dev, "valid req_base2 on %s\n", + adux1050->dt_device_node->full_name); + } else { + dev_dbg(adux1050->dev, "Invalid req_base2 on %s\n", + adux1050->dt_device_node->full_name); + } + /* Fetching required baseline value for STG 3 */ + property = of_get_property(adux1050->dt_device_node, + "adi,adux1050_stg3_base", &len); + if (property && len == sizeof(int)) { + adux1050->pdata->req_stg3_base = + be32_to_cpu(*property); + + dev_dbg(adux1050->dev, "valid req_base3 on %s\n", + adux1050->dt_device_node->full_name); + } else { + dev_dbg(adux1050->dev, "Invalid req_base3 on %s\n", + adux1050->dt_device_node->full_name); + } + } +#endif + if ((adux1050->pdata->req_stg0_base > MAX_CALIB_TARGET) || + (adux1050->pdata->req_stg0_base < MIN_CALIB_TARGET)) + adux1050->pdata->req_stg0_base = HALF_SCALE_VAL; + + if ((adux1050->pdata->req_stg1_base > MAX_CALIB_TARGET) || + (adux1050->pdata->req_stg1_base < MIN_CALIB_TARGET)) + adux1050->pdata->req_stg1_base = HALF_SCALE_VAL; + + if ((adux1050->pdata->req_stg2_base > MAX_CALIB_TARGET) || + (adux1050->pdata->req_stg2_base < MIN_CALIB_TARGET)) + adux1050->pdata->req_stg2_base = HALF_SCALE_VAL; + + if ((adux1050->pdata->req_stg3_base > MAX_CALIB_TARGET) || + (adux1050->pdata->req_stg3_base < MIN_CALIB_TARGET)) + adux1050->pdata->req_stg3_base = HALF_SCALE_VAL; + + /* Data from either DT or initial platform data */ + if ((!df_regs) || (df_prop_length % sizeof(u32))) { + if (df_prop_length % sizeof(u32)) + dev_err(adux1050->dev, "[ADUX1050]: Malformed prop regs\n"); + init_buffer = adux1050->pdata->init_regs; + data_cnt = sizeof(adux1050->pdata->init_regs)/sizeof(int); + } else { + init_buffer = df_regs; + data_cnt = df_prop_length / sizeof(u32); + of_reg_found = true; + } + /* Setting enable for INT_CTRL register */ + adux1050->reg[INT_CTRL_REG].wr_flag = ADUX1050_ENABLE; + + for (lcnt = 0; lcnt < data_cnt; lcnt++) { + u8 addr; + u16 value; + /* getting the address and the value to be written */ + if (likely(of_reg_found)) { + addr = (u8)((be32_to_cpu(init_buffer[lcnt]) + & ADDR_MASK) >> HEX_BASE); + value = (u16)(be32_to_cpu(init_buffer[lcnt]) + & DATA_MASK); + } else { + addr = (u8)((init_buffer[lcnt] & ADDR_MASK) + >> HEX_BASE); + value = (u16)(init_buffer[lcnt] & DATA_MASK); + } + /* Having a copy of device tree values in driver */ + if ((addr >= DEV_ID_REG) && + (addr <= HIGHEST_WR_ACCESS)) { + adux1050->reg[addr].wr_flag = ADUX1050_ENABLE; + adux1050->reg[addr].value = value; + /* pr_debug("!!!!!!! ADDR - %x ; VALUE - %x !!!!\n", */ + /* addr, adux1050->reg[addr].value);*/ + } + /* Copy of Baseline registers */ + if ((addr >= BASELINE_STG0_REG) && + (addr <= BASELINE_STG3_REG)) { + switch (addr) { + case BASELINE_STG0_REG: + adux1050->bs_reg[STG_ZERO].wr_flag = + ADUX1050_ENABLE; + adux1050->bs_reg[STG_ZERO].value = value; + break; + case BASELINE_STG1_REG: + adux1050->bs_reg[STG_ONE].wr_flag = + ADUX1050_ENABLE; + adux1050->bs_reg[STG_ONE].value = value; + break; + case BASELINE_STG2_REG: + adux1050->bs_reg[STG_TWO].wr_flag = + ADUX1050_ENABLE; + adux1050->bs_reg[STG_TWO].value = value; + break; + case BASELINE_STG3_REG: + adux1050->bs_reg[STG_THREE].wr_flag = + ADUX1050_ENABLE; + adux1050->bs_reg[STG_THREE].value = value; + } + } + } + + return 0; +} + +static void high_threshold_int_check(struct adux1050_chip *adux1050, + u16 high_status_change); + +/* if all RESULT_STGx is under BASELINE_STGx, do FORCE_CAL */ +static void adjust_baseline_after_enabled(struct adux1050_chip *adux1050) +{ + u8 stg_cnt = 0; + u16 result_cdc; + u16 baseline_cdc; + s16 err = 0; + bool do_force_cal = true; + + for (stg_cnt = 0; stg_cnt < adux1050->num_stages; stg_cnt++) { + /** Fetch the CDC only if that stage is connected */ + if (adux1050->stg_info[stg_cnt].status != CIN_CONNECTED) + continue; + err = adux1050->read(adux1050->dev, GET_RESULT_REG(stg_cnt), + &result_cdc, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C Read Error %d in %s", + err, __func__); + ADUX1050_DRIVER_DBG("RESULT_STG%d: 0x%04x\n", + stg_cnt, result_cdc); + err = adux1050->read(adux1050->dev, GET_BASE_LINE_REG(stg_cnt), + &baseline_cdc, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C Read Error %d in %s", + err, __func__); + if (result_cdc > baseline_cdc) + do_force_cal = false; + } + if (do_force_cal) + adux1050_force_cal(adux1050, adux1050->slp_time_conv_complete); +} + +/* + * int adux1050_hw_init(struct adux1050_chip *adux1050) + * To initialize the ADUX1050 device with register set defined in + * platform file or device tree + * @param adux1050 The Device structure + * @return Zero on success + */ +static int adux1050_hw_init(struct adux1050_chip *adux1050) +{ + u32 lcnt = 0; + u16 addr; + u16 slp_time = 0; + u16 temp_baseline_ctrl = 0; + u16 pwr_ctrl_buff = 0; + u16 temp_reg_value = 0; + u16 value = 0; + s16 err = 0; + + for (lcnt = 0; lcnt < (GLOBAL_REG_CNT + STG_CNF_CNT); lcnt++) { + addr = lcnt; + if (adux1050->reg[addr].wr_flag == ADUX1050_ENABLE) { + value = adux1050->reg[addr].value; + if (addr == BASELINE_CTRL_REG) { + value = value & ANTI_FORCE_CAL_MASK; + if (value & AUTO_TH_MASK) { + temp_baseline_ctrl = value; + value = value & (~AUTO_TH_MASK); + } + } else if (addr == CTRL_REG) { + value = value & ~RESET_MASK; + pwr_ctrl_buff = value; + value = SET_PWR_MODE(value, PWR_STAND_BY); + } else if (addr == INT_CTRL_REG) { + if (adux1050->int_pol == ACTIVE_HIGH) + adux1050->int_ctrl = + (value | ACTIVE_HIGH); + else + adux1050->int_ctrl = + (value & ~ACTIVE_HIGH); + value = adux1050->int_ctrl | DISABLE_DEV_INT; + } + ADUX1050_DRIVER_DBG("Addr %x Val %x\n", addr, value); + err = adux1050->write(adux1050->dev, addr, + &value, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + return err; + } + } + } + /* Baseline registers update */ + if (adux1050->bs_reg[STG_ZERO].wr_flag == ADUX1050_ENABLE) { + err = adux1050->write(adux1050->dev, BASELINE_STG0_REG, + &adux1050->bs_reg[STG_ZERO].value, + DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + return err; + } + } + if (adux1050->bs_reg[STG_ONE].wr_flag == ADUX1050_ENABLE) { + err = adux1050->write(adux1050->dev, BASELINE_STG1_REG, + &adux1050->bs_reg[STG_ONE].value, + DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + return err; + } + } + if (adux1050->bs_reg[STG_TWO].wr_flag == ADUX1050_ENABLE) { + err = adux1050->write(adux1050->dev, BASELINE_STG2_REG, + &adux1050->bs_reg[STG_TWO].value, + DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + return err; + } + } + if (adux1050->bs_reg[STG_THREE].wr_flag == ADUX1050_ENABLE) { + err = adux1050->write(adux1050->dev, BASELINE_STG3_REG, + &adux1050->bs_reg[STG_THREE].value, + DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + return err; + } + } + /* Restoring the power mode given in configuration */ + if (pwr_ctrl_buff) { + err = adux1050->write(adux1050->dev, CTRL_REG, + &pwr_ctrl_buff, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + return err; + } + slp_time = get_conv_time(adux1050, CONV_TIME); + if (slp_time > 0) + msleep(slp_time); + ADUX1050_DRIVER_DBG("Addr %x New Val %x\n", + CTRL_REG, pwr_ctrl_buff); + } + /* Auto threshold enable */ + if (temp_baseline_ctrl) { + err = adux1050->write(adux1050->dev, BASELINE_CTRL_REG, + &temp_baseline_ctrl, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + return err; + } + ADUX1050_DRIVER_DBG("Addr %x New Val %x\n", + BASELINE_CTRL_REG, + temp_baseline_ctrl); + } + /* Clearing the device interrupt STATUS register */ + err = adux1050->read(adux1050->dev, INT_STATUS_REG, + &temp_reg_value, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", err, __FILE__); + return err; + } + adux1050->prev_low_status = GET_LOW_STATUS(temp_reg_value); + adux1050->high_status = + adux1050->prev_high_status = GET_HIGH_STATUS(temp_reg_value); + + /* Report initial state before enabling interrupt */ + /* Note: high_thresh_enable is not set until we call getstageinfo() */ + adux1050->high_thresh_enable = CHECK_THRESH_HIGH_EN(adux1050->int_ctrl); + if (adux1050->high_thresh_enable) { + const u16 high_status_change = adux1050->high_thresh_enable; + + high_threshold_int_check(adux1050, high_status_change); + ADUX1050_DRIVER_DBG("Report Initial High State\n"); + } + + /* Getting the stage info */ + err = getstageinfo(adux1050); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, "Err in getstageinfo %d in %s\n", + err, __FILE__); + return err; + } + + /* Storing the sleeping time required for this configuration */ + adux1050->slp_time_conv_complete = get_conv_time(adux1050, + TWICE_CONV_DELAY_TIME); + dev_dbg(adux1050->dev, " CONV TIME: %d\n", + adux1050->slp_time_conv_complete); + + /* if all RESULT_STGx is under BASELINE_STGx, do FORCE_CAL */ + adjust_baseline_after_enabled(adux1050); + + /* Enabling the device interrupt */ + err = adux1050->write(adux1050->dev, INT_CTRL_REG, + &adux1050->int_ctrl, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", err, __FILE__); + return err; + } + ADUX1050_DRIVER_DBG("Addr %x New Val %x\n", + INT_CTRL_REG, adux1050->int_ctrl); + + err = get_intr_mask_info(adux1050); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, "Err in get_intr_mask_info %d in %s\n", + err, __FILE__); + return err; + } + + return 0; +} + +/* + * offset_write(struct adux1050_chip *adux1050, u16 stg_num, u16 data, + u16 slp_time) + * Internal function used to write the offset of the Stages + with a predeterminded delay + * @param adux1050 ADUX chip structure + * @param stg_num The stage to which offset has to be written + * @param data The Value to be written to the offset register + * @param slp_time Sleep time to given after writing the offset register + * @return write status is returned + */ +static int offset_write(struct adux1050_chip *adux1050, u16 stg_num, u16 data, + u16 slp_time) +{ + s32 ret = 0; + + ret = adux1050->write(adux1050->dev, GET_OFFSET_REG(stg_num), &data, + DEF_WR); + if (ret < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", ret, __FILE__); + return ret; + } + if (likely(slp_time)) + msleep(slp_time); + return ret; +} +/* + * inline s16 get_calc_dac_offset(u16 offset, u16 stg_cfg_reg) + * To calculate effective DAC offset value from pos & neg dac offset + * @param offset The current offset available. + * @param stg_cfg_reg The current stage configuration. + * @return the equivalant offset based on the swap bits. + */ +static inline s16 get_calc_dac_offset(u16 offset, u16 stg_cfg_reg) +{ + s16 cal_offset = 0; + + cal_offset = LD_POS_DAC_OFFSET(offset, stg_cfg_reg) + + LD_NEG_DAC_OFFSET(offset, stg_cfg_reg); + return cal_offset; +} + +/* + * inline int set_calc_dac_offset(struct adux1050_chip *adux1050, + s16 cal_offset, u16 stg_num, u16 *stg_cfg_reg, + u16 *offset, u16 sleep_time) + * Helper function to calculate the DAC offset for a stage. + * @param adux1050 Chip structure. + * @param cal_offset Calculated equalized offset + * @param stg_num Stage number + * @param *stg_cfg_reg Stage configuration register value. + * @param *offset Current offset set. + * @param sleep_time Sleep time required for register result cdc. + * @return Offset to be set to the register. +*/ +static inline int set_calc_dac_offset(struct adux1050_chip *adux1050, + s16 cal_offset, u16 stg_num, u16 *stg_cfg_reg, + u16 *offset, u16 sleep_time) +{ + s16 err = 0; + + if ((-MAX_OFFSET > cal_offset) || (cal_offset > MAX_OFFSET)) { + dev_err(adux1050->dev, "[ADUX1050]: %s, offset ERROR(%d)\n", + __func__, cal_offset); + return -EINVAL; + } + *offset = set_dac_offset(cal_offset); + err = set_swap_state(adux1050, stg_num, stg_cfg_reg, cal_offset); + if (err < 0) + return -EIO; + dev_dbg(adux1050->dev, "--->> %s, cal_off(%d)offset(%x) swap(%x)\n", + __func__, cal_offset, *offset, *stg_cfg_reg); + return offset_write(adux1050, stg_num, *offset, sleep_time); +} + +/* + * static int adux1050_offset_check(struct adux1050_chip *adux1050, + * u16 max_cnt, s16 dir_flag, u16 slp_time) + * ADUX1050 Saturation routine for bringing the device out of saturation using + directional hopping method + * @param adux1050 The device structure to be calibrated + * @param max_cnt Maximum count + * @param dir_flag Direction flag + * @param slp_time Sleep time to be given after offset check + * @return 0 if success or error on failure. + */ +static int adux1050_offset_check(struct adux1050_chip *adux1050, u16 max_cnt, + s16 dir_flag, u16 slp_time) +{ + s16 cal_offset; + u16 data; + u16 power_ctrl; + u16 cin_range; + u16 lp_count; + u16 stg_num; + u16 *offset; + u16 *stg_cfg_reg; + s16 err = 0; + + err = adux1050->read(adux1050->dev, CTRL_REG, &power_ctrl, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", err, __FILE__); + return err; + } + cin_range = GET_CIN_RANGE(power_ctrl); + adux1050->tot_stg = adux1050->num_stages; + for (lp_count = 0; lp_count < max_cnt ; lp_count++) { + for (stg_num = 0; stg_num < adux1050->tot_stg; stg_num++) { + if ((!CHECK_CAL_STATE( + adux1050->dac_calib.sat_comp_stat, stg_num)) || + (adux1050->stg_info[stg_num].status == + CIN_NOT_CONNECTED)) + continue; + offset = &adux1050->cur_dac_offset[stg_num]; + stg_cfg_reg = &adux1050->cur_swap_state[stg_num]; + err = adux1050->read(adux1050->dev, + GET_RESULT_REG(stg_num), + &data, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, + "I2C WR Err %d in %s\n", + err, __FILE__); + return err; + } + if ((data <= ZERO_SCALE_VALUE) || + (data >= FULL_SCALE_VALUE)) { + if (lp_count != 0) + cal_offset = get_calc_dac_offset( + *offset, *stg_cfg_reg); + else + cal_offset = 0; + cal_offset += + (GET_DAC_STEP_SIZE(DAC_CODEOUT_SAT, + cin_range) * dir_flag); + pr_debug("[%d]Curr offset set (%d)\n", + stg_num, cal_offset); + err = set_calc_dac_offset(adux1050, cal_offset, + stg_num, stg_cfg_reg, + offset, 0); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, + "set_calc_dac_offset failed %d\n", + err); + return err; + } + } else { + CLR_CAL_STATUS( + adux1050->dac_calib.sat_comp_stat, stg_num); + pr_debug("%s - SATUR state(%d)\n", __func__, + adux1050->dac_calib.sat_comp_stat); + if (!adux1050->dac_calib.sat_comp_stat) + goto adux_offset_ok; + } + ADUX1050_DRIVER_DBG("SATURATION STATUS - %x\n", + adux1050->dac_calib.sat_comp_stat); + } + msleep(slp_time); + } + return -EIO; +adux_offset_ok: + ADUX1050_DRIVER_DBG("%s, offset ok (%d)\n", __func__, data); + return 0; +} + +static inline void reset_stgcal_flag(struct adux1050_chip *adux1050, u16 *count) +{ + u16 lp_cnt = 0; + + for (; lp_cnt < adux1050->tot_stg; lp_cnt++) { + if (adux1050->stg_info[lp_cnt].status == CIN_CONNECTED) { + adux1050->dac_calib.stg_cal_stat |= (1 << lp_cnt); + count[lp_cnt] = 1; + } + } + +} +/* + * int adux1050_binary_offset_check(struct adux1050_chip *adux1050, + * u16 slp_time) + * ADUX1050 Saturation routine for bringing the device out of saturation using + * binary search method + * @param adux1050 The device structure to be calibrated + * @param slp_time Sleep time to be given after offset check + * @return Returns 0 on success and -EIO on error. + */ +static int adux1050_binary_offset_check(struct adux1050_chip *adux1050, + u16 slp_time) +{ + u16 data; + u16 lp_count; + u16 stg_num; + u16 *offset; + u16 *stg_cfg_reg; + s16 err = 0; + s16 low_dac_offset[TOTAL_STG] = {-MAX_OFFSET, + -MAX_OFFSET, + -MAX_OFFSET, + -MAX_OFFSET}; + s16 curr_dac_offset[TOTAL_STG] = {ZERO_VAL, + ZERO_VAL, + ZERO_VAL, + ZERO_VAL}; + s16 high_dac_offset[TOTAL_STG] = {MAX_OFFSET, + MAX_OFFSET, + MAX_OFFSET, + MAX_OFFSET}; + adux1050->tot_stg = adux1050->num_stages; + + for (lp_count = 0; lp_count < MAX_SEARCH_DEPTH ; lp_count++) { + for (stg_num = 0; stg_num < adux1050->tot_stg; stg_num++) { + if ((!CHECK_CAL_STATE( + adux1050->dac_calib.sat_comp_stat, stg_num)) || + (adux1050->stg_info[stg_num].status == + CIN_NOT_CONNECTED)) + continue; + offset = &adux1050->cur_dac_offset[stg_num]; + stg_cfg_reg = &adux1050->cur_swap_state[stg_num]; + err = adux1050->read(adux1050->dev, + GET_RESULT_REG(stg_num), &data, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C Read Err %d in %s\n", + err, __func__); + return err; + } + if (data >= FULL_SCALE_VALUE) { + low_dac_offset[stg_num] = + curr_dac_offset[stg_num]; + } else if (data <= ZERO_SCALE_VALUE) { + high_dac_offset[stg_num] = + curr_dac_offset[stg_num]; + } else { + CLR_CAL_STATUS( + adux1050->dac_calib.sat_comp_stat, + stg_num); + dev_dbg(adux1050->dev, + " SATUR state(%d)\n", + adux1050->dac_calib.sat_comp_stat); + if (!adux1050->dac_calib.sat_comp_stat) + goto adux_offset_ok; + + } + curr_dac_offset[stg_num] = (low_dac_offset[stg_num] + + high_dac_offset[stg_num]) / 2; + err = set_calc_dac_offset(adux1050, + curr_dac_offset[stg_num], + stg_num, stg_cfg_reg, + offset, 0); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, + "set_calc_dac_offset failed %d in %s\n", + err, __func__); + return err; + } + pr_debug("Offset Set for stg[%d]", stg_num); + pr_debug("Set to L[%d] C[%d] H[%d]", + low_dac_offset[stg_num], + curr_dac_offset[stg_num], + high_dac_offset[stg_num]); + } + msleep(slp_time); + } + err = adux1050->read(adux1050->dev, + GET_RESULT_REG(stg_num), + &data, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", err, __func__); + return err; + } + if (data <= ZERO_SCALE_VALUE || data >= FULL_SCALE_VALUE) { + pr_debug("offset_check- EIO"); + return -EIO; + } +adux_offset_ok: + pr_debug("offset ok (%d)", data); + return 0; +} + +/* + * do_dac_compensation(struct adux1050_chip *adux1050, u16 power_ctrl, + u16 cin_range, u16 *slp_time) + * Non saturation CDC compensation routine. + * @param adux1050 The device structure to be calibrated + * @param power_ctrl The power control register read + * @param cin_range The current configuration's CIN range value + * @param slp_time The sleep time to be given + */ +static int do_dac_compensation(struct adux1050_chip *adux1050, u16 power_ctrl, + u16 cin_range, u16 *slp_time) +{ + u16 cdc_diff; + u16 lp_count = 0; + u16 stg_num = 0; + u16 count[TOTAL_STG] = {0}; + s16 cal_offset = 0; + u16 flr_cnt = 0; + u16 data[TOTAL_STG] = {0}; + u16 hys_reg[TOTAL_STG] = {0}; + u16 offset = 0; + u16 stg_cfg_reg = 0; + s16 digi_offset = 0; + u16 use_digi_offset = 0; + s32 err = -EIO; + u16 u16_div = GET_ARB_DAC_STEP_SIZE(cin_range); + u16 dac_step = u16_div; + u16 trgt[TOTAL_STG] = {0}; + u16 init_swap_state[TOTAL_STG]; + u16 init_dac_offset[TOTAL_STG]; + struct adux1050_platform_data *pdata = adux1050->pdata; + u16 org_trgt[TOTAL_STG] = {pdata->req_stg0_base, + pdata->req_stg1_base, + pdata->req_stg2_base, + pdata->req_stg3_base}; + + *slp_time = get_conv_time(adux1050, TWICE_CONV_DELAY_TIME); + adux1050->dac_calib.sat_comp_stat = ZERO_VAL; + if (adux1050->metal_id >= MET_VER1) { + for (lp_count = 0; lp_count < TOTAL_STG; lp_count++) + trgt[lp_count] = org_trgt[lp_count]; + use_digi_offset = 1; + ADUX1050_DRIVER_DBG("%s Binary Search defined\n", __func__); + } + + /*Writing initial values to zero*/ + for (lp_count = 0; lp_count < adux1050->tot_stg; lp_count++) { + err = adux1050->read(adux1050->dev, GET_OFFSET_REG(lp_count), + &offset, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", + err, __FILE__); + goto calib_failed_break; + } + err = adux1050->read(adux1050->dev, GET_CONFIG_REG(lp_count), + &stg_cfg_reg, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", + err, __FILE__); + goto calib_failed_break; + } + init_swap_state[lp_count] = stg_cfg_reg; + init_dac_offset[lp_count] = offset; + /*Set initial step count to 1 WARNING: DO NOT set it to zero */ + count[lp_count] = 1; + cal_offset = get_calc_dac_offset(offset, stg_cfg_reg); + if (cal_offset != ZERO_VAL) + cal_offset = 0; + err = set_calc_dac_offset(adux1050, cal_offset, lp_count, + &stg_cfg_reg, &offset, 0); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, "CALC OFF ERR %d in %s\n", + err, __FILE__); + goto calib_failed_break; + } + adux1050->cur_swap_state[lp_count] = stg_cfg_reg; + adux1050->cur_dac_offset[lp_count] = offset; + if (adux1050->metal_id < MET_VER1) + trgt[lp_count] = HALF_SCALE_VAL; + /** Clear the Digital offset if set already*/ + err = adux1050->read(adux1050->dev, GET_HYS_REG(lp_count), + &hys_reg[lp_count], DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", + err, __FILE__); + goto calib_failed_break; + } + hys_reg[lp_count] &= HYS_BYTE_MASK; + err = adux1050->write(adux1050->dev, GET_HYS_REG(lp_count), + &hys_reg[lp_count], DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + goto calib_failed_break; + } + if (adux1050->stg_info[lp_count].status == CIN_CONNECTED) { + adux1050->dac_calib.sat_comp_stat |= (1 << lp_count); + adux1050->dac_calib.stg_cal_stat |= (1 << lp_count); + } + } + msleep(*slp_time); + u16_div = GET_ARB_DAC_STEP_SIZE(GET_CIN_RANGE(power_ctrl)); + dac_step = u16_div; + /* Clear the device from saturation*/ + if (adux1050->metal_id < MET_VER1) { + flr_cnt = (MAX_OFFSET / (GET_DAC_STEP_SIZE(DAC_CODEOUT_SAT, + GET_CIN_RANGE(power_ctrl)))); + ADUX1050_DRIVER_DBG("%s - POSITIVE SAT ROUTINE\n", __func__); + err = adux1050_offset_check(adux1050, flr_cnt, 1, *slp_time); + if (err < 0) { + ADUX1050_DRIVER_DBG("%s - NEGATIVE SAT ROUTINE\n", + __func__); + err = adux1050_offset_check(adux1050, flr_cnt, + MINUS_VAL, *slp_time); + if (err < 0) { + dev_err(adux1050->dev, + "adux1050_offset_chk fail %d in %s\n", + err, __func__); + goto calib_failed_break; + } + } + } else if (adux1050->metal_id >= MET_VER1) { + ADUX1050_DRIVER_DBG("%s - Binary SAT ROUTINE begins\n", + __func__); + err = adux1050_binary_offset_check(adux1050, *slp_time); + if (err < 0) { + dev_err(adux1050->dev, "%s - Fail for both directions", + __func__); + goto calib_failed_break; + } + dev_dbg(adux1050->dev, "%s - Binary SAT ROUTINE ends", + __func__); + } + /* Set Stage cal status to uncalibrated initial value*/ + + /* Make the stages to their corresponding target value*/ + for (lp_count = 0; lp_count < CALIB_LOOP_CNT; lp_count++) { + u16 prv_data; + + for (stg_num = 0; stg_num < adux1050->tot_stg; stg_num++) { + /*Skip the stage if already calibrated*/ + if ((!CHECK_CAL_STATE( + adux1050->dac_calib.stg_cal_stat, stg_num)) || + (adux1050->stg_info[stg_num].status == + CIN_NOT_CONNECTED)) + continue; + prv_data = data[stg_num]; + err = adux1050->read(adux1050->dev, + GET_RESULT_REG(stg_num), + &data[stg_num], DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, + "I2C RD Err %d in %s\n", + err, __FILE__); + goto calib_failed_break; + } + pr_debug("DATA %d,OFF %x,SWAP %x,STG %d\n", + data[stg_num], + adux1050->cur_dac_offset[stg_num], + adux1050->cur_swap_state[stg_num], + stg_num); + /*Device not in saturation*/ + cdc_diff = abs(data[stg_num] - trgt[stg_num]); + if (use_digi_offset && (cdc_diff < DIGI_OFFSET_SIZE)) { + count[stg_num] = 0; + } else { + u16_div = abs(prv_data - data[stg_num]); + u16_div = u16_div / count[stg_num]; + if ((u16_div < (dac_step/2)) || + (u16_div > (dac_step + dac_step))) + u16_div = dac_step; + dev_dbg(adux1050->dev, "%s,p_data(%d)", + __func__, prv_data); + flr_cnt = cdc_diff / u16_div; + count[stg_num] = ((cdc_diff % u16_div) > + GET_60_PERCENT(u16_div)) ? + (flr_cnt + 1) : flr_cnt; + pr_debug("FLC(%d),CNT(%d),DIV(%d),DIF(%d)\n", + flr_cnt, count[stg_num], + u16_div, cdc_diff); + } + if (count[stg_num] != 0) { + /* DAC step can be used to minimize the */ + /* difference in current and required CDC*/ + cal_offset = get_calc_dac_offset( + adux1050->cur_dac_offset[stg_num], + adux1050->cur_swap_state[stg_num]); + dev_dbg(adux1050->dev, "%s,cal_off %d\n", + __func__, cal_offset); + if (data[stg_num] > trgt[stg_num]) + cal_offset += count[stg_num]; + else + cal_offset -= count[stg_num]; + err = set_calc_dac_offset(adux1050, cal_offset, + stg_num, &adux1050->cur_swap_state[stg_num], + &adux1050->cur_dac_offset[stg_num], 0); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, "%s S_Off err\n", + __func__); + goto calib_failed_break; + } + } else { + CLR_CAL_STATUS( + adux1050->dac_calib.stg_cal_stat, stg_num); + /*When all the stages are in halfscale change*/ + /* to the original target and CIN_range*/ + if ((!adux1050->dac_calib.stg_cal_stat) && + (!use_digi_offset) && + (adux1050->metal_id < MET_VER1)) { + trgt[STG_ZERO] = pdata->req_stg0_base; + trgt[STG_ONE] = pdata->req_stg1_base; + trgt[STG_TWO] = pdata->req_stg2_base; + trgt[STG_THREE] = pdata->req_stg3_base; + pr_debug("R TRGT %x,%x,%x,%x\n", + trgt[STG_ZERO], trgt[STG_ONE], + trgt[STG_TWO], + trgt[STG_THREE]); + power_ctrl = SET_CIN_RANGE(power_ctrl, + cin_range); + err = adux1050->write(adux1050->dev, + CTRL_REG, &power_ctrl, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, + " I2C WR Err %d in %s\n", + err, __func__); + goto calib_failed_break; + } + u16_div = GET_ARB_DAC_STEP_SIZE( + GET_CIN_RANGE(power_ctrl)); + dac_step = u16_div; + reset_stgcal_flag(adux1050, count); + + use_digi_offset++; + /* msleep(*slp_time);*/ + break; + } + /*Not in saturation and DAC minimal step size*/ + /* is higher than the required correction*/ + digi_offset = GET_DIGI_OFFSET(trgt[stg_num], + data[stg_num]); + digi_offset = CLAMP_DIGI_OFFSET(digi_offset); + + adux1050->pdata->cal_offset[stg_num] = + adux1050->cur_dac_offset[stg_num]; + adux1050->pdata->digi_offset[stg_num] = + (u8)digi_offset; + + hys_reg[stg_num] = + ((hys_reg[stg_num] & HYS_BYTE_MASK) | + (adux1050->pdata->digi_offset[stg_num] << 8)); + err = adux1050->write(adux1050->dev, + GET_HYS_REG(stg_num), + &hys_reg[stg_num], DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, + "I2C WR Err %d in %s\n", + err, __func__); + goto calib_failed_break; + } + ADUX1050_DRIVER_DBG("Hys value = %x\n", + hys_reg[stg_num]); + msleep(*slp_time); + err = adux1050->read(adux1050->dev, + GET_RESULT_REG(stg_num), + &data[stg_num], DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, + "I2C RD Err %d in %s\n", + err, __func__); + goto calib_failed_break; + } + adux1050->pdata->cal_fact_base[stg_num] = + data[stg_num]; + adux1050->pdata->stg_cfg[stg_num] = + adux1050->cur_swap_state[stg_num]; + dev_dbg(adux1050->dev, + "bas(%d)off(%x)st_con(%x)dioff(%d)\n", + data[stg_num], offset, + stg_cfg_reg, digi_offset); + err = ZERO_VAL; + pr_debug("CAL STATUS - %x\n", + adux1050->dac_calib.stg_cal_stat); + if (!adux1050->dac_calib.stg_cal_stat) + goto calib_success_break; + else + continue; + } + } /*End of stage based loop*/ + msleep(*slp_time); + } /*End of for*/ + +calib_failed_break: + /*Failed to complete the compensation, return to the original values*/ + for (lp_count = 0; lp_count < adux1050->tot_stg; lp_count++) { + err = adux1050->write(adux1050->dev, GET_CONFIG_REG(lp_count), + &init_swap_state[lp_count], DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + err = adux1050->write(adux1050->dev, GET_OFFSET_REG(lp_count), + &init_dac_offset[lp_count], DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __FILE__); + adux1050->pdata->cal_fact_base[lp_count] = 0; + adux1050->pdata->cal_offset[lp_count] = 0; + adux1050->pdata->digi_offset[lp_count] = 0; + adux1050->pdata->stg_cfg[lp_count] = 0; + adux1050->dac_calib.cal_flags = CAL_RET_FAIL; + } + msleep(*slp_time); + return err; +calib_success_break: + adux1050->dac_calib.cal_flags = CAL_RET_SUCCESS; + return CAL_RET_SUCCESS; +} + +/* + * static inline int adux1050_disable(struct adux1050_chip *adux1050) + * Routine to set the power mode to standby in the ADUX1050 chip + * @param adux1050 Chip structure to set the power mode to shutdown + * @return Zero on success. + */ +static inline int adux1050_disable(struct adux1050_chip *adux1050) +{ + u16 data = 0; + s16 err = 0; + + mutex_lock(&adux1050->mutex); + err = adux1050->read(adux1050->dev, CTRL_REG, &data, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", err, __func__); + mutex_unlock(&adux1050->mutex); + return err; + } + if (GET_PWR_MODE(data) != PWR_STAND_BY) { + data = SET_PWR_MODE(data, PWR_STAND_BY); + err = adux1050->write(adux1050->dev, CTRL_REG, &data, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __func__); + mutex_unlock(&adux1050->mutex); + return err; + } + } + + mutex_unlock(&adux1050->mutex); + return 0; +} + +/* + * static int adux1050_enable(struct adux1050_chip *adux1050) + * Routine to set the driver to enable state in the ADUX1050 chip + * @param adux1050 Chip structure to set the power mode to shutdown + * @return Zero on success. + */ +static int adux1050_enable(struct adux1050_chip *adux1050) +{ + s16 err = 0; + + mutex_lock(&adux1050->mutex); + err = adux1050_hw_init(adux1050); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, "Failed in adux1050_hw_init %d\n", err); + mutex_unlock(&adux1050->mutex); + return err; + } + +#ifdef CONFIG_ADUX1050_EVAL + if (adux1050->power_mode_flag == ADUX1050_ENABLE) { + err = adux1050->write(adux1050->dev, CTRL_REG, + &adux1050->ctrl_reg, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d in %s\n", + err, __func__); + mutex_unlock(&adux1050->mutex); + return err; + } + adux1050->power_mode_flag = ADUX1050_DISABLE; + } +#endif + + mutex_unlock(&adux1050->mutex); + return 0; +} + +/* + * static void adux1050_calibration(struct work_struct *cal_work) + * ADUX1050 Calibration routine + * @param cal_work Pointer to "calib_work", member of ADUX1050 chip structure + * @return int + */ +static void adux1050_calibration(struct work_struct *cal_work) +{ + struct adux1050_chip *adux1050 = + container_of(cal_work, struct adux1050_chip, calib_work); + u8 slp_time_flag = 0; + s32 err = 0; + u16 data; + u16 power_ctrl; + u16 lp_cnt; + u16 slp_time = 0; + u16 s_t = 0; + u16 cv_time_ctrl = 0; + u16 cin_range = 0; + u16 cur_power = 0; + u32 start_time = jiffies; + + mutex_lock(&adux1050->mutex); + err = adux1050->read(adux1050->dev, INT_CTRL_REG, + &adux1050->dac_calib.enable_setting, DEF_WR); + if (err < I2C_WRMSG_LEN) + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + /* + * Disable interrupt and digital offset + */ + data = DISABLE_DEV_INT | adux1050->int_pol; + err = adux1050->write(adux1050->dev, INT_CTRL_REG, &data, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C WR Err %d\n", err); + adux1050->dac_calib.cal_flags = CAL_RET_PENDING; + if (adux1050->dac_calib.action_flag) { + if (adux1050->proxy_enable) { + ADUX1050_DRIVER_DBG("%s -proxywork cancelled in calib", + __func__); + adux1050->proxy_cancel = TRUE; + cancel_delayed_work(&adux1050->proxy_work); + } + dev_dbg(adux1050->dev, "\n\n\n%s CALIB STARTED\n\n", __func__); + slp_time = get_conv_time(adux1050, CONV_DELAY_TIME); + err = adux1050->read(adux1050->dev, CONV_TIME_CTRL_REG, + &cv_time_ctrl, DEF_WR); + if (err < I2C_WRMSG_LEN) + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + if (CHECK_MIN_TIME(cv_time_ctrl)) { + data = SET_MIN_TIME(cv_time_ctrl); + err = adux1050->write(adux1050->dev, CONV_TIME_CTRL_REG, + &data, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C WR Err %d\n", err); + slp_time_flag = 1; + dev_dbg(adux1050->dev, "%s Check Min time %x\n", + __func__, data); + } + + err = adux1050->read(adux1050->dev, CTRL_REG, + &power_ctrl, DEF_WR); + if (err < I2C_WRMSG_LEN) + dev_err(adux1050->dev, "I2C RD Error %d\n", err); + if (GET_PWR_MODE(power_ctrl) != PWR_FULL_POWER) { + cur_power = SET_PWR_MODE(power_ctrl, PWR_FULL_POWER); + err = adux1050->write(adux1050->dev, CTRL_REG, + &cur_power, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C WR Err %d\n", err); + slp_time_flag = slp_time_flag | 0x2; + slp_time += get_conv_time(adux1050, CONV_TIME); + dev_dbg(adux1050->dev, "Mode change %d\n", slp_time); + } else { + cur_power = power_ctrl; + } + cin_range = GET_CIN_RANGE(cur_power); + if (adux1050->metal_id < MET_VER1) { + if (cin_range != PICO_5) { + cur_power = SET_CIN_RANGE(cur_power, PICO_5); + err = adux1050->write(adux1050->dev, CTRL_REG, + &cur_power, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, + "I2C WR Err %d\n", + err); + } + } + if (slp_time_flag) { + dev_dbg(adux1050->dev, "Calib sleep %d\n", slp_time); + msleep(slp_time); + } + err = do_dac_compensation(adux1050, cur_power, + cin_range, &s_t); + dev_dbg(adux1050->dev, "calibration return %d\n", err); + } else { + adux1050->dac_calib.cal_flags = CAL_RET_NONE; + for (lp_cnt = 0; lp_cnt < TOTAL_STG; lp_cnt++) { + adux1050->pdata->cal_fact_base[lp_cnt] = 0; + adux1050->pdata->cal_offset[lp_cnt] = 0; + adux1050->pdata->digi_offset[lp_cnt] = 0; + adux1050->pdata->stg_cfg[lp_cnt] = 0; + } + } + + if (adux1050->dac_calib.action_flag) { + /* If calibration succeed set baseline by using force calib*/ + if (adux1050->dac_calib.cal_flags == CAL_RET_SUCCESS) { + dev_dbg(adux1050->dev, "before FC in calib"); + err = adux1050_force_cal(adux1050, s_t); + if (err < 0) + dev_err(adux1050->dev, "I2C RD/WR err %d\n", + err); + } + /* Restore the prv conversion time settings */ + if (slp_time_flag & 1) { + err = adux1050->write(adux1050->dev, CONV_TIME_CTRL_REG, + &cv_time_ctrl, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C WR Err %d\n", err); + } + /* Restore the previous power Setting */ + if (slp_time_flag & 2) { + err = adux1050->write(adux1050->dev, CTRL_REG, + &power_ctrl, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C WR Err %d\n", err); + } + } + data = adux1050->dac_calib.enable_setting | adux1050->int_pol; + err = adux1050->write(adux1050->dev, INT_CTRL_REG, &data, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C WR Error %d\n", err); + dev_dbg(adux1050->dev, "\n\nCALIB ENDS Status %d Time %d ms\n\n", + adux1050->dac_calib.cal_flags, + jiffies_to_msecs(jiffies - start_time)); + mutex_unlock(&adux1050->mutex); + if ((adux1050->proxy_enable) && + (adux1050->dac_calib.action_flag) && + (adux1050->dev_enable)) { + adux1050->proxy_cancel = FALSE; + schedule_delayed_work(&adux1050->proxy_work, + msecs_to_jiffies + (PROXY_TIME * adux1050->allowed_proxy_time)); + } + if (adux1050->dev_enable == ADUX1050_DISABLE) { + err = adux1050_disable(adux1050); + if (err < ZERO_VAL) + dev_err(adux1050->dev, "adux1050_disable failed %d in %s\n", + err, __func__); + } + +} + +/* + * static ssize_t store_enable(struct device *dev, + * struct device_attribute *attr, + * const char *buf, size_t count) + * This function is used to enable or to disable the device. The Sysfs attribute + * is given as "enable", writing a '0' Disables the device. + * While writing '1' , enables the device. + * @param dev The Device Id structure(linux standard argument) + * @param attr Standard Linux Device attributes to the ADUX1050. + * @param buf The buffer which contains the data. + * @param count The count of bytes to be transferred to the Device. + * \note This is evoked upon an echo/write request in /sys/../devices region. + * \note This also prints the results in the console for the user. + * @return count of data written. + */ +static ssize_t store_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + char *dev_state[2] = { "DISABLED", "ENABLED"}; + s32 err; + u16 val; + + err = kstrtou16(buf, 0, &val); + if (err < 0) { + dev_err(adux1050->dev, "%s,kstrtoint failed\n", __func__); + return err; + } + if (val > 1) { + dev_dbg(adux1050->dev, "%s Invalid- Enable:1 Disable:0\n", + __func__); + goto exit; + } + dev_dbg(adux1050->dev, "%s - prv_flag %d curr %d\n", __func__, + adux1050->dev_enable, val); + if (adux1050->dev_enable == val) { + dev_dbg(adux1050->dev, "%s - Device already in %s state\n", + __func__, dev_state[val]); + goto exit; + } + if (val == ADUX1050_ENABLE) { + adux1050->dev_enable = val; +#ifdef CONFIG_ADUX1050_POLL + err = adux1050_enable(adux1050); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, " ENABLE Err %d\n", err); + adux1050->dev_enable = ZERO_VAL; + return count; + } + wake_up_process(adux1050->polling_task); +#else + enable_irq(adux1050->irq); + enable_irq_wake(adux1050->irq); + err = adux1050_enable(adux1050); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, " ENABLE Err %d\n", err); + adux1050->dev_enable = ZERO_VAL; + return count; + } +#endif + if (adux1050->proxy_enable) { + adux1050->proxy_cancel = FALSE; + schedule_delayed_work(&adux1050->proxy_work, + msecs_to_jiffies + (PROXY_TIME * adux1050->allowed_proxy_time)); + } + dev_dbg(adux1050->dev, "ADUX1050 is enabled\n"); + } else { + adux1050->dev_enable = val; +#ifdef CONFIG_ADUX1050_POLL + +#else + disable_irq_wake(adux1050->irq); + disable_irq(adux1050->irq); +#endif + err = adux1050_disable(adux1050); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, "DISABLE Err %d\n", err); + adux1050->dev_enable = ADUX1050_ENABLE; + return count; + } + adux1050->proxy_cancel = TRUE; + cancel_delayed_work(&adux1050->proxy_work); + dev_dbg(adux1050->dev, "ADUX1050 is Disabled\n"); + } +exit: + return count; +} + +/* + * static ssize_t show_enable(struct device *dev, + struct device_attribute *attr, char *buf) + * This Function is used to show the status of the driver + * Status '1' signifies the device is ENABLED, + * while the status '0' signifies a DISABLED device. + * @param dev The Device Id structure(linux standard argument) + * @param attr standard Linux Device attributes to the ADUX1050. + * @param buf The buffer to store the data to be written. + * \note This is evoked upon an cat/read request in /sys/../devices region. + * \note This also prints the results in the console for the user. + * @return The count of data written. + */ +static ssize_t show_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", adux1050->dev_enable); +} + +#ifdef CONFIG_ADUX1050_EVAL + +/* + * static ssize_t show_dumpregs(struct device *dev, + struct device_attribute *attr, char *buf) + * This Function is used for dumping the registers value of the ADUX1050. + * @param dev The Device Id structure(linux standard argument) + * @param attr standard Linux Device attributes to the ADUX1050 + * @param buf The buffer to store the data to be written + * \note This is evoked upon an cat/read request in /sys/../devices region. + * \note This also prints the results in the console for the user. + * @return count of data written + */ +static ssize_t show_dumpregs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + u16 u16temp[MAX_ADUX1050_WR_LEN]; + u32 u32_lpcnt = 0; + u16 ret = 0; + s16 err = 0; + + mutex_lock(&adux1050->mutex); + dev_dbg(adux1050->dev, "Global control registers\n"); + err = adux1050->read(adux1050->dev, DEV_ID_REG, + u16temp, GLOBAL_REG_CNT); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C Read Error %d\n", err); + goto unlock_mut; + } + for (; u32_lpcnt < GLOBAL_REG_CNT; u32_lpcnt++) { + dev_dbg(adux1050->dev, "Reg 0X%x val 0x%x\n", + u32_lpcnt, u16temp[u32_lpcnt]); + ret += snprintf(buf + ret, PAGE_SIZE, "%4x ", + u16temp[u32_lpcnt]); + } + dev_dbg(adux1050->dev, "Stage config registers\n"); + err = adux1050->read(adux1050->dev, CONFIG_STG0_REG, + u16temp, STG_CNF_CNT); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C Read Error %d\n", err); + goto unlock_mut; + } + for (u32_lpcnt = CONFIG_STG0_REG; + u32_lpcnt <= GET_HYS_REG(STG_THREE) ; u32_lpcnt++) { + dev_dbg(adux1050->dev, "Reg 0X%x val 0x%x\n", + u32_lpcnt, u16temp[u32_lpcnt-CONFIG_STG0_REG]); + ret += snprintf(buf + ret, PAGE_SIZE, "%4x ", + u16temp[u32_lpcnt-CONFIG_STG0_REG]); + } + dev_dbg(adux1050->dev, "Result/Base/p2p registers\n"); + err = adux1050->read(adux1050->dev, INT_STATUS_REG, + u16temp, STATUS_REG_CNT); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C Read Error %d\n", err); + goto unlock_mut; + } + for (u32_lpcnt = INT_STATUS_REG; + u32_lpcnt <= PROX_STATUS_REG; u32_lpcnt++) { + dev_dbg(adux1050->dev, "Reg 0X%x val 0x%x\n", + u32_lpcnt, u16temp[u32_lpcnt-INT_STATUS_REG]); + ret += snprintf(buf + ret, PAGE_SIZE, "%4x ", + u16temp[u32_lpcnt-INT_STATUS_REG]); + } +unlock_mut: + mutex_unlock(&adux1050->mutex); + return ret; +} + + +/* + * static ssize_t adux1050_name_show(struct device *dev, + struct device_attribute *attr, char *buf) + * This is used to display the device name of the chipset. + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an cat/read request in /sys/../devices region. + * @return Returns the size of the output buffer with the on/off status + */ +static ssize_t adux1050_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", DEVICE_NAME); +} + +/* + * static ssize_t adux1050_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) + * This is used to display the vendor name of the device. + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an cat/read request in /sys/../devices region. + * @return Returns the size of the output buffer with the on/off status + */ +static ssize_t adux1050_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME); +} + +/* + * static ssize_t adux1050_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) + * This is used to display the Raw CDC data of a stage + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an cat/read request in /sys/../devices region. + * @return Returns the size of the raw data of all the stages + */ +static ssize_t adux1050_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + u16 temp_cdc = 0; + s16 ret = 0; + s16 err = 0; + + if (!adux1050->dev_enable) { + ADUX1050_DRIVER_DBG("Device is not enabled\n"); + goto data_show_err; + } + mutex_lock(&adux1050->mutex); + err = adux1050->read(adux1050->dev, + GET_RESULT_REG(adux1050->stg_raw_cdc), + &temp_cdc, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C Read Error %d\n", err); + goto unlock_mut; + } + ADUX1050_DRIVER_DBG("%s, STG_NO - %d : raw_data - %x\n", __func__, + adux1050->stg_raw_cdc, temp_cdc); + ret = snprintf(buf, PAGE_SIZE, "0x%04x ", temp_cdc); +unlock_mut: + mutex_unlock(&adux1050->mutex); +data_show_err: + return ret; +} + +/* + * adux1050_raw_data_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + * This is used to set the stage number to display CDC raw data. + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer which contains the data. + * @param count The count of bytes to be transferred to the Device. + * \note This is evoked upon an echo/write request in /sys/../devices region. + * @return Returns the count of the raw data value of a single stage + */ +static ssize_t adux1050_raw_data_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + s32 err; + u16 val; + + err = kstrtou16(buf, 0, &val); + if (err) { + dev_err(adux1050->dev, "%s, kstrtoint failed\n", __func__); + return err; + } + if (val < TOTAL_STG) + adux1050->stg_raw_cdc = val; + else + ADUX1050_DRIVER_DBG("%s, Invalid input %d\n", __func__, val); + + return count; +} + +/* + * adux1050_send_event_show(struct device *dev, + struct device_attribute *attr, char *buf) + * This is used to display the send event status of the driver. + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an cat/read request in /sys/../devices region. + * @return Returns the size of the output buffer with the send event status + */ +static ssize_t adux1050_send_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", adux1050->send_event); +} + +/* + * adux1050_send_event_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + * This is used to set the send event flag in the driver + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param count The count of bytes to be transferred to the Buffer + * \note This is evoked upon an write request in the /sys/.../devices region. + * @return Returns the size of the input buffer + */ +static ssize_t adux1050_send_event_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + s32 val, err; + + err = kstrtoint(buf, 0, &val); + if (err < 0) { + dev_err(adux1050->dev, "%s, kstrtoint failed\n", __func__); + return err; + } + if ((val == ADUX1050_ENABLE) || (val == ADUX1050_DISABLE)) + adux1050->send_event = (unsigned char)val; + else + ADUX1050_DRIVER_DBG("%s - Invalid input %d\n", __func__, val); + return count; +} + +/* + * This is used to show the threshold status of a stage + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an read request in the /sys/.../devices region. + * @return Returns the size of the output buffer + */ +static ssize_t adux1050_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + u16 temp_base = 0; + u16 temp_ht = 0; + u16 temp_lt = 0; + u32 htresult = 0; + s32 ltresult = 0; + s16 ret = 0; + s16 err = 0; + + if (!adux1050->dev_enable) { + ADUX1050_DRIVER_DBG("Device is not enabled\n"); + goto err; + } + mutex_lock(&adux1050->mutex); + err = adux1050->read(adux1050->dev, + GET_BASE_LINE_REG(adux1050->stg_threshold), + &temp_base, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C Read Error %d\n", err); + goto unlock_mut; + } + err = adux1050->read(adux1050->dev, + GET_HIGH_TH_REG(adux1050->stg_threshold), + &temp_ht, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C Read Error %d\n", err); + goto unlock_mut; + } + err = adux1050->read(adux1050->dev, + GET_LOW_TH_REG(adux1050->stg_threshold), + &temp_lt, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C Read Error %d\n", err); + goto unlock_mut; + } + + mutex_unlock(&adux1050->mutex); + htresult = temp_base + temp_ht; + if (htresult >= FULL_SCALE_VALUE) + htresult = FULL_SCALE_VALUE; + + ltresult = temp_base - temp_lt; + if (ltresult <= 0) + ltresult = 0; + + ADUX1050_DRIVER_DBG("%s, STG_NO - %d : BS: %x, HT - %x : LT - %x\n", + __func__, adux1050->stg_threshold, + temp_base, temp_ht, temp_lt); + ret = snprintf(buf, PAGE_SIZE, "0x%04x 0x%04x ", + htresult, ltresult); + +err: + return ret; +unlock_mut: + mutex_unlock(&adux1050->mutex); + return ret; +} + +/* + * This is used to set the stage number to show the threhold details of that + * stage + * + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param count The count of bytes to be transferred to the Buffer + * \note This is evoked upon an write request in the /sys/.../devices region. + * @return Returns the size of the input buffer + */ +static ssize_t adux1050_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + s32 err; + u16 val; + + err = kstrtou16(buf, 0, &val); + if (err) { + dev_err(adux1050->dev, "%s, kstrtoint failed\n", __func__); + return err; + } + if (val < TOTAL_STG) + adux1050->stg_threshold = val; + else + ADUX1050_DRIVER_DBG("%s, Invalid input %d\n", __func__, val); + + return count; +} + +/* + * This is used to check the DAC offset calibration work status from the driver + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an read request in the /sys/.../devices region. + * @return Returns the size of the output buffer + */ +static ssize_t adux1050_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + s32 ret = 0; + s32 stg_cnt = 0; + struct adux1050_platform_data *lpdata = adux1050->pdata; + + if (adux1050->dac_calib.cal_flags == CAL_RET_SUCCESS) { + ret = snprintf(buf + ret, PAGE_SIZE, "%1d %1d ", + adux1050->dac_calib.cal_flags, adux1050->conn_stg_cnt); + for (stg_cnt = 0; stg_cnt < adux1050->num_stages; stg_cnt++) { + if (adux1050->stg_info[stg_cnt].status == + CIN_CONNECTED) { + /*The STATUS, Stage number, Target, Offset,*/ + /* SWAP_state, Digi_offset*/ + dev_dbg(adux1050->dev, + "%1d %1d 0x%04x 0x%04x 0x%04x 0x%04x ", + adux1050->dac_calib.cal_flags, stg_cnt, + lpdata->cal_fact_base[stg_cnt], + lpdata->cal_offset[stg_cnt], + lpdata->digi_offset[stg_cnt], + lpdata->stg_cfg[stg_cnt]); + ret += + snprintf(buf + ret, PAGE_SIZE, + "%1d 0x%04x 0x%04x 0x%04x 0x%04x ", + (u16)stg_cnt, + (u16)lpdata->cal_fact_base[stg_cnt], + (u16)lpdata->cal_offset[stg_cnt], + (u16)lpdata->digi_offset[stg_cnt], + (u16)lpdata->stg_cfg[stg_cnt]); + } + } + ret--; + } else { + ret = snprintf(buf, PAGE_SIZE, "%1d\n", + (u32)adux1050->dac_calib.cal_flags); + } + return ret; +} + +/* + * This is used to call the DAC offset calibration in the driver + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param size The count of bytes to be transferred to the Buffer + * \note This is evoked upon an write request in the /sys/.../devices region. + * @return Returns the size of the input buffer + */ +static ssize_t adux1050_calibration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + s32 err; + u32 val; + + err = kstrtoint(buf, 0, &val); + + if (err < 0) { + dev_err(adux1050->dev, "%s, kstrtoint failed\n", __func__); + return err; + } + + if ((val == ADUX1050_ENABLE) || (val == ADUX1050_DISABLE)) { + if (adux1050->dev_enable != ADUX1050_ENABLE) { + mutex_lock(&adux1050->mutex); + err = adux1050_hw_init(adux1050); + mutex_unlock(&adux1050->mutex); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, "HW Init failed %d\n", + err); + return size; + } + ADUX1050_DRIVER_DBG("%s, hw_init_done\n", __func__); + } + mutex_lock(&adux1050->mutex); + adux1050->dac_calib.action_flag = (u8)val; + adux1050->dac_calib.cal_flags = CAL_RET_NONE; + ADUX1050_DRIVER_DBG("%s, Calling schedule_work\n", __func__); + mutex_unlock(&adux1050->mutex); + schedule_work(&adux1050->calib_work); + } else { + dev_err(adux1050->dev, "%s, Invalid input %d\n", + __func__, val); + } + return size; +} + +#endif + +/* + * Command parsing function for echo/cat commands from command prompt. + * This function is called when ever the User tries an echo / cat command + * to the /../sysfs/devices especially during read/write registers. + * + * @return void Returns Nothing + * @see store_reg_read + */ +static int cmd_parsing(const char *buf, u16 *addr, u16 *cnt, + u16 *data, u16 data_limit) +{ + char **bp = (char **)&buf; + u8 *token, minus, parsing_cnt = 0; + u16 val; + s32 ret; + s32 pos; + + data_limit = data_limit + 2; + while ((token = strsep(bp, SPACE_CHAR))) { + pos = 0; + minus = false; + if ((char)token[pos] == MINUS_CHAR) { + minus = true; + pos++; + } + + ret = kstrtou16(&token[pos], 0, (unsigned short *)&val); + if (ret) + return ret; + if ((parsing_cnt == 0) & (val > HIGHEST_READ_REG)) + return -ERANGE; + if (minus) + val *= MINUS_VAL; + + switch (parsing_cnt) { + case PARSE_ADDR: + *addr = val; + break; + case PARSE_CNT: + *cnt = val; + break; + default: + case PARSE_DATA: + *data = val; + data++; + break; + } + parsing_cnt++; + if (parsing_cnt > data_limit) + return parsing_cnt; + } + return parsing_cnt; +} + +#ifdef CONFIG_ADUX1050_EVAL + +/* + * Command parsing function for echo/cat commands from command prompt. + * This function is called when ever the User tries an echo / cat command + * to the /../sysfs/devices especially during proxy_time + * + * @return void Returns Nothing + * @see store_proxy_time + */ +static int cmd_parsing_for_proxy(const char *buf, u16 *addr, u16 *cnt, + u16 *data, u16 data_limit) +{ + char **bp = (char **)&buf; + u8 *token, minus, parsing_cnt = 0; + u16 val; + s32 ret; + s32 pos; + + data_limit = data_limit + 2; + while ((token = strsep(bp, SPACE_CHAR))) { + pos = 0; + minus = false; + if ((char)token[pos] == MINUS_CHAR) { + minus = true; + pos++; + } + + ret = kstrtou16(&token[pos], 0, (unsigned short *)&val); + if (ret) + return ret; + if (minus) + val *= MINUS_VAL; + + switch (parsing_cnt) { + case PARSE_ADDR: + *addr = val; + break; + case PARSE_CNT: + *cnt = val; + break; + default: + case PARSE_DATA: + *data = val; + data++; + break; + } + parsing_cnt++; + if (parsing_cnt >= data_limit) + return parsing_cnt; + } + return parsing_cnt; +} + +/* + * This is used to update the calib output to the device from the application + * space + * @return The Size of the Read data + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param size The count of bytes to be transferred to the Buffer + * \note This is evoked upon an wr request in the /sys/.../devices region. + * @return Returns the size of the data handled + */ +static ssize_t adux1050_update_calib_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + s32 err; + u16 data[FILP_PARAM_CNT * TOTAL_STG]; + u16 status; + u16 cnt; + + err = cmd_parsing(buf, &status, &cnt, data, + (FILP_PARAM_CNT * TOTAL_STG)); + if (err < 0) { + dev_err(adux1050->dev, "%s,kstrtos16 failed %x\n", + __func__, err); + return err; + } + dev_err(adux1050->dev, "cmd_ret %d", err); + + mutex_lock(&adux1050->mutex); + if (status == 1) { + dev_err(adux1050->dev, "%s , Invalid option\n", __func__); + } else if ((status == CAL_RET_SUCCESS) && (cnt <= TOTAL_STG)) { + if ((cnt * FILP_PARAM_CNT + sizeof(u16)) != err) { + dev_err(adux1050->dev, "%s,insuff data(%d)\n", + __func__, err); + goto err_data_cnt; + } + err = update_calib_settings(adux1050, cnt, data, + ADUX1050_ENABLE, ZERO_VAL); + if (err < ZERO_VAL) + dev_err(adux1050->dev, + "Failed in Update_calib_settings %d\n", err); + } else {/*TODO: Case 3 for File Write*/ + dev_err(adux1050->dev, "%s, Error for data val(%d)\n", + __func__, err); + } +err_data_cnt: + mutex_unlock(&adux1050->mutex); + return size; +} + + + +/* + * This is used to show the DAC calibration routine baseline CDC value. + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an read request in the /sys/.../devices region. + *@return Returns the Size of the output buffer + */ +static ssize_t calib_target_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + struct adux1050_platform_data *pdata = adux1050->pdata; + + return snprintf(buf, PAGE_SIZE, "%d,%d,%d,%d\n", pdata->req_stg0_base, + pdata->req_stg1_base, pdata->req_stg2_base, + pdata->req_stg3_base); +} + +/* + * This is used to set the DAC calibration offset baseline CDC value. + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param size The count of bytes to be transferred to the Buffer + * \note This is evoked upon an write request in the /sys/.../devices region. + * @return Returns the size of the data handled + */ +static ssize_t calib_target_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + u16 stg_num = 0; + u16 val = 0; + s32 err = 0; + u16 dummy; + + err = cmd_parsing(buf, &stg_num, &val, &dummy, 0); + if (err < 0) { + dev_err(adux1050->dev, "%s, kstrtos16 failed\n", __func__); + return err; + } + if ((val <= MAX_CALIB_TARGET) && (val >= MIN_CALIB_TARGET)) { + switch (stg_num) { + case STG_ZERO: + adux1050->pdata->req_stg0_base = val; + break; + case STG_ONE: + adux1050->pdata->req_stg1_base = val; + break; + case STG_TWO: + adux1050->pdata->req_stg2_base = val; + break; + case STG_THREE: + adux1050->pdata->req_stg3_base = val; + break; + default: + dev_dbg(adux1050->dev, "%s,Invalid stg no %d\n", + __func__, stg_num); + } + } else { + dev_err(adux1050->dev, "[%d,%d] Limit exceeded(%d)\n", + MAX_CALIB_TARGET, MIN_CALIB_TARGET, val); + } + + return size; +} + + +/* + * This is used to get register address whose data is to be read and + * count of data to be read via sysfs + * This function Reads the value at the Device's Register for the i2c client + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param count The count of bytes to be transferred to the Buffer + * \note This called when the user requires to read the configuration + * \note This is evoked upon an echo request in the /sys/.../devices region. + * \note it hold the register address to be read. + * @return The Size of the Read Register 0 if not read + * @see cmd_parsing + */ +static ssize_t store_reg_read(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + s32 ret; + u16 addr; + u16 cnt = 0; + u16 val = 0; + u16 lp_cnt = 0; + s16 err = 0; + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + + mutex_lock(&adux1050->mutex); + adux1050->stored_data_cnt = 0; + ret = cmd_parsing(buf, &addr, &cnt, &val, 0); + if (cnt == 0) { + dev_err(adux1050->dev, "[ADUX1050]: Invalid COM/ARG\n"); + goto error; + } else if (ret == -ERANGE || (addr+cnt > HIGHEST_READ_REG+1)) { + dev_err(adux1050->dev, "[ADUX1050]: Values not in RANGE\n"); + goto error; + } else if ((ret == -EINVAL) || (cnt > MAX_ADUX1050_WR_LEN)) { + dev_err(adux1050->dev, "[ADUX1050]: Invalid COMD/ARG\n"); + goto error; + } else { + val = 0; + } + memset(adux1050->stored_reg_data, 0, sizeof(adux1050->stored_reg_data)); + + err = adux1050->read(adux1050->dev, addr, + adux1050->stored_reg_data, cnt); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + goto error; + } + adux1050->stored_data_cnt = cnt; + for (lp_cnt = 0; lp_cnt < adux1050->stored_data_cnt; lp_cnt++) { + ADUX1050_AWFUL_LOG("Reg Read cmd:reg 0x%04x Data 0x%04x\n", + addr + lp_cnt, + adux1050->stored_reg_data[lp_cnt]); + } + +error: + mutex_unlock(&adux1050->mutex); + return count; +} + +/* + * This is used to read the data of the register address via sysfs sent to + * reg_read + * This function Reads the value at the Device's Register for the given + * client and Prints it in the output window + * @param dev The Device ID structure(linux standard argument) + * @param attr standard linux device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an cat request in the /sys/.../devices region. + * @return The Size of the read data, 0 if not read + * + * @see dev_get_drvdata + * @see store_reg_read + */ +static ssize_t show_reg_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + s32 val = 0, lp_cnt = 0; + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + + for (lp_cnt = 0; lp_cnt < adux1050->stored_data_cnt; lp_cnt++) { + val += snprintf(buf + val, PAGE_SIZE, "0x%x ", + adux1050->stored_reg_data[lp_cnt]); + } + return val; +} + +/* + * This is used to write data to a register through i2c. + * This functions Writes the value of the buffer to the given client + * provided the count value to write + * @param dev The device ID structure(linux standard argument) + * @param attr standard linux device attributes to the ADUX1050 + * @param count The number of bytes to write from the buffer + * @param buf The buffer to store the Read data + * \note This is used to store the register address to write the data. + * \note This is evoked upon an echo request in the /sys/.../devices region. + * \note This also prints the command received before writing the Registers. + * @return The Size of the writen Data, 0 if not writen + */ +static ssize_t store_reg_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + u16 addr, cnt; + u16 wr_data[MAX_ADUX1050_WR_LEN], loop_cnt; + s32 ret; + s32 err = 0; + + mutex_lock(&adux1050->mutex); + ret = cmd_parsing(buf, &addr, &cnt, &wr_data[0], MAX_ADUX1050_WR_LEN); + if (ret == -ERANGE || (ret == -EINVAL)) { + dev_err(adux1050->dev, "%s - Values not in RANGE\n", __func__); + goto error; + } else if ((addr >= BASELINE_STG0_REG && + addr <= GET_BASE_LINE_REG(STG_THREE)) && + (addr+cnt <= GET_BASE_LINE_REG(STG_THREE) + 1)) { + ADUX1050_DRIVER_DBG("%s Baseline write register\n", __func__); + + } else if (((addr + cnt) > (HIGHEST_WR_ACCESS + 1)) || + (addr == ZERO_VAL)) { + dev_err(adux1050->dev, "%s - Addr reaches RD only regs\n", + __func__); + goto error; + } else if (cnt > MAX_ADUX1050_WR_LEN) { + dev_err(adux1050->dev, "%s - Max write length\n", __func__); + goto error; + } + ADUX1050_DRIVER_DBG("Register Write command :reg= 0x%x, size= %d\n", + addr, cnt); + + for (loop_cnt = 0; loop_cnt < cnt; loop_cnt++) { + + /* Conditions to be checked */ + /* 1.SW RESET, FORCE CALIB, AUTO_Threshold, power state ... */ + if ((addr + loop_cnt) == CTRL_REG) { + /* Clearing SW Reset bit */ + if (CHK_SW_RESET_EN(wr_data[loop_cnt])) { + ADUX1050_DRIVER_DBG("S/W reset not allowed"); + ADUX1050_DRIVER_DBG("- Use reset sysfs!!!\n"); + wr_data[loop_cnt] = + CLR_SW_RESET_EN(wr_data[loop_cnt]); + } + /*Device is not allowed to wakeup */ + /* during drv disable state*/ + if (adux1050->dev_enable == ADUX1050_DISABLE) { + if (GET_PWR_MODE(wr_data[loop_cnt]) != + PWR_STAND_BY) { + /* PWR saved, used in next enable*/ + adux1050->ctrl_reg = wr_data[loop_cnt]; + adux1050->power_mode_flag = + ADUX1050_ENABLE; + wr_data[loop_cnt] = + SET_PWR_MODE(wr_data[loop_cnt], PWR_STAND_BY); + } + } + } else if (addr + loop_cnt == BASELINE_CTRL_REG) { + if (CHK_FORCE_CALIB_EN(wr_data[loop_cnt])) { + ADUX1050_DRIVER_DBG("FORCE CAL not allowed-"); + ADUX1050_DRIVER_DBG("Use force calib sysfs\n"); + wr_data[loop_cnt] = + CLR_FORCE_CALIB_EN(wr_data[loop_cnt]); + } + } + + /* Storing local of the values before writing to registers */ + if (addr+loop_cnt <= MAX_ADUX1050_WR_LEN+1) { + adux1050->reg[addr+loop_cnt].wr_flag = ADUX1050_ENABLE; + adux1050->reg[addr+loop_cnt].value = wr_data[loop_cnt]; + } else if ((addr >= BASELINE_STG0_REG && + addr <= GET_BASE_LINE_REG(STG_THREE)) && + (addr+cnt <= GET_BASE_LINE_REG(STG_THREE) + 1)) { + if (!CHK_AUTO_TH_ENABLED( + adux1050->reg[BASELINE_CTRL_REG].value)) { + adux1050->bs_reg + [addr-BASELINE_STG0_REG + loop_cnt].wr_flag + = ADUX1050_ENABLE; + adux1050->bs_reg + [addr-BASELINE_STG0_REG + loop_cnt].value + = wr_data[loop_cnt]; + } + } + ADUX1050_DRIVER_DBG("DATA = 0x%04X\n", wr_data[loop_cnt]); + } + err = adux1050->write(adux1050->dev, addr, wr_data, cnt); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C WR Err %d\n", err); +error: + mutex_unlock(&adux1050->mutex); + return count; +} + +/* + * This is used to update the stage details upon every change in configuration + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param count The count of bytes to be transferred to the Buffer + * \note This is evoked upon an write request in the /sys/.../devices region. + * @return Returns the size of the data handled + */ +static ssize_t store_update_config(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + s32 err; + u16 val; + + if (!adux1050->dev_enable) { + ADUX1050_DRIVER_DBG("Device is not enabled\n"); + return count; + } + err = kstrtou16(buf, 0, &val); + if (err < 0) { + dev_err(adux1050->dev, "%s, kstrtos16 failed\n", __func__); + return err; + } + if (val == 1) { + mutex_lock(&adux1050->mutex); + err = get_intr_mask_info(adux1050); + err = getstageinfo(adux1050); + if (err < ZERO_VAL) + dev_err(adux1050->dev, + "Err in getstageinfo %d\n", err); + mutex_unlock(&adux1050->mutex); + msleep(adux1050->slp_time_conv_complete); + adux1050->slp_time_conv_complete = + get_conv_time(adux1050, TWICE_CONV_DELAY_TIME); + } else { + ADUX1050_DRIVER_DBG("Invalid Input %d\n", val); + } + + return count; +} + +/* + * This is used to do Software reset of the device of ADUX1050 + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param count The count of bytes to be transferred to the Buffer + * \note This is evoked upon an write request in the /sys/.../devices region. + * @return Returns the size of the data handled + */ +static ssize_t store_sw_reset(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + u16 ctrl_reg_val; + s32 err; + u16 val; + + err = kstrtou16(buf, 0, &val); + if (err < 0) { + dev_err(adux1050->dev, "%s - kstrtos16 failed\n", __func__); + return err; + } + if (val == 1) { + mutex_lock(&adux1050->mutex); + /* SW reset is enabled in ctrl register */ + err = adux1050->read(adux1050->dev, CTRL_REG, + &ctrl_reg_val, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + goto error; + } + ctrl_reg_val = SET_SW_RESET_EN(ctrl_reg_val); + err = adux1050->write(adux1050->dev, CTRL_REG, + &ctrl_reg_val, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C WR Err %d\n", err); + goto error; + } + msleep(FORCE_CALIB_SLEEP_TIME); + /*Device is put to disable mode */ + if (adux1050->dev_enable == ADUX1050_ENABLE) { + adux1050->dev_enable = ADUX1050_DISABLE; + adux1050->proxy_cancel = TRUE; + cancel_delayed_work(&adux1050->proxy_work); +#ifndef CONFIG_ADUX1050_POLL + disable_irq_wake(adux1050->irq); + disable_irq(adux1050->irq); +#endif + } +error: + mutex_unlock(&adux1050->mutex); + } else { + ADUX1050_DRIVER_DBG("%s - Invalid input %d\n", __func__, val); + } + return count; +} + +/* + * This is used to do factory calibration of the ADUX1050 + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param count The count of bytes to be transferred to the Buffer + * \note This is evoked upon an write request in the /sys/.../devices region. + * @return Returns the size of the data handled + */ +static ssize_t store_force_calib(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + u16 baseline_ctrl_val; + s32 err; + u16 val; + + err = kstrtou16(buf, 0, &val); + if (err < 0) { + dev_err(adux1050->dev, "%s, kstrtos16 failed\n", __func__); + return err; + } + if (val == 1) { + /* Force Calibration is enabled in Baseline ctrl register */ + /*TODO: Check AUTO-TH is enable or not */ + /*TODO: Can the Force calibratin be done*/ + /* when the device is at DISABLED state?*/ + mutex_lock(&adux1050->mutex); + err = adux1050->read(adux1050->dev, BASELINE_CTRL_REG, + &baseline_ctrl_val, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + goto error; + } + baseline_ctrl_val = SET_FORCE_CALIB_EN(baseline_ctrl_val); + err = adux1050->write(adux1050->dev, BASELINE_CTRL_REG, + &baseline_ctrl_val, DEF_WR); + if (err < DEF_WR) { + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + goto error; + } + msleep(FORCE_CALIB_SLEEP_TIME); +error: + mutex_unlock(&adux1050->mutex); + } else { + ADUX1050_DRIVER_DBG("%s,Value is invalid\n", __func__); + } + return count; +} + +#endif + +static ssize_t store_offset_dac_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + u16 addr, cnt; + u16 wr_data[MAX_ADUX1050_WR_LEN]; + s32 ret; + s32 err = 0; + + ret = cmd_parsing(buf, &addr, &cnt, &wr_data[0], MAX_ADUX1050_WR_LEN); + if (ret == -ERANGE || (ret == -EINVAL)) { + dev_err(adux1050->dev, "%s - Values not in RANGE\n", __func__); + return count; + } else if (cnt != 1) { + dev_err(adux1050->dev, "%s - length not 1\n", __func__); + return count; + } + switch (addr) { + case GET_OFFSET_REG(0): + case GET_OFFSET_REG(1): + break; /* right address */ + default: + dev_err(adux1050->dev, "%s - wrong address\n", __func__); + return count; + } + + ADUX1050_DRIVER_DBG("Offset Dac Write: reg= 0x%x, value= 0x%x\n", + addr, wr_data[0]); + + mutex_lock(&adux1050->mutex); + /* Storing local of the values before writing to registers */ + adux1050->reg[addr].wr_flag = ADUX1050_ENABLE; + adux1050->reg[addr].value = wr_data[0]; + ADUX1050_DRIVER_DBG("DATA = 0x%04X\n", wr_data[0]); + err = adux1050->write(adux1050->dev, addr, wr_data, cnt); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C WR Err %d\n", err); + mutex_unlock(&adux1050->mutex); + return count; +} + +#ifdef CONFIG_ADUX1050_POLL + +/* + * This is used to show the poll delay used by the driver + * @param dev The Device ID structure(linux standard argument) + * @param attr standard linux device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an cat request in the /sys/.../devices region. + * @return The Size of the read data, 0 if not read + */ +static ssize_t show_poll_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + + dev_dbg(adux1050->dev, "Poll delay =%x", adux1050->poll_delay); + return snprintf(buf, PAGE_SIZE, "%d\n", adux1050->poll_delay); +} + + + + + +/* + * This is used to set the poll delay for the adux1050 kthread which is running + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param count The count of bytes to be transferred to the Buffer + * \note This is evoked upon an write request in the /sys/.../devices region. + * @return Returns the size of the data handled + */ +static ssize_t store_poll_delay(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + s32 err; + u16 val; + + err = kstrtou16(buf, 0, &val); + if (err < 0) { + ADUX1050_DRIVER_DBG("%s -kstrou16 is not done\n", __func__); + return err; + } + if (val <= MIN_POLL_DELAY) + val = MIN_POLL_DELAY; + adux1050->poll_delay = val; + return count; +} +#endif + +#ifdef CONFIG_ADUX1050_EVAL + +/* + * This is used to read error threshold byte via sysfs to do the force + * calibration + * @param dev The Device ID structure(linux standard argument) + * @param attr standard linux device attributes to the ADUX1050 + * @param buf The buffer to store the error threshold byte + * \note This is evoked upon an cat request in the /sys/.../devices region. + * @return The Size of the read data, 0 if not read + */ +static ssize_t show_intr_err(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + + dev_dbg(adux1050->dev, "intr_err =%x", adux1050->user_intr_err); + return snprintf(buf, PAGE_SIZE, "%d\n", adux1050->user_intr_err); +} + +/* + * This is used to set the error threshold byte for the adux1050 + * @param dev The Device Id structure + * @param attr The Device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param count The count of bytes to be transferred to the Buffer + * \note This is evoked upon an write request in the /sys/.../devices region. + * @return Returns the size of the data handled + */ +static ssize_t store_intr_err(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + s8 err; + u8 val; + + err = kstrtou8(buf, 0, &val); + if (err < 0) { + ADUX1050_DRIVER_DBG("%s -kstrou8 is not done\n", __func__); + return err; + } + mutex_lock(&adux1050->mutex); + adux1050->user_intr_err = val; + mutex_unlock(&adux1050->mutex); + return count; +} + +/* + * This is used to read the maximum count and work handle frequency. + * This functions Reads the value at the Device's Register for the given + * client and Prints it in the output window + * @param dev The Device ID structure(linux standard argument) + * @param attr standard linux device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an cat request in the /sys/.../Device region. + * @return The maximum count and set set time. + */ +static ssize_t show_proxy_time(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + + dev_dbg(adux1050->dev, + " - max_count%d\n proxy_enable bit freq: %d sec\n", + adux1050->max_proxy_count, adux1050->allowed_proxy_time); + return snprintf(buf, PAGE_SIZE, + "%d\t%d\n", + adux1050->allowed_proxy_time, + adux1050->max_proxy_count); +} + +/* + * This is used to set proxy time for force calibration + * @param dev The device ID structure(linux standard argument) + * @param attr standard linux device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * @param count The number of bytes to write from the buffer + * \note This is used to store the register address to write the data. + * \note This is evoked upon an echo request in the /sys/.../Device region. + * \note This also prints the set time and loop count. + * @return The Size of the writen Data, 0 if not writen + */ +static ssize_t store_proxy_time(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + s16 ret; + u16 val = 0; + u16 cnt = 0; + u16 data = 0; + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + + mutex_lock(&adux1050->mutex); + ret = cmd_parsing_for_proxy(buf, &val, &cnt, &data, 2); + if (cnt == 0) { + dev_err(adux1050->dev, "[ADUX1050]: Invalid cnt COM/ARG\n"); + goto error; + } else if (val == 0) { + dev_err(adux1050->dev, "[ADUX1050]: Invalid COM/ARG\n"); + goto error; + } else if (ret == -ERANGE) { + dev_err(adux1050->dev, "[ADUX1050]: Values not in RANGE\n"); + goto error; + } else if ((ret == -EINVAL)) { + dev_err(adux1050->dev, "[ADUX1050]: Invalid COMD/ARG\n"); + goto error; + } else { + data = 0; + } + adux1050->proxy_count = 0; + adux1050->allowed_proxy_time = val; + adux1050->max_proxy_count = cnt; + if ((adux1050->proxy_enable) && (adux1050->dev_enable)) { + adux1050->proxy_cancel = TRUE; + cancel_delayed_work(&adux1050->proxy_work); + ADUX1050_DRIVER_DBG("%s -proxy_time is set\n", __func__); + adux1050->proxy_cancel = FALSE; + schedule_delayed_work(&adux1050->proxy_work, + msecs_to_jiffies + (PROXY_TIME * adux1050->allowed_proxy_time)); + } + dev_dbg(adux1050->dev, "val:%d\tcnt:%d\tset time =%usec\n", + adux1050->allowed_proxy_time, + adux1050->max_proxy_count, + adux1050->max_proxy_count * adux1050->allowed_proxy_time); +error: + mutex_unlock(&adux1050->mutex); + return count; +} + +/* + * This is used to read the set time and remaining time to do force calibration. + * This functions Reads the value at the Device's Register for the given + * client and Prints it in the output window + * @param dev The Device ID structure(linux standard argument) + * @param attr standard linux device attributes to the ADUX1050 + * @param buf The buffer to store the Read data + * \note This is evoked upon an cat request in the /sys/.../Device region. + * @return The remaining time and total time. + */ +static ssize_t show_proxy_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + u32 time; + u32 rem_time; + + time = (adux1050->proxy_count * adux1050->allowed_proxy_time); + dev_dbg(adux1050->dev, "time elapsed =%dsec\n", time); + rem_time = adux1050->allowed_proxy_time * adux1050->max_proxy_count - + time; + dev_dbg(adux1050->dev, "proxy :%d\ttime left:%u sec\nset time:%u\n", + adux1050->proxy_enable, rem_time, adux1050->max_proxy_count * + adux1050->allowed_proxy_time); + return snprintf(buf, PAGE_SIZE, "%d\t%u\t%u\n", adux1050->proxy_enable, + rem_time, adux1050->max_proxy_count * + adux1050->allowed_proxy_time); +} + +/* + * This is used to enable the work function to moniter proxy time for force + * calib + * if the proximity time is not set it will set it to default values + * @param dev The device ID structure(linux standard argument) + * @param attr standard linux device attributes to the ADUX1050 + * @param buf The buffer to store the read data + * @param count The number of bytes to write from the buffer + * \note This is used to store the register address to write the data. + * \note This is evoked upon an echo request in the /sys/.../Device region. + * \note This also prints proxy enable/disable. + * @return The Size of the writen Data, 0 if not writen + */ +static ssize_t store_proxy_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adux1050_chip *adux1050 = dev_get_drvdata(dev); + s8 err; + u8 val; + + err = kstrtou8(buf, 0, &val); + if (err < 0) { + ADUX1050_DRIVER_DBG("%s - kstrou8 is not done\n", __func__); + return err; + } + if (val > 1) { + dev_dbg(adux1050->dev, "%s :Invalid- Enable:1 Disable:0\n", + __func__); + } else if ((adux1050->proxy_enable == 1) && (val == 1)) { + dev_dbg(adux1050->dev, "%s : proximity is already enabled\n", + __func__); + goto error; + } else { + adux1050->proxy_count = 0; + adux1050->proxy_enable = val; + if ((adux1050->proxy_enable) && (adux1050->dev_enable)) { + adux1050->proxy_cancel = TRUE; + cancel_delayed_work(&adux1050->proxy_work); + ADUX1050_DRIVER_DBG("%s -proxy_correc_bit enabled\n", + __func__); + adux1050->proxy_cancel = FALSE; + schedule_delayed_work(&adux1050->proxy_work, + msecs_to_jiffies + (PROXY_TIME * adux1050->allowed_proxy_time)); + } else { + adux1050->proxy_cancel = TRUE; + cancel_delayed_work(&adux1050->proxy_work); + adux1050->proxy_count = 0; + ADUX1050_DRIVER_DBG("%s -proxy_bit/device is disable\n", + __func__); + } + } +error: + return count; +} +#endif + +/* + * The sysfs attributes used in the driver follows + */ +/*--------------------------------------------------------------*/ +static DEVICE_ATTR(adux1050_enable, S_IRUGO|S_IWUSR|S_IWGRP, + show_enable, store_enable); +/*--------------------------------------------------------------*/ +#ifdef CONFIG_ADUX1050_EVAL +static struct device_attribute dev_attr_sensor_name = + __ATTR(adux1050_device_name, S_IRUGO, + adux1050_name_show, NULL); +static struct device_attribute dev_attr_sensor_vendor = + __ATTR(adux1050_vendor, S_IRUGO, + adux1050_vendor_show, NULL); +static struct device_attribute dev_attr_sensor_raw_data = + __ATTR(adux1050_raw_data, S_IWUSR|S_IWGRP|S_IRUGO, + adux1050_raw_data_show, adux1050_raw_data_store); +static struct device_attribute dev_attr_sensor_send_event = + __ATTR(adux1050_send_event, S_IWUSR|S_IRUGO|S_IWGRP, + adux1050_send_event_show, adux1050_send_event_store); +static struct device_attribute dev_attr_sensor_threshold = + __ATTR(adux1050_threshold, S_IWUSR|S_IRUGO|S_IWGRP, + adux1050_threshold_show, adux1050_threshold_store); +static struct device_attribute dev_attr_sensor_calibration = + __ATTR(adux1050_calibration, S_IWUSR | S_IWGRP | S_IRUGO, + adux1050_calibration_show, adux1050_calibration_store); +static struct device_attribute dev_attr_sensor_update_calib = + __ATTR(adux1050_update_calib, S_IWUSR | S_IWGRP, + NULL, adux1050_update_calib_store); +static struct device_attribute dev_attr_sensor_dump = + __ATTR(adux1050_status, S_IRUGO, + show_dumpregs, NULL); +static struct device_attribute dev_attr_sensor_calib_target = + __ATTR(adux1050_calib_target, S_IRUGO | S_IWUSR | S_IWGRP, + calib_target_show, calib_target_store); +static struct device_attribute dev_attr_sensor_reg_read = + __ATTR(adux1050_reg_read, S_IWUSR | S_IWGRP | S_IRUGO, + show_reg_read, store_reg_read); +static struct device_attribute dev_attr_sensor_reg_write = + __ATTR(adux1050_reg_write, S_IWUSR | S_IWGRP, + NULL, store_reg_write); +static struct device_attribute dev_attr_sensor_update_config = + __ATTR(adux1050_update_config, S_IWUSR | S_IWGRP, + NULL, store_update_config); +static struct device_attribute dev_attr_sensor_sw_reset = + __ATTR(adux1050_sw_reset, S_IWUSR | S_IWGRP, + NULL, store_sw_reset); +static struct device_attribute dev_attr_sensor_force_calib = + __ATTR(adux1050_force_calib, S_IWUSR | S_IWGRP, + NULL, store_force_calib); +#endif +static struct device_attribute dev_attr_sensor_offset_dac_write = + __ATTR(adux1050_offset_dac_write, S_IWUSR | S_IWGRP, + NULL, store_offset_dac_write); +#ifdef CONFIG_ADUX1050_POLL +static struct device_attribute dev_attr_sensor_poll = + __ATTR(adux1050_poll_delay, S_IWUSR | S_IWGRP | S_IRUGO, + show_poll_delay, store_poll_delay); +#endif +#ifdef CONFIG_ADUX1050_EVAL +static struct device_attribute dev_attr_sensor_fc_intr_err = + __ATTR(adux1050_intr_error, S_IWUSR | S_IWGRP | S_IRUGO, + show_intr_err, store_intr_err); +static struct device_attribute dev_attr_sensor_proxy_timer_enable = + __ATTR(adux1050_proxy_enable, S_IWUSR | S_IWGRP | S_IRUGO, + show_proxy_enable, store_proxy_enable); +static struct device_attribute dev_attr_sensor_set_proxy_time = + __ATTR(adux1050_proxy_time, S_IWUSR | S_IWGRP | S_IRUGO, + show_proxy_time, store_proxy_time); +#endif +static struct attribute *adux1050_attrs[] = { + &dev_attr_adux1050_enable.attr, +#ifdef CONFIG_ADUX1050_EVAL + &dev_attr_sensor_name.attr, + &dev_attr_sensor_vendor.attr, + &dev_attr_sensor_raw_data.attr, + &dev_attr_sensor_send_event.attr, + &dev_attr_sensor_threshold.attr, + &dev_attr_sensor_calibration.attr, + &dev_attr_sensor_update_calib.attr, + &dev_attr_sensor_dump.attr, + &dev_attr_sensor_calib_target.attr, + &dev_attr_sensor_reg_read.attr, + &dev_attr_sensor_reg_write.attr, + &dev_attr_sensor_update_config.attr, + &dev_attr_sensor_sw_reset.attr, + &dev_attr_sensor_force_calib.attr, +#endif + &dev_attr_sensor_offset_dac_write.attr, +#ifdef CONFIG_ADUX1050_POLL + &dev_attr_sensor_poll.attr, +#endif +#ifdef CONFIG_ADUX1050_EVAL + &dev_attr_sensor_fc_intr_err.attr, + &dev_attr_sensor_proxy_timer_enable.attr, + &dev_attr_sensor_set_proxy_time.attr, +#endif + NULL, +}; + +static struct attribute_group adux1050_attr_group = { + .name = NULL, + .attrs = adux1050_attrs, +}; + +/* + * This routine reads the Device ID to confirm the existence + * of the Device in the System. + * @param adux1050 The Device structure + * @return 0 on successful detection of the device,-ENODEV on err. + */ +static int adux1050_hw_detect(struct adux1050_chip *adux1050) +{ + u16 data; + s16 err = 0; + + err = adux1050->read(adux1050->dev, DEV_ID_REG, &data, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + return -ENODEV; + } + if (likely((data & ADUX1050_ID_MASK) == ADUX1050_GENERIC_ID)) { + adux1050->product = PRODUCT_ID(data); + adux1050->version = REV_ID(data); + adux1050->metal_id = METAL_ID(data); + if (adux1050->metal_id < MET_VER1) + dev_dbg(adux1050->dev, + "Hopping Search Saturation Routine enabled"); + else if (adux1050->metal_id >= MET_VER1) + dev_dbg(adux1050->dev, + "Binary Search Saturation Routine enabled"); + dev_dbg(adux1050->dev, "Found ADUX1050, rev:%x met_id:%x", + adux1050->version, adux1050->metal_id); + return 0; + } + dev_err(adux1050->dev, "ADUX1050 Not Found,ID %4x\n", data); + return -ENODEV; +} + +#ifndef CONFIG_ADUX1050_POLL +/* + * Threaded IRQ Handler -- Assigns interrupt handling to work + * @param irq The Interrupt Request queue to be assigned for the device + * @param handle The data of the ADUX1050 Device + * @return IRQ_HANDLED + */ +static irqreturn_t adux1050_isr_thread(int irq, void *handle) +{ + s16 err = 0; + struct adux1050_chip *adux1050 = handle; + + if (!work_pending(&adux1050->work)) { + schedule_work(&adux1050->work); + } else { + mutex_lock(&adux1050->mutex); + /*Cleared the interrupt for future intterupts to occur*/ + err = adux1050->read(adux1050->dev, INT_STATUS_REG, + &adux1050->int_status, DEF_WR); + if (err < I2C_WRMSG_LEN) + dev_err(adux1050->dev, "I2C Read Error %d in %s", + err, __func__); + + mutex_unlock(&adux1050->mutex); + } + return IRQ_HANDLED; +} +#endif + +/* + * conv_complete_cdc_fetch - Fetch the CDC for the connected stage after + * receiving the conversion sequence complete interrupt is asserted + * @param adux1050 The chip structure of ADUX1050 chip + * @return void + */ +static inline void conv_complete_cdc_fetch(struct adux1050_chip *adux1050, + u16 proxy_status) +{ + u8 stg_cnt = 0; + u16 result_cdc; + u16 baseline_cdc; + s16 err = 0; + unsigned int event_value = 0; + + for (stg_cnt = 0; stg_cnt < adux1050->num_stages; stg_cnt++) { + /** Fetch the CDC only if that stage is connected */ + if (adux1050->stg_info[stg_cnt].status != CIN_CONNECTED) + continue; + err = adux1050->read(adux1050->dev, GET_RESULT_REG(stg_cnt), + &result_cdc, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C Read Error %d in %s", + err, __func__); + err = adux1050->read(adux1050->dev, GET_BASE_LINE_REG(stg_cnt), + &baseline_cdc, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C Read Error %d in %s", + err, __func__); + if (!adux1050->send_event) + continue; + event_value = PACK_FOR_CDC_EVENT(result_cdc, stg_cnt); + input_event(adux1050->input, EV_MSC, MSC_RAW, event_value); + event_value = PACK_FOR_BASELINE_EVENT(baseline_cdc, stg_cnt); + input_event(adux1050->input, EV_MSC, MSC_RAW, event_value); + } + event_value = PACK_FOR_STATUS_EVENT(adux1050->int_status, proxy_status); + input_event(adux1050->input, EV_MSC, MSC_RAW, event_value); + input_sync(adux1050->input); +} + +/* + * This function is used to send ACTIVE event for a stage in ADUX1050 + * @param adux1050 The ADUX1050 chip structure pointer + * @param stg_num The stage nmmber for which the event to be sent + * @param threshold_type Indicates Low or High threshold + * @return void + */ +static inline void indicate_active_state(struct adux1050_chip *adux1050, + int stg_num, int threshold_type) +{ + unsigned int event_value = 0; + + ADUX1050_DRIVER_DBG("%s\n", __func__); + event_value = PACK_FOR_ACTIVE_STATE(stg_num, threshold_type); + input_event(adux1050->input, EV_MSC, MSC_RAW, event_value); + input_sync(adux1050->input); + +} + +/* + * This function is used to send IDLE event for a stage in ADUX1050 + * @param adux1050 The ADUX1050 chip structure pointer + * @param stg_num The stage nmmber for which the event to be sent + * @param threshold_type Indicates Low or High threshold + * @return void + */ +static inline void indicate_idle_state(struct adux1050_chip *adux1050, + int stg_num, int threshold_type) +{ + unsigned int event_value = 0; + + ADUX1050_DRIVER_DBG("%s\n", __func__); + event_value = PACK_FOR_IDLE_STATE(stg_num, threshold_type); + input_event(adux1050->input, EV_MSC, MSC_RAW, event_value); + input_sync(adux1050->input); +} + +/* + * high_threshold_int_check - Identify which stage asserts the high threshold + * interrupt.After identifying the stage, the state(ACTIVE,IDLE) of the stage + * is sent as input event + * @param adux1050 The chip structure of ADUX1050 chip + * @param high_status_change Contains stage number which asserts INT. + * @return void + */ +static inline void high_threshold_int_check(struct adux1050_chip *adux1050, + u16 high_status_change) +{ + u8 stg_cnt; + u8 temp_ht_enable = adux1050->high_thresh_enable; + u8 temp_ht_intr_err = GET_HIGH_STATUS(adux1050->user_intr_err); + + for (stg_cnt = 0; stg_cnt < TOTAL_STG; stg_cnt++) { + if ((!(temp_ht_intr_err & 1)) && (temp_ht_enable & 1)) { + if (high_status_change & 1) { + if (adux1050->high_status & 1) { + dev_dbg(adux1050->dev, "St = %de\n", + stg_cnt); + if (adux1050->send_event) + indicate_active_state(adux1050, + stg_cnt, + TH_HIGH); + } else { + dev_dbg(adux1050->dev, "St = %dx\n", + stg_cnt); + if (adux1050->send_event) + indicate_idle_state(adux1050, + stg_cnt, + TH_HIGH); + } + } + } + temp_ht_enable = temp_ht_enable >> 1; + high_status_change = high_status_change >> 1; + adux1050->high_status = adux1050->high_status >> 1; + temp_ht_intr_err = temp_ht_intr_err >> 1; + } +} + +/* + * Low_threshold_int_check - Identify which stage asserts the low threshold + * interrupt.After identifying the stage, the state(ACTIVE,IDLE) of the stage + * is sent as input event + * @param adux1050 The chip structure of ADUX1050 chip + * @param low_status_change Contains stage number which asserts INT. + * @return void + */ +static inline void low_threshold_int_check(struct adux1050_chip *adux1050, + u16 low_status_change) +{ + u8 stg_cnt; + u8 temp_lt_enable = adux1050->low_thresh_enable; + u8 temp_intr_err = GET_LOW_STATUS(adux1050->user_intr_err); + + for (stg_cnt = 0; stg_cnt < TOTAL_STG; stg_cnt++) { + if ((!(temp_intr_err & 1)) && (temp_lt_enable & 1)) { + if (low_status_change & 1) { + if (adux1050->low_status & 1) { + dev_dbg(adux1050->dev, "St = %de\n", + stg_cnt); + if (adux1050->send_event) + indicate_active_state(adux1050, + stg_cnt, + TH_LOW); + } else { + dev_dbg(adux1050->dev, "St = %dx\n", + stg_cnt); + if (adux1050->send_event) + indicate_idle_state(adux1050, + stg_cnt, + TH_LOW); + } + } + } + temp_lt_enable = temp_lt_enable >> 1; + low_status_change = low_status_change >> 1; + adux1050->low_status = adux1050->low_status >> 1; + temp_intr_err = temp_intr_err >> 1; + } +} +/* + * int adux1050_get_result_info(struct adux1050_chip *adux1050) + * This function is used to get the CDC,base line, high threshold and + * low threshold for the connected stages and prints the information. + * @param adux1050 The chip structure of ADUX1050 driver + * @return 0 on success. + */ +static int adux1050_get_result_info(struct adux1050_chip *adux1050) +{ + u16 rs_reg[TOTAL_STG]; + u16 ls_reg[TOTAL_STG]; + u16 hs_reg[TOTAL_STG]; + u16 base_reg[TOTAL_STG]; + u8 stg_cnt = 0; + s16 err = 0; + + for (stg_cnt = 0 ; stg_cnt < TOTAL_STG ; stg_cnt++) { + if (adux1050->stg_info[stg_cnt].status == CIN_CONNECTED) { + err = adux1050->read(adux1050->dev, + GET_RESULT_REG(stg_cnt), + &rs_reg[stg_cnt], DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + return err; + } + err = adux1050->read(adux1050->dev, + GET_HIGH_TH_REG(stg_cnt), + &hs_reg[stg_cnt], DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + return err; + } + err = adux1050->read(adux1050->dev, + GET_LOW_TH_REG(stg_cnt), + &ls_reg[stg_cnt], DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + return err; + } + err = adux1050->read(adux1050->dev, + GET_BASE_LINE_REG(stg_cnt), + &base_reg[stg_cnt], DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d\n", err); + return err; + } + dev_dbg(adux1050->dev, + "STG %x CDC %x\t BS %x\t HS %x\t LS %x\n", + stg_cnt, + rs_reg[stg_cnt], + base_reg[stg_cnt], + base_reg[stg_cnt] + hs_reg[stg_cnt], + base_reg[stg_cnt] - ls_reg[stg_cnt]); + } + } + return 0; +} + +/* + * Work Handler -- Handles the interrupt status from ADUX1050 + * @param isr_work The work structure for the ADUX1050 chip + * @return void Nothing returned + */ +static void adux1050_isr_work_fn(struct work_struct *isr_work) +{ + struct adux1050_chip *adux1050 = NULL; + u16 high_status_change = 0; + u16 low_status_change = 0; + u8 curr_intr_status = 0; + u8 user_intr_error_not = 0; + u8 stg_cnt = 0; + u8 sat_flag = 0; + u16 rs_reg[TOTAL_STG]; + u16 proxy_stage_status = 0; + s16 err = 0; + + if (isr_work == NULL) { + pr_err("%s - isr_work is NULL\n", __func__); + goto isr_null; + } + adux1050 = container_of(isr_work, struct adux1050_chip, work); + mutex_lock(&adux1050->mutex); + err = adux1050->read(adux1050->dev, INT_STATUS_REG, + &adux1050->int_status, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", err, __FILE__); + goto fc_done; + } + dev_dbg(adux1050->dev, "status_reg : %x int_status :%x DEF_WR:%x\n", + INT_STATUS_REG, adux1050->int_status, DEF_WR); + adux1050->low_status = GET_LOW_STATUS(adux1050->int_status); + adux1050->high_status = GET_HIGH_STATUS(adux1050->int_status); + adux1050->conv_status = GET_CONV_STATUS(adux1050->int_status); + dev_dbg(adux1050->dev, "HS:%x LS:%x CCS:%x\n", adux1050->high_status, + adux1050->low_status, adux1050->conv_status); +/* conditions to handle error conditions for force calibration */ + for (stg_cnt = 0; stg_cnt < TOTAL_STG; stg_cnt++) { + if (adux1050->stg_info[stg_cnt].status == CIN_CONNECTED) { + adux1050->read(adux1050->dev, GET_RESULT_REG(stg_cnt), + &rs_reg[stg_cnt], DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", + err, __FILE__); + goto fc_done; + } + ((rs_reg[stg_cnt] < MIN_CALIB_TARGET) || + (rs_reg[stg_cnt] > MAX_CALIB_TARGET)) ? + (sat_flag += 1) : sat_flag; + } + } + curr_intr_status = (u8) (adux1050->int_status & LOW_BYTE_MASK); + err = adux1050->read(adux1050->dev, PROX_STATUS_REG, + &proxy_stage_status, DEF_WR); + if (err < I2C_WRMSG_LEN) { + dev_err(adux1050->dev, "I2C RD Err %d in %s\n", err, __FILE__); + goto fc_done; + } + proxy_stage_status &= LOW_NIBBLE_MASK; + dev_dbg(adux1050->dev, "proximity status:%x", + proxy_stage_status); + if ((adux1050->user_intr_err & curr_intr_status) && (sat_flag == 0)) { + user_intr_error_not = ~(adux1050->user_intr_err); + if (!(curr_intr_status & user_intr_error_not)) { + dev_dbg(adux1050->dev, "BEFORE FC:"); + err = adux1050_get_result_info(adux1050); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, "get result fail %d\n", + err); + goto fc_done; + } + err = adux1050_force_cal(adux1050, + get_conv_time(adux1050, CONV_TIME)); + if (err < 0) { + dev_err(adux1050->dev, "ERROR in FC %d\n", err); + goto fc_done; + } + ADUX1050_DRIVER_DBG("%s -FC due to ERR condition**\n", + __func__); + dev_dbg(adux1050->dev, "AFTER FC:"); + err = adux1050_get_result_info(adux1050); + if (err < ZERO_VAL) { + dev_err(adux1050->dev, + "get result fail after FC %d\n", err); + goto fc_done; + } + } + } + /* Handling High threshold interrupt */ + if (adux1050->high_thresh_enable) { + high_status_change = ((adux1050->high_status) ^ + (adux1050->prev_high_status)); + adux1050->prev_high_status = adux1050->high_status; + if (high_status_change) { + adux1050->proxy_count = 0; + dev_dbg(adux1050->dev, "H THRES CHANGE:"); + adux1050_get_result_info(adux1050); + } + high_threshold_int_check(adux1050, high_status_change); + } + + /* Handling Low threshold interrupt */ + if (adux1050->low_thresh_enable) { + low_status_change = ((adux1050->low_status) ^ + (adux1050->prev_low_status)); + adux1050->prev_low_status = adux1050->low_status; + if (low_status_change) { + adux1050->proxy_count = 0; + dev_dbg(adux1050->dev, "L THRES CHANGE:"); + adux1050_get_result_info(adux1050); + } + low_threshold_int_check(adux1050, low_status_change); + } + /* Handling Conversion complete interrupt */ + if (adux1050->conv_enable && adux1050->conv_status) + conv_complete_cdc_fetch(adux1050, proxy_stage_status); +fc_done: + mutex_unlock(&adux1050->mutex); +isr_null: + err = 0; +} +/* + * Delayed work Handler -- Handles the proximity status from ADUX1050 + * issues the force calibration when the condition meets. + * @param proxy_dwork The work structure for the ADUX1050 chip + * @return void Nothing returned + */ +static void adux1050_proxy_work(struct work_struct *proxy_dwork) +{ + s16 err = 0; + struct adux1050_chip *adux1050 = NULL; + u16 proxy_stage_status; + u16 int_status; + + if (proxy_dwork == NULL) { + pr_err("%s - _work is NULL\n", __func__); + goto proxy_null; + } + adux1050 = container_of((struct delayed_work *) + proxy_dwork, + struct adux1050_chip, + proxy_work); + mutex_lock(&adux1050->mutex); + ADUX1050_DRIVER_DBG("%s -in delayed proxy_work!!!!!!\n", __func__); + err = adux1050->read(adux1050->dev, INT_STATUS_REG, + &int_status, DEF_WR); + if (err < I2C_WRMSG_LEN) + dev_err(adux1050->dev, "I2C READ FAILED %d at %s", + err, __func__); + int_status &= LOW_BYTE_MASK; + err = adux1050->read(adux1050->dev, PROX_STATUS_REG, + &proxy_stage_status, DEF_WR); + if (err < I2C_WRMSG_LEN) + dev_err(adux1050->dev, "I2C READ FAILED %d at %s", + err, __func__); + proxy_stage_status &= LOW_NIBBLE_MASK; + dev_dbg(adux1050->dev, "proximity status:%x", proxy_stage_status); + if ((int_status == 0) && (proxy_stage_status > 0)) { + adux1050->proxy_count++; + dev_dbg(adux1050->dev, "proxy_count:%x", + adux1050->proxy_count); + if (adux1050->proxy_count >= adux1050->max_proxy_count) { + adux1050->proxy_count = 0; + ADUX1050_DRIVER_DBG("%s -FC due to time limit*\n", + __func__); + err = adux1050_force_cal(adux1050, + adux1050->slp_time_conv_complete); + dev_dbg(adux1050->dev, "slp_time in FC:%x", + adux1050->slp_time_conv_complete); + if (err < 0) + dev_err(adux1050->dev, + "FC I2C READ FAILED %d at %s", + err, __func__); + } + } else { + adux1050->proxy_count = 0; + } + mutex_unlock(&adux1050->mutex); + if (!adux1050->proxy_cancel) { + schedule_delayed_work(&adux1050->proxy_work, + msecs_to_jiffies(PROXY_TIME * + adux1050->allowed_proxy_time)); + } +proxy_null: + err = 0; +} +#ifdef CONFIG_ADUX1050_POLL +/* + * adux1050_polling_thread_fn -- Run function of the ADUX1050 Kthread + * @param chip_data - Hold the pointer of ADUX1050 chip structure + * @return int - Returns Zero + */ +static int adux1050_polling_thread_fn(void *chip_data) +{ + s16 err = 0; + struct adux1050_chip *adux1050 = chip_data; + + ADUX1050_DRIVER_DBG("Polling Thread Started\n"); + while (!kthread_should_stop()) { + if (adux1050->dev_enable == ADUX1050_ENABLE) { + if (adux1050->dac_calib.cal_flags != CAL_RET_PENDING) { + if (!work_pending(&adux1050->work)) { + schedule_work(&adux1050->work); + } else { + mutex_lock(&adux1050->mutex); + /*Clear interrupt for future to occur*/ + err = adux1050->read(adux1050->dev, + INT_STATUS_REG, + &adux1050->int_status, + DEF_WR); + if (err < I2C_WRMSG_LEN) + dev_err(adux1050->dev, + "I2C RD FAIL %d at %s", + err, __func__); + mutex_unlock(&adux1050->mutex); + } + } + if (likely(adux1050->poll_delay <= MSLEEP_MIN_TIME)) + usleep_range(adux1050->poll_delay * MS_TO_US, + adux1050->poll_delay * MS_TO_US); + else + msleep(adux1050->poll_delay); + + } else { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + } + return 0; +} +#endif + +/* + * + */ +static bool read_value_from_device_tree(struct adux1050_chip *adux1050, + const char *id, unsigned short *read_value) +{ + s32 df_prop_length = 0; + const __be32 *param = NULL; + + param = of_get_property(adux1050->dt_device_node, id, &df_prop_length); + if (!param || (df_prop_length != sizeof(int))) + return false; + *read_value = be32_to_cpu(*param); + ADUX1050_DRIVER_DBG("%s read from DT, %s:%d\n", + __func__, id, *read_value); + return true; +} + +/* + * Device probe function all initialization routines are handled here like + * the ISR registration or the polling thead registeration,Work creation, + * Input device registration, SYSFS attributes creation etc. + * @param client the i2c structure of the adux1050 device/client. + * @param id The i2c_device_id for the supported i2c device. + * @return 0 on success,and On failure -ENOMEM, -EINVAL ,etc., will be returned + */ +static int adux1050_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + s32 ret = -EINVAL; + struct input_dev *input = NULL; + struct device *dev = NULL; + struct adux1050_chip *adux1050 = NULL; + + ADUX1050_DRIVER_DBG("%s called", __func__); + + if ((client == NULL) || (&client->dev == NULL)) { + pr_err("%s: Client/client->dev doesn't exist\n", __func__); + return ret; + } + dev = &client->dev; + +#ifndef CONFIG_ADUX1050_POLL + if (client->irq <= 0) { + pr_err("%s: IRQ not configured!\n", __func__); + return ret; + } +#endif + + adux1050 = kzalloc(sizeof(*adux1050), GFP_KERNEL); + if (!adux1050) { + /*WARNING: Possible unnecessary 'out of memory' message*/ + /*pr_err("%s: Memory alloc fail - Chip struct\n", __func__);*/ + return -ENOMEM; + } + + adux1050->stg_info = + kzalloc((sizeof(struct adux1050_stage_info) * TOTAL_STG), + GFP_KERNEL); + if (!adux1050->stg_info) { + /*WARNING: Possible unnecessary 'out of memory' message*/ + /*pr_err("%s: Memory Alloc fail - Stage info\n", __func__);*/ + return -ENOMEM; + } + + if (dev->platform_data == NULL) { + adux1050->pdata = &local_plat_data; + pr_debug("%s: Platform Data Not Found\n", __func__); + +#ifdef CONFIG_OF + if (client->dev.of_node != NULL) { + struct adux1050_platform_data *pdata = adux1050->pdata; + s32 df_prop_length = 0; + const __be32 *irqf = NULL; + unsigned short value = 0; + bool is_read = false; + + adux1050->dt_device_node = client->dev.of_node; + pr_debug("%s: DT node Found\n", __func__); + irqf = of_get_property(adux1050->dt_device_node, + "adi,adux1050_irq_flags", &df_prop_length); + if (irqf && (df_prop_length == sizeof(int))) { + adux1050->pdata->irq_flags = + be32_to_cpu(*irqf); + pr_debug("%s Usg irq_flag from DT %d\n", + __func__, adux1050->pdata->irq_flags); + } + is_read = read_value_from_device_tree(adux1050, + "adi,adux1050_irq_gpio_no", &value); + if (is_read) { + pdata->is_set_irq_gpio_no = true; + pdata->irq_gpio_no = value; + } + /*initialize the force calib variables*/ + is_read = read_value_from_device_tree(adux1050, + "adi,adux1050_proxy_enable", &value); + if (is_read) + pdata->proxy_enable = value; + is_read = read_value_from_device_tree(adux1050, + "adi,adux1050_allowed_proxy_time", &value); + if (is_read) + pdata->allowed_proxy_time = value; + is_read = read_value_from_device_tree(adux1050, + "adi,adux1050_max_proxy_count", &value); + if (is_read) + pdata->max_proxy_count = value; + is_read = read_value_from_device_tree(adux1050, + "adi,adux1050_intr_err", &value); + if (is_read) + pdata->intr_err = value; + } else { + pr_debug("%s - Usg local pltfm data\n", __func__); + } +#endif + + } else { + pr_debug("%s - Pltfm Data Found\n", __func__); + adux1050->pdata = dev->platform_data; + } + + adux1050->read = adux1050_i2c_read; + adux1050->write = adux1050_i2c_write; + adux1050->dev = dev; + mutex_init(&adux1050->mutex); + + /* check if the device is existing by reading device id of ADUX1050 */ + ret = adux1050_hw_detect(adux1050); + if (ret) + goto err_kzalloc_mem; + + i2c_set_clientdata(client, adux1050); + + INIT_WORK(&adux1050->work, adux1050_isr_work_fn); + INIT_WORK(&adux1050->calib_work, adux1050_calibration); + INIT_DELAYED_WORK(&adux1050->proxy_work, adux1050_proxy_work); + /* + * Allocate and register adux1050 input device + */ + input = input_allocate_device(); + if (!input) { + pr_err("%s: could not allocate input device\n", __func__); + ret = -ENOMEM; + goto err_kzalloc_mem; + } + + input->name = DEVICE_NAME; + set_bit(EV_MSC, input->evbit); + input_set_capability(input, EV_MSC, MSC_RAW); + + ret = input_register_device(input); + if (ret) { + pr_err("%s: could not input_register_device(input);\n", + __func__); + input_free_device(input); + goto err_kzalloc_mem; + } + adux1050->input = input; + input_set_drvdata(adux1050->input, adux1050); +#ifdef CONFIG_ADUX1050_POLL + /* Creation of Thread */ + adux1050->polling_task = kthread_create(adux1050_polling_thread_fn, + (void *)adux1050, "adux1050_kthread"); + if (ERR_PTR(-ENOMEM) == adux1050->polling_task) { + pr_err("%s : Thread Creation Failed\n", __func__); + goto err_free_irq; + } + if (adux1050->dev_enable == ADUX1050_ENABLE) { + pr_err("%s : before wake up\n", __func__); + wake_up_process(adux1050->polling_task); + pr_err("%s :after wake up\n", __func__); + } + adux1050->poll_delay = MIN_POLL_DELAY; + adux1050->int_pol = ACTIVE_LOW; +#else + adux1050->irq = client->irq; + if (!adux1050->pdata->irq_flags) + adux1050->pdata->irq_flags = IRQF_TRIGGER_FALLING; + + ADUX1050_DRIVER_DBG("%s - Before request_threaded_irq %d\n", + __func__, adux1050->irq); + ret = request_threaded_irq(adux1050->irq, NULL, adux1050_isr_thread, + IRQF_ONESHOT | adux1050->pdata->irq_flags, + DEVICE_NAME, adux1050); + + if (ret) { + pr_err("%s: irq %d Driver init Failed", __func__, + adux1050->irq); + goto err_free_irq; + } + disable_irq(adux1050->irq); + if (adux1050->pdata->irq_flags & IRQF_TRIGGER_RISING) + adux1050->int_pol = ACTIVE_HIGH; + else + adux1050->int_pol = ACTIVE_LOW; + + if (adux1050->pdata->is_set_irq_gpio_no) { + unsigned gpioNo = adux1050->pdata->irq_gpio_no; + + ADUX1050_DRIVER_DBG("%s: gpioNo %d\n", __func__, gpioNo); + ret = gpio_request(gpioNo, "aDuX1050"); + if (ret) { + pr_err("%s: gpio_request: %d\n", __func__, ret); + goto err_free_irq; + } + ret = gpio_direction_input(gpioNo); + if (ret) { + pr_err("%s: gpio_direction_input: %d\n", __func__, ret); + goto err_free_gpio; + } + } +#endif + ret = sysfs_create_group(&dev->kobj, &adux1050_attr_group); + if (ret) { + pr_err("%s: cound not register sensor sysfs\n", __func__); + goto err_sysfs_create_input; + } + + /* initialize and request sw/hw resources */ + adux1050_store_register_values(adux1050); + adux1050->dev_enable = ADUX1050_DISABLE; + adux1050->send_event = ADUX1050_ENABLE; + /*initialize the force calib variables*/ + adux1050->proxy_enable = adux1050->pdata->proxy_enable; + adux1050->allowed_proxy_time = adux1050->pdata->allowed_proxy_time; + adux1050->max_proxy_count = adux1050->pdata->max_proxy_count; + adux1050->user_intr_err = adux1050->pdata->intr_err; + adux1050->proxy_cancel = TRUE; +#ifdef CONFIG_ADUX1050_EVAL + adux1050->power_mode_flag = ADUX1050_DISABLE; +#endif + dev_dbg(adux1050->dev, "%s Completed\n", __func__); + return 0; + +err_sysfs_create_input: + sysfs_remove_group(&dev->kobj, &adux1050_attr_group); +#ifdef CONFIG_ADUX1050_POLL + kthread_stop(adux1050->polling_task); +#else + free_irq(adux1050->irq, adux1050); +#endif +err_free_gpio: + if (adux1050->pdata->is_set_irq_gpio_no) + gpio_free(adux1050->pdata->irq_gpio_no); +err_free_irq: + input_unregister_device(input); + +err_kzalloc_mem: + kfree(adux1050); + return ret; +} + +/* + * Removes the device. + * This is used to remove the device or the I2C client from the system + * @param client The client structure to be removed + * @return 0 on success + */ +static int adux1050_i2c_remove(struct i2c_client *client) +{ + struct adux1050_chip *adux1050 = i2c_get_clientdata(client); + u16 data = DISABLE_DEV_INT; + s16 err = 0; + + pr_debug("%s, Start\n", __func__); + if (adux1050 != NULL) { + adux1050->proxy_cancel = TRUE; + if (adux1050->dev_enable == ADUX1050_ENABLE) { + #ifdef CONFIG_ADUX1050_POLL + #else + disable_irq(adux1050->irq); + disable_irq_wake(adux1050->irq); + #endif + err = adux1050->write(adux1050->dev, INT_CTRL_REG, + &data, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C Write failed %d at %s\n", + err, __func__); + data = data | RESET_MASK; + /* Reset should be reviewed for nesesity */ + err = adux1050->write(adux1050->dev, CTRL_REG, + &data, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, "I2C Write failed %d at %s\n", + err, __func__); + } + sysfs_remove_group(&adux1050->dev->kobj, + &adux1050_attr_group); + #ifdef CONFIG_ADUX1050_POLL + kthread_stop(adux1050->polling_task); + #else + free_irq(adux1050->irq, adux1050); + #endif + cancel_work_sync(&adux1050->work); + cancel_work_sync(&adux1050->calib_work); + cancel_delayed_work(&adux1050->proxy_work); + adux1050->proxy_count = 0; + ADUX1050_DRIVER_DBG("%s -proxywork cancelled as i2c removed\n", + __func__); + input_unregister_device(adux1050->input); + pr_debug("device unregister completed & adux1050 device free called\n"); + kfree(adux1050->stg_info); /* BJ */ + pr_debug("free of stginfo complete & adux1050 free called\n"); + kfree(adux1050); + i2c_set_clientdata(client, NULL); + } + pr_debug("%s, END\n", __func__); + return 0; +} + +/* + * Used similar to driver remove function but used as a shutdown call back + * This is used to remove the device or the I2C client from the system + * @param client The client ID to be removed + * @return void + */ +void adux1050_shutdown(struct i2c_client *client) +{ + struct adux1050_chip *adux1050 = i2c_get_clientdata(client); + u16 data = DISABLE_DEV_INT; + s16 err = 0; + + pr_debug("%s, Start\n", __func__); + if (adux1050 != NULL) { + adux1050->proxy_cancel = TRUE; + if (adux1050->dev_enable == ADUX1050_ENABLE) { + #ifdef CONFIG_ADUX1050_POLL + #else + disable_irq(adux1050->irq); + disable_irq_wake(adux1050->irq); + #endif + err = adux1050->write(adux1050->dev, INT_CTRL_REG, + &data, DEF_WR); + if (err < DEF_WR) + dev_err(adux1050->dev, + "I2C Write failed %d at %s\n", + err, __func__); + } + sysfs_remove_group(&adux1050->dev->kobj, + &adux1050_attr_group); + #ifdef CONFIG_ADUX1050_POLL + kthread_stop(adux1050->polling_task); + #else + free_irq(adux1050->irq, adux1050); + #endif + cancel_work_sync(&adux1050->work); + cancel_work_sync(&adux1050->calib_work); + cancel_delayed_work(&adux1050->proxy_work); + adux1050->proxy_count = 0; + input_unregister_device(adux1050->input); + kfree(adux1050->stg_info); + kfree(adux1050); + i2c_set_clientdata(client, NULL); + } + pr_debug("%s, END\n", __func__); +} + +#ifdef CONFIG_ADUX1050_PM +/* + * Device suspend PM operation call back function + * @param dev Device structure for handling the power management. + * @return Zero on success + */ +static int adux1050_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adux1050_chip *adux1050 = i2c_get_clientdata(client); + s16 err = 0; + + dev_dbg(adux1050->dev, "%s,check (%d)\n", + __func__, adux1050->dev_enable); + /* To put device in STANDBY Mode */ + if (adux1050->dev_enable == ADUX1050_ENABLE) { + err = adux1050_disable(adux1050); + if (err < ZERO_VAL) + dev_err(adux1050->dev, "I2C RD/WR failed %d at %s\n", + err, __func__); + adux1050->proxy_cancel = TRUE; + cancel_delayed_work(&adux1050->proxy_work); + + adux1050->dev_enable = ADUX1050_SUSPEND; + } + return 0; +} + +/* + * Device resume PM operation call back function + * @param dev Device structure for handling the power management. + * @return Zero on success + */ +static int adux1050_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adux1050_chip *adux1050 = i2c_get_clientdata(client); + s16 err = 0; + + dev_dbg(adux1050->dev, "%s,check (%d)\n", + __func__, adux1050->dev_enable); + + if (adux1050->dev_enable == ADUX1050_SUSPEND) { + adux1050->dev_enable = ADUX1050_ENABLE; + err = adux1050_enable(adux1050); + if (err < ZERO_VAL) + dev_err(adux1050->dev, "I2C RD/WR failed %d at %s\n", + err, __func__); + if (adux1050->proxy_enable) { + adux1050->proxy_cancel = FALSE; + schedule_delayed_work(&adux1050->proxy_work, + msecs_to_jiffies + (PROXY_TIME * adux1050->allowed_proxy_time)); + } +#ifdef CONFIG_ADUX1050_POLL + wake_up_process(adux1050->polling_task); +#endif + } + + return 0; +} +#endif + +/* + * Device ID table for the ADUX1050 driver + */ +static const struct of_device_id adux1050_of_id[] = { + {.compatible = "adi,adux1050"}, + {} +}; + +/* + * Device ID table for the ADUX1050 driver + */ +static const struct i2c_device_id adux1050_id[] = { + { "adux1050", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, adux1050_id); + +#ifdef CONFIG_ADUX1050_PM +/* + * The file Operation table for power + */ +static const struct dev_pm_ops adux1050_dev_pm_ops = { + .suspend = adux1050_i2c_suspend, + .resume = adux1050_i2c_resume, +}; +#endif + +/* + * I2C driver structure containing the function callbacks, + * driver name and ID tables + */ +struct i2c_driver adux1050_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_ADUX1050_PM + .pm = &adux1050_dev_pm_ops, +#endif +#ifdef CONFIG_OF + .of_match_table = adux1050_of_id, +#endif + }, + .probe = adux1050_probe, + .shutdown = adux1050_shutdown, + .remove = adux1050_i2c_remove, + .id_table = adux1050_id, +}; + +/* + * This is an init function called during module insertion. + * calls in turn i2c driver probe function + */ +static int adux1050_module_init(void) +{ + pr_debug("%s,START\n", __func__); + return i2c_add_driver(&adux1050_i2c_driver); +} +module_init(adux1050_module_init); + +/* + * This is an exit function called during module removal -- + * calls in turn i2c driver delete function + */ +static void adux1050_module_exit(void) +{ + i2c_del_driver(&adux1050_i2c_driver); +} + +module_exit(adux1050_module_exit); +MODULE_DESCRIPTION("Analog Devices ADUX1050 Driver"); +MODULE_AUTHOR("Analog Devices"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c index 82fefdff366a236df578318e5345343850459666..8f08fa4081726b9289d41619438c4d7a4dc71f1e 100644 --- a/drivers/input/misc/keychord.c +++ b/drivers/input/misc/keychord.c @@ -370,8 +370,10 @@ static ssize_t keychord_write(struct file *file, const char __user *buffer, ret = input_register_handler(&kdev->input_handler); if (ret) { - kfree(keychords); + spin_lock_irqsave(&kdev->lock, flags); + kfree(kdev->keychords); kdev->keychords = 0; + spin_unlock_irqrestore(&kdev->lock, flags); keychord_write_unlock(kdev); return ret; } diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c index 339f94c072f478dfeb3714e6704aed390a15cc8a..d85ccab2100010679c05415de4c5e86c8b90c3a1 100644 --- a/drivers/input/misc/qpnp-power-on.c +++ b/drivers/input/misc/qpnp-power-on.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -137,6 +142,8 @@ #define QPNP_PON_UVLO_DLOAD_EN BIT(7) #define QPNP_PON_SMPL_EN BIT(7) +#define QPNP_PON_DVDD_HARD_RESET_SET 0x08 + /* Ranges */ #define QPNP_PON_S1_TIMER_MAX 10256 #define QPNP_PON_S2_TIMER_MAX 2000 @@ -150,6 +157,8 @@ #define QPNP_PON_GEN2_MAX_DBC_US (USEC_PER_SEC / 4) #define QPNP_KEY_STATUS_DELAY msecs_to_jiffies(250) +#define QPNP_RESIN_STATUS_SHORT_DELAY msecs_to_jiffies(1500) +#define QPNP_RESIN_STATUS_LONG_DELAY msecs_to_jiffies(9500) #define QPNP_PON_BUFFER_SIZE 9 @@ -202,6 +211,7 @@ struct qpnp_pon { struct pon_regulator *pon_reg_cfg; struct list_head list; struct delayed_work bark_work; + struct delayed_work resin_status_work; struct dentry *debugfs; int pon_trigger_reason; int pon_power_off_reason; @@ -229,6 +239,7 @@ module_param_named( ); static struct qpnp_pon *sys_reset_dev; +static struct qpnp_pon *sys_reset_dev_2; static DEFINE_SPINLOCK(spon_list_slock); static LIST_HEAD(spon_dev_list); @@ -298,6 +309,8 @@ static const char * const qpnp_poff_reason[] = { [39] = "Triggered from S3_RESET_KPDPWR_ANDOR_RESIN (power key and/or reset line)", }; +static unsigned long resin_status_delay; + static int qpnp_pon_masked_write(struct qpnp_pon *pon, u16 addr, u8 mask, u8 val) { @@ -853,6 +866,13 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) pon->kpdpwr_last_release_time = ktime_get(); } + if (cfg->pon_type == PON_RESIN) { + if (key_status) + schedule_delayed_work(&pon->resin_status_work, resin_status_delay); + else + cancel_delayed_work_sync(&pon->resin_status_work); + } + /* * simulate press event in case release event occurred * without a press event @@ -968,6 +988,11 @@ static irqreturn_t qpnp_pmic_wd_bark_irq(int irq, void *_pon) return IRQ_HANDLED; } +static void resin_status_work_func(struct work_struct *work) +{ + pr_err("$$$$ Stage 2 reset (RESIN) $$$$\n"); +} + static void bark_work_func(struct work_struct *work) { int rc; @@ -1803,6 +1828,180 @@ static struct kernel_param_ops smpl_en_ops = { module_param_cb(smpl_en, &smpl_en_ops, &smpl_en, 0644); +static bool resin_n_reset; + +static int qpnp_pon_debugfs_resin_n_get(char *buf, + const struct kernel_param *kp) +{ + struct qpnp_pon *pon = sys_reset_dev; + int rc = 0; + uint reg; + + if (!pon) + return -ENODEV; + + rc = regmap_read(pon->regmap,QPNP_PON_RESIN_S2_CNTL(pon), ®); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read addr=%x, rc(%d)\n", + QPNP_PON_RESIN_S2_CNTL(pon), rc); + return rc; + } + pr_debug("ctrl:%p add:%x sid:%u reg:0x%02x\n", + to_spmi_device(pon->pdev->dev.parent)->ctrl, QPNP_PON_RESIN_S2_CNTL(pon), + to_spmi_device(pon->pdev->dev.parent)->usid, reg); + + return snprintf(buf, PAGE_SIZE, "Address 0x846:0x%02x", reg); +} + +static int qpnp_pon_debugfs_resin_n_set(const char *val, + const struct kernel_param *kp) +{ + struct qpnp_pon *pon = sys_reset_dev; + int rc = 0; + u8 reg = PON_POWER_OFF_WARM_RESET; + + if (!pon) + return -ENODEV; + + rc = param_set_bool(val, kp); + if (rc) { + pr_err("Unable to set bms_reset: %d\n", rc); + return rc; + } + + if (!*(bool *)kp->arg) + reg = QPNP_PON_DVDD_HARD_RESET_SET; + + rc = regmap_write(pon->regmap,QPNP_PON_RESIN_S2_CNTL(pon), reg); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to write to addr=%hx, rc(%d)\n", + QPNP_PON_RESIN_S2_CNTL(pon), rc); + return rc; + } + + return 0; +} + +static struct kernel_param_ops resin_n_ops = { + .set = qpnp_pon_debugfs_resin_n_set, + .get = qpnp_pon_debugfs_resin_n_get, +}; + +module_param_cb(resin_n_reset, &resin_n_ops, &resin_n_reset, 0644); + +static bool s3_timer; + +static int qpnp_pon_s3_timer_get(struct qpnp_pon *pon, uint *reg) +{ + int rc = 0; + rc = regmap_read(pon->regmap, QPNP_PON_S3_DBC_CTL(pon), reg); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read PMIC@SID%d addr=%x, rc(%d)\n", + to_spmi_device(pon->pdev->dev.parent)->usid, + QPNP_PON_S3_DBC_CTL(pon), rc); + return rc; + } + pr_debug("PMIC@SID%d ctrl:%p add:%x reg:0x%02x\n", + to_spmi_device(pon->pdev->dev.parent)->usid, + to_spmi_device(pon->pdev->dev.parent)->ctrl, + QPNP_PON_S3_DBC_CTL(pon), *reg); + return rc; +} +static int qpnp_pon_debugfs_s3_timer_get(char *buf, + const struct kernel_param *kp) +{ + struct qpnp_pon *pon = sys_reset_dev; + struct qpnp_pon *pon2 = sys_reset_dev_2; + int rc = 0; + uint reg, reg2; + + if (!pon) + return -ENODEV; + + if (!pon2) + return -ENODEV; + + rc = qpnp_pon_s3_timer_get(pon, ®); + if (rc) + return rc; + + rc = qpnp_pon_s3_timer_get(pon2, ®2); + if (rc) + return rc; + + return snprintf(buf, PAGE_SIZE, "PMIC@SID%d:0x%02x PMIC@SID%d:0x%02x", + to_spmi_device(pon->pdev->dev.parent)->usid, reg, + to_spmi_device(pon2->pdev->dev.parent)->usid, reg2); +} + +static int qpnp_pon_s3_timer_set(struct qpnp_pon *pon, unsigned char val) +{ + int rc = 0; + + /* s3 debounce is SEC_ACCESS register */ + rc = qpnp_pon_masked_write(pon, QPNP_PON_SEC_ACCESS(pon), + 0xFF, QPNP_PON_SEC_UNLOCK); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to do SEC_ACCESS rc:%d\n", + rc); + return rc; + } + + rc = qpnp_pon_masked_write(pon, QPNP_PON_S3_DBC_CTL(pon), + QPNP_PON_S3_DBC_DELAY_MASK, val); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to set S3 debounce rc:%d\n", + rc); + return rc; + } + return 0; +} + +static int qpnp_pon_debugfs_s3_timer_set(const char *val, + const struct kernel_param *kp) +{ + struct qpnp_pon *pon = sys_reset_dev; + struct qpnp_pon *pon2 = sys_reset_dev_2; + int rc = 0; + + if (!pon) + return -ENODEV; + + if (!pon2) + return -ENODEV; + + rc = param_set_byte(val, kp); + + if (rc) { + pr_err("Unable to set bms_reset: %d\n", rc); + return rc; + } + + rc = qpnp_pon_s3_timer_set(pon, *(unsigned char *)kp->arg); + if (rc) { + pr_err("Unable to set s3_timer for pm: %d\n", rc); + return rc; + } + + rc = qpnp_pon_s3_timer_set(pon2, *(unsigned char *)kp->arg); + if (rc) { + pr_err("Unable to set s3_timer for pmi: %d\n", rc); + return rc; + } + + return 0; +} + +static struct kernel_param_ops s3_timer_ops = { + .set = qpnp_pon_debugfs_s3_timer_set, + .get = qpnp_pon_debugfs_s3_timer_get, +}; + +module_param_cb(s3_timer, &s3_timer_ops, &s3_timer, 0644); + static bool dload_on_uvlo; static int qpnp_pon_debugfs_uvlo_dload_get(char *buf, @@ -2001,6 +2200,7 @@ static int qpnp_pon_probe(struct platform_device *pdev) u8 s3_src_reg; unsigned long flags; uint temp = 0; + uint pon_resin_timer; pon = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_pon), GFP_KERNEL); if (!pon) @@ -2064,6 +2264,25 @@ static int qpnp_pon_probe(struct platform_device *pdev) sizeof(struct qpnp_pon_config) * pon->num_pon_config, GFP_KERNEL); + /* RESIN S1 TIMER */ + rc = regmap_read(pon->regmap, QPNP_PON_RESIN_S1_TIMER(pon), &pon_resin_timer); + if (rc) { + dev_err(&pdev->dev, "Unable to read RESIN_S1_TIMER\n"); + return rc; + } + + dev_err(&pdev->dev, "pon_resin_timer 0x%x\n", pon_resin_timer); + if (pon->num_pon_config) { + if (pon_resin_timer == 0xB) + resin_status_delay = QPNP_RESIN_STATUS_SHORT_DELAY; + else if (pon_resin_timer == 0xF) + resin_status_delay = QPNP_RESIN_STATUS_LONG_DELAY; + else { + dev_err(&pdev->dev, "invalid RESIN_S1_TIMER value\n"); + resin_status_delay = QPNP_RESIN_STATUS_LONG_DELAY; + } + } + /* Read PON_PERPH_SUBTYPE register to get PON type */ rc = regmap_read(pon->regmap, QPNP_PON_PERPH_SUBTYPE(pon), @@ -2133,6 +2352,8 @@ static int qpnp_pon_probe(struct platform_device *pdev) "PMIC@SID%d Power-on reason: Unknown and '%s' boot\n", to_spmi_device(pon->pdev->dev.parent)->usid, cold_boot ? "cold" : "warm"); + if (to_spmi_device(pon->pdev->dev.parent)->usid == 2) + sys_reset_dev_2 = pon; } else { pon->pon_trigger_reason = index; dev_info(&pon->pdev->dev, @@ -2140,6 +2361,8 @@ static int qpnp_pon_probe(struct platform_device *pdev) to_spmi_device(pon->pdev->dev.parent)->usid, qpnp_pon_reason[index], cold_boot ? "cold" : "warm"); + if (to_spmi_device(pon->pdev->dev.parent)->usid == 2) + sys_reset_dev_2 = pon; } /* POFF reason */ @@ -2253,6 +2476,7 @@ static int qpnp_pon_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, pon); INIT_DELAYED_WORK(&pon->bark_work, bark_work_func); + INIT_DELAYED_WORK(&pon->resin_status_work, resin_status_work_func); /* register the PON configurations */ rc = qpnp_pon_config_init(pon); @@ -2360,6 +2584,18 @@ static int qpnp_pon_probe(struct platform_device *pdev) "qcom,store-hard-reset-reason"); qpnp_pon_debugfs_init(pdev); + + if (pon == sys_reset_dev) { + rc = qpnp_pon_reset_config(pon, PON_POWER_OFF_WARM_RESET); + if (rc) { + dev_err(&pon->pdev->dev, + "Error configuring primary PON rc: %d\n", rc); + return rc; + } + + dev_info(&pon->pdev->dev, "Configured primary PON for warm reset\n"); + } + return 0; } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 2d564aabbc74ac77711553ce6c746bd72d9759b5..115b188891d30d110ffddc4ce8dd8feafac366ce 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -633,6 +633,26 @@ config TOUCHSCREEN_MIGOR To compile this driver as a module, choose M here: the module will be called migor_ts. +config TOUCHSCREEN_CLEARPAD + tristate "Synaptics ClearPad" + default n + help + Say Y here to enable Synaptics ClearPad touchscreen. + +config TOUCHSCREEN_CLEARPAD_I2C + tristate "Synaptics ClearPad I2C" + depends on I2C && TOUCHSCREEN_CLEARPAD + default n + help + Say Y here to enable Synaptics ClearPad I2C touchscreen. + +config TOUCHSCREEN_CLEARPAD_RMI_DEV + tristate "Synaptics ClearPad RMI Dev" + depends on GPIO_SYSFS && TOUCHSCREEN_CLEARPAD && TOUCHSCREEN_CLEARPAD_I2C + default n + help + Say Y here to enable Synaptics ClearPad RMI dev. + config TOUCHSCREEN_TOUCHRIGHT tristate "Touchright serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f5be6fc1975145be8e14f2ef73723757f35b4380..4555e32b3e3d937384f844c555deed3a06b63243 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -19,6 +19,9 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS) += atmel_maxtouch_ts.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o +obj-$(CONFIG_TOUCHSCREEN_CLEARPAD) += clearpad_core.o +obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_I2C) += clearpad_i2c.o +obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV) += clearpad_rmi_dev.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o diff --git a/drivers/input/touchscreen/clearpad_core.c b/drivers/input/touchscreen/clearpad_core.c new file mode 100644 index 0000000000000000000000000000000000000000..10c75f5f54bcb9d3c8b519241a6430d635932d92 --- /dev/null +++ b/drivers/input/touchscreen/clearpad_core.c @@ -0,0 +1,9646 @@ +/* linux/drivers/input/touchscreen/clearpad_core.c + * + * Author: Courtney Cavin + * Yusuke Yoshimura + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DEBUG_FS +#include +#include +#endif +#include +#include +#include +#include +#ifdef CONFIG_FB +#include +#include +#endif +#ifdef CONFIG_ARM +#include +#endif + +#define SYN_CLEARPAD_VENDOR 0x1 +#define SYN_MAX_N_FINGERS 10 +#define SYN_DEVICE_STATUS 0x13 +#define SYN_MAX_CTRL_VALUE 80 +#define SYN_MAX_Z_VALUE 255 +#define SYN_MAX_W_VALUE 15 +#define SYN_PDT_START 0xEF +#define SYN_SIZE_OF_FD 6 +#define SYN_PAGE_SELECT_OFFSET 0xFF +#define SYN_SUPPORTED_PAGE_NUM 0x04 +#define SYN_MAX_INTERRUPT_SOURCE_COUNT 0x7 +#define SYN_STRING_LENGTH 128 +#define SYN_RETRY_NUM_OF_INITIAL_CHECK 2 +#define SYN_RETRY_NUM_OF_PROBE 3 +#define SYN_RETRY_NUM_OF_POST_PROBE 6 +#define SYN_RETRY_NUM_OF_RECOVERY 3 +#define SYN_RETRY_NUM_OF_RESET 5 +#define SYN_RETRY_NUM 3 +#define SYN_RETRY_DEFAULT_TIME_FOR_NOISE_DET 200 +#define SYN_WAIT_TIME_OF_RESET 20 +#define SYN_WAIT_TIME_AFTER_REGISTER_ACCESS 20 +#define SYN_CALIBRATION_SETUP_TIME 210 +#define SYN_CALIBRATION_WAIT 500 +#define SYN_CALIBRATION_WAIT_MS (15 * 1000) +#define SYN_CALIBRATION_EW_WAIT_MS (10 * 1000) +#define SYN_CALIBRATION_BEFORE_HWRESET_WAIT 20 +#define SYN_SINGLE_TRANSACTION_SIZE 8 +#define SYN_FP_KEY_OFFSET 6 +#define SYN_PAYLOAD_LENGTH 1 +#define SYN_DEVICE_INFO_SIZE 5 +#define SYN_DEVICE_BL_INFO_SIZE 2 +#define SYN_STAMINA_MODE_SUPPORTED_MASK 0x80000000 +#define SYN_STAMINA_REPROTRATE_SUPPORTED_MASK 0x00000001 +#define SYN_STAMINA_DOZE_HOLDOFF_SUPPORTED_MASK 0x00000002 +#define SYN_WAIT_TIME_AFTER_CHANGE_REPORTRATE 34 +#define HWTEST_SIZE_OF_COMMAND_PREFIX 2 +#define HWTEST_SIZE_OF_ONE_DIMENSION 1 +#define HWTEST_SIZE_OF_ONE_HIGH_RX 3 +#define HWTEST_SIZE_OF_TX_TO_TX_SHORT(x) (((x) + 7) / 8) +#define HWTEST_SIZE_OF_TRX_SHORT_2 7 +#define HWTEST_SIZE_OF_TRX_SHORT_2_TAB 13 +#define HWTEST_MAX_DIGITS 10 +#define HWLOG_BUF_SIZE (PAGE_SIZE * 10) +#define SYN_WAKEUP_GESTURE "wakeup_gesture" +#define WAKE_LOCK_ID "touchctrl" +#define INDENT " " +#define FLASH_DATA_CONFIGURATION_AREA_SELECT_PERM 1 +#define FLASH_DATA_CONFIGURATION_AREA_SELECT_SHIFT 13 +#define FLASH_READ_CONFIGURATION_SIZE 21 +#define FLASH_READ_FIRMWARE_INFO_SIZE_BL6X 4 +#define FLASH_READ_FIRMWARE_INFO_SIZE_BL7X 5 + +/* + * Register Offset + */ + +/* F01_RMI_CTRL05: Doze Holdoff */ +#define SYN_COVER_RECTANGLE_OFFSET 0x01 +#define SYN_EXTERNAL_REPORT_RATE_OFFSET 0x06 + +#define SYN_PAGE_ADDR(page, addr) ((page) << 8 | (addr)) +#define SYN_F_ADDR(th, func, type, reg) ((th)->pdt[func].base[type] + (reg)) +#define SYN_F_PAGE(th, func) ((th)->pdt[func].page) +#define SYN_F_PAGE_ADDR(th, func, type, reg) \ + SYN_PAGE_ADDR(SYN_F_PAGE(th, func), SYN_F_ADDR(th, func, type, reg)) +#define SYNSET(...) __VA_ARGS__ +#define SYNI(th, func, type, reg) \ + SYNSET(th, SYN_F_PAGE_ADDR(th, func, type, reg)) +#define SYNFUNC(x) SYN_##x +#define SYNTYPE(y) SYN_TYPE_##y +#define SYNF(th, func, type, reg) \ + SYNI(th, SYNFUNC(func), SYNTYPE(type), reg) +/* only F12 can use SYNA (e.g. F12_2D_CTRL) */ +#define SYNA(th, func, type, regid) \ + SYNSET(th, SYN_F_PAGE_ADDR(th, SYNFUNC(func), SYNTYPE(type), \ + ((th)->pdt[SYNFUNC(func)].offset[SYNTYPE(type)][regid]))) + +#ifdef CONFIG_DEBUG_FS +#define DEBUG_COMMAND(C0, C1) (((int)C0 << 8) + (int)C1) +#define DEBUG_ONE_BYTE_HEX 2 +#endif + +#define BIT_DEF(name, mask, shift, ...) \ +enum BIT_DEF_##name##_e { \ +name##_MASK = mask, name##_SHIFT = shift, ## __VA_ARGS__ \ +} +#define BIT_CLEAR(target, name) \ + ({ target = ((target) & ~(name##_MASK)); }) +#define BIT_SET(target, name, value) \ + ({ BIT_CLEAR(target, name); target |= ((value) << name##_SHIFT); }) +#define BIT_GET(target, name) \ + ({ (target & name##_MASK) >> name##_SHIFT; }) + +/* + * Register Bit Fields + */ + +/* F01_RMI_CMD00: Device Command */ +BIT_DEF(DEVICE_COMMAND_RESET, 0x01, 0); + +/* F01_RMI_CTRL00: Device Control */ +BIT_DEF(DEVICE_CONTROL_SLEEP_MODE, 0x03, 0, + DEVICE_CONTROL_SLEEP_MODE_NORMAL_OPERATION = 0x00, + DEVICE_CONTROL_SLEEP_MODE_SENSOR_SLEEP = 0x01, + DEVICE_CONTROL_SLEEP_MODE_SWR = 0x02); +BIT_DEF(DEVICE_CONTROL_NO_SLEEP, 0x04, 2); +BIT_DEF(DEVICE_CONTROL_CHARGER_CONNECTED, 0x20, 5); +BIT_DEF(DEVICE_CONTROL_REPORT_RATE, 0x40, 6); +BIT_DEF(DEVICE_CONTROL_CONFIGURED, 0x80, 7); + +/* F01_RMI_CTRL01_00: Interrupt Enable 0 */ +BIT_DEF(INTERRUPT_ENABLE_0, 0x3f, 0, + INTERRUPT_ENABLE_0_ENABLE_ALL = 0x3f, + INTERRUPT_ENABLE_0_DISABLE_ALL = 0x00); +BIT_DEF(INTERRUPT_ENABLE_0_FLASH, 0x01, 0); +BIT_DEF(INTERRUPT_ENABLE_0_STATUS, 0x02, 1); +BIT_DEF(INTERRUPT_ENABLE_0_ABS0, 0x04, 2); +BIT_DEF(INTERRUPT_ENALBE_0_ANALOG, 0x08, 3); +BIT_DEF(INTERRUPT_ENALBE_0_BUTTON, 0x10, 4); +BIT_DEF(INTERRUPT_ENABLE_0_SENSOR, 0x20, 5); +BIT_DEF(INTERRUPT_ENABLE_0_PRINT, 0x40, 6); + +/* F01_RMI_CTRL18: Device Control 1 */ +BIT_DEF(DEVICE_CONTROL_1_GSM_ENABLE, 0x01, 0); + +/* F01_RMI_DATA00: Device Status */ +BIT_DEF(DEVICE_STATUS_CODE, 0x0f, 0, + DEVICE_STATUS_CODE_RESET_OCCURRED = 0x01, + DEVICE_STATUS_CODE_INVALID_CONFIGURATION = 0x02, + DEVICE_STATUS_CODE_DEVICE_FAILURE = 0x03, + DEVICE_STATUS_CODE_CONFIGURATION_CRC_FAILURE = 0x04, + DEVICE_STATUS_CODE_FIRMWARE_CRC_FAILURE = 0x05, + DEVICE_STATUS_CODE_CRC_IN_PROGRESS = 0x06, + DEVICE_STATUS_CODE_GUEST_CRC_FAILURE = 0x07, + DEVICE_STATUS_CODE_EXTERNAL_AFE_FAILURE = 0x08, + DEVICE_STATUS_CODE_DISPLAY_FAILURE = 0x09); +BIT_DEF(DEVICE_STATUS_FLASH_PROG, 0x40, 6); +BIT_DEF(DEVICE_STATUS_UNCONFIGURED, 0x80, 7); + +/* F12_2D_QUERY00: General */ +BIT_DEF(GENERAL_HAS_REGISTER_DESCRIPTOR, 0x01, 0); +/* F12_2D_QUERY10_00: Supported Object Types */ +BIT_DEF(SUPPORTED_OBJECT_TYPES_HAS_GLOVED_FINGER, 0x20, 5); +BIT_DEF(SUPPORTED_OBJECT_TYPES_HAS_NARROW_OBJECT, 0x40, 6); +BIT_DEF(SUPPORTED_OBJECT_TYPES_HAS_HAND_EDGE, 0x80, 7); +/* F12_2D_QUERY10_01: Supported Object Types 2 */ +BIT_DEF(SUPPORTED_OBJECT_TYPES_HAS_COVER, 0x01, 0); +BIT_DEF(SUPPORTED_OBJECT_TYPES_HAS_STYLUS, 0x02, 1); +BIT_DEF(SUPPORTED_OBJECT_TYPES_HAS_ERASER, 0x04, 2); +BIT_DEF(SUPPORTED_OBJECT_TYPES_HAS_SMALL_OBJECT, 0x08, 3); +/* F12_2D_CTRL20_01: Report Flags */ +BIT_DEF(REPORT_FLAGS_REPORT_ALWAYS, 0x01, 0); +BIT_DEF(REPORT_FLAGS_REPORT_WAKEUP_GESTURE_ONLY, 0x02, 1); +BIT_DEF(REPORT_FLAGS_ENABLE_DRIBBLE, 0x04, 2); +/* F12_2D_CTRL23_00: Object Report Enable */ +BIT_DEF(OBJECT_REPORT_ENABLE_FINGER, 0x01, 0); +BIT_DEF(OBJECT_REPORT_ENABLE_STYLUS, 0x02, 1); +BIT_DEF(OBJECT_REPORT_ENABLE_PALM, 0x04, 2); +BIT_DEF(OBJECT_REPORT_ENABLE_UNCLASSIFIED_OBJECT, 0x08, 3); +BIT_DEF(OBJECT_REPORT_ENABLE_HOVERING_FINGER, 0x10, 4); +BIT_DEF(OBJECT_REPORT_ENABLE_GLOVED_FINGER, 0x20, 5); +BIT_DEF(OBJECT_REPORT_ENABLE_NARROW_OBJECT, 0x40, 6); +BIT_DEF(OBJECT_REPORT_ENABLE_HAND_EDGE, 0x80, 7); +/* F12_2D_CTRL23_02: Report As Finger */ +BIT_DEF(REPORT_AS_FINGER_STYLUS, 0x02, 1); +BIT_DEF(REPORT_AS_FINGER_PALM, 0x04, 2); +BIT_DEF(REPORT_AS_FINGER_UNCLASSIFIED_OBJECT, 0x08, 3); +BIT_DEF(REPORT_AS_FINGER_GLOVED_FINGER, 0x20, 5); +BIT_DEF(REPORT_AS_FINGER_NARROW_OBJECT, 0x40, 6); +BIT_DEF(REPORT_AS_FINGER_HAND_EDGE, 0x80, 7); +/* F12_2D_CTRL26: Feature Enable */ +BIT_DEF(FEATURE_ENABLE_ENABLE_GLOVED_FINGER_DETECTION, 0x01, 0); +BIT_DEF(FEATURE_ENABLE_ENABLE_CLOSED_COVER_DETECTION, 0x02, 1); +/* F12_2D_CTRL27_00: Wakeup Gesture Enable */ +BIT_DEF(WAKEUP_GESTURE_ENABLE_DOUBLE_TAP, 0x01, 0); +BIT_DEF(WAKEUP_GESTURE_ENABLE_SWIPE, 0x02, 1); +BIT_DEF(WAKEUP_GESTURE_ENABLE_TAP_AND_HOLD, 0x04, 2); +BIT_DEF(WAKEUP_GESTURE_ENABLE_CIRCLE, 0x08, 3); +BIT_DEF(WAKEUP_GESTURE_ENABLE_TRIANGLE, 0x10, 4); +BIT_DEF(WAKEUP_GESTURE_ENABLE_VEE, 0x20, 5); +BIT_DEF(WAKEUP_GESTURE_ENABLE_UNICODE, 0x40, 6); +/* F12_2D_CTRL33_00: Multi-Finger Moisture General */ +BIT_DEF(ENABLE_MULTIFINGER_MOISTURE, 0x01, 0); + +/* F34_FLASH_DATA00: Status */ +BIT_DEF(STATUS_FLASH_STATUS, 0x1F, 0, + STATUS_FLASH_STATUS_SUCCESS = 0x00, + STATUS_FLASH_STATUS_DEVICE_NOT_IN_BOOTLOADER_MODE = 0x01, + STATUS_FLASH_STATUS_INVALID_PARTITION = 0x02, + STATUS_FLASH_STATUS_INVALID_COMMAND = 0x03, + STATUS_FLASH_STATUS_INVALID_BLOCK_OFFSET = 0x04, + STATUS_FLASH_STATUS_INVALID_TRANSFER = 0x05, + STATUS_FLASH_STATUS_NOT_ERASED = 0x06, + STATUS_FLASH_STATUS_FLASH_PROGRAMMING_KEY_INCORRECT = 0x07, + STATUS_FLASH_STATUS_BAD_PARTITION_TABLE = 0x08, + STATUS_FLASH_STATUS_CHECKSUM_FAILED = 0x09, + STATUS_FLASH_STATUS_FLASH_HARDWARE_FAILURE = 0x1F); +BIT_DEF(STATUS_DEVICE_CONFIG_STATUS, 0x60, 5); +BIT_DEF(STATUS_BL_MODE, 0x80, 7); +/* F34_FLASH_DATA02: Flash Control */ +BIT_DEF(FLASH_CONTROL, 0x3F, 0, + FLASH_CONTROL_WRITE_FIRMWARE_BLOCK = 0x02, + FLASH_CONTROL_ERASE_ALL = 0x03, + FLASH_CONTROL_READ_CONFIGURATION_BLOCK = 0x05, + FLASH_CONTROL_WRITE_CONFIGURATION_BLOCK = 0x06, + FLASH_CONTROL_ERASE_CONFIGURATION = 0x07, + FLASH_CONTROL_ENABLE_FLASH_PROGRAMMING = 0x0F); +/* F34_FLASH_DATA03: Flash Status */ +BIT_DEF(FLASH_STATUS_PROGRAM_ENABLED, 0x80, 7); + +/* F51_CUSTOM_CTRL05.00: Cover */ +BIT_DEF(COVER_ENABLE, 0x01, 0); +BIT_DEF(COVER_REPORT_FINGER, 0x02, 1); + +/* F54_ANALOG_CMD00: Analog Command */ +BIT_DEF(ANALOG_COMMAND_GET_REPORT, 0x01, 0); +BIT_DEF(ANALOG_COMMAND_FORCE_CALIBRATION, 0x02, 1); +BIT_DEF(ANALOG_COMMAND_FORCE_UPDATE, 0x04, 2); + +/* F54_ANALOG_CTRL41: Multi Metric Noise Mitigation Control */ +BIT_DEF(MULTIMETRIC_NOISE_CTRL_NO_SIGNAL_CLARITY, 0x01, 0); +/* F54_ANALOG_CTRL57: 0D CBC Settings */ +BIT_DEF(CBC_SETTINGS_XMTR_CARRIER_SELECT, 0x10, 4); +/* F54_ANALOG_CTRL88: Analog Control 1 */ +BIT_DEF(ANALOG_CONTROL_1_CBC_XMTR_CARRIER_SELECT, 0x20, 5); +/* F54_ANALOG_CTRL109: General Control */ +BIT_DEF(BASELINE_CORRECTION_MODE, 0x03, 0); +/* F54_ANALOG_CTRL113: General Control */ +BIT_DEF(DISABLE_HYBRID_BASELINE, 0x2F, 5); +/* F54_ANALOG_CTRL147: General Control */ +BIT_DEF(DISABLE_HYBRID_CBC_AUTO_CORRECTION, 0x02, 1); +/* F54_ANALOG_CTRL149: Trans CBC 2 */ +BIT_DEF(TRANS_CBC_2_TRANS_CBC_GLOBAL_CAP, 0x01, 0); +/* F54_ANALOG_CTRL188: Start Calibration or Production Test */ +BIT_DEF(START_CAL_PROD_TEST_START_CALIBRATION, 0x01, 0); +BIT_DEF(START_CAL_PROD_TEST_START_IS_CALIBRATION, 0x02, 1); +BIT_DEF(START_CAL_PROD_TEST_SET_FREQUENCY, 0x0C, 2); +BIT_DEF(START_CAL_PROD_TEST_START_PROD_TEST, 0x10, 4); +BIT_DEF(START_CAL_PROD_TEST_START_SHORT_TEST_CAL, 0x60, 5); +/* F54_ANALOG_CTRL214: General Control */ +BIT_DEF(ENABLE_HYBRID_CHARGER_NOISE_MITIGATION, 0x01, 0); + +/* F54_ANALOG_DATA31: Calibration State */ +BIT_DEF(CALIBRATION_STATE_IS_CALIBRATION_CRC, 0x01, 0); +BIT_DEF(CALIBRATION_STATE_CALIBRATION_CRC, 0x02, 1); + +#define MAX_USLEEP_RANGE_IN_MS 20 + +#define LOGx(FUNC, this, X, ...) \ + FUNC(&this->pdev->dev, "(%s:%d) " X, \ + __func__, __LINE__, ## __VA_ARGS__) +#define LOGD(this, X, ...) LOGx(dev_dbg, this, X, ## __VA_ARGS__) +#define LOGI(this, X, ...) LOGx(dev_info, this, X, ## __VA_ARGS__) +#define LOGW(this, X, ...) LOGx(dev_warn, this, X, ## __VA_ARGS__) +#define LOGE(this, X, ...) LOGx(dev_err, this, X, ## __VA_ARGS__) +#define DEBUG_FLAG(this, NAME) ({ \ + bool debug_flag = false; \ + dev_dbg(&this->pdev->dev, NAME " (%d)\n", (debug_flag = true)); \ + dev_info(&this->pdev->dev, "DEBUG_FLAG " NAME " = %s", \ + debug_flag ? "true" : "false"); \ + debug_flag; \ +}) + +#define LOG_STAT(this, X, ...) LOGD(this, "stat: " X, ## __VA_ARGS__) +#define LOG_EVENT(this, X, ...) LOGD(this, "event: " X, ## __VA_ARGS__) +#define LOG_CHECK(this, X, ...) LOGD(this, "check: " X, ## __VA_ARGS__) +#define LOG_VERBOSE(this, X, ...) LOGD(this, "verbose: " X, ## __VA_ARGS__) + +#ifdef CONFIG_DEBUG_FS +#define HWLOG(this, format, ...) \ + clearpad_debug_hwtest_log(this, format, ## __VA_ARGS__) +#else +#define HWLOG(this, format, ...) +#endif +#define HWLOGx(FUNC, this, format, ...) \ +({ \ + FUNC(this, format, ## __VA_ARGS__); \ + HWLOG(this, format, ## __VA_ARGS__); \ +}) +#define HWLOGD(this, format, ...) HWLOGx(LOGD, this, format, ## __VA_ARGS__) +#define HWLOGI(this, format, ...) HWLOGx(LOGI, this, format, ## __VA_ARGS__) +#define HWLOGE(this, format, ...) HWLOGx(LOGE, this, format, ## __VA_ARGS__) +#define HWLOGW(this, format, ...) HWLOGx(LOGW, this, format, ## __VA_ARGS__) + +#define NAME_OF(NAMEARRAY, VALUE) \ + (((VALUE) < 0 || ARRAY_SIZE(NAMEARRAY) <= (VALUE)) || \ + (NAMEARRAY)[(VALUE)] == NULL ? "(unknown)" : (NAMEARRAY)[(VALUE)]) + +#define LOCK(L) \ +({ \ + LOG_STAT(this, "(will lock) <" #L ">\n"); \ + mutex_lock(&(L)->lock); \ + get_monotonic_boottime(&(L)->ts); \ + (L)->owner_func = __func__; \ + (L)->owner_line = __LINE__; \ + LOG_STAT(this, "LOCKED <" #L ">\n"); \ +}) + +#define TRYLOCK(L) \ +({ \ + int rc; \ + LOG_STAT(this, "(try lock) <" #L ">\n"); \ + rc = mutex_trylock(&(L)->lock); \ + if (rc) { \ + get_monotonic_boottime(&(L)->ts); \ + (L)->owner_func = __func__; \ + (L)->owner_line = __LINE__; \ + } \ + LOG_STAT(this, "%s <" #L ">\n", rc ? "LOCKED" : "(no lock)"); \ + rc; \ +}) + +#define UNLOCK(L) \ +({ \ + LOG_STAT(this, "UNLOCK <" #L ">\n"); \ + get_monotonic_boottime(&(L)->ts); \ + (L)->owner_func = __func__; \ + (L)->owner_line = __LINE__; \ + mutex_unlock(&(L)->lock); \ +}) + +#define IS_LOCKED(L) \ +({ \ + mutex_is_locked(&(L)->lock) ? true : false; \ +}) + + +/* + * Types + */ + +static const char * const clearpad_flash_status[] = { + [0] = "Success", + [1] = "(Reserved)", + [2] = "Flash Programming Not Enabled/Bad Command", + [3] = "Invalid Block Number", + [4] = "Block Not Erased", + [5] = "Erase Key Incorrect", + [6] = "Unknown Erase/Program Failure", + [7] = "Device has been reset", +}; + +enum clearpad_state_e { + SYN_STATE_INIT, + SYN_STATE_RUNNING, + SYN_STATE_FLASH_IMAGE_SET, + SYN_STATE_FLASH_ENABLE, + SYN_STATE_FLASH_PROGRAM, + SYN_STATE_FLASH_ERASE, + SYN_STATE_FLASH_DATA, + SYN_STATE_FLASH_CONFIG, + SYN_STATE_FLASH_DISABLE, + SYN_STATE_DISABLED, +}; + +static const char * const clearpad_state_name[] = { + [SYN_STATE_INIT] = "init", + [SYN_STATE_RUNNING] = "running", + [SYN_STATE_FLASH_IMAGE_SET] = "flash image set", + [SYN_STATE_FLASH_ENABLE] = "flash enable", + [SYN_STATE_FLASH_PROGRAM] = "flash program", + [SYN_STATE_FLASH_ERASE] = "flash erase", + [SYN_STATE_FLASH_DATA] = "flash data", + [SYN_STATE_FLASH_CONFIG] = "flash config", + [SYN_STATE_FLASH_DISABLE] = "flash disable", + [SYN_STATE_DISABLED] = "disabled", +}; + +enum clearpad_chip_e { + SYN_CHIP_3500 = 0x38, + SYN_CHIP_3330 = 0x3A, /* Hybrid incell */ + SYN_CHIP_332U = 0x40, /* Full incell */ +}; + +static const char * const clearpad_chip_name[] = { + [SYN_CHIP_3500] = "S3500", + [SYN_CHIP_3330] = "S3330", /* Hybrid incell */ + [SYN_CHIP_332U] = "S332U", /* Full incell */ +}; + +enum clearpad_function_e { + SYN_F01_RMI, + SYN_F12_2D, + SYN_F34_FLASH, + SYN_F51_CUSTOM, + SYN_F54_ANALOG, + SYN_F55_SENSOR, + SYN_N_FUNCTIONS, +}; + +static const u8 clearpad_function_value[] = { + [SYN_F01_RMI] = 0x01, + [SYN_F12_2D] = 0x12, + [SYN_F34_FLASH] = 0x34, + [SYN_F51_CUSTOM] = 0x51, + [SYN_F54_ANALOG] = 0x54, + [SYN_F55_SENSOR] = 0x55, + [SYN_N_FUNCTIONS] = 0x00, +}; + +enum clearpad_reg_type_e { + SYN_TYPE_DATA, + SYN_TYPE_CTRL, + SYN_TYPE_COMMAND, + SYN_TYPE_QUERY, + SYN_TYPE_END, +}; + +static const char * const clearpad_flash_status_name[] = { + [STATUS_FLASH_STATUS_SUCCESS] + = "Success", + [STATUS_FLASH_STATUS_DEVICE_NOT_IN_BOOTLOADER_MODE] + = "Device Not In Bootloader Mode", + [STATUS_FLASH_STATUS_INVALID_PARTITION] + = "Invalid Partition", + [STATUS_FLASH_STATUS_INVALID_COMMAND] + = "Invalid Command", + [STATUS_FLASH_STATUS_INVALID_BLOCK_OFFSET] + = "Invalid Block Offset", + [STATUS_FLASH_STATUS_INVALID_TRANSFER] + = "Invalid Transfer", + [STATUS_FLASH_STATUS_NOT_ERASED] + = "Not Erased", + [STATUS_FLASH_STATUS_FLASH_PROGRAMMING_KEY_INCORRECT] + = "Flash Programming Key Incorrect", + [STATUS_FLASH_STATUS_BAD_PARTITION_TABLE] + = "Bad Partition Table", + [STATUS_FLASH_STATUS_CHECKSUM_FAILED] + = "Checksum Failed", + [STATUS_FLASH_STATUS_FLASH_HARDWARE_FAILURE] + = "Flash Hardware Failure", +}; + +static const char * const clearpad_flash_reason_name[] = { + [DEVICE_STATUS_CODE_CONFIGURATION_CRC_FAILURE] + = "Configuration CRC Failure", + [DEVICE_STATUS_CODE_FIRMWARE_CRC_FAILURE] + = "Firmware CRC Failure", + [DEVICE_STATUS_CODE_CRC_IN_PROGRESS] + = "CRC In Progress", +}; + +enum clearpad_firmware_e { + HEADER_SIZE = 0x100, + HEADER_VERSION_OFFSET = 0x07, + HEADER_PRODUCT_ID_SIZE = 10, +}; + +enum firmware_configuration_const_e { + CONFIG_CUSTOMER_FAMILY_OFFSET, + CONFIG_FIRMWARE_REVISION_MAJOR_OFFSET, + CONFIG_FIRMWARE_REVISION_MINOR_OFFSET, + CONFIG_FIRMWARE_REVISION_EXTRA_OFFSET, + CONFIG_FIRMWARE_INFO_SIZE, +}; + +enum clearpad_container_firmware_e { + CON_TOP_START_ADDRESS_OFFSET = 0xc, + CON_CONTENT_LENGTH_OFFSET = 0x18, + CON_CONTENT_ADDRESS_OFFSET = 0x1c, + CON_START_ADDRESS_SIZE = 0x4, + CON_ID_OFFSET = 0x4, +}; + +enum firmware_partition_id_const_e { + PID_BOOTLOADER = 1, + PID_DEVICE_CONFIGURATION = 2, + PID_FLASH_CONFIGURATION = 3, + PID_MANUFACTURING_BLOCK = 4, + PID_GUEST_SERIALIZATION = 5, + PID_GLOBAL_PARAMETERS = 6, + PID_CORE_CODE = 7, + PID_CORE_CONFIGURATION = 8, + PID_DISPLAY_CONFIGURATION = 10, + PID_EXTERNAL_TOUCH_AFE_CONFIG = 11, +}; + +enum firmware_flash_command_const_e { + FLASH_CMD_IDLE, + FLASH_CMD_ENTER_BOOTLOADER, + FLASH_CMD_READ, + FLASH_CMD_WRITE, + FLASH_CMD_ERASE, + FLASH_CMD_ERASE_APPLICATION, + FLASH_CMD_SENSOR_ID, +}; + +enum firmware_container_id_const_e { + CID_CORE_CODE_CONTAINER = 0x12, + CID_CORE_CONFIGURATION_CONTAINER = 0x13, +}; + +enum firmware_bootloader_version_const_e { + BV6 = 0x06, + BV7 = 0x10, +}; + +static const int clearpad_bootloader_version_dec[] = { + [BV6] = 6, + [BV7] = 7, +}; + +enum clearpad_flash_command_e { + SYN_FORCE_FLASH, + SYN_DEFAULT_FLASH, +}; + +static const char * const clearpad_flash_command_name[] = { + [SYN_FORCE_FLASH] = "force", + [SYN_DEFAULT_FLASH] = "default", +}; + +enum clearpad_calibration_e { + SYN_CALIBRATION_NORMAL, + SYN_CALIBRATION_EW, +}; + +static const char * const clearpad_calibration_name[] = { + [SYN_CALIBRATION_NORMAL] = "normal calibration", + [SYN_CALIBRATION_EW] = "EW calibration", +}; + +struct clearpad_lock_t { + struct mutex lock; + struct timespec ts; + const char *owner_func; + int owner_line; +}; + +struct clearpad_post_probe_t { + struct delayed_work work; + bool start; + bool done; + int retry; +}; + +struct clearpad_thread_resume_t { + struct work_struct work; + struct workqueue_struct *work_queue; +}; + +struct clearpad_touchctrl_t { + struct wake_lock wakelock; + struct clearpad_lock_t session_lock; + const char *session; + int power_user; /* reference counter */ + bool will_powerdown; /* early powerdown callback has been called */ +}; + +struct clearpad_interrupt_wait_t { + const char *name; + bool use; + wait_queue_head_t wq; + atomic_t done; + int result; +}; + +struct clearpad_interrupt_t { + u32 count; + u32 wait_ms; + struct clearpad_interrupt_wait_t for_reset; + struct clearpad_interrupt_wait_t for_F34; + struct clearpad_interrupt_wait_t for_F54; + struct timespec hard_handler_ts; + struct timespec threaded_handler_ts; + struct timespec handle_first_event_ts; +}; + +enum clearpad_reset_e { + SYN_HWRESET, + SYN_SWRESET, + SYN_FORCE_HWRESET, + SYN_FORCE_SWRESET, +}; + +static const char * const clearpad_reset_name[] = { + [SYN_HWRESET] = "HW reset", + [SYN_SWRESET] = "SW reset", + [SYN_FORCE_HWRESET] = "Force HW reset", + [SYN_FORCE_SWRESET] = "Force SW reset", +}; + +struct clearpad_reset_t { + enum clearpad_reset_e mode; + struct delayed_work work; + u32 delay_for_powerup_ms; + int retry; +}; + +struct clearpad_device_info_t { + u8 manufacturer_id; + u8 product_properties; + u8 customer_family; + u8 firmware_revision_major; + u8 firmware_revision_minor; + u8 firmware_revision_extra; + u8 analog_id; + u8 product_id[HEADER_PRODUCT_ID_SIZE]; + u8 boot_loader_version_major; + u8 boot_loader_version_minor; +}; + +struct clearpad_point_t { + int id; + int x; + int y; + int wx; + int wy; + int z; + int tool; +}; + +enum clearpad_tool_e { + SYN_TOOL_FINGER = 0x01, + SYN_TOOL_PEN = 0x02, + SYN_TOOL_GLOVE = 0x03, +}; + +enum clearpad_tools_type_f12_e { + SYN_F12_TOOL_TYPE_NOOBJ, + SYN_F12_TOOL_TYPE_FINGER, + SYN_F12_TOOL_TYPE_PEN, + SYN_F12_TOOL_TYPE_PALM, + SYN_F12_TOOL_TYPE_UNCLASS, + SYN_F12_TOOL_TYPE_RESERVE, + SYN_F12_TOOL_TYPE_GLOVE, + SYN_F12_TOOL_TYPE_NARROW, + SYN_F12_TOOL_TYPE_HANDEDGE, +}; + +static const int clearpad_tool_type_f12[] = { + [SYN_F12_TOOL_TYPE_NOOBJ] = 0x00, + [SYN_F12_TOOL_TYPE_FINGER] = SYN_TOOL_FINGER, + [SYN_F12_TOOL_TYPE_PEN] = SYN_TOOL_PEN, + [SYN_F12_TOOL_TYPE_PALM] = 0x00, + [SYN_F12_TOOL_TYPE_UNCLASS] = 0x00, + [SYN_F12_TOOL_TYPE_RESERVE] = 0x00, + [SYN_F12_TOOL_TYPE_GLOVE] = SYN_TOOL_GLOVE, + [SYN_F12_TOOL_TYPE_NARROW] = 0x00, + [SYN_F12_TOOL_TYPE_HANDEDGE] = 0x00, +}; + +struct clearpad_pointer_t { + bool down; + struct clearpad_funcarea_t *funcarea; + struct clearpad_point_t cur; +}; + +struct clearpad_function_descriptor_t { + u8 number; + u8 int_count; + u8 irq_mask; + u8 page; + u8 base[SYN_TYPE_END]; + u8 offset[SYN_TYPE_END][SYN_MAX_CTRL_VALUE]; +}; + +struct clearpad_flash_block_t { + int blocks; + u32 length; + int pos; + const u8 *data; + u16 transation_count; + u16 remain_block; + u16 payload_length; + u16 block_size; +}; + +struct clearpad_flash_t { + u8 format_version; + int config_size; + u8 customer_family; + u8 firmware_revision_major; + u8 firmware_revision_minor; + u8 firmware_revision_extra; + u8 analog_id; + u8 product_id[HEADER_PRODUCT_ID_SIZE]; + struct clearpad_flash_block_t data; + struct clearpad_flash_block_t config; + const struct firmware *fw; + const char *firmware_name; + enum clearpad_flash_command_e command; + bool enter_bootloader_mode; + bool on_post_probe; + unsigned long default_timeout_ms; +}; + +struct clearpad_extents_t { + int preset_x_max, preset_y_max; + int x_min, y_min; + int x_max, y_max; + int n_fingers; + int n_bytes_per_object; +}; + +struct clearpad_charger_t { + bool supported; + bool status; +}; + +struct clearpad_pen_t { + bool supported; + bool enabled; +}; + +struct clearpad_glove_t { + bool supported; + bool enabled; +}; + +struct clearpad_cover_t { + bool supported; + bool status; + bool enabled; + int win_top; + int win_bottom; + int win_right; + int win_left; + u32 tag_x_max; + u32 tag_y_max; + u32 convert_window_size; +}; + +struct clearpad_wakeup_gesture_t { + bool supported; + bool enabled; + unsigned long time_started; + u32 timeout_delay; + bool use_workaround_for_felica; +}; + +struct clearpad_watchdog_t { + struct delayed_work work; + int delay; + bool enabled; +}; + +struct clearpad_noise_detect_t { + spinlock_t slock; + bool supported; + bool enabled; + u32 hard_handler_count; + u32 threaded_handler_count; + u32 irq_gpio_flags; + int irq_gpio; + int irq; + bool first_irq; + int retry_time_ms; + struct timespec hard_handler_ts; + struct timespec threaded_handler_ts; +}; + +struct clearpad_hwtest_t { + struct clearpad_lock_t lock; + char log_buf[HWLOG_BUF_SIZE]; + size_t log_size; +}; + +struct descriptor_t { + u16 container_id; + u8 major_version; + u8 minor_version; + u32 content_start_addr; + u32 content_len; +}; + +struct clearpad_change_reportrate_t { + bool supported; + u8 mode; +}; + +struct clearpad_doze_holdoff_t { + bool supported; + u8 default_time; + u8 glove_mode_time; + u8 cover_mode_time; +}; + +struct clearpad_stamina_mode_t { + bool supported; + bool enabled; + struct clearpad_change_reportrate_t change_reportrate; + struct clearpad_doze_holdoff_t doze_holdoff; +}; + +struct fb_t { + bool unblank_done; + bool unblank_early_done; +}; + +struct clearpad_reg_offset_t { + bool updated; + + u8 f01_cmd00; + u8 f01_ctrl00; + u8 f01_ctrl01; + u8 f01_ctrl05; + u8 f01_ctrl18; + u8 f01_data00; + u8 f01_data01; + u8 f01_query11; + + u8 f12_ctrl08; + + u8 f34_ctrl00; + u8 f34_data00; + u8 f34_data01; + u8 f34_data02; + u8 f34_data03; + u8 f34_data04; + u8 f34_data05; + u8 f34_query00; + u8 f34_query01; + u8 f34_query03; + + u8 f51_ctrl05; + u8 f51_ctrl30; + + u8 f54_cmd00; + u8 f54_ctrl41; + u8 f54_ctrl57; + u8 f54_ctrl88; + u8 f54_ctrl109; + u8 f54_ctrl113; + u8 f54_ctrl147; + u8 f54_ctrl149; + u8 f54_ctrl188; + u8 f54_ctrl214; + u8 f54_data00; + u8 f54_data01; + u8 f54_data02; + u8 f54_data03; + u8 f54_data31; + u8 f54_query38; +}; + +struct clearpad_charger_only_t { + unsigned long delay_ms; +}; + +enum clearpad_hwtest_data_type_e { + HWTEST_NULL, + HWTEST_U8, + HWTEST_S8, + HWTEST_S16, + HWTEST_U32, +}; + +enum clearpad_f54_command_e { + F54_16_IMAGE_REPORT = 2, + F54_AUTOSCAN_REPORT = 3, + F54_HIGH_RESISTANCE_REPORT = 4, + F54_RAW_CAP_RX_COUPLING_REPORT = 20, + F54_SENSOR_SPEED_REPORT = 22, + F54_TRX_TO_TRX_SHORT_2_REPORT = 26, + F54_EWMODE_RAW_CAP_REPORT = 38, + F54_ABS_RAW_REPORT = 63, + F54_FULLINCELL_RAW_CAP_REPORT = 83, + F54_FULLINCELL_CAL_DATA_CHK_REPORT = 84, + F54_FULLINCELL_SENSOR_SPEED_REPORT = 86, + F54_TRX_TO_TRX_SHORT_RAW_IMAGE_REPORT = 100, +}; + +enum clearpad_f12_2d_reporting_control_e { + F12_2D_CTRL_MOTION_SUPP_X = 0, + F12_2D_CTRL_MOTION_SUPP_Y, + F12_2D_CTRL_RPT_FLAG, + F12_2D_CTRL_HOVER_RPT, + F12_2D_CTRL_RPT_REG_MAX +}; + +enum clearpad_f12_2d_object_report_enable_e { + F12_2D_CTRL_OBJ_REPORT_ENABLE = 0, + F12_2D_CTRL_MAX_OBJ_REPORT, + F12_2D_CTRL_REPORT_AS_FINGER +}; + +enum clearpad_f12_2d_data_registers_id_e { + F12_2D_DATA_SENSED_OBJECTS = 1, + F12_2D_DATA_OBJ_ATTENTION = 15, + F12_2D_DATA_REG_MAX +}; + +enum clearpad_force_sleep_e { + FSMODE_OFF = 0, /* Normal suspend/resume */ + FSMODE_KEEP = 1, /* Sleep forever */ + FSMODE_ONESHOT = 2, /* Oneshot (will be FSMODE_OFF by resume) */ +}; + +struct clearpad_t { + enum clearpad_state_e state; + enum clearpad_chip_e chip_id; + struct input_dev *input; + struct platform_device *pdev; + struct clearpad_platform_data_t *pdata; + struct clearpad_bus_data_t *bdata; + struct clearpad_lock_t lock; + struct clearpad_touchctrl_t touchctrl; + struct clearpad_post_probe_t post_probe; + struct clearpad_thread_resume_t thread_resume; + struct clearpad_device_info_t device_info; + struct clearpad_funcarea_t *funcarea; + struct clearpad_pointer_t pointer[SYN_MAX_N_FINGERS]; + struct clearpad_function_descriptor_t pdt[SYN_N_FUNCTIONS]; + struct clearpad_flash_t flash; + struct clearpad_wakeup_gesture_t wakeup_gesture; + struct clearpad_charger_t charger; + struct clearpad_pen_t pen; + struct clearpad_glove_t glove; + struct clearpad_cover_t cover; + struct clearpad_extents_t extents; + struct clearpad_reset_t reset; + struct clearpad_noise_detect_t noise_det; + struct clearpad_interrupt_t interrupt; + struct clearpad_stamina_mode_t stamina; + struct clearpad_reg_offset_t reg_offset; + struct clearpad_charger_only_t charger_only; + int irq; + bool irq_enabled; + enum clearpad_force_sleep_e force_sleep; +#ifdef CONFIG_FB + struct notifier_block fb_notif; +#endif + char fwname[SYN_STRING_LENGTH + 1]; + char result_info[SYN_STRING_LENGTH + 1]; + bool flash_requested; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs; + struct clearpad_hwtest_t hwtest; +#endif + u32 flip_config; + u32 touch_pressure_enabled; + u32 touch_size_enabled; + u32 touch_orientation_enabled; + bool calibration_supported; + bool calibrate_on_fwflash; + struct device_node *evdt_node; + struct clearpad_watchdog_t watchdog; + spinlock_t slock; + bool dev_active; + bool dev_busy; + bool early_suspend; /* suspend mode has been set from early suspend */ + bool irq_pending; + bool last_irq; + + bool is_sol; + + struct fb_t wakeup; +}; + +/* + * Function prototypes + */ + +static void clearpad_update_chip_id(struct clearpad_t *this); +static void clearpad_set_is_sol(struct clearpad_t *this); +static void clearpad_post_probe_work(struct work_struct *work); +static void clearpad_thread_resume_work(struct work_struct *work); +static void clearpad_funcarea_initialize(struct clearpad_t *this); +static int clearpad_handle_if_first_event(struct clearpad_t *this); +static void clearpad_reset(struct clearpad_t *this, + enum clearpad_reset_e mode, const char *cause); +static void clearpad_funcarea_invalidate_all(struct clearpad_t *this); +static int clearpad_set_resume_mode(struct clearpad_t *this); +static int clearpad_set_suspend_mode(struct clearpad_t *this); +static int clearpad_read_pca_block(struct clearpad_t *this, + u16 block_num, u8 *data); +static int clearpad_write_pca_block(struct clearpad_t *this, + u16 block_num, u8 *data); +static int clearpad_process_irq(struct clearpad_t *this); +static int clearpad_flash(struct clearpad_t *this); +static bool clearpad_process_noise_det_irq(struct clearpad_t *this); +static void clearpad_touch_config_dt_for_chip_id(struct clearpad_t *this, + int chip_id); +#ifdef CONFIG_DEBUG_FS +static int clearpad_debug_hwtest_log(struct clearpad_t *this, + const char *format, ...); +static void clearpad_debug_info(struct clearpad_t *this); +#endif + +/* + * Functions + */ + +static inline bool is_equal_cstring(const char *a, const char *b) +{ + size_t length = max(strnlen(a, PAGE_SIZE), strnlen(b, PAGE_SIZE)); + + return strncmp(a, b, length) == 0; +} + +static char *clearpad_s(u8 *array, size_t size) +{ + static char string[SYN_STRING_LENGTH + 1]; + + memset(string, 0, SYN_STRING_LENGTH + 1); + size = (SYN_STRING_LENGTH < size) ? SYN_STRING_LENGTH : size; + memcpy(string, array, size); + + return string; +} + +static void clearpad_set_delay(unsigned long ms) +{ + if (ms > 0) + ms <= MAX_USLEEP_RANGE_IN_MS ? + usleep_range(ms * 1000, (ms * 1000) + 1000) : msleep(ms); +} + +static inline bool clearpad_is_valid_function(struct clearpad_t *this, int func) +{ + return (0 <= func && func < SYN_N_FUNCTIONS) + && (this->pdt[func].number == clearpad_function_value[func]); +} + +/* need LOCK(&this->lock) */ +static void clearpad_set_irq(struct clearpad_t *this, bool enable) +{ + if (enable && !this->irq_enabled) { + enable_irq(this->irq); + LOGI(this, "irq was enabled\n"); + } else if (!enable && this->irq_enabled) { + disable_irq_nosync(this->irq); + LOGI(this, "irq was disabled\n"); + } else { + LOGI(this, "no irq change (%s)\n", + this->irq_enabled ? "enable" : "disable"); + } + this->irq_enabled = enable; +} + +static int clearpad_set_noise_det_irq(struct clearpad_t *this, bool enable, + bool set_first_irq) +{ + int ret = 0; + unsigned long flags; + + if (this->noise_det.supported && this->chip_id == SYN_CHIP_332U) { + spin_lock_irqsave(&this->noise_det.slock, flags); + if (enable && set_first_irq) + this->noise_det.first_irq = true; + if (enable && !this->noise_det.enabled) { + enable_irq(this->noise_det.irq); + } else if (!enable && this->noise_det.enabled) { + disable_irq_nosync(this->noise_det.irq); + } else { + spin_unlock_irqrestore(&this->noise_det.slock, flags); + ret = -1; + goto end; + } + this->noise_det.enabled = enable; + spin_unlock_irqrestore(&this->noise_det.slock, flags); + } else { + ret = -1; + } +end: + return ret; +} + +/* need LOCK(&this->lock) */ +static void clearpad_notify_interrupt(struct clearpad_t *this, + struct clearpad_interrupt_wait_t *wait, + int result) +{ + if (wait->use) { + LOGI(this, "handled interrupt '%s' (rc=%d)\n", + wait->name, result); + atomic_inc(&wait->done); + wait->result = result; + wake_up_all(&wait->wq); + } +} + +/* need LOCK(&this->lock) */ +static void clearpad_prepare_for_interrupt(struct clearpad_t *this, + struct clearpad_interrupt_wait_t *wait, + const char *name) +{ + if (wait->use) + LOGE(this, "already used '%s' (%s)\n", wait->name, name); + + wait->name = name; + wait->use = true; + atomic_set(&wait->done, 0); +} + +/* need LOCK(&this->lock) */ +static void clearpad_undo_prepared_interrupt(struct clearpad_t *this, + struct clearpad_interrupt_wait_t *wait, + const char *name) +{ + if (is_equal_cstring(wait->name, name)) { + LOGE(this, "undo prepared interrupt %s\n", wait->name); + wait->name = NULL; + wait->use = false; + } +} + +/* NO need LOCK(&this->lock) */ +static int clearpad_wait_for_interrupt(struct clearpad_t *this, + struct clearpad_interrupt_wait_t *wait, + unsigned long ms) +{ + int rc; + int retry = 0; + + LOG_STAT(this, "(wait for interrupt) <%s>\n", wait->name); +retry: + rc = wait_event_timeout(wait->wq, + atomic_read(&wait->done), msecs_to_jiffies(ms)); + if (rc == 0) { + LOGW(this, "timeout '%s' (retry %d)\n", wait->name, retry); + rc = -ETIMEDOUT; + if (retry++ < SYN_RETRY_NUM) + goto retry; + } else if (rc < 0) { + LOGE(this, "failed to wait for '%s' (rc=%d done=%d)", + wait->name, rc, atomic_read(&wait->done)); + } else { + LOG_STAT(this, "received interrupt <%s> (done=%d)\n", + wait->name, atomic_read(&wait->done)); + rc = 0; + } + + LOCK(&this->lock); + wait->name = NULL; + wait->use = false; + if (!rc) + rc = wait->result; + UNLOCK(&this->lock); + + return rc; +} + +/* + * touchctrl : porting for exported functions + */ + +static int touchctrl_hwreset(struct clearpad_t *this, int mode) +{ + int rc = 0; + int raw_mode = 0; + int retry; + + LOGI(this, "execute '%s'\n", NAME_OF(clearpad_reset_name, mode)); + this->wakeup.unblank_done = false; + this->wakeup.unblank_early_done = false; + switch (mode) { + case SYN_HWRESET: + case SYN_FORCE_HWRESET: + raw_mode = INCELL_DISPLAY_HW_RESET; + break; + default: + LOGE(this, "unknown HW reset mode (%d)\n", mode); + return -EINVAL; + } + + for (retry = 0; retry < SYN_RETRY_NUM; retry++) { + rc = incell_control_mode(raw_mode, INCELL_FORCE); + if (rc != INCELL_EBUSY) + break; + } + + return rc; +} + +static int touchctrl_display_off(struct clearpad_t *this) +{ + int rc = 0; + int retry; + + LOGI(this, "turn display off\n"); + this->wakeup.unblank_done = false; + this->wakeup.unblank_early_done = false; + for (retry = 0; retry < SYN_RETRY_NUM; retry++) { + rc = incell_control_mode(INCELL_DISPLAY_OFF, INCELL_FORCE); + if (rc != INCELL_EBUSY) + break; + } + + return rc; +} + +static bool touchctrl_is_touch_powered(struct clearpad_t *this) +{ + incell_pw_status status = { false, false }; + int rc; + + rc = incell_get_power_status(&status); + if (rc) + LOGE(this, "failed to get power status\n"); + else + LOGD(this, "power status (touch %s, display %s)\n", + status.touch_power ? "ON" : "OFF", + status.display_power ? "ON" : "OFF"); + + return rc == 0 && status.touch_power; +} + +static bool touchctrl_is_display_powered(struct clearpad_t *this) +{ + incell_pw_status status = { false, false }; + int rc; + + rc = incell_get_power_status(&status); + if (rc) + LOGE(this, "failed to get power status\n"); + else + LOGD(this, "power status (touch %s, display %s)\n", + status.touch_power ? "ON" : "OFF", + status.display_power ? "ON" : "OFF"); + + return rc == 0 && status.display_power; +} + +/* + * need LOCK(&this->lock) + * + * @return true : locked with specified condition + * false : not locked + */ +static bool touchctrl_lock_power(struct clearpad_t *this, const char *id, + bool need_touch_power, bool need_display_power) +{ + struct clearpad_touchctrl_t *touchctrl = &this->touchctrl; + incell_pw_status status = { false, false }; + int rc; + bool result = false; + + LOG_STAT(this, "(will lock power) <%s>\n", id); + + if (touchctrl->power_user != 0) { + rc = incell_get_power_status(&status); + if (rc) + LOGE(this, "failed to get power status (rc=%d)\n", rc); + goto check_condition; + } + + /* stop lock if it will be powered down */ + if (!this->wakeup_gesture.enabled && touchctrl->will_powerdown) { + LOG_STAT(this, "(no lock) will be powered down <%s>\n", id); + goto err_in_condition_of_powerdown; + } + + /* the first user locks power */ + rc = incell_power_lock_ctrl(INCELL_DISPLAY_POWER_LOCK, &status); + switch (rc) { + case INCELL_OK: + break; + case INCELL_ALREADY_LOCKED: + LOGW(this, "already locked <%s>\n", id); + WARN_ON(rc == INCELL_ALREADY_LOCKED); + break; + default: + LOGE(this, "failed to lock power(%d) <%s>\n", rc, id); + goto err_in_power_lock_ctrl; + } + +check_condition: + if ((need_touch_power && !status.touch_power) || + (need_display_power && !status.display_power)) { + LOG_STAT(this, "(no lock) power due to condition) <%s> " + "touch=%s(%s) display=%s(%s)\n", id, + status.touch_power ? "On" : "Off", + need_touch_power ? "required" : "optional", + status.display_power ? "On" : "Off", + need_display_power ? "required" : "optional"); + if (incell_power_lock_ctrl(INCELL_DISPLAY_POWER_UNLOCK, + &status)) + LOGE(this, "failed to unlock power\n"); + rc = 0; + goto err_in_locked_power_status; + } + result = true; + touchctrl->power_user += 1; + LOG_STAT(this, "LOCKED power <%s> (%d)\n", id, touchctrl->power_user); +err_in_locked_power_status: +err_in_power_lock_ctrl: +err_in_condition_of_powerdown: + return result; +} + +/* need LOCK(&this->lock) */ +static void touchctrl_unlock_power(struct clearpad_t *this, const char *id) +{ + struct clearpad_touchctrl_t *touchctrl = &this->touchctrl; + incell_pw_status status = { false, false }; + int rc; + unsigned long flags; + + LOG_STAT(this, "unlock power for touch <%s>\n", id); + + if (this->last_irq && is_equal_cstring(id, "irq_handler")) { + LOGD(this, "last pending IRQ handled\n"); + this->last_irq = false; + touchctrl->power_user -= 1; + } + + touchctrl->power_user -= 1; + if (touchctrl->power_user > 0) { + LOG_STAT(this, "UNLOCKED power <%s> user(%d)\n", + id, touchctrl->power_user); + goto unlocked; + } else if (touchctrl->power_user < 0) { + LOGE(this, "invalid unlock <%s> user(%d)\n", + id, touchctrl->power_user); + touchctrl->power_user = 0; + } + + /* suspend when the last user unlocks power */ + if (touchctrl->will_powerdown) { + if (this->dev_active) { + rc = clearpad_set_suspend_mode(this); + if (rc) + LOGE(this, "failed to suspend\n"); + + if (this->watchdog.enabled) + cancel_delayed_work(&this->watchdog.work); + cancel_delayed_work(&this->reset.work); + } + /* check for pending IRQ on suspend mode */ + if (!this->dev_active) { + spin_lock_irqsave(&this->slock, flags); + if (unlikely(this->dev_busy || this->irq_pending)) { + touchctrl->power_user += 1; + this->last_irq = true; + spin_unlock_irqrestore(&this->slock, flags); + LOGD(this, "there is a pending IRQ, " + "will not yet Unlock Power\n"); + goto unlocked_with_pending_irq; + } + spin_unlock_irqrestore(&this->slock, flags); + } + } + + rc = incell_power_lock_ctrl(INCELL_DISPLAY_POWER_UNLOCK, &status); + switch (rc) { + case INCELL_OK: + break; + case INCELL_ALREADY_UNLOCKED: + LOGW(this, "already unlocked <%s>\n", id); + break; + default: + LOGE(this, "failed to unlock power(%d) <%s>\n", rc, id); + goto err_in_power_lock_ctrl; + } + LOG_STAT(this, "UNLOCKED power <%s> touch=%s display=%s\n", id, + status.touch_power ? "On" : "Off", + status.display_power ? "On" : "Off"); + +err_in_power_lock_ctrl: +unlocked_with_pending_irq: +unlocked: + return; +} + +static void touchctrl_notify_wakeup_gesture_mode(struct clearpad_t *this, + bool enabled) +{ + LOG_STAT(this, "%s\n", enabled ? "enable" : "disable"); + + incell_ewu_mode_ctrl(enabled ? INCELL_DISPLAY_EWU_ENABLE + : INCELL_DISPLAY_EWU_DISABLE); +} + +/* Begin a session to use touch device requiring power supply */ +static int clearpad_ctrl_session_begin(struct clearpad_t *this, + const char *session) +{ + struct clearpad_touchctrl_t *touchctrl = &this->touchctrl; + int rc = 0; + + wake_lock(&touchctrl->wakelock); + LOCK(&touchctrl->session_lock); + LOGI(this, "begin '%s' session\n", session); + touchctrl->session = session; + + LOCK(&this->lock); + /* keep touch power for this session */ + if (!touchctrl_lock_power(this, session, true, false)) { + LOGE(this, "failed to lock power\n"); + rc = -EAGAIN; + goto err_in_lock_power; + } + rc = clearpad_handle_if_first_event(this); + if (rc < 0) { + LOGE(this, "failed to handle first event\n"); + rc = -EBUSY; + goto err_in_first_event_handling; + } else { + rc = 0; + } + UNLOCK(&this->lock); + goto end; + +err_in_first_event_handling: + touchctrl_unlock_power(this, session); +err_in_lock_power: + UNLOCK(&this->lock); + touchctrl->session = NULL; + UNLOCK(&touchctrl->session_lock); + wake_unlock(&touchctrl->wakelock); +end: + return rc; +} + +/* End a session to use touch device requiring power supply */ +static void clearpad_ctrl_session_end(struct clearpad_t *this, + const char *session) +{ + struct clearpad_touchctrl_t *touchctrl = &this->touchctrl; + + if (touchctrl->session == NULL || + !is_equal_cstring(touchctrl->session, session)) { + LOGW(this, "try invalid session end '%s' (current '%s')", + session, touchctrl->session); + } + + /* release power for this session */ + LOCK(&this->lock); + touchctrl_unlock_power(this, session); + UNLOCK(&this->lock); + + LOGI(this, "end '%s' session\n", session); + touchctrl->session = NULL; + UNLOCK(&touchctrl->session_lock); + wake_unlock(&touchctrl->wakelock); +} + +/* + * Basic bus access + */ + +static int clearpad_set_page(struct clearpad_t *this, u8 page) +{ + return this->bdata->set_page(this->pdev->dev.parent, page); +} + +static int clearpad_read(struct clearpad_t *this, u16 addr, + u8 *buf, int len) +{ + return this->bdata->read(this->pdev->dev.parent, addr, buf, len); +} + +static int clearpad_write(struct clearpad_t *this, u16 addr, + const u8 *buf, u8 len) +{ + return this->bdata->write(this->pdev->dev.parent, addr, buf, len); +} + +static int clearpad_read_block(struct clearpad_t *this, u16 addr, + u8 *buf, int len) +{ + return this->bdata->read_block(this->pdev->dev.parent, + addr, buf, len); +} + +static int clearpad_write_block(struct clearpad_t *this, u16 addr, + const u8 *buf, int len) +{ + return this->bdata->write_block(this->pdev->dev.parent, + addr, buf, len); +} + +/* + * Read/Write data of specific size + */ + +static int clearpad_get(struct clearpad_t *this, u16 addr, u8 *val) +{ + int rc = clearpad_read(this, addr, val, 1); + return rc == 1 ? 0 : (rc < 0 ? rc : -EIO); +} + +static int clearpad_put(struct clearpad_t *this, u16 addr, u8 val) +{ + int rc = clearpad_write(this, addr, &val, 1); + return rc == 1 ? 0 : (rc < 0 ? rc : -EIO); +} + +static int clearpad_put_bit(struct clearpad_t *this, u16 addr, u8 val, u8 mask) +{ + int rc; + u8 buf; + + rc = clearpad_get(this, addr, &buf); + if (rc) + goto end; + + buf = (buf & ~mask) | val; + rc = clearpad_put(this, addr, buf); +end: + return rc; +} + +static int clearpad_get_block(struct clearpad_t *this, u16 addr, + u8 *buf, int len) +{ + int rc; + + rc = clearpad_read_block(this, addr, buf, len); + return rc == len ? 0 : (rc < 0 ? rc : -EIO); +} + +static int clearpad_put_block(struct clearpad_t *this, u16 addr, + const u8 *buf, int len) +{ + int rc; + + rc = clearpad_write_block(this, addr, buf, len); + return rc == len ? 0 : (rc < 0 ? rc : -EIO); +} + +static int clearpad_set_doze_holdoff_time(struct clearpad_t *this, + u8 holdoff_time) +{ + int rc = 0; + u8 current_time = 0x00; + + if (!this->stamina.supported) { + LOGI(this, "stamina mode is not supported\n"); + goto end; + } + if (!this->stamina.doze_holdoff.supported) { + LOGI(this, "doze holdoff is not supported\n"); + goto end; + } + + if (clearpad_get( + SYNF(this, F01_RMI, CTRL, this->reg_offset.f01_ctrl05), + ¤t_time)) { + LOGE(this, "failed to get current Doze Holdoff\n"); + rc = -EINVAL; + goto end; + } + if (current_time == holdoff_time) { + LOGI(this, "new Doze Holdoff is same as current=0x%02x\n", + current_time); + goto end; + } + + /* F01_RMI_CTRL05: Doze Holdoff */ + rc = clearpad_put( + SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl05), + holdoff_time); + if (rc) { + LOGE(this, "failed to set new Doze Holdoff=0x%02x\n", + holdoff_time); + goto end; + } + /* F54_ANALOG_CMD00: Analog Command */ + rc = clearpad_put( + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + ANALOG_COMMAND_FORCE_UPDATE_MASK); + if (rc) { + LOGE(this, "failed to set force update\n"); + goto end; + } + LOGI(this, "changed Doze Holdoff=0x%02x\n", holdoff_time); + +end: + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_set_doze_holdoff(struct clearpad_t *this) +{ + int rc = 0; + u8 holdoff_time = 0x00; + + if (!this->stamina.supported) { + LOGI(this, "stamina mode is not supported\n"); + goto end; + } + if (!this->stamina.doze_holdoff.supported) { + LOGI(this, "doze holdoff is not supported\n"); + goto end; + } + + LOGI(this, "glove mode: %s, cover mode/status: %s/%s\n", + this->glove.enabled ? "enable" : "disable", + this->cover.enabled ? "enable" : "disable", + this->cover.status ? "CLOSE" : "OPEN"); + + holdoff_time = this->stamina.doze_holdoff.default_time; + if (this->glove.enabled) + holdoff_time = this->stamina.doze_holdoff.glove_mode_time; + if (this->cover.enabled && this->cover.status) + holdoff_time = this->stamina.doze_holdoff.cover_mode_time; + + rc = clearpad_set_doze_holdoff_time(this, holdoff_time); + if (rc) + goto end; + +end: + return rc; +} + +static struct clearpad_funcarea_t clearpad_default_funcarea_array[] = { + { + { 0, 0, 0, 0}, { 0, 0, 0, 0}, + SYN_FUNCAREA_POINTER, NULL + }, + { .func = SYN_FUNCAREA_END } +}; + +static struct clearpad_funcarea_t *clearpad_funcarea_get( + struct clearpad_t *this, u8 module_id, u8 rev) +{ + clearpad_default_funcarea_array[0].original.x2 = + this->extents.preset_x_max; + clearpad_default_funcarea_array[0].original.y2 = + this->extents.preset_y_max; + clearpad_default_funcarea_array[0].extension.x2 = + this->extents.preset_x_max; + clearpad_default_funcarea_array[0].extension.y2 = + this->extents.preset_y_max; + return clearpad_default_funcarea_array; +} + +static int clearpad_read_pdt(struct clearpad_t *this) +{ + struct clearpad_function_descriptor_t fdes; + u8 addr = SYN_PDT_START - 1; + u8 irq_bit = 0; + u8 page = 0; + int i, j, k; + int rc; + + memset(&this->pdt, 0, sizeof(*this->pdt) * SYN_N_FUNCTIONS); + for (i = 0; i < SYN_N_FUNCTIONS && addr >= SYN_SIZE_OF_FD - 1;) { + rc = clearpad_get(this, SYN_PAGE_ADDR(page, addr--), + &fdes.number); + if (rc) + break; + rc = clearpad_get(this, SYN_PAGE_ADDR(page, addr--), + &fdes.int_count); + if (rc) + break; + fdes.int_count &= SYN_MAX_INTERRUPT_SOURCE_COUNT; + for (fdes.irq_mask = 0, j = 0; j < fdes.int_count; j++) + fdes.irq_mask |= (1 << irq_bit++); + rc = clearpad_get(this, SYN_PAGE_ADDR(page, addr--), + &fdes.base[SYN_TYPE_DATA]); + if (rc) + break; + rc = clearpad_get(this, SYN_PAGE_ADDR(page, addr--), + &fdes.base[SYN_TYPE_CTRL]); + if (rc) + break; + rc = clearpad_get(this, SYN_PAGE_ADDR(page, addr--), + &fdes.base[SYN_TYPE_COMMAND]); + if (rc) + break; + rc = clearpad_get(this, SYN_PAGE_ADDR(page, addr--), + &fdes.base[SYN_TYPE_QUERY]); + if (rc) + break; + fdes.page = page; + LOG_CHECK(this, "F%02x_IRQ_MASK = %02x\n", + fdes.number, fdes.irq_mask); + LOG_CHECK(this, "F%02x_DATA = %02x\n", + fdes.number, fdes.base[SYN_TYPE_DATA]); + LOG_CHECK(this, "F%02x_CTRL = %02x\n", + fdes.number, fdes.base[SYN_TYPE_CTRL]); + LOG_CHECK(this, "F%02x_COMMAND = %02x\n", + fdes.number, fdes.base[SYN_TYPE_COMMAND]); + LOG_CHECK(this, "F%02x_QUERY = %02x\n", + fdes.number, fdes.base[SYN_TYPE_QUERY]); + LOG_CHECK(this, "F%02x Page = %02x\n", + fdes.number, fdes.page); + if (!fdes.number) { + if (page < SYN_SUPPORTED_PAGE_NUM) { + addr = SYN_PDT_START - 1; + page += 1; + continue; + } + break; + } + for (k = 0; k < SYN_N_FUNCTIONS; k++) { + if (clearpad_function_value[k] == fdes.number) { + if (!this->pdt[k].number) + memcpy(&this->pdt[k], &fdes, + sizeof(*this->pdt)); + i++; + HWLOGI(this, "F%02x page:0x%02x " + "base[DATA:0x%02x " + "CTRL:0x%02x COMMAND:0x%02x " + "QUERY:0x%02x] irq:0x%02x\n", + fdes.number, + fdes.page, + fdes.base[SYN_TYPE_DATA], + fdes.base[SYN_TYPE_CTRL], + fdes.base[SYN_TYPE_COMMAND], + fdes.base[SYN_TYPE_QUERY], + fdes.irq_mask); + break; + } + } + } + + return rc; +} + +/* + * Charger + */ + +static int clearpad_set_charger(struct clearpad_t *this) +{ + int rc = 0; + + if (!this->charger.supported) + goto end; + + if (this->charger.status) + /* F01_RMI_CTRL00: Device Command */ + rc = clearpad_put_bit( + SYNF(this, F01_RMI, CTRL, this->reg_offset.f01_ctrl00), + DEVICE_CONTROL_CHARGER_CONNECTED_MASK, + DEVICE_CONTROL_CHARGER_CONNECTED_MASK); + else + /* F01_RMI_CTRL00: Device Command */ + rc = clearpad_put_bit( + SYNF(this, F01_RMI, CTRL, this->reg_offset.f01_ctrl00), + 0, DEVICE_CONTROL_CHARGER_CONNECTED_MASK); + if (rc) + LOGE(this, "failed to set charger status"); +end: + return rc; +} + +/* + * Pen + */ + +static int clearpad_set_pen(struct clearpad_t *this) +{ + int rc = 0; + + if (!this->pen.supported) + goto end; + + if (!clearpad_is_valid_function(this, SYN_F12_2D)) { + LOGE(this, "F12 is required to set pen\n"); + rc = -EPERM; + goto end; + } + + /* F12_2D_CTRL23_00: Object Report Enable */ + rc = clearpad_put_bit(SYNA(this, F12_2D, CTRL, 23), + this->pen.enabled ? + OBJECT_REPORT_ENABLE_STYLUS_MASK : 0, + OBJECT_REPORT_ENABLE_STYLUS_MASK); +end: + if (rc) + LOGE(this, "failed to set pen"); + return rc; +} + +/* + * Glove finger register in touch ic + */ + +static int clearpad_set_glove_finger_reg(struct clearpad_t *this, bool enable) +{ + int rc = 0; + + /* F12_2D_CTRL23_00: Object Report Enable */ + rc = clearpad_put_bit(SYNA(this, F12_2D, CTRL, 23), + enable ? + OBJECT_REPORT_ENABLE_GLOVED_FINGER_MASK : 0, + OBJECT_REPORT_ENABLE_GLOVED_FINGER_MASK); + if (rc) { + LOGE(this, "error in setting for object report enable\n"); + goto end; + } + if (this->chip_id == SYN_CHIP_3330 || + this->chip_id == SYN_CHIP_332U) { + /* F12_2D_CTRL26: Feature Enable */ + rc = clearpad_put_bit(SYNA(this, F12_2D, CTRL, 26), + enable ? + FEATURE_ENABLE_ENABLE_GLOVED_FINGER_DETECTION_MASK : 0, + FEATURE_ENABLE_ENABLE_GLOVED_FINGER_DETECTION_MASK); + if (rc) { + LOGE(this, "error in setting for feature enable\n"); + goto end; + } + } +end: + if (rc) + LOGE(this, "failed to set glove finger register in touch ic \n"); + return rc; +} + +/* + * Glove + */ + +static int clearpad_set_glove_mode(struct clearpad_t *this, bool enable) +{ + int rc = 0; + u8 buf; + + if (!this->glove.supported) + goto end; + + if (!clearpad_is_valid_function(this, SYN_F12_2D)) { + LOGE(this, "F12 is required to set glove mode\n"); + rc = -EPERM; + goto end; + } + + /* F12_2D_QUERY10: Supported Object Types */ + rc = clearpad_get(SYNA(this, F12_2D, QUERY, 10), &buf); + if (rc) { + LOGE(this, "failed to get supported types"); + goto end; + } + if (!BIT_GET(buf, SUPPORTED_OBJECT_TYPES_HAS_GLOVED_FINGER)) { + LOGI(this, "glove mode is not supported\n"); + goto end; + } + + /* Set glove finger register in touch ic */ + if (!this->cover.status) { + rc = clearpad_set_glove_finger_reg(this, enable); + if (rc) + goto end; + rc = clearpad_set_doze_holdoff(this); + if (rc) + LOGE(this, "failed to set Doze Holdoff\n"); + } +end: + if (rc) + LOGE(this, "failed to set glove mode"); + return rc; +} + +/* + * Smart Cover + */ + +static int clearpad_set_cover_status(struct clearpad_t *this) +{ + int rc = 0; + size_t buf_size = 3; + u8 buf[buf_size]; + + if (!clearpad_is_valid_function(this, SYN_F12_2D) && + !clearpad_is_valid_function(this, SYN_F51_CUSTOM) && + !clearpad_is_valid_function(this, SYN_F54_ANALOG)) { + LOGE(this, "F12, F51 and F54 are required to set cover status"); + rc = -EPERM; + goto end; + } + + switch (this->chip_id) { + case SYN_CHIP_3330: + case SYN_CHIP_332U: + /* F12_2D_CTRL26: Feature Enable */ + rc = clearpad_put_bit(SYNA(this, F12_2D, CTRL, 26), + this->cover.status ? + FEATURE_ENABLE_ENABLE_CLOSED_COVER_DETECTION_MASK : 0, + FEATURE_ENABLE_ENABLE_CLOSED_COVER_DETECTION_MASK); + break; + case SYN_CHIP_3500: + /* F51_CUSTOM_CTRL05.00: Cover */ + rc = clearpad_put( + SYNF(this, F51_CUSTOM, CTRL, + this->reg_offset.f51_ctrl05), + this->cover.status ? + COVER_ENABLE_MASK | COVER_REPORT_FINGER_MASK : 0x00); + break; + default: + LOGE(this, "not supported chip id (0x%02x)\n", this->chip_id); + rc = -EINVAL; + break; + } + if (rc) + goto end; + + /* Set glove finger register in touch ic */ + if (this->cover.status) { + rc = clearpad_set_glove_finger_reg(this, true); + } else { + rc = clearpad_set_glove_finger_reg(this, this->glove.enabled); + } + if (rc) + goto end; + + switch (this->chip_id) { + case SYN_CHIP_3330: + case SYN_CHIP_332U: + /* Report Glove As Finger setting is not needed */ + break; + case SYN_CHIP_3500: + /* F12_2D_CTRL23_02: Report As Finger */ + rc = clearpad_get_block(SYNA(this, F12_2D, CTRL, 23), buf, + buf_size); + if (rc) + goto end; + + BIT_SET(buf[F12_2D_CTRL_REPORT_AS_FINGER], + REPORT_AS_FINGER_GLOVED_FINGER, + this->cover.status ? 1 : 0); + rc = clearpad_put_block(SYNA(this, F12_2D, CTRL, 23), buf, + buf_size); + if (rc) + goto end; + break; + default: + LOGE(this, "not supported chip id (0x%02x)\n", this->chip_id); + break; + } + + /* F54_ANALOG_CMD00: Analog Command */ + rc = clearpad_put( + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + ANALOG_COMMAND_FORCE_UPDATE_MASK); + if (rc) + goto end; + + rc = clearpad_set_doze_holdoff(this); + if (rc) + LOGE(this, "failed to set Doze Holdoff\n"); +end: + if (rc) + LOGE(this, "failed to set cover status"); + + return rc; +} + +static int clearpad_set_cover_window(struct clearpad_t *this) +{ + int rc; + u8 block_buf[8]; + + if (this->chip_id == SYN_CHIP_3330 || + this->chip_id == SYN_CHIP_332U) { + /* Cover window size register F12_2D_CTRL25 */ + if (!clearpad_is_valid_function(this, SYN_F12_2D)) { + LOGE(this, "F12_2D is required to set cover window"); + rc = -EPERM; + goto end; + } + + block_buf[0] = this->cover.win_left; + block_buf[1] = this->cover.win_left >> 8; + block_buf[2] = this->cover.win_right; + block_buf[3] = this->cover.win_right >> 8; + block_buf[4] = this->cover.win_top; + block_buf[5] = this->cover.win_top >> 8; + block_buf[6] = this->cover.win_bottom; + block_buf[7] = this->cover.win_bottom >> 8; + + /* F12_2D_CTRL25: Closed Xmin/Xmax/Ymin/Ymax */ + rc = clearpad_put_block(SYNA(this, F12_2D, CTRL, 25), + block_buf, 8); + if (rc) + goto end; + } else { + /* Cover window size register F51_CUSTOM_CTRL05 */ + if (!clearpad_is_valid_function(this, SYN_F51_CUSTOM)) { + LOGE(this, "F51 is required to set cover window"); + rc = -EPERM; + goto end; + } + + block_buf[0] = this->cover.win_left; + block_buf[1] = this->cover.win_left >> 8; + block_buf[2] = this->cover.win_top; + block_buf[3] = this->cover.win_top >> 8; + block_buf[4] = this->cover.win_right; + block_buf[5] = this->cover.win_right >> 8; + block_buf[6] = this->cover.win_bottom; + block_buf[7] = this->cover.win_bottom >> 8; + + /* F51_CUSTOM_CTRL05.01: Cover Left/Top/Right/Bottom */ + rc = clearpad_put_block( + SYNF(this, F51_CUSTOM, CTRL, + this->reg_offset.f51_ctrl05 + + SYN_COVER_RECTANGLE_OFFSET), + block_buf, 8); + if (rc) + goto end; + } + + rc = clearpad_set_cover_status(this); +end: + if (rc) + LOGE(this, "failed to set cover window"); + return rc; +} + +/* + * Stamina (Report rate) + */ + +/* need LOCK(&this->lock) */ +static int clearpad_change_report_rate(struct clearpad_t *this) +{ + int rc = 0; + bool enable = false, report_status = false; + u8 buf; + + if (!this) { + LOGE(this, "failed get this data\n"); + rc = -EINVAL; + goto exit; + } + + if (!this->stamina.change_reportrate.supported) + goto exit; + + if (this->stamina.change_reportrate.mode > 0) + enable = true; + else + enable = false; + + switch (this->chip_id) { + case SYN_CHIP_3330: + case SYN_CHIP_332U: + rc = clearpad_get(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), &buf); + if (rc) { + LOGE(this, "failed to get status\n"); + rc = -EINVAL; + goto exit; + } + if (BIT_GET(buf, DEVICE_CONTROL_REPORT_RATE)) + report_status = true; + + if (enable == report_status) { + LOGI(this, "report rate is already %s\n", + this->stamina.enabled ? "enabled" : "disabled"); + goto exit; + } + + rc = clearpad_put_bit( + SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), + enable ? DEVICE_CONTROL_REPORT_RATE_MASK : 0, + DEVICE_CONTROL_REPORT_RATE_MASK); + if (rc) { + LOGE(this, "failed to set report rate\n"); + goto exit; + } + LOGI(this, "report rate %s\n", + enable ? "enable" : "disable"); + + clearpad_set_delay(SYN_WAIT_TIME_AFTER_CHANGE_REPORTRATE); + + /* F54_ANALOG_CMD00: Analog Command */ + rc = clearpad_put(SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + ANALOG_COMMAND_FORCE_UPDATE_MASK); + if (rc) { + LOGE(this, "failed to set force update\n"); + goto exit; + } + + break; + case SYN_CHIP_3500: + /* F51_CUSTOM_CTRL30.06[1:0]: External Report Rate Selection */ + rc = clearpad_put( + SYNF(this, F51_CUSTOM, CTRL, + this->reg_offset.f51_ctrl30 + + SYN_EXTERNAL_REPORT_RATE_OFFSET), + this->stamina.change_reportrate.mode); + if (rc) { + LOGE(this, "failed to set change report rate\n"); + goto exit; + } + + break; + default: + LOGE(this, "not supported on chip id 0x0%2x\n", this->chip_id); + goto exit; + } + +exit: + return rc; +} + +/* + * Stamina + */ + +/* need LOCK(&this->lock) */ +static int clearpad_set_stamina_mode(struct clearpad_t *this) +{ + int rc = 0; + + if (!this->stamina.supported) + goto end; + + rc = clearpad_change_report_rate(this); + if (rc) + LOGE(this, "failed to change report rate\n"); + +end: + return rc; +} + +/* + * Wakeup gesture (EW) + */ + +/* need LOCK(&this->lock) */ +static int clearpad_enable_wakeup_gesture(struct clearpad_t *this) +{ + u8 buf[F12_2D_CTRL_RPT_REG_MAX]; + int rc = 0; + + if (!clearpad_is_valid_function(this, SYN_F12_2D)) { + LOGE(this, "F12 is required to set wakeup gesture\n"); + rc = -EPERM; + goto end; + } + + if (this->chip_id == SYN_CHIP_3330 && + !clearpad_is_valid_function(this, SYN_F54_ANALOG)) { + LOGE(this, "F54 is required to set wakeup gesture\n"); + rc = -EPERM; + goto end; + } + + /* F01_RMI_CTRL00: Device Control */ + rc = clearpad_put_bit( + SYNF(this, F01_RMI, CTRL, this->reg_offset.f01_ctrl00), + DEVICE_CONTROL_CONFIGURED_MASK, + DEVICE_CONTROL_CONFIGURED_MASK); + if (rc) { + LOGE(this, "failed to set device configured bit\n"); + goto end; + } + clearpad_set_delay(SYN_WAIT_TIME_AFTER_REGISTER_ACCESS); + + if (this->chip_id == SYN_CHIP_3330 && + this->wakeup_gesture.use_workaround_for_felica) { + /* F12_2D_CTRL33_00: Multi-Finger Moisture General */ + rc = clearpad_get_block(SYNA(this, F12_2D, CTRL, 33), + buf, 1); + if (rc) { + LOGE(this, "failed to read enable multifinger " + "moisture register\n"); + goto end; + } + BIT_SET(buf[0], ENABLE_MULTIFINGER_MOISTURE, 0); + rc = clearpad_put_block(SYNA(this, F12_2D, CTRL, 33), + buf, 1); + if (rc) { + LOGE(this, "failed to set enable multifinger " + "moisture bit\n"); + goto end; + } + + /* F54_ANALOG_CTRL113_00: General Control */ + rc = clearpad_put_bit(SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl113), + DISABLE_HYBRID_BASELINE_MASK, + DISABLE_HYBRID_BASELINE_MASK); + if (rc) { + LOGE(this, "failed to set disable hybrid baseline\n"); + goto end; + } + /* F54_ANALOG_CTRL109_00: General Control */ + rc = clearpad_put(SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl109), + BASELINE_CORRECTION_MODE_MASK); + if (rc) { + LOGE(this, "failed to set baseline correction mode\n"); + goto end; + } + /* F54_ANALOG_CTRL147_00: Disable Hybrid CBC Auto Correction */ + rc = clearpad_put_bit(SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl147), + DISABLE_HYBRID_CBC_AUTO_CORRECTION_MASK, + DISABLE_HYBRID_CBC_AUTO_CORRECTION_MASK); + if (rc) { + LOGE(this, "failed to set Disable Hybrid CBC Auto " + "Correction\n"); + goto end; + } + /* F54_ANALOG_CTRL214_00: General Control */ + rc = clearpad_put_bit(SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl214), 0, + ENABLE_HYBRID_CHARGER_NOISE_MITIGATION_MASK); + if (rc) { + LOGE(this, "failed to set enable hybrid charger noise " + "mitigation\n"); + goto end; + } + /* F54_ANALOG_CMD00: Analog Command */ + rc = clearpad_put( + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + ANALOG_COMMAND_FORCE_UPDATE_MASK); + if (rc) { + LOGE(this, "failed to set force update\n"); + goto end; + } + clearpad_set_delay(50); + } + /* F12_2D_CTRL20_01: Report Flags */ + rc = clearpad_get_block(SYNA(this, F12_2D, CTRL, 20), + buf, F12_2D_CTRL_RPT_REG_MAX); + if (rc) { + LOGE(this, "failed to read control report register\n"); + goto end; + } + BIT_SET(buf[F12_2D_CTRL_RPT_FLAG], + REPORT_FLAGS_REPORT_WAKEUP_GESTURE_ONLY, 1); + rc = clearpad_put_block(SYNA(this, F12_2D, CTRL, 20), + buf, F12_2D_CTRL_RPT_REG_MAX); + if (rc) { + LOGE(this, "failed to enable wakeup gesture\n"); + goto end; + } + + /* F12_2D_CTRL27_00: Wakeup Gesture Enable */ + rc = clearpad_put(SYNA(this, F12_2D, CTRL, 27), + WAKEUP_GESTURE_ENABLE_DOUBLE_TAP_MASK); + if (rc) { + LOGE(this, "failed to enable double tap gesture\n"); + goto end; + } +end: + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_disable_wakeup_gesture(struct clearpad_t *this) +{ + u8 buf[F12_2D_CTRL_RPT_REG_MAX]; + int rc = 0; + + if (!clearpad_is_valid_function(this, SYN_F12_2D)) { + LOGE(this, "F12 is required to set wakeup gesture\n"); + rc = -EPERM; + goto end; + } + + switch (this->chip_id) { + case SYN_CHIP_3330: + case SYN_CHIP_332U: + LOGD(this, "already disabled wakeup gesture by HW reset\n"); + break; + case SYN_CHIP_3500: + /* F12_2D_CTRL27_00: Wakeup Gesture Enable */ + rc = clearpad_put(SYNA(this, F12_2D, CTRL, 27), 0); + if (rc) { + LOGE(this, "failed to disable double tap gesture\n"); + goto end; + } + + /* F12_2D_CTRL20_01: Report Flags */ + rc = clearpad_get_block(SYNA(this, F12_2D, CTRL, 20), + buf, F12_2D_CTRL_RPT_REG_MAX); + if (rc) { + LOGE(this, "failed to read control report register\n"); + goto end; + } + BIT_CLEAR(buf[F12_2D_CTRL_RPT_FLAG], + REPORT_FLAGS_REPORT_WAKEUP_GESTURE_ONLY); + rc = clearpad_put_block(SYNA(this, F12_2D, CTRL, 20), + buf, F12_2D_CTRL_RPT_REG_MAX); + if (rc) { + LOGE(this, "failed to disable report gesture\n"); + goto end; + } + + /* F54_ANALOG_CMD00: Analog Command */ + rc = clearpad_put( + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + ANALOG_COMMAND_FORCE_CALIBRATION_MASK); + if (rc) + LOGE(this, "failed to force calibrate\n"); + break; + default: + LOGE(this, "not supported on chip id 0x0%2x\n", this->chip_id); + break; + } +end: + return rc; +} + +static int clearpad_set_feature_settings(struct clearpad_t *this) +{ + int rc; + + rc = clearpad_set_charger(this); + if (rc) + goto end; + rc = clearpad_set_pen(this); + if (rc) + goto end; + rc = clearpad_set_glove_mode(this, this->glove.enabled); + if (rc) + goto end; + if (this->cover.enabled) + rc = clearpad_set_cover_window(this); + if (rc) + goto end; + if (this->stamina.enabled) + rc = clearpad_set_stamina_mode(this); + if (rc) + goto end; + rc = clearpad_set_doze_holdoff(this); +end: + return rc; +} + +static int clearpad_gen_offsets(u8 desc, u8 offset_from, u8 reg_array[], + int reg_from, int array_size) +{ + const u8 unused_offset = 0xFF; + int offset = offset_from; + int bit, reg = reg_from; + + for (bit = 0; bit < 8 && reg < array_size; bit++) + reg_array[reg++] = desc & (1 << bit) ? offset++ : unused_offset; + + if (reg >= SYN_MAX_CTRL_VALUE) { + WARN_ON(reg >= SYN_MAX_CTRL_VALUE); + offset = -1; + }; + + return offset; +} + +static int clearpad_query_regs(struct clearpad_t *this, + enum clearpad_function_e func, + u8 query, u8 reg_array[], int reg_array_size) +{ + /* register presence descriptors: maximum 32 bytes */ + const int max_size_presence = 32; + /* size of register structure: 1 or 3 bytes */ + const int max_size_bytes = 3; + + u8 buffer[max_size_bytes + max_size_presence]; + u8 size_presence; + int offset = 0; + int size_num_bytes; + int rc, i; + + rc = clearpad_get(SYNI(this, func, SYN_TYPE_QUERY, query), + &size_presence); + if (rc) + goto end; + + BUG_ON(size_presence > max_size_presence); + + /* The size of the control register structure is currently unused, + * but we need to find out how many bytes are allocated for it. + */ + rc = clearpad_get(SYNI(this, func, SYN_TYPE_QUERY, query + 1), buffer); + if (rc) + goto end; + + size_num_bytes = buffer[0] == 0 ? max_size_bytes : 1; + + rc = clearpad_get_block(SYNI(this, func, SYN_TYPE_QUERY, query + 1), + buffer, size_num_bytes + size_presence); + if (rc) + goto end; + + for (i = 0; i < size_presence; i++) { + offset = clearpad_gen_offsets(buffer[i + size_num_bytes], + offset, reg_array, i * 8, reg_array_size); + if (offset < 0) { + HWLOGW(this, "register id is out of range\n"); + goto end; + } + } +end: + return rc; +} + +static void clearpad_log_offsets(struct clearpad_t *this, const char *header, + enum clearpad_function_e func, + u8 reg_array[], int reg_array_size) +{ + int i; + + LOGD(this, "%s(%d)\n", header, func); + for (i = 0; i < reg_array_size; i++) + LOGD(this, "[%.2d]=0x%02hX\n", i, (u16)reg_array[i]); +} + +static int clearpad_init_reg_offsets(struct clearpad_t *this, + enum clearpad_function_e func) +{ + const u8 query_query_register_presence = 0x01; + const u8 query_ctrl_register_presence = 0x04; + const u8 query_data_register_presence = 0x07; + u8 query_desc; + int rc; + + rc = clearpad_get(SYNI(this, func, SYN_TYPE_QUERY, 0x00), &query_desc); + if (rc) + goto end; + + if (!(BIT_GET(query_desc, GENERAL_HAS_REGISTER_DESCRIPTOR))) { + LOGE(this, "register descriptors not supported!\n"); + rc = -EINVAL; + goto end; + } + + rc = clearpad_query_regs(this, func, query_query_register_presence, + this->pdt[func].offset[SYN_TYPE_QUERY], + ARRAY_SIZE(this->pdt[func].offset[SYN_TYPE_QUERY])); + if (rc) + goto end; + + clearpad_log_offsets(this, "query offsets", func, + this->pdt[func].offset[SYN_TYPE_QUERY], + ARRAY_SIZE(this->pdt[func].offset[SYN_TYPE_QUERY])); + + rc = clearpad_query_regs(this, func, query_ctrl_register_presence, + this->pdt[func].offset[SYN_TYPE_CTRL], + ARRAY_SIZE(this->pdt[func].offset[SYN_TYPE_CTRL])); + if (rc) + goto end; + + clearpad_log_offsets(this, "control offsets", func, + this->pdt[func].offset[SYN_TYPE_CTRL], + ARRAY_SIZE(this->pdt[func].offset[SYN_TYPE_CTRL])); + + rc = clearpad_query_regs(this, func, query_data_register_presence, + this->pdt[func].offset[SYN_TYPE_DATA], + ARRAY_SIZE(this->pdt[func].offset[SYN_TYPE_DATA])); + + clearpad_log_offsets(this, "data offsets", func, + this->pdt[func].offset[SYN_TYPE_DATA], + ARRAY_SIZE(this->pdt[func].offset[SYN_TYPE_DATA])); +end: + return rc; +} + +static int clearpad_prepare_f12_2d(struct clearpad_t *this) +{ + int rc; + int i; + u8 buf[4]; + enum registers { + REG_X_LSB, + REG_X_MSB, + REG_Y_LSB, + REG_Y_MSB, + }; + + rc = clearpad_init_reg_offsets(this, SYN_F12_2D); + if (rc) + goto end; + + /* F12_2D_CTRL08_[00-03]: Maximum XY Coordinate */ + rc = clearpad_get_block(SYNA(this, F12_2D, CTRL, 8), buf, sizeof(buf)); + if (rc) + goto end; + this->extents.x_max = (buf[REG_X_LSB] | (buf[REG_X_MSB] << 8)); + this->extents.y_max = (buf[REG_Y_LSB] | (buf[REG_Y_MSB] << 8)); + + WARN_ON(this->extents.preset_x_max != this->extents.x_max); + WARN_ON(this->extents.preset_y_max != this->extents.y_max); + + /* F12_2D_CTRL23_01: Max Number Of Reported Objects */ + rc = clearpad_get_block(SYNA(this, F12_2D, CTRL, 23), buf, 2); + if (rc) + goto end; + this->extents.n_fingers = buf[F12_2D_CTRL_MAX_OBJ_REPORT]; + + /* F12_2D_CTRL28: Data Reporting Enable Mask */ + rc = clearpad_get(SYNA(this, F12_2D, CTRL, 28), buf); + if (rc) + goto end; + + this->extents.n_bytes_per_object = 0; + + for (i = 0; i < BITS_PER_BYTE; i++) + this->extents.n_bytes_per_object += (buf[0] & BIT(i)) >> i; + + LOGI(this, "x_max=%d, y_max=%d, n_fingers=%d, n_bytes_per_object=%d\n", + this->extents.x_max, this->extents.y_max, + this->extents.n_fingers, + this->extents.n_bytes_per_object); + + rc = clearpad_set_feature_settings(this); + +end: + return rc; +} + +static void clearpad_firmware_reset(struct clearpad_t *this) +{ + this->flash.data.pos = 0; + this->flash.config.pos = 0; + if (this->flash.fw) { + release_firmware(this->flash.fw); + this->flash.fw = NULL; + } + HWLOGI(this, "firmware image has been reset\n"); +} + +static int clearpad_initialize(struct clearpad_t *this) +{ + int rc; + u8 fw_info[SYN_DEVICE_INFO_SIZE], bl_ver[SYN_DEVICE_BL_INFO_SIZE]; + u8 device_status, fw_status; + struct clearpad_device_info_t *info = &this->device_info; + u8 product_id[HEADER_PRODUCT_ID_SIZE]; + + LOGI(this, "initialize device\n"); + + /* read device product id */ + /* F01_RMI_QUERY11: Product ID Query */ + rc = clearpad_get_block(SYNF(this, F01_RMI, QUERY, + this->reg_offset.f01_query11), + product_id, HEADER_PRODUCT_ID_SIZE); + if (rc) + goto end; + memcpy(info->product_id, product_id, HEADER_PRODUCT_ID_SIZE); + + clearpad_update_chip_id(this); + + clearpad_set_is_sol(this); + + /* read device status */ + /* F01_RMI_DATA00: Device Status */ + rc = clearpad_get(SYNF(this, F01_RMI, DATA, + this->reg_offset.f01_data00), + &device_status); + if (rc) + goto end; + + LOGI(this, "device status 0x%02x\n", device_status); + + if (this->is_sol) { + /* F34_FLASH_QUERY00: Bootloader Revision */ + rc = clearpad_get_block(SYNF(this, F34_FLASH, QUERY, + this->reg_offset.f34_query00), + bl_ver, SYN_DEVICE_BL_INFO_SIZE); + if (rc) + goto end; + + /* Flash memory management, Version 1 */ + /* Bootloader Revision is stored as character */ + LOGI(this, "bl[0]:%c\n", bl_ver[0]); + LOGI(this, "bl[1]:%c\n", bl_ver[1]); + if (bl_ver[1] >= 0x30 && bl_ver[1] <= 0x39) { + /* change character to decimal */ + bl_ver[1] -= 0x30; + bl_ver[0] = 0x00; /* minor value is 0 */ + } + LOGI(this, "bootloader revision %d.%03d\n", bl_ver[1], bl_ver[0]); + } else { + /* F34_FLASH_QUERY01: Bootloader Revision */ + rc = clearpad_get_block(SYNF(this, F34_FLASH, QUERY, + this->reg_offset.f34_query01), + bl_ver, SYN_DEVICE_BL_INFO_SIZE); + if (rc) + goto end; + + /* Flash memory management, Version 2 */ + /* Bootloader Revision is stored as decimal */ + LOGI(this, "bootloader revision %d.%03d\n", bl_ver[1], bl_ver[0]); + } + + switch (BIT_GET(device_status, DEVICE_STATUS_CODE)) { + case DEVICE_STATUS_CODE_CONFIGURATION_CRC_FAILURE: + case DEVICE_STATUS_CODE_FIRMWARE_CRC_FAILURE: + case DEVICE_STATUS_CODE_CRC_IN_PROGRESS: + /* force firmware recovery */ + memset(fw_info, 0, sizeof(fw_info)); + break; + default: + if (bl_ver[1] >= clearpad_bootloader_version_dec[BV7]) { + /* F34_FLASH_DATA00: Status */ + rc = clearpad_get(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data00), + &fw_status); + if (rc) + goto end; + + LOGI(this, "FW status 0x%02x\n", fw_status); + + switch (BIT_GET(fw_status, STATUS_FLASH_STATUS)) { + case STATUS_FLASH_STATUS_BAD_PARTITION_TABLE: + case STATUS_FLASH_STATUS_CHECKSUM_FAILED: + /* force firmware recovery */ + memset(fw_info, 0, sizeof(fw_info)); + break; + default: + break; + } + rc = clearpad_get_block( + SYNF(this, F34_FLASH, CTRL, + this->reg_offset.f34_ctrl00), fw_info, + FLASH_READ_FIRMWARE_INFO_SIZE_BL7X); + if (rc) + goto end; + + info->customer_family = fw_info[0]; + info->firmware_revision_major = fw_info[1]; + info->firmware_revision_minor = fw_info[2]; + info->firmware_revision_extra = fw_info[3]; + info->analog_id = fw_info[4]; + info->boot_loader_version_minor = bl_ver[0]; + info->boot_loader_version_major = bl_ver[1]; + } else if (bl_ver[1] >= clearpad_bootloader_version_dec[BV6]) { + rc = clearpad_get_block(SYNF(this, F34_FLASH, CTRL, + this->reg_offset.f34_ctrl00), + fw_info, FLASH_READ_FIRMWARE_INFO_SIZE_BL6X); + if (rc) + goto end; + + info->customer_family = fw_info[0]; + info->firmware_revision_major = fw_info[1]; + info->firmware_revision_minor = fw_info[2]; + info->firmware_revision_extra = fw_info[3]; + info->boot_loader_version_minor = bl_ver[0]; + info->boot_loader_version_major = bl_ver[1]; + } + break; + } + + /* overwrite default settings with actual chip settings */ + clearpad_touch_config_dt_for_chip_id(this, this->chip_id); + + if (this->state != SYN_STATE_RUNNING) { + LOGI(this, "device mid %d, prop %d, family 0x%02x, " + "rev 0x%02x.%02x, extra 0x%02x, aid 0x%02x\n", + info->manufacturer_id, info->product_properties, + info->customer_family, info->firmware_revision_major, + info->firmware_revision_minor, + info->firmware_revision_extra, + info->analog_id); + LOGI(this, "product id '%s'\n", + clearpad_s(info->product_id, HEADER_PRODUCT_ID_SIZE)); + } + + if (clearpad_is_valid_function(this, SYN_F12_2D)) { + rc = clearpad_prepare_f12_2d(this); + if (rc) + goto end; + } + + /* set device configured bit */ + /* F01_RMI_CTRL00: Device Control */ + rc = clearpad_put_bit(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), + DEVICE_CONTROL_CONFIGURED_MASK, + DEVICE_CONTROL_CONFIGURED_MASK); + if (rc) + goto end; + + clearpad_set_delay(SYN_WAIT_TIME_AFTER_REGISTER_ACCESS); + + snprintf(this->result_info, sizeof(this->result_info), + "%s, family 0x%02x, fw rev 0x%02x.%02x, extra 0x%02x, (%s)\n", + clearpad_s(this->device_info.product_id, + HEADER_PRODUCT_ID_SIZE), + this->device_info.customer_family, + this->device_info.firmware_revision_major, + this->device_info.firmware_revision_minor, + this->device_info.firmware_revision_extra, + this->flash_requested ? "fw updated" : "no fw update"); + this->flash_requested = false; + + /* notify end of task */ + LOGI(this, "result: %s", this->result_info); +end: + /* inform running state */ + this->state = SYN_STATE_RUNNING; + return rc; +} + +/* + * @return 1 : initialized + * 0 : not initialized since interrupt.count != 0 + * negative value : error + * need LOCK(&this->lock) + */ +static int clearpad_initialize_if_first_event(struct clearpad_t *this, + u8 *interrupt_status, + u8 *device_status) +{ + struct timespec ts; + int rc = 0; + int retry; + + if (this->interrupt.count != 0) + goto read_interrupt; + + get_monotonic_boottime(&ts); + HWLOGI(this, "read first event (power=%s active=%s) @ %ld.%06ld\n", + touchctrl_is_touch_powered(this) ? "OK" : "NG", + this->dev_active ? "true" : "false", + ts.tv_sec, ts.tv_nsec); + + for (retry = 0; retry < SYN_RETRY_NUM; retry++) { + clearpad_set_delay(this->reset.delay_for_powerup_ms); + rc = clearpad_set_page(this, 0); + if (!rc) + goto read_pdt; + } + HWLOGE(this, "failed to set page 0\n"); + goto err_in_set_page; + +read_pdt: + rc = clearpad_read_pdt(this); + if (rc) { + HWLOGE(this, "failed to read pdt\n"); + goto err_in_read_pdt; + } + +read_interrupt: + /* F01_RMI_DATA01: Interrupt Status */ + rc = clearpad_get(SYNF(this, F01_RMI, DATA, + this->reg_offset.f01_data01), interrupt_status); + if (rc) { + HWLOGE(this, "failed to read interrupt status\n"); + goto end; + } + /* F01_RMI_DATA00: Device Status */ + rc = clearpad_get(SYNF(this, F01_RMI, DATA, + this->reg_offset.f01_data00), device_status); + if (rc) { + HWLOGE(this, "failed to read device status\n"); + goto end; + } + if (this->interrupt.count != 0) + goto end; + + rc = clearpad_initialize(this); + if (rc) { + HWLOGE(this, "failed to initialize (rc=%d)\n", rc); + goto end; + } + rc = 1; +end: + return rc; + +err_in_set_page: +err_in_read_pdt: + /* this workaround will be replaced by HW reset */ + HWLOGE(this, "retry reading interrupt status as workaround\n"); + /* F01_RMI_DATA01: Interrupt Status */ + rc = clearpad_get(SYNF(this, F01_RMI, DATA, + this->reg_offset.f01_data01), interrupt_status); + if (rc) + HWLOGE(this, "failed to read interrupt status\n"); + return -EIO; +} + +/* + * @return 1 : initialized + * 0 : not initialized since interrupt.count != 0 + * negative value : error + * need LOCK(&this->lock) + */ +static int clearpad_handle_if_first_event(struct clearpad_t *this) +{ + int rc = 0; + + if (this->interrupt.count != 0) + goto end; + + get_monotonic_boottime(&this->interrupt.handle_first_event_ts); + + HWLOGI(this, "first event (power=%s active=%s) @ %ld.%06ld\n", + touchctrl_is_touch_powered(this) ? "OK" : "NG", + this->dev_active ? "true" : "false", + this->interrupt.handle_first_event_ts.tv_sec, + this->interrupt.handle_first_event_ts.tv_nsec); + + rc = clearpad_process_irq(this); + if (rc) + HWLOGE(this, "failed to read interrupt, " + "but ignore (rc=%d)\n", rc); + + if (!this->post_probe.done) + goto end; + + if (this->force_sleep == FSMODE_ONESHOT) { + LOGI(this, "clear force sleep mode\n"); + this->force_sleep = FSMODE_OFF; + } + if (this->force_sleep != FSMODE_OFF) { + HWLOGI(this, "force sleep mode\n"); + if (this->dev_active) { + rc = clearpad_set_suspend_mode(this); + if (rc) + HWLOGE(this, "failed to force sleep mode\n"); + } + } else if (!this->dev_active) { + rc = clearpad_set_resume_mode(this); + if (rc) + HWLOGE(this, "failed to resume (rc=%d)\n", rc); + } + rc = rc < 0 ? rc : 1; +end: + return rc; +} + +void clearpad_parse_descriptor(struct clearpad_t *this, u32 container_addr) +{ + struct descriptor_t desc; + struct clearpad_flash_t *f = &this->flash; + const u8 *data = this->flash.fw->data; + u8 *cc_content_addr; + + desc.container_id = (u16)le32_to_cpu(*(u32 *) + (data + container_addr + CON_ID_OFFSET)); + LOGD(this, "container_addr=%d", container_addr); + desc.content_len = le32_to_cpu(*(u32 *)(data + + container_addr + CON_CONTENT_LENGTH_OFFSET)); + desc.content_start_addr = le32_to_cpu(*(u32 *) + (data + container_addr + CON_CONTENT_ADDRESS_OFFSET)); + LOGD(this, + "container_id=%d, desc.content_len=%d, desc.content_start_addr=%d\n", + desc.container_id, desc.content_len, desc.content_start_addr); + + cc_content_addr = (u8 *)(data + desc.content_start_addr); + /* Set up block info */ + switch (desc.container_id) { + case CID_CORE_CODE_CONTAINER: + f->data.length = desc.content_len; + f->data.data = cc_content_addr; + HWLOGD(this, "core code: data (length %u)\n", f->data.length); + break; + case CID_CORE_CONFIGURATION_CONTAINER: + f->config.length = desc.content_len; + f->config.data = cc_content_addr; + f->customer_family = *cc_content_addr + + CONFIG_CUSTOMER_FAMILY_OFFSET; + f->firmware_revision_major = + *(cc_content_addr + CONFIG_FIRMWARE_REVISION_MAJOR_OFFSET); + f->firmware_revision_minor = + *(cc_content_addr + CONFIG_FIRMWARE_REVISION_MINOR_OFFSET); + f->firmware_revision_extra = + *(cc_content_addr + CONFIG_FIRMWARE_REVISION_EXTRA_OFFSET); + HWLOGD(this, + "core config: family 0x%02x, rev 0x%02x.%02x.0x%02x\n", + f->customer_family, f->firmware_revision_major, + f->firmware_revision_minor, f->firmware_revision_extra); + break; + default: + HWLOGD(this, "core(container id %d)\n", desc.container_id); + break; + } +} + +void clearpad_parse_container(struct clearpad_t *this) +{ + int i, j = 0; + u8 cont_count; + u32 offset, top_container_addr, container_addr, cont_len; + const u8 *data = this->flash.fw->data; + + HWLOGI(this, "start parse\n"); + offset = le32_to_cpu(*(u32 *)(data + CON_TOP_START_ADDRESS_OFFSET)); + LOGD(this, "offset=%d\n", offset); + + cont_len = le32_to_cpu(*(u32 *)(data + offset + + CON_CONTENT_LENGTH_OFFSET)); + cont_count = cont_len / CON_START_ADDRESS_SIZE; + top_container_addr = le32_to_cpu(*(u32 *)(data + offset + + CON_CONTENT_ADDRESS_OFFSET)); + LOGD(this, "cont_len=%d, top_container_addr=%d\n", + cont_len, top_container_addr); + + for (i = 0; i < cont_count; i++) { + container_addr = + le32_to_cpu(*(u32 *)(data + top_container_addr + j)); + j += CON_START_ADDRESS_SIZE; + LOGD(this, "container_addr=%d, i=%d, j=%d\n", + container_addr, i, j); + clearpad_parse_descriptor(this, container_addr); + } +} + +static void clearpad_parse_firmware(struct clearpad_t *this) +{ + const u8 *data; + struct clearpad_flash_t *f = &this->flash; + + data = this->flash.fw->data; + LOGI(this, "data:%p\n", data); + + /* Set up data block info */ + f->data.length = le32_to_cpu(*(u32 *)(data + 8)); + f->data.blocks = (f->data.length / 16) + !!(f->data.length % 16); + f->data.data = data + HEADER_SIZE; + f->data.pos = 0; + LOGI(this, "DATA: length=%x blocks=%x data=%p\n", + f->data.length, f->data.blocks, f->data.data); + + /* Set up configuration block info */ + f->config.length = le32_to_cpu(*(u32 *)(data + 12)); + f->config.blocks = (f->config.length / 16) + !!(f->config.length % 16); + f->config.data = data + HEADER_SIZE + f->data.length; + f->config.pos = 0; + LOGI(this, "CONFIG: length=%x blocks=%x data=%p\n", + f->config.length, f->config.blocks, f->config.data); + + /* family Rev major minor extra info */ + f->customer_family = + *(data + f->data.length + HEADER_SIZE + CONFIG_CUSTOMER_FAMILY_OFFSET); + f->firmware_revision_major = + *(data + f->data.length + HEADER_SIZE + CONFIG_FIRMWARE_REVISION_MAJOR_OFFSET); + f->firmware_revision_minor = + *(data + f->data.length + HEADER_SIZE + CONFIG_FIRMWARE_REVISION_MINOR_OFFSET); + f->firmware_revision_extra = + *(data + f->data.length + HEADER_SIZE + CONFIG_FIRMWARE_REVISION_EXTRA_OFFSET); + LOGI(this, + "core config: family 0x%02x, rev 0x%02x.%02x.0x%02x\n", + f->customer_family, f->firmware_revision_major, + f->firmware_revision_minor, f->firmware_revision_extra); + +} + +static ssize_t clearpad_get_firmware(struct clearpad_t *this, char *filename) +{ + int rc; + struct clearpad_flash_t *f = &this->flash; + + rc = request_firmware(&f->fw, filename, this->bdata->dev); + if (rc || !f->fw) { + HWLOGE(this, "fw request failed (rc=%d, fw=%p, name=%s)\n", + rc, f->fw, filename); + rc = -EINVAL; + goto end; + } + + f->format_version = f->fw->data[HEADER_VERSION_OFFSET]; + if (f->format_version >= BV7) { + clearpad_parse_container(this); + } else if (f->format_version >= BV6) { + clearpad_parse_firmware(this); + } else { + HWLOGE(this, "bootloader version not supported\n"); + rc = -ENODEV; + goto end; + } + + HWLOGD(this, "DATA: length=%d blocks=%d\n", + f->data.length, f->data.blocks); + HWLOGD(this, "CONFIG: length=%d blocks=%d\n", + f->config.length, f->config.blocks); + HWLOGI(this, "firmware image size=%zu\n", this->flash.fw->size); +end: + return rc; +} + +static int clearpad_get_pca_module_id(struct clearpad_t *this) +{ + u8 pca_block_data[SYN_PCA_BLOCK_SIZE]; + int i; + int rc; + + for (i = SYN_PCA_BLOCK_NUMBER_MAX; i >= 0; i--) { + rc = clearpad_read_pca_block(this, i, pca_block_data); + if (rc) { + HWLOGE(this, "failed to read pca\n"); + rc = -EIO; + goto end; + } + if (pca_block_data[PCA_DATA] == PCA_FW_INFO && + pca_block_data[PCA_IC] == SYN_CLEARPAD_VENDOR) { + rc = pca_block_data[PCA_MODULE]; + HWLOGI(this, "found module id 0x%02x in pca\n", rc); + goto end; + } + } + HWLOGI(this, "no module id in pca data:%x ic:%x chip:%x module:%x\n", + pca_block_data[PCA_DATA], pca_block_data[PCA_IC], + pca_block_data[PCA_CHIP], pca_block_data[PCA_MODULE]); + rc = -EINVAL; +end: + return rc; +} + +static int clearpad_get_pca_blank_block(struct clearpad_t *this) +{ + u8 pca_block_data[SYN_PCA_BLOCK_SIZE]; + int i; + int empty = -ENOMEM; + int rc; + + for (i = SYN_PCA_BLOCK_NUMBER_MAX; i >= 0; i--) { + rc = clearpad_read_pca_block(this, i, pca_block_data); + if (rc) { + HWLOGE(this, "failed to read pca\n"); + empty = rc; + goto end; + } + if (pca_block_data[PCA_DATA] == PCA_FW_INFO && + pca_block_data[PCA_IC] == SYN_CLEARPAD_VENDOR) + goto end; + if (pca_block_data[PCA_DATA] == PCA_NO_USE) + empty = i; + } +end: + return empty; +} + +/* need LOCK(&this->lock) */ +static int clearpad_judge_firmware_flash(struct clearpad_t *this, + unsigned int id, enum clearpad_flash_command_e flash_cmd) +{ + int pca_module_id; + int rc = 0; + int blank_block; + char filename[SYN_STRING_LENGTH] = ""; + u8 fw_info[SYN_PCA_BLOCK_SIZE] = {PCA_FW_INFO, SYN_CLEARPAD_VENDOR}; + + if (!this->device_info.customer_family) + HWLOGI(this, "device is broken\n"); + else + HWLOGI(this, "device is normal\n"); + + if (flash_cmd != SYN_FORCE_FLASH) { + pca_module_id = clearpad_get_pca_module_id(this); + if (pca_module_id < 0) + rc = pca_module_id; + if (pca_module_id == -EINVAL && + this->device_info.customer_family && this->chip_id) { + blank_block = clearpad_get_pca_blank_block(this); + if (blank_block < 0) { + HWLOGI(this, "no room to write\n"); + goto end_write_pca_block; + } + + fw_info[PCA_CHIP] = this->chip_id; + fw_info[PCA_MODULE] = this->device_info.customer_family; + rc = clearpad_write_pca_block(this, blank_block, + fw_info); + if (rc) { + HWLOGE(this, "failed to write pca\n"); + goto end_write_pca_block; + } + HWLOGI(this, "PCA written in block %d\n", blank_block); + } +end_write_pca_block: + if (pca_module_id > 0) { + id = pca_module_id; + HWLOGI(this, "pca value is valid\n"); + } else if (this->device_info.customer_family > 0) { + id = this->device_info.customer_family; + HWLOGI(this, "use device id since pca was invalid\n"); + } else { + HWLOGE(this, "failed to get valid id\n"); + } + } + + snprintf(filename, SYN_STRING_LENGTH, this->flash.firmware_name, id); + rc = clearpad_get_firmware(this, filename); + if (rc) { + HWLOGE(this, "failed to get firmware (module id 0x%02x)\n", id); + goto end; + } + + HWLOGI(this, "module id (device = 0x%02x, fw file = 0x%02x)\n", + id, this->flash.customer_family); + if (id != this->flash.customer_family) { + HWLOGW(this, "stop flashing, id is not matched with fw file\n"); + goto end; + } + + HWLOGI(this, "old firmware 0x%02x revision 0x%02x.%02x\n", + this->device_info.customer_family, + this->device_info.firmware_revision_major, + this->device_info.firmware_revision_minor); + HWLOGI(this, "new firmware 0x%02x revision 0x%02x.%02x\n", + this->flash.customer_family, + this->flash.firmware_revision_major, + this->flash.firmware_revision_minor); + + if (flash_cmd == SYN_FORCE_FLASH || + (this->device_info.customer_family != + this->flash.customer_family) || + (this->device_info.firmware_revision_major != + this->flash.firmware_revision_major) || + (this->device_info.firmware_revision_minor != + this->flash.firmware_revision_minor)) { + HWLOGI(this, "update of firmware is necessary\n"); + rc = 1; + } else { + HWLOGI(this, "update of firmware is unnecessary\n"); + } +end: + return rc; +} + +/* for Bootloader v6.0 */ +static int clearpad_flash_enable_bl_v6_0(struct clearpad_t *this) +{ + int rc; + u8 buf[SYN_DEVICE_BL_INFO_SIZE]; + + /* read bootloader id */ + rc = clearpad_get_block(SYNF(this, F34_FLASH, QUERY, + this->reg_offset.f34_query00), + buf, sizeof(buf)); + LOG_CHECK(this, "rc=%d\n", rc); + if (rc) + goto end; + + /* write bootloader id to block data */ + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data01), + buf, sizeof(buf)); + LOG_CHECK(this, "rc=%d\n", rc); + if (rc) + goto end; + + clearpad_set_delay(10); + + /* issue a flash program enable */ + this->flash.enter_bootloader_mode = true; + rc = clearpad_put(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data02), + FLASH_CONTROL_ENABLE_FLASH_PROGRAMMING); + LOG_CHECK(this, "rc=%d\n", rc); + if (rc) + goto end; + + this->state = SYN_STATE_FLASH_ENABLE; + clearpad_set_delay(100); + + clearpad_set_irq(this, true); + +end: + return rc; +} + +static int clearpad_flash_program_bl_v6_0(struct clearpad_t *this) +{ + struct clearpad_flash_t *flash = &this->flash; + int rc; + u8 buf[SYN_DEVICE_BL_INFO_SIZE]; + + /* make sure that we are in programming mode and there are no issues */ + rc = clearpad_get(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data03), buf); + if (rc) { + HWLOGE(this, "failed to get flash programming status\n"); + goto end; + } + if (!(BIT_GET(buf[0], STATUS_BL_MODE))) { + HWLOGE(this, "failed enabling flash (%s)\n", + NAME_OF(clearpad_flash_status_name, + BIT_GET(buf[0], STATUS_FLASH_STATUS))); + rc = -EIO; + goto end; + } + + HWLOGI(this, "flashing enabled\n"); + + if (this->state == SYN_STATE_FLASH_ENABLE) { + /* PDT may have changed, re-read */ + rc = clearpad_read_pdt(this); + if (rc) { + HWLOGE(this, "failed to read pdt\n"); + goto end; + } + } + + /* read bootloader id */ + rc = clearpad_get_block(SYNF(this, F34_FLASH, QUERY, + this->reg_offset.f34_query00), buf, sizeof(buf)); + if (rc) + goto end; + + /* write bootloader id to block data */ + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data01), buf, sizeof(buf)); + if (rc) + goto end; + + if (this->state == SYN_STATE_FLASH_ENABLE && + (flash->command == SYN_FORCE_FLASH || + flash->command == SYN_DEFAULT_FLASH)) { + rc = clearpad_put(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data02), + FLASH_CONTROL_ERASE_ALL); + if (rc) + goto end; + this->state = SYN_STATE_FLASH_PROGRAM; + } else if ((this->state == SYN_STATE_FLASH_PROGRAM && + (flash->command == SYN_FORCE_FLASH || + flash->command == SYN_DEFAULT_FLASH))) { + rc = clearpad_put(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data02), + FLASH_CONTROL_ERASE_CONFIGURATION); + if (rc) + goto end; + this->state = SYN_STATE_FLASH_ERASE; + } else { + HWLOGE(this, "invalid state(%s) for command(%s)\n", + NAME_OF(clearpad_state_name, this->state), + NAME_OF(clearpad_flash_command_name, flash->command)); + rc = -EINVAL; + goto end; + } + + HWLOGI(this, "firmware erasing\n"); +end: + return rc; +} + +static int clearpad_flash_update_bl_v6_0(struct clearpad_t *this) +{ + struct clearpad_flash_t *flash = &this->flash; + int rc, len; + const u8 *data; + u8 buf, pid; + struct clearpad_flash_block_t *object; + enum clearpad_state_e finish_state; + u8 pos[2]; + + if (this->state == SYN_STATE_FLASH_ERASE && + (flash->command == SYN_FORCE_FLASH || + flash->command == SYN_DEFAULT_FLASH)) { + object = &(&this->flash)->data; + pid = PID_CORE_CODE; + finish_state = SYN_STATE_FLASH_DATA; + } else if ((this->state == SYN_STATE_FLASH_DATA && + (flash->command == SYN_FORCE_FLASH || + flash->command == SYN_DEFAULT_FLASH))) { + object = &(&this->flash)->config; + pid = PID_CORE_CONFIGURATION; + finish_state = SYN_STATE_FLASH_CONFIG; + } else { + HWLOGE(this, "invalid state(%s) for command(%s)\n", + NAME_OF(clearpad_state_name, this->state), + NAME_OF(clearpad_flash_command_name, flash->command)); + rc = -EINVAL; + goto end; + } + + if (object->pos > 0) + goto write_block_data; + + /* make sure that we are in programming mode and there are no issues */ + rc = clearpad_get(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data03), &buf); + if (rc) { + HWLOGE(this, "failed to get flash programming status\n"); + goto end; + } + + if (!(BIT_GET(buf, STATUS_BL_MODE))) { + HWLOGE(this, "failed flashing in %s (%s)\n", + NAME_OF(clearpad_state_name, this->state), + NAME_OF(clearpad_flash_status_name, + BIT_GET(buf, STATUS_FLASH_STATUS))); + rc = -EIO; + goto end; + } + + object->payload_length = SYN_PAYLOAD_LENGTH; + + /* block # low and high byte */ + pos[0] = object->pos & 0xff; + pos[1] = (object->pos >> 8) & 0xff; + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data00), pos, 2); + if (rc) + goto end; + +write_block_data: + data = object->data + object->pos * 16; + len = object->length - object->pos * 16; + if (len > 16) + len = 16; + + /* write block data */ + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data01), data, len); + if (rc) + goto end; + /* issue a write data block command */ + if (pid == PID_CORE_CODE) + rc = clearpad_put(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data02), + FLASH_CONTROL_WRITE_FIRMWARE_BLOCK); + else + rc = clearpad_put(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data02), + FLASH_CONTROL_WRITE_CONFIGURATION_BLOCK); + if (rc) + goto end; + + if (object->pos % 100 == 0) + LOGI(this, "wrote %d blocks\n", object->pos); + + /* if we've reached the end of the data/configuration flashing */ + if (++object->pos == object->blocks) { + HWLOGI(this, "flash finished on %s\n", + NAME_OF(clearpad_state_name, this->state)); + this->state = finish_state; + } +end: + return rc; +} + +static int clearpad_flash_disable_bl_v6_0(struct clearpad_t *this) +{ + int rc; + u8 buf; + + /* make sure that we are in programming mode and there are no issues */ + rc = clearpad_get(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data03), &buf); + if (rc) + goto end; + + if (!(BIT_GET(buf, STATUS_BL_MODE))) { + HWLOGE(this, "failed flashing config (%s)\n", + NAME_OF(clearpad_flash_status_name, + BIT_GET(buf, STATUS_FLASH_STATUS))); + rc = -EIO; + goto end; + } + + clearpad_set_delay(SYN_WAIT_TIME_AFTER_REGISTER_ACCESS); + + /* send a reset to the device to complete the flash procedure */ + clearpad_reset(this, SYN_SWRESET, __func__); + + HWLOGI(this, "flashing finished, resetting\n"); + this->state = SYN_STATE_FLASH_DISABLE; +end: + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_flash_bl_v6_0(struct clearpad_t *this) +{ + int rc = 0; + + switch (this->state) { + case SYN_STATE_FLASH_IMAGE_SET: + rc = clearpad_flash_enable_bl_v6_0(this); + break; + case SYN_STATE_FLASH_ENABLE: + case SYN_STATE_FLASH_PROGRAM: + rc = clearpad_flash_program_bl_v6_0(this); + break; + case SYN_STATE_FLASH_ERASE: + case SYN_STATE_FLASH_DATA: + rc = clearpad_flash_update_bl_v6_0(this); + break; + case SYN_STATE_FLASH_CONFIG: + rc = clearpad_flash_disable_bl_v6_0(this); + clearpad_firmware_reset(this); + break; + case SYN_STATE_FLASH_DISABLE: + HWLOGE(this, "should be handled by clearpad_initialize\n"); + break; + default: + HWLOGE(this, "invalid state(%s)", + NAME_OF(clearpad_state_name, this->state)); + rc = -EINVAL; + break; + } + return rc; +} + +/* for Bootloader v7.x */ +static int clearpad_flash_enable_bl_v7_x(struct clearpad_t *this) +{ + int rc; + u8 buf[SYN_SINGLE_TRANSACTION_SIZE] = { + PID_BOOTLOADER, 0x00, 0x00, 0x00, 0x00, + FLASH_CMD_ENTER_BOOTLOADER}; + + /* set device configured bit */ + /* F01_RMI_CTRL00: Device Control */ + rc = clearpad_put_bit(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), + DEVICE_CONTROL_CONFIGURED_MASK, + DEVICE_CONTROL_CONFIGURED_MASK); + if (rc) { + HWLOGE(this, "failed to set device configured bit\n"); + goto end; + } + clearpad_set_delay(SYN_WAIT_TIME_AFTER_REGISTER_ACCESS); + + /* read flash program key */ + rc = clearpad_get_block(SYNF(this, F34_FLASH, QUERY, + this->reg_offset.f34_query01), + buf + SYN_FP_KEY_OFFSET, 2); + if (rc) { + HWLOGE(this, "failed to read flash program key\n"); + goto end; + } + + /* issue command to enter bootloader mode with key*/ + this->flash.enter_bootloader_mode = true; + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data01), + buf, sizeof(buf)); + + if (rc) { + HWLOGE(this, "failed to enter bootloader mode\n"); + goto end; + } + + this->state = SYN_STATE_FLASH_ENABLE; + +end: + return rc; +} + +static int clearpad_flash_program_bl_v7_x(struct clearpad_t *this) +{ + struct clearpad_flash_t *flash = &this->flash; + int rc; + u8 buf[SYN_SINGLE_TRANSACTION_SIZE] = { + 0x00, 0x00, 0x00, 0x00, 0x00, FLASH_CMD_ERASE}; + + /* make sure that we are in programming mode and there are no issues */ + rc = clearpad_get(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data00), buf); + if (rc) { + HWLOGE(this, "failed to get flash programming status\n"); + goto end; + } + + if (!(BIT_GET(buf[0], STATUS_BL_MODE))) { + HWLOGE(this, "failed enabling flash (%s)\n", + NAME_OF(clearpad_flash_status_name, + BIT_GET(buf[0], STATUS_FLASH_STATUS))); + rc = -EIO; + goto end; + } + + HWLOGI(this, "flashing enabled\n"); + + if (this->state == SYN_STATE_FLASH_ENABLE) { + /* PDT may have changed, re-read */ + rc = clearpad_read_pdt(this); + if (rc) { + HWLOGE(this, "failed to read pdt\n"); + goto end; + } + } + + /* read flash program key */ + rc = clearpad_get_block(SYNF(this, F34_FLASH, QUERY, + this->reg_offset.f34_query01), + buf + SYN_FP_KEY_OFFSET, 2); + if (rc) { + HWLOGE(this, "failed to flash program key\n"); + goto end; + } + + if (this->state == SYN_STATE_FLASH_ENABLE && + (flash->command == SYN_FORCE_FLASH || + flash->command == SYN_DEFAULT_FLASH)) { + buf[0] = PID_CORE_CODE; + this->state = SYN_STATE_FLASH_PROGRAM; + } else if ((this->state == SYN_STATE_FLASH_PROGRAM && + (flash->command == SYN_FORCE_FLASH || + flash->command == SYN_DEFAULT_FLASH))) { + buf[0] = PID_CORE_CONFIGURATION; + this->state = SYN_STATE_FLASH_ERASE; + } else { + HWLOGE(this, "invalid state(%s) for command(%s)\n", + NAME_OF(clearpad_state_name, this->state), + NAME_OF(clearpad_flash_command_name, flash->command)); + rc = -EINVAL; + goto end; + } + + /* issue command to erase partition with key*/ + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data01), buf, sizeof(buf)); + if (rc) { + HWLOGE(this, "failed to erase partition with key\n"); + goto end; + } + + HWLOGI(this, "firmware erasing\n"); +end: + return rc; +} + +static int clearpad_flash_update_bl_v7_x(struct clearpad_t *this) +{ + struct clearpad_flash_t *flash = &this->flash; + int rc, len; + const u8 *data; + u8 buf, pid; + u8 block_buf[3]; + u16 block_num = 0; + struct clearpad_flash_block_t *object; + enum clearpad_state_e finish_state; + + if (this->state == SYN_STATE_FLASH_ERASE && + (flash->command == SYN_FORCE_FLASH || + flash->command == SYN_DEFAULT_FLASH)) { + object = &(&this->flash)->data; + pid = PID_CORE_CODE; + finish_state = SYN_STATE_FLASH_DATA; + } else if ((this->state == SYN_STATE_FLASH_DATA && + (flash->command == SYN_FORCE_FLASH || + flash->command == SYN_DEFAULT_FLASH))) { + object = &(&this->flash)->config; + pid = PID_CORE_CONFIGURATION; + finish_state = SYN_STATE_FLASH_CONFIG; + } else { + HWLOGE(this, "invalid state(%s) for command(%s)\n", + NAME_OF(clearpad_state_name, this->state), + NAME_OF(clearpad_flash_command_name, flash->command)); + rc = -EINVAL; + goto end; + } + + if (object->pos > 0) + goto write_block_data; + + /* make sure that we are in programming mode and there are no issues */ + rc = clearpad_get(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data00), &buf); + if (rc) { + HWLOGE(this, "failed to get flash programming status\n"); + goto end; + } + + if (!(BIT_GET(buf, STATUS_BL_MODE))) { + HWLOGE(this, "failed flashing in %s (%s)\n", + NAME_OF(clearpad_state_name, this->state), + NAME_OF(clearpad_flash_status_name, + BIT_GET(buf, STATUS_FLASH_STATUS))); + rc = -EIO; + goto end; + } + + object->payload_length = SYN_PAYLOAD_LENGTH; + + /* read block size */ + rc = clearpad_get_block(SYNF(this, F34_FLASH, QUERY, + this->reg_offset.f34_query03), + block_buf, 3); + if (rc) { + HWLOGE(this, "unable to read block size of fw\n"); + goto end; + } + + object->block_size = ((block_buf[2] << 8) | block_buf[1]); + + object->remain_block = (object->length / object->block_size) + % object->payload_length; + object->transation_count = (object->length / object->block_size) + / object->payload_length; + object->blocks = (object->length / object->block_size) + + !!(object->length % object->block_size); + + if (object->remain_block > 0) + object->transation_count++; + + /* write partition id */ + rc = clearpad_put(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data01), pid); + if (rc) { + HWLOGE(this, "unable to write partition id\n"); + goto end; + } + + /* write block num */ + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data02), + (u8 *)&block_num, 2); + if (rc) { + HWLOGE(this, "unable to write block number\n"); + goto end; + } + +write_block_data: + if ((object->pos == (object->transation_count - 1)) + && (object->remain_block > 0)) + object->payload_length = object->remain_block; + + data = object->data + object->pos * object->block_size; + len = object->length - object->pos * object->block_size; + + if (len > object->block_size) + len = object->block_size; + + /* write payload length */ + rc = clearpad_put_block(SYNF( + this, F34_FLASH, DATA, this->reg_offset.f34_data03), + (u8 *)&(object->payload_length), 2); + if (rc) { + HWLOGE(this, "unable to write payload length\n"); + goto end; + } + + /* write command */ + rc = clearpad_put( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data04), + FLASH_CMD_WRITE); + if (rc) { + HWLOGE(this, "unable to write flash cmd\n"); + goto end; + } + + /* issue a write data/configuration block command with data */ + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data05), data, len); + if (rc) { + HWLOGE(this, "unable to write data/configuration block cmd\n"); + goto end; + } + + if (object->pos % 100 == 0) + LOGI(this, "wrote %d blocks\n", object->pos); + + /* if we've reached the end of the data/configuration flashing */ + if (++object->pos == object->blocks) { + HWLOGI(this, "flash finished on %s\n", + NAME_OF(clearpad_state_name, this->state)); + this->state = finish_state; + } +end: + return rc; +} + +static int clearpad_flash_disable_bl_v7_x(struct clearpad_t *this) +{ + int rc; + u8 buf; + + /* make sure that we are in programming mode and there are no issues */ + rc = clearpad_get(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data00), &buf); + if (rc) + goto end; + + if (!(BIT_GET(buf, STATUS_BL_MODE))) { + HWLOGE(this, "failed flashing config (%s)\n", + NAME_OF(clearpad_flash_status_name, + BIT_GET(buf, STATUS_FLASH_STATUS))); + rc = -EIO; + goto end; + } + + clearpad_set_delay(SYN_WAIT_TIME_AFTER_REGISTER_ACCESS); + + /* send a reset to the device to complete the flash procedure */ + clearpad_reset(this, SYN_SWRESET, __func__); + + HWLOGI(this, "flashing finished, resetting\n"); + this->state = SYN_STATE_FLASH_DISABLE; +end: + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_flash_bl_v7_x(struct clearpad_t *this) +{ + int rc = 0; + + switch (this->state) { + case SYN_STATE_FLASH_IMAGE_SET: + rc = clearpad_flash_enable_bl_v7_x(this); + break; + case SYN_STATE_FLASH_ENABLE: + case SYN_STATE_FLASH_PROGRAM: + rc = clearpad_flash_program_bl_v7_x(this); + break; + case SYN_STATE_FLASH_ERASE: + case SYN_STATE_FLASH_DATA: + rc = clearpad_flash_update_bl_v7_x(this); + break; + case SYN_STATE_FLASH_CONFIG: + rc = clearpad_flash_disable_bl_v7_x(this); + clearpad_firmware_reset(this); + break; + case SYN_STATE_FLASH_DISABLE: + HWLOGE(this, "should be handled by clearpad_initialize\n"); + break; + default: + HWLOGE(this, "invalid state(%s)", + NAME_OF(clearpad_state_name, this->state)); + rc = -EINVAL; + break; + } + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_flash(struct clearpad_t *this) +{ + int rc = 0; + + LOGD(this, "flash state=%s\n", + NAME_OF(clearpad_state_name, this->state)); + + if (this->is_sol) + rc = clearpad_flash_bl_v6_0(this); + else + rc = clearpad_flash_bl_v7_x(this); + if (rc) { + HWLOGE(this, "failed during flash (%s), rc = %d\n", + NAME_OF(clearpad_state_name, this->state), rc); + + snprintf(this->result_info, SYN_STRING_LENGTH, + "%s, family 0x%02x, fw rev 0x%02x.%02x, " + "extra 0x%02x, failed fw update\n", + clearpad_s(this->device_info.product_id, + HEADER_PRODUCT_ID_SIZE), + this->device_info.customer_family, + this->device_info.firmware_revision_major, + this->device_info.firmware_revision_minor, + this->device_info.firmware_revision_extra); + this->flash_requested = false; + clearpad_firmware_reset(this); + HWLOGI(this, "result: %s", this->result_info); + + if (this->state != SYN_STATE_FLASH_IMAGE_SET) + clearpad_notify_interrupt(this, + &this->interrupt.for_reset, rc); + } + return rc; +} + +/* need LOCK(&this->lock) */ +bool clearpad_is_healthy(struct clearpad_t *this) +{ + u8 status = 0; + int rc = 0; + + switch (this->chip_id) { + case SYN_CHIP_332U: + /* F01_RMI_CTRL00: Device Control */ + rc = clearpad_get(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), &status); + if (rc) { + LOGE(this, "rc=%d\n", rc); + return false; + } + if (BIT_GET(status, DEVICE_CONTROL_CONFIGURED)) { + LOGE(this, "status=0x%02x\n", status); + return false; + } + /* fall-through */ + case SYN_CHIP_3330: + case SYN_CHIP_3500: + /* F01_RMI_DATA00: Device Status */ + rc = clearpad_get(SYNF(this, F01_RMI, DATA, + this->reg_offset.f01_data00), &status); + if (rc) { + LOGE(this, "rc=%d\n", rc); + return false; + } + if (BIT_GET(status, DEVICE_STATUS_CODE)) { + LOGE(this, "status=0x%02x\n", status); + switch (BIT_GET(status, DEVICE_STATUS_CODE)) { + case DEVICE_STATUS_CODE_DEVICE_FAILURE: + case DEVICE_STATUS_CODE_CONFIGURATION_CRC_FAILURE: + case DEVICE_STATUS_CODE_FIRMWARE_CRC_FAILURE: + case DEVICE_STATUS_CODE_GUEST_CRC_FAILURE: + case DEVICE_STATUS_CODE_EXTERNAL_AFE_FAILURE: + case DEVICE_STATUS_CODE_DISPLAY_FAILURE: + return false; + default: + return true; + } + } + break; + default: + LOGE(this, "not supported chip id (0x%02x)\n", this->chip_id); + rc = -EINVAL; + break; + } + return true; +} + +static void clearpad_watchdog_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct clearpad_watchdog_t *wd = (struct clearpad_watchdog_t *)dwork; + struct clearpad_t *this = container_of(wd, struct clearpad_t, watchdog); + bool healthy = true; + int rc; + + LOGD(this, "start\n"); + + LOCK(&this->lock); + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "avoid to access I2C before waiting %d ms delay\n", + this->reset.delay_for_powerup_ms); + + if (!this->touchctrl.will_powerdown && !this->flash_requested) + schedule_delayed_work(&this->watchdog.work, + this->watchdog.delay); + + goto not_ready_to_access_i2c; + } + + if (!touchctrl_lock_power(this, __func__, true, false)) { + LOG_STAT(this, "stop watchdog because of no lock power\n"); + UNLOCK(&this->lock); + return; + } + + if (this->dev_active && !this->flash_requested) + healthy = clearpad_is_healthy(this); + LOG_STAT(this, "%s (icount=%u)\n", + healthy ? "healthy" : "NEED HW RESET", this->interrupt.count); + if (!healthy) { + clearpad_debug_info(this); + clearpad_reset(this, SYN_HWRESET, "Watchdog"); + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, + &this->interrupt.for_reset, + this->interrupt.wait_ms); + LOCK(&this->lock); + if (rc) + LOGE(this, "failed to get interrupt (rc=%d)\n", rc); + } + if (this->dev_active && !this->flash_requested) + schedule_delayed_work(&this->watchdog.work, + this->watchdog.delay); + touchctrl_unlock_power(this, __func__); +not_ready_to_access_i2c: + UNLOCK(&this->lock); +} + +/* need LOCK(&this->lock) */ +static void clearpad_watchdog_update(struct clearpad_t *this) +{ + if (!this->watchdog.enabled) + return; + + cancel_delayed_work(&this->watchdog.work); + + if (this->dev_active && !this->flash_requested) + schedule_delayed_work(&this->watchdog.work, + this->watchdog.delay); +} + +/* need LOCK(&this->lock) */ +static int clearpad_set_resume_mode(struct clearpad_t *this) +{ + int rc = 0; + u8 interrupt; + u8 value; + + HWLOGI(this, "set resume mode\n"); + WARN_ON(this->dev_active && !this->early_suspend); + + if (this->post_probe.done) + clearpad_funcarea_invalidate_all(this); + + /* F01_RMI_CTRL01.00: Interrupt Enable 0 */ + rc = clearpad_put(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl01), + INTERRUPT_ENABLE_0_ENABLE_ALL); + if (rc) { + LOGE(this, "failed to set interrupt enable\n"); + goto end; + } + + /* F01_RMI_CTRL00: Device Control */ + rc = clearpad_get(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), &value); + if (rc) { + LOGE(this, "failed to get sleep status\n"); + goto end; + } + if (BIT_GET(value, DEVICE_CONTROL_SLEEP_MODE)) { + rc = clearpad_put_bit(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), + DEVICE_CONTROL_SLEEP_MODE_NORMAL_OPERATION, + DEVICE_CONTROL_SLEEP_MODE_MASK); + if (rc) { + LOGE(this, "failed to exit sleep mode\n"); + goto end; + } + clearpad_set_delay(this->charger_only.delay_ms); + } + + this->early_suspend = false; + + if (this->wakeup_gesture.enabled) { + rc = clearpad_disable_wakeup_gesture(this); + if (rc) { + LOGE(this, "failed to disable wakeup gesture\n"); + goto end; + } + } + + if (this->cover.enabled) + rc = clearpad_set_cover_status(this); + + this->dev_active = true; + + if (clearpad_set_noise_det_irq(this, true, true)) + HWLOGI(this, "no noise_det irq change (enable)\n"); + else + LOGI(this, "noise_det irq was enabled\n"); + + if (!this->irq_enabled) { + /* F01_RMI_DATA01: Interrupt Status */ + rc = clearpad_get(SYNF(this, F01_RMI, DATA, + this->reg_offset.f01_data01), &interrupt); + if (rc) { + LOGE(this, "failed to read interrupt status\n"); + goto end; + } + LOGI(this, "ignore interrupt 0x%02x\n", interrupt); + clearpad_set_irq(this, true); + } + +end: + LOGI(this, "set resume mode (rc=%d)\n", rc); + return rc; +} + +/* This function is called from touch backlight daemon context + * while clearpad_set_suspend_mode() is called from EARLY POWERDOWN. + * + * need LOCK(&this->lock) + */ +static int clearpad_set_early_suspend_mode(struct clearpad_t *this) +{ + int rc = 0; + + HWLOGI(this, "set early suspend mode\n"); + + if (this->force_sleep != FSMODE_KEEP) { + /* F01_RMI_CTRL01.00: Interrupt Enable 0 */ + rc = clearpad_put(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl01), + INTERRUPT_ENABLE_0_DISABLE_ALL); + if (rc) { + LOGE(this, "failed to disable interrupt mode\n"); + goto end; + } + } else { + /* F01_RMI_CTRL00: Device Control */ + rc = clearpad_put_bit(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), + DEVICE_CONTROL_SLEEP_MODE_SENSOR_SLEEP, + DEVICE_CONTROL_SLEEP_MODE_MASK); + if (rc) { + LOGE(this, "failed to exit normal mode\n"); + goto end; + } + clearpad_set_delay(this->charger_only.delay_ms); + } + clearpad_set_irq(this, false); + this->early_suspend = true; + + HWLOGI(this, "enter sleep mode\n"); +end: + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_set_suspend_mode(struct clearpad_t *this) +{ + int rc = 0; + + if (this->dev_active) + HWLOGI(this, "set suspend mode\n"); + else + HWLOGI(this, "change suspend mode\n"); + + if (this->force_sleep != FSMODE_KEEP && + this->wakeup_gesture.enabled) { + HWLOGI(this, "prepare for wakeup gesture mode"); + if (this->early_suspend) { + /* F01_RMI_CTRL01.00: Interrupt Enable 0 */ + rc = clearpad_put(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl01), + INTERRUPT_ENABLE_0_ENABLE_ALL); + if (rc) { + LOGE(this, "failed to exit sleep mode\n"); + goto end; + } + } + + rc = clearpad_enable_wakeup_gesture(this); + if (rc) { + LOGE(this, "failed to enable wakeup gesture\n"); + goto end; + } + + this->wakeup_gesture.time_started = jiffies - 1; + clearpad_set_delay(SYN_WAIT_TIME_AFTER_REGISTER_ACCESS); + clearpad_set_irq(this, true); + HWLOGI(this, "enter wakeup gesture mode\n"); + } else { + if (!this->early_suspend) { + if (this->force_sleep != FSMODE_KEEP) { + /* F01_RMI_CTRL01.00: Interrupt Enable 0 */ + rc = clearpad_put(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl01), + INTERRUPT_ENABLE_0_DISABLE_ALL); + if (rc) { + LOGE(this, "failed to set interrupt" + "disable\n"); + goto end; + } + } else { + /* F01_RMI_CTRL00: Device Control */ + rc = clearpad_put_bit( + SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), + DEVICE_CONTROL_SLEEP_MODE_SENSOR_SLEEP, + DEVICE_CONTROL_SLEEP_MODE_MASK); + if (rc) { + LOGE(this, "failed to exit normal" + "mode\n"); + goto end; + } + clearpad_set_delay(this->charger_only.delay_ms); + } + clearpad_set_irq(this, false); + HWLOGI(this, "enter sleep mode\n"); + } else { + HWLOGI(this, "already in sleep mode\n"); + } + } + + this->dev_active = false; + + if (clearpad_set_noise_det_irq(this, false, false)) + HWLOGI(this, "no noise_det irq change (disable)\n"); + else + LOGI(this, "noise_det irq was disabled\n"); + +end: + if (rc) + LOGI(this, "set suspend mode (rc=%d)\n", rc); + + return rc; +} + +/* + * Reset + */ + +static void clearpad_reset_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct clearpad_reset_t *reset + = container_of(dwork, struct clearpad_reset_t, work); + struct clearpad_t *this + = container_of(reset, struct clearpad_t, reset); + int retry = 0; + int rc; + unsigned long flags; + + LOGI(this, "start %s '%s'\n", + NAME_OF(clearpad_reset_name, reset->mode), + this->interrupt.for_reset.name); + + LOCK(&this->lock); + if (!touchctrl_lock_power(this, __func__, true, false)) { + LOG_STAT(this, "stop reset work because of no power\n"); + clearpad_notify_interrupt(this, + &this->interrupt.for_reset, -EPERM); + goto err_in_lock_power; + } + +retry_reset: + this->interrupt.count = 0; /* to detect first interrupt */ + spin_lock_irqsave(&this->slock, flags); + this->dev_busy = false; + this->irq_pending = false; + spin_unlock_irqrestore(&this->slock, flags); + switch (reset->mode) { + case SYN_HWRESET: + case SYN_FORCE_HWRESET: + rc = touchctrl_hwreset(this, reset->mode); + if (rc && retry++ < SYN_RETRY_NUM_OF_RESET) + goto retry_reset; + break; + case SYN_SWRESET: + case SYN_FORCE_SWRESET: + /* F01_RMI_CMD00: Device Command */ + rc = clearpad_put( + SYNF(this, F01_RMI, COMMAND, + this->reg_offset.f01_cmd00), + DEVICE_COMMAND_RESET_MASK); + if (rc && retry++ < SYN_RETRY_NUM_OF_RESET) + goto retry_reset; + break; + default: + rc = -EINVAL; + LOGE(this, "invalid mode %d\n", reset->mode); + break; + } + LOGW(this, "state '%s' was interrupted by reset\n", + NAME_OF(clearpad_state_name, this->state)); + /* will be changed to RUNNING by clearpad_initialize */ + this->state = SYN_STATE_DISABLED; + /* inform about this reset to running session */ + clearpad_notify_interrupt(this, &this->interrupt.for_F34, -EINTR); + clearpad_notify_interrupt(this, &this->interrupt.for_F54, -EINTR); + + if ((rc == -EAGAIN || rc == -EBUSY) && + reset->retry++ < SYN_RETRY_NUM_OF_RESET) { + LOGI(this, "schedule reset for retry (rc=%d)\n", rc); + schedule_delayed_work(&reset->work, 1 * HZ); + } else if (rc) { + LOGE(this, "failed to execute %s '%s'\n", + NAME_OF(clearpad_reset_name, reset->mode), + this->interrupt.for_reset.name); + clearpad_notify_interrupt(this, + &this->interrupt.for_reset, rc); + } + if (this->watchdog.enabled) + clearpad_watchdog_update(this); + + touchctrl_unlock_power(this, __func__); + LOGI(this, "done\n"); +err_in_lock_power: + UNLOCK(&this->lock); + return; +} + +/* + * request asynchronized reset + * need LOCK(&this->lock) */ +static void clearpad_reset(struct clearpad_t *this, + enum clearpad_reset_e mode, const char *cause) +{ + struct clearpad_reset_t *reset = &this->reset; + + if (!this->dev_active + && mode != SYN_FORCE_HWRESET && mode != SYN_FORCE_SWRESET) { + HWLOGW(this, "could not execute %s because " + "device is suspended and not force reset\n", + NAME_OF(clearpad_reset_name, mode)); + goto err_in_device_mode; + } + + if (atomic_read(&this->interrupt.for_reset.done) == 0 && + this->interrupt.for_reset.name) { + LOGI(this, "request %s '%s' continued to %s '%s'\n", + NAME_OF(clearpad_reset_name, mode), cause, + NAME_OF(clearpad_reset_name, reset->mode), + this->interrupt.for_reset.name); + } + + if (this->watchdog.enabled) + cancel_delayed_work(&this->watchdog.work); + + reset->mode = mode; + this->interrupt.for_reset.name = cause; + clearpad_prepare_for_interrupt(this, &this->interrupt.for_reset, cause); + + reset->retry = 0; + LOGE(this, "schedule %s '%s'\n", + NAME_OF(clearpad_reset_name, mode), cause); + if (!schedule_delayed_work(&reset->work, 0)) + LOGI(this, "already reset on queue\n"); + +err_in_device_mode: + return; +} + +static int clearpad_gpio_export(struct clearpad_t *this, + struct device *dev, bool export) +{ + int rc = 0; + + if (export) { + rc = gpio_export(this->pdata->irq_gpio, false); + if (rc) { + LOGE(this, "failed to export gpio (rc=%d)\n", rc); + } else { + rc = gpio_export_link(dev, "attn", + this->pdata->irq_gpio); + if (rc) + LOGE(this, "failed to export (rc=%d)\n", rc); + } + if (this->noise_det.supported) { + rc = gpio_export(this->noise_det.irq_gpio, false); + if (rc) { + LOGE(this, "failed to export noise_det gpio\n" + " (rc=%d)\n", rc); + } else { + rc = gpio_export_link(dev, "noise_det", + this->noise_det.irq_gpio); + if (rc) + LOGE(this, + "failed to export noise_det link" + " (rc=%d)\n", rc); + } + } + } else { + gpio_unexport(this->pdata->irq_gpio); + sysfs_remove_link(&dev->kobj, "attn"); + if (this->noise_det.supported) { + gpio_unexport(this->noise_det.irq_gpio); + sysfs_remove_link(&dev->kobj, "noise_det"); + } + } + + return rc; +} + +static void clearpad_funcarea_initialize(struct clearpad_t *this) +{ + struct clearpad_funcarea_t *funcarea; + struct clearpad_area_t pointer_area; + struct clearpad_button_data_t *button; + struct clearpad_pointer_data_t *pointer_data; + static const char const *func_name[] = { + [SYN_FUNCAREA_INSENSIBLE] = "insensible", + [SYN_FUNCAREA_POINTER] = "pointer", + [SYN_FUNCAREA_BUTTON] = "button", + }; + + this->funcarea = clearpad_funcarea_get(this, + this->device_info.customer_family, + this->device_info.firmware_revision_major); + funcarea = this->funcarea; + + if (funcarea == NULL) { + LOGI(this, "no funcarea\n"); + return; + } + + for (; funcarea->func != SYN_FUNCAREA_END; funcarea++) { + switch (funcarea->func) { + case SYN_FUNCAREA_POINTER: + pointer_area = (struct clearpad_area_t) + funcarea->original; + pointer_data = (struct clearpad_pointer_data_t *) + funcarea->data; + if (pointer_data) { + pointer_area.x1 -= pointer_data->offset_x; + pointer_area.x2 -= pointer_data->offset_x; + pointer_area.y1 -= pointer_data->offset_y; + pointer_area.y2 -= pointer_data->offset_y; + } + input_set_abs_params(this->input, ABS_MT_TRACKING_ID, + 0, this->extents.n_fingers, 0, 0); + input_set_abs_params(this->input, ABS_MT_POSITION_X, + pointer_area.x1, + pointer_area.x2, 0, 0); + input_set_abs_params(this->input, ABS_MT_POSITION_Y, + pointer_area.y1, + pointer_area.y2, 0, 0); + if (this->touch_pressure_enabled) + input_set_abs_params(this->input, + ABS_MT_PRESSURE, 0, + SYN_MAX_Z_VALUE, 0, 0); + if (this->touch_size_enabled) { + input_set_abs_params(this->input, + ABS_MT_TOUCH_MAJOR, 0, + SYN_MAX_W_VALUE + 1, 0, 0); + input_set_abs_params(this->input, + ABS_MT_TOUCH_MINOR, 0, + SYN_MAX_W_VALUE + 1, 0, 0); + } + if (this->touch_orientation_enabled) + input_set_abs_params(this->input, + ABS_MT_ORIENTATION, -1, 1, 0, 0); + input_set_abs_params(this->input, ABS_MT_TOOL_TYPE, + 0, this->pen.enabled ? MT_TOOL_PEN : + MT_TOOL_FINGER, 0, 0); + break; + case SYN_FUNCAREA_BUTTON: + button = + (struct clearpad_button_data_t *)funcarea->data; + input_set_capability(this->input, EV_KEY, button->code); + break; + default: + continue; + } + + LOGI(this, "funcarea '%s' [%d, %d, %d, %d] [%d, %d, %d, %d]\n", + NAME_OF(func_name, funcarea->func), + funcarea->original.x1, funcarea->original.y1, + funcarea->original.x2, funcarea->original.y2, + funcarea->extension.x1, funcarea->extension.y1, + funcarea->extension.x2, funcarea->extension.y2); + } +} + +static inline bool clearpad_funcarea_test(struct clearpad_area_t *area, + struct clearpad_point_t *point) +{ + return area->x1 <= point->x && point->x <= area->x2 + && area->y1 <= point->y && point->y <= area->y2; +} + +static struct clearpad_funcarea_t * +clearpad_funcarea_search(struct clearpad_t *this, + struct clearpad_pointer_t *pointer) +{ + struct clearpad_funcarea_t *funcarea = this->funcarea; + + if (funcarea == NULL) + goto end; + + /* get new funcarea */ + for (; funcarea->func != SYN_FUNCAREA_END; funcarea++) { + if (clearpad_funcarea_test(&funcarea->original, + &pointer->cur)) + goto end; + if (funcarea->func == SYN_FUNCAREA_POINTER + && clearpad_funcarea_test(&funcarea->extension, + &pointer->cur)) + goto end; + } + funcarea = NULL; +end: + return funcarea; +} + +static void clearpad_funcarea_crop(struct clearpad_area_t *area, + struct clearpad_point_t *point) +{ + + if (point->x < area->x1) + point->x = area->x1; + else if (area->x2 < point->x) + point->x = area->x2; + + if (point->y < area->y1) + point->y = area->y1; + else if (area->y2 < point->y) + point->y = area->y2; + +} + +static void clearpad_funcarea_down(struct clearpad_t *this, + struct clearpad_pointer_t *pointer) +{ + int touch_major, touch_minor; + struct clearpad_button_data_t *button; + struct clearpad_pointer_data_t *pointer_data; + struct clearpad_point_t *cur = &pointer->cur; + struct input_dev *idev = this->input; + bool valid; + + switch (pointer->funcarea->func) { + case SYN_FUNCAREA_INSENSIBLE: + LOG_EVENT(this, "insensible\n"); + pointer->down = false; + break; + case SYN_FUNCAREA_POINTER: + clearpad_funcarea_crop(&pointer->funcarea->original, cur); + pointer_data = (struct clearpad_pointer_data_t *) + pointer->funcarea->data; + if (pointer_data) { + cur->x -= pointer_data->offset_x; + cur->y -= pointer_data->offset_y; + } + if (cur->tool == SYN_TOOL_PEN) { + cur->tool = MT_TOOL_PEN; + } else { + /* shift range if glove event */ + if (cur->tool == SYN_TOOL_GLOVE) { + if (cur->z > 0) + cur->z += SYN_MAX_Z_VALUE + 1; + else + cur->z = 0; + } else { + if (cur->z > SYN_MAX_Z_VALUE) + cur->z = SYN_MAX_Z_VALUE; + } + cur->tool = MT_TOOL_FINGER; + } + valid = idev->users > 0; + LOG_EVENT(this, "%s[%d]: (x,y)=(%d,%d) w=(%d,%d) z=%d t=%d\n", + valid ? "pt" : "unused pt", cur->id, cur->x, cur->y, + cur->wx, cur->wy, cur->z, cur->tool); + if (!valid) + break; + touch_major = max(cur->wx, cur->wy) + 1; + touch_minor = min(cur->wx, cur->wy) + 1; + input_report_abs(idev, ABS_MT_TRACKING_ID, cur->id); + input_report_abs(idev, ABS_MT_TOOL_TYPE, cur->tool); + input_report_abs(idev, ABS_MT_POSITION_X, cur->x); + input_report_abs(idev, ABS_MT_POSITION_Y, cur->y); + if (this->touch_pressure_enabled) + input_report_abs(idev, ABS_MT_PRESSURE, cur->z); + if (this->touch_size_enabled) { + input_report_abs(idev, ABS_MT_TOUCH_MAJOR, touch_major); + input_report_abs(idev, ABS_MT_TOUCH_MINOR, touch_minor); + } + if (this->touch_orientation_enabled) + input_report_abs(idev, ABS_MT_ORIENTATION, + (cur->wx > cur->wy)); + input_mt_sync(idev); + break; + case SYN_FUNCAREA_BUTTON: + LOG_EVENT(this, "button\n"); + button = (struct clearpad_button_data_t *) + pointer->funcarea->data; + if (button) + button->down = true; + break; + default: + break; + } +} + +static void clearpad_funcarea_up(struct clearpad_t *this, + struct clearpad_pointer_t *pointer) +{ + struct clearpad_button_data_t *button; + struct input_dev *idev = this->input; + bool valid; + + switch (pointer->funcarea->func) { + case SYN_FUNCAREA_INSENSIBLE: + LOG_EVENT(this, "insensible up\n"); + break; + case SYN_FUNCAREA_POINTER: + valid = idev->users > 0; + LOG_EVENT(this, "%s up\n", valid ? "pt" : "unused pt"); + if (!valid) + break; + input_mt_sync(idev); + break; + case SYN_FUNCAREA_BUTTON: + LOG_EVENT(this, "button up\n"); + button = (struct clearpad_button_data_t *) + pointer->funcarea->data; + if (button) + button->down = false; + break; + default: + break; + } + pointer->funcarea = NULL; +} + +static void clearpad_funcarea_out(struct clearpad_t *this, + struct clearpad_pointer_t *pointer) +{ + struct clearpad_funcarea_t *new_funcarea; + + clearpad_funcarea_up(this, pointer); + + new_funcarea = clearpad_funcarea_search(this, pointer); + if (new_funcarea == NULL) + return; + + switch (new_funcarea->func) { + case SYN_FUNCAREA_INSENSIBLE: + pointer->down = false; + break; + default: + break; + } +} + +static void clearpad_report_button(struct clearpad_t *this, + struct clearpad_button_data_t *button) +{ + bool valid = this->input->users > 0; + + if (button->down) { + if (!button->down_report) { + button->down_report = true; + if (valid) + input_report_key(this->input, button->code, 1); + LOG_EVENT(this, "%s(%d): down\n", + valid ? "key" : "unused key", button->code); + } + } else { + if (button->down_report) { + button->down_report = false; + if (valid) + input_report_key(this->input, button->code, 0); + LOG_EVENT(this, "%s(%d): up\n", + valid ? "key" : "unused key", button->code); + } + } +} + +static void +clearpad_funcarea_report_extra_events(struct clearpad_t *this) +{ + struct clearpad_funcarea_t *funcarea = this->funcarea; + struct clearpad_button_data_t *button; + + if (funcarea == NULL) + return; + + for (; funcarea->func != SYN_FUNCAREA_END; funcarea++) { + if (funcarea->func == SYN_FUNCAREA_BUTTON) { + button = + (struct clearpad_button_data_t *)funcarea->data; + if (button) + clearpad_report_button(this, button); + } + } +} + +static void clearpad_funcarea_invalidate_all(struct clearpad_t *this) +{ + struct clearpad_pointer_t *pointer; + int i; + + for (i = 0; i < this->extents.n_fingers; ++i) { + pointer = &this->pointer[i]; + if (pointer->down) { + pointer->down = false; + LOG_VERBOSE(this, "invalidate pointer %d\n", i); + } + if (pointer->funcarea) + clearpad_funcarea_up(this, pointer); + } + clearpad_funcarea_report_extra_events(this); + if (this->input->users) + input_sync(this->input); +} + +static void clearpad_parse_finger_n_f12(struct clearpad_t *this, + const u8 *buf, int finger, struct clearpad_point_t *new_point) +{ + enum registers { + REG_TYPE_STATUS, + REG_X_LSB, + REG_X_MSB, + REG_Y_LSB, + REG_Y_MSB, + REG_Z, + REG_WX, + REG_WY, + }; + buf += this->extents.n_bytes_per_object * finger; + new_point->tool = buf[REG_TYPE_STATUS]; + new_point->tool = clearpad_tool_type_f12[new_point->tool]; + new_point->id = finger; + new_point->x = (buf[REG_X_MSB] << 8) | buf[REG_X_LSB]; + new_point->y = (buf[REG_Y_MSB] << 8) | buf[REG_Y_LSB]; + new_point->wx = buf[REG_WX]; + new_point->wy = buf[REG_WY]; + new_point->z = buf[REG_Z]; +} + +static void clearpad_report_finger_n(struct clearpad_t *this, + const u8 *buf, int finger) +{ + struct clearpad_pointer_t *pointer = &this->pointer[finger]; + struct clearpad_point_t new_point; + struct clearpad_area_t *extension; + + if (clearpad_is_valid_function(this, SYN_F12_2D)) + clearpad_parse_finger_n_f12(this, buf, finger, &new_point); + else + return; + + /* check finger state */ + if (new_point.tool == SYN_TOOL_FINGER || + (this->glove.enabled && new_point.tool == SYN_TOOL_GLOVE) || + (this->pen.enabled && (new_point.tool == SYN_TOOL_PEN))) { + + switch (this->flip_config) { + case SYN_FLIP_X: + new_point.x = this->extents.x_max - new_point.x; + break; + case SYN_FLIP_Y: + new_point.y = this->extents.y_max - new_point.y; + break; + case SYN_FLIP_XY: + new_point.x = this->extents.x_max - new_point.x; + new_point.y = this->extents.y_max - new_point.y; + break; + case SYN_FLIP_NONE: + default: + break; + } + + LOG_VERBOSE(this, "pt[%d]: (x,y)=(%d,%d) w=(%d,%d) z=%d t=%d\n", + new_point.id, new_point.x, new_point.y, + new_point.wx, new_point.wy, new_point.z, + new_point.tool); + + if (!pointer->down) { + if (clearpad_funcarea_test(&this->funcarea->original, &new_point)) { + /* first touch event */ + pointer->down = true; + pointer->cur = new_point; + pointer->funcarea + = clearpad_funcarea_search(this, pointer); + LOG_VERBOSE(this, "validate pointer %d [func %d]\n", + new_point.id, pointer->funcarea + ? pointer->funcarea->func : -1); + } else { + LOGW(this, "invalidate pointer %d\n", new_point.id); + } + } + if (pointer->funcarea) { + extension = &pointer->funcarea->extension; + + if (clearpad_funcarea_test(extension, &new_point)) { + pointer->cur = new_point; + clearpad_funcarea_down(this, pointer); + } else { + pointer->down = false; + clearpad_funcarea_out(this, pointer); + LOGW(this, "invalidate pointer %d\n", new_point.id); + } + } + } else { + if (pointer->down) { + pointer->down = false; + LOG_VERBOSE(this, "invalidate pointer %d\n", finger); + } + if (pointer->funcarea) + clearpad_funcarea_up(this, pointer); + } +} + +static int get_num_fingers_f12(struct clearpad_t *this, + int *num_fingers) +{ + int rc, num; + u16 val, mask; + const int max_objects = this->extents.n_fingers; + + /* F12_2D_DATA15: Object Attention */ + rc = clearpad_get_block(SYNA(this, F12_2D, DATA, 15), + (u8 *)&val, sizeof(val)); + if (rc) + goto end; + + val = le16_to_cpu(val); + + for (num = 0, mask = 0x1; num < max_objects; num++, mask <<= 1) + if (val < mask) + break; + + *num_fingers = num; + + LOG_CHECK(this, "fingers=%d, 0x%04hX", num, val); +end: + return rc; +} + +static int clearpad_read_fingers_f12(struct clearpad_t *this) +{ + int rc, finger, num_fingers; + u8 buf[this->extents.n_fingers * this->extents.n_bytes_per_object]; + + memset(buf, 0, sizeof(buf)); + + rc = get_num_fingers_f12(this, &num_fingers); + if (rc) + goto end; + + if (num_fingers > 0) { + /* F12_2D_DATA01: Sensed Objects */ + rc = clearpad_get_block(SYNA(this, F12_2D, DATA, 1), + buf, num_fingers * this->extents.n_bytes_per_object); + if (rc) + goto end; + } + + for (finger = 0; finger < this->extents.n_fingers; finger++) + clearpad_report_finger_n(this, buf, finger); +end: + return rc; +} + +static int clearpad_handle_gesture(struct clearpad_t *this) +{ + u8 wakeint = 0; + int rc = -EIO; + + if (clearpad_is_valid_function(this, SYN_F12_2D)) + /* F12_2D_DATA04: Detected Gestures */ + rc = clearpad_get(SYNA(this, F12_2D, DATA, 0x04), &wakeint); + if (rc) + goto end; + + LOGI(this, "gesture: %d\n", wakeint); + + if (time_after(jiffies, this->wakeup_gesture.time_started)) + this->wakeup_gesture.time_started = jiffies + + msecs_to_jiffies(this->wakeup_gesture.timeout_delay); + else + goto end; + + evdt_execute(this->evdt_node, this->input, wakeint); +end: + return rc; +} + +static int clearpad_process_F01_RMI(struct clearpad_t *this, u8 device_status) +{ + int rc = 0; + + HWLOGI(this, "device status 0x%02x (code=%d, unconfig=%d)\n", + device_status, BIT_GET(device_status, DEVICE_STATUS_CODE), + BIT_GET(device_status, DEVICE_STATUS_UNCONFIGURED)); + if (BIT_GET(device_status, DEVICE_STATUS_CODE) + == DEVICE_STATUS_CODE_RESET_OCCURRED && + BIT_GET(device_status, DEVICE_STATUS_UNCONFIGURED)) { + LOGI(this, "device reset\n"); + /* initialized already */ + WARN_ON(this->interrupt.count != 0); + /* check result of flash disabling */ + WARN_ON(BIT_GET(device_status, DEVICE_STATUS_FLASH_PROG)); + HWLOGI(this, "clear glitch (irq_pending=%s) for reset\n", + this->irq_pending ? "true" : "false"); + + if (this->force_sleep == FSMODE_ONESHOT) { + LOGI(this, "clear force sleep mode\n"); + this->force_sleep = FSMODE_OFF; + } + if (this->force_sleep != FSMODE_OFF) { + HWLOGI(this, "force sleep mode\n"); + if (this->dev_active) { + rc = clearpad_set_suspend_mode(this); + if (rc) + HWLOGE(this, "failed to suspend " + "(rc=%d)\n", rc); + } + } else if (!this->dev_active) { + rc = clearpad_set_resume_mode(this); + if (rc) + HWLOGE(this, "failed to resume (rc=%d)\n", rc); + } + clearpad_notify_interrupt(this, &this->interrupt.for_reset, 0); + } else if (BIT_GET(device_status, DEVICE_STATUS_CODE) + == DEVICE_STATUS_CODE_DEVICE_FAILURE) { + clearpad_reset(this, SYN_HWRESET, __func__); + } else { + LOGI(this, "unexpected device status=0x%02x\n", device_status); + } + return rc; +} + +static int clearpad_process_F12_2D(struct clearpad_t *this, bool *waked) +{ + int rc = 0; + u8 status; + + *waked = false; + + if (!this->post_probe.done) { + LOGI(this, "ignore event before end of post probe\n"); + goto end; + } + + if (this->wakeup_gesture.enabled && !this->dev_active && + !this->last_irq) { + rc = clearpad_handle_gesture(this); + if (!rc) + *waked = true; + goto end; + } + + /* F01_RMI_DATA00: Device Status */ + rc = clearpad_get(SYNF(this, F01_RMI, DATA, + this->reg_offset.f01_data00), &status); + LOG_CHECK(this, "rc=%d F01_RMI_DATA00=0x%x\n", rc, status); + if (rc) + goto end; + + if (BIT_GET(status, DEVICE_STATUS_CODE) + == DEVICE_STATUS_CODE_DEVICE_FAILURE) { + LOGE(this, "found device failure\n"); + WARN_ON(1); /* instead of HWRESET */ + goto end; + } + + rc = clearpad_read_fingers_f12(this); + if (rc) + goto end; + if (this->input->users) + input_sync(this->input); +end: + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_process_irq(struct clearpad_t *this) +{ + int rc = 0; + u8 interrupt_status = 0; + u8 device_status = 0; + bool waked = false; + unsigned long flags; + + if (this->flash.enter_bootloader_mode) { + clearpad_set_delay(this->reset.delay_for_powerup_ms); + this->flash.enter_bootloader_mode = false; + HWLOGI(this, "clear glitch (irq_pending=%s) " + "for entering bootloader mode\n", + this->irq_pending ? "true" : "false"); + spin_lock_irqsave(&this->slock, flags); + this->irq_pending = false; + spin_unlock_irqrestore(&this->slock, flags); + } + if (clearpad_initialize_if_first_event(this, + &interrupt_status, &device_status) < 0) { + LOGE(this, "failed to initialize for first event\n"); + rc = -EBUSY; + goto no_valid_interrupt; + } + + if (interrupt_status & this->pdt[SYN_F34_FLASH].irq_mask) { + if (this->flash.fw) + clearpad_flash(this); + clearpad_notify_interrupt(this, &this->interrupt.for_F34, 0); + } else if (interrupt_status & this->pdt[SYN_F01_RMI].irq_mask) { + rc = clearpad_process_F01_RMI(this, device_status); + } else if (interrupt_status & this->pdt[SYN_F54_ANALOG].irq_mask) { + this->state = SYN_STATE_RUNNING; + clearpad_notify_interrupt(this, &this->interrupt.for_F54, 0); + } else if (interrupt_status & this->pdt[SYN_F12_2D].irq_mask) { + rc = clearpad_process_F12_2D(this, &waked); + if (!rc && waked) { + /* will resume in next first event */ + this->interrupt.count = 0; + goto no_valid_interrupt; + } + } else { + LOGI(this, "no work, interrupt=[0x%02x]\n", interrupt_status); + } + + if (this->interrupt.count == 0 && rc) + goto no_valid_interrupt; + + this->interrupt.count += 1; + if (this->interrupt.count == 0) { + LOGI(this, "rewind interrupt.count\n"); + this->interrupt.count = 1; + } + +no_valid_interrupt: + if (rc) { + LOGE(this, "error (icount=%u rc=%d)\n", + this->interrupt.count, rc); + WARN_ON(1); /* instead of HWRESET */ + } + + if (this->watchdog.enabled) + clearpad_watchdog_update(this); + + return rc; +} + +/* need LOCK(&this->lock) */ +static bool clearpad_process_noise_det_irq(struct clearpad_t *this) +{ + bool retry = false; + int rc = 0; + + if (!this->post_probe.done) { + LOGW(this, "post_probe hasn't finished, not need to retry\n"); + goto not_done_post_probe; + } + + if (this->touchctrl.will_powerdown) { + LOGW(this, "will_powerdown, not need to retry\n"); + goto will_powerdown; + } + + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "not read I2C access, need to retry\n"); + retry = true; + goto not_ready_to_access_i2c; + } + + if (!touchctrl_lock_power(this, "noise_det_irq", true, false)) { + HWLOGW(this, "power is already turned off, need to retry\n"); + retry = true; + goto end; + } + + /* F01_RMI_CTRL18: Device Control 1 */ + rc = clearpad_put_bit( + SYNF(this, F01_RMI, CTRL, this->reg_offset.f01_ctrl18), + DEVICE_CONTROL_1_GSM_ENABLE_MASK, + DEVICE_CONTROL_1_GSM_ENABLE_MASK); + if (rc) { + LOGE(this, "failed to set noise reduction bit\n"); + retry = true; + goto err_i2c_access; + } + +err_i2c_access: + touchctrl_unlock_power(this, "noise_det_irq"); + +not_done_post_probe: +will_powerdown: +not_ready_to_access_i2c: +end: + return retry; +} + +static irqreturn_t clearpad_threaded_handler(int irq, void *dev_id) +{ + struct clearpad_t *this = dev_id; + unsigned long flags; + bool locked; + + get_monotonic_boottime(&this->interrupt.threaded_handler_ts); + + LOCK(&this->lock); + locked = touchctrl_lock_power(this, "irq_handler", true, false); + /* workaround to clear interrupt status */ + if (!locked) + HWLOGW(this, "read interrupt status though no power lock\n"); + + do { + (void)clearpad_process_irq(this); + + spin_lock_irqsave(&this->slock, flags); + if (likely(!this->irq_pending)) { + this->dev_busy = false; + spin_unlock_irqrestore(&this->slock, flags); + break; + } + this->irq_pending = false; + spin_unlock_irqrestore(&this->slock, flags); + LOGD(this, "touch irq pending\n"); + } while (true); + + if (locked) + touchctrl_unlock_power(this, "irq_handler"); + UNLOCK(&this->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t clearpad_hard_handler(int irq, void *dev_id) +{ + struct clearpad_t *this = dev_id; + unsigned long flags; + irqreturn_t ret; + + get_monotonic_boottime(&this->interrupt.hard_handler_ts); + + spin_lock_irqsave(&this->slock, flags); + if (unlikely(this->dev_busy)) { + this->irq_pending = true; + ret = IRQ_HANDLED; + } else { + this->dev_busy = true; + ret = IRQ_WAKE_THREAD; + } + spin_unlock_irqrestore(&this->slock, flags); + if (ret == IRQ_HANDLED) + LOGD(this, "touch irq busy\n"); + return ret; +} + +static irqreturn_t clearpad_noise_det_threaded_handler(int irq, void *dev_id) +{ + struct clearpad_t *this = dev_id; + bool retry = false; + + get_monotonic_boottime(&this->noise_det.threaded_handler_ts); + LOCK(&this->lock); + + retry = clearpad_process_noise_det_irq(this); + + this->noise_det.threaded_handler_count += 1; + if (this->noise_det.threaded_handler_count == 0) { + LOGI(this, "rewind noise_det.threaded_handler_count\n"); + this->noise_det.threaded_handler_count = 1; + } + UNLOCK(&this->lock); + + if (retry) { + clearpad_set_delay(this->noise_det.retry_time_ms); + LOGI(this, "Enable det_irq for retry hic=%u tic=%u@ " + "@ %ld.%06ld\n", + this->noise_det.hard_handler_count, + this->noise_det.threaded_handler_count, + this->noise_det.threaded_handler_ts.tv_sec, + this->noise_det.threaded_handler_ts.tv_nsec); + if (clearpad_set_noise_det_irq(this, true, true)) + HWLOGE(this, "no noise_det irq change (enable)\n"); + else + LOGI(this, "noise_det irq was enabled\n"); + } else { + LOGI(this, "Success set fw_bit hic=%u tic=%u@ %ld.%06ld\n", + this->noise_det.hard_handler_count, + this->noise_det.threaded_handler_count, + this->noise_det.threaded_handler_ts.tv_sec, + this->noise_det.threaded_handler_ts.tv_nsec); + } + return IRQ_HANDLED; +} + +static irqreturn_t clearpad_noise_det_hard_handler(int irq, void *dev_id) +{ + struct clearpad_t *this = dev_id; + irqreturn_t ret; + unsigned long flags; + + get_monotonic_boottime(&this->noise_det.hard_handler_ts); + if (clearpad_set_noise_det_irq(this, false, false)) + HWLOGE(this, "no noise_det irq change(disable)\n"); + else + LOGD(this, "noise_det irq was disabled\n"); + + spin_lock_irqsave(&this->noise_det.slock, flags); + if (this->noise_det.first_irq) { + this->noise_det.first_irq = false; + ret = IRQ_HANDLED; + } else { + ret = IRQ_WAKE_THREAD; + } + this->noise_det.hard_handler_count += 1; + if (this->noise_det.hard_handler_count == 0) { + LOGI(this, "rewind hard_handler_count.th_handler_count\n"); + this->noise_det.hard_handler_count = 1; + } + spin_unlock_irqrestore(&this->noise_det.slock, flags); + + if (ret == IRQ_HANDLED) { + if (clearpad_set_noise_det_irq(this, true, false)) + HWLOGE(this, "no noise_det irq change" + " w/o 1st_irq(enable)\n"); + else + LOGD(this, "noise_det irq was enabled w/o 1st_irq\n"); + } else { + LOGD(this, "Wake up noise_det_threaded_handle hic=%u tic=%u " + "@ %ld.%06ld\n", + this->noise_det.hard_handler_count, + this->noise_det.threaded_handler_count, + this->noise_det.hard_handler_ts.tv_sec, + this->noise_det.hard_handler_ts.tv_nsec); + } + return ret; +} + +static int clearpad_device_open(struct input_dev *dev) +{ + struct clearpad_t *this = input_get_drvdata(dev); + int rc; + + LOG_STAT(this, "state=%s\n", NAME_OF(clearpad_state_name, this->state)); + + switch (this->state) { + case SYN_STATE_INIT: + rc = 0; + break; + case SYN_STATE_DISABLED: + rc = -ENODEV; + break; + case SYN_STATE_RUNNING: + rc = this->post_probe.done ? 0 : -ENODEV; + break; + default: + rc = -EBUSY; + break; + } + + return rc; +} + +static void clearpad_device_close(struct input_dev *dev) +{ + struct clearpad_t *this = input_get_drvdata(dev); + + LOCK(&this->lock); + LOG_STAT(this, "state=%s\n", NAME_OF(clearpad_state_name, this->state)); + + UNLOCK(&this->lock); +} + +/* need LOCK(&this->lock) */ +static int clearpad_command_fwflash(struct clearpad_t *this) +{ + struct clearpad_device_info_t *info = &this->device_info; + int rc; + + memset(this->result_info, 0, SYN_STRING_LENGTH); + this->flash_requested = true; + this->reg_offset.updated = false; + + /* prepare to waiting for end of flash */ + clearpad_prepare_for_interrupt(this, &this->interrupt.for_reset, + "wait for flashing"); + + if (this->flash.fw) { + HWLOGI(this, "start firmware flash\n"); + this->flash.analog_id = info->analog_id; + this->state = SYN_STATE_FLASH_IMAGE_SET; + rc = clearpad_flash(this); + LOG_CHECK(this, "rc=%d\n", rc); + if (rc) { + clearpad_undo_prepared_interrupt(this, + &this->interrupt.for_reset, "wait for flashing"); + goto error; + } + } + + /* wait for end of flash */ + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, &this->interrupt.for_reset, + this->flash.default_timeout_ms); + LOCK(&this->lock); + if (rc) { + LOGE(this, "failed to get interrupt (rc=%d)\n", rc); + goto error; + } + +error: + snprintf(this->result_info, SYN_STRING_LENGTH, + "%s, family 0x%02x, fw rev 0x%02x.%02x, extra 0x%02x, %s\n", + clearpad_s(this->device_info.product_id, + HEADER_PRODUCT_ID_SIZE), + this->device_info.customer_family, + this->device_info.firmware_revision_major, + this->device_info.firmware_revision_minor, + this->device_info.firmware_revision_extra, + rc == 0 ? "succeeded fw update" : "failed fw update"); + this->flash_requested = false; + HWLOGI(this, "result: %s", this->result_info); + + if (this->watchdog.enabled) + clearpad_watchdog_update(this); + + if (rc) { + /* inform disabled state */ + this->state = SYN_STATE_DISABLED; + clearpad_reset(this, SYN_SWRESET, __func__); + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, + &this->interrupt.for_reset, + this->flash.default_timeout_ms); + LOCK(&this->lock); + if (rc) + LOGE(this, "failed to get interrupt (rc=%d)\n", rc); + } + return rc; +} + +static ssize_t clearpad_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct clearpad_t *this = dev_get_drvdata(dev); + struct clearpad_touchctrl_t *touchctrl = &this->touchctrl; + + if (!strncmp(attr->attr.name, __stringify(post_probe_start), + PAGE_SIZE)) { + snprintf(buf, PAGE_SIZE, + "%d", this->post_probe.start); + goto end; + } + + if (!this->post_probe.done) { + LOGW(this, "post_probe hasn't finished, please retry later\n"); + buf[0] = '\0'; + goto end; + } + + if (!strncmp(attr->attr.name, __stringify(fwinfo), PAGE_SIZE)) { + snprintf(buf, PAGE_SIZE, + "%s, family 0x%02x, fw rev 0x%02x.%02x, " + "extra 0x%02x, aid 0x%02x, state=%s, " + "active=%s, type=%s, icount=%u, session=%s, " + "power(touch=%s display=%s user=%d)\n", + clearpad_s(this->device_info.product_id, + HEADER_PRODUCT_ID_SIZE), + this->device_info.customer_family, + this->device_info.firmware_revision_major, + this->device_info.firmware_revision_minor, + this->device_info.firmware_revision_extra, + this->device_info.analog_id, + NAME_OF(clearpad_state_name, this->state), + this->dev_active ? "true" : "false", + NAME_OF(clearpad_chip_name, this->chip_id), + this->interrupt.count, touchctrl->session, + touchctrl_is_touch_powered(this) ? "OK" : "NG", + touchctrl_is_display_powered(this) ? "OK" : "NG", + touchctrl->power_user); + } else if (!strncmp(attr->attr.name, __stringify(fwchip_id), PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%x", this->chip_id); + else if (!strncmp(attr->attr.name, __stringify(fwfamily), PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%x", this->device_info.customer_family); + else if (!strncmp(attr->attr.name, __stringify(fwrevision), PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%x", this->device_info.firmware_revision_major); + else if (!strncmp(attr->attr.name, + __stringify(fwrevision_minor), PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%x", this->device_info.firmware_revision_minor); + else if (!strncmp(attr->attr.name, + __stringify(fwrevision_extra), PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%x", this->device_info.firmware_revision_extra); + else if (!strncmp(attr->attr.name, __stringify(fwstate), PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, "%s", + NAME_OF(clearpad_state_name, this->state)); + else if (!strncmp(attr->attr.name, __stringify(wakeup_gesture), + PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, "%d", + this->wakeup_gesture.enabled); + else if (!strncmp(attr->attr.name, __stringify(pen), PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->pen.enabled); + else if (!strncmp(attr->attr.name, __stringify(glove), PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->glove.enabled); + else if (!strncmp(attr->attr.name, __stringify(force_sleep), PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->force_sleep); + else if (!strncmp(attr->attr.name, __stringify(charger_status), + PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->charger.status); + else if (!strncmp(attr->attr.name, __stringify(cover_status), + PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->cover.status); + else if (!strncmp(attr->attr.name, __stringify(cover_mode_enabled), + PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->cover.enabled); + else if (!strncmp(attr->attr.name, __stringify(cover_win_top), + PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->cover.win_top); + else if (!strncmp(attr->attr.name, __stringify(cover_win_bottom), + PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->cover.win_bottom); + else if (!strncmp(attr->attr.name, __stringify(cover_win_right), + PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->cover.win_right); + else if (!strncmp(attr->attr.name, __stringify(cover_win_left), + PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->cover.win_left); + else if (!strncmp(attr->attr.name, __stringify(stamina_mode), + PAGE_SIZE)) + snprintf(buf, PAGE_SIZE, + "%d", this->stamina.enabled); + else + snprintf(buf, PAGE_SIZE, "illegal sysfs file"); +end: + return strnlen(buf, PAGE_SIZE); +} + +/* + * Calibration + */ + +/* need LOCK(&this->lock) */ +static int clearpad_do_calibration(struct clearpad_t *this, int mode, + u8 calibration_crc) +{ + unsigned long timeout; + int rc = 0; + int reset_rc = 0; + bool calibrate = false; + u8 status, bit, need_bit; + + switch (mode) { + case SYN_CALIBRATION_NORMAL: + need_bit = CALIBRATION_STATE_CALIBRATION_CRC_MASK; + calibrate = !!BIT_GET(calibration_crc, + CALIBRATION_STATE_CALIBRATION_CRC); + bit = START_CAL_PROD_TEST_START_CALIBRATION_MASK; + timeout = jiffies + msecs_to_jiffies(SYN_CALIBRATION_WAIT_MS); + break; + case SYN_CALIBRATION_EW: + need_bit = CALIBRATION_STATE_IS_CALIBRATION_CRC_MASK; + calibrate = !!BIT_GET(calibration_crc, + CALIBRATION_STATE_IS_CALIBRATION_CRC); + if (!calibrate) + break; + /* make display sleep state */ + touchctrl_notify_wakeup_gesture_mode(this, true); + rc = touchctrl_display_off(this); + if (rc) { + LOGE(this, "failed to turn display off\n"); + calibrate = false; + break; + } + /* set correct state after powerdown */ + touchctrl_notify_wakeup_gesture_mode(this, + this->wakeup_gesture.enabled); + + bit = START_CAL_PROD_TEST_START_IS_CALIBRATION_MASK; + timeout = jiffies + + msecs_to_jiffies(SYN_CALIBRATION_EW_WAIT_MS); + break; + default: + HWLOGI(this, "unknown mode %d\n", mode); + rc = -EINVAL; + goto end; + } + HWLOGI(this, "%s : %s\n", NAME_OF(clearpad_calibration_name, mode), + calibrate ? "Start" : "Skip"); + if (!calibrate) + goto end; + + clearpad_set_delay(SYN_CALIBRATION_SETUP_TIME); + /* start calibration */ + /* F54_ANALOG_CTRL188: Start Calibration or Production Test */ + rc = clearpad_put_bit(SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl188), bit, bit); + if (rc) { + HWLOGE(this, "failed to start %s\n", + NAME_OF(clearpad_calibration_name, mode)); + goto end; + } + + /* wait until end of calibration */ + do { + clearpad_set_delay(SYN_CALIBRATION_WAIT); + /* F54_ANALOG_CTRL188: Start Calibration or Production Test */ + rc = clearpad_get(SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl188), + &status); + if (rc) + HWLOGE(this, "failed to read status\n"); + if (!(status & bit)) + goto succeeded; + } while (time_before(jiffies, timeout)); + HWLOGE(this, "calibration time out\n"); + rc = -EIO; + goto end; + +succeeded: + rc = clearpad_get(SYNF(this, F54_ANALOG, DATA, + this->reg_offset.f54_data31), &status); + if (rc) { + HWLOGE(this, "failed to get calibration status\n"); + goto end; + } + /* check calibration result */ + if (status & need_bit) { + HWLOGE(this, "%s was failed, crc = %x\n", + NAME_OF(clearpad_calibration_name, mode), status); + rc = -EIO; + } + HWLOGI(this, "%s : %s\n", NAME_OF(clearpad_calibration_name, mode), + status & need_bit ? "Failed" : "Succeed"); + + clearpad_set_delay(20); + +end: + switch (mode) { + case SYN_CALIBRATION_EW: + if (!calibrate) + break; + clearpad_set_delay(SYN_CALIBRATION_BEFORE_HWRESET_WAIT); + clearpad_reset(this, SYN_HWRESET, "Calibration(EW)"); + UNLOCK(&this->lock); + reset_rc = clearpad_wait_for_interrupt(this, + &this->interrupt.for_reset, + this->interrupt.wait_ms); + LOCK(&this->lock); + if (reset_rc) + LOGE(this, "failed to get interrupt (rc=%d)\n", rc); + break; + default: + break; + } + + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_calibrate_on_fwflash(struct clearpad_t *this, + bool fwflashed) +{ + struct clearpad_device_info_t *info = &this->device_info; + int rc, i; + u8 calibration_crc = 0; + + if (!clearpad_is_valid_function(this, SYN_F54_ANALOG)) { + rc = -EIO; + HWLOGE(this, "F54_ANALOG invalid\n"); + goto end; + } + + HWLOGI(this, "old aid = %x, new aid = %x\n", + this->flash.analog_id, info->analog_id); + /* check if needed */ + if (fwflashed && (this->flash.analog_id != info->analog_id)) { + HWLOGW(this, "analog ID is not matching\n"); + BIT_SET(calibration_crc, + CALIBRATION_STATE_CALIBRATION_CRC, 1); + BIT_SET(calibration_crc, + CALIBRATION_STATE_IS_CALIBRATION_CRC, 1); + } else { + rc = clearpad_get(SYNF(this, F54_ANALOG, DATA, + this->reg_offset.f54_data31), + &calibration_crc); + if (rc) { + HWLOGE(this, "failed to get calibration status\n"); + goto end; + } + } + + for (i = 0; i < 2; i++) { + rc = clearpad_do_calibration(this, SYN_CALIBRATION_NORMAL, + calibration_crc); + if (rc) { + LOGE(this, "failed normal calibration\n"); + if (i > 0) + goto end; + clearpad_reset(this, SYN_SWRESET, __func__); + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, + &this->interrupt.for_reset, + this->interrupt.wait_ms); + LOCK(&this->lock); + if (rc) + LOGE(this, "failed to get interrupt (rc=%d)\n", + rc); + continue; + } + break; + } + + rc = clearpad_do_calibration(this, SYN_CALIBRATION_EW, calibration_crc); + if (rc) + LOGE(this, "failed ew calibration\n"); + +end: + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_fwflash_core(struct clearpad_t *this, + enum clearpad_flash_command_e command, + unsigned int id) +{ + struct clearpad_flash_t *flash = &this->flash; + int rc; + bool do_update = false; + + if (!this->dev_active) { + rc = -EINVAL; + HWLOGW(this, "could not start fwflash because " + "device is in suspended mode\n"); + goto err_in_device_mode; + } + + if (this->flash.fw) { + rc = -EAGAIN; + HWLOGE(this, "previous fw update is ongoing\n"); + goto err_in_busy_check; + } + clearpad_firmware_reset(this); + flash->command = command; + + rc = clearpad_judge_firmware_flash(this, id, command); + if (rc < 0) + goto err_in_judgement; + + do_update = (rc == 1) ? true : false; + if (do_update) { + /* enable irq inside flash_enable */ + rc = clearpad_command_fwflash(this); + if (rc) { + HWLOGE(this, "firmware flash was failed\n"); + goto err_in_fw_flash; + } + } + + if (this->calibration_supported && this->calibrate_on_fwflash) { + rc = clearpad_calibrate_on_fwflash(this, do_update); + if (rc) { + HWLOGE(this, "calibrate fatal error\n"); + goto err_in_calibration; + } + } + +err_in_calibration: +err_in_fw_flash: +err_in_judgement: + clearpad_firmware_reset(this); +err_in_busy_check: +err_in_device_mode: + return rc; +} + +static ssize_t clearpad_fwflash_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct clearpad_t *this = dev_get_drvdata(dev); + const char **command = (const char **)clearpad_flash_command_name; + const char *session = "fwflash store"; + int rc = 0; + unsigned int id = 0; + char request[SYN_STRING_LENGTH] = ""; + + if (!this->post_probe.done) { + LOGW(this, "post_probe hasn't finished, please retry later\n"); + goto err_in_check_post_probe; + } + + HWLOGI(this, "flash command: %s\n", buf); + if (sscanf(buf, "%7s %4x", request, &id) < 0) { + HWLOGE(this, "%s sscanf failed ", buf); + rc = -EINVAL; + goto err_in_fw_flash_command_check; + } + HWLOGI(this, "request: %s\n", request); + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) + goto err_in_ctrl_begin; + + LOCK(&this->lock); + if (!strncmp(request, command[SYN_FORCE_FLASH], PAGE_SIZE)) { + HWLOGI(this, "start force firmware flash, id=%x\n", id); + rc = clearpad_fwflash_core(this, SYN_FORCE_FLASH, id); + } else if (!strncmp(request, command[SYN_DEFAULT_FLASH], PAGE_SIZE)) { + HWLOGI(this, "start firmware config & data\n"); + rc = clearpad_fwflash_core(this, SYN_DEFAULT_FLASH, 0); + } else { + HWLOGE(this, "illegal command\n"); + rc = -EINVAL; + } + UNLOCK(&this->lock); + + clearpad_ctrl_session_end(this, session); + +err_in_ctrl_begin: +err_in_fw_flash_command_check: +err_in_check_post_probe: + return rc ? rc : size; +} + +static ssize_t clearpad_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct clearpad_t *this = dev_get_drvdata(dev); + int rc = 0; + + LOGD(this, "start\n"); + + if (!this->post_probe.done) { + LOGW(this, "post_probe hasn't finished, please retry later\n"); + goto err_in_check_post_probe; + } + + LOCK(&this->lock); + + if (sysfs_streq(buf, "1")) { + rc = clearpad_gpio_export(this, &this->input->dev, + false); + if (rc) + LOGE(this, "failed to unexport gpio\n"); + goto enable; + } else if (sysfs_streq(buf, "0")) { + devm_free_irq(&this->pdev->dev, this->irq, this); + if (this->noise_det.supported) + devm_free_irq(&this->pdev->dev, + this->noise_det.irq, this); + rc = clearpad_gpio_export(this, &this->input->dev, + true); + if (rc) { + LOGE(this, "failed to export gpio\n"); + goto enable; + } + } + goto end; + +enable: + rc = devm_request_threaded_irq(&this->pdev->dev, + this->irq, + clearpad_hard_handler, + clearpad_threaded_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + this->pdev->dev.driver->name, this); + if (rc) + LOGE(this, "irq %d busy? <%d>\n", this->irq, rc); + + if (this->noise_det.supported) { + rc = devm_request_threaded_irq(&this->pdev->dev, + this->noise_det.irq, + clearpad_noise_det_hard_handler, + clearpad_noise_det_threaded_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "clearpad_noise_det", this); + if (rc) + LOGE(this, "noise_det irq %d busy? <%d>\n", + this->noise_det.irq, rc); + } +end: + UNLOCK(&this->lock); + +err_in_check_post_probe: + LOG_STAT(this, "state=%s\n", NAME_OF(clearpad_state_name, this->state)); + return strnlen(buf, PAGE_SIZE); +} + +static ssize_t clearpad_glove_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct clearpad_t *this = dev_get_drvdata(dev); + const char *session = "glove enabled store"; + int rc = 0; + bool lazy_update = false; + + LOGD(this, "start\n"); + + if (!this->glove.supported) { + LOGI(this, "glove mode is not supported"); + goto err_in_check_support; + } + + if (!this->post_probe.done) { + LOGI(this, "post_probe hasn't finished, will apply later\n"); + lazy_update = true; + goto err_in_check_post_probe; + } + + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "avoid to access I2C before waiting %d ms delay\n", + this->reset.delay_for_powerup_ms); + lazy_update = true; + goto not_ready_to_access_i2c; + } + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) { + LOGI(this, "not powered, will be applied later\n"); + lazy_update = true; + goto err_in_session_begin; + } + +err_in_session_begin: +not_ready_to_access_i2c: +err_in_check_post_probe: + LOCK(&this->lock); + this->glove.enabled = sysfs_streq(buf, "0") ? false : true; + if (!lazy_update) { + rc = clearpad_set_glove_mode(this, this->glove.enabled); + if (rc) + LOGE(this, "failed to set glove for device\n"); + } + LOGI(this, "glove mode: %s", + this->glove.enabled ? "ENABLE" : "DISABLE"); + UNLOCK(&this->lock); + + if (!lazy_update) + clearpad_ctrl_session_end(this, session); + +err_in_check_support: + return size; +} + +static ssize_t clearpad_pen_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct clearpad_t *this = dev_get_drvdata(dev); + const char *session = "pen enabled store"; + int rc = 0; + bool lazy_update = false; + + LOGD(this, "start\n"); + + if (!this->pen.supported) { + LOGI(this, "pen is not supported"); + goto err_in_check_support; + } + + if (!this->post_probe.done) { + LOGI(this, "post_probe hasn't finished, will apply later\n"); + lazy_update = true; + goto err_in_check_post_probe; + } + + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "avoid to access I2C before waiting %d ms delay\n", + this->reset.delay_for_powerup_ms); + lazy_update = true; + goto not_ready_to_access_i2c; + } + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) { + LOGI(this, "not powered, will be applied later\n"); + lazy_update = true; + goto err_in_session_begin; + } + +err_in_session_begin: +not_ready_to_access_i2c: +err_in_check_post_probe: + LOCK(&this->lock); + this->pen.enabled = sysfs_streq(buf, "0") ? false : true; + if (!lazy_update) { + rc = clearpad_set_pen(this); + if (rc) + LOGE(this, "failed to set pen for device\n"); + } + LOGI(this, "pen mode: %s", this->pen.enabled ? "ENABLE" : "DISABLE"); + UNLOCK(&this->lock); + + if (!lazy_update) + clearpad_ctrl_session_end(this, session); + +err_in_check_support: + return size; +} + +static ssize_t clearpad_wakeup_gesture_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int rc = 0; + const char *session = "wakeup gesture store"; + struct clearpad_t *this = dev_get_drvdata(dev); + bool new = false; + bool old; + bool lazy_update = false; + + LOGD(this, "start\n"); + + if (!this->wakeup_gesture.supported) { + LOGI(this, "wakeup gesture is not supported"); + goto err_in_check_support; + } + + if (!this->post_probe.done) { + LOGI(this, "post_probe hasn't finished, will apply later\n"); + lazy_update = true; + goto err_in_check_post_probe; + } + + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "avoid to access I2C before waiting %d ms delay\n", + this->reset.delay_for_powerup_ms); + lazy_update = true; + goto not_ready_to_access_i2c; + } + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) { + LOGI(this, "not powered, will be applied later\n"); + lazy_update = true; + goto err_in_session_begin; + + } + +err_in_session_begin: +not_ready_to_access_i2c: +err_in_check_post_probe: + LOCK(&this->lock); + new = sysfs_streq(buf, "0") ? false : true; + old = this->wakeup_gesture.enabled; + this->wakeup_gesture.enabled = new; + device_init_wakeup(&this->pdev->dev, + this->wakeup_gesture.enabled ? 1 : 0); + touchctrl_notify_wakeup_gesture_mode(this, + this->wakeup_gesture.enabled); + if (!lazy_update && !this->dev_active && old != new) { + /* mode is changed in already suspended state */ + rc = clearpad_set_suspend_mode(this); + if (rc) + LOGE(this, "failed change suspend mode (rc=%d)\n", rc); + + } + LOGI(this, "wakeup gesture: %s", + this->wakeup_gesture.enabled ? "ENABLE" : "DISABLE"); + + UNLOCK(&this->lock); + + if (!lazy_update) + clearpad_ctrl_session_end(this, session); + +err_in_check_support: + return size; +} + +static ssize_t clearpad_force_sleep_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct clearpad_t *this = dev_get_drvdata(dev); + const char *session = "force sleep store"; + int rc = 0; + int value; + enum clearpad_force_sleep_e old; + bool lazy_update = false; + + LOGD(this, "start\n"); + + if (!this->post_probe.done) { + LOGI(this, "post_probe hasn't finished, will apply later\n"); + lazy_update = true; + goto err_in_check_post_probe; + } + + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "avoid to access I2C before waiting %d ms delay\n", + this->reset.delay_for_powerup_ms); + lazy_update = true; + goto not_ready_to_access_i2c; + } + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) { + LOGI(this, "not powered, will be applied later\n"); + lazy_update = true; + goto err_in_session_begin; + } + +err_in_session_begin: +not_ready_to_access_i2c: +err_in_check_post_probe: + LOCK(&this->lock); + this->wakeup.unblank_done = false; + this->wakeup.unblank_early_done = false; + old = this->force_sleep; + + rc = kstrtoint(buf, 0, &value); + if (rc) { + LOGE(this, "failed to read force_sleep\n"); + goto err_in_value; + } + this->force_sleep = value; + if (this->force_sleep < FSMODE_OFF || + FSMODE_ONESHOT < this->force_sleep) { + LOGE(this, "Invalid force_sleep mode\n"); + this->force_sleep = old; + goto err_in_value; + } + + if (old == FSMODE_ONESHOT && this->force_sleep == FSMODE_KEEP) { + LOGW(this, "refuse KEEP mode from ONESHOT mode\n"); + this->force_sleep = old; + } + LOG_STAT(this, "force_sleep: %d\n", this->force_sleep); + + if (!lazy_update) { + if (this->force_sleep == FSMODE_OFF && + this->state == SYN_STATE_RUNNING && + (!this->dev_active || this->early_suspend)) { + rc = clearpad_set_resume_mode(this); + if (rc) + LOGE(this, "failed to set resume for device\n"); + } else if (this->force_sleep != FSMODE_OFF && + this->state == SYN_STATE_RUNNING && + this->dev_active) { + rc = clearpad_set_early_suspend_mode(this); + if (rc) + LOGE(this, "failed to force sleep device\n"); + } + } +err_in_value: + UNLOCK(&this->lock); + + if (!lazy_update) + clearpad_ctrl_session_end(this, session); + + return size; +} + +static ssize_t clearpad_charger_status_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int rc = 0; + const char *session = "charger status store"; + struct clearpad_t *this = dev_get_drvdata(dev); + bool lazy_update = false; + + LOGD(this, "start\n"); + + if (!this->charger.supported) { + LOGI(this, "charger mode is not supported\n"); + goto err_in_check_support; + } + + if (!this->post_probe.done) { + LOGI(this, "post_probe hasn't finished, will apply later\n"); + lazy_update = true; + goto err_in_check_post_probe; + } + + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "avoid to access I2C before waiting %d ms delay\n", + this->reset.delay_for_powerup_ms); + lazy_update = true; + goto not_ready_to_access_i2c; + } + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) { + LOGI(this, "not powered, will be applied later\n"); + lazy_update = true; + goto err_in_session_begin; + } + +err_in_session_begin: +not_ready_to_access_i2c: +err_in_check_post_probe: + LOCK(&this->lock); + this->charger.status = sysfs_streq(buf, "0") ? false : true; + if (!lazy_update) { + rc = clearpad_set_charger(this); + if (rc) + LOGE(this, "failed to set charger for device\n"); + } + LOGI(this, "charger status: %s\n", + this->charger.status ? "CONN" : "DISCONN"); + UNLOCK(&this->lock); + + clearpad_ctrl_session_end(this, session); + +err_in_check_support: + return size; +} + +static ssize_t clearpad_cover_status_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int rc = 0; + const char *session = "cover status store"; + struct clearpad_t *this = dev_get_drvdata(dev); + bool lazy_update = false; + + if (!this->cover.supported) { + LOGI(this, "cover mode is not supported"); + goto err_in_check_support; + } + + if (!this->post_probe.done) { + LOGI(this, "post_probe hasn't finished, will apply later\n"); + lazy_update = true; + goto err_in_check_post_probe; + } + + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "avoid to access I2C before waiting %d ms delay\n", + this->reset.delay_for_powerup_ms); + lazy_update = true; + goto not_ready_to_access_i2c; + } + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) { + LOGI(this, "not powered, will be applied later\n"); + lazy_update = true; + goto err_in_session_begin; + } + +err_in_session_begin: +not_ready_to_access_i2c: +err_in_check_post_probe: + LOCK(&this->lock); + this->cover.status = sysfs_streq(buf, "0") ? false : true; + if (!lazy_update && this->cover.enabled) { + rc = clearpad_set_cover_status(this); + if (rc) + LOGE(this, "failed to set cover status for device\n"); + } + LOGI(this, "cover status: %s", this->cover.status ? "CLOSE" : "OPEN"); + UNLOCK(&this->lock); + + if (!lazy_update) + clearpad_ctrl_session_end(this, session); + +err_in_check_support: + return size; +} + +static ssize_t clearpad_cover_mode_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int rc = 0; + const char *session = "cover mode enabled store"; + struct clearpad_t *this = dev_get_drvdata(dev); + bool lazy_update = false; + + if (!this->cover.supported) { + LOGI(this, "cover mode is not supported"); + goto err_in_check_support; + } + + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "avoid to access I2C before waiting %d ms delay\n", + this->reset.delay_for_powerup_ms); + lazy_update = true; + goto not_ready_to_access_i2c; + } + + this->cover.enabled = sysfs_streq(buf, "0") ? false : true; + + if (!this->post_probe.done) { + LOGI(this, "post_probe hasn't finished, will apply later\n"); + lazy_update = true; + goto err_in_check_post_probe; + } + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) { + LOGI(this, "not powered, will be applied later\n"); + lazy_update = true; + goto err_in_session_begin; + } + +err_in_session_begin: +not_ready_to_access_i2c: +err_in_check_post_probe: + LOCK(&this->lock); + this->cover.enabled = sysfs_streq(buf, "0") ? false : true; + if (!lazy_update) { + if (this->cover.enabled) { + LOGI(this, "cover mode enabled\n"); + } else { + this->cover.status = false; + rc = clearpad_set_cover_status(this); + if (rc) + LOGE(this, "failed to set cover mode\n"); + LOGI(this, "cover mode disabled\n"); + } + } + UNLOCK(&this->lock); + + if (!lazy_update) + clearpad_ctrl_session_end(this, session); + +err_in_check_support: + return size; +} + +static ssize_t clearpad_cover_win_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int win_size = 0; + int win_size_org = 0; + int rc = 0; + const char *session = "cover win store"; + struct clearpad_t *this = dev_get_drvdata(dev); + bool lazy_update = false; + + if (!this->cover.supported) { + LOGI(this, "cover mode is not supported"); + goto err_in_check_support; + } + + if (!this->post_probe.done) { + LOGI(this, "post_probe hasn't finished, will apply later\n"); + lazy_update = true; + goto err_in_check_post_probe; + } + + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "avoid to access I2C before waiting %d ms delay\n", + this->reset.delay_for_powerup_ms); + lazy_update = true; + goto not_ready_to_access_i2c; + } + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) { + LOGI(this, "not powered, will be applied later\n"); + lazy_update = true; + goto err_in_session_begin; + } + +err_in_session_begin: +not_ready_to_access_i2c: +err_in_check_post_probe: + LOCK(&this->lock); + if (kstrtoint(buf, 0, &win_size)) { + LOGE(this, "failed to read %s", attr->attr.name); + rc = -EINVAL; + goto err_in_read_size; + } + win_size_org = win_size; + + if (!strncmp(attr->attr.name, "cover_win_top", PAGE_SIZE)) { + if (this->cover.convert_window_size && this->cover.tag_y_max) + win_size = win_size * (this->extents.preset_y_max + 1) / + this->cover.tag_y_max; + this->cover.win_top = win_size; + } else if (!strncmp(attr->attr.name, "cover_win_bottom", PAGE_SIZE)) { + if (this->cover.convert_window_size && this->cover.tag_y_max) + win_size = win_size * (this->extents.preset_y_max + 1) / + this->cover.tag_y_max; + this->cover.win_bottom = win_size; + } else if (!strncmp(attr->attr.name, "cover_win_right", PAGE_SIZE)) { + if (this->cover.convert_window_size && this->cover.tag_x_max) + win_size = win_size * (this->extents.preset_x_max + 1) / + this->cover.tag_x_max; + this->cover.win_right = win_size; + } else if (!strncmp(attr->attr.name, "cover_win_left", PAGE_SIZE)) { + if (this->cover.convert_window_size && this->cover.tag_x_max) + win_size = win_size * (this->extents.preset_x_max + 1) / + this->cover.tag_x_max; + this->cover.win_left = win_size; + } + if (!lazy_update) { + rc = clearpad_set_cover_window(this); + if (rc) + LOGE(this, "failed to set cover window for device\n"); + } +err_in_read_size: + LOGI(this, "%s = %d (org %d) rc = %d\n", + attr->attr.name, win_size, win_size_org, rc); + UNLOCK(&this->lock); + + if (!lazy_update) + clearpad_ctrl_session_end(this, session); + +err_in_check_support: + return size; +} + +static ssize_t clearpad_stamina_mode_enabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int rc = 0; + int value = 0; + const char *session = "stamina mode enabled store"; + struct clearpad_t *this = dev_get_drvdata(dev); + bool lazy_update = false; + + LOGD(this, "start\n"); + + if (!this->stamina.supported) { + LOGI(this, "stamina mode is not supported\n"); + goto err_in_check_support; + } + + if (!this->post_probe.done) { + LOGI(this, "post_probe hasn't finished, will apply later\n"); + lazy_update = true; + goto err_in_check_post_probe; + } + + if (!this->dev_active || this->interrupt.count == 0) { + LOGI(this, "avoid to access I2C before waiting %d ms delay\n", + this->reset.delay_for_powerup_ms); + lazy_update = true; + goto not_ready_to_access_i2c; + } + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) { + LOGI(this, "not powered, will be applied later\n"); + lazy_update = true; + goto err_in_session_begin; + } + +err_in_session_begin: +err_in_check_post_probe: +not_ready_to_access_i2c: + LOCK(&this->lock); + if (kstrtoint(buf, 0, &value)) { + LOGE(this, "failed to read %s", attr->attr.name); + rc = -EINVAL; + goto err_in_read_size; + } + switch (this->chip_id) { + case SYN_CHIP_3330: + case SYN_CHIP_332U: + if (value < 0 || 1 < value) { + LOGE(this, "invalid stamina report rate mode %d\n", + value); + goto err_in_read_size; + } + if (value) { + this->stamina.enabled = true; + this->stamina.change_reportrate.mode = 1; + } else { + this->stamina.enabled = false; + this->stamina.change_reportrate.mode = 0; + } + break; + case SYN_CHIP_3500: + if (value < 0 || 3 < value) { + LOGE(this, "invalid stamina report rate mode %d\n", + value); + goto err_in_read_size; + } + if (value) + this->stamina.enabled = true; + else + this->stamina.enabled = false; + this->stamina.change_reportrate.mode = value; + break; + default: + LOGE(this, "not supported on chip id 0x0%2x\n", this->chip_id); + goto err_in_read_size; + } + if (!lazy_update) { + rc = clearpad_set_stamina_mode(this); + if (rc) + LOGE(this, "failed to set stamina for device\n"); + } +err_in_read_size: + LOGI(this, "stamina mode %s\n", + this->stamina.enabled ? "enable" : "disable"); + LOGI(this, "change report rate mode %d\n", + this->stamina.change_reportrate.mode); + UNLOCK(&this->lock); + + if (!lazy_update) + clearpad_ctrl_session_end(this, session); + +err_in_check_support: + return size; +} + +static ssize_t clearpad_post_probe_start_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct clearpad_t *this = dev_get_drvdata(dev); + + LOGD(this, "start\n"); + + LOCK(&this->lock); + this->post_probe.start = sysfs_streq(buf, "0") ? false : true; + if (this->post_probe.done) { + HWLOGI(this, "already post probed, need reboot\n"); + } else { + if (this->post_probe.start) { + HWLOGI(this, "start post probe" + "with post_probe_start sysfs\n"); + schedule_delayed_work(&this->post_probe.work, 0); + } + } + UNLOCK(&this->lock); + + return size; +} + +static struct device_attribute clearpad_sysfs_attrs[] = { + __ATTR(fwinfo, S_IRUGO, clearpad_state_show, 0), + __ATTR(fwchip_id, S_IRUGO, clearpad_state_show, 0), + __ATTR(fwfamily, S_IRUGO, clearpad_state_show, 0), + __ATTR(fwrevision, S_IRUGO, clearpad_state_show, 0), + __ATTR(fwrevision_minor, S_IRUGO, clearpad_state_show, 0), + __ATTR(fwrevision_extra, S_IRUGO, clearpad_state_show, 0), + __ATTR(fwstate, S_IRUGO, clearpad_state_show, 0), + __ATTR(fwflash, S_IWUSR, 0, clearpad_fwflash_store), + __ATTR(enabled, S_IWUSR, 0, clearpad_enabled_store), + __ATTR(pen, S_IRUGO | S_IWUSR, clearpad_state_show, + clearpad_pen_enabled_store), + __ATTR(glove, S_IRUGO | S_IWUSR, clearpad_state_show, + clearpad_glove_enabled_store), + __ATTR(force_sleep, S_IRUGO | S_IWUSR, clearpad_state_show, + clearpad_force_sleep_store), + + __ATTR(charger_status, S_IRUGO | S_IWUSR, clearpad_state_show, + clearpad_charger_status_store), + __ATTR(cover_status, S_IRUGO | S_IWUSR, + clearpad_state_show, + clearpad_cover_status_store), + __ATTR(cover_mode_enabled, S_IRUGO | S_IWUSR, + clearpad_state_show, + clearpad_cover_mode_enabled_store), + __ATTR(cover_win_top, S_IRUGO | S_IWUSR, + clearpad_state_show, + clearpad_cover_win_store), + __ATTR(cover_win_bottom, S_IRUGO | S_IWUSR, + clearpad_state_show, + clearpad_cover_win_store), + __ATTR(cover_win_right, S_IRUGO | S_IWUSR, + clearpad_state_show, + clearpad_cover_win_store), + __ATTR(cover_win_left, S_IRUGO | S_IWUSR, + clearpad_state_show, + clearpad_cover_win_store), + __ATTR(stamina_mode, S_IRUGO | S_IWUSR, + clearpad_state_show, + clearpad_stamina_mode_enabled_store), + __ATTR(post_probe_start, S_IRUGO | S_IWUSR, + clearpad_state_show, + clearpad_post_probe_start_store), + __ATTR_NULL +}; + +static struct device_attribute clearpad_wakeup_gesture_attr = + __ATTR(wakeup_gesture, S_IRUGO | S_IWUSR, + clearpad_state_show, + clearpad_wakeup_gesture_store); + +static int clearpad_create_sysfs_entries(struct clearpad_t *this, + struct device_attribute *attrs) +{ + int i, rc = 0; + + for (i = 0; attrs[i].attr.name; i++) { + rc = device_create_file(&this->input->dev, &attrs[i]); + if (rc) + goto err_in_create_file; + } + goto end; + +err_in_create_file: + for (i = i - 1 ; i >= 0; --i) + device_remove_file(&this->input->dev, &attrs[i]); +end: + return rc; +} + +static void clearpad_remove_sysfs_entries(struct clearpad_t *this, + struct device_attribute *attrs) +{ + int i; + + for (i = 0; attrs[i].attr.name; i++) + device_remove_file(&this->input->dev, &attrs[i]); +} + +static int clearpad_read_config_u8(struct device_node *devnode, + char *conf_name, u8 *container) +{ + int rc = 0; + u32 value; + + if (!of_property_read_u32(devnode, conf_name, &value)) + *container = (u8)value; + else + rc = -EINVAL; + + return rc; +} + +static void clearpad_reg_offset_print(struct clearpad_t *this) +{ + /* F01_RMI */ + LOGD(this, "f01-cmd00:0x%02X\n", this->reg_offset.f01_cmd00); + LOGD(this, "f01-ctrl00:0x%02X\n", this->reg_offset.f01_ctrl00); + LOGD(this, "f01-ctrl01:0x%02X\n", this->reg_offset.f01_ctrl01); + LOGD(this, "f01-ctrl05:0x%02X\n", this->reg_offset.f01_ctrl05); + LOGD(this, "f01-ctrl18:0x%02X\n", this->reg_offset.f01_ctrl18); + LOGD(this, "f01-data00:0x%02X\n", this->reg_offset.f01_data00); + LOGD(this, "f01-data01:0x%02X\n", this->reg_offset.f01_data01); + LOGD(this, "f01-query11:0x%02X\n", this->reg_offset.f01_query11); + + /* F02_2D */ + LOGD(this, "f12-ctrl08:0x%02X\n", this->reg_offset.f12_ctrl08); + + /* F34_FLASH */ + LOGD(this, "f34-ctrl00:0x%02X\n", this->reg_offset.f34_ctrl00); + LOGD(this, "f34-data00:0x%02X\n", this->reg_offset.f34_data00); + LOGD(this, "f34-data01:0x%02X\n", this->reg_offset.f34_data01); + LOGD(this, "f34-data02:0x%02X\n", this->reg_offset.f34_data02); + LOGD(this, "f34-data03:0x%02X\n", this->reg_offset.f34_data03); + LOGD(this, "f34-data04:0x%02X\n", this->reg_offset.f34_data04); + LOGD(this, "f34-data05:0x%02X\n", this->reg_offset.f34_data05); + LOGD(this, "f34-query00:0x%02X\n", this->reg_offset.f34_query00); + LOGD(this, "f34-query01:0x%02X\n", this->reg_offset.f34_query01); + LOGD(this, "f34-query03:0x%02X\n", this->reg_offset.f34_query03); + + /* F51_CUSTOM */ + LOGD(this, "f51-ctrl05:0x%02X\n", this->reg_offset.f51_ctrl05); + LOGD(this, "f51-ctrl30:0x%02X\n", this->reg_offset.f51_ctrl30); + + /* F54_ANALOG */ + LOGD(this, "f54-cmd00:0x%02X\n", this->reg_offset.f54_cmd00); + LOGD(this, "f54-ctrl41:0x%02X\n", this->reg_offset.f54_ctrl41); + LOGD(this, "f54-ctrl57:0x%02X\n", this->reg_offset.f54_ctrl57); + LOGD(this, "f54-ctrl88:0x%02X\n", this->reg_offset.f54_ctrl88); + LOGD(this, "f54-ctrl109:0x%02X\n", this->reg_offset.f54_ctrl109); + LOGD(this, "f54-ctrl113:0x%02X\n", this->reg_offset.f54_ctrl113); + LOGD(this, "f54-ctrl147:0x%02X\n", this->reg_offset.f54_ctrl147); + LOGD(this, "f54-ctrl149:0x%02X\n", this->reg_offset.f54_ctrl149); + LOGD(this, "f54-ctrl188:0x%02X\n", this->reg_offset.f54_ctrl188); + LOGD(this, "f54-ctrl214:0x%02X\n", this->reg_offset.f54_ctrl214); + LOGD(this, "f54-data00:0x%02X\n", this->reg_offset.f54_data00); + LOGD(this, "f54-data01:0x%02X\n", this->reg_offset.f54_data01); + LOGD(this, "f54-data02:0x%02X\n", this->reg_offset.f54_data02); + LOGD(this, "f54-data03:0x%02X\n", this->reg_offset.f54_data03); + LOGD(this, "f54-data31:0x%02X\n", this->reg_offset.f54_data31); + LOGD(this, "f54-query38:0x%02X\n", this->reg_offset.f54_query38); +} + +static void clearpad_reg_offset_dt(struct clearpad_t *this, + struct device_node *devnode) +{ + if (!devnode) { + LOGW(this, "device node is invalid\n"); + return; + } + + /* F01_RMI */ + clearpad_read_config_u8(devnode, + "somc,clearpad-f01-rmi-cmd00", &this->reg_offset.f01_cmd00); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f01-rmi-ctrl00", &this->reg_offset.f01_ctrl00); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f01-rmi-ctrl01", &this->reg_offset.f01_ctrl01); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f01-rmi-ctrl05", &this->reg_offset.f01_ctrl05); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f01-rmi-ctrl18", &this->reg_offset.f01_ctrl18); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f01-rmi-data00", &this->reg_offset.f01_data00); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f01-rmi-data01", &this->reg_offset.f01_data01); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f01-rmi-query11", &this->reg_offset.f01_query11); + + /* F02_2D */ + clearpad_read_config_u8(devnode, + "somc,clearpad-f12-2d-ctrl08", &this->reg_offset.f12_ctrl08); + + /* F34_FLASH */ + clearpad_read_config_u8(devnode, + "somc,clearpad-f34-flash-ctrl00", &this->reg_offset.f34_ctrl00); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f34-flash-data00", &this->reg_offset.f34_data00); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f34-flash-data01", &this->reg_offset.f34_data01); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f34-flash-data02", &this->reg_offset.f34_data02); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f34-flash-data03", &this->reg_offset.f34_data03); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f34-flash-data04", &this->reg_offset.f34_data04); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f34-flash-data05", &this->reg_offset.f34_data05); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f34-flash-query00", &this->reg_offset.f34_query00); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f34-flash-query01", &this->reg_offset.f34_query01); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f34-flash-query03", &this->reg_offset.f34_query03); + + /* F51_CUSTOM */ + clearpad_read_config_u8(devnode, + "somc,clearpad-f51-custom-ctrl05", &this->reg_offset.f51_ctrl05); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f51-custom-ctrl30", &this->reg_offset.f51_ctrl30); + + /* F54_ANALOG */ + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-cmd00", &this->reg_offset.f54_cmd00); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-ctrl41", &this->reg_offset.f54_ctrl41); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-ctrl57", &this->reg_offset.f54_ctrl57); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-ctrl88", &this->reg_offset.f54_ctrl88); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-ctrl109", &this->reg_offset.f54_ctrl109); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-ctrl113", &this->reg_offset.f54_ctrl113); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-ctrl147", &this->reg_offset.f54_ctrl147); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-ctrl149", &this->reg_offset.f54_ctrl149); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-ctrl188", &this->reg_offset.f54_ctrl188); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-ctrl214", &this->reg_offset.f54_ctrl214); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-data00", &this->reg_offset.f54_data00); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-data01", &this->reg_offset.f54_data01); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-data02", &this->reg_offset.f54_data02); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-data03", &this->reg_offset.f54_data03); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-data31", &this->reg_offset.f54_data31); + + clearpad_read_config_u8(devnode, + "somc,clearpad-f54-analog-query38", &this->reg_offset.f54_query38); +} + +static void clearpad_reg_offset_config_dt(struct clearpad_t *this) +{ + struct device_node *devnode = this->bdata->of_node; + + clearpad_reg_offset_dt(this, devnode); +} + +static void clearpad_reg_offset_config_dt_for_extra_id( + struct clearpad_t *this, + struct device_node *chip_node) +{ + struct device_node *extra_node = NULL; + char extra_name[SYN_STRING_LENGTH]; + + snprintf(extra_name, SYN_STRING_LENGTH, + "EXTRA_0x%02X", this->device_info.firmware_revision_extra); + + extra_node = of_find_node_by_name(chip_node, extra_name); + if (extra_node == NULL) { + LOGE(this, "no settings for %s\n", extra_name); + return; + } + + LOGI(this, "read settings for %s\n", extra_name); + + clearpad_reg_offset_dt(this, extra_node); + clearpad_reg_offset_print(this); + + this->reg_offset.updated = true; +} + +static void clearpad_touch_config_dt_for_chip_id(struct clearpad_t *this, + int chip_id) +{ + struct device_node *dev_node = this->bdata->of_node; + struct device_node *chip_node = NULL; + const char *chip_name; + u32 value; + + if (chip_id) + chip_name = NAME_OF(clearpad_chip_name, chip_id); + else + chip_name = "clearpad_default"; + if (chip_name == NULL) { + LOGE(this, "unknown chip (0x%x)\n", chip_id); + return; + } + chip_node = of_find_node_by_name(dev_node, chip_name); + if (chip_node == NULL) { + LOGE(this, "no settings for %s\n", chip_name); + return; + } + + LOGI(this, "read settings for %s\n", chip_name); + + if (of_property_read_u32(chip_node, "flash_default_timeout_ms", &value)) + LOGW(this, "no flash_default_timeout_ms\n"); + else + this->flash.default_timeout_ms = (unsigned long)value; + + if (of_property_read_u32(chip_node, "calibrate_on_fwflash", &value)) + LOGW(this, "no calibrate_on_fwflash config\n"); + else + this->calibrate_on_fwflash = value ? true : false; + + if (of_property_read_u32(chip_node, "calibration_supported", &value)) + LOGW(this, "no calibration_supported\n"); + else + this->calibration_supported = value ? true : false; + + if (of_property_read_u32(chip_node, "hwreset_delay_for_powerup_ms", + &this->reset.delay_for_powerup_ms)) + LOGW(this, "no hwreset_delay_for_powerup_ms config\n"); + + if (of_property_read_u32(chip_node, "interrupt_default_wait_ms", + &this->interrupt.wait_ms)) + LOGW(this, "no interrupt_default_wait_ms config\n"); + + if (of_property_read_u32(chip_node, "charger_only_delay_ms", &value)) + LOGW(this, "no charger_only_delay_ms config\n"); + else + this->charger_only.delay_ms = (unsigned long)value; + + if (!this->reg_offset.updated) + clearpad_reg_offset_config_dt_for_extra_id(this, chip_node); +} + +static int clearpad_touch_config_dt(struct clearpad_t *this) +{ + int rc = 0; + struct device_node *devnode = this->bdata->of_node; + u32 value; + + if (of_property_read_u32(devnode, "chip_id", &this->chip_id)) + LOGW(this, "no chip_id config\n"); + + if (of_property_read_u32(devnode, "post_probe_start", &value)) + LOGW(this, "no post_probe_start config\n"); + else + this->post_probe.start = value ? true : false; + + if (of_property_read_string(devnode, "synaptics,firmware_name", + &this->flash.firmware_name)) + LOGW(this, "no firmware_name config\n"); + + if (of_property_read_u32(devnode, "flash_on_post_probe", &value)) + LOGW(this, "no flash_on_post_probe config\n"); + else + this->flash.on_post_probe = value ? true : false; + + if (of_property_read_u32(devnode, "flip_config", &this->flip_config)) + LOGW(this, "no flip_config config\n"); + + if (of_property_read_u32(devnode, "charger_supported", &value)) + LOGW(this, "no charger_supported config\n"); + else + this->charger.supported = value ? true : false; + + if (of_property_read_u32(devnode, "pen_supported", &value)) + LOGW(this, "no pen_supported config\n"); + else + this->pen.supported = value ? true : false; + + if (of_property_read_u32(devnode, "glove_supported", &value)) + LOGW(this, "no glove_supported config\n"); + else + this->glove.supported = value ? true : false; + + if (of_property_read_u32(devnode, "cover_supported", &value)) + LOGW(this, "no cover_supported config\n"); + else + this->cover.supported = value ? true : false; + + if (of_property_read_u32(devnode, "cover_tag_x_max", + &this->cover.tag_x_max)) + LOGW(this, "no cover_tag_x_max\n"); + + if (of_property_read_u32(devnode, "cover_tag_y_max", + &this->cover.tag_y_max)) + LOGW(this, "no cover_tag_y_max\n"); + + if (of_property_read_u32(devnode, "convert_cover_win_size", + &this->cover.convert_window_size)) + LOGW(this, "no convert_cover_win_size\n"); + + if (of_property_read_u32(devnode, "touch_pressure_enabled", + &this->touch_pressure_enabled)) + LOGW(this, "no touch_pressure_enabled\n"); + + if (of_property_read_u32(devnode, "touch_size_enabled", + &this->touch_size_enabled)) + LOGW(this, "no touch_size_enabled config\n"); + + if (of_property_read_u32(devnode, "touch_orientation_enabled", + &this->touch_orientation_enabled)) + LOGW(this, "no touch_orientation_enabled\n"); + + if (of_property_read_u32(devnode, "preset_x_max", + &this->extents.preset_x_max)) + LOGW(this, "no preset_x_max config\n"); + + if (of_property_read_u32(devnode, "preset_y_max", + &this->extents.preset_y_max)) + LOGW(this, "no preset_y_max config\n"); + + if (of_property_read_u32(devnode, "preset_n_fingers", + &this->extents.n_fingers)) + LOGW(this, "no preset_n_fingers config\n"); + + if (of_property_read_u32(devnode, "wakeup_gesture_supported", &value)) + LOGW(this, "no wakeup_gesture_supported\n"); + else + this->wakeup_gesture.supported = value ? true : false; + + if (of_property_read_u32(devnode, "wakeup_gesture_timeout", + &this->wakeup_gesture.timeout_delay)) + LOGW(this, "no wakeup_gesture_timeout\n"); + + if (of_property_read_u32(devnode, + "wakeup_gesture_use_workaround_for_felica", &value)) + LOGW(this, "no wakeup_gesture_use_workaround_for_felica\n"); + else + this->wakeup_gesture.use_workaround_for_felica = + value ? true : false; + + if (of_property_read_u32(devnode, "watchdog_enabled", &value)) + LOGW(this, "no watchdog_enabled\n"); + else + this->watchdog.enabled = value ? true : false; + + if (of_property_read_u32(devnode, "watchdog_delay_ms", &value)) { + LOGW(this, "no watchdog_delay_ms, watchdog is disabled\n"); + this->watchdog.enabled = false; + } else + this->watchdog.delay = msecs_to_jiffies(value); + + if (of_find_property(devnode, "synaptics,irq_gpio_noise_det", NULL)) { + this->noise_det.supported = true; + this->noise_det.irq_gpio = + of_get_named_gpio_flags(devnode, + "synaptics,irq_gpio_noise_det", 0, + &this->noise_det.irq_gpio_flags); + } else { + LOGI(this, "no synaptics,irq_gpio_noise_det config\n"); + this->noise_det.supported = false; + } + + if (of_property_read_u32(devnode, "noise_det_retrytime_ms", &value)) { + LOGI(this, "no noise_det_retrytime_ms and use default time\n"); + this->noise_det.retry_time_ms = + SYN_RETRY_DEFAULT_TIME_FOR_NOISE_DET; + } else { + this->noise_det.retry_time_ms = value; + } + + if (of_property_read_u32(devnode, "stamina_mode_supported", &value)) { + LOGW(this, "no stamina_mode_supported config\n"); + } else { + this->stamina.supported = + (value & SYN_STAMINA_MODE_SUPPORTED_MASK) ? true : false; + + this->stamina.change_reportrate.supported = + (value & SYN_STAMINA_REPROTRATE_SUPPORTED_MASK) ? true : false; + + this->stamina.doze_holdoff.supported = + (value & SYN_STAMINA_DOZE_HOLDOFF_SUPPORTED_MASK) + ? true : false; + } + + if (of_property_read_u32(devnode, "doze_default_time", &value)) + LOGW(this, "no doze_default_time config\n"); + else + this->stamina.doze_holdoff.default_time = (u8)value; + + if (of_property_read_u32(devnode, "doze_glove_mode_time", &value)) + LOGW(this, "no doze_glove_mode_time config\n"); + else + this->stamina.doze_holdoff.glove_mode_time = (u8)value; + + if (of_property_read_u32(devnode, "doze_cover_mode_time", &value)) + LOGW(this, "no doze_cover_mode_time config\n"); + else + this->stamina.doze_holdoff.cover_mode_time = (u8)value; + + /* read default register offset params */ + clearpad_reg_offset_config_dt(this); + + /* set default chip id settings */ + clearpad_touch_config_dt_for_chip_id(this, 0); + + return rc; +} + +static void clearpad_update_chip_id(struct clearpad_t *this) +{ + u8 *product_id = clearpad_s(this->device_info.product_id, + HEADER_PRODUCT_ID_SIZE); + int id; + + for (id = 0; id < ARRAY_SIZE(clearpad_chip_name); id++) { + if (clearpad_chip_name[id] == NULL) + continue; + if (is_equal_cstring(product_id, clearpad_chip_name[id])) { + this->chip_id = id; + goto update; + } + } +update: + LOGI(this, "chip_id=0x%x\n", this->chip_id); +} + +static void clearpad_set_is_sol(struct clearpad_t *this) +{ + this->is_sol = false; + if (this->chip_id == SYN_CHIP_3500) + this->is_sol = true; + LOGI(this, "is_sol=%s\n", this->is_sol ? "ture" : "false"); +} + +static int clearpad_input_init(struct clearpad_t *this) +{ + int rc = 0; + + if (this->input) { + LOGI(this, "already input device has been allocated\n"); + goto end; + } + + this->input = input_allocate_device(); + if (!this->input) { + rc = -ENOMEM; + goto end; + } + + input_set_drvdata(this->input, this); + + this->input->open = clearpad_device_open; + this->input->close = clearpad_device_close; + this->input->name = CLEARPAD_NAME; + this->input->id.vendor = SYN_CLEARPAD_VENDOR; + this->input->id.product = 1; + this->input->id.version = 1; + this->input->id.bustype = this->bdata->bustype; + + clearpad_funcarea_initialize(this); + + set_bit(EV_ABS, this->input->evbit); + + set_bit(ABS_MT_TRACKING_ID, this->input->absbit); + set_bit(ABS_MT_TOOL_TYPE, this->input->absbit); + + LOGI(this, "touch area [%d, %d, %d, %d]\n", + this->extents.x_min, this->extents.y_min, + this->extents.preset_x_max, this->extents.preset_y_max); + + rc = input_register_device(this->input); + if (rc) { + LOGE(this, "failed to register device\n"); + input_set_drvdata(this->input, NULL); + input_free_device(this->input); + this->input = NULL; + goto end; + } + +end: + return rc; +} + +static int clearpad_input_ev_init(struct clearpad_t *this) +{ + int rc = 0; + + if (this->evdt_node) { + LOGI(this, "already input ev has been initialized\n"); + goto end; + } + + if (this->wakeup_gesture.supported) { + this->evdt_node = evdt_initialize(this->bdata->dev, this->input, + SYN_WAKEUP_GESTURE); + if (!this->evdt_node) { + LOGE(this, "no wakeup_gesture dt\n"); + goto end; + } + + rc = device_create_file(&this->input->dev, + &clearpad_wakeup_gesture_attr); + if (rc) { + LOGE(this, "sysfs_create_file failed: %d\n", rc); + this->evdt_node = NULL; + goto end; + } + LOGI(this, "touch wakeup feature ok\n"); + device_init_wakeup(&this->pdev->dev, 0); + } +end: + return rc; +} + +static int clearpad_pm_suspend(struct device *dev) +{ +#ifdef CLEARPAD_WAKEUP_GESTURE + struct clearpad_t *this = dev_get_drvdata(dev); + + HWLOGI(this, "pm suspend was called\n"); + + if (device_may_wakeup(dev)) { + enable_irq_wake(this->irq); + HWLOGI(this, "enable irq wake"); + } + + HWLOGI(this, "pm suspend(active=%s)\n", + this->dev_active ? "true" : "false"); +#endif + return 0; +} + +static int clearpad_pm_resume(struct device *dev) +{ +#ifdef CLEARPAD_WAKEUP_GESTURE + struct clearpad_t *this = dev_get_drvdata(dev); + + HWLOGI(this, "pm resume was called\n"); + if (device_may_wakeup(dev)) { + disable_irq_wake(this->irq); + HWLOGI(this, "disable irq wake"); + } + + HWLOGI(this, "pm resume(active=%s)\n", + this->dev_active ? "true" : "false"); +#endif + return 0; +} + +/* + * fb and display + */ + +#ifdef CONFIG_FB + +/* need LOCK(&this->lock) */ +static void clearpad_powerdown_core(struct clearpad_t *this, const char *id) +{ + struct clearpad_touchctrl_t *touchctrl = &this->touchctrl; + struct timespec ts; + bool already_powerdown; + bool locked = false; + + get_monotonic_boottime(&ts); + already_powerdown = this->touchctrl.will_powerdown; + if (already_powerdown) { + HWLOGI(this, "received %s again " + "(power=%s icount=%u) @ %ld.%06ld\n", + id, touchctrl_is_touch_powered(this) ? "OK" : "NG", + this->interrupt.count, ts.tv_sec, ts.tv_nsec); + goto end; + } + HWLOGI(this, "%s (power=%s icount=%u) @ %ld.%06ld\n", + id, touchctrl_is_touch_powered(this) ? "OK" : "NG", + this->interrupt.count, ts.tv_sec, ts.tv_nsec); + + if (work_pending(&this->thread_resume.work)) + HWLOGI(this, "flushing pending thread_resume\n"); + UNLOCK(&this->lock); + flush_workqueue(this->thread_resume.work_queue); + LOCK(&this->lock); + locked = touchctrl_lock_power(this, __func__, true, false); + if (!locked) + /* TODO consider this fatal error case */ + LOGE(this, "failed to lock power(user=%d), " + "might cause suspend error\n", touchctrl->power_user); + + this->touchctrl.will_powerdown = true; + + /* suspend mode is set if no user */ + if (locked) + touchctrl_unlock_power(this, __func__); + +end: + return; +} + +static void clearpad_fb_early_powerdown_handler(struct clearpad_t *this) +{ + LOCK(&this->lock); + if (this->wakeup_gesture.enabled) + goto end; + + clearpad_powerdown_core(this, "EARLY POWERDOWN"); +end: + this->wakeup.unblank_early_done = false; + UNLOCK(&this->lock); + return; +} + +static void clearpad_fb_powerdown_handler(struct clearpad_t *this) +{ + LOCK(&this->lock); + if (this->wakeup_gesture.enabled) + clearpad_powerdown_core(this, "POWERDOWN"); + this->wakeup.unblank_done = false; + UNLOCK(&this->lock); + return; +} + +static void clearpad_fb_early_unblank_handler(struct clearpad_t *this) +{ + struct timespec ts; + unsigned long flags; + + if (this->wakeup.unblank_early_done) + return; + + get_monotonic_boottime(&ts); + LOCK(&this->lock); + this->wakeup.unblank_early_done = true; + + if (this->touchctrl.will_powerdown && this->touchctrl.power_user == 0) { + this->interrupt.count = 0; + spin_lock_irqsave(&this->slock, flags); + this->dev_busy = false; + this->irq_pending = false; + spin_unlock_irqrestore(&this->slock, flags); + } + + this->touchctrl.will_powerdown = false; + UNLOCK(&this->lock); + HWLOGI(this, "EARLY UNBLANK @ %ld.%06ld\n", ts.tv_sec, ts.tv_nsec); +} + +static void clearpad_fb_unblank_handler(struct clearpad_t *this) +{ + struct timespec ts; + bool power = touchctrl_is_touch_powered(this); + + get_monotonic_boottime(&ts); + HWLOGI(this, "UNBLANK (power=%s icount=%u active=%s) @ %ld.%06ld\n", + power ? "OK" : "NG", + this->interrupt.count, + this->dev_active ? "true" : "false", + ts.tv_sec, ts.tv_nsec); + + if (!power) { + HWLOGI(this, "ignore UNBLANK event before power on\n"); + return; + } + + if (this->wakeup.unblank_done) + return; + + LOCK(&this->lock); + this->wakeup.unblank_done = true; + if (!this->post_probe.done) { + HWLOGI(this, "ignore UNBLANK event before post probe\n"); + if (this->post_probe.start) { + HWLOGI(this, "schedule post probe work for fail-safe\n"); + schedule_delayed_work(&this->post_probe.work, 0); + } + goto err_in_post_probe_done; + } + + if (work_pending(&this->thread_resume.work)) + HWLOGI(this, "fb_unblank was called again before flushing\n"); + + HWLOGI(this, "schedule for thread resume operation\n"); + queue_work(this->thread_resume.work_queue, + &this->thread_resume.work); + +err_in_post_probe_done: + UNLOCK(&this->lock); + + get_monotonic_boottime(&ts); + HWLOGI(this, "end UNBLANK @ %ld.%06ld\n", + ts.tv_sec, ts.tv_nsec); +} + +static int clearpad_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int blank; + struct clearpad_t *this = + container_of(self, struct clearpad_t, fb_notif); + + if (evdata && evdata->data) { + if (event == FB_EARLY_EVENT_BLANK || + event == FB_EXT_EARLY_EVENT_BLANK) { + blank = *(int *)evdata->data; + HWLOGI(this, "%s: %s\n", + (event == FB_EARLY_EVENT_BLANK) ? "Early" : + "ExtEarly", + (blank == FB_BLANK_POWERDOWN) ? "Powerdown" : + (blank == FB_BLANK_UNBLANK) ? "Unblank" : + "???"); + switch (blank) { + case FB_BLANK_POWERDOWN: + clearpad_fb_early_powerdown_handler(this); + break; + case FB_BLANK_UNBLANK: + clearpad_fb_early_unblank_handler(this); + break; + default: + break; + } + } else if (event == FB_EVENT_BLANK || + event == FB_EXT_EVENT_BLANK) { + blank = *(int *)evdata->data; + HWLOGI(this, "%s: %s\n", + (event == FB_EVENT_BLANK) ? "Blank" : + "ExtBlank", + (blank == FB_BLANK_POWERDOWN) ? "Powerdown" : + (blank == FB_BLANK_UNBLANK) ? "Unblank" : + "???"); + switch (blank) { + case FB_BLANK_POWERDOWN: + clearpad_fb_powerdown_handler(this); + break; + case FB_BLANK_UNBLANK: + clearpad_fb_unblank_handler(this); + default: + break; + } + } + } + return 0; +} + +#else +#error Need CONFIG_FB +#endif + + +/* + * analog test + */ + +#ifdef CONFIG_DEBUG_FS +static int clearpad_debug_hwtest_log(struct clearpad_t *this, + const char *format, ...) +{ + va_list args; + struct clearpad_hwtest_t *hwt = &this->hwtest; + int remain = sizeof(hwt->log_buf) - hwt->log_size; + int length = 0; + struct timespec ts; + + if (remain <= 1) { + /* Rewind */ + get_monotonic_boottime(&ts); + hwt->log_size = + scnprintf(hwt->log_buf, sizeof(hwt->log_buf), + "Rewound @ %ld.%06ld\n", + ts.tv_sec, ts.tv_nsec); + remain = sizeof(hwt->log_buf) - hwt->log_size; + BUG_ON(remain <= 1); + } + + va_start(args, format); + length = vscnprintf(hwt->log_buf + hwt->log_size, remain, + format, args); + va_end(args); + hwt->log_size += length; + + return length; +} + +static void clearpad_analog_test_get_loop_count(struct clearpad_t *this, + u8 mode, int num_tx, int num_rx, int *loop_count_i, + int *loop_count_j, int *data_type) +{ + switch (mode) { + case F54_16_IMAGE_REPORT: + case F54_AUTOSCAN_REPORT: + case F54_RAW_CAP_RX_COUPLING_REPORT: + case F54_SENSOR_SPEED_REPORT: + case F54_FULLINCELL_RAW_CAP_REPORT: + case F54_FULLINCELL_CAL_DATA_CHK_REPORT: + case F54_FULLINCELL_SENSOR_SPEED_REPORT: + case F54_TRX_TO_TRX_SHORT_RAW_IMAGE_REPORT: + *loop_count_i = num_tx; + *loop_count_j = num_rx; + *data_type = HWTEST_S16; + break; + case F54_HIGH_RESISTANCE_REPORT: + *loop_count_i = HWTEST_SIZE_OF_ONE_DIMENSION; + *loop_count_j = HWTEST_SIZE_OF_ONE_HIGH_RX; + *data_type = HWTEST_S16; + break; + case F54_TRX_TO_TRX_SHORT_2_REPORT: + if (this->chip_id == SYN_CHIP_3500) + *loop_count_i = HWTEST_SIZE_OF_TRX_SHORT_2; + else if (this->chip_id == SYN_CHIP_3330) + *loop_count_i = HWTEST_SIZE_OF_TRX_SHORT_2 + 1; + else + *loop_count_i = HWTEST_SIZE_OF_TRX_SHORT_2_TAB; + *loop_count_j = HWTEST_SIZE_OF_ONE_DIMENSION; + *data_type = HWTEST_U8; + break; + case F54_ABS_RAW_REPORT: + *loop_count_i = num_rx + num_tx; + *loop_count_j = HWTEST_SIZE_OF_ONE_DIMENSION; + *data_type = HWTEST_U32; + break; + case F54_EWMODE_RAW_CAP_REPORT: + *loop_count_i = num_rx; + *loop_count_j = HWTEST_SIZE_OF_ONE_DIMENSION; + *data_type = HWTEST_U32; + break; + default: + *loop_count_i = *loop_count_j = *data_type = 0; + break; + } + HWLOGI(this, "loop_count_i[%d], loop_count_j[%d], data_type[%d]\n", + *loop_count_i, *loop_count_j, *data_type); +} + +static void clearpad_analog_test(struct clearpad_t *this, + u8 f_analog, u8 mode, u8 count) +{ + int rc, i, j, k, len, num_tx = 0, num_rx = 0; + int loop_count_i, loop_count_j, data_type, data_size; + u8 buf[14], *data, *line, *pl, fw_rev_extra; + const char delimeter[] = " | "; + + /* Handle unsupported test report */ + switch (this->chip_id) { + case SYN_CHIP_3500: + switch (mode) { + case F54_16_IMAGE_REPORT: + case F54_AUTOSCAN_REPORT: + case F54_HIGH_RESISTANCE_REPORT: + case F54_RAW_CAP_RX_COUPLING_REPORT: + case F54_SENSOR_SPEED_REPORT: + case F54_TRX_TO_TRX_SHORT_2_REPORT: + break; + default: + HWLOGE(this, "analog test is not supported\n"); + goto end; + } + break; + case SYN_CHIP_3330: + switch (mode) { + case F54_16_IMAGE_REPORT: + case F54_AUTOSCAN_REPORT: + case F54_HIGH_RESISTANCE_REPORT: + case F54_RAW_CAP_RX_COUPLING_REPORT: + case F54_SENSOR_SPEED_REPORT: + case F54_TRX_TO_TRX_SHORT_2_REPORT: + case F54_EWMODE_RAW_CAP_REPORT: + case F54_ABS_RAW_REPORT: + case F54_TRX_TO_TRX_SHORT_RAW_IMAGE_REPORT: + break; + default: + HWLOGE(this, "analog test is not supported\n"); + goto end; + } + break; + case SYN_CHIP_332U: + switch (mode) { + case F54_16_IMAGE_REPORT: + case F54_FULLINCELL_RAW_CAP_REPORT: + case F54_FULLINCELL_CAL_DATA_CHK_REPORT: + case F54_FULLINCELL_SENSOR_SPEED_REPORT: + break; + default: + HWLOGE(this, "analog test is not supported\n"); + goto end; + } + break; + default: + HWLOGE(this, "unsupported chip id\n"); + goto end; + } + + memset(buf, 0, sizeof(buf)); + + LOCK(&this->lock); + + /* F01_RMI_CTRL00: Device Control */ + rc = clearpad_put_bit(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), + DEVICE_CONTROL_NO_SLEEP_MASK, + DEVICE_CONTROL_NO_SLEEP_MASK); + if (rc) + goto update_mode; + + /* Wait until sleep mode is completely changed to NO_SLEEP */ + clearpad_set_delay(100); + + /* F01_RMI_CTRL01: Interrupt Enable */ + rc = clearpad_put(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl01), + this->pdt[f_analog].irq_mask); + if (rc) + goto unset_no_sleep_mode; + + rc = clearpad_get_block(SYNF(this, F12_2D, CTRL, + this->reg_offset.f12_ctrl08), + buf, 14); + if (rc) + goto err_set_irq_xy; + num_rx = buf[12]; + num_tx = buf[13]; + + fw_rev_extra = this->device_info.firmware_revision_extra; + + if (this->chip_id == SYN_CHIP_332U) { + if (mode == F54_FULLINCELL_RAW_CAP_REPORT) { + /* F54_ANALOG_CTRL188: Start Calibration or Prod Test */ + rc = clearpad_put_bit( + SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl188), + 0, START_CAL_PROD_TEST_SET_FREQUENCY_MASK); + if (rc) + goto err_set_irq_xy; + /* F54_ANALOG_CTRL188: Start Calibration or Prod Test */ + rc = clearpad_put_bit( + SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl188), + START_CAL_PROD_TEST_START_PROD_TEST_MASK, + START_CAL_PROD_TEST_START_PROD_TEST_MASK); + if (rc) + goto err_set_irq_xy; + } + } else if (this->chip_id == SYN_CHIP_3500) { + if (mode == F54_RAW_CAP_RX_COUPLING_REPORT || + mode == F54_HIGH_RESISTANCE_REPORT || + mode == F54_TRX_TO_TRX_SHORT_2_REPORT) { + switch (fw_rev_extra) { + case 0x03: + /* F54_ANALOG_CTRL149: Trans CBC 2 */ + rc = clearpad_put_bit( + SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl149), 0, + TRANS_CBC_2_TRANS_CBC_GLOBAL_CAP_MASK); + if (rc) + goto err_set_irq_xy; + /* F54_ANALOG_CTRL88: Analog Control 1 */ + rc = clearpad_put_bit( + SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl88), 0, + ANALOG_CONTROL_1_CBC_XMTR_CARRIER_SELECT_MASK); + if (rc) + goto err_set_irq_xy; + /* F54_ANALOG_CTRL57: 0D CBC Settings */ + rc = clearpad_put_bit( + SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl57), 0, + CBC_SETTINGS_XMTR_CARRIER_SELECT_MASK); + if (rc) + goto err_set_irq_xy; + /* F54_ANALOG_CTRL41: + Multimetric Noise Control*/ + rc = clearpad_put_bit( + SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl41), + MULTIMETRIC_NOISE_CTRL_NO_SIGNAL_CLARITY_MASK, + MULTIMETRIC_NOISE_CTRL_NO_SIGNAL_CLARITY_MASK); + if (rc) + goto err_set_irq_xy; + + break; + default: + HWLOGE(this, "firmware is not supported\n"); + break; + } + /* F54_ANALOG_CMD00: Analog Command */ + rc = clearpad_put( + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + ANALOG_COMMAND_FORCE_UPDATE_MASK); + if (rc) + goto err_set_irq_xy; + rc = clearpad_get(SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + buf); + if (rc) + goto err_set_irq_xy; + for (i = 0; + BIT_GET(buf[0], ANALOG_COMMAND_FORCE_UPDATE) != 0; + i++) { + clearpad_set_delay( + SYN_WAIT_TIME_AFTER_REGISTER_ACCESS); + rc = clearpad_get( + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), buf); + if (rc || i > 100) + goto err_set_irq_xy; + HWLOGI(this, + "force update flag = %x, loop = %d\n", + buf[0], i); + } + /* F54_ANALOG_CMD00: Analog Command */ + rc = clearpad_put( + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + ANALOG_COMMAND_FORCE_CALIBRATION_MASK); + if (rc) + goto err_set_irq_xy; + rc = clearpad_get(SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + buf); + if (rc || BIT_GET(buf[0], ANALOG_COMMAND_GET_REPORT)) + goto err_set_irq_xy; + for (i = 0; + BIT_GET(buf[0], ANALOG_COMMAND_FORCE_CALIBRATION) != 0; + i++) { + clearpad_set_delay( + SYN_WAIT_TIME_AFTER_REGISTER_ACCESS); + rc = clearpad_get( + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), buf); + if (rc || i > 100) + goto err_set_irq_xy; + HWLOGI(this, "force cal flag = %x, loop = %d\n", + buf[0], i); + } + } + } + + clearpad_analog_test_get_loop_count(this, mode, num_tx, num_rx, + &loop_count_i, &loop_count_j, &data_type); + switch (data_type) { + case HWTEST_U8: + case HWTEST_S8: + data_size = sizeof(u8); + break; + case HWTEST_S16: + data_size = sizeof(s16); + break; + case HWTEST_U32: + data_size = sizeof(u32); + break; + default: + HWLOGE(this, "unsupported command\n"); + goto err_set_irq_xy; + } + + data = devm_kzalloc(&this->pdev->dev, loop_count_i * loop_count_j * data_size, + GFP_KERNEL); + if (!data) + goto err_set_irq_xy; + len = HWTEST_MAX_DIGITS + 1; + line = devm_kzalloc(&this->pdev->dev, + loop_count_j * (len + sizeof(delimeter)), + GFP_KERNEL); + if (!line) + goto err_kfree_data; + + for (k = 0; k < count; k++) { + s64 min_val = UINT_MAX, max_val = INT_MIN; + + HWLOGI(this, "ANALOG: mode[%d], num[%d], rx[%d], tx[%d]", + mode, k, num_rx, num_tx); + + rc = clearpad_put(SYNF(this, F54_ANALOG, DATA, + this->reg_offset.f54_data00), mode); + if (rc) + goto err_reset; + + clearpad_prepare_for_interrupt(this, &this->interrupt.for_F54, + "F54_ANALOG_CMD00 for analog test"); + + /* F54_ANALOG_CMD00: Analog Command */ + rc = clearpad_put( + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + ANALOG_COMMAND_GET_REPORT_MASK); + if (rc) { + clearpad_undo_prepared_interrupt(this, + &this->interrupt.for_F54, + "F54_ANALOG_CMD00 for analog test"); + goto err_reset; + } + + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, + &this->interrupt.for_F54, + this->interrupt.wait_ms); + LOCK(&this->lock); + if (rc) { + LOGE(this, "failed to get interrupt (rc=%d)\n", rc); + goto err_reset; + } + + rc = clearpad_put(SYNF(this, F54_ANALOG, DATA, + this->reg_offset.f54_data01), 0x00); + if (rc) + goto err_reset; + rc = clearpad_put(SYNF(this, F54_ANALOG, DATA, + this->reg_offset.f54_data02), 0x00); + if (rc) + goto err_reset; + clearpad_set_delay(20); + rc = clearpad_get_block( + SYNF(this, F54_ANALOG, DATA, this->reg_offset.f54_data03), + data, loop_count_i * loop_count_j * data_size); + if (rc) + goto err_reset; + for (i = 0; i < loop_count_i; i++) { + pl = line; + for (j = 0; j < loop_count_j; j++) { + s64 val = 0; + + switch (data_type) { + case HWTEST_U8: + val = (u8)(*(data + (i * loop_count_j) + j)); + break; + case HWTEST_S8: + val = (s8)(*(data + (i * loop_count_j) + j)); + break; + case HWTEST_S16: + val = (s16)le16_to_cpup( + (const u16 *)(data + ((i * loop_count_j) + j) * data_size)); + break; + case HWTEST_U32: + val = (u32)le32_to_cpup( + (const u32 *)(data + ((i * loop_count_j) + j) * data_size)); + break; + default: + break; + } + if (val >= max_val) + max_val = val; + if (val <= min_val) + min_val = val; + pl += snprintf(pl, len, "%6lld", val); + if ((j + 1) % loop_count_j) + pl += snprintf(pl, sizeof(delimeter), + delimeter); + } + HWLOGI(this, "%s\n", line); + } + HWLOGI(this, "MIN = %06lld / MAX = %06lld\n", min_val, max_val); + clearpad_set_delay(100); + } + +err_reset: + devm_kfree(&this->pdev->dev, line); + devm_kfree(&this->pdev->dev, data); + clearpad_reset(this, SYN_FORCE_SWRESET, __func__); + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, + &this->interrupt.for_reset, + this->interrupt.wait_ms); + LOCK(&this->lock); + if (rc) + LOGE(this, "failed to get interrupt (rc=%d)\n", rc); + goto update_mode; + +err_kfree_data: + devm_kfree(&this->pdev->dev, data); +err_set_irq_xy: + if (clearpad_is_valid_function(this, SYN_F12_2D)) + clearpad_put(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl01), + this->pdt[SYN_F12_2D].irq_mask); +unset_no_sleep_mode: + /* F01_RMI_CTRL00: Device Control */ + clearpad_put_bit(SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), + 0, DEVICE_CONTROL_NO_SLEEP_MASK); +update_mode: + UNLOCK(&this->lock); +end: + return; +} + +static int clearpad_debug_hwtest_open(struct inode *inode, + struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int clearpad_hextou8(struct clearpad_t *this, + char **ptr, const char *guard, + u8 *value) +{ + int rc = 0; + char *p = *ptr; + char s[DEBUG_ONE_BYTE_HEX + 1]; + unsigned long v; + + p = skip_spaces(p); + if (p + DEBUG_ONE_BYTE_HEX > guard) { + rc = -EINVAL; + goto end; + } + strlcpy(s, p, DEBUG_ONE_BYTE_HEX + 1); + p += DEBUG_ONE_BYTE_HEX; + rc = kstrtoul(s, 16, &v); + if (rc) { + p = NULL; + goto end; + } + *value = (u8)v; + *ptr = skip_spaces(p); +end: + return rc; +} + +static int clearpad_debug_read_reg(struct clearpad_t *this, + u8 page, u8 reg) +{ + int rc; + u8 value; + + LOCK(&this->lock); + rc = clearpad_get(this, SYN_PAGE_ADDR(page, reg), &value); + if (!rc) + HWLOGI(this, "read page=0x%02x, addr=0x%02x, value=0x%02x\n", + page, reg, value); + else + HWLOGE(this, "error in reading single register\n"); + UNLOCK(&this->lock); + return rc; +} + +static int clearpad_debug_read_packet(struct clearpad_t *this, + u8 page, u8 reg, u8 length) +{ + int rc, i; + u8 *pkt; + + LOCK(&this->lock); + pkt = devm_kzalloc(&this->pdev->dev, length, GFP_KERNEL); + if (!pkt) { + rc = -ENOMEM; + goto end; + } + rc = clearpad_read_block(this, SYN_PAGE_ADDR(page, reg), pkt, length); + if (rc > 0) { + HWLOGI(this, "read page=0x%02x, addr=0x%02x\n", page, reg); + for (i = 0; i < length; i++) + HWLOGI(this, "index[%d]=0x%02x\n", i, pkt[i]); + rc = 0; + } else { + HWLOGE(this, "error in reading packet register\n"); + } + devm_kfree(&this->pdev->dev, pkt); +end: + UNLOCK(&this->lock); + return rc; +} + +static int clearpad_debug_write_reg(struct clearpad_t *this, + u8 page, u8 reg, u8 value) +{ + int rc; + + LOCK(&this->lock); + rc = clearpad_put(this, SYN_PAGE_ADDR(page, reg), value); + if (!rc) + HWLOGI(this, "write page=0x%02x, addr=0x%02x, value=0x%02x\n", + page, reg, value); + else + HWLOGE(this, "error in writing to register\n"); + UNLOCK(&this->lock); + return rc; +} + +static int clearpad_debug_write_packet(struct clearpad_t *this, + u8 page, u8 reg, u8 length, char *b, char *guard) +{ + u8 *pkt; + u8 index, value; + int rc, i; + + LOCK(&this->lock); + pkt = devm_kzalloc(&this->pdev->dev, length, GFP_KERNEL); + if (!pkt) { + rc = -ENOMEM; + goto end; + } + + /* read modified part of packet */ + rc = clearpad_read_block(this, SYN_PAGE_ADDR(page, reg), pkt, length); + if (rc < 0) + goto err_free; + + /* modify indexed data */ + while (b != NULL && *(b + 1) != '\0') { + if (clearpad_hextou8(this, &b, guard, &index) || + clearpad_hextou8(this, &b, guard, &value) || + index >= length) { + HWLOGE(this, "invalid arguments > %s\n", b); + rc = -EINVAL; + goto err_free; + } + HWLOGI(this, "mod index[%d]=0x%02x(0x%02x)\n", + index, value, pkt[index]); + pkt[index] = value; + } + + /* write back packet*/ + rc = clearpad_put_block(this, SYN_PAGE_ADDR(page, reg), pkt, length); + if (rc) { + HWLOGE(this, "error in writing to pkt register\n"); + goto err_free; + } + + HWLOGI(this, "write page=0x%02x, addr=0x%02x\n", page, reg); + for (i = 0; i < length; i++) + HWLOGI(this, "index[%d]=0x%02x\n", i, pkt[i]); + rc = 0; + +err_free: + devm_kfree(&this->pdev->dev, pkt); +end: + UNLOCK(&this->lock); + return rc; +} + +static int clearpad_debug_get_calibration_result(struct clearpad_t *this, + u8 check_mode) +{ + int rc = 0; + u8 buf; + + if (!this->calibration_supported) { + HWLOGE(this, "%s doesn't support calibration\n", + NAME_OF(clearpad_chip_name, this->chip_id)); + goto end; + } + + if (!clearpad_is_valid_function(this, SYN_F54_ANALOG)) { + rc = -EIO; + HWLOGE(this, "F54_ANALOG invalid\n"); + goto end; + } + LOCK(&this->lock); + rc = clearpad_get(SYNF(this, F54_ANALOG, DATA, + this->reg_offset.f54_data31), &buf); + UNLOCK(&this->lock); + if (rc) { + HWLOGE(this, "failed to get CRC\n"); + goto end; + } + + HWLOGI(this, "CRC = 0x%02x\n", buf); + + if (buf & check_mode) { + rc = -EIO; + HWLOGE(this, "calibration is not successful\n"); + } +end: + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_debug_do_calibration(struct clearpad_t *this, int mode) +{ + int rc = 0; + + if (!this->calibration_supported) { + HWLOGE(this, "%s doesn't support calibration\n", + NAME_OF(clearpad_chip_name, this->chip_id)); + goto end; + } + + if (!clearpad_is_valid_function(this, SYN_F54_ANALOG)) { + rc = -EIO; + HWLOGE(this, "F54_ANALOG invalid\n"); + goto end; + } + + rc = clearpad_do_calibration(this, mode, + CALIBRATION_STATE_CALIBRATION_CRC_MASK | + CALIBRATION_STATE_IS_CALIBRATION_CRC_MASK); +end: + return rc; +} + +static int clearpad_debug_print_reg(const char *desc, struct clearpad_t *this, + u16 addr, u8 *buf, int length) +{ + int rc; + int i; + + rc = clearpad_get_block(this, addr, buf, length); + if (rc == 0) + for (i = 0; i < length; i++) + HWLOGI(this, "[0x%04x]/%02d 0x%02x %s\n", + addr, i, buf[i], i == 0 ? desc : ""); + else + HWLOGI(this, "[0x%04x] N/A %s\n", addr, desc); + + return rc; +} + +static void clearpad_debug_registers(struct clearpad_t *this) +{ + u8 buf[80]; + + LOCK(&this->lock); + if (!touchctrl_lock_power(this, __func__, true, false)) { + LOGI(this, "No register info due to no power\n"); + goto end; + } + + if (!clearpad_is_valid_function(this, SYN_F34_FLASH)) + goto reg_F01_RMI; + + HWLOGI(this, "=== F34_FLASH ===\n"); + + /* F34_FLASH_DATA00: Status */ + if (clearpad_debug_print_reg("F34_FLASH_DATA00: Status", + SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data00), buf, 1) == 0) { + HWLOGI(this, INDENT "Status = %d\n", + BIT_GET(buf[0], STATUS_FLASH_STATUS)); + HWLOGI(this, INDENT "Device Cfg Status = %d\n", + BIT_GET(buf[0], STATUS_DEVICE_CONFIG_STATUS)); + HWLOGI(this, INDENT "BL Mode = %d\n", + BIT_GET(buf[0], STATUS_BL_MODE)); + } + + /* F34_FLASH_QUERY01: Bootloader Revision */ + clearpad_debug_print_reg("F34_FLASH_QUERY01: Bootloader Revision", + SYNF(this, F34_FLASH, QUERY, + this->reg_offset.f34_query01), buf, 2); + + /* F34_FLASH_CTRL00: Customer Configuration ID */ + if (clearpad_debug_print_reg("F34_FLASH_CTRL00: Customer Config ID", + SYNF(this, F34_FLASH, CTRL, + this->reg_offset.f34_ctrl00), buf, 5) == 0) + HWLOGI(this, INDENT "family=0x%02x rev=0x%02x.%02x " + "extra=0x%02x aid=0x%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4]); + +reg_F01_RMI: + if (!clearpad_is_valid_function(this, SYN_F01_RMI)) + goto reg_F12_2D; + + HWLOGI(this, "=== F01_RMI ===\n"); + + /* F01_RMI_CTRL00: Device Control */ + if (clearpad_debug_print_reg("F01_RMI_CTRL00: Device Control", + SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl00), buf, 1) == 0) { + HWLOGI(this, INDENT "Sleep Mode = %d\n", + BIT_GET(buf[0], DEVICE_CONTROL_SLEEP_MODE)); + HWLOGI(this, INDENT "No Sleep = %d\n", + BIT_GET(buf[0], DEVICE_CONTROL_NO_SLEEP)); + HWLOGI(this, INDENT "Charger Connected = %d\n", + BIT_GET(buf[0], DEVICE_CONTROL_CHARGER_CONNECTED)); + HWLOGI(this, INDENT "Report Rate = %d\n", + BIT_GET(buf[0], DEVICE_CONTROL_REPORT_RATE)); + HWLOGI(this, INDENT "Configured = %d\n", + BIT_GET(buf[0], DEVICE_CONTROL_CONFIGURED)); + } + + /* F01_RMI_CTRL01: Interrupt Enable 0 */ + clearpad_debug_print_reg("F01_RMI_CTRL01: Interrupt Enable 0", + SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl01), buf, 1); + + /* F01_RMI_CTRL18: Device Control 1 */ + clearpad_debug_print_reg("F01_RMI_CTRL18: Device Control 1", + SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl18), buf, 1); + + /* F01_RMI_CTRL05: Doze Holdoff */ + clearpad_debug_print_reg("F01_RMI_CTRL05: Doze Holdoff", + SYNF(this, F01_RMI, CTRL, + this->reg_offset.f01_ctrl05), buf, 1); + + /* F01_RMI_DATA00: Device Status */ + if (clearpad_debug_print_reg("F01_RMI_DATA00: Device Status", + SYNF(this, F01_RMI, DATA, + this->reg_offset.f01_data00), buf, 1) == 0) { + HWLOGI(this, INDENT "Status Code = %d\n", + BIT_GET(buf[0], DEVICE_STATUS_CODE)); + HWLOGI(this, INDENT "Flash Prog = %d\n", + BIT_GET(buf[0], DEVICE_STATUS_FLASH_PROG)); + HWLOGI(this, INDENT "Unconfigured = %d\n", + BIT_GET(buf[0], DEVICE_STATUS_UNCONFIGURED)); + } + + /* F01_RMI_DATA01.00: Interrupt Status */ + clearpad_debug_print_reg("F01_RMI_DATA01.00: Interrupt Status", + SYNF(this, F01_RMI, DATA, + this->reg_offset.f01_data01), buf, 1); + +reg_F12_2D: + if (!clearpad_is_valid_function(this, SYN_F12_2D)) + goto reg_end; + + HWLOGI(this, "=== F12_2D ===\n"); + + /* F12_2D_DATA15: Object Attention */ + clearpad_debug_print_reg("F12_2D_DATA15: Object Attention", + SYNA(this, F12_2D, DATA, 15), buf, 2); + + /* F12_2D_DATA01: Object Type and Status 0-9 */ + if (clearpad_debug_print_reg("F12_2D_DATA01: Obj Type and Status 0-9", + SYNA(this, F12_2D, DATA, 01), buf, 8 * 10) == 0) { + int i; + u8 *b; + + for (i = 0; i < 10; i++) { + b = &buf[i * 8]; + HWLOGI(this, INDENT "Object %2d: (x,y)=(%d,%d) " + "w=(%d,%d) z=%d t=%d\n", + i, b[1] + (b[2] << 8), b[3] + (b[4] << 8), + b[6], b[7], b[5], b[0]); + } + } + + /* F12_2D_QUERY00: General */ + clearpad_debug_print_reg("F12_2D_QUERY00: General", + SYNA(this, F12_2D, QUERY, 0), buf, 1); + + /* F12_2D_QUERY10: Supported Object Types */ + if (this->chip_id == SYN_CHIP_3330) { + if (clearpad_debug_print_reg( + "F12_2D_QUERY10: Supported Object Types", + SYNA(this, F12_2D, QUERY, 10), buf, 1) == 0) { + HWLOGI(this, INDENT "Gloved Finger = %d\n", + BIT_GET(buf[0], + SUPPORTED_OBJECT_TYPES_HAS_GLOVED_FINGER)); + HWLOGI(this, INDENT "Narrow Object = %d\n", + BIT_GET(buf[0], + SUPPORTED_OBJECT_TYPES_HAS_NARROW_OBJECT)); + HWLOGI(this, INDENT "Hand Edge = %d\n", + BIT_GET(buf[0], + SUPPORTED_OBJECT_TYPES_HAS_HAND_EDGE)); + } + } else { + if (clearpad_debug_print_reg( + "F12_2D_QUERY10: Supported Object Types", + SYNA(this, F12_2D, QUERY, 10), buf, 2) == 0) { + HWLOGI(this, INDENT "Gloved Finger = %d\n", + BIT_GET(buf[0], + SUPPORTED_OBJECT_TYPES_HAS_GLOVED_FINGER)); + HWLOGI(this, INDENT "Cover = %d\n", + BIT_GET(buf[1], + SUPPORTED_OBJECT_TYPES_HAS_COVER)); + HWLOGI(this, INDENT "Stylus = %d\n", + BIT_GET(buf[1], + SUPPORTED_OBJECT_TYPES_HAS_STYLUS)); + HWLOGI(this, INDENT "Eraser = %d\n", + BIT_GET(buf[1], + SUPPORTED_OBJECT_TYPES_HAS_ERASER)); + HWLOGI(this, INDENT "Small Object = %d\n", + BIT_GET(buf[1], + SUPPORTED_OBJECT_TYPES_HAS_SMALL_OBJECT)); + } + } + + /* F12_2D_CTRL20_01: Report Flags */ + if (clearpad_debug_print_reg("F12_2D_CTRL20_01: Report Flags", + SYNA(this, F12_2D, CTRL, 20), buf, F12_2D_CTRL_RPT_REG_MAX) == 0) { + HWLOGI(this, INDENT "Report Always = %d\n", + BIT_GET(buf[F12_2D_CTRL_RPT_FLAG], + REPORT_FLAGS_REPORT_ALWAYS)); + HWLOGI(this, INDENT "Report Wakeup Gesture Only = %d\n", + BIT_GET(buf[F12_2D_CTRL_RPT_FLAG], + REPORT_FLAGS_REPORT_WAKEUP_GESTURE_ONLY)); + HWLOGI(this, INDENT "Enable Dribble = %d\n", + BIT_GET(buf[F12_2D_CTRL_RPT_FLAG], + REPORT_FLAGS_ENABLE_DRIBBLE)); + } + + /* F12_2D_CTRL23_00: Object Report Enable */ + if (clearpad_debug_print_reg("F12_2D_CTRL23_00: Object Report Enable", + SYNA(this, F12_2D, CTRL, 23), buf, 1) == 0) { + HWLOGI(this, INDENT "Finger = %d\n", + BIT_GET(buf[0], OBJECT_REPORT_ENABLE_FINGER)); + HWLOGI(this, INDENT "Stylus = %d\n", + BIT_GET(buf[0], OBJECT_REPORT_ENABLE_STYLUS)); + HWLOGI(this, INDENT "Palm = %d\n", + BIT_GET(buf[0], OBJECT_REPORT_ENABLE_PALM)); + HWLOGI(this, INDENT "Unclassified Object = %d\n", + BIT_GET(buf[0], OBJECT_REPORT_ENABLE_UNCLASSIFIED_OBJECT)); + HWLOGI(this, INDENT "Hovering Finger = %d\n", + BIT_GET(buf[0], OBJECT_REPORT_ENABLE_HOVERING_FINGER)); + HWLOGI(this, INDENT "Gloved Finger = %d\n", + BIT_GET(buf[0], OBJECT_REPORT_ENABLE_GLOVED_FINGER)); + HWLOGI(this, INDENT "Narrow Object = %d\n", + BIT_GET(buf[0], OBJECT_REPORT_ENABLE_NARROW_OBJECT)); + HWLOGI(this, INDENT "Hand Edge = %d\n", + BIT_GET(buf[0], OBJECT_REPORT_ENABLE_HAND_EDGE)); + } + + /* F12_2D_CTRL23_02: Report As Finger */ + if (clearpad_debug_print_reg("F12_2D_CTRL23_02: Report As Finger", + SYNA(this, F12_2D, CTRL, 23), buf, 3) == 0) { + HWLOGI(this, INDENT "Stylus = %d\n", + BIT_GET(buf[F12_2D_CTRL_REPORT_AS_FINGER], + REPORT_AS_FINGER_STYLUS)); + HWLOGI(this, INDENT "Palm = %d\n", + BIT_GET(buf[F12_2D_CTRL_REPORT_AS_FINGER], + REPORT_AS_FINGER_PALM)); + HWLOGI(this, INDENT "Unclassified Object = %d\n", + BIT_GET(buf[F12_2D_CTRL_REPORT_AS_FINGER], + REPORT_AS_FINGER_UNCLASSIFIED_OBJECT)); + HWLOGI(this, INDENT "Gloved Finger = %d\n", + BIT_GET(buf[F12_2D_CTRL_REPORT_AS_FINGER], + REPORT_AS_FINGER_GLOVED_FINGER)); + HWLOGI(this, INDENT "Narrow Object = %d\n", + BIT_GET(buf[F12_2D_CTRL_REPORT_AS_FINGER], + REPORT_AS_FINGER_NARROW_OBJECT)); + HWLOGI(this, INDENT "Hand Edge = %d\n", + BIT_GET(buf[F12_2D_CTRL_REPORT_AS_FINGER], + REPORT_AS_FINGER_HAND_EDGE)); + } + + if (this->chip_id == SYN_CHIP_3330 || + this->chip_id == SYN_CHIP_332U) { + /* F12_2D_CTRL25: Closed Xmin/Xmax/Ymin/Ymax */ + if (clearpad_debug_print_reg( + "F12_2D_CTRL25: Closed Xmin/Xmax/Ymin/Ymax", + SYNA(this, F12_2D, CTRL, 25), buf, 8) == 0) { + HWLOGI(this, INDENT "Closed Cover Xmin = %d\n", + ((buf[1] << 8) | buf[0])); + HWLOGI(this, INDENT " Xmax = %d\n", + ((buf[3] << 8) | buf[2])); + HWLOGI(this, INDENT " Ymin = %d\n", + ((buf[5] << 8) | buf[4])); + HWLOGI(this, INDENT " Ymax = %d\n", + ((buf[7] << 8) | buf[6])); + } + } + + /* F12_2D_CTRL26: Feature Enable */ + if (clearpad_debug_print_reg("F12_2D_CTRL26: Feature Enable", + SYNA(this, F12_2D, CTRL, 26), buf, 1) == 0) { + HWLOGI(this, INDENT "Glove Finger Detection = %d\n", + BIT_GET(buf[0], FEATURE_ENABLE_ENABLE_GLOVED_FINGER_DETECTION)); + HWLOGI(this, INDENT "Closed Cover Detection = %d\n", + BIT_GET(buf[0], FEATURE_ENABLE_ENABLE_CLOSED_COVER_DETECTION)); + } + + /* F12_2D_CTRL27_00: Wakeup Gesture Enable */ + if (clearpad_debug_print_reg("F12_2D_CTRL27_00: Wakeup Gesture Enable", + SYNA(this, F12_2D, CTRL, 27), buf, 1) == 0) { + HWLOGI(this, INDENT "Double Tap = %d\n", + BIT_GET(buf[0], WAKEUP_GESTURE_ENABLE_DOUBLE_TAP)); + HWLOGI(this, INDENT "Swipe = %d\n", + BIT_GET(buf[0], WAKEUP_GESTURE_ENABLE_SWIPE)); + HWLOGI(this, INDENT "Tap And Hold = %d\n", + BIT_GET(buf[0], WAKEUP_GESTURE_ENABLE_TAP_AND_HOLD)); + HWLOGI(this, INDENT "Circle = %d\n", + BIT_GET(buf[0], WAKEUP_GESTURE_ENABLE_CIRCLE)); + HWLOGI(this, INDENT "Triangle = %d\n", + BIT_GET(buf[0], WAKEUP_GESTURE_ENABLE_TRIANGLE)); + HWLOGI(this, INDENT "Vee = %d\n", + BIT_GET(buf[0], WAKEUP_GESTURE_ENABLE_VEE)); + HWLOGI(this, INDENT "Unicode = %d\n", + BIT_GET(buf[0], WAKEUP_GESTURE_ENABLE_UNICODE)); + } + + /* F12_2D_CTRL33_00: Multi-Finger Moisture General */ + if (clearpad_debug_print_reg("F12_2D_CTRL33_00: Multi-Finger " + "Moisture General", + SYNA(this, F12_2D, CTRL, 33), buf, 1) == 0) { + HWLOGI(this, INDENT "Enable Multifinger Moisture = %d\n", + BIT_GET(buf[0], ENABLE_MULTIFINGER_MOISTURE)); + } + if (!clearpad_is_valid_function(this, SYN_F54_ANALOG)) + goto reg_end; + + HWLOGI(this, "=== F51_CUSTOM ===\n"); + + if (this->chip_id == SYN_CHIP_3500) { + /* F51_CUSTOM_CTRL30_06: Custom Report Rate */ + if (clearpad_debug_print_reg( + "F51_CUSTOM_CTRL30_06: Custom Report Rate State", + SYNF(this, F51_CUSTOM, CTRL, + this->reg_offset.f51_ctrl30 + + SYN_EXTERNAL_REPORT_RATE_OFFSET), buf, 1) == 0) { + HWLOGI(this, + INDENT "Custom Report Rate Mode = %d\n", + *buf); + } + } + + HWLOGI(this, "=== F54_ANALOG ===\n"); + + if (this->calibration_supported) { + /* F54_ANALOG_DATA31: Calibration State */ + if (clearpad_debug_print_reg("F54_ANALOG_DATA31: Calibration " + "State", + SYNF(this, F54_ANALOG, DATA, + this->reg_offset.f54_data31), buf, 1) == 0) { + HWLOGI(this, INDENT "Normal Calibration CRC = %d\n", + BIT_GET(buf[0], + CALIBRATION_STATE_CALIBRATION_CRC)); + HWLOGI(this, INDENT "Wake-up Gesture Calibration " + "CRC = %d\n", + BIT_GET(buf[0], + CALIBRATION_STATE_IS_CALIBRATION_CRC)); + } + } + + /* F54_ANALOG_CMD00: Analog Command 0 */ + if (clearpad_debug_print_reg("F54_ANALOG_CMD00: Analog Command 0", + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), buf, 1) == 0) { + HWLOGI(this, INDENT "Force Cal = %d\n", + BIT_GET(buf[0], ANALOG_COMMAND_FORCE_CALIBRATION)); + HWLOGI(this, INDENT "Force Update = %d\n", + BIT_GET(buf[0], ANALOG_COMMAND_FORCE_UPDATE)); + } + + if (this->chip_id == SYN_CHIP_3330) { + + /* F54_ANALOG_CTRL109_00: General Control */ + if (clearpad_debug_print_reg("F54_ANALOG_CTRL109_00: General Control", + SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl109), + buf, 1) == 0) { + HWLOGI(this, INDENT "Baseline Correction Mode = %d\n", + BIT_GET(buf[0], BASELINE_CORRECTION_MODE)); + } + + /* F54_ANALOG_CTRL113_00: General Control */ + if (clearpad_debug_print_reg("F54_ANALOG_CTRL113_00: General Control", + SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl113), + buf, 1) == 0) { + HWLOGI(this, INDENT "Disable Hybrid Baseline = %d\n", + BIT_GET(buf[0], DISABLE_HYBRID_BASELINE)); + } + + /* F54_ANALOG_CTRL147_00: Disable Hybrid CBC Auto Correction */ + if (clearpad_debug_print_reg("F54_ANALOG_CTRL147_00: General Control", + SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl147), + buf, 1) == 0) { + HWLOGI(this, INDENT "Disable Hybrid CBC Auto Correction = %d\n", + BIT_GET(buf[0], DISABLE_HYBRID_CBC_AUTO_CORRECTION)); + } + + /* F54_ANALOG_CTRL214_00: General Control */ + if (clearpad_debug_print_reg("F54_ANALOG_CTRL214_00: General Control", + SYNF(this, F54_ANALOG, CTRL, + this->reg_offset.f54_ctrl214), + buf, 1) == 0) { + HWLOGI(this, INDENT "Enable Hybrid Charger noise " + "Mitigation = %d\n", + BIT_GET(buf[0], ENABLE_HYBRID_CHARGER_NOISE_MITIGATION)); + } + } + + clearpad_reg_offset_print(this); + +reg_end: + touchctrl_unlock_power(this, __func__); + +end: + UNLOCK(&this->lock); + return; +} + +static void clearpad_debug_info(struct clearpad_t *this) +{ + struct clearpad_touchctrl_t *touchctrl = &this->touchctrl; + + HWLOGI(this, "%s, family 0x%02x, fw rev 0x%02x.%02x, " + "extra 0x%02x, aid 0x%02x, state=%s, " + "active=%s, type=%s, irq=%s icount=%u\n", + clearpad_s(this->device_info.product_id, HEADER_PRODUCT_ID_SIZE), + this->device_info.customer_family, + this->device_info.firmware_revision_major, + this->device_info.firmware_revision_minor, + this->device_info.firmware_revision_extra, + this->device_info.analog_id, + NAME_OF(clearpad_state_name, this->state), + this->dev_active ? "true" : "false", + NAME_OF(clearpad_chip_name, this->chip_id), + this->irq_enabled ? "enabled" : "disabled", + this->interrupt.count); + + /* clearpad_post_probe_t */ + HWLOGI(this, "[post_probe] done=%s retry=%d\n", + this->post_probe.done ? "true" : "false", + this->post_probe.retry); + + /* clearpad_touchctrl_t */ + HWLOGI(this, "[power] touch=%s display=%s user=%d will_powerdown=%s\n", + touchctrl_is_touch_powered(this) ? "OK" : "NG", + touchctrl_is_display_powered(this) ? "OK" : "NG", + touchctrl->power_user, + touchctrl->will_powerdown ? "true" : "false"); + + /* clearpad_interrupt_t */ + HWLOGI(this, "[interrupt] hard_handler @ %ld.%06ld " + "threaded_handler @ %ld.%06ld handle_first_event @ %ld.%06ld\n", + this->interrupt.hard_handler_ts.tv_sec, + this->interrupt.hard_handler_ts.tv_nsec, + this->interrupt.threaded_handler_ts.tv_sec, + this->interrupt.threaded_handler_ts.tv_nsec, + this->interrupt.handle_first_event_ts.tv_sec, + this->interrupt.handle_first_event_ts.tv_nsec); + HWLOGI(this, INDENT "dev_busy=%s irq_pending=%s\n", + this->dev_busy ? "true" : "false", + this->irq_pending ? "true" : "false"); + HWLOGI(this, INDENT "reset(use=%s done=%s) " + "F34(use=%s done=%s) F54(use=%s done=%s)\n", + this->interrupt.for_reset.use ? "true" : "false", + atomic_read(&this->interrupt.for_reset.done) ? "true" : "false", + this->interrupt.for_F34.use ? "true" : "false", + atomic_read(&this->interrupt.for_F34.done) ? "true" : "false", + this->interrupt.for_F54.use ? "true" : "false", + atomic_read(&this->interrupt.for_F54.done) ? "true" : "false"); + + /* clearpad_noise_detect_t */ + HWLOGI(this, "[noise_detect] supported=%s 1st_irq=%s" + " retry_time_ms=%d\n", + this->noise_det.supported ? "true" : "false", + this->noise_det.first_irq ? "true" : "false", + this->noise_det.retry_time_ms); + HWLOGI(this, INDENT "enabled=%s hard_irq_c=%u threaded_irq_c=%u\n", + this->noise_det.enabled ? "true" : "false", + this->noise_det.hard_handler_count, + this->noise_det.threaded_handler_count); + HWLOGI(this, INDENT "hard_handler @ %ld.%06ld " + "threaded_handler @ %ld.%06ld\n", + this->noise_det.hard_handler_ts.tv_sec, + this->noise_det.hard_handler_ts.tv_nsec, + this->noise_det.threaded_handler_ts.tv_sec, + this->noise_det.threaded_handler_ts.tv_nsec); + + /* locks */ + HWLOGI(this, "[lock] this->lock: %s owner(%s:%d @ %ld.%06ld)\n", + IS_LOCKED(&this->lock) ? "LOCKED" : "UNLOCKED", + this->lock.owner_func, this->lock.owner_line, + this->lock.ts.tv_sec, this->lock.ts.tv_nsec); + HWLOGI(this, INDENT "session_lock: %s <%s> owner(%s:%d @ %ld.%06ld)\n", + IS_LOCKED(&touchctrl->session_lock) + ? "LOCKED" : "UNLOCKED", touchctrl->session, + touchctrl->session_lock.owner_func, + touchctrl->session_lock.owner_line, + touchctrl->session_lock.ts.tv_sec, + touchctrl->session_lock.ts.tv_nsec); + HWLOGI(this, INDENT "hwtest.lock: %s owner(%s:%d @ %ld.%06ld)\n", + IS_LOCKED(&this->hwtest.lock) ? "LOCKED" : "UNLOCKED", + this->hwtest.lock.owner_func, this->hwtest.lock.owner_line, + this->hwtest.lock.ts.tv_sec, this->hwtest.lock.ts.tv_nsec); + + /* feature flags */ + HWLOGI(this, "[force_sleep] mode=%d\n", this->force_sleep); + HWLOGI(this, "[charger] supported=%s status=%s\n", + this->charger.supported ? "true" : "false", + this->charger.status ? "true" : "false"); + HWLOGI(this, "[charger only] delay_ms=%lu\n", + this->charger_only.delay_ms); + HWLOGI(this, "[stylus] supported=%s enabled=%s\n", + this->pen.supported ? "true" : "false", + this->pen.enabled ? "true" : "false"); + HWLOGI(this, "[glove] supported=%s enabled=%s\n", + this->glove.supported ? "true" : "false", + this->glove.enabled ? "true" : "false"); + HWLOGI(this, "[cover] supported=%s status=%s enabled=%s\n", + this->cover.supported ? "true" : "false", + this->cover.status ? "true" : "false", + this->cover.enabled ? "true" : "false"); + HWLOGI(this, INDENT "win top=%d bottom=%d right=%d left=%d\n", + this->cover.win_top, this->cover.win_bottom, + this->cover.win_right, this->cover.win_left); + HWLOGI(this, INDENT "tag x_max=%u y_max=%u convert_window_size=%u\n", + this->cover.tag_x_max, this->cover.tag_y_max, + this->cover.convert_window_size); + HWLOGI(this, "[wakeup_gesture] supported=%s enabled=%s\n", + this->wakeup_gesture.supported ? "true" : "false", + this->wakeup_gesture.enabled ? "true" : "false"); + HWLOGI(this, "[watchdog] enabled=%s delay=%d\n", + this->watchdog.enabled ? "true" : "false", this->watchdog.delay); + HWLOGI(this, "[stamina_mode] supported=%s enabled=%s\n", + this->stamina.supported ? "true" : "false", + this->stamina.enabled ? "true" : "false"); + HWLOGI(this, INDENT "[change_reportrate] supported=%s mode=%d\n", + this->stamina.change_reportrate.supported ? "true" : "false", + this->stamina.change_reportrate.mode); + HWLOGI(this, INDENT "[doze holdoff] supported=%s default_time=%u " + "glove_mode_time=%u cover_mode_time=%u\n", + this->stamina.doze_holdoff.supported ? "true" : "false", + this->stamina.doze_holdoff.default_time, + this->stamina.doze_holdoff.glove_mode_time, + this->stamina.doze_holdoff.cover_mode_time); + HWLOGI(this, "[early unblank] done=%s early_done=%s\n", + this->wakeup.unblank_done ? "true" : "false", + this->wakeup.unblank_early_done ? "true" : "false"); +} + +static ssize_t clearpad_debug_hwtest_write(struct file *file, + const char __user *buf, size_t count, loff_t *pos) +{ + struct clearpad_t *this = (struct clearpad_t *)file->private_data; + struct clearpad_hwtest_t *hwt = &this->hwtest; + const char *session = "hwtest"; + int rc = 0; + unsigned long arg; + u8 page, reg, value, length, id; + int ms; + char *bhead = NULL; + char *b; + char *guard; + + /* print debug_info if no argument as default without lock */ + if (count < HWTEST_SIZE_OF_COMMAND_PREFIX || *pos != 0) { + clearpad_debug_info(this); + goto end; + } + + LOCK(&hwt->lock); + b = bhead = devm_kzalloc(&this->pdev->dev, count + 1, GFP_KERNEL); + if (!b) { + rc = -ENOMEM; + goto err_in_devm_kzalloc; + } + rc = strncpy_from_user(b, buf, count); + if (!rc) + goto err_in_strncpy_from_user; + + guard = b + count; + while (guard > b) { + if (isascii(guard[-1]) && isgraph(guard[-1])) + break; + guard[-1] = '\0'; + guard--; /* remove garbages */ + } + + + /* init hwtest log_buf with command */ + hwt->log_size = + scnprintf(hwt->log_buf, sizeof(hwt->log_buf), "%s\n", b); + + switch (DEBUG_COMMAND(b[0], b[1])) { + case DEBUG_COMMAND('R', 'B'): + case DEBUG_COMMAND('R', '0'): + /* RB/R0[2:page][2:reg] */ + b += HWTEST_SIZE_OF_COMMAND_PREFIX; + if (clearpad_hextou8(this, &b, guard, &page) || + clearpad_hextou8(this, &b, guard, ®)) + goto err_invalid_arg; + rc = clearpad_debug_read_reg(this, page, reg); + break; + case DEBUG_COMMAND('R', 'P'): + /* RP[2:page][2:reg] [2:length] */ + b += HWTEST_SIZE_OF_COMMAND_PREFIX; + if (clearpad_hextou8(this, &b, guard, &page) || + clearpad_hextou8(this, &b, guard, ®) || + clearpad_hextou8(this, &b, guard, &length)) + goto err_invalid_arg; + rc = clearpad_debug_read_packet(this, page, reg, length); + break; + case DEBUG_COMMAND('W', 'B'): + case DEBUG_COMMAND('W', '0'): + /* WB/W0[2:page][2:reg][2:value] */ + b += HWTEST_SIZE_OF_COMMAND_PREFIX; + if (clearpad_hextou8(this, &b, guard, &page) || + clearpad_hextou8(this, &b, guard, ®) || + clearpad_hextou8(this, &b, guard, &value)) + goto err_invalid_arg; + rc = clearpad_debug_write_reg(this, page, reg, value); + break; + case DEBUG_COMMAND('W', 'P'): + /* WP[2:page][2:reg] [2:length] [2:index][2:value] ... */ + b += HWTEST_SIZE_OF_COMMAND_PREFIX; + if (clearpad_hextou8(this, &b, guard, &page) || + clearpad_hextou8(this, &b, guard, ®) || + clearpad_hextou8(this, &b, guard, &length)) + goto err_invalid_arg; + rc = clearpad_debug_write_packet(this, page, reg, + length, b, guard); + break; + case DEBUG_COMMAND('A', '0'): + /* A0[2:test type][2:count] */ + b += HWTEST_SIZE_OF_COMMAND_PREFIX; + rc = kstrtoul(b, 16, &arg); + if (rc) + goto err_invalid_arg; + if (!clearpad_is_valid_function(this, SYN_F54_ANALOG)) { + HWLOGE(this, "F54 is not supported\n"); + goto err_invalid_arg; + } + value = arg; + reg = arg >> 8; + if (clearpad_ctrl_session_begin(this, session) == 0) { + clearpad_analog_test(this, SYN_F54_ANALOG, reg, value); + clearpad_ctrl_session_end(this, session); + } + break; + case DEBUG_COMMAND('R', 'H'): + /* RH/PO - HW reset */ + if (clearpad_ctrl_session_begin(this, session) == 0) { + LOCK(&this->lock); + clearpad_reset(this, SYN_HWRESET, "HW reset cmd"); + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, + &this->interrupt.for_reset, + this->interrupt.wait_ms); + if (rc) + LOGE(this, "failed to get interrupt (rc=%d)\n", + rc); + clearpad_ctrl_session_end(this, session); + } + break; + case DEBUG_COMMAND('P', '0'): + case DEBUG_COMMAND('R', 'F'): + /* RH/PO - HW reset */ + if (clearpad_ctrl_session_begin(this, session) == 0) { + LOCK(&this->lock); + clearpad_reset(this, SYN_FORCE_HWRESET, "Force HW reset cmd"); + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, + &this->interrupt.for_reset, + this->interrupt.wait_ms); + if (rc) + LOGE(this, "failed to get interrupt (rc=%d)\n", + rc); + clearpad_ctrl_session_end(this, session); + } + break; + case DEBUG_COMMAND('R', 'S'): + case DEBUG_COMMAND('P', '1'): + /* RS/P1 - SW reset */ + if (clearpad_ctrl_session_begin(this, session) == 0) { + LOCK(&this->lock); + clearpad_reset(this, SYN_SWRESET, "SW reset cmd"); + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, + &this->interrupt.for_reset, + this->interrupt.wait_ms); + if (rc) + LOGE(this, "failed to get interrupt (rc=%d)\n", + rc); + clearpad_ctrl_session_end(this, session); + } + break; + case DEBUG_COMMAND('F', 'D'): + /* FD - FW default flash */ + b += HWTEST_SIZE_OF_COMMAND_PREFIX; + HWLOGI(this, "start default firmware flash\n"); + if (clearpad_ctrl_session_begin(this, session) == 0) { + LOCK(&this->lock); + rc = clearpad_fwflash_core(this, SYN_DEFAULT_FLASH, 0); + UNLOCK(&this->lock); + clearpad_ctrl_session_end(this, session); + } + break; + case DEBUG_COMMAND('F', 'F'): + /* FF[2:id] - FW force flash */ + b += HWTEST_SIZE_OF_COMMAND_PREFIX; + if (clearpad_hextou8(this, &b, guard, &id)) { + HWLOGE(this, "need module id for force flash\n"); + goto err_invalid_arg; + } + HWLOGI(this, "start force firmware flash\n"); + if (clearpad_ctrl_session_begin(this, session) == 0) { + LOCK(&this->lock); + rc = clearpad_fwflash_core(this, SYN_FORCE_FLASH, id); + UNLOCK(&this->lock); + clearpad_ctrl_session_end(this, session); + } + break; + case DEBUG_COMMAND('G', 'N'): + case DEBUG_COMMAND('C', '0'): + /* GN/C0 - get normal calibration result */ + if (clearpad_ctrl_session_begin(this, session) == 0) { + rc = clearpad_debug_get_calibration_result(this, + CALIBRATION_STATE_CALIBRATION_CRC_MASK); + clearpad_ctrl_session_end(this, session); + } + break; + case DEBUG_COMMAND('G', 'E'): + case DEBUG_COMMAND('C', '1'): + /* GE/C1 - get EW calibration result */ + if (clearpad_ctrl_session_begin(this, session) == 0) { + rc = clearpad_debug_get_calibration_result(this, + CALIBRATION_STATE_IS_CALIBRATION_CRC_MASK); + clearpad_ctrl_session_end(this, session); + } + break; + case DEBUG_COMMAND('C', 'N'): + case DEBUG_COMMAND('C', '2'): + /* CN/C2 - execute normal calibration */ + if (clearpad_ctrl_session_begin(this, session) == 0) { + LOCK(&this->lock); + rc = clearpad_debug_do_calibration(this, + SYN_CALIBRATION_NORMAL); + UNLOCK(&this->lock); + clearpad_ctrl_session_end(this, session); + } + break; + case DEBUG_COMMAND('C', 'E'): + case DEBUG_COMMAND('C', '3'): + /* CE/C3 - execute EW calibration */ + if (clearpad_ctrl_session_begin(this, session) == 0) { + LOCK(&this->lock); + rc = clearpad_debug_do_calibration(this, + SYN_CALIBRATION_EW); + UNLOCK(&this->lock); + clearpad_ctrl_session_end(this, session); + } + break; + case DEBUG_COMMAND('I', 'N'): + /* IN[cstring:subcommand] - incell command */ + b += HWTEST_SIZE_OF_COMMAND_PREFIX; + b = skip_spaces(b); + if (is_equal_cstring(b, "help")) { + HWLOGI(this, "usage: IN sub-command\n"); + HWLOGI(this, "sub-command: display_off, " + "lock_power, unlock_power\n"); + } else if (is_equal_cstring(b, "display_off")) { + rc = touchctrl_display_off(this); + } else if (is_equal_cstring(b, "lock_power")) { + LOCK(&this->lock); + rc = touchctrl_lock_power(this, __func__, true, false); + UNLOCK(&this->lock); + } else if (is_equal_cstring(b, "unlock_power")) { + LOCK(&this->lock); + touchctrl_unlock_power(this, __func__); + UNLOCK(&this->lock); + } + break; + case DEBUG_COMMAND('X', 'W'): + /* XW[number:interval(ms)] - watchdog (0:stop, else:start) */ + b += HWTEST_SIZE_OF_COMMAND_PREFIX; + b = skip_spaces(b); + cancel_delayed_work_sync(&this->watchdog.work); + if (kstrtoint(b, 0, &ms) || ms == 0) { + this->watchdog.enabled = false; + HWLOGI(this, "stop watchdog\n"); + } else { + this->watchdog.delay = msecs_to_jiffies(ms); + this->watchdog.enabled = true; + HWLOGI(this, "start watchdog (interval ms=%d)\n", ms); + schedule_delayed_work(&this->watchdog.work, + this->watchdog.delay); + } + break; + case DEBUG_COMMAND('X', 'R'): + /* XR - registers */ + clearpad_debug_registers(this); + break; + case DEBUG_COMMAND('X', 'X'): + /* XX - debug info */ + clearpad_debug_info(this); + break; + default: + goto err_invalid_arg; + } + goto end_free; + +err_invalid_arg: + HWLOGE(this, "illegal command\n"); + rc = -EINVAL; +err_in_strncpy_from_user: +end_free: + devm_kfree(&this->pdev->dev, bhead); +err_in_devm_kzalloc: + UNLOCK(&hwt->lock); +end: + return rc ? rc : count; +} + +static ssize_t clearpad_debug_hwtest_read(struct file *file, + char __user *buf, size_t count, loff_t *pos) +{ + struct clearpad_t *this = (struct clearpad_t *)file->private_data; + struct clearpad_hwtest_t *hwt = &this->hwtest; + ssize_t readable_size = min((ssize_t)hwt->log_size - (ssize_t)*pos, + (ssize_t)count); + ssize_t rc; + + LOCK(&hwt->lock); + if (readable_size <= 0) { + HWLOGE(this, "readable size <= 0\n"); + rc = 0; + goto end; + } + if (copy_to_user(buf, &hwt->log_buf[*pos], readable_size)) { + HWLOGE(this, "copying of buffer to read failed\n"); + rc = -EFAULT; + goto end; + } + *pos += readable_size; + rc = readable_size; +end: + UNLOCK(&hwt->lock); + return rc; +} + +/* for Bootloader v6.0 */ +/* need LOCK(&this->lock) */ +static int clearpad_read_pca_block_bl_v6_0(struct clearpad_t *this, + u16 block_num, u8 *data) +{ + int rc; + u16 block; + + block = block_num | + (FLASH_DATA_CONFIGURATION_AREA_SELECT_PERM << + FLASH_DATA_CONFIGURATION_AREA_SELECT_SHIFT); + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data00), + (u8 *)&block, 2); + if (rc) { + HWLOGE(this, "set block number error\n"); + goto end; + } + + /* issue read configuration block command */ + rc = clearpad_put(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data02), + FLASH_CONTROL_READ_CONFIGURATION_BLOCK); + if (rc) { + HWLOGE(this, "issue config error\n"); + goto end; + } + + /* read data block */ + rc = clearpad_get_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data01), + data, SYN_PCA_BLOCK_SIZE); + if (rc) { + HWLOGE(this, "read data error\n"); + goto end; + } + +end: + return rc; +} + +/* for Bootloader v7.x */ +/* need LOCK(&this->lock) */ +static int clearpad_read_pca_block_bl_v7_x(struct clearpad_t *this, + u16 block_num, u8 *data) +{ + int rc; + u16 payload_len = SYN_PAYLOAD_LENGTH; + + /* write partition id */ + rc = clearpad_put( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data01), + PID_GUEST_SERIALIZATION); + if (rc) { + HWLOGE(this, "set partition id error\n"); + goto end; + } + + /* set block number */ + rc = clearpad_put_block( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data02), + (u8 *)&block_num, 2); + if (rc) { + HWLOGE(this, "set block offset error\n"); + goto end; + } + + /* set payload length */ + rc = clearpad_put_block( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data03), + (u8 *)&payload_len, 2); + if (rc) { + HWLOGE(this, "set payload length error\n"); + goto end; + } + + clearpad_prepare_for_interrupt(this, &this->interrupt.for_F34, + "F34_FLASH_DATA04 for read pca"); + /* issue read command */ + /* F34_FLASH_DATA04: Programming Command */ + rc = clearpad_put( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data04), + FLASH_CMD_READ); + if (rc) { + HWLOGE(this, "set flash read command error\n"); + clearpad_undo_prepared_interrupt(this, + &this->interrupt.for_F34, + "F34_FLASH_DATA04 for read pca"); + goto end; + } + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, &this->interrupt.for_F34, + this->interrupt.wait_ms); + if (rc) { + HWLOGE(this, "wait for interrupt status failed %d\n", rc); + LOCK(&this->lock); + goto end; + } + LOCK(&this->lock); + + /* read pca block */ + rc = clearpad_get_block( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data05), + data, SYN_PCA_BLOCK_SIZE); + if (rc) { + HWLOGE(this, "read pca block error\n"); + goto end; + } + +end: + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_read_pca_block(struct clearpad_t *this, + u16 block_num, u8 *data) +{ + int rc; + + if (this->is_sol) + rc = clearpad_read_pca_block_bl_v6_0(this, block_num, data); + else + rc = clearpad_read_pca_block_bl_v7_x(this, block_num, data); + if (rc) + HWLOGE(this, "failed pca read rc = %d\n", rc); + return rc; +} + +/* for Bootloader v6.0 */ +/* need LOCK(&this->lock) */ +static int clearpad_write_pca_block_bl_v6_0(struct clearpad_t *this, + u16 block_num, u8 *data) +{ + int rc; + u16 block; + u8 buf[2]; + + /* change to bootloader mode start */ + this->flash.enter_bootloader_mode = true; + + /* change to bootloader mode start */ + /* read bootloader id */ + rc = clearpad_get_block(SYNF(this, F34_FLASH, QUERY, + this->reg_offset.f34_query00), + buf, sizeof(buf)); + if (rc) { + HWLOGE(this, "get id error\n"); + goto end; + } + + /* write bootloader id to block data */ + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data01), + buf, sizeof(buf)); + if (rc) { + HWLOGE(this, "write id error\n"); + goto end; + } + + clearpad_prepare_for_interrupt(this, &this->interrupt.for_F34, + "F34_FLASH_DATA02 for write pca"); + + /* issue a flash program enable */ + rc = clearpad_put(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data02), + FLASH_CONTROL_ENABLE_FLASH_PROGRAMMING); + if (rc) { + HWLOGE(this, "issue config error\n"); + clearpad_undo_prepared_interrupt(this, + &this->interrupt.for_F34, + "F34_FLASH_DATA02 for write pca"); + goto end; + } + + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, &this->interrupt.for_F34, + this->interrupt.wait_ms); + if (rc) { + HWLOGE(this, "wait for interrupt status failed %d\n", rc); + LOCK(&this->lock); + goto err_exit_bl; + } + LOCK(&this->lock); + + /* reread PDT if it was changed. On success, driver can reset */ + rc = clearpad_read_pdt(this); + if (rc) { + HWLOGE(this, "set pdt error\n"); + goto err_exit_bl; + } + + /* make sure that we are in programming mode and there are no issues */ + rc = clearpad_get(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data03), + buf); + if (rc) { + HWLOGE(this, "get mode error\n"); + goto end; + } + + if (!(BIT_GET(buf[0], FLASH_STATUS_PROGRAM_ENABLED))) { + HWLOGE(this, "failed enabling flash (%s)\n", + clearpad_flash_status[buf[0] & 7]); + rc = -EIO; + goto end; + } + /* changing finished */ + + block = block_num | + (FLASH_DATA_CONFIGURATION_AREA_SELECT_PERM << + FLASH_DATA_CONFIGURATION_AREA_SELECT_SHIFT); + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data00), + (u8 *)&block, 2); + if (rc) { + HWLOGE(this, "set block offset error\n"); + goto err_exit_bl; + } + + /* write block data */ + rc = clearpad_put_block(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data01), + data, SYN_PCA_BLOCK_SIZE); + if (rc) { + dev_err(&this->pdev->dev, + "%s: set data error\n", __func__); + goto end; + } + + clearpad_prepare_for_interrupt(this, &this->interrupt.for_F34, + "F34_FLASH_DATA02 for write pca"); + + /* issue a write configuration block command */ + rc = clearpad_put(SYNF(this, F34_FLASH, DATA, + this->reg_offset.f34_data02), + FLASH_CONTROL_WRITE_CONFIGURATION_BLOCK); + if (rc) { + dev_err(&this->pdev->dev, + "%s: flash error\n", __func__); + clearpad_undo_prepared_interrupt(this, + &this->interrupt.for_F34, + "F34_FLASH_DATA02 for write pca"); + goto end; + } + + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, &this->interrupt.for_F34, + this->interrupt.wait_ms); + if (rc) + HWLOGE(this, "wait for interrupt status failed %d\n", rc); + LOCK(&this->lock); + +err_exit_bl: + /* exit bootloader mode */ + clearpad_reset(this, SYN_SWRESET, __func__); + + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, &this->interrupt.for_reset, + this->interrupt.wait_ms); + if (rc) + HWLOGE(this, "wait for interrupt status failed %d\n", rc); + LOCK(&this->lock); +end: + return rc; +} + +/* for Bootloader v7.x */ +/* need LOCK(&this->lock) */ +static int clearpad_write_pca_block_bl_v7_x(struct clearpad_t *this, + u16 block_num, u8 *data) +{ + int rc; + u8 buf, bl_buf[SYN_SINGLE_TRANSACTION_SIZE] = { + PID_BOOTLOADER, 0x00, 0x00, 0x00, 0x00, + FLASH_CMD_ENTER_BOOTLOADER}; + u16 payload_len = SYN_PAYLOAD_LENGTH; + + /* change to bootloader mode start */ + this->flash.enter_bootloader_mode = true; + + /* read flash program key */ + rc = clearpad_get_block( + SYNF(this, F34_FLASH, QUERY, this->reg_offset.f34_query01), + bl_buf + SYN_FP_KEY_OFFSET, 2); + if (rc) { + HWLOGE(this, "get boot loader revision error\n"); + goto end; + } + + clearpad_prepare_for_interrupt(this, &this->interrupt.for_F34, + "F34_FLASH_DATA01 for write pca"); + + /* issue command to enter bootloader mode with key*/ + /* F34_FLASH_DATA01: Partition ID + * F34_FLASH_DATA02_00: Block Offset + * F34_FLASH_DATA02_01: Block Offset + * F34_FLASH_DATA03_00: Data Transfer Settings + * F34_FLASH_DATA03_01: Data Transfer Settings + * F34_FLASH_DATA04: Programming Command + */ + rc = clearpad_put_block( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data01), + bl_buf, sizeof(bl_buf)); + if (rc) { + HWLOGE(this, "set bl mode error\n"); + clearpad_undo_prepared_interrupt(this, + &this->interrupt.for_F34, + "F34_FLASH_DATA01 for write pca"); + goto end; + } + + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, &this->interrupt.for_F34, + this->interrupt.wait_ms); + if (rc) { + HWLOGE(this, "wait for interrupt status failed %d\n", rc); + LOCK(&this->lock); + goto err_exit_bl; + } + LOCK(&this->lock); + + /* reread PDT if it was changed. On success, driver can reset */ + rc = clearpad_read_pdt(this); + if (rc) { + HWLOGE(this, "set pdt error\n"); + goto err_exit_bl; + } + + /* make sure that we are in programming mode and there are no issues */ + rc = clearpad_get( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data00), + &buf); + if (rc) { + HWLOGE(this, "failed to get flash programming status\n"); + goto err_exit_bl; + } + + if (!(BIT_GET(buf, STATUS_BL_MODE))) { + HWLOGE(this, "failed enabling flash (%s)\n", + NAME_OF(clearpad_flash_status_name, + BIT_GET(buf, STATUS_FLASH_STATUS))); + rc = -EIO; + goto err_exit_bl; + } + /* changing finished */ + + /* write partition id */ + rc = clearpad_put( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data01), + PID_GUEST_SERIALIZATION); + if (rc) { + HWLOGE(this, "set partition id error\n"); + goto err_exit_bl; + } + + /* set block number */ + rc = clearpad_put_block( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data02), + (u8 *)&block_num, 2); + if (rc) { + HWLOGE(this, "set block offset error\n"); + goto err_exit_bl; + } + + /* set payload length */ + rc = clearpad_put_block( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data03), + (u8 *)&payload_len, 2); + if (rc) { + HWLOGE(this, "set length error\n"); + goto err_exit_bl; + } + + /* write command */ + rc = clearpad_put( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data04), + FLASH_CMD_WRITE); + if (rc) { + HWLOGE(this, "set write command error\n"); + goto err_exit_bl; + } + + clearpad_prepare_for_interrupt(this, &this->interrupt.for_F34, + "F34_FLASH_DATA05 for write pca"); + + /* write pca block */ + /* F34_FLASH_DATA05: Payload */ + rc = clearpad_put_block( + SYNF(this, F34_FLASH, DATA, this->reg_offset.f34_data05), + data, SYN_PCA_BLOCK_SIZE); + if (rc) { + HWLOGE(this, "write error\n"); + clearpad_undo_prepared_interrupt(this, + &this->interrupt.for_F34, + "F34_FLASH_DATA05 for write pca"); + goto err_exit_bl; + } + + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, &this->interrupt.for_F34, + this->interrupt.wait_ms); + if (rc) + HWLOGE(this, "wait for interrupt status failed %d\n", rc); + LOCK(&this->lock); + +err_exit_bl: + /* exit bootloader mode */ + clearpad_reset(this, SYN_SWRESET, __func__); + + UNLOCK(&this->lock); + rc = clearpad_wait_for_interrupt(this, &this->interrupt.for_reset, + this->interrupt.wait_ms); + if (rc) + HWLOGE(this, "wait for interrupt status failed %d\n", rc); + LOCK(&this->lock); +end: + return rc; +} + +/* need LOCK(&this->lock) */ +static int clearpad_write_pca_block(struct clearpad_t *this, + u16 block_num, u8 *data) +{ + int rc; + + if (this->is_sol) + rc = clearpad_write_pca_block_bl_v6_0(this, block_num, data); + else + rc = clearpad_write_pca_block_bl_v7_x(this, block_num, data); + if (rc) + HWLOGE(this, "failed pca write rc = %d\n", rc); + return rc; +} + +static long clearpad_debug_pca_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct clearpad_t *this = (struct clearpad_t *)file->private_data; + struct clearpad_hwtest_t *hwt = &this->hwtest; + const char *session = "debug ioctl"; + struct clearpad_ioctl_pca_info pca_info; + int rc; + + LOCK(&hwt->lock); + rc = clearpad_ctrl_session_begin(this, session); + if (rc) + goto err_in_ctrl_begin; + + LOGI(this, "ioctl: %x\n", cmd); + if (copy_from_user(&pca_info, (void *) arg, + sizeof(struct clearpad_ioctl_pca_info))) { + rc = -EFAULT; + HWLOGE(this, "copy_from_user error\n"); + goto err_in_copy_from_user; + } + + switch (cmd) { + case SYN_PCA_IOCTL_GET: + LOCK(&this->lock); + rc = clearpad_read_pca_block(this, pca_info.block_pos, + pca_info.data); + UNLOCK(&this->lock); + if (rc) + break; + + if (copy_to_user((void *) arg, &pca_info, + sizeof(struct clearpad_ioctl_pca_info))) { + rc = -EFAULT; + HWLOGE(this, "copy_to_user error\n"); + } + break; + case SYN_PCA_IOCTL_SET: + LOCK(&this->lock); + rc = clearpad_write_pca_block(this, pca_info.block_pos, + pca_info.data); + UNLOCK(&this->lock); + break; + default: + rc = -EINVAL; + HWLOGE(this, "cmd %d error\n", cmd); + break; + } + +err_in_copy_from_user: + if (rc) + LOGE(this, "failed to access to touch device\n"); + + clearpad_ctrl_session_end(this, session); +err_in_ctrl_begin: + UNLOCK(&hwt->lock); + return rc; +} + +static const struct file_operations clearpad_debug_hwtest_fops = { + .owner = THIS_MODULE, + .open = clearpad_debug_hwtest_open, + .write = clearpad_debug_hwtest_write, + .read = clearpad_debug_hwtest_read, + .unlocked_ioctl = clearpad_debug_pca_ioctl, +}; + +static int clearpad_debug_init(struct clearpad_t *this) +{ + struct dentry *dent = NULL; + int rc = 0; + + mutex_init(&this->hwtest.lock.lock); + dent = debugfs_create_dir("clearpad", 0); + if (!dent || IS_ERR(dent)) { + HWLOGE(this, "debugfs_create_dir error: dent=0x%p\n", dent); + rc = -ENODEV; + goto end; + } + + this->debugfs = dent; + + dent = debugfs_create_file("hwtest", 0600, this->debugfs, + (void *)this, + &clearpad_debug_hwtest_fops); + if (!dent || IS_ERR(dent)) { + HWLOGE(this, "debugfs_create_file error: dent=0x%p\n", dent); + rc = -ENODEV; + goto error; + } + + goto end; + +error: + debugfs_remove_recursive(this->debugfs); + this->debugfs = NULL; +end: + return rc; +} +#endif /* CONFIG_DEBUG_FS */ + +static int clearpad_probe(struct platform_device *pdev) +{ + struct clearpad_data_t *cdata = pdev->dev.platform_data; + struct clearpad_t *this; + struct clearpad_touchctrl_t *touchctrl; + struct kobject *parent; + char *symlink_name; + struct timespec ts; + int rc; + bool retry = false; +#ifdef CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV + struct platform_device *rmi_dev = NULL; +#endif + this = devm_kzalloc(&pdev->dev, sizeof(struct clearpad_t), GFP_KERNEL); + if (!this) { + rc = -ENOMEM; + retry = true; + goto end; + } + /* Start logging for probe (no lock until end of probe) */ + get_monotonic_boottime(&ts); + this->hwtest.log_size = + scnprintf(this->hwtest.log_buf, sizeof(this->hwtest.log_buf), + "start probe @ %ld.%06ld\n", ts.tv_sec, ts.tv_nsec); + + this->state = SYN_STATE_INIT; + this->wakeup.unblank_done = false; + this->wakeup.unblank_early_done = false; + mutex_init(&this->lock.lock); + spin_lock_init(&this->slock); + + touchctrl = &this->touchctrl; + mutex_init(&touchctrl->session_lock.lock); + wake_lock_init(&touchctrl->wakelock, WAKE_LOCK_SUSPEND, WAKE_LOCK_ID); + + atomic_set(&this->interrupt.for_reset.done, 0); + init_waitqueue_head(&this->interrupt.for_reset.wq); + atomic_set(&this->interrupt.for_F34.done, 0); + init_waitqueue_head(&this->interrupt.for_F34.wq); + atomic_set(&this->interrupt.for_F54.done, 0); + init_waitqueue_head(&this->interrupt.for_F54.wq); + INIT_DELAYED_WORK(&this->reset.work, clearpad_reset_work); + INIT_DELAYED_WORK(&this->post_probe.work, clearpad_post_probe_work); + INIT_DELAYED_WORK(&this->watchdog.work, clearpad_watchdog_work); + + this->thread_resume.work_queue + = create_workqueue("clearpad_thread_resume"); + if (!this->thread_resume.work_queue) { + HWLOGE(this, "no create workqueue\n"); + rc = -EINVAL; + goto err_work_queue; + } + INIT_WORK(&this->thread_resume.work, clearpad_thread_resume_work); + + dev_set_drvdata(&pdev->dev, this); + /* LOGx is available after this */ + LOCK(&touchctrl->session_lock); + this->pdev = pdev; + this->pdata = cdata->pdata; + if (!this->pdata) { + HWLOGE(this, "no platform data\n"); + rc = -EINVAL; + goto err_free; + } + + this->bdata = cdata->bdata; + if (!this->bdata) { + HWLOGE(this, "no bus data\n"); + rc = -EINVAL; + goto err_free; + } + + if (this->bdata->of_node) { + rc = clearpad_touch_config_dt(this); + if (rc) { + HWLOGE(this, "err in device tree\n"); + goto err_free; + } + } + + spin_lock_init(&this->noise_det.slock); +#ifdef CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV + if (!cdata->rmi_dev) { + rmi_dev = platform_device_alloc(CLEARPAD_RMI_DEV_NAME, -1); + if (!rmi_dev) { + HWLOGE(this, "err in platform_device_alloc\n"); + rc = -ENOMEM; + retry = true; + goto err_free; + } + + rmi_dev->dev.parent = &pdev->dev; + rc = platform_device_add_data(rmi_dev, cdata, + sizeof(struct clearpad_data_t)); + if (rc) { + HWLOGE(this, "err in platform_device_add_data\n"); + goto err_device_put; + } + + rc = platform_device_add(rmi_dev); + if (rc) { + HWLOGE(this, "err in platform_device_add\n"); + goto err_device_put; + } + + if (!rmi_dev->dev.driver) { + HWLOGE(this, "no rmi dev\n"); + rc = -ENODEV; + goto err_device_del; + } + cdata->rmi_dev = rmi_dev; + } +#endif + +#ifdef CONFIG_DEBUG_FS + /* debugfs */ + rc = clearpad_debug_init(this); + if (rc) { + HWLOGE(this, "failed debug init\n"); + goto err_device_del; + } +#endif + +#ifdef CONFIG_FB + /* Execute post probe the first UNBLANK event + TODO : Must update after API update. */ + HWLOGI(this, "register fb callback\n"); + this->fb_notif.notifier_call = clearpad_fb_notifier_callback; + rc = fb_register_client(&this->fb_notif); + if (rc) { + HWLOGE(this, "unable to register fb_notifier\n"); + goto err_in_fb_register_client; + } +#endif + + this->force_sleep = FSMODE_OFF; + + this->irq = gpio_to_irq(this->pdata->irq_gpio); + rc = devm_request_threaded_irq(&this->pdev->dev, + this->irq, + clearpad_hard_handler, + clearpad_threaded_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + this->pdev->dev.driver->name, this); + if (rc) { + HWLOGE(this, "failed to request threaded irq %d (rc=%d)\n", + this->irq, rc); + goto err_in_request_threaded_irq; + } + disable_irq_nosync(this->irq); + + if (this->noise_det.supported) { + this->noise_det.irq = gpio_to_irq(this->noise_det.irq_gpio); + rc = devm_request_threaded_irq(&this->pdev->dev, + this->noise_det.irq, + clearpad_noise_det_hard_handler, + clearpad_noise_det_threaded_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "clearpad_noise_det", this); + if (rc) { + HWLOGE(this, "failed to request threaded irq %d" + " (rc=%d)\n", + this->noise_det.irq, rc); + goto err_in_request_threaded_irq_gpio_noise_det; + } + disable_irq_nosync(this->noise_det.irq); + } + + HWLOGI(this, "input init\n"); + rc = clearpad_input_init(this); + if (rc) { + HWLOGE(this, "err in input init\n"); + goto err_in_input_init; + } + + HWLOGI(this, "ev init\n"); + rc = clearpad_input_ev_init(this); + if (rc) { + HWLOGE(this, "err in ev init\n"); + goto err_in_ev_init; + } + + /* sysfs */ + HWLOGI(this, "create sysfs\n"); + rc = clearpad_create_sysfs_entries(this, clearpad_sysfs_attrs); + if (rc) { + HWLOGE(this, "unable to create feature sysfs\n"); + goto err_in_create_sysfs_entries; + } + + /* create symlink */ + parent = this->input->dev.kobj.parent; + symlink_name = this->pdata->symlink_name ? : CLEARPAD_NAME; + rc = sysfs_create_link(parent, &this->input->dev.kobj, symlink_name); + if (rc) { + HWLOGE(this, "sysfs_create_link error\n"); + goto err_in_create_link; + } + + if (this->post_probe.start) { + HWLOGI(this, "schedule post probe\n"); + schedule_delayed_work(&this->post_probe.work, 0); + } else { + HWLOGI(this, "post_probe_start sysfs is required" + "to start post probe\n"); + } + UNLOCK(&touchctrl->session_lock); + goto end_log; + +err_in_create_link: + clearpad_remove_sysfs_entries(this, clearpad_sysfs_attrs); +err_in_create_sysfs_entries: +err_in_ev_init: +err_in_input_init: +err_in_request_threaded_irq_gpio_noise_det: +err_in_request_threaded_irq: +#ifdef CONFIG_FB + fb_unregister_client(&this->fb_notif); +err_in_fb_register_client: +#endif +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(this->debugfs); +#endif +err_device_del: +#ifdef CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV + if (rmi_dev) + platform_device_del(rmi_dev); +err_device_put: + if (rmi_dev) + platform_device_put(rmi_dev); +#endif +err_free: + UNLOCK(&touchctrl->session_lock); + dev_set_drvdata(&pdev->dev, NULL); + destroy_workqueue(this->thread_resume.work_queue); +err_work_queue: +end_log: + get_monotonic_boottime(&ts); + HWLOGI(this, "end probe @ %ld.%06ld (rc=%d)\n", + ts.tv_sec, ts.tv_nsec, rc); +end: + if (retry) { + if (cdata->probe_retry < SYN_RETRY_NUM_OF_PROBE) { + rc = -EPROBE_DEFER; + cdata->probe_retry++; + } + } + + return rc; +} + +static void clearpad_post_probe_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct clearpad_post_probe_t *post_probe + = container_of(dwork, struct clearpad_post_probe_t, work); + struct clearpad_t *this + = container_of(post_probe, struct clearpad_t, post_probe); + const char *session = "post probe"; + struct timespec ts; + + int retry; + bool do_reschedule = false; + int rc; + + LOCK(&this->lock); + if (this->post_probe.done) { + HWLOGI(this, "already post probe has been done\n"); + UNLOCK(&this->lock); + goto post_probe_done; + } + UNLOCK(&this->lock); + + get_monotonic_boottime(&ts); + HWLOGI(this, "start post probe @ %ld.%06ld\n", ts.tv_sec, ts.tv_nsec); + + rc = clearpad_ctrl_session_begin(this, session); + if (rc) { + HWLOGE(this, "failed to begin post probe session\n"); + do_reschedule = true; + goto err_in_ctrl_session_begin; + } + + WARN_ON(!touchctrl_is_display_powered(this)); + + LOCK(&this->lock); + if (!this->dev_active) { + rc = clearpad_set_resume_mode(this); + if (rc) + HWLOGE(this, "failed to set resume mode\n"); + } + if (this->flash.on_post_probe) { + this->state = SYN_STATE_DISABLED; + HWLOGI(this, "ensure firmware\n"); + for (retry = 0; retry < SYN_RETRY_NUM_OF_RECOVERY; retry++) { + rc = clearpad_fwflash_core(this, SYN_DEFAULT_FLASH, 0); + if (rc == 0) + break; + HWLOGI(this, "retry fwflash (%d)\n", retry); + clearpad_set_delay(50); + } + if (rc) { + rc = 0; + HWLOGW(this, "fw flash failed but continue init\n"); + } + } + if (this->chip_id == SYN_CHIP_3500) { + /* Remove this part after firmware flash sequence released */ + /* @ Yoshino1.0 */ + HWLOGW(this, "WA for Yoshino BU\n"); + clearpad_reset(this, SYN_SWRESET, __func__); + + this->state = SYN_STATE_RUNNING; + + UNLOCK(&this->lock); + + rc = clearpad_wait_for_interrupt(this, + &this->interrupt.for_reset, this->interrupt.wait_ms); + if (rc) + LOGE(this, "failed to get interrupt (rc=%d)\n", rc); + } else { + this->state = SYN_STATE_RUNNING; + + UNLOCK(&this->lock); + } + + clearpad_ctrl_session_end(this, session); + +err_in_ctrl_session_begin: + get_monotonic_boottime(&ts); + HWLOGI(this, "end post probe @ %ld.%06ld (rc=%d)\n", + ts.tv_sec, ts.tv_nsec, rc); + + if (do_reschedule) { + this->post_probe.retry++; + if (this->post_probe.retry <= SYN_RETRY_NUM_OF_POST_PROBE) { + HWLOGI(this, "reschedule post probe (%d)\n", + this->post_probe.retry); + schedule_delayed_work(&this->post_probe.work, 3 * HZ); + } else { + this->post_probe.retry = 0; + HWLOGE(this, "stop post probe\n"); + } + } + if (!rc) { + LOCK(&this->lock); + this->post_probe.done = true; + clearpad_set_feature_settings(this); + if (this->force_sleep != FSMODE_OFF && this->dev_active) { + rc = clearpad_set_suspend_mode(this); + if (rc) + HWLOGE(this, "failed to force sleep device\n"); + } + UNLOCK(&this->lock); + } + +post_probe_done: + return; +} + +static void clearpad_thread_resume_work(struct work_struct *work) +{ + struct clearpad_thread_resume_t *thread_resume + = container_of(work, struct clearpad_thread_resume_t, work); + struct clearpad_t *this + = container_of(thread_resume, + struct clearpad_t, thread_resume); + struct timespec ts; + bool locked = false; + int rc; + + get_monotonic_boottime(&ts); + LOCK(&this->lock); + if (this->dev_active) { + HWLOGI(this, "device is already active\n"); + goto already_active; + } + + if (this->touchctrl.will_powerdown) { + HWLOGI(this, "not necessary to do thread_resume " + "(power=%s icount=%u) @ %ld.%06ld\n", + touchctrl_is_touch_powered(this) ? "OK" : "NG", + this->interrupt.count, ts.tv_sec, ts.tv_nsec); + goto will_powerdown; + } + + HWLOGI(this, "start thread_resume @ %ld.%06ld\n", + ts.tv_sec, ts.tv_nsec); + + locked = touchctrl_lock_power(this, "fb_unblank", true, false); + if (!locked) { + HWLOGW(this, "power is already turned off"); + goto end; + } + + this->interrupt.count = 0; + if (clearpad_handle_if_first_event(this) < 0) + LOGE(this, "failed to handle first event\n"); + /* Workaround for Kagura sharp panel id 9 & Maple*/ + if (this->chip_id == SYN_CHIP_3500) { + switch (this->device_info.customer_family) { + case 0xd0: + case 0xd1: + HWLOGW(this, "Force Calibration for Maple\n"); + rc = clearpad_put( + SYNF(this, F54_ANALOG, COMMAND, + this->reg_offset.f54_cmd00), + ANALOG_COMMAND_FORCE_CALIBRATION_MASK); + if (rc) + LOGE(this, "failed to force calibrate\n"); + break; + default: + break; + } + } + + touchctrl_unlock_power(this, "fb_unblank"); + + get_monotonic_boottime(&ts); + HWLOGI(this, "end thread_resume @ %ld.%06ld\n", + ts.tv_sec, ts.tv_nsec); + goto end; + +already_active: +will_powerdown: +end: + UNLOCK(&this->lock); + return; +} + +static int clearpad_remove(struct platform_device *pdev) +{ +#ifdef CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV + struct clearpad_data_t *cdata = pdev->dev.platform_data; +#endif + struct clearpad_t *this = dev_get_drvdata(&pdev->dev); + struct clearpad_touchctrl_t *touchctrl = &this->touchctrl; + char *symlink_name = this->pdata->symlink_name ? : CLEARPAD_NAME; + + wake_lock_destroy(&touchctrl->wakelock); + + cancel_delayed_work_sync(&this->watchdog.work); + cancel_delayed_work_sync(&this->reset.work); + flush_workqueue(this->thread_resume.work_queue); + destroy_workqueue(this->thread_resume.work_queue); + device_init_wakeup(&this->pdev->dev, 0); + devm_free_irq(&this->pdev->dev, this->irq, this); + if (this->noise_det.supported) + devm_free_irq(&this->pdev->dev, this->noise_det.irq, this); + sysfs_remove_link(this->input->dev.kobj.parent, symlink_name); +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(this->debugfs); +#endif + clearpad_remove_sysfs_entries(this, clearpad_sysfs_attrs); +#ifdef CONFIG_FB + fb_unregister_client(&this->fb_notif); +#endif + input_unregister_device(this->input); +#ifdef CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV + platform_device_unregister(cdata->rmi_dev); +#endif + dev_set_drvdata(&pdev->dev, NULL); + return 0; +} + +static const struct dev_pm_ops clearpad_pm = { + .suspend = clearpad_pm_suspend, + .resume = clearpad_pm_resume, +}; + +static struct platform_driver clearpad_driver = { + .driver = { + .name = CLEARPAD_NAME, + .owner = THIS_MODULE, + .pm = &clearpad_pm, + }, + .probe = clearpad_probe, + .remove = clearpad_remove, +}; + +static int __init clearpad_init(void) +{ + return platform_driver_register(&clearpad_driver); +} + +static void __exit clearpad_exit(void) +{ + platform_driver_unregister(&clearpad_driver); +} + +late_initcall(clearpad_init); +module_exit(clearpad_exit); + +MODULE_DESCRIPTION(CLEARPAD_NAME "ClearPad Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/clearpad_i2c.c b/drivers/input/touchscreen/clearpad_i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..45f98bf2dc5042b13dc1aba389a3f2aa0ad12db6 --- /dev/null +++ b/drivers/input/touchscreen/clearpad_i2c.c @@ -0,0 +1,343 @@ +/* linux/drivers/input/touchscreen/clearpad_i2c.c + * + * Copyright (c) 2011 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * Author: Yusuke Yoshimura + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CLEARPAD_PAGE_SELECT_REGISTER 0xff +#define CLEARPAD_REG(addr) ((addr) & 0xff) +#define CLEARPAD_PAGE(addr) (((addr) >> 8) & 0xff) + +struct clearpad_i2c_t { + struct platform_device *pdev; + unsigned int page; + struct mutex page_mutex; +}; + +static int clearpad_i2c_set_page(struct device *dev, u8 page) +{ + struct clearpad_i2c_t *this = dev_get_drvdata(dev); + char txbuf[2] = {CLEARPAD_PAGE_SELECT_REGISTER, page}; + int rc = 0; + + rc = i2c_master_send(to_i2c_client(dev), txbuf, sizeof(txbuf)); + if (rc != sizeof(txbuf)) { + dev_err(dev, + "%s: set page failed: %d.", __func__, rc); + rc = (rc < 0) ? rc : -EIO; + goto exit; + } + this->page = page; + rc = 0; +exit: + return rc; +} + +static int clearpad_i2c_read(struct device *dev, u16 addr, u8 *buf, u8 len) +{ + struct clearpad_i2c_t *this = dev_get_drvdata(dev); + s32 rc = 0; + u8 page = CLEARPAD_PAGE(addr); + u8 reg = CLEARPAD_REG(addr); + int rsize = I2C_SMBUS_BLOCK_MAX; + int off; + + mutex_lock(&this->page_mutex); + + if (page != this->page) { + rc = clearpad_i2c_set_page(dev, page); + if (rc < 0) + goto exit; + } + + for (off = 0; off < len; off += rsize) { + if (len < off + I2C_SMBUS_BLOCK_MAX) + rsize = len - off; + rc = i2c_smbus_read_i2c_block_data(to_i2c_client(dev), + reg + off, rsize, &buf[off]); + if (rc != rsize) { + dev_err(dev, "%s: rc = %d\n", __func__, rc); + if (rc > 0) { + off += rc; + break; + } + goto exit; + } + } + rc = off; +exit: + mutex_unlock(&this->page_mutex); + return rc; +} + +static int clearpad_i2c_write(struct device *dev, u16 addr, + const u8 *buf, u8 len) +{ + struct clearpad_i2c_t *this = dev_get_drvdata(dev); + int rc = 0; + u8 reg = CLEARPAD_REG(addr); + u8 i; + + mutex_lock(&this->page_mutex); + + if (CLEARPAD_PAGE(addr) != this->page) { + rc = clearpad_i2c_set_page(dev, CLEARPAD_PAGE(addr)); + if (rc < 0) + goto exit; + } + + for (i = 0; i < len; i++) { + rc = i2c_smbus_write_byte_data(to_i2c_client(dev), + reg + i, buf[i]); + if (rc) + goto exit; + } + rc = i; +exit: + mutex_unlock(&this->page_mutex); + return rc; +} + +static int clearpad_i2c_read_block(struct device *dev, u16 addr, u8 *buf, + int len) +{ + struct clearpad_i2c_t *this = dev_get_drvdata(dev); + u8 txbuf[1] = {addr & 0xff}; + int rc = 0; + + mutex_lock(&this->page_mutex); + + if (CLEARPAD_PAGE(addr) != this->page) { + rc = clearpad_i2c_set_page(dev, CLEARPAD_PAGE(addr)); + if (rc < 0) + goto exit; + } + + rc = i2c_master_send(to_i2c_client(dev), txbuf, sizeof(txbuf)); + if (rc != sizeof(txbuf)) { + rc = (rc < 0) ? rc : -EIO; + goto exit; + } + + rc = i2c_master_recv(to_i2c_client(dev), buf, len); + if (rc < 0) + dev_err(dev, "%s: rc = %d\n", __func__, rc); +exit: + mutex_unlock(&this->page_mutex); + return rc; +} + +static int clearpad_i2c_write_block(struct device *dev, u16 addr, const u8 *buf, + int len) +{ + struct clearpad_i2c_t *this = dev_get_drvdata(dev); + u8 txbuf[len + 1]; + int rc = 0; + + txbuf[0] = addr & 0xff; + memcpy(txbuf + 1, buf, len); + + mutex_lock(&this->page_mutex); + + if (CLEARPAD_PAGE(addr) != this->page) { + rc = clearpad_i2c_set_page(dev, CLEARPAD_PAGE(addr)); + if (rc < 0) + goto exit; + } + + rc = i2c_master_send(to_i2c_client(dev), txbuf, sizeof(txbuf)); + if (rc < 0) + dev_err(dev, "%s: rc = %d\n", __func__, rc); + else + rc -= 1; +exit: + mutex_unlock(&this->page_mutex); + return rc; +} + +static struct clearpad_bus_data_t clearpad_i2c_bus_data = { + .bustype = BUS_I2C, + .set_page = clearpad_i2c_set_page, + .read = clearpad_i2c_read, + .write = clearpad_i2c_write, + .read_block = clearpad_i2c_read_block, + .write_block = clearpad_i2c_write_block, +}; + +#ifdef CONFIG_OF +static int clearpad_parse_dt(struct device *dev, + struct clearpad_platform_data_t *pdata) +{ + struct device_node *np = dev->of_node; + + pdata->irq_gpio = of_get_named_gpio_flags(np, "synaptics,irq_gpio", + 0, &pdata->irq_gpio_flags); + return 0; +} +#else +static int clearpad_parse_dt(struct device *dev, + struct clearpad_platform_data *pdata) +{ + return -ENODEV; +} +#endif + +static int clearpad_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct clearpad_data_t clearpad_data = { + .pdata = NULL, + .bdata = &clearpad_i2c_bus_data, + .probe_retry = 0, +#ifdef CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV + .rmi_dev = NULL, +#endif + }; + struct clearpad_i2c_t *this; + int rc; + + if (client->dev.of_node) { + clearpad_data.pdata = devm_kzalloc(&client->dev, + sizeof(struct clearpad_platform_data_t), + GFP_KERNEL); + if (!clearpad_data.pdata) { + dev_err(&client->dev, "failed to allocate memory\n"); + rc = -ENOMEM; + goto exit; + } + rc = clearpad_parse_dt(&client->dev, clearpad_data.pdata); + if (rc) { + dev_err(&client->dev, "failed to parse device tree\n"); + goto exit; + } + } else { + clearpad_data.pdata = client->dev.platform_data; + } + + this = kzalloc(sizeof(struct clearpad_i2c_t), GFP_KERNEL); + if (!this) { + rc = -ENOMEM; + goto exit; + } + + dev_set_drvdata(&client->dev, this); + + mutex_init(&this->page_mutex); + + this->pdev = platform_device_alloc(CLEARPAD_NAME, -1); + if (!this->pdev) { + rc = -ENOMEM; + goto err_free; + } + clearpad_data.bdata->dev = &client->dev; + clearpad_data.bdata->of_node = client->dev.of_node; + this->pdev->dev.parent = &client->dev; + rc = platform_device_add_data(this->pdev, + &clearpad_data, sizeof(clearpad_data)); + if (rc) + goto err_device_put; + + rc = platform_device_add(this->pdev); + if (rc) + goto err_device_put; + + dev_info(&client->dev, "%s: success\n", __func__); + goto exit; + +err_device_put: + platform_device_put(this->pdev); +err_free: + dev_set_drvdata(&client->dev, NULL); + kfree(this); +exit: + return rc; +} + +static int clearpad_i2c_remove(struct i2c_client *client) +{ + struct clearpad_i2c_t *this = dev_get_drvdata(&client->dev); + platform_device_unregister(this->pdev); + dev_set_drvdata(&client->dev, NULL); + kfree(this); + return 0; +} + +static const struct i2c_device_id clearpad_id[] = { + { CLEARPADI2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, clearpad_id); + +#ifdef CONFIG_OF +static struct of_device_id clearpad_match_table[] = { + { .compatible = "synaptics,clearpad", }, + { }, +}; +#else +#define clearpad_match_table NULL +#endif + +static struct i2c_driver clearpad_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = CLEARPADI2C_NAME, + .of_match_table = clearpad_match_table, + }, + .id_table = clearpad_id, + .probe = clearpad_i2c_probe, + .remove = clearpad_i2c_remove, +}; + +#ifndef MODULE +void clearpad_i2c_init_async(void *unused, async_cookie_t cookie) +{ + int rc; + + rc = i2c_add_driver(&clearpad_i2c_driver); + if (rc != 0) + pr_err("Clearpad I2C registration failed rc = %d\n", rc); +} +#endif + +static int __init clearpad_i2c_init(void) +{ +#ifdef MODULE + return i2c_add_driver(&clearpad_i2c_driver); +#else + async_schedule(clearpad_i2c_init_async, NULL); + return 0; +#endif +} + +static void __exit clearpad_i2c_exit(void) +{ + i2c_del_driver(&clearpad_i2c_driver); +} + +MODULE_DESCRIPTION(CLEARPADI2C_NAME "ClearPad I2C Driver"); +MODULE_LICENSE("GPL v2"); + +module_init(clearpad_i2c_init); +module_exit(clearpad_i2c_exit); diff --git a/drivers/input/touchscreen/clearpad_rmi_dev.c b/drivers/input/touchscreen/clearpad_rmi_dev.c new file mode 100644 index 0000000000000000000000000000000000000000..eb6be785d724bad126e91fdf6ae933ac46dfcb2b --- /dev/null +++ b/drivers/input/touchscreen/clearpad_rmi_dev.c @@ -0,0 +1,506 @@ +/* linux/drivers/input/touchscreen/clearpad_rmi_dev.c + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHAR_DEVICE_NAME "rmi" +#define DEVICE_CLASS_NAME "rmidev" + +#define RMI_CHAR_DEV_TMPBUF_SZ (1024 * 8) +#define RMI_REG_ADDR_PAGE_SELECT 0xFF +#define REG_ADDR_LIMIT 0xFFFF + +struct rmidev_data { + /* mutex for file operation*/ + struct mutex file_mutex; + /* main char dev structure */ + struct cdev main_dev; + + /* pointer to the corresponding RMI4 device. We use this to do */ + /* read, write, etc. */ + + struct platform_device *pdev; + struct clearpad_platform_data_t *pdata; + struct clearpad_bus_data_t *bdata; + /* reference count */ + int ref_count; + + struct class *device_class; + + unsigned char tmpbuf[RMI_CHAR_DEV_TMPBUF_SZ]; +}; + +/*store dynamically allocated major number of char device*/ +static int rmidev_major_num; + + +static struct class *rmidev_device_class; + +static int rmi_read_block(struct rmidev_data *data, u16 addr, u8 *buf, int len) +{ + return data->bdata->read_block(data->bdata->dev, addr, buf, len); +} + +static int rmi_write_block(struct rmidev_data *data, u16 addr, const u8 *buf, + int len) +{ + return data->bdata->write_block(data->bdata->dev, addr, buf, len); +} + +/* file operations for RMI char device */ + +/* + * rmidev_llseek: - use to setup 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(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 *data = filp->private_data; + + if (IS_ERR(data)) { + pr_err("%s: pointer of char device is invalid", __func__); + newpos = -EBADF; + goto exit; + } + + mutex_lock(&(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: /* can't happen */ + newpos = -EINVAL; + goto clean_up; + } + + if (newpos < 0 || newpos > REG_ADDR_LIMIT) { + dev_err(&data->pdev->dev, "newpos 0x%04x is invalid.\n", + (unsigned int)newpos); + newpos = -EINVAL; + goto clean_up; + } + + filp->f_pos = newpos; + +clean_up: + mutex_unlock(&(data->file_mutex)); +exit: + return newpos; +} + +/* + * rmidev_read: - use to read data from RMI stream + * + * @filp: file structure for read + * @buf: user-level buffer pointer + * + * @count: number of byte read + * @f_pos: offset (starting register address) + * + * @return number of bytes read into user buffer (buf) if succeeds + * negative number if error occurs. + */ +static ssize_t rmidev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + struct rmidev_data *data = filp->private_data; + ssize_t retval = 0; + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EINVAL; + goto exit; + } + + /* limit offset to REG_ADDR_LIMIT-1 */ + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + if (count > RMI_CHAR_DEV_TMPBUF_SZ) + count = RMI_CHAR_DEV_TMPBUF_SZ; + + if (count == 0) + goto exit; + + if (IS_ERR(data)) { + pr_err("%s: pointer of char device is invalid", __func__); + retval = -EBADF; + goto exit; + } + + mutex_lock(&(data->file_mutex)); + + retval = rmi_read_block(data, *f_pos, data->tmpbuf, count); + + if (retval < 0) + goto clean_up; + else + *f_pos += retval; + + if (copy_to_user(buf, data->tmpbuf, count)) + retval = -EFAULT; + +clean_up: + + mutex_unlock(&(data->file_mutex)); +exit: + return retval; +} + +/* + * rmidev_write: - use to write data into RMI stream + * + * @filep : file structure for write + * @buf: user-level buffer pointer contains data to be written + * @count: number of byte be be written + * @f_pos: offset (starting register address) + * + * @return number of bytes written from user buffer (buf) if succeeds + * negative number if error occurs. + */ +static ssize_t rmidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct rmidev_data *data = filp->private_data; + ssize_t retval = 0; + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EINVAL; + goto exit; + } + + /* limit offset to REG_ADDR_LIMIT-1 */ + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + if (count > RMI_CHAR_DEV_TMPBUF_SZ) + count = RMI_CHAR_DEV_TMPBUF_SZ; + + if (count == 0) + goto exit; + + if (IS_ERR(data)) { + pr_err("%s: pointer of char device is invalid", __func__); + retval = -EBADF; + goto exit; + } + + if (copy_from_user(data->tmpbuf, buf, count)) { + retval = -EFAULT; + goto exit; + } + + mutex_lock(&(data->file_mutex)); + + retval = rmi_write_block(data, *f_pos, data->tmpbuf, (int)count); + + if (retval >= 0) + *f_pos += count; + + mutex_unlock(&(data->file_mutex)); +exit: + return retval; +} + +/* + * rmidev_open: - get a new handle for from RMI stream + * @inp : inode struture + * @filp: file structure for read/write + * + * @return 0 if succeeds + */ +static int rmidev_open(struct inode *inp, struct file *filp) +{ + struct rmidev_data *data = container_of(inp->i_cdev, + struct rmidev_data, main_dev); + int retval = 0; + + filp->private_data = data; + + if (!data->pdata) { + retval = -EACCES; + goto exit; + } + + mutex_lock(&(data->file_mutex)); + if (data->ref_count < 1) + data->ref_count++; + else + retval = -EACCES; + + mutex_unlock(&(data->file_mutex)); +exit: + return retval; +} + +/* + * rmidev_release: - release an existing handle + * @inp: inode structure + * @filp: file structure for read/write + * + * @return 0 if succeeds + */ +static int rmidev_release(struct inode *inp, struct file *filp) +{ + struct rmidev_data *data = container_of(inp->i_cdev, + struct rmidev_data, main_dev); + int retval = 0; + + if (!data->pdev) { + retval = -EACCES; + goto exit; + } + + mutex_lock(&(data->file_mutex)); + + data->ref_count--; + if (data->ref_count < 0) + data->ref_count = 0; + + mutex_unlock(&(data->file_mutex)); +exit: + return retval; +} + +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, +}; + +/* + * rmi_char_dev_clean_up - release memory or unregister driver + * @rmi_char_dev: rmi_char_dev structure + * + */ +static void rmidev_device_cleanup(struct rmidev_data *data) +{ + dev_t devno; + + /* Get rid of our char dev entries */ + if (data) { + devno = data->main_dev.dev; + + if (data->device_class) + device_destroy(data->device_class, devno); + + cdev_del(&data->main_dev); + + /* cleanup_module is never called if registering failed */ + unregister_chrdev_region(devno, 1); + pr_debug("%s: rmidev device is removed\n", __func__); + } +} + +/* + * rmi_char_devnode - return device permission + * + * @dev: char device structure + * @mode: file permission + * + */ +static char *rmi_char_devnode(struct device *dev, umode_t *mode) +{ + char *pret = NULL; + + if (mode) { + /**mode = 0600*/ + *mode = S_IRUSR|S_IWUSR; + pret = kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev)); + } + return pret; +} + +static int rmidev_init_device(struct rmidev_data *data) +{ + dev_t dev_no; + int retval; + struct device *device_ptr; + + if (rmidev_major_num) { + dev_no = MKDEV(rmidev_major_num, 0); + retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME); + } else { + retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME); + /* let kernel allocate a major for us */ + rmidev_major_num = MAJOR(dev_no); + dev_info(&data->pdev->dev, "Major number of rmidev: %d\n", + rmidev_major_num); + } + if (retval < 0) { + dev_err(&data->pdev->dev, + "Failed to register or allocate char dev, code %d.\n", + retval); + goto exit; + } else { + dev_info(&data->pdev->dev, "Allocated rmidev %d %d.\n", + MAJOR(dev_no), MINOR(dev_no)); + } + + mutex_init(&data->file_mutex); + + cdev_init(&data->main_dev, &rmidev_fops); + + retval = cdev_add(&data->main_dev, dev_no, 1); + if (retval) { + dev_err(&data->pdev->dev, "Error %d adding rmi_char_dev.\n", + retval); + rmidev_device_cleanup(data); + goto exit; + } + + dev_set_name(&data->pdev->dev, "rmidev%d", MINOR(dev_no)); + data->device_class = rmidev_device_class; + device_ptr = device_create( + data->device_class, + NULL, dev_no, NULL, + CHAR_DEVICE_NAME"%d", + MINOR(dev_no)); + + if (IS_ERR(device_ptr)) { + dev_err(&data->pdev->dev, + "Failed to create rmi device.\n"); + rmidev_device_cleanup(data); + retval = -ENODEV; + } +exit: + return retval; +} + +static int rmi_dev_probe(struct platform_device *pdev) +{ + + struct clearpad_data_t *cdata = pdev->dev.platform_data; + struct rmidev_data *data; + int retval = 0; + + data = kzalloc(sizeof(struct rmidev_data), GFP_KERNEL); + if (!data) { + retval = -ENOMEM; + goto exit; + } + + dev_set_drvdata(&pdev->dev, data); + data->pdev = pdev; + data->pdata = cdata->pdata; + if (!data->pdata) { + dev_err(&data->pdev->dev, "no platform data\n"); + retval = -EINVAL; + goto err_free; + } + data->bdata = cdata->bdata; + if (!data->bdata) { + dev_err(&data->pdev->dev, "no bus data\n"); + retval = -EINVAL; + goto err_free; + } + + retval = rmidev_init_device(data); + if (!retval) + goto exit; + +err_free: + dev_set_drvdata(&pdev->dev, NULL); + kfree(data); +exit: + return retval; +} + +static int rmi_dev_remove(struct platform_device *pdev) +{ + struct rmidev_data *data = dev_get_drvdata(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); + kfree(data); + return 0; +} + +static struct platform_driver rmidev_driver = { + .driver = { + .name = CLEARPAD_RMI_DEV_NAME, + .owner = THIS_MODULE, + }, + .probe = rmi_dev_probe, + .remove = rmi_dev_remove, +}; + +static int __init rmidev_init(void) +{ + int retval = 0; + + pr_debug("%s: rmi_dev initialization.\n", __func__); + + /* create device node */ + rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME); + + if (IS_ERR(rmidev_device_class)) { + pr_err("%s: ERROR - Failed to create /dev/%s.\n", __func__, + CHAR_DEVICE_NAME); + retval = -ENODEV; + goto exit; + } + /* setup permission */ + rmidev_device_class->devnode = rmi_char_devnode; + + retval = platform_driver_register(&rmidev_driver); +exit: + return retval; +} + +static void __exit rmidev_exit(void) +{ + pr_debug("%s: exiting.\n", __func__); + platform_driver_unregister(&rmidev_driver); + class_unregister(rmidev_device_class); + class_destroy(rmidev_device_class); +} + +module_init(rmidev_init); +module_exit(rmidev_exit); + +MODULE_AUTHOR("Christopher Heiny "); +MODULE_DESCRIPTION("RMI4 Char Device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 94f1bf772ec93da011dae7415afb7316d25a2204..1c10273d17d4bb8805404150c5456b483fbcaa1d 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -16,6 +16,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 966227a3df1a18fa123fd273b2da4247bfe998e7..271189ea1a43ad24621ed68df9edd32c5c49fba5 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -624,6 +624,13 @@ config LEDS_QPNP_WLED variable brightness. It also supports outputting the Avdd supply for AMOLED displays. +config LEDS_QPNP_RGB_SCALE + bool "Support for QPNP RGB LED brightness scale" + depends on LEDS_QPNP + default n + help + Adds kernel support for scaling the RGB LED brightness. + config LEDS_SYSCON bool "LED support for LEDs on system controllers" depends on LEDS_CLASS=y diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index ba20b8e42fbd687a2f24f16cf34be865977d963b..ca04ba69822e72000fbd7881510abccb21086130 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -8,6 +8,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index c90633b16fadedd276c2b09e98f8535a8257b48e..5a0308539b97fa40324797e0a90540af15b2a5ee 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "flashv2: %s: " fmt, __func__ @@ -101,6 +106,7 @@ #define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25) #define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000) #define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100) +#define SAFETY_TMR_TO_REG_VAL(duration_ms) ((duration_ms / 10) - 1) #define THERMAL_HYST_TEMP_TO_VAL(val, divisor) (val / divisor) #define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6 @@ -203,6 +209,7 @@ struct flash_node_data { int current_ma; int prev_current_ma; u8 duration; + int duration_ms; u8 id; u8 type; u8 ires_idx; @@ -1360,9 +1367,149 @@ static ssize_t qpnp_flash_led_max_current_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", max_current); } +/* sysfs show function for flash_led_fault_status */ +static ssize_t qpnp_flash_led_fault_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct flash_switch_data *snode; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_flash_led *led; + int rc; + uint val; + + snode = container_of(led_cdev, struct flash_switch_data, cdev); + led = dev_get_drvdata(&snode->pdev->dev); + + rc = regmap_read(led->regmap, + FLASH_LED_REG_LED_STATUS1(led->base), &val); + if (rc) { + pr_err("Unable to read fault status rc(%d)\n", rc); + return rc; + } + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +/* sysfs store function for flash strobe */ +static ssize_t qpnp_flash_led_strobe_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct flash_node_data *fnode; + unsigned long state; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; + + fnode = container_of(led_cdev, struct flash_node_data, cdev); + + /* '0' for sw strobe; '1' for hw strobe */ + if (state == 1) + fnode->strobe_ctrl |= FLASH_LED_HW_SW_STROBE_SEL_BIT; + else + fnode->strobe_ctrl &= ~FLASH_LED_HW_SW_STROBE_SEL_BIT; + + return count; +} + +/* sysfs show function for flash duration */ +static ssize_t qpnp_flash_led_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qpnp_flash_led *led; + struct flash_node_data *flash_node; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + flash_node = container_of(led_cdev, struct flash_node_data, cdev); + led = dev_get_drvdata(&flash_node->pdev->dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", flash_node->duration_ms); +} + +/* sysfs store function for flash duration */ +static ssize_t qpnp_flash_led_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qpnp_flash_led *led; + struct flash_node_data *flash_node; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t ret; + unsigned long state; + + flash_node = container_of(led_cdev, struct flash_node_data, cdev); + led = dev_get_drvdata(&flash_node->pdev->dev); + + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; + + flash_node->duration_ms = state; + flash_node->duration = (u8)(SAFETY_TMR_TO_REG_VAL(flash_node->duration_ms) | + FLASH_LED_SAFETY_TMR_ENABLE); + + return count; +} + +/* sysfs show function for flash ires_ua */ +static ssize_t qpnp_flash_led_ires_ua_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qpnp_flash_led *led; + struct flash_node_data *flash_node; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + flash_node = container_of(led_cdev, struct flash_node_data, cdev); + led = dev_get_drvdata(&flash_node->pdev->dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", flash_node->ires_ua); +} + +/* sysfs store function for flash ires_ua */ +static ssize_t qpnp_flash_led_ires_ua_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qpnp_flash_led *led; + struct flash_node_data *flash_node; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t ret; + unsigned long state; + + flash_node = container_of(led_cdev, struct flash_node_data, cdev); + led = dev_get_drvdata(&flash_node->pdev->dev); + + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; + + flash_node->default_ires_ua = flash_node->ires_ua = state; + flash_node->default_ires_idx = flash_node->ires_idx = FLASH_LED_IRES_BASE - + (flash_node->ires_ua - FLASH_LED_IRES_MIN_UA) / FLASH_LED_IRES_DIVISOR; + + return count; +} + /* sysfs attributes exported by flash_led */ static struct device_attribute qpnp_flash_led_attrs[] = { __ATTR(max_current, 0664, qpnp_flash_led_max_current_show, NULL), + __ATTR(fault_status, S_IRUSR | S_IRGRP, + qpnp_flash_led_fault_status_show, + NULL), +}; + +static struct device_attribute qpnp_flash_fnode_attrs[] = { + __ATTR(strobe, (S_IRUGO | S_IWUSR | S_IWGRP), + NULL, + qpnp_flash_led_strobe_type_store), + __ATTR(duration, (S_IRUGO | S_IWUSR | S_IWGRP), + qpnp_flash_led_duration_show, + qpnp_flash_led_duration_store), + __ATTR(ires_ua, (S_IRUGO | S_IWUSR | S_IWGRP), + qpnp_flash_led_ires_ua_show, + qpnp_flash_led_ires_ua_store), }; static int flash_led_psy_notifier_call(struct notifier_block *nb, @@ -1575,6 +1722,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED; rc = of_property_read_u32(node, "qcom,duration-ms", &val); if (!rc) { + fnode->duration_ms = val; fnode->duration = get_safety_timer_code(val); if (fnode->duration) fnode->duration |= FLASH_LED_SAFETY_TMR_ENABLE; @@ -1648,7 +1796,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, "qcom,hw-strobe-edge-trigger"); active_high = !of_property_read_bool(node, "qcom,hw-strobe-active-low"); - hw_strobe = 1; + hw_strobe = !of_property_read_bool(node, "somc,sw-strobe-init"); } else if (fnode->strobe_sel == LPG_STROBE) { /* LPG strobe requires level trigger and active high */ edge_trigger = 0; @@ -1751,7 +1899,8 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led, if (IS_ERR_OR_NULL(snode->vreg)) { rc = PTR_ERR(snode->vreg); if (rc != -EPROBE_DEFER) - pr_err("Failed to get regulator, rc=%d\n", rc); + dev_err(&led->pdev->dev, "Failed to get regulator, rc=%d\n", + rc); snode->vreg = NULL; return rc; } @@ -2210,6 +2359,29 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return 0; } +static int qpnp_flash_led_init_strobe_settings(struct qpnp_flash_led *led) +{ + int rc = 0, i, addr_offset; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_STROBE_CFG(led->base), + FLASH_LED_STROBE_MASK, + led->pdata->hw_strobe_option); + if (rc < 0) + return rc; + + for (i = 0; i < led->num_fnodes; i++) { + addr_offset = led->fnode[i].id; + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_STROBE_CTRL(led->base + addr_offset), + FLASH_LED_STROBE_MASK, led->fnode[i].strobe_ctrl); + if (rc < 0) + return rc; + } + + return rc; +} + static int qpnp_flash_led_probe(struct platform_device *pdev) { struct qpnp_flash_led *led; @@ -2379,6 +2551,23 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) goto unreg_notifier; } + rc = qpnp_flash_led_init_strobe_settings(led); + if (rc < 0) { + pr_err("Failed to initialize flash strobe, rc=%d\n", rc); + goto unreg_notifier; + } + + for (i = 0; i < led->num_fnodes; i++) { + for (j = 0; j < ARRAY_SIZE(qpnp_flash_fnode_attrs); j++) { + rc = sysfs_create_file(&led->fnode[i].cdev.dev->kobj, + &qpnp_flash_fnode_attrs[j].attr); + if (rc < 0) { + pr_err("sysfs creation failed, rc=%d\n", rc); + goto sysfs_fnode_fail; + } + } + } + for (i = 0; i < led->num_snodes; i++) { for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) { rc = sysfs_create_file(&led->snode[i].cdev.dev->kobj, @@ -2396,6 +2585,16 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) return 0; +sysfs_fnode_fail: + for (--j; j >= 0; j--) + sysfs_remove_file(&led->fnode[i].cdev.dev->kobj, + &qpnp_flash_fnode_attrs[j].attr); + + for (--i; i >= 0; i--) { + for (j = 0; j < ARRAY_SIZE(qpnp_flash_fnode_attrs); j++) + sysfs_remove_file(&led->fnode[i].cdev.dev->kobj, + &qpnp_flash_fnode_attrs[j].attr); + } sysfs_fail: for (--j; j >= 0; j--) sysfs_remove_file(&led->snode[i].cdev.dev->kobj, @@ -2406,7 +2605,6 @@ sysfs_fail: sysfs_remove_file(&led->snode[i].cdev.dev->kobj, &qpnp_flash_led_attrs[j].attr); } - i = led->num_snodes; unreg_notifier: power_supply_unreg_notifier(&led->nb); @@ -2440,6 +2638,13 @@ static int qpnp_flash_led_remove(struct platform_device *pdev) led_classdev_unregister(&led->snode[--i].cdev); i = led->num_fnodes; + + for (i = 0; i < led->num_fnodes; i++) { + for (j = 0; j < ARRAY_SIZE(qpnp_flash_fnode_attrs); j++) + sysfs_remove_file(&led->fnode[i].cdev.dev->kobj, + &qpnp_flash_fnode_attrs[j].attr); + } + while (i > 0) led_classdev_unregister(&led->fnode[--i].cdev); diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c index 461693ef9d279e97a4757f0d2db87a9932473c37..d9ecc3c5242eab98226d3b9e165c29ed682d85b2 100644 --- a/drivers/leds/leds-qpnp-wled.c +++ b/drivers/leds/leds-qpnp-wled.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -237,6 +242,13 @@ #define QPNP_WLED_AVDD_MV_TO_REG(val) \ ((val - QPNP_WLED_AVDD_MIN_MV) / QPNP_WLED_AVDD_STEP_MV) +#define QPNP_WLED_CURR_SCALE_MAX 100 +#define QPNP_WLED_BL_SCALE_MAX 1000 +#define QPNP_WLED_BUFF_SIZE 128 + +#define BR_MAX_FIGURE 9 +#define AREA_COUNT_MAX 9999999 + /* output feedback mode */ enum qpnp_wled_fdbk_op { QPNP_WLED_FDBK_AUTO, @@ -311,6 +323,12 @@ static struct wled_vref_setting vref_setting_pmi8998 = { 60000, 397500, 22500, 127500, }; +static unsigned long *area_count; +static int *area_count_table; +static int area_count_table_size; +static int now_area; +static unsigned long start_jiffies; + /** * qpnp_wled - wed data structure * @ cdev - led class device @@ -424,8 +442,24 @@ struct qpnp_wled { bool auto_calib_done; bool module_dis_perm; ktime_t start_ovp_fault_time; + int init_br_ua; + bool bl_scale_enabled; + int bl_scale; + bool calc_curr; + int curr_scale; }; +static bool bl_on_in_boot; +static int __init continous_splash_setup(char *str) +{ + if (!str) + return 0; + if (!strncmp(str, "on", 2)) + bl_on_in_boot = true; + return 0; +} +__setup("display_status=", continous_splash_setup); + static int qpnp_wled_step_delay_us = 52000; module_param_named( total_step_delay_us, qpnp_wled_step_delay_us, int, 0600 @@ -574,10 +608,20 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) u8 reg; u16 low_limit = WLED_MAX_LEVEL_4095 * 4 / 1000; + if (wled->calc_curr && + wled->curr_scale != QPNP_WLED_CURR_SCALE_MAX) + level = level * wled->curr_scale / QPNP_WLED_CURR_SCALE_MAX; + + if (wled->bl_scale_enabled && wled->bl_scale > 0 && + wled->bl_scale < QPNP_WLED_BL_SCALE_MAX) + level = level * wled->bl_scale / QPNP_WLED_BL_SCALE_MAX; + /* WLED's lower limit of operation is 0.4% */ if (level > 0 && level < low_limit) level = low_limit; + pr_debug("%s: brightness=%d level=%d\n", + __func__, wled->cdev.brightness, level); /* set brightness registers */ for (i = 0; i < wled->max_strings; i++) { reg = level & QPNP_WLED_BRIGHT_LSB_MASK; @@ -1025,7 +1069,11 @@ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev, else if (data > QPNP_WLED_FS_CURR_MAX_UA) data = QPNP_WLED_FS_CURR_MAX_UA; - reg = data / QPNP_WLED_FS_CURR_STEP_UA; + if (wled->calc_curr) + reg = (data + (QPNP_WLED_FS_CURR_STEP_UA - 1)) / + QPNP_WLED_FS_CURR_STEP_UA; + else + reg = data / QPNP_WLED_FS_CURR_STEP_UA; rc = qpnp_wled_masked_write_reg(wled, QPNP_WLED_FS_CURR_REG(wled->sink_base, i), QPNP_WLED_FS_CURR_MASK, reg); @@ -1044,6 +1092,148 @@ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev, return count; } +static ssize_t qpnp_wled_bl_scale_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qpnp_wled *wled = dev_get_drvdata(dev); + if (!wled->bl_scale_enabled) + wled->bl_scale = QPNP_WLED_BL_SCALE_MAX; + dev_dbg(&wled->pdev->dev, "%s: bl_scale = %d\n", __func__, wled->bl_scale); + + return snprintf(buf, QPNP_WLED_BUFF_SIZE, "%u\n", wled->bl_scale); +} + +static ssize_t qpnp_wled_bl_scale_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct qpnp_wled *wled = dev_get_drvdata(dev); + unsigned long scale = 0; + ssize_t ret = -EINVAL; + + if (!wled->bl_scale_enabled) { + wled->bl_scale = QPNP_WLED_BL_SCALE_MAX; + dev_err(&wled->pdev->dev, "Sysfs bl_scale is not enabled\n"); + goto exit; + } + + ret = kstrtoul(buf, 10, &scale); + if (!ret) { + ret = size; + if (scale > QPNP_WLED_BL_SCALE_MAX) + scale = QPNP_WLED_BL_SCALE_MAX; + wled->bl_scale = scale; + dev_dbg(&wled->pdev->dev, "%s: bl_scale = %d\n", __func__, wled->bl_scale); + } else { + dev_err(&wled->pdev->dev, "Failure to set sysfs bl_scale\n"); + ret = -EINVAL; + } + +exit: + return ret; +} + +static void update_areacount(int new_area) +{ + unsigned long now; + unsigned long duration; + + now = jiffies; + duration = (now - start_jiffies) >= 0 ? + (now - start_jiffies) : (now + start_jiffies); + area_count[now_area] = area_count[now_area] + + jiffies_to_msecs(duration); + + now_area = new_area; + start_jiffies = now; +} + +static ssize_t qpnp_wled_area_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qpnp_wled *wled = dev_get_drvdata(dev); + int i; + char area_count_str[QPNP_WLED_BUFF_SIZE]; + char count_data[BR_MAX_FIGURE]; + + if (!area_count_table_size) + return snprintf(buf, QPNP_WLED_BUFF_SIZE, "area_count is not supported\n"); + + mutex_lock(&wled->cdev.led_access); + /* fixed statistics */ + update_areacount(now_area); + + memset(area_count_str, 0, sizeof(char) * QPNP_WLED_BUFF_SIZE); + for (i = 0; i < area_count_table_size; i++) { + dev_dbg(&wled->pdev->dev, "%ld, ", area_count[i]); + memset(count_data, 0, sizeof(char) * BR_MAX_FIGURE); + if (area_count[i] > AREA_COUNT_MAX) { + /* over 167 min */ + area_count[i] = AREA_COUNT_MAX; + } + if (i == 0) { + snprintf(count_data, BR_MAX_FIGURE, + "%ld", area_count[i]); + strlcpy(area_count_str, count_data, QPNP_WLED_BUFF_SIZE); + } else { + snprintf(count_data, BR_MAX_FIGURE, + ",%ld", area_count[i]); + strlcat(area_count_str, count_data, QPNP_WLED_BUFF_SIZE); + } + } + + memset(area_count, 0, sizeof(unsigned long) * area_count_table_size); + mutex_unlock(&wled->cdev.led_access); + + return snprintf(buf, QPNP_WLED_BUFF_SIZE, "%s\n", area_count_str); +} + +static ssize_t qpnp_wled_en_cabc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qpnp_wled *wled = dev_get_drvdata(dev); + char *str = wled->en_cabc ? "true" : "false"; + + dev_dbg(&wled->pdev->dev, "%s: en_cabc:%s\n", __func__, str); + + return snprintf(buf, QPNP_WLED_BUFF_SIZE, "%s\n", str); +} + +static ssize_t qpnp_wled_en_cabc_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct qpnp_wled *wled = dev_get_drvdata(dev); + int data, i, rc; + u8 reg, mask; + + rc = kstrtoint(buf, 10, &data); + if (rc) + return rc; + + if (data) + wled->en_cabc = true; + else + wled->en_cabc = false; + + pr_info("%s: set wled->en_cabc:%d\n", __func__, wled->en_cabc); + for (i = 0; i < wled->num_strings; i++) { + reg = wled->en_cabc ? (1 << QPNP_WLED_CABC_SHIFT) : 0; + mask = QPNP_WLED_CABC_MASK; + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_CABC_REG(wled->sink_base, i), + mask, reg); + if (rc < 0) + return rc; + } + + rc = qpnp_wled_sync_reg_toggle(wled); + if (rc < 0) { + dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc); + return rc; + } + + return size; +} + /* sysfs attributes exported by wled */ static struct device_attribute qpnp_wled_attrs[] = { __ATTR(dump_regs, 0664, qpnp_wled_dump_regs_show, NULL), @@ -1055,8 +1245,42 @@ static struct device_attribute qpnp_wled_attrs[] = { __ATTR(ramp_ms, 0664, qpnp_wled_ramp_ms_show, qpnp_wled_ramp_ms_store), __ATTR(ramp_step, 0664, qpnp_wled_ramp_step_show, qpnp_wled_ramp_step_store), + __ATTR(bl_scale, (S_IRUGO | S_IWUSR | S_IWGRP), + qpnp_wled_bl_scale_show, + qpnp_wled_bl_scale_store), + __ATTR(area_count, (S_IRUSR | S_IRGRP), + qpnp_wled_area_count_show, + NULL), + __ATTR(en_cabc, (S_IRUGO | S_IWUSR | S_IWGRP), + qpnp_wled_en_cabc_show, + qpnp_wled_en_cabc_store), }; +static int get_area(int level) +{ + int i; + + for (i = 0; i < area_count_table_size; i++) { + if (i == 0) { + if (level == area_count_table[i]) + break; + } else { + if (level <= area_count_table[i]) + break; + } + } + + return i; +} + +static void qpnp_wled_update_area(int level) +{ + int new_area = get_area(level); + + if (now_area != new_area) + update_areacount(new_area); +} + /* worker for setting wled brightness */ static void qpnp_wled_work(struct work_struct *work) { @@ -1067,6 +1291,7 @@ static void qpnp_wled_work(struct work_struct *work) mutex_lock(&wled->lock); level = wled->cdev.brightness; + qpnp_wled_update_area(level); if (wled->brt_map_table) { /* @@ -1115,6 +1340,7 @@ static void qpnp_wled_work(struct work_struct *work) level ? "en" : "dis"); goto unlock_mutex; } + usleep_range(100, 101); } wled->prev_state = !!level; @@ -2072,44 +2298,49 @@ static int qpnp_wled_config(struct qpnp_wled *wled) if (rc) return rc; - /* disable all current sinks and enable selected strings */ - reg = 0x00; - rc = qpnp_wled_write_reg(wled, QPNP_WLED_CURR_SINK_REG(wled->sink_base), - reg); + if (!bl_on_in_boot) { + /* disable all current sinks and enable selected strings */ + reg = 0x00; + rc = qpnp_wled_write_reg(wled, QPNP_WLED_CURR_SINK_REG(wled->sink_base), + reg); - for (i = 0; i < wled->max_strings; i++) { - /* SYNC DELAY */ - if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US) - wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US; + for (i = 0; i < wled->max_strings; i++) { + /* SYNC DELAY */ + if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US) + wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US; reg = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US; mask = QPNP_WLED_SYNC_DLY_MASK; rc = qpnp_wled_masked_write_reg(wled, QPNP_WLED_SYNC_DLY_REG(wled->sink_base, i), mask, reg); - if (rc < 0) - return rc; + if (rc < 0) + return rc; - /* FULL SCALE CURRENT */ - if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA) - wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA; + /* FULL SCALE CURRENT */ + if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA) + wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA; reg = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA; mask = QPNP_WLED_FS_CURR_MASK; rc = qpnp_wled_masked_write_reg(wled, QPNP_WLED_FS_CURR_REG(wled->sink_base, i), mask, reg); - if (rc < 0) - return rc; + if (rc < 0) + return rc; + if (wled->calc_curr) + temp = (wled->fs_curr_ua + (QPNP_WLED_FS_CURR_STEP_UA - 1)) / + QPNP_WLED_FS_CURR_STEP_UA; + else - /* CABC */ + /* CABC */ reg = wled->en_cabc ? (1 << QPNP_WLED_CABC_SHIFT) : 0; mask = QPNP_WLED_CABC_MASK; rc = qpnp_wled_masked_write_reg(wled, QPNP_WLED_CABC_REG(wled->sink_base, i), mask, reg); - if (rc < 0) - return rc; + if (rc < 0) + return rc; } /* Settings specific to valid sinks */ @@ -2119,10 +2350,10 @@ static int qpnp_wled_config(struct qpnp_wled *wled) return -EINVAL; } /* MODULATOR */ - rc = qpnp_wled_read_reg(wled, + rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_EN_REG(wled->sink_base, i), ®); - if (rc < 0) - return rc; + if (rc < 0) + return rc; reg &= QPNP_WLED_MOD_EN_MASK; reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT); @@ -2131,10 +2362,10 @@ static int qpnp_wled_config(struct qpnp_wled *wled) else reg |= ~QPNP_WLED_GATE_DRV_MASK; - rc = qpnp_wled_write_reg(wled, + rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg); - if (rc) - return rc; + if (rc) + return rc; /* SINK EN */ temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT; @@ -2148,6 +2379,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled) dev_err(&wled->pdev->dev, "Failed to enable WLED sink config rc = %d\n", rc); return rc; + } } rc = qpnp_wled_sync_reg_toggle(wled); @@ -2245,6 +2477,39 @@ static int qpnp_wled_config(struct qpnp_wled *wled) return 0; } +static void qpnp_wled_set_init_br(struct qpnp_wled *wled) +{ + int calc_init_br; + if (wled->fs_curr_ua) { + calc_init_br = (wled->cdev.max_brightness * wled->init_br_ua) + / wled->fs_curr_ua; + } else { + calc_init_br = (wled->cdev.max_brightness * wled->init_br_ua) + / QPNP_WLED_FS_CURR_MAX_UA; + } + qpnp_wled_set(&wled->cdev, calc_init_br); +} + +static int qpnp_wled_set_scale(struct qpnp_wled *wled) +{ + u8 reg = 0; + int rc, curr; + + wled->curr_scale = QPNP_WLED_CURR_SCALE_MAX; + rc = qpnp_wled_read_reg(wled, QPNP_WLED_FS_CURR_REG(wled->sink_base, + wled->strings[0]), ®); + if (rc) + return rc; + reg &= QPNP_WLED_FS_CURR_MASK; + curr = reg * QPNP_WLED_FS_CURR_STEP_UA; + if (curr > wled->fs_curr_ua) + wled->curr_scale = (wled->fs_curr_ua * + QPNP_WLED_CURR_SCALE_MAX) / curr; + dev_dbg(&wled->pdev->dev, "%s: curr = %d fs_curr_ua = %d curr_scale = %d\n", + __func__, curr, wled->fs_curr_ua, wled->curr_scale); + return 0; +} + /* parse wled dtsi parameters */ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) { @@ -2575,6 +2840,10 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) if (!rc) wled->cons_sync_write_delay_us = temp_val; + wled->calc_curr = of_property_read_bool(pdev->dev.of_node, + "somc,calc-curr"); + wled->bl_scale_enabled = of_property_read_bool(pdev->dev.of_node, + "somc,bl-scale-enabled"); wled->en_9b_dim_res = of_property_read_bool(pdev->dev.of_node, "qcom,en-9b-dim-res"); wled->en_phase_stag = of_property_read_bool(pdev->dev.of_node, @@ -2617,6 +2886,50 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) wled->auto_calib_enabled = of_property_read_bool(pdev->dev.of_node, "qcom,auto-calibration-enable"); + wled->init_br_ua = LED_OFF; + rc = of_property_read_u32(pdev->dev.of_node, + "somc,init-br-ua", &temp_val); + if (!rc) { + wled->init_br_ua = temp_val; + } else if (rc != -EINVAL) { + dev_err(&pdev->dev, "Unable to read init ua\n"); + return rc; + } + + rc = of_property_read_u32(pdev->dev.of_node, + "somc,area_count_table_size", &temp_val); + if (!rc) { + area_count_table_size = temp_val; + } else if (rc != -EINVAL) { + dev_err(&pdev->dev, "Unable to read area_count_table_size\n"); + return rc; + } + + if (area_count_table_size > 0) { + area_count_table = devm_kzalloc(&pdev->dev, + sizeof(int) * area_count_table_size, GFP_KERNEL); + if (!area_count_table) + return -ENOMEM; + + area_count = devm_kzalloc(&pdev->dev, + sizeof(unsigned long) * area_count_table_size, GFP_KERNEL); + if (!area_count_table) + return -ENOMEM; + + rc = of_property_read_u32_array(pdev->dev.of_node, "somc,area_count_table", + area_count_table, area_count_table_size); + if (rc < 0) { + dev_err(&pdev->dev, "Unable to read somc,area_count_table\n"); + temp_val = (WLED_MAX_LEVEL_4095 % (area_count_table_size - 1) > 0 ? + ((WLED_MAX_LEVEL_4095 / (area_count_table_size - 1)) + 1) : + (WLED_MAX_LEVEL_4095 / (area_count_table_size - 1))); + area_count_table[0] = 0; + for (i = 1; i < area_count_table_size; i++) { + area_count_table[i] = temp_val * i; + } + } + } + return 0; } @@ -2698,6 +3011,14 @@ static int qpnp_wled_probe(struct platform_device *pdev) return rc; } + if (wled->calc_curr) { + rc = qpnp_wled_set_scale(wled); + if (rc) { + dev_err(&pdev->dev, "wled config failed\n"); + return rc; + } + } + INIT_WORK(&wled->work, qpnp_wled_work); wled->ramp_ms = QPNP_WLED_RAMP_DLY_MS; wled->ramp_step = 1; @@ -2706,6 +3027,8 @@ static int qpnp_wled_probe(struct platform_device *pdev) wled->cdev.brightness_get = qpnp_wled_get; wled->cdev.max_brightness = WLED_MAX_LEVEL_4095; + now_area = 0; + start_jiffies = jiffies; rc = led_classdev_register(&pdev->dev, &wled->cdev); if (rc) { @@ -2722,6 +3045,9 @@ static int qpnp_wled_probe(struct platform_device *pdev) } } + if (wled->init_br_ua && !bl_on_in_boot) + qpnp_wled_set_init_br(wled); + return 0; sysfs_fail: @@ -2748,6 +3074,8 @@ static int qpnp_wled_remove(struct platform_device *pdev) led_classdev_unregister(&wled->cdev); cancel_work_sync(&wled->work); destroy_workqueue(wled->wq); + devm_kfree(&pdev->dev, area_count_table); + devm_kfree(&pdev->dev, area_count); mutex_destroy(&wled->lock); return 0; diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c index e3cfffb5c563cebad18c9fe1c94f64ae3bf21495..ca1f7632ba300fd861b3e9a0fabe120198727af9 100644 --- a/drivers/leds/leds-qpnp.c +++ b/drivers/leds/leds-qpnp.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -175,7 +180,7 @@ #define RGB_LED_EN_CTL(base) (base + 0x46) #define RGB_LED_ATC_CTL(base) (base + 0x47) -#define RGB_MAX_LEVEL LED_FULL +#define RGB_MAX_LEVEL 512 #define RGB_LED_ENABLE_RED 0x80 #define RGB_LED_ENABLE_GREEN 0x40 #define RGB_LED_ENABLE_BLUE 0x20 @@ -187,6 +192,7 @@ #define PWM_LUT_MAX_SIZE 63 #define PWM_GPLED_LUT_MAX_SIZE 31 #define RGB_LED_DISABLE 0x00 +#define PWM_US 1000 #define MPP_MAX_LEVEL LED_FULL #define LED_MPP_MODE_CTRL(base) (base + 0x40) @@ -251,6 +257,13 @@ #define NUM_KPDBL_LEDS 4 #define KPDBL_MASTER_BIT_INDEX 0 +#define RGB_LED_MAX_PAUSE_REG 254 +#define RGB_LED_MAX_PAUSE_TIME 130048 +#define RGB_LED_LUT_MAX_PAUSE 255 +#define RGB_LUT_INDEX_OFFSET 1 +#define RGB_CURR_DEFAULT_PATTERN 1 +#define RGB_CURR_UNIT_NUM 3 + /** * enum qpnp_leds - QPNP supported led ids * @QPNP_ID_WLED - White led backlight @@ -306,6 +319,8 @@ enum led_mode { PWM_MODE = 0, LPG_MODE, MANUAL_MODE, + PWM_DIRECT_MODE, + LPG_SYNC_MODE, }; static u8 wled_debug_regs[] = { @@ -353,6 +368,7 @@ static u8 gpio_debug_regs[] = { * @default_mode - default mode of LED as set in device tree * @use_blink - use blink sysfs entry * @blinking - device is currently blinking w/LPG mode + * @pwm_channel - pwm channel to be configured for led */ struct pwm_config_data { struct lut_params lut_params; @@ -365,6 +381,7 @@ struct pwm_config_data { bool pwm_enabled; bool use_blink; bool blinking; + int pwm_channel; }; /** @@ -499,10 +516,33 @@ struct kpdbl_config_data { * rgb_config_data - rgb configuration data * @pwm_cfg - device pwm configuration * @enable - bits to enable led + * @single_pwm_value - single color max value + * @mix_pwm_value - max color max value */ struct rgb_config_data { struct pwm_config_data *pwm_cfg; u8 enable; + u32 single_pwm_value; + u32 mix_pwm_value; +}; + +struct qpnp_led_data; +enum rgb_sync_state { + RGBSYNC_STATE_NOT_BLINK, + RGBSYNC_STATE_CONFIGURE_TO_BLINK, + RGBSYNC_STATE_BLINKING, +}; +#define LPG_CHANNEL_MAX 6 + +/** + * struct rgb_sync - rgb led synchrnize structure + * @config - pointer to rgb config data + */ +struct rgb_sync { + struct led_classdev cdev; + struct platform_device *pdev; + enum rgb_sync_state sync_state; + struct qpnp_led_data *led_data[LPG_CHANNEL_MAX]; }; /** @@ -552,6 +592,7 @@ struct qpnp_led_data { struct rgb_config_data *rgb_cfg; struct mpp_config_data *mpp_cfg; struct gpio_config_data *gpio_cfg; + struct rgb_sync *rgb_sync; int max_current; bool default_on; bool in_order_command_processing; @@ -564,6 +605,19 @@ static u32 kpdbl_master_period_us; DECLARE_BITMAP(kpdbl_leds_in_use, NUM_KPDBL_LEDS); static bool is_kpdbl_master_turn_on; +static int rgb_current_index; +static int __init rgb_current_setup(char *str) +{ + unsigned int res; + + if (!*str) + return 0; + if (!kstrtouint(str, 16, &res)) + rgb_current_index = res; + return 1; +} +__setup("oemandroidboot.babe137e=", rgb_current_setup); + static int qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val) { @@ -1708,12 +1762,101 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led) return 0; } +#ifdef CONFIG_LEDS_QPNP_RGB_SCALE +static int rgb_scale = 100; + +static int scale_brightness(const struct qpnp_led_data *led) +{ + int brightness = led->cdev.brightness; + + if (rgb_scale != 100) { + brightness = (brightness*rgb_scale + 50) / 100; + brightness = clamp(brightness, + 1, (int)led->cdev.max_brightness); + } + + return brightness; +} +#else +static int scale_brightness(const struct qpnp_led_data *led) +{ + return led->cdev.brightness; +} +#endif + +static int qpnp_rgb_set_direct(struct qpnp_led_data *led) +{ + struct pwm_period_config pwm_conf = { + .pwm_size = PM_PWM_SIZE_9BIT, + .clk = PM_PWM_CLK_19P2MHZ, + .pre_div = PM_PWM_PDIV_5, + .pre_div_exp = 3, + }; + int rc; + int duty_ns,period_us; + + if (led->rgb_cfg->pwm_cfg->mode == LPG_SYNC_MODE) { + dev_err(&led->pdev->dev, "%s wrong mode\n", __func__); + return -EFAULT; + } + + if (led->cdev.brightness) { + int brightness = scale_brightness(led); + period_us = PWM_US; + duty_ns = ((period_us * NSEC_PER_USEC) / + (RGB_MAX_LEVEL-1)) * brightness; + rc = pwm_config(led->rgb_cfg->pwm_cfg->pwm_dev, duty_ns, + period_us * NSEC_PER_USEC); + rc = pwm_config_period_value(led->rgb_cfg->pwm_cfg->pwm_dev, + &pwm_conf,brightness); + if (rc < 0) { + dev_err(&led->pdev->dev, + "pwm config failed\n"); + return rc; + } + rc = qpnp_led_masked_write(led, + RGB_LED_EN_CTL(led->base), + led->rgb_cfg->enable, led->rgb_cfg->enable); + if (rc) { + dev_err(&led->pdev->dev, + "Failed to write led enable reg\n"); + return rc; + } + if (led->rgb_cfg->pwm_cfg->pwm_enabled) { + pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev); + led->rgb_cfg->pwm_cfg->pwm_enabled = 0; + } + rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev); + if (!rc) + led->rgb_cfg->pwm_cfg->pwm_enabled = 1; + } else { + pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev); + led->rgb_cfg->pwm_cfg->pwm_enabled = 0; + rc = qpnp_led_masked_write(led, + RGB_LED_EN_CTL(led->base), + led->rgb_cfg->enable, RGB_LED_DISABLE); + if (rc) { + dev_err(&led->pdev->dev, + "Failed to write led enable reg\n"); + return rc; + } + } + + return 0; +} + static int qpnp_rgb_set(struct qpnp_led_data *led) { int rc; int duty_us, duty_ns, period_us; + if ((led->rgb_cfg->pwm_cfg->mode == PWM_DIRECT_MODE) + || (led->rgb_cfg->pwm_cfg->mode == LPG_SYNC_MODE)) + return qpnp_rgb_set_direct(led); + if (led->cdev.brightness) { + int brightness = scale_brightness(led); + if (!led->rgb_cfg->pwm_cfg->blinking) led->rgb_cfg->pwm_cfg->mode = led->rgb_cfg->pwm_cfg->default_mode; @@ -1728,7 +1871,7 @@ static int qpnp_rgb_set(struct qpnp_led_data *led) } period_us = led->rgb_cfg->pwm_cfg->pwm_period_us; if (period_us > INT_MAX / NSEC_PER_USEC) { - duty_us = (period_us * led->cdev.brightness) / + duty_us = (period_us * brightness) / LED_FULL; rc = pwm_config_us( led->rgb_cfg->pwm_cfg->pwm_dev, @@ -1736,7 +1879,7 @@ static int qpnp_rgb_set(struct qpnp_led_data *led) period_us); } else { duty_ns = ((period_us * NSEC_PER_USEC) / - LED_FULL) * led->cdev.brightness; + LED_FULL) * brightness; rc = pwm_config( led->rgb_cfg->pwm_cfg->pwm_dev, duty_ns, @@ -1887,7 +2030,7 @@ static int qpnp_led_set_max_brightness(struct qpnp_led_data *led) case QPNP_ID_RGB_RED: case QPNP_ID_RGB_GREEN: case QPNP_ID_RGB_BLUE: - led->cdev.max_brightness = RGB_MAX_LEVEL; + led->cdev.max_brightness = led->max_current; break; case QPNP_ID_LED_MPP: if (led->mpp_cfg->pwm_mode == MANUAL_MODE) @@ -2958,9 +3101,456 @@ static int qpnp_kpdbl_init(struct qpnp_led_data *led) return 0; } + +static void qpnp_rgb_set_duration(struct qpnp_led_data *led) +{ + unsigned long long_pause, duration; + const unsigned max_pause = RGB_LED_MAX_PAUSE_REG; + const unsigned max_pause_time = RGB_LED_MAX_PAUSE_TIME; + + if (led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi > max_pause_time) + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi + = max_pause_time; + if (led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo > max_pause_time) + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo + = max_pause_time; + + long_pause = ((led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi + >= led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo) ? + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi : + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo); + duration = (long_pause + (max_pause - 1)) / max_pause; + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi + = (led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi / duration) + 1; + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo + = (led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo / duration) + 1; + led->rgb_cfg->pwm_cfg->lut_params.ramp_step_ms = (duration - 1); + led->rgb_cfg->pwm_cfg->lut_params.use_duration = false; + + pr_debug("%s : pause_hi=%d pause_lo=%d step_ms=%d\n", __func__, + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi, + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo, + led->rgb_cfg->pwm_cfg->lut_params.ramp_step_ms); + + return; +} + +static int rgbled_set_lut_to_register(struct qpnp_led_data *led) +{ + int rc; + int i; + + /* set PWM period to 1.06666[ms] (2^9 {9bit} * 5 * 2^3 / 19.2 [us]) */ + struct pwm_period_config pwm_config = { + .pwm_size = PM_PWM_SIZE_9BIT, + .clk = PM_PWM_CLK_19P2MHZ, + .pre_div = PM_PWM_PDIV_5, + .pre_div_exp = 3, + }; + struct lut_config lut_conf = { + .hi_index = led->rgb_cfg->pwm_cfg->duty_cycles->start_idx + + led->rgb_cfg->pwm_cfg->lut_params.idx_len - 1, + .lo_index = led->rgb_cfg->pwm_cfg->duty_cycles->start_idx, + .lut_pause_hi = led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi, + .lut_pause_lo = led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo, + .ramp_step_ms = led->rgb_cfg->pwm_cfg->lut_params.ramp_step_ms, + .flags = PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP + | PM_PWM_LUT_PAUSE_HI_EN + | PM_PWM_LUT_PAUSE_LO_EN | PM_PWM_LUT_USE_RAW_VALUE, + }; + int *lut = led->rgb_cfg->pwm_cfg->duty_cycles->duty_pcts; + + /* set LPG offset 0x41,0x42 */ + rc = pwm_config_period(led->rgb_cfg->pwm_cfg->pwm_dev, &pwm_config); + for (i = 0; i < INDEX_MAX_EACH_LED; ++i) + lut_conf.lut[i] = lut[i]; + /* set LPG offset 0x40,0x46,0x50-0x57 and LUT */ + rc = pwm_config_lut(led->rgb_cfg->pwm_cfg->pwm_dev, &lut_conf); + + return rc; +} + +static ssize_t rgbled_lut_pwm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qpnp_led_data *led; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + int *lut; + + led = container_of(led_cdev, struct qpnp_led_data, cdev); + lut = led->rgb_cfg->pwm_cfg->duty_cycles->duty_pcts; + + ret = sscanf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + lut, lut+1, lut+2, lut+3, lut+4, lut+5, lut+6, lut+7, + lut+8, lut+9, lut+10, lut+11, lut+12, lut+13, lut+14, lut+15); + if (ret == 0) { + lut[0] = 0; + ret = 1; + } + + led->rgb_cfg->pwm_cfg->duty_cycles->start_idx + = led->rgb_cfg->pwm_cfg->lut_params.start_idx + = (led->rgb_cfg->pwm_cfg->pwm_channel * INDEX_MAX_EACH_LED) + + RGB_LUT_INDEX_OFFSET; + led->rgb_cfg->pwm_cfg->lut_params.idx_len = ret; + + return count; +} + +static ssize_t rgbled_lut_pwm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_led_data *led; + int *lut; + + led = container_of(led_cdev, struct qpnp_led_data, cdev); + lut = led->rgb_cfg->pwm_cfg->duty_cycles->duty_pcts; + + return scnprintf(buf, PAGE_SIZE, "len:%u,[0]:%d,[1]:%d\n", + led->rgb_cfg->pwm_cfg->lut_params.idx_len, lut[0], lut[1]); +} + +static ssize_t rgbled_step_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qpnp_led_data *led; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + unsigned long duration; + const unsigned ramp_step_duration_max = 511; + + led = container_of(led_cdev, struct qpnp_led_data, cdev); + + ret = kstrtoul(buf, 10, &duration); + if (ret) + return ret; + + if (duration > ramp_step_duration_max) + duration = ramp_step_duration_max; + led->rgb_cfg->pwm_cfg->lut_params.ramp_step_ms = duration; + + return count; +} + +static ssize_t rgbled_step_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_led_data *led; + + led = container_of(led_cdev, struct qpnp_led_data, cdev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + led->rgb_cfg->pwm_cfg->lut_params.ramp_step_ms); +} + +static ssize_t rgbled_pause_hi_multi_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qpnp_led_data *led; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + unsigned long lut_pause; + const unsigned lut_pause_max = RGB_LED_LUT_MAX_PAUSE; + + led = container_of(led_cdev, struct qpnp_led_data, cdev); + + ret = kstrtoul(buf, 10, &lut_pause); + if (ret) + return ret; + + if (lut_pause > lut_pause_max) + led->rgb_cfg->pwm_cfg->lut_params.use_duration = true; + + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi = lut_pause; + + return count; +} + +static ssize_t rgbled_pause_hi_multi_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_led_data *led; + + led = container_of(led_cdev, struct qpnp_led_data, cdev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi); +} + +static ssize_t rgbled_pause_lo_multi_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qpnp_led_data *led; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + unsigned long lut_pause; + const unsigned lut_pause_max = RGB_LED_LUT_MAX_PAUSE; + + led = container_of(led_cdev, struct qpnp_led_data, cdev); + + ret = kstrtoul(buf, 10, &lut_pause); + if (ret) + return ret; + + if (lut_pause > lut_pause_max) + led->rgb_cfg->pwm_cfg->lut_params.use_duration = true; + + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo = lut_pause; + return count; +} + +static ssize_t rgbled_pause_lo_multi_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_led_data *led; + + led = container_of(led_cdev, struct qpnp_led_data, cdev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo); +} + +static ssize_t rgbled_single_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_led_data *led; + + led = container_of(led_cdev, struct qpnp_led_data, cdev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + led->rgb_cfg->single_pwm_value); +} + +static ssize_t rgbled_mix_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_led_data *led; + + led = container_of(led_cdev, struct qpnp_led_data, cdev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + led->rgb_cfg->mix_pwm_value); +} + +static struct device_attribute rgbled_attrs[] = { + __ATTR(lut_pwm, S_IRUGO | S_IWUSR | S_IWGRP, + rgbled_lut_pwm_show, rgbled_lut_pwm_store), + __ATTR(step_duration, S_IRUGO | S_IWUSR | S_IWGRP, + rgbled_step_duration_show, rgbled_step_duration_store), + __ATTR(pause_hi_multi, S_IRUGO | S_IWUSR | S_IWGRP, + rgbled_pause_hi_multi_show, rgbled_pause_hi_multi_store), + __ATTR(pause_lo_multi, S_IRUGO | S_IWUSR | S_IWGRP, + rgbled_pause_lo_multi_show, rgbled_pause_lo_multi_store), + __ATTR(max_single_brightness, S_IRUGO, + rgbled_single_value_show, NULL), + __ATTR(max_mix_brightness, S_IRUGO, + rgbled_mix_value_show, NULL), + __ATTR_NULL, +}; + +static int rgbcommon_turn_off_force(struct rgb_sync *rgb_sync) +{ + int i; + int rc = 0; + for (i = 0; i < LPG_CHANNEL_MAX; ++i) + if (rgb_sync->led_data[i]) { + struct qpnp_led_data *led = rgb_sync->led_data[i]; + led->cdev.brightness = 0; + led->rgb_cfg->pwm_cfg->lut_params.idx_len = 0; + rc = qpnp_led_masked_write(led, + RGB_LED_EN_CTL(led->base), + led->rgb_cfg->enable, RGB_LED_DISABLE); + if (rc) + break; + } + + return rc; +} + +static ssize_t rgbcommon_sync_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rgb_sync *rgb_sync; + unsigned long state; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + int i; + int rc; + enum led_mode led_mode; + enum pm_pwm_mode pwm_mode; + + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; + if (state > RGBSYNC_STATE_BLINKING) { + dev_err(dev, "%s out of range\n", __func__); + return -EFAULT; + } + rgb_sync = container_of(led_cdev, struct rgb_sync, cdev); + if (!rgb_sync) { + dev_err(dev, "%s rgb_sync NULL\n", __func__); + return -EFAULT; + } + + if (rgb_sync->sync_state == state) { + dev_err(dev, "%s do nothing. same state", __func__); + return count; + } else if ((rgb_sync->sync_state == RGBSYNC_STATE_BLINKING) + || ((rgb_sync->sync_state == RGBSYNC_STATE_NOT_BLINK) + && (state == RGBSYNC_STATE_CONFIGURE_TO_BLINK))) { + dev_err(dev, "%s turn off force. state changed\n", __func__); + rc = rgbcommon_turn_off_force(rgb_sync); + if (rc) + dev_err(dev, "Failed to turn off force\n"); + } + switch (rgb_sync->sync_state = state) { + case RGBSYNC_STATE_NOT_BLINK: + led_mode = PWM_DIRECT_MODE; + pwm_mode = PM_PWM_MODE_PWM; + break; + case RGBSYNC_STATE_CONFIGURE_TO_BLINK: + led_mode = LPG_SYNC_MODE; + pwm_mode = PM_PWM_MODE_LPG; + break; + default: + return count; + break; + } + + for (i = 0; i < LPG_CHANNEL_MAX; ++i) + if (rgb_sync->led_data[i]) { + struct qpnp_led_data *led = rgb_sync->led_data[i]; + led->rgb_cfg->pwm_cfg->mode = led_mode; + rc = pwm_change_mode(led->rgb_cfg->pwm_cfg->pwm_dev, + pwm_mode); + if (rc) + break; + } + + return count; +} + +static ssize_t rgbcommon_sync_state_show(struct device *ldev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(ldev); + struct device *dev = led_cdev->dev->parent; + struct rgb_sync *rgb_sync; + + rgb_sync = container_of(led_cdev, struct rgb_sync, cdev); + if (!rgb_sync) { + dev_err(dev, "%s rgb_sync NULL\n", __func__); + return -EFAULT; + } + return scnprintf(buf, PAGE_SIZE, "%u\n", rgb_sync->sync_state); +} + +static ssize_t rgbcommon_start_blink_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rgb_sync *rgb_sync; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + int i; + struct qpnp_led_data *led = NULL; + int rc; + int ramp_control = 0; + + rgb_sync = container_of(led_cdev, struct rgb_sync, cdev); + if (!rgb_sync) { + dev_err(dev, "%s rgb_sync NULL\n", __func__); + return -EFAULT; + } + + if (rgb_sync->sync_state != RGBSYNC_STATE_CONFIGURE_TO_BLINK) { + dev_err(dev, "%s ignore. not configure to blink state\n", + __func__); + return count; + } + + rgb_sync->sync_state = RGBSYNC_STATE_BLINKING; + /* must set only led which is blinked */ + for (i = 0; i < LPG_CHANNEL_MAX; ++i) { + if (!rgb_sync->led_data[i]) + continue; + led = rgb_sync->led_data[i]; + if (led->rgb_cfg->pwm_cfg->lut_params.use_duration) + qpnp_rgb_set_duration(led); + if (led->rgb_cfg->pwm_cfg->lut_params.idx_len) { + rgbled_set_lut_to_register(led); + ramp_control |= (1 << i); + rc = qpnp_led_masked_write(led, + RGB_LED_EN_CTL(led->base), + led->rgb_cfg->enable, led->rgb_cfg->enable); + } + } + + if (led) { + rc = pwm_start_lut_ramp(led->rgb_cfg->pwm_cfg->pwm_dev, + ramp_control); + if (rc) + dev_err(dev, "Failed to start blink\n"); + } else { + dev_err(dev, "try to start blink. however not configured.\n"); + } + + return count; +} + +static struct device_attribute rgbcommon_attr[] = { + __ATTR(sync_state, S_IRUGO | S_IWUSR | S_IWGRP, + rgbcommon_sync_state_show, rgbcommon_sync_state_store), + __ATTR(start_blink, S_IWUSR | S_IWGRP, + NULL, rgbcommon_start_blink_store), + __ATTR_NULL, +}; + +static void qpnp_remove_attributes(struct device *dev, + struct device_attribute *attrs) +{ + int i; + + for (i = 0; attrs[i].attr.name; i++) + device_remove_file(dev, &attrs[i]); +} + +static int qpnp_add_attributes(struct device *dev, + struct device_attribute *attrs) +{ + int error = 0; + int i; + + for (i = 0; attrs[i].attr.name; i++) { + error = device_create_file(dev, &attrs[i]); + if (error) { + dev_err(dev, "%s: failed.\n", __func__); + goto err; + } + } + return 0; +err: + while (--i >= 0) + device_remove_file(dev, &attrs[i]); + return error; +} + + static int qpnp_rgb_init(struct qpnp_led_data *led) { int rc; + int max_pwm_value; rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base), RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR); @@ -2975,6 +3565,24 @@ static int qpnp_rgb_init(struct qpnp_led_data *led) dev_err(&led->pdev->dev, "Failed to initialize pwm\n"); return rc; } + + if (led->rgb_sync) { + led->rgb_sync->led_data[led->rgb_cfg->pwm_cfg->pwm_channel] + = led; + led->rgb_cfg->pwm_cfg->mode = PWM_DIRECT_MODE; + rc = pwm_change_mode(led->rgb_cfg->pwm_cfg->pwm_dev, + PM_PWM_MODE_PWM); + if (rc < 0) { + dev_err(&led->pdev->dev, "Failed to " \ + "configure pwm mode\n"); + return rc; + } + } + pwm_set_max_pwm_value(led->rgb_cfg->pwm_cfg->pwm_dev, led->max_current); + max_pwm_value = pwm_get_max_pwm_value(led->rgb_cfg->pwm_cfg->pwm_dev); + led->max_current + = max_pwm_value ? max_pwm_value : RGB_MAX_LEVEL; + /* Initialize led for use in auto trickle charging mode */ rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base), led->rgb_cfg->enable, led->rgb_cfg->enable); @@ -3645,7 +4253,9 @@ static int qpnp_get_config_kpdbl(struct qpnp_led_data *led, static int qpnp_get_config_rgb(struct qpnp_led_data *led, struct device_node *node) { - int rc; + int rc, current_index, i; + u32 val, color_variation_max_num; + u32 *rgb_current_table; u8 led_mode; const char *mode; @@ -3684,6 +4294,82 @@ static int qpnp_get_config_rgb(struct qpnp_led_data *led, return rc; } + rc = of_property_read_u32(node, + "somc,color_variation_max_num", + &color_variation_max_num); + if (rc < 0) { + dev_err(&led->pdev->dev, "Unable to read color_variation_max_num\n"); + color_variation_max_num = RGB_CURR_DEFAULT_PATTERN; + } + dev_info(&led->pdev->dev, "color_variation_max_num[%d] rgb_current_index[%d]\n", + color_variation_max_num, rgb_current_index); + + rgb_current_table = devm_kzalloc(&led->pdev->dev, + sizeof(u32) * color_variation_max_num * RGB_CURR_UNIT_NUM, + GFP_KERNEL); + if (rgb_current_table) { + rc = of_property_read_u32_array(node, "somc,max_current", + rgb_current_table, + color_variation_max_num * RGB_CURR_UNIT_NUM); + if (rc < 0) { + dev_err(&led->pdev->dev, "Unable to read max_mix_current\n"); + led->rgb_cfg->single_pwm_value = RGB_MAX_LEVEL - 1; + led->rgb_cfg->mix_pwm_value = RGB_MAX_LEVEL - 1; + } else { + for (i = 0; i < color_variation_max_num; i++) { + current_index = i * RGB_CURR_UNIT_NUM; + if (rgb_current_index == rgb_current_table[current_index]) { + break; + } + } + led->rgb_cfg->single_pwm_value = + rgb_current_table[current_index + 1]; + led->rgb_cfg->mix_pwm_value = + rgb_current_table[current_index + 2]; + } + } else { + dev_err(&led->pdev->dev, "Unable to allocate memory\n"); + led->rgb_cfg->single_pwm_value = RGB_MAX_LEVEL - 1; + led->rgb_cfg->mix_pwm_value = RGB_MAX_LEVEL - 1; + } + devm_kfree(&led->pdev->dev, rgb_current_table); + + led->max_current = (led->rgb_cfg->single_pwm_value > + led->rgb_cfg->mix_pwm_value ? + led->rgb_cfg->single_pwm_value : + led->rgb_cfg->mix_pwm_value); + dev_info(&led->pdev->dev, "single_pwm_value[%d] mix_pwm_value[%d]\n", + led->rgb_cfg->single_pwm_value, led->rgb_cfg->mix_pwm_value); + dev_info(&led->pdev->dev, "max_current[%d]\n", led->max_current); + + if (led->rgb_sync) { + rc = of_property_read_u32(node, "somc,pwm-channel", &val); + if (!rc) + led->rgb_cfg->pwm_cfg->pwm_channel = val; + else + return rc; + + /* allocate memory for simple use case */ + led->rgb_cfg->pwm_cfg->duty_cycles = + devm_kzalloc(&led->pdev->dev, + sizeof(struct pwm_duty_cycles), GFP_KERNEL); + if (!led->rgb_cfg->pwm_cfg->duty_cycles) { + dev_err(&led->pdev->dev, + "Unable to allocate memory\n"); + return -ENOMEM; + } + led->rgb_cfg->pwm_cfg->duty_cycles->duty_pcts = + devm_kzalloc(&led->pdev->dev, + sizeof(int) * INDEX_MAX_EACH_LED, GFP_KERNEL); + if (!led->rgb_cfg->pwm_cfg->duty_cycles->duty_pcts) { + dev_err(&led->pdev->dev, + "Unable to allocate memory\n"); + return -ENOMEM; + } + led->rgb_cfg->pwm_cfg->pwm_dev = of_pwm_get(node, NULL); + return 0; + } + rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->pdev, node); if (rc < 0) { if (led->rgb_cfg->pwm_cfg->pwm_dev) @@ -3872,6 +4558,8 @@ static int qpnp_leds_probe(struct platform_device *pdev) int rc, i, num_leds = 0, parsed_leds = 0; const char *led_label; bool regulator_probe = false; + int prepare_rgb_sync; + struct rgb_sync *rgb_sync = NULL; node = pdev->dev.of_node; if (node == NULL) @@ -3889,10 +4577,34 @@ static int qpnp_leds_probe(struct platform_device *pdev) if (!led_array) return -ENOMEM; + rc = of_property_read_u32(node, "qcom,rgb_sync", &prepare_rgb_sync); + if (rc < 0) + prepare_rgb_sync = 0; + if (prepare_rgb_sync) + rgb_sync = devm_kzalloc(&pdev->dev, + sizeof(struct rgb_sync), GFP_KERNEL); + if (rgb_sync) { + rgb_sync->cdev.name = "rgb"; + rgb_sync->pdev = pdev; + rc = led_classdev_register(&pdev->dev, &rgb_sync->cdev); + if (rc) { + dev_err(&pdev->dev, "unable to register rgb %d\n", rc); + rgb_sync = 0; + } + rc = rc ? rc : + qpnp_add_attributes(rgb_sync->cdev.dev, rgbcommon_attr); + if (rc) { + dev_err(&pdev->dev, "unable to create sysfs %d\n", rc); + rgb_sync = 0; + } + } + dev_info(&pdev->dev, "rgb_sync prepare %d\n", prepare_rgb_sync); + for_each_child_of_node(node, temp) { led = &led_array[parsed_leds]; led->num_leds = num_leds; led->regmap = dev_get_regmap(pdev->dev.parent, NULL); + led->rgb_sync = rgb_sync; if (!led->regmap) { dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); return -EINVAL; @@ -4048,6 +4760,14 @@ static int qpnp_leds_probe(struct platform_device *pdev) } + if (led->id == QPNP_ID_RGB_RED || + led->id == QPNP_ID_RGB_GREEN || + led->id == QPNP_ID_RGB_BLUE) { + rc = qpnp_add_attributes(led->cdev.dev, rgbled_attrs); + if (rc) + goto fail_id_check; + } + if (led->id == QPNP_ID_LED_MPP) { if (!led->mpp_cfg->pwm_cfg) break; @@ -4182,6 +4902,8 @@ static int qpnp_leds_remove(struct platform_device *pdev) case QPNP_ID_RGB_RED: case QPNP_ID_RGB_GREEN: case QPNP_ID_RGB_BLUE: + qpnp_remove_attributes(led_array[i].cdev.dev, + rgbled_attrs); if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE) sysfs_remove_group(&led_array[i].cdev.dev->kobj, &pwm_attr_group); @@ -4256,6 +4978,52 @@ static struct platform_driver qpnp_leds_driver = { .remove = qpnp_leds_remove, }; +#ifdef CONFIG_LEDS_QPNP_RGB_SCALE +static int update_rgb_brightness(struct device *dev, void *arg) +{ + struct qpnp_led_data *led = dev_get_drvdata(dev); + int i; + + for (i = 0; i < led->num_leds; i++) { + if (led[i].id != QPNP_ID_RGB_RED && + led[i].id != QPNP_ID_RGB_GREEN && + led[i].id != QPNP_ID_RGB_BLUE) { + continue; + } + + dev_info(led[i].cdev.dev, "brightness scale %d%%\n", rgb_scale); + + qpnp_led_set(&led[i].cdev, led[i].cdev.brightness); + } + + return 0; +} + +int qpnp_led_set_rgb_scale(int scale) +{ + int ret = 0; + static DEFINE_MUTEX(scale_lock); + + if (!qpnp_leds_driver.driver.bus) + return -ENODEV; + + if (scale < 1 || scale > 100) + return -EINVAL; + + mutex_lock(&scale_lock); + + if (scale != rgb_scale) { + rgb_scale = scale; + ret = driver_for_each_device(&qpnp_leds_driver.driver, + NULL, NULL, update_rgb_brightness); + } + + mutex_unlock(&scale_lock); + + return ret; +} +#endif + static int __init qpnp_led_init(void) { return platform_driver_register(&qpnp_leds_driver); diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c index b88900d721e4494e9e05c494e7d9977501cda97b..f05a9889de84c9be9dc41cae02d5bbc507bf5f58 100644 --- a/drivers/leds/leds-syscon.c +++ b/drivers/leds/leds-syscon.c @@ -19,6 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 0ad06670fa99fcbb0857dff8aded495158cef7ee..f65f2cd988664afc0d00cca14b96efbf3de38f2e 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -582,4 +582,11 @@ config DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED any more after all the data blocks it covers have been verified anyway. If unsure, say N. + +config PANIC_ON_DM_VERITY_ERRORS + bool "panic on dm verity errors" + depends on DM_VERITY = y + default n + ---help--- + This enables panic on hash mismatch dm-verity errors. endif # MD diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index d2e3abc182b37e24b6053e958451a63998f842e7..4c5842c5c653778a8ae85783346df0db23cd6478 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -13,6 +13,11 @@ * are on the same disk on different partitions on devices with poor random * access behavior. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "dm-verity.h" #include "dm-verity-fec.h" @@ -21,6 +26,10 @@ #include #include +#ifdef CONFIG_RAMDUMP_TAGS +#include +#endif + #define DM_MSG_PREFIX "verity" #define DM_VERITY_ENV_LENGTH 42 @@ -41,6 +50,11 @@ static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE; module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR); +#ifdef CONFIG_PANIC_ON_DM_VERITY_ERRORS +static unsigned dm_verity_panic_on_err; +module_param_named(panic_on_err, dm_verity_panic_on_err, uint, S_IRUGO | S_IWUSR); +#endif + struct dm_verity_prefetch_work { struct work_struct work; struct dm_verity *v; @@ -190,6 +204,16 @@ static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level, *offset = idx << (v->hash_dev_block_bits - v->hash_per_block_bits); } +static void add_verity_block_tag(unsigned long long blk) +{ + char verity_blk[64]; + int count = 0; + + count = snprintf(verity_blk, sizeof(verity_blk), "0x%llx", blk); + + rdtags_add_tag("rdtag_verity_block_nr", verity_blk, count); +} + /* * Handle verification errors. */ @@ -223,6 +247,16 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type, DMERR("%s: %s block %llu is corrupted", v->data_dev->name, type_str, block); +#ifdef CONFIG_PANIC_ON_DM_VERITY_ERRORS + if (dm_verity_panic_on_err) { +#ifdef CONFIG_RAMDUMP_TAGS + add_verity_block_tag(block); +#endif + panic("%s: %s block %llu is corrupted", + v->data_dev->name, type_str, block); + } +#endif + if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS) DMERR("%s: reached maximum errors", v->data_dev->name); diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig index 568f817e8614ceca298db1e823eb62bc9c2781c3..6d3f9c5b6f23c2a099e385e7023ad4d609c25741 100644 --- a/drivers/media/platform/msm/camera_v2/Kconfig +++ b/drivers/media/platform/msm/camera_v2/Kconfig @@ -203,6 +203,23 @@ config OV12830 snapshot config = 4224 * 3000 at 15 fps. 2 lanes max fps is 18, 4 lanes max fps is 24. +config SONY_CAM_V4L2 + bool "SONY specific camera" + depends on MSMB_CAMERA + default n + ---help--- + SONY specific camera module + +config FRONT_CAMERA_LED_SCALE + int "Reduce RGB LED brightness when front camera is active" + depends on SONY_CAM_V4L2 && LEDS_QPNP_RGB_SCALE + range 0 99 + default 0 + ---help--- + Reduce the brightness of front RGB LED by specified percentage + when front camera sensor is powered on. Set it to zero to disable + the feature. + config MSM_V4L2_VIDEO_OVERLAY_DEVICE tristate "QTI MSM V4l2 video overlay device" ---help--- diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c index 8a49c7cf9f4ada0ee8bf78fa6b8d60a08e185ce5..3e478cc488a7caeb623db0c5f7d866ce7f056080 100644 --- a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c +++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -23,6 +28,9 @@ #define BUFF_SIZE_128 128 #undef CDBG +#if defined(CONFIG_SONY_CAM_V4L2) +#define CDBG(fmt, args...) +#else #define CDBG(fmt, args...) pr_debug(fmt, ##args) void msm_camera_io_w(u32 data, void __iomem *addr) @@ -30,6 +38,7 @@ void msm_camera_io_w(u32 data, void __iomem *addr) CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data)); writel_relaxed((data), (addr)); } +#endif /* This API is to write a block of data * to same address @@ -45,7 +54,11 @@ int32_t msm_camera_io_w_block(const u32 *addr, void __iomem *base, for (i = 0; i < len; i++) { CDBG("%s: len =%d val=%x base =%pK\n", __func__, len, addr[i], base); +#if defined(CONFIG_SONY_CAM_V4L2) + writel_relaxed_no_log(addr[i], base); +#else writel_relaxed(addr[i], base); +#endif } return 0; } @@ -64,7 +77,11 @@ int32_t msm_camera_io_w_reg_block(const u32 *addr, void __iomem *base, for (i = 0; i < len; i = i + 2) { CDBG("%s: len =%d val=%x base =%pK reg=%x\n", __func__, len, addr[i + 1], base, addr[i]); +#if defined(CONFIG_SONY_CAM_V4L2) + writel_relaxed_no_log(addr[i + 1], base + addr[i]); +#else writel_relaxed(addr[i + 1], base + addr[i]); +#endif } return 0; } @@ -74,7 +91,11 @@ void msm_camera_io_w_mb(u32 data, void __iomem *addr) CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data)); /* ensure write is done */ wmb(); +#if defined(CONFIG_SONY_CAM_V4L2) + writel_relaxed_no_log((data), (addr)); +#else writel_relaxed((data), (addr)); +#endif /* ensure write is done */ wmb(); } @@ -91,13 +112,18 @@ int32_t msm_camera_io_w_mb_block(const u32 *addr, void __iomem *base, u32 len) wmb(); CDBG("%s: len =%d val=%x base =%pK\n", __func__, len, addr[i], base); +#if defined(CONFIG_SONY_CAM_V4L2) + writel_relaxed_no_log(addr[i], base); +#else writel_relaxed(addr[i], base); +#endif } /* ensure last write is done */ wmb(); return 0; } +#if !defined(CONFIG_SONY_CAM_V4L2) u32 msm_camera_io_r(void __iomem *addr) { uint32_t data = readl_relaxed(addr); @@ -105,6 +131,7 @@ u32 msm_camera_io_r(void __iomem *addr) CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data)); return data; } +#endif u32 msm_camera_io_r_mb(void __iomem *addr) { @@ -126,7 +153,11 @@ void msm_camera_io_memcpy_toio(void __iomem *dest_addr, u32 *s = (u32 *) src_addr; for (i = 0; i < len; i++) +#if defined(CONFIG_SONY_CAM_V4L2) + writel_relaxed_no_log(*s++, d++); +#else writel_relaxed(*s++, d++); +#endif } int32_t msm_camera_io_poll_value(void __iomem *addr, u32 wait_data, u32 retry, diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.h b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.h index 04c8822330f04dfc2b60f799099eab8601ec28ca..b85884c7849472a1c5c5432c6048df163575949b 100644 --- a/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.h +++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __MSM_CAMERA_IO_UTIL_H #define __MSM_CAMERA_IO_UTIL_H @@ -16,6 +21,9 @@ #include #include #include +#if defined(CONFIG_SONY_CAM_V4L2) +#include +#endif #include #include #include @@ -36,9 +44,25 @@ struct msm_cam_dump_string_info { uint32_t offset; }; +#if defined(CONFIG_SONY_CAM_V4L2) +static inline void msm_camera_io_w(u32 data, void __iomem *addr) +{ + writel_relaxed_no_log((data), (addr)); +} +#else void msm_camera_io_w(u32 data, void __iomem *addr); +#endif void msm_camera_io_w_mb(u32 data, void __iomem *addr); +#if defined(CONFIG_SONY_CAM_V4L2) +static inline u32 msm_camera_io_r(void __iomem *addr) +{ + uint32_t data = readl_relaxed_no_log(addr); + + return data; +} +#else u32 msm_camera_io_r(void __iomem *addr); +#endif u32 msm_camera_io_r_mb(void __iomem *addr); void msm_camera_io_dump(void __iomem *addr, int size, int enable); void msm_camera_io_memcpy(void __iomem *dest_addr, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 661850d6d7c68c443ddee9b0175567384c5b93b8..aeae841a8b89afb71d1985c5fc05b93b845afdf4 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -3361,6 +3366,10 @@ int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg) rc = msm_isp_start_axi_stream( vfe_dev, stream_cfg_cmd); +#if defined(CONFIG_SONY_CAM_V4L2) + pr_info("%s: msm_isp_start_axi_stream: rc %d\n", + __func__, rc); +#endif } else { rc = msm_isp_stop_axi_stream( vfe_dev, stream_cfg_cmd); @@ -3383,8 +3392,13 @@ int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg) } if (rc < 0) +#if defined(CONFIG_SONY_CAM_V4L2) + pr_err("%s: start/stop %d stream failed: rc %d\n", __func__, + stream_cfg_cmd->cmd, rc); +#else pr_err("%s: start/stop %d stream failed\n", __func__, stream_cfg_cmd->cmd); +#endif return rc; } diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index 9c3bd7b41ce90007778940b221639fb735e8ddfc..2446f8502f503a2915213e330a25b7b4b2c65508 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -1619,7 +1624,11 @@ static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out, ispif_process_irq(ispif, out, VFE0); } +#if defined(CONFIG_SONY_CAM_V4L2) + if (ispif->vfe_info.num_vfe > 1) { +#else if (ispif->hw_num_isps > 1) { +#endif if (out[VFE1].ispifIrqStatus0 & RESET_DONE_IRQ) { if (atomic_dec_and_test(&ispif->reset_trig[VFE1])) complete(&ispif->reset_complete[VFE1]); @@ -1905,9 +1914,20 @@ static long msm_ispif_subdev_ioctl_unlocked(struct v4l2_subdev *sd, { struct ispif_device *ispif = (struct ispif_device *)v4l2_get_subdevdata(sd); +#if defined(CONFIG_SONY_CAM_V4L2) + struct ispif_cfg_data *pcdata = (struct ispif_cfg_data *)arg; +#endif switch (cmd) { case VIDIOC_MSM_ISPIF_CFG: +#if defined(CONFIG_SONY_CAM_V4L2) + if (pcdata->cfg_type == ISPIF_RELEASE) { + ispif->ispif_sof_debug = 0; + ispif->ispif_rdi0_debug = 0; + ispif->ispif_rdi1_debug = 0; + ispif->ispif_rdi2_debug = 0; + } +#endif return msm_ispif_cmd(sd, arg); case VIDIOC_MSM_ISPIF_CFG_EXT: return msm_ispif_cmd_ext(sd, arg); @@ -2034,6 +2054,9 @@ static int ispif_probe(struct platform_device *pdev) if (rc) /* backward compatibility */ ispif->hw_num_isps = 1; +#if defined(CONFIG_SONY_CAM_V4L2) + ispif->vfe_info.num_vfe = ispif->hw_num_isps; +#endif /* not an error condition */ rc = 0; } diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c index d27f56a9ad658dc7d2eb6f1dc07795c30d74c9ff..8eebb2f49eeb9166003e1c471893174f20b434e8 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include @@ -827,7 +832,11 @@ int msm_jpeg_ioctl_hw_cmd(struct msm_jpeg_device *pgmn_dev, __func__, __LINE__, hw_cmd.type, hw_cmd.n, hw_cmd.offset, hw_cmd.mask, hw_cmd.data, (unsigned long) hw_cmd.pdata); +#if defined(CONFIG_SONY_CAM_V4L2) + if (is_copy_to_user > 0) { +#else if (is_copy_to_user >= 0) { +#endif if (copy_to_user(arg, &hw_cmd, sizeof(hw_cmd))) { JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__); return -EFAULT; diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 4b5671cd9c282058f97420e16ee2d1896ebdb3e4..b94a41f3c4d913532c852f11b4d39433df846b13 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "MSM-CPP %s:%d " fmt, __func__, __LINE__ @@ -124,6 +129,12 @@ static int msm_cpp_dump_addr(struct cpp_device *cpp_dev, struct msm_cpp_frame_info_t *frame_info); static int32_t msm_cpp_reset_vbif_and_load_fw(struct cpp_device *cpp_dev); +#if defined(CONFIG_SONY_CAM_V4L2) +#define CPP_DBG(fmt, args...) +#define CPP_LOW(fmt, args...) +#define ERR_USER_COPY(to) +#define ERR_COPY_FROM_USER() +#else #if CONFIG_MSM_CPP_DBG #define CPP_DBG(fmt, args...) pr_err(fmt, ##args) #else @@ -138,6 +149,7 @@ static int32_t msm_cpp_reset_vbif_and_load_fw(struct cpp_device *cpp_dev); #define ERR_USER_COPY(to) pr_err("copy %s user\n", \ ((to) ? "to" : "from")) #define ERR_COPY_FROM_USER() ERR_USER_COPY(0) +#endif #define msm_dequeue(queue, member, pop_dir) ({ \ unsigned long flags; \ @@ -304,7 +316,11 @@ static void cpp_timer_callback(unsigned long data); uint8_t induce_error; static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev); +#if defined(CONFIG_SONY_CAM_V4L2) +static inline void msm_cpp_write(u32 data, void __iomem *cpp_base) +#else static void msm_cpp_write(u32 data, void __iomem *cpp_base) +#endif { msm_camera_io_w((data), cpp_base + MSM_CPP_MICRO_FIFO_RX_DATA); } @@ -1092,6 +1108,14 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev) goto reg_enable_failed; } +#if defined(CONFIG_SONY_CAM_V4L2) +/* TODO: Temporary fix for cpp poll command fail */ + rc = msm_cpp_set_micro_clk(cpp_dev); + if (rc < 0) { + pr_err("%s: reset micro clk failed\n", __func__); + goto clk_failed; + } +#else if (cpp_dev->micro_reset) { rc = msm_cpp_set_micro_clk(cpp_dev); if (rc < 0) { @@ -1099,6 +1123,7 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev) goto clk_failed; } } +#endif rc = msm_camera_clk_enable(&cpp_dev->pdev->dev, cpp_dev->clk_info, cpp_dev->cpp_clk, cpp_dev->num_clks, true); diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile index b04560fe42bc07533ad8884f337d9aaaa6f3a87c..6f60e036d6bd92995aabeaa410b6e1a7d787ca2f 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/Makefile +++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile @@ -7,3 +7,4 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ eeprom/ ois/ flash/ ir_led/ ir_cut/ obj-$(CONFIG_MSMB_CAMERA) += laser_led/ obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor_init.o msm_sensor_driver.o msm_sensor.o +obj-$(CONFIG_SONY_CAM_V4L2) += sony_camera_v4l2.o diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c index f2c765a4649f5497cdc4d71ffb1a436020cf2d54..e27fcc5294c09daaa7907b86152d15d458b625cf 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -471,6 +476,7 @@ static int32_t msm_cci_wait_report_cmd(struct cci_device *cci_dev, return msm_cci_wait(cci_dev, master, queue); } +#if !defined(CONFIG_SONY_CAM_V4L2) static void msm_cci_process_half_q(struct cci_device *cci_dev, enum cci_i2c_master_t master, enum cci_i2c_queue_t queue) @@ -489,6 +495,7 @@ static void msm_cci_process_half_q(struct cci_device *cci_dev, spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. lock_q[queue], flags); } +#endif static int32_t msm_cci_process_full_q(struct cci_device *cci_dev, enum cci_i2c_master_t master, @@ -709,7 +716,9 @@ static int32_t msm_cci_data_queue(struct cci_device *cci_dev, } continue; } +#if !defined(CONFIG_SONY_CAM_V4L2) msm_cci_process_half_q(cci_dev, master, queue); +#endif } CDBG("%s cmd_size %d addr 0x%x data 0x%x\n", __func__, diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c index 4f7a62716810efc7402de4dd8250c5bc3e470261..e3ede02654f05280c12f66a1c5ad5e2dd39edc89 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -611,7 +616,9 @@ static int msm_csiphy_2phase_lane_config_v50( uint32_t lane_enable = 0, mask = 1; uint16_t lane_mask = 0, i = 0, offset; void __iomem *csiphybase; - +/* SONY_BEGIN (Change to internal bias) */ + uint32_t tmp = 0; +/* SONY_END (Change to internal bias) */ csiphybase = csiphy_dev->base; lane_mask = csiphy_params->lane_mask & 0x1f; @@ -760,6 +767,28 @@ static int msm_csiphy_2phase_lane_config_v50( mask <<= 1; } } +/* SONY_BEGIN (Change to internal bias) */ + /* 0x0CA34024 */ + tmp = msm_camera_io_r(csiphybase + 0x24); + tmp |= 0x04; + msm_camera_io_w(tmp, csiphybase + 0x24); + /* 0x0CA34224 */ + tmp = msm_camera_io_r(csiphybase + 0x224); + tmp |= 0x04; + msm_camera_io_w(tmp, csiphybase + 0x224); + /* 0x0CA34424 */ + tmp = msm_camera_io_r(csiphybase + 0x424); + tmp |= 0x04; + msm_camera_io_w(tmp, csiphybase + 0x424); + /* 0x0CA34624 */ + tmp = msm_camera_io_r(csiphybase + 0x624); + tmp |= 0x04; + msm_camera_io_w(tmp, csiphybase + 0x624); + /* 0x0CA34724 */ + tmp = msm_camera_io_r(csiphybase + 0x724); + tmp |= 0x04; + msm_camera_io_w(tmp, csiphybase + 0x724); +/* SONY_END (Change to internal bias) */ msm_csiphy_cphy_irq_config(csiphy_dev, csiphy_params); return 0; } diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c index 457bd1730232844f7da75ce59bd08db7f69448af..45d42a31e170141d5aaf2feaaead4a6aa5076d3b 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "msm_camera_dt_util.h" #include "msm_camera_io_util.h" @@ -769,6 +774,92 @@ ERROR1: return rc; } +#if defined(CONFIG_SONY_CAM_V4L2) +int msm_camera_get_dt_gpio_set_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-set-tbl-num", &count)) + return 0; + + count /= sizeof(uint32_t); + if (!count) { + pr_err("%s qcom,gpio-set-tbl-num 0\n", __func__); + return 0; + } + + val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL); + if (!val_array) { + pr_err("%s failed %d\n", __func__, __LINE__); + return -ENOMEM; + } + + gconf->cam_gpio_set_tbl = kzalloc(sizeof(struct msm_gpio_set_tbl) * + count, GFP_KERNEL); + if (!gconf->cam_gpio_set_tbl) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto ERROR1; + } + gconf->cam_gpio_set_tbl_size = count; + + rc = of_property_read_u32_array(of_node, "qcom,gpio-set-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 set tbl index %d invalid\n", + __func__, val_array[i]); + return -EINVAL; + } + gconf->cam_gpio_set_tbl[i].gpio = gpio_array[val_array[i]]; + CDBG("%s cam_gpio_set_tbl[%d].gpio = %d\n", __func__, i, + gconf->cam_gpio_set_tbl[i].gpio); + } + + rc = of_property_read_u32_array(of_node, "qcom,gpio-set-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_set_tbl[i].flags = val_array[i]; + CDBG("%s cam_gpio_set_tbl[%d].flags = %ld\n", __func__, i, + gconf->cam_gpio_set_tbl[i].flags); + } + + rc = of_property_read_u32_array(of_node, "qcom,gpio-set-tbl-delay", + 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_set_tbl[i].delay = val_array[i]; + CDBG("%s cam_gpio_set_tbl[%d].delay = %d\n", __func__, i, + gconf->cam_gpio_set_tbl[i].delay); + } + + kfree(val_array); + return rc; + +ERROR2: + kfree(gconf->cam_gpio_set_tbl); +ERROR1: + kfree(val_array); + gconf->cam_gpio_set_tbl_size = 0; + return rc; +} +#endif + int msm_camera_init_gpio_pin_tbl(struct device_node *of_node, struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, uint16_t gpio_array_size) diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h index 220915511cce470377b8e96baebf0ab82b48ae27..992b137da3676e30bd5db1ea7e32fcc3051eba7e 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef MSM_CAMERA_DT_UTIL_H__ #define MSM_CAMERA_DT_UTIL_H__ @@ -40,6 +45,12 @@ int msm_camera_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); +#if defined(CONFIG_SONY_CAM_V4L2) +int msm_camera_get_dt_gpio_set_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size); +#endif + int msm_camera_init_gpio_pin_tbl(struct device_node *of_node, struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, uint16_t gpio_array_size); diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c index fcef053740989eee97e937cbf6853f380f002cb7..ecabed32be24978cf3a76b099fe3c4e28807b140 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c @@ -9,6 +9,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ +#if defined(CONFIG_SONY_CAM_V4L2) +#include +#endif #include "msm_sensor.h" #include "msm_sd.h" #include "camera.h" @@ -23,6 +31,9 @@ static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl; static struct msm_camera_i2c_fn_t msm_sensor_secure_func_tbl; +#if defined(CONFIG_SONY_CAM_V4L2) +static struct v4l2_file_operations msm_sensor_v4l2_subdev_fops; +#endif static void msm_sensor_adjust_mclk(struct msm_camera_power_ctrl_t *ctrl) { @@ -44,6 +55,343 @@ static void msm_sensor_adjust_mclk(struct msm_camera_power_ctrl_t *ctrl) return; } +#if defined(CONFIG_SONY_CAM_V4L2) +static int32_t msm_camera_get_power_settimgs_from_sensor_lib( + struct msm_camera_power_ctrl_t *power_info, + struct msm_sensor_power_setting_array *power_setting_array) +{ + int32_t rc = 0; + uint32_t size; + struct msm_sensor_power_setting *ps; + bool need_reverse = 0; + + if ((NULL == power_info->power_setting) || + (0 == power_info->power_setting_size)) { + + ps = power_setting_array->power_setting; + size = power_setting_array->size; + if ((NULL == ps) || (0 == size)) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -EINVAL; + goto FAILED_1; + } + + power_info->power_setting = + kzalloc(sizeof(*ps) * size, GFP_KERNEL); + if (!power_info->power_setting) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto FAILED_1; + } + memcpy(power_info->power_setting, + power_setting_array->power_setting, + sizeof(*ps) * size); + power_info->power_setting_size = size; + } + + ps = power_setting_array->power_down_setting; + size = power_setting_array->size_down; + if (NULL == ps || 0 == size) { + ps = power_info->power_setting; + size = power_info->power_setting_size; + need_reverse = 1; + } + + power_info->power_down_setting = + kzalloc(sizeof(*ps) * size, GFP_KERNEL); + if (!power_info->power_down_setting) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_UP; + } + memcpy(power_info->power_down_setting, + ps, + sizeof(*ps) * size); + power_info->power_down_setting_size = size; + + if (need_reverse) { + int c, end = size - 1; + struct msm_sensor_power_setting power_down_setting_t; + for (c = 0; c < size/2; c++) { + power_down_setting_t = + power_info->power_down_setting[c]; + power_info->power_down_setting[c] = + power_info->power_down_setting[end]; + power_info->power_down_setting[end] = + power_down_setting_t; + end--; + } + } + + return 0; +FREE_UP: + kfree(power_info->power_setting); +FAILED_1: + return rc; +} + +static int32_t msm_sensor_get_dt_data(struct device_node *of_node, + struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0, i = 0, ret = 0; + struct msm_camera_gpio_conf *gconf = NULL; + struct msm_camera_sensor_board_info *sensordata = NULL; + uint16_t *gpio_array = NULL; + uint16_t gpio_array_size = 0; + uint32_t id_info[MSM_SENSOR_NUM_ID_INFO_DATA]; + uint32_t count; + const uint32_t *p; + struct msm_camera_slave_info *slave_info; + + s_ctrl->sensordata = kzalloc(sizeof( + struct msm_camera_sensor_board_info), + GFP_KERNEL); + if (!s_ctrl->sensordata) { + pr_err("%s failed %d\n", __func__, __LINE__); + return -ENOMEM; + } + + sensordata = s_ctrl->sensordata; + + rc = of_property_read_string(of_node, "qcom,sensor-name", + &sensordata->sensor_name); + CDBG("%s qcom,sensor-name %s, rc %d\n", __func__, + sensordata->sensor_name, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_SENSORDATA; + } + + rc = of_property_read_u32(of_node, "qcom,cci-master", + &s_ctrl->cci_i2c_master); + CDBG("%s qcom,cci-master %d, rc %d\n", __func__, s_ctrl->cci_i2c_master, + rc); + if (rc < 0) { + /* Set default master 0 */ + s_ctrl->cci_i2c_master = MASTER_0; + rc = 0; + } + + rc = msm_sensor_get_sub_module_index(of_node, &sensordata->sensor_info); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_SENSORDATA; + } + + /* Get sensor mount angle */ + if (0 > of_property_read_u32(of_node, "qcom,mount-angle", + &sensordata->sensor_info->sensor_mount_angle)) { + /* Invalidate mount angle flag */ + CDBG("%s:%d Default sensor mount angle\n", + __func__, __LINE__); + sensordata->sensor_info->is_mount_angle_valid = 0; + sensordata->sensor_info->sensor_mount_angle = 0; + } else { + sensordata->sensor_info->is_mount_angle_valid = 1; + } + CDBG("%s qcom,mount-angle %d\n", __func__, + sensordata->sensor_info->sensor_mount_angle); + if (0 > of_property_read_u32(of_node, "qcom,sensor-position", + &sensordata->sensor_info->position)) { + CDBG("%s:%d Default sensor position\n", __func__, __LINE__); + sensordata->sensor_info->position = 0; + } + CDBG("%s qcom,sensor-position %d\n", __func__, + sensordata->sensor_info->position); + if (0 > of_property_read_u32(of_node, "qcom,sensor-mode", + &sensordata->sensor_info->modes_supported)) { + CDBG("%s:%d Default sensor mode\n", __func__, __LINE__); + sensordata->sensor_info->modes_supported = 0; + } + CDBG("%s qcom,sensor-mode %d\n", __func__, + sensordata->sensor_info->modes_supported); + + s_ctrl->set_mclk_23880000 = of_property_read_bool(of_node, + "qcom,mclk-23880000"); + + CDBG("%s qcom,mclk-23880000 %d\n", __func__, + s_ctrl->set_mclk_23880000); + + rc = msm_sensor_get_dt_csi_data(of_node, &sensordata->csi_lane_params); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_SENSOR_INFO; + } + + /*Get clocks information*/ + rc = msm_camera_get_clk_info(s_ctrl->pdev, + &s_ctrl->sensordata->power_info.clk_info, + &s_ctrl->sensordata->power_info.clk_ptr, + &s_ctrl->sensordata->power_info.clk_info_size); + if (rc < 0) { + pr_err("failed: msm_camera_get_clk_info rc %d", rc); + goto FREE_CSI; + } + + rc = msm_camera_get_dt_vreg_data(of_node, + &sensordata->power_info.cam_vreg, + &sensordata->power_info.num_vreg); + if (rc < 0) + goto FREE_CLK_DATA; + + rc = msm_camera_get_dt_power_setting_data(of_node, + sensordata->power_info.cam_vreg, + sensordata->power_info.num_vreg, + &sensordata->power_info); + + + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_VREG; + } + + + rc = msm_camera_get_power_settimgs_from_sensor_lib( + &sensordata->power_info, + &s_ctrl->power_setting_array); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_VREG; + } + + sensordata->power_info.gpio_conf = kzalloc( + sizeof(struct msm_camera_gpio_conf), GFP_KERNEL); + if (!sensordata->power_info.gpio_conf) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto FREE_PS; + } + gconf = sensordata->power_info.gpio_conf; + + gpio_array_size = of_gpio_count(of_node); + CDBG("%s gpio count %d\n", __func__, gpio_array_size); + + if (gpio_array_size) { + gpio_array = kzalloc(sizeof(uint16_t) * gpio_array_size, + GFP_KERNEL); + if (!gpio_array) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_GPIO_CONF; + } + for (i = 0; i < gpio_array_size; i++) { + gpio_array[i] = of_get_gpio(of_node, i); + CDBG("%s gpio_array[%d] = %d\n", __func__, i, + gpio_array[i]); + } + + rc = msm_camera_get_dt_gpio_req_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_GPIO_CONF; + } + + rc = msm_camera_get_dt_gpio_set_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_GPIO_REQ_TBL; + } + + rc = msm_camera_init_gpio_pin_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_GPIO_SET_TBL; + } + } + rc = msm_sensor_get_dt_actuator_data(of_node, + &sensordata->actuator_info); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_GPIO_PIN_TBL; + } + + sensordata->slave_info = kzalloc(sizeof(struct msm_camera_slave_info), + GFP_KERNEL); + if (!sensordata->slave_info) { + pr_err("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto FREE_ACTUATOR_INFO; + } + + slave_info = sensordata->slave_info; + + p = of_get_property(of_node, "qcom,slave-id", &count); + if (!p || !count) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_SLAVE_INFO; + } + + count /= sizeof(uint32_t); + + if (count > MSM_SENSOR_NUM_ID_INFO_DATA) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_SLAVE_INFO; + } + + memset(id_info, 0, sizeof(*id_info)*MSM_SENSOR_NUM_ID_INFO_DATA); + + rc = of_property_read_u32_array(of_node, "qcom,slave-id", + id_info, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto FREE_SLAVE_INFO; + } + + slave_info->sensor_slave_addr = id_info[MSM_SENSOR_SLAVEADDR_DATA]; + slave_info->sensor_id_reg_addr = id_info[MSM_SENSOR_IDREGADDR_DATA]; + slave_info->sensor_id = id_info[MSM_SENSOR_SENSOR_ID_DATA]; + slave_info->sensor_id_mask = id_info[MSM_SENSOR_SENIDMASK_DATA]; + CDBG("%s:%d slave addr 0x%x sensor reg 0x%x id 0x%x mask 0x%x\n", + __func__, __LINE__, + slave_info->sensor_slave_addr, + slave_info->sensor_id_reg_addr, + slave_info->sensor_id, + slave_info->sensor_id_mask); + + /*Optional property, don't return error if absent */ + ret = of_property_read_string(of_node, "qcom,vdd-cx-name", + &sensordata->misc_regulator); + CDBG("%s qcom,misc_regulator %s, rc %d\n", __func__, + sensordata->misc_regulator, ret); + + kfree(gpio_array); + + return rc; + +FREE_SLAVE_INFO: + kfree(s_ctrl->sensordata->slave_info); +FREE_ACTUATOR_INFO: + kfree(s_ctrl->sensordata->actuator_info); +FREE_GPIO_PIN_TBL: + kfree(s_ctrl->sensordata->power_info.gpio_conf->gpio_num_info); +FREE_GPIO_SET_TBL: + kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_set_tbl); +FREE_GPIO_REQ_TBL: + kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_req_tbl); +FREE_GPIO_CONF: + kfree(s_ctrl->sensordata->power_info.gpio_conf); +FREE_PS: + kfree(s_ctrl->sensordata->power_info.power_setting); + kfree(s_ctrl->sensordata->power_info.power_down_setting); +FREE_VREG: + kfree(s_ctrl->sensordata->power_info.cam_vreg); +FREE_CLK_DATA: + msm_camera_put_clk_info(s_ctrl->pdev, + &s_ctrl->sensordata->power_info.clk_info, + &s_ctrl->sensordata->power_info.clk_ptr, + s_ctrl->sensordata->power_info.clk_info_size); +FREE_CSI: + kfree(s_ctrl->sensordata->csi_lane_params); +FREE_SENSOR_INFO: + kfree(s_ctrl->sensordata->sensor_info); +FREE_SENSORDATA: + kfree(s_ctrl->sensordata); + kfree(gpio_array); + return rc; +} +#endif + static void msm_sensor_misc_regulator( struct msm_sensor_ctrl_t *sctrl, uint32_t enable) { @@ -87,6 +435,9 @@ int32_t msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl) kfree(s_ctrl->sensordata->cam_slave_info); kfree(s_ctrl->sensordata->actuator_info); kfree(s_ctrl->sensordata->power_info.gpio_conf->gpio_num_info); +#if defined(CONFIG_SONY_CAM_V4L2) + kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_set_tbl); +#endif kfree(s_ctrl->sensordata->power_info.gpio_conf->cam_gpio_req_tbl); kfree(s_ctrl->sensordata->power_info.gpio_conf); kfree(s_ctrl->sensordata->power_info.cam_vreg); @@ -354,6 +705,7 @@ static long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd, case MSM_SD_UNNOTIFY_FREEZE: return 0; default: + pr_err("%s cmd = %d\n", __func__, cmd); return -ENOIOCTLCMD; } } @@ -365,6 +717,23 @@ static long msm_sensor_subdev_do_ioctl( struct video_device *vdev = video_devdata(file); struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); switch (cmd) { +#if defined(CONFIG_SONY_CAM_V4L2) + case VIDIOC_DQEVENT: { + struct v4l2_fh *vfh = file->private_data; + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) + return -ENOIOCTLCMD; + return v4l2_event_dequeue(vfh, arg, + file->f_flags & O_NONBLOCK); + } + case VIDIOC_SUBSCRIBE_EVENT: { + struct v4l2_fh *vfh = file->private_data; + return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg); + } + case VIDIOC_UNSUBSCRIBE_EVENT: { + struct v4l2_fh *vfh = file->private_data; + return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); + } +#endif case VIDIOC_MSM_SENSOR_CFG32: cmd = VIDIOC_MSM_SENSOR_CFG; default: @@ -372,13 +741,42 @@ static long msm_sensor_subdev_do_ioctl( } } +#if defined(CONFIG_SONY_CAM_V4L2) +static int msm_sensor_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd); + int rc = v4l2_event_subscribe(fh, sub, 100, NULL); + if (rc != 0) { + pr_err("%s: Subs event_type =0x%x failed\n", __func__, sub->type); + } + if (s_ctrl) + s_ctrl->sof_count = 0; + return rc; +} + +static int msm_sensor_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + int rc = v4l2_event_unsubscribe(fh, sub); + if (rc != 0) { + pr_err("%s: Subs event_type =0x%x failed\n", __func__, sub->type); + } + return rc; +} +#endif + long msm_sensor_subdev_fops_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return video_usercopy(file, cmd, arg, msm_sensor_subdev_do_ioctl); } +#if defined(CONFIG_SONY_CAM_V4L2) +int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, +#else static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, +#endif void __user *argp) { struct sensorb_cfg_data32 *cdata = (struct sensorb_cfg_data32 *)argp; @@ -700,6 +1098,96 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, kfree(reg_setting); break; } +#if defined(CONFIG_SONY_CAM_V4L2) + case CFG_WRITE_I2C_SEQ_ARRAY: { + struct msm_camera_i2c_seq_reg_setting32 conf_array32; + struct msm_camera_i2c_seq_reg_setting conf_array; + struct msm_camera_i2c_seq_reg_array *reg_setting = NULL; + uint16_t orig_sid = 0; + uint16_t orig_addr_type = 0; + + if (s_ctrl->sensor_state != MSM_SENSOR_POWER_UP) { + pr_err("%s:%d failed: invalid state %d\n", __func__, + __LINE__, s_ctrl->sensor_state); + rc = -EFAULT; + break; + } + + if (copy_from_user(&conf_array32, + (void *)compat_ptr(cdata->cfg.setting), + sizeof(struct msm_camera_i2c_seq_reg_setting32))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + conf_array.addr_type = conf_array32.addr_type; + conf_array.delay = conf_array32.delay; + conf_array.size = conf_array32.size; + conf_array.slave_addr = conf_array32.slave_addr; + conf_array.reg_setting = compat_ptr(conf_array32.reg_setting); + + if (!conf_array.size) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } + + reg_setting = kzalloc(conf_array.size * + (sizeof(struct msm_camera_i2c_seq_reg_array)), + GFP_KERNEL); + if (!reg_setting) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -ENOMEM; + break; + } + if (copy_from_user(reg_setting, + (void *)(conf_array.reg_setting), + conf_array.size * + sizeof(struct msm_camera_i2c_seq_reg_array))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + + if (s_ctrl->sensor_i2c_client->cci_client) { + orig_sid = s_ctrl->sensor_i2c_client->cci_client->sid; + s_ctrl->sensor_i2c_client->cci_client->sid = + conf_array.slave_addr; + } else if (s_ctrl->sensor_i2c_client->client) { + orig_sid = s_ctrl->sensor_i2c_client->client->addr; + s_ctrl->sensor_i2c_client->client->addr = + conf_array.slave_addr; + } else { + pr_err("%s: error: no i2c/cci client found.", __func__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + if (conf_array.addr_type < MSM_CAMERA_I2C_ADDR_TYPE_MAX) { + orig_addr_type = s_ctrl->sensor_i2c_client->addr_type; + s_ctrl->sensor_i2c_client->addr_type = + conf_array.addr_type; + } else { + pr_err("%s: error: address type failed.", __func__); + kfree(reg_setting); + rc = -EFAULT; + break; + } + conf_array.reg_setting = reg_setting; + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl-> + i2c_write_seq_table(s_ctrl->sensor_i2c_client, + &conf_array); + if (s_ctrl->sensor_i2c_client->cci_client) + s_ctrl->sensor_i2c_client->cci_client->sid = orig_sid; + else + s_ctrl->sensor_i2c_client->client->addr = orig_sid; + s_ctrl->sensor_i2c_client->addr_type = orig_addr_type; + kfree(reg_setting); + break; + } +#else case CFG_WRITE_I2C_SEQ_ARRAY: { struct msm_camera_i2c_seq_reg_setting32 conf_array32; struct msm_camera_i2c_seq_reg_setting conf_array; @@ -759,7 +1247,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, kfree(reg_setting); break; } - +#endif case CFG_POWER_UP: if (s_ctrl->is_csid_tg_mode) goto DONE; @@ -1426,6 +1914,12 @@ static int msm_sensor_power(struct v4l2_subdev *sd, int on) static struct v4l2_subdev_core_ops msm_sensor_subdev_core_ops = { .ioctl = msm_sensor_subdev_ioctl, .s_power = msm_sensor_power, +#ifdef CONFIG_COMPAT +#if defined(CONFIG_SONY_CAM_V4L2) + .subscribe_event = msm_sensor_subscribe_event, + .unsubscribe_event = msm_sensor_unsubscribe_event, +#endif +#endif }; static struct v4l2_subdev_ops msm_sensor_subdev_ops = { .core = &msm_sensor_subdev_core_ops, @@ -1486,6 +1980,100 @@ static struct msm_camera_i2c_fn_t msm_sensor_secure_func_tbl = { .i2c_write_table_sync_block = msm_camera_tz_i2c_write_table_sync_block, }; +#if defined(CONFIG_SONY_CAM_V4L2) +int32_t msm_sensor_platform_probe(struct platform_device *pdev, + const void *data) +{ + int rc = 0; + struct msm_sensor_ctrl_t *s_ctrl = + (struct msm_sensor_ctrl_t *)data; + struct msm_camera_cci_client *cci_client = NULL; + uint32_t session_id; + unsigned long mount_pos = 0; + s_ctrl->pdev = pdev; + CDBG("%s called data %p\n", __func__, data); + CDBG("%s pdev name %s\n", __func__, pdev->id_entry->name); + if (pdev->dev.of_node) { + rc = msm_sensor_get_dt_data(pdev->dev.of_node, s_ctrl); + if (rc < 0) { + pr_err("%s failed line %d\n", __func__, __LINE__); + return rc; + } + } + s_ctrl->sensordata->power_info.dev = &pdev->dev; + s_ctrl->sensor_device_type = MSM_CAMERA_PLATFORM_DEVICE; + s_ctrl->sensor_i2c_client->cci_client = kzalloc(sizeof( + struct msm_camera_cci_client), GFP_KERNEL); + if (!s_ctrl->sensor_i2c_client->cci_client) { + pr_err("%s failed line %d\n", __func__, __LINE__); + return rc; + } + /* TODO: get CCI subdev */ + cci_client = s_ctrl->sensor_i2c_client->cci_client; + cci_client->cci_subdev = msm_cci_get_subdev(); + cci_client->cci_i2c_master = s_ctrl->cci_i2c_master; + cci_client->sid = + s_ctrl->sensordata->slave_info->sensor_slave_addr >> 1; + cci_client->cid = 0; + cci_client->retries = 3; + cci_client->id_map = 0; + if (!s_ctrl->func_tbl) + s_ctrl->func_tbl = &msm_sensor_func_tbl; + if (!s_ctrl->sensor_i2c_client->i2c_func_tbl) + s_ctrl->sensor_i2c_client->i2c_func_tbl = + &msm_sensor_cci_func_tbl; + if (!s_ctrl->sensor_v4l2_subdev_ops) + s_ctrl->sensor_v4l2_subdev_ops = &msm_sensor_subdev_ops; + rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl); + if (rc < 0) { + pr_err("%s %s power up failed\n", __func__, + s_ctrl->sensordata->sensor_name); + kfree(cci_client); + return rc; + } + + pr_info("%s %s probe succeeded\n", __func__, + s_ctrl->sensordata->sensor_name); + v4l2_subdev_init(&s_ctrl->msm_sd.sd, + s_ctrl->sensor_v4l2_subdev_ops); + snprintf(s_ctrl->msm_sd.sd.name, + sizeof(s_ctrl->msm_sd.sd.name), "%s", + s_ctrl->sensordata->sensor_name); + v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, pdev); + s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS; + media_entity_init(&s_ctrl->msm_sd.sd.entity, 0, NULL, 0); + s_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR; + s_ctrl->msm_sd.sd.entity.name = + s_ctrl->msm_sd.sd.name; + + mount_pos = s_ctrl->sensordata->sensor_info->position << 16; + mount_pos = mount_pos | ((s_ctrl->sensordata->sensor_info-> + sensor_mount_angle / 90) << 8); + s_ctrl->msm_sd.sd.entity.flags = mount_pos | MEDIA_ENT_FL_DEFAULT; + + rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id); + CDBG("%s rc %d session_id %d\n", __func__, rc, session_id); + s_ctrl->sensordata->sensor_info->session_id = session_id; + s_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x3; + msm_sd_register(&s_ctrl->msm_sd); + msm_sensor_v4l2_subdev_fops = v4l2_subdev_fops; +#ifdef CONFIG_COMPAT + msm_sensor_v4l2_subdev_fops.compat_ioctl32 = + msm_sensor_subdev_fops_ioctl; +#endif + s_ctrl->msm_sd.sd.devnode->fops = + &msm_sensor_v4l2_subdev_fops; + + CDBG("%s:%d\n", __func__, __LINE__); + + s_ctrl->func_tbl->sensor_power_down(s_ctrl); + CDBG("%s:%d\n", __func__, __LINE__); + return rc; +} +#endif + int32_t msm_sensor_init_default_params(struct msm_sensor_ctrl_t *s_ctrl) { struct msm_camera_cci_client *cci_client = NULL; diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h index 8f55f453bf0363d23c4a02fea05e211f6b7807fa..23e0aaebb18e8fc71f21074786981f0fc20bff2b 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef MSM_SENSOR_H #define MSM_SENSOR_H @@ -90,10 +95,19 @@ struct msm_sensor_ctrl_t { uint8_t is_csid_tg_mode; uint32_t is_secure; uint8_t bypass_video_node_creation; +#if defined(CONFIG_SONY_CAM_V4L2) + uint32_t sof_count; +#endif }; int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp); +#if defined(CONFIG_SONY_CAM_V4L2) +#ifdef CONFIG_COMPAT +int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp); +#endif +#endif + int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl); int msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl); @@ -102,6 +116,11 @@ int msm_sensor_check_id(struct msm_sensor_ctrl_t *s_ctrl); int msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl); +#if defined(CONFIG_SONY_CAM_V4L2) +int32_t msm_sensor_platform_probe(struct platform_device *pdev, + const void *data); +#endif + int msm_sensor_update_cfg(struct msm_sensor_ctrl_t *s_ctrl); int msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl); diff --git a/drivers/media/platform/msm/camera_v2/sensor/sony_camera_v4l2.c b/drivers/media/platform/msm/camera_v2/sensor/sony_camera_v4l2.c new file mode 100644 index 0000000000000000000000000000000000000000..0f00fbab15f247fbc3766fd5b5f6ef1c89e653ad --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/sony_camera_v4l2.c @@ -0,0 +1,2439 @@ +/* drivers/media/platform/msm/camera_v2/sensor/sony_camera_v4l2.c + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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 DEBUG +#define DEBUG +#endif + +#define ENABLE_LOGE +#define ENABLE_LOGI +/*#define ENABLE_LOGD*/ + +#include +#include +#include +#include +#include "camera.h" +#include "msm_sensor.h" +#include "msm_camera_i2c.h" +#include "msm_cci.h" +#include "msm_camera_i2c_mux.h" +#include "msm_camera_io_util.h" +#include "sony_camera_v4l2.h" +#include + +#ifdef ENABLE_LOGE +#define LOGE(f, a...) dev_err(camera_device, "%s: " f, __func__, ##a) +#else +#define LOGE(f, a...) +#endif + +#ifdef ENABLE_LOGD +#define LOGD(f, a...) dev_dbg(camera_device, "%s: " f, __func__, ##a) +#else +#define LOGD(f, a...) +#endif + +#ifdef ENABLE_LOGI +#define LOGI(f, a...) dev_info(camera_device, "%s: " f, __func__, ##a) +#else +#define LOGI(f, a...) +#endif + +#define I2C_MAX_DATA_LEN 256 +#define SENSOR_NAME_LEN 8 +#define EEPROM_MAX_DATA_LEN 2048 +#define EEPROM_READ_FREQ_MODE 1 +#define SENSOR_MCLK_DEFAULT 8000000 + +#define SENSOR_ID_MT9M114 0x2481 +#define SENSOR_ID_MT9V115 0x2284 +#define MODULE_STW01BM0 "STW01BM0" +#define MODULE_APT01BM0 "APT01BM0" +#define MODULE_APT00YP1 "APT00YP1" +#define MODULE_STW00YP1 "STW00YP1" +#define CAMERA_DEV_NAME "sony_camera_%d" +#define CAMERA_THERMAL_NAME_0 "sony_camera_0" +#define CAMERA_THERMAL_NAME_1 "sony_camera_1" +#define CAPS_MAX_STR_LEN 32 + +#define CAM_SENSOR_PINCTRL_STATE_SLEEP "cam_suspend" +#define CAM_SENSOR_PINCTRL_STATE_DEFAULT "cam_default" + +struct sony_camera_data { + bool probe_done; + bool gpio_requested; + struct device *d; + struct msm_sensor_ctrl_t s_ctrl; + uint8_t buf[I2C_MAX_DATA_LEN]; + uint8_t eeprom[EEPROM_MAX_DATA_LEN]; + uint16_t eeprom_len; + const struct sony_camera_module *module; + struct regulator *cam_vio; + struct regulator *cam_vana; + struct regulator *cam_vdig; + struct regulator *cam_vaf; + struct regulator *cam_vio_gpio; + struct regulator *cam_vana_gpio; + struct regulator *cam_vdig_gpio; + struct regulator *cam_vaf_gpio; + struct clk *clk_handle[2]; + struct device info_dev; + bool power_up_done; + struct thermal_zone_device *thermal_zone_dev; + int32_t thermal_sensor_temp; + int thermal_ret_val; + uint32_t sof_irq; + int sof_gpio; +}; + +struct camera_read_info { + uint32_t mount_angle; + uint32_t sensor_rotation; + uint32_t sensor_facing; + uint32_t sensor_config_delay_num; + uint32_t sensor_config_delay[MAX_CONFIG_DELAY_NUM]; + uint32_t temperature_check_skip_num; + uint32_t total_pixel_number_w; + uint32_t total_pixel_number_h; + uint32_t active_pixel_number_x; + uint32_t active_pixel_number_y; + uint32_t active_pixel_number_w; + uint32_t active_pixel_number_h; + uint32_t min_focus_distance; + uint32_t hyper_focal_distance; + char diagonal_len[CAPS_MAX_STR_LEN]; + char unit_cell_size_w[CAPS_MAX_STR_LEN]; + char unit_cell_size_h[CAPS_MAX_STR_LEN]; + char min_f_number[CAPS_MAX_STR_LEN]; + char max_f_number[CAPS_MAX_STR_LEN]; + uint32_t min_focus_pos; + uint32_t max_focus_pos; + uint32_t min_focus_dac; + uint32_t max_focus_dac; + uint32_t focus_inf_range_offset; + uint32_t focus_macro_range_offset; + uint32_t focus_lens_stroke_inf_to_1m; + uint32_t focus_lens_stroke_1m_to_macro; + uint32_t focus_lens_stroke_inf_to_macro; + uint32_t focus_calc_type; + uint32_t focus_wob_time; + uint32_t has_3a; + uint32_t has_focus_actuator; + uint32_t has_pdaf; + uint32_t has_rs; + uint32_t has_multi_output; + uint32_t has_super_slow; + uint32_t has_sub_sensor; + uint32_t has_aube; + uint32_t has_flicker_detector; + uint32_t has_hw_sof; + uint32_t has_hdr; + uint32_t has_seamless_mode_change; + uint32_t has_gph; + uint32_t pdaf_free_area_num; + uint32_t pdaf_fixed_area_size_w; + uint32_t pdaf_fixed_area_size_h; + uint32_t eeprom_size; + uint8_t eeprom[EEPROM_MAX_DATA_LEN]; + uint32_t pll_num; + uint32_t pll[MAX_PLL_NUM]; +}; + +struct camera_write_info { + int32_t sensor_temp; +}; + +static struct sony_camera_info *camera_info; +static struct sony_camera_data camera_data[]; +static struct device *camera_device; +static DEFINE_MUTEX(sensor_mutex); +static DEFINE_MUTEX(af_standby_mutex); +static struct task_struct *af_task; +static uint16_t sensor_num; +static struct msm_cam_clk_info cam_clk_info[] = { + {"cam_src_clk", 0}, + {"cam_clk", -1}, +}; + +static struct msm_sensor_power_setting sony_power_setting[] = { + { + .seq_type = 0, + .seq_val = 0, + .config_val = 0, + .delay = 0, + }, +}; + +#if defined(CONFIG_LEDS_QPNP_RGB_SCALE) && (CONFIG_FRONT_CAMERA_LED_SCALE > 0) +extern int qpnp_led_set_rgb_scale(int scale); +#endif + +static int32_t sony_util_get_context(struct msm_sensor_ctrl_t *s_ctrl) +{ + uint16_t i; + char sensor_name[CAPS_MAX_STR_LEN]; + + memset(sensor_name, 0, sizeof(sensor_name)); + for (i = 0; i < sensor_num; i++) { + snprintf(sensor_name, sizeof(sensor_name), CAMERA_DEV_NAME, i); + if (!strncmp(s_ctrl->sensordata->sensor_name, + sensor_name, sizeof(sensor_name))) + break; + } + + if (camera_data[i].d) + camera_device = camera_data[i].d; + + return i; +} + +static int sony_util_camera_info_init(struct platform_device *pdev, uint16_t id) +{ + int rc = 0; + int count = 0; + uint16_t i = 0; + uint16_t j = 0; + uint32_t val_u32[4] = {0}; + struct device_node *of_node = pdev->dev.of_node; + struct device_node *of_node_power_sequence = NULL; + struct device_node *of_node_modules = NULL; + struct device_node *of_node_modules_power_off = NULL; + struct device_node *of_node_modules_power_on = NULL; + const int8_t *power_order_name = NULL; + + rc = of_property_read_u32(of_node, "sony,i2c_addr", &val_u32[0]); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + camera_info[id].i2c_addr = val_u32[0]; + + rc = of_property_read_u32(of_node, "sony,eeprom_addr", &val_u32[0]); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + camera_info[id].eeprom_addr = val_u32[0]; + + rc = of_property_read_u32(of_node, "sony,eeprom_type", &val_u32[0]); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + camera_info[id].eeprom_type = val_u32[0]; + + rc = of_property_read_u32(of_node, "sony,eeprom_max_len", &val_u32[0]); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + camera_info[id].eeprom_max_len = val_u32[0]; + + rc = of_property_read_u32(of_node, "sony,gpio_af", &val_u32[0]); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + camera_info[id].gpio_af = val_u32[0]; + + rc = of_property_read_u32(of_node, "sony,subdev_code", &val_u32[0]); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + camera_info[id].subdev_code = val_u32[0]; + + rc = of_property_read_u32_array(of_node, "interrupts", &val_u32[0], 2); + if (rc < 0) { + camera_data[id].sof_gpio = -1; + } else { + camera_data[id].sof_gpio = val_u32[0]; + } + + of_node_power_sequence = of_find_node_by_name(of_node, + "sony,camera_modules"); + if (!of_node_power_sequence) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -EFAULT; + goto fail; + } + + count = of_property_count_strings(of_node_power_sequence, + "module_names"); + if (count < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -EFAULT; + goto fail; + } + camera_info[id].modules_num = count; + + camera_info[id].modules = kzalloc( + sizeof(struct sony_camera_module) * count, GFP_KERNEL); + if (!camera_info[id].modules) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto fail; + } + + for (i = 0; i < camera_info[id].modules_num; i++) { + rc = of_property_read_string_index(of_node_power_sequence, + "module_names", i, + (const char **)(&camera_info[id].modules[i].name)); + LOGD("%s name[%d] = %s\n", __func__, i, + camera_info[id].modules[i].name); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + } + + for (i = 0; i < camera_info[id].modules_num; i++) { + of_node_modules = of_find_node_by_name(of_node_power_sequence, + camera_info[id].modules[i].name); + if (!of_node_modules) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -EFAULT; + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "mount_angle", + &camera_info[id].modules[i].mount_angle); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "sensor_rotation", + &camera_info[id].modules[i].sensor_rotation); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "sensor_facing", + &camera_info[id].modules[i].sensor_facing); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "sensor_config_delay_num", + &camera_info[id].modules[i].sensor_config_delay_num); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + if (camera_info[id].modules[i].sensor_config_delay_num > MAX_CONFIG_DELAY_NUM) { + LOGE("%s failed %d sensor_config_delay_num %d\n", + __func__, __LINE__, + camera_info[id].modules[i].sensor_config_delay_num); + camera_info[id].modules[i].sensor_config_delay_num = MAX_CONFIG_DELAY_NUM; + } + + rc = of_property_read_u32_array(of_node_modules, + "sensor_config_delay", + camera_info[id].modules[i].sensor_config_delay, + camera_info[id].modules[i].sensor_config_delay_num); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "temperature_check_skip_num", + &camera_info[id].modules[i].temperature_check_skip_num); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "total_pixel_number_w", + &camera_info[id].modules[i].total_pixel_number_w); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "total_pixel_number_h", + &camera_info[id].modules[i].total_pixel_number_h); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "active_pixel_number_x", + &camera_info[id].modules[i].active_pixel_number_x); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "active_pixel_number_y", + &camera_info[id].modules[i].active_pixel_number_y); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + rc = of_property_read_u32(of_node_modules, + "active_pixel_number_w", + &camera_info[id].modules[i].active_pixel_number_w); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "active_pixel_number_h", + &camera_info[id].modules[i].active_pixel_number_h); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "min_focus_distance", + &camera_info[id].modules[i].min_focus_distance); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "hyper_focal_distance", + &camera_info[id].modules[i].hyper_focal_distance); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_string(of_node_modules, + "diagonal_len", + (const char **)( + &camera_info[id].modules[i].diagonal_len)); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_string(of_node_modules, + "unit_cell_size_w", + (const char **)( + &camera_info[id].modules[i].unit_cell_size_w)); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_string(of_node_modules, + "unit_cell_size_h", + (const char **)( + &camera_info[id].modules[i].unit_cell_size_h)); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_string(of_node_modules, + "min_f_number", + (const char **)( + &camera_info[id].modules[i].min_f_number)); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_string(of_node_modules, + "max_f_number", + (const char **)( + &camera_info[id].modules[i].max_f_number)); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "min_focus_pos", + &camera_info[id].modules[i].min_focus_pos); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "max_focus_pos", + &camera_info[id].modules[i].max_focus_pos); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "min_focus_dac", + &camera_info[id].modules[i].min_focus_dac); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "max_focus_dac", + &camera_info[id].modules[i].max_focus_dac); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "focus_inf_range_offset", + &camera_info[id].modules[i].focus_inf_range_offset); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "focus_macro_range_offset", + &camera_info[id].modules[i].focus_macro_range_offset); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "focus_lens_stroke_inf_to_1m", + &camera_info[id].modules[i].focus_lens_stroke_inf_to_1m); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "focus_lens_stroke_1m_to_macro", + &camera_info[id].modules[i].focus_lens_stroke_1m_to_macro); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "focus_lens_stroke_inf_to_macro", + &camera_info[id].modules[i].focus_lens_stroke_inf_to_macro); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "focus_calc_type", + &camera_info[id].modules[i].focus_calc_type); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "focus_wob_time", + &camera_info[id].modules[i].focus_wob_time); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_3a", + &camera_info[id].modules[i].has_3a); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_focus_actuator", + &camera_info[id].modules[i].has_focus_actuator); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "need_standby_af", + &camera_info[id].modules[i].need_standby_af); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "i2c_freq_mode", + &camera_info[id].modules[i].i2c_freq_mode); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_pdaf", + &camera_info[id].modules[i].has_pdaf); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_rs", + &camera_info[id].modules[i].has_rs); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_multi_output", + &camera_info[id].modules[i].has_multi_output); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_super_slow", + &camera_info[id].modules[i].has_super_slow); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_sub_sensor", + &camera_info[id].modules[i].has_sub_sensor); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_aube", + &camera_info[id].modules[i].has_aube); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_flicker_detector", + &camera_info[id].modules[i].has_flicker_detector); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_hdr", + &camera_info[id].modules[i].has_hdr); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_seamless_mode_change", + &camera_info[id].modules[i].has_seamless_mode_change); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "has_gph", + &camera_info[id].modules[i].has_gph); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "pdaf_free_area_num", + &camera_info[id].modules[i].pdaf_free_area_num); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "pdaf_fixed_area_size_w", + &camera_info[id].modules[i].pdaf_fixed_area_size_w); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "pdaf_fixed_area_size_h", + &camera_info[id].modules[i].pdaf_fixed_area_size_h); + + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32(of_node_modules, + "pll_num", + &camera_info[id].modules[i].pll_num); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32_array(of_node_modules, + "pll", + camera_info[id].modules[i].pll, + camera_info[id].modules[i].pll_num); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + of_node_modules_power_off = of_find_node_by_name( + of_node_modules, + "power_off"); + if (!of_node_modules_power_off) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -EFAULT; + goto fail; + } + + count = of_property_count_strings(of_node_modules_power_off, + "commands"); + if (count < 0) { + LOGE("%s failed power off commands 0\n", __func__); + rc = -EFAULT; + goto fail; + } + camera_info[id].modules[i].seq_off = kzalloc( + sizeof(struct sony_camera_seq) * count, GFP_KERNEL); + if (!camera_info[id].modules[i].seq_off) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto fail; + } + + for (j = 0; j < count; j++) { + rc = of_property_read_string_index( + of_node_modules_power_off, + "commands", j, + (const char **)(&power_order_name)); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + rc = of_property_read_u32_array( + of_node_modules_power_off, power_order_name, + &val_u32[0], 4); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + camera_info[id].modules[i].seq_off[j].cmd = + val_u32[0]; + camera_info[id].modules[i].seq_off[j].val1 = + val_u32[1]; + camera_info[id].modules[i].seq_off[j].val2 = + val_u32[2]; + camera_info[id].modules[i].seq_off[j].wait = + val_u32[3]; + } + + of_node_modules_power_on = of_find_node_by_name(of_node_modules, + "power_on"); + if (!of_node_modules_power_on) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -EFAULT; + goto fail; + } + + count = of_property_count_strings(of_node_modules_power_on, + "commands"); + if (count < 0) { + LOGE("%s failed power on commands 0\n", __func__); + rc = -EFAULT; + goto fail; + } + + camera_info[id].modules[i].seq_on = kzalloc( + sizeof(struct sony_camera_seq) * count, GFP_KERNEL); + if (!camera_info[id].modules[i].seq_on) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto fail; + } + + for (j = 0; j < count; j++) { + rc = of_property_read_string_index( + of_node_modules_power_on, + "commands", j, + (const char **)(&power_order_name)); + if (rc < 0) { + LOGE("%s failed %d j=%d count=%d\n", __func__, __LINE__, j, count); + goto fail; + } + + rc = of_property_read_u32_array( + of_node_modules_power_on, power_order_name, + &val_u32[0], 4); + if (rc < 0) { + LOGE("%s failed %d j=%d count=%d\n", __func__, __LINE__, j, count); + goto fail; + } + camera_info[id].modules[i].seq_on[j].cmd = + val_u32[0]; + camera_info[id].modules[i].seq_on[j].val1 = + val_u32[1]; + camera_info[id].modules[i].seq_on[j].val2 = + val_u32[2]; + camera_info[id].modules[i].seq_on[j].wait = + val_u32[3]; + } + } + + rc = of_property_read_string(of_node_power_sequence, + "default_module_name", + &camera_info[id].default_module_name); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto fail; + } + + return 0; +fail: + if (camera_info[id].modules) { + for (i = 0; i < camera_info[id].modules_num; i++) { + kfree(camera_info[id].modules[i].seq_on); + kfree(camera_info[id].modules[i].seq_off); + } + kfree(camera_info[id].modules); + } + memset(&(camera_info[id]), 0, sizeof(struct sony_camera_info)); + + return rc; +} + +static int sony_util_camera_info_deinit(uint16_t id) +{ + uint16_t i = 0; + + if (camera_info[id].modules) { + for (i = 0; i < camera_info[id].modules_num; i++) { + kfree(camera_info[id].modules[i].seq_on); + kfree(camera_info[id].modules[i].seq_off); + } + kfree(camera_info[id].modules); + } + memset(&(camera_info[id]), 0, sizeof(struct sony_camera_info)); + + return 0; +} + +static int sony_util_gpio_init(struct msm_sensor_ctrl_t *s_ctrl) +{ + int rc = 0; + uint16_t id = sony_util_get_context(s_ctrl); + + if (!camera_data[id].gpio_requested) { + rc = msm_camera_request_gpio_table( + s_ctrl->sensordata->power_info + .gpio_conf->cam_gpio_req_tbl, + s_ctrl->sensordata->power_info + .gpio_conf->cam_gpio_req_tbl_size, + 1); + if (rc == 0) + camera_data[id].gpio_requested = true; + } + + return rc; +} + +static int sony_util_gpio_deinit(struct msm_sensor_ctrl_t *s_ctrl) +{ + int rc = 0; + uint16_t id = sony_util_get_context(s_ctrl); + + if (camera_data[id].gpio_requested) { + rc = msm_camera_request_gpio_table( + s_ctrl->sensordata->power_info + .gpio_conf->cam_gpio_req_tbl, + s_ctrl->sensordata->power_info + .gpio_conf->cam_gpio_req_tbl_size, + 0); + camera_data[id].gpio_requested = false; + } + + return rc; +} + +static int sony_util_gpio_set(struct msm_sensor_ctrl_t *s_ctrl, + int gpio_pin, int value) +{ + int rc = 0; + uint16_t id = sony_util_get_context(s_ctrl); + + if (camera_data[id].gpio_requested) + gpio_set_value_cansleep(gpio_pin, value); + else + rc = -EPERM; + + return rc; +} + +static int sony_util_vreg_set(struct msm_sensor_ctrl_t *s_ctrl, + struct sony_camera_data *data, enum sony_camera_cmd cmd, int level, + int op_mode) +{ + int rc = 0; + struct regulator *vreg; + struct device *dev = &s_ctrl->pdev->dev; + + if (cmd == SONY_CAM_VDIG) { + if (data->cam_vdig) { + vreg = data->cam_vdig; + } else { + vreg = regulator_get(dev, "cam_vdig"); + if (IS_ERR(vreg)) { + LOGE("could not get cam_vdig, vreg = %ld\n", + PTR_ERR(vreg)); + rc = -ENODEV; + goto exit; + } + data->cam_vdig = vreg; + } + } else if (cmd == SONY_CAM_VIO) { + if (data->cam_vio) { + vreg = data->cam_vio; + } else { + vreg = regulator_get(dev, "cam_vio"); + if (IS_ERR(vreg)) { + LOGE("could not get cam_vio, vreg = %ld\n", + PTR_ERR(vreg)); + rc = -ENODEV; + goto exit; + } + data->cam_vio = vreg; + } + } else if (cmd == SONY_CAM_VANA) { + if (data->cam_vana) { + vreg = data->cam_vana; + } else { + vreg = regulator_get(dev, "cam_vana"); + if (IS_ERR(vreg)) { + LOGE("could not get cam_vana, vreg = %ld\n", + PTR_ERR(vreg)); + rc = -ENODEV; + goto exit; + } + data->cam_vana = vreg; + } + } else if (cmd == SONY_CAM_VAF) { + if (data->cam_vaf) { + vreg = data->cam_vaf; + } else { + vreg = regulator_get(dev, "cam_vaf"); + if (IS_ERR(vreg)) { + LOGE("could not get cam_vaf, vreg = %ld\n", + PTR_ERR(vreg)); + rc = -ENODEV; + goto exit; + } + data->cam_vaf = vreg; + } + } else if (cmd == SONY_CAM_VDIG_GPIO) { + if (data->cam_vdig_gpio) { + vreg = data->cam_vdig_gpio; + } else { + vreg = regulator_get(dev, "cam_vdig_gpio"); + if (IS_ERR(vreg)) { + LOGE("could not get cam_vdig_gpio, vreg = %ld\n", + PTR_ERR(vreg)); + rc = -ENODEV; + goto exit; + } + data->cam_vdig_gpio = vreg; + } + } else if (cmd == SONY_CAM_VIO_GPIO) { + if (data->cam_vio_gpio) { + vreg = data->cam_vio_gpio; + } else { + vreg = regulator_get(dev, "cam_vio_gpio"); + if (IS_ERR(vreg)) { + LOGE("could not get cam_vio_gpio, vreg = %ld\n", + PTR_ERR(vreg)); + rc = -ENODEV; + goto exit; + } + data->cam_vio_gpio = vreg; + } + } else if (cmd == SONY_CAM_VANA_GPIO) { + if (data->cam_vana_gpio) { + vreg = data->cam_vana_gpio; + } else { + vreg = regulator_get(dev, "cam_vana_gpio"); + if (IS_ERR(vreg)) { + LOGE("could not get cam_vana_gpio, vreg = %ld\n", + PTR_ERR(vreg)); + rc = -ENODEV; + goto exit; + } + data->cam_vana_gpio = vreg; + } + } else if (cmd == SONY_CAM_VAF_GPIO) { + if (data->cam_vaf_gpio) { + vreg = data->cam_vaf_gpio; + } else { + vreg = regulator_get(dev, "cam_vaf_gpio"); + if (IS_ERR(vreg)) { + LOGE("could not get cam_vaf_gpio, vreg = %ld\n", + PTR_ERR(vreg)); + rc = -ENODEV; + goto exit; + } + data->cam_vaf_gpio = vreg; + } + } else { + rc = -EINVAL; + LOGE("invalid resource\n"); + goto exit; + } + + level *= 1000; + if (level >= 0) { + if (level > 0) { + rc = regulator_set_voltage(vreg, level, level); + if (rc < 0) + goto set_voltage_fail; + } + if (op_mode > 0) { + rc = regulator_set_load(vreg, op_mode); + if (rc < 0) + goto set_voltage_fail; + } + rc = regulator_enable(vreg); + if (rc < 0) + goto enable_fail; + } else { + if (op_mode == 0) + (void)regulator_set_load(vreg, 0); + (void)regulator_disable(vreg); + regulator_put(vreg); + } + goto exit; + +enable_fail: + (void)regulator_set_load(vreg, 0); + +set_voltage_fail: + regulator_put(vreg); + +exit: + if (rc < 0 || level < 0) { + if (vreg == data->cam_vdig) + data->cam_vdig = NULL; + else if (vreg == data->cam_vio) + data->cam_vio = NULL; + else if (vreg == data->cam_vana) + data->cam_vana = NULL; + else if (vreg == data->cam_vaf) + data->cam_vaf = NULL; + else if (vreg == data->cam_vdig_gpio) + data->cam_vdig_gpio = NULL; + else if (vreg == data->cam_vio_gpio) + data->cam_vio_gpio = NULL; + else if (vreg == data->cam_vana_gpio) + data->cam_vana_gpio = NULL; + else if (vreg == data->cam_vaf_gpio) + data->cam_vaf_gpio = NULL; + } + + if (rc < 0) + LOGE("error happened (%d)\n", rc); + return rc; +} + +static int sony_util_mclk_set(struct msm_sensor_ctrl_t *s_ctrl, int value) +{ + int rc; + struct device *dev = &s_ctrl->pdev->dev; + uint16_t id = sony_util_get_context(s_ctrl); + + if (value >= 0) { + if (value == 0) + cam_clk_info[0].clk_rate = SENSOR_MCLK_DEFAULT; + else + cam_clk_info[0].clk_rate = (long)value; + + rc = msm_cam_clk_enable( + dev, + (struct msm_cam_clk_info *)cam_clk_info, + (struct clk **)&camera_data[id].clk_handle[0], + ARRAY_SIZE(cam_clk_info), 1); + } else { + rc = msm_cam_clk_enable( + dev, + (struct msm_cam_clk_info *)cam_clk_info, + (struct clk **)&camera_data[id].clk_handle[0], + ARRAY_SIZE(cam_clk_info), 0); + } + + if (rc < 0) + LOGE("error happened (%d)\n", rc); + + return rc; +} + +static int sony_util_i2c_mux_enable(struct msm_camera_i2c_conf *i2c_conf) +{ + struct v4l2_subdev *i2c_mux_sd = + dev_get_drvdata(&i2c_conf->mux_dev->dev); + + v4l2_subdev_call(i2c_mux_sd, core, ioctl, + VIDIOC_MSM_I2C_MUX_INIT, NULL); + v4l2_subdev_call(i2c_mux_sd, core, ioctl, + VIDIOC_MSM_I2C_MUX_CFG, (void *)&i2c_conf->i2c_mux_mode); + return 0; +} + +static int sony_util_i2c_mux_disable(struct msm_camera_i2c_conf *i2c_conf) +{ + struct v4l2_subdev *i2c_mux_sd = + dev_get_drvdata(&i2c_conf->mux_dev->dev); + + v4l2_subdev_call(i2c_mux_sd, core, ioctl, + VIDIOC_MSM_I2C_MUX_RELEASE, NULL); + return 0; +} + +static int sony_util_cci_init(struct msm_camera_i2c_client *client) +{ + return msm_sensor_cci_i2c_util(client, MSM_CCI_INIT); +} + +static int sony_util_cci_deinit(struct msm_camera_i2c_client *client) +{ + return msm_sensor_cci_i2c_util(client, MSM_CCI_RELEASE); +} + +static int sony_util_cam_i2c_read(struct msm_sensor_ctrl_t *s_ctrl, + uint8_t slave_addr, uint32_t addr, + uint8_t type, uint16_t len, uint8_t *data) +{ + int rc = 0; + uint16_t i = 0; + uint16_t read_addr = 0x00; + uint16_t read_data = 0x00; + uint16_t orig_sid; + uint16_t orig_addr_type; + + orig_sid = s_ctrl->sensor_i2c_client->cci_client->sid; + orig_addr_type = s_ctrl->sensor_i2c_client->addr_type; + + s_ctrl->sensor_i2c_client->addr_type = type; + s_ctrl->sensor_i2c_client->cci_client->sid = slave_addr >> 1; + + for (i = 0; i < len; i++) { + read_addr = addr + i; + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read( + s_ctrl->sensor_i2c_client, + read_addr, + &read_data, + MSM_CAMERA_I2C_BYTE_DATA); + if (rc < 0) { + LOGE("slave0x%04x,addr0x%04x,type0x%02x,len0x%02x\n", + slave_addr, addr, type, len); + LOGE("i2c read failed(%d)\n", rc); + rc = -EIO; + goto exit; + } + data[i] = (uint8_t)(read_data & 0xFF); + } + +exit: + s_ctrl->sensor_i2c_client->cci_client->sid = orig_sid; + s_ctrl->sensor_i2c_client->addr_type = orig_addr_type; + + return rc; +} + +static int sony_util_cam_i2c_write(struct msm_sensor_ctrl_t *s_ctrl, + uint8_t slave_addr, uint32_t addr, uint8_t type, + uint16_t len, uint8_t *data) +{ + int rc = 0; + uint16_t i = 0; + uint16_t write_addr = 0x00; + uint16_t write_data = 0x00; + uint16_t orig_sid; + uint16_t orig_addr_type; + + orig_sid = s_ctrl->sensor_i2c_client->cci_client->sid; + orig_addr_type = s_ctrl->sensor_i2c_client->addr_type; + + s_ctrl->sensor_i2c_client->addr_type = type; + s_ctrl->sensor_i2c_client->cci_client->sid = slave_addr >> 1; + + for (i = 0; i < len; i++) { + write_data = data[i]; + write_addr = addr + i; + rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write( + s_ctrl->sensor_i2c_client, + write_addr, + write_data, + MSM_CAMERA_I2C_BYTE_DATA); + if (rc < 0) { + LOGE("slave0x%04x,addr0x%04x,type0x%02x,len0x%02x,data", + slave_addr, addr, type, len); + for (i = 0; i < len; i++) + LOGE("0x%02x ", *(data + i)); + LOGE("i2c write failed(%d)\n", rc); + rc = -EIO; + goto exit; + } + } + +exit: + s_ctrl->sensor_i2c_client->cci_client->sid = orig_sid; + s_ctrl->sensor_i2c_client->addr_type = orig_addr_type; + + return rc; +} + +static int sony_util_power_ctrl(struct msm_sensor_ctrl_t *s_ctrl, + struct sony_camera_data *data, bool on) +{ + int rc = 0; + uint16_t id = sony_util_get_context(s_ctrl); + const struct sony_camera_module *mod = data->probe_done ? + data->module : camera_info[id].modules; + const struct sony_camera_seq *seq = on ? mod->seq_on : mod->seq_off; + + while (seq->cmd != EXIT) { + uint8_t iodt = 0x00; + switch (seq->cmd) { + case SONY_GPIO_RESET: + rc = sony_util_gpio_set(s_ctrl, + s_ctrl->sensordata + ->power_info.gpio_conf + ->cam_gpio_req_tbl[1].gpio, + seq->val1); + break; + case SONY_GPIO_AF: + if (camera_info[id].gpio_af <= 0) { + rc = -EPERM; + break; + } + + rc = sony_util_gpio_set(s_ctrl, + camera_info[id].gpio_af, seq->val1); + break; + case SONY_CAM_VDIG: + case SONY_CAM_VIO: + case SONY_CAM_VANA: + case SONY_CAM_VAF: + case SONY_CAM_VIO_GPIO: + case SONY_CAM_VANA_GPIO: + case SONY_CAM_VDIG_GPIO: + case SONY_CAM_VAF_GPIO: + rc = sony_util_vreg_set(s_ctrl, + data, seq->cmd, seq->val1, seq->val2); + break; + case SONY_CAM_CLK: + rc = sony_util_mclk_set(s_ctrl, seq->val1); + break; + case SONY_I2C_WRITE: + rc = sony_util_cam_i2c_write(s_ctrl, + camera_info[id].i2c_addr, + seq->val1, + MSM_CAMERA_I2C_WORD_ADDR, + 1, + &iodt); + break; + default: + goto exit; + } + mdelay(seq->wait); + if (rc < 0 && on) + goto exit; + seq++; + } +exit: + return rc; +} + +static int sony_util_sub_camera_af_standby_task(void *sdata) +{ + int rc = 0; + struct msm_sensor_ctrl_t *s_ctrl = (struct msm_sensor_ctrl_t *)sdata; + struct sony_camera_data *main_data = &camera_data[0]; + struct sony_camera_data *sub_data = &camera_data[1]; + struct msm_camera_sensor_board_info *sub_sensordata = + sub_data->s_ctrl.sensordata; + uint16_t id = sony_util_get_context(s_ctrl); + uint8_t data[2]; + + mutex_lock(&af_standby_mutex); + if (id == 0 && + main_data->probe_done == true && + sub_data->probe_done == true && + sub_data->module->need_standby_af && + !sub_data->power_up_done) { + int r = 0; + uint32_t i = 0; + + rc = sony_util_cci_init(sub_data->s_ctrl.sensor_i2c_client); + if (rc < 0) { + LOGE("sub camera cci_init failed\n"); + goto exit; + } + if (sub_sensordata->power_info.i2c_conf && + sub_sensordata->power_info.i2c_conf->use_i2c_mux) + sony_util_i2c_mux_enable( + sub_sensordata->power_info.i2c_conf); + for (i = 0; i < 1000; i++) { + r = sony_util_cam_i2c_read(&sub_data->s_ctrl, + 0x72 << 1, 0xB3, MSM_CAMERA_I2C_BYTE_ADDR, + 1, data); + if (r < 0) { + LOGE("sub camera af read failed\n"); + goto sub_exit; + } else if (!data[0]) { + LOGD("wake up flag = 0x%0x\n", data[0]); + break; + } + } + if (i >= 1000) { + LOGE("sub camera af wake up check failed. i:%d\n", i); + goto sub_exit; + } + data[0] = 0x02; + r = sony_util_cam_i2c_write(&sub_data->s_ctrl, + 0x72 << 1, 0xF3, MSM_CAMERA_I2C_BYTE_ADDR, 1, data); + if (r < 0) + goto sub_exit; + data[0] = 0x20; + r = sony_util_cam_i2c_write(&sub_data->s_ctrl, + 0x72 << 1, 0xF2, MSM_CAMERA_I2C_BYTE_ADDR, 1, data); + if (r < 0) + goto sub_exit; + data[0] = 0x02; + r = sony_util_cam_i2c_write(&sub_data->s_ctrl, + 0x72 << 1, 0xA1, MSM_CAMERA_I2C_BYTE_ADDR, 1, data); + if (r < 0) + goto sub_exit; + data[0] = 0xC0; + r = sony_util_cam_i2c_write(&sub_data->s_ctrl, + 0x72 << 1, 0x98, MSM_CAMERA_I2C_BYTE_ADDR, 1, data); + if (r < 0) + goto sub_exit; + data[0] = 0x28; + r = sony_util_cam_i2c_write(&sub_data->s_ctrl, + 0x72 << 1, 0x96, MSM_CAMERA_I2C_BYTE_ADDR, 1, data); + if (r < 0) + goto sub_exit; + data[0] = 0x80; + r = sony_util_cam_i2c_write(&sub_data->s_ctrl, + 0x72 << 1, 0xF6, MSM_CAMERA_I2C_BYTE_ADDR, 1, data); + LOGD("%s sub camera standby down\n", __func__); +sub_exit: + if (sub_sensordata->power_info.i2c_conf && + sub_sensordata->power_info.i2c_conf->use_i2c_mux) + sony_util_i2c_mux_disable( + sub_sensordata->power_info.i2c_conf); + rc = sony_util_cci_deinit(sub_data->s_ctrl.sensor_i2c_client); + if (rc < 0) + LOGE("sub camera cci_deinit failed\n"); + if (r < 0) + rc = -EINVAL; + } else if (id == 1 && + main_data->probe_done == true && + sub_data->probe_done == true && + sub_data->module->need_standby_af && + main_data->power_up_done) { + data[0] = 0x00; + rc = sony_util_cam_i2c_write(&sub_data->s_ctrl, + 0x72 << 1, 0xF6, MSM_CAMERA_I2C_BYTE_ADDR, 1, data); + if (rc < 0) + goto exit; + data[0] = 0x20; + rc = sony_util_cam_i2c_write(&sub_data->s_ctrl, + 0x72 << 1, 0x96, MSM_CAMERA_I2C_BYTE_ADDR, 1, data); + if (rc < 0) + goto exit; + data[0] = 0x00; + rc = sony_util_cam_i2c_write(&sub_data->s_ctrl, + 0x72 << 1, 0x98, MSM_CAMERA_I2C_BYTE_ADDR, 1, data); + if (rc < 0) + goto exit; + data[0] = 0x01; + rc = sony_util_cam_i2c_write(&sub_data->s_ctrl, + 0x72 << 1, 0xE0, MSM_CAMERA_I2C_BYTE_ADDR, 1, data); + LOGD("%s sub camera resume down\n", __func__); + } +exit: + af_task = NULL; + mutex_unlock(&af_standby_mutex); + return rc; +} + +static int sony_camera_thermal_get_temp(struct thermal_zone_device *thermal, + int *temp) +{ + int rc = 0; + int id = 0; + + if (!temp) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -EPERM; + goto error; + } + + if (!strncmp(thermal->type, CAMERA_THERMAL_NAME_0, + sizeof(CAMERA_THERMAL_NAME_0))) { + id = 0; + } else if (!strncmp(thermal->type, CAMERA_THERMAL_NAME_1, + sizeof(CAMERA_THERMAL_NAME_1))) { + id = 1; + } else { + rc = -EPERM; + goto error; + } + + if (camera_data[id].thermal_ret_val < 0) + rc = camera_data[id].thermal_ret_val; + else + *temp = (int)camera_data[id].thermal_sensor_temp; + LOGD("%s %d, id = %d rc = %d *temp = %d\n", + __func__, __LINE__, id, rc, *temp); + +error: + return rc; +} + +static struct thermal_zone_device_ops sony_camera_thermal_ops = { + .get_temp = sony_camera_thermal_get_temp, +}; + +static ssize_t sony_camera_info_read(struct device *ldev, + struct device_attribute *attr, char *buf) +{ + char sensor_name[CAPS_MAX_STR_LEN]; + int id = 0; + uint16_t info_len = 0; + struct camera_read_info *info = (struct camera_read_info *)buf; + + memset(sensor_name, 0, sizeof(sensor_name)); + for (id = 0; id < sensor_num; id++) { + snprintf(sensor_name, sizeof(sensor_name), CAMERA_DEV_NAME, id); + if (!strncmp(ldev->kobj.name, + sensor_name, sizeof(sensor_name))) { + info->mount_angle = + camera_data[id].module->mount_angle; + info->sensor_rotation = + camera_data[id].module->sensor_rotation; + info->sensor_facing = + camera_data[id].module->sensor_facing; + info->sensor_config_delay_num = + camera_data[id].module->sensor_config_delay_num; + memset(info->sensor_config_delay, 0, sizeof(info->sensor_config_delay)); + memcpy(info->sensor_config_delay, camera_data[id].module->sensor_config_delay, + sizeof(info->sensor_config_delay)); + info->temperature_check_skip_num = + camera_data[id].module->temperature_check_skip_num; + info->total_pixel_number_w = + camera_data[id].module->total_pixel_number_w; + info->total_pixel_number_h = + camera_data[id].module->total_pixel_number_h; + info->active_pixel_number_x = + camera_data[id].module->active_pixel_number_x; + info->active_pixel_number_y = + camera_data[id].module->active_pixel_number_y; + info->active_pixel_number_w = + camera_data[id].module->active_pixel_number_w; + info->active_pixel_number_h = + camera_data[id].module->active_pixel_number_h; + info->min_focus_distance = + camera_data[id].module->min_focus_distance; + info->hyper_focal_distance = + camera_data[id].module->hyper_focal_distance; + memset(info->diagonal_len, 0, + sizeof(info->diagonal_len)); + strlcpy(info->diagonal_len, + camera_data[id].module->diagonal_len, + sizeof(info->diagonal_len)); + memset(info->unit_cell_size_w, 0, + sizeof(info->unit_cell_size_w)); + strlcpy(info->unit_cell_size_w, + camera_data[id].module->unit_cell_size_w, + sizeof(info->unit_cell_size_w)); + memset(info->unit_cell_size_h, 0, + sizeof(info->unit_cell_size_h)); + strlcpy(info->unit_cell_size_h, + camera_data[id].module->unit_cell_size_h, + sizeof(info->unit_cell_size_h)); + memset(info->min_f_number, 0, + sizeof(info->min_f_number)); + strlcpy(info->min_f_number, + camera_data[id].module->min_f_number, + sizeof(info->min_f_number)); + memset(info->max_f_number, 0, + sizeof(info->max_f_number)); + strlcpy(info->max_f_number, + camera_data[id].module->max_f_number, + sizeof(info->max_f_number)); + info->min_focus_pos = + camera_data[id].module->min_focus_pos; + info->max_focus_pos = + camera_data[id].module->max_focus_pos; + info->min_focus_dac = + camera_data[id].module->min_focus_dac; + info->max_focus_dac = + camera_data[id].module->max_focus_dac; + info->focus_inf_range_offset = + camera_data[id].module->focus_inf_range_offset; + info->focus_macro_range_offset = + camera_data[id].module->focus_macro_range_offset; + info->focus_lens_stroke_inf_to_1m = + camera_data[id].module->focus_lens_stroke_inf_to_1m; + info->focus_lens_stroke_1m_to_macro = + camera_data[id].module->focus_lens_stroke_1m_to_macro; + info->focus_lens_stroke_inf_to_macro = + camera_data[id].module->focus_lens_stroke_inf_to_macro; + info->focus_calc_type = + camera_data[id].module->focus_calc_type; + info->focus_wob_time = + camera_data[id].module->focus_wob_time; + info->has_3a = + camera_data[id].module->has_3a; + info->has_focus_actuator = + camera_data[id].module->has_focus_actuator; + info->has_pdaf = + camera_data[id].module->has_pdaf; + info->has_rs = + camera_data[id].module->has_rs; + info->has_multi_output = + camera_data[id].module->has_multi_output; + info->has_super_slow = + camera_data[id].module->has_super_slow; + info->has_sub_sensor = + camera_data[id].module->has_sub_sensor; + info->has_aube = + camera_data[id].module->has_aube; + info->has_flicker_detector = + camera_data[id].module->has_flicker_detector; + info->has_hw_sof = + (camera_data[id].sof_gpio >= 0) ? 1 : 0; + info->has_hdr = + camera_data[id].module->has_hdr; + info->has_seamless_mode_change = + camera_data[id].module->has_seamless_mode_change; + info->has_gph = + camera_data[id].module->has_gph; + info->pdaf_free_area_num = + camera_data[id].module->pdaf_free_area_num; + info->pdaf_fixed_area_size_w = + camera_data[id].module->pdaf_fixed_area_size_w; + info->pdaf_fixed_area_size_h = + camera_data[id].module->pdaf_fixed_area_size_h; + info->pll_num = + camera_data[id].module->pll_num; + memset(info->pll, 0, sizeof(info->pll)); + memcpy(info->pll, camera_data[id].module->pll, + sizeof(info->pll)); + memset(info->eeprom, 0, sizeof(info->eeprom)); + memcpy(info->eeprom, camera_data[id].eeprom, + camera_data[id].eeprom_len); + info->eeprom_size = camera_data[id].eeprom_len; + info_len = sizeof(struct camera_read_info); + break; + } + } + return info_len; +} + +static ssize_t sony_camera_info_write(struct device *ldev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char sensor_name[CAPS_MAX_STR_LEN]; + int id = 0; + uint16_t info_len = 0; + struct camera_write_info *info = (struct camera_write_info *)buf; + + memset(sensor_name, 0, sizeof(sensor_name)); + for (id = 0; id < sensor_num; id++) { + snprintf(sensor_name, sizeof(sensor_name), CAMERA_DEV_NAME, id); + if (!strncmp(ldev->kobj.name, + sensor_name, sizeof(sensor_name))) { + camera_data[id].thermal_sensor_temp = info->sensor_temp; + camera_data[id].thermal_ret_val = 0; + info_len = sizeof(struct camera_write_info); + LOGD("%d thermal_sensor_temp[%d] = %d\n", __LINE__, id, + camera_data[id].thermal_sensor_temp); + } + } + return info_len; +} + +static struct device_attribute sony_camera_info_attributes[] = { + __ATTR(info, S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, + sony_camera_info_read, sony_camera_info_write), + __ATTR(info, S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, + sony_camera_info_read, sony_camera_info_write), +}; + +static int sony_camera_info_init(int id) +{ + int rc = 0; + + rc = dev_set_name(&camera_data[id].info_dev, + CAMERA_DEV_NAME, id); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto reg_fail; + } + rc = device_register(&camera_data[id].info_dev); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + goto reg_fail; + } + + rc = device_create_file(&camera_data[id].info_dev, + &sony_camera_info_attributes[id]); + if (rc < 0) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -ENODEV; + goto create_fail; + } + return 0; + +create_fail: + device_unregister(&camera_data[id].info_dev); +reg_fail: + return rc; +} + +static void sony_camera_info_deinit(void) +{ + int id = 0; + + for (id = 0; id < sensor_num; id++) { + device_remove_file(&camera_data[id].info_dev, + &sony_camera_info_attributes[id]); + device_unregister(&camera_data[id].info_dev); + } +} + +static int sony_eeprom_load(struct msm_sensor_ctrl_t *s_ctrl) +{ + int rc = 0; + uint16_t i; + uint16_t len; + uint16_t id = sony_util_get_context(s_ctrl); + uint8_t *d = camera_data[id].eeprom; + + LOGI("load eeprom\n"); + + /* load eeprom */ + if (camera_info[id].eeprom_type == 0) { + for (i = 0; i < camera_info[id].eeprom_max_len; + i += I2C_MAX_DATA_LEN) { + uint8_t slave_addr = camera_info[id].eeprom_addr + + ((i & 0x0700) >> 7); + uint32_t offset = i & 0x00FF; + + rc = sony_util_cam_i2c_read( + &camera_data[id].s_ctrl, + slave_addr, + offset, MSM_CAMERA_I2C_BYTE_ADDR, + I2C_MAX_DATA_LEN, d + i); + if (rc < 0) { + LOGE("eeprom type %d i2c read fail %d\n", + camera_info[id].eeprom_type, rc); + camera_data[id].eeprom_len = 0; + goto exit; + } + } + len = i; + } else if (camera_info[id].eeprom_type == 2) { + len = camera_info[id].eeprom_max_len; + rc = sony_util_cam_i2c_read( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0, MSM_CAMERA_I2C_WORD_ADDR, + camera_info[id].eeprom_max_len, d); + if (rc < 0) { + LOGE("eeprom type %d i2c read fail %d\n", + camera_info[id].eeprom_type, rc); + camera_data[id].eeprom_len = 0; + goto exit; + } + } else { + len = camera_info[id].eeprom_max_len; + + d[0] = 0x03; + d[1] = 0x25; + rc = sony_util_cam_i2c_write( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x0010, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + + d[0] = 0x08; + d[1] = 0x00; + rc = sony_util_cam_i2c_write( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x0012, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + + i = 0; + do { + msleep(20); + i++; + rc = sony_util_cam_i2c_read( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x0014, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + } while ((d[0] & 0x80) && i < 100); + + if (i >= 100) { + rc = -ENODEV; + goto exit; + } + + d[0] = 0x45; + d[1] = 0x04; + rc = sony_util_cam_i2c_write( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x0018, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + + i = 0; + do { + msleep(20); + i++; + rc = sony_util_cam_i2c_read( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x0018, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + } while ((d[1] & 0x40) && i < 100); + + if (i >= 100) { + rc = -ENODEV; + goto exit; + } + + d[0] = 0x05; + d[1] = 0x20; + rc = sony_util_cam_i2c_write( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x001A, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + + d[0] = 0x05; + d[1] = 0x64; + rc = sony_util_cam_i2c_write( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x001A, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + + rc = sony_util_cam_i2c_read(&camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x0000, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) { + LOGE("i2c read faile\n"); + goto exit; + } + + if (((uint16_t)d[0] << 8 | d[1]) == SENSOR_ID_MT9M114) { + d[0] = 0x00; + d[1] = 0x55; + rc = sony_util_cam_i2c_write( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x3052, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + + d[0] = 0x00; + d[1] = 0x10; + rc = sony_util_cam_i2c_write( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x3050, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + + i = 0; + do { + msleep(20); + i++; + rc = sony_util_cam_i2c_read( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x3050, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + } while (!(d[1] & 0x20) && i < 100); + + if (i >= 100) { + rc = -ENODEV; + goto exit; + } + + rc = sony_util_cam_i2c_read( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x313A, MSM_CAMERA_I2C_WORD_ADDR, 2, d + 8); + if (rc < 0) + goto exit; + + rc = sony_util_cam_i2c_read( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x313C, MSM_CAMERA_I2C_WORD_ADDR, 2, d + 10); + if (rc < 0) + goto exit; + + if ((d[8] & 0xC0) == 0x00) + memcpy(d, MODULE_STW01BM0, SENSOR_NAME_LEN); + else + memcpy(d, MODULE_APT01BM0, SENSOR_NAME_LEN); + } else if (((uint16_t)d[0] << 8 | d[1]) == SENSOR_ID_MT9V115) { + rc = sony_util_cam_i2c_read( + &camera_data[id].s_ctrl, + camera_info[id].eeprom_addr, + 0x001A, MSM_CAMERA_I2C_WORD_ADDR, 2, d); + if (rc < 0) + goto exit; + + if ((d[0] & 0xF0) == 0xB0) + memcpy(d, MODULE_APT00YP1, 8); + else if ((d[0] & 0xF0) == 0xE0 || (d[0] & 0xF0) == 0x00) + memcpy(d, MODULE_STW00YP1, 8); + else { + LOGE("%s ModuleID is unknown\n", __func__); + memcpy(d, MODULE_APT00YP1, 8); + } + } else { + LOGE("%s Wrong camera module.\n", __func__); + } + } + + + /* identify sensor module */ + for (i = 1; i < camera_info[id].modules_num; i++) { + if (!strncmp(camera_info[id].modules[i].name, + camera_data[id].eeprom, + SENSOR_NAME_LEN)) { + camera_data[id].module = + &camera_info[id].modules[i]; + LOGD("detected sensor module name\n"); + + break; + } + } + + if (camera_data[id].module) + camera_data[id].eeprom_len = len; + else { + LOGE("Module name not recognized. Force name.\n"); + + camera_data[id].module = &camera_info[id].modules[0]; + + for (i = 1; i < camera_info[id].modules_num; i++) { + if (!strncmp(camera_info[id].modules[i].name, + camera_info[id].default_module_name, + SENSOR_NAME_LEN)) { + camera_data[id].module + = &camera_info[id].modules[i]; + LOGD("detected sensor Force name\n"); + break; + } + } + + memcpy(d, camera_info[id].default_module_name, SENSOR_NAME_LEN); + camera_data[id].eeprom_len = len; + } + + LOGI("MODULE NAME: len=%d, name=%c%c%c%c%c%c%c%c\n", + len, d[0], d[1], d[2], d[3], + d[4], d[5], d[6], d[7]); + +exit: + return rc; +} + +static int32_t sony_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl) +{ + int rc = 0; + uint16_t id = sony_util_get_context(s_ctrl); + + if (camera_data[id].probe_done == false) { + /* force EEPROM read speed to 400KHz, it will be overwritten + * to the speed defined in dtsi when probe is done */ + s_ctrl->sensor_i2c_client->cci_client->i2c_freq_mode = + EEPROM_READ_FREQ_MODE; + rc = sony_eeprom_load(s_ctrl); + if (rc < 0) + LOGE("eeprom load fail\n"); + } + return rc; +} + +static int sony_sensor_pinctrl_init(struct msm_camera_power_ctrl_t *ctrl) +{ + struct msm_pinctrl_info *sensor_pctrl = NULL; + + sensor_pctrl = &ctrl->pinctrl_info; + sensor_pctrl->pinctrl = devm_pinctrl_get(ctrl->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, + CAM_SENSOR_PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_active)) { + pr_err("%s:%d Failed to get the active state pinctrl handle\n", + __func__, __LINE__); + return -EINVAL; + } + sensor_pctrl->gpio_state_suspend + = pinctrl_lookup_state(sensor_pctrl->pinctrl, + CAM_SENSOR_PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_suspend)) { + pr_err("%s:%d Failed to get the suspend state pinctrl handle\n", + __func__, __LINE__); + return -EINVAL; + } + return 0; +} + +static void sony_sensor_send_event(struct v4l2_subdev *sd, + uint32_t event_type, struct msm_sensor_event_data *event_data) +{ + struct v4l2_event sensor_event; +#ifdef CONFIG_COMPAT + struct msm_sensor_event_data32 *event_data32 = NULL; +#endif + memset(&sensor_event, 0, sizeof(struct v4l2_event)); + sensor_event.id = 0; + sensor_event.type = event_type; +#ifdef CONFIG_COMPAT + event_data32 = + (struct msm_sensor_event_data32 *)(&sensor_event.u.data[0]); + event_data32->mono_timestamp.tv_sec = + event_data->mono_timestamp.tv_sec; + event_data32->mono_timestamp.tv_usec = + event_data->mono_timestamp.tv_usec; + event_data32->sof_count = event_data->sof_count; +#else + memcpy(&sensor_event.u.data[0], event_data, + sizeof(struct msm_sensor_event_data)); +#endif + v4l2_event_queue(sd->devnode, &sensor_event); +} + +static irqreturn_t sony_sensor_sof_irq(int irq, void *handle) +{ + struct msm_sensor_event_data event_data; + struct msm_sensor_ctrl_t *s_ctrl = (struct msm_sensor_ctrl_t *)handle; + struct timespec ts; + + get_monotonic_boottime(&ts); + s_ctrl->sof_count++; + if (s_ctrl->sof_count > 0xFFFFFFF0) + s_ctrl->sof_count = 1; + + memset(&event_data, 0, sizeof(event_data)); + event_data.sof_count = s_ctrl->sof_count; + event_data.mono_timestamp.tv_sec = ts.tv_sec; + event_data.mono_timestamp.tv_usec = ts.tv_nsec / 1000; + sony_sensor_send_event(&s_ctrl->msm_sd.sd, + SENSOR_EVENT_SOF, &event_data); + return IRQ_HANDLED; +} + +static int sony_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl) +{ + int rc = 0; + struct msm_camera_sensor_board_info *data = s_ctrl->sensordata; + struct task_struct *task = NULL; + uint16_t id = sony_util_get_context(s_ctrl); + + mutex_lock(&af_standby_mutex); + task = af_task; + af_task = NULL; + mutex_unlock(&af_standby_mutex); + if (task) + kthread_stop(task); + mutex_lock(&af_standby_mutex); + + LOGD("%s: %d\n", __func__, __LINE__); + rc = sony_util_gpio_init(s_ctrl); + if (rc < 0) { + LOGE("%s: gpio_init failed\n", __func__); + goto exit; + } + rc = sony_sensor_pinctrl_init(&data->power_info); + if (rc < 0) { + pr_err("%s:%d Initialization of pinctrl failed\n", + __func__, __LINE__); + data->power_info.cam_pinctrl_status = 0; + } else { + data->power_info.cam_pinctrl_status = 1; + } + if (data->power_info.cam_pinctrl_status) { + rc = pinctrl_select_state(data->power_info.pinctrl_info.pinctrl, + data->power_info.pinctrl_info.gpio_state_active); + if (rc) + pr_err("%s:%d cannot set pin to active state", + __func__, __LINE__); + } + LOGE("%s: id = %d, sof_gpio %d %p\n", __func__, id, camera_data[id].sof_gpio, dev_name(camera_data[id].d)); + if (camera_data[id].sof_gpio >= 0) { + camera_data[id].sof_irq = gpio_to_irq(camera_data[id].sof_gpio); + rc = request_threaded_irq(camera_data[id].sof_irq, NULL, + sony_sensor_sof_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(camera_data[id].d), s_ctrl); + if (rc) { + LOGE("Failed to request sof irq\n"); + goto exit; + } + } + + rc = sony_util_power_ctrl(s_ctrl, &camera_data[id], true); + if (rc < 0) { + LOGE("power_up fail\n"); + goto exit; + } + + rc = sony_util_cci_init(s_ctrl->sensor_i2c_client); + if (rc < 0) { + LOGE("%s cci_init failed\n", __func__); + goto exit; + } + + if (data->power_info.i2c_conf && + data->power_info.i2c_conf->use_i2c_mux) + sony_util_i2c_mux_enable( + data->power_info.i2c_conf); + + af_task = kthread_run(sony_util_sub_camera_af_standby_task, + s_ctrl, "sony_util_task"); + if (!af_task) { + LOGE("%s standby sub camera af kthread_run failed\n", + __func__); + goto exit; + } + + if (s_ctrl->func_tbl->sensor_match_id) + rc = s_ctrl->func_tbl->sensor_match_id(s_ctrl); + camera_data[id].power_up_done = true; + camera_data[id].thermal_ret_val = -EINVAL; + +#if defined(CONFIG_LEDS_QPNP_RGB_SCALE) && (CONFIG_FRONT_CAMERA_LED_SCALE > 0) + if (s_ctrl->sensordata->sensor_info->position == 1) { + qpnp_led_set_rgb_scale(CONFIG_FRONT_CAMERA_LED_SCALE); + } +#endif + +exit: + mutex_unlock(&af_standby_mutex); + + return rc; +} + +static int sony_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl) +{ + int rc = 0; + struct msm_camera_sensor_board_info *data = s_ctrl->sensordata; + struct task_struct *task = NULL; + uint16_t id = sony_util_get_context(s_ctrl); + + mutex_lock(&af_standby_mutex); + task = af_task; + af_task = NULL; + mutex_unlock(&af_standby_mutex); + if (task) + kthread_stop(task); + mutex_lock(&af_standby_mutex); + + rc = sony_util_power_ctrl(s_ctrl, &camera_data[id], false); + if (rc < 0) + LOGE("power_down fail\n"); + + if (data->power_info.i2c_conf && + data->power_info.i2c_conf->use_i2c_mux) + sony_util_i2c_mux_disable( + data->power_info.i2c_conf); + + rc = sony_util_cci_deinit(s_ctrl->sensor_i2c_client); + if (rc < 0) + LOGE("%s cci_deinit failed\n", __func__); + + LOGE("%s: sof_gpio %d\n", __func__, camera_data[id].sof_gpio); + if (camera_data[id].sof_gpio >= 0) + free_irq(camera_data[id].sof_irq, s_ctrl); + + if (data->power_info.cam_pinctrl_status) { + rc = pinctrl_select_state(data->power_info.pinctrl_info.pinctrl, + data->power_info.pinctrl_info.gpio_state_suspend); + if (rc) + pr_err("%s:%d cannot set pin to suspend state", + __func__, __LINE__); + } + data->power_info.cam_pinctrl_status = 0; + + rc = sony_util_gpio_deinit(s_ctrl); + if (rc < 0) + LOGE("%s: gpio_deinit failed\n", __func__); + camera_data[id].power_up_done = false; + mutex_unlock(&af_standby_mutex); + camera_data[id].thermal_ret_val = -ENODEV; + +#if defined(CONFIG_LEDS_QPNP_RGB_SCALE) && (CONFIG_FRONT_CAMERA_LED_SCALE > 0) + if (s_ctrl->sensordata->sensor_info->position == 1) { + qpnp_led_set_rgb_scale(100); + } +#endif + + return rc; +} + +static struct v4l2_subdev_info sony_sensor_subdev_info[] = { + { + .code = 0x00, + .colorspace = V4L2_COLORSPACE_JPEG, + .fmt = 1, + .order = 0, + }, + { + .code = 0x00, + .colorspace = V4L2_COLORSPACE_JPEG, + .fmt = 1, + .order = 0, + }, +}; + +static struct msm_camera_i2c_client sony_sensor_i2c_client[] = { + { + .addr_type = MSM_CAMERA_I2C_WORD_ADDR, + }, + { + .addr_type = MSM_CAMERA_I2C_WORD_ADDR, + }, +}; + +static const struct of_device_id sony_camera_0_dt_match[] = { + { + .compatible = "qcom,sony_camera_0", + .data = &camera_data[0].s_ctrl + }, + { + }, +}; + +static const struct of_device_id sony_camera_1_dt_match[] = { + { + .compatible = "qcom,sony_camera_1", + .data = &camera_data[1].s_ctrl + }, + { + }, +}; + +MODULE_DEVICE_TABLE(of, sony_camera_0_dt_match); +MODULE_DEVICE_TABLE(of, sony_camera_1_dt_match); + +static struct platform_driver sony_sensor_platform_driver[] = { + { + .driver = { + .name = "qcom,sony_camera_0", + .owner = THIS_MODULE, + .of_match_table = sony_camera_0_dt_match, + }, + }, + { + .driver = { + .name = "qcom,sony_camera_1", + .owner = THIS_MODULE, + .of_match_table = sony_camera_1_dt_match, + }, + }, +}; + +static int sony_camera_platform_probe(struct platform_device *pdev) +{ + int rc = 0; + const struct of_device_id *match; + uint16_t id = 0; + struct msm_sensor_ctrl_t *s_ctrl = NULL; + struct msm_sensor_info_t *sensor_init_params; + + mutex_lock(&af_standby_mutex); + match = of_match_device(sony_camera_0_dt_match, &pdev->dev); + if (!match && 1 < sensor_num) { + match = of_match_device(sony_camera_1_dt_match, &pdev->dev); + id = 1; + } + if (!match) { + LOGE("of_match_device fail\n"); + rc = -EFAULT; + mutex_unlock(&af_standby_mutex); + goto fail; + } + camera_data[id].d = &pdev->dev; + camera_data[id].probe_done = false; + + rc = sony_util_camera_info_init(pdev, id); + if (rc < 0) { + LOGE("%s sony_util_camera_info_init failed %d\n", + __func__, __LINE__); + mutex_unlock(&af_standby_mutex); + goto fail; + } + + s_ctrl = (struct msm_sensor_ctrl_t *)match->data; + s_ctrl->sensor_v4l2_subdev_info->code = camera_info[id].subdev_code; + + af_task = NULL; + mutex_unlock(&af_standby_mutex); + + rc = msm_sensor_platform_probe(pdev, match->data); + if (rc < 0) { + LOGE("%s msm_sensor_platform_probe failed %d\n", + __func__, __LINE__); + goto fail; + } + + s_ctrl->sensor_i2c_client->cci_client->i2c_freq_mode = + camera_info[id].modules[id].i2c_freq_mode; + sensor_init_params = s_ctrl->sensordata->sensor_info; + mutex_lock(&af_standby_mutex); + camera_data[id].probe_done = true; + mutex_unlock(&af_standby_mutex); + LOGI("camera %d probe ok\n", id); + + return 0; +fail: + return rc; +} + +static int __init sony_sensor_init_module(void) +{ + int rc = 0; + uint16_t i; + uint16_t probe_count = 0; + char *thermal_name[2] = {CAMERA_THERMAL_NAME_0, CAMERA_THERMAL_NAME_1}; + + sensor_num = ARRAY_SIZE(sony_sensor_platform_driver); + + camera_info = kzalloc(sizeof(struct sony_camera_info) * sensor_num, + GFP_KERNEL); + if (!camera_info) { + LOGE("%s failed %d\n", __func__, __LINE__); + rc = -ENOMEM; + goto fail_alloc; + } + + for (i = 0; i < sensor_num; i++) { + rc = platform_driver_probe(&sony_sensor_platform_driver[i], + sony_camera_platform_probe); + if (rc < 0) { + LOGE("%s platform_driver_probe (%u) %d\n", + __func__, i, __LINE__); + continue; + } + rc = sony_camera_info_init(i); + if (rc < 0) { + LOGE("%s sony_camera_info_init (%u) %d\n", + __func__, i, __LINE__); + platform_driver_unregister( + &sony_sensor_platform_driver[i]); + continue; + } + camera_data[i].thermal_zone_dev = + thermal_zone_device_register(thermal_name[i], + 0, 0, 0, &sony_camera_thermal_ops, 0, 0, 0); + if (IS_ERR(camera_data[i].thermal_zone_dev)) { + LOGE("%s thermal_zone_device_register (%u) %d\n", + __func__, i, __LINE__); + rc = PTR_ERR(camera_data[i].thermal_zone_dev); + platform_driver_unregister( + &sony_sensor_platform_driver[i]); + continue; + } + probe_count++; + } + + if (!probe_count) { + LOGE("%s platform_driver_probe (%u) %d\n", + __func__, probe_count, __LINE__); + goto fail_probe; + } + + return 0; +fail_probe: + kfree(camera_info); + camera_info = NULL; +fail_alloc: + return rc; +} + +static void __exit sony_sensor_exit_module(void) +{ + uint16_t i; + + sony_camera_info_deinit(); + for (i = 0; i < sensor_num; i++) { + platform_driver_unregister(&sony_sensor_platform_driver[i]); + msm_sensor_free_sensor_data(&camera_data[i].s_ctrl); + sony_util_camera_info_deinit(i); + thermal_zone_device_unregister(camera_data[i].thermal_zone_dev); + } + kfree(camera_info); + camera_info = NULL; +} + +static struct msm_sensor_fn_t sony_sensor_func_tbl = { +#ifdef CONFIG_COMPAT + .sensor_config32 = msm_sensor_config32, +#endif + .sensor_config = msm_sensor_config, + .sensor_power_up = sony_sensor_power_up, + .sensor_power_down = sony_sensor_power_down, + .sensor_match_id = sony_sensor_match_id, +}; + +static struct sony_camera_data camera_data[] = { + { + .s_ctrl = { + .sensor_i2c_client = &sony_sensor_i2c_client[0], + .power_setting_array.power_setting = sony_power_setting, + .power_setting_array.size = + ARRAY_SIZE(sony_power_setting), + .msm_sensor_mutex = &sensor_mutex, + .sensor_v4l2_subdev_info = &sony_sensor_subdev_info[0], + .sensor_v4l2_subdev_info_size = + ARRAY_SIZE(sony_sensor_subdev_info), + .func_tbl = &sony_sensor_func_tbl, + }, + .thermal_sensor_temp = 0, + .thermal_ret_val = -ENODEV, + .sof_irq = 0, + .sof_gpio = -1, + }, + { + .s_ctrl = { + .sensor_i2c_client = &sony_sensor_i2c_client[1], + .power_setting_array.power_setting = sony_power_setting, + .power_setting_array.size = + ARRAY_SIZE(sony_power_setting), + .msm_sensor_mutex = &sensor_mutex, + .sensor_v4l2_subdev_info = + &sony_sensor_subdev_info[1], + .sensor_v4l2_subdev_info_size = + ARRAY_SIZE(sony_sensor_subdev_info), + .func_tbl = &sony_sensor_func_tbl, + }, + .thermal_sensor_temp = 0, + .thermal_ret_val = -ENODEV, + .sof_irq = 0, + .sof_gpio = -1, + }, +}; + +module_init(sony_sensor_init_module); +module_exit(sony_sensor_exit_module); + +MODULE_DESCRIPTION("SONY V4L2 camera sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_v2/sensor/sony_camera_v4l2.h b/drivers/media/platform/msm/camera_v2/sensor/sony_camera_v4l2.h new file mode 100644 index 0000000000000000000000000000000000000000..ff635f0a29520b4efbc2f694e301bbf8242009c7 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/sony_camera_v4l2.h @@ -0,0 +1,116 @@ +/* drivers/media/platform/msm/camera_v2/sensor/sony_camera_v4l2.h + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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 __LINUX_SONY_CAMERA_V4L2_H +#define __LINUX_SONY_CAMERA_V4L2_H + +#ifdef __KERNEL__ + +#define MAX_CONFIG_DELAY_NUM 20 +#define MAX_PLL_NUM 30 + +enum sony_camera_cmd { + SONY_CAM_VDIG, + SONY_CAM_VIO, + SONY_CAM_VANA, + SONY_CAM_VAF, + SONY_GPIO_AF, + SONY_GPIO_RESET, + SONY_CAM_VDIG_GPIO, + SONY_CAM_VIO_GPIO, + SONY_CAM_VANA_GPIO, + SONY_CAM_VAF_GPIO, + SONY_CAM_CLK, + SONY_I2C_WRITE, + EXIT, +}; + +struct sony_camera_seq { + enum sony_camera_cmd cmd; + int val1; + int val2; + int wait; +}; + +struct sony_camera_module { + const char *name; + struct sony_camera_seq *seq_on; + struct sony_camera_seq *seq_off; + uint32_t mount_angle; + uint32_t sensor_rotation; + uint32_t sensor_facing; + uint32_t sensor_config_delay_num; + uint32_t sensor_config_delay[MAX_CONFIG_DELAY_NUM]; + uint32_t temperature_check_skip_num; + uint32_t total_pixel_number_w; + uint32_t total_pixel_number_h; + uint32_t active_pixel_number_x; + uint32_t active_pixel_number_y; + uint32_t active_pixel_number_w; + uint32_t active_pixel_number_h; + uint32_t min_focus_distance; + uint32_t hyper_focal_distance; + const char *diagonal_len; + const char *unit_cell_size_w; + const char *unit_cell_size_h; + const char *min_f_number; + const char *max_f_number; + uint32_t min_focus_pos; + uint32_t max_focus_pos; + uint32_t min_focus_dac; + uint32_t max_focus_dac; + uint32_t focus_inf_range_offset; + uint32_t focus_macro_range_offset; + uint32_t focus_lens_stroke_inf_to_1m; + uint32_t focus_lens_stroke_1m_to_macro; + uint32_t focus_lens_stroke_inf_to_macro; + uint32_t focus_calc_type; + uint32_t focus_wob_time; + uint32_t has_3a; + uint32_t has_focus_actuator; + uint32_t need_standby_af; + uint32_t i2c_freq_mode; + uint32_t has_pdaf; + uint32_t has_rs; + uint32_t has_multi_output; + uint32_t has_super_slow; + uint32_t has_sub_sensor; + uint32_t has_aube; + uint32_t has_flicker_detector; + uint32_t has_hdr; + uint32_t has_seamless_mode_change; + uint32_t has_gph; + uint32_t pdaf_free_area_num; + uint32_t pdaf_fixed_area_size_w; + uint32_t pdaf_fixed_area_size_h; + uint32_t pll_num; + uint32_t pll[MAX_PLL_NUM]; + uint32_t reserved; +}; + +struct sony_camera_info { + uint16_t i2c_addr; + uint16_t eeprom_addr; + int eeprom_type; + uint16_t eeprom_max_len; + int gpio_af; + int subdev_code; + struct sony_camera_module *modules; + int modules_num; + const char *default_module_name; +}; + +#endif +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index fc8da8dc0c2c982b83bf80fecaf8d83b63e4fe5d..d878a161b332da7d1704af5d2ca67b7526f92d91 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -1626,6 +1626,7 @@ static int set_max_internal_buffers_size(struct msm_vidc_inst *inst) frame_sz.height = (inst->capability.mbs_per_frame.max * 256) / inst->capability.width.max; + msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); dprintk(VIDC_DBG, "Max buffer reqs, buffer type = %d width = %d, height = %d, max_mbs_per_frame = %d\n", @@ -1658,11 +1659,9 @@ static int set_max_internal_buffers_size(struct msm_vidc_inst *inst) __func__, rc); goto alloc_fail; } + frame_sz.buffer_type = HAL_BUFFER_OUTPUT2; + msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); } - - msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); - frame_sz.buffer_type = HAL_BUFFER_OUTPUT2; - msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); rc = msm_comm_try_get_bufreqs(inst); if (rc) { dprintk(VIDC_ERR, @@ -1711,6 +1710,10 @@ static int set_max_internal_buffers_size(struct msm_vidc_inst *inst) output_count_actual, rc); goto alloc_fail; } + frame_sz.buffer_type = HAL_BUFFER_OUTPUT; + frame_sz.width = inst->prop.width[CAPTURE_PORT]; + frame_sz.height = inst->prop.height[CAPTURE_PORT]; + msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); } frame_sz.buffer_type = HAL_BUFFER_INPUT; diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 20f02ce46029fb2f0ada3af7c9114dff87e2a63e..df143e5794c5182126f2b53dd278dc5596099e21 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -3283,6 +3288,8 @@ static void venus_hfi_pm_handler(struct work_struct *work) const int max_tries = 5; struct venus_hfi_device *device = list_first_entry( &hal_ctxt.dev_head, struct venus_hfi_device, list); + char msg[SUBSYS_CRASH_REASON_LEN]; + if (!device) { dprintk(VIDC_ERR, "%s: NULL device\n", __func__); return; @@ -3296,6 +3303,9 @@ static void venus_hfi_pm_handler(struct work_struct *work) dprintk(VIDC_WARN, "Failed to PC for %d times\n", device->skip_pc_count); device->skip_pc_count = 0; + snprintf(msg, sizeof(msg), + "Failed to prepare for PC, rc : %d\n", rc); + subsystem_crash_reason("venus", msg); __process_fatal_error(device); return; } @@ -3374,6 +3384,15 @@ exit: return; } +static void venus_hfi_crash_reason(struct hfi_sfr_struct *vsfr) +{ + char msg[SUBSYS_CRASH_REASON_LEN]; + + snprintf(msg, sizeof(msg), "SFR Message from FW : %s", + vsfr->rg_data); + subsystem_crash_reason("venus", msg); +} + static void __process_sys_error(struct venus_hfi_device *device) { struct hfi_sfr_struct *vsfr = NULL; @@ -3398,6 +3417,7 @@ static void __process_sys_error(struct venus_hfi_device *device) dprintk(VIDC_ERR, "SFR Message from FW: %s\n", vsfr->rg_data); + venus_hfi_crash_reason(vsfr); } } @@ -3495,9 +3515,11 @@ static int __response_handler(struct venus_hfi_device *device) } }; - if (vsfr) + if (vsfr) { dprintk(VIDC_ERR, "SFR Message from FW: %s\n", vsfr->rg_data); + venus_hfi_crash_reason(vsfr); + } dprintk(VIDC_ERR, "Received watchdog timeout\n"); packets[packet_count++] = info; diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 1b7ec0870c2a8e0a8e772bcf93f15d701fa1a1e0..07763d4fc292009f6d6da7cb8af9602cd5fd9cb1 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c index 1ac7b597fdd35111d25bf363ce86c1f614141f40..54a94d21079cc440f5c7c5cb2381744b21fec733 100644 --- a/drivers/mfd/wcd9xxx-slimslave.c +++ b/drivers/mfd/wcd9xxx-slimslave.c @@ -10,10 +10,13 @@ * GNU General Public License for more details. */ #include +#include #include #include #include +#define SLIM_RETRY_MAX 3 + struct wcd9xxx_slim_sch { u16 rx_port_ch_reg_base; u16 port_tx_cfg_reg_base; @@ -426,6 +429,7 @@ int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, int ch_cnt = 0 ; int ret = 0; struct wcd9xxx_ch *rx; + u32 retry = 0; list_for_each_entry(rx, wcd9xxx_ch_list, list) sph[ch_cnt++] = rx->sph; @@ -435,7 +439,16 @@ int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, /* slim_control_ch (REMOVE) */ pr_debug("%s before slim_control_ch grph %d\n", __func__, grph); - ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true); + do { + ret = slim_control_ch(wcd9xxx->slim, grph, + SLIM_CH_REMOVE, true); + if (ret < 0) { + retry++; + pr_err("%s: failed ret[%d] retry_cnt:%d\n", + __func__, ret, retry); + usleep_range(5000, 5100); + } + } while (ret < 0 && (retry < SLIM_RETRY_MAX)); if (ret < 0) { pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret); goto err; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 52f75b1faec05685c8a2d1555c195c4b00cc8314..0949878b2d28d18c6b26d54ab83c9a7d7b75fc1f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -412,6 +412,10 @@ config TI_DAC7512 This driver can also be built as a module. If so, the module will be called ti_dac7512. +config UID_STAT + bool "UID based statistics tracking exported to /proc/uid_stat" + default n + config VMWARE_BALLOON tristate "VMware Balloon Driver" depends on VMWARE_VMCI && X86 && HYPERVISOR_GUEST @@ -579,6 +583,81 @@ config MEMORY_STATE_TIME help Memory time statistics exported to /sys/kernel/memory_state_time +config RAMDUMP_TAGS + bool "Ramdump tags to communicate between kernel and ramdump" + default n + depends on !RAMDUMP_CRASH_LOGS + help + This option enables a driver which can be interacted with to + supply the ramdumper with more information about the current + state of the crashing kernel such as build info, HWWD registers, + additional CPU registers not stored in crash_notes, crashing + application info etc. A small amount of memory will be set + aside to hold the information which will later be used by + the ramdump application. + +config RAMDUMP_MEMDESC + bool "Ramdump memory descriptors" + default n + help + This is a ramdump_mem_desc driver which adds the + memory descriptors in Normal mode into a piece of debug memory + and split the contents and exports it as /proc/mem_desc + in Ramdump mode to support ramdump functionality + +config POWERKEY_FORCECRASH + tristate "Force a crash on powerkey long press" + default n + help + Say Y here if you want force crash functionality through power + key. Triggers panic if power key is hold for more than 10secs. + +config CXD224X_NFC + bool "CXD224X driver for FeliCa and NFC" + depends on I2C + default n + help + This option enables device driver support for Sony CXD224X + series. This driver enables the control of CXD224X series + device by using I2C. Furthermore, this driver enables the + control of PON using device file. + +config BD7602_POWER_IC + bool "BD7602 Power IC control" + depends on I2C + default n + help + This option enables device driver support for BD7602 Power IC + control device. + +config ONESEG_TUNER_SMTVJ19X + tristate "ONESEG tuner driver for SMT-VJ19x" + default n + depends on SPI + help + Select this module to enable 1seg Tuner Driver which supports + 1seg contents viewing. + This module controls power switching and reset switching of + SMT-VJ19x. + +config TOF_SENSOR + tristate "ToF Sensor" + depends on I2C + ---help--- + Say Y here if you want to use ToF sensor. + + To compile this driver as a module, choose M here: the + module will be called tof_sensor. + +config SENSORS_TCS3490 + tristate "AMS TCS3490 Sensor Driver" + depends on I2C + default n + help + If you say yes here you get support for the AMS tcs3490, + ALS and color light sensors. + This driver can also be build as module. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" @@ -592,4 +671,5 @@ source "drivers/misc/mic/Kconfig" source "drivers/misc/genwqe/Kconfig" source "drivers/misc/echo/Kconfig" source "drivers/misc/cxl/Kconfig" +source "drivers/misc/mm_tuner/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b0718228d2d933ca796487915aa0b427c4c8406b..5fa37c3a35fdc2fd2376d002b9e8f7f5bafc93a4 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_ISL29020) += isl29020.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o +obj-$(CONFIG_UID_STAT) += uid_stat.o obj-$(CONFIG_C2PORT) += c2port/ obj-$(CONFIG_HMC6352) += hmc6352.o obj-y += eeprom/ @@ -63,6 +64,15 @@ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o +obj-$(CONFIG_BD7602_POWER_IC) += bd7602.o +obj-$(CONFIG_CXD224X_NFC) += cxd224x-i2c.o obj-y += qcom/ obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o +obj-$(CONFIG_TOF_SENSOR) += tof_sensor.o +obj-$(CONFIG_SENSORS_TCS3490) += tcs3490.o +obj-$(CONFIG_RAMDUMP_TAGS) += rdtags.o +obj-$(CONFIG_RAMDUMP_MEMDESC) += ramdump_mem_desc.o +obj-$(CONFIG_POWERKEY_FORCECRASH) += powerkey_forcecrash.o +obj-$(CONFIG_MMTUNER_MN8855x) += mm_tuner/ +obj-$(CONFIG_ONESEG_TUNER_SMTVJ19X) += oneseg_tuner_drv.o diff --git a/drivers/misc/bd7602.c b/drivers/misc/bd7602.c new file mode 100644 index 0000000000000000000000000000000000000000..3fbd6a101af58b0051988aa8d81a4e252d3be5ad --- /dev/null +++ b/drivers/misc/bd7602.c @@ -0,0 +1,302 @@ +/* drivers/misc/bd7602.c + * + * Author: Manabu Yoshida + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include + +#define BD7602_DEVICE_NAME "bd7602" +#define BD7602_MODE_MAX 0x04 + +struct bd7602_dev { + struct mutex mutex; + struct i2c_client *client; + u8 mode; +}; + +static ssize_t bd7602_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + u8 mode; + struct bd7602_dev *bd7602_dev = dev_get_drvdata(dev); + + if (!bd7602_dev) { + ret = -ENODEV; + goto err; + } + + if (sizeof(mode) != size) { + dev_err(dev, "%s: Invalid size %ld\n", __func__, size); + ret = -EINVAL; + goto err; + } + + memcpy(&mode, buf, size); + if (BD7602_MODE_MAX < mode) { + dev_err(dev, "%s: Invalid mode 0x%02x\n", __func__, mode); + ret = -EINVAL; + goto err; + } + + mutex_lock(&bd7602_dev->mutex); + bd7602_dev->mode = mode; + mutex_unlock(&bd7602_dev->mutex); + dev_info(dev, "%s: mode: 0x%02x\n", __func__, bd7602_dev->mode); + + return size; + +err: + return ret; +} + +static ssize_t bd7602_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct bd7602_dev *bd7602_dev = dev_get_drvdata(dev); + + if (!bd7602_dev) { + ret = -ENODEV; + goto err; + } + + mutex_lock(&bd7602_dev->mutex); + memcpy(buf, &bd7602_dev->mode, sizeof(bd7602_dev->mode)); + mutex_unlock(&bd7602_dev->mutex); + dev_info(dev, "%s: mode: 0x%02x\n", __func__, bd7602_dev->mode); + + return sizeof(bd7602_dev->mode); + +err: + return ret; +} + +static ssize_t bd7602_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 data[2]; + int ret; + int len = 2; + struct bd7602_dev *bd7602_dev = dev_get_drvdata(dev); + + if (!bd7602_dev) { + ret = -ENODEV; + goto err; + } + + if (sizeof(u8) != size) { + dev_err(dev, "%s: Invalid size %ld\n", __func__, size); + ret = -EINVAL; + goto err; + } + + mutex_lock(&bd7602_dev->mutex); + data[0] = bd7602_dev->mode; + memcpy(&data[1], buf, size); + ret = i2c_master_send(bd7602_dev->client, data, len); + if (ret != len) { + mutex_unlock(&bd7602_dev->mutex); + dev_err(dev, "%s: Failed to write %d\n", __func__, ret); + ret = -EIO; + goto err; + } + mutex_unlock(&bd7602_dev->mutex); + + dev_info(dev, "%s: mode:0x%02x value:0x%02x\n", + __func__, data[0], data[1]); + + return size; + +err: + return ret; +} + +static ssize_t bd7602_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data; + int ret; + struct bd7602_dev *bd7602_dev = dev_get_drvdata(dev); + + if (!bd7602_dev) { + ret = -ENODEV; + goto err; + } + + mutex_lock(&bd7602_dev->mutex); + ret = i2c_master_send(bd7602_dev->client, &bd7602_dev->mode, + sizeof(bd7602_dev->mode)); + if (sizeof(bd7602_dev->mode) != ret) { + mutex_unlock(&bd7602_dev->mutex); + dev_err(dev, "%s: Failed to write %d\n", __func__, ret); + ret = -EIO; + goto err; + } + + ret = i2c_master_recv(bd7602_dev->client, &data, sizeof(data)); + if (sizeof(data) != ret) { + mutex_unlock(&bd7602_dev->mutex); + dev_err(dev, "%s: Failed to read %d\n", __func__, ret); + ret = -EIO; + goto err; + } + + memcpy(buf, &data, sizeof(data)); + mutex_unlock(&bd7602_dev->mutex); + + dev_info(dev, "%s: mode:0x%02x value:0x%02x\n", + __func__, bd7602_dev->mode, data); + + return sizeof(data); + +err: + return ret; +} + +static struct device_attribute bd7602_sysfs_attrs[] = { + __ATTR(mode, S_IRUSR | S_IWUSR, bd7602_mode_show, bd7602_mode_store), + __ATTR(value, S_IRUSR | S_IWUSR, bd7602_value_show, bd7602_value_store), +}; + +static int bd7602_create_sysfs_entries(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bd7602_sysfs_attrs); i++) + if (device_create_file(dev, bd7602_sysfs_attrs + i)) + goto err_create_file; + return 0; + +err_create_file: + for (i = i - 1; i >= 0; i--) + device_remove_file(dev, bd7602_sysfs_attrs + i); + + dev_err(dev, "Unable to create sysfs interfaces\n"); + + return -EIO; +} + +static void bd7602_remove_sysfs_entries(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bd7602_sysfs_attrs); i++) + device_remove_file(dev, bd7602_sysfs_attrs + i); +} + +static int bd7602_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct bd7602_dev *bd7602_dev; + + dev_info(&client->dev, "%s, probing bd7602 driver flags = %x\n", + __func__, client->flags); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_exit; + } + + bd7602_dev = kzalloc(sizeof(*bd7602_dev), GFP_KERNEL); + if (bd7602_dev == NULL) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + ret = -ENOMEM; + goto err_kzalloc; + } + + bd7602_dev->client = client; + i2c_set_clientdata(client, bd7602_dev); + + mutex_init(&bd7602_dev->mutex); + + ret = bd7602_create_sysfs_entries(&client->dev); + if (ret) { + dev_err(&client->dev, "bd7602_create_sysfs_entries failed\n"); + goto err_create_sysfs_entries; + } + + dev_info(&client->dev, + "%s, probing bd7602 driver exited successfully\n", + __func__); + return 0; + +err_create_sysfs_entries: + mutex_destroy(&bd7602_dev->mutex); + i2c_set_clientdata(client, NULL); + kzfree(bd7602_dev); +err_kzalloc: +err_exit: + return ret; +} + +static int bd7602_remove(struct i2c_client *client) +{ + struct bd7602_dev *bd7602_dev = i2c_get_clientdata(client); + + bd7602_remove_sysfs_entries(&client->dev); + mutex_destroy(&bd7602_dev->mutex); + i2c_set_clientdata(client, NULL); + kzfree(bd7602_dev); + + return 0; +} + +static const struct i2c_device_id bd7602_id[] = { + { BD7602_DEVICE_NAME, 0 }, + { } +}; + +static struct of_device_id bd7602_match_table[] = { + { .compatible = "rohm,bd7602", }, + { }, +}; + +static struct i2c_driver bd7602_driver = { + .id_table = bd7602_id, + .probe = bd7602_probe, + .remove = bd7602_remove, + .driver = { + .owner = THIS_MODULE, + .name = BD7602_DEVICE_NAME, + .of_match_table = bd7602_match_table, + }, +}; + +static int __init bd7602_dev_init(void) +{ + return i2c_add_driver(&bd7602_driver); +} +module_init(bd7602_dev_init); + +static void __exit bd7602_dev_exit(void) +{ + i2c_del_driver(&bd7602_driver); +} +module_exit(bd7602_dev_exit); + +MODULE_AUTHOR("Manabu Yoshida "); +MODULE_DESCRIPTION("ROHM BD7602 Power IC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/cxd224x-i2c.c b/drivers/misc/cxd224x-i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..cd784b2ce07ab68efcb81096443b64cccb8b7fc3 --- /dev/null +++ b/drivers/misc/cxd224x-i2c.c @@ -0,0 +1,739 @@ +/* drivers/misc/cxd224x-i2c.c + * + * Copyright (C) 2013 Sony Corporation. + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* do not change below */ +#define MAX_BUFFER_SIZE 780 + +/* Read data */ +#define PACKET_HEADER_SIZE_NCI (3) +#define PACKET_HEADER_SIZE_HCI (3) +#define PACKET_TYPE_NCI (16) +#define PACKET_TYPE_HCIEV (4) +#define MAX_PACKET_SIZE (PACKET_HEADER_SIZE_NCI + 255) + +#define CXD224X_WAKE_LOCK_TIMEOUT 3 +#define CXD224X_WAKE_LOCK_NAME CXD224X_DEVICE_NAME +#define CXD224X_WAKE_LOCK_TIMEOUT_LP 3 +#define CXD224X_WAKE_LOCK_NAME_LP "cxd224x-i2c-lp" + +#define CXD224X_PINCTRL_ACTIVE "felica_active" +#define CXD224X_PINCTRL_SUSPEND "felica_suspend" + +struct cxd224x_dev { + wait_queue_head_t read_wq; + struct mutex read_mutex; + struct i2c_client *client; + struct miscdevice cxd224x_device; + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + unsigned int irq_gpio; + unsigned int wake_gpio; + bool irq_enabled; + spinlock_t irq_enabled_lock; + unsigned int count_irq; + struct wake_lock wakelock; + struct wake_lock wakelock_lp; +}; + +static struct cxd224x_dev *p_cxd224x_dev; + +static void cxd224x_init_stat(struct cxd224x_dev *cxd224x_dev) +{ + cxd224x_dev->count_irq = 0; +} + +static void cxd224x_disable_irq(struct cxd224x_dev *cxd224x_dev) +{ + unsigned long flags; + spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags); + if (cxd224x_dev->irq_enabled) { + disable_irq_nosync(cxd224x_dev->client->irq); + cxd224x_dev->irq_enabled = false; + } + spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags); +} + +static void cxd224x_enable_irq(struct cxd224x_dev *cxd224x_dev) +{ + unsigned long flags; + spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags); + if (!cxd224x_dev->irq_enabled) { + cxd224x_dev->irq_enabled = true; + enable_irq(cxd224x_dev->client->irq); + } + spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags); +} + +static irqreturn_t cxd224x_dev_irq_handler(int irq, void *dev_id) +{ + struct cxd224x_dev *cxd224x_dev = dev_id; + unsigned long flags; + + spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags); + cxd224x_dev->count_irq++; + spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags); + wake_up(&cxd224x_dev->read_wq); + + dev_info(&cxd224x_dev->client->dev, "%s\n", __func__); + + return IRQ_HANDLED; +} + +static unsigned int cxd224x_dev_poll(struct file *filp, poll_table *wait) +{ + struct cxd224x_dev *cxd224x_dev = filp->private_data; + unsigned int mask = 0; + unsigned long flags; + + poll_wait(filp, &cxd224x_dev->read_wq, wait); + + spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags); + if (cxd224x_dev->count_irq > 0) { + cxd224x_dev->count_irq--; + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags); + + if (mask) + wake_lock_timeout(&cxd224x_dev->wakelock, + CXD224X_WAKE_LOCK_TIMEOUT * HZ); + return mask; +} + +static ssize_t cxd224x_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + struct cxd224x_dev *cxd224x_dev = filp->private_data; + unsigned char tmp[MAX_BUFFER_SIZE]; + int total, len, ret; + + total = 0; + len = 0; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + mutex_lock(&cxd224x_dev->read_mutex); + + ret = i2c_master_recv(cxd224x_dev->client, tmp, 3); + if (ret == 3 && (tmp[0] != 0xff)) { + total = ret; + + len = tmp[PACKET_HEADER_SIZE_NCI-1]; + + /* make sure full packet fits in the buffer */ + if (len > 0 && (len + total) <= count) { + /* read the remainder of the packet */ + ret = i2c_master_recv(cxd224x_dev->client, tmp+total, + len); + if (ret == len) + total += len; + } + } + + mutex_unlock(&cxd224x_dev->read_mutex); + + if (total > count || copy_to_user(buf, tmp, total)) { + dev_err(&cxd224x_dev->client->dev, + "failed to copy to user space, total = %d\n", total); + total = -EFAULT; + } + + return total; +} + +static ssize_t cxd224x_dev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + struct cxd224x_dev *cxd224x_dev = filp->private_data; + char tmp[MAX_BUFFER_SIZE]; + int ret; + + if (count > MAX_BUFFER_SIZE) { + dev_err(&cxd224x_dev->client->dev, "out of memory\n"); + return -ENOMEM; + } + + if (copy_from_user(tmp, buf, count)) { + dev_err(&cxd224x_dev->client->dev, + "failed to copy from user space\n"); + return -EFAULT; + } + + mutex_lock(&cxd224x_dev->read_mutex); + /* Write data */ + + ret = i2c_master_send(cxd224x_dev->client, tmp, count); + if (ret != count) { + dev_err(&cxd224x_dev->client->dev, + "failed to write %d\n", ret); + ret = -EIO; + } + mutex_unlock(&cxd224x_dev->read_mutex); + + return ret; +} + +static int cxd224x_dev_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + + struct cxd224x_dev *cxd224x_dev = container_of(filp->private_data, + struct cxd224x_dev, + cxd224x_device); + filp->private_data = cxd224x_dev; + cxd224x_init_stat(cxd224x_dev); + cxd224x_enable_irq(cxd224x_dev); + dev_info(&cxd224x_dev->client->dev, + "%d,%d\n", imajor(inode), iminor(inode)); + + return ret; +} + +static const struct file_operations cxd224x_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .poll = cxd224x_dev_poll, + .read = cxd224x_dev_read, + .write = cxd224x_dev_write, + .open = cxd224x_dev_open, +}; + +static ssize_t cxd224x_dev_wake_ctl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 mode; + int ret, size; + struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev); + + + if (!cxd224x_dev) { + ret = -ENODEV; + goto err; + } + ret = gpio_get_value_cansleep(cxd224x_dev->wake_gpio); + if (ret < 0) { + dev_err(dev, "%s: Unable to read WAKE GPIO\n", __func__); + goto err; + } + mode = !ret; + size = sizeof(mode); + memcpy(buf, &mode, size); + + return size; + +err: + return ret; +} + +static ssize_t cxd224x_dev_wake_ctl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 mode; + int ret, value; + struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev); + + if (!cxd224x_dev) { + ret = -ENODEV; + goto err; + } + + if (sizeof(mode) != size) { + dev_err(dev, "%s: Invalid size %ld\n", __func__, size); + ret = -EINVAL; + goto err; + } + + memcpy(&mode, buf, size); + value = mode ? 0 : 1; + if (1 == value) { + wake_lock_timeout(&cxd224x_dev->wakelock_lp, CXD224X_WAKE_LOCK_TIMEOUT_LP*HZ); + gpio_set_value_cansleep(cxd224x_dev->wake_gpio, 1); + } else if (0 == value) { + gpio_set_value_cansleep(cxd224x_dev->wake_gpio, 0); + wake_unlock(&cxd224x_dev->wakelock_lp); + } else { + /* do nothing */ + } + return size; + +err: + return ret; +} + +static struct device_attribute cxd224x_sysfs_attrs[] = { + __ATTR(wake_ctl, S_IRUSR | S_IWUSR, cxd224x_dev_wake_ctl_show, + cxd224x_dev_wake_ctl_store), +}; + +static int cxd224x_pinctrl_init(struct device *dev, struct cxd224x_dev *cxd224x_dev) +{ + int ret = 0; + + cxd224x_dev->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(cxd224x_dev->pinctrl)) { + dev_err(dev, "error devm_pinctrl_get() failed err:%ld\n", + PTR_ERR(cxd224x_dev->pinctrl)); + ret = PTR_ERR(cxd224x_dev->pinctrl); + goto out; + } + + cxd224x_dev->gpio_state_active = pinctrl_lookup_state( + cxd224x_dev->pinctrl, CXD224X_PINCTRL_ACTIVE); + + if (IS_ERR_OR_NULL(cxd224x_dev->gpio_state_active)) { + ret = PTR_ERR(cxd224x_dev->gpio_state_active); + dev_info(dev, "note pinctrl_lookup_state(%s) err:%d\n", + CXD224X_PINCTRL_ACTIVE, ret); + goto out; + } + + cxd224x_dev->gpio_state_suspend = pinctrl_lookup_state( + cxd224x_dev->pinctrl, CXD224X_PINCTRL_SUSPEND); + + if (IS_ERR_OR_NULL(cxd224x_dev->gpio_state_suspend)) { + ret = PTR_ERR(cxd224x_dev->gpio_state_suspend); + dev_info(dev, "note pinctrl_lookup_state(%s) err:%d\n", + CXD224X_PINCTRL_ACTIVE, ret); + goto out; + } + +out: + return ret; +} + +static void cxd224x_pinctrl_select_state(struct device *dev, bool active) +{ + struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev); + struct pinctrl_state *pins_state; + const char *pins_state_name; + + if (active) { + pins_state = cxd224x_dev->gpio_state_active; + pins_state_name = CXD224X_PINCTRL_ACTIVE; + } else { + pins_state = cxd224x_dev->gpio_state_suspend; + pins_state_name = CXD224X_PINCTRL_SUSPEND; + } + + if (!IS_ERR_OR_NULL(pins_state)) { + int ret = pinctrl_select_state(cxd224x_dev->pinctrl, + pins_state); + if (ret) + dev_err(dev, "error pinctrl_select_state(%s) err:%d\n", + pins_state_name, ret); + } else { + dev_err(dev, + "error pinctrl state-name:'%s' is not configured\n", + pins_state_name); + } +} + +static int cxd224x_create_sysfs_entries(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cxd224x_sysfs_attrs); i++) + if (device_create_file(dev, cxd224x_sysfs_attrs + i)) + goto err_create_file; + return 0; + +err_create_file: + for (i = i - 1; i >= 0; i--) + device_remove_file(dev, cxd224x_sysfs_attrs + i); + + dev_err(dev, "Unable to create sysfs interfaces\n"); + + return -EIO; +} + +static void cxd224x_remove_sysfs_entries(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cxd224x_sysfs_attrs); i++) + device_remove_file(dev, cxd224x_sysfs_attrs + i); +} + +static struct cxd224x_platform_data *cxd224x_dt_to_pdata(struct device *dev) +{ + int ret; + struct device_node *node = dev->of_node; + struct cxd224x_platform_data *pdata; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "unable to allocate memory for platform data\n"); + ret = -ENOMEM; + goto err; + } + pdata->irq_gpio = of_get_named_gpio(node, "cxd224x,irq_gpio", 0); + if (!gpio_is_valid(pdata->irq_gpio)) { + ret = -EINVAL; + dev_err(dev, + "failed to of_get_named_gpio(cxd224x,irq_gpio)\n"); + goto err; + } + pdata->wake_gpio = of_get_named_gpio(node, "cxd224x,wake_gpio", 0); + if (!gpio_is_valid(pdata->wake_gpio)) { + ret = -EINVAL; + dev_err(dev, + "failed to of_get_named_gpio(cxd224x,wake_gpio)\n"); + goto err; + } + + return pdata; +err: + if (pdata) + devm_kfree(dev, pdata); + + return ERR_PTR(ret); +} + +static int cxd224x_gpio_request(struct device *dev, + struct cxd224x_platform_data *pdata) +{ + int ret; + + ret = gpio_request(pdata->irq_gpio, "cxd224x_irq"); + if (ret) + goto err_irq; + ret = gpio_request(pdata->wake_gpio, "cxd224x_wake"); + if (ret) + goto err_wake; + + return 0; + +err_wake: + gpio_free(pdata->wake_gpio); +err_irq: + gpio_free(pdata->irq_gpio); + + dev_err(dev, "%s: failed to gpio request %d\n", __func__, ret); + return ret; +} + +static void cxd224x_gpio_free(struct device *dev, + struct cxd224x_platform_data *pdata) +{ + gpio_free(pdata->wake_gpio); + gpio_free(pdata->irq_gpio); +} + +static int cxd224x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct cxd224x_platform_data *platform_data; + struct cxd224x_dev *cxd224x_dev; + struct clk *felica_clk = NULL; + + platform_data = client->dev.platform_data; + + dev_info(&client->dev, "%s, probing cxd224x driver flags = %x\n", + __func__, client->flags); + + if (client->dev.of_node) { + platform_data = cxd224x_dt_to_pdata(&client->dev); + if (IS_ERR(platform_data)) { + ret = PTR_ERR(platform_data); + dev_err(&client->dev, "failed to dt_to_pdata\n"); + goto err_exit; + } + client->dev.platform_data = platform_data; + } + + if (platform_data == NULL) { + dev_err(&client->dev, "nfc probe fail\n"); + ret = -ENODEV; + goto err_exit; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_exit; + } + + ret = cxd224x_gpio_request(&client->dev, platform_data); + if (ret) { + dev_err(&client->dev, "failed to gpio_request\n"); + goto err_exit; + } + + cxd224x_dev = kzalloc(sizeof(*cxd224x_dev), GFP_KERNEL); + if (cxd224x_dev == NULL) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + ret = -ENOMEM; + goto err_kzalloc; + } + + cxd224x_dev->irq_gpio = platform_data->irq_gpio; + cxd224x_dev->wake_gpio = platform_data->wake_gpio; + cxd224x_dev->client = client; + wake_lock_init(&cxd224x_dev->wakelock, WAKE_LOCK_SUSPEND, + CXD224X_WAKE_LOCK_NAME); + wake_lock_init(&cxd224x_dev->wakelock_lp, WAKE_LOCK_SUSPEND, CXD224X_WAKE_LOCK_NAME_LP); + + /* init mutex and queues */ + init_waitqueue_head(&cxd224x_dev->read_wq); + mutex_init(&cxd224x_dev->read_mutex); + spin_lock_init(&cxd224x_dev->irq_enabled_lock); + + ret = cxd224x_pinctrl_init(&client->dev, cxd224x_dev); + if (ret) { + dev_err(&client->dev, "pinctrl_init failed\n"); + goto err_pinctrl_init; + } + + cxd224x_dev->cxd224x_device.minor = MISC_DYNAMIC_MINOR; + cxd224x_dev->cxd224x_device.name = CXD224X_DEVICE_NAME; + cxd224x_dev->cxd224x_device.fops = &cxd224x_dev_fops; + + ret = misc_register(&cxd224x_dev->cxd224x_device); + if (ret) { + dev_err(&client->dev, "misc_register failed\n"); + goto err_misc_register; + } + + felica_clk = clk_get(&client->dev, "felica_clk"); + if (IS_ERR(felica_clk)) { + dev_err(&client->dev, "Couldn't get felica_clk\n"); + goto err_clk; + } + ret = clk_prepare_enable(felica_clk); + if (ret) { + dev_err(&client->dev, "failed to enable felica_clk\n"); + goto err_clk_enable; + } + + ret = cxd224x_create_sysfs_entries(&client->dev); + if (ret) { + dev_err(&client->dev, "create_sysfs_entries failed\n"); + goto err_create_sysfs_entries; + } + + /* request irq. the irq is set whenever the chip has data available + * for reading. it is cleared when all data has been read. + */ + dev_info(&client->dev, "requesting IRQ %d\n", client->irq); + cxd224x_dev->irq_enabled = true; + ret = request_threaded_irq(client->irq, NULL, cxd224x_dev_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, cxd224x_dev); + if (ret) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_request_irq_failed; + } + + cxd224x_disable_irq(cxd224x_dev); + i2c_set_clientdata(client, cxd224x_dev); + cxd224x_pinctrl_select_state(&client->dev, true); + device_init_wakeup(&client->dev, 1); + p_cxd224x_dev = cxd224x_dev; + + dev_info(&client->dev, + "%s, probing cxd224x driver exited successfully\n", + __func__); + + return 0; + +err_request_irq_failed: + cxd224x_remove_sysfs_entries(&client->dev); +err_create_sysfs_entries: + misc_deregister(&cxd224x_dev->cxd224x_device); +err_clk_enable: + clk_put(felica_clk); +err_clk: +err_misc_register: +err_pinctrl_init: + mutex_destroy(&cxd224x_dev->read_mutex); + wake_lock_destroy(&cxd224x_dev->wakelock); + kzfree(cxd224x_dev); +err_kzalloc: + cxd224x_gpio_free(&client->dev, platform_data); +err_exit: + return ret; +} + +static int cxd224x_remove(struct i2c_client *client) +{ + struct cxd224x_dev *cxd224x_dev = i2c_get_clientdata(client); + + free_irq(client->irq, cxd224x_dev); + cxd224x_remove_sysfs_entries(&client->dev); + misc_deregister(&cxd224x_dev->cxd224x_device); + mutex_destroy(&cxd224x_dev->read_mutex); + wake_lock_destroy(&cxd224x_dev->wakelock); + wake_lock_destroy(&cxd224x_dev->wakelock_lp); + i2c_set_clientdata(client, NULL); + kzfree(cxd224x_dev); + cxd224x_gpio_free(&client->dev, client->dev.platform_data); + + return 0; +} + +#ifdef CONFIG_PM +static int cxd224x_suspend(struct device *dev) +{ + struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev); + dev_info(dev, "%s\n", __func__); + + if (device_may_wakeup(&cxd224x_dev->client->dev)) + enable_irq_wake(cxd224x_dev->client->irq); + + cxd224x_pinctrl_select_state(dev, false); + + return 0; +} + +static int cxd224x_resume(struct device *dev) +{ + struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev); + dev_info(dev, "%s\n", __func__); + + if (device_may_wakeup(&cxd224x_dev->client->dev)) + disable_irq_wake(cxd224x_dev->client->irq); + + cxd224x_pinctrl_select_state(dev, true); + + return 0; +} + +static const struct dev_pm_ops cxd224x_pm_ops = { + .suspend = cxd224x_suspend, + .resume = cxd224x_resume, +}; +#endif + +static const struct i2c_device_id cxd224x_id[] = { + { CXD224X_DEVICE_NAME, 0 }, + { } +}; + +static struct of_device_id cxd224x_match_table[] = { + { .compatible = "sony,cxd224x", }, + { }, +}; + +static struct i2c_driver cxd224x_driver = { + .id_table = cxd224x_id, + .probe = cxd224x_probe, + .remove = cxd224x_remove, + .driver = { + .owner = THIS_MODULE, + .name = CXD224X_DEVICE_NAME, + .of_match_table = cxd224x_match_table, + }, +}; + +static const struct platform_device_id cxd224x_pm_ops_id[] = { + { CXD224X_PM_OPS_DEVICE_NAME, 0 }, + { } +}; + +static const struct of_device_id cxd224x_pm_ops_match_table[] = { + { .compatible = "sony,cxd224x-pm-ops" }, + { } +}; + +static int cxd224x_pm_ops_probe(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s, probing cxd224x PM OPS driver\n", __func__); + + if (!p_cxd224x_dev) + return -ENODEV; + + platform_set_drvdata(pdev, p_cxd224x_dev); + + dev_info(&pdev->dev, + "%s, probing cxd224x PM OPS driver successfully\n", __func__); + + return 0; +} + +static struct platform_driver cxd224x_pm_ops_driver = { + .id_table = cxd224x_pm_ops_id, + .probe = cxd224x_pm_ops_probe, + .driver = { + .name = CXD224X_PM_OPS_DEVICE_NAME, + .of_match_table = cxd224x_pm_ops_match_table, +#ifdef CONFIG_PM + .pm = &cxd224x_pm_ops, +#endif + }, +}; + +static int __init cxd224x_dev_init(void) +{ + int ret; + + ret = i2c_add_driver(&cxd224x_driver); + + if (ret) + goto exit; + + ret = platform_driver_register(&cxd224x_pm_ops_driver); + + if (ret) + goto exit_del_i2c_driver; + + return 0; + +exit_del_i2c_driver: + i2c_del_driver(&cxd224x_driver); + +exit: + return ret; +} +module_init(cxd224x_dev_init); + +static void __exit cxd224x_dev_exit(void) +{ + if (p_cxd224x_dev) + p_cxd224x_dev = NULL; + + i2c_del_driver(&cxd224x_driver); + platform_driver_unregister(&cxd224x_pm_ops_driver); +} +module_exit(cxd224x_dev_exit); + +MODULE_AUTHOR("Sony"); +MODULE_DESCRIPTION("NFC cxd224x driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mm_tuner/Kconfig b/drivers/misc/mm_tuner/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..5f5c51a84ec839ef66825b8c3bbfefe55b196b0c --- /dev/null +++ b/drivers/misc/mm_tuner/Kconfig @@ -0,0 +1,16 @@ +config MMTUNER_MN8855x + tristate "Socionext mm_tuner driver support" + ---help--- + This module adds support for wireless adapters based on + Socionext MN8855x chipset. + + If you choose to build a module, it'll be called mm_tuner. Say Y if + unsure. + +config MMTUNER_DEBUG + bool "Socionext MN8855x Debug message mode" + depends on MMTUNER_MN8855x + default n + ---help--- + MN8855x driver debug message mode. Say Y when you want to debug. + diff --git a/drivers/misc/mm_tuner/Makefile b/drivers/misc/mm_tuner/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0ecf8e1dbf67db0084f3cec22c80de2a90fec642 --- /dev/null +++ b/drivers/misc/mm_tuner/Makefile @@ -0,0 +1,162 @@ +############################################################################## +# Makefile for TUNER Driver +############################################################################## + +#driver name +ifndef DRV_NAME +DRV_NAME = mm_tuner +endif + +LOCAL_INC += $(src)/include +LOCAL_COMMON += $(src)/include_share + +subdir-ccflags-y += -I$(LOCAL_INC) +subdir-ccflags-y += -I$(LOCAL_COMMON) + +OBJS := src/tuner_drv.o +OBJS += src/tuner_drv_hw.o + +#PATH compile options +CONFIG_MMTUNER_DPATH=SPI +CONFIG_MMTUNER_CPATH=SPI + +ifeq ($(CONFIG_MMTUNER_DEBUG), y) + subdir-ccflags-y += -DDEBUG +endif +ifeq ($(CONFIG_MMTUNER_MN8855x), m) + subdir-ccflags-y += -fno-pic +endif +ifdef $(CONFIG_MMTUNER_I2CPARPORTX) + subdir-ccflags-y += -DTUNER_CONFIG_IRQ_PC_LINUX +endif +ifeq ($(CONFIG_MMTUNER_HOSTCPU), pandaboard) + subdir-ccflags-y += -DPANDABOARD +endif +ifeq ($(CONFIG_MMTUNER_HOSTCPU), dragon410c) + subdir-ccflags-y += -DDRAGON410C +endif +ifeq ($(CONFIG_NO_DEVICE_TREE), y) + subdir-ccflags-y += -DTUNER_CONFIG_NO_DEVICE_TREE +endif + +# +# SPI compile options +# CONFIG_MMTUNER_TSIF_SPI_EDGE : Use edge mode for SPI +# CONFIG_MMTUNER_TSIF_SI_EXT : Use Extend Read command for packet read +# CONFIG_MMTUNER_DIV_SPI_MSG : Use message separetion mode for SPI +# +ifeq ($(CONFIG_MMTUNER_TSIF_SPI_EDGE), y) + subdir-ccflags-y += -DTUNER_CONFIG_SPI_EDGE +endif +ifeq ($(CONFIG_MMTUNER_TSIF_SPI_EXT), y) + subdir-ccflags-y += -DTUNER_CONFIG_SPI_EXTREAD +endif +ifeq ($(CONFIG_MMTUNER_DIV_SPI_MSG), y) + OBJS += src/tuner_drv_hw_spi.o + subdir-ccflags-y += -DTUNER_CONFIG_SPI_DIVMSG +endif +ifeq ($(CONFIG_MMTUNER_SPI_BREAKCODE), y) + subdir-ccflags-y += -DTUNER_CONFIG_SPI_BREAKCODE +endif + + +ifeq ($(CONFIG_MMTUNER_DPATH), SDIO) + OBJS += src/tuner_drv_hw_sdio.o + subdir-ccflags-y += -DDPATH_SDIO +endif +ifeq ($(CONFIG_MMTUNER_DPATH), SPI) + OBJS += src/tuner_drv_hw_spi.o + subdir-ccflags-y += -DDPATH_SPI +endif +ifeq ($(CONFIG_MMTUNER_CPATH), I2C) + OBJS += src/tuner_drv_hw_i2c.o + subdir-ccflags-y += -DCPATH_I2C +endif +ifeq ($(CONFIG_MMTUNER_CPATH), SPI) + subdir-ccflags-y += -DCPATH_SPI +endif +ifeq ($(CONFIG_MMTUNER_CPATH), SDIO) + subdir-ccflags-y += -DCPATH_SDIO +endif + +#SPI performance measurement debug +#GP Get Packet +#DR Get DATAREADY +#subdir-ccflags-y += -DSPI_GP_MESURE_DEBUG +#subdir-ccflags-y += -DSPI_DR_MESURE_DEBUG + +# +# PID NULL FILTER +# +ifeq ($(CONFIG_MMTUNER_NULLFILTER), y) + subdir-ccflags-y += -DTUNER_CONFIG_PF_NULLFILTER +endif + +# +# BYTE ORDER CONFIGURATION. +# +ifeq ($(CONFIG_MMTUNER_MSBFIRST), y) + subdir-ccflags-y += -DTUNER_CONFIG_SLV_MSBFIRST +endif + +# +# DMA ALIGNMENT +# +ifdef CONFIG_MMTUNER_ALIGN + subdir-ccflags-y += -DTUNER_CONFIG_SPI_ALIGN=${CONFIG_MMTUNER_ALIGN} +endif + + +# +# AES compile options +# +# CONFIG_MMTUNER_AES = y : Use AES but does not check TS header of 1st packet +# CONFIG_MMTUNER_AES = c : Use AES and check TS header of 1st packet +# +ifeq ($(CONFIG_MMTUNER_AES), y) + OBJS += src/tuner_drv_hw_aes.o + subdir-ccflags-y += -DTUNER_CONFIG_AES_ENABLE +endif + +ifeq ($(CONFIG_MMTUNER_AES), c) + OBJS += src/tuner_drv_hw_aes.o + OBJS += src/aes_sample.o + subdir-ccflags-y += -DTUNER_CONFIG_AES_ENABLE + subdir-ccflags-y += -DTUNER_CONFIG_AES_TSCHK +endif + + +# +# GPIF compile options +# +# CONFIG_MMTUNER_DPATH = GPIF : Use GPIF for DPATH +# CONFIG_MMTUNER_CPATH = GPIF : Use GPIF for CPATH +# CONFIG_MMTUNER_TSIF_GPIF_DMA : Use DMA transfer for DPATH +# +ifeq ($(CONFIG_MMTUNER_DPATH), GPIF) + OBJS += src/gpif.o + OBJS += src/gpmc.o + OBJS += src/tuner_drv_hw_gpif.o + subdir-ccflags-y += -DDPATH_GPIF +endif +ifeq ($(CONFIG_MMTUNER_CPATH), GPIF) + subdir-ccflags-y += -DCPATH_GPIF +endif +ifeq ($(CONFIG_MMTUNER_TSIF_GPIF_DMA), y) + subdir-ccflags-y += -DTUNER_CONFIG_GPIF_DMA +endif + +# +# Addtional libaray path is here +# +ifdef CONFIG_MMTUNER_LIBS + subdir-ccflags-y += -I${CONFIG_MMTUNER_LIBS} +endif + + + +$(DRV_NAME)-objs += $(OBJS) + +obj-$(CONFIG_MMTUNER_MN8855x) = $(DRV_NAME).o + +clean-files := *.o .*.cmd modules.builtin modules.order diff --git a/drivers/misc/mm_tuner/include/tuner_drv.h b/drivers/misc/mm_tuner/include/tuner_drv.h new file mode 100644 index 0000000000000000000000000000000000000000..146cf343886fa8db4a42f5890710d42679aa1c8f --- /dev/null +++ b/drivers/misc/mm_tuner/include/tuner_drv.h @@ -0,0 +1,170 @@ +/***************************************************************************//** + * + * @file tuner_drv.h + * + * @brief common header file for mm_tuner55x driver + * + ****************************************************************************//* + * Copyright (c) 2015 Socionext 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. + * + ******************************************************************************/ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ +/*..+....1....+....2....+....3....+....4....+....5....+....6....+....7....+...*/ +#ifndef _TUNER_DRV_H +#define _TUNER_DRV_H + +#ifdef DEBUG +#include +#define __file__ (strrchr(__FILE__, '/') + 1) +#else +#endif + +#include +#include +#include + +#include "tuner_drv_sys.h" + +/** + * Structure providing the version of the tuner driver runtime + */ +struct _mmtuner_version { + /** target device identifier + * + * Device id means two digits of bottom of type name. + */ + const uint8_t device; + + /** driver major version */ + const uint8_t major; + + /** driver minor version */ + const uint8_t minor; + + /** driver tinny version */ + const uint8_t hotfix; + + /** driver release candidate suffix string, e.g. "-rc4". */ + const char *rc; + + /** driver description */ + const char *describe; +}; + +/** + * MMTUNER context structure that contain some parameters and + * handle of the kernel threads. + */ +struct _mmtuner_cntxt { + /* wait queue for the poll/select system call */ + wait_queue_head_t poll_waitq; + /* exclusive control to access poll_flag */ + spinlock_t poll_lock; + /* status flag for poll/select */ + uint32_t poll_flag; + + /* ID of kernel thread that handle interrupts (IRQ) from tuner device */ + struct task_struct *irqth_id; + /* IRQ kernel thread wake up flag */ + uint32_t irqth_flag; + /* IRQ kernel thread wait queue */ + wait_queue_head_t irqth_waitq; + + /* to record the IRQ (event) factor */ + union _tuner_data_event ev; + + /* multiple open count */ + uint32_t opcnt; +}; + +struct _tsif_cntxt { + /* TS I/F Thread */ + struct task_struct *tsifth_id; /* Kernel thread ID */ + bool tsifth_wait; /* Wait indicator */ + uint32_t tsifth_flag; /* wake up flag */ + wait_queue_head_t tsifth_waitq; /* wait queue */ + + /* TS packet buffer */ + uint8_t *pktbuf; /* pointer to TS packet buffer */ + uint8_t *spibuf; /* pointer to SPI transfer*/ + uint32_t pwr; /* write position of TS packet buffer */ + uint32_t prd; /* read position of TS packet buffer */ + uint32_t ovf; /* packet buffer overflow counter */ + + enum _bw_seg bw; /* reception SEGment system */ + size_t ts_rxpkt_num; /* num of packets a RX transaction */ + size_t ts_rx_size; /* RX transaction byte size */ + size_t ts_pktbuf_size; /* TS receive buffer size */ + + /* read (TS data read) */ + uint32_t tsread_flag; /* wake up flag */ + wait_queue_head_t tsread_waitq; /* wait queue */ + + struct _tuner_data_tsif *tsif; +}; + +/* flag bit position of IRQ kernel thread */ +#define TUNER_IRQTH_NONE 0x00000000 /* initial flag */ +#define TUNER_IRQKTH_CATCHIRQ 0x00000001 /* interrupt flag position */ +#define TUNER_KTH_END 0x80000000 /* end flag position */ + +/* flag bit position of TS buffering thread */ +#define TUNER_TSIFTH_NONE 0x00000000 /* initial flag */ +#define TUNER_TSIFTH_ACTIVE 0x00000001 /* thread is working */ +#define TUNER_TSIFTH_END 0x80000000 /* end flag position */ + +#define TUNER_TSIFTH_FIFOERROR_MAX (10) + +/* TS I/F thread retry & wait time */ +#define TUNER_TSIFTH_SLEEP_RETRY (500) +#define TUNER_TSIFTH_SLEEP_MIN (1000) +#define TUNER_TSIFTH_SLEEP_MAX (2000) + +/* flag bit position of TS reading */ +#define TUNER_TSREAD_WAIT 0x00000000 /* wait */ +#define TUNER_TSREAD_IDLE 0x00000001 /* idle (initial) */ +#define TUNER_TSREAD_ACTIVE 0x00000002 /* active (reading) */ +#define TUNER_TSREAD_TIMEOUT 0x00000004 /* Timeout */ +#define TUNER_TSREAD_END 0x80000000 /* end */ + + +/* 4 : Use both IL=2 and IL=4 , then packet buffer is 1.0Mb (default)*/ +/* 2 : USE ONLY IL=2 , then expand packet buffer to 2.7Mb */ +/* Please set TUNER_TSPKTBUF_MODE to 2 +* when you don't need to Interleave mode 4 +*/ +#define TUNER_TSPKTBUF_MODE 4 + +/* Maximum packet buffer size is defined by MAX_TSPKTBUF_SIZE. +* Also it will be limited by MAX_TSPKTBUF_BANK. The size of +* packet buffer will be calculated by the below, +* maxbank = TUNER_MAX_TSPKTBUF_SIZE/lowerlimit; +* if ( maxbank > TUNER_MAX_TSPKTBUF_BANK ) maxbank = TUNER_MAX_TSPKTBUF_BANK; +* packet buffer size = tc->ts_rx_size) * maxbank; +*/ +#define TUNER_MAX_TSPKTBUF_SIZE 2097152 +#define TUNER_MAX_TSPKTBUF_BANK 12 + +extern irqreturn_t tuner_interrupt(int irq, void *dev_id); + +#endif /* _TUNER_DRV_H */ +/******************************************************************************* + * Copyright (c) 2015 Socionext Inc. + ******************************************************************************/ diff --git a/drivers/misc/mm_tuner/include/tuner_drv_hw.h b/drivers/misc/mm_tuner/include/tuner_drv_hw.h new file mode 100644 index 0000000000000000000000000000000000000000..8ebcc829a5106d10754dac39119042df8e4dfdd0 --- /dev/null +++ b/drivers/misc/mm_tuner/include/tuner_drv_hw.h @@ -0,0 +1,127 @@ +/**************************************************************************//** + * + * @file tuner_drv_hw.h + * + * @brief Common header file of the HardWare control layer. + * + ***************************************************************************//* + * Copyright (c) 2015 Socionext 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. + * + ******************************************************************************/ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ +/*..+....1....+....2....+....3....+....4....+....5....+....6....+....7....+...*/ +#ifndef _TUNER_DRV_HW_H +#define _TUNER_DRV_HW_H + +/****************************************************************************** + * include + ******************************************************************************/ +#include "tuner_drv.h" + +/****************************************************************************** + * prototype + ******************************************************************************/ + +/* following functions are described in "tuner_drv_hw.c" */ +int tuner_drv_hw_reqirq(void); +void tuner_drv_hw_freeirq(void); + +#ifdef TUNER_CONFIG_IRQ_LEVELTRIGGER + +void tuner_drv_hw_enable_interrupt(void); +void tuner_drv_hw_disable_interrupt(void); + +#endif + +/* + * following functions are described in "tuner_drv_.c" + */ +int tuner_drv_hw_read_reg( + enum _reg_bank bank, + uint8_t adr, + uint16_t len, + uint8_t *rd + ); +int tuner_drv_hw_write_reg( + enum _reg_bank bank, + uint8_t adr, + uint16_t len, + uint8_t *wd + ); +int tuner_drv_hw_rmw_reg( + enum _reg_bank bank, + uint8_t adr, + uint8_t mask, + uint8_t wd + ); +int tuner_drv_hw_setev(union _tuner_data_event *ev); +int tuner_drv_hw_relev(union _tuner_data_event *ev); + +#ifndef CPATH_I2C +int tuner_drv_hw_write_prg( + enum _reg_bank bank, + uint8_t adr, + uint16_t len, + uint8_t *wd + ); +#endif +#ifdef CPATH_I2C +int tuner_drv_hw_set_id(enum _tuner_cpathid cpath_id); +#endif +extern enum _tuner_cpathid cpath_id; + +/* + * following functions are described in "tuner_drv_hw_.c". + * is NOT "i2c". + */ +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) +int tuner_drv_hw_tsif_set_tpm(void); +int tuner_drv_hw_tsif_register(void); +void tuner_drv_hw_tsif_unregister(void); + +int tuner_drv_hw_tsif_set_cntxt(struct _tsif_cntxt *tc); +int tuner_drv_hw_tsif_config(struct _tsif_cntxt *tc); +int tuner_drv_hw_tsif_get_pkts(struct _tsif_cntxt *tc); +int tuner_drv_hw_tsif_get_dready(void); +int tuner_drv_hw_tsif_sync_pkt(void); + +/* +* Common Setting for slave IF +* These setting are only valid when DPATH is enabled. +*/ + +extern struct snglreg slvif_cfgregs[]; +/* register offset is below */ +#define SLVIF_CFG_SLVINTEN (0) +#define SLVIF_CFG_ISEGSEL (1) +#define SLVIF_CFG_DOSET4 (2) +#define SLVIF_CFG_WATERLINE (3) +#define SLVIF_CFG_BYTEORDER (4) +#define SLVIF_CFG_PKTSYNCC3 (5) +#define SLVIF_CFG_IFPWDSET1 (6) + +#endif + +extern int TUNER_CONFIG_INT; + +#endif /* _TUNER_DRV_HW_H */ +/******************************************************************************* + * Copyright (c) 2015 Socionext Inc. + ******************************************************************************/ diff --git a/drivers/misc/mm_tuner/include/version.h b/drivers/misc/mm_tuner/include/version.h new file mode 100644 index 0000000000000000000000000000000000000000..5d5df8484f449fbbf01173d4515215739dbbbbb7 --- /dev/null +++ b/drivers/misc/mm_tuner/include/version.h @@ -0,0 +1,60 @@ +/***************************************************************************//** + * + * @file version.h + * + * @brief define version of mm_tuner driver + * + ****************************************************************************//* + * Copyright (c) 2015 Socionext 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. + * + ******************************************************************************/ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ +/*..+....1....+....2....+....3....+....4....+....5....+....6....+....7....+...*/ +#ifndef _MMTUNER_VERSION_H +#define _MMTUNER_VERSION_H + +#ifndef MMTUENR_DEVICE +#define MMTUNER_DEVICE (0x53) /*!< Target device id. (MN885xx) */ +#endif +#ifndef MMTUNER_MAJOR +#define MMTUNER_MAJOR (0) +#endif +#ifndef MMTUNER_MINOR +#define MMTUNER_MINOR (4) +#endif +#ifndef MMTUNER_HOTFIX +#define MMTUNER_HOTFIX (1) +#endif +/* + * MMTUNER_RC is the release candidate suffix. + * Should normally be empty. + */ +#ifndef MMTUNER_RC +#define MMTUNER_RC "" +#endif + +#ifndef MMTUNER_DESC +#define MMTUNER_DESC "" +#endif + +#endif +/******************************************************************************* + * Copyright (c) 2015 Socionext Inc. + ******************************************************************************/ diff --git a/drivers/misc/mm_tuner/include_share/tuner_drv_config.h b/drivers/misc/mm_tuner/include_share/tuner_drv_config.h new file mode 100644 index 0000000000000000000000000000000000000000..b5116c8100c4cda6db56375ff4df301ca1d57a64 --- /dev/null +++ b/drivers/misc/mm_tuner/include_share/tuner_drv_config.h @@ -0,0 +1,156 @@ +/**************************************************************************//** + * + * @file tuner_drv_config.h + * + * @brief configuration header of the mm_tuner55x driver + * + ****************************************************************************//* + * Copyright (c) 2015 Socionext 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. + * + ******************************************************************************/ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ +/*..+....1....+....2....+....3....+....4....+....5....+....6....+....7....+...*/ +#ifndef _TUNER_DRV_CONFIG_H +#define _TUNER_DRV_CONFIG_H + +/****************************************************************************** + * define + ******************************************************************************/ +#define TUNER_SET_ON 1 /* setting ON */ +#define TUNER_SET_OFF 0 /* setting OFF */ + +/* device driver file name */ +#define TUNER_CONFIG_DRIVER_NAME "mmtuner" + +/* device number */ +#define TUNER_CONFIG_DRV_MAJOR 100 /* MAJOR No. */ +#define TUNER_CONFIG_DRV_MINOR 200 /* MINOR No. */ + +/* compile switch for IRQ */ +/* #define TUNER_CONFIG_IRQ_PC_LINUX */ + +/* IRQ */ +/* #define TUNER_CONFIG_IRQ_ENABLE */ /* System IRQ Enable */ +extern int TUNER_CONFIG_INT; + +/** + * IRQ kernel thread priority (0-99) + */ +#define TUNER_CONFIG_KTH_PRI 95 + +/* TS I/F kernel thread priority (0-99) */ +#define TUNER_CONFIG_TSBTH_PRI 95 + +/* exclusive access control */ +/* #define TUNER_CONFIG_DRV_MULTI */ + +/** + * @brief Compile switch for Device Tree. + * + * This definition should be enabled + * if the kernel does NOT support Device Tree. + */ +/* #define TUNER_CONFIG_NO_DEVICE_TREE */ + +/** + * @brief Compile switch for the SPI EDGE mode. + * + * Use SPI edge mode when using SPI slave I/F. + * Please define this macro TUNER_CONFIG_SPI_EDGE + * when you use 30MHz over SPI CLK. + */ +/* #define TUNER_CONFIG_SPI_EDGE */ + +/* @brief SPI calibration Timeout [ms]. */ +#define TUNER_CONFIG_CALIBRATION_TIMEOUT (1800000UL) + +/** + * @brief Compile switch for the SPI BREAK code at every SPI command. + * + * Send SPI BREAKCODE. + * Insert SPI break pattern before every SPI command when defined. + */ +/* #define TUNER_CONFIG_SPI_BREAKCODE */ + +/** + * @brief Compile switch for the SPI divide transaction mode. + * + * Divide SPI Packet Read command into 2 transactions + * (command phase and data phase). + */ +/* #define TUNER_CONFIG_SPI_DIV_MSG */ + +/** + * @brief Compile switch for the extend packet read mode. + * + * Use Extend Packet read with SPI slave I/F. + * We DON'T need to use this option usually. + */ +/* #define TUNER_CONFIG_SPI_EXTREAD */ + +/* @brief TS-Read timeout limit [ms]. */ +#define TUNER_CONFIG_TSREAD_TIMEOUT (1200UL) + + +/* Using DMA transfer when using GPIF I/F */ +/* #define TUNER_CONFIG_GPIF_DMA */ + +/* Enable AES on DPATH */ +/* #define TUNER_CONFIG_AES_ENABLE */ + +/* Enable TS packet when AES is on */ +/* #define TUNER_CONFIG_AES_TSCHK */ + +/* Enable PID NULL Packet filter */ +/* #define TUNER_CONFIG_PF_NULLFILTER */ + +/* Configurate byte order of TS stream via slave i/f */ +/* Plase set the following MACRO when you need the byte order transform */ +/* +-----------+------------+------------+-----------+------------* */ +/* | Slave | Size | MACRO | BYTEORDER | CPU ENDIAN | */ +/* +-----------+------------+------------+-----------+------------+ */ +/* | SPI Slave | 8 | Don't care | MSB First | BIG/LITTLE | */ +/* | SPI Slave | 32 | Undefined | LSB First | LITTLE | */ +/* | SPI Slave | 32 | Defined | MSB First | BIG | */ +/* +-----------+------------+------------+-----------+------------+ */ +/* | SDIO | Don't care | Don't care | MSB First | Don't care | */ +/* +-----------+------------+------------+-----------+------------+ */ +/* | GPIF | Don't care | Undefined | LSB First | Don't care | */ +/* | GPIF | Don't care | Defined | MSB First | Don't care | */ +/* +-----------+------------+------------+-----------+------------+ */ +/* LSB First Stream ( .. .. .. 0x47 .. .. ) */ +/* MSB First Stream ( 0x47 .. .. .. ) */ +/* The order of GPIF depends on memory controller */ +/* #define TUNER_CONFIG_SLV_MSBFIRST */ + + +/* DMA Boundary */ +/* Some SoC has the transfer size limitation to use DMA with SPI */ +/* It's limitation is that the data size must be align with 1K */ +/* So, when you want to use these SoC,please set the following MACRO */ +/* When SoC has 512byte boundary limitation,set to 512 */ +/* When SoC has 1024byte boundary limitation,set to 1024 */ +/* #define TUNER_CONFIG_SPI_ALIGN 1024 */ + + +#endif/* _TUNER_DRV_CONFIG_H */ +/******************************************************************************* + * Copyright (c) 2015 Socionext Inc. + ******************************************************************************/ diff --git a/drivers/misc/mm_tuner/include_share/tuner_drv_sys.h b/drivers/misc/mm_tuner/include_share/tuner_drv_sys.h new file mode 100644 index 0000000000000000000000000000000000000000..9ad47fda665e9869f930dc925e129b3637b15c35 --- /dev/null +++ b/drivers/misc/mm_tuner/include_share/tuner_drv_sys.h @@ -0,0 +1,179 @@ +/**************************************************************************//** + * + * @file tuner_drv_sys.h + * @brief The public header for the mm_tuner55x driver + * + ****************************************************************************//* + * Copyright (c) 2015 Socionext 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. + * + ******************************************************************************/ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ +/*..+....1....+....2....+....3....+....4....+....5....+....6....+....7....+...*/ +#ifndef _TUNER_DRV_SYS_H +#define _TUNER_DRV_SYS_H + +/****************************************************************************** + * include + ******************************************************************************/ +#include "tuner_drv_config.h" + +/****************************************************************************** + * define + ******************************************************************************/ +/* IOCTL parameters */ +#define TUNER_IOC_MAGIC 'd' +#define TUNER_IOCTL_VALGET _IOWR(TUNER_IOC_MAGIC, 1, union _tuner_data_rw) +#define TUNER_IOCTL_VALSET _IOW(TUNER_IOC_MAGIC, 2, union _tuner_data_rw) +#define TUNER_IOCTL_EVENT_GET _IOWR(TUNER_IOC_MAGIC, 5, union _tuner_data_event) +#define TUNER_IOCTL_EVENT_SET _IOW(TUNER_IOC_MAGIC, 6, union _tuner_data_event) +#define TUNER_IOCTL_EVENT_REL _IOW(TUNER_IOC_MAGIC, 7, union _tuner_data_event) +#define TUNER_IOCTL_CNTSET _IOW(TUNER_IOC_MAGIC, 3, union _tuner_data_rw) +#define TUNER_IOCTL_CNTGET _IOWR(TUNER_IOC_MAGIC, 4, union _tuner_data_rw) +#define TUNER_IOCTL_TSIF_START _IOW(TUNER_IOC_MAGIC, 10,\ + struct _tuner_data_tsif) +#define TUNER_IOCTL_TSIF_STOP _IO(TUNER_IOC_MAGIC, 11) +#define TUNER_IOCTL_TSIF_PKTSIZE _IOR(TUNER_IOC_MAGIC, 12, unsigned int) +#define TUNER_IOCTL_GETVER _IOR(TUNER_IOC_MAGIC, 19, unsigned int) +#define TUNER_IOCTL_CPATHID _IOW(TUNER_IOC_MAGIC, 20, enum _tuner_cpathid) +#define TUNER_IOCTL_NRST_CTL _IOW(TUNER_IOC_MAGIC, 30, unsigned int) + +#define TUNER_IOCTL_TSIF_BLKSIZE _IOR(TUNER_IOC_MAGIC, 64, int) +#define TUNER_IOCTL_TSIF_GET_IV _IOR(TUNER_IOC_MAGIC, 65, uint8_t) +#define TUNER_IOCTL_TSIF_INIT_IV _IOW(TUNER_IOC_MAGIC, 66, uint8_t) + + +/****************************************************************************** + * enumerator type + ******************************************************************************/ +/** @addtogroup group_libtuner_API_public */ + +/** @brief Register bank enumerator */ +enum _reg_bank { + Sub = 0, /*!< Register bank in RF circuit */ + Main1 = 1, /*!< Register bank in the 13(full)-segment demodulator */ + Main2 = 2, /*!< Register bank in the 1-segment dedicated demodulator */ +#ifdef __KERNEL__ + SSub = 3, + SMain1 = 4, + SMain2 = 5, + END_SLVCFG = 255 /* End Mark */ +#endif +}; + +/** @brief Event (interrupt) setting mode */ +enum _evset_mode { + /** Add the specified event definition to existence definitions */ + TUNER_EVENT_MODE_ADD = 0, + /** Clear existence definitions and set specified definitions only */ + TUNER_EVENT_MODE_OVW = 1, +}; + +/** @brief OFDM demodulator circuit enumerators. */ +enum _bw_seg { + TUNER_DRV_BW13 = 0, /*!< 13(full)-seg. reception. */ + TUNER_DRV_BW1 = 1, /*!< 1-segment reception. */ + TUNER_DRV_BW3 = 2, /*!< 3-segment reception. */ +}; + +/** @brief TS packet type enumerator */ +enum _ts_pkt_type { + TUNER_DRV_TS_NORMAL = 0, /*!< 188 bytes length */ + TUNER_DRV_TS_ADDFEC = 1, /*!< 204 bytes length (with redundancy bits) */ + TUNER_DRV_TS_TSTAMP = 2, /*!< 192 bytes length (with Time-Stamp) */ +}; +/** @} */ +enum _tuner_cpathid { + DVR_MASTER = 0, + DVR_SLAVE = 3, +}; + +/****************************************************************************** + * struct/union type + ******************************************************************************/ +/* for register read/write */ +/* @brief Structure to write some (masked) bits of a register. */ +struct snglreg { + enum _reg_bank bank; /*!< Register bank. */ + uint8_t adr; /*!< Offset address in a bank. */ + uint8_t enabit; /*!< bit-mask specifies some bits of a register. */ + uint8_t param; /*!< read/write data. */ +}; + +union _tuner_data_rw { + struct snglreg sngl; + struct { + enum _reg_bank bank; /* reg. bank */ + uint8_t adr; /* reg. address */ + uint16_t len; /* continuous length */ + uint8_t *buf; /* buffer for continuous read/write */ + } cont; +}; + +/** @addtogroup group_libtuner_API_public */ + +/** @brief Union to set/get the event (interrupt) definitions/factors */ +union _tuner_data_event { + struct { + unsigned intst: 8; /*!< INTST register */ + unsigned intcnd: 8; /*!< INTCND register */ + unsigned intset1: 8; /*!< INTSET1 register */ + unsigned irqnum: 8; /*!< Num of the IRQ */ + } get; /*!< To access as the event (interrupt) factor */ + struct { + unsigned intdef2: 4; /*!< INTDEF2[3:0] register */ + unsigned reserve: 4; /*!< reserve bits */ + unsigned intdef1: 8; /*!< INTDEF1 register */ + unsigned intset1: 8; /*!< INTSET1 register */ + unsigned mode: 4; /*!< ::_evset_mode */ + unsigned reserve1: 4; /*!< reserve bits */ + } set; /*!< To access as the event (interrupt) definition */ + /** @brief For the Event Enumerator (::_tuner_event) */ + uint32_t pack; +}; + +/** @brief Event (interrupt) definition/factor type */ + +/** @brief TS slave I/F control parameters + * + * Control TS slave I/F sub-system of the tuner device. + */ +struct _tuner_data_tsif { + uint8_t dwind[3]; /*!< Data window width */ + uint8_t thl[3]; /*!< Water line lower threshold */ + + enum _ts_pkt_type ts_pkt_type; /*!< TS packet type */ + + /* AES configuration */ + uint8_t aes_mode; + + /* depend on the SPI (HOST-Master) I/F */ + uint8_t spi_ts_bit_per_word; /*!< Bits per a word of SPI I/F */ + /*1: calibration execute when tuner_drv_hw_tsif_get_pkts called. */ + /*0: calibration off. */ + uint8_t spi_calibration_flag; +}; +/** @brief TS slave I/F control type */ + +/** @} */ + +#endif/* _TUNER_DRV_SYS_H */ +/******************************************************************************* + * Copyright (c) 2015 Socionext Inc. + ******************************************************************************/ diff --git a/drivers/misc/mm_tuner/src/tuner_drv.c b/drivers/misc/mm_tuner/src/tuner_drv.c new file mode 100644 index 0000000000000000000000000000000000000000..5f8a3237ebe63c994197e539ad1f2705c3392002 --- /dev/null +++ b/drivers/misc/mm_tuner/src/tuner_drv.c @@ -0,0 +1,1952 @@ +/**************************************************************************//** + * + * @file tuner_drv.c + * + * @brief The implementation that is independence in physical I/F. + * + ***************************************************************************//* + * Copyright (c) 2015 Socionext 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. + * + ******************************************************************************/ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ +/*..+....1....+....2....+....3....+....4....+....5....+....6....+....7....+...*/ + +/****************************************************************************** + * include + ******************************************************************************/ +#include "tuner_drv.h" +#include "tuner_drv_hw.h" +#include "version.h" + +#include +#include +#include +#include +#include +/* For Timeout procedure */ +#include +#include + +#if defined(DPATH_GPIF) +/* For DMA Mapping for gpif */ +#include +#endif + +#include +#include +#include +#include + +#define D_TUNER_POWER_ON_WAIT_US 10000 /* 10ms */ +#define D_TUNER_POWER_ON_WAIT_RANGE_US 10100 +#define D_TUNER_NRST_WAIT_US 10000 /* 10ms */ +#define D_TUNER_NRST_WAIT_RANGE_US 10100 +#define D_TUNER_WAIT_1SEG_MS 10 + +#define D_TUNER_CONFIG_PLATFORM_DRIVER_NAME "mmtuner_pdev" +#define D_TUNER_CONFIG_SYSFS_DEV_NAME "mmtuner_pdev" +#define D_TUNER_CONFIG_MATCH_TABLE "socionext,mn88553" + +enum gpio_id { + TUNER_POWER_PIN = 0, + TUNER_RESET_PIN, + TUNER_INT_PIN, +}; + +static char const * const gpio_rsrcs[] = { + "ISDB-T tuner power", + "ISDB-T tuner reset", + "ISDB-T tuner int", +}; + +enum TUNER_DRV_CTL { + TUNER_DRV_CTL_POWON, + TUNER_DRV_CTL_POWOFF +}; + +enum TUNER_DTV_SIG_LEVEL { + TUNER_DRV_SIG_L, + TUNER_DRV_SIG_H +}; + +struct tuner_drvdata { + struct device *dev; + struct device sysfs_dev; + struct mutex mutex_lock; + unsigned int gpios[ARRAY_SIZE(gpio_rsrcs)]; +}; + +struct g_tuner_device { + struct mutex g_tuner_mutex; + unsigned int gpios[ARRAY_SIZE(gpio_rsrcs)]; +}; +static struct g_tuner_device tnr_dev; + +static enum gpio_id req_ids[] = { + TUNER_POWER_PIN, + TUNER_RESET_PIN, + TUNER_INT_PIN, +}; + +/****************************************************************************** + * variables + ******************************************************************************/ + +/****************************************************************************** + * data + ******************************************************************************/ +static struct _mmtuner_cntxt g_cnt; /* mm_tuner driver context */ + +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) +static struct _tsif_cntxt g_tscnt; /* TS I/F context */ +#endif + +/** @brief TS packet size. + * + * Array index of this constant array is + * equivalent to ::_ts_pkt_type (enumeration type). + */ +static const size_t g_ts_pkt_size[3] = { 188, 204, 192 }; + +static const struct _mmtuner_version mmtuner_version = { + MMTUNER_DEVICE, + MMTUNER_MAJOR, MMTUNER_MINOR, MMTUNER_HOTFIX, + MMTUNER_RC, MMTUNER_DESC +}; + +enum _tuner_cpathid cpath_id = DVR_MASTER; +int TUNER_CONFIG_INT; + +/****************************************************************************** + * function + ******************************************************************************/ +static ssize_t tuner_module_entry_read(struct file *FIle, char __user *Buffer, + size_t Count, loff_t *OffsetPosition); +static ssize_t tuner_module_entry_write(struct file *FIle, + const char __user *Buffer, size_t Count, + loff_t *OffsetPosition); +#ifdef TUNER_CONFIG_IRQ_ENABLE +static unsigned int tuner_module_entry_poll(struct file *file, + struct poll_table_struct *poll_tbl); +#endif + +static long tuner_module_entry_ioctl(struct file *file, + unsigned int uCommand, + unsigned long uArgument); + +static int tuner_module_entry_open(struct inode *Inode, struct file *FIle); +static int tuner_module_entry_close(struct inode *Inode, struct file *FIle); +static int tuner_probe(struct platform_device *pdev); +static int __exit tuner_remove(struct platform_device *pdev); +static int tuner_pm_suspend(struct device *dev); +static int tuner_pm_resume(struct device *dev); +static int __init tuner_drv_start(void); +static void __exit tuner_drv_end(void); +#ifdef TUNER_CONFIG_IRQ_ENABLE +static int tuner_drv_irq_th(void *arc); +#endif +static int tuner_drv_read_regs(union _tuner_data_rw *rw, int num); +static int tuner_drv_write_regs(union _tuner_data_rw *rw, int num); + +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) +static int tuner_drv_tsif_set_cntxt(struct _tsif_cntxt *tc); +static inline int tuner_drv_bwseg(enum _bw_seg *pbw); +static int tuner_drv_tsif_start(void); +static int tuner_drv_tsif_stop(void); +static int tuner_drv_tsif_pktsize(void); +static int tuner_drv_tsif_blksize(void); +static int tuner_drv_tsif_th(void *arg); + +static void tsread_timer_handler(unsigned long data); +static struct timer_list tsread_timer; +#endif + +/* entry point */ +static const struct file_operations TunerFileOperations = { + .owner = THIS_MODULE, + .read = tuner_module_entry_read, + .write = tuner_module_entry_write, +#ifdef TUNER_CONFIG_IRQ_ENABLE + .poll = tuner_module_entry_poll, +#endif + .unlocked_ioctl = tuner_module_entry_ioctl, + .open = tuner_module_entry_open, + .release = tuner_module_entry_close +}; + +static const struct dev_pm_ops mmtuner_driver_pm_ops = { + .suspend = tuner_pm_suspend, + .resume = tuner_pm_resume, +}; + +static const struct of_device_id mn88553_match_table[] = { +{ .compatible = D_TUNER_CONFIG_MATCH_TABLE, +}, +{} +}; + +static struct platform_driver mmtuner_driver = { + .probe = tuner_probe, + .remove = __exit_p(tuner_remove), + .driver = { + .name = D_TUNER_CONFIG_PLATFORM_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mn88553_match_table), + } +}; + +static struct platform_device *mmtuner_device; +static struct class *device_class; + +/****************************************************************************** + * code area + ******************************************************************************/ + +static void dtv_tunerpm_power_control(struct tuner_drvdata *drvdata, int on) +{ + gpio_set_value_cansleep(drvdata->gpios[TUNER_POWER_PIN], on); +} + +static void dtv_tuner_reset_control(struct g_tuner_device *a_tnr_dev, + int on_off) +{ + gpio_set_value_cansleep(a_tnr_dev->gpios[TUNER_RESET_PIN], on_off); +} + +static int tuner_drv_ctl_reset(struct g_tuner_device *a_tnr_dev, int data) +{ + int ret = 0; + + pr_debug("%s,sig=%d\n", __func__, data); + + switch (data) { + /* reset */ + case TUNER_DRV_SIG_L: + mutex_lock(&a_tnr_dev->g_tuner_mutex); + dtv_tuner_reset_control(a_tnr_dev, 0); + mutex_unlock(&a_tnr_dev->g_tuner_mutex); + usleep_range(D_TUNER_NRST_WAIT_US, + D_TUNER_NRST_WAIT_RANGE_US); + break; + /* reset off*/ + case TUNER_DRV_SIG_H: + mutex_lock(&a_tnr_dev->g_tuner_mutex); + dtv_tuner_reset_control(a_tnr_dev, 1); + mutex_unlock(&a_tnr_dev->g_tuner_mutex); + usleep_range(D_TUNER_NRST_WAIT_US, + D_TUNER_NRST_WAIT_RANGE_US); + break; + default: + return -EINVAL; + } + return ret; +} + +static int tuner_drv_ctl_power(struct tuner_drvdata *drvdata, int data) +{ + int ret = 0; + + pr_debug("%s,data=%d\n", __func__, data); + + switch (data) { + case TUNER_DRV_CTL_POWON: + mutex_lock(&drvdata->mutex_lock); + dtv_tunerpm_power_control(drvdata, 1); + mutex_unlock(&drvdata->mutex_lock); +#ifdef CONFIG_CONTROL_NPDLOG + /* if npdlog is used, waiting is necessary. */ + usleep_range(D_TUNER_POWER_ON_WAIT_US, + D_TUNER_POWER_ON_WAIT_RANGE_US); +#endif + break; + + case TUNER_DRV_CTL_POWOFF: + mutex_lock(&drvdata->mutex_lock); + dtv_tunerpm_power_control(drvdata, 0); + mutex_unlock(&drvdata->mutex_lock); + break; + default: + return -EINVAL; + } + return ret; +} + +static int tunerpm_dev_init(struct platform_device *pdev, + struct tuner_drvdata *drvdata) +{ + int i, ret, gpio; + unsigned int flags; + struct device_node *of_node = pdev->dev.of_node; + + mutex_init(&drvdata->mutex_lock); + + for (i = 0; i < ARRAY_SIZE(gpio_rsrcs); i++) { + gpio = of_get_gpio_flags(of_node, i, &flags); + + pr_debug("%s,%d\n", __func__, gpio); + + if (!gpio_is_valid(gpio)) { + ret = -EINVAL; + goto error_gpio; + } + drvdata->gpios[i] = gpio; + tnr_dev.gpios[i] = gpio; + } + + for (i = 0; i < ARRAY_SIZE(req_ids); i++) { + + ret = gpio_request(drvdata->gpios[req_ids[i]], + gpio_rsrcs[req_ids[i]]); + + if (ret) + goto error_gpio_request; + } + + TUNER_CONFIG_INT = gpio_to_irq(drvdata->gpios[TUNER_INT_PIN]); + tuner_drv_ctl_reset(&tnr_dev, (int)TUNER_DRV_SIG_L); + tuner_drv_ctl_power(drvdata, TUNER_DRV_CTL_POWOFF); + + return 0; + +error_gpio_request: + for (i--; i >= 0; i--) + gpio_free(drvdata->gpios[req_ids[i]]); +error_gpio: + return ret; +} + +static ssize_t tuner_module_power_ctrl(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct tuner_drvdata *drvdata = dev_get_drvdata(dev); + unsigned long value; + int ret; + + pr_debug("%s\n", __func__); + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + if (!value) { + ret = tuner_drv_ctl_power(drvdata, TUNER_DRV_CTL_POWON); + if (ret) + return -EINVAL; + } else { + tuner_drv_ctl_power(drvdata, TUNER_DRV_CTL_POWOFF); + } + + return count; +} + +static struct device_attribute tuner_sysfs_attrs[] = { + __ATTR(power_ctrl, S_IWUSR, 0, tuner_module_power_ctrl), +}; + +/**************************************************************************//** + * probe control of a driver + * + * @retval 0 normal + * @retval <0 error + * + * @param [in] pdev pointer to the structure "platform_device" + ******************************************************************************/ +static int tuner_probe(struct platform_device *pdev) +{ + int ret = 0, i, retval = 0; + struct tuner_drvdata *drvdata; + struct device *dev = NULL; + + pr_debug("%s-S,%p\n", __func__, pdev); + + /* memory allocation */ + mmtuner_device = platform_device_alloc(TUNER_CONFIG_DRIVER_NAME, -1); + + if (!mmtuner_device) { + pr_err("platform_device_alloc() failed.\n"); + return -ENOMEM; + } + + /* add device */ + ret = platform_device_add(mmtuner_device); + if (ret) { + pr_err("platform_device_add() failed.\n"); + retval = ret; + goto err_platform_device_add; + } + + /* create the node of device */ + device_class = class_create(THIS_MODULE, TUNER_CONFIG_DRIVER_NAME); + if (IS_ERR(device_class)) { + pr_err("class_create() failed.\n"); + retval = PTR_ERR(device_class); + goto err_class_create; + } + + /* create the logical device */ + dev = device_create(device_class, NULL, + MKDEV(TUNER_CONFIG_DRV_MAJOR, TUNER_CONFIG_DRV_MINOR), NULL, + TUNER_CONFIG_DRIVER_NAME); + if (IS_ERR(dev)) { + pr_err("device_create() failed.\n"); + retval = PTR_ERR(dev); + goto err_device_create; + } + + drvdata = kzalloc(sizeof(struct tuner_drvdata), GFP_KERNEL); + if (!drvdata) { + retval = -ENOMEM; + goto err_alloc_data; + } + + drvdata->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, drvdata); + drvdata->sysfs_dev.init_name = D_TUNER_CONFIG_SYSFS_DEV_NAME; + dev_set_drvdata(&drvdata->sysfs_dev, drvdata); + + ret = device_register(&drvdata->sysfs_dev); + if (ret) { + retval = -EINVAL; + goto err_set_dev; + } + + /* register the driver */ + if (register_chrdev(TUNER_CONFIG_DRV_MAJOR, TUNER_CONFIG_DRIVER_NAME, + &TunerFileOperations)) { + pr_err("register_chrdev() failed (Major:%d).\n", + TUNER_CONFIG_DRV_MAJOR); + retval = -EINVAL; + goto err_register_device; + } + + mutex_init(&tnr_dev.g_tuner_mutex); + ret = tunerpm_dev_init(pdev, drvdata); + if (ret) { + retval = -EINVAL; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(tuner_sysfs_attrs); i++) { + ret = device_create_file(&drvdata->sysfs_dev, + &tuner_sysfs_attrs[i]); + if (ret) { + for (; i >= 0; --i) + device_remove_file(&drvdata->sysfs_dev, + &tuner_sysfs_attrs[i]); + retval = -EINVAL; + goto out; + } + } + +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) + /* register the TS I/F driver */ + ret = tuner_drv_hw_tsif_register(); + if (ret) { + pr_err("tuner_drv_hw_tsif_register() failed.\n"); + retval = ret; + goto out; + } +#endif + + /* dispatch the kernel thread to handle the interrupt (IRQ) */ +#ifdef TUNER_CONFIG_IRQ_ENABLE + g_cnt.irqth_flag = TUNER_IRQTH_NONE; + init_waitqueue_head(&g_cnt.irqth_waitq); + g_cnt.irqth_id = kthread_create(tuner_drv_irq_th, NULL, + "tuner_drv_irq_th"); + if (IS_ERR(g_cnt.irqth_id)) { + retval = PTR_ERR(g_cnt.irqth_id); + g_cnt.irqth_id = NULL; + goto out; + } + + init_waitqueue_head(&g_cnt.poll_waitq); + spin_lock_init(&g_cnt.poll_lock); + g_cnt.poll_flag = 0x00; + + g_cnt.ev.pack = 0; + g_cnt.opcnt = 0; + + wake_up_process(g_cnt.irqth_id); +#endif + +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) + /* dispatch a kernel thread to receive TS data */ + g_tscnt.tsifth_wait = true; + g_tscnt.tsifth_flag = TUNER_TSIFTH_NONE; + init_waitqueue_head(&g_tscnt.tsifth_waitq); + g_tscnt.tsifth_id = kthread_create(tuner_drv_tsif_th, NULL, + "tuner_drv_tsif_th"); + if (IS_ERR(g_tscnt.tsifth_id)) { + pr_err("kthread_create() failed.\n"); + retval = PTR_ERR(g_tscnt.tsifth_id); + g_tscnt.tsifth_id = NULL; + goto out; + } + + pr_debug("%s, kthread_create(tuner_drv_tsif_th)-after debug\n", + __func__); + + g_tscnt.pktbuf = NULL; + g_tscnt.spibuf = NULL; + g_tscnt.pwr = g_tscnt.prd = 0; + g_tscnt.ovf = 0; + + wake_up_process(g_tscnt.tsifth_id); + + /* initialize the status flag and + *the wait-queue for Ts Buffering Thread + */ + g_tscnt.tsread_flag = TUNER_TSREAD_IDLE; + init_waitqueue_head(&g_tscnt.tsread_waitq); +#endif + + pr_debug("%s-E\n", __func__); + return 0; + +out: + unregister_chrdev(TUNER_CONFIG_DRV_MAJOR, + TUNER_CONFIG_DRIVER_NAME); +err_register_device: + device_unregister(&drvdata->sysfs_dev); +err_set_dev: + kzfree(drvdata); +err_alloc_data: + device_destroy(device_class, MKDEV(TUNER_CONFIG_DRV_MAJOR, + TUNER_CONFIG_DRV_MINOR)); +err_device_create: + class_destroy(device_class); +err_class_create: + platform_device_del(mmtuner_device); +err_platform_device_add: + platform_device_put(mmtuner_device); + return retval; +} + +/**************************************************************************//** + * remove control of a driver + * + * @retval 0 normal + * @retval <0 error + * + * @param [in] pdev pointer to the structure "platform_device" + ******************************************************************************/ +static int __exit tuner_remove(struct platform_device *pdev) +{ + pr_debug("%s\n", __func__); + + /* release interrupt (IRQ) */ + tuner_drv_hw_freeirq(); + + /* un-register driver */ + unregister_chrdev(TUNER_CONFIG_DRV_MAJOR, TUNER_CONFIG_DRIVER_NAME); + + return 0; +} + +/*************************************************************************//*** + * suspend control of a driver + * + * @retval 0 normal + * @retval <0 error + * + * @param [in] dev device + ******************************************************************************/ +static int tuner_pm_suspend(struct device *dev) +{ + pr_debug("%s\n", __func__); + + return 0; +} + +/*************************************************************************//*** + * resume control of a driver + * + * @retval 0 normal + * @retval <0 error + * + * @param [in] dev device + ******************************************************************************/ +static int tuner_pm_resume(struct device *dev) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) + ret = tuner_drv_hw_tsif_set_tpm(); + if (ret) { + pr_err("write TPM bits failed.\n"); + return ret; + } +#endif + return ret; +} + +/**************************************************************************//** + * Interrupt handling thread function + * + * @retval 0 normal + * @retval <0 error + * + * @param [in] arg (no effect) + ******************************************************************************/ +#ifdef TUNER_CONFIG_IRQ_ENABLE +static int tuner_drv_irq_th(void *arg) +{ + int ret = 0; + unsigned long flags; + unsigned long kthread_flg; + + mm_segment_t oldfs; + struct sched_param param; + + uint8_t buf[2] = { 0x00, 0x00 }; + + pr_debug("%s\n", __func__); + + /* initialize inner values */ + ret = 0; + flags = 0; + kthread_flg = 0; + param.sched_priority = TUNER_CONFIG_KTH_PRI; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = sched_setscheduler(g_cnt.irqth_id, SCHED_FIFO, ¶m); + set_fs(oldfs); + + while (1) { + buf[0] = buf[1] = 0; + + pr_debug("%s waiting...\n", __func__); + wait_event_interruptible(g_cnt.irqth_waitq, g_cnt.irqth_flag); + + spin_lock_irqsave(&g_cnt.poll_lock, flags); + kthread_flg = g_cnt.irqth_flag; + g_cnt.irqth_flag &= ~TUNER_IRQKTH_CATCHIRQ; + spin_unlock_irqrestore(&g_cnt.poll_lock, flags); + + if ((kthread_flg & TUNER_IRQKTH_CATCHIRQ) == + TUNER_IRQKTH_CATCHIRQ) { + /* receive IRQ */ + pr_debug("%s IRQHANDLER start\n", __func__); + + /* Read INTCND, INTST */ + ret = tuner_drv_hw_read_reg( + Main1, + 0xE3, + 2, + buf); + if (ret) { + pr_err("Read IRQ factor/state, failed.\n"); + kthread_flg |= TUNER_KTH_END; + } + /* write (back) INTCND to clear + * the factor (register) of interrupt (IRQ) + */ + ret = tuner_drv_hw_write_reg( + Main1, + 0xE3, + 1, + buf); + if (ret) { + pr_err("Clear IRQ factor, failed."); + kthread_flg |= TUNER_KTH_END; + } + + g_cnt.ev.get.intcnd |= buf[0]; + g_cnt.ev.get.intst |= buf[1]; + g_cnt.ev.get.irqnum++; + + pr_debug("IRQ factor update (%d): INTCND:0x%02x INTST:0x%02x\n", + g_cnt.ev.get.irqnum, g_cnt.ev.get.intcnd, + g_cnt.ev.get.intst); + + /* release poll/select */ + g_cnt.poll_flag = 0x01; + wake_up(&g_cnt.poll_waitq); + + pr_debug("%s main loop go to next turn.\n", __func__); + +#ifdef TUNER_CONFIG_IRQ_LEVELTRIGGER + /* activate (re-activate) the interrupt, + *if the level-interrupt is active + */ + tuner_drv_hw_enable_interrupt(); +#endif /* TUNER_CONFIG_IRQ_LEVELTRIGGER */ + } + + /* request to finish the interrupt kernel thread */ + if ((kthread_flg & TUNER_KTH_END) == TUNER_KTH_END) { + pr_debug("%s caught a stop request.\n", __func__); + + spin_lock_irqsave(&g_cnt.poll_lock, flags); + g_cnt.irqth_flag &= ~TUNER_KTH_END; + spin_unlock_irqrestore(&g_cnt.poll_lock, flags); + + break; + } + } + + pr_debug("Tail of thread function %s.\n", __func__); + return 0; +} +#endif + +/**************************************************************************//** + * initialization control of a driver + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +static int __init tuner_drv_start(void) +{ + int ret = 0; + + pr_debug("%s-S\n", __func__); + + pr_debug("mmtuner (for MN885%02x) ver. %d.%d.%d %s (%s)\n", + mmtuner_version.device, mmtuner_version.major, + mmtuner_version.minor, mmtuner_version.hotfix, + mmtuner_version.rc, mmtuner_version.describe); + + /* register "mmtuner" driver */ + ret = platform_driver_register(&mmtuner_driver); + if (ret != 0) { + pr_err("platform_driver_register failed.\n"); + return ret; + } + + pr_debug("%s-E\n", __func__); + + return 0; /* normal */ +} + +/**************************************************************************//** + * exit control of a driver + * + ******************************************************************************/ +static void __exit tuner_drv_end(void) +{ + pr_debug("%s\n", __func__); + +#ifdef TUNER_CONFIG_IRQ_ENABLE + g_cnt.irqth_flag |= TUNER_KTH_END; + /* exit the kernel thread (for IRQ) */ + if (waitqueue_active(&g_cnt.irqth_waitq)) + wake_up(&g_cnt.irqth_waitq); + + /* unregister the IRQ kernel thread */ + if (g_cnt.irqth_id) + kthread_stop(g_cnt.irqth_id); +#endif + +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) + g_tscnt.tsifth_flag = TUNER_TSIFTH_END; + /* exit the kernel thread (for TS) */ + if (waitqueue_active(&g_tscnt.tsifth_waitq)) + wake_up_interruptible(&g_tscnt.tsifth_waitq); + + g_tscnt.tsifth_wait = true; + + /* unregister the TS kernel thread */ + if (g_tscnt.tsifth_id) + kthread_stop(g_tscnt.tsifth_id); + + /* execute the unregister scheme of TS I/F */ + tuner_drv_hw_tsif_unregister(); +#endif + + /* Destroy device */ + device_destroy(device_class, + MKDEV(TUNER_CONFIG_DRV_MAJOR, TUNER_CONFIG_DRV_MINOR)); + /* delete a entry of class */ + class_destroy(device_class); + /* unregister the driver */ + platform_device_unregister(mmtuner_device); + /* unregister the platform entry */ + platform_driver_unregister(&mmtuner_driver); + +} + +/**************************************************************************//** + * open control of a driver + * + * @retval 0 normal + * @retval <0 error + * + * @param [in] Inode, FIle + ******************************************************************************/ +static int tuner_module_entry_open(struct inode *Inode, struct file *FIle) +{ + pr_debug("%s\n", __func__); + +#ifndef TUNER_CONFIG_DRV_MULTI + if (g_cnt.opcnt > 0) { + pr_err("It's being used.\n"); + return -EBUSY; + } +#endif /* TUNER_CONFIG_DRV_MULTI */ + + g_cnt.opcnt++; + pr_debug("user count: %d\n", g_cnt.opcnt); + + return 0; +} + +/**************************************************************************//** + * close control of a driver + * + * @retval 0 normal + * @retval <0 error + * + * @param [in] Inode, FIle regular argument for linux system call + ******************************************************************************/ +static int tuner_module_entry_close(struct inode *Inode, struct file *FIle) +{ + struct devone_data *dev; + + pr_debug("%s\n", __func__); + + if (g_cnt.opcnt == 0) { /* not open */ + pr_err("NOT IN USE.\n"); + return -ENODEV; + } + g_cnt.opcnt--; + + if (g_cnt.opcnt == 0) { /* All logical device is closed. */ + /* free IRQ */ + tuner_drv_hw_freeirq(); + if (FIle == NULL) + return -EINVAL; + dev = FIle->private_data; + kfree(dev); + } + + return 0; +} + +/**************************************************************************//** + * @brief "read" system call of mm_tuner device + * + * The "read" system-call acquires the TS data stream of + * the designated byte size. + * + * @retval >=0 normal (byte size) + * @retval <0 error + ******************************************************************************/ +static ssize_t tuner_module_entry_read(struct file *FIle, char __user *Buffer, + size_t Count, loff_t *OffsetPosition) +{ +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) + ssize_t size = 0; + int ret; + int copy_size; + + if (!Buffer) { + pr_err("arg. \"Buffer\" is Null\n"); + return -EINVAL; + } + if (Count <= 0) { + pr_err("arg. \"Count\" is illegal (%zu)\n", Count); + return -EINVAL; + } + + if (g_tscnt.tsifth_wait) { + pr_warn("TS I/F thread is NOT active (waiting...)\n"); + return -EAGAIN; + } + if (g_tscnt.tsread_flag != TUNER_TSREAD_IDLE) { + pr_warn("read (TS) system call is executed in duplicate flag:%d\n", + g_tscnt.tsread_flag); + return -EBUSY; + } + g_tscnt.tsread_flag = TUNER_TSREAD_ACTIVE; + + /* clear the over-flow flag of the packet buffer */ + g_tscnt.ovf = 0; + + do { + int able_size = 0; + + /* TS FIFO is empty */ + if (g_tscnt.prd == g_tscnt.pwr) { + /* TS-Read timer */ + init_timer(&tsread_timer); + tsread_timer.expires = jiffies + + msecs_to_jiffies(TUNER_CONFIG_TSREAD_TIMEOUT); + tsread_timer.data = (unsigned long)jiffies; + tsread_timer.function = tsread_timer_handler; + add_timer(&tsread_timer); + + g_tscnt.tsread_flag = TUNER_TSREAD_WAIT; + usleep_range(100, 500); + wait_event_interruptible(g_tscnt.tsread_waitq, + g_tscnt.tsread_flag); + + del_timer(&tsread_timer); + if (g_tscnt.tsread_flag != TUNER_TSREAD_IDLE) { + pr_warn("Stop reading TS data.\n"); + pr_debug("TS-Read status flag: 0x%08x.\n", + g_tscnt.tsread_flag); + break; + } + g_tscnt.tsread_flag = TUNER_TSREAD_ACTIVE; + continue; + } + + /* readable data size of TS FIFO */ + able_size = (g_tscnt.pwr > g_tscnt.prd) ? + (g_tscnt.pwr - g_tscnt.prd) : + (g_tscnt.ts_pktbuf_size - g_tscnt.prd); + + /* decide copy_size */ + copy_size = ((size + able_size) > Count) ? + (Count - size) : + able_size; + ret = copy_to_user( + Buffer + size, + g_tscnt.pktbuf + g_tscnt.prd, + copy_size); + if (ret) { + pr_err("copy_to_user() failed.\n"); + return -EFAULT; + } + + /* increment read position */ + g_tscnt.prd += copy_size; + if (g_tscnt.prd == g_tscnt.ts_pktbuf_size) + g_tscnt.prd = 0; + + /* increment total copied size */ + size += copy_size; + + } while (size != Count); + + g_tscnt.tsread_flag = TUNER_TSREAD_IDLE; + + return size; +#else + pr_warn("NOT support the TS Slave I/F of the tuner device.\n"); + return -EIO; +#endif +} + +/**************************************************************************//** + * write control of a driver + * + * @caution The previous version of mm_tuner driver have "write" + * system call to write registers continuously. + * But, ioctl(TUNER_IOCTL_CNTSET) is implemented in this + * version. + * + * @retval 0 normal + * @retval -ENOSYS (not implemented) + * + * @param [in] FIle, Buffer, Count, OffsetPosition + * These are regular argument of the system call "write" + ******************************************************************************/ +static ssize_t tuner_module_entry_write(struct file *FIle, + const char __user *Buffer, size_t Count, loff_t *OffsetPosition) +{ + pr_warn("The \"write\" system-call is not supported.\n"); + return -EIO; +} + +/**************************************************************************//** + * ioctl system call + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +static long tuner_module_entry_ioctl(struct file *file, + unsigned int uCommand, unsigned long uArgument) +{ + int ret = 0; + int retval = 0; + union _tuner_data_rw rw; + union _tuner_data_event ev; + uint8_t *buf; + uint32_t ver; + uint8_t data; +#if defined(CPATH_I2C) + enum _tuner_cpathid cid; +#endif + unsigned int sig = 0; + + pr_debug("[IOCTL(uCommand): %x]\n", uCommand); + + switch (uCommand) { + /* get a parameter of the register of Tuner */ + case TUNER_IOCTL_VALGET: + if (copy_from_user(&rw, + (union _tuner_data_rw __user *)uArgument, + sizeof(union _tuner_data_rw))) { + pr_err("copy_from_user() failed.\n"); + return -EFAULT; + } + ret = tuner_drv_read_regs(&rw, 1); + if (!ret) { + if (copy_to_user( + (union _tuner_data_rw __user *)uArgument, &rw, + sizeof(union _tuner_data_rw))) { + pr_err("copy_to_user() failed.\n"); + return -EFAULT; + } + } else { + pr_err("read a register, failed.\n"); + return ret; + } + return 0; + /* write a parameters to the register of Tuner */ + case TUNER_IOCTL_VALSET: + if (copy_from_user(&rw, + (union _tuner_data_rw __user *)uArgument, + sizeof(union _tuner_data_rw))) { + pr_err("copy_from_user() failed.\n"); + return -EFAULT; + } + ret = tuner_drv_write_regs(&rw, 1); + if (ret) { + pr_err("write a register, failed.\n"); + return ret; + } + return 0; + /* write registers continuously */ + case TUNER_IOCTL_CNTSET: + if (copy_from_user(&rw, + (union _tuner_data_rw __user *)uArgument, + sizeof(union _tuner_data_rw))) { + pr_err("copy_from_user() failed.\n"); + return -EFAULT; + } + buf = kmalloc(rw.cont.len, GFP_KERNEL); + if (!buf) { + pr_err("memory allocation failed(CNTSET).\n"); + return -ENOMEM; + } + if (copy_from_user(buf, (void __user *)rw.cont.buf, + rw.cont.len)) { + pr_err("copy_from_user() failed.\n"); + retval = -EFAULT; + } else { +#if defined(CPATH_SPI) || defined(CPATH_SDIO) || defined(CPATH_GPIF) + if ((rw.cont.adr == 0xF6) && ((rw.cont.bank == 0) || + (rw.cont.bank == 2))) { + ret = tuner_drv_hw_write_prg(rw.cont.bank, + rw.cont.adr, rw.cont.len, buf); + if (ret) { + pr_err("write program continuously, failed.\n"); + retval = ret; + } + } else { +#endif + ret = tuner_drv_hw_write_reg((int)rw.cont.bank + + (int)cpath_id, rw.cont.adr, + rw.cont.len, buf); + if (ret) { + pr_err("write continuously, failed.\n"); + retval = ret; + } +#if defined(CPATH_SPI) || defined(CPATH_SDIO) || defined(CPATH_GPIF) + } +#endif + } + kfree(buf); + return retval; + /* read registers continuously */ + case TUNER_IOCTL_CNTGET: + if (copy_from_user(&rw, + (union _tuner_data_rw __user *)uArgument, + sizeof(union _tuner_data_rw))) { + pr_err("copy_from_user() failed.\n"); + return -EFAULT; + } + buf = kmalloc(rw.cont.len, GFP_KERNEL); + if (!buf) { + pr_err("memory allocation failed(CNTGET).\n"); + return -ENOMEM; + } + ret = tuner_drv_hw_read_reg((int)rw.cont.bank + + (int)cpath_id, rw.cont.adr, rw.cont.len, buf); + if (ret) { + pr_err("read continuously, failed.\n"); + retval = ret; + } else { + if (copy_to_user((void __user *)rw.cont.buf, + buf, rw.cont.len)) { + pr_err("copy_to_user() failed.\n"); + retval = -EFAULT; + } + } + kfree(buf); + return retval; + /* get the interrupt factor and status */ + case TUNER_IOCTL_EVENT_GET: + if (copy_to_user((union _tuner_data_event __user *)uArgument, + &g_cnt.ev, + sizeof(union _tuner_data_event))) { + pr_err("copy_to_user() failed.\n"); + return -EFAULT; + } + pr_debug("IRQ factor send: intset1:0x%02x intcnd:0x%02x intst:0x%02x\n", + g_cnt.ev.get.intset1, g_cnt.ev.get.intcnd, g_cnt.ev.get.intst); + + /* initialize the variables for the interrupt information */ + g_cnt.ev.pack = 0; + return 0; + /* be available some interrupts */ + case TUNER_IOCTL_EVENT_SET: + pr_debug("*** VALSET_EVENT ***\n"); + if (copy_from_user(&ev, + (union _tuner_data_event __user *)uArgument, + sizeof(union _tuner_data_event))) { + pr_err("copy_from_user() failed.\n"); + return -EFAULT; + } + ret = tuner_drv_hw_setev(&ev); + if (ret) { + pr_err("tuner_drv_setev() failed.\n"); + return ret; + } + return 0; + /* be disable some interrupts */ + case TUNER_IOCTL_EVENT_REL: + if (copy_from_user(&ev, + (union _tuner_data_event __user *)uArgument, + sizeof(union _tuner_data_event))) { + pr_err("copy_from_user() failed.\n"); + return -EFAULT; + } + ret = tuner_drv_hw_relev(&ev); + if (ret) { + pr_err("tuner_drv_relev() failed.\n"); + return ret; + } + return 0; + + /* start to receive TS data from Tuner */ + case TUNER_IOCTL_TSIF_START: +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) + if (!g_tscnt.tsifth_wait) { + pr_warn("TS data buffering had already been started.\n"); + return -EBUSY; + } + if (g_tscnt.tsif != NULL) { + kfree(g_tscnt.tsif); + g_tscnt.tsif = NULL; + } + g_tscnt.tsif = + kmalloc(sizeof(struct _tuner_data_tsif), GFP_KERNEL); + if (g_tscnt.tsif == NULL) + return -ENOMEM; + if (copy_from_user(g_tscnt.tsif, + (struct _tuner_data_tsif __user *)uArgument, + sizeof(struct _tuner_data_tsif))) { + pr_err("copy_from_user() failed.\n"); + retval = -EFAULT; + } else { + ret = tuner_drv_tsif_start(); + if (ret) { + pr_err("tuner_drv_tsif_start() failed.\n"); + retval = ret; + } else { + return 0; + } + } + kfree(g_tscnt.tsif); + g_tscnt.tsif = NULL; + return retval; +#else + return -EINVAL; +#endif + /* stop to receive TS data from Tuner */ + case TUNER_IOCTL_TSIF_STOP: +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) + return tuner_drv_tsif_stop(); +#else + return -EINVAL; +#endif + /* return byte num of a TS packet */ + case TUNER_IOCTL_TSIF_PKTSIZE: +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) + ret = tuner_drv_tsif_pktsize(); + if (ret < 0) { + pr_err("tuner_drv_tsif_pktsize() failed.\n"); + return ret; + } + if (copy_to_user((unsigned int __user *)uArgument, + &ret, sizeof(unsigned int))) { + pr_err("copy_to_user() failed.\n"); + return -EFAULT; + } + return 0; +#else + pr_info("NOT support the TS Slave I/F of the tuner device.\n"); + return -EINVAL; +#endif + case TUNER_IOCTL_GETVER: + ver = (mmtuner_version.device << 24) | + (mmtuner_version.major << 16) | + (mmtuner_version.minor << 8) | + mmtuner_version.hotfix; + put_user(ver, (unsigned int __user *)uArgument); + + ret = tuner_drv_hw_read_reg(Sub, 0xFF, 1, &data); + if (ret) { + pr_err("Read CHIPID, failed.\n"); + return -EFAULT; + } + if (data != MMTUNER_DEVICE) { + pr_err("Unexpected CHIPRD (0x%02x).\n", data); + return -ENXIO; + } + return 0; + case TUNER_IOCTL_TSIF_BLKSIZE: +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) + ret = tuner_drv_tsif_blksize(); + if (ret < 0) { + pr_err("tuner_drv_tsif_blksize() failed.\n"); + return ret; + } + if (copy_to_user((int __user *)uArgument, &ret, sizeof(int))) { + pr_err("copy_to_user() failed.\n"); + return -EFAULT; + } + return 0; +#else + pr_info("NOT support the TS Slave I/F of the tuner device.\n"); + return -EINVAL; +#endif + case TUNER_IOCTL_TSIF_GET_IV: { + pr_info("NOT support the TS Slave I/F of the tuner device.\n"); + return -EINVAL; + } + case TUNER_IOCTL_TSIF_INIT_IV: { + pr_info("NOT support the TS encryption mode\n"); + return -EINVAL; + } + case TUNER_IOCTL_CPATHID: +#ifdef CPATH_I2C + if (copy_from_user(&cid, + (enum _tuner_cpathid __user *)uArgument, + sizeof(enum _tuner_cpathid))) { + pr_err("copy_from_user() failed.\n"); + return -EFAULT; + } + ret = tuner_drv_hw_set_id(cid); + if (ret) { + pr_err("set id, failed.\n"); + retval = ret; + } + return retval; +#else + return -EINVAL; +#endif + /* write a SIG to the nrst of Tuner */ + case TUNER_IOCTL_NRST_CTL: + + if (copy_from_user(&sig, (unsigned int __user *)uArgument, + sizeof(unsigned int))) { + pr_err("copy_from_user() failed.\n"); + return -EFAULT; + } + + ret = tuner_drv_ctl_reset(&tnr_dev, (int)sig); + + if (ret) { + pr_err("write a nrst, failed.\n"); + return ret; + } + return 0; + + /* return byte num of a TS packet */ + default: + pr_err("illegal ioctl request.\n"); + return -EINVAL; + } +} + +/**************************************************************************//** + * poll control of a driver + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +#ifdef TUNER_CONFIG_IRQ_ENABLE +static unsigned int tuner_module_entry_poll(struct file *file, + struct poll_table_struct *poll_tbl) +{ + unsigned long tuner_flags; + unsigned int tuner_mask; + + /* initialize */ + tuner_mask = 0; + + /* wait */ + poll_wait(file, &g_cnt.poll_waitq, poll_tbl); + + /* disable the interrupt */ + spin_lock_irqsave(&g_cnt.poll_lock, tuner_flags); + + /* release */ + if (g_cnt.poll_flag == 0x01) + tuner_mask = (POLLIN | POLLRDNORM); + + g_cnt.poll_flag = 0x00; + + /* enable the interrupt */ + spin_unlock_irqrestore(&g_cnt.poll_lock, tuner_flags); + + return tuner_mask; +} +#endif + +/**************************************************************************//** + * interrupt control of a driver + * + * @retval IRQ_NONE interrupt was not from this device + * @retval IRQ_HANDLED interrupt was handled by this device + * @retval IRQ_WAKE_THREADhandler requests to wake the handler + * thread + * + * @param [in] irq irq# + * @parma [in] dev_id device ID + ******************************************************************************/ +#ifdef TUNER_CONFIG_IRQ_ENABLE +irqreturn_t tuner_interrupt(int irq, void *dev_id) +{ + pr_debug("%s\n", __func__); + + g_cnt.irqth_flag |= TUNER_IRQKTH_CATCHIRQ; + /* The main scheme for IRQ is in the IRQ kernel thread. + * Here is the wake up operation, only. + */ + if (waitqueue_active(&g_cnt.irqth_waitq)) { +#ifdef TUNER_CONFIG_IRQ_LEVELTRIGGER + /* disabling IRQ, when the level interrupt is avail. */ + tuner_drv_hw_disable_interrupt(); +#endif /* TUNER_CONFIG_IRQ_LEVELTRIGGER */ + wake_up(&g_cnt.irqth_waitq); + } else { + pr_err("waitqueue_active() failed.\n"); + /* When the wake-up scheme don't work, stop the receiving IRQ */ + } + + return IRQ_HANDLED; +} +#endif + +/**************************************************************************//** + * @brief Read some registers. + * + * Repeat single read transaction. + * + * @param [in] arg Address to a argument of ioctl() in unsigned long. + * @num [in] num Repeat count. + * + * @returns 0 on success. + * @returns Negative on error. + ******************************************************************************/ +static int tuner_drv_read_regs(union _tuner_data_rw *rw, int num) +{ + int i; + int ret; + + for (i = 0; i < num; i++) { + ret = tuner_drv_hw_read_reg( + rw[i].sngl.bank + (int)cpath_id, + rw[i].sngl.adr, + 1, + &(rw[i].sngl.param)); + if (ret) { + pr_err("copy_to_user() failed.\n"); + return ret; + } + } + return 0; +} + +/**************************************************************************//** + * write some registers of the tuner device. + * + * (no description) + * + * @retval 0 normal + * @retval -EINVAL error + ******************************************************************************/ +static int tuner_drv_write_regs(union _tuner_data_rw *rw, int num) +{ + int i; + int ret; + + for (i = 0; i < num; i++) { + ret = tuner_drv_hw_rmw_reg( + rw->sngl.bank + (int)cpath_id, + rw->sngl.adr, + rw->sngl.enabit, + rw->sngl.param); + if (ret) + return ret; + } + + return 0; +} + +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) +/**************************************************************************//** + * TS I/F (buffering) thread function + * + * @caption + * Original is "tuner_tsbufferring_thread" implemented by + * + * @retval 0 Normal end + * @retval <0 error (refer the errno) + * + * @param [in] arg pointer to the TUNER_DATA_TSIF structure + ******************************************************************************/ +static int tuner_drv_tsif_th(void *arg) +{ + int ret = 0; + int retval = 0; + struct sched_param param; + mm_segment_t oldfs; + int dataready = 0; + int fifo_err = 0; + int maxwaitcnt = 8; + int waitcnt; + + pr_debug("%s\n", __func__); + + /* set the priority of thread */ + param.sched_priority = TUNER_CONFIG_TSBTH_PRI; + /* daemonize("mmtuner_tsifth"); */ + + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = sched_setscheduler(g_tscnt.tsifth_id, SCHED_FIFO, ¶m); + set_fs(oldfs); + + waitcnt = maxwaitcnt; + + /* thread main loop */ + while (1) { + wait_event_interruptible(g_tscnt.tsifth_waitq, + g_tscnt.tsifth_flag); + + /* TS buffering */ + if (g_tscnt.tsifth_flag & TUNER_TSIFTH_ACTIVE) { + ret = tuner_drv_hw_tsif_get_dready(); + if (ret < 0) { + pr_debug("get DATAREADY, failed.\n"); + retval = ret; + goto out; + } else { + dataready = (uint8_t)ret; + } + if (dataready & 0x06) { /* OVER/UNDER-Run */ + if (fifo_err++ >= TUNER_TSIFTH_FIFOERROR_MAX) { + pr_err("FIFO is in serious status!!!\n"); + pr_warn("TS I/F thread go to waiting.\n"); + + g_tscnt.tsifth_flag = TUNER_TSIFTH_NONE; + g_tscnt.tsifth_wait = true; + fifo_err = 0; + continue; + } + pr_warn("OVER/UNER-Run(0x%02x) (%d).\n", + dataready, fifo_err); + dataready = 0x00; + ret = tuner_drv_hw_tsif_sync_pkt(); + if (ret) { + pr_err("tuner_drv_hw_tsif_sync_pkt() failed.\n"); + retval = ret; + goto out; + } + waitcnt = maxwaitcnt; + } + if (!(dataready & 0x01)) { /* not DATAREADY */ + + if (g_tscnt.bw == TUNER_DRV_BW1) + msleep(D_TUNER_WAIT_1SEG_MS); + else + usleep_range(500, 1000); + + if (waitcnt > 0) + waitcnt--; + continue; + } + waitcnt = maxwaitcnt; + + /* TS buffering */ + fifo_err = 0; + ret = tuner_drv_hw_tsif_get_pkts(&g_tscnt); + if (ret < 0) { + pr_err("Receive/Store TS data, failed\n"); + retval = ret; + goto out; + } + + /* TS packet buffer over-flow detection */ + if (g_tscnt.pwr <= g_tscnt.prd && + g_tscnt.prd < + g_tscnt.pwr + g_tscnt.ts_rx_size) { + if (g_tscnt.tsread_flag) + g_tscnt.ovf++; + if ((g_tscnt.ovf % 100) == 1) { + pr_info("packet buffer over flow (%d)", + g_tscnt.ovf); + } + } + + /* wake up the TS read */ + if (g_tscnt.tsread_flag == TUNER_TSREAD_WAIT) { + g_tscnt.tsread_flag = TUNER_TSREAD_IDLE; + /* wake up the TS read */ + if (waitqueue_active(&g_tscnt.tsread_waitq)) + wake_up_interruptible( + &g_tscnt.tsread_waitq); + } + } + + /* check the stop request to the TS I/F thread */ + if (g_tscnt.tsifth_flag & TUNER_TSIFTH_END) { + pr_debug("Caught the stop request.\n"); + g_tscnt.tsifth_flag = TUNER_TSIFTH_NONE; + break; + } + } /* while */ + pr_debug("Tail of thread function %s.\n", __func__); + +out: + return retval; +} + +/**************************************************************************//** + * Set TS I/F context + * + * This function detect active OFDM circuit, and calculate suitable + * memory size for handling TS data. + * After execution, the following member variables of "_tsif_context" + * structure are update. + * bw: active OFDM circuit + * ts_packet_size: byte num of an TS packet + * ts_record_size: RX transfer size a transaction. + * ts_pktbuf_size: buffer size stored by TS I/F thread + * + * @retval 0 Normal end + * @retval <0 error + * + * @param [out] ptscnt pointer to TS I/F context structure + ******************************************************************************/ +static int tuner_drv_tsif_set_cntxt(struct _tsif_cntxt *tc) +{ + int ret = 0; + uint8_t pbuf_max_size; + uint16_t pktbuf_size; + uint32_t lowerlimit; + uint32_t maxbank = 48; + uint32_t rxsize; + +#if TUNER_TSPKTBUF_MODE == 2 + const uint16_t bufsize_tbl[8] = { + 1915, 1851, 1787, 1659, 1403, 891, 635, 379}; +#else + const uint16_t bufsize_tbl[8] = { + 695, 631, 567, 439, 183, 695, 695, 695}; +#endif + +#ifdef DEBUG + const char *bwseg[3] = { "BW13", "BW1", "BW3" }; +#endif + + if (tc == NULL || tc->tsif == NULL) { + pr_err("illegal argument.\n"); + return -EINVAL; + } + if (!tc->tsifth_wait) { + pr_warn("TS buffering have been already started.\n"); + return -EINPROGRESS; + } + + /* detect the reception segment system */ + ret = tuner_drv_bwseg(&tc->bw); + if (ret) { + pr_err("tuner_drv_bwseg(), failed.\n"); + return ret; + } + +#ifdef DEBUG + pr_debug("Reception segment system: %s", bwseg[tc->bw]); +#endif + + /* TS record size a RX transaction */ + /* Readable packet number, when DATAREADY is high. */ + + /* Check packet buffer size configuration. */ + ret = tuner_drv_hw_read_reg(2, 0x62, 1, &pbuf_max_size); + if (ret) { + pr_err("Main2 register read failed.\n"); + return ret; + } + + pktbuf_size = bufsize_tbl[(pbuf_max_size>>4)&0x7]; + + /* Calculate hardware lower limit value */ + switch ((tc->tsif->thl[tc->bw]&0x7)) { + case 0: + case 6: + case 7: + lowerlimit = 16; + break; + default: + lowerlimit = (pktbuf_size)>>(6-(tc->tsif->thl[tc->bw]&0x7)); + break; + } + + rxsize = lowerlimit; + /* re-calculate lowerlimit when use fixed rxsize */ + if (tc->tsif->thl[tc->bw] >= 0x10) { + rxsize = 4<<(tc->tsif->thl[tc->bw]>>4); + if (rxsize > lowerlimit) + rxsize = lowerlimit; + } + + tc->ts_rxpkt_num = rxsize&0x07fc; + + tc->ts_rx_size = + tc->ts_rxpkt_num * g_ts_pkt_size[tc->tsif->ts_pkt_type]; + + /* TS buffer size for the TS I/F thread */ + maxbank = TUNER_MAX_TSPKTBUF_SIZE/tc->ts_rx_size; + if (maxbank > TUNER_MAX_TSPKTBUF_BANK) + maxbank = TUNER_MAX_TSPKTBUF_BANK; + + tc->ts_pktbuf_size = (tc->ts_rx_size) * maxbank; + +#ifdef DEBUG + { + uint32_t datawindow; + + if (tc->tsif->dwind[tc->bw] == 0) { + datawindow = 16; + } else { + datawindow = 0; + if (tc->tsif->dwind[tc->bw] & 0x01) + datawindow += (pktbuf_size>>5); + if (tc->tsif->dwind[tc->bw] & 0x02) + datawindow += (pktbuf_size>>4); + if (tc->tsif->dwind[tc->bw] & 0x04) + datawindow += (pktbuf_size>>3); + if (tc->tsif->dwind[tc->bw] & 0x08) + datawindow += (pktbuf_size>>2); + if (tc->tsif->dwind[tc->bw] & 0x10) + datawindow += (pktbuf_size>>1); + } + pr_info("MEMSIZE=0x%02x(%d) THL=%02x/DWIND=%02x WL(%d-%d) RXPKT=%zu BANK=%d BUFSIZE=%zu\n", + pbuf_max_size, pktbuf_size, + tc->tsif->thl[tc->bw], tc->tsif->dwind[tc->bw], + lowerlimit, lowerlimit+datawindow, + tc->ts_rxpkt_num, maxbank, tc->ts_pktbuf_size); + + } +#endif + + if ((tc->bw == 0 && tc->ts_rx_size < + (64*g_ts_pkt_size[tc->tsif->ts_pkt_type])) || + (tc->bw == 2 && tc->ts_rx_size < + (32*g_ts_pkt_size[tc->tsif->ts_pkt_type]))) { + pr_warn("Looks waterline of interrput is too low. Please confirm it.\n"); + } + + return 0; +} + +/**************************************************************************//** + * Start to receive TS data. + * + * This function activate to receive TS data. + * The "tsifth" which is the kernel thread to receive and store + * the TS data from the tuner device had been dispatched and wait. + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +static int tuner_drv_tsif_start(void) +{ + int ret = 0; + int buffer_size; + + pr_debug("%s\n", __func__); + + + if (!g_tscnt.tsifth_wait) { + /* TS I/F active! */ + pr_warn("TS buffering had already been started.\n"); + return -EINPROGRESS; + } + + ret = tuner_drv_tsif_set_cntxt(&g_tscnt); + + if (ret) { + pr_err("tuner_drv_hw_tsif_set_cntxt() failed.\n"); + return ret; + } + + pr_debug("PKTSIZE:%zu PKTNUM:%zu RXSIZE:%zu BUFSIZE:%zu\n", + g_ts_pkt_size[g_tscnt.tsif->ts_pkt_type], + g_tscnt.ts_rxpkt_num, g_tscnt.ts_rx_size, + g_tscnt.ts_pktbuf_size); + + vfree(g_tscnt.pktbuf); + kfree(g_tscnt.spibuf); + g_tscnt.pktbuf = NULL; + g_tscnt.spibuf = NULL; + +#ifdef TUNER_CONFIG_SPI_ALIGN + buffer_size = g_tscnt.ts_pktbuf_size + TUNER_CONFIG_SPI_ALIGN - 1; +#else + buffer_size = g_tscnt.ts_pktbuf_size; +#endif + +#if defined(DPATH_GPIF) && defined(TUNER_CONFIG_GPIF_DMA) + g_tscnt.pktbuf = kmalloc(buffer_size, GFP_KERNEL | GFP_DMA); +#else + g_tscnt.pktbuf = vmalloc_user(buffer_size); +#endif + + if (g_tscnt.pktbuf == NULL) { + pr_err("memory allocation failed.\n"); + return -ENOMEM; + } + + g_tscnt.spibuf = kmalloc(g_tscnt.ts_rx_size, GFP_KERNEL); + if (g_tscnt.spibuf == NULL) { + vfree(g_tscnt.pktbuf); + pr_err("memory allocation failed.(spibuf)\n"); + return -ENOMEM; + } + memset(g_tscnt.pktbuf, 0, g_tscnt.ts_pktbuf_size); + memset(g_tscnt.spibuf, 0, g_tscnt.ts_rx_size); + g_tscnt.pwr = g_tscnt.prd = 0; + g_tscnt.ovf = 0; + + ret = tuner_drv_hw_tsif_config(&g_tscnt); + if (ret) { + pr_err("tuner_drv_hw_tsif_config() failed.\n"); + vfree(g_tscnt.pktbuf); + kfree(g_tscnt.spibuf); + return ret; + } + + /* TS read operation is IDLE state. */ + g_tscnt.tsread_flag = TUNER_TSREAD_IDLE; + pr_debug("TS-Read Timeout limit: %lu [ms]\n", + TUNER_CONFIG_TSREAD_TIMEOUT); + + ret = tuner_drv_hw_tsif_sync_pkt(); + if (ret) { + pr_err("tuner_drv_hw_tsif_sync_pkt failed.\n"); + vfree(g_tscnt.pktbuf); + kfree(g_tscnt.spibuf); + return ret; + } + + g_tscnt.tsifth_flag = TUNER_TSIFTH_ACTIVE; + /* re-activate TS I/F */ + if (waitqueue_active(&g_tscnt.tsifth_waitq)) + wake_up_interruptible(&g_tscnt.tsifth_waitq); + g_tscnt.tsifth_wait = false; + + + return ret; +} + +/**************************************************************************//** + * Stop receiving TS data. + * + * This function make the TS I/F thread wait status. + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +static int tuner_drv_tsif_stop(void) +{ + int ret = 0; + uint32_t i = 0; + + pr_debug("%s\n", __func__); + + if (g_tscnt.tsifth_wait) { + /* TS I/F is NOT active! */ + pr_warn("TS buffering is not active.\n"); + return 0; + } + + /* stop (be waiting status) TS I/F thread */ + g_tscnt.tsifth_flag = TUNER_TSIFTH_NONE; + g_tscnt.tsifth_wait = true; + + /* confirm the status of the TS I/F thread */ + while (1) { + /* + * NOTE + * It takes about 20-30[ms] to become able to detect the TS + * I/F thread changed in waiting state in the return value of + * the waitqueue_activate() function. + */ + if (waitqueue_active(&g_tscnt.tsifth_waitq)) { + pr_debug("TS I/F thread is going to do the stop procedure.\n"); + break; + } + + usleep_range(TUNER_TSIFTH_SLEEP_MIN, TUNER_TSIFTH_SLEEP_MAX); + + i++; + if (i >= TUNER_TSIFTH_SLEEP_RETRY) { + pr_crit("cannot stop TS I/F thread.\n"); + break; + } + } + + /* release the waiting in TS read operation */ + if (g_tscnt.tsread_flag == TUNER_TSREAD_WAIT) { + g_tscnt.tsread_flag = TUNER_TSREAD_END; + /* release the waiting in TS read operation */ + if (waitqueue_active(&g_tscnt.tsread_waitq)) + wake_up_interruptible(&g_tscnt.tsread_waitq); + } + + g_tscnt.pwr = g_tscnt.prd = 0; + g_tscnt.ovf = 0; + g_tscnt.ts_rxpkt_num = 0; + g_tscnt.ts_rx_size = 0; + g_tscnt.ts_pktbuf_size = 0; + + vfree(g_tscnt.pktbuf); + kfree(g_tscnt.spibuf); + kfree(g_tscnt.tsif); + + g_tscnt.pktbuf = NULL; + g_tscnt.spibuf = NULL; + g_tscnt.tsif = NULL; + + return ret; +} + +/**************************************************************************//** + * return TS packet size + * + * @retval >0 packet size + * @retval <0 error + ******************************************************************************/ +static int tuner_drv_tsif_pktsize(void) +{ + if (g_tscnt.tsifth_wait) { + pr_warn("TS buffering is not started.\n"); + return -EAGAIN; + } + + return (int)(g_ts_pkt_size[g_tscnt.tsif->ts_pkt_type]); +} + +/**************************************************************************//** + * @brief Call-back function for TS-Read Timeout. + * + ******************************************************************************/ +static void tsread_timer_handler(unsigned long data) +{ + pr_info("Timeout (registered %ld, now %ld).", data, jiffies); + + if (g_tscnt.tsread_flag != TUNER_TSREAD_WAIT) { + pr_info("Timer is not to be restarted."); + return; + } + + g_tscnt.tsread_flag = TUNER_TSREAD_TIMEOUT; + /* time out wake up*/ + if (waitqueue_active(&g_tscnt.tsread_waitq)) + wake_up_interruptible(&g_tscnt.tsread_waitq); +} + +/**************************************************************************//** + * return TS block count + * + * @retval >0 block size + * @retval <0 error + ******************************************************************************/ +static int tuner_drv_tsif_blksize(void) +{ + if (g_tscnt.tsifth_wait) { + pr_warn("TS buffering is not started.\n"); + return -EAGAIN; + } + + return (int)(g_tscnt.ts_rxpkt_num); +} + + +/**************************************************************************//** + * detect active OFDM block (BW13 or BW01) + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +static inline int tuner_drv_bwseg(enum _bw_seg *pbw) +{ + int ret; + uint8_t rd; + uint8_t sys; + + ret = tuner_drv_hw_read_reg(Main1, 0x02, 1, &rd); /* SYSSET */ + if (ret) { + pr_debug("register SYSSET, read fail.\n"); + return ret; + } + sys = (rd & 0xC0) >> 6; /* SYS is SYSSET[7:6]. */ + switch (sys) { + case 0: + *pbw = TUNER_DRV_BW13; + break; + case 1: + *pbw = TUNER_DRV_BW1; + break; + case 3: + *pbw = TUNER_DRV_BW3; + break; + default: + pr_err("illegal SYS parameter.\n"); + return -EFAULT; + } + + return 0; +} + +#endif /* TUNER_CONFIG_DPATH != TUNER_DPATH_NONE */ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Socionext Inc."); +MODULE_DESCRIPTION("MM Tuner Driver"); + +module_init(tuner_drv_start); +module_exit(tuner_drv_end); +/******************************************************************************* + * Copyright (c) 2015 Socionext Inc. + ******************************************************************************/ diff --git a/drivers/misc/mm_tuner/src/tuner_drv_hw.c b/drivers/misc/mm_tuner/src/tuner_drv_hw.c new file mode 100644 index 0000000000000000000000000000000000000000..399d8dd7534e7fb49835dae8cc4dc1273d19c3df --- /dev/null +++ b/drivers/misc/mm_tuner/src/tuner_drv_hw.c @@ -0,0 +1,318 @@ +/**************************************************************************//** + * + * @file tuner_drv_hw.c + * + * @brief The HW Wrapping Layer for Tmm Tuner Driver + * + ***************************************************************************//* + * Copyright (c) 2015 Socionext 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. + * + ******************************************************************************/ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ +/*..+....1....+....2....+....3....+....4....+....5....+....6....+....7....+...*/ +/****************************************************************************** + * include + ******************************************************************************/ +#include "tuner_drv.h" +#include "tuner_drv_hw.h" + +#ifdef TUNER_CONFIG_IRQ_ENABLE +#include +#include +#include +#endif + +/****************************************************************************** + * data + ******************************************************************************/ +static bool g_tuner_irq_flag; + +#if defined(DPATH_SPI) || defined(DPATH_SDIO) || defined(DPATH_GPIF) + /* Configuration registers list for slave i/f. */ + /* Don't Edit from here */ +struct snglreg slvif_cfgregs[] = { + { Main2, 0x6B, 0x20, 0x20 }, /* #0 EXINTSET.SLVINTEN = 1 */ + { Main1, 0xE2, 0xF0, 0x90 }, /* #1 INTSET5.ISEGSEL[3:0] = 9 */ + /* #2 STM_SYNCSEL[4:0] DOSET4[4:0] = 1 */ + { Main1, 0xD9, 0x1F, 0x01 }, + /* #3 PKTWLSET1.DATA_WINDOW0[4:0], PKTWLSET2.LOWER_LEVEL0[2:0] */ + { Main2, 0x66, 0xFF, 0x00 }, + /* #4 PKTBUFCTL.ECONFIG[1:0] Set byte order of slave i/f */ + { Main2, 0x60, 0x40, 0x00 }, + /* #5 PKTSYNCC3[7:4] COFF2=ON NULLOFF2=ON */ + { Main2, 0x65, 0x90, 0x90 }, + /* #6 IFPWDSET1[6].[2:1] PKTBUF0,INTGEN0,PKTPACK0 ON*/ + { Main2, 0x70, 0x46, 0x00 }, +#ifdef TUNER_CONFIG_PF_NULLFILTER + { Main2, 0x70, 0x01, 0x00 }, /* #7 IFPWDSET1[0] PF0 CLK ON */ + /* #8 PFTBLSET0A ADDR=0x41(PF0CONFIG) Set address */ + { Main2, 0x59, 0xFF, 0x41 }, + /* #9 PFTBLSET0L DATA_LOW=0x02 Set Null Filter Enable */ + { Main2, 0x57, 0x02, 0x02 }, + /* #10 PFTBLSET0U DATA_HI=0x00 */ + { Main2, 0x58, 0xFF, 0x00 }, + /* #11 PFTBLSET0A ADDR=0x41(PF0CONFIG) Write Data */ + { Main2, 0x59, 0xFF, 0xC1 }, +#endif + /* Don't Edit to here */ + + /* If you want to add any setting the below */ + /* { Mainx , 0xxx, 0xxx, 0x00 }, */ + + /* End Mark. Do not remove this line */ + { END_SLVCFG, 0x00, 0x00, 0x00 } + +}; +#endif + + +/****************************************************************************** + * code area + ******************************************************************************/ + +/**************************************************************************//** + * interruption registration control of a driver + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +int tuner_drv_hw_reqirq(void) +{ + int ret = 0; /* function return */ + + /* the sub-system of IRQ has been already activated */ + if (g_tuner_irq_flag == true) { + pr_debug("IRQ (#%d) is already active, so do nothing\n", + TUNER_CONFIG_INT); + return 0; + } + +#ifdef TUNER_CONFIG_IRQ_ENABLE + pr_debug("*** request IRQ ***\n"); + /* request IRQ */ + ret = request_irq( + TUNER_CONFIG_INT, /* number of IRQ */ + tuner_interrupt, /* call-back function */ + IRQF_TRIGGER_RISING, + "mm_tuner", /* IRQ name */ + NULL /* device ID is not specified */ + ); + if (ret != 0) { + pr_err("request_irq() fail (return:%d)\n", ret); + return -EINVAL; + } +#else +#ifdef DEBUG + pr_debug("TUNER_CONFIG_IRQ_ENABLE is not defined\n"); +#endif +#endif + /* IRQ status flag: on */ + g_tuner_irq_flag = true; + + return ret; +} + +/**************************************************************************//** + * interruption registration release control of a driver + * + ******************************************************************************/ +void tuner_drv_hw_freeirq(void) +{ + if (g_tuner_irq_flag == false) { + /* IRQ line is not active */ + pr_debug("IRQ (#%d) is not active, so do nothing.\n", + TUNER_CONFIG_INT); + return; + } + +#ifdef TUNER_CONFIG_IRQ_ENABLE + /* disable IRQ line */ + pr_debug("*** FREE IRQ ***\n"); + free_irq(TUNER_CONFIG_INT, NULL); +#else +#ifdef DEBUG + pr_debug("TUNER_CONFIG_IRQ_ENABLE is not defined.\n"); +#endif +#endif + + /* flag off */ + g_tuner_irq_flag = false; +} + +/**************************************************************************//** + * Write masked bits of the Register. (Read and Modified Write) + * + * @retval 0 Normal end + * @retval <0 error (refer the errno) + * + * @param [in] bank register bank enumerator + * @param [in] adr start address for continuous write + * @param [in] mask continuous write length + * @param [in] wd write data + ******************************************************************************/ +int tuner_drv_hw_rmw_reg(enum _reg_bank bank, uint8_t adr, + uint8_t mask, uint8_t wd) +{ + int ret; + uint8_t data; + + if (mask == 0x00) { + pr_warn("%s(): Bitmask is 0x00, so write nothing.\n", __func__); + goto _out; + } + if (mask == 0xff) { + data = wd; + } else { + ret = tuner_drv_hw_read_reg(bank, adr, 1, &data); + if (ret) + return ret; + data = (data & ~mask) | wd; + pr_debug("%s(): R,M(m:0x%02x,d:0x%02x),W(0x%02x).\n", __func__, + mask, wd, data); + } + ret = tuner_drv_hw_write_reg(bank, adr, 1, &data); + if (ret) + return ret; + +_out: + return 0; +} + +/**************************************************************************//** + * @brief Set the event (interrupt) condition. + * + * This function set some specified interrupt (event) conditions, + * and, be enable the interrupt sub system. + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +int tuner_drv_hw_setev(union _tuner_data_event *ev) +{ + int ret; + uint8_t buf[2] = { 0x00, 0x00 }; + + pr_debug("mode:%u intset1:0x%02x intdef1:0x%02x intdef2:0x%01x", + ev->set.mode, ev->set.intset1, ev->set.intdef1, ev->set.intdef2); + + if (ev->set.mode == TUNER_EVENT_MODE_ADD) { + /* read INTDEF1 and INTDEF2 */ + ret = tuner_drv_hw_read_reg(Main1, 0xDC, 2, buf); + if (ret) { + pr_err("Read INTDEF1/2, failed\n"); + return ret; + } + buf[0] |= ev->set.intdef1; + buf[1] |= ev->set.intdef2; + } else { /* Overwrite mode: TUNER_EVENT_MODE_OVW */ + buf[0] = ev->set.intdef1; + buf[1] = ev->set.intdef2; + } + + /* write INTDEF1 and INTDEF2 */ + ret = tuner_drv_hw_write_reg(Main1, 0xDC, 2, buf); + if (ret) { + pr_err("Write INTDEF1/2, fail.\n"); + return ret; + } + + /* write INTSET1[3](NINTEN) and NITSET1[0](INTMD) */ + ret = tuner_drv_hw_rmw_reg(Main1, 0xDE, 0x09, ev->set.intset1); + if (ret) { + pr_err("Write INTSET1.NINTEN/INTMD, failed\n"); + return ret; + } + if ((buf[0] | (buf[1] & 0x0F)) != 0x00) { + pr_debug("Enable system IRQ line.\n"); + ret = tuner_drv_hw_reqirq(); + if (ret) { + pr_err("tuner_drv_hw_reqirq() failed.\n"); + return ret; + } + } + + return 0; /* normal exit */ +} + +/**************************************************************************//** + * Clear the IRQ (interrupt) conditions. + * + * This function clear the specified interrupt conditions. + * And, be disabled the IRQ sub-system, when the all interrupt conditions + * are not active. + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +int tuner_drv_hw_relev(union _tuner_data_event *ev) +{ + int ret; + uint8_t buf[2] = { 0x00, 0x00 }; + + /* read INTDEF1/2 */ + ret = tuner_drv_hw_read_reg(Main1, 0xDC, 2, buf); + if (ret) { + pr_err("Read INTDEF1/2, failed.\n"); + return ret; + } + + /* clear specified bits */ + buf[0] &= ~(ev->set.intdef1); + buf[1] &= ~(ev->set.intdef2); + + /* write INTDEF1/2 */ + ret = tuner_drv_hw_write_reg(Main1, 0xDC, 2, buf); + if (ret) { + pr_debug("Write INTDEF1/2, failed.\n"); + return ret; + } + + if ((buf[0] | (buf[1] & 0x0F)) == 0x00) { + pr_debug("Disable system IRQ line.\n"); + tuner_drv_hw_freeirq(); + } + + return 0; /* normal return */ +} + +#ifdef TUNER_CONFIG_IRQ_LEVELTRIGGER +/**************************************************************************//** + * interruption registration enable control of a driver + * + ******************************************************************************/ +void tuner_drv_hw_enable_interrupt(void) +{ + /* enabling interrupt */ + enable_irq(TUNER_CONFIG_INT, NULL); +} + +/**************************************************************************//** + * interruption registration disable control of a driver + * + ******************************************************************************/ +void tuner_drv_hw_disable_interrupt(void) +{ + /* disabling interrupt */ + disable_irq(TUNER_CONFIG_INT, NULL); +} +#endif /* TUNER_CONFIG_IRQ_LEVELTRIGGER */ +/******************************************************************************* + * Copyright (c) 2015 Socionext Inc. + ******************************************************************************/ diff --git a/drivers/misc/mm_tuner/src/tuner_drv_hw_spi.c b/drivers/misc/mm_tuner/src/tuner_drv_hw_spi.c new file mode 100644 index 0000000000000000000000000000000000000000..b50d9de524d55d476cc50c3fd7ba463b90001aa0 --- /dev/null +++ b/drivers/misc/mm_tuner/src/tuner_drv_hw_spi.c @@ -0,0 +1,1219 @@ +/**************************************************************************//** + * + * @file tuner_drv_hw_spi.c + * + * @brief Implementation of the hardware control layer in SPI. + * + ****************************************************************************//* + * Copyright (c) 2015 Socionext 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. + * + ******************************************************************************/ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ +/*..+....1....+....2....+....3....+....4....+....5....+....6....+....7....+...*/ +/****************************************************************************** + * include + ******************************************************************************/ +#include "tuner_drv_hw.h" + +#if defined(SPI_GP_MESURE_DEBUG) || defined(DSPI_DR_MESURE_DEBUG) +#include +#endif + +#include +#include + +#ifdef TUNER_CONFIG_SPI_DIVMSG +#include +#endif + +/****************************************************************************** + * function + ******************************************************************************/ +static int tuner_drv_spi_probe(struct spi_device *spidev); +static int tuner_drv_spi_remove(struct spi_device *spidev); + +/* Private functions */ +#ifdef TUNER_CONFIG_SPI_EDGE +static int tuner_drv_spi_edge(int ctrl); +static int tuner_drv_spi_calibration(void); +#endif + + +#if defined(TUNER_CONFIG_SPI_EXTREAD) || defined(TUNER_CONFIG_AES_ENABLE) +static int tuner_drv_hw_tsif_set_dmycnt(unsigned int cnt); +#endif + +#define SPI_CMD_NUM 0xc +#define SPI_DATA_NUM 0xc +#define SPI_PRG_MAX_NUM 0x100 +#define SPI_PSEQ_ADRS_INIT 0x00 +#define SPI_BREAKCODE_PATTERN {0xff, 0xfe, 0x81, 0x00} + +/****************************************************************************** + * Macro + ******************************************************************************/ +#define CALC_LENGTH(len) ((len >= SPI_PRG_MAX_NUM) ? SPI_PRG_MAX_NUM : len) + +#define SPI_TSREAD_DMYCNT 10 + +/* SPI configuration setting(SPI internal register address=0x08) for each mode +* SPI_DIVMSG SPI_BREAKCODE SPI_CONFIG_SET +* ON OFF 0x00 : XCS_RESET=OFF , TRANSFER BREAK=DISABLE +* ON ON 0x02 : XCS_RESET=OFF , TRANSFER BREAK=ENABLE +* OFF OFF 0x03(0x01) : XCS_RESET=ON , TRANSFER BREAK=DISABLE +* OFF ON 0x03 : XCS_RESET=ON , TRANSFER BREAK=ENABLE +*/ + +#if defined(TUNER_CONFIG_SPI_DIVMSG) && defined(TUNER_CONFIG_SPI_BREAKCODE) +#define SPI_CONFIG_SET (0x02) +#define CMDBUF_POS(x) (void *)(x) +#define CMDBUF_LEN(x) (x+4) +#elif defined(TUNER_CONFIG_SPI_DIVMSG) && !defined(TUNER_CONFIG_SPI_BREAKCODE) +#define SPI_CONFIG_SET (0x00) +#define CMDBUF_POS(x) (void *)(x+4) +#define CMDBUF_LEN(x) (x) +#elif !defined(TUNER_CONFIG_SPI_DIVMSG) && !defined(TUNER_CONFIG_SPI_BREAKCODE) +#define SPI_CONFIG_SET (0x03) +#define CMDBUF_POS(x) (void *)(x+4) +#define CMDBUF_LEN(x) (x) +#else + /* elif !defined(TUNER_CONFIG_SPI_DIVMSG) && +* defined(TUNER_CONFIG_SPI_BREAKCODE) + */ +#define SPI_CONFIG_SET (0x03) +#define CMDBUF_POS(x) (void *)(x) +#define CMDBUF_LEN(x) (x+4) +#endif + +#if defined(TUNER_CONFIG_SPI_EXTREAD) || defined(TUNER_CONFIG_AES_ENABLE) +/* Extend Read command for AES */ +#define SPI_READ_COMMAND 0x3b +#else +/* Normal Read command */ +#define SPI_READ_COMMAND 0x0b +#endif + + +#if defined(TUNER_CONFIG_SPI_ALIGN) +#define BUFLEN_ALIGN(size) ((size + (TUNER_CONFIG_SPI_ALIGN - 1))&\ + (~(TUNER_CONFIG_SPI_ALIGN - 1))) +/* For example +* TUNER_CONFIG_SPI_ALIGN = 1024 +* Expression : (size + 1023) & 0xffffffc00 +* size : 256*188=48128 : +* (48128+1023)&0xfffffc00 = 0xbc00 = 48128 = 256*188 ( Aligned ) +* size : 344*188=64672 : +* (64672+1023)&0xfffffc00 = 0x10000 = 65536 = 344*188 + 864 +*/ + +#ifndef TUNER_CONFIG_SPI_DIVMSG +#error "TUNER_CONFIG_SPI_ALIGN requires TUNRE_CONFIG_SPI_DIVMSG" +#endif + +#else +#define BUFLEN_ALIGN(size) (size) +#endif + +#define D_TUNER_SPI_MATCH_TABLE "socionext,mn553-spi" + +/****************************************************************************** + * global + ******************************************************************************/ +#ifdef TUNER_CONFIG_SPI_DIVMSG +DEFINE_MUTEX(g_spi_mutex); +#define mtxLock() mutex_lock(&g_spi_mutex) +#define mtxUnlock() mutex_unlock(&g_spi_mutex) +#else +#define mtxLock() +#define mtxUnlock() +#endif + +/****************************************************************************** + * data + ******************************************************************************/ +static const struct of_device_id spi_dt_match[] = { + { + .compatible = D_TUNER_SPI_MATCH_TABLE + }, + { }, +}; +MODULE_DEVICE_TABLE(of, spi_dt_match); + +static struct spi_driver mn8855x_spi_driver = { + .driver = { + .name = "553spi", + .owner = THIS_MODULE, + .of_match_table = spi_dt_match, + }, + .probe = tuner_drv_spi_probe, + .remove = tuner_drv_spi_remove, +}; + +struct spi_drvdata { + struct spi_device *spi; + spinlock_t spi_lock; +}; + +static struct spi_drvdata *g_spi_drvdata; + +/****************************************************************************** + * Variable + ******************************************************************************/ +#ifdef TUNER_CONFIG_SPI_EDGE +static int g_edge_mode; +#endif +/****************************************************************************** + * code area + ******************************************************************************/ +/**************************************************************************//** + * Set TPM register + * + * The TPM (register of the tuner device) control the I/F port of + * the tuner device. + * TPM must be set to 0x02 when the Data-PATH (TS I/F) use SPI and + * Control-PATH use I2C. + * + * @retval 0 Normal end + * @retval <0 error + ******************************************************************************/ +int tuner_drv_hw_tsif_set_tpm(void) +{ + int ret = 0; + uint8_t buf = 0x00; + + /* TPM[4:0] (PINCNT0[4:0]) */ + ret = tuner_drv_hw_read_reg(Main2, 0x00, 1, &buf); + if (ret) { + pr_err("Read PINCNT0, failed.\n"); + return ret; + } + if ((buf & 0x1F) != 0x0a) { /* NOT Diver Mode */ +#ifdef CPATH_I2C + buf = 0x02; /* CPATH:I2C, DPATH:SPI(slave-IF) */ +#else + buf = 0x00; /* CPATH:SPI, DPATH:SPI(slave-IF) */ +#endif + ret = tuner_drv_hw_write_reg(Main2, 0x00, 1, &buf); + if (ret) { + pr_err("write PINCNT0.TPM, failed.\n"); + return ret; + } + } + return ret; +} + +/**************************************************************************//** + * address incremental read from some registers of the Tuner device. + * + * @retval 0 Normal end + * @retval <0 error + * + * @param [in] bank register bank enumerator + * @param [in] adr address of the register to read-out start + * @param [in] len continuous read length + * @param [out] rd pointer to the buffer + ******************************************************************************/ +#ifdef CPATH_SPI +int tuner_drv_hw_read_reg(enum _reg_bank bank, uint8_t adr, uint16_t len, + uint8_t *rd) +{ + int ret = 0; + struct spi_message msg; + struct spi_transfer xfer; + + unsigned short loop_cnt; + unsigned char read_data; + + /* breakcode 0xff, 0xfe, 0x81, 0x00 */ + uint8_t scmd[SPI_CMD_NUM] = { 0xff, 0xfe, 0x81, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t sdata[SPI_DATA_NUM]; + uint8_t bank_set[3] = { + 0x90, /* Sub 0x80(I2C) + 0x10(R) + 0x00 */ + 0x91, /* Main1 0x80(I2C) + 0x10(R) + 0x01 */ + 0x92 /* Main2 0x80(I2C) + 0x10(R) + 0x02 */ + }; + uint8_t n = 0; + + if (g_spi_drvdata == NULL) { + pr_debug("[%s](%d) ERR:can't spi read. g_spi_drvdata NULL", + __func__, __LINE__); + return -EINVAL; + } + + if (bank > Main2) { + pr_err("[%s](%d) Illegal bank %d\n", __func__, __LINE__, bank); + return -EINVAL; + } + + /* access loop */ + for (loop_cnt = 0; loop_cnt < len; loop_cnt++) { + memset(sdata, 0x00, sizeof(unsigned char) * SPI_DATA_NUM); + memset(&xfer, 0, sizeof(struct spi_transfer)); + + spi_message_init(&msg); + + scmd[4 + 0] = 0x03; /* command(read) */ + scmd[4 + 1] = bank_set[bank]; /* bank */ + scmd[4 + 2] = (uint8_t) (adr + n); /* address */ + + xfer.tx_buf = CMDBUF_POS(scmd); + xfer.len = CMDBUF_LEN(8); + xfer.bits_per_word = 8; + xfer.rx_buf = CMDBUF_POS(sdata); + + spi_message_add_tail(&xfer, &msg); + mtxLock(); + ret = spi_sync(g_spi_drvdata->spi, &msg); + mtxUnlock(); + if (ret) { + pr_debug("spi_sync() return with %d", ret); + return ret; + } + + /* read data */ + /* MOSI = {0x72, xx, xx, xx, RD, RD, RD, RD} */ + read_data = sdata[11]; + *(rd + n) = read_data; + + n++; + } +#ifdef DEBUG + { + int i; + char lbuf[128] = { 0 }; + + pr_debug("SPI(R) bank:%d bankset:0x%02x offset:0x%02x len:%d\n", + (int)bank, bank_set[(int)bank], adr, len); + for (i = 0; i < len; i++) { + if (0 == (i % 16)) { + if (i) + pr_debug("%s\n", lbuf); + snprintf(lbuf, sizeof(lbuf)-1, "[%04x] %02x", + i, rd[i]); + } else { + snprintf(lbuf, sizeof(lbuf)-1, "%s %02x", + lbuf, rd[i]); + } + } + pr_debug("%s\n", lbuf); + } +#endif + + return ret; +} + +/**************************************************************************//** + * address incremental write to some registers of the Tuner device. + * + * @retval 0 Normal end + * @retval <0 error (refer the errno) + * + * @param [in] bank register bank enumerator + * @param [in] adr start address for continuous write + * @param [in] len continuous write length + * @param [out] wd pointer to the write data array + ******************************************************************************/ +int tuner_drv_hw_write_reg(enum _reg_bank bank, uint8_t adr, uint16_t len, + uint8_t *wd) +{ + int ret = 0; + struct spi_message msg; + struct spi_transfer xfer; + + unsigned short loop_cnt; + unsigned char write_data; + + /* breakcode 0xff, 0xfe, 0x81, 0x00 */ + uint8_t scmd[SPI_CMD_NUM] = { 0xff, 0xfe, 0x81, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t bank_set[3] = { + 0x80, /* Sub 0x80(I2C) + 0x00(W) + 0x00 */ + 0x81, /* Main1 0x80(I2C) + 0x00(W) + 0x01 */ + 0x82, /* Main2 0x80(I2C) + 0x00(W) + 0x02 */ + }; + int i; + uint8_t n = 0; + + if (g_spi_drvdata == NULL) { + pr_debug("[%s](%d) ERR:can't spi read. g_spi_drvdata NULL", + __func__, __LINE__); + return -EINVAL; + } + + if (bank > Main2) { + pr_err("[%s](%d) Illegal bank %d\n", __func__, __LINE__, bank); + return -EINVAL; + } + + memset(&xfer, 0, sizeof(struct spi_transfer)); + + /* access loop */ + for (loop_cnt = 0; loop_cnt < len; loop_cnt++) { + memset(&xfer, 0, sizeof(struct spi_transfer)); + + spi_message_init(&msg); + + scmd[4 + 0] = 0x03; /* command(write) */ + scmd[4 + 1] = bank_set[bank]; /* bank */ + scmd[4 + 2] = (unsigned char) adr + n; /* address */ + /* write data */ + write_data = *(wd + n); + for (i = 3; i < 8; i++) + scmd[4 + i] = write_data; + + xfer.tx_buf = CMDBUF_POS(scmd); + xfer.len = CMDBUF_LEN(8); + xfer.bits_per_word = 8; + + spi_message_add_tail(&xfer, &msg); + mtxLock(); + ret = spi_sync(g_spi_drvdata->spi, &msg); + mtxUnlock(); + if (ret) { + pr_debug("spi_sync() return with %d", ret); + return ret; + } + n++; + } + +#ifdef DEBUG + { + int i; + char lbuf[128] = { 0 }; + + pr_debug("SPI(W) bank:%d bankset:0x%02x adr:0x%02x len:%d", + (int)bank, bank_set[(int)bank], adr, len); + for (i = 0; i < len; i++) { + if (0 == (i % 16)) { + if (i) + pr_debug("%s\n", lbuf); + snprintf(lbuf, sizeof(lbuf)-1, "[%04x] %02x", + i, wd[i]); + } else { + snprintf(lbuf, sizeof(lbuf)-1, "%s %02x", + lbuf, wd[i]); + } + } + pr_debug("%s\n", lbuf); + } +#endif + + return ret; +} + +/************************************************************************//** + * address incremental write for PSEQ/TNCTL program download + * to the Tuner device. + * @retval 0 Normal end + * @retval <0 error (refer the errno) + * + * @param [in] bank register bank enumerator + * @param [in] adr start address for continuous write + * @param [in] len continuous write length + * @param [out] wd pointer to the write data array + ****************************************************************************/ +int tuner_drv_hw_write_prg(enum _reg_bank bank, uint8_t adr, + uint16_t len, uint8_t *wd) +{ + int ret = 0; + struct spi_message msg; + struct spi_transfer xfer; + uint8_t buf[3]; + uint8_t scmd[SPI_PRG_MAX_NUM + 4 + 4]; + uint8_t stadu = 0x00; + uint8_t stadl = 0x00; + + int i, loop_cnt; + uint16_t write_length = 0; + int16_t remain_length = 0; + uint16_t n = 0; + + const uint8_t stadu_mask[3] = { + 0x3F, /* TNCSTAD[12:8] is TNCSTADU[4:0] */ + 0x00, /* (ignore) */ + 0x1F /* PSCSTAD[ */ + }; + const uint8_t dl_flg[3] = { + 0x01, /* Sub (TNCTL) */ + 0xFF, /* (ignore) */ + 0x00 /* Main2 (PSCTL) */ + }; + + struct snglreg rmw[3] = { + /** + * Upper bits of the offset address for up-loading firmware + */ + { bank, 0xF4, stadu_mask[bank], 0x00, }, /* *STADU */ + /** + * Lower bits of the offset address for up-loading firmware + */ + { bank, 0xF5, 0xFF, 0x00, }, /* *STADL */ + /** + * PSEQ Program Reset (*PGRST) + */ + { bank, 0xF3, 0x02, 0x02, } /* *PGRST */ + }; + + const uint8_t break_code[] = { 0xff, 0xfe, 0x81, 0x00, }; + + if (g_spi_drvdata == NULL) { + pr_debug("[%s](%d) ERR:can't spi read. g_spi_drvdata NULL", + __func__, __LINE__); + return -EINVAL; + } + + if (bank != Main2 && bank != Sub) { + pr_err("[%s](%d) Illegal bank %d\n", __func__, __LINE__, bank); + return -EINVAL; + } + + memset(&xfer, 0, sizeof(struct spi_transfer)); + + /* PRTYMD setting */ + ret = tuner_drv_hw_rmw_reg(bank, 0xF2, 0x40, 0x40); /* PRTYMD=1 */ + if (ret) { + pr_err("Write PRTYMD of bank#%d, failed.\n", bank); + return ret; + } + + memcpy(scmd, break_code, sizeof(break_code)); + + /* access loop */ + loop_cnt = 0; + remain_length = len; + do { + rmw[0].param = stadu + (loop_cnt & 0x00FF); + rmw[1].param = stadl; + + for (i = 0; i < 3; i++) { + ret = tuner_drv_hw_rmw_reg(rmw[i].bank, rmw[i].adr, + rmw[i].enabit, rmw[i].param); + if (ret) + return ret; + } + + write_length = CALC_LENGTH(remain_length); + remain_length -= write_length; + memset(scmd+8, 0x00, sizeof(unsigned char) * (write_length)); + memset(&xfer, 0, sizeof(struct spi_transfer)); + + spi_message_init(&msg); + + scmd[4 + 0] = 0x02; /* command */ + scmd[4 + 1] = dl_flg[bank]; /* 0: PSEQ / 1: TNCTL */ + /* size(upper) */ + scmd[4 + 2] = (write_length >> 8) & 0x00FF; + scmd[4 + 3] = write_length & 0x00FF; /* size(lower) */ + /* write data */ + for (i = 0; i < write_length; i++) { + scmd[4 + i + 4] = *(wd + n); + n++; + } + + xfer.tx_buf = CMDBUF_POS(scmd); + xfer.len = CMDBUF_LEN(write_length + 4); + xfer.bits_per_word = 8; + + spi_message_add_tail(&xfer, &msg); + mtxLock(); + ret = spi_sync(g_spi_drvdata->spi, &msg); + mtxUnlock(); + if (ret) { + pr_debug("spi_sync() return with %d", ret); + return ret; + } + + WARN_ON(remain_length < 0); + if (remain_length == 0) + break; + + loop_cnt++; + } while (-1); + + /* Dummy read */ + ret = tuner_drv_hw_read_reg(bank, 0xF8, 1, buf); + if (ret) { + pr_err("RAM read mode setting fail.\n"); + return ret; + } + + return ret; +} +#endif /* CPATH_SPI */ + +/**************************************************************************//** + * Register the TS I/F driver + * + * @retval 0 Normal end + * @retval <0 error + ******************************************************************************/ +int tuner_drv_hw_tsif_register(void) +{ + int ret = 0; + + pr_debug("%s\n", __func__); + + ret = spi_register_driver(&mn8855x_spi_driver); + if (ret) { + pr_err("spi_register_driver() failed.\n"); + return ret; + } + + return ret; +} + +/**************************************************************************//** + * Configure the SPI-Slave I/F of the tuner device. + * + * @retval 0 Normal end + * @retval <0 error + * + * @param [in] + ******************************************************************************/ +int tuner_drv_hw_tsif_config(struct _tsif_cntxt *tc) +{ + int ret = 0; + struct _tuner_data_tsif *tsif; + union _tuner_data_event iberint; + int i; + + /* breakcode 0xff, 0xfe, 0x81, 0x00 */ + uint8_t tx[12] = { 0xff, 0xfe, 0x81, 0x00, + 0x03, 0x00, 0x08, 0x00, SPI_CONFIG_SET, 0x00, 0x00, 0x00 }; + struct spi_message msg; + struct spi_transfer xfer; + uint8_t pbuf_max_size = 0; + uint8_t byte_order_set = 0; + + pr_debug("%s\n", __func__); + + ret = tuner_drv_hw_tsif_set_tpm(); + if (ret) { + pr_err("tuner_drv_hw_tsif_set_tpm() failed.\n"); + return ret; + } + + if (tc == NULL || tc->tsif == NULL) { + pr_err("illegal arguments.\n"); + return -EINVAL; + } + + tsif = (struct _tuner_data_tsif *) tc->tsif; + + /* configure the SPI(slave) I/F sub-system of Tuner device */ + memset(&xfer, 0, sizeof(struct spi_transfer)); + spi_message_init(&msg); + + xfer.tx_buf = (void *) tx; + xfer.len = 4 + 8; + xfer.bits_per_word = 8; + spi_message_add_tail(&xfer, &msg); + mtxLock(); + ret = spi_sync(g_spi_drvdata->spi, &msg); + mtxUnlock(); + if (ret) { + pr_err("spi_sync() failed. (return:%d).\n", ret); + return ret; + } + +#if defined(TUNER_CONFIG_SPI_EXTREAD) || defined(TUNER_CONFIG_AES_ENABLE) + tuner_drv_hw_tsif_set_dmycnt(SPI_TSREAD_DMYCNT); +#endif + +#ifdef TUNER_CONFIG_SPI_EDGE + tuner_drv_spi_edge(1); + ret = tuner_drv_spi_calibration(); + if (ret) { + pr_err("tuner_drv_spi_calibration() failed. (return:%d).\n", + ret); + return ret; + } +#endif + + /* Water Line setting */ + slvif_cfgregs[SLVIF_CFG_WATERLINE].param = + ((uint8_t) (tsif->dwind[tc->bw]) << 3) | (tsif->thl[tc->bw]&0x7); + + /* Byte order configuration */ + if (tsif->spi_ts_bit_per_word == 32) { +#ifdef TUNER_CONFIG_SLV_MSBFIRST + slvif_cfgregs[SLVIF_CFG_BYTEORDER].param = 0x00; +#else + slvif_cfgregs[SLVIF_CFG_BYTEORDER].param = 0x40; +#endif + } else { + slvif_cfgregs[SLVIF_CFG_BYTEORDER].param = 0x00; + } + + for (i = 0; slvif_cfgregs[i].bank != END_SLVCFG ; i++) { + ret = tuner_drv_hw_rmw_reg(slvif_cfgregs[i].bank, + slvif_cfgregs[i].adr, slvif_cfgregs[i].enabit, + slvif_cfgregs[i].param); + if (ret) { + pr_err("TS slave-IF configuration, failed.\n"); + return ret; + } + } + + ret = tuner_drv_hw_read_reg(Main2, 0x62, 1, &pbuf_max_size); + if (ret) { + pr_err("Read PKTMSIZE register, failed.\n"); + return ret; + } + if ((pbuf_max_size & 0x0F) == 0) + pr_debug("PKTMSIZE.MEMSIZE0[3:0] != 0x3.\n"); + + /* Interrupt setting */ + /* IBERINT_F */ + iberint.pack = 0; + iberint.set.mode = TUNER_EVENT_MODE_ADD; + iberint.set.intdef1 = 0x80; /* IBERINT */ + iberint.set.intset1 = 0x09; /* NINTEN, INTMD = 1 */ + ret = tuner_drv_hw_setev(&iberint); + if (ret) { + pr_err("tuner_drv_setev(F) failed.\n"); + return ret; + } + + /* Check byte order configuration. */ + ret = tuner_drv_hw_read_reg(Main2, 0x60, 1, &byte_order_set); + if (ret) { + pr_err("Main2 register read failed.\n"); + return ret; + } +#ifdef TUNER_CONFIG_SPI_DIVMSG + pr_debug("\n"); +#else + pr_debug("\n"); +#endif + pr_debug("\n", + tsif->spi_ts_bit_per_word); + pr_debug("\n", + byte_order_set, + (byte_order_set & 0x40) ? "LSB" : "MSB"); + return 0; +} + +/**************************************************************************//** + * Unregister the TS I/F driver + * + ******************************************************************************/ +void tuner_drv_hw_tsif_unregister(void) +{ + pr_debug("%s\n", __func__); + + spi_unregister_driver(&mn8855x_spi_driver); +} + +/**************************************************************************//** + * Get the DATAREADY flag + * + * This function return the DATAREADY flag of the Slave-I/F of + * tuner device. DATAREADY flag contain OVER/UNER-Run indicator. + * It is below there bit position. + * OVER-Run is bit-2. UNDER-Run is bit-1, DATA-Ready is bit-0. + * + * @retval >=0 DATAREADY flag (casted from uint8_t) + * @retval <0 error + ******************************************************************************/ +int tuner_drv_hw_tsif_get_dready(void) +{ + int ret; + /* breakcode 0xff, 0xfe, 0x81, 0x00 */ + uint8_t tx[12] = { 0xff, 0xfe, 0x81, 0x00, + 0x03, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t rx[12] = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + struct spi_message msg; + struct spi_transfer xfer; + +#ifdef DSPI_DR_MESURE_DEBUG + struct timeval s1; + struct timeval t1; + unsigned long temp; +#endif + + memset(&xfer, 0, sizeof(struct spi_transfer)); + spi_message_init(&msg); + + xfer.tx_buf = CMDBUF_POS(tx); + xfer.rx_buf = CMDBUF_POS(rx); + xfer.len = CMDBUF_LEN(8); + xfer.bits_per_word = 8; + + spi_message_add_tail(&xfer, &msg); + mtxLock(); + +#ifdef DSPI_DR_MESURE_DEBUG + do_gettimeofday(&s1); +#endif + + ret = spi_sync(g_spi_drvdata->spi, &msg); + +#ifdef DSPI_DR_MESURE_DEBUG + do_gettimeofday(&t1); + temp = ((t1.tv_sec - s1.tv_sec) * 1000000 + (t1.tv_usec - s1.tv_usec)); + pr_err("dready() sa=%lu\n", temp); +#endif + + mtxUnlock(); + if (ret) { + pr_err("spi_sync() failed (rc:%d)\n", ret); + return ret; + } + + return (int) rx[11]; +} + +#if defined(TUNER_CONFIG_SPI_EXTREAD) || defined(TUNER_CONFIG_AES_ENABLE) +/**************************************************************************//** + * Set dummy cnt of SPI Extend Read + * + * This function set DMYCNT when use SPI EXTEND READ COMMAND for AES + * + * @retval >=0 + * @retval <0 error + ******************************************************************************/ +int tuner_drv_hw_tsif_set_dmycnt(unsigned int cnt) +{ + int ret; + uint8_t tx[12] = { + 0xff, 0xfe, 0x81, 0x00, /* breakcode 0xff, 0xfe, 0x81, 0x00 */ + 0x03, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + struct spi_message msg; + struct spi_transfer xfer; + + memset(&xfer, 0, sizeof(struct spi_transfer)); + spi_message_init(&msg); + + xfer.tx_buf = CMDBUF_POS(tx); + xfer.len = CMDBUF_LEN(8); + xfer.bits_per_word = 8; + +#ifdef TUNER_CONFIG_SPI_EDGE + tx[8] = (cnt & 0xf) | 0x80; +#else + tx[8] = cnt & 0xf; +#endif + spi_message_add_tail(&xfer, &msg); + mtxLock(); + ret = spi_sync(g_spi_drvdata->spi, &msg); + mtxUnlock(); + if (ret) { + pr_err("spi_sync() failed (rc:%d)\n", ret); + return ret; + } + + return ret; +} +#endif + +/**************************************************************************//** + * Send the transaction command to synchronize slave I/F of tuner. + * + * This function send the packet synchronization command. + * It initialize the read pointer and clear the FIFO buffer. + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +int tuner_drv_hw_tsif_sync_pkt(void) +{ + int ret = 0; + /* breakcode 0xff, 0xfe, 0x81, 0x00 */ + uint8_t tx[8] = { 0xff, 0xfe, 0x81, 0x00, 0xd8, 0x00, 0x00, 0x00 }; + struct spi_message msg; + struct spi_transfer xfer; + + pr_debug("%s\n", __func__); + + memset(&xfer, 0, sizeof(struct spi_transfer)); + spi_message_init(&msg); + + xfer.tx_buf = CMDBUF_POS(tx); + xfer.len = CMDBUF_LEN(4); + xfer.bits_per_word = 8; + + spi_message_add_tail(&xfer, &msg); + mtxLock(); + ret = spi_sync(g_spi_drvdata->spi, &msg); + mtxUnlock(); + if (ret) { + pr_err("spi_sync() failed (rc:%d)\n", ret); + return ret; + } + + return 0; +} + +/**************************************************************************//** + * Get the TS packets of the appointed number. + * + * @retval >=0 Normal end (number of the get packet) + * @retval <0 error (refer the errno) + * + * @param [in] num num of packets + * @param [out] pktbuf packet storage + * @param [in] pktsize packet size enumerator + ******************************************************************************/ +int tuner_drv_hw_tsif_get_pkts(struct _tsif_cntxt *tc) +{ + int ret; + struct spi_message msg; +#ifdef TUNER_CONFIG_SPI_DIVMSG + struct spi_message tsmsg; +#endif + struct spi_transfer xfer[2]; + int sum = 0; + struct _tuner_data_tsif *tsif = tc->tsif; + unsigned int ts_rdelay = 0; + + /* TS packet size: 188Byte, num of TS packets:256 */ + /* Break code0xff, 0xfe, 0x81, 0x00 + Packet Read commnad */ + uint8_t tx[9 + 17] = { 0xff, 0xfe, 0x81, 0x00, /* 4 byte length */ + /* 5 byte length command ex. 0x0b .. */ + SPI_READ_COMMAND, 0x00, 0x00, 0xFF, 0x00, + /* This 16 bytes is dummy cycles */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* for extend read command */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Last byte is for additional cycle in edge mode and + * extend read is enabled + */ + 0x00 }; +#ifdef SPI_GP_MESURE_DEBUG + struct timeval s1; + struct timeval t1; + unsigned long temp; +#endif + +#if defined(TUNER_CONFIG_SPI_EXTREAD) || defined(TUNER_CONFIG_AES_ENABLE) + ts_rdelay = ts_rdelay + SPI_TSREAD_DMYCNT; +#endif /* TUNER_CONFIG_SPI_EXTREAD */ +#ifdef TUNER_CONFIG_SPI_EDGE + ts_rdelay = ts_rdelay + 1; /* EDGE==1 */ +#endif /* TUNER_CONFIG_SPI_EDGE */ + + if (!g_spi_drvdata) { + pr_err("SPI I/F not active.\n"); + return -ENXIO; + } + if (!tc->pktbuf) { + pr_err("TS buffer not found.\n"); + return -EINVAL; + } + if (tc->tsif->ts_pkt_type == TUNER_DRV_TS_TSTAMP) { + pr_err("not support the Time-Stamp TS"); + return -EINVAL; + } + if (!tc->spibuf) { + pr_err("SPI buf not found.\n"); + return -EINVAL; + } + memset(tc->spibuf, 0, tc->ts_rx_size); + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + tx[4 + 2] = (tc->ts_rxpkt_num - 1) >> 8; + tx[4 + 3] = (tc->ts_rxpkt_num - 1) & 0xff; + /* break | read mode | edge mode || ts_rdelay(=edge delay+DMYCNT delay) + * | nomral | off || 0 + * | nomral | on || 1 + * on | nomral | off || 0 + * on | nomral | on || 1 + * | extend | off || DMYCNT + * | extned | on || DMYCNT+1 + * on | extend | off || DMYCNT + * on | extend | on || DMYCNT+1 + */ + xfer[0].tx_buf = CMDBUF_POS(tx); + xfer[0].len = CMDBUF_LEN(5) + ts_rdelay; + xfer[0].bits_per_word = 8; + + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = (void *) (tc->spibuf); + xfer[1].bits_per_word = tsif->spi_ts_bit_per_word; +#ifdef TUNER_CONFIG_SPI_DIVMSG + xfer[1].len = BUFLEN_ALIGN(tc->ts_rx_size); + spi_message_init(&tsmsg); + spi_message_add_tail(&xfer[1], &tsmsg); + mtxLock(); + ret = spi_sync(g_spi_drvdata->spi, &msg); + if (ret) { + pr_err("spi_sync() failed (rc:%d)\n", ret); + mtxUnlock(); + return ret; + } + + ret = spi_sync(g_spi_drvdata->spi, &tsmsg); + mtxUnlock(); + if (ret) { + pr_err("spi_sync() failed (rc:%d)\n", ret); + return ret; + } +#else + xfer[1].len = tc->ts_rx_size; + spi_message_add_tail(&xfer[1], &msg); + + mtxLock(); + +#ifdef SPI_GP_MESURE_DEBUG + do_gettimeofday(&s1); +#endif + + ret = spi_sync(g_spi_drvdata->spi, &msg); + +#ifdef SPI_GP_MESURE_DEBUG + do_gettimeofday(&t1); + temp = ((t1.tv_sec - s1.tv_sec) * 1000000 + (t1.tv_usec - s1.tv_usec)); + pr_err("spi_sync() sa=%lu\n", temp); +#endif + + mtxUnlock(); + if (ret) { + pr_err("spi_sync() failed (rc:%d)\n", ret); + return ret; + } +#endif + + /* Plase add the code to transform endian , if you need */ + /* ---------------------------------------------------- */ + + /* ---------------------------------------------------- */ + + memcpy((void *) (tc->pktbuf + tc->pwr), + (void *) (tc->spibuf), tc->ts_rx_size); + tc->pwr += tc->ts_rx_size; + + if (tc->pwr == tc->ts_pktbuf_size) + tc->pwr = 0; + + return sum; +} + +/**************************************************************************//** + * probe function called by spi_register_driver() + * + * @retval 0 Normal end + * @retval <0 error (refer the errno) + * + * @param [in] spidev pointer to the "spi_device" structure + ******************************************************************************/ +static int tuner_drv_spi_probe(struct spi_device *spidev) +{ + int ret = 0; + struct spi_drvdata *drvdata; + + pr_debug("%s\n", __func__); + + if (g_spi_drvdata != NULL) { + pr_err("SPI I/F not active.\n"); + return -EBUSY; + } + if (spidev == NULL) { + pr_err("illegal argument.\n"); + return -EINVAL; + } + + drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->spi = spidev; + spin_lock_init(&drvdata->spi_lock); + spi_set_drvdata(spidev, drvdata); + g_spi_drvdata = drvdata; + + ret = spi_setup(spidev); + if (ret) { + pr_err("spi_setup() failed.\n"); + return ret; + } +#ifdef DEBUG + pr_info("max_speed_hz :%d\n", spidev->max_speed_hz); + pr_info("chip_select :%d\n", spidev->chip_select); + pr_info("mode :%d\n", spidev->mode); + pr_info("bits_per_word :%d\n", spidev->bits_per_word); + pr_info("irq :%d\n", spidev->irq); + pr_info("modalias :%s\n", spidev->modalias); +#endif + return ret; +} + +/**************************************************************************//** + * remove function called by spi_register_driver() + * + * @retval 0 Normal end + * @retval <0 error (refer the errno) + * + * @param [in] spidev pointer to the "spi_device" structure + ******************************************************************************/ +static int tuner_drv_spi_remove(struct spi_device *spidev) +{ + pr_debug("%s\n", __func__); + + spi_set_drvdata(spidev, NULL); + kfree(g_spi_drvdata); + g_spi_drvdata = NULL; + + return 0; +} + +/**************************************************************************//** + * TS read Calibration + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +#ifdef TUNER_CONFIG_SPI_EDGE +int tuner_drv_spi_calibration(void) +{ + int ret = 0; + /* breakcode 0xff, 0xfe, 0x81, 0x00 */ + uint8_t tx1[7 + 32] = { 0xff, 0xfe, 0x81, 0x00, + 0x4b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; /* Calibration command */ + uint8_t rx1[7 + 4 * 8] = { 0x00 }; /* Read calib. result */ + /* breakcode 0xff, 0xfe, 0x81, 0x00 */ + uint8_t tx2[12] = { 0xff, 0xfe, 0x81, 0x00, + 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* Delay set */ + struct spi_message msg; + struct spi_transfer xfer[3]; + int i; + int sp, ep, pos; + + pr_debug("%s\n", __func__); + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + /* Calibration Command */ + xfer[0].tx_buf = CMDBUF_POS(tx1); + xfer[0].rx_buf = CMDBUF_POS(rx1); + xfer[0].len = CMDBUF_LEN(3+32); + xfer[0].bits_per_word = 8; + spi_message_add_tail(&xfer[0], &msg); + + mtxLock(); + ret = spi_sync(g_spi_drvdata->spi, &msg); + mtxUnlock(); + if (ret) { + pr_err("spi_sync() failed (rc:%d)\n", ret); + return ret; + } + + pr_debug("\n"); + for (i = 7; i < 39; i += 4) { + pr_debug( + " (%1d) rx[%2d]:%02X, rx[%2d]:%02X, rx[%2d]:%02X, rx[%2d]:%02X\n", + (int)((i-7)/4), i+0, rx1[i+0], i+1, rx1[i+1], i+2, rx1[i+2], + i+3, rx1[i+3]); + } + + /* Calculation delay */ + sp = 0; + ep = 0; + for (i = 0; i < 8; i++) { + if (rx1[4 + 5 + i * 4] == 0x72) { + if (sp == 0) + sp = i + 1; + else + ep = i + 1; + } + } + + /* Delay set */ + pos = (int) ((sp + ep) / 2); + tx2[4 + 4] = pos; + pr_debug("sp(%d),ep(%d)==>pos: %d (edge_mode:%d)\n", + sp, ep, pos, g_edge_mode); + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + xfer[2].tx_buf = CMDBUF_POS(tx2); + xfer[2].len = CMDBUF_LEN(8); + xfer[2].bits_per_word = 8; + spi_message_add_tail(&xfer[2], &msg); + mtxLock(); + ret = spi_sync(g_spi_drvdata->spi, &msg); + mtxUnlock(); + if (ret) { + pr_err("spi_sync() failed (rc:%d)\n", ret); + return ret; + } + + if (sp == 0) { + pr_err("spi_calibration() failed. sp = %d", sp); + return -EAGAIN; + } + return 0; +} +#endif + +/**************************************************************************//** + * EDGE mode setting + * + * @retval 0 normal + * @retval <0 error + ******************************************************************************/ +#ifdef TUNER_CONFIG_SPI_EDGE +int tuner_drv_spi_edge(int ctrl) +{ + int ret = 0; + /* breakcode 0xff, 0xfe, 0x81, 0x00 */ + uint8_t tx[12] = { 0xff, 0xfe, 0x81, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + struct spi_message msg; + struct spi_transfer xfer; + + pr_debug("%s\n", __func__); + + memset(&xfer, 0, sizeof(struct spi_transfer)); + spi_message_init(&msg); + + tx[4 + 4] = (ctrl << 7) | SPI_TSREAD_DMYCNT; + + xfer.tx_buf = CMDBUF_POS(tx); + xfer.len = CMDBUF_LEN(8); + xfer.bits_per_word = 8; + spi_message_add_tail(&xfer, &msg); + mtxLock(); + ret = spi_sync(g_spi_drvdata->spi, &msg); + mtxUnlock(); + if (ret) { + pr_err("spi_sync() return with %d", ret); + return ret; + } + g_edge_mode = ctrl; + pr_debug("SPI EDGE mode (%d)", g_edge_mode); + return 0; +} +#endif + +/******************************************************************************* + * Copyright (c) 2015 Socionext Inc. + ******************************************************************************/ diff --git a/drivers/misc/oneseg_tuner_drv.c b/drivers/misc/oneseg_tuner_drv.c new file mode 100644 index 0000000000000000000000000000000000000000..275144c2dc9b1945d46705027708387ad44d8ce7 --- /dev/null +++ b/drivers/misc/oneseg_tuner_drv.c @@ -0,0 +1,476 @@ +/* drivers/misc/oneseg_tuner_drv.c + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ +#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 D_ONESEG_CONFIG_MODULE_NAME "Oneseg Tuner Driver" +#define D_ONESEG_CONFIG_MODULE_LICENCE "GPL" +#define D_ONESEG_CONFIG_DRIVER_NAME "onesegtuner_drv" +#define D_ONESEG_CONFIG_PLATFORM_DRIVER_NAME "onesegtuner_pdev" +#define D_ONESEG_CONFIG_SYSFS_DEV_NAME "onesegtuner_pdev" +#define D_ONESEG_CONFIG_CLASS_NAME "onesegtuner" +#define D_ONESEG_CONFIG_MATCH_TABLE "sony,vj190" +#define D_SPI_PLATFORM_DRIVER_NAME "oneseg_tuner_spi_dev" +#define D_SPI_CONFIG_MATCH_TABLE "sony,oneseg-tuner" + +#define D_ONESEG_CONFIG_DRV_MAJOR 110 +#define D_ONESEG_CONFIG_DRV_MINOR 210 +#define D_ONESEG_POWER_ON_WAIT_US 3000 +#define D_ONESEG_POWER_ON_WAIT_RANGE_US 3100 +#define D_ONESEG_RESET_ON_WAIT_US 1000 +#define D_ONESEG_RESET_ON_WAIT_RANGE_US 1100 +#define D_ONESEG_RESET_OFF_WAIT_US 1000 +#define D_ONESEG_RESET_OFF_WAIT_RANGE_US 1100 + +enum oneseg_gpio_id { + ONESEG_POWER_PIN = 0, + ONESEG_RESET_PIN, + ONESEG_INT_PIN, +}; + +static char const * const oneseg_gpio_rsrcs[] = { + "Oneseg tuner power", + "Oneseg tuner reset", + "Oneseg tuner int", +}; + +enum ONESEG_DRV_CTL { + ONESEG_DRV_CTL_POWON, + ONESEG_DRV_CTL_POWOFF, + ONESEG_DRV_CTL_RESET +}; + +enum ONESEG_DRV_IRQ { + ONESEG_DRV_IRQ_NOT_DETECTED, + ONESEG_DRV_IRQ_DETECTED, +}; + +struct oneseg_tuner_drvdata { + struct device *dev; + struct device sysfs_dev; + struct mutex mutex_lock; + unsigned int gpios[ARRAY_SIZE(oneseg_gpio_rsrcs)]; +}; + +struct g_oneseg_tuner_device { + struct mutex g_tuner_mutex; + unsigned long open_cnt; + struct platform_device *onesegtuner_device; + struct class *device_class; + u32 irq_flag; + wait_queue_head_t irq_wait_q; + int irq_num; +} oneseg_dev; + +static enum oneseg_gpio_id req_ids[] = { + ONESEG_POWER_PIN, + ONESEG_RESET_PIN, + ONESEG_INT_PIN, +}; + +static void oneseg_tunerpm_power_control(struct oneseg_tuner_drvdata + *drvdata, int on) +{ + gpio_set_value_cansleep(drvdata->gpios[ONESEG_POWER_PIN], on); +} + +static void oneseg_tunerpm_reset_control(struct oneseg_tuner_drvdata *drvdata, + int on) +{ + gpio_set_value_cansleep(drvdata->gpios[ONESEG_RESET_PIN], on); +} + +static int oneseg_dev_init(struct platform_device *pdev, + struct oneseg_tuner_drvdata *drvdata) +{ + int i, ret, gpio; + unsigned int flags; + struct device_node *of_node = pdev->dev.of_node; + + mutex_init(&drvdata->mutex_lock); + + for (i = 0; i < ARRAY_SIZE(oneseg_gpio_rsrcs); i++) { + gpio = of_get_gpio_flags(of_node, i, &flags); + if (!gpio_is_valid(gpio)) { + ret = -EINVAL; + goto error_gpio; + } + drvdata->gpios[i] = gpio; + } + + for (i = 0; i < ARRAY_SIZE(req_ids); i++) { + ret = gpio_request(drvdata->gpios[req_ids[i]], + oneseg_gpio_rsrcs[req_ids[i]]); + if (ret) + goto error_gpio_request; + } + + oneseg_tunerpm_power_control(drvdata, 0); + oneseg_tunerpm_reset_control(drvdata, 0); + + return 0; + +error_gpio_request: + for (i--; i >= 0; i--) + gpio_free(drvdata->gpios[req_ids[i]]); +error_gpio: + return ret; +} + +static void oneseg_dev_finalize(struct oneseg_tuner_drvdata *drvdata) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(req_ids); i++) + gpio_free(drvdata->gpios[req_ids[i]]); +} + +static int tuner_drv_ctl_power(struct oneseg_tuner_drvdata *drvdata, int data) +{ + switch (data) { + case ONESEG_DRV_CTL_POWON: + mutex_lock(&drvdata->mutex_lock); + oneseg_tunerpm_power_control(drvdata, 1); + mutex_unlock(&drvdata->mutex_lock); + usleep_range(D_ONESEG_POWER_ON_WAIT_US, + D_ONESEG_POWER_ON_WAIT_RANGE_US); + break; + case ONESEG_DRV_CTL_RESET: + mutex_lock(&drvdata->mutex_lock); + oneseg_tunerpm_reset_control(drvdata, 1); + mutex_unlock(&drvdata->mutex_lock); + usleep_range(D_ONESEG_RESET_ON_WAIT_US, + D_ONESEG_RESET_ON_WAIT_RANGE_US); + break; + case ONESEG_DRV_CTL_POWOFF: + mutex_lock(&drvdata->mutex_lock); + usleep_range(D_ONESEG_RESET_OFF_WAIT_US, + D_ONESEG_RESET_OFF_WAIT_RANGE_US); + oneseg_tunerpm_reset_control(drvdata, 0); + oneseg_tunerpm_power_control(drvdata, 0); + mutex_unlock(&drvdata->mutex_lock); + break; + default: + return -EINVAL; + } + return 0; +} + +irqreturn_t tuner_interrupt(int irq, void *dev_id) +{ + if (oneseg_dev.irq_flag == ONESEG_DRV_IRQ_NOT_DETECTED) { + oneseg_dev.irq_flag = ONESEG_DRV_IRQ_DETECTED; + wake_up_interruptible(&oneseg_dev.irq_wait_q); + } + return IRQ_HANDLED; +} + +static int tuner_drv_set_interrupt(int int_num) +{ + int ret; + + oneseg_dev.irq_num = gpio_to_irq(int_num); + ret = request_threaded_irq(oneseg_dev.irq_num, tuner_interrupt, + NULL, IRQF_TRIGGER_RISING, D_ONESEG_CONFIG_CLASS_NAME, NULL); + if (ret) { + gpio_free(int_num); + return -EINVAL; + } + return 0; +} + +static void tuner_drv_release_interrupt(void) +{ + free_irq(oneseg_dev.irq_num, NULL); +} + +static ssize_t tuner_module_power_ctrl(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct oneseg_tuner_drvdata *drvdata = dev_get_drvdata(dev); + unsigned long value; + int ret; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + if (!value) { + ret = tuner_drv_ctl_power(drvdata, ONESEG_DRV_CTL_POWON); + if (ret) + return -EINVAL; + ret = tuner_drv_ctl_power(drvdata, ONESEG_DRV_CTL_RESET); + if (ret) { + tuner_drv_ctl_power(drvdata, ONESEG_DRV_CTL_POWOFF); + return -EINVAL; + } + } else { + tuner_drv_ctl_power(drvdata, ONESEG_DRV_CTL_POWOFF); + } + return count; +} + +static ssize_t tuner_module_irq_ctrl(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value; + struct oneseg_tuner_drvdata *drvdata = dev_get_drvdata(dev); + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + if (!value) { + if (tuner_drv_set_interrupt(drvdata->gpios[ONESEG_INT_PIN])) + return -EINVAL; + } else { + tuner_drv_release_interrupt(); + oneseg_dev.irq_flag = ONESEG_DRV_IRQ_NOT_DETECTED; + } + + return count; +} + +static ssize_t tuner_module_irq_detect_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + wait_event_interruptible(oneseg_dev.irq_wait_q, + (oneseg_dev.irq_flag == ONESEG_DRV_IRQ_DETECTED)); + return snprintf(buf, 1, "%d", oneseg_dev.irq_flag); +} + +static ssize_t tuner_module_irq_detect_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + oneseg_dev.irq_flag = ONESEG_DRV_IRQ_NOT_DETECTED; + return count; +} + +static int tuner_module_entry_open(struct inode *inode, struct file *file) +{ + if (oneseg_dev.open_cnt > 0) + return -EBUSY; + oneseg_dev.open_cnt++; + + oneseg_dev.irq_flag = ONESEG_DRV_IRQ_NOT_DETECTED; + + return 0; +} + +static int tuner_module_entry_close(struct inode *inode, struct file *file) +{ + struct devone_data *dev; + + if (oneseg_dev.open_cnt <= 0) + return -ESRCH; + oneseg_dev.open_cnt--; + + if (oneseg_dev.open_cnt == 0) { + if (!file) + return -ESRCH; + dev = file->private_data; + } + + return 0; +} + +static struct device_attribute tuner_sysfs_attrs[] = { + __ATTR(oneseg_power_ctrl, S_IWUSR, 0, tuner_module_power_ctrl), + __ATTR(oneseg_irq_ctrl, S_IWUSR, 0, tuner_module_irq_ctrl), + __ATTR(oneseg_irq, S_IWUSR | S_IRUSR, tuner_module_irq_detect_read, + tuner_module_irq_detect_write), +}; + +static const struct file_operations tuner_file_operations = { + .owner = THIS_MODULE, + .open = tuner_module_entry_open, + .release = tuner_module_entry_close +}; + +static int oneseg_tuner_probe(struct platform_device *pdev) +{ + int ret; + int i; + struct device *dev = NULL; + struct oneseg_tuner_drvdata *drvdata; + + oneseg_dev.onesegtuner_device = platform_device_alloc( + D_ONESEG_CONFIG_CLASS_NAME, -1); + + if (!oneseg_dev.onesegtuner_device) { + ret = -ENOMEM; + goto err_platform_device_alloc; + } + + ret = platform_device_add(oneseg_dev.onesegtuner_device); + if (ret) + goto err_platform_device_add; + + oneseg_dev.device_class = class_create(THIS_MODULE, + D_ONESEG_CONFIG_CLASS_NAME); + if (IS_ERR(oneseg_dev.device_class)) { + ret = PTR_ERR(oneseg_dev.device_class); + goto err_class_create; + } + + dev = device_create(oneseg_dev.device_class, NULL, + MKDEV(D_ONESEG_CONFIG_DRV_MAJOR, D_ONESEG_CONFIG_DRV_MINOR), + NULL, D_ONESEG_CONFIG_CLASS_NAME); + + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); + goto err_device_create; + } + + drvdata = kzalloc(sizeof(struct oneseg_tuner_drvdata), GFP_KERNEL); + if (!drvdata) { + ret = -ENOMEM; + goto err_alloc_data; + } + + drvdata->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, drvdata); + drvdata->sysfs_dev.init_name = D_ONESEG_CONFIG_SYSFS_DEV_NAME; + dev_set_drvdata(&drvdata->sysfs_dev, drvdata); + ret = device_register(&drvdata->sysfs_dev); + if (ret) + goto err_set_dev; + + ret = register_chrdev(D_ONESEG_CONFIG_DRV_MAJOR, + D_ONESEG_CONFIG_DRIVER_NAME, &tuner_file_operations); + if (ret < 0) + goto err_register_device; + + ret = oneseg_dev_init(pdev, drvdata); + if (ret) + goto err_gpio_init; + + oneseg_dev.open_cnt = 0; + + for (i = 0; i < ARRAY_SIZE(tuner_sysfs_attrs); i++) { + ret = device_create_file(&drvdata->sysfs_dev, + &tuner_sysfs_attrs[i]); + if (ret) { + for (; i >= 0; --i) + device_remove_file(&drvdata->sysfs_dev, + &tuner_sysfs_attrs[i]); + goto err_create_file; + } + } + + mutex_init(&oneseg_dev.g_tuner_mutex); + + init_waitqueue_head(&oneseg_dev.irq_wait_q); + + if (tuner_drv_set_interrupt(drvdata->gpios[ONESEG_INT_PIN])) + goto err_irq_set; + return 0; + +err_irq_set: +err_create_file: +err_gpio_init: + unregister_chrdev(D_ONESEG_CONFIG_DRV_MAJOR, + D_ONESEG_CONFIG_DRIVER_NAME); +err_register_device: + device_unregister(&drvdata->sysfs_dev); +err_set_dev: + kzfree(drvdata); +err_alloc_data: + device_destroy(oneseg_dev.device_class, MKDEV(D_ONESEG_CONFIG_DRV_MAJOR, + D_ONESEG_CONFIG_DRV_MINOR)); +err_device_create: + class_destroy(oneseg_dev.device_class); +err_class_create: + platform_device_del(oneseg_dev.onesegtuner_device); +err_platform_device_add: + platform_device_put(oneseg_dev.onesegtuner_device); +err_platform_device_alloc: + return ret; +} + +static int oneseg_tuner_remove(struct platform_device *pdev) +{ + struct oneseg_tuner_drvdata *drvdata = dev_get_drvdata(&pdev->dev); + + oneseg_dev_finalize(drvdata); + unregister_chrdev(D_ONESEG_CONFIG_DRV_MAJOR, + D_ONESEG_CONFIG_DRIVER_NAME); + + return 0; +} + +static void oneseg_tuner_shutdown(struct platform_device *pdev) +{ + device_destroy(oneseg_dev.device_class, MKDEV(D_ONESEG_CONFIG_DRV_MAJOR, + D_ONESEG_CONFIG_DRV_MINOR)); + class_destroy(oneseg_dev.device_class); + platform_device_unregister(oneseg_dev.onesegtuner_device); +} + +static const struct of_device_id vj190_match_table[] = { +{ .compatible = D_ONESEG_CONFIG_MATCH_TABLE, +}, +{} +}; + +static struct platform_driver onesegtuner_driver = { + .probe = oneseg_tuner_probe, + .remove = oneseg_tuner_remove, + .shutdown = oneseg_tuner_shutdown, + .driver = { + .name = D_ONESEG_CONFIG_PLATFORM_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = vj190_match_table, + }, +}; + +static int __init oneseg_drv_start(void) +{ + return platform_driver_register(&onesegtuner_driver); +} + +static void __exit oneseg_drv_end(void) +{ + platform_driver_unregister(&onesegtuner_driver); +} + +MODULE_LICENSE(D_ONESEG_CONFIG_MODULE_LICENCE); +MODULE_DESCRIPTION(D_ONESEG_CONFIG_MODULE_NAME); + +module_init(oneseg_drv_start); +module_exit(oneseg_drv_end); diff --git a/drivers/misc/powerkey_forcecrash.c b/drivers/misc/powerkey_forcecrash.c new file mode 100644 index 0000000000000000000000000000000000000000..a8f2ab62cc23a364b89dc042a6a78f86bef6b1cc --- /dev/null +++ b/drivers/misc/powerkey_forcecrash.c @@ -0,0 +1,168 @@ +/* + * drivers/misc/powerkey_forcecrash.c + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FORCE_CRASH_TIMEOUT 10 +static struct timer_list forcecrash_timer; + +#define PKEY_FORCECRASH_DEV_NAME "powerkey_forcecrash" +static struct wake_lock powerkey_lock; + +static int forcecrash_on; +module_param(forcecrash_on, int, S_IRUGO | S_IWUSR); + +static void forcecrash_timeout(unsigned long data) +{ + panic("Force crash triggered!!!\n"); +} + +static void forcecrash_timer_setup(bool key_pressed) +{ + if (!forcecrash_on) + return; + + if (key_pressed) { + pr_debug("Power key pressed..\n"); + mod_timer(&forcecrash_timer, + jiffies + FORCE_CRASH_TIMEOUT * HZ); + wake_lock(&powerkey_lock); + } else { + pr_debug("released.\n"); + del_timer(&forcecrash_timer); + wake_unlock(&powerkey_lock); + } +} + +static void powerkey_input_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + switch (code) { + case KEY_POWER: + forcecrash_timer_setup(value); + break; + default: + break; + } +} + +static const char *powerkey_match_tbl[] = { + "qpnp_pon", + NULL +}; + +static bool powerkey_input_match(struct input_handler *handler, + struct input_dev *dev) +{ + const char *match = powerkey_match_tbl[0]; + + while (*match) { + size_t len = strlen(match); + if (!strncmp(dev->name, match, len)) + return true; + match++; + } + + pr_info("Ignoring %s handle for %s\n", handler->name, dev->name); + + return false; +} + +static int powerkey_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "powerkey_handle"; + pr_info("registering %s handle for %s\n", handle->name, dev->name); + + error = input_register_handle(handle); + if (error) + goto err2; + + error = input_open_device(handle); + if (error) + goto err1; + + return 0; +err1: + input_unregister_handle(handle); +err2: + kfree(handle); + return error; +} + +static void powerkey_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id input_dev_ids[] = { + /* Only Powerkey inputs */ + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER) }, + }, + { }, +}; + +static struct input_handler powerkey_input_handler = { + .event = powerkey_input_event, + .match = powerkey_input_match, + .connect = powerkey_input_connect, + .disconnect = powerkey_input_disconnect, + .name = "powerkey_handler", + .id_table = input_dev_ids, +}; + +static int __init powerkey_forcecrash_init(void) +{ + init_timer(&forcecrash_timer); + forcecrash_timer.function = forcecrash_timeout; + wake_lock_init(&powerkey_lock, WAKE_LOCK_SUSPEND, + PKEY_FORCECRASH_DEV_NAME); + return input_register_handler(&powerkey_input_handler); +} + +static void __exit powerkey_forcecrash_exit(void) +{ + del_timer(&forcecrash_timer); + wake_lock_destroy(&powerkey_lock); + input_unregister_handler(&powerkey_input_handler); +} + +module_init(powerkey_forcecrash_init); +module_exit(powerkey_forcecrash_exit); + +MODULE_AUTHOR("Srinivasa Nagaraju "); +MODULE_DESCRIPTION("Force crash on power key long press of 10 secs"); +MODULE_LICENSE("GPL V2"); diff --git a/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c index 92faa1b899c9bf54ee7664a3b0a4c05743847417..77781622a214eb8cf4fc2c79f285baabebb18bd3 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c +++ b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c @@ -14,6 +14,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -110,7 +115,7 @@ static long audio_ioctl_shared(struct file *file, unsigned int cmd, pr_err("cmd media format block failed\n"); break; } - rc = q6asm_set_encdec_chan_map(audio->ac, 2); + rc = q6asm_set_encdec_chan_map(audio->ac, aac_cfg.ch_cfg); if (rc < 0) { pr_err("%s: cmd set encdec_chan_map failed\n", __func__); diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index f0140e8bbe688d0ace1c6c058e0337a4490cd7c5..5efe1c4bfb13ea8c29e27ecb2195cb9dac47c395 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -11,6 +11,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "QSEECOM: %s: " fmt, __func__ @@ -2560,6 +2565,11 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data, goto unload_exit; } + if (!memcmp(data->client.app_name, "tzxflattest", strlen("tzxflattest"))) { + pr_debug("Do not unload tzxflattest app from tz\n"); + goto unload_exit; + } + __qseecom_cleanup_app(data); __qseecom_reentrancy_check_if_no_app_blocked(TZ_OS_APP_SHUTDOWN_ID); diff --git a/drivers/misc/ramdump_mem_desc.c b/drivers/misc/ramdump_mem_desc.c new file mode 100644 index 0000000000000000000000000000000000000000..95fb81889df99db3446bc45a3d69f1fc2e1ac356 --- /dev/null +++ b/drivers/misc/ramdump_mem_desc.c @@ -0,0 +1,515 @@ +/* + * Author: Nandhakumar Rangasamy + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_HAVE_MEMBLOCK +#include +#endif + +static struct device *dev; +static void *mem_desc_base; +static size_t mem_desc_size; +static char *mem_desc_buf; +static unsigned int mem_desc_data_size; + +static DEFINE_MUTEX(mem_desc_lock); + +/*Random number*/ +#define RDTAGS_MEM_DESC_SIG 0x42972468 +#define DUMP_TABLE_OFFSET 0x5014 +#define MEM_DESC_MAX 64 +#define MEM_DESC_MASK 0xf + +struct mem_desc_hdr { + u32 sig; + u32 version; + u32 num_desc; + u32 reserved; +}; + +struct ramdump_mem_desc { + struct mem_desc_hdr hdr; + struct mem_desc desc[MEM_DESC_MAX]; +}; + +#define MEM_DESC_FORMAT_SIZE 114 +#define MEM_DESC_FORMAT "0x%016llx:0x%016llx:%s:%s:0x%08x\n" + +static int match_dev_by_volname(struct device *pdev, const void *data) +{ + char *volname = "cache"; + struct hd_struct *part = dev_to_part(pdev); + + if (!part->info) + goto no_match; + + if (strncmp(volname, part->info->volname, + sizeof(part->info->volname))) + goto no_match; + + return 1; +no_match: + return 0; +} + +static int get_ramdump_partition_index(void) +{ + struct device *pdev = NULL; + u8 uuid[16] = {0}; + int ret = -1; + + pdev = class_find_device(&block_class, NULL, &uuid, + match_dev_by_volname); + if (!pdev) { + dev_info(dev, "Partition device not found"); + return ret; + } + + return dev_to_part(pdev)->partno; +} + +static u32 mem_desc_update_flags(u64 start, u64 end, + struct ramdump_mem_desc *m_desc) +{ + int i, ret; + u32 flags; + + ret = get_ramdump_partition_index(); + if (ret < 0) + return 0; + + flags = ret << 9; + for (i = 0; i < m_desc->hdr.num_desc; i++) { + if (start >= m_desc->desc[i].phys_addr && + (end <= (m_desc->desc[i].phys_addr + + m_desc->desc[i].size))) + flags |= m_desc->desc[i].flags; + } + + return flags; +} + +static void mem_desc_split_sect_format( + u64 sort_buffer[], + unsigned int sort_count, + struct ramdump_mem_desc *m_desc) +{ + unsigned int i, j; + + for (j = 0; j < sort_count; j++) { + u32 flags = 0; u64 size = 0; + char *p_name = NULL, *c_name = NULL; + + if ((j+1) < sort_count) { + if (sort_buffer[j] == sort_buffer[j+1]) + continue; + } + + for (i = 0; i < m_desc->hdr.num_desc; i++) { + if (sort_buffer[j] >= m_desc->desc[i].phys_addr && + (sort_buffer[j] < (m_desc->desc[i].phys_addr + + m_desc->desc[i].size))) { + + size = sort_buffer[j + 1] - sort_buffer[j]; + flags = mem_desc_update_flags(sort_buffer[j], + sort_buffer[j+1], + m_desc); + + switch (m_desc->desc[i].flags & MEM_DESC_MASK) { + case MEM_DESC_PLATFORM: + if (p_name == NULL) + p_name = m_desc->desc[i].name; + else if ((!strncmp(p_name, + m_desc->desc[i].name, + sizeof(m_desc->desc[i].name))) + && + (m_desc->desc[i].flags >> 31)) + continue; + else { + mem_desc_data_size += snprintf( + (mem_desc_buf + + mem_desc_data_size), + MEM_DESC_FORMAT_SIZE, + MEM_DESC_FORMAT, + sort_buffer[j], + size, + m_desc->desc[i].name, + "NULL", + flags); + } + break; + case MEM_DESC_CORE: + c_name = m_desc->desc[i].name; + break; + } + } + } + if (p_name != NULL) { + mem_desc_data_size += snprintf( + (mem_desc_buf + + mem_desc_data_size), + MEM_DESC_FORMAT_SIZE, + MEM_DESC_FORMAT, + sort_buffer[j], + size, + p_name, + c_name ? c_name : "NULL", + flags); + } + } +} + +void ramdump_add_mem_desc(struct mem_desc *desc) +{ + struct ramdump_mem_desc *m_desc = NULL; + unsigned int offset; + + if (!mem_desc_base) { + dev_info(dev, "Adding mem_desc failed\n"); + return; + } + + mutex_lock(&mem_desc_lock); + m_desc = (struct ramdump_mem_desc *)mem_desc_base; + offset = sizeof(struct mem_desc_hdr) + + m_desc->hdr.num_desc * sizeof(struct mem_desc); + + if (m_desc->hdr.num_desc >= MEM_DESC_MAX) + goto exit; + + if ((offset + sizeof(struct mem_desc)) > mem_desc_size) + goto exit; + + memcpy_toio( + ((struct ramdump_mem_desc *)(((unsigned long)m_desc) + offset)), + desc, sizeof(struct mem_desc)); + + m_desc->hdr.num_desc++; +exit: + mutex_unlock(&mem_desc_lock); +} +EXPORT_SYMBOL(ramdump_add_mem_desc); + +void ramdump_remove_mem_desc(struct mem_desc *desc) +{ + struct ramdump_mem_desc *m_desc = NULL; + struct ramdump_mem_desc *m_desc_buf = NULL; + unsigned int i, offset, m_desc_size; + + if (!mem_desc_base) { + dev_info(dev, "Removing mem_desc failed\n"); + return; + } + + mutex_lock(&mem_desc_lock); + m_desc = (struct ramdump_mem_desc *)mem_desc_base; + m_desc_size = sizeof(struct mem_desc_hdr) + + m_desc->hdr.num_desc * sizeof(struct mem_desc); + + m_desc_buf = vmalloc(m_desc_size); + if (!m_desc_buf) { + dev_err(dev, "Failed to allocate mem_desc buffer\n"); + goto exit; + } + + memcpy_fromio(m_desc_buf, mem_desc_base, m_desc_size); + offset = sizeof(struct mem_desc_hdr) + + m_desc_buf->hdr.num_desc * sizeof(struct mem_desc); + + for (i = 0; i < m_desc_buf->hdr.num_desc; i++) { + if ((desc->phys_addr == m_desc_buf->desc[i].phys_addr) && + !strncmp(desc->name, m_desc_buf->desc[i].name, + sizeof(m_desc_buf->desc[i].name))) { + memset(&m_desc_buf->desc[i], 0, + sizeof(struct mem_desc)); + memmove(&m_desc_buf->desc[i], + &m_desc_buf->desc[i + 1], + (m_desc_buf->hdr.num_desc - i - 1) + * sizeof(struct mem_desc)); + m_desc_buf->hdr.num_desc--; + } + } + + memset_io(m_desc, 0, m_desc_size); + memcpy_toio(m_desc, m_desc_buf, m_desc_size); + vfree(m_desc_buf); +exit: + mutex_unlock(&mem_desc_lock); +} +EXPORT_SYMBOL(ramdump_remove_mem_desc); + +static int cmp_sort_addr(const void *a, const void *b) +{ + u64 x = *(u64 *)a; + u64 y = *(u64 *)b; + int ret = 0; + + if (x < y) + ret = -1; + if (x > y) + ret = 1; + + return ret; +} + +static void get_mem_desc(void) +{ + unsigned int count = 0; + struct ramdump_mem_desc *m_desc = + (struct ramdump_mem_desc *)mem_desc_base; + unsigned int i, num_desc; + + num_desc = (m_desc->hdr.num_desc < MEM_DESC_MAX) ? + m_desc->hdr.num_desc : MEM_DESC_MAX; + + if (m_desc->hdr.sig == RDTAGS_MEM_DESC_SIG) { + u64 sort_buffer[MEM_DESC_MAX << 1]; + for (i = 0; i < num_desc; i++) { + u64 start, end; + start = m_desc->desc[i].phys_addr; + end = m_desc->desc[i].phys_addr + m_desc->desc[i].size; + + memcpy(&sort_buffer[count++], &start, + sizeof(u64)); + memcpy(&sort_buffer[count++], &end, + sizeof(u64)); + } + + sort(sort_buffer, count, sizeof(u64), + cmp_sort_addr, NULL); + mem_desc_split_sect_format(sort_buffer, count, m_desc); + } +} + +#ifdef CONFIG_HAVE_MEMBLOCK +static void add_memblock_info(void) +{ + int i; + struct memblock_type *block = &memblock.memory; + + for (i = 0; i < block->cnt; i++) { + struct mem_desc desc; + + desc.phys_addr = (u64)block->regions[i].base; + desc.size = (u64)block->regions[i].size; + desc.flags = MEM_DESC_CORE; + strlcpy(desc.name, "vmcore", sizeof(desc.name)); + ramdump_add_mem_desc(&desc); + } +} +#else +static void add_ioresource(struct resource *res, + char *iores_name, + char *core_name) +{ + while (res != NULL) { + if (res->child != NULL) + add_ioresource(res->child, iores_name, core_name); + + if ((res->flags & IORESOURCE_MEM) && + strcmp(res->name, iores_name) == 0) { + if (core_name != NULL) { + struct mem_desc desc; + + desc.phys_addr = (u64)res->start; + desc.size = + ((u64)res->end - (u64)res->start) + 1; + desc.flags = MEM_DESC_CORE; + strlcpy(desc.name, core_name, + sizeof(desc.name)); + ramdump_add_mem_desc(&desc); + } + } + res = res->sibling; + } +} +#endif + +static void mem_desc_add_linux_meminfo(void) +{ +#ifdef CONFIG_HAVE_MEMBLOCK + add_memblock_info(); +#else + add_ioresource(&iomem_resource, "System RAM", "vmcore"); +#endif +} + +static ssize_t mem_desc_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + loff_t pos = *offset; + ssize_t count; + + if (pos == 0) + get_mem_desc(); + + if (pos >= mem_desc_data_size) { + memset_io(mem_desc_base, 0x0, mem_desc_data_size); + return 0; + } + + count = min(len, (size_t)(mem_desc_data_size - pos)); + if (copy_to_user(buf, mem_desc_buf + pos, count)) + return -EFAULT; + + *offset += count; + return count; +} + +static const struct file_operations mem_desc_file_ops = { + .owner = THIS_MODULE, + .read = mem_desc_read, +}; + +#define MAX_DESC_PER_TYPE 32 +static int mem_desc_init(void) +{ + int ret = 0; + struct proc_dir_entry *subentry_mem_desc; + + mem_desc_buf = kmalloc((MEM_DESC_FORMAT_SIZE * 3 * MAX_DESC_PER_TYPE), + GFP_KERNEL); + if (mem_desc_buf == NULL) { + dev_err(dev, "Failed to allocte memory regs_buf\n"); + ret = -ENOMEM; + goto exit; + } + + subentry_mem_desc = proc_create_data("mem_desc", + S_IFREG | S_IRUGO, NULL, + &mem_desc_file_ops, NULL); + if (!subentry_mem_desc) { + dev_err(dev, "Failed to create proc subentry mem_desc\n"); + ret = -1; + goto exit1; + } + + return ret; + +exit1: + kfree(mem_desc_buf); + mem_desc_buf = NULL; +exit: + return ret; +} + +static int is_ramdump_mode(void) +{ + return *((int *)(dev->platform_data)); +} + +static int mem_desc_driver_probe(struct platform_device *pdev) +{ + struct resource *res_mem_desc; + struct ramdump_mem_desc *m_desc; + void *mem_desc_end; + int ret = 0; + + dev = &pdev->dev; + + res_mem_desc = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "ramdump_memdesc"); + if (!res_mem_desc || !res_mem_desc->start) { + dev_err(dev, + "Ramdump tags driver mem desc resource" + " invalid/absent\n"); + ret = -ENODEV; + goto exit; + } + + mem_desc_size = res_mem_desc->end + - res_mem_desc->start + 1; + mem_desc_base = (void *)ioremap(res_mem_desc->start, + mem_desc_size); + if (!mem_desc_base) { + dev_err(dev, "Failed to mem desc map %ld bytes at 0x%08llx\n", + mem_desc_size, res_mem_desc->start); + ret = -EINVAL; + goto exit; + } + + mem_desc_end = mem_desc_base + mem_desc_size; + dev_info(dev, "res_mem_desc_base = 0x%08llx mem_desc size = %ld\n", + res_mem_desc->start, mem_desc_size); + m_desc = (struct ramdump_mem_desc *)mem_desc_base; + + dev_info(dev, "ramdump_mode = %s\n", + is_ramdump_mode() ? "ramdump" : "normal"); + + if (!is_ramdump_mode()) { + memset_io(mem_desc_base, 0x0, mem_desc_size); + m_desc->hdr.sig = RDTAGS_MEM_DESC_SIG; + mem_desc_add_linux_meminfo(); + } else { + if (m_desc->hdr.sig == RDTAGS_MEM_DESC_SIG) { + dev_info(dev, "Found memory descriptors\n"); + if (mem_desc_init() != 0) + goto exit; + } else { + dev_info(dev, "NO valid memory descriptors found!!\n"); + ret = -EINVAL; + goto exit; + } + } + + return 0; +exit: + if (mem_desc_base) { + iounmap(mem_desc_base); + mem_desc_base = NULL; + } + + return ret; +} + +static struct platform_driver mem_desc_driver = { + .probe = mem_desc_driver_probe, + .driver = { + .name = "ramdump_memdesc", + }, +}; + +static int __init mem_desc_core_init(void) +{ + return platform_driver_register(&mem_desc_driver); +} + +static void __exit mem_desc_module_exit(void) +{ + if (mem_desc_base) { + iounmap(mem_desc_base); + mem_desc_base = NULL; + } + + platform_driver_unregister(&mem_desc_driver); +} + +core_initcall(mem_desc_core_init); +module_exit(mem_desc_module_exit); diff --git a/drivers/misc/rdtags.c b/drivers/misc/rdtags.c new file mode 100644 index 0000000000000000000000000000000000000000..0c40b2ff6c64cf5a3b411da2d18a49b521700e7a --- /dev/null +++ b/drivers/misc/rdtags.c @@ -0,0 +1,1176 @@ +/* + * + * Author: Nilsson, Stefan 2 + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RDTAGS_SIG 0x47415452 +#define RDTAGS_NAME_SIZE 28 +#define RDTAGS_ALIGNMENT 64 + +#define RDTAGS_PROC_NODE_NAME "rdtag" +#define RDTAGS_PROC_DIR_NAME "rdtags" +#define LAST_RDTAGS_PROC_DIR_NAME "last_rdtags" + +static struct device *dev; +static struct proc_dir_entry *entry; +static void *rdtags_io_base; +static void *rdtags_base; +static void *rdtags_end; +static void *last_rdtags_base; +static void *last_rdtags_end; +static struct proc_dir_entry *last_entry; +static size_t rdtags_size; +static DEFINE_MUTEX(rdlock); +static uint8_t rdtags_initialized; + +struct rtag_head { + uint32_t sig; + uint32_t data_size; + uint8_t name[RDTAGS_NAME_SIZE]; + uint8_t null_term; /* Shall be 0 to preserve compatibility */ + uint8_t flags; + uint16_t crc; + uint8_t data[0]; +}; + +enum procfs_cmd { + PROCFS_CMD_ADD, + PROCFS_CMD_DELETE, +}; + +struct procfs_fifo_item { + enum procfs_cmd cmd; + char name[RDTAGS_NAME_SIZE]; +}; + +#define PROCFS_FIFO_SIZE 64 +static DEFINE_KFIFO(procfs_fifo, struct procfs_fifo_item, PROCFS_FIFO_SIZE); + +static void procfs_work_func(struct work_struct *work); +static DECLARE_WORK(procfs_work, procfs_work_func); + +#define PROCFS_ASYNC_ADD(x) procfs_async_cmd((x), PROCFS_CMD_ADD) +#define PROCFS_ASYNC_DELETE(x) procfs_async_cmd((x), PROCFS_CMD_DELETE) + +#define SLEEP_TIME_ASYNC_FINISH 10 /* ms */ + +#define MAX(X, Y) ((X) >= (Y) ? (X) : (Y)) +#define MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) + +#define RDTAGS_NEXT_TAG(x) ((struct rtag_head *) \ + ALIGN((unsigned long)x + x->data_size + \ + sizeof(*x), RDTAGS_ALIGNMENT)) + +#define RDTAGS_BLK_SIZE(x) ALIGN(x->data_size + sizeof(*x), RDTAGS_ALIGNMENT) +#define RDTAGS_BASE_VALID(x) ((void *)x >= rdtags_base && \ + (void *)x < rdtags_end) +#define LAST_RDTAGS_BASE_VALID(x) ((void *)x >= last_rdtags_base && \ + (void *)x < last_rdtags_end) + +#define RDTAGS_ADDR_VALID(x) (RDTAGS_BASE_VALID(x) || LAST_RDTAGS_BASE_VALID(x)) + +#define CRC_SIZE 2 /* bytes */ +#define RDTAG_FLAGS_CRC_PRESENT 0x1 + +static ssize_t tag_read(struct file *file, char __user *buf, size_t size, + loff_t *off); + +static const struct file_operations tag_fops = { + .read = tag_read, +}; + +static int procfs_create_node(char *name) +{ + struct proc_dir_entry *subentry; + + if (!entry) + return -ENXIO; + + subentry = proc_create_data(name, S_IFREG | S_IRUSR, entry, &tag_fops, + (void *)name); + if (!subentry) + return -ENOMEM; + + + return 0; +} + +static int procfs_delete_node(char *name) +{ + if (!entry) + return -ENXIO; + + remove_proc_entry(name, entry); + + return 0; +} + +static void procfs_work_func(struct work_struct *work) +{ + struct procfs_fifo_item item; + + while (kfifo_get(&procfs_fifo, &item)) { + int ret; + char *name = (char *)&item.name; + + dev_dbg(dev, "wq: Processing request %s %s\n", + item.cmd == PROCFS_CMD_ADD ? "ADD" : "DELETE", name); + + switch (item.cmd) { + case PROCFS_CMD_ADD: + ret = procfs_create_node(name); + if (ret < 0) + dev_err(dev, "wq: Failed to create proc" \ + "subentry \"%s\": %d\n", name, ret); + break; + case PROCFS_CMD_DELETE: + ret = procfs_delete_node(name); + if (ret < 0) + dev_err(dev, "wq: Failed to remove proc" \ + "subentry \"%s\": %d\n", name, ret); + break; + default: + dev_err(dev, "wq: Unknown request: %d for %s\n", + item.cmd, name); + } + } +} + +static void procfs_async_cmd(char *name, enum procfs_cmd cmd) +{ + struct procfs_fifo_item item; + + if (unlikely(kfifo_is_full(&procfs_fifo))) { + dev_err(dev, "procfs FIFO buffer overflow! procfs will" \ + "be out of sync!\n"); + return; + } + item.cmd = cmd; + strlcpy(item.name, name, RDTAGS_NAME_SIZE); + dev_dbg(dev, "procfs WQ request: %s %s\n", + cmd == PROCFS_CMD_ADD ? "ADD" : "DELETE", name); + kfifo_put(&procfs_fifo, item); + schedule_work(&procfs_work); +} + +static void rdtags_flush(void) +{ + if (rdtags_io_base && rdtags_base) + memcpy_toio(rdtags_io_base, rdtags_base, rdtags_size); +} + +/* + * Calculates the CRC for a RDTAG. Both headers and data is covered by the CRC. + * + * mt: Pointer to a valid (size checked) RDTAG + * + * Returns: The calculated CRC + */ +static uint16_t calc_crc(struct rtag_head *mt) +{ + uint16_t crc; + + /* Calculate CRC of header (minus crc field) */ + crc = crc16(0, (char *)mt, sizeof(struct rtag_head) - CRC_SIZE); + + /* Update the crc with the data part */ + crc = crc16(crc, mt->data, mt->data_size); + + return crc; +} + +/* + * Updates a RDTAG with a correct CRC + * + * mt: Pointer to a RDTAG + * + */ +static void update_crc(struct rtag_head *mt) +{ + /* First do some basic sanity checking */ + void *tag_end = (void *)(((unsigned long)mt) + sizeof(*mt) + + mt->data_size); + if (!RDTAGS_BASE_VALID(mt) || + !RDTAGS_BASE_VALID((tag_end)) || + (mt->data_size > rdtags_size)) { + /* Unable to calculate CRC */ + return; + } + + /* Update the rdtag with correct CRC value */ + mt->null_term = 0; + mt->flags = RDTAG_FLAGS_CRC_PRESENT; + + /* Calculate the crc */ + mt->crc = calc_crc(mt); +} + +/* + * Verifies the CRC of a tag + * + * mt: Pointer to a RDTAG + * + * Returns 1 on successful CRC verification + * Returns 0 if CRC field is missing (earlier version of RDTAGS) + * Returns -EILSEQ if CRC is present and verfication fails, or an unexpected + * error is encountered. + */ +static int verify_crc(struct rtag_head *mt) +{ + uint16_t crc; + uint16_t rdtag_crc; + + /* First do some basic sanity checking */ + void *tag_end = (void *)(((unsigned long)mt) + sizeof(*mt) + + mt->data_size); + if (!RDTAGS_ADDR_VALID(mt) || + !RDTAGS_ADDR_VALID((tag_end)) || + (mt->data_size > rdtags_size)) { + dev_err(dev, "Tag fails basic sanity check, " \ + "clearing rest of rdtags area!\n"); + goto error; + } + + /* Get CRC from tag */ + rdtag_crc = mt->flags & RDTAG_FLAGS_CRC_PRESENT ? mt->crc : 0; + if (rdtag_crc == 0) { + /* No CRC present, probably an older version of rdtags */ + return 0; + } + + /* Calculate expected CRC and compare */ + crc = calc_crc(mt); + if (crc == rdtag_crc) + return 1; + + dev_err(dev, "Tag fails CRC check, suspected memory corruption, " \ + "clearing rest of rdtags area!\n"); +error: + if (LAST_RDTAGS_BASE_VALID(mt)) { + memset(mt, 0x0, last_rdtags_end - (void *)mt); + } + + if (RDTAGS_BASE_VALID(mt)) { + memset(mt, 0x0, rdtags_end - (void *)mt); + rdtags_flush(); + } + + return -EILSEQ; +} + +static char *get_valid_name(char *name) +{ + char *temp; + + if (!name) + return NULL; + + /* Make sure the name is not longer than RDTAGS_NAME_SIZE */ + if (RDTAGS_NAME_SIZE == strnlen(name, RDTAGS_NAME_SIZE)) + name[RDTAGS_NAME_SIZE - 1] = 0x0; + + /* Check the name for disallowed characters */ + temp = strpbrk(name, "\r\n /"); + if (temp) + *temp = 0x0; + + /* Check that the name still actually contains something */ + if (name[0] == 0x0) + return NULL; + + return name; +} + +static struct rtag_head *get_next_free(void) +{ + struct rtag_head *mt = (struct rtag_head *)rdtags_base; + + while (mt->sig == RDTAGS_SIG) { + + /* Verify CRC of tag, if fail, it can be overwritten */ + if (verify_crc(mt) < 0) + return mt; + + /* Go to next tag */ + mt = RDTAGS_NEXT_TAG(mt); + + /* Check that we are not outside the buffer */ + if (!RDTAGS_BASE_VALID(mt)) + return NULL; + } + + return mt; +} + +static struct rtag_head *get_tag(const char *name) +{ + struct rtag_head *mt = (struct rtag_head *)rdtags_base; + + while (mt->sig == RDTAGS_SIG) { + + /* Verify CRC of tag */ + if (verify_crc(mt) < 0) + return NULL; + + if (strncmp(name, mt->name, RDTAGS_NAME_SIZE) == 0) + return mt; + + /* Go to next tag */ + mt = RDTAGS_NEXT_TAG(mt); + + /* Check that we are not outside the buffer */ + if (!RDTAGS_BASE_VALID(mt)) + return NULL; + } + + return NULL; +} + +static void _remove_tag(struct rtag_head *mt) +{ + struct rtag_head *mt_next, *mt_free; + size_t size_mv, size_clr; + + if (!RDTAGS_BASE_VALID(mt)) + return; + /* + * we need to get some address & size for recompacting tags: + * |- size_mv -| + * -------------------------------------------------------------- + * |rdtags_base|...| mt |mt_next|...|mt_free|...|rdtags_end| + * -------------------------------------------------------------- + * |-size_clr-| + */ + mt_next = RDTAGS_NEXT_TAG(mt); + if (!RDTAGS_BASE_VALID(mt_next)) + mt_next = (struct rtag_head *)rdtags_end; + + mt_free = get_next_free(); + if (NULL == mt_free) + mt_free = (struct rtag_head *)rdtags_end; + size_mv = ((unsigned long)mt_free) - ((unsigned long)mt_next); + size_clr = RDTAGS_BLK_SIZE(mt); + + /* Remove procfs interface asynchronously */ + PROCFS_ASYNC_DELETE(mt->name); + + /* move rear tags */ + memmove((void *)mt, (void *)mt_next, size_mv); + + /* clear vacated memory */ + if ((size_clr <= rdtags_size) && + ((void *)(unsigned long)mt_free - size_clr) >= rdtags_base) { + memset((void *)(((unsigned long)mt_free) - size_clr), 0, + size_clr); + } + + rdtags_flush(); +} + +/* + * Removes a tag + * + * name: Name of tag to remove + * + * Returns 0 on success or a negative error code on failure + */ +int rdtags_remove_tag(const char *name) +{ + struct rtag_head *mt; + int ret = 0; + + if (!name) + return -EINVAL; + + if (!rdtags_initialized) + return -ENODEV; + + dev_dbg(dev, "Removing tag \"%s\"\n", name); + + mutex_lock(&rdlock); + + /* Get the tag */ + mt = get_tag(name); + if (!mt) { + ret = -ENOENT; + goto exit; + } + + _remove_tag(mt); + +exit: + mutex_unlock(&rdlock); + + if (ret) + dev_err(dev, "Could not remove tag \"%s\"\n", name); + else + dev_dbg(dev, "Removed tag \"%s\"\n", name); + + return ret; +} +EXPORT_SYMBOL(rdtags_remove_tag); + +static int _add_tag(const char *name, const unsigned char *data, size_t size) +{ + struct rtag_head *mt = get_next_free(); + void *tag_end = (void *)(((unsigned long)mt) + sizeof(*mt) + size); + + if (!mt || (tag_end > rdtags_end)) { + /* We have run out of tag space */ + dev_err(dev, "Out of tag space! Could not add tag \"%s\" " \ + "with %zd bytes of data!\n", name, size); + return -ENOMEM; + } + + mt->sig = RDTAGS_SIG; + strlcpy(mt->name, name, RDTAGS_NAME_SIZE); + memcpy(mt->data, data, size); + mt->data_size = size; + update_crc(mt); + + /* Add procfs interface asynchronously */ + PROCFS_ASYNC_ADD(mt->name); + + rdtags_flush(); + + return 0; +} + +static int _update_tag(struct rtag_head *mt, const unsigned char *data, + size_t size) +{ + char name[RDTAGS_NAME_SIZE]; + struct rtag_head *mt_free; + size_t free_size; + + if (!mt || mt->sig != RDTAGS_SIG) { + dev_err(dev, "Not a valid RTAG!\n"); + return -ENOENT; + } + + /* If the size is identical, we can update the previous tag */ + if (size == mt->data_size) { + dev_dbg(dev, "Updating tag \"%s\"\n", mt->name); + memcpy(mt->data, data, size); + update_crc(mt); + return 0; + } + + /* compute available memory size if recompact tags */ + free_size = RDTAGS_BLK_SIZE(mt); + mt_free = get_next_free(); + if (NULL == mt_free) + mt_free = (struct rtag_head *)rdtags_end; + free_size += ((unsigned long)rdtags_end) - ((unsigned long)mt_free); + + /* make sure the new tag fits before removing the old one */ + if (free_size < size + sizeof(*mt)) { + dev_err(dev, "No enough memory, abort updating tag \"%s\"\n", + mt->name); + return -ENOMEM; + } else { + dev_dbg(dev, "Rewriting tag \"%s\"\n", mt->name); + strlcpy(name, mt->name, RDTAGS_NAME_SIZE); + _remove_tag(mt); + + return _add_tag(name, data, size); + } +} + +/* + * append data onto tag + * + * Note that if the tag name already exists, the existing tag + * will be updated (names must be unique). + * + * name: Name of tag to add + * data: Pointer to the data to add + * size: Size of the data to add + * + * Returns 0 on success or a negative error code on failure + */ +int rdtags_append_tagdata(const char *name, const unsigned char *data, + const uint32_t size) +{ + struct rtag_head *mt; + int ret = 0; + + if (!name || !data || size == 0) + return -EINVAL; + + if (!rdtags_initialized) + return -ENODEV; + + dev_dbg(dev, "Appending tag \"%s\"\n", name); + + mutex_lock(&rdlock); + + /* First check if the tag exists */ + mt = get_tag(name); + if (mt != NULL) { + char *kbuf; + unsigned int totalsize = 0; + + totalsize = size + mt->data_size; + + kbuf = vmalloc(totalsize); + if (NULL == kbuf) { + dev_err(dev, "Unable to assign memory.\n"); + ret = -ENOMEM; + goto exit; + } + + /* Update the tag */ + memcpy(kbuf, mt->data, mt->data_size); + memcpy((kbuf + mt->data_size), data, size); + ret = _update_tag(mt, kbuf, totalsize); + vfree(kbuf); + goto exit; + } + + /* Add the tag */ + ret = _add_tag(name, data, size); +exit: + mutex_unlock(&rdlock); + if (ret) + dev_err(dev, "Could not add/update tag \"%s\" with %d bytes of data\n\n", + name, size); + else + dev_dbg(dev, "Added/updated tag \"%s\" with %d bytes of data\n\n", + name, size); + + return ret; +} +EXPORT_SYMBOL(rdtags_append_tagdata); + +/* + * Adds a tag + * + * Note that if the tag name already exists, the existing tag + * will be updated (names must be unique). + * + * name: Name of tag to add + * data: Pointer to the data to add + * size: Size of the data to add + * + * Returns 0 on success or a negative error code on failure + */ +int rdtags_add_tag(const char *name, const unsigned char *data, + const size_t size) +{ + struct rtag_head *mt; + int ret = 0; + + if (!name || !data || size == 0) + return -EINVAL; + + if (!rdtags_initialized) + return -ENODEV; + + dev_dbg(dev, "Adding tag \"%s\"\n", name); + + mutex_lock(&rdlock); + + /* First check if the tag exists */ + mt = get_tag(name); + + if (mt != NULL) { + /* Update the tag */ + ret = _update_tag(mt, data, size); + goto exit; + } + + /* Add the tag */ + ret = _add_tag(name, data, size); +exit: + mutex_unlock(&rdlock); + + if (ret) + dev_err(dev, "Could not add/update tag \"%s\" with %zd" \ + "bytes of data\n\n", name, size); + else + dev_dbg(dev, "Added/updated tag \"%s\" with %zd" \ + "bytes of data\n\n", name, size); + + return ret; +} +EXPORT_SYMBOL(rdtags_add_tag); + +/* + * Gets the data from a tag + * + * name: Name of tag to get data from + * data: Pointer to the buffer to receive the data + * size: Size of the buffer to receive the data. Will be updated with the + * actual size of the data. + * + * Returns a negative error code or 0 on success + */ +int rdtags_get_tag_data(const char *name, unsigned char *data, size_t *size) +{ + struct rtag_head *mt; + int ret = 0; + + if (!name || !size) + return -EINVAL; + + if (!rdtags_initialized) + return -ENODEV; + + mutex_lock(&rdlock); + + /* First check if the tag exists */ + mt = get_tag(name); + + if (mt == NULL) { + ret = -ENOENT; + goto error; + } + + /* Check if the buffer is valid and that it is large enough */ + if (!data || (*size < mt->data_size)) { + /* Update "size" with the required size */ + *size = mt->data_size; + ret = -ENOBUFS; + goto error; + } + + /* Copy the data and update the size */ + memcpy(data, mt->data, mt->data_size); + *size = mt->data_size; +error: + mutex_unlock(&rdlock); + if (ret == -ENOBUFS) + dev_dbg(dev, "Returning size %zd for tag \"%s\"!\n", + *size, name); + else if (ret) + dev_err(dev, "Could not get data for tag \"%s\": %d!\n", + name, ret); + else + dev_dbg(dev, "Read data of %zd bytes for tag \"%s\"!\n", + *size, name); + + return ret; +} +EXPORT_SYMBOL(rdtags_get_tag_data); + +/* + * Clears all tags and "reformats" the entire tag area + */ +void rdtags_clear_tags(void) +{ + struct rtag_head *mt = (struct rtag_head *)rdtags_base; + + if (!rdtags_initialized) { + dev_err(dev, "Not yet initialized, cannot clear!\n"); + return; + } + + dev_dbg(dev, "Clearing rdtags!\n"); + + mutex_lock(&rdlock); + + /* Go through all tags and remove their procfs nodes */ + while (mt->sig == RDTAGS_SIG) { + /* Verify tag integrity */ + if (verify_crc(mt) < 0) + break; + + /* Remove the procfs entry for the tag */ + PROCFS_ASYNC_DELETE(mt->name); + + mt = RDTAGS_NEXT_TAG(mt); + } + + /* Finally reset the entire area to make it clean */ + memset(rdtags_base, 0x0, rdtags_size); + rdtags_flush(); + mutex_unlock(&rdlock); +} +EXPORT_SYMBOL(rdtags_clear_tags); + +static int rebuild_tag_tree(const unsigned char *buf, size_t size) +{ + struct rtag_head *mt = (struct rtag_head *)buf; + int count = 0; + + dev_dbg(dev, "Building tag tree\n"); + + while (mt && mt->sig == RDTAGS_SIG) { + /* Verify tag integrity */ + if (verify_crc(mt) < 0) + break; + + if (!get_valid_name(mt->name)) { + dev_warn(dev, "Found tag with invalid name!" \ + "Skipping it!\n"); + goto loop_next; + } + + count++; + dev_dbg(dev, " Found tag: \"%s\" - with %d bytes of data\n", + mt->name, mt->data_size); + + /* Add procfs interface synchronously */ + if (procfs_create_node(mt->name) < 0) + dev_warn(dev, "Failed to create proc subentry \"%s\"\n", + mt->name); +loop_next: + /* Go to next tag */ + mt = RDTAGS_NEXT_TAG(mt); + + /* Check that we are not outside the buffer */ + if (!RDTAGS_BASE_VALID(mt) && !LAST_RDTAGS_BASE_VALID(mt)) + break; + } + + return count; +} + +static ssize_t last_tags_read(struct file *file, char __user *ubuf, + size_t len, loff_t *offset) +{ + loff_t pos = *offset; + ssize_t count = 0; + struct rtag_head *mt = (struct rtag_head *)last_rdtags_base; + char *name = file->f_path.dentry->d_iname; + + while (mt->sig == RDTAGS_SIG) { + + if (strncmp(name, mt->name, RDTAGS_NAME_SIZE) == 0) { + if ((size_t)pos >= mt->data_size) + return 0; + count = min(len, (size_t)(mt->data_size - pos)); + if (copy_to_user(ubuf, mt->data + pos, count)) + return -EFAULT; + break; + } + + /* Go to next tag */ + mt = RDTAGS_NEXT_TAG(mt); + + /* Check that we are not outside the buffer */ + if (!LAST_RDTAGS_BASE_VALID(mt)) + return 0; + } + + *offset += count; + return count; +} + +static ssize_t tag_read (struct file *file, char __user *ubuf, size_t len, + loff_t *offset) +{ + ssize_t count; + loff_t pos = *offset; + size_t bufsize; + unsigned char *buf; + char *name = file->f_path.dentry->d_iname; + char *dir = file->f_path.dentry->d_parent ? + file->f_path.dentry->d_parent->d_iname : NULL; + int ret; + + if (!name || !dir) + return 0; + + /* check for last_rdtags list */ + if (!strncmp(dir, "last_rdtags", 11)) + return last_tags_read(file, ubuf, len, offset); + + + /* Get the size of the required data buffer */ + if (rdtags_get_tag_data(name, NULL, &bufsize) != -ENOBUFS) { + dev_err(dev, "Could not find tag \"%s\"!\n", + name ? name : "NULL"); + return 0; + } + + buf = kzalloc(bufsize, GFP_KERNEL); + if (!buf) { + dev_err(dev, "Could not allocate %zd bytes of memory!\n", + bufsize); + return 0; + } + + /* + * Fill the buffer with data. + * This assumes that the tag size has not changed since the previous + * call to rdtags_get_tag_data. If it has, this call will fail, and + * the caller has to re-read the tag. + */ + ret = rdtags_get_tag_data(name, buf, &bufsize); + if (ret) { + dev_err(dev, "Could not get %zd bytes of data for" \ + "tag \"%s\": %d!\n", bufsize, name, ret); + kfree(buf); + return 0; + } + + count = MIN(len, bufsize - pos); + if (copy_to_user(ubuf, buf + pos, count)) { + kfree(buf); + return -EFAULT; + } + + kfree(buf); + *offset += count; + return count; +} + +static ssize_t tags_read(struct file *file, char __user *ubuf, size_t len, + loff_t *offset) +{ + /* Assume that this text always fits in count bytes */ + char *message = "Usage: [tag data]\n" + "\n" + "command may be one of the following:\n" + " * clear\n" + " * append \n" + " * delete \n"; + loff_t pos = *offset; + len = strlen(message); + if (pos >= len) + return 0; + + if (copy_to_user(ubuf, message, len)) + return -EFAULT; + + *offset += len; + + return len; +} + +static ssize_t tags_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *offset) +{ + char *tag_data; + int tag_size; + void *kbuf; + char *tag_append_data; + + if (0 == count) + return 0; + + kbuf = kmalloc(count + 1, GFP_KERNEL); + if (NULL == kbuf) { + dev_err(dev, "Unable to assign memory.\n"); + return -ENOMEM; + } + + /* Copy to kernel space */ + tag_size = copy_from_user(kbuf, ubuf, count); + + /* NULL terminate is needed, since we will handle strings on kbuf */ + *((char *)(kbuf + count)) = 0x00; + + if (tag_size > 0) { + dev_err(dev, "Unable to copy %d bytes from user space!\n", + (int)tag_size); + goto exit; + } + + /* Check for special single commands */ + if (strncmp(kbuf, "clear", 5) == 0) { + rdtags_clear_tags(); + goto exit; + } + + /* Find delimiter */ + tag_data = strnchr(kbuf, RDTAGS_NAME_SIZE, ' '); + + if (!tag_data) { + dev_err(dev, "Incorrect format, please supply a string of " \ + "format: \n"); + goto exit; + } + + /* Null terminate name at delimiter and increment tag_data pointer */ + *tag_data++ = 0x0; + + /* Do some basic sanity checking */ + if (!get_valid_name(kbuf)) + goto exit; + + /* + * get tag data size. note that: + * get_valid_name(kbuf) has guaranteed that + * strlen(kbuf) < RDTAGS_NAME_SIZE + */ + tag_size = count - (strnlen(kbuf, RDTAGS_NAME_SIZE - 1) + 1); + + /* Check for special dual commands */ + if (strncmp(kbuf, "delete", RDTAGS_NAME_SIZE) == 0) { + /* Do some basic sanity checking */ + if (!get_valid_name(tag_data)) + goto exit; + + /* Remove the tag */ + rdtags_remove_tag(tag_data); + goto exit; + } + + if (strncmp(kbuf, "append", RDTAGS_NAME_SIZE) == 0) { + tag_append_data = strnchr(tag_data, RDTAGS_NAME_SIZE, ' '); + if (!tag_append_data) { + dev_err(dev, "Incorrect format to append, " \ + "please supply " \ + "a string of \n"); + goto exit; + } + + *tag_append_data = 0x0; + /* Do some basic sanity checking */ + if (!get_valid_name(tag_data)) + goto exit; + + tag_append_data++; + tag_size = strlen(tag_append_data); + /*append data on the tag */ + rdtags_append_tagdata(tag_data, tag_append_data, tag_size); + goto exit; + } + + /* Add the tag */ + if (rdtags_add_tag(kbuf, tag_data, tag_size)) + dev_err(dev, "rdtags_add_tag failed.\n"); + +exit: + kfree(kbuf); + + /* Make sure all asynchronous work is complete before returning */ + while (!kfifo_is_empty(&procfs_fifo) && work_busy(&procfs_work)) { + dev_dbg(dev, "Sleeping while waiting for WQ to finish\n"); + msleep(SLEEP_TIME_ASYNC_FINISH); + } + + return count; +} + +static const struct file_operations rdnode_fops = { + .read = tags_read, + .write = tags_write, +}; + +static int last_rdtags_init(void) +{ + struct rtag_head *mt; + int nbr_old_tags = 0; + int ret = 0; + + last_rdtags_base = (void *)__get_free_pages(GFP_KERNEL, + get_order(rdtags_size)); + if (!last_rdtags_base) { + dev_err(dev, "Failed to allocate last_rdtags buffer\n"); + goto exit; + } + + memcpy_fromio(last_rdtags_base, rdtags_io_base, rdtags_size); + last_rdtags_end = last_rdtags_base + rdtags_size; + + /* Check if the buffer contains a valid start tag */ + + mt = (struct rtag_head *)last_rdtags_base; + if (mt->sig == RDTAGS_SIG) { + + dev_dbg(dev, "Found existing tags in memory!\n"); + + /* Create procfs directory to hold the last tags */ + last_entry = proc_mkdir_mode(LAST_RDTAGS_PROC_DIR_NAME, + S_IRWXU | S_IRGRP | S_IXGRP, NULL); + if (!last_entry) { + dev_err(dev, "Failed to create last proc entry\n"); + goto exit; + } + + /* Temporarily entry is assigned to create procfs files while + * rebuilding the tags inside /proc/last_rdtags. + */ + entry = last_entry; + + /* Rebuilding the tags as last_rdtags */ + nbr_old_tags = rebuild_tag_tree(last_rdtags_base, rdtags_size); + + /* Initializing entry to NULL, otherwise there is a chance that + * new rdtags will take "last_entry or entry" as a parent + * directory incase failure to create /proc/rdtags. + */ + entry = NULL; + } + + return nbr_old_tags; +exit: + if (last_rdtags_base) { + free_pages((unsigned long)last_rdtags_base, + get_order(rdtags_size)); + last_rdtags_base = NULL; + } + + return ret; +} + +static int rdtags_driver_probe(struct platform_device *pdev) +{ + struct resource *res; + struct rdtags_platform_data *platform_data; + int nbr_old_tags = 0; + int nbr_new_tags = 0; + int ret = 0; + + dev = &pdev->dev; + + /* Get resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "rdtags_mem"); + if (!res || !res->start) { + dev_err(dev, "Resource invalid/absent\n"); + ret = -ENODEV; + goto exit; + } + + /* ioremap the static area */ + rdtags_size = res->end - res->start + 1; + rdtags_io_base = (void *)ioremap(res->start, rdtags_size); + if (!rdtags_io_base) { + dev_err(dev, "Failed to map %zd bytes of memory at 0x%llx!\n", + rdtags_size, res->start); + ret = -EINVAL; + goto exit; + } + + nbr_old_tags = last_rdtags_init(); + + rdtags_base = (void *)__get_free_pages(GFP_KERNEL, + get_order(rdtags_size)); + if (!rdtags_base) { + dev_err(dev, "Failed to allocate rdtags buffer\n"); + ret = -ENOMEM; + goto exit; + } + + rdtags_end = rdtags_base + rdtags_size; + memset(rdtags_base, 0x0, rdtags_size); + memset_io(rdtags_io_base, 0x0, rdtags_size); + + /* Add procfs interface */ + entry = proc_create_data(RDTAGS_PROC_NODE_NAME, + S_IFREG | S_IRUGO | S_IWUSR, NULL, + &rdnode_fops, NULL); + if (!entry) { + dev_err(dev, "Failed to create proc entry\n"); + ret = -ENOMEM; + goto exit; + } + + /* Create procfs directory to hold the tags */ + entry = proc_mkdir_mode(RDTAGS_PROC_DIR_NAME, + S_IRWXU | S_IRGRP | S_IXGRP, NULL); + + rdtags_initialized = 1; + + /* Check if the platform has specified a platform specific callback */ + platform_data = dev->platform_data; + if (platform_data && platform_data->platform_init) + nbr_new_tags = platform_data->platform_init + (platform_data->ramdump_mode); + + dev_info(dev, "Loaded with %d existing and " \ + "%d new tags. Size: %zd@0x%.8llx\n", + nbr_old_tags, nbr_new_tags, rdtags_size, res->start); + + return 0; + +exit: + if (rdtags_io_base) { + iounmap(rdtags_io_base); + rdtags_io_base = NULL; + } + + if (rdtags_base) { + free_pages((unsigned long)rdtags_base, get_order(rdtags_size)); + rdtags_base = NULL; + } + + return ret; +} + +static struct platform_driver rdtags_driver = { + .probe = rdtags_driver_probe, + .driver = { + .name = "rdtags", + }, +}; + +static int __init rdtags_core_init(void) +{ + int err; + err = platform_driver_register(&rdtags_driver); + return err; +} + +static void __exit rdtags_module_exit(void) +{ + rdtags_initialized = 0; + + if (entry) { + remove_proc_entry(RDTAGS_PROC_NODE_NAME, NULL); + remove_proc_entry(RDTAGS_PROC_DIR_NAME, NULL); + entry = NULL; + } + + if (last_entry) { + remove_proc_entry(LAST_RDTAGS_PROC_DIR_NAME, NULL); + last_entry = NULL; + } + + /* Unmap everything, but do not clear the area */ + if (rdtags_io_base) { + iounmap(rdtags_io_base); + rdtags_io_base = NULL; + } + + if (last_rdtags_base) { + free_pages((unsigned long)last_rdtags_base, + get_order(rdtags_size)); + last_rdtags_base = NULL; + } + + if (rdtags_base) { + free_pages((unsigned long)rdtags_base, get_order(rdtags_size)); + rdtags_base = NULL; + } + + platform_driver_unregister(&rdtags_driver); +} + +MODULE_AUTHOR("Sony Mobile Communications"); +MODULE_DESCRIPTION("ramdump tags"); +MODULE_LICENSE("GPL V2"); + +core_initcall(rdtags_core_init); +module_exit(rdtags_module_exit); diff --git a/drivers/misc/tcs3490.c b/drivers/misc/tcs3490.c new file mode 100644 index 0000000000000000000000000000000000000000..7acf24d2109a89f12fe2ba4901e50f41db809a72 --- /dev/null +++ b/drivers/misc/tcs3490.c @@ -0,0 +1,1654 @@ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ +/* + * Device driver for monitoring ambient light intensity in (lux), RGB, and + * color temperature (in kelvin) within the AMS-TAOS TCS family of devices. + * + * Copyright (c) 2016, AMS-TAOS USA, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tcs3490.h" + +// +// Debug options +// + +// Create an ABI for dumping CHIP_ID, REV_ID, and colorbin register contents? +#define CHIP_ID_ABI 1 + +// Print info re lux calculations (printed per sample, potentially voluminous)? +//#define LUX_MESSAGES 1 + + +// +// Configurational options +// + +// Define this as nonzero, or not, depending on your system configuration. +// The color-bin correction factors need to be applied at a different +// point in the computation with inked glass vs. air or clear glass. +#define INKED_GLASS 0 + + +// +// Constants +// + +#define TCS3490_CMD_ALS_INT_CLR 0xE6 +#define TCS3490_CMD_ALL_INT_CLR 0xE7 +#define TCS3490_CHANNEL 0xC0 + +#define CENTI_MSEC_PER_ATIME_TICK 278 +#define CENTI_MSEC_PER_MSEC 100 + +#define I2C_ADDR_OFFSET 0x80 + +#define GAIN1 0 +#define GAIN4 1 +#define GAIN16 2 +#define GAIN64 3 + +#define TCS3490_MAX_INTEGRATION_CYCLES 256 + +// +// pertaining to the Color Bins register, used for improved color accuracy +// + +#define _Q 16 // fixed point arithmetic shift + +#define COLOR_BINS_MSK_COEFF (1 << 15) +#define CONFIG_SPARE_2_MSK (1 << 3) + +#define COLOR_BINS_SHFT_RED 11 +#define COLOR_BINS_MSK_RED (((1 << 4) - 1) << COLOR_BINS_SHFT_RED) +#define COLOR_BINS_INT_MSK_RED ((1 << (4-1)) - 1) + +#define COLOR_BINS_SHFT_GRN 6 +#define COLOR_BINS_MSK_GRN (((1 << 5) - 1) << COLOR_BINS_SHFT_GRN) +#define COLOR_BINS_INT_MSK_GRN ((1 << (5-1)) - 1) + +#define COLOR_BINS_SHFT_BLU 0 +#define COLOR_BINS_MSK_BLU (((1 << 6) - 1) << COLOR_BINS_SHFT_BLU) +#define COLOR_BINS_INT_MSK_BLU ((1 << (6-1)) - 1) + +// These are processed only at compile time; there is no floating point +// arithmetic at runtime +#define SLOPE_RED (-0.0111) +#define OFFSET_RED (1.0174) +#define SLOPE_GRN (-0.0110) +#define OFFSET_GRN (1.0602) +#define SLOPE_BLU (-0.0074) +#define OFFSET_BLU (1.0631) + +#define RGBCIR_SENSOR_SYSFS_LINK_NAME "rgbcir_sensor" +#define RGBCIR_SENSOR_PINCTRL_IRQ_ACTIVE "rgbcir_irq_active" +#define RGBCIR_SENSOR_PINCTRL_IRQ_SUSPEND "rgbcir_irq_suspend" +#define RGBCIR_SENSOR_REGULATOR_NOTIFY_PRIORITY (1000) + +enum tcs3490_regs { + TCS3490_CONTROL, + TCS3490_ALS_TIME, // 0x81 + TCS3490_RESV_1, + TCS3490_WAIT_TIME, // 0x83 + TCS3490_ALS_MINTHRESHLO, // 0x84 + TCS3490_ALS_MINTHRESHHI, // 0x85 + TCS3490_ALS_MAXTHRESHLO, // 0x86 + TCS3490_ALS_MAXTHRESHHI, // 0x87 + TCS3490_RESV_2, // 0x88 + TCS3490_PRX_MINTHRESHLO, // 0x89 -> Not used for TCS3490 + + TCS3490_RESV_3, // 0x8A + TCS3490_PRX_MAXTHRESHHI, // 0x8B -> Not used for TCS3490 + TCS3490_PERSISTENCE, // 0x8C + TCS3490_CONFIG, // 0x8D + TCS3490_PRX_PULSE_COUNT, // 0x8E -> Not used for TCS3490 + TCS3490_GAIN, // 0x8F : Gain Control Register + TCS3490_AUX, // 0x90 + TCS3490_REVID, + TCS3490_CHIPID, + TCS3490_STATUS, // 0x93 + + TCS3490_CLR_CHANLO, // 0x94 + TCS3490_CLR_CHANHI, // 0x95 + TCS3490_RED_CHANLO, // 0x96 + TCS3490_RED_CHANHI, // 0x97 + TCS3490_GRN_CHANLO, // 0x98 + TCS3490_GRN_CHANHI, // 0x99 + TCS3490_BLU_CHANLO, // 0x9A + TCS3490_BLU_CHANHI, // 0x9B + TCS3490_PRX_HI, // 0x9C + TCS3490_PRX_LO, // 0x9D + + TCS3490_PRX_OFFSET, // 0x9E + TCS3490_RESV_4, // 0x9F + TCS3490_IRBEAM_CFG, // 0xA0 + TCS3490_IRBEAM_CARR, // 0xA1 + TCS3490_IRBEAM_NS, // 0xA2 + TCS3490_IRBEAM_ISD, // 0xA3 + TCS3490_IRBEAM_NP, // 0xA4 + TCS3490_IRBEAM_IPD, // 0xA5 + TCS3490_IRBEAM_DIV, // 0xA6 + TCS3490_IRBEAM_LEN, // 0xA7 + + TCS3490_IRBEAM_STAT, // 0xA8 + + TCS3490_REG_COLOR_BINLO=0x55, // 0xD5 + TCS3490_REG_COLOR_BINHI=0x56, // 0xD6 + + TCS3490_REG_MAX, + +}; + +enum tcs3490_en_reg { + TCS3490_EN_PWR_ON = (1 << 0), + TCS3490_EN_ALS = (1 << 1), + TCS3490_EN_PRX = (1 << 2), + TCS3490_EN_WAIT = (1 << 3), + TCS3490_EN_ALS_IRQ = (1 << 4), + TCS3490_EN_PRX_IRQ = (1 << 5), + TCS3490_EN_IRQ_PWRDN = (1 << 6), + TCS3490_EN_BEAM = (1 << 7), +}; + +enum tcs3490_status { + TCS3490_ST_ALS_VALID = (1 << 0), + TCS3490_ST_PRX_VALID = (1 << 1), + TCS3490_ST_BEAM_IRQ = (1 << 3), + TCS3490_ST_ALS_IRQ = (1 << 4), + TCS3490_ST_PRX_IRQ = (1 << 5), + TCS3490_ST_PRX_SAT = (1 << 6), +}; + +enum { + TCS3490_ALS_GAIN_MASK = (3 << 0), + TCS3490_PRX_GAIN_MASK = (3 << 2), + TCS3490_ALS_AGL_MASK = (1 << 2), + TCS3490_ALS_AGL_SHIFT = 2, + TCS3490_ATIME_PER_100 = 273, + TCS3490_ATIME_DEFAULT_MS = 50, + SCALE_SHIFT = 11, + RATIO_SHIFT = 10, + MAX_ALS_VALUE = 0xffff, + MIN_ALS_VALUE = 10, + GAIN_SWITCH_LEVEL = 100, + GAIN_AUTO_INIT_VALUE = AGAIN_16, +}; + +static u8 const restorable_regs[] = { + TCS3490_ALS_TIME, + TCS3490_PERSISTENCE, + TCS3490_GAIN, +}; + +static u8 const als_gains[] = { + 1, + 4, + 16, + 64 +}; + +struct tcs3490_als_info { + u32 saturation; + u16 clear_red_raw; + u16 clear_green_raw; + u16 clear_blue_raw; + u16 clear_raw; + u16 red_raw; + u16 green_raw; + u16 blue_raw; + u16 ir_raw; + uint64_t timestamp; +}; + +struct tcs3490_chip { + struct mutex lock; + struct i2c_client *client; + struct tcs3490_als_info als_inf; + struct tcs3490_parameters params; + struct tcs3490_i2c_platform_data *pdata; + u8 shadow[42]; + + struct input_dev *a_idev; + int in_suspend; + int wake_irq; + int irq_pending; + bool unpowered; + bool als_enabled; + bool is_register_regulator_cam_vio_notify; + int als_thres_enabled; + int als_switch_ch_enabled; + int vdd_supply_enable; + int gpio_vdd_enable; + int vio_supply_enable; + + bool als_gain_auto; + u8 als_channel; + u8 device_index; + struct regulator *vdd; + struct regulator *gpio_vdd; + struct regulator *vio; + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + struct notifier_block cam_io_regulator_cb; +}; + +static int tcs3490_power_on(struct tcs3490_chip *chip); +static int tcs3490_pltf_power_off(struct tcs3490_chip *chip); + +static int tcs3490_pinctrl_init(struct tcs3490_chip *data) +{ + data->pinctrl = devm_pinctrl_get(&data->client->dev); + if (IS_ERR_OR_NULL(data->pinctrl)) { + dev_err(&data->client->dev, + "%s:%d Getting pinctrl handle failed\n", + __func__, __LINE__); + return -EINVAL; + } + data->gpio_state_active = + pinctrl_lookup_state(data->pinctrl, + RGBCIR_SENSOR_PINCTRL_IRQ_ACTIVE); + if (IS_ERR_OR_NULL(data->gpio_state_active)) { + dev_err(&data->client->dev, + "%s:Failed to get the active state pinctrl handle\n", + __func__); + return -EINVAL; + } + data->gpio_state_suspend = + pinctrl_lookup_state(data->pinctrl, + RGBCIR_SENSOR_PINCTRL_IRQ_SUSPEND); + if (IS_ERR_OR_NULL(data->gpio_state_suspend)) { + dev_err(&data->client->dev, + "%s:Failed to get the suspend state pinctrl handle\n", + __func__); + return -EINVAL; + } + return 0; +} + +static int tcs3490_i2c_blk_read(struct tcs3490_chip *chip, + u8 reg, u8 *val, int size) +{ + s32 ret; + struct i2c_client *client = chip->client; + + uint8_t w_buf[1]; + uint8_t *r_buf; + struct i2c_msg msg[2]; + + reg |= I2C_ADDR_OFFSET; + w_buf[0] = reg; + msg[0].addr = (client->addr) >> 1; + msg[0].flags = 0; + msg[0].buf = w_buf; + msg[0].len = 1; + + r_buf = kmalloc(size + 1, GFP_KERNEL); + if (!r_buf) + return -ENOMEM; + memset(r_buf, 0, size); + msg[1].addr = (client->addr) >> 1; + msg[1].flags = I2C_M_RD; + msg[1].buf = r_buf; + msg[1].len = size; + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != 2) { + usleep_range(3000, 4000); + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != 2) { + dev_err(&client->dev, + "%s: i2c ERROR !! reg=0x%x ret=%d\n", + __func__, reg, ret); + kfree(r_buf); + return ret; + } + } else { + ret = 0; + } + memcpy(val, r_buf, sizeof(u8) * size); + kfree(r_buf); + + return ret; +} + +static int tcs3490_i2c_read(struct tcs3490_chip *chip, u8 reg, u8 *val) +{ + int ret; + struct i2c_client *client = chip->client; + uint8_t w_buf[1]; + uint8_t r_buf[1]; + struct i2c_msg msg[2]; + + reg |= I2C_ADDR_OFFSET; + w_buf[0] = reg; + msg[0].addr = (client->addr) >> 1; + msg[0].flags = 0; + msg[0].buf = w_buf; + msg[0].len = 1; + + r_buf[0] = 0x0; + msg[1].addr = (client->addr) >> 1; + msg[1].flags = I2C_M_RD; + msg[1].buf = r_buf; + msg[1].len = 1; + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != 2) { + usleep_range(3000, 4000); + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != 2) { + dev_err(&client->dev, + "%s: i2c ERROR !! reg=0x%x ret=%d\n", + __func__, reg, ret); + return ret; + } + } else { + ret = 0; + } + *val = r_buf[0]; + + return 0; +} + +static int tcs3490_i2c_write(struct tcs3490_chip *chip, u8 reg, u8 val) +{ + int ret; + struct i2c_client *client = chip->client; + + uint8_t w_buf[2]; + struct i2c_msg msg[1]; + + if (reg == TCS3490_CONTROL) { + dev_dbg(&client->dev, "%s: CONTRL addr[0x%x] data[0x%x]", + __func__, reg, val); + } + + reg |= I2C_ADDR_OFFSET; + w_buf[0] = reg; + w_buf[1] = val; + msg[0].addr = (client->addr) >> 1; + msg[0].flags = 0; + msg[0].buf = w_buf; + msg[0].len = 2; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret != 1) { + usleep_range(3000, 4000); + ret = i2c_transfer(client->adapter, msg, 1); + if (ret != 1) { + dev_err(&client->dev, + "%s: i2c ERROR !! reg=0x%x ret=%d\n", + __func__, reg, ret); + ret = -1; + } + } else { + ret = 0; + } + + return ret; +} + +static int tcs3490_i2c_reg_blk_write(struct tcs3490_chip *chip, + u8 reg, u8 *val, int size) +{ + s32 ret; + struct i2c_client *client = chip->client; + + uint8_t *w_buf; + struct i2c_msg msg[2]; + + reg |= I2C_ADDR_OFFSET; + w_buf = kmalloc(size + 1, GFP_KERNEL); + if (!w_buf) { + dev_err(&client->dev, "%s: failed to allocate buffer", + __func__); + return -ENOMEM; + } + w_buf[0] = reg; + memmove(&w_buf[1], val, size); + msg[0].addr = (client->addr) >> 1; + msg[0].flags = 0; + msg[0].buf = w_buf; + msg[0].len = size + 1; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret != 1) { + usleep_range(3000, 4000); + ret = i2c_transfer(client->adapter, msg, 1); + if (ret != 1) { + dev_err(&client->dev, + "%s: i2c ERROR !! reg=0x%x ret=%d\n", + __func__, reg, ret); + } + } else { + ret = 0; + } + kfree(w_buf); + + return ret; +} + +static int tcs3490_flush_regs(struct tcs3490_chip *chip) +{ + unsigned i; + int rc; + u8 reg; + + for (i = 0; i < ARRAY_SIZE(restorable_regs); i++) { + reg = restorable_regs[i]; + rc = tcs3490_i2c_write(chip, reg, chip->shadow[reg]); + if (rc) { + dev_err(&chip->client->dev, "%s: err on reg 0x%02x\n", + __func__, reg); + break; + } + } + return rc; +} + +static int tcs3490_update_enable_reg(struct tcs3490_chip *chip) +{ + dev_info(&chip->client->dev, "%s: Writing CONTROL, val[0x0%x]", + __func__, chip->shadow[TCS3490_CONTROL]); + return tcs3490_i2c_write(chip, TCS3490_CONTROL, + chip->shadow[TCS3490_CONTROL]); +} + +static int tcs3490_set_als_gain(struct tcs3490_chip *chip, int gain) +{ + int rc; + u8 ctrl_reg = chip->shadow[TCS3490_GAIN] & ~TCS3490_ALS_GAIN_MASK; + + switch (gain) { + case 1: + ctrl_reg |= AGAIN_1; + break; + case 4: + ctrl_reg |= AGAIN_4; + break; + case 16: + ctrl_reg |= AGAIN_16; + break; + case 64: + ctrl_reg |= AGAIN_64; + break; + default: + dev_err(&chip->client->dev, "%s: wrong als gain %d\n", + __func__, gain); + return -EINVAL; + } + + rc = tcs3490_i2c_write(chip, TCS3490_GAIN, ctrl_reg); + if (!rc) { + chip->shadow[TCS3490_GAIN] = ctrl_reg; + chip->params.als_gain = gain; + dev_info(&chip->client->dev, "%s: new als gain %d\n", + __func__, gain); + } + return rc; +} + + +static int tcs3490_irq_clr(struct tcs3490_chip *chip, u8 int2clr) +{ + int ret; + + ret = tcs3490_i2c_write(chip, int2clr, 0); + if (ret) { + dev_err(&chip->client->dev, "%s: failed 2x, int to clr %02x\n", + __func__, int2clr); + } + + return ret; +} + +static void tcs3490_get_als_setup_next(struct tcs3490_chip *chip) +{ + u8 *buf; + u32 sat; + int rc; + u8 atime = 0; + u8 cur_channel = 0xFF; + + mutex_lock(&chip->lock); + buf = &chip->shadow[TCS3490_CLR_CHANLO]; + atime = chip->shadow[TCS3490_ALS_TIME]; + mutex_unlock(&chip->lock); + + tcs3490_i2c_read(chip, TCS3490_CHANNEL, &cur_channel); + if ((chip->als_switch_ch_enabled == 1) && (cur_channel == 0x00)) { + /*RGBC*/ + /*extract raw channel data*/ + mutex_lock(&chip->lock); + chip->als_inf.clear_raw = + le16_to_cpup((const __le16 *)&buf[0]); + chip->als_inf.clear_red_raw = + le16_to_cpup((const __le16 *)&buf[2]); + chip->als_inf.clear_green_raw = + le16_to_cpup((const __le16 *)&buf[4]); + chip->als_inf.clear_blue_raw = + le16_to_cpup((const __le16 *)&buf[6]); + mutex_unlock(&chip->lock); + + /*Switch to RGB-IR*/ + rc = tcs3490_i2c_write(chip, TCS3490_CHANNEL, 0x80); + if (!rc) { + dev_dbg(&chip->client->dev, + "%s: Channel: RGB-IR", __func__); + mutex_lock(&chip->lock); + chip->als_channel = 0x80; + mutex_unlock(&chip->lock); + } + dev_dbg(&chip->client->dev, + "%s: Changed ALS channel from RGBC to 0x%x\n", + __func__, chip->als_channel); + } else if ((chip->als_switch_ch_enabled == 1) && + (cur_channel == 0x80)) { + /*RGBC-IR*/ + /*extract ir channel data*/ + mutex_lock(&chip->lock); + chip->als_inf.ir_raw = + le16_to_cpup((const __le16 *)&buf[0]); + chip->als_inf.red_raw = + le16_to_cpup((const __le16 *)&buf[2]); + chip->als_inf.green_raw = + le16_to_cpup((const __le16 *)&buf[4]); + chip->als_inf.blue_raw = + le16_to_cpup((const __le16 *)&buf[6]); + mutex_unlock(&chip->lock); + /*Switch to RGBC*/ + rc = tcs3490_i2c_write(chip, TCS3490_CHANNEL, 0x00); + if (!rc) { + dev_dbg(&chip->client->dev, "%s: Channel: RGBC", + __func__); + mutex_lock(&chip->lock); + chip->als_channel = 0x00; + mutex_unlock(&chip->lock); + } + chip->als_inf.timestamp = ktime_get_boot_ns(); + dev_dbg(&chip->client->dev, + "%s: Changed channel from RGBC-IR to 0x%x Time %llu\n", + __func__, chip->als_channel, chip->als_inf.timestamp); + } else { + if (cur_channel == 0x00) { + mutex_lock(&chip->lock); + chip->als_inf.clear_raw = + le16_to_cpup((const __le16 *)&buf[0]); + mutex_unlock(&chip->lock); + } else { + mutex_lock(&chip->lock); + chip->als_inf.ir_raw = + le16_to_cpup((const __le16 *)&buf[0]); + mutex_unlock(&chip->lock); + } + mutex_lock(&chip->lock); + chip->als_inf.red_raw = + le16_to_cpup((const __le16 *)&buf[2]); + chip->als_inf.green_raw = + le16_to_cpup((const __le16 *)&buf[4]); + chip->als_inf.blue_raw = + le16_to_cpup((const __le16 *)&buf[6]); + mutex_unlock(&chip->lock); + chip->als_inf.timestamp = ktime_get_boot_ns(); + } + + sat = min_t(u32, MAX_ALS_VALUE, + (u32)(TCS3490_MAX_INTEGRATION_CYCLES - atime) << 10); + sat = sat * 8 / 10; + chip->als_inf.saturation = sat; + + dev_dbg(&chip->client->dev, + "%s: raw c/ir,r,g,b: %d, %d, %d, %d\n", + __func__, + (cur_channel == 0x00) ? + chip->als_inf.clear_raw : chip->als_inf.ir_raw, + chip->als_inf.red_raw, + chip->als_inf.green_raw, + chip->als_inf.blue_raw); +} + +static int tcs3490_read_all(struct tcs3490_chip *chip) +{ + int ret = 0; + + mutex_lock(&chip->lock); + tcs3490_i2c_read(chip, TCS3490_STATUS, + &chip->shadow[TCS3490_STATUS]); + + ret = tcs3490_i2c_blk_read(chip, TCS3490_CLR_CHANLO, + &chip->shadow[TCS3490_CLR_CHANLO], 2); + ret = tcs3490_i2c_blk_read(chip, TCS3490_RED_CHANLO, + &chip->shadow[TCS3490_RED_CHANLO], 2); + ret = tcs3490_i2c_blk_read(chip, TCS3490_GRN_CHANLO, + &chip->shadow[TCS3490_GRN_CHANLO], 2); + ret = tcs3490_i2c_blk_read(chip, TCS3490_BLU_CHANLO, + &chip->shadow[TCS3490_BLU_CHANLO], 2); + mutex_unlock(&chip->lock); + return (ret < 0) ? ret : 0; +} + +static int tcs3490_update_als_thres(struct tcs3490_chip *chip, bool on_enable) +{ + s32 ret; + u8 *buf = &chip->shadow[TCS3490_ALS_MINTHRESHLO]; + u16 deltaP = chip->params.als_deltaP; + u16 from, to, cur; + u16 saturation = chip->als_inf.saturation; + + mutex_lock(&chip->lock); + cur = chip->als_inf.clear_raw; + mutex_unlock(&chip->lock); + + if (!on_enable) + /* move deltaP far away from current position to force an irq */ + from = to = cur > saturation / 2 ? 0 : saturation; + else { + deltaP = cur * deltaP / 100; + if (!deltaP) + deltaP = 1; + if (cur > deltaP) + from = cur - deltaP; + else + from = 0; + if (cur < (saturation - deltaP)) + to = cur + deltaP; + else + to = saturation; + } + + *buf++ = from & 0xff; + *buf++ = from >> 8; + *buf++ = to & 0xff; + *buf++ = to >> 8; + mutex_lock(&chip->lock); + ret = tcs3490_i2c_reg_blk_write(chip, TCS3490_ALS_MINTHRESHLO, + &chip->shadow[TCS3490_ALS_MINTHRESHLO], + TCS3490_ALS_MAXTHRESHHI - TCS3490_ALS_MINTHRESHLO + 1); + mutex_unlock(&chip->lock); + + dev_info(&chip->client->dev, + "%s: on_enable[%s] cur[%u] deltaP[%u] from[%u] to[%u]", + __func__, on_enable?"TRUE":"FALSE", cur, deltaP, from, to); + + return (ret < 0) ? ret : 0; +} + +static int tcs3490_check_and_report(struct tcs3490_chip *chip) +{ + u8 status; + u8 saturation; + + int ret = tcs3490_read_all(chip); + if (ret) + goto exit_clr; + + mutex_lock(&chip->lock); + status = chip->shadow[TCS3490_STATUS]; + mutex_unlock(&chip->lock); + + saturation = chip->als_inf.saturation; + + if ((status & (TCS3490_ST_ALS_VALID | TCS3490_ST_ALS_IRQ)) == + (TCS3490_ST_ALS_VALID | TCS3490_ST_ALS_IRQ)) { + tcs3490_get_als_setup_next(chip); + tcs3490_irq_clr(chip, TCS3490_CMD_ALS_INT_CLR); + if (chip->als_thres_enabled) + tcs3490_update_als_thres(chip, 1); + } + +exit_clr: + tcs3490_irq_clr(chip, TCS3490_CMD_ALL_INT_CLR); + + return ret; +} + +static irqreturn_t tcs3490_irq(int irq, void *handle) +{ + struct tcs3490_chip *chip = handle; + + (void)tcs3490_check_and_report(chip); + sysfs_notify(&chip->a_idev->dev.kobj, NULL, "notify"); + return IRQ_HANDLED; +} + +static void tcs3490_set_defaults(struct tcs3490_chip *chip) +{ + u8 *sh = chip->shadow; + struct device *dev = &chip->client->dev; + + dev_info(dev, "%s: use defaults\n", __func__); + mutex_lock(&chip->lock); + sh[TCS3490_ALS_TIME] = 0xFE;/*0xEE;*/ /* integration time : 254 ms */ + mutex_unlock(&chip->lock); + sh[TCS3490_WAIT_TIME] = 0xFF; /* wait time : 2.4 ms */ + sh[TCS3490_PERSISTENCE] = ALS_PERSIST(0); + sh[TCS3490_PRX_PULSE_COUNT] = 8; + sh[TCS3490_GAIN] = AGAIN_1;/*AGAIN_16;*/ + sh[TCS3490_AUX] = 0x00; /* No saturation int */ + + chip->als_gain_auto = false; + mutex_lock(&chip->lock); + chip->als_channel = 0x00; /* Clear channel */ + mutex_unlock(&chip->lock); + chip->als_switch_ch_enabled = 1; /* Obtain RGBC and IR in turn */ +} + +static ssize_t tcs3490_chip_pow_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", chip->unpowered ? 0 : 1); +} + +static ssize_t tcs3490_chip_pow_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + int rc = 0; + bool value; + + dev_info(&chip->client->dev, "CHK 1 %s\n", __func__); + + if (strtobool(buf, &value)) + return -EINVAL; + + dev_info(&chip->client->dev, "CHK 2 %s: value = %s\n", + __func__, value?"TRUE":"FALSE"); + + if (value) { + if (!rc && chip->unpowered) + tcs3490_power_on(chip); + if (!rc && chip->pinctrl && chip->gpio_state_active) { + rc = pinctrl_select_state(chip->pinctrl, + chip->gpio_state_active); + if (rc) + dev_err(&chip->client->dev, + "%s: cannot set pin to active state", + __func__); + } + if (!rc) { + dev_info(&chip->client->dev, + "tcs3490: Request threaded IRQ\n"); + rc = request_threaded_irq(chip->client->irq, NULL, + &tcs3490_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&chip->client->dev), chip); + if (rc) { + dev_info(&chip->client->dev, + "Failed to request irq %d\n", + chip->client->irq); + (void)pinctrl_select_state(chip->pinctrl, + chip->gpio_state_suspend); + } + } + if (rc) + tcs3490_pltf_power_off(chip); + } else { + if (chip->pinctrl && chip->gpio_state_suspend) { + free_irq(chip->client->irq, chip); + rc = pinctrl_select_state(chip->pinctrl, + chip->gpio_state_suspend); + if (rc) + dev_err(&chip->client->dev, + "%s: cannot set pin to active state", + __func__); + } + if (!chip->unpowered) + tcs3490_pltf_power_off(chip); + } + + return size; +} + +static int tcs3490_als_enable(struct tcs3490_chip *chip, int on) +{ + int rc; + + dev_info(&chip->client->dev, "%s: on = %d\n", __func__, on); + if (on) { + tcs3490_irq_clr(chip, TCS3490_CMD_ALS_INT_CLR); + tcs3490_update_als_thres(chip, 1); + chip->shadow[TCS3490_CONTROL] |= + (TCS3490_EN_PWR_ON | TCS3490_EN_ALS | + TCS3490_EN_ALS_IRQ); + + rc = tcs3490_update_enable_reg(chip); + if (rc) + return rc; + mdelay(3); + } else { + chip->shadow[TCS3490_CONTROL] &= + ~(TCS3490_EN_ALS_IRQ); + + if (!(chip->shadow[TCS3490_CONTROL] & TCS3490_EN_PRX)) + chip->shadow[TCS3490_CONTROL] &= ~TCS3490_EN_PWR_ON; + rc = tcs3490_update_enable_reg(chip); + if (rc) + return rc; + chip->als_inf.timestamp = 0; + tcs3490_irq_clr(chip, TCS3490_CMD_ALS_INT_CLR); + } + if (!rc) + chip->als_enabled = on; + + return rc; +} + +static ssize_t tcs3490_als_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", chip->als_enabled); +} + +static ssize_t tcs3490_als_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + bool value; + + if (strtobool(buf, &value)) + return -EINVAL; + + if (value) + tcs3490_als_enable(chip, 1); + else + tcs3490_als_enable(chip, 0); + + return size; +} + +static ssize_t tcs3490_auto_gain_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", + chip->als_gain_auto ? "auto" : "manual"); +} + +static ssize_t tcs3490_auto_gain_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + bool value; + + if (strtobool(buf, &value)) + return -EINVAL; + + if (value) + chip->als_gain_auto = true; + else + chip->als_gain_auto = false; + + return size; +} + +static ssize_t tcs3490_als_gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", + chip->params.als_gain); +} + +static ssize_t tcs3490_als_all_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + + mutex_lock(&chip->lock); + ret = snprintf(buf, PAGE_SIZE, "%u,%u,%u,%u,%u,%u,%u,%u,%llu", + chip->als_inf.clear_red_raw, + chip->als_inf.clear_green_raw, + chip->als_inf.clear_blue_raw, + chip->als_inf.clear_raw, + chip->als_inf.red_raw, + chip->als_inf.green_raw, + chip->als_inf.blue_raw, + chip->als_inf.ir_raw, + chip->als_inf.timestamp); + mutex_unlock(&chip->lock); + return ret; +} + +static ssize_t tcs3490_als_red_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + mutex_lock(&chip->lock); + ret = snprintf(buf, PAGE_SIZE, "%d", chip->als_inf.red_raw); + mutex_lock(&chip->lock); + return ret; +} + +static ssize_t tcs3490_als_green_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + mutex_lock(&chip->lock); + ret = snprintf(buf, PAGE_SIZE, "%d", chip->als_inf.green_raw); + mutex_unlock(&chip->lock); + return ret; +} + +static ssize_t tcs3490_als_blue_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + mutex_lock(&chip->lock); + ret = snprintf(buf, PAGE_SIZE, "%d", chip->als_inf.blue_raw); + mutex_unlock(&chip->lock); + return ret; +} + +static ssize_t tcs3490_als_clear_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + mutex_lock(&chip->lock); + ret = snprintf(buf, PAGE_SIZE, "%d", chip->als_inf.clear_raw); + mutex_unlock(&chip->lock); + return ret; +} + +static ssize_t tcs3490_als_gain_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long gain; + int rc; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + + rc = kstrtoul(buf, 10, &gain); + + if (rc) + return -EINVAL; + if (gain != 0 && gain != 1 && gain != 4 && gain != 16 && + gain != 60 && gain != 64) + return -EINVAL; + + mutex_lock(&chip->lock); + if (gain) { + chip->als_gain_auto = false; + rc = tcs3490_set_als_gain(chip, gain); + } else { + chip->als_gain_auto = true; + } + tcs3490_flush_regs(chip); + mutex_unlock(&chip->lock); + return rc ? rc : size; +} + +static ssize_t tcs3490_als_persist_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d", + (((chip->params.persist) & 0x0f))); +} + +static ssize_t tcs3490_als_persist_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + long persist; + int rc; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + + rc = kstrtoul(buf, 10, &persist); + if (rc) + return -EINVAL; + + mutex_lock(&chip->lock); + chip->shadow[TCS3490_PERSISTENCE] &= 0xF0; + chip->shadow[TCS3490_PERSISTENCE] |= ((u8)persist & 0x0F); + + rc = tcs3490_flush_regs(chip); + if (!rc) + chip->params.persist = chip->shadow[TCS3490_PERSISTENCE]; + mutex_unlock(&chip->lock); + return size; +} + +static ssize_t tcs3490_als_itime_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + int t; + mutex_lock(&chip->lock); + t = TCS3490_MAX_INTEGRATION_CYCLES - chip->params.als_time; + mutex_unlock(&chip->lock); + return snprintf(buf, PAGE_SIZE, "%d\n", t); +} + +static ssize_t tcs3490_als_itime_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + long itime; + int rc; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + + rc = kstrtoul(buf, 10, &itime); + if (rc) + return -EINVAL; + + mutex_lock(&chip->lock); + chip->shadow[TCS3490_ALS_TIME] = + TCS3490_MAX_INTEGRATION_CYCLES - (u8)itime; + rc = tcs3490_flush_regs(chip); + if (!rc) + chip->params.als_time = chip->shadow[TCS3490_ALS_TIME]; + mutex_unlock(&chip->lock); + return size; +} + +static ssize_t tcs3490_als_thres_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d", chip->als_thres_enabled); +} + +static ssize_t tcs3490_als_thres_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + int rc; + int on_enable; + + rc = kstrtoint(buf, 10, &on_enable); + + if (rc) + return -EINVAL; + + if (on_enable == 1) + tcs3490_update_als_thres(chip, 1); + else + tcs3490_update_als_thres(chip, 0); + chip->als_thres_enabled = 1; + return size; +} + +static ssize_t tcs3490_als_deltaP_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, + "%d (in %%)", chip->params.als_deltaP); +} + +static ssize_t tcs3490_als_deltaP_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long deltaP; + int rc; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + + rc = kstrtoul(buf, 10, &deltaP); + if (rc || deltaP > 100) + return -EINVAL; + mutex_lock(&chip->lock); + chip->params.als_deltaP = deltaP; + mutex_unlock(&chip->lock); + dev_info(&chip->client->dev, + "%s: Changed ALS deltaP, deltaP[%lu]", + __func__, deltaP); + return size; +} + +/*=========== Switch IR/Clear channel ==================*/ +static ssize_t tcs3490_als_channel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + + mutex_lock(&chip->lock); + ret = snprintf(buf, PAGE_SIZE, "%d", + ((chip->als_channel & 0x80) == 0x00) ? 0 : 1); + mutex_unlock(&chip->lock); + return ret; +} + +static ssize_t tcs3490_als_channel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long channel; + u8 ch_val = 0; + int rc; + struct tcs3490_chip *chip = dev_get_drvdata(dev); + + rc = kstrtoul(buf, 10, &channel); + if (rc || channel > 2) + return -EINVAL; + + ch_val = (channel == 0) ? 0x00 : 0x80; + rc = tcs3490_i2c_write(chip, TCS3490_CHANNEL, ch_val); + if (!rc) { + dev_info(&chip->client->dev, "%s: new channel %s\n", + __func__, ((ch_val & 0x80) == 0x00) ? "clear" : "ir"); + mutex_lock(&chip->lock); + chip->als_channel = ch_val; + mutex_unlock(&chip->lock); + } + return size; +} +/*======================================================*/ + +static ssize_t tcs3490_chip_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tcs3490_chip *chip = dev_get_drvdata(dev); + uint8_t id; + + tcs3490_i2c_read(chip, TCS3490_CHIPID, &id); + chip->shadow[TCS3490_CHIPID] = id; + + return snprintf(buf, + PAGE_SIZE, + "0x%02x", + chip->shadow[TCS3490_CHIPID]); +} + +static ssize_t tcs3490_notify(struct device *dev, + struct device_attribute *attr, char *buf) +{ + dev_dbg(dev, "%s: Notify is called", __func__); + return snprintf(buf, PAGE_SIZE, "%d", 1); +} + +/*=========== DEFINE DEVICE_ATTR ==================*/ +static DEVICE_ATTR(chip_pow, S_IRUGO | S_IWUSR | S_IWGRP, + tcs3490_chip_pow_show, tcs3490_chip_pow_store); +static DEVICE_ATTR(als_Itime, S_IRUGO | S_IWUSR | S_IWGRP, + tcs3490_als_itime_show, tcs3490_als_itime_store); +static DEVICE_ATTR(als_thres, S_IRUGO | S_IWUSR | S_IWGRP, + tcs3490_als_thres_show, tcs3490_als_thres_store); +static DEVICE_ATTR(als_red, S_IRUGO, tcs3490_als_red_show, NULL); +static DEVICE_ATTR(als_green, S_IRUGO, tcs3490_als_green_show, NULL); +static DEVICE_ATTR(als_blue, S_IRUGO, tcs3490_als_blue_show, NULL); +static DEVICE_ATTR(als_clear, S_IRUGO, tcs3490_als_clear_show, NULL); +static DEVICE_ATTR(als_all, S_IRUGO, tcs3490_als_all_show, NULL); +static DEVICE_ATTR(als_gain, S_IRUGO | S_IWUSR | S_IWGRP, + tcs3490_als_gain_show, tcs3490_als_gain_store); +static DEVICE_ATTR(als_thresh_deltaP, S_IRUGO | S_IWUSR | S_IWGRP, + tcs3490_als_deltaP_show, tcs3490_als_deltaP_store); +static DEVICE_ATTR(als_auto_gain, S_IRUGO | S_IWUSR | S_IWGRP, + tcs3490_auto_gain_enable_show, + tcs3490_auto_gain_enable_store); +static DEVICE_ATTR(als_power_state, S_IRUGO | S_IWUSR | S_IWGRP, + tcs3490_als_enable_show, tcs3490_als_enable_store); +static DEVICE_ATTR(als_persist, S_IRUGO | S_IWUSR | S_IWGRP, + tcs3490_als_persist_show, tcs3490_als_persist_store); +static DEVICE_ATTR(als_channel, S_IRUGO | S_IWUSR | S_IWGRP, + tcs3490_als_channel_show, tcs3490_als_channel_store); +static DEVICE_ATTR(chip_id, S_IRUGO, tcs3490_chip_id_show, NULL); +static DEVICE_ATTR(notify, S_IRUGO, tcs3490_notify, NULL); + +static struct attribute *tcs3490_attributes[] = { + &dev_attr_chip_pow.attr, + &dev_attr_als_Itime.attr, + &dev_attr_als_thres.attr, + &dev_attr_als_red.attr, + &dev_attr_als_green.attr, + &dev_attr_als_blue.attr, + &dev_attr_als_clear.attr, + &dev_attr_als_all.attr, + &dev_attr_als_gain.attr, + &dev_attr_als_thresh_deltaP.attr, + &dev_attr_als_auto_gain.attr, + &dev_attr_als_power_state.attr, + &dev_attr_als_persist.attr, + &dev_attr_als_channel.attr, + &dev_attr_chip_id.attr, + &dev_attr_notify.attr, + NULL +}; + +static const struct attribute_group tcs3490_attr_group = { + .attrs = tcs3490_attributes, +}; + +static int tcs3490_sensor_cam_io_notifier_call(struct notifier_block *self, + unsigned long event, void *data) +{ + struct tcs3490_chip *chip; + uint32_t rc; + + if (event & REGULATOR_EVENT_DISABLE) { + chip = container_of(self, struct tcs3490_chip, cam_io_regulator_cb); + dev_info(&chip->client->dev, + "%s REGULATOR_EVENT_DISABLE occurred event \n", __func__); + if (chip->vdd_supply_enable) + rc = regulator_disable(chip->vdd); + else if (chip->gpio_vdd_enable) + rc = regulator_disable(chip->gpio_vdd); + if (!rc) { + chip->unpowered = true; + dev_info(&chip->client->dev, + "%s: Regulator vdd disable OK", __func__); + } else { + dev_err(&chip->client->dev, + "%s: Regulator vdd disable failed", __func__); + } + } + return NOTIFY_OK; +} + +static int tcs3490_pltf_power_on(struct tcs3490_chip *chip) +{ + int rc = 0; + + mutex_lock(&chip->lock); + if (chip->vdd_supply_enable) + rc = regulator_enable(chip->vdd); + else if (chip->gpio_vdd_enable) + rc = regulator_enable(chip->gpio_vdd); + if (rc) { + dev_err(&chip->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + chip->unpowered = true; + mutex_unlock(&chip->lock); + } else { + if (chip->is_register_regulator_cam_vio_notify) { + regulator_unregister_notifier(chip->vio, + &(chip->cam_io_regulator_cb)); + chip->is_register_regulator_cam_vio_notify = false; + } + if (chip->vio_supply_enable) + rc = regulator_enable(chip->vio); + if (rc) { + dev_err(&chip->client->dev, + "%s: Regulator vio enable failed rc=%d\n", __func__, rc); + chip->unpowered = true; + mutex_unlock(&chip->lock); + } + } + if (!rc) { + dev_dbg(&chip->client->dev, + "%s: rgbcir, power init, regulator enable OK\n", __func__); + + /* Enable Oscillator */ + usleep_range(1000, 1000); + tcs3490_i2c_write(chip, TCS3490_CONTROL, 0x01); + mutex_unlock(&chip->lock); + usleep_range(10000, 11000); + } + return rc; +} + +static int tcs3490_power_on(struct tcs3490_chip *chip) +{ + int rc = 0; + + rc = tcs3490_pltf_power_on(chip); + if (rc) + return rc; + dev_info(&chip->client->dev, "%s: chip was off, restoring regs\n", + __func__); + + dev_info(&chip->client->dev, "tcs3490: Setting defaults\n"); + tcs3490_set_defaults(chip); + rc = tcs3490_flush_regs(chip); + + if (!rc) { + chip->unpowered = false; + } else { + tcs3490_pltf_power_off(chip); + } + + return rc; +} + +static int tcs3490_pltf_power_off(struct tcs3490_chip *chip) +{ + int rc = 0; + int rc2 = 0; + + mutex_lock(&chip->lock); + /* Disable Oscillator */ + tcs3490_i2c_write(chip, TCS3490_CONTROL, 0x00); + usleep_range(3000, 4000); + if (chip->vio_supply_enable && !chip->is_register_regulator_cam_vio_notify) { + chip->cam_io_regulator_cb.notifier_call = tcs3490_sensor_cam_io_notifier_call; + chip->cam_io_regulator_cb.priority = RGBCIR_SENSOR_REGULATOR_NOTIFY_PRIORITY; + if (regulator_register_notifier(chip->vio, &(chip->cam_io_regulator_cb))) + dev_err(&chip->client->dev, + "%s Regulator notification registration failed!\n", __func__); + else + chip->is_register_regulator_cam_vio_notify = true; + } + if (chip->vio_supply_enable) { + rc2 = regulator_disable(chip->vio); + if (rc2) + dev_err(&chip->client->dev, + "%s: Regulator vio disable failed", __func__); + else + dev_info(&chip->client->dev, + "%s: Regulator vio disable OK", __func__); + } + if (!chip->is_register_regulator_cam_vio_notify) { + if (chip->vdd_supply_enable) + rc = regulator_disable(chip->vdd); + else if (chip->gpio_vdd_enable) + rc = regulator_disable(chip->gpio_vdd); + + if (!rc && !rc2) + chip->unpowered = true; + } + mutex_unlock(&chip->lock); + return rc; +} + + +static int tcs3490_power_init(struct tcs3490_chip *chip) +{ + int rc = 0; + if (!chip) + return -EINVAL; + chip->unpowered = true; + if (chip->vdd_supply_enable) { + chip->vdd = regulator_get(&chip->client->dev, "rgbcir_vdd"); + if (IS_ERR(chip->vdd)) { + rc = PTR_ERR(chip->vdd); + dev_err(&chip->client->dev, + "Regulator get failed, vdd, rc = %d\n", rc); + return rc; + } + } else if (chip->gpio_vdd_enable) { + chip->gpio_vdd = regulator_get(&chip->client->dev, "rgbcir-gpio-vdd"); + if (IS_ERR(chip->gpio_vdd)) { + rc = PTR_ERR(chip->gpio_vdd); + dev_err(&chip->client->dev, + "Gpio-Regulator get failed,gpio_vdd, rc = %d\n", rc); + return rc; + } + } + if (chip->vio_supply_enable) { + chip->vio = regulator_get(&chip->client->dev, "rgbcir-vio"); + if (IS_ERR(chip->vio)) { + rc = PTR_ERR(chip->vio); + dev_err(&chip->client->dev, + "%s: Regulator get failed,vio, rc = %d\n", __func__, rc); + return rc; + } + } + chip->is_register_regulator_cam_vio_notify = false; + dev_info(&chip->client->dev, + "%s: rgbcir, power init, regulator get OK vdd=%d, gpio_vdd=%d, vio=%d\n", __func__, chip->vdd_supply_enable, chip->gpio_vdd_enable, chip->vio_supply_enable); + return rc; +} + +static int tcs3490_power_deinit(struct tcs3490_chip *chip) +{ + int rc = 0; + if (!chip) + return -EINVAL; + chip->unpowered = true; + if (chip->vio_supply_enable && chip->vio) { + regulator_put(chip->vio); + } + if (chip->is_register_regulator_cam_vio_notify) { + regulator_unregister_notifier(chip->vio, &(chip->cam_io_regulator_cb)); + chip->is_register_regulator_cam_vio_notify = false; + } + if (chip->vdd_supply_enable && chip->vdd) { + regulator_put(chip->vdd); + } else if (chip->gpio_vdd_enable && chip->gpio_vdd) { + regulator_put(chip->gpio_vdd); + } + return rc; +} + +static int tcs3490_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tcs3490_chip *chip; + int rc = 0; + uint32_t val_u32; + + dev_info(&client->dev, "start probing tcs3490\n"); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, + "%s: check functionality failed", __func__); + return -EIO; + } + dev_info(&client->dev, "tcs3490: Checking IRQ number %d\n", + client->irq); + if (client->irq < 0) { + dev_err(&client->dev, "%s: no reason to run.\n", __func__); + rc = -EINVAL; + goto init_failed; + } else { + dev_info(&client->dev, "%s: client->irq = %d\n", + __func__, client->irq); + } + chip = kzalloc(sizeof(struct tcs3490_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + i2c_set_clientdata(client, chip); + chip->client = client; + chip->device_index = 0; /*use default*/ + if (chip->client->dev.of_node) { + rc = tcs3490_pinctrl_init(chip); + if (rc) { + dev_err(&client->dev, + "%s: failed to pinctrl init\n", __func__); + goto pinctrl_init_failed; + } + } + + dev_info(&chip->client->dev, "tcs3490: Initializing mutex\n"); + mutex_init(&chip->lock); + + rc = of_property_read_u32(client->dev.of_node, + "ams,rgbcir-vdd-supply", &val_u32); + if (rc < 0) { + dev_err(&client->dev, "%s failed %d\n", __func__, __LINE__); + goto pinctrl_init_failed; + } + chip->vdd_supply_enable = val_u32; + + rc = of_property_read_u32(client->dev.of_node, + "ams,rgbcir-gpio-vdd", &val_u32); + if (rc < 0) { + dev_err(&client->dev, "%s failed %d\n", __func__, __LINE__); + goto pinctrl_init_failed; + } + chip->gpio_vdd_enable = val_u32; + + rc = of_property_read_u32(client->dev.of_node, + "ams,rgbcir-vio-supply", &val_u32); + if (rc < 0) { + dev_err(&client->dev, "%s failed %d\n", __func__, __LINE__); + goto pinctrl_init_failed; + } + chip->vio_supply_enable = val_u32; + + rc = tcs3490_power_init(chip); + if (rc) + goto pinctrl_init_failed; + + chip->a_idev = input_allocate_device(); + if (!chip->a_idev) { + rc = -ENOMEM; + dev_err(&client->dev, + "%s: failed to allocate input device", __func__); + goto allocate_device_failed; + } + chip->a_idev->name = "AMS TCS3490 Sensor"; + + dev_info(&chip->client->dev, "tcs3490: set bit EV_ABS\n"); + set_bit(EV_ABS, chip->a_idev->evbit); + + dev_info(&chip->client->dev, "tcs3490: set bit ABS_MISC\n"); + set_bit(ABS_MISC, chip->a_idev->absbit); + + dev_info(&chip->client->dev, "tcs3490: Set ABS params\n"); + input_set_abs_params(chip->a_idev, ABS_MISC, 0, 65535, 0, 0); /*check*/ + + dev_info(&chip->client->dev, + "tcs3490: assigning open/close functions\n"); + + rc = input_register_device(chip->a_idev); + if (rc) { + dev_err(&client->dev, + "failed to register input device"); + goto register_device_failed; + } + input_set_drvdata(chip->a_idev, chip); + dev_info(&chip->client->dev, "tcs3490: i2c nr %d\n", + client->adapter->nr); + + rc = sysfs_create_group(&chip->a_idev->dev.kobj, + &tcs3490_attr_group); + if (rc) { + rc = -ENOMEM; + dev_err(&client->dev, + "%s: failed to create sysfs group", __func__); + goto create_group_failed; + } + + rc = sysfs_create_link(chip->a_idev->dev.kobj.parent, + &chip->a_idev->dev.kobj, RGBCIR_SENSOR_SYSFS_LINK_NAME); + if (rc) { + rc = -ENOMEM; + dev_err(&client->dev, + "%s: failed to create sysfs link", __func__); + goto create_link_failed; + } + + dev_info(&client->dev, "Probe ok.\n"); + return 0; + +create_link_failed: + sysfs_remove_group(&chip->a_idev->dev.kobj, + &tcs3490_attr_group); +create_group_failed: + input_unregister_device(chip->a_idev); +register_device_failed: + input_free_device(chip->a_idev); +allocate_device_failed: + tcs3490_power_deinit(chip); +pinctrl_init_failed: + kfree(chip); +init_failed: + dev_err(&client->dev, "Probe failed.\n"); + return rc; +} + +static int tcs3490_remove(struct i2c_client *client) +{ + struct tcs3490_chip *chip = i2c_get_clientdata(client); + sysfs_remove_link(&chip->a_idev->dev.kobj, + RGBCIR_SENSOR_SYSFS_LINK_NAME); + sysfs_remove_group(&chip->a_idev->dev.kobj, + &tcs3490_attr_group); + free_irq(client->irq, chip); + if (chip->a_idev) { + input_unregister_device(chip->a_idev); + } + + i2c_set_clientdata(client, NULL); + kfree(chip); + return 0; +} + +static const struct i2c_device_id tcs3490_idtable[] = { + { "tcs3490", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tcs3490_idtable); + +static const struct of_device_id tcs3490_dt_match[] = { + { .compatible = "ams,tcs3490", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tcs3490_dt_match); + +static struct i2c_driver tcs3490_driver = { + .driver = { + .name = "tcs3490", + .owner = THIS_MODULE, + .of_match_table = tcs3490_dt_match, + }, + .id_table = tcs3490_idtable, + .probe = tcs3490_probe, + .remove = tcs3490_remove, +}; + +static int __init tcs3490_init(void) +{ + int rc = 0; + + pr_info("Initialize TCS3490 driver"); + rc = i2c_add_driver(&tcs3490_driver); + pr_info("TCS3490 added i2c driver rc = %d", rc); + + return rc; +} + +static void __exit tcs3490_exit(void) +{ + pr_info("Delete TCS3490 driver"); + i2c_del_driver(&tcs3490_driver); +} + +module_init(tcs3490_init); +module_exit(tcs3490_exit); + +MODULE_DESCRIPTION("AMS tcs3490 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/tcs3490.h b/drivers/misc/tcs3490.h new file mode 100644 index 0000000000000000000000000000000000000000..702eeff91e773279e29521062a9be13596ccedd3 --- /dev/null +++ b/drivers/misc/tcs3490.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ +/* + * Device driver for monitoring ambient light intensity in (lux), RGB, and + * color temperature (in kelvin) within the AMS-TAOS TCS family of devices. + * + * Copyright (c) 2016, AMS-TAOS USA, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __TCS3490_H +#define __TCS3490_H + +#include + +/* Max number of segments allowable in LUX table */ +#define TCS3490_MAX_LUX_TABLE_SIZE 9 +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * TCS3490_MAX_LUX_TABLE_SIZE) + +/* Default LUX and Color coefficients */ + +#define D_Factor 304 +#define R_Coef 205 +#define G_Coef 1024 +#define B_Coef (-184) +#define CT_Coef (4659) +#define CT_Offset (1023) + +#define D_Factor1 304 +#define R_Coef1 205 +#define G_Coef1 1024 +#define B_Coef1 (-184) +#define CT_Coef1 (4659) +#define CT_Offset1 (1023) + +struct device; + +enum tcs3490_pwr_state { + POWER_ON, + POWER_OFF, + POWER_STANDBY, +}; + +enum tcs3490_ctrl_reg { + AGAIN_1 = (0 << 0), + AGAIN_4 = (1 << 0), + AGAIN_16 = (2 << 0), + AGAIN_64 = (3 << 0), +}; + +#define ALS_PERSIST(p) (((p) & 0xf) << 3) + +struct tcs3490_parameters { + u8 als_time; + u16 als_deltaP; + u8 als_gain; + u8 persist; +}; + +struct lux_segment { + int d_factor; + int r_coef; + int g_coef; + int b_coef; + int ct_coef; + int ct_offset; +}; + + +struct tcs3490_i2c_platform_data { + /* The following callback for power events received and handled by + the driver. Currently only for SUSPEND and RESUME */ + int (*platform_power)(struct device *dev, enum tcs3490_pwr_state state); + int (*platform_init)(void); + void (*platform_teardown)(struct device *dev); + char const *als_name; + struct tcs3490_parameters parameters; + bool als_can_wake; + struct lux_segment *segment; + int segment_num; +}; + +#endif /* __TCS3490_H */ diff --git a/drivers/misc/tof_sensor.c b/drivers/misc/tof_sensor.c new file mode 100644 index 0000000000000000000000000000000000000000..9809c25d03fb686f96ffbed8f6204f38baab1178 --- /dev/null +++ b/drivers/misc/tof_sensor.c @@ -0,0 +1,646 @@ +/* + * Tof sensor driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define ENABLE_LOGE + +#ifdef ENABLE_LOGE +#define LOGE(dev, f, a...) dev_err(dev, "%s: " f, __func__, ##a) +#else +#define LOGE(dev, f, a...) +#endif + +#ifdef ENABLE_LOGI +#define LOGI(dev, f, a...) dev_info(dev, "%s: " f, __func__, ##a) +#else +#define LOGI(dev, f, a...) +#endif + +#ifdef ENABLE_LOGD +#define LOGD(dev, f, a...) dev_dbg(dev, "%s: " f, __func__, ##a) +#else +#define LOGD(dev, f, a...) +#endif + +#define TOF_SENSOR_SYSFS_LINK_NAME "tof_sensor" +#define TOF_SENSOR_PINCTRL_IRQ_ACTIVE "tof_irq_active" +#define TOF_SENSOR_PINCTRL_IRQ_SUSPEND "tof_irq_suspend" +#define TOF_SENSOR_REGULATOR_NOTIFY_PRIORITY (1000) + +struct tof_sensor_info { + char name[8]; + uint32_t i2c_client_id; + uint32_t need_camera_on; + uint32_t facing; + uint32_t vdd_supply_enable; + uint32_t gpio_avdd_enable; +}; + +struct tof_sensor_data { + struct i2c_client *client; + struct regulator *avdd; + struct regulator *gpio_avdd; + uint32_t min_voltage; + uint32_t max_voltage; + uint8_t power_up; + bool is_avdd_enabled; + bool is_register_regulator_cam_vio_notify; + struct input_dev *input; + const char *dev_name; + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + struct tof_sensor_info info; + struct notifier_block cam_io_regulator_cb; + int tof_reset_gpio; + uint32_t ref_cnt; +}; + +static int tof_sensor_cam_io_notifier_call(struct notifier_block *self, + unsigned long event, void *data) +{ + struct tof_sensor_data *tof_data; + uint32_t rc; + + if (event & REGULATOR_EVENT_DISABLE) { + tof_data = container_of(self, struct tof_sensor_data, + cam_io_regulator_cb); + LOGD(&tof_data->client->dev, + "%s REGULATOR_EVENT_DISABLE occurred event \n", __func__); + if (tof_data->is_avdd_enabled && tof_data->avdd) { + rc = regulator_disable(tof_data->avdd); + tof_data->is_avdd_enabled = false; + if (!rc) + LOGD(&tof_data->client->dev, + "%s: Regulator avdd disable OK", + __func__); + else + LOGE(&tof_data->client->dev, + "%s: Regulator avdd disable failed ", + __func__); + } + } + return NOTIFY_OK; +} + +static int tof_sensor_power_init(struct tof_sensor_data *data) +{ + int rc = 0; + + if ((data->avdd == NULL) && data->info.vdd_supply_enable) { + data->avdd = regulator_get(&data->client->dev, "tof_avdd"); + if (IS_ERR(data->avdd)) { + rc = PTR_ERR(data->avdd); + LOGE(&data->client->dev, + "Regulator get failed, avdd, rc = %d\n", rc); + return rc; + } + } + if ((data->gpio_avdd == NULL) && data->info.gpio_avdd_enable) { + data->gpio_avdd = regulator_get(&data->client->dev, "tof-gpio-avdd"); + if (IS_ERR(data->gpio_avdd)) { + rc = PTR_ERR(data->gpio_avdd); + LOGE(&data->client->dev, + "Regulator get failed, avdd, rc = %d\n", rc); + return rc; + } + } + data->is_avdd_enabled = false; + data->is_register_regulator_cam_vio_notify = false; + LOGI(&data->client->dev, "%s: power init, regulator get OK", + __func__); + return rc; +} + +static int tof_sensor_power_deinit(struct tof_sensor_data *data) +{ + int rc = 0; + if (!data) + return -EINVAL; + if (data->info.vdd_supply_enable && data->avdd) + regulator_put(data->avdd); + if (data->is_register_regulator_cam_vio_notify) { + regulator_unregister_notifier(data->gpio_avdd, &(data->cam_io_regulator_cb)); + data->is_register_regulator_cam_vio_notify = false; + } + if (data->info.gpio_avdd_enable && data->gpio_avdd) + regulator_put(data->gpio_avdd); + return rc; +} + +static int tof_sensor_pinctrl_init(struct tof_sensor_data *data) +{ + + data->pinctrl = devm_pinctrl_get(&data->client->dev); + if (IS_ERR_OR_NULL(data->pinctrl)) { + LOGE(&data->client->dev, + "%s:%d Getting pinctrl handle failed\n", + __func__, __LINE__); + return -EINVAL; + } + data->gpio_state_active = + pinctrl_lookup_state(data->pinctrl, + TOF_SENSOR_PINCTRL_IRQ_ACTIVE); + if (IS_ERR_OR_NULL(data->gpio_state_active)) { + LOGE(&data->client->dev, + "%s:Failed to get the active state pinctrl handle\n", + __func__); + return -EINVAL; + } + data->gpio_state_suspend + = pinctrl_lookup_state(data->pinctrl, + TOF_SENSOR_PINCTRL_IRQ_SUSPEND); + if (IS_ERR_OR_NULL(data->gpio_state_suspend)) { + LOGE(&data->client->dev, + "%s:Failed to get the suspend state pinctrl handle\n", + __func__); + return -EINVAL; + } + return 0; +} + +static int tof_sensor_gpio_init(struct tof_sensor_data *data) +{ + int rc = 0; + int flag = 0; + + rc = gpio_request_one(data->tof_reset_gpio, flag, "tof-reset-gpio"); + if (rc) { + LOGE(&data->client->dev, + "%s:%d Getting reset gpio failed\n", + __func__, __LINE__); + return -EINVAL; + } else { + return 0; + } +} + +static int tof_sensor_parse_dt(struct tof_sensor_data *data) +{ + int rc = 0; + const char *name = NULL; + uint32_t val_u32 = 0; + + rc = of_property_read_string_index(data->client->dev.of_node, + "sony,tof-sensor-name", 0, (const char **)(&name)); + if (rc < 0) { + LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); + goto fail; + } + memcpy(data->info.name, name, 8); + + rc = of_property_read_u32(data->client->dev.of_node, + "sony,tof-need-cam-on", &val_u32); + if (rc < 0) { + LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); + goto fail; + } + data->info.need_camera_on = val_u32; + + rc = of_property_read_u32(data->client->dev.of_node, + "sony,tof-sensor-facing", &val_u32); + if (rc < 0) { + LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); + goto fail; + } + data->info.facing = val_u32; + + data->tof_reset_gpio = of_get_named_gpio(data->client->dev.of_node, + "tof-reset-gpio", 0); + + rc = of_property_read_u32(data->client->dev.of_node, + "sony,tof-avdd-supply", &val_u32); + if (rc < 0) { + LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); + goto fail; + } + data->info.vdd_supply_enable = val_u32; + + rc = of_property_read_u32(data->client->dev.of_node, + "sony,tof-gpio-avdd", &val_u32); + if (rc < 0) { + LOGE(&data->client->dev, "%s failed %d\n", __func__, __LINE__); + goto fail; + } + data->info.gpio_avdd_enable = val_u32; + + + LOGD(&data->client->dev, "%s name %s, need_camera_on %d, facing %d\n", + __func__, data->info.name, data->info.need_camera_on, + data->info.facing); + +fail: + return rc; +} + +static irqreturn_t tof_sensor_irq(int irq, void *handle) +{ + struct tof_sensor_data *data = handle; + + LOGI(&data->client->dev, "Tof sensor irq"); + sysfs_notify(&data->input->dev.kobj, NULL, "tof_ranging_notify"); + + return IRQ_HANDLED; +} + +static ssize_t tof_sensor_power_ctl(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct tof_sensor_data *data = dev_get_drvdata(dev); + int rc = 0; + unsigned long value = 0; + + LOGD(&data->client->dev, + "%s: Power control data", __func__); + rc = kstrtoul(buf, sizeof(value), &value); + if (rc) { + LOGE(&data->client->dev, + "%s: covert error. rc = %d", __func__, rc); + return count; + } + if (value == TOF_SENSOR_POWER_CONTROL_ON) { + if (!data->ref_cnt) { + /* power on */ + if (!rc && + data->pinctrl && data->gpio_state_active) { + rc = pinctrl_select_state(data->pinctrl, + data->gpio_state_active); + if (rc) + LOGE(&data->client->dev, + "%s: cannot set pin to active state", + __func__); + } + if (!rc) { + rc = request_threaded_irq(data->client->irq, NULL, + &tof_sensor_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&data->client->dev), data); + if (rc) { + LOGE(&data->client->dev, + "Failed to request irq %d\n", + data->client->irq); + (void)pinctrl_select_state(data->pinctrl, + data->gpio_state_suspend); + } + } + if (!rc && data->is_register_regulator_cam_vio_notify) { + regulator_unregister_notifier(data->gpio_avdd, + &(data->cam_io_regulator_cb)); + data->is_register_regulator_cam_vio_notify = false; + } + if (!rc && data->info.vdd_supply_enable) { + rc = regulator_enable(data->avdd); + if (rc) { + LOGE(&data->client->dev, + "Regulator avdd enable failed rc=%d", + rc); + pinctrl_select_state(data->pinctrl, + data->gpio_state_suspend); + free_irq(data->client->irq, data); + return count; + } + data->is_avdd_enabled = true; + usleep_range(3000, 4000); + } + if (!rc && data->info.gpio_avdd_enable) { + rc = regulator_enable(data->gpio_avdd); + if (rc) { + LOGE(&data->client->dev, + "Regulator gpio_avdd enable failed rc=%d", rc); + pinctrl_select_state(data->pinctrl, + data->gpio_state_suspend); + free_irq(data->client->irq, data); + return count; + } + } + if (!rc && data->info.need_camera_on) { + rc = gpio_direction_output(data->tof_reset_gpio, 1); + if (rc) { + LOGE(&data->client->dev, + "%s: reset enable failed", + __func__); + regulator_disable(data->avdd); + free_irq(data->client->irq, data); + pinctrl_select_state(data->pinctrl, + data->gpio_state_suspend); + return count; + } + LOGD(&data->client->dev, + "%s: power up regulator enable OK", + __func__); + usleep_range(3000, 4000); + data->power_up = 1; + } + } + data->ref_cnt++; + } else if (value == TOF_SENSOR_POWER_CONTROL_OFF) { + /* power off */ + if (data->ref_cnt > 0) { + data->ref_cnt--; + if (!data->ref_cnt) { + if (data->info.need_camera_on) { + rc = gpio_direction_output(data->tof_reset_gpio, 0); + if (rc) + LOGE(&data->client->dev, + "%s: reset disable failed", + __func__); + else { + data->power_up = 0; + LOGD(&data->client->dev, + "%s: reset disable ok", + __func__); + } + } + if (data->info.gpio_avdd_enable && data->info.vdd_supply_enable && + !data->is_register_regulator_cam_vio_notify) { + data->cam_io_regulator_cb.notifier_call = + tof_sensor_cam_io_notifier_call; + data->cam_io_regulator_cb.priority = + TOF_SENSOR_REGULATOR_NOTIFY_PRIORITY; + if (regulator_register_notifier(data->gpio_avdd, + &(data->cam_io_regulator_cb))) + LOGE(&data->client->dev, + "%s Regulator notification registration failed!\n", + __func__); + else + data->is_register_regulator_cam_vio_notify = true; + } + if (data->info.gpio_avdd_enable) { + rc = regulator_disable(data->gpio_avdd); + if (rc) { + LOGE(&data->client->dev, + "%s: Regulator gpio_avdd disable failed ", + __func__); + } else { + LOGE(&data->client->dev, + "%s: power up gpio_regulator disable OK", + __func__); + } + } + if (data->info.vdd_supply_enable) { + if (!data->is_register_regulator_cam_vio_notify) { + rc = regulator_disable(data->avdd); + data->is_avdd_enabled = 0; + if (!rc) + LOGD(&data->client->dev, + "%s: Regulator avdd disable OK", + __func__); + else + LOGE(&data->client->dev, + "%s: Regulator avdd disable failed ", + __func__); + } + } + free_irq(data->client->irq, data); + if (data->pinctrl && data->gpio_state_suspend) { + rc = pinctrl_select_state(data->pinctrl, + data->gpio_state_suspend); + if (rc) + LOGE(&data->client->dev, + "%s: cannot set pin to suspend state", + __func__); + } + } + } + } else + LOGE(&data->client->dev, + "%s: Power control data error [%ld]", __func__, + value); + + return count; +} + +static ssize_t tof_sensor_show_power_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tof_sensor_data *data = dev_get_drvdata(dev); + + LOGI(dev, "Get power stat is called"); + + return snprintf(buf, PAGE_SIZE, "%d\n", data->power_up); +} + +static ssize_t tof_sensor_ranging_notify(struct device *dev, + struct device_attribute *attr, char *buf) +{ + LOGI(dev, "Ranging notify is called"); + + return snprintf(buf, PAGE_SIZE, "%d", 1); +} + +static ssize_t tof_sensor_get_info(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tof_sensor_data *data = dev_get_drvdata(dev); + + LOGI(dev, "Get information is called"); + + return snprintf(buf, PAGE_SIZE, "%s,%u,%u,%u", data->info.name, + data->info.i2c_client_id, data->info.need_camera_on, + data->info.facing); +} + +static DEVICE_ATTR(tof_get_info, S_IRUGO, tof_sensor_get_info, NULL); +static DEVICE_ATTR(tof_ranging_notify, S_IRUGO, tof_sensor_ranging_notify, + NULL); +static DEVICE_ATTR(tof_power_ctl, S_IRUGO | S_IWUSR | S_IWGRP, + tof_sensor_show_power_status, tof_sensor_power_ctl); +static struct attribute *tof_sensor_attributes[] = { + &dev_attr_tof_get_info.attr, + &dev_attr_tof_ranging_notify.attr, + &dev_attr_tof_power_ctl.attr, + NULL +}; + +static const struct attribute_group tof_sensor_attr_group = { + .attrs = tof_sensor_attributes, +}; + +static int tof_sensor_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tof_sensor_data *data; + int rc = 0; + + LOGI(&client->dev, "start probing stmdata\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + LOGE(&client->dev, + "%s: check_functionality failed.", __func__); + return -EIO; + } + + data = kzalloc(sizeof(struct tof_sensor_data), GFP_KERNEL); + if (!data) { + LOGE(&client->dev, "failed: no memory to alloc data %p", data); + return -ENOMEM; + } + + i2c_set_clientdata(client, data); + data->client = client; + if (data->client->dev.of_node) { + rc = tof_sensor_pinctrl_init(data); + if (rc) { + LOGE(&client->dev, + "%s: failed to pinctrl init", __func__); + goto exit_free_i2c; + } + rc = tof_sensor_parse_dt(data); + if (rc) { + LOGE(&client->dev, + "%s: failed to parse dt", __func__); + goto exit_free_i2c; + } + } + rc = tof_sensor_power_init(data); + if (rc) { + LOGE(&client->dev, + "%s: failed to power init", __func__); + goto exit_free_i2c; + } + tof_sensor_gpio_init(data); + data->ref_cnt = 0; + + data->dev_name = dev_name(&client->dev); + data->input = input_allocate_device(); + if (!data->input) { + rc = -ENOMEM; + LOGE(&client->dev, + "%s: failed to allocate input device", __func__); + goto exit_free_i2c; + } + data->input->name = "ToF Sensor"; + rc = input_register_device(data->input); + if (rc) { + LOGE(&client->dev, "Failed to register input device"); + goto exit_free_dev; + } + input_set_drvdata(data->input, data); + + LOGI(&client->dev, "tof sensor: i2c nr%d\n", client->adapter->nr); + data->info.i2c_client_id = client->adapter->nr; + data->power_up = 0; + + /* sysfs */ + rc = sysfs_create_group(&data->input->dev.kobj, + &tof_sensor_attr_group); + if (rc) { + rc = -ENOMEM; + LOGE(&client->dev, + "%s: failed to create sysfs group", __func__); + goto exit_unregister_sysfs; + } + + rc = sysfs_create_link(data->input->dev.kobj.parent, + &data->input->dev.kobj, + TOF_SENSOR_SYSFS_LINK_NAME); + if (rc) { + rc = -ENOMEM; + LOGE(&client->dev, + "%s: failed to create sysfs link", __func__); + goto exit_unregister_sysfs; + } + return rc; + +exit_unregister_sysfs: + input_unregister_device(data->input); +exit_free_dev: + input_free_device(data->input); +exit_free_i2c: + tof_sensor_power_deinit(data); + i2c_set_clientdata(data->client, NULL); + kfree(data); + + return rc; +} + +static int tof_sensor_remove(struct i2c_client *client) +{ + struct tof_sensor_data *data = i2c_get_clientdata(client); + + gpio_free(data->tof_reset_gpio); + tof_sensor_power_deinit(data); + sysfs_remove_link(&data->input->dev.kobj, + TOF_SENSOR_SYSFS_LINK_NAME); + input_unregister_device(data->input); + input_free_device(data->input); + LOGI(&data->client->dev, "Removed."); + i2c_set_clientdata(data->client, NULL); + kfree(data); + + return 0; +} + +static struct i2c_device_id tof_sensor_id[] = { + { "tof_sensor", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, tof_sensor_id); + +static struct of_device_id st_tof_sensor_dt_match[] = { + { .compatible = "tof_sensor", }, + { }, +}; + +static struct i2c_driver tof_sensor_i2c_driver = { + .driver = { + .name = "tof_sensor", + .owner = THIS_MODULE, + .of_match_table = st_tof_sensor_dt_match, + }, + .probe = tof_sensor_probe, + .remove = tof_sensor_remove, + .id_table = tof_sensor_id, +}; + +static int __init tof_sensor_init(void) +{ + int rc = 0; + + pr_info("%s: Initialize i2c driver", __func__); + rc = i2c_add_driver(&tof_sensor_i2c_driver); + pr_info("%s: Added i2c driver rc = %d", __func__, rc); + + return rc; +} + +static void __exit tof_sensor_exit(void) +{ + pr_info("Delete i2c driver"); + i2c_del_driver(&tof_sensor_i2c_driver); +} + +module_init(tof_sensor_init); +module_exit(tof_sensor_exit); + +MODULE_DESCRIPTION("Tof sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c new file mode 100644 index 0000000000000000000000000000000000000000..fa5f8ab1d744d7ee50995bad584b2bdda7ad3d6b --- /dev/null +++ b/drivers/misc/uid_stat.c @@ -0,0 +1,158 @@ +/* drivers/misc/uid_stat.c + * + * Copyright (C) 2008 - 2009 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. + * + */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(uid_lock); +static LIST_HEAD(uid_list); +static struct proc_dir_entry *parent; + +struct uid_stat { + struct list_head link; + uid_t uid; + atomic_t tcp_rcv; + atomic_t tcp_snd; +}; + +static struct uid_stat *find_uid_stat(uid_t uid) { + struct uid_stat *entry; + + list_for_each_entry(entry, &uid_list, link) { + if (entry->uid == uid) { + return entry; + } + } + return NULL; +} + +static int uid_stat_atomic_int_show(struct seq_file *m, void *v) +{ + unsigned int bytes; + atomic_t *counter = m->private; + + bytes = (unsigned int) (atomic_read(counter) + INT_MIN); + seq_printf(m, "%u\n", bytes); + return 0; +} + +static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file) +{ + return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode)); +} + +static const struct file_operations uid_stat_read_atomic_int_fops = { + .open = uid_stat_read_atomic_int_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/* Create a new entry for tracking the specified uid. */ +static struct uid_stat *create_stat(uid_t uid) { + struct uid_stat *new_uid; + /* Create the uid stat struct and append it to the list. */ + new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC); + if (!new_uid) + return NULL; + + new_uid->uid = uid; + /* Counters start at INT_MIN, so we can track 4GB of network traffic. */ + atomic_set(&new_uid->tcp_rcv, INT_MIN); + atomic_set(&new_uid->tcp_snd, INT_MIN); + + list_add_tail(&new_uid->link, &uid_list); + return new_uid; +} + +static void create_stat_proc(struct uid_stat *new_uid) +{ + char uid_s[32]; + struct proc_dir_entry *entry; + sprintf(uid_s, "%d", new_uid->uid); + entry = proc_mkdir(uid_s, parent); + + /* Keep reference to uid_stat so we know what uid to read stats from. */ + proc_create_data("tcp_snd", S_IRUGO, entry, + &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd); + + proc_create_data("tcp_rcv", S_IRUGO, entry, + &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv); +} + +static struct uid_stat *find_or_create_uid_stat(uid_t uid) +{ + struct uid_stat *entry; + unsigned long flags; + spin_lock_irqsave(&uid_lock, flags); + entry = find_uid_stat(uid); + if (entry) { + spin_unlock_irqrestore(&uid_lock, flags); + return entry; + } + entry = create_stat(uid); + spin_unlock_irqrestore(&uid_lock, flags); + if (entry) + create_stat_proc(entry); + return entry; +} + +int uid_stat_tcp_snd(uid_t uid, int size) { + struct uid_stat *entry; + activity_stats_update(); + entry = find_or_create_uid_stat(uid); + if (!entry) + return -1; + atomic_add(size, &entry->tcp_snd); + return 0; +} + +int uid_stat_tcp_rcv(uid_t uid, int size) { + struct uid_stat *entry; + activity_stats_update(); + entry = find_or_create_uid_stat(uid); + if (!entry) + return -1; + atomic_add(size, &entry->tcp_rcv); + return 0; +} + +static int __init uid_stat_init(void) +{ + parent = proc_mkdir("uid_stat", NULL); + if (!parent) { + pr_err("uid_stat: failed to create proc entry\n"); + return -1; + } + return 0; +} + +__initcall(uid_stat_init); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 38ed503c637ba1c0dd8b9dbe2675ef7c4b787a9f..a3392f98dca51df31efcab9a5e672e169d63c37f 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -17,6 +17,11 @@ * Author: Andrew Christian * 28 May 2002 */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -1537,12 +1542,19 @@ static int get_card_status(struct mmc_card *card, u32 *status, int retries) return err; } +#define EXE_ERRORS \ + (R1_OUT_OF_RANGE | /* Command argument out of range */ \ + R1_ADDRESS_ERROR | /* Misaligned address */ \ + R1_WP_VIOLATION | /* Tried to write to protected block */ \ + R1_CARD_ECC_FAILED | /* ECC error */ \ + R1_ERROR) /* General/unknown error */ + static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, bool hw_busy_detect, struct request *req, int *gen_err) { unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); int err = 0; - u32 status; + u32 status, first_status = 0; do { err = get_card_status(card, &status, 5); @@ -1552,6 +1564,9 @@ static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, return err; } + if (!first_status) + first_status = status; + if (status & R1_ERROR) { pr_err("%s: %s: error sending status cmd, status %#x\n", req->rq_disk->disk_name, __func__, status); @@ -1582,6 +1597,14 @@ static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, } while (!(status & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(status) == R1_STATE_PRG)); + /* Check for errors during cmd execution. In this case + * the execution was terminated. */ + if (first_status & EXE_ERRORS) { + pr_err("%s: error during r/w command, err status %#x, status %#x\n", + req->rq_disk->disk_name, first_status, status); + return MMC_BLK_ABORT; + } + return err; } @@ -2264,12 +2287,25 @@ static int mmc_blk_err_check(struct mmc_card *card, return MMC_BLK_ABORT; } + /* Check execution mode errors. If stop cmd was sent, these + * errors would be reported in response to it. In this case + * the execution is retried using single-block read. */ + if (brq->stop.resp[0] & EXE_ERRORS) { + pr_err("%s: error during r/w command, stop response %#x\n", + req->rq_disk->disk_name, brq->stop.resp[0]); + return MMC_BLK_RETRY_SINGLE; + } + /* * Everything else is either success, or a data error of some * kind. If it was a write, we may have transitioned to * program mode, which we have to wait for it to complete. + * If pre defined block count (CMD23) was used, no stop + * cmd was sent and we need to read status to check + * for errors during cmd execution. */ - if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) { + if (!mmc_host_is_spi(card->host) && + (rq_data_dir(req) != READ || brq->sbc.opcode == MMC_SET_BLOCK_COUNT)) { int err; /* Check stop command response */ @@ -3850,6 +3886,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) break; goto cmd_abort; } + case MMC_BLK_RETRY_SINGLE: case MMC_BLK_ECC_ERR: if (brq->data.blocks > 1) { /* Redo read one sector at a time */ @@ -4736,7 +4773,13 @@ static int mmc_blk_probe(struct mmc_card *card) } pm_runtime_use_autosuspend(&card->dev); - pm_runtime_set_autosuspend_delay(&card->dev, MMC_AUTOSUSPEND_DELAY_MS); + + if (mmc_card_sd(card)) + pm_runtime_set_autosuspend_delay(&card->dev, + MMC_SDCARD_AUTOSUSPEND_DELAY_MS); + else + pm_runtime_set_autosuspend_delay(&card->dev, MMC_AUTOSUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(&card->dev); /* diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index 9e0ccdc44d6b1fccbdb9cd7d084a2652223d3b34..2b41b09667405eeddd087c7c23d18280836760ee 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -37,3 +37,19 @@ config MMC_CLKGATE support handling this in order for it to be of any use. If unsure, say N. + +config MMC_CMD_DEBUG + bool "Debug feature to get mmc command issued" + default n + help + This is a debug feature to get the mmc command issued + in order to debug certain issues from the logs. + +config MMC_CMD_QUEUE_SIZE + int "mmc command queue size" + depends on MMC_CMD_DEBUG + default 256 + help + Select the size of the circular queue to store the MMC command + issued. + diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 76dbbbde884bfcf0d19e3a68ed66d7c9070e02b2..35642bb1462af8746be9351a241a122a26c1a910 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -10,6 +10,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -1098,6 +1103,24 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) if (mmc_card_removed(host->card)) return -ENOMEDIUM; +#ifdef CONFIG_MMC_CMD_DEBUG + if (host->card) { + struct mmc_cmdq *cq = NULL; + cq = &host->card->cmd_stats.cmdq[host->card-> + cmd_stats.next_idx]; + cq->opcode = mrq->cmd->opcode; + cq->arg = mrq->cmd->arg; + cq->flags = mrq->cmd->flags; + cq->timestamp = sched_clock(); + host->card->cmd_stats.next_idx++; + + if (host->card->cmd_stats.next_idx == CMD_QUEUE_SIZE) { + host->card->cmd_stats.next_idx = 0; + host->card->cmd_stats.wrapped = 1; + } + } +#endif + if (mrq->sbc) { pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n", mmc_hostname(host), mrq->sbc->opcode, @@ -3061,7 +3084,10 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) host->card_clock_off = false; /* Wait for at least 1 ms according to spec */ - mmc_delay(1); + if (host->caps & MMC_CAP_NONREMOVABLE) + mmc_delay(1); + else + mmc_delay(40); /* * Failure to switch is indicated by the card holding @@ -4172,6 +4198,8 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) return 0; if (!mmc_attach_sd(host)) return 0; + else + mmc_gpio_tray_close_set_uim2(host, 1); if (!mmc_attach_mmc(host)) return 0; @@ -4372,6 +4400,8 @@ void mmc_stop_host(struct mmc_host *host) cancel_delayed_work_sync(&host->detect); mmc_flush_scheduled_work(); + mmc_gpio_set_uim2_en(host, 0); + /* clear pm flags now and let card drivers set them as needed */ host->pm_flags = 0; @@ -4537,6 +4567,9 @@ int mmc_pm_notify(struct notifier_block *notify_block, host->rescan_disable = 1; spin_unlock_irqrestore(&host->lock, flags); cancel_delayed_work_sync(&host->detect); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + mmc_cd_prepare_suspend(host); +#endif if (!host->bus_ops) break; diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 26fa4a4d96b06bc1ed78f1cd5823ad96f6a2baef..ef23ec94a13867c09c0e793a95ae975fc0506a8b 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -7,6 +7,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -866,6 +871,50 @@ static const struct file_operations mmc_dbg_bkops_stats_fops = { .write = mmc_bkops_stats_write, }; +#ifdef CONFIG_MMC_CMD_DEBUG +static int mmc_cmd_stats_show(struct seq_file *s, void *data) +{ + int i; + struct mmc_card *card = s->private; + unsigned long long t; + unsigned long rem_nsec; + + if (card->cmd_stats.wrapped) { + for (i = card->cmd_stats.next_idx; i < CMD_QUEUE_SIZE; i++) { + t = card->cmd_stats.cmdq[i].timestamp; + rem_nsec = do_div(t, 1000000000); + seq_printf(s, "[%5llu.%06lu] CMD%u arg %08x flags %08x\n", + t, (rem_nsec/1000), + card->cmd_stats.cmdq[i].opcode, + card->cmd_stats.cmdq[i].arg, + card->cmd_stats.cmdq[i].flags); + } + } + + for (i = 0; i < card->cmd_stats.next_idx; i++) { + t = card->cmd_stats.cmdq[i].timestamp; + rem_nsec = do_div(t, 1000000000); + seq_printf(s, "[%5llu.%06lu] CMD%u arg %08x flags %08x\n", + t, (rem_nsec/1000), + card->cmd_stats.cmdq[i].opcode, + card->cmd_stats.cmdq[i].arg, + card->cmd_stats.cmdq[i].flags); + } + + return 0; +} + +static int mmc_cmd_stats_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, mmc_cmd_stats_show, inode->i_private); +} + +static const struct file_operations mmc_dbg_card_rq_cmdq_fops = { + .open = mmc_cmd_stats_open, + .read = seq_read, +}; +#endif + void mmc_add_card_debugfs(struct mmc_card *card) { struct mmc_host *host = card->host; @@ -892,7 +941,12 @@ void mmc_add_card_debugfs(struct mmc_card *card) if (!debugfs_create_file("status", S_IRUSR, root, card, &mmc_dbg_card_status_fops)) goto err; - +#ifdef CONFIG_MMC_CMD_DEBUG + if (mmc_card_mmc(card) || mmc_card_sd(card)) + if (!debugfs_create_file("mmc_cmd_stats", S_IRUSR, root, + card, &mmc_dbg_card_rq_cmdq_fops)) + goto err; +#endif if (mmc_card_mmc(card)) if (!debugfs_create_file("ext_csd", S_IRUSR, root, card, &mmc_dbg_ext_csd_fops)) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index bf896b6054873b085f94c4e5d1789a67365b3ecc..de936e2ff365a3beced1c8ec95dc6884c71f8305 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -9,6 +9,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -312,7 +317,7 @@ static int mmc_read_switch(struct mmc_card *card) * The argument does not matter, as the support bits do not * change with the arguments. */ - err = mmc_sd_switch(card, 0, 0, 0, status); + err = mmc_sd_switch(card, 0, 0, 1, status); if (err) { /* * If the host or the card can't do the switch, diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index baa60fbccbaeedad1b8f65e134b60d14f9353a74..83c2926e98e577fd57c56ff0a4ad83c5174a549c 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -7,6 +7,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -26,21 +31,93 @@ struct mmc_gpio { bool override_ro_active_level; bool override_cd_active_level; irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id); + bool status; + int uim2_gpio; char *ro_label; char cd_label[0]; +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + bool suspended; +#endif }; +int mmc_gpio_get_status(struct mmc_host *host) +{ + int ret = -ENOSYS; + struct mmc_gpio *ctx = host->slot.handler_priv; + + if (!ctx || !gpio_is_valid(desc_to_gpio(ctx->cd_gpio))) + goto out; + + ret = !gpio_get_value_cansleep(desc_to_gpio(ctx->cd_gpio)) ^ + !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); +out: + return ret; +} + +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME +void mmc_cd_prepare_suspend(struct mmc_host *host) +{ + struct mmc_gpio *ctx = host->slot.handler_priv; + + if (!ctx) + return; + + ctx->suspended = true; +} +EXPORT_SYMBOL(mmc_cd_prepare_suspend); +#endif + static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) { /* Schedule a card detection after a debounce timeout */ struct mmc_host *host = dev_id; + struct mmc_gpio *ctx = host->slot.handler_priv; + int status; +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + unsigned long flags; +#endif int present = host->ops->get_cd(host); pr_debug("%s: cd gpio irq, gpio state %d (CARD_%s)\n", mmc_hostname(host), present, present?"INSERT":"REMOVAL"); - host->trigger_card_event = true; - mmc_detect_change(host, msecs_to_jiffies(200)); + if (!host->ops) + goto out; + + status = mmc_gpio_get_status(host); + if (unlikely(status < 0)) + goto out; + + if (status == 0) + mmc_gpio_set_uim2_en(host, 0); + + if (status ^ ctx->status) { + pr_info("%s: slot status change detected (%d -> %d), GPIO_ACTIVE_%s\n", + mmc_hostname(host), ctx->status, status, + (host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH) ? + "HIGH" : "LOW"); + ctx->status = status; + + host->trigger_card_event = true; +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + if (ctx->suspended) { + /* + * host->rescan_disable is normally set to 0 in + * PM_POST_RESTORE of mmc_pm_notify but in case + * of a deferred resume we might get IRQ before + * it is called. + */ + spin_lock_irqsave(&host->lock, flags); + host->rescan_disable = 0; + spin_unlock_irqrestore(&host->lock, flags); + } + ctx->suspended = false; +#endif + + /* Schedule a card detection after a debounce timeout */ + mmc_detect_change(host, msecs_to_jiffies(200)); + } +out: return IRQ_HANDLED; } @@ -57,6 +134,7 @@ int mmc_gpio_alloc(struct mmc_host *host) snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); host->slot.handler_priv = ctx; host->slot.cd_irq = -EINVAL; + ctx->uim2_gpio = -EINVAL; } return ctx ? 0 : -ENOMEM; @@ -140,6 +218,12 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host) if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL) irq = -EINVAL; + ret = mmc_gpio_get_status(host); + if (ret < 0) + pr_warn("%s: failed to init cd_gpio status\n", mmc_hostname(host)); + else + ctx->status = ret; + if (irq >= 0) { if (!ctx->cd_gpio_isr) ctx->cd_gpio_isr = mmc_gpio_cd_irqt; @@ -211,6 +295,9 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, ctx->override_cd_active_level = true; ctx->cd_gpio = gpio_to_desc(gpio); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + ctx->suspended = false; +#endif return 0; } EXPORT_SYMBOL(mmc_gpio_request_cd); @@ -307,3 +394,42 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, return 0; } EXPORT_SYMBOL(mmc_gpiod_request_ro); + + +void mmc_gpio_init_uim2(struct mmc_host *host, unsigned int gpio) +{ + struct mmc_gpio *ctx; + + ctx = host->slot.handler_priv; + + ctx->uim2_gpio = gpio; + + pr_info("## %s: %s: gpio=%d\n", mmc_hostname(host), __func__, gpio); + + mmc_gpio_set_uim2_en(host, 0); +} +EXPORT_SYMBOL(mmc_gpio_init_uim2); + +void mmc_gpio_set_uim2_en(struct mmc_host *host, int value) +{ + struct mmc_gpio *ctx = host->slot.handler_priv; + + if (!ctx || !gpio_is_valid(ctx->uim2_gpio)) { + pr_err("## %s: gpio_set failure: ctx=%p, uim2_gpio=%d\n", + mmc_hostname(host), ctx, ctx ? ctx->uim2_gpio : 0); + return; + } + gpio_set_value(ctx->uim2_gpio, value); + pr_info("## %s: %s: gpio=%d value=%d\n", mmc_hostname(host), __func__, + ctx->uim2_gpio, value); +} +EXPORT_SYMBOL(mmc_gpio_set_uim2_en); + +void mmc_gpio_tray_close_set_uim2(struct mmc_host *host, int value) +{ + struct mmc_gpio *ctx = host->slot.handler_priv; + + if (ctx && ctx->status) + mmc_gpio_set_uim2_en(host, value); +} +EXPORT_SYMBOL(mmc_gpio_tray_close_set_uim2); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 01959bd2d523a8be18699233df59523ff9bad947..e0d8e1958ae4774041f0408bb194668c45becbc9 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -431,6 +431,14 @@ config MMC_SDHCI_MSM_ICE Select this if you have ICE supported for SDHCI on QTI chipset. If unsure, say N. +config MMC_ENABLE_CLK_SCALE + bool "Clock Scaling of SDHCI support" + depends on MMC_SDHCI_MSM + default n + help + This selects support for the SD/MMC Host Controller on + Clock Scaling. + config MMC_MSM tristate "Qualcomm SDCC Controller Support" depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index acece3299756e997b15020a100ad14def283c020..ab8452c73dba04974cd992c6f1eee9372cf37996 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -8,6 +8,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 81a781c1f9d6af6eeebab5d207e7e2abc7df8356..05ef6577341806ea7471a4eccf7249039ca54ea3 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -14,6 +14,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -1849,6 +1854,12 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, if (gpio_is_valid(pdata->status_gpio) && !(flags & OF_GPIO_ACTIVE_LOW)) pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; + pdata->uim2_gpio = of_get_named_gpio(np, "uim2-gpios", 0); + if (!gpio_is_valid(pdata->uim2_gpio)) { + pr_err("## %s: gpio_is_valid(pdata->uim2_gpio)=%d: failure\n", + mmc_hostname(msm_host->mmc), pdata->uim2_gpio); + } + of_property_read_u32(np, "qcom,bus-width", &bus_width); if (bus_width == 8) pdata->mmc_bus_width = MMC_CAP_8_BIT_DATA; @@ -4621,7 +4632,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->mmc->caps2 |= msm_host->pdata->caps2; msm_host->mmc->caps2 |= MMC_CAP2_BOOTPART_NOACC; msm_host->mmc->caps2 |= MMC_CAP2_HS400_POST_TUNING; +#ifdef CONFIG_MMC_ENABLE_CLK_SCALE msm_host->mmc->caps2 |= MMC_CAP2_CLK_SCALE; +#endif msm_host->mmc->caps2 |= MMC_CAP2_SANITIZE; msm_host->mmc->caps2 |= MMC_CAP2_MAX_DISCARD_SIZE; msm_host->mmc->caps2 |= MMC_CAP2_SLEEP_AWAKE; @@ -4674,6 +4687,13 @@ static int sdhci_msm_probe(struct platform_device *pdev) } } + if (gpio_is_valid(msm_host->pdata->uim2_gpio)) { + mmc_gpio_init_uim2(msm_host->mmc, msm_host->pdata->uim2_gpio); + } else { + pr_err("## %s: can't set uim2_gpio: %d\n", mmc_hostname(host->mmc), + msm_host->pdata->uim2_gpio); + } + if ((sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) && (dma_supported(mmc_dev(host->mmc), DMA_BIT_MASK(64)))) { host->dma_mask = DMA_BIT_MASK(64); diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h index 79949c2c537f8f79e4d0124d498f96328fa0234e..841b2462a197c060e25ff0d2e34262c2f6f000a2 100644 --- a/drivers/mmc/host/sdhci-msm.h +++ b/drivers/mmc/host/sdhci-msm.h @@ -11,6 +11,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __SDHCI_MSM_H__ #define __SDHCI_MSM_H__ @@ -143,6 +148,7 @@ struct sdhci_msm_pltfm_data { struct sdhci_msm_pin_data *pin_data; struct sdhci_pinctrl_data *pctrl_data; int status_gpio; /* card detection GPIO that is configured as IRQ */ + int uim2_gpio; struct sdhci_msm_bus_voting_data *voting_data; u32 *sup_clk_table; unsigned char sup_clk_cnt; diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 89f8d5979402dfaecd9d7565798804ed581cf261..7d8b3e4bf8f2c27a5725b031377ac391262caab2 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_AR5523) += ar5523/ -obj-$(CONFIG_WIL6210) += wil6210/ + obj-$(CONFIG_ATH10K) += ath10k/ obj-$(CONFIG_WCN36XX) += wcn36xx/ diff --git a/drivers/of/base.c b/drivers/of/base.c index 31341290cd9136ab0b2830c67422eb334ed32f4e..064299a672afde803c6af1b6c95a04b1aa63aa6f 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -17,6 +17,11 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 2a547ca3d44329d0d550dcd4517223157e20c341..fc563dd531943542e2718256e0d13766ae063207 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1,6 +1,11 @@ /* * Self tests for device tree subsystem */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "### dt-test ### " fmt diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 073b6d1e5efa493fcc4cc704288be32c5076f685..6a32fd4db2e9203c553dd325442d03fcc4979026 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -272,4 +272,10 @@ config PINCTRL_TB10X depends on OF && ARC_PLAT_TB10X select GPIOLIB +config PINCTRL_SOMC + bool "SoMC pinctrl driver" + default n + help + Select this to enable SoMC pinctrl driver. + endmenu diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 502b91f455d7b8656fa4e2146da94c6ab939e70b..31892072d0b03f44bf84a846c9683d458c003c94 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_PINCTRL_MSM8998) += pinctrl-msm8998.o obj-$(CONFIG_PINCTRL_SDM660) += pinctrl-sdm660.o obj-$(CONFIG_PINCTRL_WCD) += pinctrl-wcd.o obj-$(CONFIG_PINCTRL_LPI) += pinctrl-lpi.o +obj-$(CONFIG_PINCTRL_SOMC) += pinctrl-somc.o diff --git a/drivers/pinctrl/qcom/pinctrl-ipq8064.c b/drivers/pinctrl/qcom/pinctrl-ipq8064.c index bcb29c02f4b019288f7bc94b15775467941d9fb4..117190f01b9f44d0183bcdbd038d9be2f504451c 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq8064.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq8064.c @@ -11,6 +11,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 7b36cc4ad1101f72cb6a56785b04a313c39bf96d..18f227574d5911cd32530a8621db4b27c63471b8 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -66,6 +66,7 @@ struct msm_pinctrl { DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO); DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO); + DECLARE_BITMAP(disabled_pins, MAX_NR_GPIO); const struct msm_pinctrl_soc_data *soc; void __iomem *regs; @@ -502,9 +503,14 @@ static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { unsigned gpio = chip->base; unsigned i; + struct msm_pinctrl *pctrl = container_of(chip, + struct msm_pinctrl, chip); for (i = 0; i < chip->ngpio; i++, gpio++) { - msm_gpio_dbg_show_one(s, NULL, chip, i, gpio); + if (test_bit(i, pctrl->disabled_pins)) + seq_printf(s, " gpio%d is not accessible.", i); + else + msm_gpio_dbg_show_one(s, NULL, chip, i, gpio); seq_puts(s, "\n"); } } @@ -951,6 +957,8 @@ int msm_pinctrl_probe(struct platform_device *pdev, struct msm_pinctrl *pctrl; struct resource *res; int ret; + int disabled_pins_num; + const struct device_node *np = pdev->dev.of_node; msm_pinctrl_data = pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); @@ -986,6 +994,23 @@ int msm_pinctrl_probe(struct platform_device *pdev, return PTR_ERR(pctrl->pctrl); } + disabled_pins_num = of_property_count_u32_elems(np, "disabled-pins"); + if (disabled_pins_num > 0) { + int i; + u32 pin; + + for (i = 0; i < disabled_pins_num; i++) { + of_property_read_u32_index(np, + "disabled-pins", i, &pin); + if (pin < MAX_NR_GPIO) { + set_bit(pin, pctrl->disabled_pins); + dev_info(&pdev->dev, "pin %d disabled\n", pin); + } else { + dev_err(&pdev->dev, "pin %d out of range\n", + pin); + } + } + } ret = msm_gpio_init(pctrl); if (ret) { pinctrl_unregister(pctrl->pctrl); diff --git a/drivers/pinctrl/qcom/pinctrl-somc.c b/drivers/pinctrl/qcom/pinctrl-somc.c new file mode 100644 index 0000000000000000000000000000000000000000..4b7d3769d4e9365f5601fad7df6d9a36e1d91bd6 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-somc.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ +#include +#include +#include + +#define PLATFORM_COMMON_DEFAULT "platform_common_default" +#define PRODUCT_COMMON_DEFAULT "product_common_default" +#define VARIANT_DEFAULT "variant_default" + +static int somc_pinctrl_probe(struct platform_device *pdev) +{ + struct pinctrl *pin; + struct pinctrl_state *plat_default, *prod_default, *variant_default; + int ret = 0; + + pin = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(pin)) { + dev_err(&pdev->dev, "faild to get pinctrl handle\n"); + ret = -EPROBE_DEFER; + goto out; + }; + + /* Platform common settings */ + plat_default = pinctrl_lookup_state(pin, PLATFORM_COMMON_DEFAULT); + if (IS_ERR(plat_default)) { + dev_dbg(&pdev->dev, + "Can not lookup %s state\n", PLATFORM_COMMON_DEFAULT); + } else { + ret = pinctrl_select_state(pin, plat_default); + if (ret) + dev_err(&pdev->dev, + "failed to select %s state\n", + PLATFORM_COMMON_DEFAULT); + } + + /* Product common settings */ + prod_default = pinctrl_lookup_state(pin, PRODUCT_COMMON_DEFAULT); + if (IS_ERR(prod_default)) { + dev_dbg(&pdev->dev, + "Can not lookup %s state\n", PRODUCT_COMMON_DEFAULT); + } else { + ret = pinctrl_select_state(pin, prod_default); + if (ret) + dev_err(&pdev->dev, + "failed to select %s state\n", + PRODUCT_COMMON_DEFAULT); + } + + /* Variant specific settings */ + variant_default = pinctrl_lookup_state(pin, VARIANT_DEFAULT); + if (IS_ERR(variant_default)) { + dev_dbg(&pdev->dev, + "Can not lookup %s state\n", VARIANT_DEFAULT); + } else { + ret = pinctrl_select_state(pin, variant_default); + if (ret) + dev_err(&pdev->dev, + "failed to select %s state\n", + VARIANT_DEFAULT); + } + + devm_pinctrl_put(pin); +out: + return ret; +}; + +static const struct of_device_id somc_pinctrl_dt_match[] = { + { .compatible = "somc-pinctrl", }, + { }, +}; + +static struct platform_driver somc_pinctrl_drv = { + .probe = somc_pinctrl_probe, + .driver = { + .name = "somc-pinctrl", + .owner = THIS_MODULE, + .of_match_table = somc_pinctrl_dt_match, + }, +}; + +static int __init somc_pinctrl_drv_register(void) +{ + return platform_driver_register(&somc_pinctrl_drv); +} +arch_initcall(somc_pinctrl_drv_register); + +static void __exit somc_pinctrl_drv_unregister(void) +{ + platform_driver_unregister(&somc_pinctrl_drv); +} +module_exit(somc_pinctrl_drv_unregister); + +MODULE_LICENSE("GPL v2"); + + diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c index 73547abc5cf56fbddf49a8bdca945a39dc9d9638..4c0f13dfd0b99ad526e75dc219857095e7c98126 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c index 0fec8acde96ca776e273f884d43884190e7723a2..00ab55a49c3d00d0f68b6424f6f1975a2ada8606 100644 --- a/drivers/platform/msm/qpnp-revid.c +++ b/drivers/platform/msm/qpnp-revid.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -18,6 +23,9 @@ #include #include #include +#ifdef CONFIG_RAMDUMP_TAGS +#include +#endif #define REVID_REVISION1 0x0 #define REVID_REVISION2 0x1 @@ -127,6 +135,11 @@ static size_t build_pmic_string(char *buf, size_t n, int sid, u8 subtype, u8 rev1, u8 rev2, u8 rev3, u8 rev4) { size_t pos = 0; +#ifdef CONFIG_RAMDUMP_TAGS + char tag_name[64]; + char tag_data[64]; + int version_pos = 0; +#endif /* * In early versions of PM8941 and PM8226, the major revision number * started incrementing from 0 (eg 0 = v1.0, 1 = v2.0). @@ -145,11 +158,21 @@ static size_t build_pmic_string(char *buf, size_t n, int sid, else pos += snprintf(buf + pos, n - pos, ": %s", pmic_names[subtype]); +#ifdef CONFIG_RAMDUMP_TAGS + snprintf(tag_name, sizeof(tag_name), "pmic_%s_revision_str", + pmic_names[(subtype < ARRAY_SIZE(pmic_names)) ? subtype : 0]); + version_pos = pos + 1; +#endif pos += snprintf(buf + pos, n - pos, " v%d.%d", rev4, rev3); if (rev2 || rev1) pos += snprintf(buf + pos, n - pos, ".%d", rev2); if (rev1) pos += snprintf(buf + pos, n - pos, ".%d", rev1); + +#ifdef CONFIG_RAMDUMP_TAGS + snprintf(tag_data, sizeof(tag_data), "%s", buf + version_pos); + rdtags_add_tag(tag_name, tag_data, strlen(tag_data)); +#endif return pos; } diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index b13cd074c52afc90c086bc16cc011b7adb0ac91f..296ab2854be4c509e55bdfdc172687dfed7e06ff 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -9,6 +9,11 @@ * * You may use this code as per GPL version 2 */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 1974d6ee032b5190916bd1b6728b069162a5f48f..e258a107a0dab7778c61c1d61dbfab55be323e75 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -10,6 +10,11 @@ * * You may use this code as per GPL version 2 */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -81,7 +86,7 @@ static ssize_t power_supply_show_property(struct device *dev, "Non compliant", }; static char *typec_pr_text[] = { - "none", "dual power role", "sink", "source" + "none", "dual power role", "sink", "source", "sink_delay" }; ssize_t ret = 0; struct power_supply *psy = dev_get_drvdata(dev); @@ -306,6 +311,21 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(pd_voltage_min), POWER_SUPPLY_ATTR(sdp_current_max), POWER_SUPPLY_ATTR(fcc_stepper_enable), + POWER_SUPPLY_ATTR(skin_temp), + POWER_SUPPLY_ATTR(smart_charging_activation), + POWER_SUPPLY_ATTR(smart_charging_interruption), + POWER_SUPPLY_ATTR(smart_charging_status), + POWER_SUPPLY_ATTR(lrc_enable), + POWER_SUPPLY_ATTR(lrc_socmax), + POWER_SUPPLY_ATTR(lrc_socmin), + POWER_SUPPLY_ATTR(lrc_not_startup), + POWER_SUPPLY_ATTR(max_charge_current), + POWER_SUPPLY_ATTR(charge_full_raw), + POWER_SUPPLY_ATTR(time_to_cap_learning), + POWER_SUPPLY_ATTR(int_cld), + POWER_SUPPLY_ATTR(monotonic_soc), + POWER_SUPPLY_ATTR(legacy_cable_status), + POWER_SUPPLY_ATTR(running_status), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ @@ -313,6 +333,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(manufacturer), POWER_SUPPLY_ATTR(serial_number), POWER_SUPPLY_ATTR(battery_type), + POWER_SUPPLY_ATTR(charger_type), }; static struct attribute * diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index d052e9518060f96161943a15560b9e721b52154c..f16ef8557d2d5f6c6386d83e63377e937d39c1d2 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -157,6 +162,7 @@ static bool get_dload_mode(void) return dload_mode_enabled; } +#if 0 static void enable_emergency_dload_mode(void) { int ret; @@ -181,6 +187,7 @@ static void enable_emergency_dload_mode(void) if (ret) pr_err("Failed to set secure EDLOAD mode: %d\n", ret); } +#endif static int dload_set(const char *val, struct kernel_param *kp) { @@ -294,6 +301,9 @@ static void msm_restart_prepare(const char *cmd) (cmd != NULL && cmd[0] != '\0')); } + if (in_panic) + need_warm_reset = true; + /* Hard reset the PMIC unless memory contents must be maintained. */ if (need_warm_reset) { qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET); @@ -301,7 +311,15 @@ static void msm_restart_prepare(const char *cmd) qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); } - if (cmd != NULL) { + if (in_panic) { + u32 prev_reason; + + prev_reason = __raw_readl(restart_reason); + if (prev_reason != 0xABADF00D) { + __raw_writel(0xC0DEDEAD, restart_reason); + qpnp_pon_set_restart_reason(PON_RESTART_REASON_KERNEL_PANIC); + } + } else if (cmd != NULL) { if (!strncmp(cmd, "bootloader", 10)) { qpnp_pon_set_restart_reason( PON_RESTART_REASON_BOOTLOADER); @@ -346,15 +364,34 @@ static void msm_restart_prepare(const char *cmd) } else { qpnp_pon_set_restart_reason( reset_reason); + if ((code & 0xff) == 'N') { + qpnp_pon_set_restart_reason( + PON_RESTART_REASON_SYSTEM); + } else if ((code & 0xff) == 'S') { + qpnp_pon_set_restart_reason( + PON_RESTART_REASON_XFL); + } else if ((code & 0xff) == 'F') { + qpnp_pon_set_restart_reason( + PON_RESTART_REASON_OEM_F); + } else if ((code & 0xff) == 'P') { + qpnp_pon_set_restart_reason( + PON_RESTART_REASON_OEM_P); + } + __raw_writel(0x6f656d00 | (code & 0xff), + restart_reason); } - __raw_writel(0x6f656d00 | (code & 0xff), - restart_reason); } +#if 0 } else if (!strncmp(cmd, "edl", 3)) { enable_emergency_dload_mode(); +#endif } else { __raw_writel(0x77665501, restart_reason); + qpnp_pon_set_restart_reason(PON_RESTART_REASON_UNKNOWN); } + } else { + __raw_writel(0x77665501, restart_reason); + qpnp_pon_set_restart_reason(PON_RESTART_REASON_UNKNOWN); } flush_cache_all(); @@ -420,6 +457,7 @@ static void do_msm_poweroff(void) set_dload_mode(0); scm_disable_sdi(); qpnp_pon_system_pwr_off(PON_POWER_OFF_SHUTDOWN); + qpnp_pon_set_restart_reason(PON_RESTART_REASON_NONE); halt_spmi_pmic_arbiter(); deassert_ps_hold(); @@ -675,6 +713,7 @@ skip_sysfs_create: if (!download_mode) scm_disable_sdi(); #endif + return 0; err_restart_reason: diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig index b919c688e6278e486e73a56300a9d87dd65417ec..72b3f3450135bf699f670e3ec4282e1dc668d16a 100644 --- a/drivers/power/supply/qcom/Kconfig +++ b/drivers/power/supply/qcom/Kconfig @@ -100,4 +100,18 @@ config QPNP_QNOVO module. It also allows userspace code to read diagnostics of voltage and current measured during certain phases of the pulses. +config QNS_SYSTEM + bool "Qnovo QNS wrapper implementation" + default n + help + Say Y here to enable support for QNS system. + QNS is a battery charging controller and it necessary to access a + kernel driver for a battery charging. + This adds sysfs interface which enables QNS daemon to access it. + +config SOMC_CHARGER_EXTENSION + bool "Charger driver extension" + help + Say Y here to enable extensional function of charger driver. + endmenu diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index 92310ef5c8037caffd4b42708faddda2e829684f..01eb1f49d3911402e76a789208b32da2d32bf14b 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_BATTERY_BCL) += battery_current_limit.o obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o obj-$(CONFIG_SMB138X_CHARGER) += battery.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o obj-$(CONFIG_QPNP_QNOVO) += battery.o qpnp-qnovo.o +obj-$(CONFIG_QNS_SYSTEM) += qns_system.o diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 6d5308b3dd0bd948a6216687eddc03cead109f0c..9b259a51bb8a516b8a908356faca4e193c99ff6d 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "QCOM-BATT: %s: " fmt, __func__ @@ -1353,9 +1358,16 @@ int qcom_batt_init(void) goto release_wakeup_source; } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) chip->fv_votable = create_votable("FV", VOTE_MAX, pl_fv_vote_callback, chip); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chip->fv_votable = create_votable("FV", VOTE_MIN, + pl_fv_vote_callback, + chip); +#endif if (IS_ERR(chip->fv_votable)) { rc = PTR_ERR(chip->fv_votable); goto destroy_votable; diff --git a/drivers/power/supply/qcom/bcl_peripheral.c b/drivers/power/supply/qcom/bcl_peripheral.c index 2d237f27b062c470cb408dc38703e025faa2f6f3..d43f456aec5a45e43a7ebe262bc9d6420ee84318 100644 --- a/drivers/power/supply/qcom/bcl_peripheral.c +++ b/drivers/power/supply/qcom/bcl_peripheral.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__ @@ -76,7 +81,7 @@ #define BCL_8998_VBAT_SCALING 39000 #define BCL_8998_IBAT_SCALING 80000 #define BCL_VBAT_LOW_THRESHOLD 0x7 /* 3.1V */ -#define BCL_VBAT_TLOW_THRESHOLD 0x5 /* 2.9v */ +#define BCL_VBAT_TLOW_THRESHOLD 0x0 /* 2.4v */ #define BCL_IBAT_HIGH_THRESH_UA 4300000 #define BCL_LMH_CFG_VAL 0x3 #define BCL_CFG_VAL 0x81 diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index 076cd49e6dd53dccc21d49274ba9f28794d0a197..6cefd23afbf6c8d2ed4864ea62fc6aa5b1c4d2ef 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __FG_CORE_H__ #define __FG_CORE_H__ @@ -95,6 +100,9 @@ enum fg_debug_flag { FG_BUS_READ = BIT(6), /* Show REGMAP reads */ FG_CAP_LEARN = BIT(7), /* Show capacity learning */ FG_TTF = BIT(8), /* Show time to full */ +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + FG_SOMC = BIT(15), +#endif }; /* SRAM access */ @@ -179,6 +187,12 @@ enum fg_sram_param_id { FG_SRAM_ESR_TIGHT_FILTER, FG_SRAM_ESR_BROAD_FILTER, FG_SRAM_SLOPE_LIMIT, +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + FG_SRAM_SOC_SYSTEM, + FG_SRAM_SOC_MONOTONIC, + FG_SRAM_SOC_CUTOFF, + FG_SRAM_SOC_FULL, +#endif FG_SRAM_MAX, }; @@ -292,6 +306,11 @@ struct fg_dt_props { int ki_coeff_hi_dischg[KI_COEFF_SOC_LEVELS]; int slope_limit_coeffs[SLOPE_LIMIT_NUM_COEFFS]; u8 batt_therm_coeffs[BATT_THERM_NUM_COEFFS]; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int therm_coeff_c1; + int therm_coeff_c2; + int therm_coeff_c3; +#endif }; /* parameters from battery profile */ @@ -320,6 +339,17 @@ struct fg_cap_learning { int64_t final_cc_uah; int64_t learned_cc_uah; struct mutex lock; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int batt_soc_drop; + int cc_soc_drop; + int max_bsoc_during_active; + int max_ccsoc_during_active; + s64 max_bsoc_time_ms; + s64 start_time_ms; + s64 hold_time; + s64 total_time; + s64 learned_time_ms; +#endif }; struct fg_irq_info { @@ -384,6 +414,15 @@ static const struct fg_pt fg_tsmc_osc_table[] = { { 90, 444992 }, }; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define ORG_BATT_TYPE_SIZE 9 +#define BATT_TYPE_SIZE (ORG_BATT_TYPE_SIZE + 2) +#define BATT_TYPE_FIRST_HYPHEN 4 +#define BATT_TYPE_SECOND_HYPHEN 9 +#define BATT_TYPE_AGING_LEVEL 10 + +#endif + struct fg_chip { struct device *dev; struct pmic_revid_data *pmic_rev_id; @@ -463,6 +502,21 @@ struct fg_chip { struct work_struct esr_filter_work; struct alarm esr_filter_alarm; ktime_t last_delta_temp_time; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* Learning */ + int64_t charge_full_raw; + int rated_capacity; + int initial_capacity; + + /* Soft Charge */ + int batt_aging_level; + int saved_batt_aging_level; + char org_batt_type_str[ORG_BATT_TYPE_SIZE + 1]; + + /* Recharge */ + bool recharge_starting; + int recharge_voltage_mv; +#endif }; /* Debugfs data structures are below */ diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c index 3d0a718446086c343cd0f3e51fefa17db5591fa5..31e4df8e1c2b32afb107d1f614914081a54daf5e 100644 --- a/drivers/power/supply/qcom/pmic-voter.c +++ b/drivers/power/supply/qcom/pmic-voter.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -20,7 +25,9 @@ #include +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) #define NUM_MAX_CLIENTS 16 +#endif #define DEBUG_FORCE_CLIENT "DEBUG_FORCE_CLIENT" static DEFINE_SPINLOCK(votable_list_slock); @@ -693,3 +700,65 @@ void destroy_votable(struct votable *votable) kfree(votable->name); kfree(votable); } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +ssize_t somc_output_voter_param(struct votable *votable, + char *buf, size_t size) +{ + int i; + int stored_size = 0; + char *print_format; + + memset(buf, '\0', size); + size--; + if (votable->type == VOTE_SET_ANY) { + for (i = 0; i < votable->num_clients + && votable->client_strs[i]; i++) { + if (!votable->votes[i].enabled) + continue; + + if (stored_size == 0) + print_format = "%s"; + else + print_format = "; %s"; + stored_size += scnprintf(buf + stored_size, + size - stored_size, print_format, + votable->client_strs[i]); + if (stored_size >= size) + break; + } + } else { + for (i = 0; i < votable->num_clients + && votable->client_strs[i]; i++) { + if (!votable->votes[i].enabled) + continue; + + if (stored_size == 0) + print_format = "%s:%d"; + else + print_format = "; %s:%d"; + stored_size += scnprintf(buf + stored_size, + size - stored_size, print_format, + votable->client_strs[i], + get_client_vote(votable, + votable->client_strs[i])); + if (stored_size >= size) + break; + } + } + if (stored_size < size) + stored_size += scnprintf(buf + stored_size, + size - stored_size, "\n"); + return stored_size; +} + +int somc_get_vote_clients(struct votable *votable, char *clients[]) +{ + int i; + int num_clients = 0; + for (i = 0; i < votable->num_clients; i++) { + if (votable->client_strs[i]) + clients[num_clients++] = votable->client_strs[i]; + } + return num_clients; +} +#endif diff --git a/drivers/power/supply/qcom/qns_system.c b/drivers/power/supply/qcom/qns_system.c new file mode 100644 index 0000000000000000000000000000000000000000..722110ceaa3d6a2cb86cbcf6125d807f51d34d24 --- /dev/null +++ b/drivers/power/supply/qcom/qns_system.c @@ -0,0 +1,608 @@ +/* + * qns_system.c version 1.2 + * Qnovo QNS wrapper implementation + * Copyright (C) 2014 Qnovo Corp + * Miro Zmrzli + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QNS_USE_PM8941 +#define READ_CURRENT_SIGN (-1) +#define CHARGE_CURRENT_PROP POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT +#define CHARGE_VOLTAGE_PROP POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE + +#ifdef QNS_USE_PM8941 +#define IBATMANAME "battery" +#endif + +#define QNS_OK 0 +#define QNS_ERROR -1 + +//#define DEBUG + +static struct power_supply * ibat_psy = NULL; +static struct power_supply * battery_psy = NULL; +static struct power_supply * bms_psy = NULL; + +static struct alarm alarm; +static bool alarm_inited = false; +static int alarm_value = 0; + +static struct wake_lock wakelock; +static bool wakelock_inited = false; +static bool wakelock_held = false; + +static struct wake_lock charge_wakelock; +static bool charge_wakelock_inited = false; +static bool charge_wakelock_held = false; + +static int options = -1; + +static int qns_set_ibat(int ibatmA) +{ + union power_supply_propval propVal = {ibatmA*1000,}; + + static int prev_ibat_for_deblog = -1; + + if (ibatmA != prev_ibat_for_deblog) + pr_info("QNS: new charge current:%d mA\n", ibatmA); + else + pr_debug("QNS: new charge current:%d mA\n", ibatmA); + + if(ibat_psy == NULL) + { + ibat_psy = power_supply_get_by_name(IBATMANAME); + if(ibat_psy == NULL) + { + pr_info("QNS: ERROR: unable to get " IBATMANAME ". Can't set the current!"); + return QNS_ERROR; + } + } + if (ibatmA != prev_ibat_for_deblog) { + if(ibat_psy->desc->set_property(ibat_psy, + CHARGE_CURRENT_PROP, &propVal) != 0) + { + pr_info("QNS: ERROR: unable to set charging current! Does " IBATMANAME " have " + "POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT property?\n"); + return QNS_ERROR; + } + prev_ibat_for_deblog = ibatmA; + } + return QNS_OK; +} + +static int qns_set_vbat(int vbatmV) +{ +#if 0 + union power_supply_propval propVal = {vbatmV*1000,}; + + pr_info("QNS: new charge voltage:%d mV", vbatmV); + + if(ibat_psy == NULL) + { + ibat_psy = power_supply_get_by_name(IBATMANAME); + if(ibat_psy == NULL) + { + pr_info("QNS: ERROR: unable to get " IBATMANAME ". Can't set the voltage!"); + return QNS_ERROR; + } + } + if(ibat_psy->desc->set_property(ibat_psy, + CHARGE_VOLTAGE_PROP, &propVal) != 0) + { + pr_info("QNS: ERROR: unable to set charging voltage! Does " IBATMANAME " have " + "POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE property?"); + return QNS_ERROR; + } +#endif + return QNS_OK; +} + +static bool qns_is_charging(void) +{ + union power_supply_propval propVal = {0, }; + + if (battery_psy == NULL) + { + battery_psy = power_supply_get_by_name("battery"); + if(battery_psy == NULL) + { + pr_info("QNS: ERROR: unable to get \"battery\". Can't read charging state!"); + return false; + } + } + + if(battery_psy->desc->get_property(battery_psy, POWER_SUPPLY_PROP_STATUS, + &propVal) != 0) + { + pr_info("QNS: ERROR: unable to read charger properties! Does \"battery\" have " + "POWER_SUPPLY_PROP_STATUS property?"); + return false; + } + + return propVal.intval == POWER_SUPPLY_STATUS_CHARGING; +} + +static int qns_get_scvt(int *soc, int *c, int *v, int *tx10) +{ + /* + soc in % + c in ma + v in mv + t in 0.1 deg c + */ + union power_supply_propval ret = {0,}; + int retVal = QNS_OK; + + if (battery_psy == NULL) + { + battery_psy = power_supply_get_by_name("battery"); + if(battery_psy == NULL) + { + pr_info("QNS: ERROR: unable to get \"battery\". Can't read soc/c/v/t!"); + retVal = QNS_ERROR; + } + } + + if (battery_psy) + { + if(c != NULL) + { + if(battery_psy->desc->get_property(battery_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &ret) != 0) + { + pr_info("QNS: ERROR: unable to read battery property POWER_SUPPLY_PROP_CURRENT_NOW"); + *c = 0; + retVal = QNS_ERROR; + } + else + *c = READ_CURRENT_SIGN * ret.intval/1000; + } + + if(v != NULL) + { + if(battery_psy->desc->get_property(battery_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &ret) != 0) + { + pr_info("QNS: ERROR: unable to read battery property POWER_SUPPLY_PROP_VOLTAGE_NOW"); + *v = 0; + retVal = QNS_ERROR; + } + else + *v = ret.intval/1000; + } + + if(tx10 != NULL) + { + if(battery_psy->desc->get_property(battery_psy, + POWER_SUPPLY_PROP_TEMP, &ret) != 0) + { + pr_info("QNS: ERROR: unable to read battery property POWER_SUPPLY_PROP_TEMP"); + *tx10 = 0; + retVal = QNS_ERROR; + } + else + *tx10 = ret.intval; + } + + if(soc != NULL) + { + if(battery_psy->desc->get_property(battery_psy, + POWER_SUPPLY_PROP_CAPACITY, &ret) != 0) + { + pr_info("QNS: ERROR: unable to read battery property POWER_SUPPLY_PROP_CAPACITY"); + *soc = 0; + retVal = QNS_ERROR; + } + else + *soc = ret.intval; + } + } + else + { + pr_info("QNS: battery power supply is not registered yet."); + if(c != NULL) *c = 0; + if(v != NULL) *v = 4000; + if(tx10 != NULL) *tx10 = 250; + if(soc != NULL) *soc = 50; + retVal = QNS_ERROR; + } + return retVal; +} + +static int qns_get_fcc(int *fcc, int *design) +{ + union power_supply_propval ret = {0,}; + int retVal = QNS_OK; + + if (battery_psy == NULL) + { + battery_psy = power_supply_get_by_name("battery"); + if(battery_psy == NULL) + { + pr_info("QNS: ERROR: unable to get \"battery\". Can't read fcc/design!"); + retVal = QNS_ERROR; + } + } + + if (battery_psy) + { + if(fcc != NULL) + { + if(battery_psy->desc->get_property(battery_psy, + POWER_SUPPLY_PROP_CHARGE_FULL, &ret) != 0) + { + pr_info("QNS: ERRROR: unable to read battery POWER_SUPPLY_PROP_CHARGE_FULL property."); + *fcc = 0; + retVal = QNS_ERROR; + } + else + *fcc = ret.intval/1000; + } + if(design != NULL) + { + if(battery_psy->desc->get_property(battery_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &ret) != 0) + { + pr_info("QNS: ERROR: unable to read battery POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN property."); + *design = 0; + retVal = QNS_ERROR; + } + else + *design = ret.intval/1000; + } + } + return retVal; +} + +static int qns_get_battery_type(const char **battery_type) +{ + union power_supply_propval ret = {0,}; + int retVal = QNS_OK; + + if (bms_psy == NULL) + { + bms_psy = power_supply_get_by_name("bms"); + if(bms_psy == NULL) + { + pr_info("QNS: ERROR: unable to get \"bms\". Can't read battery_type!"); + *battery_type = "Unknown"; + retVal = QNS_ERROR; + } + } + + if (bms_psy) + { + if(battery_type != NULL) + { + if(bms_psy->desc->get_property(bms_psy, + POWER_SUPPLY_PROP_BATTERY_TYPE, &ret) != 0) + { + pr_info("QNS: ERRROR: unable to read battery POWER_SUPPLY_PROP_BATTERY_TYPE property."); + *battery_type = "Unknown"; + retVal = QNS_ERROR; + } + else + *battery_type = ret.strval; + } + } + return retVal; +} + +static ssize_t qns_param_show(struct class *dev, + struct class_attribute *attr, + char *buf); + +static ssize_t qns_param_store(struct class *dev, + struct class_attribute *attr, + const char *buf, + size_t count); + +enum +{ + IS_CHARGING, + CURRENT, + VOLTAGE, + TEMPERATURE, + FCC, + DESIGN, + SOC, + BATTERY_TYPE, + CHARGE_CURRENT, + CHARGE_VOLTAGE, + ALARM, + OPTIONS, +}; + +static struct class_attribute qns_attrs[] = { + __ATTR(charging_state, S_IRUGO, qns_param_show, NULL), + __ATTR(current_now, S_IRUGO, qns_param_show, NULL), + __ATTR(voltage, S_IRUGO, qns_param_show, NULL), + __ATTR(temp, S_IRUGO, qns_param_show, NULL), + __ATTR(fcc, S_IRUGO, qns_param_show, NULL), + __ATTR(design, S_IRUGO, qns_param_show, NULL), + __ATTR(soc, S_IRUGO, qns_param_show, NULL), +#ifdef DEBUG + __ATTR(battery_type, S_IWUSR | S_IRUGO, qns_param_show, qns_param_store), +#else + __ATTR(battery_type, S_IRUGO, qns_param_show, NULL), +#endif + __ATTR(charge_current, S_IWUSR, NULL, qns_param_store), + __ATTR(charge_voltage, S_IWUSR, NULL, qns_param_store), + __ATTR(alarm, S_IWUSR | S_IRUGO, qns_param_show, qns_param_store), + __ATTR(options, S_IWUSR | S_IRUGO, qns_param_show, qns_param_store), + __ATTR_NULL, +}; + +static enum alarmtimer_restart qns_alarm_handler(struct alarm * alarm, ktime_t now) +{ + pr_info("QNS: ALARM! System wakeup!"); + wake_lock(&wakelock); + wakelock_held = true; + alarm_value = 1; + return ALARMTIMER_NORESTART; +} + +#ifdef DEBUG +char battid[32] = {'1','2','9','8','-','9','2','3','9'}; +#endif + +static ssize_t qns_param_show(struct class *dev, + struct class_attribute *attr, + char *buf) +{ + ssize_t size = 0; + const ptrdiff_t off = attr - qns_attrs; + static int t, c, v; + const char *battery_type; + + switch(off) + { + case IS_CHARGING: + size = scnprintf(buf, PAGE_SIZE, "%d\n", qns_is_charging() ? 1 : 0); + break; + case CURRENT: + qns_get_scvt(NULL, &c, &v, NULL); + size = scnprintf(buf, PAGE_SIZE, "%d\n", c); + break; + case VOLTAGE: + size = scnprintf(buf, PAGE_SIZE, "%d\n", v); + break; + case TEMPERATURE: + qns_get_scvt(NULL, NULL, NULL, &t); + size = scnprintf(buf, PAGE_SIZE, "%d\n", t); + break; + case FCC: + qns_get_fcc(&t, NULL); + size = scnprintf(buf, PAGE_SIZE, "%d\n", t); + break; + case DESIGN: + qns_get_fcc(NULL, &t); + size = scnprintf(buf, PAGE_SIZE, "%d\n", t); + break; + case SOC: + qns_get_scvt(&t, NULL, NULL, NULL); + size = scnprintf(buf, PAGE_SIZE, "%d\n", t); + break; + case ALARM: + size = scnprintf(buf, PAGE_SIZE, "%d\n", alarm_value); + break; + case OPTIONS: + size = scnprintf(buf, PAGE_SIZE, "%d\n", options); + break; + case BATTERY_TYPE: +#ifdef DEBUG + size = scnprintf(buf, PAGE_SIZE, "%s\n", battid); +#else + qns_get_battery_type(&battery_type); + size = scnprintf(buf, PAGE_SIZE, "%s\n", battery_type); +#endif + break; + } + + return size; +} + +enum alarm_values +{ + CHARGE_WAKELOCK = -4, + CHARGE_WAKELOCK_RELEASE = -3, + HANDLED = -2, + CANCEL = -1, + IMMEDIATE = 0, +}; + +static ssize_t qns_param_store(struct class *dev, + struct class_attribute *attr, + const char *buf, + size_t count) +{ + int val, ret = -EINVAL; + ktime_t next_alarm; + const ptrdiff_t off = attr - qns_attrs; + + switch(off) + { + case CHARGE_CURRENT: + ret = kstrtoint(buf, 10, &val); + if (!ret && (val > 0)) + { + qns_set_ibat(val); + return count; + } + else + return -EINVAL; + break; + + case CHARGE_VOLTAGE: + ret = kstrtoint(buf, 10, &val); + if (!ret && (val > 0)) + { + qns_set_vbat(val); + return count; + } + else + return -EINVAL; + break; + + case ALARM: + ret = kstrtoint(buf, 10, &val); + + if(!wakelock_inited) + { + wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "QnovoQNS"); + wakelock_inited = true; + } + + if(!charge_wakelock_inited) + { + wake_lock_init(&charge_wakelock, WAKE_LOCK_SUSPEND, "QnovoQNS"); + charge_wakelock_inited = true; + } + + if (!ret) + { + if(val == CHARGE_WAKELOCK) + { + if(!charge_wakelock_held) + { + pr_info("QNS: Alarm: acquiring charge_wakelock via CHARGE_WAKELOCK"); + + wake_lock(&charge_wakelock); + charge_wakelock_held = true; + } + } + else if(val == CHARGE_WAKELOCK_RELEASE) + { + if(charge_wakelock_held) + { + pr_info("QNS: Alarm: releasing charge_wakelock via CHARGE_WAKELOCK_RELEASE"); + + wake_unlock(&charge_wakelock); + charge_wakelock_held = false; + } + } + else if(val == HANDLED) + { + if(wakelock_held) + { + pr_info("QNS: Alarm: releasing wakelock via HANDLED"); + wake_unlock(&wakelock); + } + alarm_value = 0; + wakelock_held = false; + } + else if(val == CANCEL) + { + if(alarm_inited) + { + alarm_cancel(&alarm); + } + alarm_value = 0; + if(wakelock_held) + { + pr_info("QNS: Alarm: releasing wakelock via CANCEL"); + wake_unlock(&wakelock); + } + wakelock_held = false; + } + else if(val == IMMEDIATE) + { + if(!wakelock_held) + { + pr_info("QNS: Alarm: acquiring wakelock via IMMEDIATE"); + + wake_lock(&wakelock); + wakelock_held = true; + } + } + else if(val > 0) + { + if(!alarm_inited) + { + alarm_init(&alarm, ALARM_REALTIME, qns_alarm_handler); + alarm_inited = true; + } + + next_alarm = ktime_set(val, 0); + alarm_start_relative(&alarm, next_alarm); + + if(wakelock_held) + { + pr_info("QNS: Alarm: releasing wakelock via alarm>0"); + + wake_unlock(&wakelock); + } + alarm_value = 0; + wakelock_held = false; + } + } + break; + + case OPTIONS: + ret = kstrtoint(buf, 10, &val); + if (!ret && (val >= 0)) + options = val; + else + return -EINVAL; + break; +#ifdef DEBUG + case BATTERY_TYPE: + strcpy(battid , buf); + pr_info("QNS: received '%s'", battid); + val = strlen(battid); + if (val > 0 && val < 32) + battid[val-1] = 0; + pr_info("QNS: new id '%s'", battid); + break; +#endif + } + return count; +} + +static struct class qns_class = +{ + .name = "qns", + .owner = THIS_MODULE, + .class_attrs = qns_attrs +}; + +MODULE_AUTHOR("Miro Zmrzli "); +MODULE_DESCRIPTION("QNS System Driver v2"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("QNS"); + +static int qnovo_qns_init(void) +{ + class_register(&qns_class); + return 0; +} +static void qnovo_qns_exit(void) +{ + class_unregister(&qns_class); +} + +module_init(qnovo_qns_init); +module_exit(qnovo_qns_exit); diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index a546621d083797137947e494d4aabe5335cdad9d..969fd2c8cc691207e868ac4fca6f52379a6becd5 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -9,10 +9,18 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "FG: %s: " fmt, __func__ #include +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#include +#endif #include #include #include @@ -29,7 +37,14 @@ #define FG_BATT_SOC_PMI8998 0x10 #define FG_BATT_INFO_PMI8998 0x11 #define FG_MEM_INFO_PMI8998 0x0D - +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define FG_ESR_CURRENT_THR_VALUE 0x1C +#define KI_COEFF_CUTOFF_VOLT_VALUE 0x18 +#define ESR_VCTIBTRSLWEN_MASK 0xC0 +#define ESR_VCTIBTRSLWEN_VALUE 0xC0 +#define SAT_CC_CLR_AUTO_MASK 0x08 +#define SAT_CC_CLR_AUTO_VALUE 0x08 +#endif /* SRAM address and offset in ascending order */ #define ESR_PULSE_THRESH_WORD 2 #define ESR_PULSE_THRESH_OFFSET 3 @@ -37,6 +52,10 @@ #define SLOPE_LIMIT_OFFSET 0 #define CUTOFF_CURR_WORD 4 #define CUTOFF_CURR_OFFSET 0 +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define ESR_CURRENT_THR_WORD 2 +#define ESR_CURRENT_THR_OFFSET 3 +#endif #define CUTOFF_VOLT_WORD 5 #define CUTOFF_VOLT_OFFSET 0 #define SYS_TERM_CURR_WORD 6 @@ -57,6 +76,10 @@ #define KI_COEFF_LOW_DISCHG_OFFSET 2 #define KI_COEFF_FULL_SOC_WORD 12 #define KI_COEFF_FULL_SOC_OFFSET 2 +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define KI_COEFF_CUTOFF_VOLT_WORD 12 +#define KI_COEFF_CUTOFF_VOLT_OFFSET 1 +#endif #define DELTA_MSOC_THR_WORD 12 #define DELTA_MSOC_THR_OFFSET 3 #define DELTA_BSOC_THR_WORD 13 @@ -79,6 +102,12 @@ #define ESR_TIMER_CHG_INIT_OFFSET 2 #define ESR_EXTRACTION_ENABLE_WORD 19 #define ESR_EXTRACTION_ENABLE_OFFSET 0 +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define SAT_CC_CLR_AUTO_WORD 19 +#define SAT_CC_CLR_AUTO_OFFSET 0 +#define ESR_VCTIBTRSLWEN_WORD 19 +#define ESR_VCTIBTRSLWEN_OFFSET 1 +#endif #define PROFILE_LOAD_WORD 24 #define PROFILE_LOAD_OFFSET 0 #define ESR_RSLOW_DISCHG_WORD 34 @@ -89,6 +118,10 @@ #define NOM_CAP_OFFSET 0 #define ACT_BATT_CAP_BKUP_WORD 74 #define ACT_BATT_CAP_BKUP_OFFSET 0 +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define BATT_AGING_LEVEL_WORD 74 +#define BATT_AGING_LEVEL_OFFSET 3 +#endif #define CYCLE_COUNT_WORD 75 #define CYCLE_COUNT_OFFSET 0 #define PROFILE_INTEGRITY_WORD 79 @@ -96,8 +129,16 @@ #define PROFILE_INTEGRITY_OFFSET 3 #define BATT_SOC_WORD 91 #define BATT_SOC_OFFSET 0 +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define SOC_CUTOFF_WORD 93 +#define SOC_CUTOFF_OFFSET 0 +#endif #define FULL_SOC_WORD 93 #define FULL_SOC_OFFSET 2 +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define SOC_SYSTEM_WORD 94 +#define SOC_SYSTEM_OFFSET 0 +#endif #define MONOTONIC_SOC_WORD 94 #define MONOTONIC_SOC_OFFSET 2 #define CC_SOC_WORD 95 @@ -212,8 +253,20 @@ static struct fg_sram_param pmi8998_v1_sram_params[] = { 100000, 390625, 0, fg_encode_current, NULL), PARAM(CUTOFF_CURR, CUTOFF_CURR_WORD, CUTOFF_CURR_OFFSET, 3, 1000000, 122070, 0, fg_encode_current, NULL), +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* + * Changed the value of numrtr from 2048 to 1024. By this change, + * the value of DELTA_MSOC threshold turns into 0x0A(0.48828%) + * from 0x14(0.97656%). In addition, the value of DELTA_MSOC + * threshold is calculated in following macro. + * DIV_ROUND_CLOSEST(qcom,fg-delta-soc-thr * numrtr), denmtr) + */ + PARAM(DELTA_MSOC_THR, DELTA_MSOC_THR_WORD, DELTA_MSOC_THR_OFFSET, 1, + 1024, 100, 0, fg_encode_default, NULL), +#else PARAM(DELTA_MSOC_THR, DELTA_MSOC_THR_WORD, DELTA_MSOC_THR_OFFSET, 1, 2048, 100, 0, fg_encode_default, NULL), +#endif PARAM(DELTA_BSOC_THR, DELTA_BSOC_THR_WORD, DELTA_BSOC_THR_OFFSET, 1, 2048, 100, 0, fg_encode_default, NULL), PARAM(RECHARGE_SOC_THR, RECHARGE_SOC_THR_WORD, RECHARGE_SOC_THR_OFFSET, @@ -245,6 +298,16 @@ static struct fg_sram_param pmi8998_v1_sram_params[] = { 1, 512, 1000000, 0, fg_encode_default, NULL), PARAM(SLOPE_LIMIT, SLOPE_LIMIT_WORD, SLOPE_LIMIT_OFFSET, 1, 8192, 1000, 0, fg_encode_default, NULL), +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + PARAM(SOC_SYSTEM, SOC_SYSTEM_WORD, SOC_SYSTEM_OFFSET, 2, 1, + 1, 0, NULL, fg_decode_default), + PARAM(SOC_MONOTONIC, MONOTONIC_SOC_WORD, MONOTONIC_SOC_OFFSET, 2, 1, + 1, 0, NULL, fg_decode_default), + PARAM(SOC_CUTOFF, SOC_CUTOFF_WORD, SOC_CUTOFF_OFFSET, 2, 1, + 1, 0, NULL, fg_decode_default), + PARAM(SOC_FULL, FULL_SOC_WORD, FULL_SOC_OFFSET, 2, 1, + 1, 0, NULL, fg_decode_default), +#endif }; static struct fg_sram_param pmi8998_v2_sram_params[] = { @@ -290,8 +353,20 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = { fg_encode_current, NULL), PARAM(CUTOFF_CURR, CUTOFF_CURR_WORD, CUTOFF_CURR_OFFSET, 3, 1000000, 122070, 0, fg_encode_current, NULL), +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* + * Changed the value of numrtr from 2048 to 1024. By this change, + * the value of DELTA_MSOC threshold turns into 0x0A(0.48828%) + * from 0x14(0.97656%). In addition, the value of DELTA_MSOC + * threshold is calculated in following macro. + * DIV_ROUND_CLOSEST(qcom,fg-delta-soc-thr * numrtr), denmtr) + */ + PARAM(DELTA_MSOC_THR, DELTA_MSOC_THR_v2_WORD, DELTA_MSOC_THR_v2_OFFSET, + 1, 1024, 100, 0, fg_encode_default, NULL), +#else PARAM(DELTA_MSOC_THR, DELTA_MSOC_THR_v2_WORD, DELTA_MSOC_THR_v2_OFFSET, 1, 2048, 100, 0, fg_encode_default, NULL), +#endif PARAM(DELTA_BSOC_THR, DELTA_BSOC_THR_v2_WORD, DELTA_BSOC_THR_v2_OFFSET, 1, 2048, 100, 0, fg_encode_default, NULL), PARAM(RECHARGE_SOC_THR, RECHARGE_SOC_THR_v2_WORD, @@ -327,6 +402,16 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = { 1, 512, 1000000, 0, fg_encode_default, NULL), PARAM(SLOPE_LIMIT, SLOPE_LIMIT_WORD, SLOPE_LIMIT_OFFSET, 1, 8192, 1000, 0, fg_encode_default, NULL), +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + PARAM(SOC_SYSTEM, SOC_SYSTEM_WORD, SOC_SYSTEM_OFFSET, 2, 1, + 1, 0, NULL, fg_decode_default), + PARAM(SOC_MONOTONIC, MONOTONIC_SOC_WORD, MONOTONIC_SOC_OFFSET, 2, 1, + 1, 0, NULL, fg_decode_default), + PARAM(SOC_CUTOFF, SOC_CUTOFF_WORD, SOC_CUTOFF_OFFSET, 2, 1, + 1, 0, NULL, fg_decode_default), + PARAM(SOC_FULL, FULL_SOC_WORD, FULL_SOC_OFFSET, 2, 1, + 1, 0, NULL, fg_decode_default), +#endif }; static struct fg_alg_flag pmi8998_v1_alg_flags[] = { @@ -390,7 +475,13 @@ static struct fg_alg_flag pmi8998_v2_alg_flags[] = { }, }; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) static int fg_gen3_debug_mask; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +static int fg_gen3_debug_mask = + FG_SOMC; +#endif module_param_named( debug_mask, fg_gen3_debug_mask, int, S_IRUSR | S_IWUSR ); @@ -594,6 +685,11 @@ static int fg_get_charge_counter(struct fg_chip *chip, int *val) } *val = div_s64(cc_soc * chip->cl.learned_cc_uah, CC_SOC_30BIT); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (*val < 1) + *val = 1; + +#endif return 0; } @@ -634,6 +730,9 @@ static int fg_get_jeita_threshold(struct fg_chip *chip, #define BATT_TEMP_NUMR 1 #define BATT_TEMP_DENR 1 +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define CONV_BATT_TEMP_DEGC_FROM_LSB(t) (t * 25 / 10 - 2730) +#endif static int fg_get_battery_temp(struct fg_chip *chip, int *val) { int rc = 0, temp; @@ -648,11 +747,17 @@ static int fg_get_battery_temp(struct fg_chip *chip, int *val) temp = ((buf[1] & BATT_TEMP_MSB_MASK) << 8) | (buf[0] & BATT_TEMP_LSB_MASK); +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) temp = DIV_ROUND_CLOSEST(temp, 4); /* Value is in Kelvin; Convert it to deciDegC */ temp = (temp - 273) * 10; *val = temp; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* Value is in Kelvin; Convert it to deciDegC with keeping accuracy */ + *val = CONV_BATT_TEMP_DEGC_FROM_LSB(temp); +#endif return 0; } @@ -728,6 +833,20 @@ static int fg_get_battery_voltage(struct fg_chip *chip, int *val) return 0; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +static int fg_get_vbatt_predict(struct fg_chip *chip, int *val) +{ + int rc; + + rc = fg_get_sram_prop(chip, FG_SRAM_VOLTAGE_PRED, val); + if (rc < 0) { + pr_err("Error in getting VOLTAGE_PRED, rc=%d\n", rc); + return rc; + } + return 0; +} + +#endif #define MAX_TRIES_SOC 5 static int fg_get_msoc_raw(struct fg_chip *chip, int *val) { @@ -872,6 +991,9 @@ static bool is_debug_batt_id(struct fg_chip *chip) #define DEBUG_BATT_SOC 67 #define BATT_MISS_SOC 50 +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define UNKNOWN_BATT_SOC 20 +#endif #define EMPTY_SOC 0 static int fg_get_prop_capacity(struct fg_chip *chip, int *val) { @@ -883,7 +1005,15 @@ static int fg_get_prop_capacity(struct fg_chip *chip, int *val) } if (chip->fg_restarting) { +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) *val = chip->last_soc; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chip->last_soc) + *val = chip->last_soc; + else + *val = UNKNOWN_BATT_SOC; +#endif return 0; } @@ -897,6 +1027,21 @@ static int fg_get_prop_capacity(struct fg_chip *chip, int *val) return 0; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (!chip->profile_available) { + *val = UNKNOWN_BATT_SOC; + return 0; + } + + if (!chip->profile_loaded) { + if (chip->last_soc) + *val = chip->last_soc; + else + *val = UNKNOWN_BATT_SOC; + return 0; + } + +#endif if (chip->charge_full) { *val = FULL_CAPACITY; return 0; @@ -922,10 +1067,24 @@ static const char *fg_get_battery_type(struct fg_chip *chip) return MISSING_BATT_TYPE; if (chip->bp.batt_type_str) { +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (chip->profile_loaded) return chip->bp.batt_type_str; else if (chip->profile_available) return LOADING_BATT_TYPE; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chip->profile_loaded) { + if (strlen(chip->org_batt_type_str) == + ORG_BATT_TYPE_SIZE) + return chip->org_batt_type_str; + else + return chip->bp.batt_type_str; + + } else { + return LOADING_BATT_TYPE; + } +#endif } return DEFAULT_BATT_TYPE; @@ -978,6 +1137,151 @@ out: return rc; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +static int fg_somc_batterydata_read_batt_ids(const struct device_node *np, + struct batt_ids *batt_ids) +{ + struct property *prop; + const __be32 *data; + int num, i; + int *id_kohm = batt_ids->kohm; + + prop = of_find_property(np, "qcom,batt-id-kohm", NULL); + if (!prop) { + pr_err("%s: No battery id resistor found\n", np->name); + return -EINVAL; + } else if (!prop->value) { + pr_err("%s: No battery id resistor value found, np->name\n", + np->name); + return -ENODATA; + } else if (prop->length > MAX_BATT_ID_NUM * sizeof(__be32)) { + pr_err("%s: Too many battery id resistors\n", np->name); + return -EINVAL; + } + + num = prop->length / sizeof(__be32); + batt_ids->num = num; + data = prop->value; + for (i = 0; i < num; i++) + *id_kohm++ = be32_to_cpup(data++); + + return 0; +} + +int fg_somc_check_battery_type(char *battery_type) +{ + int i; + int rc; + int aging_level; + + if (battery_type == NULL || strlen(battery_type) != BATT_TYPE_SIZE) + return -EINVAL; + + for (i = 0; i < BATT_TYPE_SIZE; i++) { + if (i == BATT_TYPE_FIRST_HYPHEN || + i == BATT_TYPE_SECOND_HYPHEN) { + if (battery_type[i] != '-') + break; + } else { + if (!isdigit(battery_type[i])) + break; + } + } + + if (i < BATT_TYPE_SIZE) + return -EINVAL; + + rc = kstrtoint(&battery_type[BATT_TYPE_AGING_LEVEL], 10, &aging_level); + if (rc < 0) + return -EINVAL; + + return aging_level; +} + +struct device_node *fg_somc_battery_data_get_best_profile( + const struct device_node *batterydata_container_node, + int batt_id_kohm, int batt_aging_level) +{ + struct batt_ids batt_ids; + struct device_node *node; + struct device_node *best_node = NULL; + char *battery_type = NULL; + char *best_node_battery_type = NULL; + int delta = 0; + int best_delta = 0; + int best_id_kohm = 0; + int i = 0; + int rc = 0; + int limit = 0; + int id_range_pct; + int matching_batt_aging_level; + bool in_range = false; + + /*read battery id range percentage for best profile*/ + rc = of_property_read_u32(batterydata_container_node, + "qcom,batt-id-range-pct", &id_range_pct); + if (rc) { + if (rc == -EINVAL) { + id_range_pct = 0; + } else { + pr_err("failed to read battery id range\n"); + return NULL; + } + } + /* + * Find the battery data with a battery id resistor closest to this one + */ + for_each_child_of_node(batterydata_container_node, node) { + rc = of_property_read_string(node, "qcom,battery-type", + (const char **)&battery_type); + if (!rc) { + matching_batt_aging_level = + fg_somc_check_battery_type(battery_type); + } else { + matching_batt_aging_level = -1; + battery_type = NULL; + } + + if (matching_batt_aging_level >= 0 && + matching_batt_aging_level != batt_aging_level) + continue; + + rc = fg_somc_batterydata_read_batt_ids(node, &batt_ids); + if (rc) + continue; + for (i = 0; i < batt_ids.num; i++) { + delta = abs(batt_ids.kohm[i] - batt_id_kohm); + limit = (batt_ids.kohm[i] * id_range_pct) / 100; + in_range = (delta <= limit); + + /* + * Check if the delta is the lowest one + * and also if the limits are in range + * before selecting the best node. + */ + if (in_range && (delta < best_delta || !best_node)) { + best_node = node; + best_node_battery_type = battery_type; + best_delta = delta; + best_id_kohm = batt_ids.kohm[i]; + } + } + } + + if (best_node == NULL) { + pr_err("No battery data found\n"); + return best_node; + } + + if (best_node_battery_type) + pr_info("%s found\n", best_node_battery_type); + else + pr_info("%s found\n", best_node->name); + + return best_node; +} + +#endif static int fg_get_batt_profile(struct fg_chip *chip) { struct device_node *node = chip->dev->of_node; @@ -991,8 +1295,14 @@ static int fg_get_batt_profile(struct fg_chip *chip) return -ENXIO; } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) profile_node = of_batterydata_get_best_profile(batt_node, chip->batt_id_ohms / 1000, NULL); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + profile_node = fg_somc_battery_data_get_best_profile(batt_node, + chip->batt_id_ohms / 1000, chip->batt_aging_level); +#endif if (IS_ERR(profile_node)) return PTR_ERR(profile_node); @@ -1008,6 +1318,13 @@ static int fg_get_batt_profile(struct fg_chip *chip) return rc; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + memset(chip->org_batt_type_str, '\0', ORG_BATT_TYPE_SIZE + 1); + if (fg_somc_check_battery_type((char *)chip->bp.batt_type_str) >= 0) + strlcpy(chip->org_batt_type_str, + chip->bp.batt_type_str, ORG_BATT_TYPE_SIZE + 1); + +#endif rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv", &chip->bp.float_volt_uv); if (rc < 0) { @@ -1406,10 +1723,22 @@ static void fg_cap_learning_post_process(struct fg_chip *chip) do_div(chip->cl.final_cc_uah, 1000); } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) max_inc_val = chip->cl.learned_cc_uah * (1000 + chip->dt.cl_max_cap_inc); do_div(max_inc_val, 1000); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (!chip->dt.cl_max_cap_inc) { + max_inc_val = chip->cl.nom_cap_uah; + } else { + max_inc_val = chip->cl.learned_cc_uah + * (1000 + chip->dt.cl_max_cap_inc); + do_div(max_inc_val, 1000); + } + +#endif min_dec_val = chip->cl.learned_cc_uah * (1000 - chip->dt.cl_max_cap_dec); do_div(min_dec_val, 1000); @@ -1449,14 +1778,30 @@ static void fg_cap_learning_post_process(struct fg_chip *chip) if (rc < 0) pr_err("Error in saving learned_cc_uah, rc=%d\n", rc); +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) fg_dbg(chip, FG_CAP_LEARN, "final cc_uah = %lld, learned capacity %lld -> %lld uah\n", chip->cl.final_cc_uah, old_cap, chip->cl.learned_cc_uah); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chip->charge_full_raw = chip->cl.final_cc_uah; + chip->cl.learned_time_ms = ktime_to_ms(ktime_get_boottime()); + fg_dbg(chip, FG_SOMC, + "final cc_uah = %lld, learned capacity %lld -> %lld, time=%lld\n", + chip->cl.final_cc_uah, old_cap, + chip->cl.learned_cc_uah, chip->cl.learned_time_ms); +#endif } static int fg_cap_learning_process_full_data(struct fg_chip *chip) { +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) int rc, cc_soc_sw, cc_soc_delta_pct; int64_t delta_cc_uah; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int rc, cc_soc_sw; + int64_t cc_soc_delta_100pct, delta_cc_uah; +#endif rc = fg_get_sram_prop(chip, FG_SRAM_CC_SOC_SW, &cc_soc_sw); if (rc < 0) { @@ -1464,6 +1809,7 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip) return rc; } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) cc_soc_delta_pct = div64_s64((int64_t)(cc_soc_sw - chip->cl.init_cc_soc_sw) * 100, CC_SOC_30BIT); @@ -1476,9 +1822,24 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip) delta_cc_uah = div64_s64(chip->cl.learned_cc_uah * cc_soc_delta_pct, 100); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + cc_soc_delta_100pct = DIV_ROUND_CLOSEST( + (s64)(abs(cc_soc_sw - chip->cl.init_cc_soc_sw)) + * 10000, CC_SOC_30BIT); + delta_cc_uah = div64_s64(chip->cl.learned_cc_uah * cc_soc_delta_100pct, + 10000); +#endif chip->cl.final_cc_uah = chip->cl.init_cc_uah + delta_cc_uah; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) fg_dbg(chip, FG_CAP_LEARN, "Current cc_soc=%d cc_soc_delta_pct=%d total_cc_uah=%lld\n", cc_soc_sw, cc_soc_delta_pct, chip->cl.final_cc_uah); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + fg_dbg(chip, FG_SOMC, + "cc_soc_sw=%d cc_soc_delta_100pct=%lld total_cc_uah=%lld\n", + cc_soc_sw, cc_soc_delta_100pct, chip->cl.final_cc_uah); +#endif return 0; } @@ -1507,8 +1868,19 @@ static int fg_cap_learning_begin(struct fg_chip *chip, u32 batt_soc) } chip->cl.init_cc_soc_sw = cc_soc_sw; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) fg_dbg(chip, FG_CAP_LEARN, "Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n", batt_soc_msb, chip->cl.init_cc_soc_sw); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chip->cl.max_ccsoc_during_active = cc_soc_sw; + chip->cl.max_bsoc_during_active = batt_soc; + chip->cl.max_bsoc_time_ms = ktime_to_ms(ktime_get_boottime()); + chip->cl.start_time_ms = chip->cl.max_bsoc_time_ms; + fg_dbg(chip, FG_SOMC, + "Capacity learning started. bsoc:%d cc_soc_sw:%d time:%lld\n", + batt_soc, cc_soc_sw, chip->cl.start_time_ms); +#endif out: return rc; } @@ -1537,11 +1909,33 @@ out: return rc; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define SOC_MAX_RANGE 999985458LL /* 0.999985458 * (10^9) */ +#define SOC_LSB 15300LL /* 1.53E-05 * (10^9) */ +#define BATT_SOC_MAX_RANGE 1000001530000LL /* 1.00000153 * (10^12) */ +#define BATT_SOC_LSB 233LL /* 2.33E-10 * (10^12) */ +#define CC_SOC_MAX_RANGE 1000000455000LL /* (2.00000091/2) * (10^12) */ +#define CC_SOC_LSB 931LL /* 9.31E-10 * (10^12) */ + +#define CL_ABORT_BSOC_10PER 20 /* 2.0 persetnt */ +#define CL_ABORT_BSOC_RAW (int)((CL_ABORT_BSOC_10PER * \ + BATT_SOC_MAX_RANGE) / (BATT_SOC_LSB * 1000)) +#define CL_ABORT_CCSOC_10PER 5 /* 0.5 percent */ +#define CL_ABORT_CCSOC_RAW (int)((CL_ABORT_CCSOC_10PER * CC_SOC_MAX_RANGE)\ + / (CC_SOC_LSB * 1000)) +#define CL_ABORT_KEEP_TIMEOUT_MS (7 * 60 * 60 * 1000) +#define CL_ABORT_SLOW_TIMEOUT_MS (10 * 60 * 60 * 1000) +#endif static void fg_cap_learning_update(struct fg_chip *chip) { int rc, batt_soc, batt_soc_msb, cc_soc_sw; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) bool input_present = is_input_present(chip); +#endif bool prime_cc = false; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int msoc; +#endif mutex_lock(&chip->cl.lock); @@ -1567,6 +1961,14 @@ static void fg_cap_learning_update(struct fg_chip *chip) fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n", chip->charge_status, chip->cl.active, batt_soc_msb); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + rc = fg_get_sram_prop(chip, FG_SRAM_CC_SOC_SW, &cc_soc_sw); + if (rc < 0) { + pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc); + goto out; + } + +#endif /* Initialize the starting point of learning capacity */ if (!chip->cl.active) { if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) { @@ -1580,15 +1982,34 @@ static void fg_cap_learning_update(struct fg_chip *chip) } } else { if (chip->charge_done) { +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + rc = fg_get_prop_capacity(chip, &msoc); + if (rc < 0) { + pr_err("Error in getting capacity rc=%d\n", rc); + goto deactive; + } + if (msoc < 100) { + fg_dbg(chip, FG_SOMC, + "learning aborted due to not 100pc %d\n", + msoc); + goto deactive; + } +#endif rc = fg_cap_learning_done(chip); if (rc < 0) pr_err("Error in completing capacity learning, rc=%d\n", rc); +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) chip->cl.active = false; chip->cl.init_cc_uah = 0; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + goto deactive; +#endif } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (chip->charge_status == POWER_SUPPLY_STATUS_DISCHARGING) { if (!input_present) { fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n", @@ -1615,6 +2036,55 @@ static void fg_cap_learning_update(struct fg_chip *chip) prime_cc = true; } } +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chip->cl.batt_soc_drop = + chip->cl.max_bsoc_during_active - batt_soc; + chip->cl.cc_soc_drop = + chip->cl.max_ccsoc_during_active - cc_soc_sw; + chip->cl.hold_time = ktime_to_ms(ktime_get_boottime()) - + chip->cl.max_bsoc_time_ms; + chip->cl.total_time = ktime_to_ms(ktime_get_boottime()) - + chip->cl.start_time_ms; + + if (chip->cl.cc_soc_drop > CL_ABORT_CCSOC_RAW) { + fg_dbg(chip, FG_SOMC, + "CL aborted due to cc_soc_sw drop from %d to %d", + chip->cl.max_ccsoc_during_active, cc_soc_sw); + goto deactive; + } else if (chip->cl.batt_soc_drop > CL_ABORT_BSOC_RAW) { + fg_dbg(chip, FG_SOMC, + "CL aborted due to bsoc drop from %d to %d", + chip->cl.max_bsoc_during_active, batt_soc); + goto deactive; + } else if (chip->cl.hold_time > CL_ABORT_KEEP_TIMEOUT_MS) { + fg_dbg(chip, FG_SOMC, + "CL aborted due to soc holding for long hours"); + goto deactive; + } else if (chip->cl.total_time > CL_ABORT_SLOW_TIMEOUT_MS) { + fg_dbg(chip, FG_SOMC, + "CL aborted due to slow charging"); + goto deactive; + } + + /* reset params if increasing */ + if (chip->cl.batt_soc_drop < 0 || chip->cl.cc_soc_drop < 0) { + chip->cl.max_ccsoc_during_active = cc_soc_sw; + chip->cl.max_bsoc_during_active = batt_soc; + chip->cl.max_bsoc_time_ms = + ktime_to_ms(ktime_get_boottime()); + chip->cl.cc_soc_drop = 0; + chip->cl.batt_soc_drop = 0; + fg_dbg(chip, FG_CAP_LEARN, + "max bsoc/ccsoc updated: %d/%d\n", + chip->cl.max_bsoc_during_active, + chip->cl.max_ccsoc_during_active); + } + goto out; +deactive: + chip->cl.active = false; + chip->cl.init_cc_uah = 0; +#endif } /* @@ -1741,6 +2211,11 @@ static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv) if (voltage_mv == chip->last_recharge_volt_mv) return 0; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chip->recharge_voltage_mv == voltage_mv) + return 0; + +#endif fg_dbg(chip, FG_STATUS, "Setting recharge voltage to %dmV\n", voltage_mv); fg_encode(chip->sp, FG_SRAM_RECHARGE_VBATT_THR, voltage_mv, &buf); @@ -1756,6 +2231,9 @@ static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv) } chip->last_recharge_volt_mv = voltage_mv; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chip->recharge_voltage_mv = voltage_mv; +#endif return 0; } @@ -1791,6 +2269,11 @@ static int fg_charge_full_update(struct fg_chip *chip) { union power_supply_propval prop = {0, }; int rc, msoc, bsoc, recharge_soc, msoc_raw; + u8 full_soc[2] = {0xFF, 0xFF}; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + bool need_monotonic_soc_update = false; + int cc_soc_sw; +#endif if (!chip->dt.hold_soc_while_full) return 0; @@ -1830,6 +2313,7 @@ static int fg_charge_full_update(struct fg_chip *chip) fg_dbg(chip, FG_STATUS, "msoc: %d bsoc: %x health: %d status: %d full: %d\n", msoc, bsoc, chip->health, chip->charge_status, chip->charge_full); +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (chip->charge_done && !chip->charge_full) { if (msoc >= 99 && chip->health == POWER_SUPPLY_HEALTH_GOOD) { fg_dbg(chip, FG_STATUS, "Setting charge_full to true\n"); @@ -1897,6 +2381,106 @@ static int fg_charge_full_update(struct fg_chip *chip) msoc_raw, bsoc >> 8, recharge_soc, chip->delta_soc); } +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chip->charge_full) { + if (chip->charge_done) { + if ((bsoc >> 8) <= recharge_soc && + !chip->recharge_starting) { + fg_dbg(chip, FG_SOMC, "Cause to recharge\n"); + rc = fg_set_recharge_voltage(chip, + chip->bp.float_volt_uv / 1000); + if (rc < 0) { + pr_err("Error in recharge rc=%d\n", rc); + goto out; + } + chip->recharge_starting = true; + need_monotonic_soc_update = true; + } else { + fg_dbg(chip, FG_SOMC, + "Other case during charge_full.\n"); + } + } else if (chip->charge_status == + POWER_SUPPLY_STATUS_CHARGING) { + fg_dbg(chip, FG_SOMC, "Confirmed recharging\n"); + rc = fg_set_recharge_voltage(chip, + AUTO_RECHG_VOLT_LOW_LIMIT_MV); + if (rc < 0) { + pr_err("Error in recharge voltage rc=%d\n", rc); + goto out; + } + chip->recharge_starting = false; + chip->charge_full = false; + } else if (chip->charge_status == + POWER_SUPPLY_STATUS_DISCHARGING || + chip->charge_status == + POWER_SUPPLY_STATUS_NOT_CHARGING) { + fg_dbg(chip, FG_SOMC, "Removed during full\n"); + fg_dbg(chip, FG_SOMC, "Undo recharge voltage thresh\n"); + rc = fg_set_recharge_voltage(chip, + AUTO_RECHG_VOLT_LOW_LIMIT_MV); + if (rc < 0) { + pr_err("Error in recharge voltage rc=%d\n", rc); + goto out; + } + chip->charge_full = false; + need_monotonic_soc_update = true; + } else { + fg_dbg(chip, FG_SOMC, "Invalid charge_status %d\n", + chip->charge_status); + } + } else { + if (chip->charge_done) { + fg_dbg(chip, FG_SOMC, "Undo recharge voltage thresh\n"); + rc = fg_set_recharge_voltage(chip, + AUTO_RECHG_VOLT_LOW_LIMIT_MV); + if (rc < 0) { + pr_err("Error in recharge voltage rc=%d\n", rc); + goto out; + } + if (msoc >= 99 && + chip->health == POWER_SUPPLY_HEALTH_GOOD) { + fg_dbg(chip, FG_SOMC, "Detected FULL\n"); + chip->charge_full = true; + need_monotonic_soc_update = true; + } + /* Write a FULL value to cc_soc_sw */ + cc_soc_sw = CC_SOC_30BIT; + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_CC_SOC_SW].addr_word, + chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, + (u8 *)&cc_soc_sw, + chip->sp[FG_SRAM_CC_SOC_SW].len, + FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("Error in writing cc_soc_sw, rc=%d\n", + rc); + goto out; + } + } else { + fg_dbg(chip, FG_STATUS, + "Other case during not charge_full.\n"); + } + } + if (need_monotonic_soc_update) { + fg_dbg(chip, FG_SOMC, "Update FULL_SOC to bsoc=%d\n", bsoc); + rc = fg_sram_write(chip, FULL_SOC_WORD, FULL_SOC_OFFSET, + (u8 *)&bsoc, 2, FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("failed to write full_soc rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_SOMC, "Update MONOTONIC SOC to 100\n"); + rc = fg_sram_write(chip, MONOTONIC_SOC_WORD, + MONOTONIC_SOC_OFFSET, + full_soc, 2, FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("failed to write monotonic_soc rc=%d\n", rc); + goto out; + } + } +#endif out: mutex_unlock(&chip->charge_full_lock); return rc; @@ -2137,6 +2721,7 @@ static int fg_adjust_recharge_soc(struct fg_chip *chip) static int fg_adjust_recharge_voltage(struct fg_chip *chip) { +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) int rc, recharge_volt_mv; if (chip->dt.auto_recharge_soc) @@ -2159,6 +2744,7 @@ static int fg_adjust_recharge_voltage(struct fg_chip *chip) return rc; } +#endif return 0; } @@ -2855,6 +3441,7 @@ static bool is_profile_load_required(struct fg_chip *chip) } profiles_same = memcmp(chip->batt_profile, buf, PROFILE_COMP_LEN) == 0; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (profiles_same) { fg_dbg(chip, FG_STATUS, "Battery profile is same, not loading it\n"); return false; @@ -2874,8 +3461,39 @@ static bool is_profile_load_required(struct fg_chip *chip) } fg_dbg(chip, FG_STATUS, "Profiles are different, loading the correct one\n"); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (profiles_same && !chip->dt.force_load_profile) { + fg_dbg(chip, FG_SOMC, "Battery profile is same, not loading it since force_load_profile is disabled\n"); + return false; + } + + if (!profiles_same) { + fg_dbg(chip, FG_SOMC, "Profiles are different, loading the correct one\n"); + if (fg_sram_dump) { + fg_dbg(chip, FG_SOMC, "FG: loaded profile:\n"); + dump_sram(buf, PROFILE_LOAD_WORD, + PROFILE_COMP_LEN); + fg_dbg(chip, FG_SOMC, "FG: available profile:\n"); + dump_sram(chip->batt_profile, PROFILE_LOAD_WORD, + PROFILE_LEN); + } + } else { + fg_dbg(chip, FG_SOMC, "force_load_profile is enabled, loading the correct one\n"); + if (fg_sram_dump) { + fg_dbg(chip, FG_SOMC, "FG: loaded profile:\n"); + dump_sram(chip->batt_profile, PROFILE_LOAD_WORD, + PROFILE_LEN); + } + } +#endif } else { +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) fg_dbg(chip, FG_STATUS, "Profile integrity bit is not set\n"); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + fg_dbg(chip, FG_SOMC, "Profile integrity bit is not set\n"); +#endif if (fg_profile_dump) { pr_info("FG: profile to be loaded:\n"); dump_sram(chip->batt_profile, PROFILE_LOAD_WORD, @@ -2985,6 +3603,72 @@ out: return rc; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +static int fg_somc_write_back_sram_params(struct fg_chip *chip) +{ + int rc; + int16_t act_cap_mah; + u8 buf[4], val; + + /* Rewrite the CYCLE_COUNT */ + rc = fg_sram_write(chip, CYCLE_COUNT_WORD, CYCLE_COUNT_OFFSET, + (u8 *)&chip->cyc_ctr.count, + sizeof(chip->cyc_ctr.count) / sizeof(u8 *), + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in rewriting cycle counter, rc=%d\n", rc); + return rc; + } + + /* Rewrite the ACT_BATT_CAP_BKUP */ + act_cap_mah = div64_s64(chip->cl.learned_cc_uah, 1000); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_ACT_BATT_CAP].addr_word, + chip->sp[FG_SRAM_ACT_BATT_CAP].addr_byte, + (u8 *)&act_cap_mah, chip->sp[FG_SRAM_ACT_BATT_CAP].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in rewriting act_batt_cap_bkup, rc=%d\n", rc); + return rc; + } + + /* Write the BATT_AGING_LEVEL */ + val = chip->batt_aging_level; + rc = fg_sram_write(chip, BATT_AGING_LEVEL_WORD, + BATT_AGING_LEVEL_OFFSET, &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing batt_aging_level, rc=%d\n", rc); + return rc; + } + + /* This SRAM register is only present in v2.0 and above */ + if (!(chip->wa_flags & PMI8998_V1_REV_WA) && + chip->bp.float_volt_uv > 0) { + fg_encode(chip->sp, FG_SRAM_FLOAT_VOLT, + chip->bp.float_volt_uv / 1000, buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_FLOAT_VOLT].addr_word, + chip->sp[FG_SRAM_FLOAT_VOLT].addr_byte, buf, + chip->sp[FG_SRAM_FLOAT_VOLT].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing float_volt, rc=%d\n", rc); + return rc; + } + } + + if (chip->bp.vbatt_full_mv > 0) { + rc = fg_set_constant_chg_voltage(chip, + chip->bp.vbatt_full_mv * 1000); + if (rc < 0) + return rc; + } + + return 0; +} + +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define VBATT_RANGE_CHARGE_UV 250000 +#define VBATT_RANGE_DISCHARGE_UV 250000 +#endif static void profile_load_work(struct work_struct *work) { struct fg_chip *chip = container_of(work, @@ -2992,6 +3676,9 @@ static void profile_load_work(struct work_struct *work) profile_load_work.work); u8 buf[2], val; int rc; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int vbatt_predict_uv, vbatt_uv; +#endif vote(chip->awake_votable, PROFILE_LOAD, true, 0); @@ -3010,17 +3697,58 @@ static void profile_load_work(struct work_struct *work) if (!chip->profile_available) goto out; - fg_update_batt_profile(chip); - +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (!is_profile_load_required(chip)) goto done; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (!is_profile_load_required(chip)) { + rc = fg_get_vbatt_predict(chip, &vbatt_predict_uv); + if (rc < 0) { + pr_err("failed to get battery voltage, rc=%d\n", rc); + goto out; + } + + rc = fg_get_battery_voltage(chip, &vbatt_uv); + if (rc < 0) { + pr_err("failed to get battery voltage, rc=%d\n", rc); + goto out; + } + fg_dbg(chip, FG_SOMC, "VBATT vs PREDICT : %d vs %d\n", + vbatt_uv, vbatt_predict_uv); + + if (vbatt_predict_uv - vbatt_uv > VBATT_RANGE_DISCHARGE_UV || + vbatt_uv - vbatt_predict_uv > VBATT_RANGE_CHARGE_UV) { + fg_dbg(chip, FG_SOMC, + "out of range. So restart FG\n"); + rc = __fg_restart(chip); + if (rc < 0) { + pr_err("Error in restarting FG, rc=%d\n", rc); + goto out; + } + fg_dbg(chip, FG_SOMC, "SOC is ready\n"); + } + goto done; + } +#endif +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) clear_cycle_counter(chip); mutex_lock(&chip->cl.lock); chip->cl.learned_cc_uah = 0; chip->cl.active = false; mutex_unlock(&chip->cl.lock); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chip->batt_aging_level == chip->saved_batt_aging_level) { + clear_cycle_counter(chip); + mutex_lock(&chip->cl.lock); + chip->cl.learned_cc_uah = 0; + chip->cl.active = false; + mutex_unlock(&chip->cl.lock); + } +#endif fg_dbg(chip, FG_STATUS, "profile loading started\n"); rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); @@ -3038,6 +3766,16 @@ static void profile_load_work(struct work_struct *work) goto out; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chip->batt_aging_level != chip->saved_batt_aging_level) { + rc = fg_somc_write_back_sram_params(chip); + if (rc < 0) { + pr_err("Error in write sram params, rc=%d\n", rc); + goto out; + } + } + +#endif rc = __fg_restart(chip); if (rc < 0) { pr_err("Error in restarting FG, rc=%d\n", rc); @@ -3055,6 +3793,9 @@ static void profile_load_work(struct work_struct *work) goto out; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chip->saved_batt_aging_level = chip->batt_aging_level; +#endif done: rc = fg_bp_params_config(chip); if (rc < 0) @@ -3083,6 +3824,9 @@ done: chip->profile_loaded = true; fg_dbg(chip, FG_STATUS, "profile loaded successfully"); out: +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chip->batt_aging_level = chip->saved_batt_aging_level; +#endif chip->soc_reporting_ready = true; vote(chip->awake_votable, PROFILE_LOAD, false, 0); } @@ -3789,7 +4533,15 @@ static int fg_psy_get_property(struct power_supply *psy, rc = fg_get_sram_prop(chip, FG_SRAM_OCV, &pval->intval); break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) pval->intval = chip->cl.nom_cap_uah; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chip->initial_capacity >= 0) + pval->intval = chip->initial_capacity; + else + pval->intval = chip->cl.nom_cap_uah; +#endif break; case POWER_SUPPLY_PROP_RESISTANCE_ID: pval->intval = chip->batt_id_ohms; @@ -3850,6 +4602,17 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CC_STEP_SEL: pval->intval = chip->ttf.cc_step.sel; break; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + case POWER_SUPPLY_PROP_CHARGE_FULL_RAW: + pval->intval = chip->charge_full_raw; + break; + case POWER_SUPPLY_PROP_TIME_TO_CAP_LEARNING: + pval->intval = chip->cl.learned_time_ms / 1000; + break; + case POWER_SUPPLY_PROP_MONOTONIC_SOC: + rc = fg_get_msoc(chip, &pval->intval); + break; +#endif default: pr_err("unsupported property %d\n", psp); rc = -EINVAL; @@ -4047,6 +4810,11 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CC_STEP, POWER_SUPPLY_PROP_CC_STEP_SEL, +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + POWER_SUPPLY_PROP_CHARGE_FULL_RAW, + POWER_SUPPLY_PROP_TIME_TO_CAP_LEARNING, + POWER_SUPPLY_PROP_MONOTONIC_SOC, +#endif }; static const struct power_supply_desc fg_psy_desc = { @@ -4335,6 +5103,74 @@ static int fg_hw_init(struct fg_chip *chip) } } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chip->dt.therm_coeff_c1 > 0) { + rc = fg_masked_write(chip, BATT_INFO_THERM_C1(chip), + BATT_INFO_THERM_COEFF_MASK, + chip->dt.therm_coeff_c1); + if (rc < 0) { + pr_err("Error in writing therm_coeff_c1, rc=%d\n", rc); + return rc; + } + } + + if (chip->dt.therm_coeff_c2 > 0) { + rc = fg_masked_write(chip, BATT_INFO_THERM_C2(chip), + BATT_INFO_THERM_COEFF_MASK, + chip->dt.therm_coeff_c2); + if (rc < 0) { + pr_err("Error in writing therm_coeff_c2, rc=%d\n", rc); + return rc; + } + } + + if (chip->dt.therm_coeff_c3 > 0) { + rc = fg_masked_write(chip, BATT_INFO_THERM_C3(chip), + BATT_INFO_THERM_COEFF_MASK, + chip->dt.therm_coeff_c3); + if (rc < 0) { + pr_err("Error in writing therm_coeff_c3, rc=%d\n", rc); + return rc; + } + } + + val = FG_ESR_CURRENT_THR_VALUE; + rc = fg_sram_write(chip, ESR_CURRENT_THR_WORD, ESR_CURRENT_THR_OFFSET, + &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing ESR current threshold, rc=%d\n", rc); + return rc; + } + + val = KI_COEFF_CUTOFF_VOLT_VALUE; + rc = fg_sram_write(chip, KI_COEFF_CUTOFF_VOLT_WORD, + KI_COEFF_CUTOFF_VOLT_OFFSET, + &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing KI_COEFF_CUTOFF_VOLT_OFFSET, rc=%d\n", + rc); + return rc; + } + + rc = fg_sram_masked_write(chip, ESR_VCTIBTRSLWEN_WORD, + ESR_VCTIBTRSLWEN_OFFSET, + ESR_VCTIBTRSLWEN_MASK, + ESR_VCTIBTRSLWEN_VALUE, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing ESR_VCTIBTRSLWEN_OFFSET, rc=%d\n", rc); + return rc; + } + + rc = fg_sram_masked_write(chip, SAT_CC_CLR_AUTO_WORD, + SAT_CC_CLR_AUTO_OFFSET, + SAT_CC_CLR_AUTO_MASK, + SAT_CC_CLR_AUTO_VALUE, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing SAT_CC_CLR_AUTO_OFFSET, rc=%d\n", rc); + return rc; + } + +#endif return 0; } @@ -5145,6 +5981,20 @@ static int fg_parse_dt(struct fg_chip *chip) chip->dt.linearize_soc = of_property_read_bool(node, "qcom,linearize-soc"); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + rc = of_property_read_u32(node, "somc,rated-capacity-uah", &temp); + if (rc < 0) + chip->rated_capacity = -EINVAL; + else + chip->rated_capacity = temp; + + rc = of_property_read_u32(node, "somc,initial-capacity-uah", &temp); + if (rc < 0) + chip->initial_capacity = -EINVAL; + else + chip->initial_capacity = temp; + +#endif rc = fg_parse_ki_coefficients(chip); if (rc < 0) pr_err("Error in parsing Ki coefficients, rc=%d\n", rc); @@ -5235,6 +6085,26 @@ static int fg_parse_dt(struct fg_chip *chip) chip->dt.esr_meas_curr_ma = temp; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + rc = of_property_read_u32(node, "somc,therm-coeff-c1", &temp); + if (rc < 0) + chip->dt.therm_coeff_c1 = -EINVAL; + else + chip->dt.therm_coeff_c1 = temp; + + rc = of_property_read_u32(node, "somc,therm-coeff-c2", &temp); + if (rc < 0) + chip->dt.therm_coeff_c2 = -EINVAL; + else + chip->dt.therm_coeff_c2 = temp; + + rc = of_property_read_u32(node, "somc,therm-coeff-c3", &temp); + if (rc < 0) + chip->dt.therm_coeff_c3 = -EINVAL; + else + chip->dt.therm_coeff_c3 = temp; + +#endif return 0; } @@ -5258,6 +6128,360 @@ static void fg_cleanup(struct fg_chip *chip) dev_set_drvdata(chip->dev, NULL); } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +/***************************** + * somc sysfs implementation * + *****************************/ +enum fg_somc_sysfs { + ATTR_RSLOW = 0, + ATTR_BATTERY_SOC, + ATTR_CC_SOC, + ATTR_SOC_SYSTEM, + ATTR_SOC_MONOTONIC, + ATTR_SOC_CUTOFF, + ATTR_SOC_FULL, + ATTR_SW_CC_SOC, + ATTR_FG_CAPACITY, + ATTR_SOC_INT, + ATTR_BATT_INT, + ATTR_PMIC_SUBTYPE, + ATTR_BATT_INFO, + ATTR_BATT_AGING_LEVEL, + ATTR_RATED_CAPACITY, + ATTR_RECAHRGE_VOLTAGE_MV, + ATTR_CHARGE_FULL, + ATTR_CL_ACTIVE, + ATTR_CL_BSOC_DROP, + ATTR_CL_CCSOC_DROP, + ATTR_CL_HOLD_TIME, + ATTR_CL_TOTAL_TIME, +}; + +static ssize_t fg_somc_param_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t fg_somc_param_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count); + +static struct device_attribute fg_somc_attrs[] = { + __ATTR(rslow, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(battery_soc, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(cc_soc, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(soc_system, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(soc_monotonic, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(soc_cutoff, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(soc_full, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(sw_cc_soc, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(capacity, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(soc_int, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(batt_int, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(pmic_subtype, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(batt_info, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(batt_aging_level, S_IRUGO|S_IWUSR, + fg_somc_param_show, fg_somc_param_store), + __ATTR(rated_capacity, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(recharge_voltage_mv, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(charge_full, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(cl_active, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(cl_bsoc_drop, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(cl_ccsoc_drop, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(cl_hold_time, S_IRUGO, fg_somc_param_show, NULL), + __ATTR(cl_total_time, S_IRUGO, fg_somc_param_show, NULL), +}; + +#define DECIMAL_CELL 100 +#define DECIMAL_MAG 10000LL + +ssize_t fg_somc_get_sram_soc_str(struct fg_chip *chip, + enum fg_sram_param_id id, s64 soc_max_range, s64 soc_lsb, + bool is_signed, char *buf, int size) +{ + int rc = 0; + int value, capacity, high_cap, low_cap; + + rc = fg_get_sram_prop(chip, id, &value); + if (rc < 0) { + pr_err("Error reading address rc=%d\n", rc); + return 0; + } + + if (is_signed) + capacity = (int)(((s64)value * soc_lsb * DECIMAL_MAG) / + soc_max_range); + else + capacity = (int)(((u64)((u32)value) * soc_lsb * DECIMAL_MAG) / + soc_max_range); + + high_cap = capacity / DECIMAL_CELL; + low_cap = abs(capacity % DECIMAL_CELL); + size = scnprintf(buf, size, "%d.%02d", high_cap, low_cap); + return size; +} + +static ssize_t fg_somc_param_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fg_chip *chip = dev_get_drvdata(dev); + ssize_t size = 0; + const ptrdiff_t off = attr - fg_somc_attrs; + int rc = 0; + int val; + u8 reg; + u8 sram_buf_1; + u8 sram_buf_4[4]; + + switch (off) { + case ATTR_RSLOW: + rc = fg_get_sram_prop(chip, FG_SRAM_RSLOW, &val); + if (rc < 0) + pr_err("Error reading address rc=%d\n", rc); + else + size = scnprintf(buf, PAGE_SIZE, "%d\n", val); + break; + case ATTR_BATTERY_SOC: + size = fg_somc_get_sram_soc_str(chip, FG_SRAM_BATT_SOC, + BATT_SOC_MAX_RANGE, BATT_SOC_LSB, + false, buf, PAGE_SIZE); + break; + case ATTR_CC_SOC: + size = fg_somc_get_sram_soc_str(chip, FG_SRAM_CC_SOC, + CC_SOC_MAX_RANGE, CC_SOC_LSB, + true, buf, PAGE_SIZE); + break; + case ATTR_SOC_SYSTEM: + size = fg_somc_get_sram_soc_str(chip, FG_SRAM_SOC_SYSTEM, + SOC_MAX_RANGE, SOC_LSB, + false, buf, PAGE_SIZE); + break; + case ATTR_SOC_MONOTONIC: + size = fg_somc_get_sram_soc_str(chip, FG_SRAM_SOC_MONOTONIC, + SOC_MAX_RANGE, SOC_LSB, + false, buf, PAGE_SIZE); + break; + case ATTR_SOC_CUTOFF: + size = fg_somc_get_sram_soc_str(chip, FG_SRAM_SOC_CUTOFF, + SOC_MAX_RANGE, SOC_LSB, + false, buf, PAGE_SIZE); + break; + case ATTR_SOC_FULL: + size = fg_somc_get_sram_soc_str(chip, FG_SRAM_SOC_FULL, + SOC_MAX_RANGE, SOC_LSB, + false, buf, PAGE_SIZE); + break; + case ATTR_SW_CC_SOC: + size = fg_somc_get_sram_soc_str(chip, FG_SRAM_CC_SOC_SW, + CC_SOC_MAX_RANGE, CC_SOC_LSB, + true, buf, PAGE_SIZE); + break; + case ATTR_FG_CAPACITY: + rc = fg_read(chip, BATT_SOC_FG_MONOTONIC_SOC_CP(chip), ®, 1); + if (rc < 0) + pr_err("Error reading address rc=%d\n", rc); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02x\n", reg); + break; + case ATTR_SOC_INT: + rc = fg_read(chip, BATT_SOC_INT_RT_STS(chip), ®, 1); + if (rc < 0) + pr_err("Error reading address rc=%d\n", rc); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02x\n", reg); + break; + case ATTR_BATT_INT: + rc = fg_read(chip, BATT_INFO_INT_RT_STS(chip), ®, 1); + if (rc < 0) + pr_err("Error reading address rc=%d\n", rc); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02x\n", reg); + break; + case ATTR_PMIC_SUBTYPE: + switch (chip->pmic_rev_id->pmic_subtype) { + case PMI8998_SUBTYPE: + if (chip->pmic_rev_id->rev4 < PMI8998_V2P0_REV4) + size = scnprintf(buf, PAGE_SIZE, "PMI8998-v1\n"); + else if (chip->pmic_rev_id->rev4 == PMI8998_V2P0_REV4) + size = scnprintf(buf, PAGE_SIZE, "PMI8998-v2\n"); + else + size = scnprintf(buf, PAGE_SIZE, "not supported\n"); + break; + case PM660_SUBTYPE: + size = scnprintf(buf, PAGE_SIZE, "PM660\n"); + break; + default: + size = scnprintf(buf, PAGE_SIZE, "not supported\n"); + } + break; + case ATTR_BATT_INFO: + rc = fg_sram_read(chip, 0, 0, sram_buf_4, 4, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in readging addr 0, rc:%d\n", rc); + break; + } + rc = fg_sram_read(chip, 20, 0, &sram_buf_1, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in readging addr 20, rc:%d\n", rc); + break; + } + size = scnprintf(buf, PAGE_SIZE, + "%s/%d/%d/%02x %02x %02x %02x|%02x\n", + fg_get_battery_type(chip), + chip->batt_id_ohms, + chip->batt_aging_level, + sram_buf_4[0], + sram_buf_4[1], + sram_buf_4[2], + sram_buf_4[3], + sram_buf_1); + break; + case ATTR_BATT_AGING_LEVEL: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + chip->batt_aging_level); + break; + case ATTR_RATED_CAPACITY: + if (chip->rated_capacity >= 0) + size = scnprintf(buf, PAGE_SIZE, "%d\n", + chip->rated_capacity); + else + size = scnprintf(buf, PAGE_SIZE, "%d\n", + (int)chip->cl.nom_cap_uah); + break; + case ATTR_RECAHRGE_VOLTAGE_MV: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + chip->recharge_voltage_mv); + break; + case ATTR_CHARGE_FULL: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + (int)chip->charge_full); + break; + case ATTR_CL_ACTIVE: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + (int)chip->cl.active); + break; + case ATTR_CL_BSOC_DROP: + val = (int)((u64)chip->cl.batt_soc_drop + * BATT_SOC_LSB * DECIMAL_MAG + / BATT_SOC_MAX_RANGE); + size = scnprintf(buf, PAGE_SIZE, "%d.%02d", + val / DECIMAL_CELL, val % DECIMAL_CELL); + break; + case ATTR_CL_CCSOC_DROP: + val = (int)((u64)chip->cl.cc_soc_drop + * CC_SOC_LSB * DECIMAL_MAG + / CC_SOC_MAX_RANGE); + size = scnprintf(buf, PAGE_SIZE, "%d.%02d", + val / DECIMAL_CELL, val % DECIMAL_CELL); + break; + case ATTR_CL_HOLD_TIME: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + (int)(chip->cl.hold_time / 1000)); + break; + case ATTR_CL_TOTAL_TIME: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + (int)(chip->cl.total_time / 1000)); + break; + default: + size = 0; + break; + } + return size; +} + +static int fg_somc_set_batt_aging_level(struct fg_chip *chip, const char *buf) +{ + int rc, msoc; + + rc = kstrtoint(buf, 10, &chip->batt_aging_level); + if (rc < 0) { + pr_err("Error in batt aging level being invalid, rc:%d\n", rc); + goto err; + } + + if (chip->batt_aging_level == chip->saved_batt_aging_level) + return 0; + + rc = fg_get_prop_capacity(chip, &msoc); + if (rc < 0) { + pr_err("Error in getting capacity, rc=%d\n", rc); + goto err; + } + + chip->last_soc = msoc; + chip->profile_loaded = false; + schedule_delayed_work(&chip->profile_load_work, 0); + return 0; + +err: + chip->batt_aging_level = chip->saved_batt_aging_level; + return -EINVAL; +} + +static void fg_somc_restore_batt_aging_level(struct fg_chip *chip) +{ + int rc; + u8 val; + + rc = fg_sram_read(chip, BATT_AGING_LEVEL_WORD, + BATT_AGING_LEVEL_OFFSET, &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to read batt aging level rc=%d\n", rc); + chip->saved_batt_aging_level = 0; + } else { + chip->saved_batt_aging_level = val; + } + + chip->batt_aging_level = chip->saved_batt_aging_level; +} + +static ssize_t fg_somc_param_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fg_chip *chip = dev_get_drvdata(dev); + const ptrdiff_t off = attr - fg_somc_attrs; + int rc; + + switch (off) { + case ATTR_BATT_AGING_LEVEL: + rc = fg_somc_set_batt_aging_level(chip, buf); + if (rc < 0) + return rc; + + break; + default: + break; + } + + return count; +} + +static int fg_somc_create_sysfs_entries(struct device *dev) +{ + int i; + int rc = 0; + + for (i = 0; i < ARRAY_SIZE(fg_somc_attrs); i++) { + rc = device_create_file(dev, &fg_somc_attrs[i]); + if (rc < 0) { + dev_err(dev, "device_create_file failed rc = %d\n", rc); + goto revert; + } + } + return 0; +revert: + for (i = i - 1; i >= 0; i--) + device_remove_file(dev, &fg_somc_attrs[i]); + return rc; +} + +static void fg_somc_remove_sysfs_entries(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fg_somc_attrs); i++) + device_remove_file(dev, &fg_somc_attrs[i]); +} + +#endif static int fg_gen3_probe(struct platform_device *pdev) { struct fg_chip *chip; @@ -5364,6 +6588,9 @@ static int fg_gen3_probe(struct platform_device *pdev) goto exit; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + fg_somc_restore_batt_aging_level(chip); +#endif rc = fg_hw_init(chip); if (rc < 0) { dev_err(chip->dev, "Error in initializing FG hardware, rc:%d\n", @@ -5417,6 +6644,17 @@ static int fg_gen3_probe(struct platform_device *pdev) goto exit; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + rc = fg_somc_create_sysfs_entries(chip->dev); + if (rc < 0) { + dev_err(chip->dev, + "Error in creating fg_somc_sysfs entries, rc:%d\n", + rc); + goto exit; + } + +#endif + rc = fg_get_battery_voltage(chip, &volt_uv); if (!rc) rc = fg_get_prop_capacity(chip, &msoc); @@ -5496,6 +6734,9 @@ static int fg_gen3_remove(struct platform_device *pdev) { struct fg_chip *chip = dev_get_drvdata(&pdev->dev); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + fg_somc_remove_sysfs_entries(chip->dev); +#endif fg_cleanup(chip); return 0; } diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index a7c206c42418d43cb519dcbc8002f1e9375d8b30..5fefe52fe1d389a8e895da44666e2a9433b4c0fd 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -24,6 +29,10 @@ #include #include #include +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#include +#include +#endif #include "smb-reg.h" #include "smb-lib.h" #include "storm-watch.h" @@ -179,7 +188,14 @@ struct smb2 { bool bad_part; }; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) static int __debug_mask; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +static int __debug_mask = + PR_INTERRUPT | + PR_SOMC; +#endif module_param_named( debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR ); @@ -241,6 +257,17 @@ static int smb2_parse_dt(struct smb2 *chip) if (rc < 0) chip->dt.usb_icl_ua = -EINVAL; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + rc = of_property_read_u32(node, + "somc,product-icl-ua", &chg->product_icl_ua); + if (rc < 0) + chg->product_icl_ua = -EINVAL; + + rc = of_property_read_u32(node, + "somc,high-voltage-icl-ua", &chg->high_voltage_icl_ua); + if (rc < 0) + chg->high_voltage_icl_ua = -EINVAL; +#endif rc = of_property_read_u32(node, "qcom,otg-cl-ua", &chg->otg_cl_ua); if (rc < 0) @@ -274,6 +301,7 @@ static int smb2_parse_dt(struct smb2 *chip) if (rc < 0) chip->dt.wipower_max_uw = -EINVAL; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (of_find_property(node, "qcom,thermal-mitigation", &byte_len)) { chg->thermal_mitigation = devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); @@ -292,6 +320,69 @@ static int smb2_parse_dt(struct smb2 *chip) return rc; } } +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (of_find_property(node, "somc,thermal-fcc-ua", &byte_len)) { + chg->thermal_fcc_ua = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_fcc_ua == NULL) + return -ENOMEM; + + chg->thermal_fcc_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "somc,thermal-fcc-ua", + chg->thermal_fcc_ua, + chg->thermal_fcc_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } else { + pr_err("Coudn't find thermal-fcc-ua table\n"); + } + if (of_find_property(node, "somc,thermal-lo-volt-icl-ua", &byte_len)) { + chg->thermal_lo_volt_icl_ua = devm_kzalloc(chg->dev, + byte_len, GFP_KERNEL); + + if (chg->thermal_lo_volt_icl_ua == NULL) + return -ENOMEM; + + chg->thermal_lo_volt_icl_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "somc,thermal-lo-volt-icl-ua", + chg->thermal_lo_volt_icl_ua, + chg->thermal_lo_volt_icl_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } else { + pr_err("Coudn't find thermal-lo-volt-icl-ua table\n"); + } + if (of_find_property(node, "somc,thermal-hi-volt-icl-ua", &byte_len)) { + chg->thermal_hi_volt_icl_ua = devm_kzalloc(chg->dev, + byte_len, GFP_KERNEL); + + if (chg->thermal_hi_volt_icl_ua == NULL) + return -ENOMEM; + + chg->thermal_hi_volt_icl_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "somc,thermal-hi-volt-icl-ua", + chg->thermal_hi_volt_icl_ua, + chg->thermal_hi_volt_icl_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } else { + pr_err("Coudn't find thermal-hi-volt-icl-ua table\n"); + } +#endif of_property_read_u32(node, "qcom,float-option", &chip->dt.float_option); if (chip->dt.float_option < 0 || chip->dt.float_option > 4) { @@ -302,6 +393,7 @@ static int smb2_parse_dt(struct smb2 *chip) chip->dt.hvdcp_disable = of_property_read_bool(node, "qcom,hvdcp-disable"); +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) of_property_read_u32(node, "qcom,chg-inhibit-threshold-mv", &chip->dt.chg_inhibit_thr_mv); if ((chip->dt.chg_inhibit_thr_mv < 0 || @@ -309,6 +401,7 @@ static int smb2_parse_dt(struct smb2 *chip) pr_err("qcom,chg-inhibit-threshold-mv is incorrect\n"); return -EINVAL; } +#endif chip->dt.auto_recharge_soc = of_property_read_bool(node, "qcom,auto-recharge-soc"); @@ -328,6 +421,67 @@ static int smb2_parse_dt(struct smb2 *chip) chg->fcc_stepper_mode = of_property_read_bool(node, "qcom,fcc-stepping-enable"); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* USB_SWITCH_SEL */ + chg->usb_switch_sel_gpio = + of_get_named_gpio(node, "usb_switch_sel", 0); + if (!gpio_is_valid(chg->usb_switch_sel_gpio)) { + pr_err("usb_switch_sel_gpio is not available\n"); + return -EINVAL; + } + + chg->jeita_sw_ctl_en = of_property_read_bool(node, + "somc,jeita-sw-ctrl-en"); + chg->jeita_use_aux = of_property_read_bool(node, + "somc,jeita-use-aux-therm"); + + if (chg->jeita_sw_ctl_en) { + /* fcc limitation for JEITA */ + rc = of_property_read_u32(node, "somc,jeita-warm-fcc-ua", + &chg->jeita_warm_fcc_ua); + if (rc < 0) + chg->jeita_warm_fcc_ua = -EINVAL; + + rc = of_property_read_u32(node, "somc,jeita-cool-fcc-ua", + &chg->jeita_cool_fcc_ua); + if (rc < 0) + chg->jeita_cool_fcc_ua = -EINVAL; + } + + if (chg->jeita_use_aux) { + /* JEITA AUX_THERM threshold*/ + rc = of_property_read_u32(node, "somc,jeita-aux-thresh-hot", + &chg->jeita_aux_thresh_hot); + if (rc < 0) { + pr_err("Coudn't find jeita-aux-thresh-hot\n"); + chg->jeita_use_aux = false; + } + + rc = of_property_read_u32(node, "somc,jeita-aux-thresh-warm", + &chg->jeita_aux_thresh_warm); + if (rc < 0) { + pr_err("Coudn't find jeita-aux-thresh-warm\n"); + chg->jeita_use_aux = false; + } + + if (chg->jeita_aux_thresh_hot <= + chg->jeita_aux_thresh_warm) { + pr_err("Invalid parameter jeita_aux_thresh_warm/hot\n"); + chg->jeita_use_aux = false; + } + } + + if (chg->jeita_sw_ctl_en) + pr_debug("JEITA SW Contorol is enabled.\n"); + else + pr_debug("JEITA SW Contorol is disabled.\n"); + + if (chg->jeita_use_aux) + pr_debug("AUX_THERM Monitoring is enabled.\n"); + else + pr_debug("AUX_THERM Monitoring is disabled.\n"); + +#endif return 0; } @@ -359,6 +513,10 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, POWER_SUPPLY_PROP_SDP_CURRENT_MAX, +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + POWER_SUPPLY_PROP_CHARGER_TYPE, + POWER_SUPPLY_PROP_LEGACY_CABLE_STATUS, +#endif }; static int smb2_usb_get_prop(struct power_supply *psy, @@ -387,8 +545,10 @@ static int smb2_usb_get_prop(struct power_supply *psy, val->intval = 0; else val->intval = 1; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN) val->intval = 0; +#endif break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_get_prop_usb_voltage_max(chg, val); @@ -474,6 +634,14 @@ static int smb2_usb_get_prop(struct power_supply *psy, val->intval = get_client_vote(chg->usb_icl_votable, USB_PSY_VOTER); break; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + case POWER_SUPPLY_PROP_CHARGER_TYPE: + val->strval = smblib_somc_get_charger_type(chg); + break; + case POWER_SUPPLY_PROP_LEGACY_CABLE_STATUS: + rc = smblib_get_prop_legacy_cable_status(chg, val); + break; +#endif default: pr_err("get prop %d is not supported in usb\n", psp); rc = -EINVAL; @@ -495,7 +663,9 @@ static int smb2_usb_set_prop(struct power_supply *psy, int rc = 0; mutex_lock(&chg->lock); - if (!chg->typec_present) { + if (!chg->typec_present && + psp != POWER_SUPPLY_PROP_TYPEC_POWER_ROLE) { + pr_warn("set_prop is inhibited because typec is not present\n"); rc = -EINVAL; goto unlock; } @@ -571,6 +741,9 @@ static int smb2_init_usb_psy(struct smb2 *chip) chg->usb_psy_desc.get_property = smb2_usb_get_prop; chg->usb_psy_desc.set_property = smb2_usb_set_prop; chg->usb_psy_desc.property_is_writeable = smb2_usb_prop_is_writeable; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chg->usb_params.apsd_result_bit = 0; +#endif usb_cfg.drv_data = chip; usb_cfg.of_node = chg->dev->of_node; @@ -917,6 +1090,10 @@ static int smb2_init_dc_psy(struct smb2 *chip) *************************/ static enum power_supply_property smb2_batt_props[] = { +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + POWER_SUPPLY_PROP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_SKIN_TEMP, +#endif POWER_SUPPLY_PROP_INPUT_SUSPEND, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, @@ -945,8 +1122,21 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_DP_DM, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, - POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CYCLE_COUNT, +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + POWER_SUPPLY_PROP_SMART_CHARGING_ACTIVATION, + POWER_SUPPLY_PROP_SMART_CHARGING_INTERRUPTION, + POWER_SUPPLY_PROP_SMART_CHARGING_STATUS, + POWER_SUPPLY_PROP_LRC_ENABLE, + POWER_SUPPLY_PROP_LRC_SOCMAX, + POWER_SUPPLY_PROP_LRC_SOCMIN, + POWER_SUPPLY_PROP_LRC_NOT_STARTUP, + POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_INT_CLD, + POWER_SUPPLY_PROP_RUNNING_STATUS, +#endif }; static int smb2_batt_get_prop(struct power_supply *psy, @@ -967,6 +1157,11 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_PRESENT: rc = smblib_get_prop_batt_present(chg, val); break; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + rc = smblib_get_prop_charging_enabled(chg, val); + break; +#endif case POWER_SUPPLY_PROP_INPUT_SUSPEND: rc = smblib_get_prop_input_suspend(chg, val); break; @@ -979,6 +1174,11 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: rc = smblib_get_prop_system_temp_level(chg, val); break; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + case POWER_SUPPLY_PROP_SKIN_TEMP: + rc = smblib_get_prop_skin_temp(chg, val); + break; +#endif case POWER_SUPPLY_PROP_CHARGER_TEMP: /* do not query RRADC if charger is not present */ rc = smblib_get_prop_usb_present(chg, &pval); @@ -1044,7 +1244,6 @@ static int smb2_batt_get_prop(struct power_supply *psy, val->intval = 0; break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: - case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_CYCLE_COUNT: case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_CURRENT_NOW: @@ -1054,6 +1253,42 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE: val->intval = chg->fcc_stepper_mode; break; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + case POWER_SUPPLY_PROP_SMART_CHARGING_ACTIVATION: + val->intval = chg->smart_charge_enabled; + break; + case POWER_SUPPLY_PROP_SMART_CHARGING_INTERRUPTION: + case POWER_SUPPLY_PROP_SMART_CHARGING_STATUS: + val->intval = chg->smart_charge_suspended; + break; + case POWER_SUPPLY_PROP_LRC_ENABLE: + val->intval = chg->lrc_enabled; + break; + case POWER_SUPPLY_PROP_LRC_SOCMAX: + val->intval = chg->lrc_socmax; + break; + case POWER_SUPPLY_PROP_LRC_SOCMIN: + val->intval = chg->lrc_socmin; + break; + case POWER_SUPPLY_PROP_LRC_NOT_STARTUP: + val->intval = chg->lrc_fake_capacity; + break; + case POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT: + val->intval = get_client_vote(chg->fcc_votable, QNS_VOTER); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + rc = smblib_get_prop_charge_full_design(chg, val); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + rc = smblib_get_prop_charge_full(chg, val); + break; + case POWER_SUPPLY_PROP_INT_CLD: + val->intval = chg->int_cld; + break; + case POWER_SUPPLY_PROP_RUNNING_STATUS: + val->intval = chg->running_status; + break; +#endif default: pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; @@ -1067,6 +1302,10 @@ static int smb2_batt_get_prop(struct power_supply *psy, return 0; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define FAKE_CAPACITY_HYSTERISIS 1 + +#endif static int smb2_batt_set_prop(struct power_supply *psy, enum power_supply_property prop, const union power_supply_propval *val) @@ -1075,6 +1314,11 @@ static int smb2_batt_set_prop(struct power_supply *psy, struct smb_charger *chg = power_supply_get_drvdata(psy); switch (prop) { +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + rc = smblib_set_prop_charging_enabled(chg, val); + break; +#endif case POWER_SUPPLY_PROP_INPUT_SUSPEND: rc = smblib_set_prop_input_suspend(chg, val); break; @@ -1089,6 +1333,9 @@ static int smb2_batt_set_prop(struct power_supply *psy, break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: chg->batt_profile_fv_uv = val->intval; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_somc_ctrl_inhibit(chg, true); +#endif vote(chg->fv_votable, BATT_PROFILE_VOTER, true, val->intval); break; case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE: @@ -1148,6 +1395,52 @@ static int smb2_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: rc = smblib_set_prop_input_current_limited(chg, val); break; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + case POWER_SUPPLY_PROP_SMART_CHARGING_ACTIVATION: + if (val->intval) { + pr_debug("Smart Charging was activated.\n"); + chg->smart_charge_enabled = true; + } + break; + case POWER_SUPPLY_PROP_SMART_CHARGING_INTERRUPTION: + if (chg->smart_charge_enabled) { + chg->smart_charge_suspended = (bool)val->intval; + rc = smblib_somc_smart_set_suspend(chg); + power_supply_changed(chg->batt_psy); + } + break; + case POWER_SUPPLY_PROP_LRC_ENABLE: + chg->lrc_enabled = val->intval; + smblib_somc_lrc_check(chg); + break; + case POWER_SUPPLY_PROP_LRC_SOCMAX: + chg->lrc_socmax = (int)val->intval; + break; + case POWER_SUPPLY_PROP_LRC_SOCMIN: + chg->lrc_socmin = (int)val->intval; + break; + case POWER_SUPPLY_PROP_LRC_NOT_STARTUP: + chg->lrc_fake_capacity = (int)val->intval; + if (chg->lrc_fake_capacity) + chg->lrc_hysterisis = FAKE_CAPACITY_HYSTERISIS; + break; + case POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT: + vote(chg->fcc_votable, QNS_VOTER, true, val->intval); + break; + case POWER_SUPPLY_PROP_INT_CLD: + chg->int_cld = (int)val->intval; + if (chg->int_cld) + power_supply_changed(chg->batt_psy); + break; + case POWER_SUPPLY_PROP_RUNNING_STATUS: + if ((val->intval == RUNNING_STATUS_NORMAL) || + (val->intval == RUNNING_STATUS_OFF_CHARGE) || + (val->intval == RUNNING_STATUS_SHUTDOWN)) + chg->running_status = val->intval; + else + rc = -EINVAL; + break; +#endif default: rc = -EINVAL; } @@ -1159,6 +1452,9 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + case POWER_SUPPLY_PROP_CHARGING_ENABLED: +#endif case POWER_SUPPLY_PROP_INPUT_SUSPEND: case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: case POWER_SUPPLY_PROP_CAPACITY: @@ -1168,6 +1464,17 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + case POWER_SUPPLY_PROP_SMART_CHARGING_ACTIVATION: + case POWER_SUPPLY_PROP_SMART_CHARGING_INTERRUPTION: + case POWER_SUPPLY_PROP_LRC_ENABLE: + case POWER_SUPPLY_PROP_LRC_SOCMAX: + case POWER_SUPPLY_PROP_LRC_SOCMIN: + case POWER_SUPPLY_PROP_LRC_NOT_STARTUP: + case POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_INT_CLD: + case POWER_SUPPLY_PROP_RUNNING_STATUS: +#endif return 1; default: break; @@ -1213,6 +1520,10 @@ static struct regulator_ops smb2_vbus_reg_ops = { .enable = smblib_vbus_regulator_enable, .disable = smblib_vbus_regulator_disable, .is_enabled = smblib_vbus_regulator_is_enabled, +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + .register_ocp_notification + = somc_usb_otg_regulator_register_ocp_notification, +#endif }; static int smb2_init_vbus_regulator(struct smb2 *chip) @@ -1470,7 +1781,12 @@ static int smb2_init_hw(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; int rc; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + u8 stat; +#endif +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) u8 stat, val; +#endif if (chip->dt.no_battery) chg->fake_capacity = 50; @@ -1519,6 +1835,14 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chg->product_icl_ua < 0) + chg->product_icl_ua = chg->default_icl_ua; + + if (chg->high_voltage_icl_ua < 0) + chg->high_voltage_icl_ua = chg->default_icl_ua; +#endif + chg->boost_threshold_ua = chip->dt.boost_threshold_ua; rc = smblib_read(chg, APSD_RESULT_STATUS_REG, &stat); @@ -1536,6 +1860,10 @@ static int smb2_init_hw(struct smb2 *chip) } /* votes must be cast before configuring software control */ +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 0); + vote(chg->usb_icl_votable, PRODUCT_VOTER, true, chg->product_icl_ua); +#endif /* vote 0mA on usb_icl for non battery platforms */ vote(chg->usb_icl_votable, DEFAULT_VOTER, chip->dt.no_battery, 0); @@ -1561,7 +1889,16 @@ static int smb2_init_hw(struct smb2 *chip) chg->micro_usb_mode, 0); vote(chg->hvdcp_enable_votable, MICRO_USB_VOTER, chg->micro_usb_mode, 0); - +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* ICL is controlled by SW always */ + rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG, + AICL_USE_SW_AFTER_APSD, AICL_USE_SW_AFTER_APSD); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't set AICL_USE_SW_AFTER_APSD rc=%d\n", rc); + return rc; + } +#endif /* * AICL configuration: * start from min and AICL ADC disable @@ -1573,7 +1910,127 @@ static int smb2_init_hw(struct smb2 *chip) dev_err(chg->dev, "Couldn't configure AICL rc=%d\n", rc); return rc; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* Vote thermal fcc at Lv0 */ + smblib_somc_thermal_icl_change(chg); +#endif + +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* disable watchdog timer */ + rc = smblib_write(chg, WD_CFG_REG, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't wdog cfg rc=%d\n", rc); + return rc; + } + + /* just in case, allow charging when bite watchdog timer expires */ + rc = smblib_masked_write(chg, SNARL_BARK_BITE_WD_CFG_REG, + BITE_WDOG_DISABLE_CHARGING_CFG_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't enable wdog charging rc=%d\n", rc); + return rc; + } + + /* set adapter allowance to default value */ + rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, + USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set adapter allow cfg rc=%d\n", rc); + return rc; + } + + /* add a setting to prevent the rise of output voltage over 9V */ + rc = smblib_masked_write(chg, HVDCP_PULSE_COUNT_MAX_CFG_REG, + HVDCP_PULSE_COUNT_MAX_QC3P0 | + HVDCP_PULSE_COUNT_MAX_QC2P0, + QC3P0_MAX_PULSE_9V | QC2P0_MAX_PULSE_9V); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set hvdcp pulse cfg rc=%d\n", rc); + return rc; + } + + /* Disable INOV controller */ + rc = smblib_masked_write(chg, THERMREG_SRC_CFG_REG, + THERMREG_SKIN_ADC_SRC_EN_BIT | + THERMREG_DIE_ADC_SRC_EN_BIT | + THERMREG_DIE_CMP_SRC_EN_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't disable INOV rc=%d\n", rc); + return rc; + } + + rc = smblib_masked_write(chg, USBIN_5V_AICL_THRESHOLD_CFG_REG, + USBIN_5V_AICL_THRESHOLD_CFG_MASK, + USBIN_5V_AICL_THRESHOLD_4P5V); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set 5v aicl thresh rc=%d\n", rc); + return rc; + } + + rc = smblib_masked_write(chg, USBIN_9V_AICL_THRESHOLD_CFG_REG, + USBIN_9V_AICL_THRESHOLD_CFG_MASK, + USBIN_9V_AICL_THRESHOLD_7P6V); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set 9v aicl thresh rc=%d\n", rc); + return rc; + } + + rc = smblib_masked_write(chg, USBIN_CONT_AICL_THRESHOLD_CFG_REG, + USBIN_CONT_AICL_THRESHOLD_CFG_MASK, + USBIN_CONT_AICL_THRESHOLD_4P5V); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set cont aicl thresh rc=%d\n", rc); + return rc; + } + rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG, + USBIN_IN_COLLAPSE_GF_SEL, + USBIN_IN_COLLAPSE_GF_30US); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set Glitch Filter rc=%d\n", rc); + return rc; + } + + /* Enable JEITA Hard Limit under any settings */ + rc = smblib_masked_write(chg, JEITA_EN_CFG_REG, + JEITA_EN_HARDLIMIT_BIT, + JEITA_EN_HARDLIMIT_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't enable JEITA Hard Limit rc=%d\n", + rc); + return rc; + } + + if (chg->jeita_sw_ctl_en) { + /* Disable FVCOMP and CCCOMP with warm and cool */ + rc = smblib_masked_write(chg, JEITA_EN_CFG_REG, + JEITA_EN_HOT_SL_FCV_BIT | + JEITA_EN_COLD_SL_FCV_BIT | + JEITA_EN_HOT_SL_CCC_BIT | + JEITA_EN_COLD_SL_CCC_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't disable JEITA comp rc=%d\n", + rc); + return rc; + } + + /* Disable FVCOMP CFG */ + rc = smblib_write(chg, JEITA_FVCOMP_CFG_REG, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't disable fvcomp cfg rc=%d\n", + rc); + return rc; + } + + /* Disable CCCOMP CFG */ + rc = smblib_write(chg, JEITA_CCCOMP_CFG_REG, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't disable cc comp rc=%d\n", + rc); + return rc; + } + } +#endif /* Configure charge enable for software control; active high */ rc = smblib_masked_write(chg, CHGR_CFG2_REG, CHG_EN_POLARITY_BIT | @@ -1618,6 +2075,7 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) val = (ilog2(chip->dt.wd_bark_time / 16) << BARK_WDOG_TIMEOUT_SHIFT) & BARK_WDOG_TIMEOUT_MASK; val |= BITE_WDOG_TIMEOUT_8S; @@ -1641,6 +2099,7 @@ static int smb2_init_hw(struct smb2 *chip) pr_err("Couldn't configue WD config rc=%d\n", rc); return rc; } +#endif /* configure wipower watts */ rc = smb2_config_wipower_input_power(chip, chip->dt.wipower_max_uw); @@ -1716,6 +2175,7 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) switch (chip->dt.chg_inhibit_thr_mv) { case 50: rc = smblib_masked_write(chg, CHARGE_INHIBIT_THRESHOLD_CFG_REG, @@ -1749,7 +2209,28 @@ static int smb2_init_hw(struct smb2 *chip) rc); return rc; } +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + rc = smblib_masked_write(chg, CHARGE_INHIBIT_THRESHOLD_CFG_REG, + CHARGE_INHIBIT_THRESHOLD_MASK, + CHARGE_INHIBIT_THRESHOLD_50MV); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure charge inhibit cfg rc=%d\n", + rc); + return rc; + } + + rc = smblib_masked_write(chg, CHGR_CFG2_REG, + CHARGER_INHIBIT_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure charge inhibit threshold rc=%d\n", + rc); + return rc; + } +#endif if (chip->dt.auto_recharge_soc) { rc = smblib_masked_write(chg, FG_UPDATE_CFG_2_SEL_REG, SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT | @@ -2046,7 +2527,12 @@ static struct smb_irq_info smb2_irqs[] = { }, [AICL_DONE_IRQ] = { .name = "aicl-done", +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) .handler = smblib_handle_debug, +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + .handler = smblib_handle_aicl_done, +#endif }, [HIGH_DUTY_CYCLE_IRQ] = { .name = "high-duty-cycle", @@ -2244,6 +2730,547 @@ static void smb2_create_debugfs(struct smb2 *chip) #endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +/***************************** + * somc sysfs implementation * + *****************************/ +enum smb2_somc_sysfs { + ATTR_CHGR_BATTERY_CHARGER_STATUS_1 = 0, + ATTR_CHGR_BATTERY_CHARGER_STATUS_2, + ATTR_CHGR_BATTERY_CHARGER_STATUS_3, + ATTR_CHGR_BATTERY_CHARGER_STATUS_4, + ATTR_CHGR_BATTERY_CHARGER_STATUS_5, + ATTR_CHGR_BATTERY_CHARGER_STATUS_6, + ATTR_CHGR_BATTERY_CHARGER_STATUS_7, + ATTR_CHGR_BATTERY_CHARGER_STATUS_8, + ATTR_CHGR_INT_RT_STS, + ATTR_CHGR_FAST_CHARGE_CURRENT_CFG, + ATTR_CHGR_FLOAT_VOLTAGE_CFG, + ATTR_BATIF_INT_RT_STS, + ATTR_USB_USBIN_INPUT_STATUS, + ATTR_USB_APSD_STATUS, + ATTR_USB_APSD_RESULT_STATUS, + ATTR_USB_QC_CHANGE_STATUS, + ATTR_USB_QC_PULSE_COUNT_STATUS, + ATTR_USB_TYPE_C_STATUS_1, + ATTR_USB_TYPE_C_STATUS_2, + ATTR_USB_TYPE_C_STATUS_3, + ATTR_USB_TYPE_C_STATUS_4, + ATTR_USB_TYPE_C_STATUS_5, + ATTR_USB_INT_RT_STS, + ATTR_USB_CMD_IL, + ATTR_USB_USBIN_CURRENT_LIMIT_CFG, + ATTR_MISC_TEMP_RANGE_STATUS, + ATTR_MISC_ICL_STATUS, + ATTR_MISC_ADAPTER_5V_ICL_STATUS, + ATTR_MISC_ADAPTER_9V_ICL_STATUS, + ATTR_MISC_AICL_STATUS, + ATTR_MISC_POWER_PATH_STATUS, + ATTR_MISC_WDOG_STATUS, + ATTR_MISC_INT_RT_STS, + ATTR_USB_ICL_VOTER, + ATTR_USB_ICL_VOTER_EFFECTIVE, + ATTR_FCC_VOTER, + ATTR_FCC_VOTER_EFFECTIVE, + ATTR_CHG_DISABLE_VOTER, + ATTR_ENABLE_SHUTDOWN_AT_LOW_BATTERY, + ATTR_USB_MAX_CURRENT_HC, + ATTR_USB_MAX_CURRENT_LIMITED, + ATTR_FV_VOTER, + ATTR_FV_VOTER_EFFECTIVE, + ATTR_JEITA_AUX_THRESH_HOT, + ATTR_JEITA_AUX_THRESH_WARM, + ATTR_USBIN_ADAPTER_ALLOW_CFG, + ATTR_FAKED_STATUS, + ATTR_BATTERY_CHARGER_STATUS, +}; + +static ssize_t smb2_somc_param_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t smb2_somc_param_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static struct device_attribute smb2_somc_attrs[] = { + __ATTR(bat_chg_sts1, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(bat_chg_sts2, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(bat_chg_sts3, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(bat_chg_sts4, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(bat_chg_sts5, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(bat_chg_sts6, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(bat_chg_sts7, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(bat_chg_sts8, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(chgr_int_rt_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(fast_chg_current_cfg, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(float_voltage_cfg, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(batif_int_rt_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(usbin_input_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(apsd_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(apsd_result_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(qc_change_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(qc_pulse_count_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(type_c_sts1, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(type_c_sts2, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(type_c_sts3, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(type_c_sts4, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(type_c_sts5, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(usb_int_rt_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(cmd_il, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(usbin_current_limit_cfg, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(temp_renge_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(icl_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(adapter_5v_icl_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(adapter_9v_icl_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(aicl_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(power_path_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(wdog_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(misc_int_rt_sts, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(usb_icl_voter, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(usb_icl_voter_effective, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(fcc_voter, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(fcc_voter_effective, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(chg_disable_voter, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(enable_shutdown_at_low_battery, S_IRUGO|S_IWUSR, + smb2_somc_param_show, smb2_somc_param_store), + __ATTR(usb_max_current_hc, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(usb_max_current_limited, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(fv_voter, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(fv_voter_effective, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(jeita_aux_thresh_hot, S_IRUGO|S_IWUSR, + smb2_somc_param_show, smb2_somc_param_store), + __ATTR(jeita_aux_thresh_warm, S_IRUGO|S_IWUSR, + smb2_somc_param_show, smb2_somc_param_store), + __ATTR(usbin_adapter_allow_cfg, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(faked_status, S_IRUGO, smb2_somc_param_show, NULL), + __ATTR(battery_charger_status, S_IRUGO, smb2_somc_param_show, NULL), +}; + +static ssize_t smb2_somc_param_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct smb2 *chip = dev_get_drvdata(dev); + struct smb_charger *chg = &chip->chg; + ssize_t size = 0; + const ptrdiff_t off = attr - smb2_somc_attrs; + int ret = 0; + u8 reg; + + switch (off) { + case ATTR_CHGR_BATTERY_CHARGER_STATUS_1: + ret = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, ®); + if (ret) + dev_err(dev, + "Can't read BATTERY_CHARGER_STATUS_1: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_CHGR_BATTERY_CHARGER_STATUS_2: + ret = smblib_read(chg, + BATTERY_CHARGER_STATUS_2_REG, ®); + if (ret) + dev_err(dev, + "Can't read BATTERY_CHARGER_STATUS_2: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_CHGR_BATTERY_CHARGER_STATUS_3: + ret = smblib_read(chg, BATTERY_CHARGER_STATUS_3_REG, ®); + if (ret) + dev_err(dev, + "Can't read BATTERY_CHARGER_STATUS_3: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_CHGR_BATTERY_CHARGER_STATUS_4: + ret = smblib_read(chg, BATTERY_CHARGER_STATUS_4_REG, ®); + if (ret) + dev_err(dev, + "Can't read BATTERY_CHARGER_STATUS_4: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_CHGR_BATTERY_CHARGER_STATUS_5: + ret = smblib_read(chg, BATTERY_CHARGER_STATUS_5_REG, ®); + if (ret) + dev_err(dev, + "Can't read BATTERY_CHARGER_STATUS_5: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_CHGR_BATTERY_CHARGER_STATUS_6: + ret = smblib_read(chg, BATTERY_CHARGER_STATUS_6_REG, ®); + if (ret) + dev_err(dev, + "Can't read BATTERY_CHARGER_STATUS_6: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_CHGR_BATTERY_CHARGER_STATUS_7: + ret = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, ®); + if (ret) + dev_err(dev, + "Can't read BATTERY_CHARGER_STATUS_7: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_CHGR_BATTERY_CHARGER_STATUS_8: + ret = smblib_read(chg, BATTERY_CHARGER_STATUS_8_REG, ®); + if (ret) + dev_err(dev, + "Can't read BATTERY_CHARGER_STATUS_8: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_CHGR_INT_RT_STS: + ret = smblib_read(chg, CHGR_INT_RT_STS_REG, ®); + if (ret) + dev_err(dev, + "Can't read CHGR_INT_RT_STS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_CHGR_FAST_CHARGE_CURRENT_CFG: + ret = smblib_read(chg, FAST_CHARGE_CURRENT_CFG_REG, ®); + if (ret) + dev_err(dev, + "Can't read FAST_CHARGE_CURRENT_CFG: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_CHGR_FLOAT_VOLTAGE_CFG: + ret = smblib_read(chg, FLOAT_VOLTAGE_CFG_REG, ®); + if (ret) + dev_err(dev, + "Can't read FLOAT_VOLTAGE_CFG: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_BATIF_INT_RT_STS: + ret = smblib_read(chg, BATIF_INT_RT_STS_REG, ®); + if (ret) + dev_err(dev, + "Can't read BATIF_INT_RT_STS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_USBIN_INPUT_STATUS: + ret = smblib_read(chg, USBIN_INPUT_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read USBIN_INPUT_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_APSD_STATUS: + ret = smblib_read(chg, APSD_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read APSD_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_APSD_RESULT_STATUS: + ret = smblib_read(chg, APSD_RESULT_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read APSD_RESULT_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_QC_CHANGE_STATUS: + ret = smblib_read(chg, QC_CHANGE_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read QC_CHANGE_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_QC_PULSE_COUNT_STATUS: + ret = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read QC_PULSE_COUNT_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_TYPE_C_STATUS_1: + ret = smblib_read(chg, TYPE_C_STATUS_1_REG, ®); + if (ret) + dev_err(dev, + "Can't read TYPE_C_STATUS_1: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_TYPE_C_STATUS_2: + ret = smblib_read(chg, TYPE_C_STATUS_2_REG, ®); + if (ret) + dev_err(dev, + "Can't read TYPE_C_STATUS_2: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_TYPE_C_STATUS_3: + ret = smblib_read(chg, TYPE_C_STATUS_3_REG, ®); + if (ret) + dev_err(dev, + "Can't read TYPE_C_STATUS_3: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_TYPE_C_STATUS_4: + ret = smblib_read(chg, TYPE_C_STATUS_4_REG, ®); + if (ret) + dev_err(dev, + "Can't read TYPE_C_STATUS_4: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_TYPE_C_STATUS_5: + ret = smblib_read(chg, TYPE_C_STATUS_5_REG, ®); + if (ret) + dev_err(dev, + "Can't read TYPE_C_STATUS_5: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_INT_RT_STS: + ret = smblib_read(chg, USB_INT_RT_STS_REG, ®); + if (ret) + dev_err(dev, + "Can't read USB_INT_RT_STS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_CMD_IL: + ret = smblib_read(chg, USBIN_CMD_IL_REG, ®); + if (ret) + dev_err(dev, + "Can't read USBIN_CMD_IL: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_USBIN_CURRENT_LIMIT_CFG: + ret = smblib_read(chg, USBIN_CURRENT_LIMIT_CFG_REG, ®); + if (ret) + dev_err(dev, + "Can't read USBIN_CURRENT_LIMIT_CFG: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_MISC_TEMP_RANGE_STATUS: + ret = smblib_read(chg, TEMP_RANGE_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read TEMP_RANGE_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_MISC_ICL_STATUS: + ret = smblib_read(chg, ICL_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read MISC_ICL_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_MISC_ADAPTER_5V_ICL_STATUS: + ret = smblib_read(chg, ADAPTER_5V_ICL_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read ADAPTER_5V_ICL_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_MISC_ADAPTER_9V_ICL_STATUS: + ret = smblib_read(chg, ADAPTER_9V_ICL_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read ADAPTER_9V_ICL_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_MISC_AICL_STATUS: + ret = smblib_read(chg, AICL_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read MISC_AICL_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_MISC_POWER_PATH_STATUS: + ret = smblib_read(chg, POWER_PATH_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read POWER_PATH_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_MISC_WDOG_STATUS: + ret = smblib_read(chg, WDOG_STATUS_REG, ®); + if (ret) + dev_err(dev, + "Can't read WDOG_STATUS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_MISC_INT_RT_STS: + ret = smblib_read(chg, MISC_INT_RT_STS_REG, ®); + if (ret) + dev_err(dev, + "Can't read MISC_INT_RT_STS: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_USB_ICL_VOTER: + size = somc_output_voter_param(chg->usb_icl_votable, + buf, PAGE_SIZE); + break; + case ATTR_USB_ICL_VOTER_EFFECTIVE: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + get_effective_result(chg->usb_icl_votable)); + break; + case ATTR_FCC_VOTER: + size = somc_output_voter_param(chg->fcc_votable, + buf, PAGE_SIZE); + break; + case ATTR_FCC_VOTER_EFFECTIVE: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + get_effective_result(chg->fcc_votable)); + break; + case ATTR_CHG_DISABLE_VOTER: + size = somc_output_voter_param(chg->chg_disable_votable, + buf, PAGE_SIZE); + break; + case ATTR_ENABLE_SHUTDOWN_AT_LOW_BATTERY: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + chg->low_batt_shutdown_enabled); + break; + case ATTR_USB_MAX_CURRENT_HC: + ret = smblib_read(chg, USBIN_CURRENT_LIMIT_CFG_REG, ®); + if (ret) + dev_err(dev, + "Can't read USBIN_CURRENT_LIMIT_CFG: %d\n", ret); + else + size = scnprintf(buf, PAGE_SIZE, "%d\n", reg * 25); + break; + case ATTR_USB_MAX_CURRENT_LIMITED: + ret = smblib_get_usb_max_current_limited(chg); + if (ret) + size = scnprintf(buf, PAGE_SIZE, "%d\n", ret); + break; + case ATTR_FV_VOTER: + size = somc_output_voter_param(chg->fv_votable, buf, PAGE_SIZE); + break; + case ATTR_FV_VOTER_EFFECTIVE: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + get_effective_result(chg->fv_votable)); + break; + case ATTR_JEITA_AUX_THRESH_HOT: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + chg->jeita_aux_thresh_hot); + break; + case ATTR_JEITA_AUX_THRESH_WARM: + size = scnprintf(buf, PAGE_SIZE, "%d\n", + chg->jeita_aux_thresh_warm); + break; + case ATTR_USBIN_ADAPTER_ALLOW_CFG: + ret = smblib_read(chg, USBIN_ADAPTER_ALLOW_CFG_REG, ®); + if (ret) + dev_err(dev, + "Can't read USBIN_ADAPTER_ALLOW_CFG_REG: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "0x%02X\n", reg); + break; + case ATTR_FAKED_STATUS: + size = scnprintf(buf, PAGE_SIZE, "%d\n", chg->faked_status); + break; + case ATTR_BATTERY_CHARGER_STATUS: + ret = smblib_somc_get_battery_charger_status(chg, ®); + if (ret) + dev_err(dev, "Can't read battery charger status: %d\n", + ret); + else + size = scnprintf(buf, PAGE_SIZE, "%d\n", reg); + break; + default: + size = 0; + break; + } + return size; +} + +static ssize_t smb2_somc_param_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct smb2 *chip = dev_get_drvdata(dev); + struct smb_charger *chg = &chip->chg; + const ptrdiff_t off = attr - smb2_somc_attrs; + int ret; + + switch (off) { + case ATTR_ENABLE_SHUTDOWN_AT_LOW_BATTERY: + ret = kstrtoint(buf, 10, &chg->low_batt_shutdown_enabled); + if (ret < 0) + return ret; + break; + case ATTR_JEITA_AUX_THRESH_HOT: + ret = kstrtoint(buf, 10, &chg->jeita_aux_thresh_hot); + if (ret < 0) + return ret; + break; + case ATTR_JEITA_AUX_THRESH_WARM: + ret = kstrtoint(buf, 10, &chg->jeita_aux_thresh_warm); + if (ret < 0) + return ret; + break; + default: + break; + } + return count; +} + +static int smb2_somc_create_sysfs_entries(struct device *dev) +{ + int i; + int rc = 0; + + for (i = 0; i < ARRAY_SIZE(smb2_somc_attrs); i++) { + rc = device_create_file(dev, &smb2_somc_attrs[i]); + if (rc < 0) { + dev_err(dev, "device_create_file failed rc = %d\n", rc); + goto revert; + } + } + return 0; +revert: + for (i = i - 1; i >= 0; i--) + device_remove_file(dev, &smb2_somc_attrs[i]); + return rc; +} + +static void smb2_somc_remove_sysfs_entries(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(smb2_somc_attrs); i++) + device_remove_file(dev, &smb2_somc_attrs[i]); +} + +#endif static int smb2_probe(struct platform_device *pdev) { struct smb2 *chip; @@ -2324,6 +3351,17 @@ static int smb2_probe(struct platform_device *pdev) goto cleanup; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chg->xo_clk = devm_clk_get(chg->dev, "xo"); + if (IS_ERR(chg->xo_clk)) { + rc = PTR_ERR(chg->extcon); + dev_err(chg->dev, "failed to get XO buffer handle rc=%d\n", + rc); + goto cleanup; + } + clk_set_rate(chg->xo_clk, 19200000); + +#endif rc = smb2_init_hw(chip); if (rc < 0) { pr_err("Couldn't initialize hardware rc=%d\n", rc); @@ -2381,6 +3419,20 @@ static int smb2_probe(struct platform_device *pdev) smb2_create_debugfs(chip); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + rc = smb2_somc_create_sysfs_entries(chg->dev); + if (rc < 0) { + pr_err("Couldn't create sysfs entries rc=%d\n", rc); + goto cleanup; + } + + rc = somc_usb_register(chg); + if (rc < 0) { + pr_err("somc usb register failed rc = %d\n", rc); + goto cleanup; + } +#endif + rc = smblib_get_prop_usb_present(chg, &val); if (rc < 0) { pr_err("Couldn't get usb present rc=%d\n", rc); @@ -2418,6 +3470,9 @@ static int smb2_probe(struct platform_device *pdev) cleanup: smb2_free_interrupts(chg); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + somc_usb_unregister(chg); +#endif if (chg->batt_psy) power_supply_unregister(chg->batt_psy); if (chg->usb_main_psy) @@ -2444,6 +3499,11 @@ static int smb2_remove(struct platform_device *pdev) struct smb2 *chip = platform_get_drvdata(pdev); struct smb_charger *chg = &chip->chg; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + somc_usb_unregister(chg); + smb2_somc_remove_sysfs_entries(chg->dev); + clk_put(chg->xo_clk); +#endif power_supply_unregister(chg->batt_psy); power_supply_unregister(chg->usb_psy); power_supply_unregister(chg->usb_port_psy); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 81623c65ea8e3c294f6f1eabbc1bab1abc161a4a..de3e3dccf1ba165afefa2a4f7b79942e180c0f1a 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -20,6 +25,12 @@ #include #include #include +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#include +#include +#include +#include +#endif #include "smb-lib.h" #include "smb-reg.h" #include "battery.h" @@ -108,6 +119,12 @@ static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua) int rc, cc_minus_ua; u8 stat; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chg->jeita_sw_ctl_en) { + *cc_delta_ua = 0; + return 0; + } +#endif rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", @@ -144,6 +161,42 @@ int smblib_icl_override(struct smb_charger *chg, bool override) return rc; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define USB_SWITCH_SEL_USB1 0 +#define USB_SWITCH_SEL_USB2 1 + +static void smblib_select_usb_switch(struct smb_charger *chg, int port) +{ + struct qpnp_pin_cfg pincfg; + + if (gpio_is_valid(chg->usb_switch_sel_gpio)) { + if (port == USB_SWITCH_SEL_USB1) { + if (qpnp_get_pin_config(chg->usb_switch_sel_gpio, + &pincfg)) { + pr_err("qpnp_get_pin_config is fail\n"); + return; + } + pincfg.mode = 1; /* Out */ + pincfg.output_type = 0; /* CMOS */ + pincfg.invert = 0; /* Low */ + pincfg.vin_sel = 0; /* VPH */ + pincfg.out_strength = 1; /* Low */ + pincfg.src_sel = 0; /* GPIO */ + pincfg.master_en = 1; /* Enable */ + + if (qpnp_pin_config(chg->usb_switch_sel_gpio, + &pincfg)) { + pr_err("qpnp_pin_config is fail\n"); + return; + } + } + + gpio_set_value(chg->usb_switch_sel_gpio, port); + dev_info(chg->dev, "select port=%d\n", port); + } +} +#endif + /******************** * REGISTER GETTERS * ********************/ @@ -192,6 +245,7 @@ struct apsd_result { const enum power_supply_type pst; }; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) enum { UNKNOWN, SDP, @@ -246,6 +300,87 @@ static const struct apsd_result const smblib_apsd_results[] = { .pst = POWER_SUPPLY_TYPE_USB_HVDCP_3, }, }; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +enum { + UNKNOWN, + SDP, + CDP, + DCP, + OCP, + FLOAT, + HVDCP2, + HVDCP3, + PD, + MAX_TYPES +}; + +static const struct apsd_result const smblib_apsd_results[] = { + [UNKNOWN] = { + .name = "UNKNOWN_CHARGER", + .bit = 0, + .pst = POWER_SUPPLY_TYPE_UNKNOWN + }, + [SDP] = { + .name = "USB_SDP_CHARGER", + .bit = SDP_CHARGER_BIT, + .pst = POWER_SUPPLY_TYPE_USB + }, + [CDP] = { + .name = "USB_CDP_CHARGER", + .bit = CDP_CHARGER_BIT, + .pst = POWER_SUPPLY_TYPE_USB_CDP + }, + [DCP] = { + .name = "USB_DCP_CHARGER", + .bit = DCP_CHARGER_BIT, + .pst = POWER_SUPPLY_TYPE_USB_DCP + }, + [OCP] = { + .name = "USB_PROPRIETARY_CHARGER", + .bit = OCP_CHARGER_BIT, + .pst = POWER_SUPPLY_TYPE_USB_DCP + }, + [FLOAT] = { + .name = "USB_FLOATED_CHARGER", + .bit = FLOAT_CHARGER_BIT, + .pst = POWER_SUPPLY_TYPE_USB_DCP + }, + [HVDCP2] = { + .name = "USB_HVDCP_CHARGER", + .bit = DCP_CHARGER_BIT | QC_2P0_BIT, + .pst = POWER_SUPPLY_TYPE_USB_HVDCP + }, + [HVDCP3] = { + .name = "USB_HVDCP_3_CHARGER", + .bit = DCP_CHARGER_BIT | QC_3P0_BIT, + .pst = POWER_SUPPLY_TYPE_USB_HVDCP_3, + }, + [PD] = { + .name = "USB_PD_CHARGER", + .bit = 0xff, + .pst = POWER_SUPPLY_TYPE_USB_PD, + }, +}; + +const char *smblib_somc_get_charger_type(struct smb_charger *chg) +{ + const char *charger_type = NULL; + int i; + + for (i = UNKNOWN; i < MAX_TYPES; i++) { + if (smblib_apsd_results[i].bit == + chg->usb_params.apsd_result_bit) { + charger_type = smblib_apsd_results[i].name; + break; + } + } + if (!charger_type) + charger_type = smblib_apsd_results[UNKNOWN].name; + + return charger_type; +} +#endif static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg) { @@ -260,8 +395,10 @@ static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg) } smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", apsd_stat); - if (!(apsd_stat & APSD_DTC_STATUS_DONE_BIT)) + if (!(apsd_stat & APSD_DTC_STATUS_DONE_BIT)) { + smblib_err(chg, "APSD not done yet.\n"); return result; + } rc = smblib_read(chg, APSD_RESULT_STATUS_REG, &stat); if (rc < 0) { @@ -386,6 +523,16 @@ int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend) } } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* WA for outbreak of icl charge irq during suspneded */ + rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG, + USBIN_AICL_RERUN_EN_BIT, + suspend ? 0 : USBIN_AICL_RERUN_EN_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't set AICL rerun en bit rc=%d\n", rc); + return rc; + } +#endif rc = smblib_masked_write(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0); if (rc < 0) @@ -566,6 +713,12 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) chg->real_charger_type = apsd_result->pst; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chg->pd_active) + chg->usb_params.apsd_result_bit = 0xff; + else + chg->usb_params.apsd_result_bit = apsd_result->bit; +#endif smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n", apsd_result->name, chg->pd_active); @@ -734,6 +887,9 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) { union power_supply_propval val; int rc; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + const struct apsd_result *apsd_result; +#endif rc = smblib_get_prop_usb_present(chg, &val); if (rc < 0) { @@ -744,6 +900,36 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) if (!val.intval) return 0; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + apsd_result = smblib_get_apsd_result(chg); + if (apsd_result->pst == POWER_SUPPLY_TYPE_USB || + apsd_result->pst == POWER_SUPPLY_TYPE_USB_CDP) { + goto skip_typec_reconnection; + } + smblib_dbg(chg, PR_SOMC, "start type-c reconnection (%s)\n", + apsd_result->name); + + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, + TYPEC_DISABLE_CMD_BIT); + if (rc < 0) + smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc); + + /* wait for the adapter to turn off VBUS */ + msleep(400); + + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc); + + /* wait for type-c detection to complete */ + msleep(200); + smblib_dbg(chg, PR_SOMC, "complete type-c reconnection\n"); +skip_typec_reconnection: +#endif rc = smblib_request_dpdm(chg, true); if (rc < 0) smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); @@ -811,7 +997,15 @@ static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count) #define USBIN_150MA 150000 #define USBIN_500MA 500000 #define USBIN_900MA 900000 - +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define USBIN_50MA 50000 +#define USBIN_75MA 75000 +#define USBIN_1000MA 1000000 +#define USBIN_1500MA 1500000 +#define USBIN_3000MA 3000000 +#endif + +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) static int set_sdp_current(struct smb_charger *chg, int icl_ua) { int rc; @@ -865,6 +1059,7 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) return rc; } +#endif static int get_sdp_current(struct smb_charger *chg, int *icl_ua) { @@ -892,6 +1087,12 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) { int rc = 0; bool override; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + u8 reg; + bool icl_setting_again = false; + + smblib_dbg(chg, PR_SOMC, "set ICL to %duA\n", icl_ua); +#endif /* suspend and return if 25mA or less is requested */ if (icl_ua < USBIN_25MA) @@ -901,6 +1102,7 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) goto override_suspend_config; /* configure current */ +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) { rc = set_sdp_current(chg, icl_ua); @@ -916,10 +1118,29 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) goto enable_icl_changed_interrupt; } } +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + rc = smblib_read(chg, USBIN_CMD_IL_REG, ®); + if (rc < 0) { + smblib_err(chg, "Couldn't read USBIN_CMD_IL rc=%d\n", rc); + goto enable_icl_changed_interrupt; + } + + if ((reg & USBIN_SUSPEND_BIT) && + (icl_ua > USBIN_75MA)) { + icl_setting_again = true; + rc = smblib_set_charge_param(chg, &chg->param.usb_icl, + USBIN_75MA); + } else { + rc = smblib_set_charge_param(chg, &chg->param.usb_icl, + icl_ua); + } +#endif override_suspend_config: /* determine if override needs to be enforced */ override = true; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (icl_ua == INT_MAX) { /* remove override if no voters - hw defaults is desired */ override = false; @@ -935,6 +1156,7 @@ override_suspend_config: */ override = false; } +#endif /* enforce override */ rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, @@ -952,6 +1174,16 @@ override_suspend_config: smblib_err(chg, "Couldn't resume input rc=%d\n", rc); goto enable_icl_changed_interrupt; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (icl_setting_again) { + rc = smblib_set_charge_param(chg, &chg->param.usb_icl, + icl_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc); + goto enable_icl_changed_interrupt; + } + } +#endif enable_icl_changed_interrupt: return rc; @@ -1120,8 +1352,20 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable, } /* re-run APSD if HVDCP was detected */ +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (stat & QC_CHARGER_BIT) smblib_rerun_apsd(chg); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (hvdcp_enable) { + vote(chg->usb_icl_votable, HIGH_VOLTAGE_VOTER, true, + chg->high_voltage_icl_ua); + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); + smblib_dbg(chg, PR_SOMC, + "HVDCP has been enabled, rerun APSD\n"); + smblib_rerun_apsd(chg); + } +#endif return 0; } @@ -1232,9 +1476,82 @@ static int smblib_typec_irq_disable_vote_callback(struct votable *votable, return 0; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +/***************** + * USB CALLBACKS * + *****************/ +int somc_usb_register(struct smb_charger *chg) +{ + struct usb_somc_params *params = &chg->usb_params; + struct somc_usb_ocp *ocp = ¶ms->ocp; + + memset(&ocp->notification, 0, sizeof(ocp->notification)); + spin_lock_init(&ocp->lock); + + smblib_dbg(chg, PR_SOMC, "somc usb register success\n"); + return 0; +} + +void somc_usb_unregister(struct smb_charger *chg) +{ + ; +} + +#endif + /******************* * VCONN REGULATOR * * *****************/ +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +const char *rdev_get_name(struct regulator_dev *rdev) +{ + if (rdev->constraints && rdev->constraints->name) + return rdev->constraints->name; + else if (rdev->desc->name) + return rdev->desc->name; + else + return ""; +} + +int somc_usb_otg_regulator_register_ocp_notification( + struct regulator_dev *rdev, + struct regulator_ocp_notification *notification) +{ + struct smb_charger *chg = rdev_get_drvdata(rdev); + struct somc_usb_ocp *ocp = &chg->usb_params.ocp; + unsigned long flags; + + spin_lock_irqsave(&ocp->lock, flags); + if (notification) + /* register ocp notification */ + ocp->notification = *notification; + else + /* unregister ocp notification */ + memset(&ocp->notification, 0, sizeof(ocp->notification)); + spin_unlock_irqrestore(&ocp->lock, flags); + + smblib_dbg(chg, PR_SOMC, "%s: registered ocp notification(notify=%p, ctxt=%p)\n", + rdev_get_name(rdev), + ocp->notification.notify, + ocp->notification.ctxt); + + return 0; +} + +static int somc_usb_otg_regulator_ocp_notify(struct smb_charger *chg) +{ + struct somc_usb_ocp *ocp = &chg->usb_params.ocp; + unsigned long flags; + + spin_lock_irqsave(&ocp->lock, flags); + if (ocp->notification.notify) + ocp->notification.notify(ocp->notification.ctxt); + spin_unlock_irqrestore(&ocp->lock, flags); + + return 0; +} + +#endif #define MAX_OTG_SS_TRIES 2 static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev) @@ -1419,6 +1736,9 @@ static int _smblib_vbus_regulator_enable(struct regulator_dev *rdev) } smblib_dbg(chg, PR_OTG, "enabling OTG\n"); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_select_usb_switch(chg, USB_SWITCH_SEL_USB1); +#endif if (chg->wa_flags & OTG_WA) { rc = smblib_enable_otg_wa(chg); @@ -1530,6 +1850,52 @@ int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev) * BATT PSY GETTERS * ********************/ +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +int smblib_get_prop_charging_enabled(struct smb_charger *chg, + union power_supply_propval *val) +{ + smblib_get_prop_input_suspend(chg, val); + val->intval = val->intval ? 0 : 1; + return 0; +} + +int smblib_get_prop_charge_full_design(struct smb_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_CHARGE_FULL_DESIGN, val); + if (rc < 0) { + smblib_err(chg, "Couldn't get prop charge_full_design rc=%d\n", rc); + return rc; + } + + return 0; +} + +int smblib_get_prop_charge_full(struct smb_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_CHARGE_FULL, val); + if (rc < 0) { + smblib_err(chg, "Couldn't get prop charge_full rc=%d\n", rc); + return rc; + } + + return 0; +} + +#endif int smblib_get_prop_input_suspend(struct smb_charger *chg, union power_supply_propval *val) { @@ -1562,17 +1928,53 @@ int smblib_get_prop_batt_capacity(struct smb_charger *chg, { int rc = -EINVAL; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (chg->fake_capacity >= 0) { val->intval = chg->fake_capacity; return 0; } +#endif if (chg->bms_psy) rc = power_supply_get_property(chg->bms_psy, POWER_SUPPLY_PROP_CAPACITY, val); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (rc < 0) { + smblib_err(chg, "Couldn't get prop capacity rc=%d\n", rc); + } else { + if (val->intval <= 0 && chg->low_batt_shutdown_enabled) { + smblib_somc_set_low_batt_suspend_en(chg); + chg->fake_capacity = -EINVAL; + } + + if (chg->fake_capacity >= 0) + val->intval = chg->fake_capacity; + } + + smblib_somc_lrc_check(chg); + val->intval = smblib_somc_lrc_get_capacity(chg, val->intval); +#endif return rc; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +enum { + TEMP_CONDITION_DEFAULT = 0, + TEMP_CONDITION_COLD, + TEMP_CONDITION_COOL, + TEMP_CONDITION_NORMAL, + TEMP_CONDITION_WARM, + TEMP_CONDITION_HOT, +}; +enum { + FAKED_STATUS_NONE = 0, + FAKED_STATUS_TYPEC_EN_DIS_ACTIVE, + FAKED_STATUS_SMART, + FAKED_STATUS_DURING_WARM_FULL, + FAKED_STATUS_RB_WA, + FAKED_STATUS_WA_FOR_WARM_FULL, +}; +#endif int smblib_get_prop_batt_status(struct smb_charger *chg, union power_supply_propval *val) { @@ -1580,6 +1982,12 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, bool usb_online, dc_online, qnovo_en; u8 stat, pt_en_cmd; int rc; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int i = 0; + int votabled, smart_votabled = 0, other_votabled = 0; + char *clients[NUM_MAX_CLIENTS]; + int num_clients = 0; +#endif rc = smblib_get_prop_usb_online(chg, &pval); if (rc < 0) { @@ -1615,6 +2023,9 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chg->faked_status = FAKED_STATUS_NONE; +#endif return rc; } @@ -1638,6 +2049,65 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, break; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chg->typec_en_dis_active || chg->duration_fake_charging) { + smblib_dbg(chg, PR_MISC, + "Fake charging during type-c en/dis is active (%d)\n", + val->intval); + val->intval = chg->status_before_typec_en_dis_active; + chg->faked_status = FAKED_STATUS_TYPEC_EN_DIS_ACTIVE; + return 0; + } + + num_clients = somc_get_vote_clients(chg->chg_disable_votable, clients); + for (i = 0; i < num_clients; i++) { + votabled = get_client_vote(chg->chg_disable_votable, + clients[i]); + if (strcmp(clients[i], BATTCHG_SMART_EN_VOTER) == 0) + smart_votabled = votabled; + else if (!other_votabled) + other_votabled = votabled; + } + if (smart_votabled && !other_votabled) { + smblib_dbg(chg, PR_MISC, + "Fake charging due to smart charge (%d)\n", + val->intval); + val->intval = POWER_SUPPLY_STATUS_CHARGING; + chg->faked_status = FAKED_STATUS_SMART; + return 0; + } + + if (val->intval == POWER_SUPPLY_STATUS_FULL && + chg->jeita_sw_ctl_en && + chg->jeita_synth_temp_condition == TEMP_CONDITION_WARM) { + smblib_dbg(chg, PR_MISC, + "Fake charging during Warm and FULL (%d)\n", + val->intval); + val->intval = POWER_SUPPLY_STATUS_CHARGING; + chg->faked_status = FAKED_STATUS_DURING_WARM_FULL; + return 0; + } + + if (chg->jeita_rb_warm_hi_vbatt_en && + !get_effective_result(chg->chg_disable_votable)) { + smblib_dbg(chg, PR_MISC, + "Fake charging due to Reverse Boost WA (%d)\n", + val->intval); + val->intval = POWER_SUPPLY_STATUS_CHARGING; + chg->faked_status = FAKED_STATUS_RB_WA; + return 0; + } + + if (chg->jeita_keep_fake_charging) { + smblib_dbg(chg, PR_MISC, + "Fake charging during WA for Warm/FULL (%d)\n", + val->intval); + val->intval = POWER_SUPPLY_STATUS_CHARGING; + chg->faked_status = FAKED_STATUS_WA_FOR_WARM_FULL; + return 0; + } + chg->faked_status = FAKED_STATUS_NONE; +#endif if (val->intval != POWER_SUPPLY_STATUS_CHARGING) return 0; @@ -1716,7 +2186,14 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, smblib_dbg(chg, PR_REGISTER, "BATTERY_CHARGER_STATUS_2 = 0x%02x\n", stat); +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT) { +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT && + (!chg->jeita_sw_ctl_en || + chg->jeita_synth_temp_condition != TEMP_CONDITION_WARM)) { +#endif rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); if (!rc) { @@ -1734,6 +2211,7 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, } } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT) val->intval = POWER_SUPPLY_HEALTH_COLD; else if (stat & BAT_TEMP_STATUS_TOO_HOT_BIT) @@ -1744,6 +2222,39 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, val->intval = POWER_SUPPLY_HEALTH_WARM; else val->intval = POWER_SUPPLY_HEALTH_GOOD; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chg->jeita_sw_ctl_en) { + switch (chg->jeita_synth_temp_condition) { + case TEMP_CONDITION_HOT: + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + break; + case TEMP_CONDITION_WARM: + val->intval = POWER_SUPPLY_HEALTH_WARM; + break; + case TEMP_CONDITION_COOL: + val->intval = POWER_SUPPLY_HEALTH_COOL; + break; + case TEMP_CONDITION_COLD: + val->intval = POWER_SUPPLY_HEALTH_COLD; + break; + default: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + } + } else { + if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT) + val->intval = POWER_SUPPLY_HEALTH_COLD; + else if (stat & BAT_TEMP_STATUS_TOO_HOT_BIT) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (stat & BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT) + val->intval = POWER_SUPPLY_HEALTH_COOL; + else if (stat & BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT) + val->intval = POWER_SUPPLY_HEALTH_WARM; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + } +#endif done: return rc; @@ -1829,6 +2340,19 @@ int smblib_get_prop_from_bms(struct smb_charger *chg, * BATTERY PSY SETTERS * ***********************/ +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +int smblib_set_prop_charging_enabled(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + union power_supply_propval tmp = *val; + + tmp.intval = tmp.intval ? 0 : 1; + rc = smblib_set_prop_input_suspend(chg, &tmp); + return rc; +} + +#endif int smblib_set_prop_input_suspend(struct smb_charger *chg, const union power_supply_propval *val) { @@ -1866,6 +2390,7 @@ int smblib_set_prop_batt_capacity(struct smb_charger *chg, int smblib_set_prop_system_temp_level(struct smb_charger *chg, const union power_supply_propval *val) { +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (val->intval < 0) return -EINVAL; @@ -1890,6 +2415,18 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg, vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, true, chg->thermal_mitigation[chg->system_temp_level]); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_dbg(chg, PR_MISC, "Changed Thernal LV from %d to %d\n", + chg->system_temp_level, val->intval); + if (val->intval < 0) + return -EINVAL; + + chg->system_temp_level = val->intval; + + smblib_somc_thermal_fcc_change(chg); + smblib_somc_thermal_icl_change(chg); +#endif return 0; } @@ -2064,6 +2601,7 @@ int smblib_dp_dm(struct smb_charger *chg, int val) int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable) { +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) int rc; u8 mask; @@ -2082,6 +2620,7 @@ int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable) rc); return rc; } +#endif return 0; } @@ -2171,17 +2710,45 @@ int smblib_get_prop_usb_present(struct smb_charger *chg, return 0; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define SDP_CURRENT_SUSPENDED 2000 +#endif int smblib_get_prop_usb_online(struct smb_charger *chg, union power_supply_propval *val) { int rc = 0; u8 stat; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int org_online; + int sdp_current; + + if (get_client_vote(chg->usb_icl_votable, LOW_BATT_EN_VOTER) == 0) { + val->intval = false; + return rc; + } + if (get_client_vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER) == 0) { + val->intval = false; + return rc; + } + sdp_current = get_client_vote(chg->usb_icl_votable, USB_PSY_VOTER); + if (sdp_current >= 0 && sdp_current <= SDP_CURRENT_SUSPENDED) { + val->intval = false; + return rc; + } +#endif if (get_client_vote_locked(chg->usb_icl_votable, USER_VOTER) == 0) { val->intval = false; return rc; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chg->typec_en_dis_active) { + val->intval = true; + return rc; + } + +#endif rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", @@ -2193,6 +2760,18 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, val->intval = (stat & USE_USBIN_BIT) && (stat & VALID_INPUT_POWER_SOURCE_STS_BIT); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + org_online = val->intval; + rc = smblib_get_prop_usb_present(chg, val); + if (rc < 0) { + smblib_err(chg, "Couldn't get present rc=%d\n", rc); + return rc; + } + if (!!val->intval != !!org_online) + smblib_dbg(chg, PR_MISC, + "online mismatch: ret=%d, POWER_PATH_STATUS=0x%02x\n", + val->intval, stat); +#endif return rc; } @@ -2206,7 +2785,12 @@ int smblib_get_prop_usb_voltage_max(struct smb_charger *chg, if (chg->smb_version == PM660_SUBTYPE) val->intval = MICRO_9V; else +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) val->intval = MICRO_12V; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + val->intval = MICRO_9V; +#endif break; default: val->intval = MICRO_5V; @@ -2248,14 +2832,34 @@ int smblib_get_prop_usb_current_now(struct smb_charger *chg, return iio_read_channel_processed(chg->iio.usbin_i_chan, &val->intval); } -int smblib_get_prop_charger_temp(struct smb_charger *chg, - union power_supply_propval *val) +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +int smblib_get_prop_skin_temp(struct smb_charger *chg, + union power_supply_propval *val) { - int rc; + int rc = 0; - if (!chg->iio.temp_chan || - PTR_ERR(chg->iio.temp_chan) == -EPROBE_DEFER) - chg->iio.temp_chan = iio_channel_get(chg->dev, "charger_temp"); + if (!chg->iio.skin_temp_chan || + PTR_ERR(chg->iio.skin_temp_chan) == -EPROBE_DEFER) + chg->iio.skin_temp_chan = iio_channel_get(chg->dev, + "skin_temp"); + + if (IS_ERR(chg->iio.skin_temp_chan)) + return PTR_ERR(chg->iio.skin_temp_chan); + + rc = iio_read_channel_processed(chg->iio.skin_temp_chan, &val->intval); + val->intval /= 100; + return rc; +} + +#endif +int smblib_get_prop_charger_temp(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->iio.temp_chan || + PTR_ERR(chg->iio.temp_chan) == -EPROBE_DEFER) + chg->iio.temp_chan = iio_channel_get(chg->dev, "charger_temp"); if (IS_ERR(chg->iio.temp_chan)) return PTR_ERR(chg->iio.temp_chan); @@ -2449,6 +3053,24 @@ int smblib_get_pe_start(struct smb_charger *chg, return 0; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +int smblib_get_prop_legacy_cable_status(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + u8 reg; + + rc = smblib_read(chg, TYPE_C_STATUS_5_REG, ®); + if (rc < 0) { + smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc); + return rc; + } + + val->intval = (reg & TYPEC_LEGACY_CABLE_STATUS_BIT) ? 1 : 0; + return 0; +} + +#endif int smblib_get_prop_die_health(struct smb_charger *chg, union power_supply_propval *val) { @@ -2490,6 +3112,7 @@ int smblib_get_prop_die_health(struct smb_charger *chg, #define TYPEC_DEFAULT_CURRENT_UA 900000 #define TYPEC_MEDIUM_CURRENT_UA 1500000 #define TYPEC_HIGH_CURRENT_UA 3000000 +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode) { int rp_ua; @@ -2507,6 +3130,7 @@ static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode) return rp_ua; } +#endif /******************* * USB PSY SETTERS * @@ -2528,6 +3152,7 @@ int smblib_set_prop_pd_current_max(struct smb_charger *chg, static int smblib_handle_usb_current(struct smb_charger *chg, int usb_current) { +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) int rc = 0, rp_ua, typec_mode; if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) { @@ -2562,6 +3187,30 @@ static int smblib_handle_usb_current(struct smb_charger *chg, rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, true, usb_current); } +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int rc = 0; + + if (usb_current == -ETIMEDOUT) + return 0; + + switch (usb_current) { + case USBIN_100MA: + case USBIN_150MA: + case USBIN_500MA: + usb_current -= USBIN_25MA; + break; + case USBIN_900MA: + usb_current -= USBIN_50MA; + break; + default: + break; + } + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, true, usb_current); + + /* Changing ICL by this function may change online */ + power_supply_changed(chg->usb_psy); +#endif return rc; } @@ -2616,6 +3265,9 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, power_role = 0; break; case POWER_SUPPLY_TYPEC_PR_SINK: +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + case POWER_SUPPLY_TYPEC_PR_SINK_DELAY: +#endif power_role = UFP_EN_CMD_BIT; break; case POWER_SUPPLY_TYPEC_PR_SOURCE: @@ -2644,6 +3296,12 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, } } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (val->intval == POWER_SUPPLY_TYPEC_PR_SINK_DELAY) { + smblib_dbg(chg, PR_SOMC, "power role set to SINK, delay 120ms\n"); + msleep(120); + } +#endif rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_POWER_ROLE_CMD_MASK, power_role); if (rc < 0) { @@ -2689,6 +3347,15 @@ int smblib_set_prop_pd_voltage_max(struct smb_charger *chg, } chg->voltage_max_uv = max_uv; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chg->voltage_max_uv > MICRO_5V) + vote(chg->usb_icl_votable, HIGH_VOLTAGE_VOTER, + true, chg->high_voltage_icl_ua); + else + vote(chg->usb_icl_votable, HIGH_VOLTAGE_VOTER, + false, 0); + smblib_somc_thermal_icl_change(chg); +#endif return rc; } @@ -2782,6 +3449,12 @@ static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active) } smblib_update_usb_type(chg); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_somc_thermal_icl_change(chg); + + smblib_dbg(chg, PR_SOMC, "%s PD\n", + chg->pd_active ? "enabling" : "disabling"); +#endif power_supply_changed(chg->usb_psy); return rc; } @@ -2968,6 +3641,11 @@ static int smblib_recover_from_soft_jeita(struct smb_charger *chg) u8 stat_1, stat_2; int rc; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chg->jeita_sw_ctl_en) + return 0; + +#endif rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat_1); if (rc < 0) { smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", @@ -3191,6 +3869,9 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data) } stat = stat & BATTERY_CHARGER_STATUS_MASK; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_dbg(chg, PR_SOMC, "current charge state: %d\n", stat); +#endif power_supply_changed(chg->batt_psy); return IRQ_HANDLED; } @@ -3208,6 +3889,13 @@ irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data) return IRQ_HANDLED; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_dbg(chg, PR_SOMC, "IRQ: %s\n", irq_data->name); + if (chg->jeita_sw_ctl_en) { + cancel_delayed_work_sync(&chg->jeita_work); + schedule_delayed_work(&chg->jeita_work, msecs_to_jiffies(0)); + } +#endif rerun_election(chg->fcc_votable); power_supply_changed(chg->batt_psy); return IRQ_HANDLED; @@ -3238,8 +3926,20 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data) struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; struct storm_watch *wdata; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + const struct apsd_result *apsd_result = smblib_update_usb_type(chg); +#endif smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* ESD workaround */ + if (!chg->typec_en_dis_active && (apsd_result->bit & QC_2P0_BIT)) { + smblib_dbg(chg, PR_SOMC, "rerun APSD for ESD WA\n"); + smblib_masked_write(chg, CMD_APSD_REG, APSD_RERUN_BIT, + APSD_RERUN_BIT); + return IRQ_HANDLED; + } +#endif if (!chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data) return IRQ_HANDLED; @@ -3301,6 +4001,10 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) } #define PL_DELAY_MS 30000 +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define REMOVAL_DELAY_MS 2000 +#define REMOVAL_WAKE_PERIOD (3 * HZ) +#endif void smblib_usb_plugin_locked(struct smb_charger *chg) { int rc; @@ -3319,7 +4023,24 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) smblib_set_opt_freq_buck(chg, vbus_rising ? chg->chg_freq.freq_5V : chg->chg_freq.freq_removal); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chg->jeita_sw_ctl_en) { + cancel_delayed_work_sync(&chg->jeita_work); + schedule_delayed_work(&chg->jeita_work, msecs_to_jiffies(0)); + } + +#endif if (vbus_rising) { +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_cc2_sink_removal_exit(chg); + + if (chg->typec_en_dis_active) { + smblib_dbg(chg, PR_SOMC, + "start fake charging by typec_en_dis_active\n"); + schedule_work(&chg->fake_charging_work); + } + smblib_select_usb_switch(chg, USB_SWITCH_SEL_USB1); +#endif rc = smblib_request_dpdm(chg, true); if (rc < 0) smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); @@ -3351,6 +4072,24 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) rc = smblib_request_dpdm(chg, false); if (rc < 0) smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_somc_lrc_check(chg); + wake_lock_timeout( + &chg->usb_removal_wakelock.lock, REMOVAL_WAKE_PERIOD); + schedule_delayed_work(&chg->usb_removal_work, + msecs_to_jiffies(REMOVAL_DELAY_MS)); + + mutex_lock(&chg->xo_lock); + if (chg->xo_holded) { + clk_disable_unprepare(chg->xo_clk); + chg->xo_holded = false; + smblib_dbg(chg, PR_SOMC, + "release xo clock due to charger dettached\n"); + } + mutex_unlock(&chg->xo_lock); + if (chg->pd_hard_reset) + smblib_cc2_sink_removal_enter(chg); +#endif } if (chg->micro_usb_mode) @@ -3367,9 +4106,14 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; mutex_lock(&chg->lock); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_somc_ctrl_inhibit(chg, false); +#endif +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (chg->pd_hard_reset) smblib_usb_plugin_hard_reset_locked(chg); else +#endif smblib_usb_plugin_locked(chg); mutex_unlock(&chg->lock); return IRQ_HANDLED; @@ -3561,7 +4305,9 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, bool rising, bool qc_charger) { +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); +#endif /* Hold off PD only until hvdcp 2.0 detection timeout */ if (rising) { @@ -3572,6 +4318,7 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, if (qc_charger) vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0); +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) /* * HVDCP detection timeout done * If adapter is not QC2.0/QC3.0 - it is a plain old DCP. @@ -3580,6 +4327,7 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, /* enforce DCP ICL if specified */ vote(chg->usb_icl_votable, DCP_VOTER, chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua); +#endif /* * if pd is not allowed, then set pd_active = false right here, @@ -3607,6 +4355,7 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) { int typec_mode; @@ -3653,6 +4402,58 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) break; } } +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +static void smblib_somc_force_legacy_icl(struct smb_charger *chg, + u8 apsd_result_bit) +{ + /* while PD is active it should have complete ICL control */ + if (chg->pd_active) { + smblib_dbg(chg, PR_SOMC, + "PD is active, does not set ICL by APSD result\n"); + return; + } + + switch (apsd_result_bit) { + case SDP_CHARGER_BIT: + if (!is_client_vote_enabled(chg->usb_icl_votable, + USB_PSY_VOTER)) + vote(chg->usb_icl_votable, USB_PSY_VOTER, true, 0); + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); + break; + case CDP_CHARGER_BIT: + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, + USBIN_1500MA); + break; + case DCP_CHARGER_BIT: + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, + chg->dcp_icl_ua != -EINVAL ? + chg->dcp_icl_ua : USBIN_1500MA); + break; + case OCP_CHARGER_BIT: + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, + USBIN_1000MA); + break; + case FLOAT_CHARGER_BIT: + if (chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, + USBIN_500MA); + else if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_MEDIUM) + || (chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH)) + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, + USBIN_1500MA); + else + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, + 0); + break; + case 0: + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 0); + break; + default: + break; + } +} +#endif static void smblib_notify_extcon_props(struct smb_charger *chg) { @@ -3691,7 +4492,12 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) apsd_result = smblib_update_usb_type(chg); if (!chg->typec_legacy_valid) +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) smblib_force_legacy_icl(chg, apsd_result->pst); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_somc_force_legacy_icl(chg, apsd_result->bit); +#endif switch (apsd_result->bit) { case SDP_CHARGER_BIT: @@ -3716,6 +4522,19 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) break; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (apsd_result->bit != SDP_CHARGER_BIT && + apsd_result->bit != CDP_CHARGER_BIT) { + mutex_lock(&chg->xo_lock); + if (!chg->xo_holded) { + clk_prepare_enable(chg->xo_clk); + chg->xo_holded = true; + smblib_dbg(chg, PR_SOMC, + "hold xo clock due to charger attached\n"); + } + mutex_unlock(&chg->xo_lock); + } +#endif smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n", apsd_result->name); } @@ -3727,6 +4546,13 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) int rc = 0; u8 stat; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (chg->typec_en_dis_active) { + smblib_dbg(chg, PR_SOMC, + "ignored handler during typec_en_dis_active\n"); + return IRQ_HANDLED; + } +#endif rc = smblib_read(chg, APSD_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc); @@ -3766,6 +4592,10 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) smblib_hvdcp_adaptive_voltage_change(chg); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_somc_thermal_icl_change(chg); + +#endif power_supply_changed(chg->usb_psy); rc = smblib_read(chg, APSD_STATUS_REG, &stat); @@ -3778,6 +4608,19 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) return IRQ_HANDLED; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +static void smblib_somc_typec_cur_source(struct smb_charger *chg, bool en180UA) +{ + int rc; + /* change CUR_SOURCE to advertise current */ + rc = smblib_masked_write(chg, TYPE_C_CFG_2_REG, + EN_80UA_180UA_CUR_SOURCE_BIT, + en180UA ? EN_80UA_180UA_CUR_SOURCE_BIT: 0); + if (rc < 0) + dev_err(chg->dev, "Couldn't change cur source rc=%d\n", rc); +} +#endif + static int typec_try_sink(struct smb_charger *chg) { union power_supply_propval val; @@ -3942,6 +4785,9 @@ static void typec_sink_insertion(struct smb_charger *chg) */ vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, false, 0); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_somc_typec_cur_source(chg, false); +#endif if (chg->use_extcon) { smblib_notify_usb_host(chg, true); chg->otg_present = true; @@ -3953,6 +4799,9 @@ static void typec_sink_removal(struct smb_charger *chg) smblib_set_charge_param(chg, &chg->param.freq_boost, chg->chg_freq.freq_above_otg_threshold); chg->boost_current_ua = 0; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_somc_typec_cur_source(chg, true); +#endif } static void smblib_handle_typec_removal(struct smb_charger *chg) @@ -3986,12 +4835,20 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) cancel_delayed_work_sync(&chg->hvdcp_detect_work); /* reset input current limit voters */ +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 0); +#endif vote(chg->usb_icl_votable, PD_VOTER, false, 0); vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); vote(chg->usb_icl_votable, DCP_VOTER, false, 0); vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0); vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + vote(chg->usb_icl_votable, HIGH_VOLTAGE_VOTER, false, 0); +#endif /* reset hvdcp voters */ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0); @@ -4024,12 +4881,14 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->pd_hard_reset = 0; chg->typec_legacy_valid = false; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) /* write back the default FLOAT charger configuration */ rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, (u8)FLOAT_OPTIONS_MASK, chg->float_cfg); if (rc < 0) smblib_err(chg, "Couldn't write float charger options rc=%d\n", rc); +#endif /* reset back to 120mS tCC debounce */ rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, 0); @@ -4098,7 +4957,13 @@ unlock: rc); typec_sink_removal(chg); +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) smblib_update_usb_type(chg); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + chg->real_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + chg->usb_params.apsd_result_bit = 0; +#endif if (chg->use_extcon) { if (chg->otg_present) @@ -4128,9 +4993,11 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg) if (rc < 0) smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); typec_sink_removal(chg); + } } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) { int rp_ua; @@ -4169,6 +5036,7 @@ static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) rp_ua = get_rp_based_dcp_current(chg, typec_mode); vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua); } +#endif static void smblib_handle_typec_cc_state_change(struct smb_charger *chg) { @@ -4178,8 +5046,10 @@ static void smblib_handle_typec_cc_state_change(struct smb_charger *chg) return; typec_mode = smblib_get_prop_typec_mode(chg); +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) if (chg->typec_present && (typec_mode != chg->typec_mode)) smblib_handle_rp_change(chg, typec_mode); +#endif chg->typec_mode = typec_mode; @@ -4195,8 +5065,10 @@ static void smblib_handle_typec_cc_state_change(struct smb_charger *chg) smblib_handle_typec_removal(chg); } +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n", smblib_typec_mode_name[chg->typec_mode]); +#endif } void smblib_usb_typec_change(struct smb_charger *chg) @@ -4245,6 +5117,10 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data) mutex_lock(&chg->lock); smblib_usb_typec_change(chg); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_dbg(chg, PR_INTERRUPT, "IRQ: typec-change; Type-C %s detected\n", + smblib_typec_mode_name[chg->typec_mode]); +#endif mutex_unlock(&chg->lock); return IRQ_HANDLED; } @@ -4364,6 +5240,32 @@ irqreturn_t smblib_handle_wdog_bark(int irq, void *data) return IRQ_HANDLED; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +irqreturn_t smblib_handle_aicl_done(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + u8 stat; + int rc; + + smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); + + rc = smblib_read(chg, AICL_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n", rc); + return IRQ_HANDLED; + } + + if (stat & ICL_IMIN_BIT) { + smblib_dbg(chg, PR_SOMC, + "ICL_IMIN is detected, suspending usbin\n"); + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 0); + } + + return IRQ_HANDLED; +} + +#endif /************** * Additional USB PSY getters/setters * that call interrupt functions @@ -4606,13 +5508,17 @@ static void smblib_otg_oc_work(struct work_struct *work) goto unlock; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + somc_usb_otg_regulator_ocp_notify(chg); +#endif +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) smblib_dbg(chg, PR_OTG, "OTG OC fell after %dms\n", 2 * i + 1); rc = _smblib_vbus_regulator_enable(chg->vbus_vreg->rdev); if (rc < 0) { smblib_err(chg, "Couldn't enable VBUS rc=%d\n", rc); goto unlock; } - +#endif unlock: mutex_unlock(&chg->otg_oc_lock); } @@ -4735,11 +5641,31 @@ static void smblib_legacy_detection_work(struct work_struct *work) legacy_detection_work); int rc; u8 stat; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) bool legacy, rp_high; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + bool legacy, rp_default; + union power_supply_propval val; +#endif +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) mutex_lock(&chg->lock); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + mutex_lock(&chg->legacy_detection_lock); + rc = smblib_get_prop_batt_status(chg, &val); + if (rc < 0) + val.intval = POWER_SUPPLY_STATUS_CHARGING; + chg->status_before_typec_en_dis_active = val.intval; +#endif chg->typec_en_dis_active = 1; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) smblib_dbg(chg, PR_MISC, "running legacy unknown workaround\n"); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + smblib_dbg(chg, PR_SOMC, "running legacy unknown workaround\n"); +#endif rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_DISABLE_CMD_BIT, @@ -4748,10 +5674,14 @@ static void smblib_legacy_detection_work(struct work_struct *work) smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc); /* wait for the adapter to turn off VBUS */ +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) msleep(1000); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + msleep(400); +#endif smblib_dbg(chg, PR_MISC, "legacy workaround enabling typec\n"); - rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_DISABLE_CMD_BIT, 0); @@ -4759,7 +5689,12 @@ static void smblib_legacy_detection_work(struct work_struct *work) smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc); /* wait for type-c detection to complete */ +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) msleep(400); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + msleep(200); +#endif rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat); if (rc < 0) { @@ -4768,21 +5703,293 @@ static void smblib_legacy_detection_work(struct work_struct *work) } chg->typec_legacy_valid = true; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); +#endif legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) rp_high = chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH; smblib_dbg(chg, PR_MISC, "legacy workaround done legacy = %d rp_high = %d\n", legacy, rp_high); if (!legacy || !rp_high) vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, false, 0); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) + val.intval = 0; + rp_default = chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT; + if (val.intval && (!legacy || rp_default)) + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, + false, 0); +#endif unlock: chg->typec_en_dis_active = 0; smblib_usb_typec_change(chg); +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) mutex_unlock(&chg->lock); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + mutex_unlock(&chg->legacy_detection_lock); +#endif +} + +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +static void smblib_somc_fake_charging_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + fake_charging_work); + chg->duration_fake_charging = true; + msleep(500); + chg->duration_fake_charging = false; } +#define FV_JEITA_WARM_UV 4200000 +#define FV_JEITA_WARM_RB_WA_ENTER_UV 4200000 +#define FV_JEITA_WARM_RB_WA_EXIT_UV 4000000 +#define JEITA_WORK_DELAY_RETRY_MS 500 +#define JEITA_WORK_DELAY_CHARGING_MS 5000 +#define JEITA_WORK_DELAY_DISCHARGING_MS 30000 +#define JEITA_FAKE_CARGING_TIME_MS 500 +static void smblib_somc_jeita_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + jeita_work.work); + union power_supply_propval pval = {0, }; + int rc; + int batt_temp, skin_temp; + u8 reg; + u8 chg_stat; + bool vbus_rising; + bool skin_temp_failed = false; + int interval_ms; + int synth_cond; + int vbatt; + + if (!chg->jeita_sw_ctl_en) + return; + + rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, ®); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); + return; + } + vbus_rising = (bool)(reg & USBIN_PLUGIN_RT_STS_BIT); + + rc = smblib_get_prop_from_bms(chg,POWER_SUPPLY_PROP_TEMP, &pval); + if (rc < 0) + smblib_err(chg, "Couldn't get batt temp rc=%d\n", rc); + + batt_temp = pval.intval; + + rc = smblib_get_prop_from_bms(chg,POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read VBATT rc=%d\n", rc); + goto reschedule; + } + vbatt = pval.intval; + + if (chg->jeita_use_aux) { + rc = smblib_get_prop_skin_temp(chg, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get skin temp rc=%d\n", rc); + skin_temp_failed = true; + } + skin_temp = pval.intval; + + smblib_dbg(chg, PR_MISC, + "vbus_rising=%d batt_temp=%d sikn_temp=%d\n", + (int)vbus_rising, batt_temp, + skin_temp_failed ? 0 : skin_temp); + } + + if (!chg->jeita_use_aux) { + chg->jeita_skin_temp_condition = TEMP_CONDITION_DEFAULT; + } else if (!skin_temp_failed) { + if (skin_temp > chg->jeita_aux_thresh_hot) + chg->jeita_skin_temp_condition = TEMP_CONDITION_HOT; + else if (skin_temp > chg->jeita_aux_thresh_warm) + chg->jeita_skin_temp_condition = TEMP_CONDITION_WARM; + else + chg->jeita_skin_temp_condition = TEMP_CONDITION_NORMAL; + } + + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &chg_stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read charger status 1 rc=%d\n", rc); + return; + } + + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, ®); + if (rc < 0) { + smblib_err(chg, "Couldn't read charger status 2 rc=%d\n", rc); + return; + } + if (reg & BAT_TEMP_STATUS_TOO_COLD_BIT) + chg->jeita_batt_temp_condition = TEMP_CONDITION_COLD; + else if (reg & BAT_TEMP_STATUS_TOO_HOT_BIT) + chg->jeita_batt_temp_condition = TEMP_CONDITION_HOT; + else if (reg & BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT) + chg->jeita_batt_temp_condition = TEMP_CONDITION_COOL; + else if (reg & BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT) + chg->jeita_batt_temp_condition = TEMP_CONDITION_WARM; + else + chg->jeita_batt_temp_condition = TEMP_CONDITION_NORMAL; + + switch (chg->jeita_batt_temp_condition) { + case TEMP_CONDITION_HOT: + synth_cond = TEMP_CONDITION_HOT; + break; + case TEMP_CONDITION_WARM: + if (chg->jeita_skin_temp_condition == TEMP_CONDITION_HOT) + synth_cond = TEMP_CONDITION_HOT; + else + synth_cond = TEMP_CONDITION_WARM; + break; + case TEMP_CONDITION_NORMAL: + if (chg->jeita_skin_temp_condition == TEMP_CONDITION_HOT) + synth_cond = TEMP_CONDITION_HOT; + else if (chg->jeita_skin_temp_condition == TEMP_CONDITION_WARM) + synth_cond = TEMP_CONDITION_WARM; + else + synth_cond = TEMP_CONDITION_NORMAL; + break; + case TEMP_CONDITION_COOL: + synth_cond = TEMP_CONDITION_COOL; + break; + case TEMP_CONDITION_COLD: + synth_cond = TEMP_CONDITION_COLD; + break; + default: + synth_cond = TEMP_CONDITION_NORMAL; + break; + } + smblib_dbg(chg, PR_MISC, "batt=%d skin=%d result=%d\n", + chg->jeita_batt_temp_condition, + chg->jeita_skin_temp_condition, + synth_cond); + + if (synth_cond == TEMP_CONDITION_HOT || + synth_cond == TEMP_CONDITION_COLD) + vote(chg->chg_disable_votable, JEITA_VOTER, true, 0); + else + vote(chg->chg_disable_votable, JEITA_VOTER, false, 0); + + if (synth_cond == TEMP_CONDITION_WARM) + vote(chg->fv_votable, JEITA_VOTER, true, FV_JEITA_WARM_UV); + else + vote(chg->fv_votable, JEITA_VOTER, false, 0); + + if (synth_cond == TEMP_CONDITION_WARM && chg->jeita_warm_fcc_ua > 0) + vote(chg->fcc_votable, JEITA_VOTER, true, + chg->jeita_warm_fcc_ua); + else if (synth_cond == TEMP_CONDITION_COOL && + chg->jeita_cool_fcc_ua > 0) + vote(chg->fcc_votable, JEITA_VOTER, true, + chg->jeita_cool_fcc_ua); + else + vote(chg->fcc_votable, JEITA_VOTER, false, 0); + + if (synth_cond != TEMP_CONDITION_NORMAL) { + power_supply_changed(chg->batt_psy); + smblib_dbg(chg, PR_SOMC, + "JEITA: batt_temp=%d(%d) skin_temp=%d(%d) result:%d\n", + batt_temp, chg->jeita_batt_temp_condition, + skin_temp, chg->jeita_skin_temp_condition, + synth_cond); + } + + /* WA for Reverse Boost */ + if (!chg->jeita_rb_warm_hi_vbatt_en && + vbus_rising && synth_cond == TEMP_CONDITION_WARM && + vbatt > FV_JEITA_WARM_RB_WA_ENTER_UV) { + smblib_dbg(chg, PR_SOMC, + "WA for RB after Warm. vbatt=%d\n", + vbatt); + chg->jeita_rb_warm_hi_vbatt_en = true; + vote(chg->usb_icl_votable, JEITA_VOTER, true, 0); + } else if (chg->jeita_rb_warm_hi_vbatt_en && + (!vbus_rising || synth_cond != TEMP_CONDITION_WARM || + vbatt < FV_JEITA_WARM_RB_WA_EXIT_UV)) { + smblib_dbg(chg, PR_SOMC, + "Release WA for RB after Warm. vbatt=%d\n", + vbatt); + vote(chg->usb_icl_votable, JEITA_VOTER, false, 0); + chg->jeita_rb_warm_hi_vbatt_en = false; + } + + /* WA for holding Charge Termination after normal */ + if (vbus_rising && + chg->jeita_synth_temp_condition == TEMP_CONDITION_WARM && + (synth_cond == TEMP_CONDITION_NORMAL || + synth_cond == TEMP_CONDITION_COOL) && + !get_effective_result(chg->chg_disable_votable) && + ((chg_stat & BATTERY_CHARGER_STATUS_MASK) == TERMINATE_CHARGE || + (chg_stat & BATTERY_CHARGER_STATUS_MASK) == INHIBIT_CHARGE || + (chg_stat & CC_SOFT_TERMINATE_BIT) == CC_SOFT_TERMINATE_BIT)) { + smblib_dbg(chg, PR_SOMC, "Execute WA for holding FULL\n"); + chg->jeita_keep_fake_charging = true; + vote(chg->chg_disable_votable, JEITA_VOTER, true, 0); + vote(chg->chg_disable_votable, JEITA_VOTER, false, 0); + msleep(JEITA_FAKE_CARGING_TIME_MS); + + smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, ®); + smblib_dbg(chg, PR_SOMC, "waiting done chg_sts1=0x%02x\n", reg); + chg->jeita_keep_fake_charging = false; + } + + chg->jeita_synth_temp_condition = synth_cond; + +reschedule: + if (vbus_rising && skin_temp_failed) + interval_ms = JEITA_WORK_DELAY_RETRY_MS; + else if (vbus_rising && !skin_temp_failed) + interval_ms = JEITA_WORK_DELAY_CHARGING_MS; + else + interval_ms = JEITA_WORK_DELAY_DISCHARGING_MS; + + smblib_dbg(chg, PR_MISC, "will schedule delayed worker (%d ms)\n", + interval_ms); + + if (chg->jeita_sw_ctl_en) + schedule_delayed_work(&chg->jeita_work, + msecs_to_jiffies(interval_ms)); +} + +static void smblib_somc_smart_charge_wdog_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + smart_charge_wdog_work.work); + + smblib_dbg(chg, PR_SOMC, "Smart Charge Watchdog timer has expired.\n"); + + mutex_lock(&chg->smart_charge_lock); + vote(chg->chg_disable_votable, BATTCHG_SMART_EN_VOTER, false, 0); + chg->smart_charge_suspended = false; + mutex_unlock(&chg->smart_charge_lock); + + power_supply_changed(chg->batt_psy); +} + +static void smblib_somc_removal_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + usb_removal_work.work); + + if (chg->usb_removal_input && !chg->low_batt_shutdown_enabled) { + /* key event for power off charge */ + smblib_dbg(chg, PR_SOMC, "input_report_key KEY_F24\n"); + input_report_key(chg->usb_removal_input, KEY_F24, 1); + input_sync(chg->usb_removal_input); + input_report_key(chg->usb_removal_input, KEY_F24, 0); + input_sync(chg->usb_removal_input); + } +} + +#endif + static int smblib_create_votables(struct smb_charger *chg) { int rc = 0; @@ -4978,6 +6185,15 @@ int smblib_init(struct smb_charger *chg) mutex_init(&chg->write_lock); mutex_init(&chg->otg_oc_lock); mutex_init(&chg->vconn_oc_lock); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + mutex_init(&chg->smart_charge_lock); + mutex_init(&chg->thermal_lock); + mutex_init(&chg->xo_lock); + mutex_init(&chg->legacy_detection_lock); + wake_lock_init(&chg->usb_removal_wakelock.lock, + WAKE_LOCK_SUSPEND, "unplug_wakelock"); + chg->usb_removal_wakelock.enabled = true; +#endif INIT_WORK(&chg->bms_update_work, bms_update_work); INIT_WORK(&chg->rdstd_cc2_detach_work, rdstd_cc2_detach_work); INIT_DELAYED_WORK(&chg->hvdcp_detect_work, smblib_hvdcp_detect_work); @@ -4990,6 +6206,33 @@ int smblib_init(struct smb_charger *chg) INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work); INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work); INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + INIT_WORK(&chg->fake_charging_work, smblib_somc_fake_charging_work); + INIT_DELAYED_WORK(&chg->smart_charge_wdog_work, + smblib_somc_smart_charge_wdog_work); + INIT_DELAYED_WORK(&chg->usb_removal_work, smblib_somc_removal_work); + INIT_DELAYED_WORK(&chg->jeita_work, smblib_somc_jeita_work); + + /* register input device */ + chg->usb_removal_input = input_allocate_device(); + if (!chg->usb_removal_input) { + dev_err(chg->dev, + "can't allocate unplug virtual button\n"); + rc = -ENOMEM; + return rc; + } + input_set_capability(chg->usb_removal_input, EV_KEY, KEY_F24); + chg->usb_removal_input->name = "SOMC USB Removal"; + chg->usb_removal_input->dev.parent = chg->dev; + + rc = input_register_device(chg->usb_removal_input); + if (rc) { + dev_err(chg->dev, + "can't register power key: %d\n", rc); + rc = -ENOMEM; + return rc; + } +#endif chg->fake_capacity = -EINVAL; chg->fake_input_current_limited = -EINVAL; @@ -5065,7 +6308,407 @@ int smblib_deinit(struct smb_charger *chg) return -EINVAL; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + if (!IS_ERR(chg->xo_clk)) + clk_put(chg->xo_clk); + + if (chg->usb_removal_input) { + input_free_device(chg->usb_removal_input); + chg->usb_removal_input = NULL; + } + if (chg->usb_removal_wakelock.enabled) { + wake_lock_destroy(&chg->usb_removal_wakelock.lock); + chg->usb_removal_wakelock.enabled = false; + } +#endif smblib_iio_deinit(chg); return 0; } + +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + +/************************** + * SOMC feature functions * + **************************/ + +void smblib_somc_thermal_fcc_change(struct smb_charger *chg) +{ + int lv = chg->system_temp_level; + + smblib_dbg(chg, PR_MISC, "thermal fcc change lv=%d\n", lv); + + if (IS_ERR_OR_NULL(chg->thermal_fcc_ua)) { + smblib_dbg(chg, PR_MISC, "thermal fcc table is NULL\n"); + return; + } + + if (lv > chg->thermal_fcc_levels - 1) { + smblib_dbg(chg, PR_MISC, "thermal lv is out of range\n"); + return; + } + + if (chg->thermal_fcc_ua[lv] > 0) { + vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, true, + chg->thermal_fcc_ua[lv]); + vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, false, 0); + } else { + vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, true, 0); + vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, false, 0); + } +} + +void smblib_somc_thermal_icl_change(struct smb_charger *chg) +{ + int rc; + u8 stat; + int pulses; + int icl = 0; + int lv = chg->system_temp_level; + int type; + + mutex_lock(&chg->thermal_lock); + smblib_dbg(chg, PR_MISC, "thermal icl change lv=%d\n", lv); + + if (IS_ERR_OR_NULL(chg->thermal_lo_volt_icl_ua) || + IS_ERR_OR_NULL(chg->thermal_hi_volt_icl_ua)) { + smblib_dbg(chg, PR_MISC, "thermal icl table is NULL\n"); + goto unlock; + } + + if (lv > chg->thermal_lo_volt_icl_levels - 1 || + lv > chg->thermal_hi_volt_icl_levels - 1) { + smblib_dbg(chg, PR_MISC, "thermal lv is out of range\n"); + goto unlock; + } + + if (chg->thermal_lo_volt_icl_levels != + chg->thermal_hi_volt_icl_levels) { + smblib_dbg(chg, PR_MISC, "thermal table size missmatch\n"); + goto unlock; + } + + type = chg->real_charger_type; + if (type == POWER_SUPPLY_TYPE_USB_HVDCP) { + rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(chg, + "Couldn't read QC_CHANGE_STATUS rc=%d\n", rc); + goto unlock; + } + + switch (stat & QC_2P0_STATUS_MASK) { + case QC_5V_BIT: + icl = chg->thermal_lo_volt_icl_ua[lv]; + break; + case QC_9V_BIT: + case QC_12V_BIT: + icl = chg->thermal_hi_volt_icl_ua[lv]; + break; + default: + icl = chg->thermal_lo_volt_icl_ua[lv]; + break; + } + smblib_dbg(chg, PR_MISC, "QC2.0: icl=%duA\n", icl); + } else if (type == POWER_SUPPLY_TYPE_USB_HVDCP_3) { + rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(chg, + "Couldn't read QC_PULSE_COUNT rc=%d\n", rc); + goto unlock; + } + pulses = (stat & QC_PULSE_COUNT_MASK); + + if (pulses >= QC3_PULSES_FOR_6V) + icl = chg->thermal_hi_volt_icl_ua[lv]; + else + icl = chg->thermal_lo_volt_icl_ua[lv]; + + smblib_dbg(chg, PR_MISC, "QC3.0: icl=%duA\n", icl); + } else if (type == POWER_SUPPLY_TYPE_USB_PD) { + if (chg->voltage_max_uv >= 6000000) + icl = chg->thermal_hi_volt_icl_ua[lv]; + else + icl = chg->thermal_lo_volt_icl_ua[lv]; + + smblib_dbg(chg, PR_MISC, "PD: icl=%duA\n", icl); + } else if (type == POWER_SUPPLY_TYPE_USB) { + if (chg->thermal_lo_volt_icl_ua[lv] == 0) + icl = 0; + else if (chg->thermal_lo_volt_icl_ua[lv] < USBIN_150MA) + icl = USBIN_100MA; + else if (chg->thermal_lo_volt_icl_ua[lv] < USBIN_500MA) + icl = USBIN_150MA; + else if (chg->thermal_lo_volt_icl_ua[lv] < USBIN_900MA) + icl = USBIN_500MA; + else + icl = USBIN_900MA; + + if (chg->thermal_lo_volt_icl_ua[lv] != icl) + smblib_dbg(chg, PR_MISC, "Round icl for SDP %d to %d\n", + chg->thermal_lo_volt_icl_ua[lv], icl); + + smblib_dbg(chg, PR_MISC, "SDP: icl=%duA\n", icl); + } else { + icl = chg->thermal_lo_volt_icl_ua[lv]; + smblib_dbg(chg, PR_MISC, "DCP/Other: icl=%duA\n", icl); + } + + vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true, icl); +unlock: + mutex_unlock(&chg->thermal_lock); + return; +} + +void smblib_somc_set_low_batt_suspend_en(struct smb_charger *chg) +{ + int rc; + + rc = vote(chg->usb_icl_votable, LOW_BATT_EN_VOTER, true, 0); + if (rc < 0) + dev_err(chg->dev, "Couldn't set usb suspend rc %d\n", rc); + + rc = vote(chg->dc_suspend_votable, LOW_BATT_EN_VOTER, true, 0); + if (rc < 0) + dev_err(chg->dev, "Couldn't set dc suspend rc %d\n", rc); +} + +#define INHIBIT_HOLD_MSOC 100 +void smblib_somc_ctrl_inhibit(struct smb_charger *chg, bool en) +{ + int rc; + int msoc; + u8 stat; + bool fv_decreased; + union power_supply_propval val = {0, }; + + if (!chg->bms_psy) { + smblib_err(chg, "Couldn't get bms_psy\n"); + return; + } + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_MONOTONIC_SOC, &val); + if (rc < 0) { + smblib_err(chg, "Couldn't get prop msoc rc=%d\n", rc); + return; + } + msoc = val.intval; + + if (chg->batt_profile_fv_uv < chg->last_batt_profile_fv_uv) + fv_decreased = true; + else + fv_decreased = false; + + rc = smblib_somc_get_battery_charger_status(chg, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't get battery charge sts rc=%d\n", rc); + return; + } + + smblib_dbg(chg, PR_MISC, "msoc:%d, FV:%d->%d, status:%d\n", + msoc, chg->last_batt_profile_fv_uv, + chg->batt_profile_fv_uv, stat); + + if (en) { + if (fv_decreased && (msoc == INHIBIT_HOLD_MSOC)) { + smblib_dbg(chg, PR_SOMC, "Enable inhibit for RB WA\n"); + rc = smblib_masked_write(chg, CHGR_CFG2_REG, + CHARGER_INHIBIT_BIT, + CHARGER_INHIBIT_BIT); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't set inhibit rc=%d\n", rc); + return; + } + } else { + smblib_dbg(chg, PR_MISC, "Don't enable inhibit\n"); + } + } else { + if (msoc < INHIBIT_HOLD_MSOC) { + smblib_dbg(chg, PR_SOMC, "Disable inhibit for RB WA\n"); + rc = smblib_masked_write(chg, CHGR_CFG2_REG, + CHARGER_INHIBIT_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't set inhibit rc=%d\n", rc); + return; + } + } else { + smblib_dbg(chg, PR_MISC, "Don't disable inhibit\n"); + } + } + chg->last_batt_profile_fv_uv = chg->batt_profile_fv_uv; +} + +int smblib_somc_get_battery_charger_status(struct smb_charger *chg, u8 *val) +{ + int rc; + u8 stat; + + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); + if (rc < 0) { + smblib_err(chg, + "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", rc); + return rc; + } + + *val = stat & BATTERY_CHARGER_STATUS_MASK; + return rc; +} + +#define FULL_CAPACITY 100 +#define DECIMAL_CEIL 100 + +int smblib_somc_lrc_get_capacity(struct smb_charger *chg, int capacity) +{ + int ceil, magni; + + if (chg->lrc_fake_capacity && + chg->lrc_enabled && chg->lrc_socmax) { + magni = FULL_CAPACITY * DECIMAL_CEIL / chg->lrc_socmax; + capacity *= magni; + ceil = (capacity % DECIMAL_CEIL) ? 1 : 0; + capacity = capacity / DECIMAL_CEIL + ceil; + if (capacity > FULL_CAPACITY) + capacity = FULL_CAPACITY; + } + return capacity; +} + +void smblib_somc_lrc_vote(struct smb_charger *chg, enum somc_lrc_status status) +{ + int rc; + + if (status == LRC_CHG_OFF) + rc = vote(chg->chg_disable_votable, BATTCHG_LRC_EN_VOTER, + true, 0); + else + rc = vote(chg->chg_disable_votable, BATTCHG_LRC_EN_VOTER, + false, 0); + + if (rc < 0) + dev_err(chg->dev, + "Couldn't vote for battchg suspend: rc = %d\n", rc); +} + +void smblib_somc_lrc_check(struct smb_charger *chg) +{ + int rc, soc = 0; + enum somc_lrc_status retcode = LRC_DISABLE; + union power_supply_propval val = {0, }; + + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0 || !val.intval) + goto exit; + + if (chg->lrc_enabled) { + if (chg->lrc_socmax <= chg->lrc_socmin) { + pr_err("invalid SOC min:%d max:%d\n", chg->lrc_socmin, + chg->lrc_socmax); + goto exit; + } + } else { + if (chg->lrc_status == LRC_CHG_OFF) + smblib_somc_lrc_vote(chg, LRC_CHG_ON); + goto exit; + } + + if (chg->bms_psy) { + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_CAPACITY, &val); + if (rc) { + pr_err("Couldn't get soc rc = %d\n", rc); + goto exit; + } else { + soc = val.intval; + } + } + + if (soc >= (chg->lrc_socmax + chg->lrc_hysterisis)) + retcode = LRC_CHG_OFF; + else if (soc <= chg->lrc_socmin) + retcode = LRC_CHG_ON; + else if (chg->lrc_status == LRC_CHG_OFF) + retcode = LRC_CHG_OFF; + else + retcode = LRC_CHG_ON; + + if (retcode != chg->lrc_status) + smblib_somc_lrc_vote(chg, retcode); + + chg->lrc_status = retcode; + + if (chg->lrc_fake_capacity && soc > chg->lrc_socmax) + vote(chg->usb_icl_votable, LRC_OVER_SOC_EN_VOTER, true, 0); + else + vote(chg->usb_icl_votable, LRC_OVER_SOC_EN_VOTER, false, 0); + return; + +exit: + chg->lrc_status = LRC_DISABLE; + return; +} + +#define SMART_CHARGE_WDOG_DELAY_MS (30 * 60 * 1000) /* 30min */ + +int smblib_somc_smart_set_suspend(struct smb_charger *chg) +{ + int rc = 0; + + if (!chg->smart_charge_enabled) { + pr_err("Couldn't set smart charge voter due to unactivated\n"); + goto exit; + } + + rc = vote(chg->chg_disable_votable, BATTCHG_SMART_EN_VOTER, + chg->smart_charge_suspended, 0); + if (rc < 0) { + pr_err("Couldn't vote en rc %d\n", rc); + goto exit; + } + + smblib_dbg(chg, PR_SOMC, "voted for smart charging (%d).\n", + chg->smart_charge_suspended); + cancel_delayed_work_sync(&chg->smart_charge_wdog_work); + if (chg->smart_charge_suspended) { + schedule_delayed_work(&chg->smart_charge_wdog_work, + msecs_to_jiffies(SMART_CHARGE_WDOG_DELAY_MS)); + } +exit: + return rc; +} + +int smblib_get_usb_max_current_limited(struct smb_charger *chg) +{ + int rc; + u8 reg; + rc = smblib_read(chg, USBIN_ICL_OPTIONS_REG, ®); + if (rc < 0) { + smblib_err(chg, "Couldn't read USBIN_ICL_OPTIONS_REG rc=%d\n", rc); + return 0; + } + + reg &= (CFG_USB3P0_SEL_BIT | USB51_MODE_BIT); + + switch (reg) { + case 0: + rc = USBIN_100MA / 1000; + break; + case CFG_USB3P0_SEL_BIT: + rc = USBIN_150MA / 1000; + break; + case USB51_MODE_BIT: + rc = USBIN_500MA / 1000; + break; + case (CFG_USB3P0_SEL_BIT | USB51_MODE_BIT): + rc = USBIN_900MA / 1000; + break; + default: + rc = 0; + break; + } + return rc; +} + +#endif diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 5ca5e923e651e78f3101ccb2b91440b7945e0686..c02318bf3d56da5b6c4cd67721334bcfa914ac27 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __SMB2_CHARGER_H #define __SMB2_CHARGER_H @@ -18,6 +23,10 @@ #include #include #include +#include +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#include +#endif #include "storm-watch.h" enum print_reason { @@ -26,6 +35,9 @@ enum print_reason { PR_MISC = BIT(2), PR_PARALLEL = BIT(3), PR_OTG = BIT(4), +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + PR_SOMC = BIT(15), +#endif }; #define DEFAULT_VOTER "DEFAULT_VOTER" @@ -35,6 +47,13 @@ enum print_reason { #define QC_VOTER "QC_VOTER" #define PL_USBIN_USBIN_VOTER "PL_USBIN_USBIN_VOTER" #define USB_PSY_VOTER "USB_PSY_VOTER" +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define BATTCHG_SMART_EN_VOTER "BATTCHG_SMART_EN_VOTER" +#define BATTCHG_LRC_EN_VOTER "BATTCHG_LRC_EN_VOTER" +#define LRC_OVER_SOC_EN_VOTER "LRC_OVER_SOC_EN_VOTER" +#define PRODUCT_VOTER "PRODUCT_VOTER" +#define HIGH_VOLTAGE_VOTER "HIGH_VOLTAGE_VOTER" +#endif #define PL_TAPER_WORK_RUNNING_VOTER "PL_TAPER_WORK_RUNNING_VOTER" #define PL_QNOVO_VOTER "PL_QNOVO_VOTER" #define USBIN_V_VOTER "USBIN_V_VOTER" @@ -73,7 +92,11 @@ enum print_reason { #define OTG_MAX_ATTEMPTS 3 #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 - +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define JEITA_VOTER "JEITA_VOTER" +#define LOW_BATT_EN_VOTER "LOW_BATT_EN_VOTER" +#define QNS_VOTER "QNS_VOTER" +#endif enum smb_mode { PARALLEL_MASTER = 0, PARALLEL_SLAVE, @@ -147,6 +170,14 @@ struct smb_irq_info { int irq; }; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +enum somc_running_status { + RUNNING_STATUS_NORMAL, + RUNNING_STATUS_OFF_CHARGE, + RUNNING_STATUS_SHUTDOWN, +}; + +#endif static const unsigned int smblib_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, @@ -158,6 +189,14 @@ static const unsigned int smblib_extcon_cable[] = { /* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */ static const u32 smblib_extcon_exclusive[] = {0x3, 0}; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +enum somc_lrc_status { + LRC_DISABLE, + LRC_CHG_OFF, + LRC_CHG_ON, +}; + +#endif struct smb_regulator { struct regulator_dev *rdev; struct regulator_desc rdesc; @@ -192,6 +231,18 @@ struct smb_chg_freq { unsigned int freq_above_otg_threshold; }; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +struct somc_usb_ocp { + struct regulator_ocp_notification notification; + spinlock_t lock; +}; + +struct usb_somc_params { + struct somc_usb_ocp ocp; + u8 apsd_result_bit; +}; + +#endif struct smb_params { struct smb_chg_param fcc; struct smb_chg_param fv; @@ -224,6 +275,9 @@ struct smb_iio { struct iio_channel *connector_temp_thr1_chan; struct iio_channel *connector_temp_thr2_chan; struct iio_channel *connector_temp_thr3_chan; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + struct iio_channel *skin_temp_chan; +#endif }; struct reg_info { @@ -234,6 +288,13 @@ struct reg_info { const char *desc; }; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +struct somc_wake_lock { + struct wake_lock lock; + bool enabled; +}; + +#endif struct smb_charger { struct device *dev; char *name; @@ -255,6 +316,11 @@ struct smb_charger { struct mutex ps_change_lock; struct mutex otg_oc_lock; struct mutex vconn_oc_lock; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + struct mutex thermal_lock; + struct mutex xo_lock; + struct mutex legacy_detection_lock; +#endif /* power supplies */ struct power_supply *batt_psy; @@ -277,6 +343,12 @@ struct smb_charger { struct smb_regulator *vconn_vreg; struct regulator *dpdm_reg; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* clocks */ + struct clk *xo_clk; + bool xo_holded; + +#endif /* votables */ struct votable *dc_suspend_votable; struct votable *fcc_votable; @@ -318,15 +390,30 @@ struct smb_charger { bool system_suspend_supported; int boost_threshold_ua; int system_temp_level; +#if !defined(CONFIG_SOMC_CHARGER_EXTENSION) int thermal_levels; int *thermal_mitigation; +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int *thermal_fcc_ua; + int *thermal_lo_volt_icl_ua; + int *thermal_hi_volt_icl_ua; + int thermal_fcc_levels; + int thermal_lo_volt_icl_levels; + int thermal_hi_volt_icl_levels; +#endif int dcp_icl_ua; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int product_icl_ua; + int high_voltage_icl_ua; +#endif int fake_capacity; bool step_chg_enabled; bool sw_jeita_enabled; bool is_hdc; bool chg_done; bool micro_usb_mode; + int input_limited_fcc_ua; bool otg_en; bool vconn_en; bool suspend_input_on_debug_batt; @@ -354,6 +441,9 @@ struct smb_charger { bool cc2_detach_wa_active; bool typec_en_dis_active; bool try_sink_active; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int status_before_typec_en_dis_active; +#endif int boost_current_ua; int temp_speed_reading_count; @@ -363,10 +453,63 @@ struct smb_charger { /* battery profile */ int batt_profile_fcc_ua; int batt_profile_fv_uv; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + int last_batt_profile_fv_uv; +#endif /* qnovo */ int usb_icl_delta_ua; int pulse_cnt; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + struct usb_somc_params usb_params; + int usb_switch_sel_gpio; + + bool duration_fake_charging; + struct work_struct fake_charging_work; + + /* jeita */ + struct delayed_work jeita_work; + bool jeita_sw_ctl_en; + bool jeita_use_aux; + int jeita_skin_temp_condition; + int jeita_batt_temp_condition; + int jeita_synth_temp_condition; + int jeita_aux_thresh_hot; + int jeita_aux_thresh_warm; + int jeita_warm_fcc_ua; + int jeita_cool_fcc_ua; + bool jeita_vbus_rising; + bool jeita_rb_warm_hi_vbatt_en; + bool jeita_keep_fake_charging; + + /* low batt shutdown */ + int low_batt_shutdown_enabled; + + /* smart charge */ + bool smart_charge_enabled; + bool smart_charge_suspended; + struct delayed_work smart_charge_wdog_work; + struct mutex smart_charge_lock; + + /* limited range charge */ + bool lrc_enabled; + int lrc_socmax; + int lrc_socmin; + int lrc_status; + bool lrc_fake_capacity; + int lrc_hysterisis; + + /* usb remove */ + struct delayed_work usb_removal_work; + struct input_dev *usb_removal_input; + struct somc_wake_lock usb_removal_wakelock; + + /* misc */ + bool int_cld; + int running_status; + int faked_status; + +#endif }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); @@ -378,6 +521,9 @@ int smblib_get_charge_param(struct smb_charger *chg, int smblib_get_usb_suspend(struct smb_charger *chg, int *suspend); int smblib_enable_charging(struct smb_charger *chg, bool enable); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +const char *smblib_somc_get_charger_type(struct smb_charger *chg); +#endif int smblib_set_charge_param(struct smb_charger *chg, struct smb_chg_param *param, int val_u); int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend); @@ -391,11 +537,19 @@ int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param, int val_u, u8 *val_raw); int smblib_set_chg_freq(struct smb_chg_param *param, int val_u, u8 *val_raw); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +int somc_usb_register(struct smb_charger *chg); +void somc_usb_unregister(struct smb_charger *chg); +#endif int smblib_vbus_regulator_enable(struct regulator_dev *rdev); int smblib_vbus_regulator_disable(struct regulator_dev *rdev); int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev); - +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +int somc_usb_otg_regulator_register_ocp_notification( + struct regulator_dev *rdev, + struct regulator_ocp_notification *notification); +#endif int smblib_vconn_regulator_enable(struct regulator_dev *rdev); int smblib_vconn_regulator_disable(struct regulator_dev *rdev); int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev); @@ -415,7 +569,18 @@ irqreturn_t smblib_handle_dc_plugin(int irq, void *data); irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data); irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data); irqreturn_t smblib_handle_wdog_bark(int irq, void *data); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +irqreturn_t smblib_handle_aicl_done(int irq, void *data); +#endif +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +int smblib_get_prop_charging_enabled(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_get_prop_charge_full_design(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_get_prop_charge_full(struct smb_charger *chg, + union power_supply_propval *val); +#endif int smblib_get_prop_input_suspend(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_present(struct smb_charger *chg, @@ -434,6 +599,10 @@ int smblib_get_prop_system_temp_level(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_input_current_limited(struct smb_charger *chg, union power_supply_propval *val); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +int smblib_set_prop_charging_enabled(struct smb_charger *chg, + const union power_supply_propval *val); +#endif int smblib_set_prop_input_suspend(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_batt_capacity(struct smb_charger *chg, @@ -478,6 +647,12 @@ int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_pe_start(struct smb_charger *chg, union power_supply_propval *val); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +int smblib_get_prop_legacy_cable_status(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_get_prop_skin_temp(struct smb_charger *chg, + union power_supply_propval *val); +#endif int smblib_get_prop_charger_temp(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_charger_temp_max(struct smb_charger *chg, @@ -528,6 +703,18 @@ int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, const union power_supply_propval *val); void smblib_usb_typec_change(struct smb_charger *chg); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +void smblib_somc_thermal_fcc_change(struct smb_charger *chg); +void smblib_somc_thermal_icl_change(struct smb_charger *chg); +void smblib_somc_set_low_batt_suspend_en(struct smb_charger *chg); +void smblib_somc_ctrl_inhibit(struct smb_charger *chg, bool en); +int smblib_somc_get_battery_charger_status(struct smb_charger *chg, u8 *val); +int smblib_somc_smart_set_suspend(struct smb_charger *chg); +int smblib_somc_lrc_get_capacity(struct smb_charger *chg, + int capacity); +void smblib_somc_lrc_check(struct smb_charger *chg); +int smblib_get_usb_max_current_limited(struct smb_charger *chg); +#endif int smblib_init(struct smb_charger *chg); int smblib_deinit(struct smb_charger *chg); #endif /* __SMB2_CHARGER_H */ diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h index 4ddb085e9300b132c1de78ed81106c9a837b158d..1b953f74ece7373ac4f2670532508241c83845c6 100644 --- a/drivers/power/supply/qcom/smb-reg.h +++ b/drivers/power/supply/qcom/smb-reg.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __SMB2_CHARGER_REG_H #define __SMB2_CHARGER_REG_H @@ -108,6 +113,10 @@ enum { #define BAT_ID_BMISS_CMP_BIT BIT(1) #define THERM_CMP_BIT BIT(0) +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define CHGR_INT_RT_STS_REG (CHGR_BASE + 0x10) + +#endif /* CHGR Interrupt Bits */ #define CHGR_7_RT_STS_BIT BIT(7) #define CHGR_6_RT_STS_BIT BIT(6) @@ -389,6 +398,10 @@ enum { #define BAT_OCP_RT_STS_BIT BIT(1) #define BAT_TEMP_RT_STS_BIT BIT(0) +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define BATIF_INT_RT_STS_REG (BATIF_BASE + 0x10) + +#endif #define SHIP_MODE_REG (BATIF_BASE + 0x40) #define SHIP_MODE_EN_BIT BIT(0) @@ -525,6 +538,10 @@ enum { #define TYPEC_TRYSOURCE_DETECT_STATUS_BIT BIT(1) #define TYPEC_TRYSINK_DETECT_STATUS_BIT BIT(0) +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define USB_INT_RT_STS_REG (USBIN_BASE + 0x10) + +#endif /* USBIN Interrupt Bits */ #define TYPE_C_CHANGE_RT_STS_BIT BIT(7) #define USBIN_ICL_CHANGE_RT_STS_BIT BIT(6) @@ -587,6 +604,18 @@ enum { #define EN_LEGACY_CABLE_DETECTION_BIT BIT(1) #define ALLOW_PD_DRING_UFP_TCCDB_BIT BIT(0) +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define HVDCP_PULSE_COUNT_MAX_CFG_REG (USBIN_BASE + 0x5B) +#define HVDCP_PULSE_COUNT_MAX_QC3P0 GENMASK(5, 0) +#define HVDCP_PULSE_COUNT_MAX_QC2P0 GENMASK(7, 6) +#define QC3P0_MAX_PULSE_5V 0 +#define QC3P0_MAX_PULSE_9V 20 +#define QC3P0_MAX_PULSE_12V 35 +#define QC2P0_MAX_PULSE_5V 0 +#define QC2P0_MAX_PULSE_9V BIT(6) +#define QC2P0_MAX_PULSE_12V BIT(7) + +#endif #define USBIN_ADAPTER_ALLOW_CFG_REG (USBIN_BASE + 0x60) #define USBIN_ADAPTER_ALLOW_MASK GENMASK(3, 0) enum { @@ -630,6 +659,14 @@ enum { #define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65) #define USBIN_OV_CH_LOAD_OPTION_BIT BIT(7) #define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define AICL_USE_SW_AFTER_APSD BIT(4) +#define USBIN_IN_COLLAPSE_GF_SEL GENMASK(1, 0) +#define USBIN_IN_COLLAPSE_GF_1MS 0 +#define USBIN_IN_COLLAPSE_GF_5MS 1 +#define USBIN_IN_COLLAPSE_GF_30MS 2 +#define USBIN_IN_COLLAPSE_GF_30US 3 +#endif #define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66) #define CFG_USB3P0_SEL_BIT BIT(2) @@ -680,15 +717,27 @@ enum { #define USBIN_5V_AICL_THRESHOLD_CFG_REG (USBIN_BASE + 0x81) #define USBIN_5V_AICL_THRESHOLD_CFG_MASK GENMASK(2, 0) +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define USBIN_5V_AICL_THRESHOLD_4P0V 0 +#define USBIN_5V_AICL_THRESHOLD_4P5V 5 +#endif #define USBIN_9V_AICL_THRESHOLD_CFG_REG (USBIN_BASE + 0x82) #define USBIN_9V_AICL_THRESHOLD_CFG_MASK GENMASK(2, 0) +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define USBIN_9V_AICL_THRESHOLD_7P2V 0 +#define USBIN_9V_AICL_THRESHOLD_7P6V 2 +#endif #define USBIN_12V_AICL_THRESHOLD_CFG_REG (USBIN_BASE + 0x83) #define USBIN_12V_AICL_THRESHOLD_CFG_MASK GENMASK(2, 0) #define USBIN_CONT_AICL_THRESHOLD_CFG_REG (USBIN_BASE + 0x84) #define USBIN_CONT_AICL_THRESHOLD_CFG_MASK GENMASK(5, 0) +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define USBIN_CONT_AICL_THRESHOLD_4P0V 0 +#define USBIN_CONT_AICL_THRESHOLD_4P5V 5 +#endif /* DCIN Peripheral Registers */ #define DCIN_INPUT_STATUS_REG (DCIN_BASE + 0x06) @@ -878,6 +927,10 @@ enum { #define SYSOK_REASON_DCIN_BIT BIT(1) #define SYSOK_REASON_USBIN_BIT BIT(0) +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define MISC_INT_RT_STS_REG (MISC_BASE + 0x10) + +#endif /* MISC Interrupt Bits */ #define SWITCHER_POWER_OK_RT_STS_BIT BIT(7) #define TEMPERATURE_CHANGE_RT_STS_BIT BIT(6) diff --git a/drivers/pwm/pwm-qpnp.c b/drivers/pwm/pwm-qpnp.c index 8edb8a61795ab551591b7c227a4c59412dcbc3ad..777e08af41c27d1d00ea399a96a88eae04648ddd 100644 --- a/drivers/pwm/pwm-qpnp.c +++ b/drivers/pwm/pwm-qpnp.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * Qualcomm Technologies, Inc. QPNP Pulse Width Modulation (PWM) driver * @@ -37,6 +42,9 @@ #define QPNP_LPG_CHAN_SUB_TYPE 0x2 #define QPNP_LPG_S_CHAN_SUB_TYPE 0x11 +/* LPG Control for LUT_RAMP_CONTROL */ +#define QPNP_LUT_RAMP_CONTROL_MASK 0xFF + /* LPG Control for LPG_PATTERN_CONFIG */ #define QPNP_RAMP_DIRECTION_SHIFT 4 #define QPNP_RAMP_DIRECTION_MASK 0x10 @@ -312,6 +320,7 @@ struct qpnp_lpg_config { struct _qpnp_pwm_config { int pwm_value; + int pwm_max_value; int pwm_period; /* in microseconds */ int pwm_duty; /* in microseconds */ struct pwm_period_config period; @@ -586,6 +595,7 @@ static int qpnp_lpg_change_table(struct qpnp_pwm_chip *chip, { unsigned int pwm_value, max_pwm_value; struct qpnp_lut_config *lut = &chip->lpg_config.lut_config; + struct _qpnp_pwm_config *pwm_config = &chip->pwm_config; int i, pwm_size, rc = 0; int burst_size = SPMI_MAX_BUF_LEN; int list_len = lut->list_len << 1; @@ -596,6 +606,9 @@ static int qpnp_lpg_change_table(struct qpnp_pwm_chip *chip, QPNP_MIN_PWM_BIT_SIZE; max_pwm_value = (1 << pwm_size) - 1; + if (pwm_config->pwm_max_value + && (max_pwm_value > pwm_config->pwm_max_value)) + max_pwm_value = pwm_config->pwm_max_value; if (unlikely(lut->list_len != (lut->hi_index - lut->lo_index + 1))) { pr_err("LUT internal Data structure corruption detected\n"); @@ -693,6 +706,9 @@ static int qpnp_lpg_save_pwm_value(struct qpnp_pwm_chip *chip) if (pwm_config->pwm_value > max_pwm_value) pwm_config->pwm_value = max_pwm_value; + if (pwm_config->pwm_max_value + && (pwm_config->pwm_value > pwm_config->pwm_max_value)) + pwm_config->pwm_value = pwm_config->pwm_max_value; value = pwm_config->pwm_value; mask = QPNP_PWM_VALUE_LSB_MASK; @@ -839,6 +855,23 @@ static int qpnp_configure_lpg_control(struct qpnp_pwm_chip *chip) } +static int qpnp_configure_enable_lpg(struct qpnp_pwm_chip *chip) +{ + struct qpnp_lpg_config *lpg_config = &chip->lpg_config; + u8 value, mask; + + value = QPNP_ENABLE_LPG_MODE(chip); + + mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK | + QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK | + QPNP_PWM_EN_RAMP_GEN_MASK; + + return qpnp_lpg_save_and_write(value, mask, + &chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL], + SPMI_LPG_REG_ADDR(lpg_config->base_addr, + QPNP_ENABLE_CONTROL), 1, chip); +} + static int qpnp_lpg_configure_ramp_step_duration(struct qpnp_pwm_chip *chip) { struct qpnp_lpg_config *lpg_config = &chip->lpg_config; @@ -846,7 +879,7 @@ static int qpnp_lpg_configure_ramp_step_duration(struct qpnp_pwm_chip *chip) int rc, value; u8 val, mask; - value = QPNP_GET_RAMP_STEP_DURATION(lut_config.ramp_step_ms); + value = lut_config.ramp_step_ms; val = value & QPNP_RAMP_STEP_DURATION_LSB_MASK; mask = QPNP_RAMP_STEP_DURATION_LSB_MASK; @@ -1315,6 +1348,60 @@ after_table_write: return rc; } +static int _pwm_config_lut(struct qpnp_pwm_chip *chip, + struct lut_config *pwm_lut) +{ + struct qpnp_lpg_config *lpg_config; + struct qpnp_lut_config *lut_config; + struct _qpnp_pwm_config *pwm_config; + int flags = pwm_lut->flags; + int raw_lut, ramp_step_ms; + int rc = 0; + + pwm_config = &chip->pwm_config; + lpg_config = &chip->lpg_config; + lut_config = &lpg_config->lut_config; + + raw_lut = 1; + + lut_config->list_len = pwm_lut->hi_index - pwm_lut->lo_index + 1; + if (lut_config->list_len < 0) { + pr_err("%s wrong LUT index\n", __func__); + return -EINVAL; + } + lut_config->lo_index = pwm_lut->lo_index; + lut_config->hi_index = pwm_lut->hi_index; + + rc = qpnp_lpg_change_table(chip, pwm_lut->lut, raw_lut); + if (rc) { + pr_err("qpnp_lpg_change_table: rc=%d\n", rc); + return -EINVAL; + } + + ramp_step_ms = pwm_lut->ramp_step_ms; + + lut_config->lut_pause_lo_cnt = pwm_lut->lut_pause_lo; + lut_config->lut_pause_hi_cnt = pwm_lut->lut_pause_hi; + + lut_config->ramp_step_ms = ramp_step_ms; + + lut_config->ramp_direction = !!(flags & PM_PWM_LUT_RAMP_UP); + lut_config->pattern_repeat = !!(flags & PM_PWM_LUT_LOOP); + lut_config->ramp_toggle = !!(flags & PM_PWM_LUT_REVERSE); + lut_config->enable_pause_hi = !!(flags & PM_PWM_LUT_PAUSE_HI_EN); + lut_config->enable_pause_lo = !!(flags & PM_PWM_LUT_PAUSE_LO_EN); + + rc = qpnp_lpg_change_lut(chip); + if (rc) { + pr_err("qpnp_lpg_change_lut: rc=%d\n", rc); + return -EINVAL; + } + + rc = qpnp_configure_enable_lpg(chip); + + return rc; +} + /* lpg_lock should be held while calling _pwm_enable() */ static int _pwm_enable(struct qpnp_pwm_chip *chip) { @@ -1741,6 +1828,131 @@ int pwm_lut_config(struct pwm_device *pwm, int period_us, } EXPORT_SYMBOL(pwm_lut_config); +/** + * pwm_config_lut - change LPG LUT device configuration + * @pwm: the PWM device + * @pwm_lut: LUT config + */ +int pwm_config_lut(struct pwm_device *pwm, + struct lut_config *pwm_lut) +{ + unsigned long flags; + struct qpnp_pwm_chip *chip; + int rc = 0; + + if (pwm == NULL || IS_ERR(pwm)) { + pr_err("Invalid pwm handle\n"); + return -EINVAL; + } + + if (pwm_lut == NULL) { + pr_err("Invalid pwm_lut handle\n"); + return -EINVAL; + } + + chip = qpnp_pwm_from_pwm_dev(pwm); + + spin_lock_irqsave(&chip->lpg_lock, flags); + + rc = _pwm_config_lut(chip, pwm_lut); + + spin_unlock_irqrestore(&chip->lpg_lock, flags); + + if (rc) + pr_err("Failed to configure LUT\n"); + + return rc; +} +EXPORT_SYMBOL_GPL(pwm_config_lut); + +/** + * pwm_start_lut_ramp - start LUT ramp to sync + * + * @pwm: the PWM device + * @ramp_control: set bit corresponding to LPG channel. + */ +int pwm_start_lut_ramp(struct pwm_device *pwm, int ramp_control) +{ + struct qpnp_lpg_config *lpg_config; + struct qpnp_pwm_chip *chip; + u8 value, mask; + u8 *reg; + u16 addr; + + chip = qpnp_pwm_from_pwm_dev(pwm); + lpg_config = &chip->lpg_config; + + value = chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL]; + reg = &chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL]; + + mask = QPNP_LUT_RAMP_CONTROL_MASK; + value = mask & ramp_control; + + addr = lpg_config->lut_base_addr + + SPMI_LPG_REV1_RAMP_CONTROL_OFFSET; + + return qpnp_lpg_save_and_write(value, mask, reg, + addr, 1, chip); +} +EXPORT_SYMBOL_GPL(pwm_start_lut_ramp); + +/** + * pwm_config_period_value - change PWM period and pwm value + * + * @pwm: the PWM device + * @pwm_p: period in struct qpnp_lpg_period + * @pwm_value: PWM value + */ +int pwm_config_period_value(struct pwm_device *pwm, + struct pwm_period_config *pwm_p, int pwm_value) +{ + unsigned long flags; + struct qpnp_pwm_chip *chip; + int rc = pwm_config_period(pwm, pwm_p); + + if (rc) + return rc; + chip = qpnp_pwm_from_pwm_dev(pwm); + + spin_lock_irqsave(&chip->lpg_lock, flags); + /* need check argment */ + chip->pwm_config.pwm_value = pwm_value; + rc = qpnp_lpg_save_pwm_value(chip); + spin_unlock_irqrestore(&chip->lpg_lock, flags); + + return rc; +} +EXPORT_SYMBOL_GPL(pwm_config_period_value); + +int pwm_get_max_pwm_value(struct pwm_device *pwm) +{ + unsigned long flags; + struct qpnp_pwm_chip *chip; + int max; + + chip = qpnp_pwm_from_pwm_dev(pwm); + + spin_lock_irqsave(&chip->lpg_lock, flags); + max = chip->pwm_config.pwm_max_value; + spin_unlock_irqrestore(&chip->lpg_lock, flags); + + return max; +} +EXPORT_SYMBOL_GPL(pwm_get_max_pwm_value); + +void pwm_set_max_pwm_value(struct pwm_device *pwm, int max) +{ + unsigned long flags; + struct qpnp_pwm_chip *chip; + + chip = qpnp_pwm_from_pwm_dev(pwm); + + spin_lock_irqsave(&chip->lpg_lock, flags); + chip->pwm_config.pwm_max_value = max; + spin_unlock_irqrestore(&chip->lpg_lock, flags); +} +EXPORT_SYMBOL_GPL(pwm_set_max_pwm_value); + static int qpnp_parse_pwm_dt_config(struct device_node *of_pwm_node, struct device_node *of_parent, struct qpnp_pwm_chip *chip) { @@ -2048,6 +2260,10 @@ static int qpnp_parse_dt_config(struct platform_device *pdev, kfree(lut_config->duty_pct_list); return rc; } + rc = of_property_read_u32(of_node, "qcom,pwm-max-value", + &chip->pwm_config.pwm_max_value); + if (rc) + chip->pwm_config.pwm_max_value = 0; } rc = of_property_read_u32(of_node, "qcom,dtest-line", diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 068c7ddfb73912cc67248be9802fb1de749aa195..ab542179b3ec7a216891c2a469ebbbfa33825599 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1003,5 +1003,9 @@ config REGULATOR_STUB Clients can use the real regulator device names with proper constraint checking while the real driver is being developed. +config SOMC_LCD_OCP_ENABLED + bool "enable SoMC LCD OCP function" + help + Select this to enable SoMC LCD Over current protection function. endif diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 188920367e842fd4d21a032ff3c3412befbe7ac1..775496960e12acc3d6670d05b01b7e210a704bb5 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -12,6 +12,11 @@ * option) any later version. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -3454,6 +3459,33 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable) } EXPORT_SYMBOL_GPL(regulator_allow_bypass); +/* + * regulator_register_ocp_notification - register ocp notification + * @regulator: regulator source + * @notification: pointer of client ocp_notification + * + */ +int regulator_register_ocp_notification(struct regulator *regulator, + struct regulator_ocp_notification *notification) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->register_ocp_notification) { + ret = -EINVAL; + goto out; + } + + ret = rdev->desc->ops->register_ocp_notification(rdev, notification); +out: + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_register_ocp_notification); + /** * regulator_register_notifier - register regulator event notifier * @regulator: regulator source diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index f9d77b4c44ef8fa491910ba57cd3eb71901b861b..11919cfce9427d8111f0d7de01b956d45894663d 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -9,6 +9,11 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c index 4cef8904a76aee33bcc31e47fc6a885b035e6180..4439a565d1dd231fcb05091338c7f95f656f1c79 100644 --- a/drivers/regulator/qpnp-labibb-regulator.c +++ b/drivers/regulator/qpnp-labibb-regulator.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -17,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -31,6 +35,11 @@ #include #include #include +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +#include +#include +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + #include #include @@ -38,7 +47,6 @@ #define REG_REVISION_2 0x01 #define REG_PERPH_TYPE 0x04 -#define REG_INT_RT_STS 0x10 #define QPNP_LAB_TYPE 0x24 #define QPNP_IBB_TYPE 0x20 @@ -51,6 +59,14 @@ /* LAB register offset definitions */ #define REG_LAB_STATUS1 0x08 +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +#define REG_LAB_INT_SET_TYPE 0x11 +#define REG_LAB_INT_POLARITY_HIGH 0x12 +#define REG_LAB_INT_POLARITY_LOW 0x13 +#define REG_LAB_INT_LATCHED_CLR 0x14 +#define REG_LAB_INT_EN_SET 0x15 +#define REG_LAB_INT_EN_CLR 0x16 +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ #define REG_LAB_SWIRE_PGM_CTL 0x40 #define REG_LAB_VOLTAGE 0x41 #define REG_LAB_RING_SUPPRESSION_CTL 0x42 @@ -78,8 +94,35 @@ /* LAB register bits definitions */ /* REG_LAB_STATUS1 */ -#define LAB_STATUS1_VREG_OK_BIT BIT(7) -#define LAB_STATUS1_SC_DETECT_BIT BIT(6) +#define LAB_STATUS1_VREG_OK_MASK BIT(7) +#define LAB_STATUS1_VREG_OK BIT(7) + +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +/* REG_LAB_INT_SET_TYPE */ +#define LAB_INT_SET_TYPE_VREG_OK_MASK BIT(0) +#define LAB_INT_SET_TYPE_VREG_OK_LEVEL 0 +#define LAB_INT_SET_TYPE_VREG_OK_EDGE 1 + +/* REG_LAB_INT_POLARITY_HIGH */ +#define LAB_INT_PRY_HIGH_VREG_OK_MASK BIT(0) +#define LAB_INT_PRY_HIGH_VREG_OK 0 + +/* REG_LAB_INT_POLARITY_LOW */ +#define LAB_INT_PRY_LOW_VREG_OK_MASK BIT(0) +#define LAB_INT_PRY_LOW_VREG_OK 1 + +/* REG_LAB_INT_LATCHED_CLR */ +#define LAB_INT_LATCHED_CLR_VREG_MASK BIT(0) +#define LAB_INT_LATCHED_CLR_VREG 1 + +/* REG_LAB_INT_EN_SET */ +#define LAB_INT_EN_SET_VREG_OK_MASK BIT(0) +#define LAB_INT_EN_SET_VREG_OK 1 + +/* REG_LAB_INT_EN_CLR */ +#define LAB_INT_EN_CLR_VREG_OK_MASK BIT(0) +#define LAB_INT_EN_CLR_VREG_OK 1 +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ /* REG_LAB_SWIRE_PGM_CTL */ #define LAB_EN_SWIRE_PGM_VOUT BIT(7) @@ -112,6 +155,10 @@ #define LAB_CURRENT_LIMIT_EN_BIT BIT(7) #define LAB_OVERRIDE_CURRENT_MAX_BIT BIT(3) +#ifdef SOMC_LABIBB_REGULATOR_ORG_IMPL +#define LAB_CURRENT_LIMIT_OVERRIDE BIT(3) +#endif /* SOMC_LABIBB_REGULATOR_ORG_IMPL */ + /* REG_LAB_CURRENT_SENSE */ #define LAB_CURRENT_SENSE_GAIN_MASK GENMASK(1, 0) @@ -154,6 +201,14 @@ /* IBB register offset definitions */ #define REG_IBB_REVISION4 0x03 #define REG_IBB_STATUS1 0x08 +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +#define REG_IBB_INT_SET_TYPE 0x11 +#define REG_IBB_INT_POLARITY_HIGH 0x12 +#define REG_IBB_INT_POLARITY_LOW 0x13 +#define REG_IBB_INT_LATCHED_CLR 0x14 +#define REG_IBB_INT_EN_SET 0x15 +#define REG_IBB_INT_EN_CLR 0x16 +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ #define REG_IBB_VOLTAGE 0x41 #define REG_IBB_RING_SUPPRESSION_CTL 0x42 #define REG_IBB_LCD_AMOLED_SEL 0x44 @@ -186,8 +241,35 @@ /* IBB register bits definition */ /* REG_IBB_STATUS1 */ -#define IBB_STATUS1_VREG_OK_BIT BIT(7) -#define IBB_STATUS1_SC_DETECT_BIT BIT(6) +#define IBB_STATUS1_VREG_OK_MASK BIT(7) +#define IBB_STATUS1_VREG_OK BIT(7) + +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +/* REG_IBB_INT_SET_TYPE */ +#define IBB_INT_SET_TYPE_VREG_OK_MASK BIT(0) +#define IBB_INT_SET_TYPE_VREG_OK_LEVEL 0 +#define IBB_INT_SET_TYPE_VREG_OK_EDGE 1 + +/* REG_IBB_INT_POLARITY_HIGH */ +#define IBB_INT_PRY_HIGH_VREG_OK_MASK BIT(0) +#define IBB_INT_PRY_HIGH_VREG_OK 1 + +/* REG_IBB_INT_POLARITY_LOW */ +#define IBB_INT_PRY_LOW_VREG_OK_MASK BIT(0) +#define IBB_INT_PRY_LOW_VREG_OK 0 + +/* REG_IBB_INT_LATCHED_CLR */ +#define IBB_INT_LATCHED_CLR_VREG_MASK BIT(0) +#define IBB_INT_LATCHED_CLR_VREG 1 + +/* REG_IBB_INT_EN_SET */ +#define IBB_INT_EN_SET_VREG_OK_MASK BIT(0) +#define IBB_INT_EN_SET_VREG_OK 1 + +/* REG_IBB_INT_EN_CLR */ +#define IBB_INT_EN_CLR_VREG_OK_MASK BIT(0) +#define IBB_INT_EN_CLR_VREG_OK 1 +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ /* REG_IBB_VOLTAGE */ #define IBB_VOLTAGE_OVERRIDE_EN BIT(7) @@ -230,6 +312,10 @@ #define IBB_ILIMIT_COUNT_CYC8 0 #define IBB_CURRENT_MAX_500MA 0xA +#ifdef SOMC_LABIBB_REGULATOR_ORG_IMPL +#define IBB_CURRENT_LIMIT_VALUE 16 +#endif /* SOMC_LABIBB_REGULATOR_ORG_IMPL */ + /* REG_IBB_PS_CTL */ #define IBB_PS_CTL_EN 0x85 @@ -306,6 +392,51 @@ #define IBB_DIS_DLY_MASK GENMASK(1, 0) #define IBB_WAIT_MBG_OK BIT(2) +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +/* enable_irq */ +#define ENABLE_IRQ(irq_num) \ +do { \ + pr_debug("ENABLE_IRQ: %s\n", __func__); \ + enable_irq(irq_num); \ +} while (0) + +/* disable_irq */ +#define DISABLE_IRQ(irq_num) \ +do { \ + pr_debug("DISABLE_IRQ_NOSYNC: %s\n", __func__); \ + disable_irq_nosync(irq_num); \ +} while (0) + +#define IRQF_LAB_FLAGS (IRQF_DISABLED | IRQF_ONESHOT | IRQF_TRIGGER_LOW) +#define IRQF_IBB_FLAGS (IRQF_DISABLED | IRQF_ONESHOT | IRQF_TRIGGER_HIGH) + +#define CHATTER_CNT_START 1 +#define DEFAULT_TARGET_CHATTER_CNT 3 +#define DEFAULT_TARGET_CHATTER_INTERVAL 500 +#define POWER_OFF_RETRY_INTERVAL 500 + +#define VREG_WORKER_ACTIVE true +#define VREG_WORKER_PASSIVE false + +static irqreturn_t ibb_vreg_handler(int irq, void *_chip); +static irqreturn_t lab_vreg_handler(int irq, void *_chip); + +static struct labibb_vreg_status_ctrl { + struct delayed_work vreg_check_work; + struct qpnp_labibb *labibb; + int current_chatter_cnt; + int target_chatter_cnt; + int target_chatter_check_interval; + bool vreg_check_working; + bool ocp_lab_detected; + bool ocp_ibb_detected; +} labibb_vreg_check; + +static int qpnp_ibb_register_irq(struct device_node *child, + struct qpnp_labibb *labibb); + +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + /* Constants */ #define SWIRE_DEFAULT_2ND_CMD_DLY_MS 20 #define SWIRE_DEFAULT_IBB_PS_ENABLE_DLY_MS 200 @@ -321,6 +452,9 @@ * supported by qpnp_labibb_regulator */ enum qpnp_labibb_mode { +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + QPNP_LABIBB_STANDALONE_MODE = 1, +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ QPNP_LABIBB_LCD_MODE, QPNP_LABIBB_AMOLED_MODE, QPNP_LABIBB_MAX_MODE, @@ -555,8 +689,6 @@ struct lab_regulator { struct mutex lab_mutex; int lab_vreg_ok_irq; - int lab_sc_irq; - int curr_volt; int min_volt; @@ -573,8 +705,6 @@ struct ibb_regulator { struct regulator_dev *rdev; struct mutex ibb_mutex; - int ibb_sc_irq; - int curr_volt; int min_volt; @@ -605,9 +735,10 @@ struct qpnp_labibb { struct mutex bus_mutex; enum qpnp_labibb_mode mode; struct work_struct lab_vreg_ok_work; - struct delayed_work sc_err_recovery_work; - struct hrtimer sc_err_check_timer; - int sc_err_count; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + int lab_vreg_irq; + int ibb_vreg_irq; +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ bool standalone; bool ttw_en; bool in_ttw_mode; @@ -1415,6 +1546,301 @@ static int qpnp_labibb_get_matching_idx(const char *val) return -EINVAL; } +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +static int qpnp_lab_interrupt_enable_ctl(struct qpnp_labibb *labibb, + bool inWork) +{ + int rc = 0; + + if (labibb_vreg_check.vreg_check_working && !inWork) { + pr_debug("%s: vreg_check_worker is already being processed.\n", + __func__); + goto exit; + } + + /* lab int latched clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_LATCHED_CLR, + LAB_INT_LATCHED_CLR_VREG_MASK, + LAB_INT_LATCHED_CLR_VREG); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_LATCHED_CLR, rc); + goto exit; + } + /* lab int en set */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_EN_SET, + LAB_INT_EN_SET_VREG_OK_MASK, + LAB_INT_EN_SET_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_EN_SET, rc); + goto exit; + } + ENABLE_IRQ(labibb->lab_vreg_irq); +exit: + return rc; +} + +static int qpnp_lab_interrupt_disable_ctl(struct qpnp_labibb *labibb) +{ + int rc = 0; + + if (labibb_vreg_check.vreg_check_working) { + pr_debug("%s: vreg_check_worker is already being processed.\n", + __func__); + goto exit; + } + + DISABLE_IRQ(labibb->lab_vreg_irq); + /* lab int en clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_EN_CLR, + LAB_INT_EN_CLR_VREG_OK_MASK, + LAB_INT_EN_CLR_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_EN_CLR, rc); + goto exit; + } +exit: + return rc; +} + +static int qpnp_ibb_interrupt_enable_ctl(struct qpnp_labibb *labibb, + bool inWork) +{ + int rc = 0; + + if (labibb_vreg_check.vreg_check_working && !inWork) { + pr_debug("%s: vreg_check_worker is already being processed.\n", + __func__); + goto exit; + } + + /* ibb int latched clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_LATCHED_CLR, + IBB_INT_LATCHED_CLR_VREG_MASK, + IBB_INT_LATCHED_CLR_VREG); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_LATCHED_CLR, rc); + goto exit; + } + /* ibb int en set */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_EN_SET, + IBB_INT_EN_SET_VREG_OK_MASK, + IBB_INT_EN_SET_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_EN_SET, rc); + goto exit; + } + ENABLE_IRQ(labibb->ibb_vreg_irq); +exit: + return rc; +} + +static int qpnp_ibb_interrupt_disable_ctl(struct qpnp_labibb *labibb) +{ + int rc = 0; + + if (labibb_vreg_check.vreg_check_working) { + pr_debug("%s: vreg_check_worker is already being processed.\n", + __func__); + goto exit; + } + + DISABLE_IRQ(labibb->ibb_vreg_irq); + /* ibb int en clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_EN_CLR, + IBB_INT_EN_CLR_VREG_OK_MASK, + IBB_INT_EN_CLR_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_EN_CLR, rc); + goto exit; + } +exit: + return rc; +} + +static int qpnp_labibb_interrupt_enable_ctl(struct qpnp_labibb *labibb, + bool inWork) +{ + int rc; + + rc = qpnp_lab_interrupt_enable_ctl(labibb, inWork); + if (rc) { + pr_err("%s: qpnp_lab_interrupt_enable_ctl failed rc = %d\n", + __func__, rc); + goto exit; + } + + rc = qpnp_ibb_interrupt_enable_ctl(labibb, inWork); + if (rc) { + pr_err("%s: qpnp_ibb_interrupt_enable_ctl failed rc = %d\n", + __func__, rc); + goto exit; + } + +exit: + return rc; +} + +static int qpnp_labibb_interrupt_disable_ctl(struct qpnp_labibb *labibb) +{ + int rc; + + rc = qpnp_lab_interrupt_disable_ctl(labibb); + if (rc) { + pr_err("%s: qpnp_lab_interrupt_disable_ctl failed rc = %d\n", + __func__, rc); + goto exit; + } + + rc = qpnp_ibb_interrupt_disable_ctl(labibb); + if (rc) { + pr_err("%s: qpnp_ibb_interrupt_disable_ctl failed rc = %d\n", + __func__, rc); + goto exit; + } + +exit: + return rc; +} + +static int qpnp_lab_request_interrupt(struct qpnp_labibb *labibb) +{ + int rc = 0; + + /* lab int set type */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_SET_TYPE, + LAB_INT_SET_TYPE_VREG_OK_MASK, + LAB_INT_SET_TYPE_VREG_OK_LEVEL); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_SET_TYPE, rc); + goto exit; + } + + /* lab int polarity high */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_POLARITY_HIGH, + LAB_INT_PRY_HIGH_VREG_OK_MASK, + LAB_INT_PRY_HIGH_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_POLARITY_HIGH, rc); + goto exit; + } + + /* lab int polarity low */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_POLARITY_LOW, + LAB_INT_PRY_LOW_VREG_OK_MASK, + LAB_INT_PRY_LOW_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_POLARITY_LOW, rc); + goto exit; + } + + /* lab int en set */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_EN_SET, + LAB_INT_EN_SET_VREG_OK_MASK, + LAB_INT_EN_SET_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_EN_SET, rc); + goto exit; + } + + /* lab int latched clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_LATCHED_CLR, + LAB_INT_LATCHED_CLR_VREG_MASK, + LAB_INT_LATCHED_CLR_VREG); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_LATCHED_CLR, rc); + goto exit; + } + +exit: + return rc; +} + +static int qpnp_ibb_request_interrupt(struct qpnp_labibb *labibb) +{ + int rc = 0; + + /* ibb int set type */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_SET_TYPE, + IBB_INT_SET_TYPE_VREG_OK_MASK, + IBB_INT_SET_TYPE_VREG_OK_LEVEL); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_SET_TYPE, rc); + goto exit; + } + + /* ibb int polarity high */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_POLARITY_HIGH, + IBB_INT_PRY_HIGH_VREG_OK_MASK, + IBB_INT_PRY_HIGH_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_POLARITY_HIGH, rc); + goto exit; + } + + /* IBB int polarity low */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_POLARITY_LOW, + IBB_INT_PRY_LOW_VREG_OK_MASK, + IBB_INT_PRY_LOW_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_POLARITY_LOW, rc); + goto exit; + } + + /* ibb int en set */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_EN_SET, + IBB_INT_EN_SET_VREG_OK_MASK, + IBB_INT_EN_SET_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_EN_SET, rc); + goto exit; + } + + /* ibb int latched clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_LATCHED_CLR, + IBB_INT_LATCHED_CLR_VREG_MASK, + IBB_INT_LATCHED_CLR_VREG); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_LATCHED_CLR, rc); + goto exit; + } + +exit: + return rc; +} +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + static int qpnp_ibb_set_mode(struct qpnp_labibb *labibb, enum ibb_mode mode) { int rc; @@ -1521,6 +1947,9 @@ static int qpnp_lab_dt_init(struct qpnp_labibb *labibb, if (of_property_read_bool(of_node, "qcom,qpnp-lab-limit-max-current-enable")) { val = LAB_CURRENT_LIMIT_EN_BIT; +#ifdef SOMC_LABIBB_REGULATOR_ORG_IMPL + val |= LAB_CURRENT_LIMIT_OVERRIDE; +#endif /* SOMC_LABIBB_REGULATOR_ORG_IMPL */ rc = of_property_read_u32(of_node, "qcom,qpnp-lab-limit-maximum-current", &tmp); @@ -2171,7 +2600,7 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work) return; } - if (val & LAB_STATUS1_VREG_OK_BIT) { + if (val & LAB_STATUS1_VREG_OK) { raw_notifier_call_chain(&labibb_notifier, LAB_VREG_OK, NULL); break; @@ -2204,74 +2633,6 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work) } } -static int qpnp_lab_enable_standalone(struct qpnp_labibb *labibb) -{ - int rc; - u8 val; - - val = LAB_ENABLE_CTL_EN; - rc = qpnp_labibb_write(labibb, - labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); - if (rc < 0) { - pr_err("Write register %x failed rc = %d\n", - REG_LAB_ENABLE_CTL, rc); - return rc; - } - - udelay(labibb->lab_vreg.soft_start); - - rc = qpnp_labibb_read(labibb, labibb->lab_base + - REG_LAB_STATUS1, &val, 1); - if (rc < 0) { - pr_err("Read register %x failed rc = %d\n", - REG_LAB_STATUS1, rc); - return rc; - } - - if (!(val & LAB_STATUS1_VREG_OK_BIT)) { - pr_err("Can't enable LAB standalone\n"); - return -EINVAL; - } - - return 0; -} - -static int qpnp_ibb_enable_standalone(struct qpnp_labibb *labibb) -{ - int rc, delay, retries = 10; - u8 val; - - rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN); - if (rc < 0) { - pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); - return rc; - } - - delay = labibb->ibb_vreg.soft_start; - while (retries--) { - /* Wait for a small period before reading IBB_STATUS1 */ - usleep_range(delay, delay + 100); - - rc = qpnp_labibb_read(labibb, labibb->ibb_base + - REG_IBB_STATUS1, &val, 1); - if (rc < 0) { - pr_err("Read register %x failed rc = %d\n", - REG_IBB_STATUS1, rc); - return rc; - } - - if (val & IBB_STATUS1_VREG_OK_BIT) - break; - } - - if (!(val & IBB_STATUS1_VREG_OK_BIT)) { - pr_err("Can't enable IBB standalone\n"); - return -EINVAL; - } - - return 0; -} - static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) { int rc; @@ -2313,7 +2674,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) labibb->lab_vreg.soft_start, labibb->ibb_vreg.soft_start, labibb->ibb_vreg.pwrup_dly, dly); - if (!(val & LAB_STATUS1_VREG_OK_BIT)) { + if (!(val & LAB_STATUS1_VREG_OK)) { pr_err("failed for LAB %x\n", val); goto err_out; } @@ -2330,7 +2691,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) goto err_out; } - if (val & IBB_STATUS1_VREG_OK_BIT) { + if (val & IBB_STATUS1_VREG_OK) { enabled = true; break; } @@ -2344,6 +2705,9 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) labibb->lab_vreg.vreg_enabled = 1; labibb->ibb_vreg.vreg_enabled = 1; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_labibb_interrupt_enable_ctl(labibb, VREG_WORKER_PASSIVE); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ return 0; err_out: @@ -2363,6 +2727,10 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb) int retries; bool disabled = false; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_labibb_interrupt_disable_ctl(labibb); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + /* * When TTW mode is enabled and LABIBB regulators are disabled, it is * recommended not to disable IBB through IBB_ENABLE_CTL when switching @@ -2401,7 +2769,7 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb) return rc; } - if (!(val & IBB_STATUS1_VREG_OK_BIT)) { + if (!(val & IBB_STATUS1_VREG_OK)) { disabled = true; break; } @@ -2430,6 +2798,8 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb) static int qpnp_lab_regulator_enable(struct regulator_dev *rdev) { int rc; + u8 val; + struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->sc_detected) { @@ -2446,15 +2816,38 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev) } if (!labibb->lab_vreg.vreg_enabled && !labibb->swire_control) { + if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); - rc = qpnp_lab_enable_standalone(labibb); - if (rc) { - pr_err("enable lab standalone failed, rc=%d\n", rc); + val = LAB_ENABLE_CTL_EN; + rc = qpnp_labibb_write(labibb, + labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); + if (rc < 0) { + pr_err("qpnp_lab_regulator_enable write register %x failed rc = %d\n", + REG_LAB_ENABLE_CTL, rc); + return rc; + } + + udelay(labibb->lab_vreg.soft_start); + + rc = qpnp_labibb_read(labibb, labibb->lab_base + + REG_LAB_STATUS1, &val, 1); + if (rc < 0) { + pr_err("qpnp_lab_regulator_enable read register %x failed rc = %d\n", + REG_LAB_STATUS1, rc); return rc; } + + if ((val & LAB_STATUS1_VREG_OK_MASK) != LAB_STATUS1_VREG_OK) { + pr_err("qpnp_lab_regulator_enable failed\n"); + return -EINVAL; + } + labibb->lab_vreg.vreg_enabled = 1; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_lab_interrupt_enable_ctl(labibb, VREG_WORKER_PASSIVE); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ } if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc) @@ -2473,6 +2866,9 @@ static int qpnp_lab_regulator_disable(struct regulator_dev *rdev) if (!labibb->standalone) return qpnp_labibb_regulator_disable(labibb); +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_lab_interrupt_disable_ctl(labibb); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ val = 0; rc = qpnp_labibb_write(labibb, @@ -2498,188 +2894,31 @@ static int qpnp_lab_regulator_is_enabled(struct regulator_dev *rdev) return labibb->lab_vreg.vreg_enabled; } -static int qpnp_labibb_force_enable(struct qpnp_labibb *labibb) +static int qpnp_lab_regulator_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned int *selector) { - int rc; + int rc, new_uV; + u8 val; + struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); - if (labibb->skip_2nd_swire_cmd) { - rc = qpnp_ibb_ps_config(labibb, false); - if (rc < 0) { - pr_err("Failed to disable IBB PS rc=%d\n", rc); - return rc; - } + if (labibb->swire_control) + return 0; + + if (min_uV < labibb->lab_vreg.min_volt) { + pr_err("min_uV %d is less than min_volt %d", min_uV, + labibb->lab_vreg.min_volt); + return -EINVAL; } - if (!labibb->swire_control) { - if (!labibb->standalone) - return qpnp_labibb_regulator_enable(labibb); + val = DIV_ROUND_UP(min_uV - labibb->lab_vreg.min_volt, + labibb->lab_vreg.step_size); + new_uV = val * labibb->lab_vreg.step_size + labibb->lab_vreg.min_volt; - rc = qpnp_ibb_enable_standalone(labibb); - if (rc < 0) { - pr_err("enable ibb standalone failed, rc=%d\n", rc); - return rc; - } - labibb->ibb_vreg.vreg_enabled = 1; - - rc = qpnp_lab_enable_standalone(labibb); - if (rc < 0) { - pr_err("enable lab standalone failed, rc=%d\n", rc); - return rc; - } - labibb->lab_vreg.vreg_enabled = 1; - } - - return 0; -} - -#define SC_ERR_RECOVERY_DELAY_MS 250 -#define SC_ERR_COUNT_INTERVAL_SEC 1 -#define POLLING_SCP_DONE_COUNT 2 -#define POLLING_SCP_DONE_INTERVAL_MS 5 -static irqreturn_t labibb_sc_err_handler(int irq, void *_labibb) -{ - int rc; - u16 reg; - u8 sc_err_mask, val; - char *str; - struct qpnp_labibb *labibb = (struct qpnp_labibb *)_labibb; - bool in_sc_err, lab_en, ibb_en, scp_done = false; - int count; - - if (irq == labibb->lab_vreg.lab_sc_irq) { - reg = labibb->lab_base + REG_LAB_STATUS1; - sc_err_mask = LAB_STATUS1_SC_DETECT_BIT; - str = "LAB"; - } else if (irq == labibb->ibb_vreg.ibb_sc_irq) { - reg = labibb->ibb_base + REG_IBB_STATUS1; - sc_err_mask = IBB_STATUS1_SC_DETECT_BIT; - str = "IBB"; - } else { - return IRQ_HANDLED; - } - - rc = qpnp_labibb_read(labibb, reg, &val, 1); - if (rc < 0) { - pr_err("Read 0x%x failed, rc=%d\n", reg, rc); - return IRQ_HANDLED; - } - pr_debug("%s SC error triggered! %s_STATUS1 = %d\n", str, str, val); - - in_sc_err = !!(val & sc_err_mask); - - /* - * The SC fault would trigger PBS to disable regulators - * for protection. This would cause the SC_DETECT status being - * cleared so that it's not able to get the SC fault status. - * Check if LAB/IBB regulators are enabled in the driver but - * disabled in hardware, this means a SC fault had happened - * and SCP handling is completed by PBS. - */ - if (!in_sc_err) { - count = POLLING_SCP_DONE_COUNT; - do { - reg = labibb->lab_base + REG_LAB_ENABLE_CTL; - rc = qpnp_labibb_read(labibb, reg, &val, 1); - if (rc < 0) { - pr_err("Read 0x%x failed, rc=%d\n", reg, rc); - return IRQ_HANDLED; - } - lab_en = !!(val & LAB_ENABLE_CTL_EN); - - reg = labibb->ibb_base + REG_IBB_ENABLE_CTL; - rc = qpnp_labibb_read(labibb, reg, &val, 1); - if (rc < 0) { - pr_err("Read 0x%x failed, rc=%d\n", reg, rc); - return IRQ_HANDLED; - } - ibb_en = !!(val & IBB_ENABLE_CTL_MODULE_EN); - if (lab_en || ibb_en) - msleep(POLLING_SCP_DONE_INTERVAL_MS); - else - break; - } while ((lab_en || ibb_en) && count--); - - if (labibb->lab_vreg.vreg_enabled - && labibb->ibb_vreg.vreg_enabled - && !lab_en && !ibb_en) { - pr_debug("LAB/IBB has been disabled by SCP\n"); - scp_done = true; - } - } - - if (in_sc_err || scp_done) { - if (hrtimer_active(&labibb->sc_err_check_timer) || - hrtimer_callback_running(&labibb->sc_err_check_timer)) { - labibb->sc_err_count++; - } else { - labibb->sc_err_count = 1; - hrtimer_start(&labibb->sc_err_check_timer, - ktime_set(SC_ERR_COUNT_INTERVAL_SEC, 0), - HRTIMER_MODE_REL); - } - schedule_delayed_work(&labibb->sc_err_recovery_work, - msecs_to_jiffies(SC_ERR_RECOVERY_DELAY_MS)); - } - - return IRQ_HANDLED; -} - -#define SC_FAULT_COUNT_MAX 4 -static enum hrtimer_restart labibb_check_sc_err_count(struct hrtimer *timer) -{ - struct qpnp_labibb *labibb = container_of(timer, - struct qpnp_labibb, sc_err_check_timer); - /* - * if SC fault triggers more than 4 times in 1 second, - * then disable the IRQs and leave as it. - */ - if (labibb->sc_err_count > SC_FAULT_COUNT_MAX) { - disable_irq(labibb->lab_vreg.lab_sc_irq); - disable_irq(labibb->ibb_vreg.ibb_sc_irq); - } - - return HRTIMER_NORESTART; -} - -static void labibb_sc_err_recovery_work(struct work_struct *work) -{ - struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb, - sc_err_recovery_work.work); - int rc; - - labibb->ibb_vreg.vreg_enabled = 0; - labibb->lab_vreg.vreg_enabled = 0; - rc = qpnp_labibb_force_enable(labibb); - if (rc < 0) - pr_err("force enable labibb failed, rc=%d\n", rc); - -} - -static int qpnp_lab_regulator_set_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV, unsigned int *selector) -{ - int rc, new_uV; - u8 val; - struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); - - if (labibb->swire_control) - return 0; - - if (min_uV < labibb->lab_vreg.min_volt) { - pr_err("min_uV %d is less than min_volt %d", min_uV, - labibb->lab_vreg.min_volt); - return -EINVAL; - } - - val = DIV_ROUND_UP(min_uV - labibb->lab_vreg.min_volt, - labibb->lab_vreg.step_size); - new_uV = val * labibb->lab_vreg.step_size + labibb->lab_vreg.min_volt; - - if (new_uV > max_uV) { - pr_err("unable to set voltage %d (min:%d max:%d)\n", new_uV, - min_uV, max_uV); - return -EINVAL; - } + if (new_uV > max_uV) { + pr_err("unable to set voltage %d (min:%d max:%d)\n", new_uV, + min_uV, max_uV); + return -EINVAL; + } rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_VOLTAGE, @@ -2716,7 +2955,7 @@ static int qpnp_skip_swire_command(struct qpnp_labibb *labibb) pr_err("Failed to read ibb_status1 reg rc=%d\n", rc); return rc; } - if (reg & IBB_STATUS1_VREG_OK_BIT) + if ((reg & IBB_STATUS1_VREG_OK_MASK) == IBB_STATUS1_VREG_OK) break; /* poll delay */ @@ -3035,33 +3274,41 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, } labibb->lab_vreg.vreg_enabled = 1; - } +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + /* request interrupt */ + rc = qpnp_lab_request_interrupt(labibb); + if (rc) { + pr_err("lab request interrupt failed rc=%d\n", + rc); + return rc; + } - if (is_lab_vreg_ok_irq_available(labibb)) { rc = devm_request_threaded_irq(labibb->dev, - labibb->lab_vreg.lab_vreg_ok_irq, NULL, - lab_vreg_ok_handler, - IRQF_ONESHOT | IRQF_TRIGGER_RISING, - "lab-vreg-ok", labibb); + labibb->lab_vreg_irq, NULL, + lab_vreg_handler, + IRQF_LAB_FLAGS, + "lab_vreg_not_ok_interrupt", labibb); if (rc) { - pr_err("Failed to register 'lab-vreg-ok' irq rc=%d\n", + pr_err("Failed to register 'lab_vreg_not_ok_interrupt' irq rc=%d\n", rc); return rc; } +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ } - if (labibb->lab_vreg.lab_sc_irq != -EINVAL) { + if (is_lab_vreg_ok_irq_available(labibb)) { rc = devm_request_threaded_irq(labibb->dev, - labibb->lab_vreg.lab_sc_irq, NULL, - labibb_sc_err_handler, + labibb->lab_vreg.lab_vreg_ok_irq, NULL, + lab_vreg_ok_handler, IRQF_ONESHOT | IRQF_TRIGGER_RISING, - "lab-sc-err", labibb); + "lab-vreg-ok", labibb); if (rc) { - pr_err("Failed to register 'lab-sc-err' irq rc=%d\n", + pr_err("Failed to register 'lab-vreg-ok' irq rc=%d\n", rc); return rc; } } + rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_MODULE_RDY, &val, 1); if (rc < 0) { @@ -3119,6 +3366,121 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, return 0; } +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +static void force_labibb_regulator_disable(struct qpnp_labibb *labibb) +{ + int rc; + u8 val; + + val = 0; + rc = qpnp_labibb_write(labibb, + labibb->ibb_base + REG_IBB_ENABLE_CTL, &val, 1); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_ENABLE_CTL, rc); + goto exit; + } + + if (labibb->mode != QPNP_LABIBB_STANDALONE_MODE) + goto exit; + + val = 0; + rc = qpnp_labibb_write(labibb, + labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_ENABLE_CTL, rc); + goto exit; + } +exit: + return; +} + +static void vreg_check_worker(struct work_struct *work) +{ + u8 val; + int rc; + struct qpnp_labibb *labibb = labibb_vreg_check.labibb; + + if ((!labibb->lab_vreg.vreg_enabled) || + (!labibb->ibb_vreg.vreg_enabled)) { + pr_debug("%s: false detection display is off\n", __func__); + goto exit; + } + + /* lab vreg status check */ + rc = qpnp_labibb_read(labibb, + labibb->lab_base + REG_LAB_STATUS1, + &val, + 1); + if (rc) { + pr_err("%s: read register %x failed rc = %d\n", + __func__, REG_LAB_STATUS1, rc); + goto read_error; + } + + if (!(val & LAB_STATUS1_VREG_OK)) + goto status_error; + + /* ibb vreg status check */ + rc = qpnp_labibb_read(labibb, + labibb->ibb_base + REG_IBB_STATUS1, + &val, + 1); + if (rc) { + pr_err("%s: read register %x failed rc = %d\n", + __func__, REG_IBB_STATUS1, rc); + goto read_error; + } + + if (!(val & IBB_STATUS1_VREG_OK)) + goto status_error; + + rc = qpnp_labibb_interrupt_enable_ctl(labibb, VREG_WORKER_ACTIVE); + if (rc) { + pr_err("%s: qpnp_labibb_interrupt_enable_ctl error\n", + __func__); + goto write_error; + } + + pr_debug("%s: vreg_check_worker done.\n", __func__); + goto exit; + +status_error: + labibb_vreg_check.current_chatter_cnt++; + pr_err("%s: VREG_NG Detection [%d]\n", + __func__, labibb_vreg_check.current_chatter_cnt); + if (labibb_vreg_check.current_chatter_cnt >= + labibb_vreg_check.target_chatter_cnt) { + pr_err("%s: execute shutdown.\n", __func__); + + /* disable vreg */ + force_labibb_regulator_disable(labibb); + /* shutdown */ + do { + pm_power_off(); + msleep(POWER_OFF_RETRY_INTERVAL); + } while (1); + goto exit; + } + +read_error: +write_error: + schedule_delayed_work(&labibb_vreg_check.vreg_check_work, + msecs_to_jiffies( + labibb_vreg_check.target_chatter_check_interval)); + return; + +exit: + /* initialize */ + labibb_vreg_check.current_chatter_cnt = 0; + labibb_vreg_check.vreg_check_working = false; + labibb_vreg_check.ocp_lab_detected = false; + labibb_vreg_check.ocp_ibb_detected = false; + return; +} +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + static int qpnp_ibb_pfm_mode_enable(struct qpnp_labibb *labibb, struct device_node *of_node) { @@ -3268,6 +3630,253 @@ static bool qpnp_ibb_poff_ctl_required(struct qpnp_labibb *labibb) return true; } +#ifdef SOMC_LABIBB_REGULATOR_ORG_IMPL +/** This API is used to set precharge of LAB regulator + * regulator: the reglator device + * time: precharge time + * en: precharge control enable or not + */ +int qpnp_lab_set_precharge(struct regulator *regulator, u32 time, bool en) +{ + struct qpnp_labibb *labibb; + u8 val; + int rc; + + labibb = regulator_get_drvdata(regulator); + + for (val = 0; val < ARRAY_SIZE(lab_max_precharge_table); val++) + if (lab_max_precharge_table[val] == time) + break; + + if (val == ARRAY_SIZE(lab_max_precharge_table)) + val = ARRAY_SIZE(lab_max_precharge_table) - 1; + + if (en) + val |= LAB_FAST_PRECHARGE_CTL_EN; + + pr_debug("write base=0x%x val=0x%x\n", + (labibb->lab_base + REG_LAB_PRECHARGE_CTL), val); + + mutex_lock(&(labibb->lab_vreg.lab_mutex)); + + rc = qpnp_labibb_write(labibb, labibb->lab_base + + REG_LAB_PRECHARGE_CTL, &val, 1); + + mutex_unlock(&(labibb->lab_vreg.lab_mutex)); + return rc; +} +EXPORT_SYMBOL(qpnp_lab_set_precharge); + +/** This API is used to set soft-start of LAB regulator + * regulator: the reglator device + * time: soft start time + */ +int qpnp_lab_set_soft_start(struct regulator *regulator, u32 time) +{ + struct qpnp_labibb *labibb; + u8 val; + int rc; + + labibb = regulator_get_drvdata(regulator); + + for (val = 0; val < sizeof(ARRAY_SIZE(lab_soft_start_table)); val++) + if (lab_soft_start_table[val] == time) + break; + + if (val == ARRAY_SIZE(lab_soft_start_table)) + val = ARRAY_SIZE(lab_soft_start_table) - 1; + + pr_debug("write base=0x%x val=0x%x\n", + (labibb->lab_base + REG_LAB_SOFT_START_CTL), val); + + mutex_lock(&(labibb->lab_vreg.lab_mutex)); + + rc = qpnp_labibb_write(labibb, labibb->lab_base + + REG_LAB_SOFT_START_CTL, &val, 1); + + mutex_unlock(&(labibb->lab_vreg.lab_mutex)); + return rc; +} +EXPORT_SYMBOL(qpnp_lab_set_soft_start); + +/** This API is used to set pull-down of LAB regulator + * regulator: the reglator device + * en: pull-down enable or not + * strength: strength pull-down + */ +int qpnp_lab_set_pull_down(struct regulator *regulator, u8 strength) +{ + struct qpnp_labibb *labibb; + u8 val; + int rc = 0; + + labibb = regulator_get_drvdata(regulator); + + if (strength > 0) + val = LAB_PD_CTL_STRONG_PULL; + else + val = 0; + + mutex_lock(&(labibb->lab_vreg.lab_mutex)); + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_PD_CTL, + LAB_PD_CTL_STRENGTH_MASK, + val); + mutex_unlock(&(labibb->lab_vreg.lab_mutex)); + + if (rc) + pr_err("qpnp_lab_set_pd_strength write register %x failed rc = %d\n", + REG_LAB_PD_CTL, rc); + + return rc; +} +EXPORT_SYMBOL(qpnp_lab_set_pull_down); + +/** This API is used to set current max of LAB regulator + * regulator: the reglator device + * limit: current max of LAB regulator + */ +int qpnp_lab_set_current_max(struct regulator *regulator, u32 limit) +{ + struct qpnp_labibb *labibb; + int rc = 0; + u8 reg; + + labibb = regulator_get_drvdata(regulator); + + for (reg = 0; reg < ARRAY_SIZE(lab_current_limit_table); reg++) + if (lab_current_limit_table[reg] == limit) + break; + + if (reg == ARRAY_SIZE(lab_current_limit_table)) + reg = ARRAY_SIZE(lab_current_limit_table) - 1; + + pr_debug("write base=0x%x val=0x%x\n", + (labibb->lab_base + REG_LAB_CURRENT_LIMIT), reg); + + mutex_lock(&(labibb->lab_vreg.lab_mutex)); + + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_CURRENT_LIMIT, + LAB_CURRENT_LIMIT_MASK, + reg); + if (rc) + pr_err("%s write register %x failed rc = %d\n", + __func__, REG_LAB_CURRENT_LIMIT, rc); + + mutex_unlock(&(labibb->lab_vreg.lab_mutex)); + return rc; +} +EXPORT_SYMBOL(qpnp_lab_set_current_max); + +/** This API is used to set soft-start of IBB regulator + * regulator: the reglator device + * time: soft start time + */ +int qpnp_ibb_set_soft_start(struct regulator *regulator, u32 time) +{ + struct qpnp_labibb *labibb; + u8 val; + int rc; + + labibb = regulator_get_drvdata(regulator); + + for (val = 0; val < sizeof(ARRAY_SIZE(ibb_dischg_res_table)); + val++) + if (ibb_dischg_res_table[val] == time) + break; + + if (val == ARRAY_SIZE(ibb_dischg_res_table)) + val = ARRAY_SIZE(ibb_dischg_res_table) - 1; + + pr_debug("write base=0x%x val=0x%x\n", + (labibb->ibb_base + REG_IBB_SOFT_START_CTL), val); + + mutex_lock(&(labibb->ibb_vreg.ibb_mutex)); + + rc = qpnp_labibb_write(labibb, labibb->ibb_base + + REG_IBB_SOFT_START_CTL, &val, 1); + + mutex_unlock(&(labibb->ibb_vreg.ibb_mutex)); + return rc; +} +EXPORT_SYMBOL(qpnp_ibb_set_soft_start); + +/** This API is used to set pull-down of IBB regulator + * regulator: the reglator device + * en: pull-down enable or not + * strength: strength pull-down + */ +int qpnp_ibb_set_pull_down(struct regulator *regulator, u8 strength) +{ + struct qpnp_labibb *labibb; + u8 val; + int rc = 0; + + labibb = regulator_get_drvdata(regulator); + + if (strength > 0) + val = 0; + else + val = IBB_PD_CTL_HALF_STRENGTH; + + mutex_lock(&(labibb->ibb_vreg.ibb_mutex)); + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_PD_CTL, + IBB_PD_CTL_STRENGTH_MASK, + val); + mutex_unlock(&(labibb->ibb_vreg.ibb_mutex)); + + if (rc) + pr_err("qpnp_ibb_set_pd_strength write register %x failed rc = %d\n", + REG_IBB_PD_CTL, rc); + + return rc; +} +EXPORT_SYMBOL(qpnp_ibb_set_pull_down); + +/** This API is used to set current max of IBB regulator + * regulator: the reglator device + * limit: current max of IBB regulator + */ +int qpnp_ibb_set_current_max(struct regulator *regulator, u32 limit) +{ + struct qpnp_labibb *labibb; + int rc = 0; + u8 reg = 0; + + labibb = regulator_get_drvdata(regulator); + + reg = IBB_CURRENT_LIMIT_VALUE; + + if (!(ibb_current_limit_table[reg] == limit)) { + pr_err("%s value mismatch\n", __func__); + return rc; + } + + if (reg == ARRAY_SIZE(ibb_current_limit_table)) + reg = ARRAY_SIZE(ibb_current_limit_table) - 1; + + pr_debug("write base=0x%x val=0x%x\n", + (labibb->ibb_base + REG_IBB_CURRENT_LIMIT), reg); + + mutex_lock(&(labibb->ibb_vreg.ibb_mutex)); + + rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base, + REG_IBB_CURRENT_LIMIT, + IBB_CURRENT_LIMIT_MASK, + reg); + + if (rc) + pr_err("%s write register %x failed rc = %d\n", + __func__, REG_IBB_CURRENT_LIMIT, rc); + + mutex_unlock(&(labibb->ibb_vreg.ibb_mutex)); + return rc; +} +EXPORT_SYMBOL(qpnp_ibb_set_current_max); +#endif /* SOMC_LABIBB_REGULATOR_ORG_IMPL */ + static int qpnp_ibb_dt_init(struct qpnp_labibb *labibb, struct device_node *of_node) { @@ -3531,7 +4140,8 @@ static int qpnp_ibb_dt_init(struct qpnp_labibb *labibb, static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev) { - int rc = 0; + int rc, delay, retries = 10; + u8 val; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->sc_detected) { @@ -3540,17 +4150,43 @@ static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev) } if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { + if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); - rc = qpnp_ibb_enable_standalone(labibb); + rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN); if (rc < 0) { - pr_err("enable ibb standalone failed, rc=%d\n", rc); + pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); return rc; } + + delay = labibb->ibb_vreg.soft_start; + while (retries--) { + /* Wait for a small period before reading IBB_STATUS1 */ + usleep_range(delay, delay + 100); + + rc = qpnp_labibb_read(labibb, labibb->ibb_base + + REG_IBB_STATUS1, &val, 1); + if (rc < 0) { + pr_err("qpnp_ibb_regulator_enable read register %x failed rc = %d\n", + REG_IBB_STATUS1, rc); + return rc; + } + + if (val & IBB_STATUS1_VREG_OK) + break; + } + + if (!(val & IBB_STATUS1_VREG_OK)) { + pr_err("qpnp_ibb_regulator_enable failed\n"); + return -EINVAL; + } + labibb->ibb_vreg.vreg_enabled = 1; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_ibb_interrupt_enable_ctl(labibb, VREG_WORKER_PASSIVE); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ } - return 0; } @@ -3559,6 +4195,10 @@ static int qpnp_ibb_regulator_disable(struct regulator_dev *rdev) int rc; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_ibb_interrupt_disable_ctl(labibb); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + if (labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) @@ -3599,6 +4239,7 @@ static int qpnp_ibb_regulator_set_voltage(struct regulator_dev *rdev, return rc; } + static int qpnp_ibb_regulator_get_voltage(struct regulator_dev *rdev) { struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); @@ -3761,6 +4402,26 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, labibb->ibb_vreg.pwrdn_dly = ibb_pwrdn_dly_table[index]; labibb->ibb_vreg.vreg_enabled = 1; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + /* request interrupt */ + rc = qpnp_ibb_request_interrupt(labibb); + if (rc) { + pr_err("ibb request interrupt failed rc=%d\n", + rc); + return rc; + } + + rc = devm_request_threaded_irq(labibb->dev, + labibb->ibb_vreg_irq, NULL, + ibb_vreg_handler, + IRQF_IBB_FLAGS, + "ibb_vreg_not_ok_interrupt", labibb); + if (rc) { + pr_err("Failed to register 'ibb_vreg_not_ok_interrupt' irq rc=%d\n", + rc); + return rc; + } +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ } else { /* SWIRE_RDY and IBB_MODULE_EN not enabled */ rc = qpnp_ibb_dt_init(labibb, of_node); @@ -3820,19 +4481,6 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, labibb->ibb_vreg.pwrdn_dly = 0; } - if (labibb->ibb_vreg.ibb_sc_irq != -EINVAL) { - rc = devm_request_threaded_irq(labibb->dev, - labibb->ibb_vreg.ibb_sc_irq, NULL, - labibb_sc_err_handler, - IRQF_ONESHOT | IRQF_TRIGGER_RISING, - "ibb-sc-err", labibb); - if (rc) { - pr_err("Failed to register 'ibb-sc-err' irq rc=%d\n", - rc); - return rc; - } - } - rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_MODULE_RDY, &val, 1); if (rc < 0) { @@ -3903,42 +4551,40 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, return 0; } -static int qpnp_lab_register_irq(struct device_node *child, +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +static int qpnp_ibb_register_irq(struct device_node *child, struct qpnp_labibb *labibb) { - int rc = 0; - - if (is_lab_vreg_ok_irq_available(labibb)) { - rc = of_irq_get_byname(child, "lab-vreg-ok"); - if (rc < 0) { - pr_err("Invalid lab-vreg-ok irq\n"); - return rc; - } - labibb->lab_vreg.lab_vreg_ok_irq = rc; + labibb->ibb_vreg_irq = + of_irq_get_byname(child, "ibb_vreg_not_ok_interrupt"); + if (!labibb->ibb_vreg_irq) { + pr_err("Invalid ibb_vreg_not_ok_interrupt irq\n"); + return -EINVAL; } - labibb->lab_vreg.lab_sc_irq = -EINVAL; - rc = of_irq_get_byname(child, "lab-sc-err"); - if (rc < 0) - pr_debug("Unable to get lab-sc-err, rc = %d\n", rc); - else - labibb->lab_vreg.lab_sc_irq = rc; - return 0; } +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ -static int qpnp_ibb_register_irq(struct device_node *child, +static int qpnp_lab_register_irq(struct device_node *child, struct qpnp_labibb *labibb) { - int rc; - - labibb->ibb_vreg.ibb_sc_irq = -EINVAL; - rc = of_irq_get_byname(child, "ibb-sc-err"); - if (rc < 0) - pr_debug("Unable to get ibb-sc-err, rc = %d\n", rc); - else - labibb->ibb_vreg.ibb_sc_irq = rc; - + if (is_lab_vreg_ok_irq_available(labibb)) { + labibb->lab_vreg.lab_vreg_ok_irq = + of_irq_get_byname(child, "lab-vreg-ok"); + if (labibb->lab_vreg.lab_vreg_ok_irq < 0) { + pr_err("Invalid lab-vreg-ok irq\n"); + return -EINVAL; + } + } +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + labibb->lab_vreg_irq = + of_irq_get_byname(child, "lab_vreg_not_ok_interrupt"); + if (!labibb->lab_vreg_irq) { + pr_err("Invalid lab_vreg_not_ok_interrupt irq\n"); + return -EINVAL; + } +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ return 0; } @@ -3979,6 +4625,121 @@ static int qpnp_labibb_check_ttw_supported(struct qpnp_labibb *labibb) return rc; } +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +static irqreturn_t lab_vreg_handler(int irq, void *_chip) +{ + u8 val; + int rc; + struct qpnp_labibb *labibb = _chip; + + labibb_vreg_check.ocp_lab_detected = true; + pr_err("%s: LAB VREG_NG interrupt!\n", __func__); + qpnp_labibb_interrupt_disable_ctl(labibb); + + if (!labibb->lab_vreg.vreg_enabled) { + pr_err("%s: false detection display is off\n", __func__); + goto exit; + } + + /* lab status1 */ + rc = qpnp_labibb_read(labibb, + labibb->lab_base + REG_LAB_STATUS1, + &val, + 1); + if (rc) { + pr_err("%s: read register %x failed rc = %d\n", + __func__, REG_LAB_STATUS1, rc); + goto exit; + } + + if (!(val & LAB_STATUS1_VREG_OK)) { + pr_err("%s: LAB VREG NG!!!\n", __func__); + } else { + labibb_vreg_check.ocp_lab_detected = false; + goto false_detection; + } + + /* start vreg check */ + if (labibb_vreg_check.vreg_check_working) + goto exit; + + labibb_vreg_check.vreg_check_working = true; + labibb_vreg_check.current_chatter_cnt = CHATTER_CNT_START; + schedule_delayed_work(&labibb_vreg_check.vreg_check_work, + msecs_to_jiffies( + labibb_vreg_check.target_chatter_check_interval)); + goto exit; + +false_detection: + rc = qpnp_labibb_interrupt_enable_ctl(labibb, VREG_WORKER_PASSIVE); + if (rc) + pr_err("%s: qpnp_labibb_interrupt_enable_ctl error\n", + __func__); +exit: + return IRQ_HANDLED; +} + +static irqreturn_t ibb_vreg_handler(int irq, void *_chip) +{ + u8 val; + int rc; + struct qpnp_labibb *labibb = _chip; + + labibb_vreg_check.ocp_ibb_detected = true; + pr_err("%s: IBB VREG_NG interrupt!\n", __func__); + qpnp_labibb_interrupt_disable_ctl(labibb); + + if (!labibb->ibb_vreg.vreg_enabled) { + pr_err("%s: false detection display is off\n", __func__); + goto exit; + } + + /* ibb status1 */ + rc = qpnp_labibb_read(labibb, + labibb->ibb_base + REG_IBB_STATUS1, + &val, + 1); + if (rc) { + pr_err("%s: read register %x failed rc = %d\n", + __func__, REG_IBB_STATUS1, rc); + goto exit; + } + + if (!(val & IBB_STATUS1_VREG_OK)) { + pr_err("%s: IBB VREG NG!!!\n", __func__); + } else { + labibb_vreg_check.ocp_ibb_detected = false; + goto false_detection; + } + + /* start vreg check */ + if (labibb_vreg_check.vreg_check_working) + goto exit; + + labibb_vreg_check.vreg_check_working = true; + labibb_vreg_check.current_chatter_cnt = CHATTER_CNT_START; + schedule_delayed_work(&labibb_vreg_check.vreg_check_work, + msecs_to_jiffies( + labibb_vreg_check.target_chatter_check_interval)); + goto exit; + +false_detection: + rc = qpnp_labibb_interrupt_enable_ctl(labibb, VREG_WORKER_PASSIVE); + if (rc) + pr_err("%s: qpnp_labibb_interrupt_enable_ctl error\n", + __func__); + +exit: + return IRQ_HANDLED; +} + +bool qpnp_labibb_ocp_check(void) +{ + return (labibb_vreg_check.ocp_lab_detected || labibb_vreg_check.ocp_ibb_detected); +} + +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + static int qpnp_labibb_regulator_probe(struct platform_device *pdev) { struct qpnp_labibb *labibb; @@ -4052,6 +4813,31 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) } } +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + /* initialize labibb_vreg_status_ctrl */ + labibb_vreg_check.labibb = labibb; + labibb_vreg_check.current_chatter_cnt = 0; + labibb_vreg_check.vreg_check_working = false; + labibb_vreg_check.target_chatter_cnt = DEFAULT_TARGET_CHATTER_CNT; + rc = of_property_read_u32(labibb->dev->of_node, + "somc,vreg-target-chatter-cnt", + &(labibb_vreg_check.target_chatter_cnt)); + if (rc) + pr_info("qpnp_labibb: Target chatter count sets default.\n"); + + labibb_vreg_check.target_chatter_check_interval + = DEFAULT_TARGET_CHATTER_INTERVAL; + rc = of_property_read_u32(labibb->dev->of_node, + "somc,vreg-target-chatter-interval", + &(labibb_vreg_check.target_chatter_check_interval)); + if (rc) + pr_info("qpnp_labibb: Target chatter interval sets default.\n"); + + /* initialize vreg_check_worker */ + INIT_DELAYED_WORK(&labibb_vreg_check.vreg_check_work, + vreg_check_worker); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + labibb->standalone = of_property_read_bool(labibb->dev->of_node, "qcom,labibb-standalone"); @@ -4141,7 +4927,14 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) case QPNP_IBB_TYPE: labibb->ibb_base = base; labibb->ibb_dig_major = revision; - qpnp_ibb_register_irq(child, labibb); +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + rc = qpnp_ibb_register_irq(child, labibb); + if (rc) { + pr_err("Failed to register IBB IRQ rc=%d\n", + rc); + goto fail_registration; + } +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ rc = register_qpnp_ibb_regulator(labibb, child); if (rc < 0) goto fail_registration; @@ -4165,11 +4958,6 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) } INIT_WORK(&labibb->lab_vreg_ok_work, qpnp_lab_vreg_notifier_work); - INIT_DELAYED_WORK(&labibb->sc_err_recovery_work, - labibb_sc_err_recovery_work); - hrtimer_init(&labibb->sc_err_check_timer, - CLOCK_MONOTONIC, HRTIMER_MODE_REL); - labibb->sc_err_check_timer.function = labibb_check_sc_err_count; dev_set_drvdata(&pdev->dev, labibb); pr_info("LAB/IBB registered successfully, lab_vreg enable=%d ibb_vreg enable=%d swire_control=%d\n", labibb->lab_vreg.vreg_enabled, @@ -4183,7 +4971,12 @@ fail_registration: regulator_unregister(labibb->lab_vreg.rdev); if (labibb->ibb_vreg.rdev) regulator_unregister(labibb->ibb_vreg.rdev); - +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + if (labibb->lab_vreg_irq) + free_irq(labibb->lab_vreg_irq, labibb); + if (labibb->ibb_vreg_irq) + free_irq(labibb->ibb_vreg_irq, labibb); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ return rc; } @@ -4210,6 +5003,13 @@ static int qpnp_labibb_regulator_remove(struct platform_device *pdev) regulator_unregister(labibb->ibb_vreg.rdev); cancel_work_sync(&labibb->lab_vreg_ok_work); +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + if (labibb->lab_vreg_irq) + free_irq(labibb->lab_vreg_irq, labibb); + if (labibb->ibb_vreg_irq) + free_irq(labibb->ibb_vreg_irq, labibb); + cancel_delayed_work_sync(&labibb_vreg_check.vreg_check_work); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ } return 0; } diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c index 4098cb91f8fb05810ed2486f99de4ea96834a8a8..70515c392d45bf875eccbffce0d4d84b2643b351 100644 --- a/drivers/regulator/qpnp-regulator.c +++ b/drivers/regulator/qpnp-regulator.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -350,6 +355,8 @@ struct qpnp_regulator { int ocp_count; int ocp_max_retries; int ocp_retry_delay_ms; + struct regulator_ocp_notification ocp_notification; + spinlock_t ocp_lock; int system_load; int hpm_min_load; int slew_rate; @@ -1255,6 +1262,34 @@ static int qpnp_regulator_common_enable_time(struct regulator_dev *rdev) return vreg->enable_time; } +static int qpnp_regulator_vs_register_ocp_notification( + struct regulator_dev *rdev, + struct regulator_ocp_notification *notification) +{ + unsigned long flags; + struct qpnp_regulator *vreg = rdev_get_drvdata(rdev); + + spin_lock_irqsave(&vreg->ocp_lock, flags); + if (notification) { + /* register ocp notification */ + vreg->ocp_notification = *notification; + } else { + /* unregister ocp notification */ + memset(&vreg->ocp_notification, 0, + sizeof(vreg->ocp_notification)); + } + spin_unlock_irqrestore(&vreg->ocp_lock, flags); + + if (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_OCP) { + pr_info("%s: registered ocp notification(notify=%p, ctxt=%p)\n", + vreg->rdesc.name, + vreg->ocp_notification.notify, + vreg->ocp_notification.ctxt); + } + + return 0; +} + static int qpnp_regulator_vs_clear_ocp(struct qpnp_regulator *vreg) { int rc; @@ -1328,8 +1363,14 @@ static irqreturn_t qpnp_regulator_vs_ocp_isr(int irq, void *data) schedule_delayed_work(&vreg->ocp_work, msecs_to_jiffies(vreg->ocp_retry_delay_ms) + 1); } else { + unsigned long flags; vreg_err(vreg, "OCP triggered %d times; no further retries\n", vreg->ocp_count); + spin_lock_irqsave(&vreg->ocp_lock, flags); + if (vreg->ocp_notification.notify) + vreg->ocp_notification.notify( + vreg->ocp_notification.ctxt); + spin_unlock_irqrestore(&vreg->ocp_lock, flags); } return IRQ_HANDLED; @@ -1560,6 +1601,8 @@ static struct regulator_ops qpnp_vs_ops = { .disable = qpnp_regulator_common_disable, .is_enabled = qpnp_regulator_common_is_enabled, .enable_time = qpnp_regulator_common_enable_time, + .register_ocp_notification + = qpnp_regulator_vs_register_ocp_notification, }; static struct regulator_ops qpnp_boost_ops = { @@ -2336,6 +2379,10 @@ static int qpnp_regulator_probe(struct platform_device *pdev) if (vreg->ocp_retry_delay_ms == 0) vreg->ocp_retry_delay_ms = QPNP_VS_OCP_DEFAULT_RETRY_DELAY_MS; + memset(&vreg->ocp_notification, 0, + sizeof(vreg->ocp_notification)); + spin_lock_init(&vreg->ocp_lock); + rdesc = &vreg->rdesc; rdesc->id = to_spmi_device(pdev->dev.parent)->ctrl->nr; rdesc->owner = THIS_MODULE; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index c0a57c8ead2242da38a2e85f129699d11ee8d97e..f979e17b142fb532eb3c2f441666729023b142de 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -433,6 +433,9 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) struct sg_header *old_hdr = NULL; int retval = 0; + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) + return -EINVAL; + /* * This could cause a response to be stranded. Close the associated * file descriptor to free up any resources being held. @@ -979,6 +982,8 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) * return an error value. So returning '0' to keep compability * with legacy applications. */ + mutex_lock(&sfp->parentdp->open_rel_lock); + mutex_unlock(&sfp->parentdp->open_rel_lock); return 0; case SG_GET_LOW_DMA: return put_user((int) sdp->device->host->unchecked_isa_dma, ip); diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index fb2f2159c0e1908da4384bc072e9f47a1b7c6361..f884d4fc2284eef5b5904bb45af1ab3e938e5854 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig @@ -96,6 +96,13 @@ config SCSI_UFS_QCOM_ICE Select this if you have ICE supported for UFS on QCOM chipset. If unsure, say N. +config SCSI_UFS_RESTRICT_TX_LANES + bool "Restrict the number of TX lanes to 1" + default n + help + Say Y here to restrict the number of TX lanes to 1. + This will save the power consumption in exchange for + the write performance. config SCSI_UFS_TEST tristate "Universal Flash Storage host controller driver unit-tests" diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c index 51eef0d5e95cf1e6940fa2e19f96166db476686c..06e371890c35d539e112736ff3722796693dcadc 100644 --- a/drivers/scsi/ufs/ufs-debugfs.c +++ b/drivers/scsi/ufs/ufs-debugfs.c @@ -16,6 +16,11 @@ * of the driver from userspace. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include "ufs-debugfs.h" @@ -25,6 +30,7 @@ enum field_width { BYTE = 1, WORD = 2, + DWORD = 4, }; struct desc_field_offset { @@ -366,6 +372,7 @@ static const struct file_operations ufsdbg_err_inj_scenario_ops = { .open = ufsdbg_err_inj_scenario_open, .read = seq_read, .write = ufsdbg_err_inj_scenario_write, + .release = single_release, }; static int ufsdbg_err_inj_stats_read(struct seq_file *file, void *data) @@ -407,6 +414,7 @@ static const struct file_operations ufsdbg_err_inj_stats_ops = { .open = ufsdbg_err_inj_stats_open, .read = seq_read, .write = ufsdbg_err_inj_stats_write, + .release = single_release, }; static void ufsdbg_setup_fault_injection(struct ufs_hba *hba) @@ -591,6 +599,7 @@ static const struct file_operations ufsdbg_tag_stats_fops = { .open = ufsdbg_tag_stats_open, .read = seq_read, .write = ufsdbg_tag_stats_write, + .release = single_release, }; static int ufsdbg_query_stats_show(struct seq_file *file, void *data) @@ -662,6 +671,7 @@ static const struct file_operations ufsdbg_query_stats_fops = { .open = ufsdbg_query_stats_open, .read = seq_read, .write = ufsdbg_query_stats_write, + .release = single_release, }; static int ufsdbg_err_stats_show(struct seq_file *file, void *data) @@ -766,6 +776,7 @@ static const struct file_operations ufsdbg_err_stats_fops = { .open = ufsdbg_err_stats_open, .read = seq_read, .write = ufsdbg_err_stats_write, + .release = single_release, }; static int ufshcd_init_statistics(struct ufs_hba *hba) @@ -845,6 +856,7 @@ static int ufsdbg_host_regs_open(struct inode *inode, struct file *file) static const struct file_operations ufsdbg_host_regs_fops = { .open = ufsdbg_host_regs_open, .read = seq_read, + .release = single_release, }; static int ufsdbg_dump_device_desc_show(struct seq_file *file, void *data) @@ -881,7 +893,15 @@ static int ufsdbg_dump_device_desc_show(struct seq_file *file, void *data) {"bUD0BaseOffset", 0x1A, BYTE}, {"bUDConfigPLength", 0x1B, BYTE}, {"bDeviceRTTCap", 0x1C, BYTE}, - {"wPeriodicRTCUpdate", 0x1D, WORD} + {"wPeriodicRTCUpdate", 0x1D, WORD}, + {"bUFSFeaturesSupport", 0x1F, BYTE}, + {"bFFUTimeout", 0x20, BYTE}, + {"bQueueDepth", 0x21, BYTE}, + {"wDeviceVersion", 0x22, WORD}, + {"bNumSecureWPArea", 0x24, BYTE}, + {"dPSAMaxDataSize", 0x25, DWORD}, + {"bPSAStateTimeout", 0x29, BYTE}, + {"iProductRevisionLevel", 0x2A, BYTE}, }; pm_runtime_get_sync(hba->dev); @@ -906,6 +926,12 @@ static int ufsdbg_dump_device_desc_show(struct seq_file *file, void *data) tmp->offset, tmp->name, *(u16 *)&desc_buf[tmp->offset]); + } else if (tmp->width_byte == DWORD) { + seq_printf(file, + "Device Descriptor[Byte offset 0x%x]: %s = 0x%x\n", + tmp->offset, + tmp->name, + *(u32 *)&desc_buf[tmp->offset]); } else { seq_printf(file, "Device Descriptor[offset 0x%x]: %s. Wrong Width = %d", @@ -965,6 +991,7 @@ static int ufsdbg_show_hba_open(struct inode *inode, struct file *file) static const struct file_operations ufsdbg_show_hba_fops = { .open = ufsdbg_show_hba_open, .read = seq_read, + .release = single_release, }; static int ufsdbg_dump_device_desc_open(struct inode *inode, struct file *file) @@ -976,6 +1003,143 @@ static int ufsdbg_dump_device_desc_open(struct inode *inode, struct file *file) static const struct file_operations ufsdbg_dump_device_desc = { .open = ufsdbg_dump_device_desc_open, .read = seq_read, + .release = single_release, +}; + +static int ufsdbg_dump_device_health_desc_show(struct seq_file *file, void *data) +{ + int err = 0; + int buff_len = QUERY_DESC_DEVICE_HEALTH_MAX_SIZE; + u8 desc_buf[QUERY_DESC_DEVICE_HEALTH_MAX_SIZE]; + struct ufs_hba *hba = (struct ufs_hba *)file->private; + + pm_runtime_get_sync(hba->dev); + err = ufshcd_read_device_health_desc(hba, desc_buf, buff_len); + pm_runtime_put_sync(hba->dev); + + if (!err) { + int i; + for (i = 0; i < QUERY_DESC_DEVICE_HEALTH_MAX_SIZE; ++i) { + seq_printf(file, "%02x", desc_buf[i]); + } + } else { + seq_printf(file, "Reading Device Health Descriptor failed. err = %d\n", + err); + } + + return err; +} + +static int ufsdbg_dump_device_health_desc_open(struct inode *inode, struct file *file) +{ + return single_open(file, + ufsdbg_dump_device_health_desc_show, inode->i_private); +} + +static const struct file_operations ufsdbg_dump_device_health_desc = { + .open = ufsdbg_dump_device_health_desc_open, + .read = seq_read, + .release = single_release, +}; + +static int ufsdbg_dump_fw_revision_show(struct seq_file *file, void *data) +{ + int err = 0; + u8 index; + u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE]; + u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE + 1]; + struct ufs_hba *hba = (struct ufs_hba *)file->private; + + pm_runtime_get_sync(hba->dev); + err = ufshcd_read_device_desc(hba, desc_buf, + QUERY_DESC_DEVICE_MAX_SIZE); + if (err) { + seq_printf(file, "Reading Device Descriptor failed. err = %d\n", + err); + goto out; + } + + index = desc_buf[DEVICE_DESC_PARAM_PRODUCT_REVISION]; + memset(str_desc_buf, 0, QUERY_DESC_STRING_MAX_SIZE + 1); + err = ufshcd_read_string_desc(hba, index, str_desc_buf, + QUERY_DESC_STRING_MAX_SIZE, ASCII_STD); + + if (err) { + seq_printf(file, "Reading String Descriptor failed. err = %d\n", + err); + } else { + seq_printf(file, "FW revision = %s\n", &str_desc_buf[QUERY_DESC_HDR_SIZE]); + } + +out: + pm_runtime_put_sync(hba->dev); + return err; +} + +static int ufsdbg_dump_fw_revision_open(struct inode *inode, struct file *file) +{ + return single_open(file, + ufsdbg_dump_fw_revision_show, inode->i_private); +} + +static const struct file_operations ufsdbg_dump_fw_revision = { + .open = ufsdbg_dump_fw_revision_open, + .read = seq_read, + .release = single_release, +}; + +static int ufsdbg_dump_serial_show(struct seq_file *file, void *data) +{ + int err = 0; + int i, len = 0; + u8 index; + u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE]; + u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE]; + struct ufs_hba *hba = (struct ufs_hba *)file->private; + + pm_runtime_get_sync(hba->dev); + err = ufshcd_read_device_desc(hba, desc_buf, + QUERY_DESC_DEVICE_MAX_SIZE); + if (err) { + seq_printf(file, "Reading Device Descriptor failed. err = %d\n", + err); + goto out; + } + + index = desc_buf[DEVICE_DESC_PARAM_SN]; + memset(str_desc_buf, 0, QUERY_DESC_STRING_MAX_SIZE); + + err = ufshcd_read_string_desc(hba, index, str_desc_buf, + QUERY_DESC_STRING_MAX_SIZE, UTF16_STD); + if (err) { + seq_printf(file, "Reading String Descriptor failed. err = %d\n", + err); + goto out; + } + + len = (int)str_desc_buf[QUERY_DESC_LENGTH_OFFSET]; + if (len > QUERY_DESC_STRING_MAX_SIZE) + len = QUERY_DESC_STRING_MAX_SIZE; + + seq_puts(file, "Serial = "); + for (i = QUERY_DESC_HDR_SIZE; i < len; i++) + seq_printf(file, "%02X", str_desc_buf[i]); + seq_puts(file, "\n"); +out: + pm_runtime_put_sync(hba->dev); + return err; +} + +static int ufsdbg_dump_serial_open(struct inode *inode, struct file *file) +{ + return single_open(file, + ufsdbg_dump_serial_show, inode->i_private); +} + +static const struct file_operations ufsdbg_dump_serial = { + .open = ufsdbg_dump_serial_open, + .read = seq_read, + .release = single_release, }; static int ufsdbg_power_mode_show(struct seq_file *file, void *data) @@ -1214,6 +1378,7 @@ static const struct file_operations ufsdbg_power_mode_desc = { .open = ufsdbg_power_mode_open, .read = seq_read, .write = ufsdbg_power_mode_write, + .release = single_release, }; static int ufsdbg_dme_read(void *data, u64 *attr_val, bool peer) @@ -1393,6 +1558,7 @@ static const struct file_operations ufsdbg_req_stats_desc = { .open = ufsdbg_req_stats_open, .read = seq_read, .write = ufsdbg_req_stats_write, + .release = single_release, }; @@ -1441,6 +1607,7 @@ static const struct file_operations ufsdbg_reset_controller = { .open = ufsdbg_reset_controller_open, .read = seq_read, .write = ufsdbg_reset_controller_write, + .release = single_release, }; static int ufsdbg_clear_err_state(void *data, u64 val) @@ -1649,6 +1816,36 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba) goto err; } + hba->debugfs_files.dump_dev_health_desc = + debugfs_create_file("dump_device_health_desc", S_IRUSR, + hba->debugfs_files.debugfs_root, hba, + &ufsdbg_dump_device_health_desc); + if (!hba->debugfs_files.dump_dev_health_desc) { + dev_err(hba->dev, + "%s: NULL dump_device_health_desc file, exiting", __func__); + goto err; + } + + hba->debugfs_files.fw_revision = + debugfs_create_file("fw_revision", S_IRUSR, + hba->debugfs_files.debugfs_root, hba, + &ufsdbg_dump_fw_revision); + if (!hba->debugfs_files.fw_revision) { + dev_err(hba->dev, + "%s: NULL fw_revision file, exiting", __func__); + goto err; + } + + hba->debugfs_files.serial = + debugfs_create_file("serial", S_IRUSR, + hba->debugfs_files.debugfs_root, hba, + &ufsdbg_dump_serial); + if (!hba->debugfs_files.serial) { + dev_err(hba->dev, + "%s: NULL serial file, exiting", __func__); + goto err; + } + ufsdbg_setup_fault_injection(hba); ufshcd_vops_add_debugfs(hba, hba->debugfs_files.debugfs_root); diff --git a/drivers/scsi/ufs/ufs-qcom-debugfs.c b/drivers/scsi/ufs/ufs-qcom-debugfs.c index db4ecec6cf2fdf27c47ffc8eb1bf10cf43e6d4f6..26147fefa1ad6252295c49bd00e2658723b7fd2a 100644 --- a/drivers/scsi/ufs/ufs-qcom-debugfs.c +++ b/drivers/scsi/ufs/ufs-qcom-debugfs.c @@ -186,6 +186,7 @@ static const struct file_operations ufs_qcom_dbg_testbus_cfg_desc = { .open = ufs_qcom_dbg_testbus_cfg_open, .read = seq_read, .write = ufs_qcom_dbg_testbus_cfg_write, + .release = single_release, }; static int ufs_qcom_dbg_testbus_bus_read(void *data, u64 *attr_val) @@ -240,6 +241,7 @@ static int ufs_qcom_dbg_dbg_regs_open(struct inode *inode, static const struct file_operations ufs_qcom_dbg_dbg_regs_desc = { .open = ufs_qcom_dbg_dbg_regs_open, .read = seq_read, + .release = single_release, }; static int ufs_qcom_dbg_pm_qos_show(struct seq_file *file, void *data) @@ -273,6 +275,7 @@ static int ufs_qcom_dbg_pm_qos_open(struct inode *inode, static const struct file_operations ufs_qcom_dbg_pm_qos_desc = { .open = ufs_qcom_dbg_pm_qos_open, .read = seq_read, + .release = single_release, }; void ufs_qcom_dbg_add_debugfs(struct ufs_hba *hba, struct dentry *root) diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c b/drivers/scsi/ufs/ufs-qcom-ice.c index 84765b17086cd2503cc4a92435eb77fdc76ca125..239b558c21eed137e9e595569ca563518113b26e 100644 --- a/drivers/scsi/ufs/ufs-qcom-ice.c +++ b/drivers/scsi/ufs/ufs-qcom-ice.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -27,6 +32,8 @@ #define UFS_QCOM_ICE_DEFAULT_DBG_PRINT_EN 0 +static struct workqueue_struct *ice_workqueue; + static void ufs_qcom_ice_dump_regs(struct ufs_qcom_host *qcom_host, int offset, int len, char *prefix) { @@ -224,6 +231,13 @@ int ufs_qcom_ice_init(struct ufs_qcom_host *qcom_host) } qcom_host->dbg_print_en |= UFS_QCOM_ICE_DEFAULT_DBG_PRINT_EN; + ice_workqueue = alloc_workqueue("ice-set-key", + WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + if (!ice_workqueue) { + dev_err(ufs_dev, "%s: workqueue allocation failed.\n", + __func__); + goto out; + } INIT_WORK(&qcom_host->ice_cfg_work, ufs_qcom_ice_cfg_work); out: @@ -284,7 +298,7 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host, if (!qcom_host->work_pending) { qcom_host->req_pending = cmd->request; - if (!schedule_work( + if (!queue_work(ice_workqueue, &qcom_host->ice_cfg_work)) { qcom_host->req_pending = NULL; @@ -377,6 +391,7 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, return -EINVAL; } + memset(&ice_set, 0, sizeof(ice_set)); memset(&ice_set, 0, sizeof(ice_set)); if (qcom_host->ice.vops->config_start) { @@ -404,7 +419,7 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, if (!qcom_host->work_pending) { qcom_host->req_pending = cmd->request; - if (!schedule_work( + if (!queue_work(ice_workqueue, &qcom_host->ice_cfg_work)) { qcom_host->req_pending = NULL; diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 6e307b5dce81030fef5947457027876c49f5de4f..2ce1727bc2968eeb3f819db401e0e8505f40ea89 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -11,6 +11,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -161,10 +166,12 @@ static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host) if (err) goto disable_tx_l0; +#ifndef CONFIG_SCSI_UFS_RESTRICT_TX_LANES /* The tx lane1 clk could be muxed, hence keep this optional */ if (host->tx_l1_sync_clk) ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk", host->tx_l1_sync_clk); +#endif } host->is_lane_clks_enabled = true; goto out; @@ -208,9 +215,11 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host) goto out; } +#ifndef CONFIG_SCSI_UFS_RESTRICT_TX_LANES /* The tx lane1 clk could be muxed, hence keep this optional */ ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk", &host->tx_l1_sync_clk); +#endif } out: return err; @@ -1507,8 +1516,7 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP); } - if (host->disable_lpm) - hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8; + hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8; } static void ufs_qcom_set_caps(struct ufs_hba *hba) diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h index fd98a3381d61808e2a0d7927296c6e467ee8f26b..a53bffc8a883328dd0383b46bb94ec3bef35fe98 100644 --- a/drivers/scsi/ufs/ufs-qcom.h +++ b/drivers/scsi/ufs/ufs-qcom.h @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef UFS_QCOM_H_ #define UFS_QCOM_H_ @@ -39,7 +44,12 @@ #define FAST 2 #define UFS_QCOM_LIMIT_NUM_LANES_RX 2 +#ifdef CONFIG_SCSI_UFS_RESTRICT_TX_LANES +#define UFS_QCOM_LIMIT_NUM_LANES_TX 1 +#else #define UFS_QCOM_LIMIT_NUM_LANES_TX 2 +#endif + #define UFS_QCOM_LIMIT_HSGEAR_RX UFS_HS_G3 #define UFS_QCOM_LIMIT_HSGEAR_TX UFS_HS_G3 #define UFS_QCOM_LIMIT_PWMGEAR_RX UFS_PWM_G4 diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index e4b2c95350ef0a31dcd6f5f4404aa9f22429491c..50184f5a4a378bb04cab65a3102e73cff9596aec 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -32,6 +32,11 @@ * any damages of any kind arising from your use or distribution of * this program. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _UFS_H #define _UFS_H @@ -156,6 +161,7 @@ enum ufs_desc_max_size { QUERY_DESC_STRING_MAX_SIZE = 0xFE, QUERY_DESC_GEOMETRY_MAZ_SIZE = 0x44, QUERY_DESC_POWER_MAX_SIZE = 0x62, + QUERY_DESC_DEVICE_HEALTH_MAX_SIZE = 0x25, QUERY_DESC_RFU_MAX_SIZE = 0x00, }; @@ -208,6 +214,14 @@ enum device_desc_param { DEVICE_DESC_PARAM_UD_LEN = 0x1B, DEVICE_DESC_PARAM_RTT_CAP = 0x1C, DEVICE_DESC_PARAM_FRQ_RTC = 0x1D, + DEVICE_DESC_PARAM_FFU_SUPPORT = 0x1F, + DEVICE_DESC_PARAM_FFU_TIMEOUT = 0x20, + DEVICE_DESC_PARAM_QUEUE_DEPTH = 0x21, + DEVICE_DESC_PARAM_DEVICE_VER = 0x22, + DEVICE_DESC_PARAM_NUM_SEC_WP_AREA = 0x24, + DEVICE_DESC_PARAM_PSM_MAX_DATA_SIZE = 0x25, + DEVICE_DESC_PARAM_PSA_STATE_TIMEOUT = 0x29, + DEVICE_DESC_PARAM_PRODUCT_REVISION = 0x2A, }; /* * Logical Unit Write Protect diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c index 7a501d6d7c843b23f665c74e2ba48b688e0c5458..b9818634aba1e98cc41d1473ed904b05288c3e8f 100644 --- a/drivers/scsi/ufs/ufs_quirks.c +++ b/drivers/scsi/ufs/ufs_quirks.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "ufshcd.h" #include "ufs_quirks.h" @@ -44,6 +49,14 @@ static struct ufs_card_fix ufs_fixups[] = { UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH), UFS_FIX(UFS_VENDOR_HYNIX, "hC8HL1", UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH), + UFS_FIX_REVISION(UFS_VENDOR_HYNIX, UFS_MODEL_HYNIX_32GB, + UFS_REVISION_HYNIX, UFS_DEVICE_QUIRK_NO_PURGE), + UFS_FIX_REVISION(UFS_VENDOR_HYNIX, UFS_MODEL_HYNIX_64GB, + UFS_REVISION_HYNIX, UFS_DEVICE_QUIRK_NO_PURGE), + UFS_FIX_REVISION(UFS_VENDOR_SAMSUNG, UFS_MODEL_SAMSUNG_64GB, + UFS_REVISION_SAMSUNG, UFS_DEVICE_QUIRK_NO_PURGE), + UFS_FIX(UFS_VENDOR_HYNIX, UFS_ANY_MODEL, + UFS_DEVICE_QUIRK_EXTEND_SYNC_LENGTH), END_FIX }; @@ -53,6 +66,7 @@ static int ufs_get_device_info(struct ufs_hba *hba, { int err; u8 model_index; + u8 revision_index; u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE + 1]; u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE]; @@ -68,7 +82,11 @@ static int ufs_get_device_info(struct ufs_hba *hba, card_data->wmanufacturerid = desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8 | desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1]; + card_data->specver = desc_buf[DEVICE_DESC_PARAM_SPEC_VER] << 8 | + desc_buf[DEVICE_DESC_PARAM_SPEC_VER + 1]; + model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; + revision_index = desc_buf[DEVICE_DESC_PARAM_PRODUCT_REVISION]; memset(str_desc_buf, 0, QUERY_DESC_STRING_MAX_SIZE); err = ufshcd_read_string_desc(hba, model_index, str_desc_buf, @@ -83,6 +101,22 @@ static int ufs_get_device_info(struct ufs_hba *hba, /* Null terminate the model string */ card_data->model[MAX_MODEL_LEN] = '\0'; + memset(str_desc_buf, 0, QUERY_DESC_STRING_MAX_SIZE); + err = ufshcd_read_string_desc(hba, revision_index, str_desc_buf, + QUERY_DESC_STRING_MAX_SIZE, ASCII_STD); + if (err) + goto out; + + str_desc_buf[QUERY_DESC_STRING_MAX_SIZE] = '\0'; + strlcpy(card_data->revision, (str_desc_buf + QUERY_DESC_HDR_SIZE), + min_t(u8, str_desc_buf[QUERY_DESC_LENGTH_OFFSET], + MAX_REVISION_LEN)); + /* Null terminate the model string */ + card_data->model[MAX_REVISION_LEN] = '\0'; + + dev_err(hba->dev, "%s : vid=%04x, model=%s, spec ver=%04x , fw ver=%s\n", + __func__, card_data->wmanufacturerid, card_data->model, + card_data->specver, card_data->revision); out: return err; } @@ -94,10 +128,15 @@ void ufs_advertise_fixup_device(struct ufs_hba *hba) struct ufs_card_info card_data; card_data.wmanufacturerid = 0; + card_data.specver = 0; card_data.model = kmalloc(MAX_MODEL_LEN + 1, GFP_KERNEL); if (!card_data.model) goto out; + card_data.revision = kmalloc(MAX_REVISION_LEN + 1, GFP_KERNEL); + if (!card_data.revision) + goto out; + /* get device data*/ err = ufs_get_device_info(hba, &card_data); if (err) { @@ -105,16 +144,24 @@ void ufs_advertise_fixup_device(struct ufs_hba *hba) goto out; } + if (card_data.specver < UFS_PURGE_SPEC_VER) + hba->dev_quirks |= UFS_DEVICE_QUIRK_NO_PURGE; + for (f = ufs_fixups; f->quirk; f++) { /* if same wmanufacturerid */ if (((f->card.wmanufacturerid == card_data.wmanufacturerid) || (f->card.wmanufacturerid == UFS_ANY_VENDOR)) && /* and same model */ (STR_PRFX_EQUAL(f->card.model, card_data.model) || - !strcmp(f->card.model, UFS_ANY_MODEL))) + !strncmp(f->card.model, UFS_ANY_MODEL, strlen(UFS_ANY_MODEL))) && + /* and same fw revision*/ + (STR_PRFX_EQUAL(f->card.revision, card_data.revision) || + !strncmp(f->card.revision, UFS_ANY_VER, strlen(UFS_ANY_VER)))) { /* update quirks */ hba->dev_quirks |= f->quirk; + } } out: kfree(card_data.model); + kfree(card_data.revision); } diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h index 3102517e841c1f787f12a0850ac9a72e42ad0fd8..c03ba7b667959408d5c8c66f93eb53fcd670796b 100644 --- a/drivers/scsi/ufs/ufs_quirks.h +++ b/drivers/scsi/ufs/ufs_quirks.h @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _UFS_QUIRKS_H_ #define _UFS_QUIRKS_H_ @@ -19,8 +24,10 @@ #define UFS_ANY_VENDOR -1 #define UFS_ANY_MODEL "ANY_MODEL" +#define UFS_ANY_VER "ANY_VER" #define MAX_MODEL_LEN 16 +#define MAX_REVISION_LEN 8 #define UFS_VENDOR_TOSHIBA 0x198 #define UFS_VENDOR_SAMSUNG 0x1CE @@ -30,6 +37,17 @@ #define UFS_MODEL_TOSHIBA_32GB "THGLF2G8D4KBADR" #define UFS_MODEL_TOSHIBA_64GB "THGLF2G9D8KBADG" +/* UFS SAMSUNG MODELS */ +#define UFS_MODEL_SAMSUNG_64GB "KLUCG4J1" +#define UFS_REVISION_SAMSUNG "0101" + +/* UFS SK HYNIX MODELS */ +#define UFS_MODEL_HYNIX_32GB "hB8aL1" +#define UFS_MODEL_HYNIX_64GB "hC8aL1" +#define UFS_REVISION_HYNIX "D001" + +#define UFS_PURGE_SPEC_VER 0x210 + /** * ufs_card_info - ufs device details * @wmanufacturerid: card details @@ -37,7 +55,9 @@ */ struct ufs_card_info { u16 wmanufacturerid; + u16 specver; char *model; + char *revision; }; /** @@ -57,6 +77,15 @@ struct ufs_card_fix { { \ .card.wmanufacturerid = (_vendor),\ .card.model = (_model), \ + .card.revision = (UFS_ANY_VER), \ + .quirk = (_quirk), \ + } + +#define UFS_FIX_REVISION(_vendor, _model, _revision, _quirk) \ + { \ + .card.wmanufacturerid = (_vendor),\ + .card.model = (_model), \ + .card.revision = (_revision), \ .quirk = (_quirk), \ } @@ -146,6 +175,8 @@ struct ufs_card_fix { * device would apply this 2 steps gear switch workaround. */ #define UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH (1 << 8) +#define UFS_DEVICE_QUIRK_EXTEND_SYNC_LENGTH (1 << 23) +#define UFS_DEVICE_QUIRK_NO_PURGE (1 << 24) struct ufs_hba; void ufs_advertise_fixup_device(struct ufs_hba *hba); diff --git a/drivers/scsi/ufs/ufs_test.c b/drivers/scsi/ufs/ufs_test.c index e23dc3e8d9dafe36c60c8dd582e44468f69ece3a..0f3a4905979980a42f2966c03c9f159cdd5998b1 100644 --- a/drivers/scsi/ufs/ufs_test.c +++ b/drivers/scsi/ufs/ufs_test.c @@ -78,6 +78,7 @@ static const struct file_operations ufs_test_ ## test_name ## _ops = { \ .open = ufs_test_ ## test_name ## _open, \ .read = seq_read, \ .write = ufs_test_ ## test_name ## _write, \ + .release = single_release, \ }; #define add_test(utd, test_name, upper_case_name) \ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6af0ca6eb7e27cf72a93fde7b16ea85a88d951a7..2cd2b25596afcf12973981d4962001519c5a0955 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -36,6 +36,11 @@ * The Linux Foundation chooses to take subject only to the GPLv2 * license terms, and distributes only under these terms. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -43,6 +48,8 @@ #include #include #include +#include +#include #include "ufshcd.h" #include "ufshci.h" @@ -249,7 +256,7 @@ static u32 ufs_query_desc_max_size[] = { QUERY_DESC_RFU_MAX_SIZE, QUERY_DESC_GEOMETRY_MAZ_SIZE, QUERY_DESC_POWER_MAX_SIZE, - QUERY_DESC_RFU_MAX_SIZE, + QUERY_DESC_DEVICE_HEALTH_MAX_SIZE, }; enum { @@ -3815,6 +3822,11 @@ int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size) return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size); } +int ufshcd_read_device_health_desc(struct ufs_hba *hba, u8 *buf, u32 size) +{ + return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE_HEALTH, 0, buf, size); +} + /** * ufshcd_read_string_desc - read string descriptor * @hba: pointer to adapter instance @@ -4717,6 +4729,12 @@ int ufshcd_change_power_mode(struct ufs_hba *hba, ufshcd_dme_set(hba, UIC_ARG_MIB(DME_LocalAFC0ReqTimeOutVal), DL_AFC0ReqTimeOutVal_Default); + if (hba->dev_quirks & UFS_DEVICE_QUIRK_EXTEND_SYNC_LENGTH) { + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TxHsG1SyncLength), 0x48); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TxHsG2SyncLength), 0x48); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TxHsG3SyncLength), 0x48); + } + ret = ufshcd_uic_change_pwr_mode(hba, pwr_mode->pwr_rx << 4 | pwr_mode->pwr_tx); @@ -5000,6 +5018,8 @@ static int ufshcd_link_startup(struct ufs_hba *hba) if (!ufshcd_is_ufs_dev_active(hba)) link_startup_again = true; + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_AVAILTXDATALANES), 0x1); + link_startup: do { ufshcd_vops_link_startup_notify(hba, PRE_CHANGE); @@ -7646,8 +7666,9 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie) * It will read the opcode, idn and buf_length parameters, and, put the * response in the buffer field while updating the used size in buf_length. */ -static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer) +static int ufshcd_query_ioctl(struct scsi_device *dev, u8 lun, void __user *buffer) { + struct ufs_hba *hba = shost_priv(dev->host); struct ufs_ioctl_query_data *ioctl_data; int err = 0; int length = 0; @@ -7718,7 +7739,6 @@ static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer) case QUERY_ATTR_IDN_ACTIVE_ICC_LVL: case QUERY_ATTR_IDN_OOO_DATA_EN: case QUERY_ATTR_IDN_BKOPS_STATUS: - case QUERY_ATTR_IDN_PURGE_STATUS: case QUERY_ATTR_IDN_MAX_DATA_IN: case QUERY_ATTR_IDN_MAX_DATA_OUT: case QUERY_ATTR_IDN_REF_CLK_FREQ: @@ -7727,12 +7747,20 @@ static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer) case QUERY_ATTR_IDN_EE_CONTROL: case QUERY_ATTR_IDN_EE_STATUS: case QUERY_ATTR_IDN_SECONDS_PASSED: + case QUERY_ATTR_IDN_FFU_STATUS: index = 0; break; case QUERY_ATTR_IDN_DYN_CAP_NEEDED: case QUERY_ATTR_IDN_CORR_PRG_BLK_NUM: index = lun; break; + case QUERY_ATTR_IDN_PURGE_STATUS: + index = 0; + if (hba->dev_quirks & UFS_DEVICE_QUIRK_NO_PURGE) { + err = -EPERM; + goto out_release_mem; + } + break; default: goto out_einval; } @@ -7776,16 +7804,36 @@ static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer) case QUERY_FLAG_IDN_PERMANENT_WPE: case QUERY_FLAG_IDN_PWR_ON_WPE: case QUERY_FLAG_IDN_BKOPS_EN: - case QUERY_FLAG_IDN_PURGE_ENABLE: case QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL: case QUERY_FLAG_IDN_BUSY_RTC: break; + case QUERY_FLAG_IDN_PURGE_ENABLE: + if (hba->dev_quirks & UFS_DEVICE_QUIRK_NO_PURGE) { + err = -EPERM; + goto out_release_mem; + } + break; default: goto out_einval; } err = ufshcd_query_flag_retry(hba, ioctl_data->opcode, ioctl_data->idn, &flag); break; + case UPIU_QUERY_OPCODE_SET_FLAG: + switch (ioctl_data->idn) { + case QUERY_FLAG_IDN_PURGE_ENABLE: + if (hba->dev_quirks & UFS_DEVICE_QUIRK_NO_PURGE) { + err = -EPERM; + goto out_release_mem; + } + pm_runtime_disable(&dev->sdev_gendev); + break; + default: + goto out_einval; + } + err = ufshcd_query_flag_retry(hba, ioctl_data->opcode, + ioctl_data->idn, NULL); + break; default: goto out_einval; } @@ -7816,6 +7864,7 @@ static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer) data_ptr = &flag; break; case UPIU_QUERY_OPCODE_WRITE_ATTR: + case UPIU_QUERY_OPCODE_SET_FLAG: goto out_release_mem; default: goto out_einval; @@ -7846,6 +7895,92 @@ out: return err; } +static int ufshcd_write_buffer(struct ufs_hba *hba, void __user *buffer) +{ + int err = 0; + unsigned char cmd[11] = {WRITE_BUFFER, 0x0E, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + struct ufs_ioctl_write_buffer_data *ioctl_data = NULL; + struct ufs_ioctl_write_buffer_data *fw_data = NULL; + struct Scsi_Host *shost = NULL; + struct scsi_device *sdev = NULL; + unsigned char sense[SCSI_SENSE_BUFFERSIZE]; + struct scsi_sense_hdr sshdr; + + ioctl_data = kmalloc(sizeof(struct ufs_ioctl_write_buffer_data), GFP_KERNEL); + if (!ioctl_data) { + dev_err(hba->dev, "%s: Failed allocating ioctl_data\n", __func__); + err = -ENOMEM; + goto out; + } + + err = copy_from_user(ioctl_data, buffer, sizeof(struct ufs_ioctl_write_buffer_data)); + if (err) { + dev_err(hba->dev, "%s: Failed copying ioctl_data from user, err %d\n", __func__, err); + goto out; + } + + fw_data = kmalloc(sizeof(struct ufs_ioctl_write_buffer_data) + ioctl_data->buf_size, GFP_KERNEL); + if (!fw_data) { + dev_err(hba->dev, "%s: Failed allocating fw_data\n", __func__); + err = -ENOMEM; + goto out; + } + + err = copy_from_user(fw_data, buffer, sizeof(struct ufs_ioctl_write_buffer_data) + ioctl_data->buf_size); + if (err) { + dev_err(hba->dev, "%s: Failed copying fw_data from user, err %d\n", __func__, err); + goto out; + } + + cmd[6] = (ioctl_data->buf_size >> 16) & 0xff; + cmd[7] = (ioctl_data->buf_size >> 8) & 0xff; + cmd[8] = ioctl_data->buf_size & 0xff; + + shost = scsi_host_lookup(0); + if (!shost) { + dev_err(hba->dev, "%s: Failed to get scsi_host\n", __func__); + err = -ENODEV; + goto out; + } + + sdev = scsi_device_lookup(shost, 0, 0, 0); + if (!sdev) { + dev_err(hba->dev, "%s: Failed to get scsi_device\n", __func__); + err = -ENODEV; + goto out; + } + + err = scsi_execute(sdev, cmd, DMA_TO_DEVICE, fw_data->buffer, ioctl_data->buf_size, sense, 10000, 1, + REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER, NULL); + if (err) { + dev_err(hba->dev, "%s: Failed write buffer %d\n", __func__, err); + goto out; + } + + if (scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) { + dev_err(hba->dev, "%s: print sense hdr\n", __func__); + scsi_print_sense_hdr(sdev, "ffu", &sshdr); + } + +out: + if (sdev) { + scsi_device_put(sdev); + } + + if (shost) { + scsi_host_put(shost); + } + + if (fw_data) { + kfree(fw_data); + } + + if (ioctl_data) { + kfree(ioctl_data); + } + return err; +} + /** * ufshcd_ioctl - ufs ioctl callback registered in scsi_host * @dev: scsi device required for per LUN queries @@ -7859,20 +7994,35 @@ static int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer) { struct ufs_hba *hba = shost_priv(dev->host); int err = 0; + struct task_struct *tsk = current; BUG_ON(!hba); if (!buffer) { - dev_err(hba->dev, "%s: User buffer is NULL!\n", __func__); + dev_err(hba->dev, "%s: User buffer is NULL! (cmd:0x%08x)\n", + __func__, cmd); + if (tsk && tsk->pid) { + dev_err(hba->dev, " pid=%d comm=%s", tsk->pid, tsk->comm); + + if (tsk->parent && tsk->parent->pid) { + dev_err(hba->dev, " ppid=%d pcomm=%s", + tsk->parent->pid, tsk->parent->comm); + } + } return -EINVAL; } switch (cmd) { case UFS_IOCTL_QUERY: pm_runtime_get_sync(hba->dev); - err = ufshcd_query_ioctl(hba, ufshcd_scsi_to_upiu_lun(dev->lun), + err = ufshcd_query_ioctl(dev, ufshcd_scsi_to_upiu_lun(dev->lun), buffer); pm_runtime_put_sync(hba->dev); break; + case UFS_IOCTL_WRITE_BUFFER: + pm_runtime_get_sync(hba->dev); + err = ufshcd_write_buffer(hba, buffer); + pm_runtime_put_sync(hba->dev); + break; default: err = -ENOIOCTLCMD; dev_dbg(hba->dev, "%s: Unsupported ioctl cmd %d\n", __func__, diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 931b6b31de190573eb9a4c54711b07cdfdc7ec20..45609b4343dc499b1ba998c7e6b182732ed6a6ca 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -33,6 +33,11 @@ * any damages of any kind arising from your use or distribution of * this program. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _UFSHCD_H #define _UFSHCD_H @@ -559,6 +564,9 @@ struct debugfs_files { struct fault_attr fail_attr; #endif bool is_sys_suspended; + struct dentry *dump_dev_health_desc; + struct dentry *fw_revision; + struct dentry *serial; }; /* tag stats statistics types */ @@ -1159,6 +1167,7 @@ out: } int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size); +int ufshcd_read_device_health_desc(struct ufs_hba *hba, u8 *buf, u32 size); static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info) { diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h index 602e196e924971b72d100d2a97898896efce783d..423b27d223039f14ad771dc5ec3cba19d69f6977 100644 --- a/drivers/scsi/ufs/unipro.h +++ b/drivers/scsi/ufs/unipro.h @@ -6,6 +6,11 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _UNIPRO_H_ #define _UNIPRO_H_ @@ -119,6 +124,10 @@ #define PA_STALLNOCONFIGTIME 0x15A3 #define PA_SAVECONFIGTIME 0x15A4 +#define PA_TxHsG1SyncLength 0x1552 +#define PA_TxHsG2SyncLength 0x1554 +#define PA_TxHsG3SyncLength 0x1556 + #define PA_TACTIVATE_TIME_UNIT_US 10 #define PA_HIBERN8_TIME_UNIT_US 100 diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 62b7d12629e4f350b73db7b6aa5be8ebcc8c0e86..4cc16b57a8e9d975482dd0b5e04639e002eb3333 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -437,6 +437,13 @@ config QCOM_WATCHDOG_V2 deadlocks. It does not run during the bootup process, so it will not catch any early lockups. +config MSM_FORCE_PANIC_ON_WDOG_BARK + bool "Force Panic on watchdog bark" + depends on QCOM_WATCHDOG_V2 + help + Triggers a kernel panic for watchdog bark. This will print more + information about the current running tasks on all the cpus. + config QCOM_IRQ_HELPER bool "QCOM Irq Helper" help @@ -915,6 +922,29 @@ config MSM_RPM_STATS_LOG the low power modes that RPM enters. The drivers outputs the message via a debugfs node. +config SUBSYS_LAST_ERR_LOG + bool "support last error log for subsystems" + help + When a fatal error is encountered, few subsystems export the + error log into SMEM. This driver supports the mechanism to read + these logs and exports them to user via procfs. + If unsure, say N + +config LAST_LOGS + bool "support last logs for system" + depends on DEBUG_FS + select SECURITY_STATUS + help + When a fatal error is encountered on system, system reboots and + saves the logs in last logs memory. This driver supports the mechanism + to read these logs and exports them via debugfs. + +config SECURITY_STATUS + bool "Get security_status" + help + Gets security status by checking oemandroidboot.securityflags + parameter. + config QSEE_IPC_IRQ_BRIDGE tristate "QSEE IPC Interrupt Bridge" help diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 5e565c8638891921789886bbc828dc4b91c53ebf..b0bedc1a9e646122862a3695bcf65811e9af23b9 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -111,3 +111,15 @@ obj-$(CONFIG_QCOM_CX_IPEAK) += cx_ipeak.o obj-$(CONFIG_MSM_CACHE_M4M_ERP64) += cache_m4m_erp64.o obj-$(CONFIG_MSM_HAB) += hab/ obj-$(CONFIG_QCOM_QDSS_BRIDGE) += qdss_bridge.o +obj-$(CONFIG_ARCH_MSM8998) += debug_memory.o +obj-$(CONFIG_RAMDUMP_TAGS) += board-rdtags.o +CFLAGS_board-rdtags.o := \ + -D"INFO_PRODUCT=\"$(if $(TARGET_PRODUCT),$(TARGET_PRODUCT),unknown)\"" \ + -D"INFO_VARIANT= \ + \"$(if $(TARGET_BUILD_VARIANT),$(TARGET_BUILD_VARIANT),unknown)\"" \ + -D"INFO_BUILDID= \ + \"$(if $(SEMC_SYSTEM_VERSION),$(SEMC_SYSTEM_VERSION),private)\"" +obj-$(CONFIG_SUBSYS_LAST_ERR_LOG) += last_subsys_errlog.o +obj-$(CONFIG_ARCH_MSM8998) += console_setup.o +obj-$(CONFIG_LAST_LOGS) += last_logs.o +obj-$(CONFIG_SECURITY_STATUS) += security_status.o diff --git a/drivers/soc/qcom/board-rdtags.c b/drivers/soc/qcom/board-rdtags.c new file mode 100644 index 0000000000000000000000000000000000000000..c1893ca77c3dc4efcebb56c092ad0d95593ea20e --- /dev/null +++ b/drivers/soc/qcom/board-rdtags.c @@ -0,0 +1,86 @@ +/* + * + * Author: Nilsson, Stefan 2 + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include "board-rdtags.h" +#include + +#define RDTAGS_TAG_MAGIC 0xBADFAD01 +#define NBR_OF_ELEMENTS 3 + +struct rdtags_tag_const { + const char const key[16]; + const char const value[64]; +}; + +struct rdtags_build_tags { + const unsigned int magic; + const unsigned int size; + const struct rdtags_tag_const tag_array[NBR_OF_ELEMENTS]; +}; + +static const struct rdtags_build_tags rdtags_build_info = { + RDTAGS_TAG_MAGIC, + sizeof(rdtags_build_info), + { + { + "build_product", + INFO_PRODUCT + }, + { + "build_variant", + INFO_VARIANT + }, + { + "build_id", + INFO_BUILDID + } + } +}; + +static int board_rdtags_init(int ramdump_mode) +{ + int nbr_tags = 0; + int i = 0; + + if (!ramdump_mode) { + for (i = 0; i < NBR_OF_ELEMENTS; i++) { + const char *key; + const unsigned char *value; + unsigned int size = 0; + + key = rdtags_build_info.tag_array[i].key; + value = rdtags_build_info.tag_array[i].value; + size = strnlen(value, + sizeof(rdtags_build_info.tag_array[i].value)); + if (!rdtags_add_tag(key, value, size)) + nbr_tags++; + } + + nbr_tags += dump_table_ramdump_setup(); + } + + return nbr_tags; +} + +struct rdtags_platform_data rdtags_platdata = { + .platform_init = board_rdtags_init, +}; diff --git a/drivers/soc/qcom/board-rdtags.h b/drivers/soc/qcom/board-rdtags.h new file mode 100644 index 0000000000000000000000000000000000000000..929ef9644304cf90bd1ce4f8d1bf12a3244ca16a --- /dev/null +++ b/drivers/soc/qcom/board-rdtags.h @@ -0,0 +1,23 @@ +/* + * + * Author: Nilsson, Stefan 2 + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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 __BOARD_RDTAGS_H_ +#define __BOARD_RDTAGS_H_ + +extern struct rdtags_platform_data rdtags_platdata; + +#endif diff --git a/drivers/soc/qcom/console_setup.c b/drivers/soc/qcom/console_setup.c new file mode 100644 index 0000000000000000000000000000000000000000..f77bc4c8f185bf21589cb2deafae53cfdfabdb66 --- /dev/null +++ b/drivers/soc/qcom/console_setup.c @@ -0,0 +1,163 @@ +/* drivers/soc/qcom/console_setup.c + * + * 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. + */ +/* + * Copyright (C) 2014 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include + +#define CONSOLE_NAME "ttyMSM" +#define CONSOLE_IX 0 +#define CONSOLE_OPTIONS "115200,n8" +#define CONSOLE_ENABLE 0x01 + +static int need_serial_console; + +static int __init setup_serial_console(char *console_flag) +{ + unsigned long val; + + if (kstrtoul(console_flag, 16, &val)) + return -EINVAL; + + if (val & CONSOLE_ENABLE) + need_serial_console = 1; + + return 0; +} + +/* +* The S1 Boot configuration TA unit can specify that the serial console +* enable flag will be passed as Kernel boot arg with tag babe09A9. +*/ +early_param("oemandroidboot.babe09a9", setup_serial_console); + +static void disable_serial_gpio(void) +{ + struct device_node *np_tx, *np_rx, *np_tx_mux, *np_rx_mux, + *np_tx_def, *np_rx_def; + struct property *pp; + static struct property gpio_function = { + .name = "function", + .value = "gpio", + .length = sizeof("gpio"), + }; + static struct property output_low = { + .name = "output-low", + .value = NULL, + .length = 0, + }; + static struct property bias_disable = { + .name = "bias-disable", + .value = NULL, + .length = 0, + }; + + np_tx = of_find_node_by_path( + "/soc/pinctrl@03400000/msm_gpio_4"); + if (!np_tx) { + pr_err("couldn't find msm_gpio_4 node\n"); + return; + } + + np_rx = of_find_node_by_path( + "/soc/pinctrl@03400000/msm_gpio_5"); + if (!np_rx) { + pr_err("couldn't find msm_gpio_5 node\n"); + goto err0; + } + + np_tx_mux = of_find_node_by_name(np_tx, "mux"); + if (!np_tx_mux) { + pr_err("couldn't find msm_gpio_4 mux node\n"); + goto err1; + } + + np_rx_mux = of_find_node_by_name(np_rx, "mux"); + if (!np_tx_mux) { + pr_err("couldn't find msm_gpio_5 mux node\n"); + goto err2; + } + + of_update_property(np_tx_mux, &gpio_function); + of_update_property(np_rx_mux, &gpio_function); + + np_tx_def = of_find_node_by_name(np_tx, "config"); + if (!np_tx_def) { + pr_err("couldn't find msm_gpio_4 config node\n"); + goto err3; + } + + np_rx_def = of_find_node_by_name(np_rx, "config"); + if (!np_rx_def) { + pr_err("couldn't find msm_gpio_5 config node\n"); + goto err4; + } + + of_add_property(np_tx_def, &output_low); + + pp = of_find_property(np_rx_def, "bias-pull-up", NULL); + if (pp) { + of_remove_property(np_rx_def, pp); + of_add_property(np_rx_def, &bias_disable); + } + of_add_property(np_rx_def, &output_low); + + of_node_put(np_rx_def); +err4: + of_node_put(np_tx_def); +err3: + of_node_put(np_rx_mux); +err2: + of_node_put(np_tx_mux); +err1: + of_node_put(np_rx); +err0: + of_node_put(np_tx); + return; +} + +static int __init init_console_setup(void) +{ + if (need_serial_console) { + pr_info("Adding %s%d as preferred console\n", + CONSOLE_NAME, CONSOLE_IX); + add_preferred_console(CONSOLE_NAME, + CONSOLE_IX, + CONSOLE_OPTIONS); + } else { + struct device_node *np; + static struct property serial_con_status = { + .name = "status", + .value = "disabled", + .length = sizeof("disabled"), + }; + + np = of_find_node_by_path("/soc/serial@0c1b0000"); + if (!np) { + pr_err("couldn't find /soc/serial@0c1b0000 node\n"); + return -EINVAL; + } + + pr_info("disabling %s node", np->full_name); + of_update_property(np, &serial_con_status); + of_node_put(np); + disable_serial_gpio(); + } + + return 0; +} +early_initcall(init_console_setup); diff --git a/drivers/soc/qcom/debug_memory.c b/drivers/soc/qcom/debug_memory.c new file mode 100644 index 0000000000000000000000000000000000000000..619201f39f26461f97713f4daa8c8f02f79231f3 --- /dev/null +++ b/drivers/soc/qcom/debug_memory.c @@ -0,0 +1,303 @@ + /* + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif +#ifdef CONFIG_RAMDUMP_TAGS +#include +#include "board-rdtags.h" +#endif +#ifdef CONFIG_LAST_LOGS +#include +#endif + +#define RAMDUMP_MEMDESC_SIZE (256 * SZ_1K) +#define RDTAGS_MEM_SIZE (256 * SZ_1K) +#define HEADER_SIZE (4 * SZ_1K) +#define SUBSYS_ERR_LOG_SIZE ((16 * SZ_1K) + HEADER_SIZE) + +#define RDTAGS_OFFSET (RAMDUMP_MEMDESC_SIZE) +#define AMSS_LOGS_OFFSET (RAMDUMP_MEMDESC_SIZE + RDTAGS_MEM_SIZE) +#define ADSP_LOGS_OFFSET (AMSS_LOGS_OFFSET + SUBSYS_ERR_LOG_SIZE) +#ifdef CONFIG_LAST_LOGS +#define LAST_LOGS_OFFSET (768 * SZ_1K) +#endif + +static int ramdump_mode; +static unsigned long debug_mem_base; +static unsigned long debug_mem_size; + +static int __init warm_boot_setup(char *p) +{ + unsigned long res; + + if (!p || !*p) + return -EINVAL; + + if (!kstrtoul(p, 0, &res)) { + if (res == 0xC0DEDEAD || res == 0xABADBABE) + ramdump_mode = 1; + } + + pr_info("board-ramdump: boot mode detected as %s\n", + ramdump_mode ? "ramdump" : "normal"); + return 0; +} +early_param("warmboot", warm_boot_setup); + +#ifdef CONFIG_OF +static const struct of_device_id ramdump_dt[] = { + { .compatible = "qcom,debug_memory" }, + {} +}; +#endif + +static int __init ramdump_debug_memory_init(void) +{ + struct device_node *node; + uint32_t *regs = NULL; + size_t cells; + + node = of_find_matching_node(NULL, ramdump_dt); + if (!node) { + pr_err("debug region node not found\n"); + return -EINVAL; + } + + cells = of_n_addr_cells(node) + of_n_size_cells(node); + regs = kcalloc(cells, sizeof(uint32_t), GFP_KERNEL); + if (!regs) { + pr_err("Failed to allocate memory for cells\n"); + of_node_put(node); + return -ENOMEM; + } + + if (of_property_read_u32_array(node, "reg", regs, cells)) { + pr_err("unable to find base address of node in dtb\n"); + goto failed; + } + + if (cells == 4) { + debug_mem_base = (unsigned long)regs[0] << 32 | regs[1]; + debug_mem_size = (unsigned long)regs[2] << 32 | regs[3]; + } else if (cells == 2) { + debug_mem_base = regs[0]; + debug_mem_size = regs[1]; + } else { + pr_err("bad number of cells in the regs property\n"); + goto failed; + } + + pr_info("board-ramdump: Initialized debug memory at %lx-%lx\n", + debug_mem_base, debug_mem_base + debug_mem_size - 1); + of_node_put(node); + kfree(regs); + + return 0; + +failed: + of_node_put(node); + kfree(regs); + return -EINVAL; +} + +#ifdef CONFIG_RAMDUMP_MEMDESC +static struct resource ramdump_memdesc_resources[] = { + [0] = { + .name = "ramdump_memdesc", + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ramdump_memdesc_device = { + .name = "ramdump_memdesc", + .id = -1, +}; +#endif + +#ifdef CONFIG_RAMDUMP_TAGS +static struct resource rdtags_resources[] = { + [0] = { + .name = "rdtags_mem", + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device rdtags_device = { + .name = "rdtags", + .id = -1, + .dev = { + .platform_data = &rdtags_platdata, + }, +}; +#endif + +#ifdef CONFIG_SUBSYS_LAST_ERR_LOG +static struct resource last_subsyslog_resources[] = { + [0] = { + .name = "amsslog", + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "adsplog", + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device last_subsyslog_device = { + .name = "last_subsyslog", + .id = -1, +}; +#endif + +#ifdef CONFIG_LAST_LOGS +static struct platform_device last_logs_device = { + .name = "rd_last_logs", + .id = -1, +}; + +static struct resource *last_logs_resources; +#endif + +static void __init ramdump_add_devices(void) +{ + if (!debug_mem_base) + return; + +#ifdef CONFIG_RAMDUMP_MEMDESC + ramdump_memdesc_resources[0].start = debug_mem_base; + ramdump_memdesc_resources[0].end = ramdump_memdesc_resources[0].start + + RAMDUMP_MEMDESC_SIZE - 1; + ramdump_memdesc_device.num_resources = + ARRAY_SIZE(ramdump_memdesc_resources); + ramdump_memdesc_device.resource = ramdump_memdesc_resources; + ramdump_memdesc_device.dev.platform_data = &ramdump_mode; + + platform_device_register(&ramdump_memdesc_device); +#endif + +#ifdef CONFIG_RAMDUMP_TAGS + rdtags_resources[0].start = debug_mem_base + RDTAGS_OFFSET; + rdtags_resources[0].end = rdtags_resources[0].start + + RDTAGS_MEM_SIZE - 1; + rdtags_device.num_resources = ARRAY_SIZE(rdtags_resources); + rdtags_device.resource = rdtags_resources; + rdtags_platdata.ramdump_mode = ramdump_mode; + + platform_device_register(&rdtags_device); +#endif + +#ifdef CONFIG_SUBSYS_LAST_ERR_LOG + last_subsyslog_resources[0].start = debug_mem_base + AMSS_LOGS_OFFSET; + last_subsyslog_resources[0].end = last_subsyslog_resources[0].start + + SUBSYS_ERR_LOG_SIZE - 1; + + last_subsyslog_resources[1].start = debug_mem_base + ADSP_LOGS_OFFSET; + last_subsyslog_resources[1].end = last_subsyslog_resources[1].start + + SUBSYS_ERR_LOG_SIZE - 1; + + last_subsyslog_device.num_resources = + ARRAY_SIZE(last_subsyslog_resources); + last_subsyslog_device.resource = last_subsyslog_resources; + last_subsyslog_device.dev.platform_data = &ramdump_mode; + + platform_device_register(&last_subsyslog_device); +#endif +} + +#ifdef CONFIG_LAST_LOGS +static char last_logs_names[MAX_LAST_LOGS_REGIONS][MAX_NAME_LENGTH]; +#define LAST_LOGS_HEADER_SIZE sizeof(last_logs_header) +static void __init ramdump_add_last_logs_device(void) +{ + int i; + last_logs_header __iomem *last_logs_addr; + last_logs_header last_logs_hdr; + phys_addr_t last_logs_paddr; + last_logs_region *region = NULL; + + if (!debug_mem_base) + return; + + last_logs_paddr = debug_mem_base + LAST_LOGS_OFFSET; + last_logs_addr = (last_logs_header *)ioremap(last_logs_paddr, + LAST_LOGS_HEADER_SIZE); + + if (last_logs_addr->version != LAST_LOGS_VERSION || + last_logs_addr->magic != LAST_LOGS_MAGIC) { + pr_err("ERROR: %s: magic = %x, version = 0x%x\n", + __func__, last_logs_addr->magic, + last_logs_addr->version); + iounmap(last_logs_addr); + return; + } + + memcpy_fromio((void *)&last_logs_hdr, last_logs_addr, + LAST_LOGS_HEADER_SIZE); + memset_io(last_logs_addr, 0, LAST_LOGS_HEADER_SIZE); + iounmap(last_logs_addr); + + if (last_logs_hdr.num_regions > MAX_LAST_LOGS_REGIONS) + return; + + last_logs_resources = (struct resource *)kcalloc( + last_logs_hdr.num_regions, + sizeof(struct resource), GFP_KERNEL); + if (!last_logs_resources) { + pr_err("%s: Failed to allocate memory for resources\n", + __func__); + return; + } + + for (i = 0; i < last_logs_hdr.num_regions; i++) { + region = &last_logs_hdr.regions[i]; + last_logs_resources[i].name = last_logs_names[i]; + + last_logs_resources[i].start = + last_logs_paddr + region->offset; + last_logs_resources[i].end = last_logs_resources[i].start + + region->size - 1; + last_logs_resources[i].flags = IORESOURCE_MEM; + memcpy((void *)last_logs_resources[i].name, region->name, + sizeof(region->name)); + } + + last_logs_device.resource = last_logs_resources; + last_logs_device.num_resources = last_logs_hdr.num_regions; + platform_device_register(&last_logs_device); +} +#endif + +static int __init board_ramdump_init(void) +{ + ramdump_debug_memory_init(); + ramdump_add_devices(); +#ifdef CONFIG_LAST_LOGS + ramdump_add_last_logs_device(); +#endif + return 0; +} +core_initcall(board_ramdump_init); diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 1fc891b0601668da286bd163b0912e35aff4f99c..5ad9496fdf9cbc19ad542702cde5c9c170ffcc6d 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "icnss: " fmt @@ -38,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -490,6 +496,9 @@ static struct icnss_priv { u8 requesting_sub_system; u16 line_number; char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1]; + char crash_reason[SUBSYS_CRASH_REASON_LEN]; + wait_queue_head_t wlan_pdr_debug_q; + int data_ready; } *penv; #ifdef CONFIG_ICNSS_DEBUG @@ -2666,6 +2675,15 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n", *state, priv->state, icnss_pdr_cause[cause]); + if (*state == USER_PD_STATE_CHANGE) { + memset(priv->crash_reason, 0, sizeof(priv->crash_reason)); + snprintf(priv->crash_reason, sizeof(priv->crash_reason), + "PD service down, pd_state: %d, state: 0x%lx: cause: %s\n", + *state, priv->state, icnss_pdr_cause[cause]); + priv->data_ready = 1; + wake_up(&priv->wlan_pdr_debug_q); + } + event_post: if (!test_bit(ICNSS_FW_DOWN, &priv->state)) { set_bit(ICNSS_FW_DOWN, &priv->state); @@ -4273,6 +4291,39 @@ static int icnss_regread_open(struct inode *inode, struct file *file) return single_open(file, icnss_regread_show, inode->i_private); } +static unsigned int wlan_pdr_crash_reason_poll(struct file *filp, + struct poll_table_struct *wait) +{ + unsigned int mask = 0; + struct icnss_priv *priv = filp->private_data; + + poll_wait(filp, &priv->wlan_pdr_debug_q, wait); + + if (priv->data_ready) + mask |= (POLLIN | POLLRDNORM); + + return mask; +} + +static ssize_t wlan_pdr_crash_reason_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + int r; + char buf[SUBSYS_CRASH_REASON_LEN]; + ssize_t size = 0; + struct icnss_priv *priv = filp->private_data; + + memset(buf, 0, sizeof(buf)); + r = snprintf(buf, sizeof(buf), "%s", priv->crash_reason); + size = simple_read_from_buffer(ubuf, cnt, ppos, buf, r); + if (*ppos == r) { + memset(priv->crash_reason, 0, sizeof(priv->crash_reason)); + priv->data_ready = 0; + } + + return size; +} + static const struct file_operations icnss_regread_fops = { .read = seq_read, .write = icnss_regread_write, @@ -4281,6 +4332,30 @@ static const struct file_operations icnss_regread_fops = { .llseek = seq_lseek, }; +static const struct file_operations wlan_pdr_crash_reason_fops = { + .open = simple_open, + .read = wlan_pdr_crash_reason_read, + .poll = wlan_pdr_crash_reason_poll, + .llseek = default_llseek, +}; + +static void wlan_pdr_debugfs_create(struct dentry *root_dentry, + struct icnss_priv *priv) +{ + struct dentry *crash_reason_dentry; + + crash_reason_dentry = debugfs_create_dir("crash_reason", + root_dentry); + if (IS_ERR(crash_reason_dentry)) + icnss_pr_err("Unable to create debugfs %ld\n", + PTR_ERR(crash_reason_dentry)); + else + debugfs_create_file("wlan_pdr_crash_reason", S_IRUGO | S_IWUSR, + crash_reason_dentry, priv, + &wlan_pdr_crash_reason_fops); + +} + #ifdef CONFIG_ICNSS_DEBUG static int icnss_debugfs_create(struct icnss_priv *priv) { @@ -4306,6 +4381,7 @@ static int icnss_debugfs_create(struct icnss_priv *priv) &icnss_regread_fops); debugfs_create_file("reg_write", 0600, root_dentry, priv, &icnss_regwrite_fops); + wlan_pdr_debugfs_create(root_dentry); out: return ret; @@ -4328,6 +4404,8 @@ static int icnss_debugfs_create(struct icnss_priv *priv) debugfs_create_file("stats", 0600, root_dentry, priv, &icnss_stats_fops); + wlan_pdr_debugfs_create(root_dentry, priv); + return 0; } #endif @@ -4552,6 +4630,7 @@ static int icnss_probe(struct platform_device *pdev) goto out_smmu_deinit; } + init_waitqueue_head(&priv->wlan_pdr_debug_q); INIT_WORK(&priv->event_work, icnss_driver_event_work); INIT_WORK(&priv->qmi_recv_msg_work, icnss_qmi_wlfw_clnt_notify_work); INIT_LIST_HEAD(&priv->event_list); diff --git a/drivers/soc/qcom/last_logs.c b/drivers/soc/qcom/last_logs.c new file mode 100644 index 0000000000000000000000000000000000000000..a293c3169cc73661feb41e08e0cffca808bd2086 --- /dev/null +++ b/drivers/soc/qcom/last_logs.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dentry *debugfs_entry; +typedef int (*func_ptr)(void *, size_t, void **, uint32_t*); + +static ssize_t last_logs_read(struct file *file, char __user *buf, + size_t len, loff_t *offp) +{ + struct last_logs_data *priv_data = + (struct last_logs_data *)file->private_data; + + return simple_read_from_buffer(buf, len, offp, + priv_data->addr, priv_data->size); +} + +static int last_logs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations last_logs_fops = { + .owner = THIS_MODULE, + .read = last_logs_read, + .open = last_logs_open, +}; + +static int last_logs_init_resource(struct platform_device *pdev, + struct resource *resource, func_ptr func) +{ + struct last_logs_data *priv_data; + size_t debug_resource_size; + int ret = -1; + void __iomem *last_virt_iobase; + void *last_logs_addr; + + debug_resource_size = resource->end - resource->start + 1; + + dev_info(&pdev->dev, "log driver initialized %u@%llx\n", + (unsigned int)debug_resource_size, resource->start); + /* + * Map address that stores the physical location diagnostic data + */ + + last_virt_iobase = devm_ioremap_nocache(&pdev->dev, resource->start, + debug_resource_size); + + if (!last_virt_iobase) { + dev_err(&pdev->dev, + "%s: ERROR could not ioremap: start=%pr, len=%u\n", + __func__, &resource->start, + (unsigned int)(debug_resource_size)); + return -ENXIO; + } + + last_logs_addr = kzalloc(debug_resource_size, GFP_KERNEL); + if (!last_logs_addr) { + pr_err("ERROR: %s could not allocate memory", __func__); + iounmap(last_virt_iobase); + return -ENOMEM; + } + + memcpy_fromio(last_logs_addr, last_virt_iobase, debug_resource_size); + /* clear & unmap last_logs debug memory */ + memset_io(last_virt_iobase, 0, debug_resource_size); + iounmap(last_virt_iobase); + + priv_data = kzalloc(sizeof(struct last_logs_data), GFP_KERNEL); + if (!priv_data) { + pr_err("ERROR: %s could not allocate memory", __func__); + kzfree(last_logs_addr); + return -ENOMEM; + } + + if (func) { + ret = (*func)(last_logs_addr, debug_resource_size, + (void **)&(priv_data->addr), (uint32_t *)&(priv_data->size)); + if (!ret) { + kzfree(last_logs_addr); + last_logs_addr = NULL; + } else if (ret) { + goto exit; + } + } else { + priv_data->addr = last_logs_addr; + priv_data->size = debug_resource_size; + } + + if (debugfs_entry) { + priv_data->debugfs_file = debugfs_create_file(resource->name, + S_IFREG | S_IRUGO, debugfs_entry, priv_data, + &last_logs_fops); + if (!priv_data->debugfs_file) { + dev_err(&pdev->dev, + "%s: Failed to create debug file entry %s\n", + __func__, resource->name); + ret = -EINVAL; + goto exit; + } + } + + return 0; + +exit: + kzfree(last_logs_addr); + kzfree(priv_data); + return ret; +} + +#define STR_TZBSP_LOG "tzbsp_log" +static int last_logs_probe(struct platform_device *pdev) +{ + int i; + + if (!debugfs_entry) { + debugfs_entry = debugfs_create_dir("last_logs", NULL); + if (!debugfs_entry) { + dev_err(&pdev->dev, + "%s: Failed to create last_logs dir\n", + __func__); + return -EINVAL; + } + } + + for (i = 0; i < pdev->num_resources; i++) { + struct resource *r = &pdev->resource[i]; + func_ptr func = NULL; + + if (!r->name) { + dev_err(&pdev->dev, "ERROR device name is invalid"); + continue; + } + +#ifdef CONFIG_MSM_TZ_LOG + if (!memcmp(r->name, STR_TZBSP_LOG, sizeof(STR_TZBSP_LOG))) + func = format_tzbsp_log; +#endif + if (last_logs_init_resource(pdev, r, func) != 0) + dev_err(&pdev->dev, "ERROR: %s last_logs_init", + r->name); + } + + return 0; +} + +static struct platform_driver last_logs_driver = { + .probe = last_logs_probe, + .driver = { + .name = "rd_last_logs", + .owner = THIS_MODULE, + }, +}; + +static int __init last_logs_init(void) +{ + return platform_driver_register(&last_logs_driver); +} + +late_initcall(last_logs_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("last_logs driver"); +MODULE_ALIAS("platform:last_logs"); +MODULE_AUTHOR("Dhamodharan Nallasivam "); diff --git a/drivers/soc/qcom/last_subsys_errlog.c b/drivers/soc/qcom/last_subsys_errlog.c new file mode 100644 index 0000000000000000000000000000000000000000..dd724fe4aa84d31eff1b901206697283f02858a7 --- /dev/null +++ b/drivers/soc/qcom/last_subsys_errlog.c @@ -0,0 +1,340 @@ +/* + * Author: Anirudh Madnurkar + * Author: Sandeep Kumar + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ERRLOG_SIG 0x48484814 + +enum { + SUBSYS_MODEM, + SUBSYS_ADSP, + SUBSYS_ID_MAX +}; + +struct subsys_log { + size_t log_size; + void __iomem *log_base; + char *log_buf; + u32 log_buf_len; +}; + +struct subsys_data { + char name[32]; + char resource_name[32]; + char lastlog_name[32]; + struct notifier_block notifier; + struct subsys_log subsys_log; +}; + +struct lastlog_debug_region { + u32 sig; + u32 size; + u8 data[0]; +}; + +static struct device *dev; + +static int modem_state_cb(struct notifier_block *nb, + unsigned long value, void *priv); +static int adsp_state_cb(struct notifier_block *nb, + unsigned long value, void *priv); + +static struct subsys_data subsys_data[] = { + [SUBSYS_MODEM] = { + .name = "modem", + .resource_name = "amsslog", + .lastlog_name = "last_amsslog", + .notifier = { + .notifier_call = modem_state_cb, + .priority = -INT_MAX, + }, + }, + [SUBSYS_ADSP] = { + .name = "adsp", + .resource_name = "adsplog", + .lastlog_name = "last_adsplog", + .notifier = { + .notifier_call = adsp_state_cb, + .priority = -INT_MAX, + }, + }, +}; + +static int lastlog_open(struct inode *inode, struct file *file) +{ + struct lastlog_debug_region __iomem *buffer = NULL; + struct subsys_data *subsys = PDE_DATA(inode); + struct subsys_log *subsys_log; + u32 max_size; + + if (!subsys) { + dev_err(dev, "subsystem data is NULL.\n"); + return -EINVAL; + } + + subsys_log = &subsys->subsys_log; + buffer = (struct lastlog_debug_region *)subsys_log->log_base; + if (!subsys_log->log_buf || !buffer) { + dev_err(dev, "Log buffer not initialized.\n"); + return -EINVAL; + } + + if (ERRLOG_SIG != readl_relaxed(&buffer->sig)) + return 0; + + max_size = subsys_log->log_size - (buffer->data - (uint8_t *)buffer); + memset(subsys_log->log_buf, 0x0, subsys_log->log_size); + if (readl_relaxed(&buffer->size) > max_size) + writel_relaxed(max_size, &buffer->size); + + memcpy_fromio(subsys_log->log_buf, buffer->data, buffer->size); + subsys_log->log_buf_len = readl_relaxed(&buffer->size); + return 0; +} + +static ssize_t lastlog_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + loff_t pos = *offset; + ssize_t count; + struct subsys_data *subsys = PDE_DATA(file_inode(file)); + struct subsys_log *subsys_log; + + if (!subsys) + return -EINVAL; + + subsys_log = &subsys->subsys_log; + if (pos >= subsys_log->log_buf_len) + return 0; + + count = min(len, (size_t)(subsys_log->log_buf_len - pos)); + if (copy_to_user(buf, subsys_log->log_buf + pos, count)) + return -EFAULT; + + *offset += count; + return count; +} + +static int lastlog_release(struct inode *inode, struct file *file) +{ + struct subsys_data *subsys = PDE_DATA(inode); + struct subsys_log *subsys_log; + + if (!subsys) + return 0; + + subsys_log = &subsys->subsys_log; + if (subsys_log->log_buf) { + memset(subsys_log->log_buf, 0x0, subsys_log->log_size); + subsys_log->log_buf_len = 0; + } + + return 0; +} + +static const struct file_operations lastlog_fops = { + .owner = THIS_MODULE, + .open = lastlog_open, + .read = lastlog_read, + .release = lastlog_release, +}; + + +static int lastlog_extract(unsigned id, + struct subsys_log *subsys_log, + char *err_str) +{ + struct lastlog_debug_region __iomem *buffer = + (struct lastlog_debug_region *)subsys_log->log_base; + char *smem_errlog = NULL; + unsigned size; + + if (!buffer) { + dev_err(dev, "Buffer in debug region not initialized.\n"); + return -EINVAL; + } + + memset_io(subsys_log->log_base, 0x0, subsys_log->log_size); + smem_errlog = smem_get_entry(id, &size, 0, SMEM_ANY_HOST_FLAG); + if (!smem_errlog) { + dev_err(dev, "Could not read SMEM ID. NULL\n"); + return -EINVAL; + } + + if (memcmp(smem_errlog, err_str, 3)) { + dev_err(dev, "SMEM error log is not starting with %s\n", + err_str); + return -EINVAL; + } + + if (size > (subsys_log->log_size - + (buffer->data - (uint8_t *)buffer))) + size = (subsys_log->log_size - + (buffer->data - (uint8_t *)buffer)); + + memcpy_toio(buffer->data, smem_errlog, size); + writel_relaxed(size, &buffer->size); + writel_relaxed(ERRLOG_SIG, &buffer->sig); + return 0; +} + +static int modem_state_cb(struct notifier_block *nb, + unsigned long value, void *priv) +{ + int ret; + + if (value == SUBSYS_RAMDUMP_NOTIFICATION || + value == SUBSYS_SOC_RESET) { + dev_info(dev, "MODEM ramdump notification received.\n"); + ret = lastlog_extract(SMEM_ERR_CRASH_LOG, + &subsys_data[SUBSYS_MODEM].subsys_log, "ERR"); + if (ret < 0) { + dev_err(dev, "Failed to extract modem error log\n"); + return NOTIFY_OK; + } + + dev_info(dev, "Modem logs extracted from smem\n"); + } + + return NOTIFY_OK; +} + +static int adsp_state_cb(struct notifier_block *nb, + unsigned long value, void *priv) +{ + int ret; + + if (value == SUBSYS_RAMDUMP_NOTIFICATION || value == SUBSYS_SOC_RESET) { + dev_info(dev, "ADSP ramdump notification received.\n"); + ret = lastlog_extract(SMEM_ERR_CRASH_LOG_ADSP, + &subsys_data[SUBSYS_ADSP].subsys_log, "err"); + if (ret < 0) { + dev_err(dev, "Failed to extract adsp error log\n"); + return NOTIFY_OK; + } + + dev_info(dev, "ADSP logs extracted from smem\n"); + } + + return NOTIFY_OK; +} + +static int lastlog_subsys_init(struct platform_device *pdev, int subsys_id) +{ + void *p, *handle; + int ret; + struct resource *res; + struct subsys_data *subsys = NULL; + struct subsys_log *subsys_log = NULL; + + subsys = &subsys_data[subsys_id]; + subsys_log = &subsys->subsys_log; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + subsys->resource_name); + if (!res || !(res->start)) { + dev_err(dev, "%s resource invalid/absent\n", + subsys->resource_name); + return -ENODEV; + } + + subsys_log->log_size = res->end - res->start + 1; + subsys_log->log_base = (char *)ioremap(res->start, + subsys_log->log_size); + if (subsys_log->log_base == NULL) { + dev_err(dev, "failed to map %s memory\n", subsys->name); + return -ENOMEM; + } + + subsys_log->log_buf = (char *)__get_free_pages(GFP_KERNEL, + get_order(subsys_log->log_size)); + if (!subsys_log->log_buf) { + dev_err(dev, "Failed to allocate pages of order %d\n", + get_order(subsys_log->log_size)); + iounmap(subsys_log->log_base); + return -ENOMEM; + } + + memset(subsys_log->log_buf, 0x0, subsys_log->log_size); + handle = subsys_notif_register_notifier(subsys->name, + &subsys->notifier); + if (IS_ERR(handle)) { + dev_err(dev, "Failed to register %s notifier\n", subsys->name); + ret = -EINVAL; + goto out; + } + + p = proc_create_data(subsys->lastlog_name, S_IRUSR, NULL, + &lastlog_fops, subsys); + if (!p) { + subsys_notif_unregister_notifier(handle, &subsys->notifier); + dev_err(dev, "failed to create %s proc entry\n", + subsys->lastlog_name); + ret = -ENOMEM; + goto out; + } + + return 0; +out: + free_pages((unsigned long)subsys_log->log_buf, + get_order(subsys_log->log_size)); + iounmap(subsys_log->log_base); + return ret; +} + +static int lastlog_subsys_driver_probe(struct platform_device *pdev) +{ + int ret, subsys_id; + + dev = &pdev->dev; + for (subsys_id = 0; subsys_id < SUBSYS_ID_MAX; subsys_id++) { + ret = lastlog_subsys_init(pdev, subsys_id); + if (ret < 0) { + dev_err(dev, "lastlogs probe failed for %s subsystem\n", + subsys_data[subsys_id].name); + return ret; + } + } + + dev_info(dev, "Last subsystem error log driver probe done.\n"); + return 0; +} + +static struct platform_driver lastlog_subsys_driver = { + .probe = lastlog_subsys_driver_probe, + .driver = { + .name = "last_subsyslog", + }, +}; + +static int __init lastlog_subsys_module_init(void) +{ + return platform_driver_register(&lastlog_subsys_driver); +} + +MODULE_AUTHOR("Anirudh Madnurkar "); +MODULE_AUTHOR("Sandeep Mantrala "); +MODULE_DESCRIPTION("subsystem error log"); +MODULE_LICENSE("GPL V2"); + +module_init(lastlog_subsys_module_init) diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c index 924c826208dd93dc39d6aef5cb3c47b831c7f49d..0c38b57732815f702faf2d0808404a53697adea8 100644 --- a/drivers/soc/qcom/memory_dump_v2.c +++ b/drivers/soc/qcom/memory_dump_v2.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -21,6 +26,9 @@ #include #include #include +#ifdef CONFIG_RAMDUMP_TAGS +#include +#endif #define MSM_DUMP_TABLE_VERSION MSM_DUMP_MAKE_VERSION(2, 0) @@ -139,6 +147,22 @@ int msm_dump_data_register(enum msm_dump_table_ids id, } EXPORT_SYMBOL(msm_dump_data_register); +#ifdef CONFIG_RAMDUMP_TAGS +int dump_table_ramdump_setup(void) +{ + char data[32]; + int count; + + snprintf(data, sizeof(data), "0x%lx", + (unsigned long) memdump.table_phys); + if (rdtags_add_tag("dump_table_addr", data, + strnlen(data, sizeof(data)))) + count++; + + return count; +} +#endif + static int __init init_memory_dump(void) { struct msm_dump_table *table; diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index 32a457730869affea4a49fc9108836410e3fbde3..84cb44da8e2719fa18a2a12cbba825e5265a2447 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -43,6 +48,9 @@ #include #include "peripheral-loader.h" +#ifdef CONFIG_RAMDUMP_MEMDESC +#include +#endif #define pil_err(desc, fmt, ...) \ dev_err(desc->dev, "%s: " fmt, desc->name, ##__VA_ARGS__) @@ -901,6 +909,43 @@ static int pil_parse_devicetree(struct pil_desc *desc) return 0; } +#ifdef CONFIG_RAMDUMP_MEMDESC +/* Tag the subsystem information in ramdump memory descriptors */ +static void get_mem_desc_subsys_name(const struct pil_priv *priv, + struct mem_desc *mem_desc_subsys) +{ + if (!strncmp(priv->desc->name, "modem", MEM_DESC_NAME_SIZE)) + strlcpy(mem_desc_subsys->name, "amsscore", MEM_DESC_NAME_SIZE); + else { + snprintf(mem_desc_subsys->name, MEM_DESC_NAME_SIZE, "%score", + priv->desc->name); + } +} + +static void add_mem_desc_subsys_info(const struct pil_priv *priv) +{ + struct mem_desc mem_desc_subsys; + + mem_desc_subsys.phys_addr = priv->region_start; + mem_desc_subsys.size = priv->region_end - priv->region_start; + mem_desc_subsys.flags = MEM_DESC_CORE; + get_mem_desc_subsys_name(priv, &mem_desc_subsys); + ramdump_add_mem_desc(&mem_desc_subsys); +} + +static void remove_mem_desc_subsys_info(const struct pil_priv *priv) +{ + struct mem_desc mem_desc_subsys; + + mem_desc_subsys.phys_addr = priv->region_start; + mem_desc_subsys.size = priv->region_end - priv->region_start; + mem_desc_subsys.flags = MEM_DESC_CORE; + get_mem_desc_subsys_name(priv, &mem_desc_subsys); + ramdump_remove_mem_desc(&mem_desc_subsys); +} + +#endif + /* Synchronize request_firmware() with suspend */ static DECLARE_RWSEM(pil_pm_rwsem); @@ -1022,6 +1067,10 @@ int pil_boot(struct pil_desc *desc) hyp_assign = true; } +#ifdef CONFIG_RAMDUMP_MEMDESC + add_mem_desc_subsys_info(priv); +#endif + trace_pil_event("before_load_seg", desc); list_for_each_entry(seg, &desc->priv->segs, list) { ret = pil_load_seg(desc, seg); @@ -1080,6 +1129,9 @@ out: } if (desc->clear_fw_region && priv->region_start) pil_clear_segment(desc); +#ifdef CONFIG_RAMDUMP_MEMDESC + remove_mem_desc_subsys_info(priv); +#endif dma_free_attrs(desc->dev, priv->region_size, priv->region, priv->region_start, &desc->attrs); @@ -1115,6 +1167,10 @@ void pil_shutdown(struct pil_desc *desc) else flush_delayed_work(&priv->proxy); desc->modem_ssr = true; + +#ifdef CONFIG_RAMDUMP_MEMDESC + remove_mem_desc_subsys_info(priv); +#endif } EXPORT_SYMBOL(pil_shutdown); diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c index c4c64409cfdaa87afac0023a8b7eea5f10ede80a..7a35c8dfb301178c88700bf2ccaec673f465d2fc 100644 --- a/drivers/soc/qcom/pil-q6v5-mss.c +++ b/drivers/soc/qcom/pil-q6v5-mss.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -44,7 +49,7 @@ #define subsys_to_drv(d) container_of(d, struct modem_data, subsys_desc) -static void log_modem_sfr(void) +static void log_modem_sfr(struct modem_data *drv) { u32 size; char *smem_reason, reason[MAX_SSR_REASON_LEN]; @@ -61,6 +66,7 @@ static void log_modem_sfr(void) } strlcpy(reason, smem_reason, min(size, MAX_SSR_REASON_LEN)); + update_crash_reason(drv->subsys, smem_reason, size); pr_err("modem subsystem failure reason: %s.\n", reason); smem_reason[0] = '\0'; @@ -69,7 +75,7 @@ static void log_modem_sfr(void) static void restart_modem(struct modem_data *drv) { - log_modem_sfr(); + log_modem_sfr(drv); drv->ignore_errors = true; subsystem_restart_dev(drv->subsys); } diff --git a/drivers/soc/qcom/ramdump.c b/drivers/soc/qcom/ramdump.c index c8353dc8a43a38600e1d7399062f857c7df8a156..2d72fac682d15251466c4e627f507912a1c92ea5 100644 --- a/drivers/soc/qcom/ramdump.c +++ b/drivers/soc/qcom/ramdump.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -24,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -168,7 +174,7 @@ static ssize_t ramdump_read(struct file *filep, char __user *buf, size_t count, goto ramdump_done; } - alignbuf = kzalloc(copy_size, GFP_KERNEL); + alignbuf = vzalloc(copy_size); if (!alignbuf) { pr_err("Ramdump(%s): Unable to alloc mem for aligned buf\n", rd_dev->name); @@ -206,7 +212,7 @@ static ssize_t ramdump_read(struct file *filep, char __user *buf, size_t count, goto ramdump_done; } - kfree(finalbuf); + vfree(finalbuf); if (!vaddr && origdevice_mem) dma_unremap(rd_dev->device.parent, origdevice_mem, copy_size); @@ -221,7 +227,7 @@ ramdump_done: if (!vaddr && origdevice_mem) dma_unremap(rd_dev->device.parent, origdevice_mem, copy_size); - kfree(finalbuf); + vfree(finalbuf); rd_dev->data_ready = 0; *pos = 0; complete(&rd_dev->ramdump_complete); diff --git a/drivers/soc/qcom/security_status.c b/drivers/soc/qcom/security_status.c new file mode 100644 index 0000000000000000000000000000000000000000..b158a52968bb65633267fbf00a3c3de01b36a313 --- /dev/null +++ b/drivers/soc/qcom/security_status.c @@ -0,0 +1,55 @@ +/* + * Author: Nandhakumar Rangasamy + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include + +#define SECURITY_ENABLED 0x2 + +static int security_status = -1; + +static int __init security_config_setup(char *p) +{ + unsigned long res; + + if (!p || !*p) + return -EINVAL; + + if (!kstrtoul(p, 0, &res)) { + if (res & SECURITY_ENABLED) + security_status = SECURITY_ON; + else + security_status = SECURITY_OFF; + } + + pr_info("system booted with SECURITY_STATUS : %s\n", + security_status ? "ON" : "OFF"); + return 0; +} +early_param("oemandroidboot.securityflags", security_config_setup); + +int get_security_status(int *status) +{ + if (security_status == -1) + return -EINVAL; + else { + *status = security_status; + return 0; + } +} diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 52355699e4f3f8bcea423f4353d0404979b7e596..4ec11a914dcfd9d4eb204def28b652acfd090c38 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -11,6 +11,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "servloc: %s: " fmt, __func__ @@ -251,7 +256,6 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd) req->domain_offset_valid = true; req->domain_offset = 0; - pd->domain_list = NULL; do { req->domain_offset += domains_read; rc = servreg_loc_send_msg(&req_desc, &resp_desc, req, resp, @@ -281,6 +285,7 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd) pr_err("Service Locator DB updated for client %s\n", pd->client_name); kfree(pd->domain_list); + pd->domain_list = NULL; rc = -EAGAIN; goto out; } @@ -360,7 +365,7 @@ int get_service_location(char *client_name, char *service_name, goto err; } - pqcd = kmalloc(sizeof(struct pd_qmi_client_data), GFP_KERNEL); + pqcd = kzalloc(sizeof(struct pd_qmi_client_data), GFP_KERNEL); if (!pqcd) { rc = -ENOMEM; pr_err("Allocation failed\n"); @@ -401,7 +406,7 @@ static void pd_locator_work(struct work_struct *work) pr_err("Unable to connect to service locator!, rc = %d\n", rc); pdqw->notifier->notifier_call(pdqw->notifier, LOCATOR_DOWN, NULL); - goto err; + goto err_init_servloc; } rc = service_locator_send_msg(data); if (rc) { @@ -409,11 +414,13 @@ static void pd_locator_work(struct work_struct *work) data->service_name, data->client_name, rc); pdqw->notifier->notifier_call(pdqw->notifier, LOCATOR_DOWN, NULL); - goto err; + goto err_servloc_send_msg; } pdqw->notifier->notifier_call(pdqw->notifier, LOCATOR_UP, data); -err: +err_servloc_send_msg: + kfree(data->domain_list); +err_init_servloc: kfree(data); kfree(pdqw); } diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 74dbd4d42272f3af8a1d0ee730a378d9b7ebd73b..bf6dd8d885790f7f3a38b7f3d4ae1f864f88e68b 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -11,6 +11,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * SOC Info Routines * @@ -29,6 +34,9 @@ #include #include +#ifdef CONFIG_RAMDUMP_TAGS +#include +#endif #include #include @@ -47,6 +55,18 @@ #define SMEM_IMAGE_VERSION_PARTITION_APPS 10 static DECLARE_RWSEM(current_image_rwsem); + +#ifdef CONFIG_RAMDUMP_TAGS +#define RDT_ADD_UINT(func, name) \ + do { \ + res = func(); \ + if (res != 0) { \ + snprintf(buf, sizeof(buf), "%u (0x%.8X)", res, res); \ + rdtags_add_tag(name, buf, strnlen(buf, sizeof(buf))); \ + } \ + } while (0) +#endif + enum { HW_PLATFORM_UNKNOWN = 0, HW_PLATFORM_SURF = 1, @@ -1571,6 +1591,34 @@ static void socinfo_select_format(void) } } +#ifdef CONFIG_RAMDUMP_TAGS +/* Extracts information from QC:s socinfo to get + * information about the hardware revisions of main soc + */ +static void add_socinfo_tags(void) +{ + uint32_t res; + char buf[64]; + char *str; + + RDT_ADD_UINT(read_cpuid_id, "cpuid_id"); + RDT_ADD_UINT(socinfo_get_platform_version, "socinfo_platform_version"); + RDT_ADD_UINT(socinfo_get_platform_subtype, "socinfo_platform_subtype"); + RDT_ADD_UINT(socinfo_get_platform_type, "socinfo_platform_type"); + RDT_ADD_UINT(socinfo_get_raw_id, "socinfo_raw_id"); + RDT_ADD_UINT(socinfo_get_id, "socinfo_id"); + + res = socinfo_get_version(); + snprintf(buf, sizeof(buf), "%u.%u", SOCINFO_VERSION_MAJOR(res), + SOCINFO_VERSION_MINOR(res)); + rdtags_add_tag("socinfo_version", buf, strnlen(buf, sizeof(buf))); + + str = socinfo_get_build_id(); + if (str) + rdtags_add_tag("socinfo_build_id", str, strlen(str)); +} +#endif + int __init socinfo_init(void) { static bool socinfo_init_done; @@ -1599,6 +1647,9 @@ int __init socinfo_init(void) socinfo_print(); arch_read_hardware_id = msm_read_hardware_id; socinfo_init_done = true; +#ifdef CONFIG_RAMDUMP_TAGS + add_socinfo_tags(); +#endif return 0; } diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index b448f5297f95f0e17941b2d3003afad97841e355..3b77251306323ddba47e53f9c298f224f9b373e1 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -806,6 +811,7 @@ static void log_failure_reason(const struct pil_tz_data *d) strlcpy(reason, smem_reason, min(size, MAX_SSR_REASON_LEN)); pr_err("%s subsystem failure reason: %s.\n", name, reason); + update_crash_reason(d->subsys, reason, size); smem_reason[0] = '\0'; wmb(); diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c index ea94456ccef835f8f169b699994dcbfb30fb9b2d..2efc9787753d133bb24dfee6d61ca89ae4c67273 100644 --- a/drivers/soc/qcom/subsystem_restart.c +++ b/drivers/soc/qcom/subsystem_restart.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "subsys-restart: %s(): " fmt, __func__ @@ -28,7 +33,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -171,6 +179,12 @@ struct subsys_device { int restart_level; int crash_count; struct subsys_soc_restart_order *restart_order; +#ifdef CONFIG_DEBUG_FS + struct dentry *reason_dentry; + char crash_reason[SUBSYS_CRASH_REASON_LEN]; + int data_ready; + wait_queue_head_t subsys_debug_q; +#endif bool do_ramdump_on_put; struct cdev char_dev; dev_t dev_no; @@ -180,6 +194,8 @@ struct subsys_device { struct list_head list; }; +static void subsys_notify_crash_reason(struct subsys_device *subsys); + static struct subsys_device *to_subsys(struct device *d) { return container_of(d, struct subsys_device, dev); @@ -1004,7 +1020,7 @@ static void subsystem_restart_wq_func(struct work_struct *work) spin_lock_irqsave(&track->s_lock, flags); track->p_state = SUBSYS_RESTARTING; spin_unlock_irqrestore(&track->s_lock, flags); - + subsys_notify_crash_reason(dev); /* Collect ram dumps for all subsystems in order here */ for_each_subsys_device(list, count, NULL, subsystem_ramdump); @@ -1104,7 +1120,7 @@ int subsystem_restart_dev(struct subsys_device *dev) return -EBUSY; } - pr_info("Restart sequence requested for %s, restart_level = %s.\n", + pr_warn("Restart sequence requested for %s, restart_level = %s.\n", name, restart_levels[dev->restart_level]); if (disable_restart_work == DISABLE_SSR) { @@ -1147,6 +1163,19 @@ int subsystem_restart(const char *name) } EXPORT_SYMBOL(subsystem_restart); +int subsystem_crash_reason(const char *name, char *msg) +{ + struct subsys_device *dev = find_subsys(name); + + if (!dev) + return -ENODEV; + update_crash_reason(dev, msg, SUBSYS_CRASH_REASON_LEN); + subsys_notify_crash_reason(dev); + + return 0; +} +EXPORT_SYMBOL(subsystem_crash_reason); + int subsystem_crashed(const char *name) { struct subsys_device *dev = find_subsys(name); @@ -1229,6 +1258,101 @@ void notify_proxy_unvote(struct device *device) notify_each_subsys_device(&dev, 1, SUBSYS_PROXY_UNVOTE, NULL); } +#ifdef CONFIG_DEBUG_FS +void update_crash_reason(struct subsys_device *subsys, + char *smem_reason, int size) +{ + memcpy(subsys->crash_reason, smem_reason, size); + subsys->data_ready = 1; +} + +static void subsys_notify_crash_reason(struct subsys_device *subsys) +{ + if (subsys->data_ready && subsys->restart_level != RESET_SOC) + wake_up(&subsys->subsys_debug_q); +} + +static ssize_t subsys_debugfs_reason_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + int r; + char buf[SUBSYS_CRASH_REASON_LEN]; + ssize_t size; + struct subsys_device *subsys = filp->private_data; + + r = snprintf(buf, sizeof(buf), "%s", subsys->crash_reason); + size = simple_read_from_buffer(ubuf, cnt, ppos, buf, r); + if (*ppos == r) { + memset(subsys->crash_reason, 0, sizeof(subsys->crash_reason)); + subsys->data_ready = 0; + } + + return size; +} + +static unsigned int subsys_debugfs_reason_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct subsys_device *subsys = filp->private_data; + unsigned int mask = 0; + + if (subsys->data_ready) + mask |= (POLLIN | POLLRDNORM); + + poll_wait(filp, &subsys->subsys_debug_q, wait); + return mask; +} + +static const struct file_operations subsys_debugfs_reason_fops = { + .open = simple_open, + .read = subsys_debugfs_reason_read, + .poll = subsys_debugfs_reason_poll, + .llseek = default_llseek, +}; + +static struct dentry *subsys_base_dir; +static struct dentry *subsys_reason_dir; + +static int __init subsys_debugfs_init(void) +{ + subsys_base_dir = debugfs_create_dir("msm_subsys", NULL); + if (subsys_base_dir) + subsys_reason_dir = debugfs_create_dir("crash_reason", + subsys_base_dir); + return !subsys_base_dir ? -ENOMEM : 0; +} + +static void subsys_debugfs_exit(void) +{ + if (subsys_reason_dir) + debugfs_remove_recursive(subsys_reason_dir); + debugfs_remove_recursive(subsys_base_dir); +} + +static int subsys_debugfs_add(struct subsys_device *subsys) +{ + if (!subsys_base_dir) + return -ENOMEM; + + if (subsys_reason_dir) + subsys->reason_dentry = debugfs_create_file(subsys->desc->name, + S_IRUGO | S_IWUSR, subsys_reason_dir, + subsys, &subsys_debugfs_reason_fops); + return !subsys->reason_dentry ? -ENOMEM : 0; +} + +static void subsys_debugfs_remove(struct subsys_device *subsys) +{ + debugfs_remove(subsys->reason_dentry); +} +#else +static int __init subsys_debugfs_init(void) { return 0; }; +static void subsys_debugfs_exit(void) { } +static int subsys_debugfs_add(struct subsys_device *subsys) { return 0; } +static void subsys_debugfs_remove(struct subsys_device *subsys) { } +static void subsys_notify_crash_reason(struct subsys_device *subsys) { } +#endif + static int subsys_device_open(struct inode *inode, struct file *file) { struct subsys_device *device, *subsys_dev = 0; @@ -1668,8 +1792,19 @@ struct subsys_device *subsys_register(struct subsys_desc *desc) mutex_init(&subsys->track.lock); + ret = subsys_debugfs_add(subsys); + if (ret) { + ida_simple_remove(&subsys_ida, subsys->id); + wakeup_source_trash(&subsys->ssr_wlock); + kfree(subsys); + return ERR_PTR(ret); + } + + init_waitqueue_head(&subsys->subsys_debug_q); + ret = device_register(&subsys->dev); if (ret) { + subsys_debugfs_remove(subsys); put_device(&subsys->dev); return ERR_PTR(ret); } @@ -1731,6 +1866,7 @@ err_setup_irqs: if (ofnode) subsys_remove_restart_order(ofnode); err_register: + subsys_debugfs_remove(subsys); device_unregister(&subsys->dev); return ERR_PTR(ret); } @@ -1759,6 +1895,7 @@ void subsys_unregister(struct subsys_device *subsys) WARN_ON(subsys->count); device_unregister(&subsys->dev); mutex_unlock(&subsys->track.lock); + subsys_debugfs_remove(subsys); subsys_char_device_remove(subsys); sysmon_notifier_unregister(subsys->desc); if (subsys->desc->edge) @@ -1798,6 +1935,9 @@ static int __init subsys_restart_init(void) ret = bus_register(&subsys_bus_type); if (ret) goto err_bus; + ret = subsys_debugfs_init(); + if (ret) + goto err_debugfs; char_class = class_create(THIS_MODULE, "subsys"); if (IS_ERR(char_class)) { @@ -1816,6 +1956,8 @@ static int __init subsys_restart_init(void) err_soc: class_destroy(char_class); err_class: + subsys_debugfs_exit(); +err_debugfs: bus_unregister(&subsys_bus_type); err_bus: destroy_workqueue(ssr_wq); diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c index 3528cb08c78eef46abbfdaa099958877a5c6722b..cebabc477465751eb68e819b8d0d060f97004c3a 100644 --- a/drivers/soc/qcom/watchdog_v2.c +++ b/drivers/soc/qcom/watchdog_v2.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -500,6 +505,10 @@ static irqreturn_t wdog_bark_handler(int irq, void *dev_id) wdog_dd->last_pet, nanosec_rem / 1000); if (wdog_dd->do_ipi_ping) dump_cpu_alive_mask(wdog_dd); +#ifdef CONFIG_MSM_FORCE_PANIC_ON_WDOG_BARK + /*Causing a panic instead of a watchdog bite */ + panic("Watchdog bark triggered!"); +#endif msm_trigger_wdog_bite(); panic("Failed to cause a watchdog bite! - Falling back to kernel panic!"); return IRQ_HANDLED; diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c index 1dcaba2e79f65987d89aab60512bc6877a3c30f4..83abd12f680736ca59ee15b6cbf88ab0518bc15f 100644 --- a/drivers/soundwire/swr-wcd-ctrl.c +++ b/drivers/soundwire/swr-wcd-ctrl.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -1727,6 +1732,8 @@ int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data) (swrm->state == SWR_MSTR_UP)) { dev_dbg(swrm->dev, "%s: SWR master is already UP: %d\n", __func__, swrm->state); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) + ret = swr_reset_device(swr_dev); } else { pm_runtime_mark_last_busy(&pdev->dev); mutex_unlock(&swrm->reslock); diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index e5ca8f15061718db2eb534e0ac6cbf656ffd2f61..99db12f45d6725aec9b92fcb2bd635406b9862bb 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -15,6 +15,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -698,6 +703,9 @@ static const struct of_device_id spidev_dt_ids[] = { { .compatible = "qcom,spi-msm-codec-slave", }, { .compatible = "nxp,mpc57xx", }, { .compatible = "infineon,sli97", }, +#ifdef CONFIG_ONESEG_TUNER_SMTVJ19X + { .compatible = "sony,vj190-spi", }, +#endif {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 4e4b39c26e89d6b3d4ea61552c971df45b6ee563..5a3ebc23ca8bc8e8297740a24c0d2dda5dbe4178 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 6c4445863705eba043476ba667359ce73baa4299..90d1e40e8290d9cf6acdbbed89bc73d563c51275 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -38,15 +38,37 @@ config ANDROID_LOW_MEMORY_KILLER scripts (/init.rc), and it defines priority values with minimum free memory size for each priority. +config ANDROID_LOW_MEMORY_KILLER_TNG + bool "Android Low Memory Killer TNG" + select OOM_SCORE_NOTIFIER + ---help--- + The NEXT generation (TNG) lowmemorykiller. + Registers processes to be killed when low memory conditions, this is useful + as there is no particular swap space on android. + + The registered process will kills according to the priorities in android init + scripts (/init.rc), and it defines priority values with minimum free memory size + for each priority. + config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES bool "Android Low Memory Killer: detect oom_adj values" - depends on ANDROID_LOW_MEMORY_KILLER + depends on ANDROID_LOW_MEMORY_KILLER || ANDROID_LOW_MEMORY_KILLER_TNG default y ---help--- Detect oom_adj values written to /sys/module/lowmemorykiller/parameters/adj and convert them to oom_score_adj values. +config ANDROID_LOW_MEMORY_KILLER_STATS + bool "Android Low Memory Killer: collect statistics" + default n + help + Create a file in /proc/lmkstats that includes + collected statistics about kills, scans and counts + and interaction with the shrinker. Its content + will be different depending on lmk implementation used + in TNG lowmemorykiller. + config SYNC bool "Synchronization framework" default n diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 8ef816152020f6b56505af4bd268694658a80ecf..55214fa7ee0a2721ed1a61063601e3e4c971d04b 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -7,6 +7,9 @@ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o +lowmemorykiller_tng := lowmemorykiller_tng.o lowmemorykiller_tasks.o lowmemorykiller_oom.o lowmemorykiller_vmpressure.o +obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG) += lowmemorykiller_tng +obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER_STATS) += lowmemorykiller_stats.o obj-$(CONFIG_SYNC) += sync.o sync_debug.o obj-$(CONFIG_SW_SYNC) += sw_sync.o obj-$(CONFIG_ONESHOT_SYNC) += oneshot_sync.o diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index cba6b4e17fee488d33f166f7a1c6f3fd8930cf48..778aaef320212a02abaa46d0d4b29be46fe0aeb7 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -15,6 +15,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "ashmem: " fmt diff --git a/drivers/staging/android/ion/ion_cma_secure_heap.c b/drivers/staging/android/ion/ion_cma_secure_heap.c index 6102b1765182c797bf338dbf9c3a7b553c342252..8a6a152c8e5678860801dfbd9308754d3e6a300d 100644 --- a/drivers/staging/android/ion/ion_cma_secure_heap.c +++ b/drivers/staging/android/ion/ion_cma_secure_heap.c @@ -15,6 +15,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -397,7 +402,7 @@ static unsigned long ion_secure_cma_shrinker(struct shrinker *shrinker, * would cause a deadlock in several places so don't shrink if that * happens. */ - if (!mutex_trylock(&sheap->chunk_lock)) + if (!mutex_trylock_spin(&sheap->chunk_lock)) return -1; freed = __ion_secure_cma_shrink_pool(sheap, nr_to_scan); diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c index 0034dfe17ac848e359baa9b189cb91b2e6dea722..5d86a0718bfd969d037b82577e085e475930ee69 100644 --- a/drivers/staging/android/ion/ion_page_pool.c +++ b/drivers/staging/android/ion/ion_page_pool.c @@ -13,18 +13,29 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include #include #include +#include +#include #include +#include #include #include #include +#include #include "ion_priv.h" +#define ION_PAGE_CACHE 1 + static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool) { struct page *page; @@ -51,12 +62,18 @@ static void ion_page_pool_free_pages(struct ion_page_pool *pool, struct page *page) { ion_page_pool_free_set_cache_policy(pool, page); + if (pool->inode && pool->order == 0) { + lock_page(page); + __ClearPageMovable(page); + unlock_page(page); + } __free_pages(page, pool->order); } static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page) { mutex_lock(&pool->mutex); + page->private = ION_PAGE_CACHE; if (PageHighMem(page)) { list_add_tail(&page->lru, &pool->high_items); pool->high_count++; @@ -65,6 +82,8 @@ static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page) pool->low_count++; } + if (pool->inode && pool->order == 0) + __SetPageMovable(page, pool->inode->i_mapping); mod_zone_page_state(page_zone(page), NR_INDIRECTLY_RECLAIMABLE_BYTES, (1 << (PAGE_SHIFT + pool->order))); mutex_unlock(&pool->mutex); @@ -85,7 +104,9 @@ static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) pool->low_count--; } - list_del(&page->lru); + clear_bit(ION_PAGE_CACHE, &page->private); + + list_del_init(&page->lru); mod_zone_page_state(page_zone(page), NR_INDIRECTLY_RECLAIMABLE_BYTES, -(1 << (PAGE_SHIFT + pool->order))); return page; @@ -99,7 +120,7 @@ void *ion_page_pool_alloc(struct ion_page_pool *pool, bool *from_pool) *from_pool = true; - if (mutex_trylock(&pool->mutex)) { + if (mutex_trylock_spin(&pool->mutex)) { if (pool->high_count) page = ion_page_pool_remove(pool, true); else if (pool->low_count) @@ -109,6 +130,10 @@ void *ion_page_pool_alloc(struct ion_page_pool *pool, bool *from_pool) if (!page) { page = ion_page_pool_alloc_pages(pool); *from_pool = false; + } else { + lock_page(page); + __ClearPageMovable(page); + unlock_page(page); } return page; } @@ -122,14 +147,18 @@ void *ion_page_pool_alloc_pool_only(struct ion_page_pool *pool) BUG_ON(!pool); - if (mutex_trylock(&pool->mutex)) { + if (mutex_trylock_spin(&pool->mutex)) { if (pool->high_count) page = ion_page_pool_remove(pool, true); else if (pool->low_count) page = ion_page_pool_remove(pool, false); mutex_unlock(&pool->mutex); } - + if (page) { + lock_page(page); + __ClearPageMovable(page); + unlock_page(page); + } return page; } @@ -191,8 +220,144 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, return freed; } +static bool ion_page_pool_isolate(struct page *page, isolate_mode_t mode) +{ + struct ion_page_pool *pool; + struct address_space *mapping = page_mapping(page); + + VM_BUG_ON_PAGE(PageIsolated(page), page); + + if (!mapping) + return false; + pool = mapping->private_data; + + mutex_lock(&pool->mutex); + /* could be removed from the cache pool and thus become unmovable */ + if (!__PageMovable(page)) { + mutex_unlock(&pool->mutex); + return false; + } + + if (unlikely(!test_bit(ION_PAGE_CACHE, &page->private))) { + mutex_unlock(&pool->mutex); + return false; + } + + list_del(&page->lru); + if (PageHighMem(page)) + pool->high_count--; + else + pool->low_count--; + mutex_unlock(&pool->mutex); + + return true; +} + +static int ion_page_pool_migrate(struct address_space *mapping, + struct page *newpage, + struct page *page, enum migrate_mode mode) +{ + struct ion_page_pool *pool = mapping->private_data; + + VM_BUG_ON_PAGE(!PageMovable(page), page); + VM_BUG_ON_PAGE(!PageIsolated(page), page); + + lock_page(page); + newpage->private = ION_PAGE_CACHE; + __SetPageMovable(newpage, page_mapping(page)); + get_page(newpage); + __ClearPageMovable(page); + ClearPagePrivate(page); + unlock_page(page); + mutex_lock(&pool->mutex); + if (PageHighMem(newpage)) { + list_add_tail(&newpage->lru, &pool->high_items); + pool->high_count++; + } else { + list_add_tail(&newpage->lru, &pool->low_items); + pool->low_count++; + } + mutex_unlock(&pool->mutex); + put_page(page); + return 0; +} + +static void ion_page_pool_putback(struct page *page) +{ + /* + * migrate function either succeeds or returns -EAGAIN, which + * results in calling it again until it succeeds, sothis callback + * is not needed. + */ +} + +static struct dentry *ion_pool_do_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + static const struct dentry_operations ops = { + .d_dname = simple_dname, + }; + + return mount_pseudo(fs_type, "ion_pool:", NULL, &ops, 0x77); +} + +static struct file_system_type ion_pool_fs = { + .name = "ion_pool", + .mount = ion_pool_do_mount, + .kill_sb = kill_anon_super, +}; + +static int ion_pool_cnt; +static struct vfsmount *ion_pool_mnt; +static int ion_pool_mount(void) +{ + int ret = 0; + + ion_pool_mnt = kern_mount(&ion_pool_fs); + if (IS_ERR(ion_pool_mnt)) + ret = PTR_ERR(ion_pool_mnt); + + return ret; +} + +static const struct address_space_operations ion_pool_aops = { + .isolate_page = ion_page_pool_isolate, + .migratepage = ion_page_pool_migrate, + .putback_page = ion_page_pool_putback, +}; + +static int ion_pool_register_migration(struct ion_page_pool *pool) +{ + int ret = simple_pin_fs(&ion_pool_fs, &ion_pool_mnt, &ion_pool_cnt); + + if (ret < 0) { + pr_err("Cannot mount pseudo fs: %d\n", ret); + return ret; + } + pool->inode = alloc_anon_inode(ion_pool_mnt->mnt_sb); + if (IS_ERR(pool->inode)) { + ret = PTR_ERR(pool->inode); + pool->inode = NULL; + simple_release_fs(&ion_pool_mnt, &ion_pool_cnt); + return ret; + } + + pool->inode->i_mapping->private_data = pool; + pool->inode->i_mapping->a_ops = &ion_pool_aops; + return 0; +} + +static void ion_pool_unregister_migration(struct ion_page_pool *pool) +{ + if (pool->inode) { + iput(pool->inode); + pool->inode = NULL; + simple_release_fs(&ion_pool_mnt, &ion_pool_cnt); + } +} + struct ion_page_pool *ion_page_pool_create(struct device *dev, gfp_t gfp_mask, - unsigned int order) + unsigned int order, bool movable) { struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool), GFP_KERNEL); @@ -208,16 +373,21 @@ struct ion_page_pool *ion_page_pool_create(struct device *dev, gfp_t gfp_mask, mutex_init(&pool->mutex); plist_node_init(&pool->list, order); + pool->inode = NULL; + if (movable) + ion_pool_register_migration(pool); + return pool; } void ion_page_pool_destroy(struct ion_page_pool *pool) { + ion_pool_unregister_migration(pool); kfree(pool); } static int __init ion_page_pool_init(void) { - return 0; + return ion_pool_mount(); } device_initcall(ion_page_pool_init); diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h index f2f2ca11c3fabaad07c3c579fcf51fdae9824c82..80b30043b023958795995609075e3f0f1e9b39f9 100644 --- a/drivers/staging/android/ion/ion_priv.h +++ b/drivers/staging/android/ion/ion_priv.h @@ -14,6 +14,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _ION_PRIV_H #define _ION_PRIV_H @@ -422,6 +427,7 @@ void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, * @gfp_mask: gfp_mask to use from alloc * @order: order of pages in the pool * @list: plist node for list of pools + * @inode: inode for ion_pool pseudo filesystem * * Allows you to keep a pool of pre allocated pages to use from your heap. * Keeping a pool of pages that is ready for dma, ie any cached mapping have @@ -438,10 +444,11 @@ struct ion_page_pool { gfp_t gfp_mask; unsigned int order; struct plist_node list; + struct inode *inode; }; struct ion_page_pool *ion_page_pool_create(struct device *dev, gfp_t gfp_mask, - unsigned int order); + unsigned int order, bool movable); void ion_page_pool_destroy(struct ion_page_pool *); void *ion_page_pool_alloc(struct ion_page_pool *, bool *from_pool); void *ion_page_pool_alloc_pool_only(struct ion_page_pool *); diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c index a2ead280ac4e80a5da1fdc4102a23b2f1591e457..05546f535e8ac4ed829a73d53781ae95dceab613 100644 --- a/drivers/staging/android/ion/ion_system_heap.c +++ b/drivers/staging/android/ion/ion_system_heap.c @@ -14,6 +14,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -768,7 +773,8 @@ static void ion_system_heap_destroy_pools(struct ion_page_pool **pools) * ion_system_heap_destroy_pools to destroy the pools. */ static int ion_system_heap_create_pools(struct device *dev, - struct ion_page_pool **pools) + struct ion_page_pool **pools, + bool movable) { int i; for (i = 0; i < num_orders; i++) { @@ -777,7 +783,8 @@ static int ion_system_heap_create_pools(struct device *dev, if (orders[i]) gfp_flags = high_order_gfp_flags; - pool = ion_page_pool_create(dev, gfp_flags, orders[i]); + pool = ion_page_pool_create(dev, gfp_flags, orders[i], + movable); if (!pool) goto err_create_pool; pools[i] = pool; @@ -816,15 +823,15 @@ struct ion_heap *ion_system_heap_create(struct ion_platform_heap *data) if (!heap->secure_pools[i]) goto err_create_secure_pools; if (ion_system_heap_create_pools( - dev, heap->secure_pools[i])) + dev, heap->secure_pools[i], false)) goto err_create_secure_pools; } } - if (ion_system_heap_create_pools(dev, heap->uncached_pools)) + if (ion_system_heap_create_pools(dev, heap->uncached_pools, false)) goto err_create_uncached_pools; - if (ion_system_heap_create_pools(dev, heap->cached_pools)) + if (ion_system_heap_create_pools(dev, heap->cached_pools, true)) goto err_create_cached_pools; mutex_init(&heap->split_page_mutex); diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 9e12b2ce3272315f579c70ad9317c15169a4135e..8d3a4ad33c88113d23390c105cb3d1e14fd96cf6 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -29,6 +29,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -42,8 +47,6 @@ #include #include #include -#include -#include #include #include #include @@ -52,6 +55,7 @@ #define CREATE_TRACE_POINTS #include +#include #ifdef CONFIG_HIGHMEM #define _ZONE ZONE_HIGHMEM @@ -62,15 +66,20 @@ #define CREATE_TRACE_POINTS #include "trace/lowmemorykiller.h" -static uint32_t lowmem_debug_level = 1; -static short lowmem_adj[6] = { +#include "lowmemorykiller_stats.h" +#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG +#include "lowmemorykiller_tng.h" +#endif + +uint32_t lowmem_debug_level = 1; +short lowmem_adj[6] = { 0, 1, 6, 12, }; static int lowmem_adj_size = 4; -static int lowmem_minfree[6] = { +int lowmem_minfree[6] = { 3 * 512, /* 6MB */ 2 * 1024, /* 8MB */ 4 * 1024, /* 16MB */ @@ -81,6 +90,17 @@ static int lmk_fast_run = 1; static unsigned long lowmem_deathpending_timeout; +int lowmem_min_param_size(void) +{ + int array_size = ARRAY_SIZE(lowmem_adj); + + if (lowmem_adj_size < array_size) + array_size = lowmem_adj_size; + if (lowmem_minfree_size < array_size) + array_size = lowmem_minfree_size; + return array_size; +} + #define lowmem_print(level, x...) \ do { \ if (lowmem_debug_level >= (level)) \ @@ -90,6 +110,7 @@ static unsigned long lowmem_deathpending_timeout; static unsigned long lowmem_count(struct shrinker *s, struct shrink_control *sc) { + lmk_inc_stats(LMK_COUNT); return global_page_state(NR_ACTIVE_ANON) + global_page_state(NR_ACTIVE_FILE) + global_page_state(NR_INACTIVE_ANON) + @@ -150,12 +171,17 @@ static int lmk_vmpressure_notifier(struct notifier_block *nb, unsigned long pressure = action; int array_size = ARRAY_SIZE(lowmem_adj); - if (!enable_adaptive_lmk) + if (!enable_adaptive_lmk) { +#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG + balance_cache(); +#endif return 0; + } if (pressure >= 95) { other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() - global_page_state(NR_SHMEM) - + global_page_state(NR_UNEVICTABLE) - total_swapcache_pages(); other_free = global_page_state(NR_FREE_PAGES); @@ -169,6 +195,7 @@ static int lmk_vmpressure_notifier(struct notifier_block *nb, other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() - global_page_state(NR_SHMEM) - + global_page_state(NR_UNEVICTABLE) - total_swapcache_pages(); other_free = global_page_state(NR_FREE_PAGES); @@ -193,7 +220,9 @@ static int lmk_vmpressure_notifier(struct notifier_block *nb, trace_almk_vmpressure(pressure, other_free, other_file); atomic_set(&shift_adj, 0); } - +#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG + balance_cache(); +#endif return 0; } @@ -217,24 +246,6 @@ static int test_task_flag(struct task_struct *p, int flag) return 0; } -static int test_task_state(struct task_struct *p, int state) -{ - struct task_struct *t; - - for_each_thread(p, t) { - task_lock(t); - if (t->state & state) { - task_unlock(t); - return 1; - } - task_unlock(t); - } - - return 0; -} - -static DEFINE_MUTEX(scan_mutex); - int can_use_cma_pages(gfp_t gfp_mask) { int can_use = 0; @@ -417,25 +428,16 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) int selected_tasksize = 0; short selected_oom_score_adj; int array_size = ARRAY_SIZE(lowmem_adj); - int other_free; - int other_file; - - if (!mutex_trylock(&scan_mutex)) - return 0; - - other_free = global_page_state(NR_FREE_PAGES); - - if (global_page_state(NR_SHMEM) + total_swapcache_pages() < - global_page_state(NR_FILE_PAGES) + zcache_pages()) - other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() - - global_page_state(NR_SHMEM) - - global_page_state(NR_UNEVICTABLE) - - total_swapcache_pages(); - else - other_file = 0; + int other_free = global_page_state(NR_FREE_PAGES); + int other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() - + global_page_state(NR_SHMEM) - + global_page_state(NR_UNEVICTABLE) - + total_swapcache_pages(); + other_file = (other_file < 0) ? 0 : other_file; tune_lmk_param(&other_free, &other_file, sc); + lmk_inc_stats(LMK_SCAN); if (lowmem_adj_size < array_size) array_size = lowmem_adj_size; if (lowmem_minfree_size < array_size) @@ -458,7 +460,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) trace_almk_shrink(0, ret, other_free, other_file, 0); lowmem_print(5, "lowmem_scan %lu, %x, return 0\n", sc->nr_to_scan, sc->gfp_mask); - mutex_unlock(&scan_mutex); + trace_lmk_remain_scan(0, sc->nr_to_scan, sc->gfp_mask); return 0; } @@ -476,10 +478,16 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (test_task_flag(tsk, TIF_MM_RELEASED)) continue; + /* Ignore task if coredump in progress */ + if (tsk->mm && tsk->mm->core_state) + continue; + if (time_before_eq(jiffies, lowmem_deathpending_timeout)) { if (test_task_flag(tsk, TIF_MEMDIE)) { rcu_read_unlock(); - mutex_unlock(&scan_mutex); + trace_lmk_remain_scan(0, sc->nr_to_scan, + sc->gfp_mask); + lmk_inc_stats(LMK_TIMEOUT); return 0; } } @@ -512,17 +520,6 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) } if (selected) { long cache_size, cache_limit, free; - - if (test_task_flag(selected, TIF_MEMDIE) && - (test_task_state(selected, TASK_UNINTERRUPTIBLE))) { - lowmem_print(2, "'%s' (%d) is already killed\n", - selected->comm, - selected->pid); - rcu_read_unlock(); - mutex_unlock(&scan_mutex); - return 0; - } - task_lock(selected); send_sig(SIGKILL, selected, 0); /* @@ -571,20 +568,23 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) lowmem_deathpending_timeout = jiffies + HZ; rem += selected_tasksize; + trace_lmk_sigkill(selected->pid, selected->comm, + selected_oom_score_adj, selected_tasksize, + sc->gfp_mask); rcu_read_unlock(); - /* give the system time to free up the memory */ - msleep_interruptible(20); trace_almk_shrink(selected_tasksize, ret, other_free, other_file, selected_oom_score_adj); + lmk_inc_stats(LMK_KILL); } else { trace_almk_shrink(1, ret, other_free, other_file, 0); + lmk_inc_stats(LMK_WASTE); rcu_read_unlock(); } lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n", sc->nr_to_scan, sc->gfp_mask, rem); - mutex_unlock(&scan_mutex); + trace_lmk_remain_scan(rem, sc->nr_to_scan, sc->gfp_mask); return rem; } @@ -597,8 +597,12 @@ static struct shrinker lowmem_shrinker = { static int __init lowmem_init(void) { +#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG + lowmem_init_tng(&lowmem_shrinker); +#endif register_shrinker(&lowmem_shrinker); vmpressure_notifier_register(&lmk_vmpr_nb); + init_procfs_lmk(); return 0; } device_initcall(lowmem_init); diff --git a/drivers/staging/android/lowmemorykiller.h b/drivers/staging/android/lowmemorykiller.h new file mode 100644 index 0000000000000000000000000000000000000000..b0d35b5773a852a8e7c7d691974ba1fcdf3bb4ee --- /dev/null +++ b/drivers/staging/android/lowmemorykiller.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 __LOWMEMORYKILLER_H +#define __LOWMEMORYKILLER_H + +#include + +/* The lowest score LMK is using */ +#define LMK_SCORE_THRESHOLD 0 + +extern u32 lowmem_debug_level; + +#define lowmem_print(level, x...) \ + do { \ + if (lowmem_debug_level >= (level)) \ + pr_info(x); \ + } while (0) + +int __init lowmemorykiller_register_oom_notifier(void); +struct calculated_params { + long selected_tasksize; + long minfree; + int other_file; + int other_free; + int dynamic_max_queue_len; + short selected_oom_score_adj; + short min_score_adj; +}; + +int kill_needed(int level, gfp_t mask, + struct calculated_params *cp); +void print_obituary(struct task_struct *doomed, + struct calculated_params *cp, + gfp_t gfp_mask); + +void balance_cache(void); +ssize_t get_task_rss(struct task_struct *tsk); + +/* kernel does not have a task_trylock and + * to make it more obvious what the code do + * we create a help function for it. + * see sched.h for task_lock and task_unlock + */ +static inline int task_trylock_lmk(struct task_struct *p) +{ + return spin_trylock(&p->alloc_lock); +} + +/* maybe not exact version, we need something betwwen 3.18 and 4.4. + * using LINUX_VERSION_CODE like this will give a warning. + * it it not OK for mainline but for multiple kernel version patches + * I think it is OK. + */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 0, 0) +#define LMK_TAG_TASK_DIE(x) set_tsk_thread_flag(x, TIF_MEMDIE) +#elif LINUX_VERSION_CODE <= KERNEL_VERSION(4, 5, 0) +#define LMK_TAG_TASK_DIE(x) mark_oom_victim(x) +#else +#define LMK_TAG_TASK_DIE(x) \ + do { \ + if (x->mm) \ + task_set_lmk_waiting(x);\ + if (!test_bit(MMF_OOM_SKIP, &x->mm->flags) && \ + oom_reaper) { \ + mark_lmk_victim(x); \ + wake_oom_reaper(x);\ + } \ + } while (0) +#endif + +#endif diff --git a/drivers/staging/android/lowmemorykiller_oom.c b/drivers/staging/android/lowmemorykiller_oom.c new file mode 100644 index 0000000000000000000000000000000000000000..f9c0353927a62e8536156a77b7c70250cfe0cb28 --- /dev/null +++ b/drivers/staging/android/lowmemorykiller_oom.c @@ -0,0 +1,97 @@ +/* + * lowmemorykiller_oom + * + * Author: Peter Enderborg + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +/* add fake print format with original module name */ +#define pr_fmt(fmt) "lowmemorykiller: " fmt + +#include +#include +#include + +#include + +#include "lowmemorykiller.h" +#include "lowmemorykiller_stats.h" +#include "lowmemorykiller_tasks.h" + +/** + * lowmemorykiller_oom_notify - OOM notifier + * @self: notifier block struct + * @notused: not used + * @parm: returned - number of pages freed + * + * Return value: + * NOTIFY_OK + **/ + +static int lowmemorykiller_oom_notify(struct notifier_block *self, + unsigned long notused, void *param) +{ + struct lmk_rb_watch *lrw; + unsigned long *nfreed = param; + + lowmem_print(2, "oom notify event\n"); + *nfreed = 0; + lmk_inc_stats(LMK_OOM_COUNT); + spin_lock_bh(&lmk_task_lock); + lrw = __lmk_task_first(); + if (lrw) { + struct task_struct *selected = lrw->tsk; + struct lmk_death_pending_entry *ldpt; + + if (!task_trylock_lmk(selected)) { + lmk_inc_stats(LMK_ERROR); + lowmem_print(1, "Failed to lock task.\n"); + lmk_inc_stats(LMK_BUSY); + goto unlock_out; + } + + /* move to kill pending set */ + ldpt = kmem_cache_alloc(lmk_dp_cache, GFP_ATOMIC); + ldpt->tsk = selected; + + __lmk_death_pending_add(ldpt); + if (!__lmk_task_remove(selected, lrw->key)) + WARN_ON(1); + + spin_unlock_bh(&lmk_task_lock); + *nfreed = get_task_rss(lrw->tsk); + send_sig(SIGKILL, selected, 0); + LMK_TAG_TASK_DIE(selected); + trace_lmk_sigkill(selected->pid, selected->comm, + -1, *nfreed, + 0); + + task_unlock(selected); + lmk_inc_stats(LMK_OOM_KILL_COUNT); + goto out; + } +unlock_out: + spin_unlock_bh(&lmk_task_lock); +out: + return NOTIFY_OK; +} + +static struct notifier_block lowmemorykiller_oom_nb = { + .notifier_call = lowmemorykiller_oom_notify +}; + +int __init lowmemorykiller_register_oom_notifier(void) +{ + register_oom_notifier(&lowmemorykiller_oom_nb); + return 0; +} diff --git a/drivers/staging/android/lowmemorykiller_oom.h b/drivers/staging/android/lowmemorykiller_oom.h new file mode 100644 index 0000000000000000000000000000000000000000..827c80e494c927d478df4dfda31f626e963e000b --- /dev/null +++ b/drivers/staging/android/lowmemorykiller_oom.h @@ -0,0 +1,25 @@ +/* + * lowmemorykiller_oom interface + * + * Author: Peter Enderborg + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include "lowmemorykiller.h" +#include "lowmemorykiller_stats.h" +#include "lowmemorykiller_tasks.h" + +int __init lowmemorykiller_register_oom_notifier(void); diff --git a/drivers/staging/android/lowmemorykiller_stats.c b/drivers/staging/android/lowmemorykiller_stats.c new file mode 100644 index 0000000000000000000000000000000000000000..59246244bad9d5e129e5cf73d8b572d57aaaa32c --- /dev/null +++ b/drivers/staging/android/lowmemorykiller_stats.c @@ -0,0 +1,144 @@ +/* + * lowmemorykiller_stats + * + * Author: Peter Enderborg + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 code is bookkeeping of statistical information + * from lowmemorykiller and provide a node in proc "/proc/lmkstats". + */ + +#include +#include +#include "lowmemorykiller_stats.h" + +struct lmk_stats { + atomic_long_t scans; /* counter as in shrinker scans */ + atomic_long_t kills; /* the number of sigkills sent */ + atomic_long_t waste; /* the number of expensive calls that did + * not lead to anything + */ + atomic_long_t timeout; /* counter for shrinker calls that needed + * to be cancelled due to pending kills + */ + atomic_long_t count; /* number of shrinker count calls */ + atomic_long_t scan_busy; /* mutex held */ + atomic_long_t no_kill; /* mutex held */ + atomic_long_t busy; + atomic_long_t error; + atomic_long_t zero_count; + atomic_long_t oom_count; + atomic_long_t oom_kill_count; + atomic_long_t morgue_count; + atomic_long_t balance_kill; + atomic_long_t balance_waste; + + atomic_long_t unknown; /* internal */ +} st; + +void lmk_inc_stats(int key) +{ + switch (key) { + case LMK_SCAN: + atomic_long_inc(&st.scans); + break; + case LMK_KILL: + atomic_long_inc(&st.kills); + break; + case LMK_WASTE: + atomic_long_inc(&st.waste); + break; + case LMK_TIMEOUT: + atomic_long_inc(&st.timeout); + break; + case LMK_COUNT: + atomic_long_inc(&st.count); + break; + case LMK_BUSY: + atomic_long_inc(&st.busy); + break; + case LMK_ERROR: + atomic_long_inc(&st.error); + break; + case LMK_NO_KILL: + atomic_long_inc(&st.no_kill); + break; + case LMK_ZERO_COUNT: + atomic_long_inc(&st.zero_count); + break; + case LMK_OOM_COUNT: + atomic_long_inc(&st.oom_count); + break; + case LMK_OOM_KILL_COUNT: + atomic_long_inc(&st.oom_kill_count); + break; + case LMK_MORGUE_COUNT: + atomic_long_inc(&st.morgue_count); + break; + case LMK_BALANCE_KILL: + atomic_long_inc(&st.balance_kill); + break; + case LMK_BALANCE_WASTE: + atomic_long_inc(&st.balance_waste); + break; + default: + atomic_long_inc(&st.unknown); + break; + } +} + +static int lmk_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "kill: %ld\n", atomic_long_read(&st.kills)); + seq_printf(m, "scan: %ld\n", atomic_long_read(&st.scans)); + seq_printf(m, "waste: %ld\n", atomic_long_read(&st.waste)); + seq_printf(m, "timeout: %ld\n", atomic_long_read(&st.timeout)); + seq_printf(m, "count: %ld\n", atomic_long_read(&st.count)); + seq_printf(m, "busy: %ld\n", atomic_long_read(&st.busy)); + seq_printf(m, "error: %ld\n", atomic_long_read(&st.error)); + seq_printf(m, "no kill: %ld\n", atomic_long_read(&st.no_kill)); + seq_printf(m, "zero: %ld\n", atomic_long_read(&st.zero_count)); + seq_printf(m, "oom: %ld\n", atomic_long_read(&st.oom_count)); + seq_printf(m, "oom kill: %ld\n", atomic_long_read(&st.oom_kill_count)); + seq_printf(m, "morgue: %ld\n", atomic_long_read(&st.morgue_count)); + seq_printf(m, "balance kill: %ld\n", + atomic_long_read(&st.balance_kill)); + seq_printf(m, "balance waste: %ld\n", + atomic_long_read(&st.balance_waste)); + seq_printf(m, "unknown: %ld (internal)\n", + atomic_long_read(&st.unknown)); + + return 0; +} + +static int lmk_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, lmk_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations lmk_proc_fops = { + .open = lmk_proc_open, + .read = seq_read, + .release = single_release +}; + +int __init init_procfs_lmk(void) +{ + proc_create_data(LMK_PROCFS_NAME, 0, NULL, &lmk_proc_fops, NULL); + return 0; +} + +void exit_procfs_lmk(void) +{ + remove_proc_entry(LMK_PROCFS_NAME, NULL); +} diff --git a/drivers/staging/android/lowmemorykiller_stats.h b/drivers/staging/android/lowmemorykiller_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..49a04b934bdf8d9d92ec5a97af477211bd26d69e --- /dev/null +++ b/drivers/staging/android/lowmemorykiller_stats.h @@ -0,0 +1,51 @@ +/* + * lowmemorykiller_stats interface + * + * Author: Peter Enderborg + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 __LOWMEMORYKILLER_STATS_H +#define __LOWMEMORYKILLER_STATS_H + +enum lmk_kill_stats { + LMK_SCAN = 1, + LMK_KILL = 2, + LMK_WASTE = 3, + LMK_TIMEOUT = 4, + LMK_COUNT = 5, + LMK_SCAN_BUSY = 6, + LMK_NO_KILL = 7, + LMK_BUSY = 8, + LMK_ERROR = 9, + LMK_ZERO_COUNT = 10, + LMK_OOM_COUNT = 11, + LMK_OOM_KILL_COUNT = 12, + LMK_BALANCE_KILL = 13, + LMK_BALANCE_WASTE = 14, + LMK_MORGUE_COUNT = 15, +}; + +#define LMK_PROCFS_NAME "lmkstats" + +#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_STATS +void lmk_inc_stats(int key); +int __init init_procfs_lmk(void); +void exit_procfs_lmk(void); +#else +static inline void lmk_inc_stats(int key) { return; }; +static inline int __init init_procfs_lmk(void) { return 0; }; +static inline void exit_procfs_lmk(void) { return; }; +#endif + +#endif diff --git a/drivers/staging/android/lowmemorykiller_tasks.c b/drivers/staging/android/lowmemorykiller_tasks.c new file mode 100644 index 0000000000000000000000000000000000000000..b105cd1956f0485488874957fa4a02a6d9c1f527 --- /dev/null +++ b/drivers/staging/android/lowmemorykiller_tasks.c @@ -0,0 +1,273 @@ +/* + * lowmemorykiller_tasks + * + * Author: Peter Enderborg + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 files contains help functions for handling tasks within the + * lowmemorykiller. It track tasks that are in it's score range, + * and it track tasks that signaled to be killed + */ + +/* add fake print format with original module name */ +#define pr_fmt(fmt) "lowmemorykiller: " fmt + +#include +#include +#include +#include +#include + +#include "lowmemorykiller.h" +#include "lowmemorykiller_tasks.h" +#include "lowmemorykiller_stats.h" + +static struct rb_root watch_tree = RB_ROOT; +struct list_head lmk_death_pending; +struct kmem_cache *lmk_dp_cache; +struct kmem_cache *lmk_task_cache; + +/* We need a well defined order for our tree, score is the major order + * and we use pid to get a unique order. + * return -1 on smaller, 0 on equal and 1 on bigger + */ + +enum { + LMK_OFR_LESS = -1, + LMK_OFR_EQUAL = 0, + LMK_OFR_GREATER = 1 +}; + +/* to protect lmk task storage data structures */ +DEFINE_SPINLOCK(lmk_task_lock); +LIST_HEAD(lmk_death_pending); + +int death_pending_len; + +static inline int lmk_task_orderfunc(int lkey, uintptr_t lpid, + int rkey, uintptr_t rpid) +{ + if (lkey > rkey) + return LMK_OFR_GREATER; + if (lkey < rkey) + return LMK_OFR_LESS; + if (lpid > rpid) + return LMK_OFR_GREATER; + if (lpid < rpid) + return LMK_OFR_LESS; + return LMK_OFR_EQUAL; +} + +static inline int __lmk_task_insert(struct task_struct *tsk) +{ + struct rb_node **new = &watch_tree.rb_node, *parent = NULL; + struct lmk_rb_watch *t; + + t = kmem_cache_alloc(lmk_task_cache, GFP_ATOMIC); + t->key = tsk->signal->oom_score_adj; + t->tsk = tsk; + + /* Figure out where to put new node */ + while (*new) { + struct lmk_rb_watch *this = rb_entry(*new, + struct lmk_rb_watch, + rb_node); + int result; + + result = lmk_task_orderfunc(t->key, + (uintptr_t)t->tsk, + this->key, + (uintptr_t)this->tsk); + + if (result == LMK_OFR_EQUAL) { + lowmem_print(1, "Dupe key %d:%d:%p - key %d:%d:%p\n", + t->key, t->tsk->pid, t->tsk, + this->key, this->tsk->pid, this->tsk); + WARN_ON(1); + return 0; + } + parent = *new; + if (result > 0) + new = &((*new)->rb_left); + else + new = &((*new)->rb_right); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&t->rb_node, parent, new); + rb_insert_color(&t->rb_node, &watch_tree); + get_task_struct(tsk); + return 1; +} + +static struct lmk_rb_watch *__lmk_task_search(struct task_struct *tsk, + int score) +{ + struct rb_node *node = watch_tree.rb_node; + struct lmk_rb_watch *data = NULL; + + while (node) { + int result; + + data = rb_entry(node, struct lmk_rb_watch, rb_node); + + result = lmk_task_orderfunc(data->key, (uintptr_t)data->tsk, + score, (uintptr_t)tsk); + switch (result) { + case LMK_OFR_LESS: + node = node->rb_left; + break; + case LMK_OFR_GREATER: + node = node->rb_right; + break; + case LMK_OFR_EQUAL: + if (data->tsk == tsk) + return data; + WARN(1, "tsk not equal %p %p", data->tsk, tsk); + /* Oh shit, hope for the best :-( */ + return NULL; + default: + WARN(1, "Unknown result %d", result); + break; + } + } + return NULL; +} + +int __lmk_task_remove(struct task_struct *tsk, + int score) +{ + struct lmk_rb_watch *lrw; + + lrw = __lmk_task_search(tsk, score); + if (lrw) { + rb_erase(&lrw->rb_node, &watch_tree); + kmem_cache_free(lmk_task_cache, lrw); + put_task_struct(tsk); + return 1; + } + + return 0; +} + +static void lmk_task_watch(struct task_struct *tsk, int old_oom_score_adj) +{ + if (thread_group_leader(tsk) && + (tsk->signal->oom_score_adj >= LMK_SCORE_THRESHOLD || + old_oom_score_adj >= LMK_SCORE_THRESHOLD) && + !(tsk->flags & PF_KTHREAD)) { + spin_lock(&lmk_task_lock); + __lmk_task_remove(tsk, old_oom_score_adj); + if (tsk->signal->oom_score_adj >= LMK_SCORE_THRESHOLD) + if (!test_tsk_thread_flag(tsk, TIF_MEMDIE)) + __lmk_task_insert(tsk); + spin_unlock(&lmk_task_lock); + } +} + +bool __lmk_death_pending_morgue(void) +{ + bool changed = false; + struct lmk_death_pending_entry *dp_iterator, *tmp; + + list_for_each_entry_safe(dp_iterator, tmp, + &lmk_death_pending, lmk_dp_list) { + if (!dp_iterator->tsk->mm) { + put_task_struct(dp_iterator->tsk); + list_del(&dp_iterator->lmk_dp_list); + kmem_cache_free(lmk_dp_cache, dp_iterator); + death_pending_len--; + lmk_inc_stats(LMK_MORGUE_COUNT); + changed = true; + } + } + return changed; +} + +bool lmk_death_pending_morgue(void) +{ + bool changed; + + spin_lock(&lmk_task_lock); + changed = __lmk_death_pending_morgue(); + spin_unlock(&lmk_task_lock); + return changed; +} + +static void lmk_task_free(struct task_struct *tsk) +{ + if (thread_group_leader(tsk) && + !(tsk->flags & PF_KTHREAD)) { + struct lmk_death_pending_entry *dp_iterator; + int clear = 1; + + spin_lock(&lmk_task_lock); + if (__lmk_task_remove(tsk, tsk->signal->oom_score_adj)) + clear = 0; + + /* check our kill queue */ + list_for_each_entry(dp_iterator, + &lmk_death_pending, lmk_dp_list) { + if (dp_iterator->tsk == tsk) { + list_del(&dp_iterator->lmk_dp_list); + kmem_cache_free(lmk_dp_cache, dp_iterator); + death_pending_len--; + put_task_struct(tsk); + clear = 0; + break; + } + } + spin_unlock(&lmk_task_lock); + if (clear) { + lowmem_print(2, "Pid not in list %d %d\n", + tsk->pid, tsk->signal->oom_score_adj); + } + } +} + +static int lmk_oom_score_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct oom_score_notifier_struct *osns = data; + + switch (action) { + case OSN_NEW: + lmk_task_watch(osns->tsk, LMK_SCORE_THRESHOLD - 1); + break; + case OSN_FREE: + lmk_task_free(osns->tsk); + break; + case OSN_UPDATE: + lmk_task_watch(osns->tsk, osns->old_score); + break; + } + return 0; +} + +int __lmk_death_pending_add(struct lmk_death_pending_entry *lwp) +{ + list_add(&lwp->lmk_dp_list, &lmk_death_pending); + get_task_struct(lwp->tsk); + death_pending_len++; + return 0; +} + +struct lmk_rb_watch *__lmk_task_first(void) +{ + return rb_entry(rb_first(&watch_tree), struct lmk_rb_watch, rb_node); +} + +struct notifier_block lmk_oom_score_nb = { + .notifier_call = lmk_oom_score_notifier, +}; diff --git a/drivers/staging/android/lowmemorykiller_tasks.h b/drivers/staging/android/lowmemorykiller_tasks.h new file mode 100644 index 0000000000000000000000000000000000000000..b2771ced9c5a945fefafb1a1e94a982500d7dbd1 --- /dev/null +++ b/drivers/staging/android/lowmemorykiller_tasks.h @@ -0,0 +1,44 @@ +/* + * lowmemorykiller_tasks interface + * + * Author: Peter Enderborg + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 __LOWMEMORYKILLER_TASKS_H +#define __LOWMEMORYKILLER_TASKS_H + +struct lmk_death_pending_entry { + struct list_head lmk_dp_list; + struct task_struct *tsk; +}; + +struct lmk_rb_watch { + struct rb_node rb_node; + struct task_struct *tsk; + int key; +}; + +extern int death_pending_len; +extern struct kmem_cache *lmk_dp_cache; +extern struct kmem_cache *lmk_task_cache; +extern spinlock_t lmk_task_lock; +extern struct notifier_block lmk_oom_score_nb; + +int __lmk_task_remove(struct task_struct *tsk, int score); +int __lmk_death_pending_add(struct lmk_death_pending_entry *lwp); +bool lmk_death_pending_morgue(void); +bool __lmk_death_pending_morgue(void); +struct lmk_rb_watch *__lmk_task_first(void); + +#endif diff --git a/drivers/staging/android/lowmemorykiller_tng.c b/drivers/staging/android/lowmemorykiller_tng.c new file mode 100644 index 0000000000000000000000000000000000000000..1253514961d7a3abb533bb9b7ac1b548ae8c246b --- /dev/null +++ b/drivers/staging/android/lowmemorykiller_tng.c @@ -0,0 +1,280 @@ +/* + * lowmemorykiller_tng + * + * Author: Peter Enderborg + * co Author: Snild Dolkow + * co Author: Björn Davidsson + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 file contain the logic for new lowmemorykiller (TNG). Parts + * of the calculations is taken from the original lowmemorykiller + */ + +/* add fake print format with original module name */ +#define pr_fmt(fmt) "lowmemorykiller: " fmt + +#include +#include +#include + +#include +#include "lowmemorykiller_tasks.h" +#include "lowmemorykiller_oom.h" +#include "lowmemorykiller_tng.h" + +#include +#ifdef CONFIG_ZCACHE +#include +#else +/*zcache.h has incorrect definition here.*/ +static inline u64 zcache_pages(void) { return 0; } +#endif +#define LMK_ZOMBIE_SIZE (4096) + +static unsigned long lowmem_count_tng(struct shrinker *s, + struct shrink_control *sc); +static unsigned long lowmem_scan_tng(struct shrinker *s, + struct shrink_control *sc); + +void __init lowmem_init_tng(struct shrinker *shrinker) +{ + lmk_dp_cache = KMEM_CACHE(lmk_death_pending_entry, 0); + lmk_task_cache = KMEM_CACHE(lmk_rb_watch, 0); + oom_score_notifier_register(&lmk_oom_score_nb); + lowmemorykiller_register_oom_notifier(); + shrinker->count_objects = lowmem_count_tng; + shrinker->scan_objects = lowmem_scan_tng; +} + +ssize_t get_task_rss(struct task_struct *tsk) +{ + unsigned long rss = 0; + struct mm_struct *mm; + + mm = ACCESS_ONCE(tsk->mm); + if (mm) + rss = get_mm_rss(mm); + if (rss < LMK_ZOMBIE_SIZE) + rss = LMK_ZOMBIE_SIZE; + return rss; +} + +static void calc_params(struct calculated_params *cp, gfp_t mask) +{ + int i; + int array_size; + + cp->other_free = global_page_state(NR_FREE_PAGES); + cp->other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() - + global_page_state(NR_SHMEM) - + global_page_state(NR_UNEVICTABLE) - + total_swapcache_pages(); + cp->other_file = (cp->other_file < 0) ? 0 : cp->other_file; + + cp->minfree = 0; + cp->min_score_adj = SHRT_MAX; + tune_lmk_param_mask(&cp->other_free, &cp->other_file, mask); + array_size = lowmem_min_param_size(); + for (i = 0; i < array_size; i++) { + cp->minfree = lowmem_minfree[i]; + if (cp->other_free < cp->minfree && + cp->other_file < cp->minfree) { + cp->min_score_adj = lowmem_adj[i]; + break; + } + } + adjust_minadj(&cp->min_score_adj); + cp->dynamic_max_queue_len = array_size - i + 1; +} + +int kill_needed(int level, gfp_t mask, + struct calculated_params *cp) +{ + calc_params(cp, mask); + cp->selected_oom_score_adj = level; + + if (level >= cp->min_score_adj) + return 1; + return 0; +} + +void print_obituary(struct task_struct *doomed, + struct calculated_params *cp, + gfp_t gfp_mask) { + long cache_size = cp->other_file * (long)(PAGE_SIZE / 1024); + long cache_limit = cp->minfree * (long)(PAGE_SIZE / 1024); + long free = cp->other_free * (long)(PAGE_SIZE / 1024); + + lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" + " to free %ldkB on behalf of '%s' (%d) because\n" + " cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" + " Free memory is %ldkB above reserved.\n" + " Free CMA is %ldkB\n" + " Total reserve is %ldkB\n" + " Total free pages is %ldkB\n" + " Total file cache is %ldkB\n" + " Slab Reclaimable is %ldkB\n" + " Slab UnReclaimable is %ldkB\n" + " Total Slab is %ldkB\n" + " GFP mask is 0x%x\n" + " queue len is %d of max %d\n", + doomed->comm, doomed->pid, + cp->selected_oom_score_adj, + cp->selected_tasksize * (long)(PAGE_SIZE / 1024), + current->comm, current->pid, + cache_size, cache_limit, + cp->min_score_adj, + free, + global_page_state(NR_FREE_CMA_PAGES) * + (long)(PAGE_SIZE / 1024), + totalreserve_pages * (long)(PAGE_SIZE / 1024), + global_page_state(NR_FREE_PAGES) * + (long)(PAGE_SIZE / 1024), + global_page_state(NR_FILE_PAGES) * + (long)(PAGE_SIZE / 1024), + global_page_state(NR_SLAB_RECLAIMABLE) * + (long)(PAGE_SIZE / 1024), + global_page_state(NR_SLAB_UNRECLAIMABLE) * + (long)(PAGE_SIZE / 1024), + global_page_state(NR_SLAB_RECLAIMABLE) * + (long)(PAGE_SIZE / 1024) + + global_page_state(NR_SLAB_UNRECLAIMABLE) * + (long)(PAGE_SIZE / 1024), + gfp_mask, + death_pending_len, + cp->dynamic_max_queue_len); +} + +static unsigned long lowmem_count_tng(struct shrinker *s, + struct shrink_control *sc) +{ + struct lmk_rb_watch *lrw; + struct calculated_params cp; + short score; + + if (current_is_kswapd()) + return 0; + lmk_inc_stats(LMK_COUNT); + cp.selected_tasksize = 0; + spin_lock(&lmk_task_lock); + lrw = __lmk_task_first(); + if (lrw) { + int rss = get_task_rss(lrw->tsk); + + score = lrw->tsk->signal->oom_score_adj; + spin_unlock(&lmk_task_lock); + if (kill_needed(score, sc->gfp_mask, &cp)) { + if (death_pending_len < cp.dynamic_max_queue_len) + cp.selected_tasksize = rss; + else if (lmk_death_pending_morgue()) + if (death_pending_len < + cp.dynamic_max_queue_len) + cp.selected_tasksize = rss; + } + } else { + spin_unlock(&lmk_task_lock); + lowmem_print(2, "Empty task list in count"); + } + if (cp.selected_tasksize == 0) + lmk_inc_stats(LMK_ZERO_COUNT); + + return cp.selected_tasksize; +} + +static unsigned long lowmem_scan_tng(struct shrinker *s, + struct shrink_control *sc) +{ + struct task_struct *selected = NULL; + unsigned long nr_to_scan = sc->nr_to_scan; + struct lmk_rb_watch *lrw; + int do_kill; + struct calculated_params cp; + + lmk_inc_stats(LMK_SCAN); + + cp.selected_tasksize = 0; + spin_lock(&lmk_task_lock); + + lrw = __lmk_task_first(); + if (lrw) { + cp.selected_tasksize = get_task_rss(lrw->tsk); + do_kill = kill_needed(lrw->key, sc->gfp_mask, &cp); + if (death_pending_len >= cp.dynamic_max_queue_len) { + lmk_inc_stats(LMK_BUSY); + cp.selected_tasksize = SHRINK_STOP; + goto unlock_out; + } + + if (do_kill) { + struct lmk_death_pending_entry *ldpt; + + selected = lrw->tsk; + + /* there is a chance that task is locked, + * and the case where it locked in oom_score_adj_write + * we might have deadlock. + */ + if (!task_trylock_lmk(selected)) { + lmk_inc_stats(LMK_ERROR); + lowmem_print(2, "Failed to lock task.\n"); + lmk_inc_stats(LMK_BUSY); + cp.selected_tasksize = SHRINK_STOP; + goto unlock_out; + } + + /* move to kill pending set */ + ldpt = kmem_cache_alloc(lmk_dp_cache, GFP_ATOMIC); + ldpt->tsk = selected; + + __lmk_death_pending_add(ldpt); + if (!__lmk_task_remove(selected, lrw->key)) + WARN_ON(1); + + spin_unlock(&lmk_task_lock); + + send_sig(SIGKILL, selected, 0); + LMK_TAG_TASK_DIE(selected); + trace_lmk_sigkill(selected->pid, selected->comm, + cp.selected_oom_score_adj, + cp.selected_tasksize, + sc->gfp_mask); + print_obituary(selected, &cp, sc->gfp_mask); + + task_unlock(selected); + lmk_inc_stats(LMK_KILL); + goto out; + } else { + lmk_inc_stats(LMK_WASTE); + } + } else { + lmk_inc_stats(LMK_NO_KILL); + } + +unlock_out: + spin_unlock(&lmk_task_lock); +out: + if (cp.selected_tasksize == 0) + lowmem_print(2, "list empty nothing to free\n"); + lowmem_print(4, "lowmem_shrink %lu, %x, return %ld\n", + nr_to_scan, sc->gfp_mask, cp.selected_tasksize); + + return cp.selected_tasksize; +} + +void tune_lmk_param_mask(int *other_free, int *other_file, gfp_t mask) +{ + struct shrink_control fake_shrink_control; + + fake_shrink_control.gfp_mask = mask; + tune_lmk_param(other_free, other_file, &fake_shrink_control); +} diff --git a/drivers/staging/android/lowmemorykiller_tng.h b/drivers/staging/android/lowmemorykiller_tng.h new file mode 100644 index 0000000000000000000000000000000000000000..7ed0e90d99a4d0f8fed50770206f6c2876829346 --- /dev/null +++ b/drivers/staging/android/lowmemorykiller_tng.h @@ -0,0 +1,31 @@ +/* + * lowmemorykiller_tng interface + * + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 __LOWMEMORYKILLER_TNG_H +#define __LOWMEMORYKILLER_TNG_H + +extern short lowmem_adj[]; +extern int lowmem_minfree[]; +/* function in lowmemorykiller.c */ +int lowmem_min_param_size(void); +int adjust_minadj(short *min_score_adj); +/* please dont use tune_lmk_param directly without good reason */ +void tune_lmk_param(int *other_free, int *other_file, + struct shrink_control *sc); +void tune_lmk_param_mask(int *other_free, int *other_file, gfp_t mask); +void __init lowmem_init_tng(struct shrinker *shrinker); +void balance_cache(void); +#endif diff --git a/drivers/staging/android/lowmemorykiller_vmpressure.c b/drivers/staging/android/lowmemorykiller_vmpressure.c new file mode 100644 index 0000000000000000000000000000000000000000..f12be1402a0570e2441e3415cb4cd752a9a57ea3 --- /dev/null +++ b/drivers/staging/android/lowmemorykiller_vmpressure.c @@ -0,0 +1,122 @@ +/* + * lowmemorykiller_vmpressure + * + * Author: Peter Enderborg + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +/* todo: Handle vmpressure notifier directly. + * It is there on this version since it us hooking + * into qualcomm vmpressure extension. + */ + +/* add fake print format with original module name */ +#define pr_fmt(fmt) "lowmemorykiller: " fmt + +#include +#include +#include +#include +#include + +#include "lowmemorykiller.h" +#include "lowmemorykiller_stats.h" +#include "lowmemorykiller_tasks.h" + +void balance_cache(void) +{ + struct task_struct *selected = NULL; + struct lmk_rb_watch *lrw; + int do_kill; + struct calculated_params cp; + gfp_t mask = ___GFP_KSWAPD_RECLAIM | + ___GFP_DIRECT_RECLAIM | __GFP_FS | __GFP_IO; + + cp.selected_tasksize = 0; + cp.dynamic_max_queue_len = 1; + spin_lock_bh(&lmk_task_lock); + + lrw = __lmk_task_first(); + + if (lrw) { + if (lrw->tsk->mm) { + cp.selected_tasksize = get_mm_rss(lrw->tsk->mm); + } else { + lowmem_print(3, "pid:%d no mem\n", lrw->tsk->pid); + lmk_inc_stats(LMK_ERROR); + goto unlock_out; + } + + do_kill = kill_needed(lrw->key, mask, &cp); + + if (death_pending_len >= cp.dynamic_max_queue_len) + __lmk_death_pending_morgue(); + if (death_pending_len >= cp.dynamic_max_queue_len) { + lmk_inc_stats(LMK_BUSY); + cp.selected_tasksize = SHRINK_STOP; + lowmem_print(3, "Queue %d >= %d", + death_pending_len, + cp.dynamic_max_queue_len); + goto unlock_out; + } + + if (do_kill) { + struct lmk_death_pending_entry *ldpt; + + selected = lrw->tsk; + + /* there is a chance that task is locked, + * and the case where it locked in oom_score_adj_write + * we might have deadlock. There is no macro for it + * and this is the only place there is a try on + * the task_lock. + */ + if (!spin_trylock(&selected->alloc_lock)) { + lowmem_print(2, "Failed to lock task.\n"); + lmk_inc_stats(LMK_BUSY); + cp.selected_tasksize = SHRINK_STOP; + goto unlock_out; + } + + /* move to kill pending set */ + ldpt = kmem_cache_alloc(lmk_dp_cache, GFP_ATOMIC); + ldpt->tsk = selected; + + __lmk_death_pending_add(ldpt); + if (!__lmk_task_remove(selected, lrw->key)) + WARN_ON(1); + + spin_unlock_bh(&lmk_task_lock); + + send_sig(SIGKILL, selected, 0); + LMK_TAG_TASK_DIE(selected); + + print_obituary(selected, &cp, 0); + + task_unlock(selected); + lmk_inc_stats(LMK_BALANCE_KILL); + goto out; + } else { + lowmem_print(3, "No kill"); + lmk_inc_stats(LMK_BALANCE_WASTE); + } + } else { + lowmem_print(2, "Nothing to kill"); + lmk_inc_stats(LMK_NO_KILL); + } +unlock_out: + spin_unlock_bh(&lmk_task_lock); +out: + if (cp.selected_tasksize == 0) + lowmem_print(2, "list empty nothing to free\n"); +} diff --git a/drivers/staging/fbtft/fb_agm1264k-fl.c b/drivers/staging/fbtft/fb_agm1264k-fl.c index 2a50cf957101116f27a14b76ee1364f030ec1efb..7397d239b65aa09dccdc3e4a6b3423ec408c0b1c 100644 --- a/drivers/staging/fbtft/fb_agm1264k-fl.c +++ b/drivers/staging/fbtft/fb_agm1264k-fl.c @@ -13,6 +13,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c index a6b43323f29ac952d98c5812fd1e7cbb9b002ce7..c5683a8b4fc08cf08fea382b641af90c84e85921 100644 --- a/drivers/staging/fbtft/fb_pcd8544.c +++ b/drivers/staging/fbtft/fb_pcd8544.c @@ -16,6 +16,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/staging/fbtft/fb_ra8875.c b/drivers/staging/fbtft/fb_ra8875.c index b167c50616318a94e98c766c9ef972f05fc60fbc..7d71872d7215b95ac61e583851e6d73d56453893 100644 --- a/drivers/staging/fbtft/fb_ra8875.c +++ b/drivers/staging/fbtft/fb_ra8875.c @@ -12,6 +12,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/staging/fbtft/fb_ssd1306.c b/drivers/staging/fbtft/fb_ssd1306.c index e0b34a42c9c635bd243737291dc8bf02ef831861..332543083a80ee2e4859a4a1aacee39607bb4b94 100644 --- a/drivers/staging/fbtft/fb_ssd1306.c +++ b/drivers/staging/fbtft/fb_ssd1306.c @@ -13,6 +13,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/staging/fbtft/fb_tls8204.c b/drivers/staging/fbtft/fb_tls8204.c index 2183f98c831514c9eef828c3ef8bbceabb47fd7a..f3b98f7fa4ccbe4f7a3f8e225007b2be6b72c424 100644 --- a/drivers/staging/fbtft/fb_tls8204.c +++ b/drivers/staging/fbtft/fb_tls8204.c @@ -17,6 +17,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/staging/fbtft/fb_uc1611.c b/drivers/staging/fbtft/fb_uc1611.c index 4e828142058ef6c5e8ae2a714f7aad8c9f41a703..dd25d72efb96fe348963ba34817cc3f9d0fcd248 100644 --- a/drivers/staging/fbtft/fb_uc1611.c +++ b/drivers/staging/fbtft/fb_uc1611.c @@ -15,6 +15,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/staging/fbtft/fb_uc1701.c b/drivers/staging/fbtft/fb_uc1701.c index 212908e3927727badbe5359d8996ce63dcd0f5be..10f4191cdc42a5f1e972f6f5ff5769bb5947c3cc 100644 --- a/drivers/staging/fbtft/fb_uc1701.c +++ b/drivers/staging/fbtft/fb_uc1701.c @@ -16,6 +16,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/staging/fbtft/fb_watterott.c b/drivers/staging/fbtft/fb_watterott.c index f8cb610a7b6984cb3bc977cf52f63966f6748620..3b6e57bf38a9a4c135fd1c5c3e75c52db5e1a7b9 100644 --- a/drivers/staging/fbtft/fb_watterott.c +++ b/drivers/staging/fbtft/fb_watterott.c @@ -13,6 +13,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/staging/fbtft/fbtft-bus.c b/drivers/staging/fbtft/fbtft-bus.c index 58449ad84f46c17f842cc32e04ee5c18cf2b58c0..097db4118a219a18f9ec17777fbfd08d7c8fbad3 100644 --- a/drivers/staging/fbtft/fbtft-bus.c +++ b/drivers/staging/fbtft/fbtft-bus.c @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index 18c2b6daf58857e1d1f34462d5e95a4e5e1d1178..4005ee42b464fe867bb0885962272ef4e729866e 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -15,6 +15,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c index 6eb600ff70569ef6ceb902afcc49ddce08b09953..d433cd6efbd6034a278b2d6fc7256fe7cae5eca5 100644 --- a/drivers/staging/iio/iio_simple_dummy_events.c +++ b/drivers/staging/iio/iio_simple_dummy_events.c @@ -7,6 +7,11 @@ * * Event handling elements of industrial I/O reference driver. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/drivers/thermal/msm_thermal-dev.c b/drivers/thermal/msm_thermal-dev.c index ead9765666c84aae5d2dd89850822a70f28a01ae..ab25987676fcc3d44638c7f843436cff7bee371c 100644 --- a/drivers/thermal/msm_thermal-dev.c +++ b/drivers/thermal/msm_thermal-dev.c @@ -100,6 +100,16 @@ static long validate_and_copy(unsigned int *cmd, unsigned long *arg, goto validate_exit; } break; + case MSM_THERMAL_GET_CLUSTER_FREQUENCY_PLAN: + if (query->clock_freq.cluster_num >= NR_CPUS) { + ret = -EINVAL; + goto validate_exit; + } + case MSM_THERMAL_GET_CLUSTER_VOLTAGE_PLAN: + if (query->voltage.cluster_num >= NR_CPUS) { + ret = -EINVAL; + goto validate_exit; + } default: break; } diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c index 2df1bf69e7c9170ba30f7d5ba7eec0c5d415aa99..62a261232236d23d47aafe4243539a694ad7642e 100644 --- a/drivers/thermal/qpnp-adc-tm.c +++ b/drivers/thermal/qpnp-adc-tm.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -1699,7 +1704,12 @@ static int qpnp_adc_tm_get_trip_temp(struct thermal_zone_device *thermal, return -EINVAL; } - rc = qpnp_adc_tm_scale_voltage_therm_pu2(chip->vadc_dev, + if (adc_tm_sensor->btm_channel_num == QPNP_ADC_TM_M1_ADC_CH_SEL_CTL || + adc_tm_sensor->btm_channel_num == QPNP_ADC_TM_M2_ADC_CH_SEL_CTL) + rc = qpnp_adc_tm_scale_voltage_therm_pu2_decidegc(chip->vadc_dev, + chip->adc->adc_prop, reg, &result); + else + rc = qpnp_adc_tm_scale_voltage_therm_pu2(chip->vadc_dev, chip->adc->adc_prop, reg, &result); if (rc < 0) { pr_err("Failed to lookup the therm thresholds\n"); @@ -1744,8 +1754,13 @@ static int qpnp_adc_tm_set_trip_temp(struct thermal_zone_device *thermal, pr_debug("requested a high - %d and low - %d with trip - %d\n", tm_config.high_thr_temp, tm_config.low_thr_temp, trip); - rc = qpnp_adc_tm_scale_therm_voltage_pu2(chip->vadc_dev, - chip->adc->adc_prop, &tm_config); + if (adc_tm->btm_channel_num == QPNP_ADC_TM_M1_ADC_CH_SEL_CTL || + adc_tm->btm_channel_num == QPNP_ADC_TM_M2_ADC_CH_SEL_CTL) + rc = qpnp_adc_tm_scale_therm_voltage_pu2_decidegc(chip->vadc_dev, + chip->adc->adc_prop, &tm_config); + else + rc = qpnp_adc_tm_scale_therm_voltage_pu2(chip->vadc_dev, + chip->adc->adc_prop, &tm_config); if (rc < 0) { pr_err("Failed to lookup the adc-tm thresholds\n"); return rc; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 619e5446cbe8114b28e21773bbcd7aa2da10b7ef..26051e115dcf50dd90bf59cb710e06ff8039cc31 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -16,6 +16,11 @@ * (C) Copyright Greg Kroah-Hartman 2002-2003 * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -23,6 +28,10 @@ #include #include "usb.h" +#ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION +#include +#endif + static inline const char *plural(int n) { return (n == 1 ? "" : "s"); @@ -168,10 +177,14 @@ int usb_choose_configuration(struct usb_device *udev) best = c; } - if (insufficient_power > 0) + if (insufficient_power > 0) { dev_info(&udev->dev, "rejected %d configuration%s " "due to insufficient available bus power\n", insufficient_power, plural(insufficient_power)); +#ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION + host_send_uevent(USB_HOST_EXT_EVENT_INSUFFICIENT_POWER); +#endif + } if (best) { /* choose usb audio class preferred config if available */ diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 5a42c459040295a98c286ca993f9468a054682ca..500cbe6652d9f0e4634e0ef61b844219cdb0d9c3 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -104,4 +104,13 @@ config USB_DWC3_QCOM Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside, say 'Y' or 'M' if you have one such device. +config USB_DWC3_MSM_ID_POLL + bool "Support ID polling function on DesignWare USB Controller for MSM" + depends on USB_DWC3_DUAL_ROLE + depends on USB_DWC3 + default n + help + Say Y here to enable the ID polling function on the DesignWare USB3.0 + (DRD) Controller for MSM driver. + endif diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 95e27e097b6bbb312de6f2b8dfc90eb380e6c0ee..0bcec982aff6264f35551719317041d6b6e2478f 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -39,6 +44,9 @@ #include #include #include +#ifdef CONFIG_USB_DWC3_MSM_ID_POLL +#include +#endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ #include #include #include @@ -47,6 +55,11 @@ #include #include #include +#ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION +#include +#endif +#include +#include #include "power.h" #include "core.h" @@ -147,6 +160,7 @@ enum plug_orientation { #define ID 0 #define B_SESS_VLD 1 #define B_SUSPEND 2 +#define A_VBUS_DROP_DET 3 #define PM_QOS_SAMPLE_SEC 2 #define PM_QOS_THRESHOLD 400 @@ -222,8 +236,10 @@ struct dwc3_msm { struct extcon_dev *extcon_vbus; struct extcon_dev *extcon_id; + struct extcon_dev *extcon_vbus_drop; struct notifier_block vbus_nb; struct notifier_block id_nb; + struct notifier_block vbus_drop_nb; struct notifier_block host_nb; bool host_only_mode; @@ -239,6 +255,29 @@ struct dwc3_msm { struct delayed_work sdp_check; bool usb_compliance_mode; struct mutex suspend_resume_mutex; + +#ifdef CONFIG_USB_DWC3_MSM_ID_POLL + /* id polling */ + bool id_polling_use; + bool id_polling_start; + struct delayed_work id_polling_work; + struct workqueue_struct *id_polling_q; + unsigned int id_polling_up_interval; + unsigned int id_polling_up_period; + int id_polling_pd_gpio; + struct qpnp_vadc_chip *usb_detect_adc; + spinlock_t id_polling_lock; + bool otg_present; + unsigned int lcd_blanked; + struct wakeup_source id_polling_wu; + struct delayed_work setsink_work; + struct wake_lock setsink_lock; + int setsink_cnt; +#ifdef CONFIG_FB + struct notifier_block fb_notif; +#endif /* CONFIG_FB */ +#endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ + bool send_vbus_drop_ue; }; #define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */ @@ -255,6 +294,17 @@ struct dwc3_msm { #define DSTS_CONNECTSPD_SS 0x4 +#ifdef CONFIG_USB_DWC3_MSM_ID_POLL +#define USB_ID_POLLING_UP_INTERVAL 1000 /* s */ +#define USB_ID_POLLING_UP_PERIOD 100 /* us */ +#define USB_ID_POLLING_WAKE_TIMEOUT 2000 + +#define SETSINK_RETRY_INTERVAL 2000 +#define WAKELOCK_RETRY_INTERVAL 2500 + +/* Max of retry to set SINK */ +#define SETSINK_RETRY_MAX 3 +#endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ static void dwc3_pwr_event_handler(struct dwc3_msm *mdwc); static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA); @@ -2488,6 +2538,284 @@ static irqreturn_t msm_dwc3_pwr_irq(int irq, void *data) return IRQ_HANDLED; } +#ifdef CONFIG_USB_DWC3_MSM_ID_POLL +static void dwc3_setsink_work(struct work_struct *w) +{ + struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, + setsink_work.work); + union power_supply_propval val = {0}; + int ret; + + if (!mdwc->otg_present && mdwc->setsink_cnt < SETSINK_RETRY_MAX) { + wake_lock_timeout(&mdwc->setsink_lock, + msecs_to_jiffies(WAKELOCK_RETRY_INTERVAL)); + mdwc->setsink_cnt++; + power_supply_get_property(mdwc->usb_psy, + POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); + if (val.intval == POWER_SUPPLY_TYPEC_PR_SINK) { + pr_info("%s(): power role was changed to sink\n", + __func__); + return; + } + + val.intval = POWER_SUPPLY_TYPEC_PR_SINK; + ret = power_supply_set_property(mdwc->usb_psy, + POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); + if (ret) { + dev_err(mdwc->dev, "set power supply fail\n"); + return; + } + pr_info("%s(): rerun set to sink\n", __func__); + schedule_delayed_work(&mdwc->setsink_work, + msecs_to_jiffies(SETSINK_RETRY_INTERVAL)); + } else { + if (mdwc->otg_present) + pr_info("%s(): cable is connected, so power role does not change\n", + __func__); + else + dev_err(mdwc->dev, + "retry count is over, power role has not changed.\n"); + } +} + +static void dwc3_id_pullup(struct dwc3_msm *mdwc, int pullup) +{ + if (pullup) { + dev_dbg(mdwc->dev, "%s: pull up ID pin\n", __func__); + if (mdwc->id_polling_pd_gpio) + gpio_set_value(mdwc->id_polling_pd_gpio, 1); + } else { + if (mdwc->id_polling_pd_gpio) + gpio_set_value(mdwc->id_polling_pd_gpio, 0); + dev_dbg(mdwc->dev, "%s: pull down ID pin\n", __func__); + } +} + +static ssize_t id_polling_up_interval_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dwc3_msm *mdwc = dev_get_drvdata(dev); + + if (!mdwc) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%u\n", mdwc->id_polling_up_interval); +} + +static ssize_t id_polling_up_interval_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct dwc3_msm *mdwc = dev_get_drvdata(dev); + + if (!mdwc) + return -EINVAL; + + if (kstrtou32(buf, 0, &mdwc->id_polling_up_interval) < 0) { + pr_err("id_polling_up_interval cannot read value\n"); + return -EINVAL; + } + + if (mdwc->id_polling_start) { + /* restart id polling with new interval value. */ + cancel_delayed_work_sync(&mdwc->id_polling_work); + queue_delayed_work(mdwc->id_polling_q, &mdwc->id_polling_work, + msecs_to_jiffies(mdwc->id_polling_up_interval)); + } + + return size; +} + +static DEVICE_ATTR(id_polling_up_interval, S_IRUGO | S_IWUSR, + id_polling_up_interval_show, + id_polling_up_interval_store); + +static ssize_t id_polling_up_period_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dwc3_msm *mdwc = dev_get_drvdata(dev); + + if (!mdwc) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%u\n", mdwc->id_polling_up_period); +} + +static ssize_t id_polling_up_period_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct dwc3_msm *mdwc = dev_get_drvdata(dev); + + if (!mdwc) + return -EINVAL; + + if (kstrtou32(buf, 0, &mdwc->id_polling_up_period) < 0) { + pr_err("id_polling_up_period cannot read value\n"); + return -EINVAL; + } + + return size; +} + +static DEVICE_ATTR(id_polling_up_period, S_IRUGO | S_IWUSR, + id_polling_up_period_show, + id_polling_up_period_store); + +static void dwc3_id_poll_update(struct dwc3_msm *mdwc) +{ + if (!mdwc->otg_present && mdwc->lcd_blanked) { + mdwc->id_polling_start = false; + cancel_delayed_work(&mdwc->id_polling_work); + } else if (!mdwc->id_polling_start) { + mdwc->id_polling_start = true; + queue_delayed_work(mdwc->id_polling_q, &mdwc->id_polling_work, + 0); + } +} + +#ifdef CONFIG_FB +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct dwc3_msm *mdwc = container_of(self, struct dwc3_msm, fb_notif); + struct fb_event *evdata = data; + unsigned int blanked; + unsigned long flags; + + if (!mdwc->id_polling_use || !evdata || !evdata->data || + event != FB_EVENT_BLANK) + return 0; + + blanked = !(*(unsigned int *)(evdata->data) == FB_BLANK_UNBLANK); + + dev_info(mdwc->dev, "receive fb event blank=%u->%u, otg_present=%d\n", + mdwc->lcd_blanked, blanked, mdwc->otg_present); + + if (blanked == mdwc->lcd_blanked) + return 0; + + spin_lock_irqsave(&mdwc->id_polling_lock, flags); + mdwc->lcd_blanked = blanked; + dwc3_id_poll_update(mdwc); + spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); + + return 0; +} +#endif /* CONFIG_FB */ + +static int dwc3_msm_set_type_power_role(struct dwc3_msm *mdwc, + int typec_power_role) +{ + union power_supply_propval pval = {0}; + int ret; + + if (!mdwc->usb_psy) { + mdwc->usb_psy = power_supply_get_by_name("usb"); + if (!mdwc->usb_psy) { + dev_warn(mdwc->dev, "Could not get usb power_supply\n"); + return -ENODEV; + } + } + pr_info("%s(): typec power role=%d\n", __func__, typec_power_role); + pval.intval = typec_power_role; + ret = power_supply_set_property(mdwc->usb_psy, + POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval); + if (ret) { + dev_err(mdwc->dev, + "power supply error when setting property\n"); + return ret; + } + + if (typec_power_role == POWER_SUPPLY_TYPEC_PR_SINK) { + mdwc->setsink_cnt = 0; + wake_lock_timeout(&mdwc->setsink_lock, + msecs_to_jiffies(WAKELOCK_RETRY_INTERVAL)); + schedule_delayed_work(&mdwc->setsink_work, + msecs_to_jiffies(SETSINK_RETRY_INTERVAL)); + } + return 0; +} + +static int dwc3_get_usb_detect_adc(struct dwc3_msm *mdwc) +{ + int rc = 0; + struct qpnp_vadc_result results; + + if (IS_ERR_OR_NULL(mdwc->usb_detect_adc)) { + mdwc->usb_detect_adc = qpnp_get_vadc(mdwc->dev, "usb_detect"); + if (IS_ERR(mdwc->usb_detect_adc)) + return PTR_ERR(mdwc->usb_detect_adc); + } + + rc = qpnp_vadc_read(mdwc->usb_detect_adc, 0x14, &results); + if (rc) + return 1; + else + return (800000 < results.physical); +} + +static void dwc3_id_polling_work(struct work_struct *w) +{ + struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, + id_polling_work.work); + enum dwc3_id_state id; + unsigned int delta; + unsigned long flags; + bool otg_present; + + if (!mdwc->id_polling_use) + return; + + spin_lock_irqsave(&mdwc->id_polling_lock, flags); + if (!mdwc->id_polling_start) { + spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); + return; + } + queue_delayed_work(mdwc->id_polling_q, to_delayed_work(w), + msecs_to_jiffies(mdwc->id_polling_up_interval)); + spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); + + pr_debug("id polling, interval=%u ms, period=%u us\n", + mdwc->id_polling_up_interval, + mdwc->id_polling_up_period); + + __pm_stay_awake(&mdwc->id_polling_wu); + dwc3_id_pullup(mdwc, 1); + + if (mdwc->id_polling_up_period) { + if (mdwc->id_polling_up_period < 10) { + udelay(mdwc->id_polling_up_period); + } else if (mdwc->id_polling_up_period < 20000) { + delta = mdwc->id_polling_up_period / 10; + usleep_range(mdwc->id_polling_up_period - delta, + mdwc->id_polling_up_period + delta); + } else if (mdwc->id_polling_up_period / 1000 < + mdwc->id_polling_up_interval) { + msleep(mdwc->id_polling_up_period / 1000); + } else { + pr_warn("pull up period is too long because it is longer than interval.\n"); + } + } + + id = dwc3_get_usb_detect_adc(mdwc) ? DWC3_ID_FLOAT : DWC3_ID_GROUND; + dwc3_id_pullup(mdwc, 0); + + spin_lock_irqsave(&mdwc->id_polling_lock, flags); + otg_present = mdwc->otg_present; + mdwc->otg_present = id == DWC3_ID_GROUND; + dwc3_id_poll_update(mdwc); + spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); + + if (mdwc->otg_present != otg_present) { + dwc3_msm_set_type_power_role(mdwc, mdwc->otg_present ? + POWER_SUPPLY_TYPEC_PR_DUAL : + POWER_SUPPLY_TYPEC_PR_SINK); + __pm_wakeup_event(&mdwc->id_polling_wu, + USB_ID_POLLING_WAKE_TIMEOUT); + } + __pm_relax(&mdwc->id_polling_wu); +} +#endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ + static int dwc3_cpu_notifier_cb(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -2681,6 +3009,24 @@ static void check_for_sdp_connection(struct work_struct *w) } } +static int dwc3_msm_vbus_drop_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc3_msm *mdwc = container_of(nb, struct dwc3_msm, vbus_drop_nb); + struct extcon_dev *edev = ptr; + + if (!edev) { + dev_err(mdwc->dev, "%s: edev null\n", __func__); + goto done; + } + + set_bit(A_VBUS_DROP_DET, &mdwc->inputs); + pr_info("%s: receive ocp notification\n", __func__); + schedule_delayed_work(&mdwc->sm_work, 0); +done: + return NOTIFY_DONE; +} + static int dwc3_msm_vbus_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { @@ -2774,10 +3120,22 @@ static int dwc3_msm_extcon_register(struct dwc3_msm *mdwc) dev_err(mdwc->dev, "failed to register notifier for USB-HOST\n"); goto err; } + + mdwc->extcon_vbus_drop = edev; + mdwc->vbus_drop_nb.notifier_call = dwc3_msm_vbus_drop_notifier; + ret = extcon_register_notifier(edev, EXTCON_VBUS_DROP, + &mdwc->vbus_drop_nb); + if (ret < 0) { + dev_err(mdwc->dev, "failed to register notifier for USB-DropT\n"); + goto err; + } } return 0; err: + if (mdwc->extcon_id) + extcon_unregister_notifier(mdwc->extcon_id, EXTCON_USB_HOST, + &mdwc->id_nb); if (mdwc->extcon_vbus) extcon_unregister_notifier(mdwc->extcon_vbus, EXTCON_USB, &mdwc->vbus_nb); @@ -2965,6 +3323,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) } mdwc->id_state = DWC3_ID_FLOAT; + mdwc->otg_present = true; set_bit(ID, &mdwc->inputs); mdwc->charging_disabled = of_property_read_bool(node, @@ -3038,6 +3397,16 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } +#ifdef CONFIG_USB_DWC3_MSM_ID_POLL + mdwc->id_polling_use = of_property_read_bool(node, "id_polling_use"); + if (mdwc->id_polling_use) { + dev_info(&pdev->dev, "id polling is enabled\n"); + mdwc->id_state = DWC3_ID_FLOAT; + } + + wake_lock_init(&mdwc->setsink_lock, WAKE_LOCK_SUSPEND, "typecsink_lock"); +#endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcsr_base"); if (!res) { dev_dbg(&pdev->dev, "missing TCSR memory resource\n"); @@ -3253,6 +3622,10 @@ static int dwc3_msm_probe(struct platform_device *pdev) else if (mdwc->extcon_id && extcon_get_cable_state_(mdwc->extcon_id, EXTCON_USB_HOST)) dwc3_msm_id_notifier(&mdwc->id_nb, true, mdwc->extcon_id); + else if (mdwc->extcon_vbus_drop && extcon_get_cable_state_( + mdwc->extcon_vbus_drop, EXTCON_VBUS_DROP)) + dwc3_msm_vbus_drop_notifier(&mdwc->vbus_drop_nb, true, + mdwc->extcon_vbus_drop); else if (!pval.intval) { /* USB cable is not connected */ queue_delayed_work(mdwc->sm_usb_wq, &mdwc->sm_work, 0); @@ -3282,6 +3655,47 @@ static int dwc3_msm_probe(struct platform_device *pdev) place_marker(boot_marker); +#ifdef CONFIG_USB_DWC3_MSM_ID_POLL + if (mdwc->id_polling_use) { + INIT_DELAYED_WORK(&mdwc->setsink_work, dwc3_setsink_work); + INIT_DELAYED_WORK(&mdwc->id_polling_work, dwc3_id_polling_work); + mdwc->id_polling_q = + create_singlethread_workqueue("id_polling_q"); + + mdwc->id_polling_up_interval = USB_ID_POLLING_UP_INTERVAL; + of_property_read_u32(node, "id_polling_up_interval", + &mdwc->id_polling_up_interval); + dev_dbg(&pdev->dev, "id_polling_up_interval=%dms\n", + mdwc->id_polling_up_interval); + device_create_file(&pdev->dev, + &dev_attr_id_polling_up_interval); + + mdwc->id_polling_up_period = USB_ID_POLLING_UP_PERIOD; + of_property_read_u32(node, "id_polling_up_period", + &mdwc->id_polling_up_period); + dev_dbg(&pdev->dev, "id_polling_up_period=%dus\n", + mdwc->id_polling_up_period); + device_create_file(&pdev->dev, &dev_attr_id_polling_up_period); + + mdwc->id_polling_pd_gpio = of_get_named_gpio(node, + "id_polling_pd_gpio", 0); + if (!gpio_is_valid(mdwc->id_polling_pd_gpio)) + dev_info(&pdev->dev, "id_polling_pd is missing\n"); + + wakeup_source_init(&mdwc->id_polling_wu, "id_polling"); + spin_lock_init(&mdwc->id_polling_lock); + +#ifdef CONFIG_FB + mdwc->fb_notif.notifier_call = fb_notifier_callback; + if (fb_register_client(&mdwc->fb_notif)) + dev_err(mdwc->dev, "failed to register fb_notifier\n"); +#endif /* CONFIG_FB */ + mdwc->id_polling_start = true; + queue_delayed_work(mdwc->id_polling_q, &mdwc->id_polling_work, + msecs_to_jiffies(mdwc->id_polling_up_interval)); + } +#endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ + return 0; put_dwc3: @@ -3289,6 +3703,7 @@ put_dwc3: msm_bus_scale_unregister_client(mdwc->bus_perf_client); of_platform_depopulate(&pdev->dev); err: + wake_lock_destroy(&mdwc->setsink_lock); destroy_workqueue(mdwc->dwc3_wq); return ret; } @@ -3325,6 +3740,22 @@ static int dwc3_msm_remove(struct platform_device *pdev) } cancel_delayed_work_sync(&mdwc->perf_vote_work); +#ifdef CONFIG_USB_DWC3_MSM_ID_POLL + cancel_delayed_work_sync(&mdwc->setsink_work); + wake_lock_destroy(&mdwc->setsink_lock); + if (mdwc->id_polling_use) { +#ifdef CONFIG_FB + fb_unregister_client(&mdwc->fb_notif); +#endif /* CONFIG_FB */ + wakeup_source_trash(&mdwc->id_polling_wu); + device_remove_file(&pdev->dev, + &dev_attr_id_polling_up_interval); + device_remove_file(&pdev->dev, &dev_attr_id_polling_up_period); + cancel_delayed_work_sync(&mdwc->id_polling_work); + destroy_workqueue(mdwc->id_polling_q); + } +#endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ + cancel_delayed_work_sync(&mdwc->sm_work); if (mdwc->hs_phy) @@ -3798,7 +4229,6 @@ set_prop: return 0; } - /** * dwc3_otg_sm_work - workqueue function. * @@ -3874,6 +4304,7 @@ static void dwc3_otg_sm_work(struct work_struct *w) work = 1; break; } else { + mdwc->send_vbus_drop_ue = false; dwc3_msm_gadget_vbus_draw(mdwc, 0); pm_relax(mdwc->dev); dev_dbg(mdwc->dev, "Cable disconnected\n"); @@ -3939,9 +4370,22 @@ static void dwc3_otg_sm_work(struct work_struct *w) /* Switch to A-Device*/ if (test_bit(ID, &mdwc->inputs)) { dev_dbg(mdwc->dev, "id\n"); + clear_bit(A_VBUS_DROP_DET, &mdwc->inputs); mdwc->otg_state = OTG_STATE_B_IDLE; mdwc->vbus_retry_count = 0; work = 1; +#ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION + host_send_uevent(USB_HOST_EXT_EVENT_NONE); +#endif + } else if (test_bit(A_VBUS_DROP_DET, &mdwc->inputs)) { + dev_dbg(mdwc->dev, "vbus_drop_det\n"); + /* staying on here until exit from A-Device */ +#ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION + if (!mdwc->send_vbus_drop_ue) { + mdwc->send_vbus_drop_ue = true; + host_send_uevent(USB_HOST_EXT_EVENT_VBUS_DROP); + } +#endif } else { mdwc->otg_state = OTG_STATE_A_HOST; ret = dwc3_otg_start_host(mdwc, 1); @@ -3974,6 +4418,20 @@ static void dwc3_otg_sm_work(struct work_struct *w) mdwc->vbus_retry_count = 0; mdwc->hc_died = false; work = 1; +#ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION + host_send_uevent(USB_HOST_EXT_EVENT_NONE); +#endif + } else if (test_bit(A_VBUS_DROP_DET, &mdwc->inputs)) { + dev_dbg(mdwc->dev, "vbus_drop_det\n"); + dwc3_otg_start_host(mdwc, 0); + mdwc->otg_state = OTG_STATE_A_IDLE; + mdwc->vbus_retry_count = 0; +#ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION + if (!mdwc->send_vbus_drop_ue) { + mdwc->send_vbus_drop_ue = true; + host_send_uevent(USB_HOST_EXT_EVENT_VBUS_DROP); + } +#endif } else { dev_dbg(mdwc->dev, "still in a_host state. Resuming root hub.\n"); dbg_event(0xFF, "XHCIResume", 0); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 0787e36742aa8b79fab0c4e81713216af5a6b7ef..042722e4f200763a44fe905f7b53fc408de42b0c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -573,6 +573,13 @@ config USB_CONFIGFS_F_CCID USB CCID function driver creats transport layer between the userspace CCID component and the Windows Host. +config USB_ANDROID_PRODUCTION + boolean "Production capable Android gadget" + depends on USB_CONFIGFS + default n + help + Provides an Android gadget where iSerialNumber is set to 0. + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index ce6a44e136dc88467d326b74804577625a681ff1..dab809314400397e147f6ce1eb0111fc0cb05b8b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -8,6 +8,11 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* #define VERBOSE_DEBUG */ @@ -35,8 +40,7 @@ (speed == USB_SPEED_SUPER ?\ SSUSB_GADGET_VBUS_DRAW : CONFIG_USB_GADGET_VBUS_DRAW) -/* disable LPM by default */ -static bool disable_l1_for_hs = true; +static bool disable_l1_for_hs; module_param(disable_l1_for_hs, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(disable_l1_for_hs, "Disable support for L1 LPM for HS devices"); @@ -1692,9 +1696,9 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) value = min(w_length, (u16) value); break; case USB_DT_BOS: - if ((gadget_is_superspeed(gadget) && - (gadget->speed >= USB_SPEED_SUPER)) - || !disable_l1_for_hs) { + if (gadget_is_superspeed(gadget) && + ((gadget->speed >= USB_SPEED_SUPER) + || !disable_l1_for_hs)) { value = bos_desc(cdev); value = min(w_length, (u16) value); } diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index a31322ed94472a1b4c1166973c831e26e82f5168..c91d69ad43a1c3623e15843ee4340799ffb5841d 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -96,6 +101,7 @@ struct gadget_info { struct work_struct work; struct device *dev; #endif + bool isMSOSDesc; }; static inline struct gadget_info *to_gadget_info(struct config_item *item) @@ -146,6 +152,10 @@ struct gadget_config_name { #define MAX_USB_STRING_LEN 126 #define MAX_USB_STRING_WITH_NULL_LEN (MAX_USB_STRING_LEN+1) +/* vendor code */ +#define MSOS_VENDOR_CODE 0x08 +#define MSOS_GOOGLE_VENDOR_CODE 0x01 + static int usb_string_copy(const char *s, char **s_copy) { int ret; @@ -332,6 +342,14 @@ err: return ret; } +static ssize_t gadget_dev_desc_isMSOSDesc_show(struct config_item *item, + char *page) +{ + struct gadget_info *gi = to_gadget_info(item); + + return snprintf(page, 3, "%s\n", gi->isMSOSDesc ? "Y" : "N"); +} + CONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass); CONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass); CONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol); @@ -341,6 +359,7 @@ CONFIGFS_ATTR(gadget_dev_desc_, idProduct); CONFIGFS_ATTR(gadget_dev_desc_, bcdDevice); CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB); CONFIGFS_ATTR(gadget_dev_desc_, UDC); +CONFIGFS_ATTR_RO(gadget_dev_desc_, isMSOSDesc); static struct configfs_attribute *gadget_root_attrs[] = { &gadget_dev_desc_attr_bDeviceClass, @@ -352,6 +371,7 @@ static struct configfs_attribute *gadget_root_attrs[] = { &gadget_dev_desc_attr_bcdDevice, &gadget_dev_desc_attr_bcdUSB, &gadget_dev_desc_attr_UDC, + &gadget_dev_desc_attr_isMSOSDesc, NULL, }; @@ -1341,7 +1361,12 @@ static int configfs_composite_bind(struct usb_gadget *gadget, gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id; gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id; +#ifdef CONFIG_USB_ANDROID_PRODUCTION + /* Set id to 0 to comply with Sony production tools */ + gi->cdev.desc.iSerialNumber = 0; +#else gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id; +#endif } if (gi->use_os_desc) { @@ -1522,6 +1547,14 @@ static int android_setup(struct usb_gadget *gadget, if (value < 0) value = composite_setup(gadget, c); + if ((c->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { + if (((c->bRequest == MSOS_GOOGLE_VENDOR_CODE) || + (c->bRequest == MSOS_VENDOR_CODE)) && + (c->bRequestType & USB_DIR_IN) && le16_to_cpu(c->wIndex == 4)) { + gi->isMSOSDesc = true; + } + } + spin_lock_irqsave(&cdev->lock, flags); if (c->bRequest == USB_REQ_SET_CONFIGURATION && cdev->config) { @@ -1568,6 +1601,7 @@ static void android_disconnect(struct usb_gadget *gadget) if (!gi->unbinding) schedule_work(&gi->work); composite_disconnect(gadget); + gi->isMSOSDesc = false; } #endif diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index 7d8bfe62b148f05efda2de248e05c8a911a8cb83..fd10944a120bdd99a9d8fda74836e85d5c9096dc 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -13,6 +13,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -246,6 +251,7 @@ struct audio_source_config { struct audio_dev { struct usb_function func; + u8 ctrl_id; struct snd_card *card; struct snd_pcm *pcm; struct snd_pcm_substream *substream; @@ -616,9 +622,12 @@ static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) * Because the data interface supports multiple altsettings, * this audio_source function *MUST* implement a get_alt() method. */ -static int audio_get_alt(struct usb_function *f, unsigned int intf) +static int audio_get_alt(struct usb_function *f, unsigned intf) { - struct audio_dev *audio = func_to_audio(f); + struct audio_dev *audio = func_to_audio(f); + if (intf == audio->ctrl_id) { + return 0; + } return audio->in_ep->enabled ? 1 : 0; } @@ -689,6 +698,8 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) status = usb_interface_id(c, f); if (status < 0) goto fail; + audio->ctrl_id = status; + ac_interface_desc.bInterfaceNumber = status; /* AUDIO_AC_INTERFACE */ diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index ff61879767b38398860a8ddae9e73ad651a6e8ed..374bdbbb050cd54f83cfc426431284b9c65de7e6 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "f_gsi.h" #include "rndis.h" @@ -2557,6 +2562,30 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f) switch (gsi->prot_id) { case IPA_USB_RNDIS: + /* "Wireless" RNDIS6; auto-detected by Windows */ + pr_debug("%s: linux_support=%d\n", __func__, + gsi->linux_support); + if (gsi->linux_support) { + pr_info("%s: RNDIS5\n", __func__); + rndis_gsi_control_intf.bInterfaceClass = + USB_CLASS_WIRELESS_CONTROLLER; + rndis_gsi_control_intf.bInterfaceSubClass = 0x01; + rndis_gsi_control_intf.bInterfaceProtocol = 0x03; + rndis_gsi_iad_descriptor.bFunctionClass = + USB_CLASS_WIRELESS_CONTROLLER; + rndis_gsi_iad_descriptor.bFunctionSubClass = 0x01; + rndis_gsi_iad_descriptor.bFunctionProtocol = 0x03; + } else { + pr_info("%s: RNDIS6\n", __func__); + rndis_gsi_control_intf.bInterfaceClass = USB_CLASS_MISC; + rndis_gsi_control_intf.bInterfaceSubClass = 0x04; + rndis_gsi_control_intf.bInterfaceProtocol = 0x01; + rndis_gsi_iad_descriptor.bFunctionClass = + USB_CLASS_MISC; + rndis_gsi_iad_descriptor.bFunctionSubClass = 0x04; + rndis_gsi_iad_descriptor.bFunctionProtocol = 0x01; + } + info.string_defs = rndis_gsi_string_defs; info.ctrl_desc = &rndis_gsi_control_intf; info.ctrl_str_idx = 0; @@ -2944,6 +2973,8 @@ static struct f_gsi *gsi_function_init(enum ipa_usb_teth_prot prot_id) gsi->d_port.ipa_usb_wq = ipa_usb_wq; + gsi->linux_support = false; + ret = gsi_function_ctrl_port_init(gsi); if (ret) { kfree(gsi); @@ -3110,6 +3141,51 @@ static ssize_t gsi_info_show(struct config_item *item, char *page) return ret; } +static ssize_t gsi_linux_support_show(struct config_item *item, char *page) +{ + struct f_gsi *gsi = to_gsi_opts(item)->gsi; + int ret; + + switch (gsi->prot_id) { + case IPA_USB_RNDIS: + /* "Y\n\0" 3characters */ + ret = snprintf(page, 3, "%c\n", gsi->linux_support ? 'Y' : 'N'); + break; + default: + ret = EBADR; + break; + } + + return ret; +} + +static ssize_t gsi_linux_support_store(struct config_item *item, + const char *page, size_t len) +{ + struct f_gsi *gsi = to_gsi_opts(item)->gsi; + bool val; + int ret = 0; + + switch (gsi->prot_id) { + case IPA_USB_RNDIS: + ret = strtobool(page, &val); + if (ret) + break; + gsi->linux_support = val; + pr_info("%s: set linux_support=%d.\n", __func__, + gsi->linux_support); + break; + default: + ret = -EBADR; + break; + } + + if (ret) + len = ret; + return len; +} + +CONFIGFS_ATTR(gsi_, linux_support); CONFIGFS_ATTR_RO(gsi_, info); static ssize_t gsi_rndis_wceis_show(struct config_item *item, char *page) @@ -3137,6 +3213,7 @@ CONFIGFS_ATTR(gsi_, rndis_wceis); static struct configfs_attribute *gsi_rndis_attrs[] = { &gsi_attr_info, + &gsi_attr_linux_support, &gsi_attr_rndis_wceis, NULL, }; diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h index 829e1fcbe156382506996d221648e3530d3e6f4c..34c6105510116d9fd72e2cf409576bab5a246aeb 100644 --- a/drivers/usb/gadget/function/f_gsi.h +++ b/drivers/usb/gadget/function/f_gsi.h @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _F_GSI_H #define _F_GSI_H @@ -244,6 +249,7 @@ struct f_gsi { struct gsi_data_port d_port; struct gsi_ctrl_port c_port; + bool linux_support; }; static inline struct f_gsi *func_to_gsi(struct usb_function *f) diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 7edc981c78b6422012f6ef798f297ac6c4bcbb3c..d430c7f92bde48391e1adbaa2e23cd436e734942 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -36,6 +36,11 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * The Mass Storage Function acts as a USB Mass Storage device, @@ -222,6 +227,7 @@ #include "configfs.h" +#define USB_SOMC_MAC_SUPPORT /*------------------------------------------------------------------------*/ @@ -249,6 +255,8 @@ static struct usb_gadget_strings *fsg_strings_array[] = { NULL, }; +#define TOC_FORMAT2_SIZE (11 * 3 + 4) + /*-------------------------------------------------------------------------*/ struct fsg_dev; @@ -1237,6 +1245,8 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) int msf = common->cmnd[1] & 0x02; int start_track = common->cmnd[6]; u8 *buf = (u8 *)bh->buf; + u8 format; + int offset; if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ start_track > 1) { @@ -1244,18 +1254,55 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) return -EINVAL; } - memset(buf, 0, 20); - buf[1] = (20-2); /* TOC data length */ - buf[2] = 1; /* First track number */ - buf[3] = 1; /* Last track number */ - buf[5] = 0x16; /* Data track, copying allowed */ - buf[6] = 0x01; /* Only track is number 1 */ - store_cdrom_address(&buf[8], msf, 0); - - buf[13] = 0x16; /* Lead-out track is data */ - buf[14] = 0xAA; /* Lead-out track number */ - store_cdrom_address(&buf[16], msf, curlun->num_sectors); - return 20; + format = common->cmnd[2] & 0x07; + /* SFF-8020i: When Format in Byte 2 is zero, then Byte 9 is used. */ + if (format == 0) + format = (common->cmnd[9] >> 6) & 0x03; + + if (format == 0) { + memset(buf, 0, 20); + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ + store_cdrom_address(&buf[8], msf, 0); + + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ + store_cdrom_address(&buf[16], msf, curlun->num_sectors); + return 20; + } else if (format == 0x02) { + memset(buf, 0, TOC_FORMAT2_SIZE); + buf[1] = (TOC_FORMAT2_SIZE-2); /* TOC data length */ + buf[2] = 1; /* First Session Number */ + buf[3] = 1; /* Last Session Number */ + offset = 4; + + buf[offset] = 1; /* Session Number */ + buf[offset + 1] = 0x16; /* Data track, copying allowed */ + buf[offset + 2] = 0; /* TNO */ + buf[offset + 3] = 0xA0; /* Point */ + buf[offset + 8] = 1; /* First track number */ + buf[offset + 9] = 0; /* Disc Type */ + offset += 11; + + buf[offset] = 1; /* Session Number */ + buf[offset + 1] = 0x16; /* Data track, copying allowed */ + buf[offset + 2] = 0; /* TNO */ + buf[offset + 3] = 0xA1; /* Point */ + buf[offset + 8] = 1; /* Last track number */ + offset += 11; + + buf[offset] = 1; /* Session Number */ + buf[offset + 1] = 0x16; /* Data track, copying allowed */ + buf[offset + 2] = 0; /* TNO */ + buf[offset + 3] = 0xA2; /* Point */ + store_cdrom_address(&buf[offset + 7], msf, curlun->num_sectors); + return TOC_FORMAT2_SIZE; + } + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; } static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) @@ -1394,6 +1441,7 @@ static int do_start_stop(struct fsg_common *common) return 0; } +#if !defined(USB_SOMC_MAC_SUPPORT) static int do_prevent_allow(struct fsg_common *common) { struct fsg_lun *curlun = common->curlun; @@ -1417,6 +1465,7 @@ static int do_prevent_allow(struct fsg_common *common) curlun->prevent_medium_removal = prevent; return 0; } +#endif static int do_read_format_capacities(struct fsg_common *common, struct fsg_buffhd *bh) @@ -1921,6 +1970,7 @@ static int do_scsi_command(struct fsg_common *common) reply = do_mode_sense(common, bh); break; +#if !defined(USB_SOMC_MAC_SUPPORT) case ALLOW_MEDIUM_REMOVAL: common->data_size_from_cmnd = 0; reply = check_command(common, 6, DATA_DIR_NONE, @@ -1929,6 +1979,7 @@ static int do_scsi_command(struct fsg_common *common) if (reply == 0) reply = do_prevent_allow(common); break; +#endif case READ_6: i = common->cmnd[4]; @@ -1990,7 +2041,7 @@ static int do_scsi_command(struct fsg_common *common) common->data_size_from_cmnd = get_unaligned_be16(&common->cmnd[7]); reply = check_command(common, 10, DATA_DIR_TO_HOST, - (7<<6) | (1<<1), 1, + (0x0f<<6) | (1<<1), 1, "READ TOC"); if (reply == 0) reply = do_read_toc(common, bh); @@ -3062,7 +3113,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) fsg->common->can_stall); if (ret) return ret; - fsg_common_set_inquiry_string(fsg->common, NULL, NULL); + fsg_common_set_inquiry_string(fsg->common, "SONY", "CD-ROM"); } if (!common->thread_task) { @@ -3310,6 +3361,7 @@ static struct config_group *fsg_lun_make(struct config_group *group, memset(&config, 0, sizeof(config)); config.removable = true; + config.cdrom = true; ret = fsg_common_create_lun(fsg_opts->common, &config, num, name, (const char **)&group->cg_item.ci_name); @@ -3502,6 +3554,7 @@ static struct usb_function_instance *fsg_alloc_inst(void) memset(&config, 0, sizeof(config)); config.removable = true; + config.cdrom = true; rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0", (const char **)&opts->func_inst.group.cg_item.ci_name); if (rc) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 1fcdbdc35cd122bfcfba28b7e720f61b8d7c88d3..e2dcd300de84796dc1543ac52b0f044870ec3bbc 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -18,6 +18,11 @@ * * Licensed under the GPL-2 or later. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -938,6 +943,8 @@ fail_f_midi: kfree(midi_function); usb_free_descriptors(f->hs_descriptors); kfree(midi_ss_function); + if (gadget_is_superspeed(c->cdev->gadget) && f->ss_descriptors) + usb_free_descriptors(f->ss_descriptors); fail: f_midi_unregister_card(midi); fail_register: diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index d8cc5fd39e854095d3d7d98b376cb8170b88df46..dd17ecae7f744ef9bb410c05e7962fee689da634 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -14,6 +14,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* #define DEBUG */ /* #define VERBOSE_DEBUG */ @@ -58,12 +63,17 @@ #define STATE_BUSY 2 /* processing userspace calls */ #define STATE_CANCELED 3 /* transaction canceled by host */ #define STATE_ERROR 4 /* error from completion routine */ +#define STATE_RESET 5 /* reset the device */ /* number of tx and rx requests to allocate */ #define MTP_TX_REQ_MAX 8 #define RX_REQ_MAX 2 #define INTR_REQ_MAX 5 +/* vendor code */ +#define MSOS_VENDOR_CODE 0x08 +#define MSOS_GOOGLE_VENDOR_CODE 0x01 + /* ID for Microsoft MTP OS String */ #define MTP_OS_STRING_ID 0xEE @@ -201,7 +211,7 @@ static struct usb_endpoint_descriptor mtp_highspeed_in_desc = { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), + .wMaxPacketSize = cpu_to_le16(512), }; static struct usb_endpoint_descriptor mtp_highspeed_out_desc = { @@ -209,7 +219,7 @@ static struct usb_endpoint_descriptor mtp_highspeed_out_desc = { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), + .wMaxPacketSize = cpu_to_le16(512), }; static struct usb_endpoint_descriptor mtp_fullspeed_in_desc = { @@ -231,7 +241,7 @@ static struct usb_endpoint_descriptor mtp_intr_desc = { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(INTR_BUFFER_SIZE), + .wMaxPacketSize = cpu_to_le16(INTR_BUFFER_SIZE), .bInterval = 6, }; @@ -322,7 +332,7 @@ static u8 mtp_os_string[] = { /* Signature field: "MSFT100" */ 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, /* vendor code */ - 1, + MSOS_GOOGLE_VENDOR_CODE, /* padding */ 0 }; @@ -347,27 +357,13 @@ struct mtp_ext_config_desc_function { /* MTP Extended Configuration Descriptor */ struct ext_mtp_desc { - struct mtp_ext_config_desc_header header; + struct mtp_ext_config_desc_header header; struct mtp_ext_config_desc_function function; }; -struct ext_mtp_desc mtp_ext_config_desc = { - .header = { - .dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)), - .bcdVersion = __constant_cpu_to_le16(0x0100), - .wIndex = __constant_cpu_to_le16(4), - .bCount = 1, - }, - .function = { - .bFirstInterfaceNumber = 0, - .bInterfaceCount = 1, - .compatibleID = { 'M', 'T', 'P' }, - }, -}; - struct ext_mtp_desc ptp_ext_config_desc = { .header = { - .dwLength = cpu_to_le32(sizeof(mtp_ext_config_desc)), + .dwLength = cpu_to_le32(sizeof(ptp_ext_config_desc)), .bcdVersion = cpu_to_le16(0x0100), .wIndex = cpu_to_le16(4), .bCount = cpu_to_le16(1), @@ -497,7 +493,7 @@ static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req) struct mtp_dev *dev = _mtp_dev; dev->rx_done = 1; - if (req->status != 0 && dev->state != STATE_OFFLINE) + if (req->status != 0 && dev->state != STATE_BUSY) dev->state = STATE_ERROR; wake_up(&dev->read_wq); @@ -639,6 +635,12 @@ static ssize_t mtp_read(struct file *fp, char __user *buf, dev->state = STATE_READY; spin_unlock_irq(&dev->lock); return -ECANCELED; + } else if (dev->state == STATE_RESET) { + /* report a reset state to userspace */ + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + DBG(cdev, "mtp_read DEVICE RESET. State: %d.\n", dev->state); + return -ECONNRESET; } dev->state = STATE_BUSY; spin_unlock_irq(&dev->lock); @@ -675,7 +677,7 @@ requeue_req: spin_unlock_irq(&dev->lock); goto done; } - if (ret < 0) { + if (ret < 0 || !dev->rx_done) { r = ret; usb_ep_dequeue(dev->ep_out, req); goto done; @@ -699,6 +701,8 @@ done: spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) r = -ECANCELED; + else if (dev->state == STATE_RESET) + r = -ECONNRESET; else if (dev->state != STATE_OFFLINE) dev->state = STATE_READY; spin_unlock_irq(&dev->lock); @@ -823,6 +827,11 @@ static void send_file_work(struct work_struct *data) offset = dev->xfer_file_offset; count = dev->xfer_file_length; + if (count < 0) { + dev->xfer_result = -EINVAL; + return; + } + DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); if (dev->xfer_send_header) { @@ -939,6 +948,11 @@ static void receive_file_work(struct work_struct *data) offset = dev->xfer_file_offset; count = dev->xfer_file_length; + if (count < 0) { + dev->xfer_result = -EINVAL; + return; + } + DBG(cdev, "receive_file_work(%lld)\n", count); if (!IS_ALIGNED(count, dev->ep_out->maxpacket)) DBG(cdev, "%s- count(%lld) not multiple of mtu(%d)\n", __func__, @@ -1016,6 +1030,21 @@ static void receive_file_work(struct work_struct *data) r = read_req->status; break; } + if (dev->state == STATE_OFFLINE) { + r = -ENODEV; + if (!dev->rx_done) + usb_ep_dequeue(dev->ep_out, read_req); + break; + } + if (dev->state == STATE_RESET) { + DBG(cdev, "%s: DEVICE RESET\n", __func__); + r = -ECONNRESET; + if (!dev->rx_done) { + DBG(cdev, "dequeue in DEVICE RESET\n"); + usb_ep_dequeue(dev->ep_out, read_req); + } + break; + } mutex_lock(&dev->read_mutex); if (dev->state == STATE_OFFLINE) { @@ -1105,6 +1134,14 @@ static long mtp_send_receive_ioctl(struct file *fp, unsigned code, ret = -ECANCELED; goto out; } + if (dev->state == STATE_RESET) { + /* report reset to userspace */ + DBG(dev->cdev, "report reset to user space\n"); + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + ret = -ECONNRESET; + goto out; + } if (dev->state == STATE_OFFLINE) { spin_unlock_irq(&dev->lock); ret = -ENODEV; @@ -1156,6 +1193,8 @@ fail: spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) ret = -ECANCELED; + else if (dev->state == STATE_RESET) + ret = -ECONNRESET; else if (dev->state != STATE_OFFLINE) dev->state = STATE_READY; spin_unlock_irq(&dev->lock); @@ -1344,26 +1383,64 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev, DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n", ctrl->bRequest, w_index, w_value, w_length); - if (ctrl->bRequest == 1 - && (ctrl->bRequestType & USB_DIR_IN) - && (w_index == 4 || w_index == 5)) { - if (!dev->is_ptp) { - value = (w_length < - sizeof(mtp_ext_config_desc) ? - w_length : - sizeof(mtp_ext_config_desc)); - memcpy(cdev->req->buf, &mtp_ext_config_desc, - value); - } else { - value = (w_length < - sizeof(ptp_ext_config_desc) ? - w_length : - sizeof(ptp_ext_config_desc)); - memcpy(cdev->req->buf, &ptp_ext_config_desc, - value); + if (((ctrl->bRequest == MSOS_GOOGLE_VENDOR_CODE) || + (ctrl->bRequest == MSOS_VENDOR_CODE)) && + (ctrl->bRequestType & USB_DIR_IN) && (w_index == 4)) { + + int total = 0; + int func_num = 0; + int interface_num = 0; + struct mtp_ext_config_desc_header *head; + struct mtp_ext_config_desc_function *func; + struct usb_configuration *cfg; + struct usb_function *f; + + head = (struct mtp_ext_config_desc_header *) + cdev->req->buf; + func = (struct mtp_ext_config_desc_function *) + (head + 1); + + /* zero clear */ + memset(cdev->req->buf, 0x00, USB_COMP_EP0_BUFSIZ); + + list_for_each_entry(cfg, &cdev->configs, list) { + + list_for_each_entry(f, &cfg->functions, list) { + if (!f) + break; + + interface_num++; + func->bFirstInterfaceNumber = func_num; + func->bInterfaceCount = 1; + if (!strncmp(f->name, "mtp", 3)) { + memcpy(func->compatibleID, + "MTP", 3); + VDBG(cdev, + "MTP interface found. Interface_num: %d.\n", + interface_num); + } + func++; + func_num++; + } + } + + total = sizeof(*head) + (sizeof(*func) * func_num); + + /* header section */ + if (w_length < total && + w_length >= (sizeof(*head) + sizeof(*func))) { + total = w_length; + func_num = (total - sizeof(*head)) / + sizeof(*func); } + head->dwLength = total; + head->bcdVersion = cpu_to_le16(0x0100); + head->wIndex = cpu_to_le16(4); + head->bCount = func_num; + value = min_t(u16, w_length, total); } - } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { + } + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { DBG(cdev, "class request: %d index: %d value: %d length: %d\n", ctrl->bRequest, w_index, w_value, w_length); @@ -1379,6 +1456,23 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev, } spin_unlock_irqrestore(&dev->lock, flags); + /* We need to queue a request to read the remaining + * bytes, but we don't actually need to look at + * the contents. + */ + value = w_length; + } else if (ctrl->bRequest == MTP_REQ_RESET && w_index == 0 + && w_value == 0) { + DBG(cdev, "MTP_REQ_RESET\n"); + + spin_lock_irqsave(&dev->lock, flags); + /* Flushing the buffers as mentioned in MTP spec */ + usb_ep_fifo_flush(dev->ep_out); + dev->state = STATE_RESET; + wake_up(&dev->read_wq); + wake_up(&dev->write_wq); + spin_unlock_irqrestore(&dev->lock, flags); + /* We need to queue a request to read the remaining * bytes, but we don't actually need to look at * the contents. @@ -1399,6 +1493,9 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev, if (dev->state == STATE_CANCELED) status->wCode = __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY); + else if (dev->state == STATE_RESET) + status->wCode = + __cpu_to_le16(MTP_RESPONSE_OK); else status->wCode = __cpu_to_le16(MTP_RESPONSE_OK); diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c index d62683017cf3c1c052f78eb243799e1ca46ca8d5..53bbb99a3d9293583d2266819963c925988b11d1 100644 --- a/drivers/usb/gadget/function/storage_common.c +++ b/drivers/usb/gadget/function/storage_common.c @@ -10,6 +10,11 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * This file requires the following identifiers used in USB strings to @@ -301,7 +306,6 @@ 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; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 95e72d75e0a0aa0c8d9ad997f6876d0fc6af8174..3b843ca018ca230563bb4ea663ba3a42c86e1a4c 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -787,3 +787,10 @@ config USB_HCD_TEST_MODE This option is of interest only to developers who need to validate their USB hardware designs. It is not needed for normal use. If unsure, say N. + +config USB_HOST_EXTRA_NOTIFICATION + bool "USB host extra notification" + depends on USB + default n + help + Provides the functions to notify extra USB host related events. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index e7558abc994d76be29a347ed6904bca36d3765de..24e95233466a7dcc29b05a1b125837d0fef68898 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -76,3 +76,4 @@ obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o +obj-$(CONFIG_USB_HOST_EXTRA_NOTIFICATION) += host_ext_event.o diff --git a/drivers/usb/host/host_ext_event.c b/drivers/usb/host/host_ext_event.c new file mode 100644 index 0000000000000000000000000000000000000000..df78d03edda813507901d519b7bf104730d65870 --- /dev/null +++ b/drivers/usb/host/host_ext_event.c @@ -0,0 +1,171 @@ + /* drivers/usb/host/host_ext_event.c + * + * USB host event handling function + * + * 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. + */ +/* + * Copyright (C) 2013 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_NAME_SIZE 32 +#define MAX_MODULE_NAME_SIZE 32 +#define MAX_EVENT_STRING_SIZE 35 + +struct host_ext_event_drv { + struct class *class; + struct device *dev; + char name[MAX_NAME_SIZE + 1]; + int event; +}; + +static DEFINE_SEMAPHORE(sem); + +static struct host_ext_event_drv *host_ext_event; + +static const char *event_string(enum usb_host_ext_event event) +{ + switch (event) { + case USB_HOST_EXT_EVENT_NONE: + return "USB_HOST_NONE"; + case USB_HOST_EXT_EVENT_VBUS_DROP: + return "USB_HOST_VBUS_DROP"; + case USB_HOST_EXT_EVENT_INSUFFICIENT_POWER: + return "USB_HOST_INSUFFICIENT_POWER"; + default: + return "UNDEFINED"; + } +} + +int host_send_uevent(enum usb_host_ext_event event) +{ + struct host_ext_event_drv *dev = host_ext_event; + + char udev_event[MAX_EVENT_STRING_SIZE]; + char module[MAX_MODULE_NAME_SIZE]; + char *envp[] = {module, udev_event, NULL}; + int ret; + + if (dev == NULL) + return -ENODEV; + + ret = down_interruptible(&sem); + if (ret < 0) + return ret; + + pr_info("%s: sending %s event\n", dev->name, event_string(event)); + + snprintf(udev_event, MAX_EVENT_STRING_SIZE, "EVENT=%s", + event_string(event)); + snprintf(module, MAX_MODULE_NAME_SIZE, "MODULE=%s", dev->name); + ret = kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, envp); + + dev->event = event; + + up(&sem); + + if (ret < 0) + pr_info("uevent sending failed with ret = %d\n", ret); + + return ret; +} + +static int usb_host_ext_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + int ret; + + struct host_ext_event_drv *udev = host_ext_event; + + if (udev == NULL) + return -ENODEV; + + ret = add_uevent_var(env, "%d", udev->event); + if (ret) { + dev_err(dev, "failed to add usb host ext uevent\n"); + return ret; + } + + return 0; +} + +static int __init host_ext_event_driver_register(void) +{ + struct host_ext_event_drv *dev; + int ret; + + if (host_ext_event) + return -EBUSY; + + dev = kzalloc(sizeof(struct host_ext_event_drv), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + host_ext_event = dev; + + dev->class = class_create(THIS_MODULE, "usb_host_ext_event"); + if (IS_ERR(dev->class)) { + pr_info("%s :failed to create class", __func__); + ret = PTR_ERR(dev->class); + goto class_create_fail; + } + + dev->class->dev_uevent = usb_host_ext_uevent; + + dev->event = USB_HOST_EXT_EVENT_NONE; + + strlcpy(dev->name, "usb_host_ext_event", MAX_NAME_SIZE); + + dev->dev = device_create(dev->class, NULL, MKDEV(0, 0), NULL, + dev->name); + if (IS_ERR(dev->dev)) { + pr_info("%s :failed to create device", __func__); + ret = PTR_ERR(dev->dev); + goto device_create_fail; + } + + pr_info("usb_host_ext_event has been registered!"); + + return 0; + +device_create_fail: + class_destroy(dev->class); +class_create_fail: + kfree(dev); + host_ext_event = NULL; + return ret; +} + +static void __exit host_ext_event_driver_unregister(void) +{ + struct host_ext_event_drv *dev = host_ext_event; + + down(&sem); + + device_destroy(dev->class, MKDEV(0, 0)); + class_destroy(dev->class); + kfree(dev); + host_ext_event = NULL; + + up(&sem); +} + +module_init(host_ext_event_driver_register); +module_exit(host_ext_event_driver_unregister); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Extra event notifier of USB host"); diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index efe1924e08754cd40ffde33936e457b5750ea891..41a7c99f529754120dba7610b722262ccc743022 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -10,6 +10,11 @@ * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index dfc02a6c6d55eb87848e86bb1d49f0dca3c4d807..35f4980087adfb9c3fc0976be3a93a76cd78184e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -19,6 +19,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 72beaa47c15b857176d65e957f70329d6cfff511..ea07fef24f3b138056f3e4d3e3df908719bf9d30 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -20,6 +20,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __LINUX_XHCI_HCD_H #define __LINUX_XHCI_HCD_H diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index d9508dba2f83cf76da6c35b487890e68f5521056..343b672830e5199c6152c7d0fa405dc2d26f878b 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -27,6 +32,7 @@ #include #include #include +#include #include "usbpd.h" /* To start USB stack for USB3.1 complaince testing */ @@ -212,7 +218,11 @@ static void *usbpd_ipc_log; #define PS_HARD_RESET_TIME 25 #define PS_SOURCE_ON 400 #define PS_SOURCE_OFF 750 +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define FIRST_SOURCE_CAP_TIME 50 +#else #define FIRST_SOURCE_CAP_TIME 200 +#endif #define VDM_BUSY_TIME 50 #define VCONN_ON_TIME 100 @@ -238,6 +248,10 @@ static void *usbpd_ipc_log; #define PD_MSG_HDR(type, dr, pr, id, cnt, rev) \ (((type) & 0x1F) | ((dr) << 5) | (rev << 6) | \ ((pr) << 8) | ((id) << 9) | ((cnt) << 12)) +#define USB_VBUS_WAIT_VOLT 900 /* mV */ +#define USB_VBUS_WAIT_ITVL 5 /* mS */ +#define USB_VBUS_WAIT_TMOUT 200 /* mS */ + #define PD_MSG_HDR_COUNT(hdr) (((hdr) >> 12) & 7) #define PD_MSG_HDR_TYPE(hdr) ((hdr) & 0x1F) #define PD_MSG_HDR_ID(hdr) (((hdr) >> 9) & 7) @@ -327,6 +341,12 @@ static void *usbpd_ipc_log; #define ID_HDR_PRODUCT_AMA 5 #define ID_HDR_VID 0x05c6 /* qcom */ #define PROD_VDO_PID 0x0a00 /* TBD */ +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#undef ID_HDR_VID +#undef PROD_VDO_PID +#define ID_HDR_VID 0x0FCE /* Sony Mobile Communications */ +#define PROD_VDO_PID 0x01F3 +#endif static bool check_vsafe0v = true; module_param(check_vsafe0v, bool, S_IRUSR | S_IWUSR); @@ -334,7 +354,7 @@ module_param(check_vsafe0v, bool, S_IRUSR | S_IWUSR); static int min_sink_current = 900; module_param(min_sink_current, int, S_IRUSR | S_IWUSR); -static const u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */ +static const u32 default_src_caps[] = { 0x3601905A }; /* VSafe5V @ 0.9A */ static const u32 default_snk_caps[] = { 0x2601912C }; /* VSafe5V @ 3A */ struct vdm_tx { @@ -376,6 +396,9 @@ struct usbpd { u32 received_pdos[PD_MAX_DATA_OBJ]; u32 received_ado; u16 src_cap_id; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + u32 org_received_pdos[7]; +#endif u8 selected_pdo; u8 requested_pdo; u32 rdo; /* can be either source or sink */ @@ -456,12 +479,28 @@ static const unsigned int usbpd_extcon_cable[] = { EXTCON_USB_HOST, EXTCON_USB_CC, EXTCON_USB_SPEED, + EXTCON_VBUS_DROP, EXTCON_NONE, }; /* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */ static const u32 usbpd_extcon_exclusive[] = {0x3, 0}; +/** + * usbpd_ocp_notification - ocp notification callback from regulator. + * @ctxt: Pointer to the dwc3_msm context + * + * NOTE: This can be called in interrupt context. + */ +static void usbpd_ocp_notification(void *ctxt) +{ + struct usbpd *pd = (struct usbpd *)ctxt; + + extcon_set_cable_state_(pd->extcon, EXTCON_VBUS_DROP, 1); + extcon_set_cable_state_(pd->extcon, EXTCON_VBUS_DROP, 0); + pr_info("%s: receive ocp notification\n", __func__); +} + enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd) { int ret; @@ -676,11 +715,36 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua) return 0; } +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define ACCEPTABLE_SRC_VOLTAGE_9V 9000 +#endif static int pd_eval_src_caps(struct usbpd *pd) { int i; union power_supply_propval val; u32 first_pdo = pd->received_pdos[0]; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + u32 *pdo = pd->received_pdos; + u32 *org_pdo = pd->org_received_pdos; + + for (i = 0; i < ARRAY_SIZE(pd->received_pdos); i++) + org_pdo[i] = pdo[i]; + + for (i = 1; i < ARRAY_SIZE(pd->received_pdos); i++) { + if (PD_SRC_PDO_TYPE(pdo[i]) == PD_SRC_PDO_TYPE_FIXED && + PD_SRC_PDO_FIXED_VOLTAGE(pdo[i]) * 50 <= + ACCEPTABLE_SRC_VOLTAGE_9V) + /* This PDO is acceptable */ + ; + else + break; + } + + usbpd_dbg(&pd->dev, "Clear PDOs from %d to %d\n", + i + 1, (int)ARRAY_SIZE(pd->received_pdos)); + for (; i < ARRAY_SIZE(pd->received_pdos); i++) + pdo[i] = 0; +#endif if (PD_SRC_PDO_TYPE(first_pdo) != PD_SRC_PDO_TYPE_FIXED) { usbpd_err(&pd->dev, "First src_cap invalid! %08x\n", first_pdo); @@ -983,9 +1047,41 @@ static void phy_msg_received(struct usbpd *pd, enum pd_sop_type sop, kick_sm(pd, 0); } +static void phy_wait_vbus_settled_down(struct usbpd *pd, int mv, + int itvl, int tmout) +{ + int rc; + int cnt = 0; + int usbin = mv; + + do { + union power_supply_propval pval; + + msleep(itvl); + rc = power_supply_get_property(pd->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); + if (IS_ERR_VALUE(rc)) + goto waitremain; + usbin = pval.intval / 1000; + cnt++; + } while (usbin >= mv && tmout > (cnt * itvl)); +waitremain: + if (usbin >= mv && tmout > (cnt * itvl)) + msleep(tmout - (cnt * itvl)); +} + + static void phy_shutdown(struct usbpd *pd) { usbpd_dbg(&pd->dev, "shutdown"); + + if (regulator_is_enabled(pd->vbus)) { + usbpd_info(&pd->dev, "turn off VBUS"); + regulator_disable(pd->vbus); + phy_wait_vbus_settled_down(pd, + USB_VBUS_WAIT_VOLT, USB_VBUS_WAIT_ITVL, + USB_VBUS_WAIT_TMOUT); + } } static enum hrtimer_restart pd_timeout(struct hrtimer *timer) @@ -1916,6 +2012,10 @@ static void usbpd_sm(struct work_struct *w) pd->requested_current = 0; pd->selected_pdo = pd->requested_pdo = 0; memset(&pd->received_pdos, 0, sizeof(pd->received_pdos)); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + memset(&pd->org_received_pdos, 0, + sizeof(pd->org_received_pdos)); +#endif rx_msg_cleanup(pd); power_supply_set_property(pd->usb_psy, @@ -1960,6 +2060,11 @@ static void usbpd_sm(struct work_struct *w) /* Set CC back to DRP toggle */ val.intval = POWER_SUPPLY_TYPEC_PR_DUAL; +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + /* special prop with delay in case of SINK via syscall */ + if (val.intval == POWER_SUPPLY_TYPEC_PR_SINK) + val.intval = POWER_SUPPLY_TYPEC_PR_SINK_DELAY; +#endif power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); pd->forced_pr = POWER_SUPPLY_TYPEC_PR_NONE; @@ -3584,6 +3689,82 @@ static ssize_t hard_reset_store(struct device *dev, } static DEVICE_ATTR_WO(hard_reset); +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +static ssize_t org_pdo_h_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usbpd *pd = dev_get_drvdata(dev); + int i; + ssize_t cnt = 0; + + for (i = 0; i < ARRAY_SIZE(pd->org_received_pdos); i++) { + u32 pdo = pd->org_received_pdos[i]; + + if (pdo == 0) + break; + + cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, "PDO %d\n", i + 1); + + if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_FIXED) { + cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, + "\tFixed supply\n" + "\tDual-Role Power:%d\n" + "\tUSB Suspend Supported:%d\n" + "\tExternally Powered:%d\n" + "\tUSB Communications Capable:%d\n" + "\tData Role Swap:%d\n" + "\tPeak Current:%d\n" + "\tVoltage:%d (mV)\n" + "\tMax Current:%d (mA)\n", + PD_SRC_PDO_FIXED_PR_SWAP(pdo), + PD_SRC_PDO_FIXED_USB_SUSP(pdo), + PD_SRC_PDO_FIXED_EXT_POWERED(pdo), + PD_SRC_PDO_FIXED_USB_COMM(pdo), + PD_SRC_PDO_FIXED_DR_SWAP(pdo), + PD_SRC_PDO_FIXED_PEAK_CURR(pdo), + PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50, + PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10); + } else if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_BATTERY) { + cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, + "\tBattery supply\n" + "\tMax Voltage:%d (mV)\n" + "\tMin Voltage:%d (mV)\n" + "\tMax Power:%d (mW)\n", + PD_SRC_PDO_VAR_BATT_MAX_VOLT(pdo) * 50, + PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo) * 50, + PD_SRC_PDO_VAR_BATT_MAX(pdo) * 250); + } else if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_VARIABLE) { + cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, + "\tVariable supply\n" + "\tMax Voltage:%d (mV)\n" + "\tMin Voltage:%d (mV)\n" + "\tMax Current:%d (mA)\n", + PD_SRC_PDO_VAR_BATT_MAX_VOLT(pdo) * 50, + PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo) * 50, + PD_SRC_PDO_VAR_BATT_MAX(pdo) * 10); + } else if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_AUGMENTED) { + cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, + "\tProgrammable Power supply\n" + "\tMax Voltage:%d (mV)\n" + "\tMin Voltage:%d (mV)\n" + "\tMax Current:%d (mA)\n", + PD_APDO_MAX_VOLT(pdo) * 100, + PD_APDO_MIN_VOLT(pdo) * 100, + PD_APDO_MAX_CURR(pdo) * 50); + } else { + cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, + "Invalid PDO\n"); + } + + buf[cnt++] = '\n'; + } + + return cnt; +} +static DEVICE_ATTR_RO(org_pdo_h); + +#endif + static int trigger_tx_msg(struct usbpd *pd, bool *msg_tx_flag) { int ret = 0; @@ -3594,7 +3775,6 @@ static int trigger_tx_msg(struct usbpd *pd, bool *msg_tx_flag) ret = -EBUSY; goto out; } - reinit_completion(&pd->is_ready); *msg_tx_flag = true; kick_sm(pd, 0); @@ -3653,7 +3833,6 @@ static ssize_t rx_ado_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usbpd *pd = dev_get_drvdata(dev); - /* dump the ADO as a hex string */ return snprintf(buf, PAGE_SIZE, "%08x\n", pd->received_ado); } @@ -3747,6 +3926,9 @@ static struct attribute *usbpd_attrs[] = { &dev_attr_rx_ado.attr, &dev_attr_get_battery_cap.attr, &dev_attr_get_battery_status.attr, +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) + &dev_attr_org_pdo_h.attr, +#endif NULL, }; ATTRIBUTE_GROUPS(usbpd); @@ -3833,6 +4015,7 @@ struct usbpd *usbpd_create(struct device *parent) { int ret; struct usbpd *pd; + struct regulator_ocp_notification ocp_ntf; pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) @@ -3896,6 +4079,11 @@ struct usbpd *usbpd_create(struct device *parent) goto put_psy; } + ocp_ntf.notify = usbpd_ocp_notification; + ocp_ntf.ctxt = pd; + ret = regulator_register_ocp_notification(pd->vbus, &ocp_ntf); + + pd->vconn = devm_regulator_get(parent, "vconn"); if (IS_ERR(pd->vconn)) { ret = PTR_ERR(pd->vconn); diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c index 51fcb545a9d578589da728c488643d6887f75008..09dea2dea86abfb352b1748a7d5fc362a3b196ce 100644 --- a/drivers/usb/phy/class-dual-role.c +++ b/drivers/usb/phy/class-dual-role.c @@ -13,6 +13,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -413,7 +418,7 @@ static umode_t dual_role_attr_is_visible(struct kobject *kobj, if (dual_role->desc->property_is_writeable && dual_role_property_is_writeable(dual_role, property) > 0) - mode |= S_IWUSR; + mode |= S_IWUSR | S_IWGRP; return mode; } diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 0c912d3950a5f177b5a0e0900b29b18074ca8fb8..39a990313d991d32bbf84a021fc8ff15678ba33c 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -21,6 +21,11 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c index e5f38e42e165738e6bfb2dea3c2c386b541c527d..336f8ad07aecc6b52657e5a1bfa42046c1b0fbb7 100644 --- a/drivers/usb/phy/phy-msm-qusb-v2.c +++ b/drivers/usb/phy/phy-msm-qusb-v2.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -51,6 +56,9 @@ #define QUSB2PHY_PORT_TUNE1 0x23c #define QUSB2PHY_TEST1 0x24C +#define QUSB2PHY_PORT_TUNE2 0x240 +#define QUSB2PHY_PORT_TUNE3 0x244 +#define QUSB2PHY_PORT_TUNE4 0x248 #define QUSB2PHY_1P2_VOL_MIN 1200000 /* uV */ #define QUSB2PHY_1P2_VOL_MAX 1200000 /* uV */ @@ -70,9 +78,39 @@ #define QUSB2PHY_PLL_ANALOG_CONTROLS_ONE 0x0 #define QUSB2PHY_PLL_ANALOG_CONTROLS_TWO 0x4 +#define USB_PHY_HSTX_TRIM 0xF0 /* TUNE1 7:4 */ +#define USB_PHY_HSTX_SR 0x0C /* TUNE1 3:2 */ +#define USB_PHY_HSTX_SR_BIAS 0x03 /* TUNE1 1:0 */ +#define USB_PHY_SEL_EMPH_HALF_WIDTH 0x10 /* TUNE2 4 */ +#define USB_PHY_EN_EMPHASIS 0x0C /* TUNE2 3:2 */ +#define USB_PHY_HS_DISCON_TRIM 0x03 /* TUNE2 1:0 */ +#define USB_PHY_CDR_WIDE 0x80 /* TUNE3 7 */ +#define USB_PHY_CDR_PULSE_SMPL 0x40 /* TUNE3 6 */ +#define USB_PHY_TX2RX_DLY 0x3F /* TUNE3 5:0 */ +#define USB_PHY_HANDOFF_PHSEL 0xE0 /* TUNE4 7:5 */ +#define USB_PHY_FORCE_HSRX_ALWAYS_ON 0x10 /* TUNE4 4 */ +#define USB_PHY_SQ_FILTER_DIS 0x08 /* TUNE4 3 */ +#define USB_PHY_SQ_LEVEL 0x07 /* TUNE4 2:0 */ + unsigned int phy_tune1; +unsigned int phy_tune2; +unsigned int phy_tune3; +unsigned int phy_tune4; +unsigned int phy_host_tune1; +unsigned int phy_host_tune2; module_param(phy_tune1, uint, S_IRUGO | S_IWUSR); +module_param(phy_tune2, uint, S_IRUGO | S_IWUSR); +module_param(phy_tune3, uint, S_IRUGO | S_IWUSR); +module_param(phy_tune4, uint, S_IRUGO | S_IWUSR); +module_param(phy_host_tune1, uint, S_IRUGO | S_IWUSR); +module_param(phy_host_tune2, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(phy_tune1, "QUSB PHY v2 TUNE1"); +MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2"); +MODULE_PARM_DESC(phy_tune3, "QUSB PHY v2 TUNE3"); +MODULE_PARM_DESC(phy_tune4, "QUSB PHY v2 TUNE4"); +MODULE_PARM_DESC(phy_host_tune1, "QUSB PHY HOST v2 TUNE1"); +MODULE_PARM_DESC(phy_host_tune2, "QUSB PHY HOST v2 TUNE2"); + struct qusb_phy { struct usb_phy phy; @@ -98,8 +136,10 @@ struct qusb_phy { int *qusb_phy_host_init_seq; u32 tune_val; + u32 host_tune_val; int efuse_bit_pos; int efuse_num_of_bits; + u32 efuse_offset; int power_enabled_ref; bool clocks_enabled; @@ -380,14 +420,16 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) return ret; } -static void qusb_phy_get_tune1_param(struct qusb_phy *qphy) +static u32 qusb_phy_get_tune1_param(struct qusb_phy *qphy) { u8 reg; u32 bit_mask = 1; + u32 tune_val; - pr_debug("%s(): num_of_bits:%d bit_pos:%d\n", __func__, + pr_debug("%s(): num_of_bits:%d bit_pos:%d offset:%d\n", __func__, qphy->efuse_num_of_bits, - qphy->efuse_bit_pos); + qphy->efuse_bit_pos, + qphy->efuse_offset); /* get bit mask based on number of bits to use with efuse reg */ bit_mask = (bit_mask << qphy->efuse_num_of_bits) - 1; @@ -396,18 +438,22 @@ static void qusb_phy_get_tune1_param(struct qusb_phy *qphy) * if efuse reg is updated (i.e non-zero) then use it to program * tune parameters */ - qphy->tune_val = readl_relaxed(qphy->efuse_reg); + tune_val = readl_relaxed(qphy->efuse_reg); pr_debug("%s(): bit_mask:%d efuse based tune1 value:%d\n", - __func__, bit_mask, qphy->tune_val); + __func__, bit_mask, tune_val); - qphy->tune_val = TUNE_VAL_MASK(qphy->tune_val, - qphy->efuse_bit_pos, bit_mask); + tune_val = TUNE_VAL_MASK(tune_val, qphy->efuse_bit_pos, bit_mask); reg = readb_relaxed(qphy->base + QUSB2PHY_PORT_TUNE1); - if (qphy->tune_val) { + if (tune_val) { + tune_val += qphy->efuse_offset; + if ((s32)tune_val < 0) + tune_val = 0x00; + else if ((s32)tune_val > 0x0f) + tune_val = 0x0f; reg = reg & 0x0f; - reg |= (qphy->tune_val << 4); + reg |= (tune_val << 4); } - qphy->tune_val = reg; + return reg; } static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt, @@ -424,6 +470,75 @@ static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt, } } +static inline u32 msm_usb_read_reg_field(void *base, u32 offset, const u32 mask) +{ + u32 shift = find_first_bit((void *)&mask, 32); + u32 val = readb_relaxed(base + offset); + val &= mask; /* clear other bits */ + val >>= shift; + return val; +} + +static void msm_qphy_param_output(struct usb_phy *phy) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + + /* PORT_TUNE1 */ + dev_dbg(phy->dev, "PORT_TUNE1:0x%02x\n", + readb_relaxed(qphy->base + QUSB2PHY_PORT_TUNE1)); + dev_dbg(phy->dev, " :HSTX_TRIM \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE1, USB_PHY_HSTX_TRIM)); + dev_dbg(phy->dev, " :HSTX_SR \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE1, USB_PHY_HSTX_SR)); + dev_dbg(phy->dev, " :HSTX_SR_BIAS \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE1, USB_PHY_HSTX_SR_BIAS)); + + /* PORT_TUNE2 */ + dev_dbg(phy->dev, "PORT_TUNE2:0x%02x\n", + readb_relaxed(qphy->base + QUSB2PHY_PORT_TUNE2)); + dev_dbg(phy->dev, " :SEL_EMPH_HALF_WIDTH \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE2, USB_PHY_SEL_EMPH_HALF_WIDTH)); + dev_dbg(phy->dev, " :EN_EMPHASIS \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE2, USB_PHY_EN_EMPHASIS)); + dev_dbg(phy->dev, " :HS_DISCON_TRIM \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE2, USB_PHY_HS_DISCON_TRIM)); + + /* PORT_TUNE3 */ + dev_dbg(phy->dev, "PORT_TUNE3:0x%02x\n", + readb_relaxed(qphy->base + QUSB2PHY_PORT_TUNE3)); + dev_dbg(phy->dev, " :CDR_WIDE \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE3, USB_PHY_CDR_WIDE)); + dev_dbg(phy->dev, " :CDR_PULSE_SMPL \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE3, USB_PHY_CDR_PULSE_SMPL)); + dev_dbg(phy->dev, " :TX2RX_DLY \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE3, USB_PHY_TX2RX_DLY)); + + /* PORT_TUNE4 */ + dev_dbg(phy->dev, "PORT_TUNE4:0x%02x\n", + readb_relaxed(qphy->base + QUSB2PHY_PORT_TUNE4)); + dev_dbg(phy->dev, " :HANDOFF_PHSEL \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE4, USB_PHY_HANDOFF_PHSEL)); + dev_dbg(phy->dev, " :FORCE_HSRX_ALWAYS_ON \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE4, USB_PHY_FORCE_HSRX_ALWAYS_ON)); + dev_dbg(phy->dev, " :SQ_FILTER_DIS \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE4, USB_PHY_SQ_FILTER_DIS)); + dev_dbg(phy->dev, " :SQ_LEVEL \t0x%02x\n", + msm_usb_read_reg_field(qphy->base, + QUSB2PHY_PORT_TUNE4, USB_PHY_SQ_LEVEL)); +} + static void qusb_phy_host_init(struct usb_phy *phy) { u8 reg; @@ -444,6 +559,32 @@ static void qusb_phy_host_init(struct usb_phy *phy) qusb_phy_write_seq(qphy->base, qphy->qusb_phy_host_init_seq, qphy->host_init_seq_len, 0); + if (qphy->efuse_reg) { + if (!qphy->host_tune_val) + qphy->host_tune_val = qusb_phy_get_tune1_param(qphy); + + pr_debug("%s(): Programming host TUNE1 parameter as:%x\n", + __func__, qphy->host_tune_val); + writel_relaxed(qphy->host_tune_val, + qphy->base + QUSB2PHY_PORT_TUNE1); + } + + /* If phy_tune1 modparam set, override tune1 value */ + if (phy_host_tune1) { + pr_debug("%s(): (modparam) HOST_TUNE1 val:0x%02x\n", + __func__, phy_host_tune1); + writel_relaxed(phy_host_tune1, + qphy->base + QUSB2PHY_PORT_TUNE1); + } + /* If phy_tune2 modparam set, override tune2 value */ + if (phy_host_tune2) { + pr_debug("%s(): (modparam) HOST TUNE2 val:0x%02x\n", + __func__, phy_host_tune2); + writel_relaxed(phy_host_tune2, + qphy->base + QUSB2PHY_PORT_TUNE2); + } + msm_qphy_param_output(phy); + /* Ensure above write is completed before turning ON ref clk */ wmb(); @@ -520,7 +661,7 @@ static int qusb_phy_init(struct usb_phy *phy) qphy->init_seq_len, 0); if (qphy->efuse_reg) { if (!qphy->tune_val) - qusb_phy_get_tune1_param(qphy); + qphy->tune_val = qusb_phy_get_tune1_param(qphy); pr_debug("%s(): Programming TUNE1 parameter as:%x\n", __func__, qphy->tune_val); @@ -535,6 +676,28 @@ static int qusb_phy_init(struct usb_phy *phy) writel_relaxed(phy_tune1, qphy->base + QUSB2PHY_PORT_TUNE1); } + /* If phy_tune2 modparam set, override tune2 value */ + if (phy_tune2) { + pr_debug("%s(): (modparam) TUNE2 val:0x%02x\n", + __func__, phy_tune2); + writel_relaxed(phy_tune2, + qphy->base + QUSB2PHY_PORT_TUNE2); + } + /* If phy_tune3 modparam set, override tune3 value */ + if (phy_tune3) { + pr_debug("%s(): (modparam) TUNE3 val:0x%02x\n", + __func__, phy_tune3); + writel_relaxed(phy_tune3, + qphy->base + QUSB2PHY_PORT_TUNE3); + } + /* If phy_tune4 modparam set, override tune4 value */ + if (phy_tune4) { + pr_debug("%s(): (modparam) TUNE4 val:0x%02x\n", + __func__, phy_tune4); + writel_relaxed(phy_tune4, + qphy->base + QUSB2PHY_PORT_TUNE4); + } + msm_qphy_param_output(phy); /* ensure above writes are completed before re-enabling PHY */ wmb(); @@ -886,6 +1049,12 @@ static int qusb_phy_probe(struct platform_device *pdev) &qphy->efuse_num_of_bits); } + if (!ret) { + ret = of_property_read_u32(dev->of_node, + "qcom,efuse-offset", + &qphy->efuse_offset); + } + if (ret) { dev_err(dev, "DT Value for efuse is invalid.\n"); diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index d1b6f1df860dfcdbc5242a7b7651a0ca092e375f..a343a2e698e10d274589236b85c9d935f90bb015 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -11,6 +11,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -57,6 +62,24 @@ enum ldo_levels { /* port select mux: 1 - sw control. 0 - HW control*/ #define SW_PORTSELECT_MX BIT(1) +#define SSUSB3PHY_TXA_DRV_LVL 0x21c +#define SSUSB3PHY_TXB_DRV_LVL 0x61c +#define SSUSB3PHY_TXA_EMP_POST1_LVL 0x20c +#define SSUSB3PHY_TXB_EMP_POST1_LVL 0x60c + +unsigned int ssphy_txa_drv_lvl; +unsigned int ssphy_txb_drv_lvl; +unsigned int ssphy_txa_emp_post1_lvl; +unsigned int ssphy_txb_emp_post1_lvl; +module_param(ssphy_txa_drv_lvl, uint, S_IRUGO | S_IWUSR); +module_param(ssphy_txb_drv_lvl, uint, S_IRUGO | S_IWUSR); +module_param(ssphy_txa_emp_post1_lvl, uint, S_IRUGO | S_IWUSR); +module_param(ssphy_txb_emp_post1_lvl, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ssphy_txa_drv_lvl, "SSUSB3PHY QSERDES TXA DRV LVL"); +MODULE_PARM_DESC(ssphy_txb_drv_lvl, "SSUSB3PHY QSERDES TXB DRV LVL"); +MODULE_PARM_DESC(ssphy_txa_emp_post1_lvl, "SSUSB3PHY QSERDES TXA EMP POST1 LVL"); +MODULE_PARM_DESC(ssphy_txb_emp_post1_lvl, "SSUSB3PHY QSERDES TXB EMP POST1 LVL"); + enum qmp_phy_rev_reg { USB3_PHY_PCS_STATUS, USB3_PHY_AUTONOMOUS_MODE_CTRL, @@ -263,6 +286,21 @@ disable_fpc_redrive: return rc < 0 ? rc : 0; } +static void msm_ssphy_param_output(struct usb_phy *uphy) +{ + struct msm_ssphy_qmp *phy = container_of(uphy, struct msm_ssphy_qmp, + phy); + + dev_dbg(uphy->dev, "SS_USB3PHY_QSERDES_TXA_TX_DEV_LVL:0x%02x\n", + readb_relaxed(phy->base + SSUSB3PHY_TXA_DRV_LVL)); + dev_dbg(uphy->dev, "SS_USB3PHY_QSERDES_TXB_TX_DEV_LVL:0x%02x\n", + readb_relaxed(phy->base + SSUSB3PHY_TXB_DRV_LVL)); + dev_dbg(uphy->dev, "SS_USB3PHY_QSERDES_TXA_TX_EMP_POST1_LVL:0x%02x\n", + readb_relaxed(phy->base + SSUSB3PHY_TXA_EMP_POST1_LVL)); + dev_dbg(uphy->dev, "SS_USB3PHY_QSERDES_TXB_TX_EMP_POST1_LVL:0x%02x\n", + readb_relaxed(phy->base + SSUSB3PHY_TXB_EMP_POST1_LVL)); +} + static int configure_phy_regs(struct usb_phy *uphy, const struct qmp_reg_val *reg) { @@ -280,6 +318,23 @@ static int configure_phy_regs(struct usb_phy *uphy, usleep_range(reg->delay, reg->delay + 10); reg++; } + + /* ssusb phy dynamic set */ + if (ssphy_txa_drv_lvl) + writel_relaxed(ssphy_txa_drv_lvl, + phy->base + SSUSB3PHY_TXA_DRV_LVL); + if (ssphy_txb_drv_lvl) + writel_relaxed(ssphy_txb_drv_lvl, + phy->base + SSUSB3PHY_TXB_DRV_LVL); + if (ssphy_txa_emp_post1_lvl) + writel_relaxed(ssphy_txa_emp_post1_lvl, + phy->base + SSUSB3PHY_TXA_EMP_POST1_LVL); + if (ssphy_txb_emp_post1_lvl) + writel_relaxed(ssphy_txb_emp_post1_lvl, + phy->base + SSUSB3PHY_TXB_EMP_POST1_LVL); + + /* parameter output */ + msm_ssphy_param_output(uphy); return 0; } diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index f1360f20ffe48e1cdd523b18d2c0b19b8c778e1e..738b50a88736008c0607998ee3f0f947814b0b95 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -15,6 +15,11 @@ * 02110-1301, USA. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 1a34d2a89de66cbfa147e67c768a696d52a57b09..4449b58a5904af09adf36207cedb6deb847cb39e 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -24,6 +24,11 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* IMPORTANT NOTE: This file must be included in another file which does * the following thing for it to work: diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index daca9e6a2bb31590071cfc7100e4c1ee8ca4f1ec..7f6bc81aa31bc64dfbbea00170afbc9da5a8af67 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -8,6 +8,11 @@ * published by the Free Software Foundation. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c index 924b3d6c3e9bd7062f2bdca91b7fff2392aadaa6..55234bcc5f5a9f8906ef5966f4fdea5aa39315b0 100644 --- a/drivers/video/fbdev/amba-clcd.c +++ b/drivers/video/fbdev/amba-clcd.c @@ -10,6 +10,11 @@ * * ARM PrimeCell PL110 Color LCD Controller */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig index 03ee89ad0d999e2e3940be0f8ba29c3e6fcb8fe9..6ae83669d71a775d82452157259eb453d6b80116 100644 --- a/drivers/video/fbdev/msm/Kconfig +++ b/drivers/video/fbdev/msm/Kconfig @@ -61,6 +61,15 @@ config FB_MSM_MDSS_WRITEBACK The MDSS Writeback Panel provides support for routing the output of MDSS frame buffer driver and MDP processing to memory. +config FB_MSM_MDSS_SPECIFIC_PANEL + depends on FB_MSM_MDSS + bool + prompt "MDSS Specific panel" + default n + ---help--- + The MDSS Specific Panel provides support for specific panel driver + file. + config FB_MSM_MDSS_HDMI_PANEL depends on FB_MSM_MDSS select MSM_EXT_DISPLAY diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index e101b873f361faa75dfb7b2e25edb16f658c4243..37d278ac161efc39ecf28b8ac69fb4e868e02834 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -32,6 +32,10 @@ obj-$(CONFIG_DEBUG_FS) += mdss_debug.o mdss_debug_xlog.o endif mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o mdss_dsi_cmd.o mdss_dsi_status.o +ifeq ($(CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL),y) +mdss-dsi-objs += mdss_dsi_panel_driver.o +mdss-dsi-objs += mdss_dsi_panel_debugfs.o +endif mdss-dsi-objs += mdss_dsi_panel.o mdss-dsi-objs += msm_mdss_io_8974.o mdss-dsi-objs += mdss_dsi_phy.o @@ -63,4 +67,7 @@ obj-$(CONFIG_FB_MSM_QPIC) += mdss-qpic.o obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o mdss_util.o +ifeq ($(CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL),y) +obj-$(CONFIG_FB_MSM_MDSS) += incell.o +endif obj-$(CONFIG_COMPAT) += mdss_compat_utils.o diff --git a/drivers/video/fbdev/msm/incell.c b/drivers/video/fbdev/msm/incell.c new file mode 100644 index 0000000000000000000000000000000000000000..c5f0b215fc67379fbe65bbcbd012f72ba4bcedbf --- /dev/null +++ b/drivers/video/fbdev/msm/incell.c @@ -0,0 +1,650 @@ +/* drivers/video/fbdev/msm/incell.c + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "mdss_fb.h" +#include "mdss_mdp.h" +#include "mdss_dsi.h" +#include "mdss_dsi_panel_driver.h" + +struct incell_ctrl *incell; +struct incell_ctrl incell_buf; + +struct incell_ctrl *incell_get_info(void) +{ + return incell; +} + +int incell_get_power_status(incell_pw_status *power_status) +{ + struct incell_ctrl *incell = incell_get_info(); + + if (!incell) { + pr_err("%s: Invalid incell data\n", __func__); + return INCELL_ERROR; + } + + pr_debug("%s: status = 0x%x\n", __func__, (incell->state)); + + if (mdss_dsi_panel_driver_is_power_on(incell->state)) { + power_status->display_power = INCELL_POWER_ON; + power_status->touch_power = INCELL_POWER_ON; + } else { + power_status->display_power = INCELL_POWER_OFF; + power_status->touch_power = INCELL_POWER_OFF; + } + + return INCELL_OK; +} + +static int incell_driver_power_off(struct fb_info *info) +{ + int ret = INCELL_ERROR; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct mdss_mdp_ctl *ctl = NULL; + struct mdss_panel_data *pdata = NULL; + struct msm_fb_data_type *mfd = NULL; + struct incell_ctrl *incell = incell_get_info(); + + if (!incell) { + pr_err("%s: Invalid incell data\n", __func__); + return ret; + } + + if (!mdata) { + pr_err("%s: Invalid mdata\n", __func__); + return ret; + } + + ctl = mdata->ctl_off; + if (!ctl) { + pr_err("%s: Invalid ctl data\n", __func__); + return ret; + } + + pdata = ctl->panel_data; + if (!pdata) { + pr_err("%s: Invalid panel data\n", __func__); + return ret; + } + + mfd = (struct msm_fb_data_type *)info->par; + if (!mfd) { + pr_err("%s: Invalid msm data\n", __func__); + return ret; + } + + if (!mdss_fb_is_power_on(mfd)) { + mdss_dsi_panel_driver_power_off_ctrl(incell); + mdss_dsi_panel_driver_state_change_off(incell); + + ret = mdss_dsi_panel_driver_power_off(pdata); + if (ret) { + pr_err("%s: Failed to power off ret=%d\n", + __func__, ret); + ret = INCELL_ERROR; + return ret; + } + } else { + ret = info->fbops->fb_blank(FB_BLANK_POWERDOWN, info); + if (ret) { + pr_err("%s: fb_blank(blank) FAIL ret=%d\n", + __func__, ret); + ret = INCELL_ERROR; + return ret; + } + } + + ret = INCELL_OK; + return ret; +} + +static int incell_driver_send_power_on_seq(struct mdss_panel_data *pdata) +{ + int ret = INCELL_ERROR; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_specific_pdata *spec_pdata = NULL; + + ctrl_pdata = container_of(pdata, + struct mdss_dsi_ctrl_pdata, panel_data); + spec_pdata = ctrl_pdata->spec_pdata; + + if (!ctrl_pdata || !spec_pdata) { + pr_err("%s: Invalid input data\n", __func__); + goto end; + } + + ret = mdss_dsi_panel_driver_power_on(pdata); + if (ret) { + pr_err("%s: Failed to power on ret=%d\n", __func__, ret); + ret = INCELL_ERROR; + goto end; + } + + if (mdss_dsi_pinctrl_set_state(ctrl_pdata, true)) + pr_debug("%s: reset enable: pinctrl not enabled\n", + __func__); + mdss_dsi_panel_reset(pdata, 1); + + if (incell->incell_intf_operation == INCELL_TOUCH_RUN && + incell->intf_mode == INCELL_DISPLAY_HW_RESET) { + mdss_dsi_panel_driver_reset_touch(pdata, 1); + mdss_dsi_panel_driver_state_change_on(incell); + } else { + if (!ctrl_pdata->on) + goto end; + + ret = ctrl_pdata->on(pdata); + if (ret) { + pr_err("%s: Failed to send on ret=%d\n", + __func__, ret); + ret = INCELL_ERROR; + goto end; + } + + if (!ctrl_pdata->post_panel_on) + goto end; + + ret = ctrl_pdata->post_panel_on(pdata); + if (ret) { + pr_err("%s: Failed to send post-on ret=%d\n", + __func__, ret); + ret = INCELL_ERROR; + goto end; + } + } + ret = INCELL_OK; +end: + return ret; +} + +static int incell_driver_send_power_on_fb(struct fb_info *info) +{ + int ret = INCELL_ERROR; + + if (!(info->fbops->fb_open) || !(info->fbops->fb_blank) + || !(info->fbops->fb_release)) { + pr_err("%s: Invalid operations\n", __func__); + goto end; + } + + ret = info->fbops->fb_blank(FB_BLANK_UNBLANK, info); + if (ret) { + pr_err("%s: fb_blank(blank) FAIL ret=%d\n", + __func__, ret); + ret = INCELL_ERROR; + goto end; + } + ret = INCELL_OK; +end: + return ret; +} + +static int incell_driver_power_on(struct fb_info *info) +{ + int ret = INCELL_ERROR; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct mdss_mdp_ctl *ctl = NULL; + struct mdss_panel_data *pdata = NULL; + struct msm_fb_data_type *mfd = NULL; + struct fb_specific_data *spec_mfd = NULL; + + if (!mdata) { + pr_err("%s: Invalid mdata\n", __func__); + goto end; + } + + ctl = mdata->ctl_off; + if (!ctl) { + pr_err("%s: Invalid ctl data\n", __func__); + goto end; + } + + pdata = ctl->panel_data; + if (!pdata) { + pr_err("%s: Invalid panel data\n", __func__); + goto end; + } + + mfd = (struct msm_fb_data_type *)info->par; + if (!mfd) { + pr_err("%s: Invalid msm data\n", __func__); + goto end; + } + spec_mfd = &mfd->spec_mfd; + + if (mdss_fb_is_power_on(mfd)) + ret = incell_driver_send_power_on_seq(pdata); + else + ret = incell_driver_send_power_on_fb(info); + + if (spec_mfd->off_sts) + spec_mfd->off_sts = false; + +end: + return ret; +} + +static int incell_display_hw_reset(struct incell_ctrl *incell, + struct fb_info *info) +{ + int ret = INCELL_ERROR; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct mdss_mdp_ctl *ctl = NULL; + struct mdss_panel_data *pdata = NULL; + + if (!incell) { + pr_err("%s: Invalid incell data\n", __func__); + goto end; + } + + if (!mdata) { + pr_err("%s: Invalid mdata\n", __func__); + goto end; + } + + ctl = mdata->ctl_off; + if (!ctl) { + pr_err("%s: Invalid ctl data\n", __func__); + goto end; + } + + pdata = ctl->panel_data; + if (!pdata) { + pr_err("%s: Invalid panel data\n", __func__); + goto end; + } + + /* Power off if LCD is on. */ + if (mdss_dsi_panel_driver_is_power_on(incell->state)) + /* + * It calls directly power off to DSI layer, + * the case of FB off. + */ + ret = incell_driver_power_off(info); + else + pr_debug("%s: Skip LCD off 0x%x\n", __func__, + (incell->state)); + + if (!mdss_dsi_panel_driver_is_system_on(incell->state) + && !mdss_dsi_panel_driver_is_power_on(incell->state)) { + pr_debug("%s: LCD on in DSI layer. sts:0x%x\n", __func__, + (incell->state)); + ret = incell_driver_send_power_on_seq(pdata); + } else { + pr_debug("%s: LCD on in FB layer. sts:0x%x\n", __func__, + (incell->state)); + ret = incell_driver_send_power_on_fb(info); + } + + ret = INCELL_OK; +end: + return ret; +} + +static int incell_display_off(struct incell_ctrl *incell, + struct fb_info *info) +{ + int ret = INCELL_ERROR; + + if (!mdss_dsi_panel_driver_is_power_on(incell->state)) { + pr_err("%s: LCD is already off. sts:0x%x\n", + __func__, (incell->state)); + ret = INCELL_EALREADY; + } else { + pr_debug("%s: incell panel sts:0x%x\n", __func__, + (incell->state)); + } + + if (ret == INCELL_EALREADY) { + pr_err("%s: Already power off ret=%d\n", __func__, ret); + return ret; + } + + ret = incell_driver_power_off(info); + return ret; +} + +int incell_control_mode(incell_intf_mode mode, bool force) +{ + int ret = INCELL_ERROR; + struct fb_info *info = NULL; + struct msm_fb_data_type *mfd = NULL; + struct fb_specific_data *spec_mfd = NULL; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct mdss_mdp_ctl *ctl = NULL; + struct mdss_panel_data *pdata = NULL; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_specific_pdata *spec_pdata = NULL; + struct incell_ctrl *incell = incell_get_info(); + + pr_notice("%s: START - %s:%s\n", __func__, + ((mode == INCELL_DISPLAY_HW_RESET) ? "INCELL_DISPLAY_HW_RESET" : + ((mode == INCELL_DISPLAY_OFF) ? "INCELL_DISPLAY_OFF" : + "INCELL_DISPLAY_ON")), + ((force) ? "force" : "unforce")); + + if (!incell) { + pr_err("%s: Invalid incell data\n", __func__); + return ret; + } + + info = registered_fb[0]; + if (!info) { + pr_err("%s: Invalid fb data\n", __func__); + return ret; + } + + if (!(info->fbops->fb_blank) || !(info->fbops->fb_release) + || !(info->fbops->fb_open)) { + pr_err("%s: Invalid operations\n", __func__); + return ret; + } + + if (!mdata) { + pr_err("%s: Invalid mdata\n", __func__); + return ret; + } + + ctl = mdata->ctl_off; + if (!ctl) { + pr_err("%s: Invalid ctl data\n", __func__); + return ret; + } + + pdata = ctl->panel_data; + if (!pdata) { + pr_err("%s: Invalid panel data\n", __func__); + return ret; + } + + mfd = (struct msm_fb_data_type *)info->par; + if (!mfd) { + pr_err("%s: Invalid msm data\n", __func__); + return ret; + } + spec_mfd = &mfd->spec_mfd; + + ctrl_pdata = container_of(pdata, + struct mdss_dsi_ctrl_pdata, panel_data); + spec_pdata = ctrl_pdata->spec_pdata; + + if (!ctrl_pdata || !spec_pdata) { + pr_err("%s: Invalid input data\n", __func__); + return ret; + } + + /* + * It returns "INCELL_ALREADY_LOCKED" + * the case of not setting "INCELL_FORCE" flag. + */ + if (mdss_dsi_panel_driver_is_power_lock(incell->state)) { + if (force == INCELL_UNFORCE) { + ret = INCELL_ALREADY_LOCKED; + pr_err("%s: Already power locked ret=%d\n", + __func__, ret); + return ret; + } + } + + if (incell->worker_state != INCELL_WORKER_OFF) { + ret = INCELL_EBUSY; + pr_err("%s: worker scheduling ret=%d\n", __func__, ret); + return ret; + } + + if (incell->incell_intf_operation == INCELL_TOUCH_RUN) { + ret = INCELL_EBUSY; + pr_err("%s: touch I/F not finished ret=%d\n", __func__, ret); + return ret; + } + + incell->incell_intf_operation = INCELL_TOUCH_RUN; + + if (!mutex_trylock(&info->lock)) { + incell->incell_intf_operation = INCELL_TOUCH_IDLE; + pr_err("%s: mutex_locked ret=%d\n", __func__, ret); + ret = INCELL_EBUSY; + return ret; + } + + incell->intf_mode = mode; + + switch (mode) { + case INCELL_DISPLAY_ON: + incell->intf_mode = INCELL_DISPLAY_HW_RESET; + case INCELL_DISPLAY_HW_RESET: + spec_mfd->off_sts = true; + ret = incell_display_hw_reset(incell, info); + spec_mfd->off_sts = false; + break; + case INCELL_DISPLAY_OFF: + spec_mfd->off_sts = true; + ret = incell_display_off(incell, info); + break; + default: + pr_err("%s: Invalid mode for touch interface %d\n", + __func__, (int)(mode)); + break; + } + + mutex_unlock(&info->lock); + incell->incell_intf_operation = INCELL_TOUCH_IDLE; + pr_notice("%s: FINISH - incell.status:0x%x\n", __func__, + (incell->state)); + return ret; +} + +static void incell_panel_power_worker(struct work_struct *work) +{ + struct incell_ctrl *incell = incell_get_info(); + struct fb_info *info = registered_fb[0]; + + pr_notice("%s: START - incell.status:0x%x\n", __func__, + (incell->state)); + + if (!incell) { + pr_err("%s: Invalid incell data\n", __func__); + return; + } + + if (!info) { + pr_err("%s: Invalid fb data\n", __func__); + return; + } + + mutex_lock(&info->lock); + incell->worker_state = INCELL_WORKER_ON; + + if (incell->state == INCELL_WORK_NEED_P_OFF) + incell_driver_power_off(info); + else if (incell->state == INCELL_WORK_NEED_P_ON + || incell->state == INCELL_WORK_NEED_P_ON_EWU) + incell_driver_power_on(info); + + incell->worker_state = INCELL_WORKER_OFF; + mutex_unlock(&info->lock); + + pr_notice("%s: FINISH - incell.status:0x%x\n", __func__, + (incell->state)); +} + +static void incell_panel_power_worker_scheduling(incell_pw_lock lock, + struct incell_ctrl *incell) +{ + unsigned char state = incell->state; + + if (lock == INCELL_DISPLAY_POWER_LOCK) + return; + + if (state != INCELL_WORK_NEED_P_OFF + && state != INCELL_WORK_NEED_P_ON + && state != INCELL_WORK_NEED_P_ON_EWU) + return; + + incell->worker_state = INCELL_WORKER_PENDING; + schedule_work(&incell->incell_work); + + pr_notice("%s: incell worker scheduled - incell.status:0x%x\n", + __func__, (incell->state)); +} + +void incell_panel_power_worker_canceling(struct incell_ctrl *incell) +{ + struct fb_info *info = registered_fb[0]; + struct msm_fb_data_type *mfd = NULL; + struct fb_specific_data *spec_mfd = NULL; + + cancel_work_sync(&incell->incell_work); + + pr_notice("%s: incell worker canceled - incell.status:0x%x\n", __func__, + (incell->state)); + + if (!info) { + pr_err("%s: Invalid fb data\n", __func__); + goto end; + } + + mfd = (struct msm_fb_data_type *)info->par; + if (!mfd) { + pr_err("%s: Invalid msm data\n", __func__); + goto end; + } + + spec_mfd = &mfd->spec_mfd; + if (spec_mfd->off_sts) + spec_mfd->off_sts = false; + +end: + incell->worker_state = INCELL_WORKER_OFF; +} + +static int incell_power_lock(unsigned char *state) +{ + + if (mdss_dsi_panel_driver_is_power_lock(*state)) { + pr_err("%s: Power state already locked", __func__); + return INCELL_ALREADY_LOCKED; + } + *state |= INCELL_LOCK_STATE_ON; + + return INCELL_OK; +} +static int incell_power_unlock(unsigned char *state) +{ + + if (!mdss_dsi_panel_driver_is_power_lock(*state)) { + pr_err("%s: Power state already unlocked", __func__); + return INCELL_ALREADY_UNLOCKED; + } + *state &= INCELL_LOCK_STATE_OFF; + + return INCELL_OK; +} + +int incell_power_lock_ctrl(incell_pw_lock lock, + incell_pw_status *power_status) +{ + int ret = INCELL_ERROR; + struct incell_ctrl *incell = incell_get_info(); + + if (!incell) { + pr_err("%s: Invalid incell data\n", __func__); + return ret; + } + + if (incell->worker_state != INCELL_WORKER_OFF) { + ret = INCELL_EBUSY; + pr_err("%s: worker scheduling ret=%d\n", __func__, ret); + return ret; + } + + if (incell->incell_intf_operation == INCELL_TOUCH_RUN) { + ret = INCELL_EBUSY; + pr_err("%s: touch I/F not finished ret=%d\n", __func__, ret); + return ret; + } + + incell->incell_intf_operation = INCELL_TOUCH_RUN; + + pr_debug("%s: status:0x%x --->\n", __func__, (incell->state)); + + mdss_dsi_panel_driver_update_incell_bk(incell); + + if (lock == INCELL_DISPLAY_POWER_LOCK) + ret = incell_power_lock(&(incell->state)); + else + ret = incell_power_unlock(&(incell->state)); + + pr_debug("%s: ---> status:0x%x\n", __func__, (incell->state)); + + incell_get_power_status(power_status); + incell_panel_power_worker_scheduling(lock, incell); + + incell->incell_intf_operation = INCELL_TOUCH_IDLE; + + return ret; +} + + +void incell_ewu_mode_ctrl(incell_ewu_mode ewu) +{ + struct incell_ctrl *incell = incell_get_info(); + + pr_notice("%s: START - %s\n", __func__, + ((ewu == INCELL_DISPLAY_EWU_DISABLE) ? + "INCELL_DISPLAY_EWU_DISABLE" : "INCELL_DISPLAY_EWU_ENABLE")); + + if (!incell) { + pr_err("%s: Invalid incell data\n", __func__); + return; + } + + pr_debug("%s: EWU mode is %s\n", __func__, + ewu == INCELL_DISPLAY_EWU_ENABLE ? "on":"off"); + + pr_debug("%s: status:0x%x --->\n", __func__, (incell->state)); + + mdss_dsi_panel_driver_update_incell_bk(incell); + + if (ewu == INCELL_DISPLAY_EWU_ENABLE) + incell->state |= INCELL_EWU_STATE_ON; + else + incell->state &= INCELL_EWU_STATE_OFF; + + pr_notice("%s: FINISH - incell.status:0x%x\n", __func__, + (incell->state)); +} + +void incell_driver_init(void) +{ + memset(&incell_buf, 0, sizeof(struct incell_ctrl)); + incell = &incell_buf; + + incell->state = INCELL_INIT_STATE_KERNEL; + incell->incell_intf_operation = INCELL_TOUCH_IDLE; + incell->worker_state = INCELL_WORKER_OFF; + + INIT_WORK(&incell->incell_work, incell_panel_power_worker); +} + diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index 1ccb27113c119a5aef1aa1e586014e197dc27432..6ebd6ac66df9368be0072ca45ba1bc5ad6487eb9 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef MDSS_H #define MDSS_H @@ -37,6 +42,10 @@ #define MDSS_PINCTRL_STATE_DEFAULT "mdss_default" #define MDSS_PINCTRL_STATE_SLEEP "mdss_sleep" +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +#define MDSS_PINCTRL_STATE_TOUCH_ACTIVE "mdss_touch_active" +#define MDSS_PINCTRL_STATE_TOUCH_SUSPEND "mdss_touch_suspend" +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ enum mdss_mdp_clk_type { MDSS_CLK_AHB, diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c index 8c28fbf8fbc2d406f0911b4ced4628292173815d..27bd09d860e0070de73b6487d32bc58f7bb4dff2 100644 --- a/drivers/video/fbdev/msm/mdss_compat_utils.c +++ b/drivers/video/fbdev/msm/mdss_compat_utils.c @@ -129,6 +129,7 @@ static void __copy_atomic_commit_struct(struct mdp_layer_commit *commit, commit->commit_v1.left_roi = commit32->commit_v1.left_roi; commit->commit_v1.right_roi = commit32->commit_v1.right_roi; commit->commit_v1.bl_level = commit32->commit_v1.bl_level; + memcpy(&commit->commit_v1.reserved, &commit32->commit_v1.reserved, count); } diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 0d41f41371ddd0d5b895ef7936d2c17489cd1254..fe62b69c96e8b5d01bdc4ad29270be48ba4e3786 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -34,6 +39,9 @@ #include "mdss_debug.h" #include "mdss_dsi_phy.h" #include "mdss_dba_utils.h" +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +#include "mdss_dsi_panel_driver.h" +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ #define CMDLINE_DSI_CTL_NUM_STRING_LEN 2 @@ -185,8 +193,10 @@ static void mdss_dsi_pm_qos_update_request(int val) pm_qos_update_request(&mdss_dsi_pm_qos_request, val); } +#ifndef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL static int mdss_dsi_pinctrl_set_state(struct mdss_dsi_ctrl_pdata *ctrl_pdata, bool active); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ static struct mdss_dsi_ctrl_pdata *mdss_dsi_get_ctrl(u32 ctrl_id) { @@ -365,6 +375,9 @@ static int mdss_dsi_regulator_init(struct platform_device *pdev, static int mdss_dsi_panel_power_off(struct mdss_panel_data *pdata) { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + return mdss_dsi_panel_driver_power_off(pdata); +#else int ret = 0; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; @@ -395,10 +408,14 @@ static int mdss_dsi_panel_power_off(struct mdss_panel_data *pdata) end: return ret; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } static int mdss_dsi_panel_power_on(struct mdss_panel_data *pdata) { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + return mdss_dsi_panel_driver_power_on(pdata); +#else int ret = 0; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; @@ -437,6 +454,7 @@ static int mdss_dsi_panel_power_on(struct mdss_panel_data *pdata) } return ret; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } static int mdss_dsi_panel_power_lp(struct mdss_panel_data *pdata, int enable) @@ -1479,6 +1497,9 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) struct mipi_panel_info *mipi; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; int cur_power_state; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct incell_ctrl *incell = incell_get_info(); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); @@ -1561,9 +1582,14 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) * data lanes for LP11 init */ if (mipi->lp11_init) { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if ((!incell) || (incell->seq == POWER_ON_EXECUTE)) + mdss_dsi_panel_driver_reset_dual_display(ctrl_pdata); +#else if (mdss_dsi_pinctrl_set_state(ctrl_pdata, true)) pr_debug("reset enable: pinctrl not enabled\n"); mdss_dsi_panel_reset(pdata, 1); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } if (mipi->init_delay) @@ -1578,9 +1604,15 @@ end: return ret; } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +int mdss_dsi_pinctrl_set_state( + struct mdss_dsi_ctrl_pdata *ctrl_pdata, + bool active) +#else static int mdss_dsi_pinctrl_set_state( struct mdss_dsi_ctrl_pdata *ctrl_pdata, bool active) +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ { struct pinctrl_state *pin_state; struct mdss_panel_info *pinfo = NULL; @@ -1638,7 +1670,11 @@ static int mdss_dsi_pinctrl_init(struct platform_device *pdev) if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.gpio_state_suspend)) pr_warn("%s: can not get sleep pinstate\n", __func__); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + return mdss_dsi_panel_driver_pinctrl_init(ctrl_pdata); +#else return 0; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } static int mdss_dsi_unblank(struct mdss_panel_data *pdata) @@ -1703,6 +1739,10 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata) ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_panel_driver_unblank(ctrl_pdata); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + error: mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle, MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_OFF); @@ -2928,13 +2968,18 @@ static struct device_node *mdss_dsi_find_panel_of_node( char panel_name[MDSS_MAX_PANEL_LEN] = ""; char ctrl_id_stream[3] = "0:"; char *str1 = NULL, *str2 = NULL, *override_cfg = NULL; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL char cfg_np_name[MDSS_MAX_PANEL_LEN] = ""; struct device_node *dsi_pan_node = NULL, *mdss_node = NULL; +#else + struct device_node *dsi_pan_node = NULL; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ struct mdss_dsi_ctrl_pdata *ctrl_pdata = platform_get_drvdata(pdev); struct mdss_panel_info *pinfo = &ctrl_pdata->panel_data.panel_info; len = strlen(panel_cfg); ctrl_pdata->panel_data.dsc_cfg_np_name[0] = '\0'; + len = 0; /* temporary */ if (!len) { /* no panel cfg chg, parse dt */ pr_debug("%s:%d: no cmd line cfg present\n", @@ -3022,12 +3067,20 @@ static struct device_node *mdss_dsi_find_panel_of_node( cfg_np_name, MDSS_MAX_PANEL_LEN); } } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_panel_driver_detection(pdev, &dsi_pan_node); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ return dsi_pan_node; } end: if (strcmp(panel_name, NONE_PANEL)) dsi_pan_node = mdss_dsi_pref_prim_panel(pdev); + +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_panel_driver_detection(pdev, &dsi_pan_node); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + exit: return dsi_pan_node; } @@ -3060,6 +3113,9 @@ static struct device_node *mdss_dsi_config_panel(struct platform_device *pdev, return NULL; } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + ctrl_pdata->panel_data.panel_pdev = pdev; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ rc = mdss_dsi_panel_init(dsi_pan_node, ctrl_pdata, ndx); if (rc) { pr_err("%s: dsi panel init failed\n", __func__); @@ -3192,6 +3248,10 @@ static int mdss_dsi_cont_splash_config(struct mdss_panel_info *pinfo, void *clk_handle; int rc = 0; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_panel_driver_check_splash_enable(ctrl_pdata); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + if (pinfo->cont_splash_enabled) { rc = mdss_dsi_panel_power_ctrl(&(ctrl_pdata->panel_data), MDSS_PANEL_POWER_ON); @@ -3567,6 +3627,11 @@ static void mdss_dsi_res_deinit(struct platform_device *pdev) if (pinfo) mdss_dba_utils_deinit(pinfo->dba_data); } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + devm_kfree(&pdev->dev, + dsi_res->ctrl_pdata[i]->spec_pdata); + if (dsi_res->ctrl_pdata[i]) +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ devm_kfree(&pdev->dev, dsi_res->ctrl_pdata[i]); } @@ -3685,6 +3750,21 @@ static int mdss_dsi_res_init(struct platform_device *pdev) rc = -ENOMEM; goto mem_fail; } + +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_res->ctrl_pdata[i]->spec_pdata = devm_kzalloc( + &pdev->dev, + sizeof(struct mdss_panel_specific_pdata), + GFP_KERNEL); + + if (!mdss_dsi_res->ctrl_pdata[i]->spec_pdata) { + pr_err("%s Unable to alloc spec_pdata =%d\n", + __func__, i); + rc = -ENOMEM; + goto mem_fail; + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + pr_debug("%s Allocated ctrl_pdata[%d]=%pK\n", __func__, i, mdss_dsi_res->ctrl_pdata[i]); mdss_dsi_res->ctrl_pdata[i]->shared_data = @@ -4315,6 +4395,9 @@ static int mdss_dsi_parse_gpio_params(struct platform_device *ctrl_pdev, ctrl_pdata->lcd_mode_sel_gpio = -EINVAL; } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_panel_driver_parse_gpio_params(ctrl_pdev, ctrl_pdata); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ return 0; } @@ -4392,6 +4475,10 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev, return rc; } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_panel_driver_labibb_vreg_init(ctrl_pdata); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + rc = mdss_dsi_parse_ctrl_params(ctrl_pdev, pan_node, ctrl_pdata); if (rc) { pr_err("%s: failed to parse ctrl settings, rc=%d\n", diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 9847016fed2963d88ced9aac73c1371a643f6169..fdfc346bd54229fed3ef0c7a7198461bccec35c4 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef MDSS_DSI_H #define MDSS_DSI_H @@ -375,6 +380,10 @@ struct dsi_pinctrl_res { struct pinctrl *pinctrl; struct pinctrl_state *gpio_state_active; struct pinctrl_state *gpio_state_suspend; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct pinctrl_state *touch_state_active; + struct pinctrl_state *touch_state_suspend; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ }; struct panel_horizontal_idle { @@ -591,6 +600,9 @@ struct mdss_dsi_ctrl_pdata { bool update_phy_timing; /* flag to recalculate PHY timings */ bool phy_power_off; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct mdss_panel_specific_pdata *spec_pdata; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ }; struct dsi_status_data { diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index 34cedaaa58644770889edd4779b7d4db03589a00..11545f85d4e31a13654ee15c7a9b3e9960b9e2e0 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -1190,12 +1195,22 @@ int mdss_dsi_reg_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) { int ret = 0; struct mdss_dsi_ctrl_pdata *sctrl_pdata = NULL; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct mipi_panel_info *mipi = NULL; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (ctrl_pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return 0; } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mipi = &ctrl_pdata->panel_data.panel_info.mipi; + if (mipi->switch_mode_pending == true) { + pr_err("%s: Skip status check, Pending switch mode\n", __func__); + return 0; + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ pr_debug("%s: Checking Register status\n", __func__); mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle, @@ -2617,8 +2632,19 @@ void mdss_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl) if (!ctrl->mdp_busy) rc = 1; spin_unlock_irqrestore(&ctrl->mdp_lock, flags); - if (!rc && mdss_dsi_mdp_busy_tout_check(ctrl)) - pr_err("%s: timeout error\n", __func__); + if (!rc) { + if (mdss_dsi_mdp_busy_tout_check(ctrl)) { + pr_err("%s: timeout error\n", __func__); + MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", + "dsi0_phy", "dsi1_ctrl", "dsi1_phy", + "vbif", "vbif_nrt", "dbg_bus", +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + "vbif_dbg_bus"); +#else + "vbif_dbg_bus", "panic"); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + } + } } pr_debug("%s: done pid=%d\n", __func__, current->pid); MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, current->pid, XLOG_FUNC_EXIT); @@ -2720,6 +2746,9 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) int rc = 0; bool hs_req = false; bool cmd_mutex_acquired = false; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + bool cmdlist_mutex_acquired = false; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (from_mdp) { /* from mdp kickoff */ if (!ctrl->burst_mode_enabled) { @@ -2735,6 +2764,11 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) if (req && from_mdp && ctrl->burst_mode_enabled) { mutex_lock(&ctrl->cmd_mutex); cmd_mutex_acquired = true; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + } else if (!from_mdp) { + mutex_lock(&ctrl->cmdlist_mutex); + cmdlist_mutex_acquired = true; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } MDSS_XLOG(ctrl->ndx, from_mdp, ctrl->mdp_busy, current->pid, @@ -2759,6 +2793,10 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) } else { if (cmd_mutex_acquired) mutex_unlock(&ctrl->cmd_mutex); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (cmdlist_mutex_acquired) + mutex_unlock(&ctrl->cmdlist_mutex); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ return -EPERM; } } @@ -2807,6 +2845,10 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) pr_err("%s: Bus bw vote failed\n", __func__); if (from_mdp) mutex_unlock(&ctrl->cmd_mutex); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (cmdlist_mutex_acquired) + mutex_unlock(&ctrl->cmdlist_mutex); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ return rc; } @@ -2815,6 +2857,10 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) if (IS_ERR_VALUE(rc)) { pr_err("IOMMU attach failed\n"); mutex_unlock(&ctrl->cmd_mutex); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (cmdlist_mutex_acquired) + mutex_unlock(&ctrl->cmdlist_mutex); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ return rc; } use_iommu = true; @@ -2878,6 +2924,11 @@ need_lock: ctrl->panel_mode == DSI_CMD_MODE && (req && (req->flags & CMD_REQ_HS_MODE))) mdss_dsi_cmd_stop_hs_clk_lane(ctrl); + +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (cmdlist_mutex_acquired) + mutex_unlock(&ctrl->cmdlist_mutex); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } return ret; diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index dbd58f93e907af2e63fa9f3c0764db407e9a00a0..bbeddd49d5bc29afc31c0af0707f47168184d093 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -26,6 +31,10 @@ #include "mdss_dsi.h" #include "mdss_dba_utils.h" #include "mdss_debug.h" +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +#include "mdss_dsi_panel_driver.h" +#include "mdss_dsi_panel_debugfs.h" +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ #define DT_CMD_HDR 6 #define DEFAULT_MDP_TRANSFER_TIME 14000 @@ -180,9 +189,13 @@ static void mdss_dsi_panel_apply_settings(struct mdss_dsi_ctrl_pdata *ctrl, mdss_dsi_cmdlist_put(ctrl, &cmdreq); } - +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds, u32 flags) +#else static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *pcmds, u32 flags) +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL*/ { struct dcs_cmd_req cmdreq; struct mdss_panel_info *pinfo; @@ -246,10 +259,20 @@ static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level) mdss_dsi_cmdlist_put(ctrl, &cmdreq); } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +#else static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ { int rc = 0; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + rc = mdss_dsi_panel_driver_request_gpios(ctrl_pdata); + if (rc) + goto specific_panel_err; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { rc = gpio_request(ctrl_pdata->disp_en_gpio, "disp_enable"); @@ -294,7 +317,13 @@ rst_gpio_err: if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) gpio_free(ctrl_pdata->disp_en_gpio); disp_en_gpio_err: +#ifndef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + return rc; +#else + mdss_dsi_panel_driver_gpio_free(ctrl_pdata); +specific_panel_err: return rc; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } int mdss_dsi_bl_gpio_ctrl(struct mdss_panel_data *pdata, int enable) @@ -374,6 +403,9 @@ ret: int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + return mdss_dsi_panel_driver_reset_panel(pdata, enable); +#else struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_panel_info *pinfo = NULL; int i, rc = 0; @@ -506,6 +538,7 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) exit: return rc; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } /** @@ -829,12 +862,23 @@ static void mdss_dsi_panel_switch_mode(struct mdss_panel_data *pdata, pr_debug("%s: sending switch commands\n", __func__); pcmds = &pt->switch_cmds; flags |= CMD_REQ_DMA_TPG; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mipi->switch_mode_pending = true; +#else flags |= CMD_REQ_COMMIT; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } else { pr_warn("%s: Invalid mode switch attempted\n", __func__); return; } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && + (mipi->switch_mode_pending == true)) + mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); + + mdss_dsi_panel_cmds_send(ctrl_pdata, pcmds, flags); +#else if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && (pdata->panel_info.send_pps_before_switch)) mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); @@ -844,6 +888,7 @@ static void mdss_dsi_panel_switch_mode(struct mdss_panel_data *pdata, if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && (!pdata->panel_info.send_pps_before_switch)) mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, @@ -921,6 +966,9 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) struct mdss_panel_info *pinfo; struct dsi_panel_cmds *on_cmds; int ret = 0; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct mdss_panel_specific_pdata *spec_pdata = NULL; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); @@ -933,6 +981,13 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) pr_debug("%s: ndx=%d\n", __func__, ctrl->ndx); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + spec_pdata = mdss_panel2spec_pdata(pdata); + + if (spec_pdata->crash_counter_reset) + spec_pdata->crash_counter_reset(); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + if (pinfo->dcs_cmd_by_left) { if (ctrl->ndx != DSI_CTRL_LEFT) goto end; @@ -970,6 +1025,11 @@ static int mdss_dsi_post_panel_on(struct mdss_panel_data *pdata) struct mdss_panel_info *pinfo; struct dsi_panel_cmds *cmds; u32 vsync_period = 0; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct incell_ctrl *incell = incell_get_info(); + unsigned char state; + struct mdss_panel_specific_pdata *spec_pdata = NULL; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); @@ -998,6 +1058,21 @@ static int mdss_dsi_post_panel_on(struct mdss_panel_data *pdata) mdss_dba_utils_hdcp_enable(pinfo->dba_data, true); } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + spec_pdata = mdss_panel2spec_pdata(pdata); + if (incell && (spec_pdata->panel_type == HYBRID_INCELL)) { + state = incell->state; + if ((!mdss_dsi_panel_driver_is_power_on(state)) && + (!mdss_dsi_panel_driver_is_ewu(state))) + mdss_dsi_panel_driver_reset_touch(pdata, 0); + } + mdss_dsi_panel_driver_reset_touch(pdata, 1); + + if (incell) + mdss_dsi_panel_driver_state_change_on(incell); + mdss_dsi_panel_driver_post_on(ctrl); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + end: pr_debug("%s:-\n", __func__); return 0; @@ -1007,6 +1082,14 @@ static int mdss_dsi_panel_off(struct mdss_panel_data *pdata) { struct mdss_dsi_ctrl_pdata *ctrl = NULL; struct mdss_panel_info *pinfo; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct incell_ctrl *incell = incell_get_info(); + + if (incell) { + mdss_dsi_panel_driver_power_off_ctrl(incell); + mdss_dsi_panel_driver_state_change_off(incell); + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); @@ -1033,6 +1116,10 @@ static int mdss_dsi_panel_off(struct mdss_panel_data *pdata) } end: +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (pdata->panel_info.dsi_master == pdata->panel_info.pdest) + mdss_dsi_panel_driver_off(ctrl); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ pr_debug("%s:-\n", __func__); return 0; } @@ -1122,9 +1209,13 @@ static void mdss_dsi_parse_trigger(struct device_node *np, char *trigger, } } - +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +int mdss_dsi_parse_dcs_cmds(struct device_node *np, + struct dsi_panel_cmds *pcmds, char *cmd_key, char *link_key) +#else static int mdss_dsi_parse_dcs_cmds(struct device_node *np, struct dsi_panel_cmds *pcmds, char *cmd_key, char *link_key) +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ { const char *data; int blen = 0, len; @@ -2585,10 +2676,17 @@ static int mdss_dsi_panel_timing_from_dt(struct device_node *np, rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-post", &tmp); pt->t_clk_post = (!rc ? tmp : 0x03); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + pt->timing.koff_thshold_enable = + of_property_read_bool(np, "somc,mdss-mdp-kickoff-threshold-enable"); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + if (np->name) { pt->timing.name = kstrdup(np->name, GFP_KERNEL); +#ifndef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL pr_info("%s: found new timing \"%s\" (%pK)\n", __func__, np->name, &pt->timing); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } return 0; @@ -2709,6 +2807,9 @@ static int mdss_panel_parse_dt(struct device_node *np, { u32 tmp; int rc, len = 0; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + int i; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ const char *data; static const char *pdest; const char *bridge_chip_name; @@ -2750,6 +2851,21 @@ static int mdss_panel_parse_dt(struct device_node *np, pinfo->mipi.dst_format = DSI_VIDEO_DST_FORMAT_RGB888; } + +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (pinfo->pdest == DISPLAY_1) { + if (of_property_read_bool(np, "somc,mdss-dsi-master")) + pinfo->dsi_master = DISPLAY_1; + else + pinfo->dsi_master = DISPLAY_2; + } else { + if (of_property_read_bool(np, "somc,mdss-dsi-master")) + pinfo->dsi_master = DISPLAY_2; + else + pinfo->dsi_master = DISPLAY_1; + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + pdest = of_get_property(np, "qcom,mdss-dsi-panel-destination", NULL); @@ -2904,6 +3020,28 @@ static int mdss_panel_parse_dt(struct device_node *np, rc = of_property_read_u32(np, "qcom,mdss-dsi-post-init-delay", &tmp); pinfo->mipi.post_init_delay = (!rc ? tmp : 0); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + data = of_get_property(np, + "somc,platform-regulator-settings", &len); + if (!data || len != 7) { + pr_debug("%s:%d, Unable to read Phy regulator settings", + __func__, __LINE__); + } else { + for (i = 0; i < len; i++) + pinfo->mipi.dsi_phy_db.regulator[i] = data[i]; + } + + data = of_get_property(np, + "somc,mdss-dsi-lane-config", &len); + if (!data || len != 45) { + pr_debug("%s:%d, Unable to read Phy lane configure settings", + __func__, __LINE__); + } else { + for (i = 0; i < len; i++) + pinfo->mipi.dsi_phy_db.lanecfg[i] = data[i]; + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + mdss_dsi_parse_trigger(np, &(pinfo->mipi.mdp_trigger), "qcom,mdss-dsi-mdp-trigger"); @@ -2922,6 +3060,14 @@ static int mdss_panel_parse_dt(struct device_node *np, pinfo->mipi.force_clk_lane_hs = of_property_read_bool(np, "qcom,mdss-dsi-force-clock-lane-hs"); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + rc = mdss_dsi_panel_driver_parse_dt(np, ctrl_pdata); + if (rc) { + pr_err("%s: failed to parse somc features\n", __func__); + goto error; + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + rc = mdss_dsi_parse_panel_features(np, ctrl_pdata); if (rc) { pr_err("%s: failed to parse panel features\n", __func__); @@ -2970,6 +3116,10 @@ int mdss_dsi_panel_init(struct device_node *node, int rc = 0; static const char *panel_name; struct mdss_panel_info *pinfo; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct platform_device *pdev; + u32 index; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (!node || !ctrl_pdata) { pr_err("%s: Invalid arguments\n", __func__); @@ -2978,6 +3128,26 @@ int mdss_dsi_panel_init(struct device_node *node, pinfo = &ctrl_pdata->panel_data.panel_info; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + pdev = ctrl_pdata->panel_data.panel_pdev; + + rc = of_property_read_u32(pdev->dev.of_node, "cell-index", &index); + if (rc) { + dev_err(&pdev->dev, + "%s: Cell-index not specified, rc=%d\n", + __func__, rc); + return rc; + } + + if (!index) { + rc = mdss_dsi_panel_create_fs(ctrl_pdata); + if (rc) { + pr_err("%s: mdss_dsi_panel_create_fs rc = %d\n", __func__, rc); + return rc; + } + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + pr_debug("%s:%d\n", __func__, __LINE__); pinfo->panel_name[0] = '\0'; panel_name = of_get_property(node, "qcom,mdss-dsi-panel-name", NULL); @@ -2985,6 +3155,9 @@ int mdss_dsi_panel_init(struct device_node *node, pr_info("%s:%d, Panel name not specified\n", __func__, __LINE__); } else { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + pinfo->panel_id_name = panel_name; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ pr_info("%s: Panel Name = %s\n", __func__, panel_name); strlcpy(&pinfo->panel_name[0], panel_name, MDSS_MAX_PANEL_LEN); } @@ -3007,6 +3180,9 @@ int mdss_dsi_panel_init(struct device_node *node, ctrl_pdata->panel_data.apply_display_setting = mdss_dsi_panel_apply_display_setting; ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode; - +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_panel_driver_init(ctrl_pdata); + pinfo->mipi.switch_mode_pending = false; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ return 0; } diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel_debugfs.c b/drivers/video/fbdev/msm/mdss_dsi_panel_debugfs.c new file mode 100644 index 0000000000000000000000000000000000000000..99ec84d4df4849feb070b56ceae06ae8e97bb932 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_panel_debugfs.c @@ -0,0 +1,1456 @@ +/* + * drivers/video/fbdev/msm/mdss_dsi_panel_debugfs.c + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mdss_fb.h" +#include "mdss_mdp.h" +#include "mdss_dsi.h" +#include "mdss_dsi_panel_driver.h" +#include "mdss_dsi_panel_debugfs.h" + +struct device virtdev; +struct device incelldev; + +static char *res_buf; +static int buf_sz; + +static struct dsi_buf debug_tx_buf; +static struct dsi_buf debug_rx_buf; + +static struct blackscreen_det bs_det; +static struct first_frame_flushed_det fff_det; + +#define DSI_BUF_SIZE 1024 +#define TMP_BUF_SZ 128 +#define MAX_WRITE_DATA 100 + +#define DEFAULT_BS_THRESHOLD 500 + +enum dbg_cmd_type { + DCS, + GEN, +}; + +static void update_res_buf(char *string) +{ + res_buf = krealloc(res_buf, buf_sz + strnlen(string, TMP_BUF_SZ) + 1, + GFP_KERNEL); + if (!res_buf) { + pr_err("%s: Failed to allocate buffer\n", __func__); + return; + } + + memcpy(res_buf + buf_sz, string, strnlen(string, TMP_BUF_SZ) + 1); + buf_sz += strnlen(string, TMP_BUF_SZ); /* Exclude NULL termination */ +} + +static void reset_res_buf(void) +{ + kzfree(res_buf); + res_buf = NULL; + buf_sz = 0; +} + +static void print_params(int dtype, u8 reg, int len, u8 *data) +{ + int i = 0; + char tmp[TMP_BUF_SZ]; + + switch (dtype) { + case DTYPE_GEN_WRITE: + update_res_buf("GEN_WRITE\n"); + break; + case DTYPE_GEN_WRITE1: + update_res_buf("GEN_WRITE1\n"); + break; + case DTYPE_GEN_WRITE2: + update_res_buf("GEN_WRITE2\n"); + break; + case DTYPE_GEN_LWRITE: + update_res_buf("GEN_LWRITE\n"); + break; + case DTYPE_GEN_READ: + update_res_buf("GEN_READ\n"); + break; + case DTYPE_GEN_READ1: + update_res_buf("GEN_READ1\n"); + break; + case DTYPE_GEN_READ2: + update_res_buf("GEN_READ2\n"); + break; + case DTYPE_DCS_LWRITE: + update_res_buf("DCS_LWRITE\n"); + break; + case DTYPE_DCS_WRITE: + update_res_buf("DCS_WRITE\n"); + break; + case DTYPE_DCS_WRITE1: + update_res_buf("DCS_WRITE1\n"); + break; + case DTYPE_DCS_READ: + update_res_buf("DCS_READ\n"); + break; + default: + snprintf(tmp, sizeof(tmp), "Unknown dtype = 0x%x\n", dtype); + update_res_buf(tmp); + } + + if (len > 0) { + snprintf(tmp, sizeof(tmp), "reg=0x%.2X\n", reg); + update_res_buf(tmp); + snprintf(tmp, sizeof(tmp), "len=%d\n", len); + update_res_buf(tmp); + for (i = 0; i < len; i++) { + snprintf(tmp, sizeof(tmp), "data[%d]=0x%.2X\n", i, + data[i]); + update_res_buf(tmp); + } + } else { + update_res_buf("Something went wrong, length is zero.\n"); + snprintf(tmp, sizeof(tmp), + "reg=0x%.2X, len=%d, data[0]=0x%.2X\n", + reg, len, data[0]); + update_res_buf(tmp); + } +} + +static int setup_reg_access(char **buf, const char __user *ubuf, size_t count) +{ + int ret = 0; + + reset_res_buf(); + + *buf = kzalloc(sizeof(char) * count, GFP_KERNEL); + if (!*buf) { + pr_err("%s: Failed to allocate buffer\n", __func__); + ret = -ENOMEM; + goto exit; + } + + if (copy_from_user(*buf, ubuf, count)) { + ret = -EFAULT; + goto exit; + } + return 0; + +exit: + kzfree(*buf); + return ret; +} + +static int get_cmd_type(char *buf, enum dbg_cmd_type *cmd) +{ + int ret = 0; + + if (!strncmp(buf, "dcs", 3)) + *cmd = DCS; + else if (!strncmp(buf, "gen", 3)) + *cmd = GEN; + else + ret = -EFAULT; + return ret; +} + +static int get_parameters(const char *p, u8 *par_buf, int par_buf_size, + int *nbr_params) +{ + int ret = 0; + + while (true) { + if (isspace(*p)) { + p++; + } else { + if (sscanf(p, "%4hhx", &par_buf[*nbr_params]) == 1) { + (*nbr_params)++; + while (isxdigit(*p) || (*p == 'x')) + p++; + } + } + if (*nbr_params > par_buf_size) { + update_res_buf("Too many parameters\n"); + ret = -EINVAL; + goto exit; + } + if (iscntrl(*p)) + break; + } +exit: + return ret; +} + +static u32 panel_cmd_read(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_cmd_desc *cmds, void (*fxn)(int), + char *rbuf, int len) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = cmds; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT; + cmdreq.rlen = len; + cmdreq.rbuf = rbuf; + cmdreq.cb = fxn; /* call back */ + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); + /* + * blocked here, until call back called + */ + + return 0; +} + +static void panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_cmd_desc *cmds, int cmd_cnt, + int link_state) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = cmds; + cmdreq.cmds_cnt = cmd_cnt; + cmdreq.flags = CMD_REQ_COMMIT; + + /* Panel ON/Off commands should be sent in DSI Low Power Mode */ + if (link_state == DSI_LP_MODE) + cmdreq.flags |= CMD_REQ_LP_MODE; + + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); +} + +static ssize_t reg_read(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct msm_fb_data_type *mfd = s->private; + struct mdss_panel_data *pdata; + struct mdss_mdp_ctl *ctl; + u8 params[3]; /* No more than reg + two parameters is allowed */ + char *buf, *rbuf; + const char *p; + int ret; + int nbr_bytes_to_read; + int i; + int j; + enum dbg_cmd_type cmd; + struct dsi_cmd_desc dsi; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + if (!mfd->panel_power_state) { + pr_err("%s: panel is NOT on\n", __func__); + goto exit; + } + + ctl = mfd_to_ctl(mfd); + if (!ctl) + goto exit; + + if (mutex_lock_interruptible(&ctl->lock)) + goto exit; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("%s: no panel connected\n", __func__); + goto exit; + } + + ctrl_pdata = mdss_dsi_get_master_ctrl(pdata); + if (!ctrl_pdata) { + pr_err("%s: Invalid input data\n", __func__); + goto exit; + } + + pr_err("%s\n", __func__); + + ret = setup_reg_access(&buf, ubuf, count); + if (ret) + goto exit; + + ret = get_cmd_type(buf, &cmd); + if (ret) { + update_res_buf("Read - unknown type\n"); + goto fail_free_buf; + } + + p = buf; + p = p+4; + + /* Get nbr_bytes_to_read */ + if (sscanf(p, "%d", &nbr_bytes_to_read) != 1) { + update_res_buf("Read - parameter error\n"); + goto fail_free_buf; + } + + while (isxdigit(*p) || (*p == 'x')) + p++; + + pr_err("%s: nbr_bytes_to_read = %d\n", __func__, nbr_bytes_to_read); + i = 0; + + ret = get_parameters(p, params, ARRAY_SIZE(params), &i); + if (ret) + goto fail_free_buf; + + if (cmd == DCS) { + dsi.dchdr.dtype = DTYPE_DCS_READ; + } else { + if (i == 1) { /* 0 parameters */ + dsi.dchdr.dtype = DTYPE_GEN_READ; + } else if (i == 2) { /* 1 parameter */ + dsi.dchdr.dtype = DTYPE_GEN_READ1; + } else { /* 2 paramters */ + dsi.dchdr.dtype = DTYPE_GEN_READ2; + } + } + dsi.dchdr.last = 1; + dsi.dchdr.vc = 0; + dsi.dchdr.ack = 1; + dsi.dchdr.wait = 5; + dsi.dchdr.dlen = i; + dsi.payload = params; + + pr_err("%s: dtype=%d, last=%d, vc=%d, ack=%d, wait=%d, dlen=%d\n", + __func__, + dsi.dchdr.dtype, dsi.dchdr.last, dsi.dchdr.vc, dsi.dchdr.ack, + dsi.dchdr.wait, dsi.dchdr.dlen); + for (j = 0; j < i; j++) + pr_err("%s: payload[%d] = 0x%x\n", + __func__, j, dsi.payload[j]); + + rbuf = kzalloc(sizeof(char) * nbr_bytes_to_read, GFP_KERNEL); + if (!rbuf) { + pr_err("%s: Failed to allocate buffer\n", __func__); + goto fail_free_buf; + } + + panel_cmd_read(ctrl_pdata, &dsi, NULL, rbuf, nbr_bytes_to_read); + + mutex_unlock(&ctl->lock); + + print_params(dsi.dchdr.dtype, params[0], nbr_bytes_to_read, + rbuf); + + if (rbuf) + kzfree(rbuf); +fail_free_buf: + if (buf) + kzfree(buf); +exit: + return count; +} + +static ssize_t reg_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct msm_fb_data_type *mfd = s->private; + struct mdss_panel_data *pdata; + struct mdss_mdp_ctl *ctl; + char *buf; + const char *p; + enum dbg_cmd_type cmd; + u8 data[MAX_WRITE_DATA]; + int i = 0; + int j; + int ret; + struct dsi_cmd_desc dsi; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + if (!mfd->panel_power_state) { + pr_err("%s: panel is NOT on\n", __func__); + goto exit; + } + + ctl = mfd_to_ctl(mfd); + if (!ctl) + goto exit; + + if (mutex_lock_interruptible(&ctl->lock)) + goto exit; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("%s: no panel connected\n", __func__); + goto exit; + } + + ctrl_pdata = mdss_dsi_get_master_ctrl(pdata); + if (!ctrl_pdata) { + pr_err("%s: Invalid input data\n", __func__); + goto exit; + } + + pr_err("%s\n", __func__); + ret = setup_reg_access(&buf, ubuf, count); + if (ret) + goto exit; + + ret = get_cmd_type(buf, &cmd); + if (ret) { + update_res_buf("Write - unknown type\n"); + goto fail_free_all; + } + + p = buf; + p = p+4; + + /* Get first param, Register */ + if (sscanf(p, "%4hhx", &data[0]) != 1) { + update_res_buf("Write - parameter error\n"); + goto fail_free_all; + } + i++; + + while (isxdigit(*p) || (*p == 'x')) + p++; + + ret = get_parameters(p, data, ARRAY_SIZE(data) - 1, &i); + if (ret) + goto fail_free_all; + + if (cmd == DCS) { + if (i == 1) { /* 0 parameters */ + dsi.dchdr.dtype = DTYPE_DCS_WRITE; + } else if (i == 2) { /* 1 parameter */ + dsi.dchdr.dtype = DTYPE_DCS_WRITE1; + } else { /* Many parameters */ + dsi.dchdr.dtype = DTYPE_DCS_LWRITE; + } + } else { + if (i == 1) { /* 0 parameters */ + dsi.dchdr.dtype = DTYPE_GEN_WRITE; + } else if (i == 2) { /* 1 parameter */ + dsi.dchdr.dtype = DTYPE_GEN_WRITE1; + } else if (i == 3) { /* 2 parameters */ + dsi.dchdr.dtype = DTYPE_GEN_WRITE2; + } else { /* Many parameters */ + dsi.dchdr.dtype = DTYPE_GEN_LWRITE; + } + } + dsi.dchdr.last = 1; + dsi.dchdr.vc = 0; + dsi.dchdr.ack = 0; + dsi.dchdr.wait = 0; + dsi.dchdr.dlen = i; + dsi.payload = data; + + pr_err("%s: last = %d, vc = %d, ack = %d, wait = %d, dlen = %d\n", + __func__, + dsi.dchdr.last, dsi.dchdr.vc, dsi.dchdr.ack, dsi.dchdr.wait, + dsi.dchdr.dlen); + for (j = 0; j < i; j++) + pr_err("%s: payload[%d] = 0x%x\n", + __func__, j, dsi.payload[j]); + + panel_cmds_send(ctrl_pdata, &dsi, 1, DSI_LP_MODE); + + print_params(dsi.dchdr.dtype, data[0], i, dsi.payload); + + mutex_unlock(&ctl->lock); + +fail_free_all: + kzfree(buf); +exit: + return count; +} + +static int result_show(struct seq_file *s, void *unused) +{ + seq_printf(s, "%s", res_buf); + if (!res_buf) + seq_printf(s, "\n"); + return 0; +} + +static int read_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, inode->i_private); +} + +static const struct file_operations read_fops = { + .owner = THIS_MODULE, + .open = read_open, + .write = reg_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int write_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, inode->i_private); +} + +static const struct file_operations write_fops = { + .owner = THIS_MODULE, + .open = write_open, + .write = reg_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static int result_open(struct inode *inode, struct file *file) +{ + return single_open(file, result_show, inode->i_private); +} + +static const struct file_operations result_fops = { + .owner = THIS_MODULE, + .open = result_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t incell_panel_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + incell_pw_status power_status; + bool display = false; + bool touch = false; + + power_status.touch_power = false; + power_status.display_power = false; + ret = incell_get_power_status(&power_status); + + if (ret == INCELL_OK) { + display = power_status.display_power; + touch = power_status.touch_power; + } else { + return scnprintf(buf, PAGE_SIZE, "%s\n", "Get failed"); + } + + return scnprintf(buf, PAGE_SIZE, "touch = %d, display = %d\n", + touch, display); +} + +static ssize_t incell_intf_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc = INCELL_OK, mode; + incell_intf_mode intf_mode; + + rc = kstrtoint(buf, 10, &mode); + if (rc < 0) { + pr_err("%s: Error, buf = %s\n", __func__, buf); + return rc; + } + + intf_mode = (incell_intf_mode)(mode); + pr_notice("%s: buf = %s\n", __func__, buf); + rc = incell_control_mode(intf_mode, INCELL_FORCE); + pr_notice("%s: Incell execution %s\n", __func__, + rc == INCELL_OK ? "suscess" : "fail"); + pr_notice("%s: return %d\n", __func__, rc); + return count; +} + +static ssize_t incell_power_lock_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc = INCELL_OK, mode; + incell_pw_lock intf_lock; + incell_pw_status power_status; + + rc = kstrtoint(buf, 10, &mode); + if (rc < 0) { + pr_err("%s: Error, buf = %s\n", __func__, buf); + return rc; + } + + intf_lock = (incell_pw_lock)(mode); + pr_notice("%s: buf = %s\n", __func__, buf); + rc = incell_power_lock_ctrl(mode, &power_status); + pr_notice("%s: Incell execution %s\n", __func__, + rc == INCELL_OK ? "suscess" : "fail"); + pr_notice("%s: return %d\n", __func__, rc); + return count; +} + +static ssize_t incell_ewu_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc = INCELL_OK, mode; + incell_ewu_mode intf_ewu; + + rc = kstrtoint(buf, 10, &mode); + if (rc < 0) { + pr_err("%s: Error, buf = %s\n", __func__, buf); + return rc; + } + + intf_ewu = (incell_ewu_mode)(mode); + pr_notice("%s: buf = %s\n", __func__, buf); + incell_ewu_mode_ctrl(intf_ewu); + pr_notice("%s: Incell execution %s\n", __func__, + rc == INCELL_OK ? "suscess" : "fail"); + pr_notice("%s: return %d\n", __func__, rc); + return count; +} + +static struct device_attribute incell_attributes[] = { + __ATTR(incell_status, S_IRUGO, incell_panel_status_show, NULL), + __ATTR(incell_intf, S_IWUSR|S_IWGRP, NULL, incell_intf_type_store), + __ATTR(incell_lock, S_IWUSR|S_IWGRP, NULL, incell_power_lock_store), + __ATTR(incell_ewu, S_IWUSR|S_IWGRP, NULL, incell_ewu_store), +}; + +static int incell_register_attributes(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(incell_attributes); i++) + if (device_create_file(dev, incell_attributes + i)) + goto err; + + return 0; + +err: + dev_err(dev, "%s: Unable to create interface\n", __func__); + for (--i; i >= 0 ; i--) + device_remove_file(dev, incell_attributes + i); + + return -ENODEV; +} + +static int incell_create_debugfs(struct msm_fb_data_type *mfd) +{ + int rc = 0; + char *path_name = "incell_dsi_panel"; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct mdss_mdp_ctl *ctl = mdata->ctl_off; + struct mdss_panel_data *pdata = ctl->panel_data; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + ctrl_pdata = container_of(pdata, + struct mdss_dsi_ctrl_pdata, panel_data); + if (!ctrl_pdata) { + pr_err("%s: Invalid input data\n", __func__); + rc = -EINVAL; + goto out; + } + + dev_set_name(&incelldev, "%s", path_name); + rc = device_register(&incelldev); + if (rc) { + pr_err("%s: device_register rc = %d\n", __func__, rc); + goto out; + } + + rc = incell_register_attributes(&incelldev); + if (rc) { + pr_err("%s: register_attributes rc = %d\n", __func__, rc); + goto err; + } + + dev_set_drvdata(&incelldev, ctrl_pdata); + goto out; + +err: + device_unregister(&incelldev); +out: + return rc; +} + +static ssize_t mdss_dsi_panel_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + char const *id = ctrl_pdata->panel_data.panel_info.panel_id_name ? + ctrl_pdata->panel_data.panel_info.panel_id_name : "default"; + + return scnprintf(buf, PAGE_SIZE, "%s\n", id); +} + +static ssize_t mdss_dsi_panel_pcc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + struct mdss_pcc_data *pcc_data; + u32 r, g, b; + + ctrl_pdata = mdss_dsi_get_master_ctrl(&ctrl_pdata->panel_data); + pcc_data = &ctrl_pdata->spec_pdata->pcc_data; + + r = g = b = 0; + if (!pcc_data->color_tbl) { + pr_err("%s: Panel has no color table\n", __func__); + goto exit; + } + if (pcc_data->u_data == 0 && pcc_data->v_data == 0) { + pr_err("%s: u,v are 0.\n", __func__); + goto exit; + } + if (pcc_data->tbl_idx >= pcc_data->tbl_size) { + pr_err("%s: Invalid color area(idx=%d)\n", + __func__, pcc_data->tbl_idx); + goto exit; + } + if (pcc_data->color_tbl[pcc_data->tbl_idx].color_type == UNUSED) { + pr_err("%s: Unsupported color type(idx=%d)\n", + __func__, pcc_data->tbl_idx); + goto exit; + } + r = pcc_data->color_tbl[pcc_data->tbl_idx].r_data; + g = pcc_data->color_tbl[pcc_data->tbl_idx].g_data; + b = pcc_data->color_tbl[pcc_data->tbl_idx].b_data; +exit: + return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x ", r, g, b); +} + +static ssize_t mdss_dsi_panel_srgb_pcc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + struct mdss_pcc_data *pcc_data; + u32 r, g, b; + + ctrl_pdata = mdss_dsi_get_master_ctrl(&ctrl_pdata->panel_data); + pcc_data = &ctrl_pdata->spec_pdata->srgb_pcc_data; + + r = g = b = 0; + if (!pcc_data->color_tbl) { + pr_err("%s: Panel has no color table\n", __func__); + goto exit; + } + if (pcc_data->u_data == 0 && pcc_data->v_data == 0) { + pr_err("%s: u,v are 0.\n", __func__); + goto exit; + } + if (pcc_data->tbl_idx >= pcc_data->tbl_size) { + pr_err("%s: Invalid color area(idx=%d)\n", + __func__, pcc_data->tbl_idx); + goto exit; + } + if (pcc_data->color_tbl[pcc_data->tbl_idx].color_type == UNUSED) { + pr_err("%s: Unsupported color type(idx=%d)\n", + __func__, pcc_data->tbl_idx); + goto exit; + } + r = pcc_data->color_tbl[pcc_data->tbl_idx].r_data; + g = pcc_data->color_tbl[pcc_data->tbl_idx].g_data; + b = pcc_data->color_tbl[pcc_data->tbl_idx].b_data; +exit: + return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x ", r, g, b); +} + +static ssize_t mdss_dsi_panel_vivid_pcc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + struct mdss_pcc_data *pcc_data; + u32 r, g, b; + + ctrl_pdata = mdss_dsi_get_master_ctrl(&ctrl_pdata->panel_data); + pcc_data = &ctrl_pdata->spec_pdata->vivid_pcc_data; + + r = g = b = 0; + if (!pcc_data->color_tbl) { + pr_err("%s: Panel has no color table\n", __func__); + goto exit; + } + if (pcc_data->u_data == 0 && pcc_data->v_data == 0) { + pr_err("%s: u,v are 0.\n", __func__); + goto exit; + } + if (pcc_data->tbl_idx >= pcc_data->tbl_size) { + pr_err("%s: Invalid color area(idx=%d)\n", + __func__, pcc_data->tbl_idx); + goto exit; + } + if (pcc_data->color_tbl[pcc_data->tbl_idx].color_type == UNUSED) { + pr_err("%s: Unsupported color type(idx=%d)\n", + __func__, pcc_data->tbl_idx); + goto exit; + } + r = pcc_data->color_tbl[pcc_data->tbl_idx].r_data; + g = pcc_data->color_tbl[pcc_data->tbl_idx].g_data; + b = pcc_data->color_tbl[pcc_data->tbl_idx].b_data; +exit: + return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x ", r, g, b); +} + +static ssize_t mdss_dsi_panel_hdr_pcc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + struct mdss_pcc_data *pcc_data; + u32 r, g, b; + + ctrl_pdata = mdss_dsi_get_master_ctrl(&ctrl_pdata->panel_data); + pcc_data = &ctrl_pdata->spec_pdata->hdr_pcc_data; + + r = g = b = 0; + if (!pcc_data->color_tbl) { + pr_err("%s: Panel has no color table\n", __func__); + goto exit; + } + if (pcc_data->u_data == 0 && pcc_data->v_data == 0) { + pr_err("%s: u,v are 0.\n", __func__); + goto exit; + } + if (pcc_data->tbl_idx >= pcc_data->tbl_size) { + pr_err("%s: Invalid color area(idx=%d)\n", + __func__, pcc_data->tbl_idx); + goto exit; + } + if (pcc_data->color_tbl[pcc_data->tbl_idx].color_type == UNUSED) { + pr_err("%s: Unsupported color type(idx=%d)\n", + __func__, pcc_data->tbl_idx); + goto exit; + } + r = pcc_data->color_tbl[pcc_data->tbl_idx].r_data; + g = pcc_data->color_tbl[pcc_data->tbl_idx].g_data; + b = pcc_data->color_tbl[pcc_data->tbl_idx].b_data; +exit: + return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x ", r, g, b); +} + +static ssize_t mdss_dsi_panel_color_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdss_panel_specific_pdata *spec_pdata; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + + ctrl_pdata = mdss_dsi_get_master_ctrl(&ctrl_pdata->panel_data); + spec_pdata = ctrl_pdata->spec_pdata; + + return scnprintf(buf, PAGE_SIZE, "%d", spec_pdata->color_mode); +} + +static ssize_t mdss_dsi_panel_color_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mdss_panel_specific_pdata *spec_pdata; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + int mode, rc = 0; + + ctrl_pdata = mdss_dsi_get_master_ctrl(&ctrl_pdata->panel_data); + spec_pdata = ctrl_pdata->spec_pdata; + + if (sscanf(buf, "%d", &mode) < 0) { + pr_err("sscanf failed to set mode. keep current mode=%d\n", + spec_pdata->color_mode); + rc = -EINVAL; + goto exit; + } + + switch (mode) { + case CLR_MODE_SELECT_SRGB: + case CLR_MODE_SELECT_DCIP3: + case CLR_MODE_SELECT_PANELNATIVE: + spec_pdata->color_mode = mode; + break; + default: + pr_err("failed set mode:invalid mode=%d. keep current mode" + "=%d\n", mode, spec_pdata->color_mode); + rc = -EINVAL; + break; + } + +exit: + return !rc ? count : rc; + +} + +static ssize_t mdss_dsi_panel_frame_counter(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fps_data fpsd = mdss_dsi_panel_driver_get_fps_data(); + + return scnprintf(buf, PAGE_SIZE, "%i\n", fpsd.frame_counter); +} + +static ssize_t mdss_dsi_panel_frames_per_ksecs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fps_data fpsd = mdss_dsi_panel_driver_get_fps_data(); + + return scnprintf(buf, PAGE_SIZE, "%i\n", fpsd.fpks); +} + +static ssize_t mdss_dsi_panel_vsyncs_per_ksecs_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return mdss_dsi_panel_driver_vsyncs_per_ksecs_store(dev, buf, count); +} + +static ssize_t mdss_dsi_panel_vsyncs_per_ksecs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fps_data vpsd = mdss_dsi_panel_driver_get_vps_data(); + + if (vpsd.vps_en) + return scnprintf(buf, PAGE_SIZE, "%i\n", vpsd.fpks); + else + return scnprintf(buf, PAGE_SIZE, + "This function is invalid now.\n" + "Please read again after writing ON.\n"); +} + +static ssize_t mdss_dsi_panel_change_fpks_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return mdss_dsi_panel_driver_change_fpks_store(dev, attr, buf, count); +} + +static ssize_t mdss_dsi_panel_change_fpks_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return mdss_dsi_panel_driver_change_fpks_show(dev, attr, buf); +} + +static ssize_t mdss_dsi_panel_change_fps_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return mdss_dsi_panel_driver_change_fps_store(dev, attr, buf, count); +} + +static ssize_t mdss_dsi_panel_change_fps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return mdss_dsi_panel_driver_change_fps_show(dev, attr, buf); +} + +static ssize_t mdss_dsi_panel_esd_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdss_panel_specific_pdata *spec_pdata; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + + ctrl_pdata = mdss_dsi_get_master_ctrl(&ctrl_pdata->panel_data); + spec_pdata = ctrl_pdata->spec_pdata; + + return scnprintf(buf, PAGE_SIZE, "esd_enable = %u\n", + spec_pdata->esd_enable_without_xlog); +} + +static ssize_t mdss_dsi_panel_esd_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc = 0; + struct mdss_panel_specific_pdata *spec_pdata; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + + ctrl_pdata = mdss_dsi_get_master_ctrl(&ctrl_pdata->panel_data); + spec_pdata = ctrl_pdata->spec_pdata; + + rc = kstrtouint(buf, 10, &spec_pdata->esd_enable_without_xlog); + if (rc < 0) { + pr_err("%s: Error, buf = %s\n", __func__, buf); + return rc; + } + pr_notice("%s: esd_enable = %u\n", + __func__, spec_pdata->esd_enable_without_xlog); + return count; +} + +static struct device_attribute panel_attributes[] = { + __ATTR(panel_id, S_IRUSR, mdss_dsi_panel_id_show, NULL), + __ATTR(cc, S_IRUGO, mdss_dsi_panel_pcc_show, NULL), + __ATTR(srgb_cc, S_IRUGO, mdss_dsi_panel_srgb_pcc_show, NULL), + __ATTR(vivid_cc, S_IRUGO, mdss_dsi_panel_vivid_pcc_show, NULL), + __ATTR(hdr_cc, S_IRUGO, mdss_dsi_panel_hdr_pcc_show, NULL), + __ATTR(c_mode, S_IRUGO|S_IWUSR|S_IWGRP, + mdss_dsi_panel_color_mode_show, + mdss_dsi_panel_color_mode_store), + __ATTR(frame_counter, S_IRUGO, mdss_dsi_panel_frame_counter, NULL), + __ATTR(frames_per_ksecs, S_IRUGO, + mdss_dsi_panel_frames_per_ksecs, NULL), + __ATTR(vsyncs_per_ksecs, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP, + mdss_dsi_panel_vsyncs_per_ksecs_show, + mdss_dsi_panel_vsyncs_per_ksecs_store), + __ATTR(change_fps, S_IRUGO|S_IWUSR|S_IWGRP, + mdss_dsi_panel_change_fps_show, + mdss_dsi_panel_change_fps_store), + __ATTR(change_fpks, S_IRUGO|S_IWUSR|S_IWGRP, + mdss_dsi_panel_change_fpks_show, + mdss_dsi_panel_change_fpks_store), + __ATTR(esd_enable_wo_xlog, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP, + mdss_dsi_panel_esd_enable_show, + mdss_dsi_panel_esd_enable_store), +}; + +static int register_attributes(struct device *dev) +{ + int i; + for (i = 0; i < ARRAY_SIZE(panel_attributes); i++) + if (device_create_file(dev, panel_attributes + i)) + goto error; + return 0; +error: + dev_err(dev, "%s: Unable to create interface\n", __func__); + for (--i; i >= 0 ; i--) + device_remove_file(dev, panel_attributes + i); + return -ENODEV; +} + +int mdss_dsi_panel_create_fs(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + int rc = 0; + char *path_name = "mdss_dsi_panel"; + + dev_set_name(&virtdev, "%s", path_name); + rc = device_register(&virtdev); + if (rc) { + pr_err("%s: device_register rc = %d\n", __func__, rc); + goto err; + } + + rc = register_attributes(&virtdev); + if (rc) { + pr_err("%s: register_attributes rc = %d\n", __func__, rc); + device_unregister(&virtdev); + goto err; + } + + dev_set_drvdata(&virtdev, ctrl_pdata); + +err: + return rc; +} + +static void blackscreen_det_init(void) +{ + bs_det.threshold = DEFAULT_BS_THRESHOLD; + bs_det.cnt_crash = 0; + bs_det.cnt_timeout = 0; + memset(&bs_det.timestamp, 0x00, sizeof(ktime_t)); + bs_det.done = 0; + init_waitqueue_head(&bs_det.wait_queue); +} + +static void first_frame_flushed_det_init(void) +{ + memset(&fff_det.timestamp, 0x00, sizeof(ktime_t)); + fff_det.done = 0; + init_waitqueue_head(&fff_det.wait_queue); +} + +static void crash_counter_reset(void) +{ + bs_det.cnt_crash = 0; +} + +static void mdss_dsi_panel_blackscreen_det(void) +{ + bs_det.cnt_timeout++; + bs_det.cnt_crash++; + bs_det.timestamp = ktime_get_boottime(); + bs_det.done = 1; + wake_up(&bs_det.wait_queue); + + if (bs_det.cnt_crash > bs_det.threshold) + panic("[BScreenD] : panel command timeout error expired"); +} + +static void mdss_dsi_panel_fff_time_update( + struct mdss_panel_specific_pdata *spec_pdata) +{ + fff_det.timestamp = ktime_get_boottime(); + spec_pdata->resume_started = false; + fff_det.done = 1; + wake_up(&fff_det.wait_queue); +} + +static char *format_timestamp(ktime_t timestamp) +{ + static char buf[20]; + + scnprintf(buf, sizeof(buf), "%lld", ktime_to_ms(timestamp)); + return &buf[0]; +} + +static int bs_threshold_show(struct seq_file *s, void *unused) +{ + struct msm_fb_data_type *mfd = s->private; + struct mdss_panel_data *pdata; + struct mdss_dsi_ctrl_pdata *ctrl_pdata; + struct mdss_panel_info *pinfo; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("%s: no panel connected\n", __func__); + goto exit; + } + + ctrl_pdata = mdss_dsi_get_master_ctrl(pdata); + if (!ctrl_pdata) { + pr_err("%s: Invalid input data\n", __func__); + goto exit; + } + pinfo = &ctrl_pdata->panel_data.panel_info; + + if (pinfo->mipi.mode == DSI_CMD_MODE) { + seq_printf(s, "%d\n", + bs_det.threshold); + } else { + seq_printf(s, "VideoMode isn't supported\n"); + } +exit: + return 0; +} + +static int timeout_counter_show(struct seq_file *s, void *unused) +{ + struct msm_fb_data_type *mfd = s->private; + struct mdss_panel_data *pdata; + struct mdss_dsi_ctrl_pdata *ctrl_pdata; + struct mdss_panel_info *pinfo; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("%s: no panel connected\n", __func__); + goto exit; + } + + ctrl_pdata = mdss_dsi_get_master_ctrl(pdata); + if (!ctrl_pdata) { + pr_err("%s: Invalid input data\n", __func__); + goto exit; + } + pinfo = &ctrl_pdata->panel_data.panel_info; + + if (pinfo->mipi.mode == DSI_CMD_MODE) { + seq_printf(s, "%s %d\n", + format_timestamp(bs_det.timestamp), bs_det.cnt_timeout); + } else { + seq_printf(s, "VideoMode isn't supported\n"); + } +exit: + return 0; +} + +static int crash_counter_show(struct seq_file *s, void *unused) +{ + struct msm_fb_data_type *mfd = s->private; + struct mdss_panel_data *pdata; + struct mdss_dsi_ctrl_pdata *ctrl_pdata; + struct mdss_panel_info *pinfo; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("%s: no panel connected\n", __func__); + goto exit; + } + + ctrl_pdata = mdss_dsi_get_master_ctrl(pdata); + if (!ctrl_pdata) { + pr_err("%s: Invalid input data\n", __func__); + goto exit; + } + pinfo = &ctrl_pdata->panel_data.panel_info; + + if (pinfo->mipi.mode == DSI_CMD_MODE) { + seq_printf(s, "%d\n", + bs_det.cnt_crash); + } else { + seq_printf(s, "VideoMode isn't supported\n"); + } +exit: + return 0; +} + +static int fff_time_show(struct seq_file *s, void *unused) +{ + seq_printf(s, "%s\n", + format_timestamp(fff_det.timestamp)); + return 0; +} + +static int bs_threshold_open(struct inode *inode, struct file *file) +{ + return single_open(file, bs_threshold_show, inode->i_private); +} + +static ssize_t bs_threshold_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct msm_fb_data_type *mfd = s->private; + struct mdss_panel_data *pdata; + int ret; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo; + char *buf = NULL; + u16 threshold; + + if (!mfd->panel_power_state) { + pr_err("%s: panel is NOT on\n", __func__); + ret = -EINVAL; + goto exit; + } + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("%s: no panel connected\n", __func__); + ret = -EINVAL; + goto exit; + } + + ctrl_pdata = mdss_dsi_get_master_ctrl(pdata); + if (!ctrl_pdata) { + pr_err("%s: Invalid input data\n", __func__); + ret = -EINVAL; + goto exit; + } + pinfo = &ctrl_pdata->panel_data.panel_info; + + if (pinfo->mipi.mode == DSI_CMD_MODE) { + ret = setup_reg_access(&buf, ubuf, count); + if (ret) { + pr_err("%s: Error, setup_reg_access\n", __func__); + ret = -EINVAL; + goto exit; + } + if (kstrtou16(buf, 10, &threshold)) { + pr_err("%s: Error, buf = %s\n", __func__, buf); + ret = -EINVAL; + goto fail_free_all; + } + pr_err("%s: threshold = %d\n", __func__, threshold); + ret = threshold; + bs_det.threshold = threshold; + + if (bs_det.cnt_crash > bs_det.threshold) + pr_notice("crash_counter is already beyond threshold\n"); + } else { + pr_err("VideoMode isn't supported\n"); + ret = -EINVAL; + goto exit; + } + +fail_free_all: + kzfree(buf); +exit: + return ret; +} + +static void mdss_dsi_panel_black_screen_off( + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + ctrl_pdata->spec_pdata->resume_started = true; +} + +static const struct file_operations bs_threshold_fops = { + .owner = THIS_MODULE, + .open = bs_threshold_open, + .read = seq_read, + .write = bs_threshold_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static int timeout_counter_open(struct inode *inode, struct file *file) +{ + return single_open(file, timeout_counter_show, inode->i_private); +} + +static unsigned int timeout_counter_poll(struct file *filp, + struct poll_table_struct *wait) +{ + unsigned int mask = 0; + + poll_wait(filp, &bs_det.wait_queue, wait); + if (bs_det.done) { + mask |= (POLLIN | POLLRDNORM); + bs_det.done = 0; + } + + return mask; +} + +static const struct file_operations timeout_counter_fops = { + .owner = THIS_MODULE, + .open = timeout_counter_open, + .read = seq_read, + .llseek = seq_lseek, + .poll = timeout_counter_poll, + .release = single_release, +}; + +static int crash_counter_open(struct inode *inode, struct file *file) +{ + return single_open(file, crash_counter_show, inode->i_private); +} + +static const struct file_operations crash_counter_fops = { + .owner = THIS_MODULE, + .open = crash_counter_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int fff_time_open(struct inode *inode, struct file *file) +{ + return single_open(file, fff_time_show, inode->i_private); +} + +static unsigned int fff_time_poll(struct file *filp, + struct poll_table_struct *wait) +{ + unsigned int mask = 0; + + poll_wait(filp, &fff_det.wait_queue, wait); + if (fff_det.done) { + mask |= (POLLIN | POLLRDNORM); + fff_det.done = 0; + } + + return mask; +} + +static const struct file_operations fff_time_fops = { + .owner = THIS_MODULE, + .open = fff_time_open, + .read = seq_read, + .llseek = seq_lseek, + .poll = fff_time_poll, + .release = single_release, +}; +static void mipi_dsi_panel_create_debugfs(struct msm_fb_data_type *mfd) +{ + struct device *dev; + struct dentry *root; + struct mdss_panel_data *pdata; + struct platform_device *pdev; + struct mdss_panel_specific_pdata *spec_pdata; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("%s: no panel connected\n", __func__); + return; + } + + blackscreen_det_init(); + first_frame_flushed_det_init(); + spec_pdata = mdss_panel2spec_pdata(pdata); + spec_pdata->crash_counter_reset = crash_counter_reset; + spec_pdata->blackscreen_det = mdss_dsi_panel_blackscreen_det; + spec_pdata->fff_time_update = mdss_dsi_panel_fff_time_update; + spec_pdata->black_screen_off = mdss_dsi_panel_black_screen_off; + spec_pdata->resume_started = false; + + pdev = pdata->panel_pdev; + if (!pdev) { + pr_err("%s: no device\n", __func__); + return; + } + + dev = &pdev->dev; + + if (!&dev->kobj) { + pr_err("%s: no &dev->kobj\n", __func__); + return; + } + + mdss_dsi_buf_alloc(dev, &debug_tx_buf, ALIGN(DSI_BUF_SIZE, SZ_4K)); + mdss_dsi_buf_alloc(dev, &debug_rx_buf, ALIGN(DSI_BUF_SIZE, SZ_4K)); + + dev_notice(dev, "%s: create folder %s\n", __func__, + kobject_name(&dev->kobj)); + + root = debugfs_create_dir("mdss_dsi_panel", 0); + + if (!root) { + dev_err(dev, "%s: dbgfs create dir failed\n", __func__); + } else { + if (!debugfs_create_file("read", S_IWUSR, root, mfd, + &read_fops)) { + dev_err(dev, "%s: failed to create dbgfs read file\n", + __func__); + return; + } + if (!debugfs_create_file("write", S_IWUSR, root, mfd, + &write_fops)) { + dev_err(dev, "%s: failed to create dbgfs write file\n", + __func__); + return; + } + if (!debugfs_create_file("result", S_IRUGO, root, mfd, + &result_fops)) { + dev_err(dev, "%s: failed to create dbgfs result file\n", + __func__); + return; + } + if (!debugfs_create_file("bs_threshold", S_IRUGO|S_IWUSR, root, + mfd, &bs_threshold_fops)) { + dev_err(dev, + "%s: failed to create dbgfs bs_threshold file\n", + __func__); + return; + } + if (!debugfs_create_file("timeout_counter", S_IRUGO, root, mfd, + &timeout_counter_fops)) { + dev_err(dev, + "%s: failed to create dbgfs timeout_counter file\n", + __func__); + return; + } + if (!debugfs_create_file("crash_counter", S_IRUGO, root, mfd, + &crash_counter_fops)) { + dev_err(dev, + "%s: failed to create dbgfs crash_counter file\n", + __func__); + return; + } + if (!debugfs_create_file("fff_time", S_IRUGO, root, mfd, + &fff_time_fops)) { + dev_err(dev, "%s: failed to create dbgfs fff_time file\n", + __func__); + return; + } + } +} + +int mdss_dsi_create_debugfs(struct msm_fb_data_type *mfd) +{ + int rc; + + mipi_dsi_panel_create_debugfs(mfd); + + rc = incell_create_debugfs(mfd); + if (rc) { + pr_err("%s: incell_create_debugfs rc = %d\n", __func__, rc); + goto out; + } +out: + return rc; +} diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel_debugfs.h b/drivers/video/fbdev/msm/mdss_dsi_panel_debugfs.h new file mode 100644 index 0000000000000000000000000000000000000000..968d1362501eeef6849e5401a8b200d956330375 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_panel_debugfs.h @@ -0,0 +1,39 @@ +/* + * drivers/video/fbdev/msm/mdss_dsi_panel_debugfs.h + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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 MDSS_DSI_PANEL_DEBUGFS_H +#define MDSS_DSI_PANEL_DEBUGFS_H + +#include "mdss_dsi.h" + +struct blackscreen_det { + u16 threshold; + u16 cnt_timeout; + u16 cnt_crash; + ktime_t timestamp; + int done; + wait_queue_head_t wait_queue; +}; + +struct first_frame_flushed_det { + ktime_t timestamp; + int done; + wait_queue_head_t wait_queue; +}; + +int mdss_dsi_panel_create_fs(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +int mdss_dsi_create_debugfs(struct msm_fb_data_type *mfd); +#endif /* MDSS_DSI_PANEL_DEBUGFS_H */ diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel_driver.c b/drivers/video/fbdev/msm/mdss_dsi_panel_driver.c new file mode 100644 index 0000000000000000000000000000000000000000..956b8b3427d73af876f5952bf1b46a2b6e7d24b1 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_panel_driver.c @@ -0,0 +1,2849 @@ +/* drivers/video/fbdev/msm/mdss_dsi_panel_driver.c + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdss.h" +#include "mdss_panel.h" +#include "mdss_dsi.h" +#include "mdss_debug.h" +#include "mdss_dsi_panel_driver.h" +#include "mdss_dsi_panel_debugfs.h" +#include + +static bool gpio_req; +static u32 down_period; + +static struct fps_data fpsd, vpsd; + +#define ADC_PNUM 2 +#define ADC_RNG_MIN 0 +#define ADC_RNG_MAX 1 + +#define NODE_OF_HYBRID "/soc/dsi_panel_pwr_supply_hybrid_incell" +#define NODE_OF_FULL "/soc/dsi_panel_pwr_supply_full_incell" + +static unsigned long lcdid_adc = 1505000; +static void vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t t); + +static struct mdss_mdp_vsync_handler vs_handle; +static bool display_onoff_state; + +static int __init lcdid_adc_setup(char *str) +{ + unsigned long res; + + if (!*str) + return 0; + if (!kstrtoul(str, 0, &res)) + lcdid_adc = res; + + return 1; +} +__setup("lcdid_adc=", lcdid_adc_setup); + +void mdss_dsi_panel_driver_detection( + struct platform_device *pdev, + struct device_node **np) +{ + u32 res[ADC_PNUM]; + int rc = 0; + struct device_node *parent; + struct device_node *next; + u32 dev_index = 0; + u32 dsi_index = 0; + u32 adc_uv = 0; + + rc = of_property_read_u32(pdev->dev.of_node, "cell-index", &dev_index); + if (rc) { + dev_err(&pdev->dev, + "%s: Cell-index not specified, rc=%d\n", + __func__, rc); + return; + } + + parent = of_get_parent(*np); + + adc_uv = lcdid_adc; + pr_notice("%s: physical:%d\n", __func__, adc_uv); + + for_each_child_of_node(parent, next) { + rc = of_property_read_u32(next, "somc,dsi-index", &dsi_index); + if (rc) + dsi_index = 0; + if (dsi_index != dev_index) + continue; + + rc = of_property_read_u32_array(next, + "somc,lcd-id-adc", res, ADC_PNUM); + if (rc) + continue; + if (adc_uv < res[ADC_RNG_MIN] || res[ADC_RNG_MAX] < adc_uv) + continue; + + *np = next; + break; + } +} + +static int mdss_dsi_panel_driver_vreg_name_to_config( + struct mdss_dsi_ctrl_pdata *ctrl_pdata, + struct dss_vreg *config, char *name) +{ + struct dss_vreg *vreg_config = ctrl_pdata->panel_power_data.vreg_config; + int num_vreg = ctrl_pdata->panel_power_data.num_vreg; + int i = 0; + int valid = -EINVAL; + + for (i = 0; i < num_vreg; i++) { + if (!strcmp(name, vreg_config[i].vreg_name)) { + *config = vreg_config[i]; + valid = 0; + break; + } + } + + return valid; +} + +static int mdss_dsi_panel_driver_vreg_ctrl( + struct mdss_dsi_ctrl_pdata *ctrl_pdata, char *vreg, bool enable) +{ + struct dss_vreg vreg_config; + struct mdss_panel_power_seq *pw_seq = NULL; + int valid = 0; + int wait = 0; + int ret = 0; + + valid = mdss_dsi_panel_driver_vreg_name_to_config( + ctrl_pdata, &vreg_config, vreg); + + if (!valid) { + if (enable) { + ret = msm_dss_enable_vreg(&vreg_config, 1, 1); + pw_seq = &ctrl_pdata->spec_pdata->on_seq; + } else { + ret = msm_dss_enable_vreg(&vreg_config, 1, 0); + pw_seq = &ctrl_pdata->spec_pdata->off_seq; + } + + if (!strcmp(vreg, "vdd")) + wait = pw_seq->disp_vdd; + else if (!strcmp(vreg, "vddio")) + wait = pw_seq->disp_vddio; + else if (!strcmp(vreg, "lab")) + wait = pw_seq->disp_vsp; + else if (!strcmp(vreg, "ibb")) + wait = pw_seq->disp_vsn; + else if (!strcmp(vreg, "touch-avdd")) + wait = pw_seq->touch_avdd; + else + wait = 0; + + if (!ret && wait) + usleep_range(wait * 1000, wait * 1000 + 100); + } + + return ret; +} + +bool mdss_dsi_panel_driver_is_power_on(unsigned char state) +{ + bool ret = false; + + if (state & INCELL_POWER_STATE_ON) + ret = true; + + pr_debug("%s: In-Cell %s state\n", __func__, (ret ? "on" : "off")); + + return ret; +} + +bool mdss_dsi_panel_driver_is_power_lock(unsigned char state) +{ + bool ret = false; + + if (state & INCELL_LOCK_STATE_ON) + ret = true; + + pr_debug("%s: In-Cell I/F %s state\n", __func__, + (ret ? "Lock" : "Unlock")); + + return ret; +} + +bool mdss_dsi_panel_driver_is_ewu(unsigned char state) +{ + bool ret = false; + + if (state & INCELL_EWU_STATE_ON) + ret = true; + + pr_debug("%s: In-Cell I/F %s state\n", __func__, + (ret ? "EWU" : "NORMAL")); + + return ret; +} + +bool mdss_dsi_panel_driver_is_system_on(unsigned char state) +{ + bool ret = false; + + if (state & INCELL_SYSTEM_STATE_ON) + ret = true; + + pr_debug("%s: In-Cell system %s state\n", __func__, + (ret ? "resume" : "suspend")); + + return ret; +} + +static bool mdss_dsi_panel_driver_is_seq_for_ewu(void) +{ + struct incell_ctrl *incell = incell_get_info(); + + if (incell && + (incell->seq == POWER_ON_EWU_SEQ)) + return true; + + return false; +} + +static bool mdss_dsi_panel_driver_is_incell_operation(void) +{ + struct incell_ctrl *incell = incell_get_info(); + + if (incell && + (incell->incell_intf_operation == INCELL_TOUCH_RUN)) + return true; + + return false; +} + +static int mdss_dsi_panel_calculation_sleep( + struct mdss_dsi_ctrl_pdata *ctrl_pdata, + int gpio, bool enable) +{ + struct mdss_panel_specific_pdata *spec_pdata = ctrl_pdata->spec_pdata; + struct mdss_panel_power_seq *pw_seq = NULL; + int wait = 0; + + if (mdss_dsi_panel_driver_is_seq_for_ewu() && + (gpio == spec_pdata->touch_reset_gpio) && + !enable) { + if (&spec_pdata->ewu_seq) + pw_seq = &spec_pdata->ewu_seq; + else + pw_seq = &spec_pdata->on_seq; + } else { + if (enable) + pw_seq = &spec_pdata->on_seq; + else + pw_seq = &spec_pdata->off_seq; + } + + if (gpio == spec_pdata->disp_dcdc_en_gpio) + wait = pw_seq->disp_dcdc; + else if (gpio == spec_pdata->touch_vddio_gpio) + wait = pw_seq->touch_vddio; + else if (gpio == spec_pdata->touch_reset_gpio) + wait = pw_seq->touch_reset; + else if (gpio == spec_pdata->touch_int_gpio) + wait = pw_seq->touch_intn; + else + wait = 0; + + wait = wait * 1000; + return wait; +} + +static void mdss_dsi_panel_driver_gpio_output( + struct mdss_dsi_ctrl_pdata *ctrl_pdata, + int gpio, bool enable, int value) +{ + int wait; + + wait = mdss_dsi_panel_calculation_sleep(ctrl_pdata, gpio, enable); + + if (gpio_is_valid(gpio)) + gpio_direction_output(gpio, value); + + if (wait) + usleep_range(wait, wait + 100); +} + +static void mdss_dsi_panel_driver_set_gpio( + struct mdss_dsi_ctrl_pdata *ctrl_pdata, + int gpio, bool enable, int value) +{ + int wait = 0; + + wait = mdss_dsi_panel_calculation_sleep(ctrl_pdata, gpio, enable); + + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, value); + + if (wait) + usleep_range(wait, wait + 100); +} + +int mdss_dsi_panel_driver_pinctrl_init(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + ctrl_pdata->pin_res.touch_state_active + = pinctrl_lookup_state(ctrl_pdata->pin_res.pinctrl, + MDSS_PINCTRL_STATE_TOUCH_ACTIVE); + if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.touch_state_active)) + pr_warn("%s: can not get touch active pinstate\n", __func__); + + ctrl_pdata->pin_res.touch_state_suspend + = pinctrl_lookup_state(ctrl_pdata->pin_res.pinctrl, + MDSS_PINCTRL_STATE_TOUCH_SUSPEND); + if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.touch_state_suspend)) + pr_warn("%s: can not get touch suspend pinstate\n", __func__); + + return 0; +} + +int mdss_dsi_panel_driver_touch_pinctrl_set_state( + struct mdss_dsi_ctrl_pdata *ctrl_pdata, + bool active) +{ + struct mdss_panel_specific_pdata *spec_pdata = NULL; + struct pinctrl_state *pin_state; + int rc = -EFAULT; + int wait = 0; + + if (IS_ERR_OR_NULL(ctrl_pdata->pin_res.pinctrl)) + return PTR_ERR(ctrl_pdata->pin_res.pinctrl); + + spec_pdata = ctrl_pdata->spec_pdata; + + pin_state = active ? ctrl_pdata->pin_res.touch_state_active + : ctrl_pdata->pin_res.touch_state_suspend; + + if (!IS_ERR_OR_NULL(pin_state)) { + rc = pinctrl_select_state(ctrl_pdata->pin_res.pinctrl, + pin_state); + if (!rc) { + wait = mdss_dsi_panel_calculation_sleep(ctrl_pdata, + spec_pdata->touch_int_gpio, active); + if (wait) + usleep_range(wait, wait + 100); + } else { + pr_err("%s: can not set %s pins\n", __func__, + active ? MDSS_PINCTRL_STATE_TOUCH_ACTIVE + : MDSS_PINCTRL_STATE_TOUCH_SUSPEND); + } + } else { + pr_err("%s: invalid '%s' pinstate\n", __func__, + active ? MDSS_PINCTRL_STATE_TOUCH_ACTIVE + : MDSS_PINCTRL_STATE_TOUCH_SUSPEND); + } + + return rc; +} + +void mdss_dsi_panel_driver_state_change_off(struct incell_ctrl *incell) +{ + incell_state_change change_state = incell->change_state; + unsigned char *state = &incell->state; + + pr_debug("%s: status:0x%x --->\n", __func__, (*state)); + + if (change_state != INCELL_STATE_NONE) + mdss_dsi_panel_driver_update_incell_bk(incell); + + switch (change_state) { + case INCELL_STATE_NONE: + pr_notice("%s: Not change off status\n", __func__); + break; + case INCELL_STATE_S_OFF: + *state &= INCELL_SYSTEM_STATE_OFF; + break; + case INCELL_STATE_P_OFF: + *state &= INCELL_POWER_STATE_OFF; + break; + case INCELL_STATE_SP_OFF: + *state &= INCELL_POWER_STATE_OFF; + *state &= INCELL_SYSTEM_STATE_OFF; + break; + default: + pr_err("%s: offmode unknown\n", __func__); + break; + } + + pr_debug("%s: ---> status:0x%x\n", __func__, (*state)); +} + +void mdss_dsi_panel_driver_power_off_ctrl(struct incell_ctrl *incell) +{ + incell_pw_seq seq = POWER_OFF_EXECUTE; + incell_state_change change_state = INCELL_STATE_NONE; + unsigned char state = incell->state; + incell_intf_mode intf_mode = incell->intf_mode; + incell_worker_state worker_state = incell->worker_state; + bool incell_intf_operation = incell->incell_intf_operation; + + if (worker_state == INCELL_WORKER_ON) { + change_state = INCELL_STATE_P_OFF; + } else if (incell_intf_operation == INCELL_TOUCH_RUN) { + /* touch I/F running mode */ + if (intf_mode == INCELL_DISPLAY_HW_RESET) { + if (!mdss_dsi_panel_driver_is_power_on(state)) { + pr_err("%s: Already power off. state:0x%x\n", + __func__, state); + seq = POWER_OFF_SKIP; + } else { + change_state = INCELL_STATE_P_OFF; + } + } else { + if (!mdss_dsi_panel_driver_is_power_on(state)) { + pr_err("%s: Power off status. state:0x%x\n", + __func__, state); + seq = POWER_OFF_SKIP; + } else if (mdss_dsi_panel_driver_is_ewu(state)) { + pr_debug("%s: Skip power off for EWU seq\n", + __func__); + seq = POWER_OFF_SKIP; + } else { + change_state = INCELL_STATE_P_OFF; + } + } + } else { + if (worker_state == INCELL_WORKER_PENDING) + incell_panel_power_worker_canceling(incell); + + /* touch I/F idling mode */ + if (mdss_dsi_panel_driver_is_power_lock(state)) { + change_state = INCELL_STATE_S_OFF; + seq = POWER_OFF_SKIP; + } else if (!mdss_dsi_panel_driver_is_power_on(state)) { + change_state = INCELL_STATE_S_OFF; + seq = POWER_OFF_SKIP; + } else if (mdss_dsi_panel_driver_is_ewu(state)) { + change_state = INCELL_STATE_S_OFF; + seq = POWER_OFF_SKIP; + } else { + change_state = INCELL_STATE_SP_OFF; + } + } + + pr_debug("%s: incell change state seq:%d change_state:%d\n", + __func__, (int)seq, (int)change_state); + incell->seq = seq; + incell->change_state = change_state; +} + +int mdss_dsi_panel_driver_power_off(struct mdss_panel_data *pdata) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_specific_pdata *spec_pdata = NULL; + struct mdss_panel_power_seq *pw_seq = NULL; + struct incell_ctrl *incell = incell_get_info(); + int ret = 0; + int rc = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + ret = -EINVAL; + goto end; + } + + if (incell) + if (incell->seq == POWER_OFF_SKIP) + return ret; + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + spec_pdata = ctrl_pdata->spec_pdata; + + pw_seq = &spec_pdata->off_seq; + + if (spec_pdata->off_seq.rst_b_seq) { + rc = mdss_dsi_panel_reset(pdata, 0); + if (rc) + pr_warn("%s: Panel reset failed. rc=%d\n", + __func__, rc); + } + + ret += mdss_dsi_panel_driver_reset_touch(pdata, 0); + ret += mdss_dsi_panel_driver_vreg_ctrl(ctrl_pdata, "ibb", false); + ret += mdss_dsi_panel_driver_vreg_ctrl(ctrl_pdata, "lab", false); + + mdss_dsi_panel_driver_set_gpio(ctrl_pdata, + (spec_pdata->disp_dcdc_en_gpio), false, 0); + + if (!spec_pdata->off_seq.rst_b_seq) { + rc = mdss_dsi_panel_reset(pdata, 0); + if (rc) + pr_warn("%s: Panel reset failed. rc=%d\n", + __func__, rc); + } + + mdss_dsi_panel_driver_touch_pinctrl_set_state(ctrl_pdata, false); + + mdss_dsi_panel_driver_set_gpio(ctrl_pdata, + (spec_pdata->touch_vddio_gpio), false, 1); + + ret += mdss_dsi_panel_driver_vreg_ctrl(ctrl_pdata, "vddio", false); + + ret += mdss_dsi_panel_driver_vreg_ctrl(ctrl_pdata, "touch-avdd", false); + + if (ret) + pr_err("%s: failed to disable vregs for %s\n", + __func__, __mdss_dsi_pm_name(DSI_PANEL_PM)); + else + pr_notice("@@@@ panel power off @@@@\n"); + + if (mdss_dsi_pinctrl_set_state(ctrl_pdata, false)) + pr_debug("reset disable: pinctrl not enabled\n"); + + if (spec_pdata->down_period) + down_period = (u32)ktime_to_ms(ktime_get()); + +end: + return ret; +} + +void mdss_dsi_panel_driver_state_change_on(struct incell_ctrl *incell) +{ + incell_state_change change_state = incell->change_state; + unsigned char *state = &incell->state; + + pr_debug("%s: status:0x%x --->\n", __func__, (*state)); + + if (change_state != INCELL_STATE_NONE) + mdss_dsi_panel_driver_update_incell_bk(incell); + + switch (change_state) { + case INCELL_STATE_NONE: + pr_notice("%s: Not change on status\n", __func__); + break; + case INCELL_STATE_S_ON: + *state |= INCELL_SYSTEM_STATE_ON; + break; + case INCELL_STATE_P_ON: + *state |= INCELL_POWER_STATE_ON; + break; + case INCELL_STATE_SP_ON: + *state |= INCELL_SYSTEM_STATE_ON; + *state |= INCELL_POWER_STATE_ON; + break; + default: + pr_err("%s: onmode unknown\n", __func__); + break; + } + + pr_debug("%s: ---> status:0x%x\n", __func__, (*state)); +} + +void mdss_dsi_panel_driver_power_on_ctrl(struct incell_ctrl *incell) +{ + incell_pw_seq seq = POWER_ON_EXECUTE; + incell_state_change change_state = INCELL_STATE_NONE; + unsigned char state = incell->state; + incell_intf_mode intf_mode = incell->intf_mode; + incell_worker_state worker_state = incell->worker_state; + bool incell_intf_operation = incell->incell_intf_operation; + + if (worker_state == INCELL_WORKER_ON) { + change_state = INCELL_STATE_P_ON; + } else if (incell_intf_operation == INCELL_TOUCH_RUN) { + /* touch I/F running mode */ + if (intf_mode != INCELL_DISPLAY_HW_RESET) { + pr_err("%s: Unknown I/F: %d\n", + __func__, (int)intf_mode); + seq = POWER_ON_SKIP; + } else if (mdss_dsi_panel_driver_is_power_on(state)) { + pr_err("%s: Already power on status. state:0x%x\n", + __func__, state); + seq = POWER_ON_SKIP; + } else { + change_state = INCELL_STATE_P_ON; + } + } else { + /* touch I/F idling mode */ + if (worker_state == INCELL_WORKER_PENDING) { + incell_panel_power_worker_canceling(incell); + change_state = INCELL_STATE_S_ON; + seq = POWER_ON_EWU_SEQ; + } else if (mdss_dsi_panel_driver_is_power_lock(state)) { + if (mdss_dsi_panel_driver_is_power_on(state)) { + change_state = INCELL_STATE_S_ON; + seq = POWER_ON_SKIP; + } else { + change_state = INCELL_STATE_SP_ON; + seq = POWER_ON_EXECUTE; + } + } else if (mdss_dsi_panel_driver_is_ewu(state)) { + if (mdss_dsi_panel_driver_is_power_on(state)) { + change_state = INCELL_STATE_S_ON; + seq = POWER_ON_EWU_SEQ; + } else { + change_state = INCELL_STATE_SP_ON; + seq = POWER_ON_EXECUTE; + } + } else if (mdss_dsi_panel_driver_is_power_on(state)) { + change_state = INCELL_STATE_S_ON; + seq = POWER_ON_EWU_SEQ; + } else { + change_state = INCELL_STATE_SP_ON; + seq = POWER_ON_EXECUTE; + } + } + + pr_debug("%s: incell change state seq:%d change_state:%d\n", + __func__, (int)seq, (int)change_state); + incell->seq = seq; + incell->change_state = change_state; +} + +static int mdss_dsi_panel_driver_ewu_seq(struct mdss_panel_data *pdata) +{ + int ret = 0; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + + ctrl_pdata = container_of(pdata, + struct mdss_dsi_ctrl_pdata, panel_data); + if (!ctrl_pdata) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ret += mdss_dsi_panel_driver_reset_touch(pdata, 0); + ret += mdss_dsi_panel_driver_reset_dual_display(ctrl_pdata); + + return ret; +} + +int mdss_dsi_panel_driver_power_on(struct mdss_panel_data *pdata) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_specific_pdata *spec_pdata = NULL; + struct mdss_panel_power_seq *pw_seq = NULL; + struct incell_ctrl *incell = incell_get_info(); + unsigned char state; + int ret = 0; + int wait; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + if (incell) { + mdss_dsi_panel_driver_power_on_ctrl(incell); + if (incell->seq != POWER_ON_EXECUTE) { + if (incell->seq == POWER_ON_EWU_SEQ) + ret = mdss_dsi_panel_driver_ewu_seq(pdata); + return ret; + } + state = incell->state; + } + + if (pdata->panel_info.pdest != DISPLAY_1) + return 0; + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + spec_pdata = ctrl_pdata->spec_pdata; + + pw_seq = &spec_pdata->on_seq; + + if (!gpio_req) { + ret = mdss_dsi_request_gpios(ctrl_pdata); + if (ret) { + pr_err("gpio request failed\n"); + return ret; + } + gpio_req = true; + } + + if (spec_pdata->down_period) { + u32 kt = (u32)ktime_to_ms(ktime_get()); + + kt = (kt < down_period) ? kt + ~down_period : kt - down_period; + if (kt < spec_pdata->down_period) + usleep_range((spec_pdata->down_period - kt) * + 1000, + (spec_pdata->down_period - kt) * + 1000 + 100); + } + + ret += mdss_dsi_panel_driver_vreg_ctrl(ctrl_pdata, "touch-avdd", true); + ret += mdss_dsi_panel_driver_vreg_ctrl(ctrl_pdata, "vddio", true); + + mdss_dsi_panel_driver_gpio_output(ctrl_pdata, + (spec_pdata->touch_vddio_gpio), true, 0); + + mdss_dsi_panel_driver_touch_pinctrl_set_state(ctrl_pdata, true); + + mdss_dsi_panel_driver_gpio_output(ctrl_pdata, + (spec_pdata->disp_dcdc_en_gpio), true, 1); + + if ((spec_pdata->panel_type == HYBRID_INCELL) && + (!mdss_dsi_panel_driver_is_power_on(state))) { + ret += mdss_dsi_panel_driver_reset_touch(pdata, 1); + wait = pw_seq->touch_reset_first; + usleep_range(wait * 1000, wait * 1000 + 100); + } + + ret += mdss_dsi_panel_driver_vreg_ctrl(ctrl_pdata, "lab", true); + ret += mdss_dsi_panel_driver_vreg_ctrl(ctrl_pdata, "ibb", true); + + if (ret) { + pr_err("%s: failed to enable vregs for %s\n", + __func__, __mdss_dsi_pm_name(DSI_PANEL_PM)); + return ret; + } + + pr_notice("@@@@ panel power on @@@@\n"); + + /* + * If continuous splash screen feature is enabled, then we need to + * request all the GPIOs that have already been configured in the + * bootloader. This needs to be done irresepective of whether + * the lp11_init flag is set or not. + */ + if (!pdata->panel_info.cont_splash_enabled && + !pdata->panel_info.mipi.lp11_init) { + if (mdss_dsi_pinctrl_set_state(ctrl_pdata, true)) + pr_debug("reset enable: pinctrl not enabled\n"); + + ret = mdss_dsi_panel_reset(pdata, 1); + if (ret) + pr_err("%s: Panel reset failed. rc=%d\n", + __func__, ret); + } + + return ret; +} + +int mdss_dsi_panel_driver_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct mdss_panel_specific_pdata *spec_pdata = NULL; + int rc = 0; + + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + spec_pdata = ctrl_pdata->spec_pdata; + + if (gpio_is_valid(spec_pdata->disp_vddio_gpio)) { + rc = gpio_request(spec_pdata->disp_vddio_gpio, + "disp_vddio"); + if (rc) { + pr_err("request disp vddio gpio failed, rc=%d\n", + rc); + goto disp_vddio_gpio_err; + } + } + + if (gpio_is_valid(spec_pdata->disp_dcdc_en_gpio)) { + rc = gpio_request(spec_pdata->disp_dcdc_en_gpio, + "disp_dcdc_en_gpio"); + if (rc) { + pr_err("request disp_dcdc_en gpio failed, rc=%d\n", rc); + goto disp_dcdc_en_gpio_err; + } + } + + if (gpio_is_valid(spec_pdata->touch_vddio_gpio)) { + rc = gpio_request(spec_pdata->touch_vddio_gpio, + "touch_vddio"); + if (rc) { + pr_err("request touch vddio gpio failed, rc=%d\n", + rc); + goto touch_vddio_gpio_err; + } + } + + if (gpio_is_valid(spec_pdata->touch_reset_gpio)) { + rc = gpio_request(spec_pdata->touch_reset_gpio, + "touch_reset"); + if (rc) { + pr_err("request touch reset gpio failed,rc=%d\n", + rc); + goto touch_reset_gpio_err; + } + } + + if (gpio_is_valid(spec_pdata->touch_int_gpio)) { + rc = gpio_request(spec_pdata->touch_int_gpio, + "touch_int"); + if (rc) { + pr_err("request touch int gpio failed,rc=%d\n", + rc); + goto touch_int_gpio_err; + } + } + + return rc; + +touch_int_gpio_err: + if (gpio_is_valid(spec_pdata->touch_reset_gpio)) + gpio_free(spec_pdata->touch_reset_gpio); +touch_reset_gpio_err: + if (gpio_is_valid(spec_pdata->touch_vddio_gpio)) + gpio_free(spec_pdata->touch_vddio_gpio); +touch_vddio_gpio_err: + if (gpio_is_valid(spec_pdata->disp_dcdc_en_gpio)) + gpio_free(spec_pdata->disp_dcdc_en_gpio); +disp_dcdc_en_gpio_err: + if (gpio_is_valid(spec_pdata->disp_vddio_gpio)) + gpio_free(spec_pdata->disp_vddio_gpio); +disp_vddio_gpio_err: + return rc; +} + +void mdss_dsi_panel_driver_gpio_free(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct mdss_panel_specific_pdata *spec_pdata = NULL; + + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return; + } + + spec_pdata = ctrl_pdata->spec_pdata; + + if (gpio_is_valid(spec_pdata->touch_int_gpio)) + gpio_free(spec_pdata->touch_int_gpio); + + if (gpio_is_valid(spec_pdata->touch_reset_gpio)) + gpio_free(spec_pdata->touch_reset_gpio); + + if (gpio_is_valid(spec_pdata->touch_vddio_gpio)) + gpio_free(spec_pdata->touch_vddio_gpio); + + if (gpio_is_valid(spec_pdata->disp_dcdc_en_gpio)) + gpio_free(spec_pdata->disp_dcdc_en_gpio); + + if (gpio_is_valid(spec_pdata->disp_vddio_gpio)) + gpio_free(spec_pdata->disp_vddio_gpio); +} + +void mdss_dsi_panel_driver_parse_gpio_params(struct platform_device *ctrl_pdev, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct mdss_panel_specific_pdata *spec_pdata = NULL; + + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return; + } + + spec_pdata = ctrl_pdata->spec_pdata; + + spec_pdata->disp_vddio_gpio = of_get_named_gpio( + ctrl_pdev->dev.of_node, + "qcom,platform-vddio-gpio", 0); + + if (!gpio_is_valid(spec_pdata->disp_vddio_gpio)) + pr_err("%s:%d, disp vddio gpio not specified\n", + __func__, __LINE__); + + spec_pdata->touch_vddio_gpio = of_get_named_gpio( + ctrl_pdev->dev.of_node, + "qcom,platform-touch-vddio-gpio", 0); + + if (!gpio_is_valid(spec_pdata->touch_vddio_gpio)) + pr_err("%s:%d, touch vddio gpio not specified\n", + __func__, __LINE__); + + spec_pdata->touch_reset_gpio = of_get_named_gpio( + ctrl_pdev->dev.of_node, + "qcom,platform-touch-reset-gpio", 0); + + if (!gpio_is_valid(spec_pdata->touch_reset_gpio)) + pr_err("%s:%d, touch reset gpio not specified\n", + __func__, __LINE__); + + spec_pdata->touch_int_gpio = of_get_named_gpio( + ctrl_pdev->dev.of_node, + "qcom,platform-touch-int-gpio", 0); + + if (!gpio_is_valid(spec_pdata->touch_int_gpio)) + pr_err("%s:%d, touch int gpio not specified\n", + __func__, __LINE__); + + spec_pdata->disp_dcdc_en_gpio = of_get_named_gpio( + ctrl_pdev->dev.of_node, + "somc,disp-dcdc-en-gpio", 0); + + if (!gpio_is_valid(spec_pdata->disp_dcdc_en_gpio)) + pr_err("%s:%d, disp dcdc en gpio not specified\n", + __func__, __LINE__); +} + + +static void mdss_dsi_panel_set_gpio_seq( + int gpio, int seq_num, const int *seq) +{ + int i; + + for (i = 0; i + 1 < seq_num; i += 2) { + gpio_set_value(gpio, seq[i]); + usleep_range(seq[i + 1] * 1000, seq[i + 1] * 1000 + 100); + pr_debug("%s: enable=%d, wait=%dms\n", + __func__, seq[i], seq[i+1]); + } +} + +int mdss_dsi_panel_driver_reset_panel(struct mdss_panel_data *pdata, int enable) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + struct mdss_panel_power_seq *pw_seq = NULL; + int rc = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (!gpio_is_valid(ctrl_pdata->rst_gpio)) { + pr_debug("%s:%d, panel reset line not configured\n", + __func__, __LINE__); + return rc; + } + + pr_debug("%s: enable = %d\n", __func__, enable); + pinfo = &(ctrl_pdata->panel_data.panel_info); + + + if (!gpio_req) { + rc = mdss_dsi_request_gpios(ctrl_pdata); + if (rc) { + pr_err("gpio request failed\n"); + return rc; + } + gpio_req = true; + } + + if (mdss_dsi_panel_driver_is_seq_for_ewu() && enable) + pw_seq = &ctrl_pdata->spec_pdata->ewu_seq ? + &ctrl_pdata->spec_pdata->ewu_seq : + &ctrl_pdata->spec_pdata->on_seq; + else + pw_seq = (enable) ? &ctrl_pdata->spec_pdata->on_seq : + &ctrl_pdata->spec_pdata->off_seq; + + mdss_dsi_panel_set_gpio_seq(ctrl_pdata->rst_gpio, + pw_seq->seq_num, pw_seq->rst_seq); + + return rc; +} + +int mdss_dsi_panel_driver_reset_touch(struct mdss_panel_data *pdata, int enable) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + struct mdss_panel_specific_pdata *spec_pdata = NULL; + int rc = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + spec_pdata = ctrl_pdata->spec_pdata; + + if (!gpio_is_valid(spec_pdata->touch_reset_gpio)) { + pr_debug("%s:%d, touch reset line not configured\n", + __func__, __LINE__); + return rc; + } + + pr_debug("%s: enable = %d\n", __func__, enable); + pinfo = &(ctrl_pdata->panel_data.panel_info); + + if (enable) { + if (!gpio_req) { + rc = mdss_dsi_request_gpios(ctrl_pdata); + if (rc) { + pr_err("gpio request failed\n"); + return rc; + } + gpio_req = true; + } + + mdss_dsi_panel_driver_gpio_output(ctrl_pdata, + (spec_pdata->touch_reset_gpio), true, 1); + } else { + mdss_dsi_panel_driver_set_gpio(ctrl_pdata, + (spec_pdata->touch_reset_gpio), false, 0); + } + + return rc; +} + +static bool mdss_dsi_panel_driver_split_display_enabled(void) +{ + /* + * currently the only supported mode is split display. + * So, if both controllers are initialized, then assume that + * split display mode is enabled. + */ + return ctrl_list[DSI_CTRL_LEFT] && ctrl_list[DSI_CTRL_RIGHT]; +} + +int mdss_dsi_panel_driver_reset_dual_display( + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct mdss_dsi_ctrl_pdata *mctrl_pdata = NULL; + int ret = 0; + + if (!mdss_dsi_panel_driver_split_display_enabled()) { + if (mdss_dsi_pinctrl_set_state(ctrl_pdata, true)) + pr_debug("reset enable: pinctrl not enabled\n"); + ret = mdss_dsi_panel_reset(&(ctrl_pdata->panel_data), 1); + } else if (ctrl_pdata->ndx == DSI_CTRL_1) { + mctrl_pdata = mdss_dsi_get_other_ctrl(ctrl_pdata); + if (!mctrl_pdata) { + pr_warn("%s: Unable to get other control\n", + __func__); + ret = -EINVAL; + } else { + if (mdss_dsi_pinctrl_set_state(mctrl_pdata, true)) + pr_debug("other reset pinctrl not enabled\n"); + ret = mdss_dsi_panel_reset(&(mctrl_pdata->panel_data), 1); + } + } else { + pr_debug("%s: reset pinctrl not yet\n", __func__); + } + + return ret; +} + +static int mdss_dsi_property_read_u32_var(struct device_node *np, + char *name, u32 **out_data, int *num) +{ + struct property *prop = of_find_property(np, name, NULL); + const __be32 *val; + u32 *out; + int s; + + if (!prop) { + pr_debug("%s:%d, unable to read %s", __func__, __LINE__, name); + return -EINVAL; + } + if (!prop->value) { + pr_debug("%s:%d, no data of %s", __func__, __LINE__, name); + return -ENODATA; + } + + *num = prop->length / sizeof(u32); + if (!*num || *num % 2) { + pr_debug("%s:%d, error reading %s, length found = %d\n", + __func__, __LINE__, name, *num); + return -ENODATA; + } + *out_data = kzalloc(prop->length, GFP_KERNEL); + if (!*out_data) { + pr_err("%s:no mem assigned: kzalloc fail\n", __func__); + *num = 0; + return -ENOMEM; + } + + val = prop->value; + out = *out_data; + s = *num; + while (s--) + *out++ = be32_to_cpup(val++); + return 0; +} + +int mdss_dsi_panel_driver_parse_dt(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct mdss_panel_specific_pdata *spec_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + u32 tmp = 0; + int rc = 0; + struct device_node *panel_np; + const char *panel_type_name; + static const char *fps_mode; + static const char *fps_type; + struct mdss_panel_labibb_data *labibb = NULL; + + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + goto error; + } + + spec_pdata = ctrl_pdata->spec_pdata; + pinfo = &(ctrl_pdata->panel_data.panel_info); + + spec_pdata->pcc_enable = of_property_read_bool(np, "somc,mdss-dsi-pcc-enable"); + if (spec_pdata->pcc_enable) { + mdss_dsi_parse_dcs_cmds(np, &spec_pdata->pre_uv_read_cmds, + "somc,mdss-dsi-pre-uv-command", NULL); + + mdss_dsi_parse_dcs_cmds(np, &spec_pdata->uv_read_cmds, + "somc,mdss-dsi-uv-command", NULL); + + rc = of_property_read_u32(np, + "somc,mdss-dsi-uv-param-type", &tmp); + spec_pdata->pcc_data.param_type = + (!rc ? tmp : CLR_DATA_UV_PARAM_TYPE_NONE); + + rc = of_property_read_u32(np, + "somc,mdss-dsi-pcc-table-size", &tmp); + spec_pdata->pcc_data.tbl_size = + (!rc ? tmp : 0); + + spec_pdata->pcc_data.color_tbl = + kzalloc(spec_pdata->pcc_data.tbl_size * + sizeof(struct mdss_pcc_color_tbl), + GFP_KERNEL); + if (!spec_pdata->pcc_data.color_tbl) { + pr_err("no mem assigned: kzalloc fail\n"); + return -ENOMEM; + } + rc = of_property_read_u32_array(np, + "somc,mdss-dsi-pcc-table", + (u32 *)spec_pdata->pcc_data.color_tbl, + spec_pdata->pcc_data.tbl_size * + sizeof(struct mdss_pcc_color_tbl) / + sizeof(u32)); + if (rc) { + spec_pdata->pcc_data.tbl_size = 0; + kzfree(spec_pdata->pcc_data.color_tbl); + spec_pdata->pcc_data.color_tbl = NULL; + pr_err("%s:%d, Unable to read pcc table", + __func__, __LINE__); + } + spec_pdata->pcc_data.pcc_sts |= PCC_STS_UD; + } + + spec_pdata->srgb_pcc_enable = of_property_read_bool(np, + "somc,mdss-dsi-srgb-pcc-enable"); + if (spec_pdata->srgb_pcc_enable) { + rc = of_property_read_u32(np, + "somc,mdss-dsi-srgb-pcc-table-size", &tmp); + spec_pdata->srgb_pcc_data.tbl_size = + (!rc ? tmp : 0); + + spec_pdata->srgb_pcc_data.color_tbl = + kzalloc(spec_pdata->srgb_pcc_data.tbl_size * + sizeof(struct mdss_pcc_color_tbl), + GFP_KERNEL); + if (!spec_pdata->srgb_pcc_data.color_tbl) { + pr_err("no mem assigned: kzalloc fail\n"); + return -ENOMEM; + } + rc = of_property_read_u32_array(np, + "somc,mdss-dsi-srgb-pcc-table", + (u32 *)spec_pdata->srgb_pcc_data.color_tbl, + spec_pdata->srgb_pcc_data.tbl_size * + sizeof(struct mdss_pcc_color_tbl) / + sizeof(u32)); + if (rc) { + spec_pdata->srgb_pcc_data.tbl_size = 0; + kzfree(spec_pdata->srgb_pcc_data.color_tbl); + spec_pdata->srgb_pcc_data.color_tbl = NULL; + pr_err("%s:%d, Unable to read sRGB pcc table", + __func__, __LINE__); + } + } + + spec_pdata->vivid_pcc_enable = of_property_read_bool(np, + "somc,mdss-dsi-vivid-pcc-enable"); + if (spec_pdata->vivid_pcc_enable) { + rc = of_property_read_u32(np, + "somc,mdss-dsi-vivid-pcc-table-size", &tmp); + spec_pdata->vivid_pcc_data.tbl_size = + (!rc ? tmp : 0); + + spec_pdata->vivid_pcc_data.color_tbl = + kzalloc(spec_pdata->vivid_pcc_data.tbl_size * + sizeof(struct mdss_pcc_color_tbl), + GFP_KERNEL); + if (!spec_pdata->vivid_pcc_data.color_tbl) { + pr_err("no mem assigned: kzalloc fail\n"); + return -ENOMEM; + } + rc = of_property_read_u32_array(np, + "somc,mdss-dsi-vivid-pcc-table", + (u32 *)spec_pdata->vivid_pcc_data.color_tbl, + spec_pdata->vivid_pcc_data.tbl_size * + sizeof(struct mdss_pcc_color_tbl) / + sizeof(u32)); + if (rc) { + spec_pdata->vivid_pcc_data.tbl_size = 0; + kzfree(spec_pdata->vivid_pcc_data.color_tbl); + spec_pdata->vivid_pcc_data.color_tbl = NULL; + pr_err("%s:%d, Unable to read Vivid pcc table", + __func__, __LINE__); + } + } + + spec_pdata->hdr_pcc_enable = of_property_read_bool(np, + "somc,mdss-dsi-hdr-pcc-enable"); + if (spec_pdata->hdr_pcc_enable) { + rc = of_property_read_u32(np, + "somc,mdss-dsi-hdr-pcc-table-size", &tmp); + spec_pdata->hdr_pcc_data.tbl_size = + (!rc ? tmp : 0); + + spec_pdata->hdr_pcc_data.color_tbl = + kzalloc(spec_pdata->hdr_pcc_data.tbl_size * + sizeof(struct mdss_pcc_color_tbl), + GFP_KERNEL); + if (!spec_pdata->hdr_pcc_data.color_tbl) { + pr_err("no mem assigned: kzalloc fail\n"); + return -ENOMEM; + } + rc = of_property_read_u32_array(np, + "somc,mdss-dsi-hdr-pcc-table", + (u32 *)spec_pdata->hdr_pcc_data.color_tbl, + spec_pdata->hdr_pcc_data.tbl_size * + sizeof(struct mdss_pcc_color_tbl) / + sizeof(u32)); + if (rc) { + spec_pdata->hdr_pcc_data.tbl_size = 0; + kzfree(spec_pdata->hdr_pcc_data.color_tbl); + spec_pdata->hdr_pcc_data.color_tbl = NULL; + pr_err("%s:%d, Unable to read HDR pcc table", + __func__, __LINE__); + } + } + + (void)mdss_dsi_property_read_u32_var(np, + "somc,pw-on-rst-seq", + (u32 **)&spec_pdata->on_seq.rst_seq, + &spec_pdata->on_seq.seq_num); + + + if (of_find_property(np, "somc,pw-off-rst-b-seq", NULL)) { + spec_pdata->off_seq.rst_b_seq = true; + + (void)mdss_dsi_property_read_u32_var(np, + "somc,pw-off-rst-b-seq", + (u32 **)&spec_pdata->off_seq.rst_seq, + &spec_pdata->off_seq.seq_num); + } else { + (void)mdss_dsi_property_read_u32_var(np, + "somc,pw-off-rst-seq", + (u32 **)&spec_pdata->off_seq.rst_seq, + &spec_pdata->off_seq.seq_num); + } + + + rc = of_property_read_u32(np, + "somc,pw-wait-after-on-vddio", &tmp); + spec_pdata->on_seq.disp_vddio = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-on-vsp", &tmp); + spec_pdata->on_seq.disp_vsp = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-on-vsn", &tmp); + spec_pdata->on_seq.disp_vsn = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-on-dcdc", &tmp); + spec_pdata->on_seq.disp_dcdc = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-off-vddio", &tmp); + spec_pdata->off_seq.disp_vddio = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-off-vsp", &tmp); + spec_pdata->off_seq.disp_vsp = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-off-vsn", &tmp); + spec_pdata->off_seq.disp_vsn = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-off-dcdc", &tmp); + spec_pdata->off_seq.disp_dcdc = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-on-touch-avdd", &tmp); + spec_pdata->on_seq.touch_avdd = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-on-touch-vddio", &tmp); + spec_pdata->on_seq.touch_vddio = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-on-touch-reset", &tmp); + spec_pdata->on_seq.touch_reset = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-on-touch-reset-first", &tmp); + spec_pdata->on_seq.touch_reset_first = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-on-touch-int-n", &tmp); + spec_pdata->on_seq.touch_intn = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-off-touch-avdd", &tmp); + spec_pdata->off_seq.touch_avdd = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-off-touch-vddio", &tmp); + spec_pdata->off_seq.touch_vddio = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-off-touch-reset", &tmp); + spec_pdata->off_seq.touch_reset = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-wait-after-off-touch-int-n", &tmp); + spec_pdata->off_seq.touch_intn = !rc ? tmp : 0; + + rc = of_property_read_u32(np, + "somc,pw-down-period", &tmp); + spec_pdata->down_period = !rc ? tmp : 0; + + rc = mdss_dsi_property_read_u32_var(np, + "somc,ewu-rst-seq", + (u32 **)&spec_pdata->ewu_seq.rst_seq, + &spec_pdata->ewu_seq.seq_num); + if (rc) { + spec_pdata->ewu_seq.rst_seq = NULL; + spec_pdata->ewu_seq.seq_num = 0; + pr_debug("%s: Unable to read ewu sequence\n", __func__); + } + + rc = of_property_read_u32(np, + "somc,ewu-wait-after-touch-reset", &tmp); + spec_pdata->ewu_seq.touch_reset = !rc ? tmp : 0; + + panel_np = of_parse_phandle(np, "qcom,panel-supply-entries", 0); + + panel_type_name = of_node_full_name(panel_np); + if (!strcmp(panel_type_name, NODE_OF_HYBRID)) + spec_pdata->panel_type = HYBRID_INCELL; + if (!strcmp(panel_type_name, NODE_OF_FULL)) + spec_pdata->panel_type = FULL_INCELL; + + panel_np = of_parse_phandle(np, "qcom,panel-supply-entries", 0); + + panel_type_name = of_node_full_name(panel_np); + if (!strcmp(panel_type_name, NODE_OF_HYBRID)) + spec_pdata->panel_type = HYBRID_INCELL; + if (!strcmp(panel_type_name, NODE_OF_FULL)) + spec_pdata->panel_type = FULL_INCELL; + + spec_pdata->chg_fps.enable = of_property_read_bool(np, + "somc,change-fps-enable"); + if (spec_pdata->chg_fps.enable) { + + spec_pdata->input_fpks = pinfo->mipi.frame_rate * 1000; + mdss_dsi_parse_dcs_cmds(np, &spec_pdata->fps_cmds, + "somc,change-fps-command", NULL); + + rc = of_property_read_u32(np, + "somc,driver-ic-vdisp", &tmp); + if (rc) { + pr_err("%s: Display vdisp not specified\n", __func__); + goto error; + } + spec_pdata->chg_fps.dric_vdisp = tmp; + + fps_type = of_get_property(np, + "somc,change-fps-panel-type", NULL); + if (!fps_type) { + pr_err("%s:%d, Panel type not specified\n", + __func__, __LINE__); + goto error; + } + + if (!strncmp(fps_type, "uhd_4k_type", 11)) { + spec_pdata->chg_fps.type = FPS_TYPE_UHD_4K; + } else if (!strncmp(fps_type, "hybrid_incell_type", 18)) { + spec_pdata->chg_fps.type = FPS_TYPE_HYBRID_INCELL; + } else if (!strncmp(fps_type, "full_incell_type", 16)) { + spec_pdata->chg_fps.type = FPS_TYPE_FULL_INCELL; + } else { + pr_err("%s: Unable to read fps panel type\n", __func__); + goto error; + } + + fps_mode = of_get_property(np, + "somc,change-fps-panel-mode", NULL); + if (!fps_mode) { + pr_err("%s:%d, Panel mode not specified\n", + __func__, __LINE__); + goto error; + } + + if (!strncmp(fps_mode, "susres_mode", 11)) { + spec_pdata->chg_fps.mode = FPS_MODE_SUSRES; + } else if (!strncmp(fps_mode, "dynamic_mode", 12)) { + spec_pdata->chg_fps.mode = FPS_MODE_DYNAMIC; + } else { + pr_err("%s: Unable to read fps panel mode\n", __func__); + goto error; + } + + switch (spec_pdata->chg_fps.type) { + case FPS_TYPE_UHD_4K: + (void)mdss_dsi_property_read_u32_var(np, + "somc,change-fps-rtn-pos", + (u32 **)&spec_pdata->chg_fps.send_pos.pos, + &spec_pdata->chg_fps.send_pos.num); + + rc = of_property_read_u32(np, + "somc,driver-ic-total-porch", &tmp); + if (rc) { + pr_err("%s: DrIC total_porch not specified\n", + __func__); + goto error; + } + spec_pdata->chg_fps.dric_total_porch = tmp; + + rc = of_property_read_u32(np, + "somc,driver-ic-rclk", &tmp); + if (rc) { + pr_err("%s: DrIC rclk not specified\n", + __func__); + goto error; + } + spec_pdata->chg_fps.dric_rclk = tmp; + + spec_pdata->chg_fps.rtn_adj = of_property_read_bool(np, + "somc,change-fps-rtn-adj"); + break; + case FPS_TYPE_HYBRID_INCELL: + (void)mdss_dsi_property_read_u32_var(np, + "somc,change-fps-send-pos", + (u32 **)&spec_pdata->chg_fps.send_pos.pos, + &spec_pdata->chg_fps.send_pos.num); + + rc = of_property_read_u32(np, + "somc,driver-ic-rtn", &tmp); + if (rc) { + pr_err("%s: DrIC rtn not specified\n", + __func__); + goto error; + } + spec_pdata->chg_fps.dric_rtn = tmp; + + (void)mdss_dsi_property_read_u32_var(np, + "somc,change-fps-send-pos", + (u32 **)&spec_pdata->chg_fps.send_pos.pos, + &spec_pdata->chg_fps.send_pos.num); + + rc = of_property_read_u32(np, + "somc,driver-ic-mclk", &tmp); + if (rc) { + pr_err("%s: DrIC mclk not specified\n", + __func__); + goto error; + } + spec_pdata->chg_fps.dric_mclk = tmp; + + rc = of_property_read_u32(np, + "somc,driver-ic-vtouch", &tmp); + if (rc) { + pr_err("%s: DrIC vtouch not specified\n", + __func__); + goto error; + } + spec_pdata->chg_fps.dric_vtouch = tmp; + + rc = of_property_read_u32(np, + "somc,change-fps-send-byte", &tmp); + if (rc) { + pr_err("%s: fps bytes send not specified\n", + __func__); + goto error; + } + spec_pdata->chg_fps.send_byte = tmp; + + rc = of_property_read_u32(np, + "somc,change-fps-porch-mask-pos", &tmp); + if (rc) { + pr_warn("%s: fps mask position not specified\n", + __func__); + spec_pdata->chg_fps.mask_pos = 0; + } else { + spec_pdata->chg_fps.mask_pos = tmp; + rc = of_property_read_u32(np, + "somc,change-fps-porch-mask", &tmp); + if (rc) { + pr_warn("%s: fps mask not specified\n", + __func__); + spec_pdata->chg_fps.mask = 0x0; + } else { + spec_pdata->chg_fps.mask = tmp; + } + } + + rc = of_property_read_u32_array(np, + "somc,change-fps-porch-range", + spec_pdata->chg_fps.porch_range, + FPS_PORCH_RNG_NUM); + if (rc) { + spec_pdata->chg_fps.porch_range[FPS_PORCH_RNG_MIN] = 0; + spec_pdata->chg_fps.porch_range[FPS_PORCH_RNG_MAX] = 0; + } + break; + case FPS_TYPE_FULL_INCELL: + (void)mdss_dsi_property_read_u32_var(np, + "somc,change-fps-rtn-pos", + (u32 **)&spec_pdata->chg_fps.send_pos.pos, + &spec_pdata->chg_fps.send_pos.num); + + rc = of_property_read_u32(np, + "somc,driver-ic-total-porch", &tmp); + if (rc) { + pr_err("%s: DrIC total_porch not specified\n", + __func__); + goto error; + } + spec_pdata->chg_fps.dric_total_porch = tmp; + + rc = of_property_read_u32(np, + "somc,driver-ic-rclk", &tmp); + if (rc) { + pr_err("%s: DrIC rclk not specified\n", + __func__); + goto error; + } + spec_pdata->chg_fps.dric_rclk = tmp; + + rc = of_property_read_u32(np, + "somc,driver-ic-vtp", &tmp); + if (rc) { + pr_err("%s: DrIC vtp not specified\n", + __func__); + goto error; + } + spec_pdata->chg_fps.dric_tp = tmp; + break; + default: + pr_err("%s: Read panel mode failed.\n", __func__); + goto error; + } + } + + /* Parsing lab/ibb register settings */ + labibb = &spec_pdata->labibb; + + memset(labibb, 0, sizeof(*labibb)); + if (of_find_property(np, "somc,lab-output-voltage", &tmp)) + labibb->labibb_ctrl_state |= OVR_LAB_VOLTAGE; + + if (of_find_property(np, "somc,ibb-output-voltage", &tmp)) + labibb->labibb_ctrl_state |= OVR_IBB_VOLTAGE; + + if (of_find_property(np, "somc,qpnp-lab-limit-maximum-current", &tmp)) + labibb->labibb_ctrl_state |= OVR_LAB_CURRENT_MAX; + + if (of_find_property(np, "somc,qpnp-ibb-limit-maximum-current", &tmp)) + labibb->labibb_ctrl_state |= OVR_IBB_CURRENT_MAX; + + if (of_find_property(np, "somc,qpnp-lab-max-precharge-time", &tmp)) + labibb->labibb_ctrl_state |= OVR_LAB_PRECHARGE_CTL; + + if (of_find_property(np, "somc,qpnp-lab-soft-start", &tmp)) + labibb->labibb_ctrl_state |= OVR_LAB_SOFT_START_CTL; + + if (of_find_property(np, "somc,qpnp-ibb-discharge-resistor", &tmp)) + labibb->labibb_ctrl_state |= OVR_IBB_SOFT_START_CTL; + + if (of_find_property(np, "somc,qpnp-lab-pull-down-enable", &tmp)) + labibb->labibb_ctrl_state |= OVR_LAB_PD_CTL; + + if (of_find_property(np, "somc,qpnp-ibb-pull-down-enable", &tmp)) + labibb->labibb_ctrl_state |= OVR_IBB_PD_CTL; + + labibb->lab_output_voltage = QPNP_REGULATOR_VSP_V_5P4V; + if (((labibb->labibb_ctrl_state) & OVR_LAB_VOLTAGE)) { + rc = of_property_read_u32(np, "somc,lab-output-voltage", &tmp); + if (!rc) + labibb->lab_output_voltage = tmp; + } + + labibb->ibb_output_voltage = QPNP_REGULATOR_VSN_V_M5P4V; + if (((labibb->labibb_ctrl_state) & OVR_IBB_VOLTAGE)) { + rc = of_property_read_u32(np, "somc,ibb-output-voltage", &tmp); + if (!rc) + labibb->ibb_output_voltage = tmp; + } + + labibb->lab_current_max = LAB_CURRENT_MAX; + if (((labibb->labibb_ctrl_state) & OVR_LAB_CURRENT_MAX)) { + rc = of_property_read_u32(np, + "somc,qpnp-lab-limit-maximum-current", &tmp); + if (!rc) + labibb->lab_current_max = tmp; + } + + labibb->ibb_current_max = IBB_CURRENT_MAX; + if (((labibb->labibb_ctrl_state) & OVR_IBB_CURRENT_MAX)) { + rc = of_property_read_u32(np, + "somc,qpnp-ibb-limit-maximum-current", &tmp); + if (!rc) + labibb->ibb_current_max = tmp; + } + + labibb->lab_fast_precharge_time = LAB_FAST_PRECHARGE_TIME; + labibb->lab_fast_precharge_en = false; + if (((labibb->labibb_ctrl_state) & OVR_LAB_PRECHARGE_CTL)) { + rc = of_property_read_u32(np, + "somc,qpnp-lab-max-precharge-time", &tmp); + if (!rc) + labibb->lab_fast_precharge_time = tmp; + + labibb->lab_fast_precharge_en = of_property_read_bool(np, + "somc,qpnp-lab-max-precharge-enable"); + } + + labibb->lab_soft_start = LAB_SOFT_START_TIME; + if (((labibb->labibb_ctrl_state) & OVR_LAB_SOFT_START_CTL)) { + rc = of_property_read_u32(np, + "somc,qpnp-lab-soft-start", &tmp); + if (!rc) + labibb->lab_soft_start = tmp; + } + + labibb->ibb_soft_start = IBB_SOFT_START_RESISTOR; + if (((labibb->labibb_ctrl_state) & OVR_IBB_SOFT_START_CTL)) { + rc = of_property_read_u32(np, + "somc,qpnp-ibb-discharge-resistor", &tmp); + if (!rc) + labibb->ibb_soft_start = tmp; + } + + labibb->lab_pd_full = false; + if (((labibb->labibb_ctrl_state) & OVR_LAB_PD_CTL)) + labibb->lab_pd_full = of_property_read_bool(np, + "somc,qpnp-lab-full-pull-down"); + + labibb->ibb_pd_full = false; + if (((labibb->labibb_ctrl_state) & OVR_IBB_PD_CTL)) + labibb->ibb_pd_full = of_property_read_bool(np, + "somc,qpnp-ibb-full-pull-down"); + + return 0; + +error: + return -EINVAL; +} + +static void conv_uv_data(char *data, int param_type, int *u_data, int *v_data) +{ + switch (param_type) { + case CLR_DATA_UV_PARAM_TYPE_RENE_DEFAULT: + *u_data = ((data[0] & 0x0F) << 2) | + /* 4bit of data[0] higher data. */ + ((data[1] >> 6) & 0x03); + /* 2bit of data[1] lower data. */ + *v_data = (data[1] & 0x3F); + /* Remainder 6bit of data[1] is effective as v_data. */ + break; + case CLR_DATA_UV_PARAM_TYPE_NOVA_DEFAULT: + case CLR_DATA_UV_PARAM_TYPE_RENE_SR: + /* 6bit is effective as u_data */ + *u_data = data[0] & 0x3F; + /* 6bit is effective as v_data */ + *v_data = data[1] & 0x3F; + break; + case CLR_DATA_UV_PARAM_TYPE_NOVA_AUO: + /* 6bit is effective as u_data */ + *u_data = data[0] & 0x3F; + /* 6bit is effective as v_data */ + *v_data = data[2] & 0x3F; + break; + default: + pr_err("%s: Failed to conv type:%d\n", __func__, param_type); + break; + } +} + +static int get_uv_param_len(int param_type, bool *short_response) +{ + int ret = 0; + + *short_response = false; + switch (param_type) { + case CLR_DATA_UV_PARAM_TYPE_RENE_DEFAULT: + ret = CLR_DATA_REG_LEN_RENE_DEFAULT; + break; + case CLR_DATA_UV_PARAM_TYPE_NOVA_DEFAULT: + ret = CLR_DATA_REG_LEN_NOVA_DEFAULT; + break; + case CLR_DATA_UV_PARAM_TYPE_NOVA_AUO: + ret = CLR_DATA_REG_LEN_NOVA_AUO; + break; + case CLR_DATA_UV_PARAM_TYPE_RENE_SR: + ret = CLR_DATA_REG_LEN_RENE_SR; + *short_response = true; + break; + default: + pr_err("%s: Failed to get param len\n", __func__); + break; + } + + return ret; +} + +static void get_uv_data(struct mdss_dsi_ctrl_pdata *ctrl_pdata, + int *u_data, int *v_data) +{ + struct dsi_cmd_desc *cmds = ctrl_pdata->spec_pdata->uv_read_cmds.cmds; + int param_type = ctrl_pdata->spec_pdata->pcc_data.param_type; + char buf[MDSS_DSI_LEN]; + char *pos = buf; + int len; + int i; + bool short_response; + struct dcs_cmd_req cmdreq; + + len = get_uv_param_len(param_type, &short_response); + + for (i = 0; i < ctrl_pdata->spec_pdata->uv_read_cmds.cmd_cnt; i++) { + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = cmds; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT; + cmdreq.rlen = short_response ? 1 : len; + cmdreq.rbuf = ctrl_pdata->rx_buf.data; + cmdreq.cb = NULL; + + mdss_dsi_cmdlist_put(ctrl_pdata, &cmdreq); + + memcpy(pos, ctrl_pdata->rx_buf.data, len); + pos += len; + cmds++; + } + conv_uv_data(buf, param_type, u_data, v_data); +} + +static int find_color_area(struct mdp_pcc_cfg_data *pcc_config, + struct mdss_pcc_data *pcc_data) +{ + int i; + int ret = 0; + + for (i = 0; i < pcc_data->tbl_size; i++) { + if (pcc_data->u_data < pcc_data->color_tbl[i].u_min) + continue; + if (pcc_data->u_data > pcc_data->color_tbl[i].u_max) + continue; + if (pcc_data->v_data < pcc_data->color_tbl[i].v_min) + continue; + if (pcc_data->v_data > pcc_data->color_tbl[i].v_max) + continue; + break; + } + pcc_data->tbl_idx = i; + if (i >= pcc_data->tbl_size) { + ret = -EINVAL; + goto exit; + } + + pcc_config->r.r = pcc_data->color_tbl[i].r_data; + pcc_config->g.g = pcc_data->color_tbl[i].g_data; + pcc_config->b.b = pcc_data->color_tbl[i].b_data; +exit: + return ret; +} + +static int find_color_area_for_srgb(struct mdss_pcc_data *pcc_data) +{ + int i; + int ret = 0; + + for (i = 0; i < pcc_data->tbl_size; i++) { + if (pcc_data->u_data < pcc_data->color_tbl[i].u_min) + continue; + if (pcc_data->u_data > pcc_data->color_tbl[i].u_max) + continue; + if (pcc_data->v_data < pcc_data->color_tbl[i].v_min) + continue; + if (pcc_data->v_data > pcc_data->color_tbl[i].v_max) + continue; + break; + } + pcc_data->tbl_idx = i; + if (i >= pcc_data->tbl_size) { + ret = -EINVAL; + } + + return ret; +} + +static int find_color_area_for_vivid(struct mdss_pcc_data *pcc_data) +{ + return find_color_area_for_srgb(pcc_data); +} + +static int find_color_area_for_hdr(struct mdss_pcc_data *pcc_data) +{ + return find_color_area_for_srgb(pcc_data); +} + +int mdss_dsi_panel_pcc_setup(struct mdss_panel_data *pdata) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_pcc_data *pcc_data = NULL; + struct mdss_pcc_data *srgb_pcc_data = NULL; + struct mdss_pcc_data *vivid_pcc_data = NULL; + struct mdss_pcc_data *hdr_pcc_data = NULL; + struct mdp_pcc_cfg_data pcc_config; + int ret; + u32 raw_u_data = 0, raw_v_data = 0; + struct mdss_panel_specific_pdata *spec_pdata = NULL; + u8 idx = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + spec_pdata = ctrl_pdata->spec_pdata; + + if (!ctrl_pdata->spec_pdata->pcc_enable) { + if (pdata->panel_info.dsi_master == pdata->panel_info.pdest) + pr_notice("%s (%d): pcc isn't enabled.\n", + __func__, __LINE__); + goto exit; + } + + pcc_data = &ctrl_pdata->spec_pdata->pcc_data; + + mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata); + if (ctrl_pdata->spec_pdata->pre_uv_read_cmds.cmds) + mdss_dsi_panel_cmds_send( + ctrl_pdata, &ctrl_pdata->spec_pdata->pre_uv_read_cmds, CMD_REQ_COMMIT); + if (ctrl_pdata->spec_pdata->uv_read_cmds.cmds) { + get_uv_data(ctrl_pdata, &pcc_data->u_data, &pcc_data->v_data); + raw_u_data = pcc_data->u_data; + raw_v_data = pcc_data->v_data; + } + if (pcc_data->u_data == 0 && pcc_data->v_data == 0) { + pr_notice("%s (%d): u,v is flashed 0.\n", + __func__, __LINE__); + goto exit; + } + if (!pcc_data->color_tbl) { + if (pdata->panel_info.dsi_master == pdata->panel_info.pdest) + pr_notice("%s (%d): color_tbl isn't found.\n", + __func__, __LINE__); + goto exit; + } + + memset(&pcc_config, 0, sizeof(struct mdp_pcc_cfg_data)); + ret = find_color_area(&pcc_config, pcc_data); + if (ret) { + pr_err("%s: failed to find color area.\n", __func__); + goto exit; + } + + if (spec_pdata->srgb_pcc_enable) { + srgb_pcc_data = &spec_pdata->srgb_pcc_data; + srgb_pcc_data->u_data = pcc_data->u_data; + srgb_pcc_data->v_data = pcc_data->v_data; + ret = find_color_area_for_srgb(srgb_pcc_data); + if (ret) { + pr_err("%s: failed to find color area.\n", __func__); + goto exit; + } + idx = srgb_pcc_data->tbl_idx; + pr_notice("SRGB : %s (%d): raw_ud=%d raw_vd=%d ct=%d " + "area=%d ud=%d vd=%d r=0x%08X g=0x%08X b=0x%08X\n", + __func__, __LINE__, + raw_u_data, raw_v_data, + srgb_pcc_data->color_tbl[idx].color_type, + srgb_pcc_data->color_tbl[idx].area_num, + srgb_pcc_data->u_data, srgb_pcc_data->v_data, + srgb_pcc_data->color_tbl[idx].r_data, + srgb_pcc_data->color_tbl[idx].g_data, + srgb_pcc_data->color_tbl[idx].b_data); + } + + if (spec_pdata->vivid_pcc_enable) { + vivid_pcc_data = &spec_pdata->vivid_pcc_data; + vivid_pcc_data->u_data = pcc_data->u_data; + vivid_pcc_data->v_data = pcc_data->v_data; + ret = find_color_area_for_vivid(vivid_pcc_data); + if (ret) { + pr_err("%s: failed to find color area.\n", __func__); + goto exit; + } + idx = vivid_pcc_data->tbl_idx; + pr_notice("Vivid : %s (%d): raw_ud=%d raw_vd=%d ct=%d " + "area=%d ud=%d vd=%d r=0x%08X g=0x%08X b=0x%08X\n", + __func__, __LINE__, + raw_u_data, raw_v_data, + vivid_pcc_data->color_tbl[idx].color_type, + vivid_pcc_data->color_tbl[idx].area_num, + vivid_pcc_data->u_data, vivid_pcc_data->v_data, + vivid_pcc_data->color_tbl[idx].r_data, + vivid_pcc_data->color_tbl[idx].g_data, + vivid_pcc_data->color_tbl[idx].b_data); + } + + if (spec_pdata->hdr_pcc_enable) { + hdr_pcc_data = &spec_pdata->hdr_pcc_data; + hdr_pcc_data->u_data = pcc_data->u_data; + hdr_pcc_data->v_data = pcc_data->v_data; + ret = find_color_area_for_hdr(hdr_pcc_data); + if (ret) { + pr_err("%s: failed to find color area.\n", __func__); + goto exit; + } + idx = hdr_pcc_data->tbl_idx; + pr_notice("HDR : %s (%d): raw_ud=%d raw_vd=%d ct=%d " + "area=%d ud=%d vd=%d r=0x%08X g=0x%08X b=0x%08X\n", + __func__, __LINE__, + raw_u_data, raw_v_data, + hdr_pcc_data->color_tbl[idx].color_type, + hdr_pcc_data->color_tbl[idx].area_num, + hdr_pcc_data->u_data, hdr_pcc_data->v_data, + hdr_pcc_data->color_tbl[idx].r_data, + hdr_pcc_data->color_tbl[idx].g_data, + hdr_pcc_data->color_tbl[idx].b_data); + } + + pr_notice("%s (%d): raw_ud=%d raw_vd=%d " + "ct=%d area=%d ud=%d vd=%d r=0x%08X g=0x%08X b=0x%08X\n", + __func__, __LINE__, + raw_u_data, raw_v_data, + pcc_data->color_tbl[pcc_data->tbl_idx].color_type, + pcc_data->color_tbl[pcc_data->tbl_idx].area_num, + pcc_data->u_data, pcc_data->v_data, + pcc_data->color_tbl[pcc_data->tbl_idx].r_data, + pcc_data->color_tbl[pcc_data->tbl_idx].g_data, + pcc_data->color_tbl[pcc_data->tbl_idx].b_data); + +exit: + return 0; +} + +struct mdss_panel_specific_pdata *mdss_panel2spec_pdata( + struct mdss_panel_data *pdata) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata; + + ctrl_pdata = container_of(pdata, + struct mdss_dsi_ctrl_pdata, panel_data); + return ctrl_pdata->spec_pdata; +} + +static u32 ts_diff_ms(struct timespec lhs, struct timespec rhs) +{ + struct timespec tdiff; + s64 nsec; + u32 msec; + + tdiff = timespec_sub(lhs, rhs); + nsec = timespec_to_ns(&tdiff); + msec = (u32)nsec; + do_div(msec, NSEC_PER_MSEC); + + return msec; +} + +static struct fps_data *mdss_dsi_panel_driver_get_fps_address(fps_type type) +{ + switch (type) { + case FPSD: + return &fpsd; + case VPSD: + return &vpsd; + default: + pr_err("%s: select Failed!\n", __func__); + return NULL; + } +} + +static void update_fps_data(struct fps_data *fps) +{ + if (mutex_trylock(&fps->fps_lock)) { + u32 fpks = 0; + u32 ms_since_last = 0; + u32 num_frames; + struct timespec tlast = fps->timestamp_last; + struct timespec tnow; + u32 msec; + + getrawmonotonic(&tnow); + msec = ts_diff_ms(tnow, tlast); + fps->timestamp_last = tnow; + + fps->interval_ms = msec; + fps->frame_counter++; + num_frames = fps->frame_counter - fps->frame_counter_last; + + fps->fa[fps->fps_array_cnt].frame_nbr = fps->frame_counter; + fps->fa[fps->fps_array_cnt].time_delta = msec; + fps->fa_last_array_pos = fps->fps_array_cnt; + (fps->fps_array_cnt)++; + if (fps->fps_array_cnt >= DEFAULT_FPS_ARRAY_SIZE) + fps->fps_array_cnt = 0; + + ms_since_last = ts_diff_ms(tnow, fps->fpks_ts_last); + + if (num_frames > 1 && ms_since_last >= fps->log_interval) { + fpks = (num_frames * 1000000) / ms_since_last; + fps->fpks_ts_last = tnow; + fps->frame_counter_last = fps->frame_counter; + fps->fpks = fpks; + } + mutex_unlock(&fps->fps_lock); + } +} + +static void mdss_dsi_panel_driver_fps_data_init(fps_type type) +{ + struct fps_data *fps = mdss_dsi_panel_driver_get_fps_address(type); + + if (!fps) { + pr_err("%s: select Failed!\n", __func__); + return; + } + + fps->frame_counter = 0; + fps->frame_counter_last = 0; + fps->log_interval = DEFAULT_FPS_LOG_INTERVAL; + fps->fpks = 0; + fps->fa_last_array_pos = 0; + fps->vps_en = false; + getrawmonotonic(&fps->timestamp_last); + mutex_init(&fps->fps_lock); +} + +void mdss_dsi_panel_driver_fps_data_update( + struct msm_fb_data_type *mfd, fps_type type) +{ + struct fps_data *fps = mdss_dsi_panel_driver_get_fps_address(type); + + if (!fps) { + pr_err("%s: select Failed!\n", __func__); + return; + } + + if (mfd->index == 0) + update_fps_data(fps); +} + +struct fps_data mdss_dsi_panel_driver_get_fps_data(void) +{ + return fpsd; +} + +struct fps_data mdss_dsi_panel_driver_get_vps_data(void) +{ + return vpsd; +} + +static void vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t t) +{ + struct msm_fb_data_type *mfd = ctl->mfd; + + mdss_dsi_panel_driver_fps_data_update(mfd, VPSD); +} + +static void mdss_dsi_panel_driver_vsync_handler_init(void) +{ + vs_handle.vsync_handler = NULL; +} + +ssize_t mdss_dsi_panel_driver_vsyncs_per_ksecs_store(struct device *dev, + const char *buf, size_t count) +{ + int ret = count; + long vps_en; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct mdss_mdp_ctl *ctl = mdata->ctl_off; + + if (kstrtol(buf, 10, &vps_en)) { + dev_err(dev, "%s: Error, buf = %s\n", __func__, buf); + ret = -EINVAL; + goto exit; + } + + vs_handle.vsync_handler = (mdp_vsync_handler_t)vsync_handler; + vs_handle.cmd_post_flush = false; + + if (vps_en) { + vs_handle.enabled = false; + if (!vpsd.vps_en && (ctl->ops.add_vsync_handler)) { + ctl->ops.add_vsync_handler(ctl, &vs_handle); + vpsd.vps_en = true; + pr_notice("%s: vsyncs_per_ksecs is valid\n", __func__); + } + } else { + vs_handle.enabled = true; + if (vpsd.vps_en && (ctl->ops.remove_vsync_handler)) { + ctl->ops.remove_vsync_handler(ctl, &vs_handle); + vpsd.vps_en = false; + fpsd.fpks = 0; + pr_notice("%s: vsyncs_per_ksecs is invalid\n", __func__); + } + } +exit: + return ret; +} + +static void mdss_dsi_panel_driver_fps_cmd_send( + struct mdss_dsi_ctrl_pdata *ctrl_pdata, + u32 dfpks_rev, int dfpks) { + char dfps = (char)(dfpks_rev / KSEC); + struct mdss_panel_specific_pdata *spec_pdata = ctrl_pdata->spec_pdata; + struct mdss_panel_info *pinfo = &ctrl_pdata->panel_data.panel_info; + + pinfo->mipi.frame_rate = dfps; + + if (spec_pdata->chg_fps.mode != FPS_MODE_SUSRES) { + pr_debug("%s: fps change sequence\n", __func__); + mdss_dsi_panel_cmds_send(ctrl_pdata, + &ctrl_pdata->spec_pdata->fps_cmds, + CMD_REQ_COMMIT); + } + pr_notice("%s: change fpks=%d\n", __func__, dfpks); + + pinfo->new_fps = dfps; + spec_pdata->input_fpks = dfpks; +} + +static int mdss_dsi_panel_driver_fps_calc_rtn( + struct mdss_dsi_ctrl_pdata *ctrl_pdata, int dfpks) { + u32 dfpks_rev; + u32 vtotal_porch, vdisp; + u32 vrclk, vtp; + u32 cmds, payload; + struct mdss_panel_specific_pdata *spec_pdata = ctrl_pdata->spec_pdata; + u16 rtn; + int i, j, byte_cnt; + char send_rtn[sizeof(u16)] = {0}; + + vtotal_porch = spec_pdata->chg_fps.dric_total_porch; + vdisp = spec_pdata->chg_fps.dric_vdisp; + + vrclk = spec_pdata->chg_fps.dric_rclk; + vtp = spec_pdata->chg_fps.dric_tp; + + if (!dfpks || !(vdisp + vtotal_porch + vtp)) { + pr_err("%s: Invalid param dfpks=%d vdisp=%d porch=%d vtp=%d\n", + __func__, dfpks, vdisp, vtotal_porch, vtp); + return -EINVAL; + } + + rtn = (u16)( + (vrclk * KSEC) / + (dfpks * (vdisp + vtotal_porch + vtp)) + ); + + if (!rtn || !(vdisp + vtotal_porch + vtp)) { + pr_err("%s: Invalid param rtn=%d vdisp=%d porch=%d vtp=%d\n", + __func__, dfpks, vdisp, vtotal_porch, vtp); + return -EINVAL; + } + + dfpks_rev = (u32)( + (vrclk * KSEC) / + (rtn * (vdisp + vtotal_porch + vtp)) + ); + + pr_debug("%s: porch=%d vdisp=%d vtp=%d vrclk=%d rtn=0x%x\n", + __func__, vtotal_porch, vdisp, vtp, vrclk, rtn); + + for (i = 0; i < sizeof(send_rtn) ; i++) { + send_rtn[i] = (char)(rtn & 0x00FF); + pr_debug("%s: send_rtn[%d]=0x%x\n", + __func__, i, send_rtn[i]); + if (rtn > 0xFF) { + rtn = (rtn >> 8); + } else { + byte_cnt = i; + break; + } + } + + for (i = 0; i < (spec_pdata->chg_fps.send_pos.num / 2); i++) { + cmds = spec_pdata->chg_fps.send_pos.pos[(i * 2)]; + payload = spec_pdata->chg_fps.send_pos.pos[(i * 2) + 1]; + for (j = 0; j <= byte_cnt ; j++) + CHANGE_PAYLOAD(cmds, payload + j) = + send_rtn[byte_cnt - j]; + } + + mdss_dsi_panel_driver_fps_cmd_send(ctrl_pdata, dfpks_rev, dfpks); + + return 0; +} + +static int mdss_dsi_panel_driver_fps_calc_porch + (struct mdss_dsi_ctrl_pdata *ctrl_pdata, int dfpks) { + u64 vmclk; + u64 vtouch; + u32 dfpks_rev; + u32 vdisp; + u32 cmds, payload; + u32 porch_range_max = 0; + u32 porch_range_min = 0; + + int i, j; + u16 porch_calc = 0; + u16 send_byte; + u16 rtn; + u8 mask_pos; + char mask; + char porch[CHANGE_FPS_PORCH] = {0}; + char send[CHANGE_FPS_SEND] = {0}; + struct mdss_panel_specific_pdata *spec_pdata = ctrl_pdata->spec_pdata; + struct dsi_panel_cmds *fps_cmds = &(spec_pdata->fps_cmds); + + rtn = spec_pdata->chg_fps.dric_rtn; + vdisp = spec_pdata->chg_fps.dric_vdisp; + vtouch = spec_pdata->chg_fps.dric_vtouch; + vmclk = (u64)spec_pdata->chg_fps.dric_mclk; + send_byte = spec_pdata->chg_fps.send_byte; + mask_pos = spec_pdata->chg_fps.mask_pos; + mask = spec_pdata->chg_fps.mask; + porch_range_max = spec_pdata->chg_fps.porch_range[FPS_PORCH_RNG_MAX]; + porch_range_min = spec_pdata->chg_fps.porch_range[FPS_PORCH_RNG_MIN]; + + if (!dfpks || !vmclk || !rtn) { + pr_err("%s: Invalid param dfpks=%d vmclk=%llu rtn%d\n", + __func__, dfpks, vmclk, rtn); + return -EINVAL; + } + + porch_calc = (u16)(( + (((PSEC * KSEC) - (KSEC * vtouch * (u64)dfpks)) / + ((u64)dfpks * vmclk * (u64)rtn)) - (u64)vdisp) / 2); + + if (porch_range_max > 0) { + if ((porch_calc < porch_range_min) + || (porch_calc > porch_range_max)) { + pr_err("%s: Not supported. porch:%d\n", + __func__, porch_calc); + return -EINVAL; + } + } + + if (!(vmclk * rtn * (vdisp + porch_calc) + vtouch)) { + pr_err("%s: Invalid param \ + vmclk=%llu rtn=%d vdisp=%d porch=%d vtouch=%llu\n", + __func__, vmclk, rtn, vdisp, porch_calc, vtouch); + return -EINVAL; + } + + dfpks_rev = (u32)( + (PSEC * KSEC) / + (vmclk * rtn * (vdisp + porch_calc) + vtouch)); + + pr_debug("%s: porch=%d vdisp=%d vtouch=%llu vmclk=%llu rtn=0x%x\n", + __func__, porch_calc, vdisp, vtouch, vmclk, rtn); + + for (i = 0; i < CHANGE_FPS_PORCH ; i++) { + porch[i] = (char)(porch_calc & 0x00FF); + pr_debug("%s: porch[%d]=0x%x\n", __func__, i, porch[i]); + porch_calc = (porch_calc >> 8); + } + + for (i = 0; i < send_byte; i = i + 2) { + memcpy(send + i, porch, sizeof(char)); + memcpy(send + i + 1, porch + 1, sizeof(char)); + } + + for (i = 0; i < (spec_pdata->chg_fps.send_pos.num / 2); i++) { + cmds = spec_pdata->chg_fps.send_pos.pos[(i * 2)]; + payload = spec_pdata->chg_fps.send_pos.pos[(i * 2) + 1]; + for (j = 0; j < send_byte ; j++) { + if (j == mask_pos) + send[j] = (mask | send[j]); + CHANGE_PAYLOAD(cmds, payload + j) = send[j]; + pr_debug("%s: fps_cmds.cmds[%d].payload[%d]) = 0x%x\n", + __func__, + cmds, payload + j, + fps_cmds->cmds[cmds].payload[payload + j]); + } + } + + mdss_dsi_panel_driver_fps_cmd_send(ctrl_pdata, dfpks_rev, dfpks); + + return 0; +} + +static int mdss_dsi_panel_driver_fps_calc_adj + (struct mdss_dsi_ctrl_pdata *ctrl_pdata, int dfpks) { + u32 dfpks_rev; + u32 vtotal_porch, vdisp, vrclk; + u32 cmds, payload; + struct mdss_panel_specific_pdata *spec_pdata = ctrl_pdata->spec_pdata; + u16 rtn; + int i, j, byte_cnt; + char send_rtn[sizeof(u16)] = {0}, adj; + + if (!spec_pdata) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + vtotal_porch = spec_pdata->chg_fps.dric_total_porch; + vdisp = spec_pdata->chg_fps.dric_vdisp; + vrclk = spec_pdata->chg_fps.dric_rclk; + adj = spec_pdata->chg_fps.rtn_adj ? 1 : 0; + + if (!dfpks || !(vdisp + vtotal_porch)) { + pr_err("%s: Invalid param dfpks=%d vdisp=%d porch=%d\n", + __func__, dfpks, vdisp, vtotal_porch); + return -EINVAL; + } + + rtn = (u16)(vrclk / (dfpks * (vdisp + vtotal_porch) / 1000)) - adj; + + if (!rtn || !(vdisp + vtotal_porch)) { + pr_err("%s: Invalid param rtn=%d vdisp=%d porch=%d\n", + __func__, rtn, vdisp, vtotal_porch); + return -EINVAL; + } + + dfpks_rev = (u32)(vrclk / (rtn * (vdisp + vtotal_porch) / 1000)); + + pr_debug("%s: porch=%d vdisp=%d vrclk=%d rtn=0x%x adj=%d\n", + __func__, vtotal_porch, vdisp, vrclk, rtn + adj, adj); + + for (i = 0; i < sizeof(send_rtn) ; i++) { + send_rtn[i] = (char)(rtn & 0x00FF); + pr_debug("%s: send_rtn[%d]=0x%x\n", + __func__, i, send_rtn[i]); + if (rtn > 0xFF) { + rtn = (rtn >> 8); + } else { + byte_cnt = i; + break; + } + } + + for (i = 0; i < (spec_pdata->chg_fps.send_pos.num / 2); i++) { + cmds = spec_pdata->chg_fps.send_pos.pos[(i * 2)]; + payload = spec_pdata->chg_fps.send_pos.pos[(i * 2) + 1]; + for (j = 0; j <= byte_cnt ; j++) + CHANGE_PAYLOAD(cmds, payload + j) = + send_rtn[byte_cnt - j]; + } + + mdss_dsi_panel_driver_fps_cmd_send(ctrl_pdata, dfpks_rev, dfpks); + + return 0; +} + +static int mdss_dsi_panel_chg_fps_calc + (struct mdss_dsi_ctrl_pdata *ctrl_pdata, int dfpks) { + int ret = -EINVAL; + struct mdss_panel_specific_pdata *spec_pdata = NULL; + + spec_pdata = ctrl_pdata->spec_pdata; + if (!spec_pdata) { + pr_err("%s: Invalid input data\n", __func__); + return ret; + } + + switch (spec_pdata->chg_fps.type) { + case FPS_TYPE_UHD_4K: + ret = mdss_dsi_panel_driver_fps_calc_adj(ctrl_pdata, dfpks); + break; + case FPS_TYPE_HYBRID_INCELL: + ret = mdss_dsi_panel_driver_fps_calc_porch(ctrl_pdata, dfpks); + break; + case FPS_TYPE_FULL_INCELL: + ret = mdss_dsi_panel_driver_fps_calc_rtn(ctrl_pdata, dfpks); + break; + default: + pr_err("%s: Invalid type data\n", __func__); + break; + } + + return ret; +} + +static int mdss_dsi_panel_chg_fps_check_state + (struct mdss_dsi_ctrl_pdata *ctrl, int dfpks) { + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct msm_fb_data_type *mfd = mdata->ctl_off->mfd; + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + struct mdss_dsi_ctrl_pdata *sctrl = NULL; + int rc = 0; + + if (!mdp5_data->ctl || !mdp5_data->ctl->power_state) + goto error; + + if ((pinfo->mipi.mode == DSI_CMD_MODE) && + (!ctrl->spec_pdata->fps_cmds.cmd_cnt)) + goto cmd_cnt_err; + + if (!display_onoff_state) + goto disp_onoff_state_err; + + if (mdss_dsi_sync_wait_enable(ctrl)) { + sctrl = mdss_dsi_get_other_ctrl(ctrl); + if (sctrl) { + if (mdss_dsi_sync_wait_trigger(ctrl)) { + rc = mdss_dsi_panel_chg_fps_calc(sctrl, dfpks); + if (rc < 0) + goto end; + rc = mdss_dsi_panel_chg_fps_calc(ctrl, dfpks); + } else { + rc = mdss_dsi_panel_chg_fps_calc(ctrl, dfpks); + if (rc < 0) + goto end; + rc = mdss_dsi_panel_chg_fps_calc(sctrl, dfpks); + } + } else { + rc = mdss_dsi_panel_chg_fps_calc(ctrl, dfpks); + } + } else { + rc = mdss_dsi_panel_chg_fps_calc(ctrl, dfpks); + } +end: + return rc; +cmd_cnt_err: + pr_err("%s: change fps isn't supported\n", __func__); + return -EINVAL; +disp_onoff_state_err: + pr_err("%s: Disp-On is not yet completed. Please retry\n", __func__); + return -EINVAL; +error: + return -EINVAL; +} + +ssize_t mdss_dsi_panel_driver_change_fpks_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + int dfpks, rc; + + if (!ctrl_pdata || !ctrl_pdata->spec_pdata) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + if (!ctrl_pdata->spec_pdata->chg_fps.enable) { + pr_err("%s: change fps not enabled\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &dfpks); + if (rc < 0) { + pr_err("%s: Error, buf = %s\n", __func__, buf); + return rc; + } + + if (dfpks < 1000 * CHANGE_FPS_MIN + || dfpks > 1000 * CHANGE_FPS_MAX) { + pr_err("%s: invalid value for change_fpks buf = %s\n", + __func__, buf); + return -EINVAL; + } + + if (dfpks == ctrl_pdata->spec_pdata->input_fpks) { + pr_notice("%s: fpks is already %d\n", __func__, dfpks); + return count; + } + + rc = mdss_dsi_panel_chg_fps_check_state(ctrl_pdata, dfpks); + if (rc) { + pr_err("%s: Error, rc = %d\n", __func__, rc); + return rc; + } + return count; +} + +ssize_t mdss_dsi_panel_driver_change_fpks_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct msm_fb_data_type *mfd = mdata->ctl_off->mfd; + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + + if (!mdp5_data->ctl || !mdp5_data->ctl->power_state) + return 0; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + ctrl_pdata->spec_pdata->input_fpks); +} + +ssize_t mdss_dsi_panel_driver_change_fps_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + int dfps, dfpks, rc; + + if (!ctrl_pdata || !ctrl_pdata->spec_pdata) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + if (!ctrl_pdata->spec_pdata->chg_fps.enable) { + pr_err("%s: change fps not enabled\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &dfps); + if (rc < 0) { + pr_err("%s: Error, buf = %s\n", __func__, buf); + return rc; + } + + if (dfps >= 1000 * CHANGE_FPS_MIN + && dfps <= 1000 * CHANGE_FPS_MAX) { + dfpks = dfps; + } else if (dfps >= CHANGE_FPS_MIN && dfps <= CHANGE_FPS_MAX) { + dfpks = dfps * 1000; + } else { + pr_err("%s: invalid value for change_fps buf = %s\n", + __func__, buf); + return -EINVAL; + } + + if (dfpks == ctrl_pdata->spec_pdata->input_fpks) { + pr_notice("%s: fpks is already %d\n", __func__, dfpks); + return count; + } + + rc = mdss_dsi_panel_chg_fps_check_state(ctrl_pdata, dfpks); + if (rc) { + pr_err("%s: Error, rc = %d\n", __func__, rc); + return rc; + } + return count; +} + +ssize_t mdss_dsi_panel_driver_change_fps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = dev_get_drvdata(dev); + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct msm_fb_data_type *mfd = mdata->ctl_off->mfd; + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + + if (!mdp5_data->ctl || !mdp5_data->ctl->power_state) + return 0; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + ctrl_pdata->spec_pdata->input_fpks / 1000); +} + +void mdss_dsi_panel_driver_check_splash_enable( + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + if (ctrl_pdata->panel_data.panel_info.cont_splash_enabled) + display_onoff_state = true; + else + display_onoff_state = false; +} + +static void mdss_dsi_panel_driver_chg_fps_cmds_send + (struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct mdss_panel_specific_pdata *spec_pdata = NULL; + u32 fps_cmds, fps_payload; + char rtn; + + spec_pdata = ctrl_pdata->spec_pdata; + + if (ctrl_pdata->panel_data.panel_info.mipi.mode == DSI_CMD_MODE) { + if (spec_pdata->fps_cmds.cmd_cnt) { + fps_cmds = spec_pdata->chg_fps.send_pos.pos[0]; + fps_payload = spec_pdata->chg_fps.send_pos.pos[1]; + rtn = CHANGE_PAYLOAD(fps_cmds, fps_payload); + pr_debug("%s: change fps sequence --- rtn = 0x%x\n", + __func__, rtn); + mdss_dsi_panel_cmds_send(ctrl_pdata, + &ctrl_pdata->spec_pdata->fps_cmds, + CMD_REQ_COMMIT); + } + } +} + +void mdss_dsi_panel_driver_fb_notifier_call_chain( + struct msm_fb_data_type *mfd, int blank, bool type) +{ + struct fb_event event; + + if ((mfd->panel_info->type == MIPI_VIDEO_PANEL) || + (mfd->panel_info->type == MIPI_CMD_PANEL)) { + if (!mdss_dsi_panel_driver_is_incell_operation()) { + event.info = mfd->fbi; + event.data = ␣ + + if (type == FB_NOTIFIER_PRE) + fb_notifier_call_chain( + FB_EXT_EARLY_EVENT_BLANK, &event); + else + fb_notifier_call_chain( + FB_EXT_EVENT_BLANK, &event); + } + } +} + +void mdss_dsi_panel_driver_labibb_vreg_init( + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + int ret; + int min_uV, max_uV = 0; + struct mdss_panel_info *pinfo = NULL; + struct mdss_panel_specific_pdata *spec_pdata = NULL; + struct mdss_panel_labibb_data *labibb = NULL; + struct dss_vreg lab_vreg_config, ibb_vreg_config; + + pinfo = &ctrl_pdata->panel_data.panel_info; + if (!pinfo) { + pr_err("%s: Invalid panel data\n", __func__); + return; + } + + spec_pdata = ctrl_pdata->spec_pdata; + if (!spec_pdata) { + pr_err("%s: Invalid specific panel data\n", __func__); + return; + } + + labibb = &(spec_pdata->labibb); + if (!labibb) { + pr_err("%s: Invalid regulator settings\n", __func__); + return; + } + + + /* + * Get lab/ibb info. + */ + ret = mdss_dsi_panel_driver_vreg_name_to_config(ctrl_pdata, + &lab_vreg_config, "lab"); + if (ret) { + pr_err("%s: lab not registered\n", __func__); + return; + } + + ret = mdss_dsi_panel_driver_vreg_name_to_config(ctrl_pdata, + &ibb_vreg_config, "ibb"); + if (ret) { + pr_err("%s: ibb not registered\n", __func__); + return; + } + + /* + * Set lab/ibb voltage. + */ + + if (((labibb->labibb_ctrl_state) & OVR_LAB_VOLTAGE)) { + min_uV = labibb->lab_output_voltage; + max_uV = min_uV; + ret = regulator_set_voltage(lab_vreg_config.vreg, + min_uV, max_uV); + if (ret) + pr_err("%s: Unable to configure of lab voltage.\n", __func__); + } + + if (((labibb->labibb_ctrl_state) & OVR_IBB_VOLTAGE)) { + min_uV = labibb->ibb_output_voltage; + max_uV = min_uV; + ret = regulator_set_voltage(ibb_vreg_config.vreg, min_uV, max_uV); + if (ret) + pr_err("%s: Unable to configure of ibb voltage.\n", + __func__); + } + + /* + * Set lab/ibb current max + */ + if (((labibb->labibb_ctrl_state) & OVR_LAB_CURRENT_MAX)) { + ret = qpnp_lab_set_current_max(lab_vreg_config.vreg, + labibb->lab_current_max); + if (ret) + pr_err("%s: Unable to configure of lab current_max.\n", + __func__); + } + + if (((labibb->labibb_ctrl_state) & OVR_IBB_CURRENT_MAX)) { + ret = qpnp_ibb_set_current_max(ibb_vreg_config.vreg, + labibb->ibb_current_max); + if (ret) + pr_err("%s: Unable to configure of ibb current_max.\n", + __func__); + } + + /* + * Set lab precharge + */ + if (((labibb->labibb_ctrl_state) & OVR_LAB_PRECHARGE_CTL)) { + ret = qpnp_lab_set_precharge(lab_vreg_config.vreg, + labibb->lab_fast_precharge_time, + labibb->lab_fast_precharge_en); + if (ret) + pr_err("%s: Unable to configure of lab precharge.\n", + __func__); + } + + /* + * Set lab/ibb soft-start control + */ + if (((labibb->labibb_ctrl_state) & OVR_LAB_SOFT_START_CTL)) { + ret = qpnp_lab_set_soft_start(lab_vreg_config.vreg, + labibb->lab_soft_start); + if (ret) + pr_err("%s: Unable to configure of lab soft-start.\n", + __func__); + } + + if (((labibb->labibb_ctrl_state) & OVR_IBB_SOFT_START_CTL)) { + ret = qpnp_ibb_set_soft_start(ibb_vreg_config.vreg, + labibb->ibb_soft_start); + if (ret) + pr_err("%s: Unable to configure of ibb soft-start.\n", + __func__); + } + + /* + * Set lab/ibb pull-down control + */ + if (((labibb->labibb_ctrl_state) & OVR_LAB_PD_CTL)) { + ret = qpnp_lab_set_pull_down(lab_vreg_config.vreg, + labibb->lab_pd_full); + if (ret) + pr_err("%s: Unable to configure of lab pull-down.\n", + __func__); + } + + if (((labibb->labibb_ctrl_state) & OVR_IBB_PD_CTL)) { + ret = qpnp_ibb_set_pull_down(ibb_vreg_config.vreg, + labibb->ibb_pd_full); + if (ret) + pr_err("%s: Unable to configure of ibb pull-down.\n", + __func__); + } +} + +void mdss_dsi_panel_driver_init(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + ctrl_pdata->spec_pdata->pcc_setup = mdss_dsi_panel_pcc_setup; + ctrl_pdata->spec_pdata->color_mode = CLR_MODE_SELECT_DCIP3; + ctrl_pdata->spec_pdata->esd_enable_without_xlog + = ESD_WITHOUT_XLOG_DISABLE_VALUE; + mdss_dsi_panel_driver_fps_data_init(FPSD); + mdss_dsi_panel_driver_fps_data_init(VPSD); + mdss_dsi_panel_driver_vsync_handler_init(); +} + +void mdss_dsi_panel_driver_off(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct mdss_mdp_ctl *ctl = mdata->ctl_off; + + if (ctrl_pdata->spec_pdata->black_screen_off) + ctrl_pdata->spec_pdata->black_screen_off(ctrl_pdata); + + vs_handle.vsync_handler = (mdp_vsync_handler_t)vsync_handler; + vs_handle.cmd_post_flush = false; + vs_handle.enabled = true; + if (vpsd.vps_en && (ctl->ops.remove_vsync_handler)) { + ctl->ops.remove_vsync_handler(ctl, &vs_handle); + vpsd.vps_en = false; + fpsd.fpks = 0; + pr_notice("%s: vsyncs_per_ksecs is invalid\n", __func__); + } + display_onoff_state = false; +} + +void mdss_dsi_panel_driver_post_on(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct mdss_panel_specific_pdata *spec_pdata = NULL; + struct mdss_panel_data *pdata; + + spec_pdata = ctrl_pdata->spec_pdata; + pdata = &(ctrl_pdata->panel_data); + + if (!pdata) + return; + + if (pdata->panel_info.pdest != DISPLAY_1) + return; + + if (spec_pdata->chg_fps.enable) { + if (spec_pdata->chg_fps.mode == FPS_MODE_SUSRES) + mdss_dsi_panel_chg_fps_calc(ctrl_pdata, + spec_pdata->input_fpks); + + mdss_dsi_panel_driver_chg_fps_cmds_send(ctrl_pdata); + } else { + pr_notice("%s: change fps is not supported.\n", __func__); + } + + display_onoff_state = true; +} + +void mdss_dsi_panel_driver_unblank(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + if (ctrl_pdata->spec_pdata->pcc_data.pcc_sts & PCC_STS_UD) { + ctrl_pdata->spec_pdata->pcc_setup(&ctrl_pdata->panel_data); + ctrl_pdata->spec_pdata->pcc_data.pcc_sts &= ~PCC_STS_UD; + } +} + +void mdss_dsi_panel_driver_dump_incell_sts(struct incell_ctrl *incell) +{ + int num; + + pr_err("%s: sts current:0x%x\n", __func__, (int)(incell->state)); + for (num = 0 ; num < INCELL_BACKUP_NUM ; num++) + pr_err("%s: back ups %d :0x%x\n", __func__, + num, (int)(incell->backups[num])); +} + +void mdss_dsi_panel_driver_update_incell_bk(struct incell_ctrl *incell) +{ + int num; + + for (num = INCELL_BACKUP_NUM - 1 ; num > 0 ; num--) + incell->backups[num] = incell->backups[num - 1]; + + incell->backups[0] = incell->state; +} diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel_driver.h b/drivers/video/fbdev/msm/mdss_dsi_panel_driver.h new file mode 100644 index 0000000000000000000000000000000000000000..0bce9d978419869d7288b49360208167e0dc0145 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_panel_driver.h @@ -0,0 +1,430 @@ +/* drivers/video/fbdev/msm/mdss_dsi_panel_driver.h + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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 MDSS_DSI_PANEL_DRIVER_H +#define MDSS_DSI_PANEL_DRIVER_H + +#include + +#include "mdss_dsi.h" +#include "mdss_fb.h" +#include "mdss_mdp.h" + +/* pcc data infomation */ +#define PCC_STS_UD 0x01 /* update request */ +#define UNUSED 0xff +#define CLR_DATA_REG_LEN_RENE_DEFAULT 2 +#define CLR_DATA_REG_LEN_NOVA_DEFAULT 1 +#define CLR_DATA_REG_LEN_NOVA_AUO 3 +#define CLR_DATA_REG_LEN_RENE_SR 1 +enum { + CLR_DATA_UV_PARAM_TYPE_NONE, + CLR_DATA_UV_PARAM_TYPE_RENE_DEFAULT, + CLR_DATA_UV_PARAM_TYPE_NOVA_DEFAULT, + CLR_DATA_UV_PARAM_TYPE_NOVA_AUO, + CLR_DATA_UV_PARAM_TYPE_RENE_SR +}; + +/* color mode */ +#define CLR_MODE_SELECT_SRGB (100) +#define CLR_MODE_SELECT_DCIP3 (101) +#define CLR_MODE_SELECT_PANELNATIVE (102) + +/* fb_notifier call type */ +#define FB_NOTIFIER_PRE ((bool)true) +#define FB_NOTIFIER_POST ((bool)false) + +/* esd */ +#define ESD_WITHOUT_XLOG_ENABLE_VALUE (1) +#define ESD_WITHOUT_XLOG_DISABLE_VALUE (0) + +/* touch I/F data information for incell */ +/* touch I/F or not */ +#define INCELL_TOUCH_RUN ((bool)true) +#define INCELL_TOUCH_IDLE ((bool)false) + +/* incell status */ +#define INCELL_POWER_STATE_ON BIT(0) +#define INCELL_EWU_STATE_ON BIT(1) +#define INCELL_LOCK_STATE_ON BIT(2) +#define INCELL_SYSTEM_STATE_ON BIT(3) + +#define INCELL_POWER_STATE_OFF ~INCELL_POWER_STATE_ON +#define INCELL_EWU_STATE_OFF ~INCELL_EWU_STATE_ON +#define INCELL_LOCK_STATE_OFF ~INCELL_LOCK_STATE_ON +#define INCELL_SYSTEM_STATE_OFF ~INCELL_SYSTEM_STATE_ON + +/* SLE000-P0: Initial setting the case of booting by Kernel */ +#define INCELL_INIT_STATE_KERNEL (0x0f \ + & INCELL_POWER_STATE_OFF \ + & INCELL_EWU_STATE_OFF \ + & INCELL_LOCK_STATE_OFF \ + & INCELL_SYSTEM_STATE_OFF) + +/* SLE100-P1: Initial setting the case of booting by LK */ +#define INCELL_INIT_STATE_LK (INCELL_POWER_STATE_ON \ + | INCELL_SYSTEM_STATE_ON) + +/* The conditions of status if incell_work needed or not */ +#define INCELL_WORK_NEED_P_OFF INCELL_POWER_STATE_ON +#define INCELL_WORK_NEED_P_ON INCELL_SYSTEM_STATE_ON +#define INCELL_WORK_NEED_P_ON_EWU (INCELL_SYSTEM_STATE_ON \ + | INCELL_EWU_STATE_ON) + +#define INCELL_BACKUP_NUM 10 + +/* status to adjust power for incell panel or not */ +typedef enum { + INCELL_WORKER_OFF, + INCELL_WORKER_PENDING, + INCELL_WORKER_ON, +} incell_worker_state; + +/* + * Incell status change mode + * + * SP means the below. + * S : System + * P : Power + */ +typedef enum { + INCELL_STATE_NONE, + INCELL_STATE_S_OFF, + INCELL_STATE_P_OFF, + INCELL_STATE_SP_OFF, + INCELL_STATE_S_ON, + INCELL_STATE_P_ON, + INCELL_STATE_SP_ON, +} incell_state_change; + +/* How to send power sequence */ +typedef enum { + POWER_OFF_EXECUTE, + POWER_OFF_SKIP, + POWER_ON_EXECUTE, + POWER_ON_SKIP, + POWER_ON_EWU_SEQ, +} incell_pw_seq; + +/* control parameters for incell panel */ +struct incell_ctrl { + unsigned char state; + unsigned char backups[INCELL_BACKUP_NUM]; + + incell_state_change change_state; + incell_pw_seq seq; + + bool incell_intf_operation; + incell_intf_mode intf_mode; + + incell_worker_state worker_state; + struct work_struct incell_work; +}; + +#define DEFAULT_FPS_LOG_INTERVAL 100 +#define DEFAULT_FPS_ARRAY_SIZE 120 + +#define HYBRID_INCELL ((bool)true) +#define FULL_INCELL ((bool)false) + +#define CHANGE_FPS_MIN 22 +#define CHANGE_FPS_MAX 60 + +#define CHANGE_FPS_PORCH 2 +#define CHANGE_FPS_SEND 10 + +#define PSEC ((u64)1000000000000) +#define KSEC ((u64)1000) +#define CHANGE_PAYLOAD(a, b) (spec_pdata->fps_cmds.cmds[a].payload[b]) + +typedef enum { + FPS_PORCH_RNG_MIN = 0, + FPS_PORCH_RNG_MAX, + FPS_PORCH_RNG_NUM, +} fps_porch_rng_index; + +typedef enum FPS_TYPE { + FPSD, + VPSD +} fps_type; + +typedef enum FPS_PANEL_TYPE { + FPS_TYPE_UHD_4K, + FPS_TYPE_HYBRID_INCELL, + FPS_TYPE_FULL_INCELL, +} fps_panel_type; + +typedef enum FPS_PANEL_MODE { + FPS_MODE_SUSRES, + FPS_MODE_DYNAMIC, +} fps_panel_mode; + +struct fps_array { + u32 frame_nbr; + u32 time_delta; +}; + +struct fps_data { + struct mutex fps_lock; + u32 log_interval; + u32 interval_ms; + struct timespec timestamp_last; + u32 frame_counter_last; + u32 frame_counter; + u32 fpks; + struct timespec fpks_ts_last; + u16 fa_last_array_pos; + struct fps_array fa[DEFAULT_FPS_ARRAY_SIZE]; + u16 fps_array_cnt; + bool vps_en; +}; + +struct change_fps_send_pos { + int num; + int *pos; +}; + +struct change_fps { + /* common */ + bool enable; + fps_panel_type type; + fps_panel_mode mode; + u32 dric_vdisp; + struct change_fps_send_pos send_pos; + u32 dric_rclk; + u32 dric_total_porch; + u8 chg_fps_type; + u8 chg_fps_mode; + + /* uhd_4k */ + bool rtn_adj; + + /* hybrid */ + u32 dric_mclk; + u32 dric_vtouch; + u32 porch_range[FPS_PORCH_RNG_NUM]; + u16 dric_rtn; + u16 send_byte; + u16 mask_pos; + char mask; + + /* full */ + u32 dric_tp; +}; + +struct mdss_panel_power_seq { + int seq_num; + int *rst_seq; + bool rst_b_seq; + + int disp_vdd; + int disp_vddio; + int disp_vsp; + int disp_vsn; + int disp_dcdc; + int touch_avdd; + int touch_vddio; + int touch_reset; + int touch_reset_first; + int touch_intn; +}; + +struct mdss_pcc_color_tbl { + u32 color_type; + u32 area_num; + u32 u_min; + u32 u_max; + u32 v_min; + u32 v_max; + u32 r_data; + u32 g_data; + u32 b_data; +} __packed; + +struct mdss_pcc_data { + struct mdss_pcc_color_tbl *color_tbl; + u32 tbl_size; + u8 tbl_idx; + u8 pcc_sts; + u32 u_data; + u32 v_data; + int param_type; +}; + +/* lab/ibb control default data */ +#define OVR_LAB_VOLTAGE BIT(0) +#define OVR_IBB_VOLTAGE BIT(1) +#define OVR_LAB_CURRENT_MAX BIT(2) +#define OVR_IBB_CURRENT_MAX BIT(3) +#define OVR_LAB_PRECHARGE_CTL BIT(4) +#define OVR_LAB_SOFT_START_CTL BIT(5) +#define OVR_IBB_SOFT_START_CTL BIT(6) +#define OVR_LAB_PD_CTL BIT(7) +#define OVR_IBB_PD_CTL BIT(8) + +#define QPNP_REGULATOR_VSP_V_5P4V ((u32)5600000) +#define QPNP_REGULATOR_VSN_V_M5P4V ((u32)5600000) +#define LAB_CURRENT_MAX ((u32)200) +#define IBB_CURRENT_MAX ((u32)800) +#define LAB_FAST_PRECHARGE_TIME ((u32)500) +#define LAB_SOFT_START_TIME ((u32)300) +#define IBB_SOFT_START_RESISTOR ((u32)32) + +struct mdss_panel_labibb_data { + u16 labibb_ctrl_state; + + u32 lab_output_voltage; + u32 ibb_output_voltage; + u32 lab_current_max; + u32 ibb_current_max; + u32 lab_fast_precharge_time; + u32 lab_soft_start; + u32 ibb_soft_start; + + bool lab_fast_precharge_en; + bool lab_pd_full; + bool ibb_pd_full; +}; + +struct mdss_panel_specific_pdata { + int disp_vddio_gpio; + int disp_dcdc_en_gpio; + int touch_vddio_gpio; + int touch_reset_gpio; + int touch_int_gpio; + + int color_mode; + bool pcc_enable; + bool srgb_pcc_enable; + bool vivid_pcc_enable; + bool hdr_pcc_enable; + struct dsi_panel_cmds pre_uv_read_cmds; + struct dsi_panel_cmds uv_read_cmds; + struct mdss_pcc_data pcc_data; + struct mdss_pcc_data srgb_pcc_data; + struct mdss_pcc_data vivid_pcc_data; + struct mdss_pcc_data hdr_pcc_data; + + struct mdss_panel_power_seq on_seq; + struct mdss_panel_power_seq off_seq; + u32 down_period; + + struct mdss_panel_labibb_data labibb; + + struct mdss_panel_power_seq ewu_seq; + + int (*pcc_setup)(struct mdss_panel_data *pdata); + + int input_fpks; + struct change_fps chg_fps; + struct dsi_panel_cmds fps_cmds; + + void (*crash_counter_reset)(void); + void (*blackscreen_det)(void); + void (*fff_time_update)(struct mdss_panel_specific_pdata *spec_pdata); + void (*black_screen_off)(struct mdss_dsi_ctrl_pdata *ctrl_pdata); + bool resume_started; + bool panel_type; + + u32 esd_enable_without_xlog; +}; + +void mdss_dsi_panel_driver_init(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +void mdss_dsi_panel_driver_fps_data_update( + struct msm_fb_data_type *mfd, fps_type type); +struct fps_data mdss_dsi_panel_driver_get_fps_data(void); +struct fps_data mdss_dsi_panel_driver_get_vps_data(void); +ssize_t mdss_dsi_panel_driver_vsyncs_per_ksecs_store(struct device *dev, + const char *buf, size_t count); + +ssize_t mdss_dsi_panel_driver_change_fpks_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +ssize_t mdss_dsi_panel_driver_change_fpks_show(struct device *dev, + struct device_attribute *attr, char *buf); +ssize_t mdss_dsi_panel_driver_change_fps_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +ssize_t mdss_dsi_panel_driver_change_fps_show(struct device *dev, + struct device_attribute *attr, char *buf); + +void mdss_dsi_panel_driver_detection(struct platform_device *pdev, + struct device_node **np); +int mdss_dsi_panel_driver_pinctrl_init(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +int mdss_dsi_panel_driver_touch_pinctrl_set_state( + struct mdss_dsi_ctrl_pdata *ctrl_pdata, bool active); +int mdss_dsi_panel_driver_power_off(struct mdss_panel_data *pdata); +int mdss_dsi_panel_driver_power_on(struct mdss_panel_data *pdata); +void mdss_dsi_panel_driver_off(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +void mdss_dsi_panel_driver_post_on(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +int mdss_dsi_panel_driver_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +void mdss_dsi_panel_driver_gpio_free(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +void mdss_dsi_panel_driver_parse_gpio_params(struct platform_device *ctrl_pdev, + struct mdss_dsi_ctrl_pdata *ctrl_pdata); +int mdss_dsi_panel_driver_reset_panel(struct mdss_panel_data *pdata, + int enable); +int mdss_dsi_panel_driver_reset_touch(struct mdss_panel_data *pdata, + int enable); +int mdss_dsi_panel_driver_reset_dual_display( + struct mdss_dsi_ctrl_pdata *ctrl_pdata); +int mdss_dsi_panel_driver_parse_dt(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl_pdata); +int mdss_dsi_panel_pcc_setup(struct mdss_panel_data *pdata); +void mdss_dsi_panel_driver_fb_notifier_call_chain( + struct msm_fb_data_type *mfd, int blank, bool type); +void mdss_dsi_panel_driver_check_splash_enable( + struct mdss_dsi_ctrl_pdata *ctrl_pdata); +void mdss_dsi_panel_driver_unblank(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +void mdss_dsi_panel_driver_labibb_vreg_init( + struct mdss_dsi_ctrl_pdata *ctrl_pdata); + +/* For incell driver */ +struct incell_ctrl *incell_get_info(void); +void incell_panel_power_worker_canceling(struct incell_ctrl *incell); +void incell_driver_init(void); +bool mdss_dsi_panel_driver_is_power_lock(unsigned char state); +bool mdss_dsi_panel_driver_is_power_on(unsigned char state); +bool mdss_dsi_panel_driver_is_ewu(unsigned char state); +bool mdss_dsi_panel_driver_is_system_on(unsigned char state); +void mdss_dsi_panel_driver_state_change_off(struct incell_ctrl *incell); +void mdss_dsi_panel_driver_power_off_ctrl(struct incell_ctrl *incell); +void mdss_dsi_panel_driver_state_change_on(struct incell_ctrl *incell); +void mdss_dsi_panel_driver_power_on_ctrl(struct incell_ctrl *incell); +struct mdss_panel_specific_pdata *mdss_panel2spec_pdata( + struct mdss_panel_data *pdata); +void mdss_dsi_panel_driver_dump_incell_sts(struct incell_ctrl *incell); +void mdss_dsi_panel_driver_update_incell_bk(struct incell_ctrl *incell); + +/* Qualcomm original function */ +int mdss_dsi_pinctrl_set_state(struct mdss_dsi_ctrl_pdata *ctrl_pdata, + bool active); +int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +int mdss_dsi_parse_dcs_cmds(struct device_node *np, + struct dsi_panel_cmds *pcmds, char *cmd_key, char *link_key); +void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds, u32 flags); + +static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_master_ctrl( + struct mdss_panel_data *pdata) +{ + int dsi_master = DSI_CTRL_0; + + if (pdata->panel_info.dsi_master == DISPLAY_2) + dsi_master = DSI_CTRL_1; + else + dsi_master = DSI_CTRL_0; + + return mdss_dsi_get_ctrl_by_index(dsi_master); +} + +#endif /* MDSS_DSI_PANEL_DRIVER_H */ diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c index 0f24f66dbcc67d44283a66a4fc6d7c368b0fef6c..0026b855b0614789bcd7ae0ee6f2ecf4b9352a22 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_status.c +++ b/drivers/video/fbdev/msm/mdss_dsi_status.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -30,7 +35,11 @@ #include "mdss_panel.h" #include "mdss_mdp.h" +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +#define STATUS_CHECK_INTERVAL_MS 20000 +#else #define STATUS_CHECK_INTERVAL_MS 5000 +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ #define STATUS_CHECK_INTERVAL_MIN_MS 50 #define DSI_STATUS_CHECK_INIT -1 #define DSI_STATUS_CHECK_DISABLE 1 diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 6c4db0f1f5bdedaa26046ec38774a3f9ae422394..68953a2fa28a96c109c7859132bc5f250c77768b 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -13,6 +13,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -56,6 +61,12 @@ #include "mdss_smmu.h" #include "mdss_mdp.h" +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +#include +#include "mdss_dsi_panel_driver.h" +#include "mdss_dsi_panel_debugfs.h" +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + #ifdef CONFIG_FB_MSM_TRIPLE_BUFFER #define MDSS_FB_NUM 3 #else @@ -82,6 +93,12 @@ */ #define MDP_TIME_PERIOD_CALC_FPS_US 1000000 +#ifdef SOMC_FEATURE_EARLY_UNBLANK +#include +/* with a define we avoid modifying fb.h's FB-enum */ +#define FB_EARLY_UNBLANK 0xC0FFEE +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ + static struct fb_info *fbi_list[MAX_FBI_LIST]; static int fbi_list_index; @@ -271,8 +288,111 @@ static int mdss_fb_notify_update(struct msm_fb_data_type *mfd, return ret; } +#ifdef SOMC_FEATURE_EARLY_UNBLANK +static void mdss_background_unblank(struct work_struct *ws); + +static int pwr_pressed; + +static void +mdss_fb_update_early_unblank_completed(struct msm_fb_data_type *mfd, + bool update) +{ + if (mfd->unblank_kworker) { + pr_debug("early_unblank_complete set to %s\n", + update ? "true" : "false"); + mfd->early_unblank_completed = update; + } +} + +static bool +mdss_fb_did_early_unblank(struct msm_fb_data_type *mfd) +{ + return mfd->early_unblank_completed; +} + +static void +mdss_input_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + /* only react on key down events */ + if (code == KEY_POWER && value == 1) { + pr_debug("power key pressed!"); + pwr_pressed = true; + } +} + +static int +mdss_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "mdss_fb"; + pr_debug("registering %s handle for %s", + handle->name, dev->name); + + error = input_register_handle(handle); + if (error) + goto err2; + + error = input_open_device(handle); + if (error) + goto err1; + + return 0; +err1: + input_unregister_handle(handle); +err2: + kfree(handle); + return error; +} + +static void +mdss_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id mdss_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_KEYBIT, + .keybit = { [BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER) }, + }, + { }, +}; + +static void mdss_ensure_kworker_done(struct workqueue_struct *wq) +{ + if (wq) { + pr_debug("wait for unblank work"); + flush_workqueue(wq); + pr_debug("done waiting for unblank work"); + } +} + +static struct input_handler mdss_input_handler = { + .event = mdss_input_event, + .connect = mdss_input_connect, + .disconnect = mdss_input_disconnect, + .name = "mdss_fb", + .id_table = mdss_ids, +}; +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ + static int lcd_backlight_registered; +extern struct kset *class_kset; +static struct kobject *backlight_link_kobj; + static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev, enum led_brightness value) { @@ -1246,6 +1366,10 @@ static int mdss_fb_probe(struct platform_device *pdev) struct fb_info *fbi; struct mdss_overlay_private *mdp5_data = NULL; int rc; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct incell_ctrl *incell = NULL; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (fbi_list_index >= MAX_FBI_LIST) return -ENOMEM; @@ -1254,6 +1378,11 @@ static int mdss_fb_probe(struct platform_device *pdev) if (!pdata) return -EPROBE_DEFER; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + if (!mdp_instance) { pr_err("mdss mdp resource not initialized yet\n"); return -ENODEV; @@ -1313,6 +1442,12 @@ static int mdss_fb_probe(struct platform_device *pdev) INIT_LIST_HEAD(&mfd->file_list); +#ifdef SOMC_FEATURE_EARLY_UNBLANK + mfd->early_unblank_completed = false; + mfd->unblank_kworker = NULL; + INIT_WORK(&mfd->unblank_work, mdss_background_unblank); +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ + mutex_init(&mfd->bl_lock); mutex_init(&mfd->mdss_sysfs_lock); mutex_init(&mfd->switch_lock); @@ -1350,6 +1485,21 @@ static int mdss_fb_probe(struct platform_device *pdev) pr_err("led_classdev_register failed\n"); else lcd_backlight_registered = 1; + + if (lcd_backlight_registered) { + backlight_link_kobj = kobject_create_and_add("backlight", + &class_kset->kobj); + if (backlight_link_kobj != NULL) { + rc = sysfs_create_link(backlight_link_kobj, + &backlight_led.dev->kobj, "panel0-backlight"); + if (rc) { + pr_err("create backlight link failed.\n"); + kobject_put(backlight_link_kobj); + } + } else { + pr_err("create backlight kobj failed.\n"); + } + } } mdss_fb_init_panel_modes(mfd, pdata); @@ -1395,8 +1545,38 @@ static int mdss_fb_probe(struct platform_device *pdev) if (mdss_fb_register_input_handler(mfd)) pr_err("failed to register input handler\n"); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (mfd->index) + goto skip; + + if ((mfd->panel_info->type == MIPI_VIDEO_PANEL) || + (mfd->panel_info->type == MIPI_CMD_PANEL)) + mdss_dsi_create_debugfs(mfd); + + incell_driver_init(); + incell = incell_get_info(); + if (incell) { + if (mfd->panel_info->cont_splash_enabled) + incell->state = INCELL_INIT_STATE_LK; + pr_debug("%s: state:0x%x touch operation:%d\n", __func__, + (incell->state), (int)(incell->incell_intf_operation)); + + } + +skip: +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + INIT_DELAYED_WORK(&mfd->idle_notify_work, __mdss_fb_idle_notify_work); +#ifdef SOMC_FEATURE_EARLY_UNBLANK + if (mfd->index == 0) { + /* only the primary panel, index 0, uses this kworker */ + mfd->unblank_kworker = + create_singlethread_workqueue("unblanker"); + + } +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ + return rc; } @@ -1449,6 +1629,10 @@ static int mdss_fb_remove(struct platform_device *pdev) unregister_framebuffer(mfd->fbi); if (lcd_backlight_registered) { + if (backlight_link_kobj != NULL) { + sysfs_remove_link(backlight_link_kobj, "panel0-backlight"); + kobject_put(backlight_link_kobj); + } lcd_backlight_registered = 0; led_classdev_unregister(&backlight_led); } @@ -1489,6 +1673,10 @@ static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd) pr_debug("mdss_fb suspend index=%d\n", mfd->index); +#ifdef SOMC_FEATURE_EARLY_UNBLANK + mdss_ensure_kworker_done(mfd->unblank_kworker); +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ + ret = mdss_fb_pan_idle(mfd); if (ret) { pr_warn("mdss_fb_pan_idle for fb%d failed. ret=%d\n", @@ -1512,6 +1700,25 @@ static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd) * as a fall back option, enter ulp state to leave the display * on, but turn off all interface clocks. */ +#ifdef SOMC_FEATURE_EARLY_UNBLANK + /* Check if Early Unblank has done an early power on of + * the panel, but got no request from layers above to do so. + * If that is the case (for instance when a smart cover is + * used), the panel must be manually powered off during + * suspend as no BLANK request will come from above layers. + */ + if (mdss_fb_did_early_unblank(mfd)) { + mdss_fb_update_early_unblank_completed(mfd, false); + + ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi, + mfd->suspend.op_enable); + if (ret) { + pr_err("can't turn off display!\n"); + return ret; + } + mfd->suspend.panel_power_state = mfd->panel_power_state; + } else +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ if (mdss_fb_is_power_on(mfd)) { ret = mdss_fb_blank_sub(BLANK_FLAG_ULP, mfd->fbi, mfd->suspend.op_enable); @@ -1523,6 +1730,11 @@ static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd) mfd->op_enable = false; fb_set_suspend(mfd->fbi, FBINFO_STATE_SUSPENDED); } + +#ifdef SOMC_FEATURE_EARLY_UNBLANK + pwr_pressed = false; +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ + exit: return ret; } @@ -1560,8 +1772,17 @@ static int mdss_fb_resume_sub(struct msm_fb_data_type *mfd) * flag. If fb was in ulp state when entering suspend, then nothing * needs to be done. */ +#ifdef SOMC_FEATURE_EARLY_UNBLANK + if (mfd->unblank_kworker && pwr_pressed && + !mdss_panel_is_power_on_ulp(mfd->suspend.panel_power_state)) { + pr_notice("starting unblank async from resume"); + queue_work(mfd->unblank_kworker, &mfd->unblank_work); + } else if (mdss_panel_is_power_on(mfd->suspend.panel_power_state) && + !mdss_panel_is_power_on_ulp(mfd->suspend.panel_power_state)) { +#else if (mdss_panel_is_power_on(mfd->suspend.panel_power_state) && !mdss_panel_is_power_on_ulp(mfd->suspend.panel_power_state)) { +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ int unblank_flag = mdss_panel_is_power_on_interactive( mfd->suspend.panel_power_state) ? FB_BLANK_UNBLANK : BLANK_FLAG_LP; @@ -1870,7 +2091,11 @@ static int mdss_fb_blank_blank(struct msm_fb_data_type *mfd, req_power_state); if (cur_power_state == req_power_state) { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + pr_notice("%s: No change in power state\n", __func__); +#else pr_debug("No change in power state\n"); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ return 0; } @@ -1932,7 +2157,11 @@ static int mdss_fb_blank_unblank(struct msm_fb_data_type *mfd) MDSS_PANEL_POWER_ON); if (mdss_panel_is_power_on_interactive(cur_power_state)) { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + pr_notice("%s: No change in power state\n", __func__); +#else pr_debug("No change in power state\n"); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ return 0; } @@ -2043,6 +2272,14 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, switch (blank_mode) { case FB_BLANK_UNBLANK: +#ifdef SOMC_FEATURE_EARLY_UNBLANK + mdss_ensure_kworker_done(mfd->unblank_kworker); + mdss_fb_update_early_unblank_completed(mfd, false); + /* if kworker was successful we are done... + * but let's check and retry if not. fall through! + */ + case FB_EARLY_UNBLANK: +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ pr_debug("unblank called. cur pwr state=%d\n", cur_power_state); ret = mdss_fb_blank_unblank(mfd); break; @@ -2076,9 +2313,20 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_POWERDOWN: default: +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_panel_driver_fb_notifier_call_chain(mfd, + FB_BLANK_POWERDOWN, FB_NOTIFIER_PRE); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + req_power_state = MDSS_PANEL_POWER_OFF; pr_debug("blank powerdown called\n"); ret = mdss_fb_blank_blank(mfd, req_power_state); + +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (!ret) + mdss_dsi_panel_driver_fb_notifier_call_chain(mfd, + FB_BLANK_POWERDOWN, FB_NOTIFIER_POST); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ break; } @@ -2090,6 +2338,27 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, return ret; } +#ifdef SOMC_FEATURE_EARLY_UNBLANK +static void mdss_background_unblank(struct work_struct *ws) +{ + int ret; + struct msm_fb_data_type *mfd; + mfd = container_of(ws, struct msm_fb_data_type, unblank_work); + + pr_notice("unblank work running"); + + ret = mdss_fb_blank_sub(FB_EARLY_UNBLANK, mfd->fbi, + mfd->op_enable); + + if (ret) { + pr_warn("can't turn on display!\n"); + } else { + mdss_fb_update_early_unblank_completed(mfd, true); + fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING); + } +} +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ + static int mdss_fb_blank(int blank_mode, struct fb_info *info) { int ret; @@ -2888,6 +3157,7 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all) pr_err("PP release failed ret %d\n", ret); } +#ifndef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL /* reset backlight before blank to prevent backlight from * enabling ahead of unblank. for some special cases like * adb shell stop/start. @@ -2895,6 +3165,12 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all) mutex_lock(&mfd->bl_lock); mdss_fb_set_backlight(mfd, 0); mutex_unlock(&mfd->bl_lock); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + +#ifdef SOMC_FEATURE_EARLY_UNBLANK + mdss_ensure_kworker_done(mfd->unblank_kworker); + mdss_fb_update_early_unblank_completed(mfd, false); +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable); @@ -2947,8 +3223,19 @@ static void mdss_fb_power_setting_idle(struct msm_fb_data_type *mfd) static void __mdss_fb_copy_fence(struct msm_sync_pt_data *sync_pt_data, struct sync_fence **fences, u32 *fence_cnt) { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct msm_fb_data_type *mfd; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + pr_debug("%s: wait for fences\n", sync_pt_data->fence_name); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mfd = container_of(sync_pt_data, struct msm_fb_data_type, + mdp_sync_pt_data); + if (mfd->spec_mfd.off_sts) + return; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + mutex_lock(&sync_pt_data->sync_mutex); /* * Assuming that acq_fen_cnt is sanitized in bufsync ioctl @@ -3108,11 +3395,22 @@ static int __mdss_fb_sync_buf_done_callback(struct notifier_block *p, struct msm_fb_data_type *mfd; int fence_cnt; int ret = NOTIFY_OK; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct mdss_panel_data *pdata; + struct mdss_panel_specific_pdata *spec_pdata; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ sync_pt_data = container_of(p, struct msm_sync_pt_data, notifier); mfd = container_of(sync_pt_data, struct msm_fb_data_type, mdp_sync_pt_data); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (mfd->spec_mfd.off_sts) { + mdss_fb_signal_timeline(sync_pt_data); + return NOTIFY_OK; + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + switch (event) { case MDP_NOTIFY_FRAME_BEGIN: if (mfd->idle_time && !mod_delayed_work(system_wq, @@ -3143,6 +3441,20 @@ static int __mdss_fb_sync_buf_done_callback(struct notifier_block *p, case MDP_NOTIFY_FRAME_FLUSHED: pr_debug("%s: frame flushed\n", sync_pt_data->fence_name); sync_pt_data->flushed = true; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (mfd->panel.type == MIPI_VIDEO_PANEL || + mfd->panel.type == MIPI_CMD_PANEL) { + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("no panel connected\n"); + return -ENODEV; + } + spec_pdata = mdss_panel2spec_pdata(pdata); + if (spec_pdata->resume_started && spec_pdata->fff_time_update) + spec_pdata->fff_time_update(spec_pdata); + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ break; case MDP_NOTIFY_FRAME_TIMEOUT: pr_err("%s: frame timeout\n", sync_pt_data->fence_name); @@ -3688,6 +4000,10 @@ static int __mdss_fb_perform_commit(struct msm_fb_data_type *mfd) int ret = -ENOSYS; u32 new_dsi_mode, dynamic_dsi_switch = 0; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (mfd->spec_mfd.off_sts) + return 0; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (!sync_pt_data->async_wait_fences) mdss_fb_wait_for_fence(sync_pt_data); sync_pt_data->flushed = false; @@ -3720,6 +4036,9 @@ static int __mdss_fb_perform_commit(struct msm_fb_data_type *mfd) else pr_warn("no kickoff function setup for fb%d\n", mfd->index); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_panel_driver_fps_data_update(mfd, FPSD); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } else if (fb_backup->atomic_commit) { if (mfd->mdp.kickoff_fnc) ret = mfd->mdp.kickoff_fnc(mfd, @@ -3727,6 +4046,9 @@ static int __mdss_fb_perform_commit(struct msm_fb_data_type *mfd) else pr_warn("no kickoff function setup for fb%d\n", mfd->index); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + mdss_dsi_panel_driver_fps_data_update(mfd, FPSD); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ fb_backup->atomic_commit = false; } else { ret = mdss_fb_pan_display_sub(&fb_backup->disp_commit.var, @@ -4950,15 +5272,6 @@ static int __ioctl_wait_idle(struct msm_fb_data_type *mfd, u32 cmd) return ret; } -static bool check_not_supported_ioctl(u32 cmd) -{ - return((cmd == MSMFB_OVERLAY_SET) || (cmd == MSMFB_OVERLAY_UNSET) || - (cmd == MSMFB_OVERLAY_GET) || (cmd == MSMFB_OVERLAY_PREPARE) || - (cmd == MSMFB_DISPLAY_COMMIT) || (cmd == MSMFB_OVERLAY_PLAY) || - (cmd == MSMFB_BUFFER_SYNC) || (cmd == MSMFB_OVERLAY_QUEUE) || - (cmd == MSMFB_NOTIFY_UPDATE)); -} - /* * mdss_fb_do_ioctl() - MDSS Framebuffer ioctl function * @info: pointer to framebuffer info @@ -4986,6 +5299,11 @@ int mdss_fb_do_ioctl(struct fb_info *info, unsigned int cmd, if (!mfd) return -EINVAL; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (mfd->spec_mfd.off_sts) + return 0; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + if (mfd->shutdown_pending) return -ESHUTDOWN; @@ -4993,11 +5311,6 @@ int mdss_fb_do_ioctl(struct fb_info *info, unsigned int cmd, if (!pdata || pdata->panel_info.dynamic_switch_pending) return -EPERM; - if (check_not_supported_ioctl(cmd)) { - pr_err("Unsupported ioctl\n"); - return -EINVAL; - } - atomic_inc(&mfd->ioctl_ref_cnt); mdss_fb_power_setting_idle(mfd); @@ -5212,6 +5525,11 @@ int __init mdss_fb_init(void) if (platform_driver_register(&mdss_fb_driver)) return rc; +#ifdef SOMC_FEATURE_EARLY_UNBLANK + if (input_register_handler(&mdss_input_handler)) + return rc; +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ + return 0; } @@ -5259,11 +5577,21 @@ void mdss_fb_report_panel_dead(struct msm_fb_data_type *mfd) char *envp[2] = {"PANEL_ALIVE=0", NULL}; struct mdss_panel_data *pdata = dev_get_platdata(&mfd->pdev->dev); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + int rc = 0; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (!pdata) { pr_err("Panel data not available\n"); return; } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + rc = qpnp_labibb_ocp_check(); + if (rc) { + pr_notice("%s: ocp check error\n", __func__); + return; + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ pdata->panel_info.panel_dead = true; kobject_uevent_env(&mfd->fbi->dev->kobj, KOBJ_CHANGE, envp); diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index 301c1386a639d2af4375d9d80d07bef344ee5a43..2096f679d516e3d62cb1a5d29ad340bd8842fc43 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef MDSS_FB_H #define MDSS_FB_H @@ -57,6 +62,13 @@ #define MDP_PP_AD_BL_LINEAR 0x0 #define MDP_PP_AD_BL_LINEAR_INV 0x1 +/* Enables Sonys feature Early Unblank for quick wakeup */ +#define SOMC_FEATURE_EARLY_UNBLANK + +#ifdef SOMC_FEATURE_EARLY_UNBLANK +#include +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ + /** * enum mdp_notify_event - Different frame events to indicate frame update state * @@ -266,6 +278,12 @@ struct msm_fb_fps_info { u32 measured_fps; }; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +struct fb_specific_data { + bool off_sts; +}; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + struct msm_fb_data_type { u32 key; u32 index; @@ -374,6 +392,19 @@ struct msm_fb_data_type { bool pending_switch; struct mutex switch_lock; struct input_handler *input_handler; + +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct fb_specific_data spec_mfd; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ +#ifdef SOMC_FEATURE_EARLY_UNBLANK + /* speed up wakeup */ + /* do unblank (>150ms) on own kworker + * so we don't starve other works + */ + struct workqueue_struct *unblank_kworker; + struct work_struct unblank_work; + bool early_unblank_completed; +#endif /* SOMC_FEATURE_EARLY_UNBLANK */ }; static inline void mdss_fb_update_notify_update(struct msm_fb_data_type *mfd) diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 4f30f7864bb084d7ccee34b7a6b804477a2b6e46..1955d4d86be24a72636e449248f1851116d453f8 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -843,7 +848,9 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev, mutex_lock(&hdmi_ctrl->tx_lock); - rc = kstrtoint(buf, 10, &hpd); + /* Force HPD to be low */ + /*rc = kstrtoint(buf, 10, &hpd);*/ + rc = kstrtoint("0", 10, &hpd); if (rc) { DEV_ERR("%s: kstrtoint failed. rc=%d\n", __func__, rc); goto end; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index a77e5c4435bb788010d83b959a1b8724340dea5b..2fb03f598ab1bf8b54aadc40fcb2fa10b14be781 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -13,6 +13,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -2034,7 +2039,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) /* prevent disable of prefill calculations */ mdata->min_prefill_lines = 0xffff; /* clock gating feature is enabled by default */ - mdata->enable_gate = true; + mdata->enable_gate = false; mdata->pixel_ram_size = 0; mem_protect_sd_ctrl_id = MEM_PROTECT_SD_CTRL_FLAT; @@ -2593,6 +2598,7 @@ static int mdss_mdp_get_cmdline_config(struct platform_device *pdev) len = strlen(mdss_mdp_panel); + len = 0; /* temporary */ if (len > 0) { rc = mdss_mdp_get_pan_cfg(pan_cfg); if (!rc) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 2c92a480af6bb991229994881a4eff474755c599..04dfc24ed4ae2fb507a42b44ecc033d13baa3baf 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -21,10 +26,16 @@ #include "mdss_debug.h" #include "mdss_mdp_trace.h" #include "mdss_dsi_clk.h" +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +#include "mdss_dsi_panel_driver.h" +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ #include #define MAX_RECOVERY_TRIALS 10 #define MAX_SESSIONS 2 +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +#define MAX_RECOVERY_TRIALS_FOR_ESD 2400 +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ #define SPLIT_MIXER_OFFSET 0x800 @@ -307,7 +318,7 @@ static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer, if (pinfo->mipi.hw_vsync_mode) cfg |= BIT(20); - if (te->refx100) { + if (te && te->refx100) { vclks_line = vclks_line * pinfo->mipi.frame_rate * 100 / te->refx100; } else { @@ -318,13 +329,15 @@ static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer, cfg |= vclks_line; - pr_debug("%s: yres=%d vclks=%x height=%d init=%d rd=%d start=%d wr=%d\n", - __func__, pinfo->yres, vclks_line, te->sync_cfg_height, - te->vsync_init_val, te->rd_ptr_irq, te->start_pos, - te->wr_ptr_irq); - pr_debug("thrd_start =%d thrd_cont=%d pp_split=%d hw_vsync_mode:%d\n", - te->sync_threshold_start, te->sync_threshold_continue, - ctx->pingpong_split_slave, pinfo->mipi.hw_vsync_mode); + if (te) { + pr_debug("%s: yres=%d vclks=%x height=%d init=%d rd=%d start=%d wr=%d\n", + __func__, pinfo->yres, vclks_line, te->sync_cfg_height, + te->vsync_init_val, te->rd_ptr_irq, te->start_pos, + te->wr_ptr_irq); + pr_debug("thrd_start =%d thrd_cont=%d pp_split=%d hw_vsync_mode:%d\n", + te->sync_threshold_start, te->sync_threshold_continue, + ctx->pingpong_split_slave, pinfo->mipi.hw_vsync_mode); + } pingpong_base = mixer->pingpong_base; @@ -1999,6 +2012,11 @@ static int mdss_mdp_cmd_remove_vsync_handler(struct mdss_mdp_ctl *ctl, spin_unlock_irqrestore(&ctx->clk_lock, flags); if (disable_vsync_irq) { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (ctx->vsync_irq_cnt == 0) + return 0; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + /* disable rd_ptr interrupt and clocks */ mdss_mdp_setup_vsync(ctx, false); complete(&ctx->stop_comp); @@ -2092,6 +2110,10 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg) struct mdss_panel_data *pdata; unsigned long flags; int rc = 0, te_irq; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct incell_ctrl *incell = incell_get_info(); + struct mdss_panel_specific_pdata *spec_pdata; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { @@ -2114,6 +2136,12 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg) if (rc <= 0) { u32 status, mask; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + spec_pdata = mdss_panel2spec_pdata(pdata); + if (spec_pdata->blackscreen_det) + spec_pdata->blackscreen_det(); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + mask = mdss_mdp_get_irq_mask(MDSS_MDP_IRQ_TYPE_PING_PONG_COMP, ctx->current_pp_num); status = mask & readl_relaxed(ctl->mdata->mdp_base + @@ -2160,18 +2188,47 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg) MDSS_XLOG(0xbac); mdss_fb_report_panel_dead(ctl->mfd); } else if (ctx->pp_timeout_report_cnt == 0) { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (!incell) + pr_err("%s: Invalid incell data\n", __func__); + else + mdss_dsi_panel_driver_dump_incell_sts(incell); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ MDSS_XLOG(0xbad); MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy", "dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt", - "dbg_bus", "vbif_dbg_bus", - "dsi_dbg_bus", "panic"); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + "dbg_bus", "vbif_dbg_bus", "dsi_dbg_bus"); +#else + "dbg_bus", "vbif_dbg_bus", "dsi_dbg_bus", "panic"); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } else if (ctx->pp_timeout_report_cnt == MAX_RECOVERY_TRIALS) { +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (spec_pdata->esd_enable_without_xlog + == ESD_WITHOUT_XLOG_DISABLE_VALUE) { + MDSS_XLOG(0xbad2); + MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", + "dsi0_phy", + "dsi1_ctrl", "dsi1_phy", "vbif", + "vbif_nrt", + "dbg_bus", "vbif_dbg_bus", "panic"); + mdss_fb_report_panel_dead(ctl->mfd); + } + } else if (ctx->pp_timeout_report_cnt + == MAX_RECOVERY_TRIALS_FOR_ESD) { MDSS_XLOG(0xbad2); MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy", "dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt", "dbg_bus", "vbif_dbg_bus", "dsi_dbg_bus", "panic"); mdss_fb_report_panel_dead(ctl->mfd); +#else + MDSS_XLOG(0xbad2); + MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy", + "dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt", + "dbg_bus", "vbif_dbg_bus", "panic"); + mdss_fb_report_panel_dead(ctl->mfd); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } /* disable te irq */ @@ -2332,6 +2389,12 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl, WARN(rc, "intf %d panel on error (%d)\n", ctl->intf_num, rc); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + rc = mdss_mdp_ctl_intf_event(ctl, + MDSS_EVENT_POST_PANEL_ON, NULL, false); + WARN(rc, "intf %d post panel on error (%d)\n", + ctl->intf_num, rc); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } rc = mdss_mdp_tearcheck_enable(ctl, true); @@ -2916,6 +2979,9 @@ static void __mdss_mdp_kickoff(struct mdss_mdp_ctl *ctl, struct mdss_data_type *mdata = mdss_mdp_get_mdata(); bool is_pp_split = is_pingpong_split(ctl->mfd); struct mdss_panel_info *pinfo = NULL; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct mdss_panel_timing *timing = NULL; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ if (ctl->panel_data) pinfo = &ctl->panel_data->panel_info; @@ -2954,7 +3020,12 @@ static void __mdss_mdp_kickoff(struct mdss_mdp_ctl *ctl, * is maintained, so we can have the less time possible * between the check of the scanline and the kickoff. */ +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + timing = ctl->panel_data->current_timing; + if (pinfo && pinfo->mdp_koff_thshold && timing->koff_thshold_enable) { +#else if (pinfo && pinfo->mdp_koff_thshold) { +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ spin_lock(&ctx->ctlstart_lock); if (wait_for_read_ptr_if_late(ctl, sctl, pinfo)) { spin_unlock(&ctx->ctlstart_lock); @@ -2968,7 +3039,11 @@ static void __mdss_mdp_kickoff(struct mdss_mdp_ctl *ctl, mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1); MDSS_XLOG(0x11, ctx->autorefresh_state); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (pinfo && pinfo->mdp_koff_thshold && timing->koff_thshold_enable) +#else if (pinfo && pinfo->mdp_koff_thshold) +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ spin_unlock(&ctx->ctlstart_lock); } } @@ -3025,6 +3100,10 @@ static int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) struct mdss_mdp_ctl *sctl = NULL, *mctl = ctl; struct mdss_mdp_cmd_ctx *ctx, *sctx = NULL; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct mdss_panel_data *pdata = NULL; + struct mipi_panel_info *mipi = NULL; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { @@ -3037,6 +3116,15 @@ static int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) return -EPERM; } +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (IS_ERR_OR_NULL(ctl->panel_data)) { + pr_warn("%s: no panel data\n", __func__); + } else { + pdata = ctl->panel_data; + mipi = &pdata->panel_info.mipi; + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + if (!ctl->is_master) { mctl = mdss_mdp_get_main_ctl(ctl); } else { @@ -3097,6 +3185,25 @@ static int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) /* * tx dcs command if had any */ +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (ctl->panel_data && mipi && + mipi->dms_mode == DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE && + mipi->switch_mode_pending == true) { + /* enable clks and rd_ptr interrupt */ + mdss_mdp_setup_vsync(ctx, true); + + atomic_inc(&ctx->rdptr_cnt); + MDSS_XLOG(atomic_read(&ctx->rdptr_cnt)); + mdss_mdp_cmd_wait4readptr(ctx); + MDSS_XLOG(atomic_read(&ctx->rdptr_cnt)); + + /* disable clks and rd_ptr interrupt */ + mdss_mdp_setup_vsync(ctx, false); + + mipi->switch_mode_pending = false; + } +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DSI_CMDLIST_KOFF, NULL, CTL_INTF_EVENT_FLAG_DEFAULT); diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 34001ab35e553cb9aac6f5fa1a8eb49b7b57e0ed..96fe564e0c34befacfa3069d2e60ee6e3632248a 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -5425,6 +5430,10 @@ static int mdss_mdp_overlay_ioctl_handler(struct msm_fb_data_type *mfd, struct msmfb_overlay_data data; struct mdp_set_cfg cfg; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (mfd->spec_mfd.off_sts) + return 0; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ switch (cmd) { case MSMFB_MDP_PP: ret = mdss_mdp_pp_ioctl(mfd, argp); @@ -6375,8 +6384,15 @@ void mdss_mdp_footswitch_ctrl_handler(bool on) static void mdss_mdp_signal_retire_fence(struct msm_fb_data_type *mfd, int retire_cnt) { - __vsync_retire_signal(mfd, retire_cnt); + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + pr_debug("Signaled (%d) pending retire fence\n", retire_cnt); + if (!mdp5_data->ctl || !mdp5_data->ctl->mfd) + return; + + if (!mdp5_data->ctl->ops.remove_vsync_handler) + return; + __vsync_retire_signal(mfd, retire_cnt); } int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index 5b9798e2c24eed82e0e6a851023549e2c8dd57f8..d181f7e138afe53c283bce1ae4a1551c3f523cec 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -6665,7 +6670,12 @@ static void pp_ad_calc_worker(struct work_struct *work) sysfs_notify_dirent(mdp5_data->ad_event_sd); if (!ad->calc_itr) { ad->state &= ~PP_AD_STATE_VSYNC; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + if (ctl->ops.remove_vsync_handler) + ctl->ops.remove_vsync_handler(ctl, &ad->handle); +#else ctl->ops.remove_vsync_handler(ctl, &ad->handle); +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ } mutex_unlock(&ad->lock); diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c b/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c index aabf7c5073765a80e2c6a88cf1a869624bf617b6..fb4b2d1ccb4c760beec4a98bd4aebcbaef63562c 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c @@ -11,6 +11,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -914,9 +919,10 @@ static int pp_gamut_set_config(char __iomem *base_addr, gamut_val = gamut_data->c1_c2_data[i][j + 1]; gamut_val = (gamut_val << 32) | gamut_data->c0_data[i][j]; - writeq_relaxed(gamut_val, + writeq_relaxed_no_log(gamut_val, base_addr + GAMUT_TABLE_UPPER_R); } + writel_relaxed(gamut_data->c0_data[i][j], base_addr + GAMUT_TABLE_UPPER_R); if ((i >= MDP_GAMUT_SCALE_OFF_TABLE_NUM) || @@ -1767,11 +1773,11 @@ static int pp_igc_set_config(char __iomem *base_addr, data &= ~IGC_INDEX_UPDATE; /* update the index for c0, c1 , c2 */ for (i = 1; i < IGC_LUT_ENTRIES; i++) { - writel_relaxed((lut_data->c0_c1_data[i] & IGC_DATA_MASK) + writel_relaxed_no_log((lut_data->c0_c1_data[i] & IGC_DATA_MASK) | data, c0); - writel_relaxed(((lut_data->c0_c1_data[i] >> 16) + writel_relaxed_no_log(((lut_data->c0_c1_data[i] >> 16) & IGC_DATA_MASK) | data, c1); - writel_relaxed((lut_data->c2_data[i] & IGC_DATA_MASK) + writel_relaxed_no_log((lut_data->c2_data[i] & IGC_DATA_MASK) | data, c2); } bail_out: @@ -1927,15 +1933,15 @@ static int pp_pgc_set_config(char __iomem *base_addr, val = pgc_data_v17->c0_data[i] & PGC_DATA_MASK; val |= (pgc_data_v17->c0_data[i + 1] & PGC_DATA_MASK) << PGC_ODD_SHIFT; - writel_relaxed(val, c0); + writel_relaxed_no_log(val, c0); val = pgc_data_v17->c1_data[i] & PGC_DATA_MASK; val |= (pgc_data_v17->c1_data[i + 1] & PGC_DATA_MASK) << PGC_ODD_SHIFT; - writel_relaxed(val, c1); + writel_relaxed_no_log(val, c1); val = pgc_data_v17->c2_data[i] & PGC_DATA_MASK; val |= (pgc_data_v17->c2_data[i + 1] & PGC_DATA_MASK) << PGC_ODD_SHIFT; - writel_relaxed(val, c2); + writel_relaxed_no_log(val, c2); } if (block_type == DSPP) { val = PGC_SWAP; diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index acac672662c14e01f14e23048dab3b3abd251d05..080a6a3b265265eceb007b1e380d06797115b8cb 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef MDSS_PANEL_H #define MDSS_PANEL_H @@ -521,6 +526,9 @@ struct mipi_panel_info { char dma_trigger; /* Dynamic Switch Support */ enum dynamic_mode_switch dms_mode; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + bool switch_mode_pending; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ u32 pixel_packing; u32 dsi_pclk_rate; @@ -929,6 +937,11 @@ struct mdss_panel_info { /* esc clk recommended for the panel */ u32 esc_clk_rate_hz; + +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + const char *panel_id_name; + int dsi_master; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ }; struct mdss_panel_timing { @@ -963,6 +976,10 @@ struct mdss_panel_timing { struct mdss_mdp_pp_tear_check te; struct mdss_panel_roi_alignment roi_alignment; + +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + bool koff_thshold_enable; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ }; struct mdss_panel_data { @@ -971,6 +988,10 @@ struct mdss_panel_data { int (*apply_display_setting)(struct mdss_panel_data *pdata, u32 mode); unsigned char *mmss_cc_base; +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL + struct platform_device *panel_pdev; +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + /** * event_handler() - callback handler for MDP core events * @pdata: Pointer refering to the panel struct associated to this diff --git a/drivers/video/fbdev/ocfb.c b/drivers/video/fbdev/ocfb.c index c9293aea8ec3502e27ddf6125f07201c16996a7b..cbbb018f53d629e3a0d8b25c5cbdd43555a19805 100644 --- a/drivers/video/fbdev/ocfb.c +++ b/drivers/video/fbdev/ocfb.c @@ -7,6 +7,11 @@ * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index e12a797cb82099a7e58c8167ba5a3c9401eb1bab..d655436f408d976619879c096340ad7b372619d3 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -8,6 +8,11 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/fs/ecryptfs/Kconfig b/fs/ecryptfs/Kconfig index 434aa313f077c2789bdef6e1cba0bf2725c37b7b..e31aa9d3fee343b74153a90366a576d8895fe358 100644 --- a/fs/ecryptfs/Kconfig +++ b/fs/ecryptfs/Kconfig @@ -20,3 +20,10 @@ config ECRYPT_FS_MESSAGING Enables the /dev/ecryptfs entry for use by ecryptfsd. This allows for userspace to wrap/unwrap file encryption keys by other backends, like OpenSSL. + +config WTL_ENCRYPTION_FILTER + bool "Enables filtering for some files not to encrypt on eCryptfs" + default y + depends on ECRYPT_FS + help + Modification of encrypted filesystem for SD card encryption diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index cf0186fd9bfe94a7fd9f427d6beabc3d0014afde..cbca0430ee42a6428f8c9cbdda22bc88fd775115 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -22,6 +22,11 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -145,6 +150,13 @@ static int ecryptfs_crypto_api_algify_cipher_name(char **algified_name, int algified_name_len; int rc; + if (!strncmp(cipher_name, "aes", sizeof("aes")) && + (!strncmp(chaining_modifier, "cbc", sizeof("cbc")) || + !strncmp(chaining_modifier, "xts", sizeof("xts")))) { + cipher_name = "fipsaes"; + cipher_name_len = strnlen(cipher_name, sizeof("fipsaes")); + } + algified_name_len = (chaining_modifier_len + cipher_name_len + 3); (*algified_name) = kmalloc(algified_name_len, GFP_KERNEL); if (!(*algified_name)) { diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index f5908e91eb1720bf2074de9d0ed0df6eaa5ba490..b38177375f5711ac31f705a948b24ecdf5ceb08f 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -24,6 +24,11 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef ECRYPTFS_KERNEL_H #define ECRYPTFS_KERNEL_H @@ -40,6 +45,13 @@ #include #include +#ifdef CONFIG_WTL_ENCRYPTION_FILTER +#define ENC_NAME_FILTER_MAX_INSTANCE 5 +#define ENC_NAME_FILTER_MAX_LEN (256*5) +#define ENC_EXT_FILTER_MAX_INSTANCE 60 +#define ENC_EXT_FILTER_MAX_LEN 16 +#endif + #define ECRYPTFS_DEFAULT_IV_BYTES 16 #define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096 #define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192 @@ -234,6 +246,9 @@ struct ecryptfs_crypt_stat { #define ECRYPTFS_ENCFN_USE_FEK 0x00001000 #define ECRYPTFS_UNLINK_SIGS 0x00002000 #define ECRYPTFS_I_SIZE_INITIALIZED 0x00004000 +#ifdef CONFIG_WTL_ENCRYPTION_FILTER +#define ECRYPTFS_ENCRYPTED_OTHER_DEVICE 0x00008000 +#endif u32 flags; unsigned int file_version; size_t iv_bytes; @@ -345,6 +360,10 @@ struct ecryptfs_mount_crypt_stat { #define ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK 0x00000020 #define ECRYPTFS_GLOBAL_ENCFN_USE_FEK 0x00000040 #define ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY 0x00000080 +#ifdef CONFIG_WTL_ENCRYPTION_FILTER +#define ECRYPTFS_ENABLE_FILTERING 0x00000100 +#define ECRYPTFS_ENABLE_NEW_PASSTHROUGH 0x00000200 +#endif u32 flags; struct list_head global_auth_tok_list; struct mutex global_auth_tok_list_mutex; @@ -357,6 +376,13 @@ struct ecryptfs_mount_crypt_stat { char global_default_fnek_sig[ECRYPTFS_SIG_SIZE_HEX + 1]; unsigned char global_default_cipher_mode[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1]; +#ifdef CONFIG_WTL_ENCRYPTION_FILTER + int max_name_filter_len; + char enc_filter_name[ENC_NAME_FILTER_MAX_INSTANCE] + [ENC_NAME_FILTER_MAX_LEN + 1]; + char enc_filter_ext[ENC_EXT_FILTER_MAX_INSTANCE] + [ENC_EXT_FILTER_MAX_LEN + 1]; +#endif }; /* superblock private data. */ @@ -781,6 +807,14 @@ int ecryptfs_set_f_namelen(long *namelen, long lower_namelen, int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat, loff_t offset); +#ifdef CONFIG_WTL_ENCRYPTION_FILTER +extern int is_file_name_match(struct ecryptfs_mount_crypt_stat *mcs, + struct dentry *fp_dentry); + +extern int is_file_ext_match(struct ecryptfs_mount_crypt_stat *mcs, + char *str); +#endif + void clean_inode_pages(struct address_space *mapping, pgoff_t start, pgoff_t end); diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index c93fe5fce41ed191954c79ccff310887a62fea82..ca64d8f31dd84bff5330bcee017526b3e4f46b2b 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -22,6 +22,11 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -33,6 +38,12 @@ #include #include #include "ecryptfs_kernel.h" +#ifdef CONFIG_WTL_ENCRYPTION_FILTER +#include +#define ECRYPTFS_IOCTL_GET_ATTRIBUTES _IOR('l', 0x10, __u32) +#define ECRYPTFS_WAS_ENCRYPTED 0x0080 +#define ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE 0x0100 +#endif /** * ecryptfs_read_update_atime @@ -138,6 +149,47 @@ static int read_or_initialize_metadata(struct dentry *dentry) crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; mount_crypt_stat = &ecryptfs_superblock_to_private( inode->i_sb)->mount_crypt_stat; + +#ifdef CONFIG_WTL_ENCRYPTION_FILTER + if (crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED + && crypt_stat->flags & ECRYPTFS_POLICY_APPLIED + && crypt_stat->flags & ECRYPTFS_ENCRYPTED + && !(crypt_stat->flags & ECRYPTFS_KEY_VALID) + && !(crypt_stat->flags & ECRYPTFS_KEY_SET) + && crypt_stat->flags & ECRYPTFS_I_SIZE_INITIALIZED) { + crypt_stat->flags |= ECRYPTFS_ENCRYPTED_OTHER_DEVICE; + } + mutex_lock(&crypt_stat->cs_mutex); + if ((mount_crypt_stat->flags & ECRYPTFS_ENABLE_NEW_PASSTHROUGH) + && (crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { + if (ecryptfs_read_metadata(dentry)) { + crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED + | ECRYPTFS_ENCRYPTED); + rc = 0; + goto out; + } + } else if ((mount_crypt_stat->flags & ECRYPTFS_ENABLE_FILTERING) + && (crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { + struct dentry *fp_dentry = + ecryptfs_inode_to_private(inode)->lower_file->f_path.dentry; + char filename[NAME_MAX+1] = {0}; + if (fp_dentry->d_name.len <= NAME_MAX) + memcpy(filename, fp_dentry->d_name.name, + fp_dentry->d_name.len + 1); + + if (is_file_name_match(mount_crypt_stat, fp_dentry) + || is_file_ext_match(mount_crypt_stat, filename)) { + if (ecryptfs_read_metadata(dentry)) + crypt_stat->flags &= + ~(ECRYPTFS_I_SIZE_INITIALIZED + | ECRYPTFS_ENCRYPTED); + rc = 0; + goto out; + } + } + mutex_unlock(&crypt_stat->cs_mutex); +#endif + mutex_lock(&crypt_stat->cs_mutex); if (crypt_stat->flags & ECRYPTFS_POLICY_APPLIED && @@ -384,6 +436,45 @@ ecryptfs_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct file *lower_file = ecryptfs_file_to_lower(file); long rc = -ENOTTY; +#ifdef CONFIG_WTL_ENCRYPTION_FILTER + if (cmd == ECRYPTFS_IOCTL_GET_ATTRIBUTES) { + u32 __user *user_attr = (u32 __user *)arg; + u32 attr = 0; + char filename[NAME_MAX+1] = {0}; + struct dentry *ecryptfs_dentry = file->f_path.dentry; + struct ecryptfs_mount_crypt_stat *mount_crypt_stat = + &ecryptfs_superblock_to_private(ecryptfs_dentry->d_sb) + ->mount_crypt_stat; + + struct inode *inode = d_inode(ecryptfs_dentry); + struct ecryptfs_crypt_stat *crypt_stat = + &ecryptfs_inode_to_private(inode)->crypt_stat; + struct dentry *fp_dentry = + ecryptfs_inode_to_private(inode)->lower_file->f_path.dentry; + if (fp_dentry->d_name.len <= NAME_MAX) + memcpy(filename, fp_dentry->d_name.name, + fp_dentry->d_name.len + 1); + + mutex_lock(&crypt_stat->cs_mutex); + if ((crypt_stat->flags & ECRYPTFS_ENCRYPTED + || crypt_stat->flags & ECRYPTFS_ENCRYPTED_OTHER_DEVICE) + || ((mount_crypt_stat->flags + & ECRYPTFS_ENABLE_FILTERING) + && (is_file_name_match + (mount_crypt_stat, fp_dentry) + || is_file_ext_match + (mount_crypt_stat, filename)))) { + if (crypt_stat->flags & ECRYPTFS_KEY_VALID) + attr = ECRYPTFS_WAS_ENCRYPTED; + else + attr = ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE; + } + mutex_unlock(&crypt_stat->cs_mutex); + put_user(attr, user_attr); + return 0; + } +#endif + if (!lower_file->f_op->unlocked_ioctl) return rc; @@ -427,6 +518,87 @@ ecryptfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } #endif +#ifdef CONFIG_WTL_ENCRYPTION_FILTER +int is_file_name_match(struct ecryptfs_mount_crypt_stat *mcs, + struct dentry *fp_dentry) +{ + int i; + char *str = NULL; + if (!(strcmp("/", fp_dentry->d_name.name)) + || !(strcmp("", fp_dentry->d_name.name))) + return 0; + str = kzalloc(mcs->max_name_filter_len + 1, GFP_KERNEL); + if (!str) { + printk(KERN_ERR "%s: Out of memory whilst attempting " + "to kzalloc [%d] bytes\n", __func__, + (mcs->max_name_filter_len + 1)); + return 0; + } + + for (i = 0; i < ENC_NAME_FILTER_MAX_INSTANCE; i++) { + int len = 0; + struct dentry *p = fp_dentry; + if (!strlen(mcs->enc_filter_name[i])) + break; + + while (1) { + if (len == 0) { + len = strlen(p->d_name.name); + if (len > mcs->max_name_filter_len) + break; + strcpy(str, p->d_name.name); + } else { + len = len + 1 + strlen(p->d_name.name) ; + if (len > mcs->max_name_filter_len) + break; + strcat(str, "/"); + strcat(str, p->d_name.name); + } + + if (strncasecmp(str, mcs->enc_filter_name[i], len)) + break; + p = p->d_parent; + + if (!(strcmp("/", p->d_name.name)) + || !(strcmp("", p->d_name.name))) { + if (len == strlen(mcs->enc_filter_name[i])) { + kfree(str); + return 1; + } + break; + } + } + } + kfree(str); + return 0; +} + +int is_file_ext_match(struct ecryptfs_mount_crypt_stat *mcs, char *str) +{ + int i; + char ext[NAME_MAX + 1] = {0}; + + char *token; + int count = 0; + while ((token = strsep(&str, ".")) != NULL) { + strncpy(ext, token, NAME_MAX); + count++; + } + if (count <= 1) + return 0; + + for (i = 0; i < ENC_EXT_FILTER_MAX_INSTANCE; i++) { + if (!strlen(mcs->enc_filter_ext[i])) + return 0; + if (strlen(ext) != strlen(mcs->enc_filter_ext[i])) + continue; + if (!strncasecmp(ext, mcs->enc_filter_ext[i], strlen(ext))) + return 1; + } + return 0; +} +#endif + const struct file_operations ecryptfs_dir_fops = { .iterate = ecryptfs_readdir, .read = generic_read_dir, diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 7f277e843ea5b2ae5762e95d2f1b9c7eebac3ee5..3414d48df3574362033108232c755fd7bc794ee9 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -22,6 +22,11 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -243,10 +248,44 @@ int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry, ecryptfs_dentry, rc); goto out; } +#ifdef CONFIG_WTL_ENCRYPTION_FILTER + mutex_lock(&crypt_stat->cs_mutex); + if (crypt_stat->flags & ECRYPTFS_ENCRYPTED) { + struct dentry *fp_dentry = + ecryptfs_inode_to_private(ecryptfs_inode) + ->lower_file->f_path.dentry; + struct ecryptfs_mount_crypt_stat *mount_crypt_stat = + &ecryptfs_superblock_to_private(ecryptfs_dentry->d_sb) + ->mount_crypt_stat; + char filename[NAME_MAX+1] = {0}; + if (fp_dentry->d_name.len <= NAME_MAX) + memcpy(filename, fp_dentry->d_name.name, + fp_dentry->d_name.len + 1); + + if ((mount_crypt_stat->flags & ECRYPTFS_ENABLE_NEW_PASSTHROUGH) + || ((mount_crypt_stat->flags & ECRYPTFS_ENABLE_FILTERING) && + (is_file_name_match(mount_crypt_stat, fp_dentry) || + is_file_ext_match(mount_crypt_stat, filename)))) { + crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED + | ECRYPTFS_ENCRYPTED); + ecryptfs_put_lower_file(ecryptfs_inode); + } else { + rc = ecryptfs_write_metadata(ecryptfs_dentry, + ecryptfs_inode); + if (rc) + printk( + KERN_ERR "Error writing headers; rc = [%d]\n" + , rc); + ecryptfs_put_lower_file(ecryptfs_inode); + } + } + mutex_unlock(&crypt_stat->cs_mutex); +#else rc = ecryptfs_write_metadata(ecryptfs_dentry, ecryptfs_inode); if (rc) printk(KERN_ERR "Error writing headers; rc = [%d]\n", rc); ecryptfs_put_lower_file(ecryptfs_inode); +#endif out: return rc; } diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 85f7a289bdac15cd22d467e2b8007f3d38986e50..b7426496ac8017e1e2b5488eb7abf90719dedcec 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -23,6 +23,11 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -38,6 +43,9 @@ #include #include #include "ecryptfs_kernel.h" +#ifdef CONFIG_WTL_ENCRYPTION_FILTER +#include +#endif /** * Module parameter that defines the ecryptfs_verbosity level. @@ -201,6 +209,9 @@ enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, ecryptfs_opt_fn_cipher, ecryptfs_opt_fn_cipher_key_bytes, ecryptfs_opt_unlink_sigs, ecryptfs_opt_mount_auth_tok_only, ecryptfs_opt_check_dev_ruid, +#ifdef CONFIG_WTL_ENCRYPTION_FILTER + ecryptfs_opt_enable_filtering, +#endif ecryptfs_opt_err }; static const match_table_t tokens = { @@ -218,6 +229,9 @@ static const match_table_t tokens = { {ecryptfs_opt_unlink_sigs, "ecryptfs_unlink_sigs"}, {ecryptfs_opt_mount_auth_tok_only, "ecryptfs_mount_auth_tok_only"}, {ecryptfs_opt_check_dev_ruid, "ecryptfs_check_dev_ruid"}, +#ifdef CONFIG_WTL_ENCRYPTION_FILTER + {ecryptfs_opt_enable_filtering, "ecryptfs_enable_filtering=%s"}, +#endif {ecryptfs_opt_err, NULL} }; @@ -259,6 +273,56 @@ static void ecryptfs_init_mount_crypt_stat( mount_crypt_stat->flags |= ECRYPTFS_MOUNT_CRYPT_STAT_INITIALIZED; } +#ifdef CONFIG_WTL_ENCRYPTION_FILTER +static int parse_enc_file_filter_parms( + struct ecryptfs_mount_crypt_stat *mcs, char *str) +{ + char *token = NULL; + int count = 0; + mcs->max_name_filter_len = 0; + while ((token = strsep(&str, "|")) != NULL) { + if (count >= ENC_NAME_FILTER_MAX_INSTANCE) + return -1; + strncpy(mcs->enc_filter_name[count++], + token, ENC_NAME_FILTER_MAX_LEN); + if (mcs->max_name_filter_len < strlen(token)) + mcs->max_name_filter_len = strlen(token); + } + return 0; +} + +static int parse_enc_ext_filter_parms( + struct ecryptfs_mount_crypt_stat *mcs, char *str) +{ + char *token = NULL; + int count = 0; + while ((token = strsep(&str, "|")) != NULL) { + if (count >= ENC_EXT_FILTER_MAX_INSTANCE) + return -1; + strncpy(mcs->enc_filter_ext[count++], + token, ENC_EXT_FILTER_MAX_LEN); + } + return 0; +} + +static int parse_enc_filter_parms( + struct ecryptfs_mount_crypt_stat *mcs, char *str) +{ + char *token = NULL; + if (!strcmp("*", str)) { + mcs->flags |= ECRYPTFS_ENABLE_NEW_PASSTHROUGH; + return 0; + } + token = strsep(&str, ":"); + if (token != NULL) + parse_enc_file_filter_parms(mcs, token); + token = strsep(&str, ":"); + if (token != NULL) + parse_enc_ext_filter_parms(mcs, token); + return 0; +} +#endif + /** * ecryptfs_parse_options * @sb: The ecryptfs super block @@ -418,6 +482,19 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options, case ecryptfs_opt_check_dev_ruid: *check_ruid = 1; break; +#ifdef CONFIG_WTL_ENCRYPTION_FILTER + case ecryptfs_opt_enable_filtering: + rc = parse_enc_filter_parms(mount_crypt_stat, + args[0].from); + if (rc) { + printk(KERN_ERR "Error attempting to parse encryption " + "filtering parameters.\n"); + rc = -EINVAL; + goto out; + } + mount_crypt_stat->flags |= ECRYPTFS_ENABLE_FILTERING; + break; +#endif case ecryptfs_opt_err: default: printk(KERN_WARNING diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c index 25e436ddcf8ef014b495697cd4d87a44627bdf0e..11cbe5340eb31cf9bc2b27263675be0b42d21c6d 100644 --- a/fs/ecryptfs/super.c +++ b/fs/ecryptfs/super.c @@ -22,6 +22,11 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -179,6 +184,10 @@ static int ecryptfs_show_options(struct seq_file *m, struct dentry *root) if (mount_crypt_stat->global_default_cipher_key_size) seq_printf(m, ",ecryptfs_key_bytes=%zd", mount_crypt_stat->global_default_cipher_key_size); +#ifdef CONFIG_WTL_ENCRYPTION_FILTER + if (mount_crypt_stat->flags & ECRYPTFS_ENABLE_FILTERING) + seq_printf(m, ",ecryptfs_enable_filtering"); +#endif if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED) seq_printf(m, ",ecryptfs_passthrough"); if (mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 4a0b3a521399dea1db8246ee09b81a1504c4915c..8b93c0097b9051ae5e0cd109b556a55db2dd87eb 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -19,6 +19,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111- */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * Extents support for EXT4 @@ -1310,6 +1315,7 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, } /* move top-level index/leaf into new block */ + memset(bh->b_data, 0, bh->b_size); memmove(bh->b_data, EXT4_I(inode)->i_data, sizeof(EXT4_I(inode)->i_data)); diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig index 182f9ffe2b5162f728a0b38dfc41984039f8eeec..d1c10a1f99c86b5cd894f92014c10d3e7ce2c392 100644 --- a/fs/fat/Kconfig +++ b/fs/fat/Kconfig @@ -74,6 +74,27 @@ config VFAT_FS To compile this as a module, choose M here: the module will be called vfat. +config VFAT_FS_NO_DUALNAMES + bool "disable VFAT dual names support (patent workaround)" + depends on VFAT_FS + help + This option disables support for dual filenames on VFAT filesystems. + If this option is enabled then file creation will either put + a short (8.3) name or a long name on the file, but never both. + The field where a shortname would normally go is filled with + invalid characters such that it cannot be considered a valid + short filename. + + That means that long filenames created with this option + disabled will not be accessible at all to operating systems + that do not understand the FAT long filename extensions. + + Users considering disabling this option should consider the + implications of any patents that may exist on dual filenames + in VFAT. + + If unsure, say N + config FAT_DEFAULT_CODEPAGE int "Default codepage for FAT" depends on MSDOS_FS || VFAT_FS diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 8b2127ffb226cca2ece880d41fd756624c5d3118..492b3eb00ed2b0e761de2152fa4ee44c87db697d 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -498,6 +498,13 @@ parse_record: goto end_of_dir; } + /* + * The FAT_NO_83NAME flag is used to mark files + * created with no 8.3 short name + */ + if (de->lcase & FAT_NO_83NAME) + goto compare_longname; + /* Never prepend '.' to hidden files here. * That is done only for msdos mounts (and only when * 'dotsOK=yes'); if we are executing here, it is in the @@ -511,6 +518,7 @@ parse_record: if (fat_name_match(sbi, name, name_len, bufname, len)) goto found; +compare_longname: if (nr_slots) { void *longname = unicode + FAT_MAX_UNI_CHARS; int size = PATH_MAX - FAT_MAX_UNI_SIZE; @@ -602,6 +610,8 @@ parse_record: if (de->attr != ATTR_EXT && IS_FREE(de->name)) goto record_end; } else { + if (de->lcase & FAT_NO_83NAME) + goto record_end; if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) goto record_end; } @@ -960,6 +970,10 @@ int fat_scan(struct inode *dir, const unsigned char *name, sinfo->bh = NULL; while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh, &sinfo->de) >= 0) { + /* skip files marked as having no 8.3 short name */ + if (sinfo->de->lcase & FAT_NO_83NAME) + continue; + if (!strncmp(sinfo->de->name, name, MSDOS_NAME)) { sinfo->slot_off -= sizeof(*sinfo->de); sinfo->nr_slots = 1; diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 7092584f424af63e66e43cf8ad365343fc18daa9..6dd7912499be21a20139ed1c809928f6876cf880 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -20,6 +20,7 @@ #include #include #include "fat.h" +#include /* * If new entry was created in the parent, it could create the 8.3 @@ -322,6 +323,17 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen; int is_shortname; struct shortname_info base_info, ext_info; + unsigned opts_shortname = opts->shortname; + +#ifdef CONFIG_VFAT_FS_NO_DUALNAMES + /* + * When we do not have dualnames, we want to maximise the + * chance that a file will be able to be represented with just + * a 8.3 entry. We can do that by using the WINNT case + * handling extensions to FAT. + */ + opts_shortname = VFAT_SFN_CREATE_WINNT; +#endif is_shortname = 1; INIT_SHORTNAME_INFO(&base_info); @@ -434,9 +446,9 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, if (vfat_find_form(dir, name_res) == 0) return -EEXIST; - if (opts->shortname & VFAT_SFN_CREATE_WIN95) { + if (opts_shortname & VFAT_SFN_CREATE_WIN95) { return (base_info.upper && ext_info.upper); - } else if (opts->shortname & VFAT_SFN_CREATE_WINNT) { + } else if (opts_shortname & VFAT_SFN_CREATE_WINNT) { if ((base_info.upper || base_info.lower) && (ext_info.upper || ext_info.lower)) { if (!base_info.upper && base_info.lower) @@ -575,6 +587,66 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname, return 0; } +#ifdef CONFIG_VFAT_FS_NO_DUALNAMES +/* + * This function creates a dummy 8.3 entry which is as compatible as + * possible with existing FAT devices, while not being a valid + * filename under windows or Linux + */ +static void vfat_build_dummy_83_buffer(struct inode *dir, char *msdos_name, + int is_dir) +{ + /* + * These characters are all invalid in 8.3 names, plus have + * been shown to be harmless on all tested devices + */ + const char invalidchar[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0B, + 0x0C, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x22, 0x2a, + 0x3a, 0x3c, 0x3e, 0x3f, 0x5b, 0x5d, 0x7c }; + int i, tilde_pos, slash_pos; + u32 rand_num = prandom_u32(); + + /* We need a '~' in the prefix to make Win98 happy. */ + tilde_pos = rand_num % 8; + rand_num >>= 3; + + /* + * the '/' makes sure that even unpatched Linux systems can't + * get at files by the 8.3 entry. Don't put in a / in + * directories as it can cause problems with some + * photo frames + */ + if (is_dir) + slash_pos = -1; + else { + slash_pos = (tilde_pos + 1 + rand_num % 7) % 8; + rand_num >>= 3; + } + + /* + * fill in the first 8 bytes with invalid characters. Note + * that we need to be careful not to run out of randomness. We + * leave the 3 byte extension in place as some cheap MP3 + * players need them. + */ + for (i = 0; i < 8; i++) { + if (i == tilde_pos) + msdos_name[i] = '~'; + else if (i == slash_pos) + msdos_name[i] = '/'; + else { + msdos_name[i] = + invalidchar[rand_num % sizeof(invalidchar)]; + rand_num /= sizeof(invalidchar); + if (rand_num < sizeof(invalidchar)) + rand_num = prandom_u32(); + } + } +} +#endif + static int vfat_build_slots(struct inode *dir, const unsigned char *name, int len, int is_dir, int cluster, struct timespec *ts, @@ -617,6 +689,13 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name, goto shortname; } +#ifdef CONFIG_VFAT_FS_NO_DUALNAMES + printk_once(KERN_INFO + "VFAT: not creating 8.3 short filenames for long names\n"); + vfat_build_dummy_83_buffer(dir, msdos_name, is_dir); + lcase = FAT_NO_83NAME; +#endif + /* build the entry of long file name */ cksum = fat_checksum(msdos_name); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index d3c77413dd563ed127c2edcba7ed1f6438c53a64..5f27aa4f7d7dca19e52d97cd9da29fc15be16e54 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -5,6 +5,11 @@ This program can be distributed under the terms of the GNU GPL. See the file COPYING. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "fuse_i.h" @@ -1627,6 +1632,12 @@ int fuse_flush_times(struct inode *inode, struct fuse_file *ff) return fuse_simple_request(fc, &args); } +static bool fuse_allow_set_time(struct fuse_conn *fc, struct inode *inode) +{ + return (fc->flags & FUSE_ALLOW_UTIME_GRP && inode->i_mode & S_IWGRP && + !uid_eq(current_uid(), inode->i_uid) && in_group_p(inode->i_gid)); +} + /* * Set attributes, and at the same time refresh them. * @@ -1646,13 +1657,22 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, bool is_truncate = false; bool is_wb = fc->writeback_cache; loff_t oldsize; + unsigned int ia_valid; int err; bool trust_local_cmtime = is_wb && S_ISREG(inode->i_mode); if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) attr->ia_valid |= ATTR_FORCE; + ia_valid = attr->ia_valid; + if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET) && + fuse_allow_set_time(fc, inode)) { + attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | + ATTR_TIMES_SET); + } + err = inode_change_ok(inode, attr); + attr->ia_valid = ia_valid; if (err) return err; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 1cc0dce47a2fc59cc77859615c66b8bcb6e4f1ca..316acdbdf2a7f6a3daa3fdb35b82127719444e08 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -5,6 +5,11 @@ This program can be distributed under the terms of the GNU GPL. See the file COPYING. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _FS_FUSE_I_H #define _FS_FUSE_I_H @@ -45,6 +50,11 @@ doing the mount will be allowed to access the filesystem */ #define FUSE_ALLOW_OTHER (1 << 1) +/** If the FUSE_ALLOW_UTIME_GRP flag is given, then call to utime() is + allowed for the current process if it's in the same group as the + file and if the file's group is writeable */ +#define FUSE_ALLOW_UTIME_GRP (1 << 2) + /** Number of page pointers embedded in fuse_req */ #define FUSE_REQ_INLINE_PAGES 1 diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index e04db24ed16435f2aaac16747af9e2d8011b165c..9dbaf3529e92e5d383d9f25105b37311c84c6a4b 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -5,6 +5,11 @@ This program can be distributed under the terms of the GNU GPL. See the file COPYING. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "fuse_i.h" @@ -438,6 +443,7 @@ enum { OPT_GROUP_ID, OPT_DEFAULT_PERMISSIONS, OPT_ALLOW_OTHER, + OPT_ALLOW_UTIME_GRP, OPT_MAX_READ, OPT_BLKSIZE, OPT_ERR @@ -450,6 +456,7 @@ static const match_table_t tokens = { {OPT_GROUP_ID, "group_id=%u"}, {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, {OPT_ALLOW_OTHER, "allow_other"}, + {OPT_ALLOW_UTIME_GRP, "allow_utime_grp"}, {OPT_MAX_READ, "max_read=%u"}, {OPT_BLKSIZE, "blksize=%u"}, {OPT_ERR, NULL} @@ -525,6 +532,10 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev) d->flags |= FUSE_ALLOW_OTHER; break; + case OPT_ALLOW_UTIME_GRP: + d->flags |= FUSE_ALLOW_UTIME_GRP; + break; + case OPT_MAX_READ: if (match_int(&args[0], &value)) return 0; @@ -560,6 +571,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root) seq_puts(m, ",default_permissions"); if (fc->flags & FUSE_ALLOW_OTHER) seq_puts(m, ",allow_other"); + if (fc->flags & FUSE_ALLOW_UTIME_GRP) + seq_puts(m, ",allow_utime_grp"); if (fc->max_read != ~0) seq_printf(m, ",max_read=%u", fc->max_read); if (sb->s_bdev && sb->s_blocksize != FUSE_DEFAULT_BLKSIZE) diff --git a/fs/proc/Makefile b/fs/proc/Makefile index baab1daf7585ad636632ebc6fc983a5dddf7ed11..f62bbd9629975a48c75350e69ca99403cae7ffae 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -13,6 +13,7 @@ proc-$(CONFIG_TTY) += proc_tty.o proc-y += cmdline.o proc-y += consoles.o proc-y += cpuinfo.o +proc-y += cpu_time_stat.o proc-y += devices.o proc-y += interrupts.o proc-y += loadavg.o diff --git a/fs/proc/base.c b/fs/proc/base.c index 4c0989076151388fdf9f6656726d9f9212125a42..bd8d8c62a5ab4f1cbcd345c3758c0b9d18c04809 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -46,6 +46,11 @@ * Paul Mundt : * Overall revision about smaps. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include @@ -88,6 +93,7 @@ #include #include #include +#include #ifdef CONFIG_HARDWALL #include #endif @@ -1067,6 +1073,7 @@ static ssize_t oom_adj_write(struct file *file, const char __user *buf, int oom_adj; unsigned long flags; int err; + int old_oom_score_adj; memset(buffer, 0, sizeof(buffer)); if (count > sizeof(buffer) - 1) @@ -1124,8 +1131,16 @@ static ssize_t oom_adj_write(struct file *file, const char __user *buf, pr_warn_once("%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n", current->comm, task_pid_nr(current), task_pid_nr(task), task_pid_nr(task)); - + old_oom_score_adj = task->signal->oom_score_adj; task->signal->oom_score_adj = oom_adj; +#ifdef CONFIG_OOM_SCORE_NOTIFIER + err = oom_score_notify_update(task, old_oom_score_adj); + if (err) { + /* rollback and error handle. */ + task->signal->oom_score_adj = old_oom_score_adj; + goto err_sighand; + } +#endif trace_oom_score_adj_update(task); err_sighand: unlock_task_sighand(task, &flags); @@ -1170,6 +1185,7 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf, unsigned long flags; int oom_score_adj; int err; + int old_oom_score_adj; memset(buffer, 0, sizeof(buffer)); if (count > sizeof(buffer) - 1) @@ -1211,7 +1227,16 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf, goto err_sighand; } + old_oom_score_adj = task->signal->oom_score_adj; task->signal->oom_score_adj = (short)oom_score_adj; +#ifdef CONFIG_OOM_SCORE_NOTIFIER + err = oom_score_notify_update(task, old_oom_score_adj); + if (err) { + /* rollback and error handle. */ + task->signal->oom_score_adj = old_oom_score_adj; + goto err_sighand; + } +#endif if (has_capability_noaudit(current, CAP_SYS_RESOURCE)) task->signal->oom_score_adj_min = (short)oom_score_adj; trace_oom_score_adj_update(task); diff --git a/fs/proc/cpu_time_stat.c b/fs/proc/cpu_time_stat.c new file mode 100644 index 0000000000000000000000000000000000000000..d0ca09b4f70a58c08d8e9e5a57525b9c366bf4d5 --- /dev/null +++ b/fs/proc/cpu_time_stat.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef arch_irq_stat_cpu +#define arch_irq_stat_cpu(cpu) 0 +#endif +#ifndef arch_irq_stat +#define arch_irq_stat() 0 +#endif + +#ifdef arch_idle_time + +static cputime64_t get_idle_time(int cpu) +{ + cputime64_t idle; + + idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; + if (cpu_online(cpu) && !nr_iowait_cpu(cpu)) + idle += arch_idle_time(cpu); + return idle; +} + +static cputime64_t get_iowait_time(int cpu) +{ + cputime64_t iowait; + + iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT]; + if (cpu_online(cpu) && nr_iowait_cpu(cpu)) + iowait += arch_idle_time(cpu); + return iowait; +} + +#else + +static u64 get_idle_time(int cpu) +{ + u64 idle, idle_time = -1ULL; + + if (cpu_online(cpu)) + idle_time = get_cpu_idle_time_us(cpu, NULL); + + if (idle_time == -1ULL) + /* !NO_HZ or cpu offline so we can rely on cpustat.idle */ + idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; + else + idle = usecs_to_cputime64(idle_time); + + return idle; +} + +static u64 get_iowait_time(int cpu) +{ + u64 iowait, iowait_time = -1ULL; + + if (cpu_online(cpu)) + iowait_time = get_cpu_iowait_time_us(cpu, NULL); + + if (iowait_time == -1ULL) + /* !NO_HZ or cpu offline so we can rely on cpustat.iowait */ + iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT]; + else + iowait = usecs_to_cputime64(iowait_time); + + return iowait; +} + +#endif + +static int show_cpu_time_stat(struct seq_file *p, void *v) +{ + int i, j; + u64 user, nice, system, idle, iowait, irq, softirq, steal; + u64 guest, guest_nice; + u64 sum = 0; + u64 sum_softirq = 0; + unsigned int per_softirq_sums[NR_SOFTIRQS] = {0}; + + user = nice = system = idle = iowait = + irq = softirq = steal = 0; + guest = guest_nice = 0; + + for_each_possible_cpu(i) { + user += kcpustat_cpu(i).cpustat[CPUTIME_USER]; + nice += kcpustat_cpu(i).cpustat[CPUTIME_NICE]; + system += kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; + idle += get_idle_time(i); + iowait += get_iowait_time(i); + irq += kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; + softirq += kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]; + steal += kcpustat_cpu(i).cpustat[CPUTIME_STEAL]; + guest += kcpustat_cpu(i).cpustat[CPUTIME_GUEST]; + guest_nice += kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]; + sum += kstat_cpu_irqs_sum(i); + sum += arch_irq_stat_cpu(i); + + for (j = 0; j < NR_SOFTIRQS; j++) { + unsigned int softirq_stat = kstat_softirqs_cpu(j, i); + + per_softirq_sums[j] += softirq_stat; + sum_softirq += softirq_stat; + } + } + sum += arch_irq_stat(); + + seq_puts(p, "cpu "); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(user)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(nice)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(system)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(idle)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(iowait)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(irq)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(softirq)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(steal)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest_nice)); + seq_putc(p, '\n'); + + for_each_possible_cpu(i) { + /* Copy values here to work around gcc-2.95.3, gcc-2.96 */ + user = kcpustat_cpu(i).cpustat[CPUTIME_USER]; + nice = kcpustat_cpu(i).cpustat[CPUTIME_NICE]; + system = kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; + idle = get_idle_time(i); + iowait = get_iowait_time(i); + irq = kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; + softirq = kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]; + steal = kcpustat_cpu(i).cpustat[CPUTIME_STEAL]; + guest = kcpustat_cpu(i).cpustat[CPUTIME_GUEST]; + guest_nice = kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]; + seq_printf(p, "cpu%d", i); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(user)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(nice)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(system)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(idle)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(iowait)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(irq)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(softirq)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(steal)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest)); + seq_put_decimal_ull(p, ' ', cputime64_to_clock_t(guest_nice)); + seq_putc(p, '\n'); + } + return 0; +} + +static int cpu_time_stat_open(struct inode *inode, struct file *file) +{ + size_t size = 128 * (num_possible_cpus() + 1); + return single_open_size(file, show_cpu_time_stat, NULL, size); +} + +static const struct file_operations proc_cpu_time_stat_operations = { + .open = cpu_time_stat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init proc_cpu_time_stat_init(void) +{ + proc_create("cpu_time_stat", 0, NULL, &proc_cpu_time_stat_operations); + return 0; +} +fs_initcall(proc_cpu_time_stat_init); diff --git a/fs/proc/page.c b/fs/proc/page.c index 93484034a03d04c38cc5ff7779fb95e7611fbd09..618f7aee74cdcc2a057211c3d61f13d260d3cbfe 100644 --- a/fs/proc/page.c +++ b/fs/proc/page.c @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 99c4ffaa43a868e73595764fc0f748218d4e0ca4..02be76827ac891ba9acc30bcc651e1ba40f9087a 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -1392,6 +1392,11 @@ static int pagemap_open(struct inode *inode, struct file *file) { struct mm_struct *mm; + /* do not disclose physical addresses: attack vector */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + mm = proc_mem_open(inode, PTRACE_MODE_READ); if (IS_ERR(mm)) return PTR_ERR(mm); diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 3795d2c2691528a86815e6e9d36e0eb65a57068a..7beacf41ab179c5d3ac77820ef5ec78e57f5a6b4 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -17,6 +17,11 @@ * under the terms of the Apache 2.0 License OR version 2 of the GNU * General Public License. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "sdcardfs.h" #include "linux/ctype.h" @@ -123,6 +128,15 @@ out: return err; } +/* 1 = delete, 0 = cache */ +static int sdcardfs_d_delete(const struct dentry *d) +{ + if (SDCARDFS_SB(d->d_sb)->options.nocache) + return d->d_inode && !S_ISDIR(d->d_inode->i_mode); + + return 0; +} + static void sdcardfs_d_release(struct dentry *dentry) { if (!dentry || !dentry->d_fsdata) @@ -182,6 +196,7 @@ static void sdcardfs_canonical_path(const struct path *path, const struct dentry_operations sdcardfs_ci_dops = { .d_revalidate = sdcardfs_d_revalidate, + .d_delete = sdcardfs_d_delete, .d_release = sdcardfs_d_release, .d_hash = sdcardfs_hash_ci, .d_compare = sdcardfs_cmp_ci, diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 1461254f301dcf81192dab8044fa36f93b8a6546..541d105e5840d06bc98d75c08ada126e5b584a2b 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -17,6 +17,11 @@ * under the terms of the Apache 2.0 License OR version 2 of the GNU * General Public License. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "sdcardfs.h" #ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE @@ -243,6 +248,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) /* save current_cred and override it */ OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode)); + file->f_mode |= FMODE_NONMAPPABLE; file->private_data = kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); if (!SDCARDFS_F(file)) { @@ -420,6 +426,11 @@ out: return err; } +static struct file *sdcardfs_get_lower_file(struct file *f) +{ + return sdcardfs_lower_file(f); +} + const struct file_operations sdcardfs_main_fops = { .llseek = generic_file_llseek, .read = sdcardfs_read, @@ -436,6 +447,7 @@ const struct file_operations sdcardfs_main_fops = { .fasync = sdcardfs_fasync, .read_iter = sdcardfs_read_iter, .write_iter = sdcardfs_write_iter, + .get_lower_file = sdcardfs_get_lower_file, }; /* trimmed directory options */ @@ -452,4 +464,5 @@ const struct file_operations sdcardfs_dir_fops = { .flush = sdcardfs_flush, .fsync = sdcardfs_fsync, .fasync = sdcardfs_fasync, + .get_lower_file = sdcardfs_get_lower_file, }; diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index b41eb7fa37bbc7f57b8a7907e2d1c2f1ecb3b61b..dbb3d81313ddd9e0c0a1ffa1a60fe94dca0eb9fa 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -17,6 +17,11 @@ * under the terms of the Apache 2.0 License OR version 2 of the GNU * General Public License. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "sdcardfs.h" #include @@ -95,9 +100,12 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, err = -ENOMEM; goto out_unlock; } + copied_fs->umask = 0; + task_lock(current); current->fs = copied_fs; - current->fs->umask = 0; - err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); + task_unlock(current); + + err = vfs_create2(lower_dentry_mnt, lower_parent_dentry->d_inode, lower_dentry, mode, want_excl); if (err) goto out; @@ -110,7 +118,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, fixup_lower_ownership(dentry, dentry->d_name.name); out: + task_lock(current); current->fs = saved_fs; + task_unlock(current); free_fs_struct(copied_fs); out_unlock: unlock_dir(lower_parent_dentry); @@ -316,9 +326,12 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode unlock_dir(lower_parent_dentry); goto out_unlock; } + copied_fs->umask = 0; + task_lock(current); current->fs = copied_fs; - current->fs->umask = 0; - err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode); + task_unlock(current); + + err = vfs_mkdir2(lower_mnt, lower_parent_dentry->d_inode, lower_dentry, mode); if (err) { unlock_dir(lower_parent_dentry); @@ -377,7 +390,10 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } } out: + task_lock(current); current->fs = saved_fs; + task_unlock(current); + free_fs_struct(copied_fs); out_unlock: sdcardfs_put_lower_path(dentry, &lower_path); @@ -868,7 +884,11 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, goto out; sdcardfs_copy_and_fix_attrs(d_inode(dentry), d_inode(lower_path.dentry)); + fsstack_copy_inode_size(d_inode(dentry), + d_inode(lower_path.dentry)); err = sdcardfs_fillattr(mnt, d_inode(dentry), &lower_stat, stat); + fsstack_copy_inode_size(d_inode(dentry), + d_inode(lower_path.dentry)); out: sdcardfs_put_lower_path(dentry, &lower_path); return err; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 206f8cbc7d7d649ee2aa4aa38138f2b2e43aceea..4ad3b124440613729abafe76923514c0a492508f 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -17,9 +17,15 @@ * under the terms of the Apache 2.0 License OR version 2 of the GNU * General Public License. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "sdcardfs.h" #include "linux/delay.h" +#include /* The dentry cache is just so we have properly sized dentries */ static struct kmem_cache *sdcardfs_dentry_cachep; @@ -112,6 +118,10 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u /* if found a cached inode, then just return it (after iput) */ if (!(inode->i_state & I_NEW)) { iput(lower_inode); + /* There can only be one alias, as we don't permit hard links + * This ensures we do not keep stale dentries that would later + * cause confusion. */ + d_prune_aliases(inode); return inode; } @@ -242,6 +252,89 @@ static int sdcardfs_name_match(struct dir_context *ctx, const char *name, return 0; } +/* The dir context used by sdcardfs_lower_filldir() */ +struct sdcardfs_lower_getent_cb { + struct dir_context ctx; + loff_t pos; + const char *target; /* search target */ + int target_len; + char alias[NAME_MAX+1]; /* alias name found in lower dir */ + int alias_len; + int result; /* 0: found, -ENOENT: not found. */ +}; + +/* The filldir used by case insensitive search in sdcardfs_ci_path_lookup() */ +static int +sdcardfs_lower_filldir(struct dir_context *ctx, const char *name, int namelen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct sdcardfs_lower_getent_cb *buf; + + buf = container_of(ctx, struct sdcardfs_lower_getent_cb, ctx); + + if (!buf->result) /* entry already found, skip search */ + return 0; + + buf->pos = buf->ctx.pos; + if (!strncasecmp(name, buf->target, namelen) && + namelen == buf->target_len) { + strlcpy(buf->alias, name, namelen + 1); + buf->alias_len = namelen; + buf->result = 0; /* 0: found matching entry */ + } + return 0; +} + +/* + * Case insentively lookup lower directory. + * + * @folder: path to the lower folder. + * @name: lookup name. + * @entry: path to the found entry. + * + * Returns: 0 (ok), -ENOENT (entry not found) + */ +static int sdcardfs_ci_path_lookup(struct path *folder, const char *name, + struct path *entry) +{ + int ret = 0; + struct file *filp; + loff_t last_pos; + struct sdcardfs_lower_getent_cb buf = { + .ctx.actor = sdcardfs_lower_filldir, + .ctx.pos = 0, + .pos = 0, + .target = name, + .alias_len = 0, + .result = -ENOENT + }; + + + buf.target_len = strlen(name); + + filp = dentry_open(folder, O_RDONLY | O_DIRECTORY, current_cred()); + + if (IS_ERR_OR_NULL(filp)) + return -ENOENT; + + while (ret >= 0) { + last_pos = filp->f_pos; + ret = iterate_dir(filp, &buf.ctx); + /* reaches end or found matching entry */ + if (last_pos == filp->f_pos || !buf.result) + break; + } + + filp_close(filp, NULL); + + if (!buf.result) + return vfs_path_lookup(folder->dentry, folder->mnt, buf.alias, + 0, entry); + else + return buf.result; + +} + /* * Main driver function for sdcardfs's lookup. * @@ -314,6 +407,36 @@ put_name: __putname(buffer.name); } + /* If the dentry was not found, and the intent is not rename file, + * try case insensitive search in lower parent directory. + */ + if ((err == -ENOENT) && !(flags & LOOKUP_RENAME_TARGET)) + err = sdcardfs_ci_path_lookup(lower_parent_path, name->name, &lower_path); +#if 0 + /* check for other cases */ + if (err == -ENOENT) { + struct dentry *child; + struct dentry *match = NULL; + spin_lock(&lower_dir_dentry->d_lock); + list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { + if (child && child->d_inode) { + if (strcasecmp(child->d_name.name, name)==0) { + match = dget(child); + break; + } + } + } + spin_unlock(&lower_dir_dentry->d_lock); + if (match) { + err = vfs_path_lookup(lower_dir_dentry, + lower_dir_mnt, + match->d_name.name, 0, + &lower_path); + dput(match); + } + } +#endif + /* no error: handle positive dentries */ if (!err) { /* check if the dentry is an obb dentry diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 27ec726e7a464046e865a9c4f35afa5a727dffbb..64714a794f7b9a6711a034dca1f08e3a0eda819a 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -17,6 +17,11 @@ * under the terms of the Apache 2.0 License OR version 2 of the GNU * General Public License. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "sdcardfs.h" #include @@ -34,6 +39,7 @@ enum { Opt_reserved_mb, Opt_gid_derivation, Opt_default_normal, + Opt_nocache, Opt_err, }; @@ -48,6 +54,7 @@ static const match_table_t sdcardfs_tokens = { {Opt_gid_derivation, "derive_gid"}, {Opt_default_normal, "default_normal"}, {Opt_reserved_mb, "reserved_mb=%u"}, + {Opt_nocache, "nocache"}, {Opt_err, NULL} }; @@ -71,6 +78,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, /* by default, gid derivation is off */ opts->gid_derivation = false; opts->default_normal = false; + opts->nocache = false; *debug = 0; @@ -128,6 +136,9 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_default_normal: opts->default_normal = true; break; + case Opt_nocache: + opts->nocache = true; + break; /* unknown option */ default: if (!silent) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 055e413509e4507ddd08204523a30cf1ec02e15b..cc246f79a01dc8501387c21606878dd786eec030 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -22,6 +22,11 @@ * under the terms of the Apache 2.0 License OR version 2 of the GNU * General Public License. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _SDCARDFS_H_ #define _SDCARDFS_H_ @@ -223,6 +228,7 @@ struct sdcardfs_mount_options { bool gid_derivation; bool default_normal; unsigned int reserved_mb; + bool nocache; }; struct sdcardfs_vfsmount_options { diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index cffcdb11cb8ac8516e246524216f634bffe1e903..611f79b923a1e115f3011208d1dc238a43fa7614 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -17,6 +17,11 @@ * under the terms of the Apache 2.0 License OR version 2 of the GNU * General Public License. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "sdcardfs.h" @@ -311,6 +316,8 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, seq_puts(m, ",default_normal"); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); + if (opts->nocache) + seq_printf(m, ",nocache"); return 0; }; diff --git a/fs/xfs/kmem.c b/fs/xfs/kmem.c index 8067364c602f42b808f501d15631b934bb9e11a8..d62e7f36320f21b917f980a40d1246edcaa47757 100644 --- a/fs/xfs/kmem.c +++ b/fs/xfs/kmem.c @@ -15,6 +15,11 @@ * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include diff --git a/fs/xfs/kmem.h b/fs/xfs/kmem.h index ae45f77ce33b665d2b614f72c7df444248313e0c..a9ab34d353a598e30a3b2f1aecbddb3525bf43ef 100644 --- a/fs/xfs/kmem.h +++ b/fs/xfs/kmem.h @@ -15,6 +15,11 @@ * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __XFS_SUPPORT_KMEM_H__ #define __XFS_SUPPORT_KMEM_H__ diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 2a673e31b3dd2d942b8d54d014b4b8ff839e7007..d1d96a7975845e9a97e48f1026544e61ad2ee968 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -49,6 +49,11 @@ * Examples are: [__initramfs_start, __initramfs_end] for initramfs and * [__nosave_begin, __nosave_end] for the nosave data */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef LOAD_OFFSET #define LOAD_OFFSET 0 diff --git a/include/dt-bindings/mfd/qcom-rpm.h b/include/dt-bindings/mfd/qcom-rpm.h index 13a9d4bf2662a1fa418f7ee358ba2eb929241a11..9d6189107a7c51d7d5977139f95def379f0a002f 100644 --- a/include/dt-bindings/mfd/qcom-rpm.h +++ b/include/dt-bindings/mfd/qcom-rpm.h @@ -1,6 +1,11 @@ /* * This header provides constants for the Qualcomm RPM bindings. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _DT_BINDINGS_MFD_QCOM_RPM_H #define _DT_BINDINGS_MFD_QCOM_RPM_H diff --git a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h index 64e2dc7183f33a8df2f5ad079ebc37faa7a320bf..119aaa7cb5546e924af31026eb43f4615c6d2a1d 100644 --- a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h +++ b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h @@ -1,6 +1,11 @@ /* * This header provides constants for the Qualcomm PMIC GPIO binding. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _DT_BINDINGS_PINCTRL_QCOM_PMIC_GPIO_H #define _DT_BINDINGS_PINCTRL_QCOM_PMIC_GPIO_H diff --git a/include/dt-bindings/pinctrl/qcom,pmic-mpp.h b/include/dt-bindings/pinctrl/qcom,pmic-mpp.h index a15c1704d0ec6175878640bbba81baae8d5a64bc..560a8fd78644c77725f27a15d85cf3120aa40f92 100644 --- a/include/dt-bindings/pinctrl/qcom,pmic-mpp.h +++ b/include/dt-bindings/pinctrl/qcom,pmic-mpp.h @@ -2,6 +2,11 @@ * This header provides constants for the Qualcomm PMIC's * Multi-Purpose Pin binding. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _DT_BINDINGS_PINCTRL_QCOM_PMIC_MPP_H #define _DT_BINDINGS_PINCTRL_QCOM_PMIC_MPP_H diff --git a/include/linux/bitrev.h b/include/linux/bitrev.h index fb790b8449c1ffba36c740f3001777fa973a11d4..50cf74521904ceace125edf06eddff8c2a2c24d6 100644 --- a/include/linux/bitrev.h +++ b/include/linux/bitrev.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _LINUX_BITREV_H #define _LINUX_BITREV_H diff --git a/include/linux/clearpad.h b/include/linux/clearpad.h new file mode 100644 index 0000000000000000000000000000000000000000..84d62b4619f79c1a56148840637ad8841a9e43a3 --- /dev/null +++ b/include/linux/clearpad.h @@ -0,0 +1,126 @@ +/* include/linux/clearpad.h + * + * Author: Courtney Cavin + * Yusuke Yoshimura + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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 __LINUX_CLEARPAD_H +#define __LINUX_CLEARPAD_H + +#include +#include +#include + +#define CLEARPAD_NAME "clearpad" +#define CLEARPADI2C_NAME "clearpad-i2c" +#define CLEARPAD_RMI_DEV_NAME "clearpad-rmi-dev" + +#define SYN_PCA_BLOCK_SIZE 16 +#define SYN_PCA_BLOCK_NUMBER_MAX 31 + +/* #define CLEARPAD_WAKEUP_GESTURE */ + +enum clearpad_funcarea_kind_e { + SYN_FUNCAREA_INSENSIBLE, + SYN_FUNCAREA_POINTER, + SYN_FUNCAREA_BUTTON, + SYN_FUNCAREA_END, +}; + +enum clearpad_flip_config_e { + SYN_FLIP_NONE, + SYN_FLIP_X, + SYN_FLIP_Y, + SYN_FLIP_XY, +}; + +enum clearpad_infomation_attribute_kind_e { + PCA_DATA = 0x00, + PCA_IC = 0x01, + PCA_CHIP = 0x03, + PCA_MODULE = 0x05, +}; + +enum clearpad_infomation_kind_e { + PCA_NO_USE = 0x00, + PCA_FW_INFO = 0x02, +}; + +struct clearpad_area_t { + int x1; + int y1; + int x2; + int y2; +}; + +struct clearpad_funcarea_t { + struct clearpad_area_t original; /* actual area */ + struct clearpad_area_t extension; /* extended area to track events */ + enum clearpad_funcarea_kind_e func; + void *data; +}; + +struct clearpad_pointer_data_t { + int offset_x; + int offset_y; +}; + +struct clearpad_button_data_t { + int code; + bool down; + bool down_report; +}; + +struct clearpad_platform_data_t { + int irq_gpio; + u32 irq_gpio_flags; + char *symlink_name; + bool watchdog_enable; + int watchdog_poll_t_ms; +}; + +struct clearpad_bus_data_t { + __u16 bustype; + struct device *dev; + struct device_node *of_node; + int (*set_page)(struct device *dev, u8 page); + int (*read)(struct device *dev, u16 addr, u8 *buf, u8 len); + int (*write)(struct device *dev, u16 addr, const u8 *buf, u8 len); + int (*read_block)(struct device *dev, u16 addr, u8 *buf, int len); + int (*write_block)(struct device *dev, u16 addr, const u8 *buf, + int len); +}; + +struct clearpad_data_t { + struct clearpad_platform_data_t *pdata; + struct clearpad_bus_data_t *bdata; + int probe_retry; +#ifdef CONFIG_TOUCHSCREEN_CLEARPAD_RMI_DEV + struct platform_device *rmi_dev; +#endif +}; + +struct clearpad_ioctl_pca_info { + u16 block_pos; + u8 data[SYN_PCA_BLOCK_SIZE]; +}; + +#define SYN_PCA_IOCTL_MAGIC 0xCC +#define SYN_PCA_IOCTL_GET \ + _IOWR(SYN_PCA_IOCTL_MAGIC, 1, struct clearpad_ioctl_pca_info *) +#define SYN_PCA_IOCTL_SET \ + _IOW(SYN_PCA_IOCTL_MAGIC, 2, struct clearpad_ioctl_pca_info *) + +#endif diff --git a/include/linux/console.h b/include/linux/console.h index ea731af2451ee3607c16fda14d50fa684a54f03b..1a82576a21ce99c7a358149d0b30dcb43fae79a4 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -10,6 +10,11 @@ * Changed: * 10-Mar-94: Arno Griffioen: Conversion for vt100 emulator port from PC LINUX */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _LINUX_CONSOLE_H_ #define _LINUX_CONSOLE_H_ 1 @@ -168,6 +173,7 @@ extern bool console_suspend_enabled; /* Suspend and resume console messages over PM events */ extern void suspend_console(void); +extern int is_console_suspended(void); extern void resume_console(void); int mda_console_init(void); diff --git a/include/linux/crash_notes.h b/include/linux/crash_notes.h new file mode 100644 index 0000000000000000000000000000000000000000..63fc0889ec445f93fdba5c0ce67d8e94d322b33a --- /dev/null +++ b/include/linux/crash_notes.h @@ -0,0 +1,37 @@ +/* include/linux/crash_notes.h + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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 __CRASH_NOTES_H +#define __CRASH_NOTES_H + +#ifdef CONFIG_CRASH_NOTES +#include + +enum crash_note_save_type { + CRASH_NOTE_INIT, + CRASH_NOTE_STOPPING, + CRASH_NOTE_CRASHING +}; + +extern void crash_notes_save_cpus(void); + +#ifndef CONFIG_ARCH_HAS_CRASH_NOTES +static inline void crash_notes_save_regs(struct pt_regs *regs) { } +#endif + +#else /*!CONFIG_CRASH_NOTES*/ +static inline void crash_notes_save_cpus(void) { } +#endif /*CONFIG_CRASH_NOTES*/ +#endif /*__CRASH_NOTES_H*/ diff --git a/include/linux/cxd224x.h b/include/linux/cxd224x.h new file mode 100644 index 0000000000000000000000000000000000000000..5756a369b92202240fc15f161605292066022f0d --- /dev/null +++ b/include/linux/cxd224x.h @@ -0,0 +1,29 @@ +/* include/linux/cxd224x.h + * + * Copyright (C) 2012 Sony Corporation. + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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 _CXD224X_H +#define _CXD224X_H + +#define CXD224X_DEVICE_NAME "cxd224x-i2c" +#define CXD224X_PM_OPS_DEVICE_NAME "cxd224x-pm-ops" + +struct cxd224x_platform_data { + unsigned int irq_gpio; + unsigned int wake_gpio; +}; + +#endif diff --git a/include/linux/extcon.h b/include/linux/extcon.h index e1360198955acd7913e47b472dcb44d963f616d6..54034e058b8f7d304b5960e9b74df7f20800e40f 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -22,6 +22,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __LINUX_EXTCON_H__ #define __LINUX_EXTCON_H__ @@ -72,7 +77,10 @@ #define EXTCON_JIG 61 #define EXTCON_MECHANICAL 62 -#define EXTCON_NUM 63 +/* Somc Extention */ +#define EXTCON_VBUS_DROP 63 + +#define EXTCON_NUM 64 struct extcon_cable; diff --git a/include/linux/fb.h b/include/linux/fb.h index 87942d80a46519b1dc38f64b6d857fd1db88099c..9104bb301ac9bdceec557be54fa2ee78770595e6 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _LINUX_FB_H #define _LINUX_FB_H @@ -163,6 +168,13 @@ struct fb_cursor_user { /* A hardware display blank revert early change occured */ #define FB_R_EARLY_EVENT_BLANK 0x11 +#ifdef CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL +/* A hardware display extension blank early change occurred */ +#define FB_EXT_EARLY_EVENT_BLANK 0xF0 +/* A hardware display extension blank change occurred */ +#define FB_EXT_EVENT_BLANK 0xF1 +#endif /* CONFIG_FB_MSM_MDSS_SPECIFIC_PANEL */ + struct fb_event { struct fb_info *info; void *data; diff --git a/include/linux/fs.h b/include/linux/fs.h index f5f4e7871865fc2fffb2f0f8a9424878e8550a54..f0e909845ba2824d064a47af2c0068329cbc4340 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -141,6 +141,9 @@ typedef void (dax_iodone_t)(struct buffer_head *bh_map, int uptodate); /* Has write method(s) */ #define FMODE_CAN_WRITE ((__force fmode_t)0x40000) +/* File hasn't page cache and can't be mmaped, for stackable filesystem */ +#define FMODE_NONMAPPABLE ((__force fmode_t)0x400000) + /* File was opened by fanotify and shouldn't generate fanotify events */ #define FMODE_NONOTIFY ((__force fmode_t)0x4000000) @@ -1717,6 +1720,7 @@ struct file_operations { #ifndef CONFIG_MMU unsigned (*mmap_capabilities)(struct file *); #endif + struct file* (*get_lower_file)(struct file *f); }; struct inode_operations { diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index ecb080d6ff42077513f03b95537dc108bded9e07..87d5a3ff2c7d683bf6e448dbb9fd69da834c4dae 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _LINUX_HUGE_MM_H #define _LINUX_HUGE_MM_H diff --git a/include/linux/incell.h b/include/linux/incell.h new file mode 100644 index 0000000000000000000000000000000000000000..3cfa3fe26e9df5613fe27ad4c701025c7ed5e2f4 --- /dev/null +++ b/include/linux/incell.h @@ -0,0 +1,103 @@ +/* include/linux/incell.h + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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 __INCELL_H__ +#define __INCELL_H__ + +#include + +#define INCELL_OK ((int)0) + +/* We need to discuss the bellow error name */ +#define INCELL_ERROR ((int)(-1)) + +#define INCELL_ALREADY_LOCKED ((int)(-2)) +#define INCELL_ALREADY_UNLOCKED ((int)(-3)) +#define INCELL_EBUSY ((int)(-4)) +#define INCELL_EALREADY ((int)(-5)) + +#define INCELL_POWER_ON ((bool)true) +#define INCELL_POWER_OFF ((bool)false) +#define INCELL_FORCE ((bool)true) +#define INCELL_UNFORCE ((bool)false) + +typedef struct { + bool touch_power; + bool display_power; +} incell_pw_status; + +typedef enum { + INCELL_DISPLAY_HW_RESET, + INCELL_DISPLAY_OFF, + INCELL_DISPLAY_ON, +} incell_intf_mode; + +typedef enum { + INCELL_DISPLAY_POWER_UNLOCK, + INCELL_DISPLAY_POWER_LOCK, +} incell_pw_lock; + +typedef enum { + INCELL_DISPLAY_EWU_DISABLE, + INCELL_DISPLAY_EWU_ENABLE, +} incell_ewu_mode; + +/** + * @brief Get incell power status. + * @param[in] power_status : touch_power/display_power
+ * INCELL_POWER_ON : Power on state
+ * INCELL_POWER_OFF : Power off state. + * @return INCELL_OK : Get power status successfully
+ * INCELL_ERROR : Failed to get power status. + */ +int incell_get_power_status(incell_pw_status *power_status); + +/** + * @brief Display control mode. + * @param[in] mode : INCELL_DISPLAY_HW_RESET(execute on/off)
+ * INCELL_DISPLAY_OFF(touch is working)
+ * INCELL_DISPLAY_ON(only LCD on) + * @param[in] force : INCELL_FORCE - Forcibly execute an interface
+ * INCELL_UNFORCE - Depending power lock or not. + * @return INCELL_OK : success
+ * INCELL_ERROR : error detected
+ * INCELL_EBUSY : try lock failed. + * @attention You cannot call this function from same fb_blank context. + */ +int incell_control_mode(incell_intf_mode mode, bool force); + +/** + * @brief LCD/Touch Power lock control. + * @param[in] lock : INCELL_DISPLAY_POWER_UNLOCK(not keep power supply)
+ * INCELL_DISPLAY_POWER_LOCK(keep power supply)
+ * @param[out] power_status : return power state of incell_pw_status + * by calling incell_get_power_status function. + * @return INCELL_OK : success
+ * INCELL_ERROR : error detected
+ * INCELL_ALREADY_LOCKED : Already power locked
+ * INCELL_ALREADY_UNLOCKED : Already power unlocked. + */ +int incell_power_lock_ctrl(incell_pw_lock lock, + incell_pw_status *power_status); + +/** + * @brief Easy wake up mode (EWU) control. + * @param[in] ewu : INCELL_DISPLAY_EWU_ENABLE (EWU mode enabled)
+ * INCELL_DISPLAY_EWU_DISABLE (EWU mode disabled) + */ +void incell_ewu_mode_ctrl(incell_ewu_mode ewu); + +#endif /* __INCELL_H__ */ + diff --git a/include/linux/input/adux1050.h b/include/linux/input/adux1050.h new file mode 100644 index 0000000000000000000000000000000000000000..58b4aa6c776219093d31d796e13357dbb8106f72 --- /dev/null +++ b/include/linux/input/adux1050.h @@ -0,0 +1,605 @@ +/* + * include/linux/input/adux1050.h + * ADUX1050 controller driver header. + * This file is the header file for the ADUX1050 driver source. + * It also has chip structure prototype and the platform data prototype. + * + * Copyright 2016 Analog Devices Inc. + * + * Licensed under the GPL version 2 or later. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 __LINUX_INPUT_ADUX1050_H__ +#define __LINUX_INPUT_ADUX1050_H__ + +/* + * REGISTER BASED DEFINES associated with the driver + */ +/* Device ID Register Address */ +#define DEV_ID_REG (0x0) +/* Power and control Register */ +#define CTRL_REG (0x1) +/* Conversion time control Register */ +#define CONV_TIME_CTRL_REG (0x2) +/* Filter and interrupt control */ +#define INT_CTRL_REG (0x3) +/* Conversion and sync input config register */ +#define CONV_SYNC_CFG_REG (0x4) +/* Baseline / Ambient control Register Address */ +#define BASELINE_CTRL_REG (0x5) +/* Stage 0 configuration register */ +#define CONFIG_STG0_REG (0x6) +/* Stage 0 high threshold register */ +#define HIGH_TH_STG0_REG (0x7) +/* Stage 0 low threshold register */ +#define LOW_TH_STG0_REG (0x8) +/* DAC offset for stage 0 */ +#define OFFSET_DAC_STG0_REG (0x9) +/* Hysteresis settings for stage 0 */ +#define HYS_STG0_REG (0xA) +/* Interrupt status register */ +#define INT_STATUS_REG (0x70) +/* Stage 0 CDC result data register */ +#define RESULT_STG0_REG (0x71) +/* Peak to peak noise mesurement result register */ +#define PK2P_STG0_REG (0x75) +/* Register address of stage 0 baseline */ +#define BASELINE_STG0_REG (0x79) +/* Register address of stage 1 baseline */ +#define BASELINE_STG1_REG (0x7A) +/* Register address of stage 2 baseline */ +#define BASELINE_STG2_REG (0x7B) +/* Register address of stage 3 baseline */ +#define BASELINE_STG3_REG (0x7C) +/* Proximity status of all stages */ +#define PROX_STATUS_REG (0x7D) +/* Lowest Read/Write accessible register */ +#define HIGHEST_WR_ACCESS (0x19) +/* Highest accesible register */ +#define HIGHEST_READ_REG (0x7D) +/* + * Total number of register count used in the initialization. + * + * note + * This should be kept exactly as the register count in the + * initialization of the platform device structure + * adux1050_platform_data. + */ +#define REGCNT (28) + +/* + * Non-Register defines + */ +/* default proxy work frequency */ +#define DEFAULT_CNT (15) +/* maximum iterations for binary search */ +#define MAX_SEARCH_DEPTH (4) +/* Total stages count */ +#define TOTAL_STG (4) +/* Total CIN Count */ +#define TOTAL_CIN (4) +/* adux1050 vendor name. */ +#define VENDOR_NAME "adi" +/* Device name */ +#define DEVICE_NAME "adux1050" +/* Driver name of this adux1050 driver */ +#define DRIVER_NAME "adux1050_generic" +/* This hold the product ID of ADUX1050 */ +#define ADUX1050_GENERIC_ID (0x5000) +/* Device/product ID mask */ +#define ADUX1050_ID_MASK (0xFF00) +/* ADUX1050 revision ID mask */ +#define REV_ID_MASK (0xF0) +/* ADUX1050 metal revision ID mask */ +#define METAL_REV_ID_MASK (0x0F) +/* Metal version 50A0/50A1 */ +#define MET_VER1 (1) +/* Extracting metal ID alone */ +#define METAL_ID(x) (x & METAL_REV_ID_MASK) +/* Extracting REV ID alone */ +#define REV_ID(x) (x & (REV_ID_MASK >> 4)) +/* Extracting metal product ID alone */ +#define PRODUCT_ID(x) (x >> 8) +/* Mask 8 LSB bits */ +#define LOW_BYTE_MASK (0x00FF) +/* Mask 4 LSB bits */ +#define LOW_NIBBLE_MASK (0x000F) +/* Low data mask for the register */ +#define ADDR_MASK (0xFFFF0000) +/* High mask for the register address */ +#define DATA_MASK (0x0000FFFF) +/* ADUX1050 Power MODE mask */ +#define PWR_MODE_MASK (0x3) +/* ADUX1050 mask for getting the number of stages */ +#define NUM_STAGE_MASK (0xC) +/* The Auto Threshold mask value */ +#define AUTO_TH_MASK (0x4) +/* Default ADUX1050 force calibration mask */ +#define FORCE_CAL_MASK (0x8000) +/* Inverted force calibration mask */ +#define ANTI_FORCE_CAL_MASK (0x7FFF) +/* Reset mask for ADUX1050 */ +#define RESET_MASK (0x0800) +/* Hexa-decimal number base value */ +#define HEX_BASE (16) +/* Decimal base value */ +#define DECIMAL_BASE (10) +/* Minus char */ +#define MINUS_CHAR '-' +/* Space char */ +#define SPACE_CHAR " " +/* Minus value */ +#define MINUS_VAL (-1) +/* Parser address field value in the cmd parsing */ +#define PARSE_ADDR (0) +/* Parser value field value in the cmd parsing */ +#define PARSE_CNT (1) +/* Parser data field start value in the cmd parsing */ +#define PARSE_DATA (2) +/* I2C transfer length for a single read/write operation */ +#define I2C_WRMSG_LEN (2) +/* I2C read/write operation buffer size max */ +#define MAX_ADUX1050_WR_LEN (24) +/* Default I2C read/write length */ +#define DEF_WR (1) +/* Count of global control registers */ +#define GLOBAL_REG_CNT (6) +/* Count of stage configuration control registers */ +#define STG_CNF_CNT (20) +/* Count of status registers */ +#define STATUS_REG_CNT (14) +/* Maximum DAC offset value */ +#define MAX_OFFSET (510) /* INSTEAD of 512 */ +/* Positive swap clear */ +#define CLR_POS_SWAP (0xBFFF) +/* Negative swap clear */ +#define CLR_NEG_SWAP (0xDFFF) +/* Positive swap set */ +#define SET_POS_SWAP (0x4000) +/* Negative swap set */ +#define SET_NEG_SWAP (0x2000) +/* Clear interrupt enable register */ +#define DISABLE_DEV_INT (0x1FF) +/* Interrupt polarity mask for ACTIVE_LOW */ +#define ACTIVE_LOW (0x0) +/* Interrupt polarity mask for ACTIVE_HIGH */ +#define ACTIVE_HIGH (0x200) +/* To get the hysteresis value from the register. */ +#define HYS_BYTE_MASK (0x00FF) +/* Initial DAC compensation codes in saturation. */ +#define DAC_CODEOUT_SAT (60000) +/* Configuration file parameters count */ +#define FILP_PARAM_CNT (5) +/* Zero's value */ +#define ZERO_VAL (0) +/* ADUX1050 full scale value */ +#define FULL_SCALE_VALUE (0xFFFF) +/* ADUX1050 zero scale value */ +#define ZERO_SCALE_VALUE (0x0000) +/* Conversion complete bit location */ +#define CONV_COMPLETE_BIT (0x100) + +/* ADUX1050 half scale value */ +#define HALF_SCALE_VAL (FULL_SCALE_VALUE / 2) +/* To get the low status */ +#define GET_LOW_STATUS(status) (((status) & 0xF0) >> 4) +/* To get the high status */ +#define GET_HIGH_STATUS(status) ((status) & 0x0F) +/* To get the Conversion complete status */ +#define GET_CONV_STATUS(status) ((status & 0x0100) >> 8) +/* Set power mode */ +#define SET_PWR_MODE(pwr, mode) (((pwr) & ~PWR_MODE_MASK) | (mode)) +/* Get power mode */ +#define GET_PWR_MODE(pwr) ((pwr) & PWR_MODE_MASK) +/* Get digital offset */ +#define GET_DIGI_OFFSET(x, y) (((x)-(y))/16) +/* Digital Offset's allowed size */ +#define DIGI_OFFSET_SIZE (2032) +/* Clamp for the digital offset */ +#define CLAMP_DIGI_OFFSET(x) (((x) > 127) ? 127 : ((-(x) > 127) ? -127 : x)) +/* Get the hysteresis register number */ +#define GET_HYS_REG(stg) (((stg) * 5) + HYS_STG0_REG) +/* Get the CONFIG_STGx register */ +#define GET_CONFIG_REG(stg) (((stg) * 5) + CONFIG_STG0_REG) +/* GET_AFE_REG to get the current stages AFE register address */ +#define GET_OFFSET_REG(stg) (((stg)*5) + OFFSET_DAC_STG0_REG) +/* To get the high threshold register value */ +#define GET_HIGH_TH_REG(stg) (((stg) * 5) + HIGH_TH_STG0_REG) +/* To get the low threshold register value */ +#define GET_LOW_TH_REG(stg) (((stg) * 5) + LOW_TH_STG0_REG) +/* To get the phase conversion time */ +#define GET_CONV_TIME(reg) (((((reg) & 0x3FC0) >> 6) > 20) ? \ + (((reg) & 0x3FC0) >> 6) : 20) +/* Check for "Is the Measure noise bit set" */ +#define IS_NOISE_MEASURE_EN(stg_cfg) ((stg_cfg) & 0x0400) +/* To get the Noise Sample count */ +#define GET_NOISE_SAMPLE(cv_time_ctrl) (1 << (((cv_time_ctrl) >> 14) + 1)) +/* Get number of measurement stages */ +#define GET_NUM_STG(pwr) ((((pwr) & NUM_STAGE_MASK) >> 2) + 1) +/* Get BASE_LINE register */ +#define GET_BASE_LINE_REG(stg) ((stg) + BASELINE_STG0_REG) +/* Get noise measurement register */ +#define GET_NOISE_MES_REG(stg) ((stg) + PK2P_STG0_REG) +/* Get result register for a stage */ +#define GET_RESULT_REG(stg) ((stg) + RESULT_STG0_REG) +/* Get AUTO_WAKE_UP time */ +#define GET_AUTO_WAKE_TIME(pwr) (((pwr) >> 12) * 20) +/* Get conversion time for ADUX1050 in TIMED CONV mode */ +#define GET_TIMED_CONV_TIME(pwr) (((((pwr) & 0x00F0) >> 4) + 1) * 100) +/* To check if the minimum time setting is done */ +#define CHECK_MIN_TIME(cv_time_ctrl) (((cv_time_ctrl) & ~(0xFFE0)) || \ + !((cv_time_ctrl) & 0x20)) +/* To check whether Conversion complete interrupt is enabled or not */ +#define CHECK_CONV_EN(int_ctrl) (!(((int_ctrl) & CONV_COMPLETE_BIT) >> 8)) +/* To check whether High threshold interrupt is enabled or not */ +#define CHECK_THRESH_HIGH_EN(int_ctrl) ((~((int_ctrl) & (0xF))) & 0xF) +/* To check whether Low threshold interrupt is enabled or not */ +#define CHECK_THRESH_LOW_EN(int_ctrl) ((~(((int_ctrl) & (0xF0)) >> 4)) & 0xF) + +/* To check whether SW Rest bit is enabled or not */ +#define CHK_SW_RESET_EN(ctrl_reg) (ctrl_reg & RESET_MASK) +/* To Clear SW Reset enable */ +#define CLR_SW_RESET_EN(ctrl_reg) (ctrl_reg & ~RESET_MASK) +/* To Set SW Reset enable */ +#define SET_SW_RESET_EN(ctrl_reg) (ctrl_reg | RESET_MASK) +/* To Check for Force Calibration enable */ +#define CHK_FORCE_CALIB_EN(bs_reg) (bs_reg & FORCE_CAL_MASK) +/* To Clear Force Calibration mask */ +#define CLR_FORCE_CALIB_EN(bs_reg) (bs_reg & ~FORCE_CAL_MASK) +/* To Force calibrate the device */ +#define SET_FORCE_CALIB_EN(bs_reg) (bs_reg | FORCE_CAL_MASK) +/* To set Minimal AVG and OSR use the following */ +#define SET_MIN_TIME(cv_time_ctrl) (((cv_time_ctrl) & 0xFFC0) | 0x20) +/* Get the DAC step size for the given cin range */ +#define GET_ARB_DAC_STEP_SIZE(cin) ((1 << (cin)) * (650)) +/* To get the AVG parameter from conv_time register */ +#define GET_AVG_CONV(conv_tmr) (((conv_tmr) & 0x3) + 1) +/* To icalculate the AVG multiplier from avg */ +#define CALC_AVG_CONV(avg) (((avg) > 1) ? (1 << (avg)) : (avg)) +/* To get the OSR parameter from the conv_time register */ +#define GET_OSR_CONV(conv_tmr) (((conv_tmr) & 0x38) >> 3) +/* To get the OSR parameter from the conv_time register */ +#define CALC_OSR_CONV(osr) (((osr) < 4) ? 1 : (1 << ((osr) - 4))) +/* Get CIN_RANGE value */ +#define GET_CIN_RANGE(ctrl_reg) (((ctrl_reg) & 0x0300) >> 8) +/* Used to set the required CIN_range of the device */ +#define SET_CIN_RANGE(pwr, cin) (((pwr) & ~(0x0300)) | ((cin) << 8)) +/* To get the DAC step size to increment for the given CDC difference & cin */ +#define GET_DAC_STEP_SIZE(x, cin) ((u32)(x) / ((1 << (cin)) * 650)) +/* To get the POS AFE value */ +#define LD_POS_DAC_OFFSET(x, y) ((((x) & 0xFF00) >> 8) * \ + (((y) & 0x4000) ? -1 : 1)) +/* Macro to get the NEG AFE value */ +#define LD_NEG_DAC_OFFSET(x, y) (((x) & 0x00FF) * (((y) & 0x2000) ? 1 : -1)) +/* Macro to set the POS AFE value */ +#define ST_POS_DAC_OFFSET(x) ((x > 255) ? ((x - 255) << 8) | 0x00FF : x << 8) +/* Macro to set the NEG AFE value */ +#define ST_NEG_DAC_OFFSET(x) ((-x > 255) ? (-x - 255) | 0xFF00 : (-x)) +/* To get the 60 percent of a value. */ +#define GET_60_PERCENT(x) (((x)*3)/5) +/* To check the calibration bit statua */ +#define CHECK_CAL_STATE(x, y) ((x) & (1 << (y))) +/* To clear the calibration status bit */ +#define CLR_CAL_STATUS(x, y) ((x) = (x) & ~(1 << (y))) +/* DAC calibration routine maximum loop count */ +#define CALIB_LOOP_CNT (10) +/* I2c retry count for the i2c transfer */ +#define I2C_RETRY_CNT (3) +/* Maximum DAC calibration target */ +#define MAX_CALIB_TARGET (63000) +/* Minimum DAC calibration target */ +#define MIN_CALIB_TARGET (2000) +/* Force calibration delay */ +#define FORCE_CALIB_SLEEP_TIME (300) + + +#ifdef CONFIG_ADUX1050_POLL + +/* Minimum msleep time */ +#define MSLEEP_MIN_TIME (20) +/* Min Polling time */ +#define MIN_POLL_DELAY (10) +/* Milli sec to micro second conversion factor */ +#define MS_TO_US (1000) + +#endif + +#define TRUE (1) +#define FALSE (0) +/**to convert proxy time units to seconds*/ +#define PROXY_TIME (1000) + + +/* Event packing for STATUS */ +#define PACK_FOR_STATUS_EVENT(status, prox_status) ((status << HEX_BASE) |\ + (prox_status << 4) |\ + (CONV_COMP_STATUS)) +/* Event packing for BASELINE */ +#define PACK_FOR_BASELINE_EVENT(baseline, stage_num) ((baseline << HEX_BASE) |\ + (stage_num << 4) |\ + (CONV_COMP_BASELINE)) +/* Event packing for CDC Interrupt */ +#define PACK_FOR_CDC_EVENT(cdc_value, stage_num) ((cdc_value << HEX_BASE) |\ + (stage_num << 4) |\ + (CONV_COMPLETE)) +/* Event packing for Active event */ +#define PACK_FOR_ACTIVE_STATE(stg_num, th_type) ((TH_ACTIVE << 12) |\ + (th_type << 8) |\ + (stg_num << 4) |\ + (THRESHOLD_CROSS)) +/* Event packing for Idle event */ +#define PACK_FOR_IDLE_STATE(stg_num, th_type) ((TH_IDLE << 12) |\ + (th_type << 8) |\ + (stg_num << 4) |\ + (THRESHOLD_CROSS)) + +/* Driver state */ +enum device_state { + ADUX1050_DISABLE = 0, + ADUX1050_ENABLE, + ADUX1050_SUSPEND +}; + +/* DAC Calibration return status */ +enum calib_ret_status { + CAL_RET_NONE = 0, + CAL_RET_EXIST, + CAL_RET_SUCCESS, + CAL_RET_FAIL, + CAL_RET_PENDING +}; +/* Interrupt types of ADUX1050 */ +enum interrupt_types { + CONV_COMPLETE = 0, + THRESHOLD_CROSS, + CONV_COMP_BASELINE, + CONV_COMP_STATUS +}; +/* Threshold types */ +enum threshold_types { + TH_LOW = 0, + TH_HIGH +}; +/* Threshold states */ +enum threshold_states { + TH_IDLE = 0, + TH_ACTIVE +}; +/* Allowed power modes */ +enum power_mode { + PWR_STAND_BY = 0, + PWR_FULL_POWER, + PWR_TIMED_CONV, + PWR_AUTO_WAKE +}; +/* Allowed stage numbers */ +enum stage_no { + STG_ZERO = 0, + STG_ONE, + STG_TWO, + STG_THREE +}; +/* Allowed Cin Range */ +enum cin_range { + PICO_5 = 0, + PICO_2P5, + PICO_1P25, + PICO_0P625 +}; +/* CIN Connection status */ +enum cin_con { + CIN_NOT_CONNECTED = 0, + CIN_CONNECTED +}; +/* CIN Connection setup */ +enum cin_conn_setup { + CIN_GND = 0, + CIN_NEG_INPUT, + CIN_POS_INPUT, + CIN_SHIELD +}; +/* enum for the conversion time calculation */ +enum conv_calc_factor { + TWICE_CONV_DELAY_TIME, + CONV_TIME, + CONV_DELAY_TIME +}; +/* READ function prototype for ADUX1050 */ +typedef int32_t (*adux1050_read_t)(struct device *, uint8_t, uint16_t*, + uint16_t); +/* WRITE function prototype for ADUX1050 */ +typedef int32_t (*adux1050_write_t)(struct device *, uint8_t, uint16_t*, + uint16_t); + +/* + * Structure to hold register values from Device tree / Run time modifications + */ +struct adux1050_registers { + bool wr_flag; + u16 value; +}; + +/* + * Platform data structure of ADUX1050_SLD driver + */ +struct adux1050_platform_data { + /* This hold the initial register configurations of ADUX1050. */ + unsigned int init_regs[REGCNT]; + /* This holds the calbirated Factory baseline. */ + unsigned short cal_fact_base[TOTAL_STG]; + /* This holds the calibrated DAC offset. */ + unsigned short cal_offset[TOTAL_STG]; + /* This holds the digital offset for the calibrated stage. */ + unsigned short digi_offset[TOTAL_STG]; + /* This holds the required factory baseline. */ + unsigned short stg_cfg[TOTAL_STG]; + /* This holds the required stage 0 baseline. */ + unsigned short req_stg0_base; + /* This holds the required stage 1 baseline. */ + unsigned short req_stg1_base; + /* This holds the required stage 2 baseline. */ + unsigned short req_stg2_base; + /* This holds the required stage 3 baseline. */ + unsigned short req_stg3_base; + /* This holds the irq flags for the device. */ + unsigned short irq_flags; + /* This holds the irq gpio number for the device. */ + bool is_set_irq_gpio_no; + unsigned short irq_gpio_no; + /* This holds the force calib variables for the device. */ + bool proxy_enable; + unsigned short allowed_proxy_time; + unsigned short max_proxy_count; + /* This holds user error threshold bits for each stage. */ + unsigned intr_err; +}; + +struct adux1050_stage_info { + enum cin_con status; + unsigned char index; +}; +struct dac_calib_structure { + /* Interrupt enable setting */ + u16 enable_setting; + /* Overall DAC calibration status */ + u8 cal_flags; + /* + * Used to denote to do or clear the DAC calibration + * for the next calibration call + */ + u8 action_flag; + /* calibration status for each stage */ + u8 stg_cal_stat; + /* Saturation complete status */ + u8 sat_comp_stat; +}; +/* + * This is the chip structure which contains software structures and + * other essential for the operation of the device. + */ +struct adux1050_chip { + /* This holds the registers value */ + struct adux1050_registers reg[GLOBAL_REG_CNT + STG_CNF_CNT]; + /* This holds the baseline registers value */ + struct adux1050_registers bs_reg[TOTAL_STG]; + /* Platform data pointer */ + struct adux1050_platform_data *pdata; + /* Stages information */ + struct adux1050_stage_info *stg_info; + /* Registered input device pointer */ + struct input_dev *input; + /* Registered workstructure for the IRQ */ + struct work_struct work; + /* Registered workstructure for the DAC calibration work */ + struct work_struct calib_work; + /* Registered delayed work for force calibration on proximity */ + struct delayed_work proxy_work; + /* I2C client device pointer */ + struct device *dev; + /* Chip mutex */ + struct mutex mutex; /* Chip Mutex */ + /* Device calibration status structure */ + struct dac_calib_structure dac_calib; +#ifdef CONFIG_OF + /* DT node pointer */ + struct device_node *dt_device_node; +#endif +#ifdef CONFIG_ADUX1050_POLL + /* Kernel Polling Task struct for the adux1050. */ + struct task_struct *polling_task; +#endif + /* Read op pointer */ + adux1050_read_t read; + /* Write op pointer */ + adux1050_write_t write; + /* IRQ number */ + s32 irq; + /* to set/reset or increament the count on constant proximity */ + u32 proxy_count; + /* to store the proximity time limit to do force_calib */ + u32 allowed_proxy_time; + /* to store the max count value to do force calib */ + u32 max_proxy_count; +#ifdef CONFIG_ADUX1050_EVAL + /* Reg buffer for read registers */ + u16 stored_reg_data[MAX_ADUX1050_WR_LEN]; + /* Stored registers count in Reg buffer */ + u16 stored_data_cnt; +#endif +#ifdef CONFIG_ADUX1050_POLL + /* Kernel Polling task delay time */ + u16 poll_delay; +#endif + + /* + * Device enable to enable the adux1050 chip + * 0 - Disabled + * 1 - Enabled + * 2 - Suspended + */ + u16 dev_enable; + /* sleep time required for the current conversion settings */ + u16 slp_time_conv_complete; + /* Stage connected count */ + u16 conn_stg_cnt; + /* Current power mode */ + u16 ctrl_reg; + /* Interrupt Control */ + u16 int_ctrl; + /* Prev current high status */ + u16 prev_high_status; + /* Prev current low status */ + u16 prev_low_status; + /* Current high status */ + u16 high_status; + /* Current low status */ + u16 low_status; + /* Conversion complete status */ + u16 conv_status; + /* Current interrupt status */ + u16 int_status; + /* Used to store the initial dac offset value */ + u16 cur_dac_offset[TOTAL_STG]; + /* Used to store the initial swap state */ + u16 cur_swap_state[TOTAL_STG]; + /* This hold the number of stage */ + u16 tot_stg; + /* Used to store the interrupt polarity */ + u16 int_pol; + /* Product ID of the device */ + u16 product; + /* Used to store version ID */ + u16 version; + /* Chip metal version */ + u16 metal_id; + /* Send event to enable/disable the input event */ + s8 send_event; + /* Error bits to do foce calib */ + u8 user_intr_err; + /* used to enable the Force_calib on proximity */ + u8 proxy_enable; + /* Low threshold interrupt control */ + u8 low_thresh_enable; + /* High threshold interrupt control */ + u8 high_thresh_enable; + /* Number of measurement stages */ + u8 num_stages; + /* Stage number to read raw CDC */ + u8 stg_raw_cdc; + /* Stage number to show threshold details */ + u8 stg_threshold; + /* Conversion enable */ + bool conv_enable; + bool proxy_cancel; + /* Power mode setting flag */ + bool power_mode_flag; +}; + +#endif /* __LINUX_INPUT_ADUX1050_H__ */ diff --git a/include/linux/input/evdt_helper.h b/include/linux/input/evdt_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..91e67739e928e1056efbef109c5321e4ed87ab70 --- /dev/null +++ b/include/linux/input/evdt_helper.h @@ -0,0 +1,175 @@ +/* include/linux/input/evdt_helper.h + * + * Author: Kenji Suzuki + * + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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 __LINUX_INPUT_EVDT_HELPER_H__ +#define __LINUX_INPUT_EVDT_HELPER_H__ + +#include +#include +#include + +enum { + EVDT_MSLEEP = 0, + EVDT_KEY, + EVDT_LOG, + EVDT_END = 99, +}; + +static inline int evdt_get_msleep(struct device_node *node, u32 *ms) +{ + return of_property_read_u32(node, "ms", ms); +} + +static inline int evdt_get_key(struct device_node *node, u32 *code, u32 *down) +{ + int rc; + rc = of_property_read_u32(node, "code", code); + if (!rc) + rc = of_property_read_u32(node, "down", down); + return rc; +} + +static inline int evdt_get_log(struct device_node *node, const char **message) +{ + return of_property_read_string(node, "message", message); +} + +static inline struct device_node *evdt_initialize(struct device *dev, + struct input_dev *input, const char *node) +{ + struct device_node *wg_node = NULL; + struct device_node *block = NULL; + struct device_node *record = NULL; + int index = 0; + u32 type; + u32 ms; + u32 code; + u32 down; + const char *message; + int rc; + + if (!dev->of_node || !input) + return NULL; + + wg_node = of_find_node_by_name(dev->of_node, node); + if (!wg_node) { + pr_err("%s: No %s node found\n", __func__, node); + return NULL; + } + + for_each_child_of_node(wg_node, block) { + index = 0; + for_each_child_of_node(block, record) { + rc = of_property_read_u32(record, "type", &type); + if (rc) + goto error; + + switch (type) { + case EVDT_MSLEEP: + rc = evdt_get_msleep(record, &ms); + if (rc) + goto error; + pr_info("%s: MSLEEP [%u]\n", __func__, ms); + break; + case EVDT_KEY: + rc = evdt_get_key(record, &code, &down); + if (rc) + goto error; + input_set_capability(input, EV_KEY, code); + pr_info("%s: KEY [%u, %s]\n", __func__, code, + down ? "DOWN" : "UP"); + break; + case EVDT_LOG: + rc = evdt_get_log(record, &message); + if (rc) + goto error; + pr_info("%s: LOG [%s]\n", __func__, message); + break; + case EVDT_END: + default: + break; + } + index++; + } + } + return wg_node; + +error: + pr_err("%s: invalid value at index %d for %s rc=%d\n", + __func__, index, block->name, rc); + return NULL; +} + +static inline int evdt_execute(struct device_node *block, + struct input_dev *input, u16 gesture_code) +{ + struct device_node *gesture = NULL; + struct device_node *record; + u32 type; + u32 ms; + u32 code; + u32 down; + const char *message; + int rc; + + if (!block || !input) + return 0; + + for_each_child_of_node(block, gesture) { + rc = of_property_read_u32(gesture, "gesture_code", &type); + if (rc) { + pr_err("%s: Error reading gesture_code for %s rc=%d\n", + __func__, gesture->name, rc); + return rc; + } + + if (gesture_code != type) + continue; + + for_each_child_of_node(gesture, record) { + of_property_read_u32(record, "type", &type); + + switch (type) { + case EVDT_MSLEEP: + evdt_get_msleep(record, &ms); + pr_debug("%s: MSLEEP(%d)\n", __func__, ms); + msleep(ms); + break; + case EVDT_KEY: + evdt_get_key(record, &code, &down); + pr_debug("%s: KEY(%d) %s\n", __func__, code, + down ? "DOWN" : "UP"); + input_report_key(input, code, down); + input_sync(input); + break; + case EVDT_LOG: + evdt_get_log(record, &message); + pr_info("%s: %s\n", __func__, message); + break; + case EVDT_END: + default: + goto loop_end; + } + } + } + pr_warn("%s: Gesture code 0x%X not defined\n", __func__, gesture_code); + +loop_end: + return 0; +} + +#endif /* __LINUX_INPUT_EVDT_HELPER_H__ */ diff --git a/include/linux/input/qpnp-power-on.h b/include/linux/input/qpnp-power-on.h index 5944f0fd341424627a66500e3a9fb52d6e49585d..7b5742a3a02d800d3783c9fa523f5cdc5cee77e6 100644 --- a/include/linux/input/qpnp-power-on.h +++ b/include/linux/input/qpnp-power-on.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef QPNP_PON_H #define QPNP_PON_H @@ -52,17 +57,26 @@ enum pon_power_off_type { enum pon_restart_reason { /* 0 ~ 31 for common defined features */ - PON_RESTART_REASON_UNKNOWN = 0x00, - PON_RESTART_REASON_RECOVERY = 0x01, - PON_RESTART_REASON_BOOTLOADER = 0x02, - PON_RESTART_REASON_RTC = 0x03, - PON_RESTART_REASON_DMVERITY_CORRUPTED = 0x04, - PON_RESTART_REASON_DMVERITY_ENFORCE = 0x05, - PON_RESTART_REASON_KEYS_CLEAR = 0x06, + PON_RESTART_REASON_NONE = 0x00, + PON_RESTART_REASON_UNKNOWN = 0x01, + PON_RESTART_REASON_RECOVERY = 0x02, + PON_RESTART_REASON_BOOTLOADER = 0x03, + PON_RESTART_REASON_RTC = 0x04, + PON_RESTART_REASON_DMVERITY_CORRUPTED = 0x05, + PON_RESTART_REASON_DMVERITY_ENFORCE = 0x06, + PON_RESTART_REASON_KEYS_CLEAR = 0x07, /* 32 ~ 63 for OEMs/ODMs secific features */ PON_RESTART_REASON_OEM_MIN = 0x20, - PON_RESTART_REASON_OEM_MAX = 0x3f, + PON_RESTART_REASON_OEM_MAX = 0x7f, + + PON_RESTART_REASON_KERNEL_PANIC = 0x40, + PON_RESTART_REASON_UNHANDLED_RESET = 0x41, + PON_RESTART_REASON_FOTA_CRASH = 0x42, + PON_RESTART_REASON_OEM_F = 0x50, + PON_RESTART_REASON_OEM_P = 0x51, + PON_RESTART_REASON_XFL = 0x60, + PON_RESTART_REASON_SYSTEM = 0x61, }; #ifdef CONFIG_INPUT_QPNP_POWER_ON diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index aa493336548585046b4e8d3d21a99de1dafe90b6..7e0c5eebb1561855d48d3a5c988a72811477cbc9 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -1,4 +1,9 @@ /* interrupt.h */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _LINUX_INTERRUPT_H #define _LINUX_INTERRUPT_H @@ -63,6 +68,9 @@ * wakeup devices users need to implement wakeup detection in * their interrupt handlers. */ +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +#define IRQF_DISABLED 0x00000020 +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ #define IRQF_SHARED 0x00000080 #define IRQF_PROBE_SHARED 0x00000100 #define __IRQF_TIMER 0x00000200 diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 05b63a1e9f849e09a6b7e42f53348d2978c1290a..2b7aca4d9b98bdf0eff556cf17778c9b091dd42f 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _LINUX_KERNEL_H #define _LINUX_KERNEL_H @@ -565,6 +570,7 @@ void tracing_snapshot_alloc(void); extern void tracing_start(void); extern void tracing_stop(void); +#ifdef CONFIG_TRACE_PRINTK static inline __printf(1, 2) void ____trace_printk_check_format(const char *fmt, ...) { @@ -634,6 +640,20 @@ int __trace_bprintk(unsigned long ip, const char *fmt, ...); extern __printf(2, 3) int __trace_printk(unsigned long ip, const char *fmt, ...); +#else +static inline __printf(1, 2) +int trace_printk(const char *fmt, ...) +{ + return 0; +} + +static inline int +ftrace_vprintk(const char *fmt, va_list ap) +{ + return 0; +} +#endif /* CONFIG_TRACE_PRINTK */ + /** * trace_puts - write a string into the ftrace buffer * @str: the string to record diff --git a/include/linux/leds.h b/include/linux/leds.h index 197b61500ab768f53be9a0d8c5213046ff6e6c55..ff772726736799cb7baf907c22f8af7605b33aa3 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -9,6 +9,11 @@ * published by the Free Software Foundation. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __LINUX_LEDS_H_INCLUDED #define __LINUX_LEDS_H_INCLUDED diff --git a/include/linux/mfd/qcom_rpm.h b/include/linux/mfd/qcom_rpm.h index 742ebf1b76ca2e74a14bb3b48eeab92653948d48..a9b9b977ccb694cda6fa07ecef1da1ed44eb0f5d 100644 --- a/include/linux/mfd/qcom_rpm.h +++ b/include/linux/mfd/qcom_rpm.h @@ -1,3 +1,10 @@ +/* + * Copyright (C) 2014 Sony Mobile Communications 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 __QCOM_RPM_H__ #define __QCOM_RPM_H__ diff --git a/include/linux/mm.h b/include/linux/mm.h index 6faba4f28ab4e5bcc9b51684672f1540e70524f4..a4a8995ee39440e3f8cd2880954107ffd36a8803 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _LINUX_MM_H #define _LINUX_MM_H diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 8e76f46adde3df7fa69fff841a9ab60de36020c8..c192913b0c3cdc1b22cc4b43eac855a6429be06d 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -7,6 +7,11 @@ * * Card driver specific definitions. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef LINUX_MMC_CARD_H #define LINUX_MMC_CARD_H @@ -238,6 +243,7 @@ enum mmc_blk_status { MMC_BLK_ECC_ERR, MMC_BLK_NOMEDIUM, MMC_BLK_NEW_REQUEST, + MMC_BLK_RETRY_SINGLE, }; enum mmc_packed_stop_reasons { @@ -342,6 +348,25 @@ enum mmc_pon_type { #define MMC_QUIRK_CMDQ_DELAY_BEFORE_DCMD 6 /* microseconds */ +#ifdef CONFIG_MMC_CMD_DEBUG +#define CMD_QUEUE_SIZE CONFIG_MMC_CMD_QUEUE_SIZE +#endif + +#ifdef CONFIG_MMC_CMD_DEBUG +struct mmc_cmdq { + u32 opcode; + u32 arg; + u32 flags; + u64 timestamp; +}; + +struct mmc_cmd_stats { + u32 next_idx; + u32 wrapped; + struct mmc_cmdq cmdq[CMD_QUEUE_SIZE]; +}; +#endif + /* * MMC device */ @@ -436,6 +461,9 @@ struct mmc_card { struct notifier_block reboot_notify; enum mmc_pon_type pon_type; bool cmdq_init; +#ifdef CONFIG_MMC_CMD_DEBUG + struct mmc_cmd_stats cmd_stats; +#endif struct mmc_bkops_info bkops; bool err_in_sdr104; bool sdr104_blocked; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 48849acf34ffc8c5905781166f1bdb7b5d99bff1..05b7b7d1ca056f317cb5c8e46d612e165a0105c3 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -7,6 +7,11 @@ * * Host driver specific definitions. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef LINUX_MMC_HOST_H #define LINUX_MMC_HOST_H @@ -24,7 +29,10 @@ #include #include +/* Default idle timeout for MMC devices: 3 seconds. */ #define MMC_AUTOSUSPEND_DELAY_MS 3000 +/* Default idle timeout for SD cards: 5 minutes. */ +#define MMC_SDCARD_AUTOSUSPEND_DELAY_MS 30000 struct mmc_ios { unsigned int clock; /* clock rate */ diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h index 3945a8c9d3cbf68ba386b60166ec0b9b359e50d0..dbc9ccb587c0f4ad4592905ed22291d446aa5fc7 100644 --- a/include/linux/mmc/slot-gpio.h +++ b/include/linux/mmc/slot-gpio.h @@ -7,6 +7,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef MMC_SLOT_GPIO_H #define MMC_SLOT_GPIO_H @@ -30,4 +35,10 @@ void mmc_gpio_set_cd_isr(struct mmc_host *host, irqreturn_t (*isr)(int irq, void *dev_id)); void mmc_gpiod_request_cd_irq(struct mmc_host *host); +void mmc_gpio_init_uim2(struct mmc_host *host, unsigned int gpio); +void mmc_gpio_set_uim2_en(struct mmc_host *host, int value); +void mmc_gpio_tray_close_set_uim2(struct mmc_host *host, int value); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME +void mmc_cd_prepare_suspend(struct mmc_host *host); +#endif #endif diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index f5349823d0d295aa40a75a817c89543a01473bfd..f2632c827e543b35c322b8e31f76257ba468ce4f 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -3,6 +3,11 @@ * scripts/mod/file2alias.c. You must keep that file in sync with this * header. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef LINUX_MOD_DEVICETABLE_H #define LINUX_MOD_DEVICETABLE_H diff --git a/include/linux/mutex.h b/include/linux/mutex.h index 2cb7531e7d7a6f5044e56abc6dc3cc80cac4e596..6e6f46eda7d3401b5f4d0d22599660eda3f3a32d 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -7,6 +7,11 @@ * * This file contains the main data structure and API definitions. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __LINUX_MUTEX_H #define __LINUX_MUTEX_H @@ -171,6 +176,7 @@ extern int __must_check mutex_lock_killable(struct mutex *lock); * Returns 1 if the mutex has been acquired successfully, and 0 on contention. */ extern int mutex_trylock(struct mutex *lock); +extern int mutex_trylock_spin(struct mutex *lock); extern void mutex_unlock(struct mutex *lock); extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock); diff --git a/include/linux/oom_score_notifier.h b/include/linux/oom_score_notifier.h new file mode 100644 index 0000000000000000000000000000000000000000..acb47a20a7f683e71bacc277f2e9a483dd53c14f --- /dev/null +++ b/include/linux/oom_score_notifier.h @@ -0,0 +1,56 @@ +/* + * oom_score_notifier interface + * + * Author: Peter Enderborg + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 _LINUX_OOM_SCORE_NOTIFIER_H +#define _LINUX_OOM_SCORE_NOTIFIER_H + +#ifdef CONFIG_OOM_SCORE_NOTIFIER + +#include +#include +#include + +enum osn_msg_type { + OSN_NEW, + OSN_FREE, + OSN_UPDATE +}; + +extern struct atomic_notifier_head oom_score_notifier; +extern int oom_score_notifier_register(struct notifier_block *n); +extern int oom_score_notifier_unregister(struct notifier_block *n); +extern int oom_score_notify_free(struct task_struct *tsk); +extern int oom_score_notify_new(struct task_struct *tsk); +extern int oom_score_notify_update(struct task_struct *tsk, int old_score); + +struct oom_score_notifier_struct { + struct task_struct *tsk; + int old_score; +}; + +#else +static inline int oom_score_notify_free(struct task_struct *tsk) { return 0; }; +static inline int oom_score_notify_new(struct task_struct *tsk) { return 0; }; +static inline int oom_score_notify_update(struct task_struct *tsk, + int old_score) +{ + return 0; +}; + +#endif /* CONFIG_OOM_SCORE_NOTIFIER */ + +#endif /* _LINUX_OOM_SCORE_NOTIFIER_H */ diff --git a/include/linux/pm.h b/include/linux/pm.h index 6a5d654f444726abc824d9d07377ad66e86c6a44..faf1e391e581eeaf4682f41fb0dc03106d876ba7 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -17,6 +17,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _LINUX_PM_H #define _LINUX_PM_H @@ -703,6 +708,11 @@ extern int dpm_suspend_late(pm_message_t state); extern int dpm_suspend(pm_message_t state); extern int dpm_prepare(pm_message_t state); +#ifdef CONFIG_PM_WAKEUP_TIMES +extern void dpm_log_start_time(pm_message_t state); +extern void dpm_log_wakeup_stats(pm_message_t state); +#endif + extern void __suspend_report_result(const char *function, void *fn, int ret); #define suspend_report_result(fn, ret) \ diff --git a/include/linux/pmic-voter.h b/include/linux/pmic-voter.h index f202bf7040559681962ba94e7c47c074f3bb0cab..2d636f458f8c73b0b354f881fd4e3effa827da58 100644 --- a/include/linux/pmic-voter.h +++ b/include/linux/pmic-voter.h @@ -15,6 +15,9 @@ #include +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +#define NUM_MAX_CLIENTS 20 +#endif struct votable; enum votable_type { @@ -46,5 +49,9 @@ struct votable *create_votable(const char *name, void destroy_votable(struct votable *votable); void lock_votable(struct votable *votable); void unlock_votable(struct votable *votable); - +#if defined(CONFIG_SOMC_CHARGER_EXTENSION) +ssize_t somc_output_voter_param(struct votable *votable, + char *buf, size_t size); +int somc_get_vote_clients(struct votable *votable, char *clients[]); +#endif #endif /* __PMIC_VOTER_H */ diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 6828063842df411d9f620121c4f3bb430ad4be4a..3f41c5d30d14aff1e0a1d6760978aa9b6ed41503 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -9,6 +9,11 @@ * * You may use this code as per GPL version 2 */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __LINUX_POWER_SUPPLY_H__ #define __LINUX_POWER_SUPPLY_H__ @@ -261,6 +266,21 @@ enum power_supply_property { POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, POWER_SUPPLY_PROP_SDP_CURRENT_MAX, POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, + POWER_SUPPLY_PROP_SKIN_TEMP, + POWER_SUPPLY_PROP_SMART_CHARGING_ACTIVATION, + POWER_SUPPLY_PROP_SMART_CHARGING_INTERRUPTION, + POWER_SUPPLY_PROP_SMART_CHARGING_STATUS, + POWER_SUPPLY_PROP_LRC_ENABLE, + POWER_SUPPLY_PROP_LRC_SOCMAX, + POWER_SUPPLY_PROP_LRC_SOCMIN, + POWER_SUPPLY_PROP_LRC_NOT_STARTUP, + POWER_SUPPLY_PROP_MAX_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_FULL_RAW, + POWER_SUPPLY_PROP_TIME_TO_CAP_LEARNING, + POWER_SUPPLY_PROP_INT_CLD, + POWER_SUPPLY_PROP_MONOTONIC_SOC, + POWER_SUPPLY_PROP_LEGACY_CABLE_STATUS, + POWER_SUPPLY_PROP_RUNNING_STATUS, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ @@ -268,6 +288,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, POWER_SUPPLY_PROP_BATTERY_TYPE, + POWER_SUPPLY_PROP_CHARGER_TYPE, }; enum power_supply_type { @@ -316,6 +337,7 @@ enum power_supply_typec_power_role { POWER_SUPPLY_TYPEC_PR_DUAL, POWER_SUPPLY_TYPEC_PR_SINK, POWER_SUPPLY_TYPEC_PR_SOURCE, + POWER_SUPPLY_TYPEC_PR_SINK_DELAY, /* to SINK with delay */ }; enum power_supply_notifier_events { diff --git a/include/linux/qpnp/pin.h b/include/linux/qpnp/pin.h index 7fb57aa7a77881ea0019d2be1d9d905fd17e7766..0a9648b2a0ea35520edbc997949dd51562fab951 100644 --- a/include/linux/qpnp/pin.h +++ b/include/linux/qpnp/pin.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* Mode select */ #define QPNP_PIN_MODE_DIG_IN 0 @@ -224,3 +229,5 @@ int qpnp_pin_config(int gpio, struct qpnp_pin_cfg *param); * For such cases, use of_get_gpio() or friends instead. */ int qpnp_pin_map(const char *name, uint32_t pmic_pin); + +int qpnp_get_pin_config(int gpio, struct qpnp_pin_cfg *param); diff --git a/include/linux/qpnp/pwm.h b/include/linux/qpnp/pwm.h index 020f18b2cc4bcd0e6ecdffe84391d9c62c298e34..7eaa7225f0ddeea4f7f8b7f811aec685b749b4ad 100644 --- a/include/linux/qpnp/pwm.h +++ b/include/linux/qpnp/pwm.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __QPNP_PWM_H__ #define __QPNP_PWM_H__ @@ -134,6 +139,7 @@ struct lut_params { int lut_pause_lo; int ramp_step_ms; int flags; + bool use_duration; }; #if IS_ENABLED(CONFIG_PWM_QPNP) @@ -155,6 +161,38 @@ int pwm_lut_config(struct pwm_device *pwm, int period_us, int pwm_config_us(struct pwm_device *pwm, int duty_us, int period_us); +/* + * lut_config: LUT config + * @hi_index: LUT high index for ramp + * @lo_index: LUT low index for ramp + * @pause_hi: pause multiplier at high index + * @pause_lo: pause multiplier at low index + * @ramp_step_ms: time before loading next LUT pattern [ms] + * @lut: LUT array + * @flags: control flags (PM_PWM_LUT_XXX) + */ +#define INDEX_MAX_EACH_LED 8 +struct lut_config { + int hi_index; + int lo_index; + int lut_pause_hi; + int lut_pause_lo; + int ramp_step_ms; + int lut[INDEX_MAX_EACH_LED]; + int flags; +}; + +int pwm_config_lut(struct pwm_device *pwm, + struct lut_config *pwm_lut); + +int pwm_start_lut_ramp(struct pwm_device *pwm, int ramp_control); + +int pwm_config_period_value(struct pwm_device *pwm, + struct pwm_period_config *pwm_p, int pwm_value); + +int pwm_get_max_pwm_value(struct pwm_device *pwm); +void pwm_set_max_pwm_value(struct pwm_device *pwm, int max); + #else static inline int pwm_config_period(struct pwm_device *pwm, struct pwm_period_config *pwm_p) diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h index af25f0c0136995b6c02217a40c48cdb2afc08631..030dabc177cb1f791537e85bb958987a3fda84fd 100644 --- a/include/linux/qpnp/qpnp-adc.h +++ b/include/linux/qpnp/qpnp-adc.h @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * Qualcomm PMIC QPNP ADC driver header file * @@ -380,6 +385,8 @@ enum qpnp_adc_channel_scaling_param { * %SCALE_QRD_SKUT1_BATT_THERM: Conversion to temperature(decidegC) based on * btm parameters for SKUT1 * %SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp + * %SCALE_THERM_100K_PULLUP_DECI: Returns temperature in decidegC. + * Uses a mapping table with 100K pullup. * %SCALE_NONE: Do not use this scaling type. */ enum qpnp_adc_scale_fn_type { @@ -397,6 +404,7 @@ enum qpnp_adc_scale_fn_type { SCALE_NCP_03WF683_THERM, SCALE_QRD_SKUT1_BATT_THERM, SCALE_PMI_CHG_TEMP = 16, + SCALE_THERM_100K_PULLUP_DECI, SCALE_NONE, }; @@ -1570,6 +1578,26 @@ int32_t qpnp_adc_scale_therm_pu2(struct qpnp_vadc_chip *dev, int32_t adc_code, const struct qpnp_adc_properties *adc_prop, const struct qpnp_vadc_chan_properties *chan_prop, struct qpnp_vadc_result *chan_rslt); +/** + * qpnp_adc_scale_therm_pu2_decidegc() - Scales the pre-calibrated digital + * output of an ADC to the ADC reference and compensates for the + * gain and offset. + * Returns the temperature of the therm in decidegC. + * It uses a mapping table computed for a 100K pull-up. + * Pull-up2 is an internal pull-up on the AMUX of 100K. + * @dev: Structure device for qpnp vadc + * @adc_code: pre-calibrated digital output of the ADC. + * @adc_prop: adc properties of the pm8xxx adc such as bit resolution, + * reference voltage. + * @chan_prop: individual channel properties to compensate the i/p scaling, + * slope and offset. + * @chan_rslt: physical result to be stored. + */ +int32_t qpnp_adc_scale_therm_pu2_decidegc(struct qpnp_vadc_chip *dev, + int32_t adc_code, + const struct qpnp_adc_properties *adc_prop, + const struct qpnp_vadc_chan_properties *chan_prop, + struct qpnp_vadc_result *chan_rslt); /** * qpnp_adc_scale_therm_ncp03() - Scales the pre-calibrated digital output * of an ADC to the ADC reference and compensates for the @@ -1712,6 +1740,31 @@ int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *dev, int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *dev, const struct qpnp_adc_properties *adc_prop, uint32_t reg, int64_t *result); +/** + * qpnp_adc_tm_scale_therm_voltage_pu2_decidegc() - Performs reverse calibration + * and convert given temperature to voltage on supported + * thermistor channels using 100k pull-up. + * @dev: Structure device for qpnp vadc + * @adc_prop: adc properties of the qpnp adc such as bit resolution, + * reference voltage. + * @param: The input temperature values. + */ +int32_t qpnp_adc_tm_scale_therm_voltage_pu2_decidegc(struct qpnp_vadc_chip *dev, + const struct qpnp_adc_properties *adc_properties, + struct qpnp_adc_tm_config *param); +/** + * qpnp_adc_tm_scale_voltage_therm_pu2_decidegc() - Performs reverse calibration + * and converts the given ADC code to temperature for + * thermistor channels using 100k pull-up. + * @dev: Structure device for qpnp vadc + * @adc_prop: adc properties of the qpnp adc such as bit resolution, + * reference voltage. + * @reg: The input ADC code. + * @result: The physical measurement temperature on the thermistor. + */ +int32_t qpnp_adc_tm_scale_voltage_therm_pu2_decidegc(struct qpnp_vadc_chip *dev, + const struct qpnp_adc_properties *adc_prop, + uint32_t reg, int64_t *result); /** * qpnp_adc_usb_scaler() - Performs reverse calibration on the low/high * voltage threshold values passed by the client. @@ -1983,6 +2036,12 @@ static inline int32_t qpnp_adc_scale_therm_pu2(struct qpnp_vadc_chip *vadc, const struct qpnp_vadc_chan_properties *chan_prop, struct qpnp_vadc_result *chan_rslt) { return -ENXIO; } +static inline int32_t qpnp_adc_scale_therm_pu2_decidegc( + struct qpnp_vadc_chip *dev, int32_t adc_code, + const struct qpnp_adc_properties *adc_prop, + const struct qpnp_vadc_chan_properties *chan_prop, + struct qpnp_vadc_result *chan_rslt) +{ return -ENXIO; } static inline int32_t qpnp_adc_scale_therm_ncp03(struct qpnp_vadc_chip *vadc, int32_t adc_code, const struct qpnp_adc_properties *adc_prop, @@ -2040,6 +2099,16 @@ static inline int32_t qpnp_adc_tm_scale_voltage_therm_pu2( const struct qpnp_adc_properties *adc_prop, uint32_t reg, int64_t *result) { return -ENXIO; } +static inline int32_t qpnp_adc_tm_scale_therm_voltage_pu2_decidegc( + struct qpnp_vadc_chip *dev, + const struct qpnp_adc_properties *adc_properties, + struct qpnp_adc_tm_config *param) +{ return -ENXIO; } +static inline int32_t qpnp_adc_tm_scale_voltage_therm_pu2_decidegc( + struct qpnp_vadc_chip *dev, + const struct qpnp_adc_properties *adc_prop, + uint32_t reg, int64_t *result) +{ return -ENXIO; } static inline int32_t qpnp_adc_smb_btm_rscaler(struct qpnp_vadc_chip *dev, struct qpnp_adc_tm_btm_param *param, uint32_t *low_threshold, uint32_t *high_threshold) diff --git a/include/linux/ramdump_mem_desc.h b/include/linux/ramdump_mem_desc.h new file mode 100644 index 0000000000000000000000000000000000000000..0945836a6db9e6b726e4989bb176fbfebf85aa4d --- /dev/null +++ b/include/linux/ramdump_mem_desc.h @@ -0,0 +1,38 @@ +/* + * Author: Nandhakumar Rangasamy + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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 __RAMDUMP_MEM_DESC_H_ +#define __RAMDUMP_MEM_DESC_H_ + +#define MEM_DESC_NAME_SIZE 32 + +enum { + MEM_DESC_PLATFORM = 0, + MEM_DESC_CORE +}; + +struct mem_desc { + u64 phys_addr; + u64 size; + u8 name[MEM_DESC_NAME_SIZE]; + u32 flags; + u32 reserved; +} __attribute__ ((__packed__)); + +void ramdump_add_mem_desc(struct mem_desc *); +void ramdump_remove_mem_desc(struct mem_desc *); + +#endif diff --git a/include/linux/rdtags.h b/include/linux/rdtags.h new file mode 100644 index 0000000000000000000000000000000000000000..6e3689f12cbfcbd25d1432c04ff7cc0920a777c6 --- /dev/null +++ b/include/linux/rdtags.h @@ -0,0 +1,49 @@ +/* + * + * Author: Nilsson, Stefan 2 + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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 __RAMDUMP_TAGS_H_ +#define __RAMDUMP_TAGS_H_ + +#include + +int rdtags_remove_tag(const char *name); +int rdtags_add_tag(const char *name, const unsigned char *data, + const size_t size); +int rdtags_get_tag_data(const char *name, unsigned char *data, size_t *size); +void rdtags_clear_tags(void); + +#define rdtags_add_tag_string(x, y) rdtags_add_tag(x, y, sizeof(y)) +#define rdtags_add_tag_type(x, y) \ + rdtags_add_tag(x, (unsigned char *)&y, sizeof(y)) + +#define rdtags_tag_symbol(name) \ +({ \ + char data[32] = {0}; \ + snprintf(data, sizeof(data), "0x%lx", \ + (unsigned long)__pa(&name)); \ + rdtags_add_tag(#name, data, strnlen(data, sizeof(data))); \ +}) + +/** + * struct rdtags_platform_data - Ramdump Tags platform data + * @platform_init: Function that will be called after rdtags is loaded + */ +struct rdtags_platform_data { + int (*platform_init)(int mode); + int ramdump_mode; +}; +#endif diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 734ae72d3c16227e524b1684df43ca046e641ed1..5af39f7c7f9b5cc0f09ce5bbd1510164728c5778 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -31,6 +31,11 @@ * in normal mode for loads > 10mA and in IDLE mode for load <= 10mA. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __LINUX_REGULATOR_CONSUMER_H_ #define __LINUX_REGULATOR_CONSUMER_H_ @@ -163,6 +168,18 @@ struct regulator_bulk_data { int ret; }; +/** + * struct regulator_ocp_notification: event notification structure + * @notify: pointer to client function to call when ocp event is detected. + * notify function runs in interrupt context. + * @ctxt: client-specific context pointer + * + */ +struct regulator_ocp_notification { + void (*notify)(void *); + void *ctxt; +}; + #if defined(CONFIG_REGULATOR) /* regulator get and put */ @@ -259,6 +276,9 @@ int regulator_get_hardware_vsel_register(struct regulator *regulator, unsigned *vsel_mask); int regulator_list_hardware_vsel(struct regulator *regulator, unsigned selector); +/* regulator register ocp notification */ +int regulator_register_ocp_notification(struct regulator *regulator, + struct regulator_ocp_notification *ocp_notification); /* regulator notifier block */ int regulator_register_notifier(struct regulator *regulator, @@ -523,6 +543,13 @@ static inline int regulator_list_hardware_vsel(struct regulator *regulator, return -EOPNOTSUPP; } +static inline int regulator_register_ocp_notification( + struct regulator *regulator, + struct regulator_ocp_notification *ocp_notification) +{ + return 0; +} + static inline int regulator_register_notifier(struct regulator *regulator, struct notifier_block *nb) { diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 9cee86d33f86ebb6280ecd71ce61afb191d6ed7e..36eeb954ad97c2f766cd720eca9e5cce01fef7f1 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -11,6 +11,11 @@ * * Regulator Driver Interface. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __LINUX_REGULATOR_DRIVER_H_ #define __LINUX_REGULATOR_DRIVER_H_ @@ -119,6 +124,8 @@ struct regulator_linear_range { * function should return the worst case. * @set_soft_start: Enable soft start for the regulator. * + * @register_ocp_notification: Register the notification for ocp. + * * @set_suspend_voltage: Set the voltage for the regulator when the system * is suspended. * @set_suspend_enable: Mark the regulator as enabled when the system is @@ -191,6 +198,10 @@ struct regulator_ops { int (*set_bypass)(struct regulator_dev *dev, bool enable); int (*get_bypass)(struct regulator_dev *dev, bool *enable); + /* register ocp notification */ + int (*register_ocp_notification) (struct regulator_dev *, + struct regulator_ocp_notification *notification); + /* the operations below are for configuration of regulator state when * its parent PMIC enters a global STANDBY/HIBERNATE state */ diff --git a/include/linux/regulator/qpnp-labibb-regulator.h b/include/linux/regulator/qpnp-labibb-regulator.h index 33985afeb6e9e10316d47631fab7e75a34a895ed..955498eaa1152389c00dd7600ec0daf2501076a4 100644 --- a/include/linux/regulator/qpnp-labibb-regulator.h +++ b/include/linux/regulator/qpnp-labibb-regulator.h @@ -9,16 +9,124 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _QPNP_LABIBB_REGULATOR_H #define _QPNP_LABIBB_REGULATOR_H +#define SOMC_LABIBB_REGULATOR_ORG_IMPL + +#include + enum labibb_notify_event { - LAB_VREG_OK = 1, + LAB_VREG_OK = 1, LAB_VREG_NOT_OK, }; int qpnp_labibb_notifier_register(struct notifier_block *nb); int qpnp_labibb_notifier_unregister(struct notifier_block *nb); -#endif +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +bool qpnp_labibb_ocp_check(void); +#else +static inline bool qpnp_labibb_ocp_check(void) +{ + return false; +} +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + + +#ifdef SOMC_LABIBB_REGULATOR_ORG_IMPL +/** This API is used to set precharge of LAB regulator + * regulator: the reglator device + * time: precharge time + * en: precharge control enable or not + */ +int qpnp_lab_set_precharge(struct regulator *regulator, u32 time, bool en); + +/** This API is used to set soft-start of LAB regulator + * regulator: the reglator device + * time: soft start time + */ +int qpnp_lab_set_soft_start(struct regulator *regulator, u32 time); + +/** This API is used to set pull-down of LAB regulator + * regulator: the reglator device + * en: pull-down enable or not + * strength: strength pull-down + */ +int qpnp_lab_set_pull_down(struct regulator *regulator, u8 strength); + +/** This API is used to set current max of LAB regulator + * regulator: the reglator device + * limit: current max of LAB regulator + */ +int qpnp_lab_set_current_max(struct regulator *regulator, u32 limit); + +/** This API is used to set soft-start of IBB regulator + * regulator: the reglator device + * time: soft start time + */ +int qpnp_ibb_set_soft_start(struct regulator *regulator, u32 time); + +/** This API is used to set pull-down of IBB regulator + * regulator: the reglator device + * en: pull-down enable or not + * strength: strength pull-down + */ +int qpnp_ibb_set_pull_down(struct regulator *regulator, u8 strength); + +/** This API is used to set current max of IBB regulator + * regulator: the reglator device + * limit: current max of IBB regulator + */ +int qpnp_ibb_set_current_max(struct regulator *regulator, u32 limit); +#else +static inline int qpnp_lab_set_precharge(struct regulator *regulator, + u32 time, bool en) +{ + return -ENODEV; +} + +static inline int qpnp_lab_set_soft_start(struct regulator *regulator, + u32 time) +{ + return -ENODEV; +} + +static inline int qpnp_lab_set_pull_down(struct regulator *regulator, + u8 strength) +{ + return -ENODEV; +} + +static inline int qpnp_lab_set_current_max(struct regulator *regulator, + u32 limit) +{ + return -ENODEV; +} + +static inline int qpnp_ibb_set_soft_start(struct regulator *regulator, + u32 time) +{ + return -ENODEV; +} + +static inline int qpnp_ibb_set_pull_down(struct regulator *regulator, + u8 strength) +{ + return -ENODEV; +} + +static inline int qpnp_ibb_set_current_max(struct regulator *regulator, + u32 limit) +{ + return -ENODEV; +} +#endif /* SOMC_LABIBB_REGULATOR_ORG_IMPL */ + +#endif /* _QPNP_LABIBB_REGULATOR_H */ diff --git a/include/linux/soc/qcom/smd-rpm.h b/include/linux/soc/qcom/smd-rpm.h index ebdabd669d932a4eb203ca8e851288543e346c14..0dfd47a0fbe7bb7fc1114bb259ef8287fb46148b 100644 --- a/include/linux/soc/qcom/smd-rpm.h +++ b/include/linux/soc/qcom/smd-rpm.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __QCOM_SMD_RPM_H__ #define __QCOM_SMD_RPM_H__ diff --git a/include/linux/soc/qcom/smd.h b/include/linux/soc/qcom/smd.h index d0cb6d189a0a02bd28459c7b143229563f23c4e4..1f7e8e3cdfb52cf807c74dd0061e6c87b7166f24 100644 --- a/include/linux/soc/qcom/smd.h +++ b/include/linux/soc/qcom/smd.h @@ -1,3 +1,10 @@ +/* + * Copyright (C) 2015 Sony Mobile Communications 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 __QCOM_SMD_H__ #define __QCOM_SMD_H__ diff --git a/include/linux/soc/qcom/smem.h b/include/linux/soc/qcom/smem.h index 785e196ee2cae6f1b0ec48fad8816db3839aa943..b2e9000df445ea2487da099d0909bc4d31f5997b 100644 --- a/include/linux/soc/qcom/smem.h +++ b/include/linux/soc/qcom/smem.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __QCOM_SMEM_H__ #define __QCOM_SMEM_H__ diff --git a/include/linux/suspend.h b/include/linux/suspend.h index be1ab158ad1a6156098960dce7e260229754eda7..383c61228c5060ff8d198ce5a32e1d023873d94d 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _LINUX_SUSPEND_H #define _LINUX_SUSPEND_H @@ -7,6 +12,10 @@ #include #include #include +#ifdef CONFIG_PM_WAKEUP_TIMES +#include +#include +#endif #include #ifdef CONFIG_VT @@ -51,6 +60,20 @@ enum suspend_stat_step { SUSPEND_RESUME }; +#ifdef CONFIG_PM_WAKEUP_TIMES +struct stats_wakeup_time { + ktime_t start; + ktime_t end; +}; + +struct suspend_stats_queue { + int resume_done; + wait_queue_head_t wait_queue; +}; + +extern struct suspend_stats_queue suspend_stats_queue; +#endif + struct suspend_stats { int success; int fail; @@ -69,6 +92,16 @@ struct suspend_stats { int errno[REC_FAILED_NUM]; int last_failed_step; enum suspend_stat_step failed_steps[REC_FAILED_NUM]; +#ifdef CONFIG_PM_WAKEUP_TIMES + struct stats_wakeup_time suspend_min_time; + struct stats_wakeup_time suspend_max_time; + struct stats_wakeup_time suspend_last_time; + ktime_t suspend_avg_time; + struct stats_wakeup_time resume_min_time; + struct stats_wakeup_time resume_max_time; + struct stats_wakeup_time resume_last_time; + ktime_t resume_avg_time; +#endif }; extern struct suspend_stats suspend_stats; diff --git a/include/linux/tof_sensor.h b/include/linux/tof_sensor.h new file mode 100644 index 0000000000000000000000000000000000000000..c2dd90a22339fa295347894139950f4c25ec890e --- /dev/null +++ b/include/linux/tof_sensor.h @@ -0,0 +1,26 @@ +/* + * 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. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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 TOF_SENSOR_H +#define TOF_SENSOR_H + +/* + * tof sensor power control. + */ +enum tof_sensor_power_control { + TOF_SENSOR_POWER_CONTROL_OFF, + TOF_SENSOR_POWER_CONTROL_ON, +}; + +#endif /* TOF_SENSOR_H */ diff --git a/include/linux/uid_stat.h b/include/linux/uid_stat.h new file mode 100644 index 0000000000000000000000000000000000000000..7c8ff2d13369f1ebe6530a4c66ecf15ff8f8e710 --- /dev/null +++ b/include/linux/uid_stat.h @@ -0,0 +1,34 @@ +/* include/linux/uid_stat.h + * + * Copyright (C) 2008-2009 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. + * + */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ + +#ifndef __uid_stat_h +#define __uid_stat_h + +/* Contains definitions for resource tracking per uid. */ + +#ifdef CONFIG_UID_STAT +int uid_stat_tcp_snd(uid_t uid, int size); +int uid_stat_tcp_rcv(uid_t uid, int size); +#else +#define uid_stat_tcp_snd(uid, size) do {} while (0); +#define uid_stat_tcp_rcv(uid, size) do {} while (0); +#endif + +#endif /* _LINUX_UID_STAT_H */ diff --git a/include/linux/usb/host_ext_event.h b/include/linux/usb/host_ext_event.h new file mode 100644 index 0000000000000000000000000000000000000000..b21477dbad94f90156bd53a1503c7da50a0d3c06 --- /dev/null +++ b/include/linux/usb/host_ext_event.h @@ -0,0 +1,30 @@ + /* include/linux/usb/host_ext_event.h + * + * USB host event handling function + * + * 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. + */ +/* + * Copyright (C) 2013 Sony Mobile Communications 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 __HOST_EXT_EVENT_H__ +#define __HOST_EXT_EVENT_H__ + +enum usb_host_ext_event { + USB_HOST_EXT_EVENT_NONE = -1, + USB_HOST_EXT_EVENT_VBUS_DROP, + USB_HOST_EXT_EVENT_INSUFFICIENT_POWER, +}; + +extern int host_send_uevent(enum usb_host_ext_event event); + +#endif diff --git a/include/linux/zpool.h b/include/linux/zpool.h index 2e97b7707dffcb9f3067e4c574e3c73700469146..0c567847e28fddaa82c90d53021c4f5743410747 100644 --- a/include/linux/zpool.h +++ b/include/linux/zpool.h @@ -7,6 +7,11 @@ * storage pool implementations. Typically, this is used to * store compressed memory. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _ZPOOL_H_ #define _ZPOOL_H_ @@ -60,6 +65,9 @@ void zpool_unmap_handle(struct zpool *pool, unsigned long handle); u64 zpool_get_total_size(struct zpool *pool); +unsigned long zpool_compact(struct zpool *pool); + +unsigned long zpool_get_num_compacted(struct zpool *zpool); /** * struct zpool_driver - driver implementation for zpool @@ -101,6 +109,10 @@ struct zpool_driver { void (*unmap)(void *pool, unsigned long handle); u64 (*total_size)(void *pool); + + unsigned long (*compact)(void *pool); + + unsigned long (*get_num_compacted)(void *pool); }; void zpool_register_driver(struct zpool_driver *driver); diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h index 2c8b651147e0b96ec55941210efca13bd619e91b..3b38035c28ec08d20df38a1ef4d0f359864887ef 100644 --- a/include/media/msm_cam_sensor.h +++ b/include/media/msm_cam_sensor.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __LINUX_MSM_CAM_SENSOR_H #define __LINUX_MSM_CAM_SENSOR_H @@ -122,6 +127,9 @@ struct msm_eeprom_cfg_data32 { }; struct msm_camera_i2c_seq_reg_setting32 { +/* extension begin */ + uint16_t slave_addr; +/* extension end */ compat_uptr_t reg_setting; uint16_t size; enum msm_camera_i2c_reg_addr_type addr_type; @@ -256,6 +264,13 @@ struct msm_flash_cfg_data_t32 { } cfg; }; +/* extension begin */ +struct msm_sensor_event_data32 { + uint32_t sof_count; + struct compat_timeval mono_timestamp; +}; +/* extension end */ + #define VIDIOC_MSM_ACTUATOR_CFG32 \ _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct msm_actuator_cfg_data32) diff --git a/include/net/activity_stats.h b/include/net/activity_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..3e85979109e0815e4234e46206512dd186b2088d --- /dev/null +++ b/include/net/activity_stats.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010 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. + * + * Author: Mike Chan (mike@android.com) + */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ + +#ifndef __activity_stats_h +#define __activity_stats_h + +#ifdef CONFIG_NET_ACTIVITY_STATS +void activity_stats_update(void); +#else +#define activity_stats_update(void) {} +#endif + +#endif /* _NET_ACTIVITY_STATS_H */ diff --git a/include/soc/qcom/camera2.h b/include/soc/qcom/camera2.h index 5a61d2b372c3a6da2f4d6d6412b0cf97c55a7084..f409ac88d336f481d0ce750631e2a4444b094f8a 100644 --- a/include/soc/qcom/camera2.h +++ b/include/soc/qcom/camera2.h @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __CAMERA2_H__ #define __CAMERA2_H__ @@ -87,6 +92,10 @@ struct msm_camera_gpio_conf { uint8_t cam_gpio_common_tbl_size; struct gpio *cam_gpio_req_tbl; uint8_t cam_gpio_req_tbl_size; +/* extension begin */ + struct msm_gpio_set_tbl *cam_gpio_set_tbl; + uint8_t cam_gpio_set_tbl_size; +/* extension end */ uint32_t gpio_no_mux; uint32_t *camera_off_table; uint8_t camera_off_table_size; diff --git a/include/soc/qcom/last_logs.h b/include/soc/qcom/last_logs.h new file mode 100644 index 0000000000000000000000000000000000000000..4e1da68a6d772457e56f98d3a5a4d5f767e7afc5 --- /dev/null +++ b/include/soc/qcom/last_logs.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 _LAST_LOGS_H +#define _LAST_LOGS_H + +#ifdef CONFIG_LAST_LOGS + +#ifdef CONFIG_MSM_TZ_LOG +#include +#endif /* CONFIG_MSM_TZ_LOG */ + +#define MAX_LAST_LOGS_REGIONS 16 +#define LAST_LOGS_VERSION 0x00010000 +#define LAST_LOGS_MAGIC 0x442474D4 +#define MAX_NAME_LENGTH 16 + +typedef struct __packed { + char name[MAX_NAME_LENGTH]; + uint32_t offset; + uint32_t size; + uint32_t reserved; +} last_logs_region; + +typedef struct __packed { + uint32_t version; + uint32_t magic; + uint32_t num_regions; + uint32_t reserved[3]; + last_logs_region regions[MAX_LAST_LOGS_REGIONS]; +} last_logs_header; + +struct last_logs_data { + void *addr; + uint32_t size; + struct dentry *debugfs_file; +}; + +#endif /* CONFIG_LAST_LOGS */ +#endif /* _LAST_LOGS_H */ diff --git a/include/soc/qcom/last_logs_tz.h b/include/soc/qcom/last_logs_tz.h new file mode 100644 index 0000000000000000000000000000000000000000..735b4f27b3dacfba624014b222a4fccbbee40de7 --- /dev/null +++ b/include/soc/qcom/last_logs_tz.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 _LAST_LOGS_TZ_H +#define _LAST_LOGS_TZ_H + +#ifdef CONFIG_MSM_TZ_LOG +int format_tzbsp_log(void *, size_t, void **, uint32_t *); +#endif /* CONFIG_MSM_TZ_LOG */ + +#endif /* _LAST_LOGS_TZ_H */ diff --git a/include/soc/qcom/memory_dump.h b/include/soc/qcom/memory_dump.h index f1170dbb3569dc2aa9fe8bfdd6899ef582d80e02..79b75ca2691545c91f37b0e66ca35794f30763c7 100644 --- a/include/soc/qcom/memory_dump.h +++ b/include/soc/qcom/memory_dump.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __MSM_MEMORY_DUMP_H #define __MSM_MEMORY_DUMP_H @@ -118,12 +123,21 @@ struct msm_dump_entry { #ifdef CONFIG_QCOM_MEMORY_DUMP_V2 extern int msm_dump_data_register(enum msm_dump_table_ids id, struct msm_dump_entry *entry); +#ifdef CONFIG_RAMDUMP_TAGS +extern int dump_table_ramdump_setup(void); +#endif #else static inline int msm_dump_data_register(enum msm_dump_table_ids id, struct msm_dump_entry *entry) { return -ENOSYS; } +#ifdef CONFIG_RAMDUMP_TAGS +static inline int dump_table_ramdump_setup(void) +{ + return -ENOSYS; +} +#endif #endif #endif diff --git a/include/soc/qcom/security_status.h b/include/soc/qcom/security_status.h new file mode 100644 index 0000000000000000000000000000000000000000..d554d14382f8c628a59fe255abcd90ecc1da281d --- /dev/null +++ b/include/soc/qcom/security_status.h @@ -0,0 +1,31 @@ +/** + * @file security_status.h + * + * @brief Gets security status of device by + * checking oemandroidboot.securityflags + * + * @author Nandhakumar Rangasamy (nandhakumar.x.rangasamy@sonymobile.com) + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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 __SECURITY_STATUS_H +#define __SECURITY_STATUS_H + +#define SECURITY_ON 1 +#define SECURITY_OFF 0 + +#ifdef CONFIG_SECURITY_STATUS +int get_security_status(int *status); +#else +static inline int get_security_status(int *status) +{ + return -ENOTSUP; +} +#endif /* CONFIG_SECURITY_STATUS */ +#endif /* __SECURITY_STATUS_H */ diff --git a/include/soc/qcom/subsystem_restart.h b/include/soc/qcom/subsystem_restart.h index 9a4d013b363cc14c3e786036935432d868bdefa2..da0c07cccba8956c50818dd7f3d34e75bd06b1cf 100644 --- a/include/soc/qcom/subsystem_restart.h +++ b/include/soc/qcom/subsystem_restart.h @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __SUBSYS_RESTART_H #define __SUBSYS_RESTART_H @@ -17,6 +22,8 @@ #include #include +#define SUBSYS_CRASH_REASON_LEN 512 + struct subsys_device; extern struct bus_type subsys_bus_type; @@ -134,12 +141,28 @@ extern void subsys_set_crash_status(struct subsys_device *dev, enum crash_status crashed); extern enum crash_status subsys_get_crash_status(struct subsys_device *dev); extern void subsys_set_error(struct subsys_device *dev, const char *error_msg); + +extern int subsystem_crash_reason(const char *name, char *reason); +#if defined(CONFIG_DEBUG_FS) +extern void update_crash_reason(struct subsys_device *dev, char *, int); +#else +static inline void update_crash_reason(struct subsys_device *dev, + char *reason, int size) { } +#endif void notify_proxy_vote(struct device *device); void notify_proxy_unvote(struct device *device); void complete_err_ready(struct subsys_device *subsys); extern int wait_for_shutdown_ack(struct subsys_desc *desc); #else +static inline void update_crash_reason(struct subsys_device *dev, + char *reason, int size) { } + +static inline int subsystem_crash_reason(const char *name, char *reason) +{ + return 0; +} + static inline int subsys_get_restart_level(struct subsys_device *dev) { return 0; diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index 48fe32252e8dc5910a5cc31f3470d14979dd112d..98ecd589d08a5a093a04925d4ed805630e7798b9 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _APR_AUDIO_V2_H_ @@ -660,7 +665,7 @@ struct adm_cmd_set_pp_params { #define ADM_CMD_GET_MTMX_STRTR_DEV_PARAMS_V1 0x00010368 #define ADM_CMDRSP_GET_MTMX_STRTR_DEV_PARAMS_V1 0x00010369 -/* Payload of the #define ADM_CMD_SET_MTMX_STRTR_DEV_PARAMS_V1 command. +/* Payload of the #define�ADM_CMD_SET_MTMX_STRTR_DEV_PARAMS_V1 command. * If the data_payload_addr_lsw and data_payload_addr_msw element * are NULL, a series of struct param_hdr_v3 structures immediately * follows, whose total size is payload_size bytes. @@ -790,6 +795,44 @@ struct adm_cmd_rsp_device_open_v5 { #define ADM_CMD_GET_PP_PARAMS_V5 0x0001032A #define ADM_CMD_GET_PP_PARAMS_V6 0x0001035E +/* Payload an #ADM_CMD_GET_PP_PARAMS_V5 command. */ +struct adm_cmd_get_pp_params_v5 { + struct apr_hdr hdr; + u32 data_payload_addr_lsw; + /* LSW of parameter data payload address.*/ + + u32 data_payload_addr_msw; + /* MSW of parameter data payload address.*/ + + /* If the mem_map_handle is non zero, + * on ACK, the ParamData payloads begin at + * the address specified (out-of-band). + */ + + u32 mem_map_handle; + /* Memory map handle returned + * by ADM_CMD_SHARED_MEM_MAP_REGIONS command. + * If the mem_map_handle is 0, it implies that + * the ACK's payload will contain the ParamData (in-band). + */ + + u32 module_id; + /* Unique ID of the module. */ + + u32 param_id; + /* Unique ID of the parameter. */ + + u16 param_max_size; + /* Maximum data size of the parameter + *ID/module ID combination. This + * field is a multiple of 4 bytes. + */ + u16 reserved; + /* Reserved for future enhancements. + * This field must be set to zero. + */ +} __packed; + /* * Structure of the ADM Get PP Params command. Parameter header must be * packed correctly for either V2 or V3. Use q6core_pack_pp_params to pack the @@ -930,6 +973,22 @@ struct adm_cmd_set_pp_params_v5 { */ } __packed; +struct adm_param_data_v5 { + u32 module_id; + /* Unique ID of the module. */ + u32 param_id; + /* Unique ID of the parameter. */ + u16 param_size; + /* Data size of the param_id/module_id combination. + * This value is a + * multiple of 4 bytes. + */ + u16 reserved; + /* Reserved for future enhancements. + * This field must be set to zero. + */ +} __packed; + struct audproc_mfc_param_media_fmt { uint32_t sampling_rate; uint16_t bits_per_sample; @@ -1199,6 +1258,7 @@ struct adm_session_copp_gain_v7 { /* Payload of the #ADM_CMD_MATRIX_MUTE_V5 command*/ struct adm_cmd_matrix_mute_v5 { + struct apr_hdr hdr; u32 matrix_id; /* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx (1). * Use the ADM_MATRIX_ID_AUDIO_RX or ADM_MATRIX_ID_AUDIOX @@ -4455,6 +4515,8 @@ struct afe_param_id_lpass_core_shared_clk_cfg { #define VPM_TX_DM_RFECNS_COPP_TOPOLOGY 0x00010F86 #define ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX 0x10015002 #define ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE 0x10028000 +#define VOICE_TOPOLOGY_LVVEFQ_TX_SM 0x1000BFF0 +#define VOICE_TOPOLOGY_LVVEFQ_TX_DM 0x1000BFF1 /* Memory map regions command payload used by the * #ASM_CMD_SHARED_MEM_MAP_REGIONS ,#ADM_CMD_SHARED_MEM_MAP_REGIONS @@ -7964,6 +8026,14 @@ struct asm_stream_cmd_open_read_compressed { 0x11000000 #define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_MCH_PEAK_VOL \ 0x0001031B +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_RX_MCH_FIR_IIR_COPP_MBDRC_V3 \ + 0x11000009 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_RX_MCH_IIR_COPP_MBDRC_V3 \ + 0x11000004 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_SOMC_HP \ + 0x11000006 +#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_RX_MCH_FIR_IIR_COPP_MBDRC_V3 \ + 0x11000009 #define ADM_CMD_COPP_OPENOPOLOGY_ID_MIC_MONO_AUDIO_COPP 0x00010315 #define ADM_CMD_COPP_OPENOPOLOGY_ID_MIC_STEREO_AUDIO_COPP 0x00010316 #define AUDPROC_COPPOPOLOGY_ID_MCHAN_IIR_AUDIO 0x00010715 @@ -10362,6 +10432,45 @@ struct afe_param_id_clip_bank_sel { uint32_t bank_map[AFE_CLIP_MAX_BANKS]; } __packed; +/* SOMC effect start */ +/* Module/Parameter IDs */ +#define ASM_MODULE_ID_SONYBUNDLE 0x10002010 + +#define PARAM_ID_SB_COMMON_USER_PARAM 0x10002011 +#define PARAM_ID_SB_DYNAMICNORMALIZER_USER_PARAM 0x10002012 +#define PARAM_ID_SB_SFORCE_USER_PARAM 0x10002013 +#define PARAM_ID_SB_VPT20_USER_PARAM 0x10002014 +#define PARAM_ID_SB_CLEARPHASE_HP_USER_PARAM 0x10002015 +#define PARAM_ID_SB_CLEARAUDIO_USER_PARAM 0x10002016 +#define PARAM_ID_SB_CLEARAUDIO_VOLUME_PARAM 0x10002017 +#define PARAM_ID_SB_CLEARPHASE_SP_USER_PARAM 0x10002018 +#define PARAM_ID_SB_XLOUD_USER_PARAM 0x10002019 + +#define PARAM_ID_SB_CLEARPHASE_HP_TUNING 0x1000201A +#define PARAM_ID_SB_SFORCE_TUNING 0x1000201B +#define PARAM_ID_SB_CLEARPHASE_SP_TUNING 0x1000201C +#define PARAM_ID_SB_XLOUD_TUNING 0x1000201D + +#define ASM_STREAM_POSTPROC_TOPO_ID_SONY 0x10002101 + +struct clearphase_hp_tuning_params { + unsigned char coefs[2064]; +} __packed; + +struct s_force_tuning_params { + unsigned char coefs[1016]; +} __packed; + +struct clearphase_sp_tuning_params { + unsigned char coefs[2360]; +} __packed; + +struct xloud_tuning_params { + unsigned int level; + unsigned char coefs[512]; +} __packed; +/* SOMC effect end */ + /* ERROR CODES */ /* Success. The operation completed with no errors. */ #define ADSP_EOK 0x00000000 @@ -10626,7 +10735,7 @@ struct afe_clk_set { * for enable and disable clock. * "clk_freq_in_hz", "clk_attri", and "clk_root" * are ignored in disable clock case. - * @values  + * @values� * - 0 -- Disabled * - 1 -- Enabled @tablebulletend */ diff --git a/include/sound/jack.h b/include/sound/jack.h index 0d2a334fbeaaf9dce583e9a2440a06774fda29ac..0eaaf0323a94f2f17f5a8135d983c09a8847b6c5 100644 --- a/include/sound/jack.h +++ b/include/sound/jack.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __SOUND_JACK_H #define __SOUND_JACK_H @@ -64,7 +69,8 @@ enum snd_jack_types { SND_JACK_MICROPHONE2 = 0x0200, SND_JACK_ANC_HEADPHONE = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | SND_JACK_MICROPHONE2, - + SND_JACK_STEREO_MICROPHONE = SND_JACK_MICROPHONE | + SND_JACK_MICROPHONE2, /* Kept separate from switches to facilitate implementation */ SND_JACK_BTN_0 = 0x8000, SND_JACK_BTN_1 = 0x4000, diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h index f04daf310182b8880e10237a3b3e84541dd2b99a..3e0d957da8334912b8abe278ebf32af571bf56a2 100644 --- a/include/sound/q6adm-v2.h +++ b/include/sound/q6adm-v2.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __Q6_ADM_V2_H__ #define __Q6_ADM_V2_H__ @@ -94,6 +99,9 @@ struct msm_pcm_channel_mixer { int override_cfg; }; +int adm_matrix_mute(int port_id, int session_id, uint32_t ramp_duration, + uint32_t mute_flag_ch1, uint32_t mute_flag_ch2); + int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id, void *srs_params); @@ -118,10 +126,13 @@ int adm_send_params_v5(int port_id, int copp_idx, char *params, int adm_dolby_dap_send_params(int port_id, int copp_idx, char *params, uint32_t params_length); +int adm_ahc_send_params(int port_id, int copp_idx, char *params, + uint32_t params_length); + int adm_set_pp_params(int port_id, int copp_idx, struct mem_mapping_hdr *mem_hdr, u8 *param_data, u32 params_size); - + int adm_pack_and_set_one_pp_param(int port_id, int copp_idx, struct param_hdr_v3 param_hdr, u8 *param_data); diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h index 8af49bb6a9fa0c02c9bf42ae7b1f1c05841a30ed..b5bb0838337f1e73c67cfd3c86aa4d219db89a3f 100644 --- a/include/sound/q6asm-v2.h +++ b/include/sound/q6asm-v2.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __Q6_ASM_V2_H__ #define __Q6_ASM_V2_H__ @@ -737,4 +742,8 @@ int q6asm_adjust_session_clock(struct audio_client *ac, uint32_t adjust_time_lsw, uint32_t adjust_time_msw); int q6asm_get_svc_version(uint32_t service_id); + +/* SOMC added: Send tuning parameter for Sony effect*/ +int sony_hweffect_send_tuning_params(unsigned int effect_id, void *client); + #endif /* __Q6_ASM_H__ */ diff --git a/include/trace/events/kmem.h b/include/trace/events/kmem.h index ba8c415771b79972e11d9014ef6510d17ad59f9c..e9e7abf7df44f470d128356473a0f9fe2635f878 100644 --- a/include/trace/events/kmem.h +++ b/include/trace/events/kmem.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #undef TRACE_SYSTEM #define TRACE_SYSTEM kmem @@ -214,6 +219,34 @@ TRACE_EVENT(mm_page_free_batched, __entry->cold) ); +TRACE_EVENT(mm_page_alloc_highorder, + + TP_PROTO(struct page *page, unsigned int order, + gfp_t gfp_flags, int migratetype), + + TP_ARGS(page, order, gfp_flags, migratetype), + + TP_STRUCT__entry(__field(struct page *, page) + __field(unsigned int, order) + __field(gfp_t, gfp_flags) + __field(int, migratetype) + ), + + TP_fast_assign( + __entry->page = page; + __entry->order = order; + __entry->gfp_flags = gfp_flags; + __entry->migratetype = migratetype; + ), + + TP_printk("page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s", + __entry->page, + page_to_pfn(__entry->page), + __entry->order, + __entry->migratetype, + show_gfp_flags(__entry->gfp_flags)) +); + TRACE_EVENT(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, @@ -310,6 +343,28 @@ TRACE_EVENT_CONDITION(mm_page_pcpu_drain, __entry->order, __entry->migratetype) ); +TRACE_EVENT(mm_page_alloc_fail, + + TP_PROTO(int alloc_order, gfp_t gfp_mask), + + TP_ARGS(alloc_order, gfp_mask), + + TP_STRUCT__entry( + __field(int, alloc_order) + __field(gfp_t, gfp_mask) + ), + + TP_fast_assign( + __entry->alloc_order = alloc_order; + __entry->gfp_mask = gfp_mask; + ), + + TP_printk("alloc_order=%d pageblock_order=%d gfp_mask=%s", + __entry->alloc_order, + pageblock_order, + show_gfp_flags(__entry->gfp_mask)) +); + TRACE_EVENT(mm_page_alloc_extfrag, TP_PROTO(struct page *page, diff --git a/include/trace/events/lmk.h b/include/trace/events/lmk.h new file mode 100644 index 0000000000000000000000000000000000000000..c0c1d7fc884f666495a02a60486ee2c0000932dd --- /dev/null +++ b/include/trace/events/lmk.h @@ -0,0 +1,95 @@ +/* + * License terms: GNU General Public License (GPL) version 2 + * + * Author: Peter Enderborg + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM lmk + +#if !defined(_TRACE_LMK_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_LMK_H + +#include +#include + +DECLARE_EVENT_CLASS(lmk_kill, + TP_PROTO(int pid, + const char *comm, + int score, + int size, + int gfp_mask), + TP_ARGS(pid, comm, score, size, gfp_mask), + + TP_STRUCT__entry( + __field(int, pid) + __array(char, comm, TASK_COMM_LEN) + __field(int, score) + __field(int, size) + __field(int, gfp_mask) + ), + + TP_fast_assign( + __entry->pid = pid; + memcpy(__entry->comm, comm, TASK_COMM_LEN); + __entry->score = score; + __entry->size = size; + __entry->gfp_mask = gfp_mask; + ), + + TP_printk("pid=%d comm=%s score=%d size=%d gfp_mask=%d", + __entry->pid, __entry->comm, + __entry->score, __entry->size, + __entry->gfp_mask) + +); + +DEFINE_EVENT(lmk_kill, lmk_sigkill, + TP_PROTO(int pid, + const char *comm, + int score, + int size, + int gfp_mask), + + TP_ARGS(pid, comm, score, size, gfp_mask) +); + +DECLARE_EVENT_CLASS(lmk_remain, + TP_PROTO(int rem, int nr_to_scan, int gfp_mask), + TP_ARGS(rem, nr_to_scan, gfp_mask), + + TP_STRUCT__entry( + __field(int, rem) + __field(int, nr_to_scan) + __field(int, gfp_mask) + ), + + TP_fast_assign( + __entry->rem = rem; + __entry->nr_to_scan = nr_to_scan; + __entry->gfp_mask = gfp_mask; + ), + + TP_printk("rem=%d scan=%d gfp_mask=%d", + __entry->rem, __entry->nr_to_scan, __entry->gfp_mask) + +); + +DEFINE_EVENT(lmk_remain, lmk_remain_scan, + TP_PROTO(int rem, int nr_to_scan, int gfp_mask), + + TP_ARGS(rem, nr_to_scan, gfp_mask) +); + + +#endif /* _TRACE_LMK_H */ + +/* This part must be outside protection */ +#include diff --git a/include/trace/events/oom.h b/include/trace/events/oom.h index 1e974983757eb2e9974d0ac619ff8b8b27a491d5..fd65bba5587ae3e441a580a9712d61eec86560d3 100644 --- a/include/trace/events/oom.h +++ b/include/trace/events/oom.h @@ -27,6 +27,47 @@ TRACE_EVENT(oom_score_adj_update, __entry->pid, __entry->comm, __entry->oom_score_adj) ); +DECLARE_EVENT_CLASS(oom_kill, + TP_PROTO(int pid, + const char *comm, + int score, + unsigned long size, + int gfp_mask), + TP_ARGS(pid, comm, score, size, gfp_mask), + + TP_STRUCT__entry( + __field(int, pid) + __field(const char *, comm) + __field(int, score) + __field(unsigned long, size) + __field(int, gfp_mask) + ), + + TP_fast_assign( + __entry->pid = pid; + __entry->comm = comm; + __entry->score = score; + __entry->size = size; + __entry->gfp_mask = gfp_mask; + ), + + TP_printk("pid=%d comm=%s score=%d size=%ld gfp_mask=%d", + __entry->pid, __entry->comm, + __entry->score, __entry->size, + __entry->gfp_mask) + +); + +DEFINE_EVENT(oom_kill, oom_sigkill, + TP_PROTO(int pid, + const char *comm, + int score, + unsigned long size, + int gfp_mask), + + TP_ARGS(pid, comm, score, size, gfp_mask) +); + #endif /* This part must be outside protection */ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index c06237170542a346afbdc119b6e7b473e492f70a..63284c85a041d0b61edea00baa4df92dadc75efd 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -526,6 +526,7 @@ header-y += msm_dsps.h header-y += msm-core-interface.h header-y += msm_thermal_ioctl.h header-y += android_pmem.h +header-y += tof_sensor.h header-y += ipa_qmi_service_v01.h header-y += rmnet_ipa_fd_ioctl.h header-y += msm_ipa.h diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 88956276c8ab95036241543381c5eba13a8605d9..68e8242fdc65eb6df9c8a61e8cd67376f7b84758 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -12,6 +12,11 @@ * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _UAPI_INPUT_EVENT_CODES_H #define _UAPI_INPUT_EVENT_CODES_H @@ -749,10 +754,11 @@ #define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ #define SW_LINEIN_INSERT 0x0d /* set = inserted */ #define SW_MUTE_DEVICE 0x0e /* set = device disabled */ -#define SW_HPHL_OVERCURRENT 0x0f /* set = over current on left hph */ -#define SW_HPHR_OVERCURRENT 0x10 /* set = over current on right hph */ -#define SW_MICROPHONE2_INSERT 0x11 /* set = inserted */ -#define SW_UNSUPPORT_INSERT 0x12 /* set = unsupported device inserted */ +#define SW_PEN_INSERTED 0x0f /* set = pen inserted */ +#define SW_HPHL_OVERCURRENT 0x10 /* set = over current on left hph */ +#define SW_HPHR_OVERCURRENT 0x11 /* set = over current on right hph */ +#define SW_MICROPHONE2_INSERT 0x12 /* set = inserted */ +#define SW_UNSUPPORT_INSERT 0x13 /* set = unsupported device inserted */ #define SW_MAX 0x20 #define SW_CNT (SW_MAX+1) diff --git a/include/uapi/linux/kernel-page-flags.h b/include/uapi/linux/kernel-page-flags.h index 5da5f8751ce7dc082a4a99cfe29e7d2eb7f6ba5a..1692fde58c2669609dfdfb895c60d78aa8adeca3 100644 --- a/include/uapi/linux/kernel-page-flags.h +++ b/include/uapi/linux/kernel-page-flags.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _UAPILINUX_KERNEL_PAGE_FLAGS_H #define _UAPILINUX_KERNEL_PAGE_FLAGS_H diff --git a/include/uapi/linux/msdos_fs.h b/include/uapi/linux/msdos_fs.h index e956704f5fb1b24ceba1b0157c8d37cf87ee3d15..5bd6dec4ff13218ca0beee44be4e733025293fd0 100644 --- a/include/uapi/linux/msdos_fs.h +++ b/include/uapi/linux/msdos_fs.h @@ -45,6 +45,7 @@ #define CASE_LOWER_BASE 8 /* base is lower case */ #define CASE_LOWER_EXT 16 /* extension is lower case */ +#define FAT_NO_83NAME 32 /* no 8.3 short filename for this file */ #define DELETED_FLAG 0xe5 /* marks file as deleted when in name[0] */ #define IS_FREE(n) (!*(n) || *(n) == DELETED_FLAG) diff --git a/include/uapi/linux/msm_mdp.h b/include/uapi/linux/msm_mdp.h index 481814cb8498ea10260c37d3efb754ae76f8c846..e738d83ac01d98721260cfb8733839b23e1992c9 100644 --- a/include/uapi/linux/msm_mdp.h +++ b/include/uapi/linux/msm_mdp.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _UAPI_MSM_MDP_H_ #define _UAPI_MSM_MDP_H_ @@ -1120,7 +1125,7 @@ enum { #define MDSS_PP_SPLIT_RIGHT_ONLY 0x20000000 #define MDSS_PP_SPLIT_MASK 0x30000000 -#define MDSS_MAX_BL_BRIGHTNESS 255 +#define MDSS_MAX_BL_BRIGHTNESS 4095 #define AD_BL_LIN_LEN 256 #define AD_BL_ATT_LUT_LEN 33 diff --git a/include/uapi/media/msm_cam_sensor.h b/include/uapi/media/msm_cam_sensor.h index e4b4554dd6908c24aff3a54d0019962d1a1d6a97..8883b3c4c887dff813ddee1ec30b991b51b5f6e0 100644 --- a/include/uapi/media/msm_cam_sensor.h +++ b/include/uapi/media/msm_cam_sensor.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __UAPI_LINUX_MSM_CAM_SENSOR_H #define __UAPI_LINUX_MSM_CAM_SENSOR_H @@ -582,6 +587,16 @@ struct sensor_init_cfg_data { } cfg; }; +/* extension begin */ +#define SENSOR_EVENT_BASE (V4L2_EVENT_PRIVATE_START) +#define SENSOR_EVENT_SOF (SENSOR_EVENT_BASE + 0) + +struct msm_sensor_event_data { + uint32_t sof_count; + struct timeval mono_timestamp; +}; +/* extension end */ + #define VIDIOC_MSM_SENSOR_CFG \ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct sensorb_cfg_data) diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h index 40731a927ce58201098af2f47b2e8b3c9f1de3d2..e211785d91db675aaf8218a1965e7377c75a3ad8 100644 --- a/include/uapi/media/msm_camsensor_sdk.h +++ b/include/uapi/media/msm_camsensor_sdk.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __UAPI_LINUX_MSM_CAMSENSOR_SDK_H #define __UAPI_LINUX_MSM_CAMSENSOR_SDK_H @@ -380,6 +385,9 @@ struct msm_camera_i2c_seq_reg_array { }; struct msm_camera_i2c_seq_reg_setting { +/* extension begin */ + unsigned short slave_addr; +/* extension end */ struct msm_camera_i2c_seq_reg_array *reg_setting; unsigned short size; enum msm_camera_i2c_reg_addr_type addr_type; diff --git a/include/uapi/scsi/ufs/ioctl.h b/include/uapi/scsi/ufs/ioctl.h index 56b2f4616aa44782841855bad87bd12796ae73c1..028309d9df4ef207752c777eb1fdce39b9a7cde4 100644 --- a/include/uapi/scsi/ufs/ioctl.h +++ b/include/uapi/scsi/ufs/ioctl.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef UAPI_UFS_IOCTL_H_ #define UAPI_UFS_IOCTL_H_ @@ -8,6 +13,7 @@ * SCSI_IOCTL_GET_PCI */ #define UFS_IOCTL_QUERY 0x5388 +#define UFS_IOCTL_WRITE_BUFFER 0x53EF /** * struct ufs_ioctl_query_data - used to transfer data to and from user via ioctl @@ -54,4 +60,9 @@ struct ufs_ioctl_query_data { __u8 buffer[0]; }; +struct ufs_ioctl_write_buffer_data { + __u32 buf_size; + __u8 buffer[0]; +}; + #endif /* UAPI_UFS_IOCTL_H_ */ diff --git a/include/uapi/scsi/ufs/ufs.h b/include/uapi/scsi/ufs/ufs.h index cd82b760bd92cbaec15f4182d71b180fe936a7d7..59ea205d3b9f7e9661362d204c39e64d56af1bf9 100644 --- a/include/uapi/scsi/ufs/ufs.h +++ b/include/uapi/scsi/ufs/ufs.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef UAPI_UFS_H_ #define UAPI_UFS_H_ @@ -36,6 +41,7 @@ enum attr_idn { QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F, QUERY_ATTR_IDN_CNTX_CONF = 0x10, QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11, + QUERY_ATTR_IDN_FFU_STATUS = 0x14, }; #define QUERY_ATTR_IDN_BOOT_LU_EN_MAX 0x02 @@ -51,7 +57,7 @@ enum desc_idn { QUERY_DESC_IDN_RFU_1 = 0x6, QUERY_DESC_IDN_GEOMETRY = 0x7, QUERY_DESC_IDN_POWER = 0x8, - QUERY_DESC_IDN_RFU_2 = 0x9, + QUERY_DESC_IDN_DEVICE_HEALTH = 0x9, QUERY_DESC_IDN_MAX, }; @@ -68,4 +74,22 @@ enum query_opcode { UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8, UPIU_QUERY_OPCODE_MAX, }; + +enum purge_status { + PURGE_STATUS_IDLE = 0x00, + PURGE_STATUS_IN_PROGRESS = 0x01, + PURGE_STATUS_STOPPED = 0x02, + PURGE_STATUS_COMPLETED = 0x03, + PURGE_STATUS_BUSY = 0x04, + PURGE_STATUS_GENERAL_ERROR = 0x05, +}; + +enum ffu_status { + FFU_STATUS_NO_INFOMATION = 0x00, + FFU_STATUS_SUCCESS = 0x01, + FFU_STATUS_CORRUPTION_ERROR = 0x02, + FFU_STATUS_INTERNAL_ERROR = 0x03, + FFU_STATUS_VERSION_MISMATCH = 0x04, + FFU_STATUS_GENERAL_ERROR = 0xFF, +}; #endif /* UAPI_UFS_H_ */ diff --git a/include/uapi/sound/Kbuild b/include/uapi/sound/Kbuild index a19a02471367008150b5efe05194e8a61f60d64a..42083b6f7b8d65bcb8e1f58ce9fdfda82b687e31 100644 --- a/include/uapi/sound/Kbuild +++ b/include/uapi/sound/Kbuild @@ -19,3 +19,5 @@ header-y += voice_svc.h header-y += devdep_params.h header-y += msmcal-hwdep.h header-y += wcd-dsp-glink.h +header-y += sony-hweffect.h +header-y += sony-hweffect-params.h diff --git a/include/uapi/sound/msmcal-hwdep.h b/include/uapi/sound/msmcal-hwdep.h index 2a294824fb0071b537787a85c158eed47d1c550c..c59c1165391b9c7f1381d5e078bba83b07d21dd9 100644 --- a/include/uapi/sound/msmcal-hwdep.h +++ b/include/uapi/sound/msmcal-hwdep.h @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _CALIB_HWDEP_H #define _CALIB_HWDEP_H @@ -31,5 +36,7 @@ struct wcdcal_ioctl_buffer { #define SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE \ _IOW('U', 0x1, struct wcdcal_ioctl_buffer) +#define SNDRV_CTL_IOCTL_HWDEP_IS_ANC_INSERT \ + _IOWR('U', 0x2, int) #endif /*_CALIB_HWDEP_H*/ diff --git a/include/uapi/sound/sony-hweffect-params.h b/include/uapi/sound/sony-hweffect-params.h new file mode 100644 index 0000000000000000000000000000000000000000..bff519aebb5ac20b9041188a956fa3a70390a483 --- /dev/null +++ b/include/uapi/sound/sony-hweffect-params.h @@ -0,0 +1,66 @@ +/* + * Author: Yoshio Yamamoto yoshio.xa.yamamoto@sonymobile.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; either version 2 + * of the License, or (at your option) any later version. + */ +/* + * Copyright (C) 2014 Sony Mobile Communications 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 __SONY_HWEFFECT_PARAMS_H +#define __SONY_HWEFFECT_PARAMS_H + +#define SONYEFFECT_HW_PARAMS_IOCTL_MAGIC 't' + +#define SFORCE_PARAM \ + _IOW(SONYEFFECT_HW_PARAMS_IOCTL_MAGIC, 0, unsigned) + +#define CLEARPHASE_HP_PARAM \ + _IOW(SONYEFFECT_HW_PARAMS_IOCTL_MAGIC, 1, unsigned) + +#define CLEARPHASE_SP_PARAM \ + _IOW(SONYEFFECT_HW_PARAMS_IOCTL_MAGIC, 2, unsigned) + +#define XLOUD_PARAM \ + _IOW(SONYEFFECT_HW_PARAMS_IOCTL_MAGIC, 3, unsigned) + +#define SFORCE_PARAM_SIZE 1016 +#define CLEARPHASE_HP_PARAM_SIZE 2064 +#define CLEARPHASE_SP_PARAM_SIZE 2360 +#define XLOUD_PARAM_SIZE 512 + +enum { + SFORCE_TYPE_MUSIC = 0, + SFORCE_TYPE_VIDEO, + SFORCE_TYPE_MAX +}; + +struct sforce_param_data { + unsigned char data[SFORCE_PARAM_SIZE]; +}; + +struct clearphase_hp_param_data { + unsigned char data[CLEARPHASE_HP_PARAM_SIZE]; +}; + +struct clearphase_sp_param_data { + unsigned char data[CLEARPHASE_SP_PARAM_SIZE]; +}; + +struct xloud_param_data { + unsigned int level; + unsigned char data[XLOUD_PARAM_SIZE]; +}; + +void *sony_hweffect_params_getparam(unsigned int effect_id); + +int sony_hweffect_params_getparam_size(long *values); + +#endif diff --git a/include/uapi/sound/sony-hweffect.h b/include/uapi/sound/sony-hweffect.h new file mode 100644 index 0000000000000000000000000000000000000000..f09b40cc747b9c783975c96bfff778a0b25d7c03 --- /dev/null +++ b/include/uapi/sound/sony-hweffect.h @@ -0,0 +1,177 @@ +/* + * Author: Yoshio Yamamoto yoshio.xa.yamamoto@sonymobile.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; either version 2 + * of the License, or (at your option) any later version. + */ +/* + * Copyright (C) 2014 Sony Mobile Communications 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 _SONY_HWEFFECT_H +#define _SONY_HWEFFECT_H + +/** SONY HW EFFECT **/ + +/* CONFIG GET/SET */ +#define CONFIG_CACHE 0 +#define CONFIG_SET 1 +#define CONFIG_GET 2 + +/* CONFIG HEADER */ +/* + + MODULE_ID, + DEVICE, + NUM_COMMANDS, + COMMAND_ID_1, + CONFIG_CACHE/SET/GET, + OFFSET_1, + LENGTH_1, + VALUES_1, + ..., + ..., + COMMAND_ID_2, + CONFIG_CACHE/SET/GET, + OFFSET_2, + LENGTH_2, + VALUES_2, + ..., + ..., + COMMAND_ID_3, + ... +*/ + +#define BAND_NUM 6 + +/* CONFIG PARAM IDs */ +#define SONYBUNDLE_MODULE 0x00011000 + +#define SONYBUNDLE_ENABLE 0x00011001 + +#define DYNAMIC_NORMALIZER_ENABLE 0x00011011 + +#define SFORCE_ENABLE 0x00011021 +#define SFORCE_PARAM_TYPE 0x00011022 + +#define VPT20_MODE 0x00011031 + +#define CLEARPHASE_HP_MODE 0x00011041 + +#define CLEARAUDIO_CHSEP 0x00011051 +#define CLEARAUDIO_EQ_COEF 0x00011052 +#define CLEARAUDIO_VOLUME 0x00011053 + +#define CLEARPHASE_SP_ENABLE 0x00011061 + +#define XLOUD_ENABLE 0x00011071 + +#define DOWN_CONVERT 0x00011081 + +#define SONYBUNDLE_ENABLE_PARAM_LEN 1 +#define DYNAMIC_NORMALIZER_ENABLE_PARAM_LEN 1 +#define SFORCE_ENABLE_PARAM_LEN 1 +#define VPT20_MODE_PARAM_LEN 1 +#define CLEARPHASE_HP_MODE_PARAM_LEN 1 +#define CLEARAUDIO_CHSEP_PARAM_LEN 1 +#define CLEARAUDIO_EQ_COEF_PARAM_LEN 6 +#define CLEARAUDIO_VOLUME_PARAM_LEN 1 +#define CLEARPHASE_SP_ENABLE_PARAM_LEN 1 +#define XLOUD_ENABLE_PARAM_LEN 1 +#define DOWN_CONVERT_PARAM_LEN 1 + +#define COMMAND_PAYLOAD_LEN 3 +#define COMMAND_PAYLOAD_SZ (COMMAND_PAYLOAD_LEN * sizeof(uint32_t)) +#define MAX_INBAND_PARAM_SZ 4096 + +#define SONYBUNDLE_ENABLE_PARAM_SZ \ + (SONYBUNDLE_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define DYNAMIC_NORMALIZER_ENABLE_PARAM_SZ \ + (DYNAMIC_NORMALIZER_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define SFORCE_ENABLE_PARAM_SZ \ + (SFORCE_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define VPT20_MODE_PARAM_SZ \ + (VPT20_MODE_PARAM_LEN*sizeof(uint32_t)) +#define CLEARPHASE_HP_MODE_PARAM_SZ \ + (CLEARPHASE_HP_MODE_PARAM_LEN*sizeof(uint32_t)) +#define CLEARAUDIO_CHSEP_PARAM_SZ \ + (CLEARAUDIO_CHSEP_PARAM_LEN*sizeof(uint32_t)) +#define CLEARAUDIO_EQ_COEF_PARAM_SZ \ + (CLEARAUDIO_EQ_COEF_PARAM_LEN*sizeof(int16_t)) +#define CLEARAUDIO_VOLUME_PARAM_SZ \ + (CLEARAUDIO_VOLUME_PARAM_LEN*sizeof(int32_t)) +#define CLEARPHASE_SP_ENABLE_PARAM_SZ \ + (CLEARPHASE_SP_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define XLOUD_ENABLE_PARAM_SZ \ + (XLOUD_ENABLE_PARAM_LEN*sizeof(uint32_t)) +#define DOWN_CONVERT_PARAM_SZ \ + (DOWN_CONVERT_PARAM_LEN*sizeof(uint32_t)) + +struct common_params { + uint16_t enable_flag; + uint16_t reserved; +}; + +struct dynamic_normalizer_params { + uint16_t enable_flag; + uint16_t reserved; +}; + +struct sforce_params { + uint16_t enable_flag; + uint16_t reserved; +}; + +struct vpt20_params { + uint16_t mode; + uint16_t reserved; +}; + +struct clearphase_hp_params { + uint16_t mode; + uint16_t reserved; +}; + +struct clearaudio_params { + int32_t chsep_coef; + int16_t eq_coef[6]; +}; + +struct clearaudio_volume { + uint16_t gain; + uint16_t reserved; +}; + +struct clearphase_sp_params { + uint16_t mode; + uint16_t reserved; +}; + +struct xloud_params { + uint16_t enable_flag; + uint16_t reserved; +}; + +struct sonybundle_params { + struct common_params common; + struct dynamic_normalizer_params dynamic_normalizer; + struct sforce_params sforce; + bool sforce_tuning_update; + struct vpt20_params vpt20; + struct clearphase_hp_params clearphase_hp; + struct clearaudio_params clearaudio; + struct clearaudio_volume ca_volume; + struct clearphase_sp_params clearphase_sp; + bool clearphase_sp_tuning_update; + struct xloud_params xloud; + bool xloud_tuning_update; + uint16_t down_convert_enable; +}; + +#endif /*_SONYEFFECT_HW_H */ diff --git a/init/initramfs.c b/init/initramfs.c index 52059169f64d23b258ffd5cd144e9ad6a10ae5f6..ddb856e1e987ea7c43a74b80e55f55b15d48bc5a 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -3,6 +3,11 @@ * to be __user pointers not __kernel pointers. To limit the sparse * noise, turn off sparse checking for this file. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifdef __CHECKER__ #undef __CHECKER__ #warning "Sparse checking disabled for this file" @@ -636,6 +641,8 @@ static int __init populate_rootfs(void) printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n"); err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start); + if (err) + panic("panic in %s()", __func__); if (!err) { free_initrd(); goto done; diff --git a/kernel/Makefile b/kernel/Makefile index 2dea801370f2e4477d5c1c93d131ac3290f60a97..e29fd0db5cfc4ad94924f29e482968b5dc4c025b 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_SYSCTL) += utsname_sysctl.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o obj-$(CONFIG_TRACEPOINTS) += tracepoint.o +obj-$(CONFIG_OOM_SCORE_NOTIFIER) += oom_score_notifier.o obj-$(CONFIG_LATENCYTOP) += latencytop.o obj-$(CONFIG_BINFMT_ELF) += elfcore.o obj-$(CONFIG_COMPAT_BINFMT_ELF) += elfcore.o @@ -115,6 +116,7 @@ obj-$(CONFIG_TORTURE_TEST) += torture.o obj-$(CONFIG_MEMBARRIER) += membarrier.o obj-$(CONFIG_HAS_IOMEM) += memremap.o +obj-$(CONFIG_CRASH_NOTES) += crash_notes.o $(obj)/configs.o: $(obj)/config_data.h diff --git a/kernel/crash_notes.c b/kernel/crash_notes.c new file mode 100644 index 0000000000000000000000000000000000000000..a8c1206ee05fd8c975bfe5eb2fa9947f91f5725c --- /dev/null +++ b/kernel/crash_notes.c @@ -0,0 +1,187 @@ +/* kernel/crash_notes.c + * + * 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CRASH_NOTE_NAME "CORE" + +#define CRASH_NOTE_SIZE (ALIGN(sizeof(struct elf_note), 4) + \ + ALIGN(sizeof(CRASH_NOTE_NAME), 4) + \ + ALIGN(sizeof(struct elf_prstatus), 4)) + +#define CRASH_NOTE_BYTES (2 * CRASH_NOTE_SIZE) + +typedef u32 note_buf_t[CRASH_NOTE_BYTES / 4]; +note_buf_t *crash_notes; + +static atomic_t crash_notes_ipi; + +static inline void crash_notes_save_this_cpu(enum crash_note_save_type type, + unsigned int cpu) +{ + struct elf_prstatus prstatus; + struct pt_regs regs; + struct elf_note *note; + u32 *buf; + u32 *start; + + buf = (u32 *)per_cpu_ptr(crash_notes, cpu); + if (!buf) + return; + + start = buf; + memset(&prstatus, 0, sizeof(prstatus)); + prstatus.pr_pid = current->pid; + + if (type != CRASH_NOTE_INIT) { + crash_notes_save_regs(®s); + elf_core_copy_regs(&prstatus.pr_reg, ®s); + } + + note = (struct elf_note *)buf; + note->n_namesz = strnlen(CRASH_NOTE_NAME, 5) + 1; + note->n_descsz = sizeof(prstatus); + note->n_type = NT_PRSTATUS; + buf += (sizeof(struct elf_note) + 3) / 4; + memcpy(buf, CRASH_NOTE_NAME, note->n_namesz); + buf += (note->n_namesz + 3) / 4; + memcpy(buf, &prstatus, sizeof(prstatus)); + buf += (note->n_descsz + 3) / 4; + + note = (struct elf_note *)buf; + note->n_namesz = 0; + note->n_descsz = 0; + note->n_type = 0; + + flush_cache_all(); +} + +static DEFINE_RAW_SPINLOCK(print_trace_lock); + +static void crash_notes_save_nopanic_cpu(void *unused) +{ + unsigned int cpu = smp_processor_id(); + + raw_spin_lock(&print_trace_lock); + pr_crit("CPU%u: stopping\n", cpu); + dump_stack(); + raw_spin_unlock(&print_trace_lock); + + crash_notes_save_this_cpu(CRASH_NOTE_STOPPING, cpu); + atomic_dec(&crash_notes_ipi); + + set_cpu_active(cpu, false); + flush_cache_all(); + + while (1) + cpu_relax(); +} + +void crash_notes_save_cpus(void) +{ + unsigned long msecs; + + local_irq_disable(); + + atomic_set(&crash_notes_ipi, num_online_cpus() - 1); + smp_call_function(crash_notes_save_nopanic_cpu, NULL, false); + msecs = 1000; + while ((atomic_read(&crash_notes_ipi) > 0) && msecs) { + mdelay(1); + msecs--; + } + + if (atomic_read(&crash_notes_ipi) > 0) + pr_warn("Non-crashing CPUs did not react to IPI\n"); +} +EXPORT_SYMBOL(crash_notes_save_cpus); + +static int crash_notes_save_panic_cpu(struct notifier_block *this, + unsigned long event, void *ptr) +{ + long i; + + crash_notes_save_this_cpu(CRASH_NOTE_CRASHING, smp_processor_id()); + + bust_spinlocks(0); + + if (panic_timeout > 0) { + /* + * Delay timeout seconds before rebooting the machine. + * We can't use the "normal" timers since we just panicked. + */ + pr_emerg("Rebooting in %d seconds..", panic_timeout); + + for (i = 0; i < panic_timeout; i++) { + touch_nmi_watchdog(); + mdelay(MSEC_PER_SEC); + } + /* + * This will not be a clean reboot, with everything + * shutting down. But if there is a chance of + * rebooting the system it will be rebooted. + */ + emergency_restart(); + } + + local_irq_enable(); + while (1) { + touch_softlockup_watchdog(); + mdelay(MSEC_PER_SEC); + } + + return NOTIFY_DONE; +} + +static struct notifier_block panic_block = { + .notifier_call = crash_notes_save_panic_cpu, + .priority = INT_MIN /* will not return; must be done last */ +}; + +static int __init crash_notes_init(void) +{ + int i; + + /* Allocate memory for saving cpu registers. */ + crash_notes = alloc_percpu(note_buf_t); + if (!crash_notes) { + pr_err("allocation of percpu crash notes failed\n"); + return -ENOMEM; + } + + /* Initialize memory with something that the tools pick up on */ + /* It will NOT be useful register info, but it's something at least */ + for_each_possible_cpu(i) { + crash_notes_save_this_cpu(CRASH_NOTE_INIT, i); + } + + atomic_notifier_chain_register(&panic_notifier_list, &panic_block); + + return 0; +} +module_init(crash_notes_init) diff --git a/kernel/exit.c b/kernel/exit.c index fc82e495b7299483b54cb5ff07e53b15ed745033..7d42bb359b9e14302473c96b083ef9c2b84d74a8 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -3,6 +3,11 @@ * * Copyright (C) 1991, 1992 Linus Torvalds */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -54,6 +59,7 @@ #include #include #include +#include #include "sched/tune.h" @@ -96,6 +102,8 @@ static void __exit_signal(struct task_struct *tsk) spin_lock(&sighand->siglock); posix_cpu_timers_exit(tsk); + oom_score_notify_free(tsk); + if (group_dead) { posix_cpu_timers_exit_group(tsk); tty = sig->tty; diff --git a/kernel/fork.c b/kernel/fork.c index caa23ca489bb6586a7971e971de57c7c852597ce..712e3e34ca5b946cab4180bd8f598a56be77c6ef 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -3,6 +3,11 @@ * * Copyright (C) 1991, 1992 Linus Torvalds */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * 'fork.c' contains the help-routines for the 'fork' system call @@ -75,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -1644,6 +1650,10 @@ static struct task_struct *copy_process(unsigned long clone_flags, init_task_pid(p, PIDTYPE_PID, pid); if (thread_group_leader(p)) { + retval = oom_score_notify_new(p); + if (retval) + goto bad_fork_cancel_cgroup; + init_task_pid(p, PIDTYPE_PGID, task_pgrp(current)); init_task_pid(p, PIDTYPE_SID, task_session(current)); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index ec72aeb6defe709029c1060b6b524b60c2abbae2..f23fe9b1ff728a46e6b5cf94ea9ff9048e08b059 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -6,6 +6,11 @@ * * This file contains driver APIs to the irq subsystem. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "genirq: " fmt diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c index c61c56f05dfa3d829604f48fd5efed7823eb7d9f..6f76c9e41258df1dc458977a59bd95a52700645f 100644 --- a/kernel/locking/mutex.c +++ b/kernel/locking/mutex.c @@ -17,6 +17,11 @@ * * Also see Documentation/locking/mutex-design.txt. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -26,7 +31,6 @@ #include #include #include -#include /* * In the DEBUG case we are using the "NULL fastpath" for mutexes, @@ -379,17 +383,6 @@ static bool mutex_optimistic_spin(struct mutex *lock, * values at the cost of a few extra spins. */ cpu_relax_lowlatency(); - - /* - * On arm systems, we must slow down the waiter's repeated - * aquisition of spin_mlock and atomics on the lock count, or - * we risk starving out a thread attempting to release the - * mutex. The mutex slowpath release must take spin lock - * wait_lock. This spin lock can share a monitor with the - * other waiter atomics in the mutex data structure, so must - * take care to rate limit the waiters. - */ - udelay(1); } osq_unlock(&lock->osq); @@ -925,6 +918,27 @@ int __sched mutex_trylock(struct mutex *lock) } EXPORT_SYMBOL(mutex_trylock); +int __sched mutex_trylock_spin(struct mutex *lock) +{ + int ret; + + ret = __mutex_fastpath_trylock(&lock->count, + __mutex_trylock_slowpath); + + if (!ret) { + preempt_disable(); + ret = mutex_optimistic_spin(lock, NULL, 0); + if (ret) + mutex_acquire(&lock->dep_map, 0, 1, _RET_IP_); + preempt_enable(); + } + if (ret) + mutex_set_owner(lock); + + return ret; +} +EXPORT_SYMBOL(mutex_trylock_spin); + #ifndef CONFIG_DEBUG_LOCK_ALLOC int __sched __ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) diff --git a/kernel/oom_score_notifier.c b/kernel/oom_score_notifier.c new file mode 100644 index 0000000000000000000000000000000000000000..8b039d38d0ef6a68334cb700909b542e846a3f4b --- /dev/null +++ b/kernel/oom_score_notifier.c @@ -0,0 +1,67 @@ +/* + * oom_score_notifier interface + * + * Author: Peter Enderborg + * + * 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. + */ +/* + * Copyright (C) 2017 Sony Mobile Communications 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. + */ + + +#include +#include + +#ifdef CONFIG_OOM_SCORE_NOTIFIER +ATOMIC_NOTIFIER_HEAD(oom_score_notifier); + +int oom_score_notifier_register(struct notifier_block *n) +{ + return atomic_notifier_chain_register(&oom_score_notifier, n); +} +EXPORT_SYMBOL_GPL(oom_score_notifier_register); + +int oom_score_notifier_unregister(struct notifier_block *n) +{ + return atomic_notifier_chain_unregister(&oom_score_notifier, n); +} +EXPORT_SYMBOL_GPL(oom_score_notifier_unregister); + +int oom_score_notify_free(struct task_struct *tsk) +{ + struct oom_score_notifier_struct osns; + + osns.tsk = tsk; + return notifier_to_errno(atomic_notifier_call_chain( + &oom_score_notifier, OSN_FREE, &osns)); +} +EXPORT_SYMBOL_GPL(oom_score_notify_free); + +int oom_score_notify_new(struct task_struct *tsk) +{ + struct oom_score_notifier_struct osns; + + osns.tsk = tsk; + return notifier_to_errno(atomic_notifier_call_chain( + &oom_score_notifier, OSN_NEW, &osns)); +} +EXPORT_SYMBOL_GPL(oom_score_notify_new); + +int oom_score_notify_update(struct task_struct *tsk, int old_score) +{ + struct oom_score_notifier_struct osns; + + osns.tsk = tsk; + osns.old_score = old_score; + return notifier_to_errno(atomic_notifier_call_chain(&oom_score_notifier, + OSN_UPDATE, &osns)); +} +EXPORT_SYMBOL_GPL(oom_score_notify_update); +#endif diff --git a/kernel/panic.c b/kernel/panic.c index 75f564a94a825f9391b4cdf776bc40eb739e912c..433f658947ebbc112930544ff2dac4f765327ab7 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -3,6 +3,11 @@ * * Copyright (C) 1991, 1992 Linus Torvalds */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * This function is used through-out the kernel (including mm and fs) @@ -25,6 +30,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -128,6 +134,9 @@ void panic(const char *fmt, ...) if (!crash_kexec_post_notifiers) crash_kexec(NULL); + /* Store crash context for all other no panic cpus */ + crash_notes_save_cpus(); + /* * Note smp_send_stop is the usual smp shutdown function, which * unfortunately means it may not be hardened to work in a panic diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 6d6f63be1f9b487a6c19c3d5210c6ec703a19216..ffa118ca86a8542c4e0111d1195f868e7bb6ddaf 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -210,6 +210,18 @@ config DPM_WATCHDOG_TIMEOUT default 60 depends on DPM_WATCHDOG +config PM_WAKEUP_TIMES + bool "Log time taken for device wakeup" + def_bool y + depends on PM && PM_SLEEP && DEBUG_FS + ---help--- + This enables code that calculates the total time spent for device + suspend and resume. These statistics are then logged to + /suspend_stats. + + The statistics include minimum, maximum, average and the most recent + times for resume and suspend. + config PM_TRACE bool help @@ -321,3 +333,10 @@ config PM_GENERIC_DOMAINS_OF config CPU_PM bool + +config WAKEUP_IRQ_DEBUG + bool "Debug info of wakeup irq" + default n + ---help--- + This enables debug information about wakeup interrupts using + sysfs (/sys/kernel/). diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 22eb9ed879ade88b5f427728b07e41742aabb486..8047d31590c2daf157b1877602a983f58738f4de 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o obj-$(CONFIG_SUSPEND) += wakeup_reason.o +obj-$(CONFIG_WAKEUP_IRQ_DEBUG) += wakeup_irq_debug.o diff --git a/kernel/power/main.c b/kernel/power/main.c index 5ea50b1b7595ba778eaaf90de9c3af51586156af..667a29a530e16bf90e7ce4684e97d80d1f3de162 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -7,6 +7,11 @@ * This file is released under the GPLv2 * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -15,12 +20,48 @@ #include #include #include +#include +#ifdef CONFIG_PM_WAKEUP_TIMES +#include +#endif #include "power.h" DEFINE_MUTEX(pm_mutex); #ifdef CONFIG_PM_SLEEP +/* + * Enable/Disable suspend back off logic attribute + */ +static bool sbo_enabled = true; + +/* + * For how many percent an alive time is decayed + * if suspend back-off keeps ongoing. + */ +static u32 sbo_decay_value = 20; + +/* + * Initial max back-off alive time + */ +static u32 sbo_initial_alive_time_msecs = 10000; + +/* + * A time in milliseconds, before which a short + * sleep counter is increased to trigger a back-off. + */ +static u32 sbo_short_sleep_msecs = 1100; + +/* + * This variable regulates starting of suspend + * back off functionality, after counter is reached + * specified value. + */ +static u32 sbo_short_sleep_count = 10; + +static u32 decay_alive_time_ms; +static u32 suspend_short_count; +static struct wakeup_source *ws; /* Routines for PM-transition notifications */ @@ -215,6 +256,51 @@ static int suspend_stats_show(struct seq_file *s, void *unused) suspend_stats.failed_steps[index])); } +#ifdef CONFIG_PM_WAKEUP_TIMES + seq_printf(s, "%s\n%s %lldms (%s %lldms %s %lldms)\n" \ + "%s %lldms (%s %lldms %s %lldms)\n" \ + "%s %lldms (%s %lldms %s %lldms)\n" \ + "%s %lldms\n", + "suspend time:", + " min:", ktime_to_ms(ktime_sub( + suspend_stats.suspend_min_time.end, + suspend_stats.suspend_min_time.start)), + "start:", ktime_to_ms(suspend_stats.suspend_min_time.start), + "end:", ktime_to_ms(suspend_stats.suspend_min_time.end), + " max:", ktime_to_ms(ktime_sub( + suspend_stats.suspend_max_time.end, + suspend_stats.suspend_max_time.start)), + "start:", ktime_to_ms(suspend_stats.suspend_max_time.start), + "end:", ktime_to_ms(suspend_stats.suspend_max_time.end), + " last:", ktime_to_ms(ktime_sub( + suspend_stats.suspend_last_time.end, + suspend_stats.suspend_last_time.start)), + "start:", ktime_to_ms(suspend_stats.suspend_last_time.start), + "end:", ktime_to_ms(suspend_stats.suspend_last_time.end), + " avg:", ktime_to_ms(suspend_stats.suspend_avg_time)); + + seq_printf(s, "%s\n%s %lldms (%s %lldms %s %lldms)\n" \ + "%s %lldms (%s %lldms %s %lldms)\n" \ + "%s %lldms (%s %lldms %s %lldms)\n" \ + "%s %lldms\n", + "resume time:", + " min:", ktime_to_ms(ktime_sub( + suspend_stats.resume_min_time.end, + suspend_stats.resume_min_time.start)), + "start:", ktime_to_ms(suspend_stats.resume_min_time.start), + "end:", ktime_to_ms(suspend_stats.resume_min_time.end), + " max:", ktime_to_ms(ktime_sub( + suspend_stats.resume_max_time.end, + suspend_stats.resume_max_time.start)), + "start:", ktime_to_ms(suspend_stats.resume_max_time.start), + "end:", ktime_to_ms(suspend_stats.resume_max_time.end), + " last:", ktime_to_ms(ktime_sub( + suspend_stats.resume_last_time.end, + suspend_stats.resume_last_time.start)), + "start:", ktime_to_ms(suspend_stats.resume_last_time.start), + "end:", ktime_to_ms(suspend_stats.resume_last_time.end), + " avg:", ktime_to_ms(suspend_stats.resume_avg_time)); +#endif return 0; } @@ -223,10 +309,29 @@ static int suspend_stats_open(struct inode *inode, struct file *file) return single_open(file, suspend_stats_show, NULL); } +#ifdef CONFIG_PM_WAKEUP_TIMES +static unsigned int suspend_stats_poll(struct file *filp, + struct poll_table_struct *wait) +{ + unsigned int mask = 0; + + poll_wait(filp, &suspend_stats_queue.wait_queue, wait); + if (suspend_stats_queue.resume_done) { + mask |= (POLLIN | POLLRDNORM); + suspend_stats_queue.resume_done = 0; + } + + return mask; +} +#endif + static const struct file_operations suspend_stats_operations = { .open = suspend_stats_open, .read = seq_read, .llseek = seq_lseek, +#ifdef CONFIG_PM_WAKEUP_TIMES + .poll = suspend_stats_poll, +#endif .release = single_release, }; @@ -234,6 +339,9 @@ static int __init pm_debugfs_init(void) { debugfs_create_file("suspend_stats", S_IFREG | S_IRUGO, NULL, NULL, &suspend_stats_operations); +#ifdef CONFIG_PM_WAKEUP_TIMES + init_waitqueue_head(&suspend_stats_queue.wait_queue); +#endif return 0; } @@ -352,10 +460,147 @@ static suspend_state_t decode_state(const char *buf, size_t n) return PM_SUSPEND_ON; } +static inline u32 +decay_val(u32 val, u32 percent) +{ + u32 ratio; + + percent = clamp(percent, 0U, 100U); + ratio = 1024 - ((1024 * percent) / 100); + + return (val * ratio) / 1024; +} + +static ssize_t sbo_decay_value_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (val > 100) + return -EINVAL; + + sbo_decay_value = val; + return n; +} + +static ssize_t sbo_decay_value_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", sbo_decay_value); +} + +power_attr(sbo_decay_value); + +static ssize_t sbo_short_sleep_msecs_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + sbo_short_sleep_msecs = val; + return n; +} + +static ssize_t sbo_short_sleep_msecs_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", sbo_short_sleep_msecs); +} + +power_attr(sbo_short_sleep_msecs); + +static ssize_t sbo_short_sleep_count_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + sbo_short_sleep_count = val; + return n; +} + +static ssize_t sbo_short_sleep_count_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", sbo_short_sleep_count); +} + +power_attr(sbo_short_sleep_count); + +static ssize_t sbo_initial_alive_time_msecs_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + sbo_initial_alive_time_msecs = val; + return n; +} + +static ssize_t sbo_initial_alive_time_msecs_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", sbo_initial_alive_time_msecs); +} + +power_attr(sbo_initial_alive_time_msecs); + +static ssize_t sbo_enabled_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (val > 1) + return -EINVAL; + + sbo_enabled = !!val; + return n; +} + +static ssize_t sbo_enabled_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", sbo_enabled); +} + +power_attr(sbo_enabled); + +static void +suspend_backoff(u32 timeout_msecs) +{ + if (!sbo_enabled) + return; + + pr_info("suspend: too many immediate wakeups, back off (%u msecs)\n", + timeout_msecs); + + __pm_wakeup_event(ws, timeout_msecs); +} + static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { suspend_state_t state; + struct timespec ts_entry, ts_exit; + u64 elapsed_msecs64; + u32 elapsed_msecs32; int error; error = pm_autosleep_lock(); @@ -368,9 +613,48 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, } state = decode_state(buf, n); - if (state < PM_SUSPEND_MAX) + if (state < PM_SUSPEND_MAX) { + /* + * We want to prevent system from frequent periodic wake-ups + * when sleeping time is less or equal certain interval. + * It's done in order to save power in certain cases, one of + * the examples is GPS tracking, but not only. + */ + getnstimeofday(&ts_entry); error = pm_suspend(state); - else if (state == PM_SUSPEND_MAX) + getnstimeofday(&ts_exit); + + elapsed_msecs64 = timespec_to_ns(&ts_exit) - + timespec_to_ns(&ts_entry); + do_div(elapsed_msecs64, NSEC_PER_MSEC); + elapsed_msecs32 = elapsed_msecs64; + + if (elapsed_msecs32 <= sbo_short_sleep_msecs) { + if (suspend_short_count == sbo_short_sleep_count) { + if (decay_alive_time_ms >= MSEC_PER_SEC) { + suspend_backoff(decay_alive_time_ms); + decay_alive_time_ms = + decay_val(decay_alive_time_ms, + sbo_decay_value); + goto out; + } + } else { + suspend_short_count++; + goto out; + } + } + + /* Start from scratch */ + suspend_short_count = 0; + + /* + * Randomize a bit an initial alive time value to be + * not synced with any wake up sources, for example + * IRQs. + */ + decay_alive_time_ms = sbo_initial_alive_time_msecs - + get_random_int() % (sbo_initial_alive_time_msecs / 10); + } else if (state == PM_SUSPEND_MAX) error = hibernate(); else error = -EINVAL; @@ -602,6 +886,11 @@ static struct attribute * g[] = { #ifdef CONFIG_PM_SLEEP &pm_async_attr.attr, &wakeup_count_attr.attr, + &sbo_enabled_attr.attr, + &sbo_decay_value_attr.attr, + &sbo_short_sleep_msecs_attr.attr, + &sbo_short_sleep_count_attr.attr, + &sbo_initial_alive_time_msecs_attr.attr, #ifdef CONFIG_PM_AUTOSLEEP &autosleep_attr.attr, #endif @@ -651,6 +940,10 @@ static int __init pm_init(void) if (error) return error; pm_print_times_init(); + +#ifdef CONFIG_PM_SLEEP + ws = wakeup_source_register("suspend_backoff"); +#endif return pm_autosleep_init(); } diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 58209d8bfc566ef283959a97dfaaff69cfd9196e..0a94d9b0ea1e15b3720613f9068b2bcc817620d8 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -7,6 +7,11 @@ * * This file is released under the GPLv2. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -373,7 +378,9 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) arch_suspend_disable_irqs(); BUG_ON(!irqs_disabled()); - +#ifdef CONFIG_PM_WAKEUP_TIMES + dpm_log_wakeup_stats(PMSG_SUSPEND); +#endif error = syscore_suspend(); if (!error) { *wakeup = pm_wakeup_pending(); @@ -393,6 +400,9 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) syscore_resume(); } +#ifdef CONFIG_PM_WAKEUP_TIMES + dpm_log_start_time(PMSG_RESUME); +#endif arch_suspend_enable_irqs(); BUG_ON(irqs_disabled()); @@ -432,6 +442,9 @@ int suspend_devices_and_enter(suspend_state_t state) suspend_console(); suspend_test_start(); +#ifdef CONFIG_PM_WAKEUP_TIMES + dpm_log_start_time(PMSG_SUSPEND); +#endif error = dpm_suspend_start(PMSG_SUSPEND); if (error) { pr_err("PM: Some devices failed to suspend, or early wake event detected\n"); @@ -449,6 +462,9 @@ int suspend_devices_and_enter(suspend_state_t state) Resume_devices: suspend_test_start(); dpm_resume_end(PMSG_RESUME); +#ifdef CONFIG_PM_WAKEUP_TIMES + dpm_log_wakeup_stats(PMSG_RESUME); +#endif suspend_test_finish("resume devices"); trace_suspend_resume(TPS("resume_console"), state, true); resume_console(); @@ -508,9 +524,9 @@ static int enter_state(suspend_state_t state) #ifndef CONFIG_SUSPEND_SKIP_SYNC trace_suspend_resume(TPS("sync_filesystems"), 0, true); - printk(KERN_INFO "PM: Syncing filesystems ... "); + printk(KERN_INFO "PM: Syncing filesystems ...\n"); sys_sync(); - printk("done.\n"); + printk(KERN_INFO "PM: Syncing filesystems ... done.\n"); trace_suspend_resume(TPS("sync_filesystems"), 0, false); #endif diff --git a/kernel/power/wakeup_irq_debug.c b/kernel/power/wakeup_irq_debug.c new file mode 100644 index 0000000000000000000000000000000000000000..eaf659a4d16c8dac709ec9a56e7f76e41eaf12af --- /dev/null +++ b/kernel/power/wakeup_irq_debug.c @@ -0,0 +1,364 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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. + */ +/* + * Copyright (C) 2015 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "wakeup_debug" + +struct irq_info { + struct list_head list; + unsigned int wakeup_count; + unsigned int irq_count; + char irq_name[128]; + int irq; +}; + +struct wakeup_irq { + struct list_head head; + bool enabled; +}; + +static int update_irq_table(struct wakeup_irq *p, int irq) +{ + struct irq_info *i; + + list_for_each_entry(i, &p->head, list) { + if (i->irq == irq) { + i->irq_count = kstat_irqs_cpu(i->irq, 0); + return 1; + } + } + + return 0; +} + +static void add_irq_to_table(struct wakeup_irq *p, int irq) +{ + struct irq_info *info; + struct irq_desc *desc; + + /* + * TODO: add here a filter for root IRQs + */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + info->irq_count = kstat_irqs_cpu(irq, 0); + info->irq = irq; + + desc = irq_to_desc(irq); + + /* + * IRQ can be disabled and freed, thus + * we save its name because of history + */ + if (desc && desc->action && desc->action->name) + (void) scnprintf( + info->irq_name, sizeof(info->irq_name), + "%s", desc->action->name); + + list_add_tail(&info->list, &p->head); +} + +static int wakeup_irq_suspend(struct device *dev) +{ + struct irq_desc *desc; + struct wakeup_irq *p; + int irq, rv; + + /* get private data */ + p = dev_get_drvdata(dev); + if (!p->enabled) + goto leave; + + /* + * we want to add IRQs to list by reverse way, + * therefore all potential triggered IRQ (nested) + * will be in the beginning of the list + */ + for_each_irq_desc_reverse(irq, desc) { + raw_spin_lock_irq(&desc->lock); + rv = irqd_is_wakeup_set(&desc->irq_data); + raw_spin_unlock_irq(&desc->lock); + + if (rv == 0) + continue; + + rv = update_irq_table(p, irq); + if (rv) + continue; + + add_irq_to_table(p, irq); + dev_dbg(dev, "IRQ %d was added to the list\n", irq); + } + +leave: + return 0; +} + +static int wakeup_irq_resume(struct device *dev) +{ + struct irq_info *i; + struct wakeup_irq *p; + struct irq_desc *desc; + unsigned long flags; + int diff; + + /* get private data */ + p = dev_get_drvdata(dev); + if (!p->enabled) + goto leave; + + /* + * go through all registered IRQs in our list + */ + list_for_each_entry(i, &p->head, list) { + desc = irq_to_desc(i->irq); + if (desc) { + raw_spin_lock_irqsave(&desc->lock, flags); + diff = kstat_irqs_cpu(i->irq, 0) - i->irq_count; + raw_spin_unlock_irqrestore(&desc->lock, flags); + + if (diff > 0) { + i->wakeup_count++; + + dev_dbg(dev, "%d, wake-up count: %d, active: %d (%s)\n", + i->irq, i->wakeup_count, + irqd_is_wakeup_set(&desc->irq_data), + desc->action && desc->action->name ? + desc->action->name : ""); + + log_wakeup_reason(i->irq); + update_irq_table(p, i->irq); + } + } + } + +leave: + return 0; +} + +static ssize_t irq_stat_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wakeup_irq *p; + struct irq_info *i; + int len = 0; + + p = dev_get_drvdata(dev); + + /* build header of the output */ + len += scnprintf(buf + len, PAGE_SIZE - len, + "IRQ\tCount\tName\n"); + + list_for_each_entry(i, &p->head, list) { + if (i->wakeup_count) { + len += scnprintf(buf + len, PAGE_SIZE - len, + "%d\t%d\t%s\n", + i->irq, i->wakeup_count, i->irq_name); + /* + * Ensure we do not write more than PAGE_SIZE + * bytes of data including null terminator and + * add '\n' symbol to the buffer if it's full + */ + if (len >= PAGE_SIZE - 1) { + buf[PAGE_SIZE - 2] = '\n'; + dev_err(dev, "%s:%d: buffer is full\n", + __func__, __LINE__); + break; + } + } + } + + return len; +} + +static ssize_t enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wakeup_irq *p; + + p = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", p->enabled); +} + +static ssize_t enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wakeup_irq *p; + int value, ret; + + p = dev_get_drvdata(dev); + + ret = kstrtoint(buf, 10, &value); + if (ret) { + dev_err(dev, "kstrtoint failed: %d\n", ret); + goto error; + } + + if (value == 1) + p->enabled = true; + else if (value == 0) + p->enabled = false; + else + goto error; + + return count; + +error: + return -EINVAL; +} + +static const struct dev_pm_ops wakeup_irq_pm_ops = { + .suspend_noirq = wakeup_irq_suspend, + .resume_early = wakeup_irq_resume, +}; + +static struct platform_device wakeup_irq_device = { + .name = MODULE_NAME, + .id = 0, +}; + +static struct platform_driver wakeup_irq_driver = { + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .pm = &wakeup_irq_pm_ops, + }, +}; + +static struct device_attribute wakeup_irq_attrs[] = { + __ATTR(wakeup_irq_stat, + S_IRUSR | S_IRGRP | S_IROTH, irq_stat_show, NULL), + __ATTR(enable, + S_IRUSR | S_IWUSR, enable_show, enable_store), +}; + +static int create_sysfs_entries(struct device *dev) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(wakeup_irq_attrs); i++) { + rc = device_create_file(dev, &wakeup_irq_attrs[i]); + if (rc < 0) + goto revert; + } + + return 0; + +revert: + for (; --i >= 0;) + device_remove_file(dev, &wakeup_irq_attrs[i]); + + return rc; +} + +static int wakeup_irq_debug_init(void) +{ + struct wakeup_irq *p; + int rv = -1; + + p = kzalloc(sizeof(struct wakeup_irq), GFP_KERNEL); + if (p == NULL) + goto out; + + INIT_LIST_HEAD(&p->head); + + /* + * we need it for PM works correctly + */ + rv = platform_device_register(&wakeup_irq_device); + if (rv) { + pr_err("%s: wakeup_irq_device register failed %d\n", + __func__, rv); + goto free_mem; + } + + rv = platform_driver_register(&wakeup_irq_driver); + if (rv) { + pr_err("%s: wakeup_irq_driver register failed %d\n", + __func__, rv); + goto unregister_device; + } + + rv = create_sysfs_entries(&wakeup_irq_device.dev); + if (rv < 0) { + pr_err("%s: create_sysfs_entries failed %d\n", + __func__, rv); + goto unregister_driver; + } + + /* + * save private data + */ + platform_set_drvdata(&wakeup_irq_device, p); + + /* + * by default it's disabled + */ + p->enabled = false; + return 0; + +unregister_driver: + platform_driver_unregister(&wakeup_irq_driver); +unregister_device: + platform_device_unregister(&wakeup_irq_device); +free_mem: + kzfree(p); +out: + return rv; +} + +static void wakeup_irq_debug_exit(void) +{ + struct irq_info *info, *tmp_info; + struct wakeup_irq *p; + int i; + + p = dev_get_drvdata(&wakeup_irq_device.dev); + + for (i = 0; i < ARRAY_SIZE(wakeup_irq_attrs); i++) + device_remove_file(&wakeup_irq_device.dev, + &wakeup_irq_attrs[i]); + + list_for_each_entry_safe(info, tmp_info, &p->head, list) { + list_del(&info->list); + kzfree(info); + } + + platform_driver_unregister(&wakeup_irq_driver); + platform_device_unregister(&wakeup_irq_device); + kzfree(p); +} + +module_init(wakeup_irq_debug_init); +module_exit(wakeup_irq_debug_exit); + +MODULE_DESCRIPTION("Wakeup IRQ Debug Module"); +MODULE_AUTHOR("Uladzislau Rezki"); +MODULE_LICENSE("GPL v2"); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index dca87791e9c1ee6b4ab02e30ffa052ae82529f5a..b9ddd23c16a33396aad83501d4dbe6f7c6957a57 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -15,6 +15,11 @@ * Rewrote bits to get rid of console_lock * 01Mar01 Andrew Morton */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -2110,6 +2115,13 @@ void suspend_console(void) up_console_sem(); } +int is_console_suspended(void) +{ + if (!console_suspend_enabled) + return 0; + return console_suspended; +} + void resume_console(void) { if (!console_suspend_enabled) diff --git a/kernel/sched/boost.c b/kernel/sched/boost.c index 5bdd51b1e55eb8a0e16b1d452a1b7e22371c2d5e..c23b1aebf3440c709d9304875530fc3ea9f19cef 100644 --- a/kernel/sched/boost.c +++ b/kernel/sched/boost.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include "sched.h" #include @@ -40,6 +45,7 @@ static void boost_kick_cpus(void) { int i; struct cpumask kick_mask; + u32 nr_running; if (boost_policy != SCHED_BOOST_ON_BIG) return; @@ -47,8 +53,20 @@ static void boost_kick_cpus(void) cpumask_andnot(&kick_mask, cpu_online_mask, cpu_isolated_mask); for_each_cpu(i, &kick_mask) { - if (cpu_capacity(i) != max_capacity) - boost_kick(i); + /* + * kick only "small" cluster + */ + if (cpu_capacity(i) != max_capacity) { + nr_running = ACCESS_ONCE(cpu_rq(i)->nr_running); + + /* + * make sense to interrupt CPU if its run-queue + * has something running in order to check for + * migration afterwards, otherwise skip it. + */ + if (nr_running) + boost_kick(i); + } } } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 6a5671bdb792868f9a468d3183df73e4ae0737b6..61a6f206dacaa07ccb62c288a8b947f982f3d5d2 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -25,6 +25,11 @@ * 2007-11-29 RT balancing improvements by Steven Rostedt, Gregory Haskins, * Thomas Gleixner, Mike Kravetz */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -2163,7 +2168,7 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags, stat: ttwu_stat(p, cpu, wake_flags); out: - raw_spin_unlock_irqrestore(&p->pi_lock, flags); + raw_spin_unlock(&p->pi_lock); if (freq_notif_allowed) { if (!same_freq_domain(src_cpu, cpu)) { @@ -2176,6 +2181,8 @@ out: } } + local_irq_restore(flags); + return success; } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 621793d3ec6df84f43d4be339823f9a3cdcac598..0ed5d8b5ccde892b0b87e32a6d3a35d616a66444 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -19,6 +19,11 @@ * Adaptive scheduling granularity, math enhancements by Peter Zijlstra * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -2751,6 +2756,7 @@ static u32 __compute_runnable_contrib(u64 n) #define SBC_FLAG_BEST_SIBLING 0x200 #define SBC_FLAG_WAKER_CPU 0x400 #define SBC_FLAG_PACK_TASK 0x800 +#define SBC_FLAG_SKIP_RT_TASK 0x1000 /* Cluster selection flag */ #define SBC_FLAG_COLOC_CLUSTER 0x10000 @@ -2987,10 +2993,11 @@ struct cpu_select_env *env, struct cluster_cpu_stats *stats) cpumask_and(&search_cpus, &env->search_cpus, &next->cpus); for_each_cpu(i, &search_cpus) { - trace_sched_cpu_load_wakeup(cpu_rq(i), idle_cpu(i), - sched_irqload(i), power_cost(i, task_load(env->p) + - cpu_cravg_sync(i, env->sync)), 0); - + if (trace_sched_cpu_load_wakeup_enabled()) { + trace_sched_cpu_load_wakeup(cpu_rq(i), idle_cpu(i), + sched_irqload(i), power_cost(i, task_load(env->p) + + cpu_cravg_sync(i, env->sync)), 0); + } update_spare_capacity(stats, env, i, next->capacity, cpu_load_sync(i, env->sync)); } @@ -3031,6 +3038,17 @@ next_best_cluster(struct sched_cluster *cluster, struct cpu_select_env *env, return next; } +/* + * Returns true, if a current task has RT/DL class: + * SCHED_FIFO + SCHED_RR + SCHED_DEADLINE + */ +static inline int +is_current_high_prio_class_task(int cpu) +{ + struct task_struct *curr = READ_ONCE(cpu_rq(cpu)->curr); + return (task_has_rt_policy(curr) | task_has_dl_policy(curr)); +} + #ifdef CONFIG_SCHED_HMP_CSTATE_AWARE static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats, struct cpu_select_env *env, int cpu_cost) @@ -3070,6 +3088,25 @@ static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats, return; } + /* + * We try to escape of selecting CPUs with running RT + * class tasks, if a power coast is the same. A reason + * is to reduce a latency, since RT task may not be + * preempted for a long time. + */ + if (is_current_high_prio_class_task(stats->best_cpu) && + !is_current_high_prio_class_task(cpu)) { + stats->best_cpu_wakeup_latency = wakeup_latency; + stats->best_load = env->cpu_load; + stats->best_cpu = cpu; + env->sbc_best_flag = SBC_FLAG_SKIP_RT_TASK; + return; + } + + if (!is_current_high_prio_class_task(stats->best_cpu) && + is_current_high_prio_class_task(cpu)) + return; + /* CPU cost is the same. Start breaking the tie by C-state */ if (wakeup_latency > stats->best_cpu_wakeup_latency) @@ -3164,12 +3201,12 @@ static void find_best_cpu_in_cluster(struct sched_cluster *c, for_each_cpu(i, &search_cpus) { env->cpu_load = cpu_load_sync(i, env->sync); - - trace_sched_cpu_load_wakeup(cpu_rq(i), idle_cpu(i), - sched_irqload(i), - power_cost(i, task_load(env->p) + - cpu_cravg_sync(i, env->sync)), 0); - + if (trace_sched_cpu_load_wakeup_enabled()) { + trace_sched_cpu_load_wakeup(cpu_rq(i), idle_cpu(i), + sched_irqload(i), + power_cost(i, task_load(env->p) + + cpu_cravg_sync(i, env->sync)), 0); + } if (skip_cpu(i, env)) continue; @@ -5258,10 +5295,12 @@ static void throttle_cfs_rq(struct cfs_rq *cfs_rq) raw_spin_unlock(&cfs_b->lock); /* Log effect on hmp stats after throttling */ - trace_sched_cpu_load_cgroup(rq, idle_cpu(cpu_of(rq)), - sched_irqload(cpu_of(rq)), - power_cost(cpu_of(rq), 0), - cpu_temp(cpu_of(rq))); + if (trace_sched_cpu_load_cgroup_enabled()) { + trace_sched_cpu_load_cgroup(rq, idle_cpu(cpu_of(rq)), + sched_irqload(cpu_of(rq)), + power_cost(cpu_of(rq), 0), + cpu_temp(cpu_of(rq))); + } } void unthrottle_cfs_rq(struct cfs_rq *cfs_rq) @@ -5315,10 +5354,12 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq) resched_curr(rq); /* Log effect on hmp stats after un-throttling */ - trace_sched_cpu_load_cgroup(rq, idle_cpu(cpu_of(rq)), - sched_irqload(cpu_of(rq)), - power_cost(cpu_of(rq), 0), - cpu_temp(cpu_of(rq))); + if (trace_sched_cpu_load_cgroup_enabled()) { + trace_sched_cpu_load_cgroup(rq, idle_cpu(cpu_of(rq)), + sched_irqload(cpu_of(rq)), + power_cost(cpu_of(rq), 0), + cpu_temp(cpu_of(rq))); + } } static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b, @@ -5877,6 +5918,10 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) if (!se) { add_nr_running(rq, 1); + + if (unlikely(p->nr_cpus_allowed == 1)) + rq->nr_pinned_tasks++; + inc_rq_hmp_stats(rq, p, 1); } @@ -5970,6 +6015,10 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) if (!se) { sub_nr_running(rq, 1); + + if (unlikely(p->nr_cpus_allowed == 1)) + rq->nr_pinned_tasks--; + dec_rq_hmp_stats(rq, p, 1); } @@ -6514,6 +6563,7 @@ static int find_new_capacity(struct energy_env *eenv, for (idx = 0; idx < sge->nr_cap_states; idx++) { if (sge->cap_states[idx].cap >= util) { + /* Keep track of SG's capacity index */ eenv->cap_idx = idx; break; } @@ -8820,6 +8870,14 @@ redo: if (env->idle != CPU_NOT_IDLE && env->src_rq->nr_running <= 1) break; + /* + * Another CPU can place tasks, since we do not hold dst_rq lock + * while doing balancing. If newly idle CPU already got something, + * give up to reduce a latency. + */ + if (env->idle == CPU_NEWLY_IDLE && env->dst_rq->nr_running > 0) + break; + p = list_first_entry(tasks, struct task_struct, se.group_node); env->loop++; @@ -8842,8 +8900,9 @@ redo: if (sched_feat(LB_MIN) && load < 16 && !env->sd->nr_balance_failed) goto next; - if ((load / 2) > env->imbalance) - goto next; + if (env->idle != CPU_NEWLY_IDLE) + if ((load / 2) > env->imbalance) + goto next; detach_task(p, env); list_add(&p->se.group_node, &env->tasks); @@ -9518,11 +9577,12 @@ static inline void update_sg_lb_stats(struct lb_env *env, for_each_cpu_and(i, sched_group_cpus(group), env->cpus) { struct rq *rq = cpu_rq(i); - trace_sched_cpu_load_lb(cpu_rq(i), idle_cpu(i), - sched_irqload(i), - power_cost(i, 0), - cpu_temp(i)); - + if (trace_sched_cpu_load_lb_enabled()) { + trace_sched_cpu_load_lb(cpu_rq(i), idle_cpu(i), + sched_irqload(i), + power_cost(i, 0), + cpu_temp(i)); + } if (cpu_isolated(i)) continue; @@ -9648,6 +9708,15 @@ static bool update_sd_pick_busiest(struct lb_env *env, if (sgs->group_type < busiest->group_type) return false; + if (sgs->avg_load <= busiest->avg_load) + return false; + + /* + * Group has no more than one task per CPU + */ + if (sgs->sum_nr_running <= sgs->group_weight) + return false; + if (energy_aware()) { /* * Candidate sg doesn't face any serious load-balance problems @@ -9657,9 +9726,6 @@ static bool update_sd_pick_busiest(struct lb_env *env, !group_has_capacity(env, &sds->local_stat)) return false; - if (sgs->avg_load <= busiest->avg_load) - return false; - if (!(env->sd->flags & SD_ASYM_CPUCAPACITY)) goto asym_packing; @@ -10475,7 +10541,6 @@ redo: * correctly treated as an imbalance. */ env.flags |= LBF_ALL_PINNED; - env.loop_max = min(sysctl_sched_nr_migrate, busiest->nr_running); more_balance: raw_spin_lock_irqsave(&busiest->lock, flags); @@ -10488,6 +10553,12 @@ more_balance: goto no_move; } + /* + * Set loop_max when rq's lock is taken to prevent a race. + */ + env.loop_max = min(sysctl_sched_nr_migrate, + busiest->cfs.h_nr_running); + /* * cur_ld_moved - load moved in current iteration * ld_moved - cumulative load moved across iterations @@ -10945,8 +11016,6 @@ out_unlock: if (p) attach_one_task(target_rq, p); - local_irq_enable(); - if (moved && !same_freq_domain(busiest_cpu, target_cpu)) { int check_groups = !!(env.flags & LBF_MOVED_RELATED_THREAD_GROUP_TASK); @@ -10956,6 +11025,8 @@ out_unlock: check_for_freq_change(target_rq, true, false); } + local_irq_enable(); + return 0; } @@ -11344,6 +11415,22 @@ static inline int _nohz_kick_needed_hmp(struct rq *rq, int cpu, int *type) sched_boost_policy() == SCHED_BOOST_ON_ALL) return 1; + if (unlikely(rq->nr_pinned_tasks > 0)) { + int delta = rq->nr_running - rq->nr_pinned_tasks; + + /* + * Check if it is possible to "unload" this CPU in case + * of having pinned/affine tasks. Do not disturb idle + * core if one of the below condition is true: + * + * - there is one pinned task and it is not "current" + * - all tasks are pinned to this CPU + */ + if (delta < 2) + if (current->nr_cpus_allowed > 1 || !delta) + return 0; + } + if (cpu_max_power_cost(cpu) == max_power_cost) return 1; diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c index ddcf7cfb7248a606c3cfc51a256c2a5b8ac214a1..5a836ed4af86f8df5ac443a3211a42c9577cfe76 100644 --- a/kernel/sched/hmp.c +++ b/kernel/sched/hmp.c @@ -13,6 +13,11 @@ * Syed Rameez Mustafa, Olav haugan, Joonwoo Park, Pavan Kumar Kondeti * and Vikram Mulukutla */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -136,6 +141,7 @@ struct freq_max_load { static DEFINE_PER_CPU(struct freq_max_load *, freq_max_load); static DEFINE_SPINLOCK(freq_max_load_lock); +static DEFINE_PER_CPU(u64, prev_group_runnable_sum); struct cpu_pwr_stats __weak *get_cpu_pwr_stats(void) { @@ -375,7 +381,6 @@ struct sched_cluster init_cluster = { .dstate_wakeup_energy = 0, .dstate_wakeup_latency = 0, .exec_scale_factor = 1024, - .notifier_sent = 0, .wake_up_idle = 0, }; @@ -552,7 +557,7 @@ static struct sched_cluster *alloc_new_cluster(const struct cpumask *cpus) if (cluster->efficiency < min_possible_efficiency) min_possible_efficiency = cluster->efficiency; - cluster->notifier_sent = 0; + atomic_set(&cluster->notifier_sent, 0); return cluster; } @@ -600,10 +605,16 @@ void update_cluster_topology(void) void init_clusters(void) { + int cpu; + bitmap_clear(all_cluster_ids, 0, NR_CPUS); init_cluster.cpus = *cpu_possible_mask; + atomic_set(&init_cluster.notifier_sent, 0); raw_spin_lock_init(&init_cluster.load_lock); INIT_LIST_HEAD(&cluster_head); + + for_each_possible_cpu(cpu) + per_cpu(prev_group_runnable_sum, cpu) = 0; } int register_cpu_cycle_counter_cb(struct cpu_cycle_counter_cb *cb) @@ -1657,6 +1668,16 @@ static inline u64 scale_exec_time(u64 delta, struct rq *rq) u32 freq; freq = cpu_cycles_to_freq(rq->cc.cycles, rq->cc.time); + + /* + * For some reason, current frequency estimation + * can be far bigger than max available frequency. + * + * TODO: need to be investigated. As for now, take + * min as a workaround. + */ + freq = min(freq, max_possible_freq); + delta = DIV64_U64_ROUNDUP(delta * freq, max_possible_freq); delta *= rq->cluster->exec_scale_factor; delta >>= 10; @@ -1718,6 +1739,8 @@ static void group_load_in_freq_domain(struct cpumask *cpus, } static inline u64 freq_policy_load(struct rq *rq, u64 load); +static inline void commit_prev_group_run_sum(struct rq *rq); +static inline u64 get_prev_group_run_sum(struct rq *rq); /* * Should scheduler alert governor for changing frequency? * @@ -1734,9 +1757,9 @@ static inline u64 freq_policy_load(struct rq *rq, u64 load); static int send_notification(struct rq *rq, int check_pred, int check_groups) { unsigned int cur_freq, freq_required; - unsigned long flags; int rc = 0; - u64 group_load = 0, new_load = 0; + u64 new_load, val = 0; + u32 prev_run_sum, group_run_sum; if (check_pred) { u64 prev = rq->old_busy_time; @@ -1755,19 +1778,19 @@ static int send_notification(struct rq *rq, int check_pred, int check_groups) if (freq_required < cur_freq + sysctl_sched_pred_alert_freq) return 0; } else { - /* - * Protect from concurrent update of rq->prev_runnable_sum and - * group cpu load - */ - raw_spin_lock_irqsave(&rq->lock, flags); + val = get_prev_group_run_sum(rq); + group_run_sum = (u32) (val >> 32); + prev_run_sum = (u32) val; + if (check_groups) - group_load = rq->grp_time.prev_runnable_sum; + /* + * prev_run_sum and group_run_sum are synced + */ + new_load = prev_run_sum + group_run_sum; + else + new_load = prev_run_sum; - new_load = rq->prev_runnable_sum + group_load; new_load = freq_policy_load(rq, new_load); - - raw_spin_unlock_irqrestore(&rq->lock, flags); - cur_freq = load_to_freq(rq, rq->old_busy_time); freq_required = load_to_freq(rq, new_load); @@ -1775,14 +1798,11 @@ static int send_notification(struct rq *rq, int check_pred, int check_groups) return 0; } - raw_spin_lock_irqsave(&rq->lock, flags); - if (!rq->cluster->notifier_sent) { - rq->cluster->notifier_sent = 1; + if (!atomic_cmpxchg(&rq->cluster->notifier_sent, 0, 1)) { rc = 1; trace_sched_freq_alert(cpu_of(rq), check_pred, check_groups, rq, new_load); } - raw_spin_unlock_irqrestore(&rq->lock, flags); return rc; } @@ -1790,14 +1810,11 @@ static int send_notification(struct rq *rq, int check_pred, int check_groups) /* Alert governor if there is a need to change frequency */ void check_for_freq_change(struct rq *rq, bool check_pred, bool check_groups) { - int cpu = cpu_of(rq); - - if (!send_notification(rq, check_pred, check_groups)) - return; - - atomic_notifier_call_chain( - &load_alert_notifier_head, 0, - (void *)(long)cpu); + if (send_notification(rq, check_pred, check_groups)) { + atomic_notifier_call_chain( + &load_alert_notifier_head, 0, + (void *)(long) cpu_of(rq)); + } } void notify_migration(int src_cpu, int dest_cpu, bool src_cpu_dead, @@ -2059,26 +2076,28 @@ void clear_top_tasks_bitmap(unsigned long *bitmap) /* * Special case the last index and provide a fast path for index = 0. - * Note that sched_load_granule can change underneath us if we are not - * holding any runqueue locks while calling the two functions below. */ -static u32 top_task_load(struct rq *rq) +static u32 top_task_load(struct rq *rq) { int index = rq->prev_top; u8 prev = 1 - rq->curr_table; + u32 sched_granule_load; + u32 ret_val = 0; + + sched_granule_load = READ_ONCE(sched_load_granule); if (!index) { int msb = NUM_LOAD_INDICES - 1; - if (!test_bit(msb, rq->top_tasks_bitmap[prev])) - return 0; - else - return sched_load_granule; + if (test_bit(msb, rq->top_tasks_bitmap[prev])) + ret_val = sched_granule_load; } else if (index == NUM_LOAD_INDICES - 1) { - return sched_ravg_window; + ret_val = sched_ravg_window; } else { - return (index + 1) * sched_load_granule; + ret_val = (index + 1) * sched_granule_load; } + + return ret_val; } static u32 load_to_index(u32 load) @@ -2262,6 +2281,22 @@ static void rollover_cpu_window(struct rq *rq, bool full_window) rq->grp_time.nt_curr_runnable_sum = 0; } +static inline void +commit_prev_group_run_sum(struct rq *rq) +{ + u64 val; + + val = rq->grp_time.prev_runnable_sum; + val = (val << 32) | rq->prev_runnable_sum; + WRITE_ONCE(per_cpu(prev_group_runnable_sum, cpu_of(rq)), val); +} + +static inline u64 +get_prev_group_run_sum(struct rq *rq) +{ + return READ_ONCE(per_cpu(prev_group_runnable_sum, cpu_of(rq))); +} + /* * Account cpu activity in its busy time counters (rq->curr/prev_runnable_sum) */ @@ -2483,7 +2518,7 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, */ if (mark_start > window_start) { *curr_runnable_sum = scale_exec_time(irqtime, rq); - return; + goto done; } /* @@ -2499,11 +2534,11 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, /* Process the remaining IRQ busy time in the current window. */ delta = wallclock - window_start; rq->curr_runnable_sum = scale_exec_time(delta, rq); - - return; } done: + commit_prev_group_run_sum(rq); + if (!is_idle_task(p) && !exiting_task(p)) update_top_tasks(p, rq, old_curr_window, new_window, full_window); @@ -2659,7 +2694,7 @@ static void update_history(struct rq *rq, struct task_struct *p, u32 *hist = &p->ravg.sum_history[0]; int ridx, widx; u32 max = 0, avg, demand, pred_demand; - u64 sum = 0; + u64 sum = 0, wma = 0, ewa = 0; /* Ignore windows where task had no activity */ if (!runtime || is_idle_task(p) || exiting_task(p) || !samples) @@ -2671,6 +2706,8 @@ static void update_history(struct rq *rq, struct task_struct *p, for (; ridx >= 0; --widx, --ridx) { hist[widx] = hist[ridx]; sum += hist[widx]; + wma += hist[widx] * (sched_ravg_hist_size - widx); + ewa += hist[widx] << (sched_ravg_hist_size - widx - 1); if (hist[widx] > max) max = hist[widx]; } @@ -2678,6 +2715,8 @@ static void update_history(struct rq *rq, struct task_struct *p, for (widx = 0; widx < samples && widx < sched_ravg_hist_size; widx++) { hist[widx] = runtime; sum += hist[widx]; + wma += hist[widx] * (sched_ravg_hist_size - widx); + ewa += hist[widx] << (sched_ravg_hist_size - widx - 1); if (hist[widx] > max) max = hist[widx]; } @@ -2690,8 +2729,34 @@ static void update_history(struct rq *rq, struct task_struct *p, demand = max; } else { avg = div64_u64(sum, sched_ravg_hist_size); + wma = div64_u64(wma, (sched_ravg_hist_size * (sched_ravg_hist_size + 1)) / 2); + ewa = div64_u64(ewa, (1 << sched_ravg_hist_size) - 1); + if (sched_window_stats_policy == WINDOW_STATS_AVG) demand = avg; + else if (sched_window_stats_policy == WINDOW_STATS_MAX_RECENT_WMA) + /* + * WMA stands for weighted moving average. It helps + * to smooth load curve and react faster while ramping + * down comparing with basic averaging. We do it only + * when load trend goes down. See below example (4 HS): + * + * WMA = (P0 * 4 + P1 * 3 + P2 * 2 + P3 * 1) / (4 + 3 + 2 + 1) + * + * This is done for power saving. Means when load disappears + * or becomes low, this algorithm caches real bottom load faster + * (because of weights) then taking AVG values. + */ + demand = max((u32) wma, runtime); + else if (sched_window_stats_policy == WINDOW_STATS_WMA) + demand = (u32) wma; + else if (sched_window_stats_policy == WINDOW_STATS_MAX_RECENT_EWA) + /* + * EWA stands for exponential weighted average + */ + demand = max((u32) ewa, runtime); + else if (sched_window_stats_policy == WINDOW_STATS_EWA) + demand = (u32) ewa; else demand = max(avg, runtime); } @@ -3063,7 +3128,7 @@ void reset_all_window_stats(u64 window_start, unsigned int window_size) if (window_size) { sched_ravg_window = window_size * TICK_NSEC; set_hmp_defaults(); - sched_load_granule = sched_ravg_window / NUM_LOAD_INDICES; + WRITE_ONCE(sched_load_granule, sched_ravg_window / NUM_LOAD_INDICES); } sched_disable_window_stats = 0; @@ -3076,6 +3141,12 @@ void reset_all_window_stats(u64 window_start, unsigned int window_size) rq->curr_runnable_sum = rq->prev_runnable_sum = 0; rq->nt_curr_runnable_sum = rq->nt_prev_runnable_sum = 0; memset(&rq->grp_time, 0, sizeof(struct group_cpu_time)); + + /* + * just commit zero, since grp_time/prev are 0 + */ + commit_prev_group_run_sum(rq); + for (i = 0; i < NUM_TRACKED_WINDOWS; i++) { memset(&rq->load_subs[i], 0, sizeof(struct load_subtractions)); @@ -3145,6 +3216,8 @@ static inline void account_load_subtractions(struct rq *rq) ls[i].new_subs = 0; } + commit_prev_group_run_sum(rq); + BUG_ON((s64)rq->prev_runnable_sum < 0); BUG_ON((s64)rq->curr_runnable_sum < 0); BUG_ON((s64)rq->nt_prev_runnable_sum < 0); @@ -3231,16 +3304,6 @@ void sched_get_cpus_busy(struct sched_load *busy, max_busy_cpu = cpu; } - /* - * sched_get_cpus_busy() is called for all CPUs in a - * frequency domain. So the notifier_sent flag per - * cluster works even when a frequency domain spans - * more than 1 cluster. - */ - if (rq->cluster->notifier_sent) { - notifier_sent = 1; - rq->cluster->notifier_sent = 0; - } early_detection[i] = (rq->ed_task != NULL); max_freq[i] = cpu_max_freq(cpu); i++; @@ -3291,8 +3354,20 @@ skip_early: i++; } - for_each_cpu(cpu, query_cpus) + for_each_cpu(cpu, query_cpus) { + rq = cpu_rq(cpu); + + /* + * sched_get_cpus_busy() is called for all CPUs in a + * frequency domain. So the notifier_sent flag per + * cluster works even when a frequency domain spans + * more than 1 cluster. + */ + if (atomic_cmpxchg(&rq->cluster->notifier_sent, 1, 0)) + notifier_sent = 1; + raw_spin_unlock(&(cpu_rq(cpu))->lock); + } local_irq_restore(flags); @@ -3658,6 +3733,9 @@ void fixup_busy_time(struct task_struct *p, int new_cpu) dest_rq->ed_task = p; } + commit_prev_group_run_sum(src_rq); + commit_prev_group_run_sum(dest_rq); + done: if (p->state == TASK_WAKING) double_rq_unlock(src_rq, dest_rq); @@ -3856,6 +3934,8 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp, p->ravg.curr_window_cpu[cpu] = p->ravg.curr_window; p->ravg.prev_window_cpu[cpu] = p->ravg.prev_window; + commit_prev_group_run_sum(rq); + trace_sched_migration_update_sum(p, migrate_type, rq); BUG_ON((s64)*src_curr_runnable_sum < 0); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index b6cd12998f16b6f1f77727c7812783671aaffb10..9d67c776ba2eb916b1a885da6006973d2f7a5a39 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -403,7 +408,7 @@ struct sched_cluster { bool freq_init_done; int dstate, dstate_wakeup_latency, dstate_wakeup_energy; unsigned int static_cluster_pwr_cost; - int notifier_sent; + atomic_t notifier_sent; bool wake_up_idle; atomic64_t last_cc_update; atomic64_t cycles; @@ -697,6 +702,7 @@ struct rq { * remote CPUs use both these fields when doing load calculation. */ unsigned int nr_running; + unsigned int nr_pinned_tasks; #ifdef CONFIG_NUMA_BALANCING unsigned int nr_numa_running; unsigned int nr_preferred_running; @@ -1113,9 +1119,13 @@ enum sched_boost_policy { #define WINDOW_STATS_MAX 1 #define WINDOW_STATS_MAX_RECENT_AVG 2 #define WINDOW_STATS_AVG 3 -#define WINDOW_STATS_INVALID_POLICY 4 +#define WINDOW_STATS_MAX_RECENT_WMA 4 +#define WINDOW_STATS_WMA 5 +#define WINDOW_STATS_MAX_RECENT_EWA 6 +#define WINDOW_STATS_EWA 7 +#define WINDOW_STATS_INVALID_POLICY 8 -#define SCHED_UPMIGRATE_MIN_NICE 15 +#define SCHED_UPMIGRATE_MIN_NICE 9 #define EXITING_TASK_MARKER 0xdeaddead #define UP_MIGRATION 1 diff --git a/kernel/softirq.c b/kernel/softirq.c index d69b77fc7cc17628f70cf42149dc1c3b3c12f391..94a3f4dff2cc902f74bb87ce19ebf6e2e7fd914c 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -7,6 +7,11 @@ * * Rewritten. Old one was good in 2.2, but in 2.3 it was immoral. --ANK (990903) */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 7902ecbce8ec01f49c49968e4af69ea6f561d677..0bbc5ce65a8b64322a86dfedb8ee9ca66c3573b8 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -7,6 +7,11 @@ * Please see that file for copyright and history logs. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -1378,7 +1383,13 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk, */ bool timekeeping_rtc_skipresume(void) { - return sleeptime_injected; + struct timekeeper *tk = &tk_core.timekeeper; + struct clocksource *clock = tk->tkr_mono.clock; + + if (sleeptime_injected || (clock->flags & CLOCK_SOURCE_SUSPEND_NONSTOP)) + return true; + + return false; } /** diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 3c7b7a9bcad11d7c80ab3c37041e34a6defddf42..d6fd4076eb86f8216e5768e6ac9242970c30c574 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -132,6 +132,11 @@ config TRACING select EVENT_TRACING select TRACE_CLOCK +config TRACE_PRINTK + bool "Enable trace_printk" + depends on TRACING + default y + config GENERIC_TRACER bool select TRACING diff --git a/kernel/trace/ipc_logging.c b/kernel/trace/ipc_logging.c index ed29c38cd7fbc0ee8c9f07447f4819409cb8116a..fcb2dcf2728c2b3cd279d5359053b818d1eef248 100644 --- a/kernel/trace/ipc_logging.c +++ b/kernel/trace/ipc_logging.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -510,6 +515,13 @@ int ipc_log_string(void *ilctxt, const char *fmt, ...) data_size = vscnprintf((ectxt.buff + ectxt.offset + hdr_size), avail_size, fmt, arg_list); va_end(arg_list); + if (data_size < 0) { + pr_err("%s: vsnprintf failed\n", __func__); + return -EINVAL; + } else if (data_size >= avail_size) { + pr_warn("%s: Truncated log output\n", __func__); + data_size = avail_size - 1; + } tsv_write_header(&ectxt, TSV_TYPE_BYTE_ARRAY, data_size); ectxt.offset += data_size; msg_encode_end(&ectxt); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index e62c44cb8b74df9b2784f8a0fb04293dca5c1640..a1bae510660486c360f0cf5bf8c9cde89e4db272 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -11,6 +11,11 @@ * Copyright (C) 2004-2006 Ingo Molnar * Copyright (C) 2004 Nadia Yvette Chambers */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -2075,6 +2080,7 @@ static char *get_trace_buf(void) return this_cpu_ptr(&percpu_buffer->buffer[0]); } +#ifdef CONFIG_TRACE_PRINTK static int alloc_percpu_trace_buffer(void) { struct trace_buffer_struct *buffers; @@ -2115,11 +2121,13 @@ static int alloc_percpu_trace_buffer(void) WARN(1, "Could not allocate percpu trace_printk buffer"); return -ENOMEM; } +#endif static int buffers_allocated; void trace_printk_init_buffers(void) { +#ifdef CONFIG_TRACE_PRINTK if (buffers_allocated) return; @@ -2156,6 +2164,9 @@ void trace_printk_init_buffers(void) */ if (global_trace.trace_buffer.buffer) tracing_start_cmdline_record(); +#else + return; +#endif } void trace_printk_start_comm(void) diff --git a/lib/bitrev.c b/lib/bitrev.c index 40ffda94cc5dbf79d382ee972a070e24a0bb68e5..998d4482a96af72af9e69d14df9f1e51cbcba823 100644 --- a/lib/bitrev.c +++ b/lib/bitrev.c @@ -1,3 +1,8 @@ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef CONFIG_HAVE_ARCH_BITREVERSE #include #include diff --git a/mm/Kconfig b/mm/Kconfig index dcca76e498df19909e95cfe0d274f18002a3f7ea..ad6867816fec41b14418f08aa20826e5b1b109a7 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -309,6 +309,15 @@ config MMU_NOTIFIER bool select SRCU +config OOM_SCORE_NOTIFIER + bool "OOM score notifier" + default n + help + This creates a notifier for process oom_score_adj status. + It create events for new, updated or freed tasks and + are used to build a mirrored task list in + lowmemorykiller. + config KSM bool "Enable KSM for page merging" depends on MMU @@ -557,7 +566,7 @@ config ZPOOL zsmalloc. config ZBUD - tristate "Low density storage for compressed pages" + tristate "Low (Up to 2x) density storage for compressed pages" default n help A special purpose allocator for storing compressed pages. @@ -566,6 +575,16 @@ config ZBUD deterministic reclaim properties that make it preferable to a higher density approach when reclaim will be used. +config Z3FOLD + tristate "Up to 3x density storage for compressed pages" + depends on ZPOOL + default n + help + A special purpose allocator for storing compressed pages. + It is designed to store up to three compressed pages per physical + page. It is a ZBUD derivative so the simplicity and determinism are + still there. + config ZSMALLOC tristate "Memory allocator for compressed pages" depends on MMU diff --git a/mm/Makefile b/mm/Makefile index 04d48b46dbe900e4fae7a0426148e9100004b5fa..0919d3bea38bf89ccb8a68771e682b49328d7b78 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o obj-$(CONFIG_ZPOOL) += zpool.o obj-$(CONFIG_ZBUD) += zbud.o obj-$(CONFIG_ZSMALLOC) += zsmalloc.o +obj-$(CONFIG_Z3FOLD) += z3fold.o obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o obj-$(CONFIG_CMA) += cma.o obj-$(CONFIG_MEMORY_BALLOON) += balloon_compaction.o diff --git a/mm/huge_memory.c b/mm/huge_memory.c index d64d48ca789cb90baa2843877bb9479882f9aa96..f8110ddbe9196432b48cb2675834c0ee75033a55 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4,6 +4,11 @@ * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/mm/mmap.c b/mm/mmap.c index c9d2061257ace2160920f8fd0b755853d7cf8c76..7c7027403027cde2e7cbcd6aa6f3977410fb3298 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1348,6 +1348,9 @@ unsigned long do_mmap(struct file *file, unsigned long addr, *populate = 0; + while (file && (file->f_mode & FMODE_NONMAPPABLE)) + file = file->f_op->get_lower_file(file); + if (!len) return -EINVAL; diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 67237b7cb17734957d5c53e69499e5ef8ca21167..cbf88958e7be548f2bf43765915de717c12b88a8 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -583,6 +583,11 @@ void oom_kill_process(struct oom_control *oc, struct task_struct *p, * space under its control. */ do_send_sig_info(SIGKILL, SEND_SIG_FORCED, victim, true); + trace_oom_sigkill(victim->pid, victim->comm, + victim_points, + get_mm_rss(victim->mm), + oc->gfp_mask); + mark_oom_victim(victim); pr_err("Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB\n", task_pid_nr(victim), victim->comm, K(victim->mm->total_vm), diff --git a/mm/page_alloc.c b/mm/page_alloc.c index b5368a3e61209ac16701d8de4e3f17e15d02f450..7f81b8addee0c2c16012e712cfb056c4bb6774c7 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -13,6 +13,11 @@ * Per cpu hot/cold page lists, bulk allocation, Martin J. Bligh, Sept 2002 * (lots of bits borrowed from Ingo Molnar & Andrew Morton) */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -2827,6 +2832,7 @@ void warn_alloc_failed(gfp_t gfp_mask, unsigned int order, const char *fmt, ...) pr_warn("%s: page allocation failure: order:%u, mode:0x%x\n", current->comm, order, gfp_mask); + trace_mm_page_alloc_fail(order, gfp_mask); dump_stack(); if (!should_suppress_show_mem()) show_mem(filter); @@ -3390,6 +3396,9 @@ retry_cpuset: kmemcheck_pagealloc_alloc(page, order, gfp_mask); trace_mm_page_alloc(page, order, alloc_mask, ac.migratetype); + if (order > 1) + trace_mm_page_alloc_highorder(page, order, + alloc_mask, ac.migratetype); out: /* diff --git a/mm/slub.c b/mm/slub.c index 675e6efd5c8f83314b78274f750d2bfb11d7d5ee..e082537d63389e7cff2bb3424242df2371fdf962 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -8,6 +8,11 @@ * (C) 2007 SGI, Christoph Lameter * (C) 2011 Linux Foundation, Christoph Lameter */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include /* struct reclaim_state */ diff --git a/mm/z3fold.c b/mm/z3fold.c new file mode 100644 index 0000000000000000000000000000000000000000..189267ca1b338e31359ce71b924814bca946b98a --- /dev/null +++ b/mm/z3fold.c @@ -0,0 +1,1108 @@ +/* + * z3fold.c + * + * This implementation is based on zbud written by Seth Jennings. + * + * z3fold is an special purpose allocator for storing compressed pages. It + * can store up to three compressed pages per page which improves the + * compression ratio of zbud while retaining its main concepts (e. g. always + * storing an integral number of objects per page) and simplicity. + * It still has simple and deterministic reclaim properties that make it + * preferable to a higher density approach (with no requirement on integral + * number of object per page) when reclaim is used. + * + * As in zbud, pages are divided into "chunks". The size of the chunks is + * fixed at compile time and is determined by NCHUNKS_ORDER below. + * + * z3fold doesn't export any API and is meant to be used via zpool API. + */ +/* + * Copyright (C) 2016 Sony Mobile Communications 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/***************** + * Structures +*****************/ +struct z3fold_pool; +struct z3fold_ops { + int (*evict)(struct z3fold_pool *pool, unsigned long handle); +}; + +enum buddy { + HEADLESS = 0, + FIRST, + MIDDLE, + LAST, + BUDDIES_MAX +}; + +/* + * struct z3fold_header - z3fold page metadata occupying first chunks of each + * z3fold page, except for HEADLESS pages + * @buddy: links the z3fold page into the relevant list in the + * pool + * @page_lock: per-page lock + * @refcount: reference count for the z3fold page + * @work: work_struct for page layout optimization + * @pool: pointer to the pool which this page belongs to + * @cpu: CPU which this page "belongs" to + * @first_chunks: the size of the first buddy in chunks, 0 if free + * @middle_chunks: the size of the middle buddy in chunks, 0 if free + * @last_chunks: the size of the last buddy in chunks, 0 if free + * @first_num: the starting number (for the first handle) + */ +struct z3fold_header { + struct list_head buddy; + spinlock_t page_lock; + struct kref refcount; + struct work_struct work; + struct z3fold_pool *pool; + short cpu; + unsigned short first_chunks; + unsigned short middle_chunks; + unsigned short last_chunks; + unsigned short start_middle; + unsigned short first_num:2; +}; + +/* + * NCHUNKS_ORDER determines the internal allocation granularity, effectively + * adjusting internal fragmentation. It also determines the number of + * freelists maintained in each pool. NCHUNKS_ORDER of 6 means that the + * allocation granularity will be in chunks of size PAGE_SIZE/64. Some chunks + * in the beginning of an allocated page are occupied by z3fold header, so + * NCHUNKS will be calculated to 63 (or 62 in case CONFIG_DEBUG_SPINLOCK=y), + * which shows the max number of free chunks in z3fold page, also there will + * be 63, or 62, respectively, freelists per pool. + */ +#define NCHUNKS_ORDER 6 + +#define CHUNK_SHIFT (PAGE_SHIFT - NCHUNKS_ORDER) +#define CHUNK_SIZE (1 << CHUNK_SHIFT) +#define ZHDR_SIZE_ALIGNED round_up(sizeof(struct z3fold_header), CHUNK_SIZE) +#define ZHDR_CHUNKS (ZHDR_SIZE_ALIGNED >> CHUNK_SHIFT) +#define TOTAL_CHUNKS (PAGE_SIZE >> CHUNK_SHIFT) +#define NCHUNKS ((PAGE_SIZE - ZHDR_SIZE_ALIGNED) >> CHUNK_SHIFT) + +#define BUDDY_MASK (0x3) + +/** + * struct z3fold_pool - stores metadata for each z3fold pool + * @name: pool name + * @lock: protects pool unbuddied/lru lists + * @stale_lock: protects pool stale page list + * @unbuddied: per-cpu array of lists tracking z3fold pages that contain 2- + * buddies; the list each z3fold page is added to depends on + * the size of its free region. + * @lru: list tracking the z3fold pages in LRU order by most recently + * added buddy. + * @stale: list of pages marked for freeing + * @pages_nr: number of z3fold pages in the pool. + * @ops: pointer to a structure of user defined operations specified at + * pool creation time. + * @compact_wq: workqueue for page layout background optimization + * @release_wq: workqueue for safe page release + * @work: work_struct for safe page release + * + * This structure is allocated at pool creation time and maintains metadata + * pertaining to a particular z3fold pool. + */ +struct z3fold_pool { + const char *name; + spinlock_t lock; + spinlock_t stale_lock; + struct list_head *unbuddied; + struct list_head lru; + struct list_head stale; + atomic64_t pages_nr; + const struct z3fold_ops *ops; + struct zpool *zpool; + const struct zpool_ops *zpool_ops; + struct workqueue_struct *compact_wq; + struct workqueue_struct *release_wq; + struct work_struct work; +}; + +/* + * Internal z3fold page flags + */ +enum z3fold_page_flags { + PAGE_HEADLESS = 0, + MIDDLE_CHUNK_MAPPED, + NEEDS_COMPACTING, + PAGE_STALE +}; + +/***************** + * Helpers +*****************/ + +/* Converts an allocation size in bytes to size in z3fold chunks */ +static int size_to_chunks(size_t size) +{ + return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT; +} + +#define for_each_unbuddied_list(_iter, _begin) \ + for ((_iter) = (_begin); (_iter) < NCHUNKS; (_iter)++) + +static void compact_page_work(struct work_struct *w); + +/* Initializes the z3fold header of a newly allocated z3fold page */ +static struct z3fold_header *init_z3fold_page(struct page *page, + struct z3fold_pool *pool) +{ + struct z3fold_header *zhdr = page_address(page); + + INIT_LIST_HEAD(&page->lru); + clear_bit(PAGE_HEADLESS, &page->private); + clear_bit(MIDDLE_CHUNK_MAPPED, &page->private); + clear_bit(NEEDS_COMPACTING, &page->private); + clear_bit(PAGE_STALE, &page->private); + + spin_lock_init(&zhdr->page_lock); + kref_init(&zhdr->refcount); + zhdr->first_chunks = 0; + zhdr->middle_chunks = 0; + zhdr->last_chunks = 0; + zhdr->first_num = 0; + zhdr->start_middle = 0; + zhdr->cpu = -1; + zhdr->pool = pool; + INIT_LIST_HEAD(&zhdr->buddy); + INIT_WORK(&zhdr->work, compact_page_work); + return zhdr; +} + +/* Resets the struct page fields and frees the page */ +static void free_z3fold_page(struct page *page) +{ + __free_page(page); +} + +/* Lock a z3fold page */ +static inline void z3fold_page_lock(struct z3fold_header *zhdr) +{ + spin_lock(&zhdr->page_lock); +} + +/* Try to lock a z3fold page */ +static inline int z3fold_page_trylock(struct z3fold_header *zhdr) +{ + return spin_trylock(&zhdr->page_lock); +} + +/* Unlock a z3fold page */ +static inline void z3fold_page_unlock(struct z3fold_header *zhdr) +{ + spin_unlock(&zhdr->page_lock); +} + +/* + * Encodes the handle of a particular buddy within a z3fold page + * Pool lock should be held as this function accesses first_num + */ +static unsigned long encode_handle(struct z3fold_header *zhdr, enum buddy bud) +{ + unsigned long handle; + + handle = (unsigned long)zhdr; + if (bud != HEADLESS) + handle += (bud + zhdr->first_num) & BUDDY_MASK; + return handle; +} + +/* Returns the z3fold page where a given handle is stored */ +static struct z3fold_header *handle_to_z3fold_header(unsigned long handle) +{ + return (struct z3fold_header *)(handle & PAGE_MASK); +} + +/* Returns buddy number */ +static enum buddy handle_to_buddy(unsigned long handle) +{ + struct z3fold_header *zhdr = handle_to_z3fold_header(handle); + return (handle - zhdr->first_num) & BUDDY_MASK; +} + +static void __release_z3fold_page(struct z3fold_header *zhdr, bool locked) +{ + struct page *page = virt_to_page(zhdr); + struct z3fold_pool *pool = zhdr->pool; + + WARN_ON(!list_empty(&zhdr->buddy)); + set_bit(PAGE_STALE, &page->private); + clear_bit(NEEDS_COMPACTING, &page->private); + spin_lock(&pool->lock); + if (!list_empty(&page->lru)) + list_del(&page->lru); + spin_unlock(&pool->lock); + if (locked) + z3fold_page_unlock(zhdr); + spin_lock(&pool->stale_lock); + list_add(&zhdr->buddy, &pool->stale); + queue_work(pool->release_wq, &pool->work); + spin_unlock(&pool->stale_lock); +} + +static void __attribute__((__unused__)) + release_z3fold_page(struct kref *ref) +{ + struct z3fold_header *zhdr = container_of(ref, struct z3fold_header, + refcount); + __release_z3fold_page(zhdr, false); +} + +static void release_z3fold_page_locked(struct kref *ref) +{ + struct z3fold_header *zhdr = container_of(ref, struct z3fold_header, + refcount); + WARN_ON(z3fold_page_trylock(zhdr)); + __release_z3fold_page(zhdr, true); +} + +static void release_z3fold_page_locked_list(struct kref *ref) +{ + struct z3fold_header *zhdr = container_of(ref, struct z3fold_header, + refcount); + spin_lock(&zhdr->pool->lock); + list_del_init(&zhdr->buddy); + spin_unlock(&zhdr->pool->lock); + + WARN_ON(z3fold_page_trylock(zhdr)); + __release_z3fold_page(zhdr, true); +} + +static void free_pages_work(struct work_struct *w) +{ + struct z3fold_pool *pool = container_of(w, struct z3fold_pool, work); + + spin_lock(&pool->stale_lock); + while (!list_empty(&pool->stale)) { + struct z3fold_header *zhdr = list_first_entry(&pool->stale, + struct z3fold_header, buddy); + struct page *page = virt_to_page(zhdr); + + list_del(&zhdr->buddy); + if (WARN_ON(!test_bit(PAGE_STALE, &page->private))) + continue; + spin_unlock(&pool->stale_lock); + cancel_work_sync(&zhdr->work); + free_z3fold_page(page); + cond_resched(); + spin_lock(&pool->stale_lock); + } + spin_unlock(&pool->stale_lock); +} + +/* + * Returns the number of free chunks in a z3fold page. + * NB: can't be used with HEADLESS pages. + */ +static int num_free_chunks(struct z3fold_header *zhdr) +{ + int nfree; + /* + * If there is a middle object, pick up the bigger free space + * either before or after it. Otherwise just subtract the number + * of chunks occupied by the first and the last objects. + */ + if (zhdr->middle_chunks != 0) { + int nfree_before = zhdr->first_chunks ? + 0 : zhdr->start_middle - ZHDR_CHUNKS; + int nfree_after = zhdr->last_chunks ? + 0 : TOTAL_CHUNKS - + (zhdr->start_middle + zhdr->middle_chunks); + nfree = max(nfree_before, nfree_after); + } else + nfree = NCHUNKS - zhdr->first_chunks - zhdr->last_chunks; + return nfree; +} + +static inline void *mchunk_memmove(struct z3fold_header *zhdr, + unsigned short dst_chunk) +{ + void *beg = zhdr; + return memmove(beg + (dst_chunk << CHUNK_SHIFT), + beg + (zhdr->start_middle << CHUNK_SHIFT), + zhdr->middle_chunks << CHUNK_SHIFT); +} + +#define BIG_CHUNK_GAP 3 +/* Has to be called with lock held */ +static int z3fold_compact_page(struct z3fold_header *zhdr) +{ + struct page *page = virt_to_page(zhdr); + + if (test_bit(MIDDLE_CHUNK_MAPPED, &page->private)) + return 0; /* can't move middle chunk, it's used */ + + if (zhdr->middle_chunks == 0) + return 0; /* nothing to compact */ + + if (zhdr->first_chunks == 0 && zhdr->last_chunks == 0) { + /* move to the beginning */ + mchunk_memmove(zhdr, ZHDR_CHUNKS); + zhdr->first_chunks = zhdr->middle_chunks; + zhdr->middle_chunks = 0; + zhdr->start_middle = 0; + zhdr->first_num++; + return 1; + } + + /* + * moving data is expensive, so let's only do that if + * there's substantial gain (at least BIG_CHUNK_GAP chunks) + */ + if (zhdr->first_chunks != 0 && zhdr->last_chunks == 0 && + zhdr->start_middle - (zhdr->first_chunks + ZHDR_CHUNKS) >= + BIG_CHUNK_GAP) { + mchunk_memmove(zhdr, zhdr->first_chunks + ZHDR_CHUNKS); + zhdr->start_middle = zhdr->first_chunks + ZHDR_CHUNKS; + return 1; + } else if (zhdr->last_chunks != 0 && zhdr->first_chunks == 0 && + TOTAL_CHUNKS - (zhdr->last_chunks + zhdr->start_middle + + zhdr->middle_chunks) >= + BIG_CHUNK_GAP) { + unsigned short new_start = TOTAL_CHUNKS - zhdr->last_chunks - + zhdr->middle_chunks; + mchunk_memmove(zhdr, new_start); + zhdr->start_middle = new_start; + return 1; + } + + return 0; +} + +static void do_compact_page(struct z3fold_header *zhdr, bool locked) +{ + struct z3fold_pool *pool = zhdr->pool; + struct page *page; + struct list_head *unbuddied; + int fchunks; + + page = virt_to_page(zhdr); + if (locked) + WARN_ON(z3fold_page_trylock(zhdr)); + else + z3fold_page_lock(zhdr); + if (WARN_ON(!test_and_clear_bit(NEEDS_COMPACTING, &page->private))) { + z3fold_page_unlock(zhdr); + return; + } + spin_lock(&pool->lock); + list_del_init(&zhdr->buddy); + spin_unlock(&pool->lock); + + if (kref_put(&zhdr->refcount, release_z3fold_page_locked)) { + atomic64_dec(&pool->pages_nr); + return; + } + + z3fold_compact_page(zhdr); + unbuddied = get_cpu_ptr(pool->unbuddied); + fchunks = num_free_chunks(zhdr); + if (fchunks < NCHUNKS && + (!zhdr->first_chunks || !zhdr->middle_chunks || + !zhdr->last_chunks)) { + /* the page's not completely free and it's unbuddied */ + spin_lock(&pool->lock); + list_add(&zhdr->buddy, &unbuddied[fchunks]); + spin_unlock(&pool->lock); + zhdr->cpu = smp_processor_id(); + } + put_cpu_ptr(pool->unbuddied); + z3fold_page_unlock(zhdr); +} + +static void compact_page_work(struct work_struct *w) +{ + struct z3fold_header *zhdr = container_of(w, struct z3fold_header, + work); + + do_compact_page(zhdr, false); +} + + +/* + * API Functions + */ + +/** + * z3fold_create_pool() - create a new z3fold pool + * @name: pool name + * @gfp: gfp flags when allocating the z3fold pool structure + * @ops: user-defined operations for the z3fold pool + * + * Return: pointer to the new z3fold pool or NULL if the metadata allocation + * failed. + */ +static struct z3fold_pool *z3fold_create_pool(const char *name, gfp_t gfp, + const struct z3fold_ops *ops) +{ + struct z3fold_pool *pool = NULL; + int i, cpu; + + pool = kzalloc(sizeof(struct z3fold_pool), gfp); + if (!pool) + goto out; + spin_lock_init(&pool->lock); + spin_lock_init(&pool->stale_lock); + pool->unbuddied = __alloc_percpu(sizeof(struct list_head)*NCHUNKS, 2); + for_each_possible_cpu(cpu) { + struct list_head *unbuddied = + per_cpu_ptr(pool->unbuddied, cpu); + for_each_unbuddied_list(i, 0) + INIT_LIST_HEAD(&unbuddied[i]); + } + INIT_LIST_HEAD(&pool->lru); + INIT_LIST_HEAD(&pool->stale); + atomic64_set(&pool->pages_nr, 0); + pool->name = name; + pool->compact_wq = create_singlethread_workqueue(pool->name); + if (!pool->compact_wq) + goto out; + pool->release_wq = create_singlethread_workqueue(pool->name); + if (!pool->release_wq) + goto out_wq; + INIT_WORK(&pool->work, free_pages_work); + pool->ops = ops; + return pool; + +out_wq: + destroy_workqueue(pool->compact_wq); +out: + kfree(pool); + return NULL; +} + +/** + * z3fold_destroy_pool() - destroys an existing z3fold pool + * @pool: the z3fold pool to be destroyed + * + * The pool should be emptied before this function is called. + */ +static void z3fold_destroy_pool(struct z3fold_pool *pool) +{ + destroy_workqueue(pool->release_wq); + destroy_workqueue(pool->compact_wq); + kfree(pool); +} + +/** + * z3fold_alloc() - allocates a region of a given size + * @pool: z3fold pool from which to allocate + * @size: size in bytes of the desired allocation + * @gfp: gfp flags used if the pool needs to grow + * @handle: handle of the new allocation + * + * This function will attempt to find a free region in the pool large enough to + * satisfy the allocation request. A search of the unbuddied lists is + * performed first. If no suitable free region is found, then a new page is + * allocated and added to the pool to satisfy the request. + * + * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used + * as z3fold pool pages. + * + * Return: 0 if success and handle is set, otherwise -EINVAL if the size or + * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate + * a new page. + */ +static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp, + unsigned long *handle) +{ + int chunks = 0, i, freechunks; + struct z3fold_header *zhdr = NULL; + struct page *page = NULL; + enum buddy bud; + bool can_sleep = (gfp & __GFP_RECLAIM) == __GFP_RECLAIM; + + if (!size || (gfp & __GFP_HIGHMEM)) + return -EINVAL; + + if (size > PAGE_SIZE) + return -ENOSPC; + + if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE) + bud = HEADLESS; + else { + struct list_head *unbuddied; + chunks = size_to_chunks(size); + +lookup: + /* First, try to find an unbuddied z3fold page. */ + unbuddied = get_cpu_ptr(pool->unbuddied); + for_each_unbuddied_list(i, chunks) { + struct list_head *l = &unbuddied[i]; + + zhdr = list_first_entry_or_null(READ_ONCE(l), + struct z3fold_header, buddy); + + if (!zhdr) + continue; + + /* Re-check under lock. */ + spin_lock(&pool->lock); + l = &unbuddied[i]; + if (unlikely(zhdr != list_first_entry(READ_ONCE(l), + struct z3fold_header, buddy)) || + !z3fold_page_trylock(zhdr)) { + spin_unlock(&pool->lock); + put_cpu_ptr(pool->unbuddied); + goto lookup; + } + list_del_init(&zhdr->buddy); + zhdr->cpu = -1; + spin_unlock(&pool->lock); + + page = virt_to_page(zhdr); + if (test_bit(NEEDS_COMPACTING, &page->private)) { + z3fold_page_unlock(zhdr); + zhdr = NULL; + put_cpu_ptr(pool->unbuddied); + if (can_sleep) + cond_resched(); + goto lookup; + } + + /* + * this page could not be removed from its unbuddied + * list while pool lock was held, and then we've taken + * page lock so kref_put could not be called before + * we got here, so it's safe to just call kref_get() + */ + kref_get(&zhdr->refcount); + break; + } + put_cpu_ptr(pool->unbuddied); + + if (zhdr) { + if (zhdr->first_chunks == 0) { + if (zhdr->middle_chunks != 0 && + chunks >= zhdr->start_middle) + bud = LAST; + else + bud = FIRST; + } else if (zhdr->last_chunks == 0) + bud = LAST; + else if (zhdr->middle_chunks == 0) + bud = MIDDLE; + else { + if (kref_put(&zhdr->refcount, + release_z3fold_page_locked)) + atomic64_dec(&pool->pages_nr); + else + z3fold_page_unlock(zhdr); + pr_err("No free chunks in unbuddied\n"); + WARN_ON(1); + goto lookup; + } + goto found; + } + bud = FIRST; + } + + page = NULL; + if (can_sleep) { + spin_lock(&pool->stale_lock); + zhdr = list_first_entry_or_null(&pool->stale, + struct z3fold_header, buddy); + /* + * Before allocating a page, let's see if we can take one from + * the stale pages list. cancel_work_sync() can sleep so we + * limit this case to the contexts where we can sleep + */ + if (zhdr) { + list_del(&zhdr->buddy); + spin_unlock(&pool->stale_lock); + cancel_work_sync(&zhdr->work); + page = virt_to_page(zhdr); + } else { + spin_unlock(&pool->stale_lock); + } + } + if (!page) + page = alloc_page(gfp); + + if (!page) + return -ENOMEM; + + atomic64_inc(&pool->pages_nr); + zhdr = init_z3fold_page(page, pool); + + if (bud == HEADLESS) { + set_bit(PAGE_HEADLESS, &page->private); + goto headless; + } + z3fold_page_lock(zhdr); + +found: + if (bud == FIRST) + zhdr->first_chunks = chunks; + else if (bud == LAST) + zhdr->last_chunks = chunks; + else { + zhdr->middle_chunks = chunks; + zhdr->start_middle = zhdr->first_chunks + ZHDR_CHUNKS; + } + + if (zhdr->first_chunks == 0 || zhdr->last_chunks == 0 || + zhdr->middle_chunks == 0) { + struct list_head *unbuddied = get_cpu_ptr(pool->unbuddied); + + /* Add to unbuddied list */ + freechunks = num_free_chunks(zhdr); + spin_lock(&pool->lock); + list_add(&zhdr->buddy, &unbuddied[freechunks]); + spin_unlock(&pool->lock); + zhdr->cpu = smp_processor_id(); + put_cpu_ptr(pool->unbuddied); + } + +headless: + spin_lock(&pool->lock); + /* Add/move z3fold page to beginning of LRU */ + if (!list_empty(&page->lru)) + list_del(&page->lru); + + list_add(&page->lru, &pool->lru); + + *handle = encode_handle(zhdr, bud); + spin_unlock(&pool->lock); + if (bud != HEADLESS) + z3fold_page_unlock(zhdr); + + return 0; +} + +/** + * z3fold_free() - frees the allocation associated with the given handle + * @pool: pool in which the allocation resided + * @handle: handle associated with the allocation returned by z3fold_alloc() + * + * In the case that the z3fold page in which the allocation resides is under + * reclaim, as indicated by the PG_reclaim flag being set, this function + * only sets the first|last_chunks to 0. The page is actually freed + * once both buddies are evicted (see z3fold_reclaim_page() below). + */ +static void z3fold_free(struct z3fold_pool *pool, unsigned long handle) +{ + struct z3fold_header *zhdr; + struct page *page; + enum buddy bud; + + zhdr = handle_to_z3fold_header(handle); + page = virt_to_page(zhdr); + + if (test_bit(PAGE_HEADLESS, &page->private)) { + /* HEADLESS page stored */ + bud = HEADLESS; + } else { + z3fold_page_lock(zhdr); + bud = handle_to_buddy(handle); + + switch (bud) { + case FIRST: + zhdr->first_chunks = 0; + break; + case MIDDLE: + zhdr->middle_chunks = 0; + zhdr->start_middle = 0; + break; + case LAST: + zhdr->last_chunks = 0; + break; + default: + pr_err("%s: unknown bud %d\n", __func__, bud); + WARN_ON(1); + z3fold_page_unlock(zhdr); + return; + } + } + + if (bud == HEADLESS) { + spin_lock(&pool->lock); + list_del(&page->lru); + spin_unlock(&pool->lock); + free_z3fold_page(page); + atomic64_dec(&pool->pages_nr); + return; + } + + if (kref_put(&zhdr->refcount, release_z3fold_page_locked_list)) { + atomic64_dec(&pool->pages_nr); + return; + } + if (test_and_set_bit(NEEDS_COMPACTING, &page->private)) { + z3fold_page_unlock(zhdr); + return; + } + if (zhdr->cpu < 0 || !cpu_online(zhdr->cpu)) { + spin_lock(&pool->lock); + list_del_init(&zhdr->buddy); + spin_unlock(&pool->lock); + zhdr->cpu = -1; + kref_get(&zhdr->refcount); + do_compact_page(zhdr, true); + return; + } + kref_get(&zhdr->refcount); + queue_work_on(zhdr->cpu, pool->compact_wq, &zhdr->work); + z3fold_page_unlock(zhdr); +} + +/** + * z3fold_reclaim_page() - evicts allocations from a pool page and frees it + * @pool: pool from which a page will attempt to be evicted + * @retires: number of pages on the LRU list for which eviction will + * be attempted before failing + * + * z3fold reclaim is different from normal system reclaim in that it is done + * from the bottom, up. This is because only the bottom layer, z3fold, has + * information on how the allocations are organized within each z3fold page. + * This has the potential to create interesting locking situations between + * z3fold and the user, however. + * + * To avoid these, this is how z3fold_reclaim_page() should be called: + + * The user detects a page should be reclaimed and calls z3fold_reclaim_page(). + * z3fold_reclaim_page() will remove a z3fold page from the pool LRU list and + * call the user-defined eviction handler with the pool and handle as + * arguments. + * + * If the handle can not be evicted, the eviction handler should return + * non-zero. z3fold_reclaim_page() will add the z3fold page back to the + * appropriate list and try the next z3fold page on the LRU up to + * a user defined number of retries. + * + * If the handle is successfully evicted, the eviction handler should + * return 0 _and_ should have called z3fold_free() on the handle. z3fold_free() + * contains logic to delay freeing the page if the page is under reclaim, + * as indicated by the setting of the PG_reclaim flag on the underlying page. + * + * If all buddies in the z3fold page are successfully evicted, then the + * z3fold page can be freed. + * + * Returns: 0 if page is successfully freed, otherwise -EINVAL if there are + * no pages to evict or an eviction handler is not registered, -EAGAIN if + * the retry limit was hit. + */ +static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries) +{ + int i, ret = 0; + struct z3fold_header *zhdr = NULL; + struct page *page = NULL; + struct list_head *pos; + unsigned long first_handle = 0, middle_handle = 0, last_handle = 0; + + spin_lock(&pool->lock); + if (!pool->ops || !pool->ops->evict || retries == 0) { + spin_unlock(&pool->lock); + return -EINVAL; + } + for (i = 0; i < retries; i++) { + if (list_empty(&pool->lru)) { + spin_unlock(&pool->lock); + return -EINVAL; + } + list_for_each_prev(pos, &pool->lru) { + page = list_entry(pos, struct page, lru); + if (test_bit(PAGE_HEADLESS, &page->private)) + /* candidate found */ + break; + + zhdr = page_address(page); + if (!z3fold_page_trylock(zhdr)) + continue; /* can't evict at this point */ + kref_get(&zhdr->refcount); + list_del_init(&zhdr->buddy); + zhdr->cpu = -1; + } + + list_del_init(&page->lru); + spin_unlock(&pool->lock); + + if (!test_bit(PAGE_HEADLESS, &page->private)) { + /* + * We need encode the handles before unlocking, since + * we can race with free that will set + * (first|last)_chunks to 0 + */ + first_handle = 0; + last_handle = 0; + middle_handle = 0; + if (zhdr->first_chunks) + first_handle = encode_handle(zhdr, FIRST); + if (zhdr->middle_chunks) + middle_handle = encode_handle(zhdr, MIDDLE); + if (zhdr->last_chunks) + last_handle = encode_handle(zhdr, LAST); + /* + * it's safe to unlock here because we hold a + * reference to this page + */ + z3fold_page_unlock(zhdr); + } else { + first_handle = encode_handle(zhdr, HEADLESS); + last_handle = middle_handle = 0; + } + + /* Issue the eviction callback(s) */ + if (middle_handle) { + ret = pool->ops->evict(pool, middle_handle); + if (ret) + goto next; + } + if (first_handle) { + ret = pool->ops->evict(pool, first_handle); + if (ret) + goto next; + } + if (last_handle) { + ret = pool->ops->evict(pool, last_handle); + if (ret) + goto next; + } +next: + spin_lock(&pool->lock); + if (test_bit(PAGE_HEADLESS, &page->private)) { + if (ret == 0) { + spin_unlock(&pool->lock); + free_z3fold_page(page); + return 0; + } + } else if (kref_put(&zhdr->refcount, release_z3fold_page)) { + atomic64_dec(&pool->pages_nr); + spin_unlock(&pool->lock); + return 0; + } + + /* + * Add to the beginning of LRU. + * Pool lock has to be kept here to ensure the page has + * not already been released + */ + list_add(&page->lru, &pool->lru); + } + spin_unlock(&pool->lock); + return -EAGAIN; +} + +/** + * z3fold_map() - maps the allocation associated with the given handle + * @pool: pool in which the allocation resides + * @handle: handle associated with the allocation to be mapped + * + * Extracts the buddy number from handle and constructs the pointer to the + * correct starting chunk within the page. + * + * Returns: a pointer to the mapped allocation + */ +static void *z3fold_map(struct z3fold_pool *pool, unsigned long handle) +{ + struct z3fold_header *zhdr; + struct page *page; + void *addr; + enum buddy buddy; + + zhdr = handle_to_z3fold_header(handle); + addr = zhdr; + page = virt_to_page(zhdr); + + if (test_bit(PAGE_HEADLESS, &page->private)) + goto out; + + z3fold_page_lock(zhdr); + buddy = handle_to_buddy(handle); + switch (buddy) { + case FIRST: + addr += ZHDR_SIZE_ALIGNED; + break; + case MIDDLE: + addr += zhdr->start_middle << CHUNK_SHIFT; + set_bit(MIDDLE_CHUNK_MAPPED, &page->private); + break; + case LAST: + addr += PAGE_SIZE - (zhdr->last_chunks << CHUNK_SHIFT); + break; + default: + pr_err("unknown buddy id %d\n", buddy); + WARN_ON(1); + addr = NULL; + break; + } + + z3fold_page_unlock(zhdr); +out: + return addr; +} + +/** + * z3fold_unmap() - unmaps the allocation associated with the given handle + * @pool: pool in which the allocation resides + * @handle: handle associated with the allocation to be unmapped + */ +static void z3fold_unmap(struct z3fold_pool *pool, unsigned long handle) +{ + struct z3fold_header *zhdr; + struct page *page; + enum buddy buddy; + + zhdr = handle_to_z3fold_header(handle); + page = virt_to_page(zhdr); + + if (test_bit(PAGE_HEADLESS, &page->private)) + return; + + z3fold_page_lock(zhdr); + buddy = handle_to_buddy(handle); + if (buddy == MIDDLE) + clear_bit(MIDDLE_CHUNK_MAPPED, &page->private); + z3fold_page_unlock(zhdr); +} + +/** + * z3fold_get_pool_size() - gets the z3fold pool size in pages + * @pool: pool whose size is being queried + * + * Returns: size in pages of the given pool. + */ +static u64 z3fold_get_pool_size(struct z3fold_pool *pool) +{ + return atomic64_read(&pool->pages_nr); +} + +/***************** + * zpool + ****************/ + +static int z3fold_zpool_evict(struct z3fold_pool *pool, unsigned long handle) +{ + if (pool->zpool && pool->zpool_ops && pool->zpool_ops->evict) + return pool->zpool_ops->evict(pool->zpool, handle); + else + return -ENOENT; +} + +static const struct z3fold_ops z3fold_zpool_ops = { + .evict = z3fold_zpool_evict +}; + +static void *z3fold_zpool_create(const char *name, gfp_t gfp, + const struct zpool_ops *zpool_ops, + struct zpool *zpool) +{ + struct z3fold_pool *pool; + + pool = z3fold_create_pool(name, gfp, + zpool_ops ? &z3fold_zpool_ops : NULL); + if (pool) { + pool->zpool = zpool; + pool->zpool_ops = zpool_ops; + pool->name = name; + } + return pool; +} + +static void z3fold_zpool_destroy(void *pool) +{ + z3fold_destroy_pool(pool); +} + +static int z3fold_zpool_malloc(void *pool, size_t size, gfp_t gfp, + unsigned long *handle) +{ + return z3fold_alloc(pool, size, gfp, handle); +} +static void z3fold_zpool_free(void *pool, unsigned long handle) +{ + z3fold_free(pool, handle); +} + +static int z3fold_zpool_shrink(void *pool, unsigned int pages, + unsigned int *reclaimed) +{ + unsigned int total = 0; + int ret = -EINVAL; + + while (total < pages) { + ret = z3fold_reclaim_page(pool, 8); + if (ret < 0) + break; + total++; + } + + if (reclaimed) + *reclaimed = total; + + return ret; +} + +static void *z3fold_zpool_map(void *pool, unsigned long handle, + enum zpool_mapmode mm) +{ + return z3fold_map(pool, handle); +} +static void z3fold_zpool_unmap(void *pool, unsigned long handle) +{ + z3fold_unmap(pool, handle); +} + +static u64 z3fold_zpool_total_size(void *pool) +{ + return z3fold_get_pool_size(pool) * PAGE_SIZE; +} + +static struct zpool_driver z3fold_zpool_driver = { + .type = "z3fold", + .owner = THIS_MODULE, + .create = z3fold_zpool_create, + .destroy = z3fold_zpool_destroy, + .malloc = z3fold_zpool_malloc, + .free = z3fold_zpool_free, + .shrink = z3fold_zpool_shrink, + .map = z3fold_zpool_map, + .unmap = z3fold_zpool_unmap, + .total_size = z3fold_zpool_total_size, +}; + +MODULE_ALIAS("zpool-z3fold"); + +static int __init init_z3fold(void) +{ + /* Make sure the z3fold header is not larger than the page size */ + BUILD_BUG_ON(ZHDR_SIZE_ALIGNED > PAGE_SIZE); + zpool_register_driver(&z3fold_zpool_driver); + + return 0; +} + +static void __exit exit_z3fold(void) +{ + zpool_unregister_driver(&z3fold_zpool_driver); +} + +module_init(init_z3fold); +module_exit(exit_z3fold); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vitaly Wool "); +MODULE_DESCRIPTION("3-Fold Allocator for Compressed Pages"); diff --git a/mm/zpool.c b/mm/zpool.c index fd3ff719c32cb9cf25dafca9d73f789d8071bad5..58aef91eb1e17bcab40cdbd301db691039dc15ee 100644 --- a/mm/zpool.c +++ b/mm/zpool.c @@ -6,6 +6,11 @@ * This is a common frontend for memory storage pool implementations. * Typically, this is used to store compressed memory. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -355,6 +360,32 @@ u64 zpool_get_total_size(struct zpool *zpool) return zpool->driver->total_size(zpool->pool); } +/** + * zpool_compact() - trigger backend-specific pool compaction + * @pool The zpool to compact + * + * This returns the total size in bytes of the pool. + * + * Returns: Number of pages compacted + */ +unsigned long zpool_compact(struct zpool *zpool) +{ + return zpool->driver->compact ? + zpool->driver->compact(zpool->pool) : 0; +} + +/** + * zpool_get_num_compacted() - get the number of migrated/compacted pages + * @stats stats to fill in + * + * Returns: the total number of migrated pages for the pool + */ +unsigned long zpool_get_num_compacted(struct zpool *zpool) +{ + return zpool->driver->get_num_compacted ? + zpool->driver->get_num_compacted(zpool->pool) : 0; +} + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Dan Streetman "); MODULE_DESCRIPTION("Common API for compressed memory storage"); diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 1eb00e3435235054129d24a009bdff5ed4958e4d..46d0741aeecad2bb09b8974b702c1f976104c771 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -10,6 +10,11 @@ * Released under the terms of 3-clause BSD License * Released under the terms of GNU General Public License Version 2.0 */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* * Following is how we use various fields and flags of underlying @@ -449,6 +454,20 @@ static u64 zs_zpool_total_size(void *pool) return zs_get_total_pages(pool) << PAGE_SHIFT; } +static unsigned long zs_zpool_compact(void *pool) +{ + return zs_compact(pool); +} + +static unsigned long zs_zpool_get_compacted(void *pool) +{ + struct zs_pool_stats stats; + + zs_pool_stats(pool, &stats); + return stats.pages_compacted; +} + + static struct zpool_driver zs_zpool_driver = { .type = "zsmalloc", .owner = THIS_MODULE, @@ -460,6 +479,8 @@ static struct zpool_driver zs_zpool_driver = { .map = zs_zpool_map, .unmap = zs_zpool_unmap, .total_size = zs_zpool_total_size, + .compact = zs_zpool_compact, + .get_num_compacted = zs_zpool_get_compacted, }; MODULE_ALIAS("zpool-zsmalloc"); diff --git a/net/Kconfig b/net/Kconfig index d9da78da8cd99451125f0f85b59814048eaf97b0..30d7e4a607d0cabcb860e2efb2daedd7809c152a 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -102,6 +102,14 @@ config ANDROID_PARANOID_NETWORK help none +config NET_ACTIVITY_STATS + bool "Network activity statistics tracking" + default y + help + Network activity statistics are useful for tracking wireless + modem activity on 2G, 3G, 4G wireless networks. Counts number of + transmissions and groups them in specified time buckets. + config NETWORK_SECMARK bool "Security Marking" help diff --git a/net/Makefile b/net/Makefile index e700aa62b1afecfa54e696415779b27d995446cd..fa0c1077b2a12d1590c87044e5824fed6294599f 100644 --- a/net/Makefile +++ b/net/Makefile @@ -79,3 +79,4 @@ obj-y += l3mdev/ endif obj-$(CONFIG_IPC_ROUTER) += ipc_router/ obj-$(CONFIG_RMNET_DATA) += rmnet_data/ +obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o diff --git a/net/activity_stats.c b/net/activity_stats.c new file mode 100644 index 0000000000000000000000000000000000000000..bc568b58c85e299f329378a65db04d840bcd23f6 --- /dev/null +++ b/net/activity_stats.c @@ -0,0 +1,121 @@ +/* net/activity_stats.c + * + * Copyright (C) 2010 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. + * + * Author: Mike Chan (mike@android.com) + */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ + +#include +#include +#include +#include + +/* + * Track transmission rates in buckets (power of 2). + * 1,2,4,8...512 seconds. + * + * Buckets represent the count of network transmissions at least + * N seconds apart, where N is 1 << bucket index. + */ +#define BUCKET_MAX 10 + +/* Track network activity frequency */ +static unsigned long activity_stats[BUCKET_MAX]; +static ktime_t last_transmit; +static ktime_t suspend_time; +static DEFINE_SPINLOCK(activity_lock); + +void activity_stats_update(void) +{ + int i; + unsigned long flags; + ktime_t now; + s64 delta; + + spin_lock_irqsave(&activity_lock, flags); + now = ktime_get(); + delta = ktime_to_ns(ktime_sub(now, last_transmit)); + + for (i = BUCKET_MAX - 1; i >= 0; i--) { + /* + * Check if the time delta between network activity is within the + * minimum bucket range. + */ + if (delta < (1000000000ULL << i)) + continue; + + activity_stats[i]++; + last_transmit = now; + break; + } + spin_unlock_irqrestore(&activity_lock, flags); +} + +static int activity_stats_show(struct seq_file *m, void *v) +{ + int i; + + seq_printf(m, "Min Bucket(sec) Count\n"); + + for (i = 0; i < BUCKET_MAX; i++) { + seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]); + } + + return 0; +} + +static int activity_stats_notifier(struct notifier_block *nb, + unsigned long event, void *dummy) +{ + switch (event) { + case PM_SUSPEND_PREPARE: + suspend_time = ktime_get_real(); + break; + + case PM_POST_SUSPEND: + suspend_time = ktime_sub(ktime_get_real(), suspend_time); + last_transmit = ktime_sub(last_transmit, suspend_time); + } + + return 0; +} + +static int activity_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, activity_stats_show, PDE_DATA(inode)); +} + +static const struct file_operations activity_stats_fops = { + .open = activity_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static struct notifier_block activity_stats_notifier_block = { + .notifier_call = activity_stats_notifier, +}; + +static int __init activity_stats_init(void) +{ + proc_create("activity", S_IRUGO, + init_net.proc_net_stat, &activity_stats_fops); + return register_pm_notifier(&activity_stats_notifier_block); +} + +subsys_initcall(activity_stats_init); + diff --git a/net/core/ethtool.c b/net/core/ethtool.c index b6bca625b0d2dca2ab586353f6aebdcad3ce7862..02fe0a88c391a5044169533cfa44b1a40a4b960f 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -930,11 +930,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr) static int ethtool_get_wol(struct net_device *dev, char __user *useraddr) { - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; + struct ethtool_wolinfo wol; if (!dev->ethtool_ops->get_wol) return -EOPNOTSUPP; + memset(&wol, 0, sizeof(struct ethtool_wolinfo)); + wol.cmd = ETHTOOL_GWOL; dev->ethtool_ops->get_wol(dev, &wol); if (copy_to_user(useraddr, &wol, sizeof(wol))) diff --git a/net/ipc_router/ipc_router_core.c b/net/ipc_router/ipc_router_core.c index bfb76a84be7343c769e44286e441a2e0229c64db..cd3cf795cf1a092bcacbd139a329685d238237b2 100644 --- a/net/ipc_router/ipc_router_core.c +++ b/net/ipc_router/ipc_router_core.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -4294,6 +4299,8 @@ void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt, } } mutex_unlock(&xprt_info->rx_lock_lhb2); + if (rport_ptr) + kref_put(&rport_ptr->ref, ipc_router_release_rport); queue_work(xprt_info->workqueue, &xprt_info->read_data); } diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index ccc5980797fcdb9ed3a1003db47e4e8cb7180279..54cf0f2251168b85a1c4bc03b3929ed30dbd9e04 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -12,6 +12,11 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -109,12 +114,14 @@ static void __inet_put_port(struct sock *sk) struct inet_bind_bucket *tb; spin_lock(&head->lock); - tb = inet_csk(sk)->icsk_bind_hash; - __sk_del_bind_node(sk); - tb->num_owners--; - inet_csk(sk)->icsk_bind_hash = NULL; - inet_sk(sk)->inet_num = 0; - inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb); + if (inet_csk(sk)->icsk_bind_hash) { + tb = inet_csk(sk)->icsk_bind_hash; + __sk_del_bind_node(sk); + tb->num_owners--; + inet_csk(sk)->icsk_bind_hash = NULL; + inet_sk(sk)->inet_num = 0; + inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb); + } spin_unlock(&head->lock); } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 2f26b0d1f1d7dd8aa73fede84921ba12ca690697..543eb29f87a73a0ebb1ba6fab38283ffcd3f7ebd 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -244,6 +244,11 @@ * * TCP_CLOSE socket is finished */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "TCP: " fmt @@ -269,6 +274,7 @@ #include #include #include +#include #include #include @@ -1299,6 +1305,10 @@ out: tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); out_nopush: release_sock(sk); + + if (copied + copied_syn) + uid_stat_tcp_snd(from_kuid(&init_user_ns, current_uid()), + copied + copied_syn); return copied + copied_syn; do_fault: @@ -1576,6 +1586,8 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, if (copied > 0) { tcp_recv_skb(sk, seq, &offset); tcp_cleanup_rbuf(sk, copied); + uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), + copied); } return copied; } @@ -1909,6 +1921,10 @@ skip_copy: tcp_cleanup_rbuf(sk, copied); release_sock(sk); + + if (copied > 0) + uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), + copied); return copied; out: @@ -1917,6 +1933,9 @@ out: recv_urg: err = tcp_recv_urg(sk, msg, len, flags); + if (err > 0) + uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), + err); goto out; recv_sndq: diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 0feede45bd28a9c31f21a75145428f3b06963b7a..ebc45d5d915d1906577ac667443a643820377050 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -25,6 +25,11 @@ * : add ip6_append_data and related functions * for datagram xmit */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -1012,11 +1017,6 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk, } } #endif - if (ipv6_addr_v4mapped(&fl6->saddr) && - !(ipv6_addr_v4mapped(&fl6->daddr) || ipv6_addr_any(&fl6->daddr))) { - err = -EAFNOSUPPORT; - goto out_err_release; - } return 0; diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 94663440d160e0eddc25d8c3755c9c193812c83f..c1603a4b27921885a268e860382726b37717fe15 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -11,6 +11,11 @@ * it under the terms of the GNU General Public License; either * version 2 of the License, as published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -296,7 +301,7 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) } ret = true; } else { - if (e->quota >= skb->len) { + if (e->quota > skb->len) { if (!(q->flags & XT_QUOTA_NO_CHANGE)) e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; ret = !ret; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 5a58f9f380958f886de0f41bb0f34e07fb0502ae..02d8641f86abad53d469231e22699051e9023912 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -14,6 +14,11 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2012 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "hci: %s: " fmt, __func__ diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c index 1fe725d660852bb2422c94d37cc566e793a18464..0487e94a0bb7fb664dd9e16f4f3f9c796647086b 100644 --- a/net/nfc/hci/hcp.c +++ b/net/nfc/hci/hcp.c @@ -14,6 +14,11 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2012 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define pr_fmt(fmt) "hci: %s: " fmt, __func__ diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 71e1f0def5a51b6ab5be270e9f75732844f8a994..7108d0891882deb0112e7660331b9ea8ec341bb5 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -16,6 +16,11 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2012 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/net/wireless/db.txt b/net/wireless/db.txt index 499d98745d4458bb98fc43bcd2f11a524b517f7d..b5be8377a0747c1fcdb94dc4d747c8b757254c2b 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -1,1402 +1,345 @@ # This is the world regulatory domain country 00: - (2402 - 2472 @ 40), (20) + (2402 - 2472 @ 20), (20) # Channel 12 - 13. - (2457 - 2482 @ 40), (20), PASSIVE-SCAN, NO-IBSS - # Channel 14. Only JP enables this and for 802.11b only - (2474 - 2494 @ 20), (20), PASSIVE-SCAN, NO-IBSS, NO-OFDM + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS # Channel 36 - 48 (5170 - 5250 @ 80), (20), PASSIVE-SCAN, NO-IBSS - (5250 - 5330 @ 80), (20), PASSIVE-SCAN, NO-IBSS - (5490 - 5710 @ 80), (20), PASSIVE-SCAN, NO-IBSS + (5250 - 5330 @ 80), (20), PASSIVE-SCAN, DFS, NO-IBSS + (5490 - 5730 @ 80), (20), PASSIVE-SCAN, DFS, NO-IBSS # NB: 5260 MHz - 5700 MHz requies DFS # Channel 149 - 165 - (5735 - 5835 @ 80), (20), PASSIVE-SCAN, NO-IBSS - # IEEE 802.11ad (60GHz), channels 1..3 - (57240 - 63720 @ 2160), (0) - - -country AE: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country AF: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country AI: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5735 - 5835 @ 80), (14), PASSIVE-SCAN, NO-IBSS -country AL: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5150 - 5250 @ 80), (23), AUTO-BW - (5250 - 5350 @ 80), (23), DFS, AUTO-BW - (5470 - 5710 @ 160), (30), DFS - -country AM: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 20), (18) - (5250 - 5330 @ 20), (18), DFS - -country AN: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country AR: - (2402 - 2482 @ 40), (36) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (36), AUTO-BW - (5490 - 5590 @ 80), (36) - (5650 - 5730 @ 80), (36) - (5735 - 5835 @ 80), (36) - # 60 gHz band channels 1-3 - (57240 - 63720 @ 2160), (40), NO-OUTDOOR - -country AS: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5850 @ 80), (30) - -country AT: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) +country AE: DFS-FCC + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (23), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN + +country AR: DFS-FCC + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (17), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5590 @ 80), (24), PASSIVE-SCAN, DFS + (5650 - 5710 @ 40), (24), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN country AU: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5590 @ 80), (24), DFS - (5650 - 5730 @ 80), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-4 - (57240 - 65880 @ 2160), (43), NO-OUTDOOR - -country AW: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country AZ: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (18), AUTO-BW - (5250 - 5330 @ 80), (18), DFS, AUTO-BW - -country BA: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country BB: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5735 - 5835 @ 80), (30) - -country BD: - (2402 - 2482 @ 40), (20) - (5735 - 5835 @ 80), (30) - -country BE: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country BF: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country BG: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country BH: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 20), (23) - (5735 - 5835 @ 20), (33) - -country BL: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country BM: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country BN: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (20), AUTO-BW - (5250 - 5330 @ 80), (20), DFS, AUTO-BW - (5735 - 5835 @ 80), (20) - -country BO: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5250 - 5330 @ 80), (30), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3, FCC - (57240 - 63720 @ 2160), (40) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5590 @ 80), (23), PASSIVE-SCAN, DFS + (5650 - 5710 @ 40), (23), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (14), PASSIVE-SCAN + +country BH: DFS-ETSI + (2402 - 2482 @ 20), (20) + (5170 - 5250 @ 20), (20), PASSIVE-SCAN + (5250 - 5330 @ 20), (20), PASSIVE-SCAN, DFS + (5735 - 5835 @ 20), (20), PASSIVE-SCAN country BR: DFS-FCC - (2402 - 2482 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3 - (57240 - 63720 @ 2160), (40) - -country BS: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country BT: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country BY: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country BZ: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (23) - (5490 - 5730 @ 160), (30) - (5735 - 5835 @ 80), (30) + (2402 - 2472 @ 20), (30) + # Channel 12 - 13. + (2457 - 2482 @ 20), (30), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (24), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (24), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN country CA: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3 - (57240 - 63720 @ 2160), (40) - -country CF: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 40), (24) - (5250 - 5330 @ 40), (24), DFS - (5490 - 5730 @ 40), (24), DFS - (5735 - 5835 @ 40), (30) - -country CH: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country CI: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country CL: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (20) - (5735 - 5835 @ 80), (20) - # 60 gHz band channels 1-3 - (57240 - 63720 @ 2160), (50), NO-OUTDOOR + (2402 - 2472 @ 20), (30) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5590 @ 80), (23), PASSIVE-SCAN, DFS + (5650 - 5710 @ 40), (23), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN + +country CL: DFS-ETSI + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (20), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (20), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 80), (20), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (20), PASSIVE-SCAN country CN: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5735 - 5835 @ 80), (33) - # 60 gHz band channels 2,3: 44dBm - (59400 - 63720 @ 2160), (44) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5735 - 5835 @ 80), (27), PASSIVE-SCAN country CO: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country CR: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 20), (24) - (5250 - 5330 @ 20), (24), DFS - (5490 - 5730 @ 20), (24), DFS - (5735 - 5835 @ 20), (30) - # 60 gHz band channels 1-3 - (57240 - 63720 @ 2160), (30) - -country CX: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country CY: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -# Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf -# and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf -country CZ: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -# Data from "Frequenznutzungsplan" (as published in April 2008), downloaded from -# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38448/publicationFile/2659/Frequenznutzungsplan2008_Id17448pdf.pdf -# For the 5GHz range also see -# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38216/publicationFile/6579/WLAN5GHzVfg7_2010_28042010pdf.pdf - -country DE: DFS-ETSI - # entries 279004 and 280006 - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country DK: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country DM: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5735 - 5835 @ 80), (30) - -country DO: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5735 - 5835 @ 80), (30) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (17), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (24), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN country DZ: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5670 @ 160), (23), DFS + (2402 - 2482 @ 20), (20) + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5670 @ 160), (23), PASSIVE-SCAN, DFS country EC: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 20), (24) - (5250 - 5330 @ 20), (24), DFS - (5490 - 5730 @ 20), (24), DFS + (2402 - 2482 @ 20), (20) + (5170 - 5250 @ 20), (17), PASSIVE-SCAN + (5250 - 5330 @ 20), (24), PASSIVE-SCAN, DFS + (5490 - 5710 @ 20), (24), PASSIVE-SCAN, DFS (5735 - 5835 @ 20), (30) - # 60 gHz band channels 1-3, FCC - (57240 - 63720 @ 2160), (40) - -country EE: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) country EG: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 40), (23) - (5250 - 5330 @ 40), (23), DFS - -country ES: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country ET: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country FI: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country FM: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country FR: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country GB: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country GD: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country GE: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (18), AUTO-BW - (5250 - 5330 @ 80), (18), DFS, AUTO-BW - -country GF: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country GH: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country GI: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 20), (23), PASSIVE-SCAN + (5250 - 5330 @ 20), (23), PASSIVE-SCAN, DFS + (5490 - 5710 @ 20), (23), PASSIVE-SCAN, DFS + (5735 - 5835 @ 20), (14), PASSIVE-SCAN + +country EU: DFS-ETSI + # AT,BE,BG,HR,CY,CZ,DK,EE,FI,FR,DE,GR,HU,IE,IT,LV,LT,LU,MT,NL,PL,PT,RO,SK,SI,ES,SE,GB,IS,LI,NO,CH,AL,ME,RS,MK,TR,BA + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (30), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (14), PASSIVE-SCAN country GL: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country GP: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country GR: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country GT: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country GU: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3, FCC - (57240 - 63720 @ 2160), (40) - -country GY: - (2402 - 2482 @ 40), (30) - (5735 - 5835 @ 80), (30) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (30), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (14), PASSIVE-SCAN country HK: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-4, ref: FCC/EU - (57240 - 65880 @ 2160), (40) - -country HN: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (24) - (5490 - 5730 @ 160), (24) - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3, FCC - (57240 - 63720 @ 2160), (40) - -country HR: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country HT: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country HU: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (23), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN country ID: # ref: http://www.postel.go.id/content/ID/regulasi/standardisasi/kepdir/bwa%205,8%20ghz.pdf - (2402 - 2482 @ 40), (30) - (5735 - 5815 @ 20), (30) - -country IE: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5735 - 5815 @ 20), (30), PASSIVE-SCAN country IL: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - # 60 gHz band channels 1-4, base on Etsi En 302 567 - (57000 - 66000 @ 2160), (40) + (2402 - 2482 @ 20), (20) + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW -country IN: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (23) - (5735 - 5835 @ 80), (33) +country IN: DFS-ETSI + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5735 - 5835 @ 80), (23), PASSIVE-SCAN country IQ: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country IS: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country IT: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) + (2402 - 2482 @ 20), (19) + (5170 - 5250 @ 80), (21), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (21), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (21), PASSIVE-SCAN, DFS country JM: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3, FCC - (57240 - 63720 @ 2160), (40) + (2402 - 2482 @ 20), (20) + (5735 - 5835 @ 80), (27) country JO: - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23) - (5735 - 5835 @ 80), (23) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) + (2402 - 2482 @ 20), (20) + (5735 - 5835 @ 80), (23), PASSIVE-SCAN country JP: DFS-JP - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (20), AUTO-BW, NO-OUTDOOR - (5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR - (5490 - 5710 @ 160), (20), DFS - # 60 gHz band channels 1-4 - (57240 - 65880 @ 2160), (40) - -country KE: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23) - (5490 - 5570 @ 80), (30), DFS - (5735 - 5775 @ 40), (23) - -country KH: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country KN: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (30), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5735 - 5815 @ 80), (30) + (2402 - 2482 @ 20), (20) + (5170 - 5250 @ 80), (20), PASSIVE-SCAN, AUTO-BW, NO-OUTDOOR + (5250 - 5330 @ 80), (20), PASSIVE-SCAN, DFS, AUTO-BW, NO-OUTDOOR + (5490 - 5710 @ 160), (20), PASSIVE-SCAN, DFS country KR: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (20), AUTO-BW - (5250 - 5330 @ 80), (20), DFS, AUTO-BW - (5490 - 5730 @ 160), (30), DFS - (5735 - 5835 @ 80), (30) - # 60 GHz band channels 1-4, - # ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99 - (57240 - 65880 @ 2160), (43) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (20), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (20), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 80), (20), PASSIVE-SCAN, DFS + (5735 - 5815 @ 80), (20), PASSIVE-SCAN country KW: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - -country KY: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country KZ: - (2402 - 2482 @ 40), (20) - -country LB: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country LC: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (20), AUTO-BW - (5250 - 5330 @ 80), (30), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - (5735 - 5815 @ 80), (30) - -country LI: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country LK: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 20), (24) - (5250 - 5330 @ 20), (24), DFS - (5490 - 5730 @ 20), (24), DFS - (5735 - 5835 @ 20), (30) - -country LS: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country LT: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country LU: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country LV: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) + (2402 - 2482 @ 20), (20) + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW country MA: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 20), (20) (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW country MC: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country MD: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country ME: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country MF: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country MH: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country MK: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country MN: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country MO: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country MP: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country MQ: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country MR: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country MT: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country MU: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country MV: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (20), AUTO-BW - (5250 - 5330 @ 80), (20), DFS, AUTO-BW - (5735 - 5835 @ 80), (20) - -country MW: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (30), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (14) country MX: DFS-FCC - (2402 - 2482 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3, FCC - (57240 - 63720 @ 2160), (40) + (2402 - 2472 @ 20), (30) + # Channel 12 - 13. + (2457 - 2482 @ 20), (30), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (17), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5590 @ 80), (24), PASSIVE-SCAN, DFS + (5650 - 5710 @ 40), (24), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN country MY: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5650 @ 160), (24), DFS - (5735 - 5835 @ 80), (24) - # 60 gHz band channels 1-3 - (57240 - 63720 @ 2160), (40) - -country NA: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5730 @ 160), (30), DFS - (5735 - 5835 @ 80), (33) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (24), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5650 @ 160), (24), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (24), PASSIVE-SCAN country NG: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5250 - 5330 @ 80), (30), DFS - (5735 - 5835 @ 80), (30) - -country NI: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3, FCC - (57240 - 63720 @ 2160), (40) - -country NL: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country NO: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country NP: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (20) - (5735 - 5835 @ 80), (20) + (2402 - 2482 @ 20), (20) + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (23), PASSIVE-SCAN country NZ: DFS-FCC - (2402 - 2482 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5590 @ 80), (23), PASSIVE-SCAN, DFS + (5650 - 5710 @ 40), (23), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (14), PASSIVE-SCAN country OM: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country PA: - (2402 - 2472 @ 40), (36) - (5170 - 5250 @ 80), (23), AUT0-BW - (5250 - 5330 @ 80), (30), AUTO-BW - (5735 - 5835 @ 80), (36) + (2402 - 2482 @ 20), (20) + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (30), PASSIVE-SCAN, DFS country PE: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country PF: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country PG: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (24), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (21), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN country PH: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country PK: - (2402 - 2482 @ 40), (30) - (5735 - 5835 @ 80), (30) - -country PL: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country PM: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5730 @ 160), (24), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN country PR: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -# Public Safety FCCA, FCC4 -# 27dBm [4.9GHz 1/4 rate], 30dBm [1/2 rate], 33dBm [full rate], and 5GHz same as FCC1 -# db.txt cannot express the limitation on 5G so disable all 5G channels for FCC4 -country PS: DFS-FCC - (2402 - 2472 @ 40), (30) - (4940 - 4990 @ 40), (33) - -country PT: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country PW: DFS-FCC - (2402 - 2472 @ 40), (30) + (2402 - 2472 @ 20), (30) (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5730 @ 160), (24), PASSIVE-SCAN, DFS (5735 - 5835 @ 80), (30) country PY: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS + (2402 - 2482 @ 20), (20) + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (24), PASSIVE-SCAN, DFS (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3, FCC - (57240 - 63720 @ 2160), (40) country QA: - (2402 - 2482 @ 40), (20) - (5735 - 5835 @ 80), (30) - -country RE: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country RO: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -# Source: -# http://www.ratel.rs/upload/documents/Plan_namene/Plan_namene-sl_glasnik.pdf -country RS: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country RU: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (23) - (5490 - 5730 @ 160), (30) - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-4 - (57240 - 65880 @ 2160), (40) + (2402 - 2482 @ 20), (10) + (5735 - 5835 @ 80), (14), PASSIVE-SCAN -country RW: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) +country RU: DFS-ETSI + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + # Channel 36 - 48 + (5170 - 5250 @ 80), (20), PASSIVE-SCAN, AUTO-BW + # Channel 52 - 64 + (5250 - 5330 @ 80), (20), PASSIVE-SCAN, DFS, AUTO-BW + # Channel 132-140 + (5650 - 5710 @ 40), (20), PASSIVE-SCAN, DFS + # Channel 149 - 165 + (5735 - 5835 @ 80), (14), PASSIVE-SCAN, NO-IBSS country SA: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country SE: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (30), PASSIVE-SCAN, DFS + (5735 - 5815 @ 80), (14) country SG: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40), NO-OUTDOOR - -country SI: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country SK: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57000 - 66000 @ 2160), (40) - -country SN: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (24) - (5490 - 5730 @ 160), (24) - (5735 - 5835 @ 80), (30) - -country SR: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country SV: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 20), (23) - (5250 - 5330 @ 20), (23), DFS - (5735 - 5835 @ 20), (30) - -country TC: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country TD: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country TG: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 40), (23) - (5250 - 5330 @ 40), (23), DFS - (5490 - 5710 @ 40), (30), DFS + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (24), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (24), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN country TH: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-4 - (57240 - 65880 @ 2160), (40) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (24), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN country TN: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - -country TR: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country TT: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (24) - (5490 - 5730 @ 160), (24) - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3, FCC - (57240 - 63720 @ 2160), (40) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW country TW: DFS-FCC - (2402 - 2472 @ 40), (30) + (2402 - 2472 @ 20), (30) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-3, FCC - (57240 - 63720 @ 2160), (40) - -country TZ: - (2402 - 2482 @ 40), (20) - (5735 - 5835 @ 80), (30) + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (24), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN # Source: # #914 / 06 Sep 2007: http://www.ucrf.gov.ua/uk/doc/nkrz/1196068874 @@ -1406,131 +349,58 @@ country TZ: # rules in the referenced laws. Such a range is used because of # disputable definitions there. country UA: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (20), AUTO-BW - (5250 - 5330 @ 80), (20), DFS, AUTO-BW - (5490 - 5670 @ 160), (20), DFS - (5735 - 5835 @ 80), (20) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (20) - -country UG: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (20), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (20), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5710 @ 160), (20), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (20), PASSIVE-SCAN country US: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (30), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS + (2402 - 2472 @ 20), (30) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (24), AUTO-BW + (5250 - 5330 @ 80), (24), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5730 @ 160), (24), PASSIVE-SCAN, DFS (5735 - 5835 @ 80), (30) - # 5.9ghz band - # reference: https://apps.fcc.gov/edocs_public/attachmatch/FCC-03-324A1.pdf - (5842 - 5863 @ 5), (30) - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5865 - 5885 @ 20), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5895 - 5915 @ 20), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) - # 60g band - # reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255 - # channels 1,2,3,4,5,6 EIRP=40dBm(43dBm peak) - (57240 - 70200 @ 2160), (40) country UY: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-4 - (57240 - 65880 @ 2160), (40) - -country UZ: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - -country VC: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN country VE: DFS-FCC - (2402 - 2482 @ 40), (30) + (2402 - 2482 @ 20), (30) (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5735 - 5835 @ 80), (30) - -country VI: DFS-FCC - (2402 - 2472 @ 40), (30) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - -country VN: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24) - (5250 - 5330 @ 80), (24), DFS - (5490 - 5730 @ 80), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-4 - (57240 - 65880 @ 2160), (40) - -country VU: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW (5735 - 5835 @ 80), (30) -country WF: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - -country WS: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 40), (23) - (5250 - 5330 @ 40), (23), DFS - (5490 - 5710 @ 40), (30), DFS - -country XA: DFS-JP - (2402 - 2482 @ 40), (20) - (2474 - 2494 @ 20), (20), NO-OFDM - (5170 - 5250 @ 80), (20), NO-IR, AUTO-BW, NO-OUTDOOR - (5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR - (5490 - 5710 @ 160), (20), DFS - -country YE: - (2402 - 2482 @ 40), (20) +country XA: + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS -country YT: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS +country XC: + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + # Channel 36 - 48 + (5170 - 5250 @ 80), (20), PASSIVE-SCAN, NO-IBSS + # Channel 52 - 64 + (5250 - 5330 @ 80), (20), PASSIVE-SCAN, NO-IBSS + # Channel 132 - 140 + (5650 - 5710 @ 40), (20), PASSIVE-SCAN, NO-IBSS + # Channel 149 - 165 + (5735 - 5835 @ 80), (14), PASSIVE-SCAN, NO-IBSS country ZA: DFS-FCC - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (24), AUTO-BW - (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS - (5735 - 5835 @ 80), (30) - # 60 gHz band channels 1-4 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR - -country ZW: DFS-ETSI - (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (2402 - 2472 @ 20), (20) + # Channel 12 - 13. + (2457 - 2482 @ 20), (20), PASSIVE-SCAN, NO-IBSS + (5170 - 5250 @ 80), (23), PASSIVE-SCAN, AUTO-BW + (5250 - 5330 @ 80), (23), PASSIVE-SCAN, DFS, AUTO-BW + (5490 - 5730 @ 160), (24), PASSIVE-SCAN, DFS + (5735 - 5835 @ 80), (30), PASSIVE-SCAN diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig index bda1a13628a8143b812554d0d29fd83cac3a1e36..0a9b7e87936f460927b19380ca9b4d9b26bcbfa5 100644 --- a/net/xfrm/Kconfig +++ b/net/xfrm/Kconfig @@ -83,3 +83,12 @@ config NET_KEY_MIGRATE If unsure, say N. +config XFRM_RFC_4868_TRUNCATION + bool "Use truncation length of RFC4868 for HMAC-SHA-256" + depends on XFRM + default n + ---help--- + Tentatively, truncate length of HMAC-SHA-256 is 96bit. + RFC4868 fixed it to 128bit. + + If unsure, say N. diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index 9adfdd711b31766c1611bbac90e75f7f9c0b90d2..2e232f7e29ed8f18f36b9eb885a6a2bd73c85f69 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -8,6 +8,11 @@ * Software Foundation; either version 2 of the License, or (at your option) * any later version. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -239,7 +244,11 @@ static struct xfrm_algo_desc aalg_list[] = { .uinfo = { .auth = { +#ifdef CONFIG_XFRM_RFC_4868_TRUNCATION .icv_truncbits = 128, +#else + .icv_truncbits = 96, +#endif .icv_fullbits = 256, } }, diff --git a/scripts/autoconfig.pl b/scripts/autoconfig.pl new file mode 100755 index 0000000000000000000000000000000000000000..df8fba46eea5f0c945c36f10b471f55d3ebccfaf --- /dev/null +++ b/scripts/autoconfig.pl @@ -0,0 +1,288 @@ +#!/usr/bin/perl + +# scripts/defconfig.pl +# +# Copyright (C) 2011 Sony Ericsson Mobile Communications AB. +# +# Author: Martin Danielsson +# +# 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. + +use strict; +use warnings; + +use Cwd 'abs_path'; +use File::Copy; + +my $defconfig; +my $kernel_dir; +my $config_dir; +my @products = (); +my $keep_tempconfig; + +# Print usage instructions +# +# In: Error message to display +sub usage($) +{ + my ($msg) = @_; + + print "\n"; + print "Error: $msg\n\n" if ($msg ne ""); + + print "This script is used to perform one or more tasks on a set of\n"; + print "defconfig files. The result will be the same as if the\n"; + print "product configuration was done using menuconfig or similar\n"; + print "tool.\n\n"; + + print "Usage: "; + print "autoconfig.pl [-k] -t [:[=]] \n\n"; + + print "-k: keep .config file after execution\n"; + print "\t.config file is removed as default behavior\n"; + print "\tbecause remaining .config file causes build error\n"; + print "\tuse this flag to stop removing (for debug purpose)\n"; + print "-t: task for new configuration\n"; + print " - action to perform\n"; + print "\ts: sync defconfig with Kconfig (s)\n"; + print "\ta: set a configuration (a:=)\n"; + print "\td: unset a configuration (d:)\n"; + print " - a configuration flag, for instance CONFIG_SWAP\n"; + print " - the value to set, for instance y, n or 1000\n"; + print "Examples:\n"; + print "\tSync all defconfigs with Kconfig\n"; + print "\t\$ autoconfig.pl -t s\n\n"; + print "\tEnable CONFIG_SWAP for Anzu and Hallon\n"; + print "\t\$ autoconfig.pl -t a:CONFIG_SWAP=y anzu hallon\n\n"; + print "\tDisable CONFIG_USB_SUPPORT for all products\n"; + print "\t\$ autoconfig.pl -t d:CONFIG_USB_SUPPORT\n\n"; + print "\tSet CONFIG_MSM_AMSS_VERSION to 1000 for Anzu\n"; + print "\t\$ autoconfig.pl -t a:CONFIG_MSM_AMSS_VERSION=1000 anzu\n"; + print "\n"; +} + +# Apply a modification to a defconfig file +# +# In: The key +# In: The value +sub apply_modification($$) +{ + my ($key, $value) = @_; + + # Settings are applied by inserting them at the end of the defconfig + # file. The kernel build system will later move it to the correct + # location and handle any duplicate entries. It will also perform + # validation of integer values. + my $file = ".config"; + open DEFCONFIG, ">>$file" or die "Failed to open file, $file"; + if ($value eq "n") { + print DEFCONFIG "\n# $key is not set\n"; + } else { + print DEFCONFIG "\n$key=$value\n"; + } + close DEFCONFIG; +} + +sub perform_task_diffconfig($$$) +{ + my ($action, $key, $value) = @_; + opendir Dir, "$config_dir/diffconfig" or exit 0; + foreach (readdir Dir) { + if (/([\w-]+)_diffconfig/) { + if ($action eq "s") { + print "Syncing $_ ...\n"; + } + if (@products != 0) { + if (is_product_selected($1) == 0) { + next; + } + if ($action eq "a") { + print "Setting $key=$value in $_...\n"; + } elsif ($action eq "d") { + print "Removing $key in $_...\n"; + } + } + if ((is_product_excluded($1) == 0)) { + $ENV{'KBUILD_DIFFCONFIG'} = $_; + system "make ARCH=arm64 `basename $defconfig` > /dev/null 2>&1"; + if ($action ne "s") { + apply_modification($key, $value); + system "make ARCH=arm64 `basename olddefconfig` > /dev/null 2>&1"; + } + $ENV{'KBUILD_DIFFCONFIG'} = $_; + system "make ARCH=arm64 savediffconfig > /dev/null 2>&1"; + move("diffconfig", "$config_dir/diffconfig/$_") + or die "failed to copy file"; + } + } + } + close Dir; +} + + +# Perform a task on a specific defconfig +# +# In: The action +# In: The key +# In: The value +sub perform_task_defconfig($$$) +{ + my ($action, $key, $value) = @_; + + if ($action eq "s") { + print "Syncing $defconfig ...\n"; + } elsif ($action eq "a") { + print "Setting $key=$value ...\n"; + } elsif ($action eq "d") { + print "Removing $key ...\n"; + } + + if (@products == 0) { + #Updating the common diffconfig + my $option ="KCONFIG_NOTIMESTAMP=true"; + $ENV{'KBUILD_DIFFCONFIG'} = "common_diffconfig"; + system "make ARCH=arm64 `basename $defconfig` > /dev/null 2>&1"; + if ($action ne "s") { + apply_modification($key, $value); + system "make ARCH=arm64 olddefconfig > /dev/null 2>&1"; + } + system "make ARCH=arm64 savecommondiffconfig > /dev/null 2>&1"; + move("diffconfig.common", "$config_dir/diffconfig/common_diffconfig") + or die "failed to copy file"; + } + + perform_task_diffconfig($action, $key, $value); +} + + + +# Parse a task description string +# +# In: The task description string +# Out: A ($action, $key, $value) array. In case of error $action is set to "-" +sub parse_task_description($) +{ + my ($task) = @_; + + my $action = ""; + my $key = ""; + my $value = ""; + if ($task =~ /^([sda])/) { + $action = $1; + } else { + print "unknown action, $task\n"; + return ("-", "", ""); + } + my $error = 1; + if ($action eq "s") { + $error = 0; + } elsif ($action eq "a") { + if ($task =~ /^a:(\w+)=(.+)/) { + $key = $1; + $value = $2; + $error = 0; + } + } elsif ($action eq "d") { + if ($task =~ /^d:(\w+)/) { + $key = $1; + $value = "n"; + $error = 0; + } + } + if ($error == 1) { + print "incorrect task description, $task\n"; + return ("-", "", ""); + } + + return ($action, $key, $value); +} + +# Check if a product should be excluded +# +# In: The product to check +# Out: 1 if the product is excluded, 0 otherwise +sub is_product_excluded($) +{ + my ($product) = @_; + + if ($product =~ /_capk/) { + return 1; + } + + if ($product =~ /common/) { + return 1; + } + + return 0; +} + +# Check if a product has been selected for processing +# +# In: The product to check +# Out: 1 if the product selected, 0 otherwise +sub is_product_selected($) +{ + my ($product) = @_; + + if (@products == 0) { + return 1; + } + foreach (@products) { + if ($product eq $_) { + return 1; + } + } + + return 0; +} + +### Program starts here ### + +# Figure out the path to the kernel directory and move to it +$defconfig = "msmcortex-perf_defconfig"; +$kernel_dir = abs_path($0); +$kernel_dir =~ s!/scripts/.*\.pl!!; +chdir $kernel_dir or die "couldn't move to kernel directory, $kernel_dir\n"; +$config_dir = "$kernel_dir/arch/arm64/configs"; +$keep_tempconfig = 0; + +# Parse command line arguments +my @tasks = (); +my $iter = 0; +my $flag = 0; +while ($iter < @ARGV) { + if ($ARGV[$iter] eq "-t") { + $iter++; + if ($iter >= @ARGV) { + usage("not enough arguments"); + exit 0; + } + push @tasks, $ARGV[$iter]; + } elsif ($ARGV[$iter] eq "-h") { + usage(""); + exit 0; + } elsif ($ARGV[$iter] eq "-k") { + $keep_tempconfig = 1; + } else { + push @products, $ARGV[$iter]; + } + $iter++; +} + +# Apply the tasks one at a time for each selected product +if (@tasks == 0) { + usage("no task specified"); + exit 0; +} +my $task; +foreach $task (@tasks) { + my ($action, $key, $value) = parse_task_description($task); + next if ($action eq "-"); + perform_task_defconfig($action, $key, $value); + if ($keep_tempconfig == 0) { + system "make mrproper"; + } +} diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index ebced77deb9c4dc380ab5f58751950adc0ac3d7f..908e961a384a2fa49a0cab5bbab0747382911e1b 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -3,7 +3,7 @@ # These targets are used from top-level makefile PHONY += xconfig gconfig menuconfig config silentoldconfig update-po-config \ - localmodconfig localyesconfig + localmodconfig localyesconfig savediffconfig ifdef KBUILD_KCONFIG Kconfig := $(KBUILD_KCONFIG) @@ -93,6 +93,18 @@ oldnoconfig: olddefconfig savedefconfig: $(obj)/conf $< $(silent) --$@=defconfig $(Kconfig) +savecommondiffconfig: +ifneq ($(KBUILD_DIFFCONFIG), ) + $(Q)$(srctree)/scripts/diffconfig -m .config.def .config > diffconfig.common + $(Q)rm -f .config.def .config.common +endif + +savediffconfig: +ifneq ($(KBUILD_DIFFCONFIG), ) + $(Q)$(srctree)/scripts/diffconfig -m .config.common .config > diffconfig + $(Q)rm -f .config.def .config.common +endif + defconfig: $(obj)/conf ifeq ($(KBUILD_DEFCONFIG),) $< $(silent) --defconfig $(Kconfig) @@ -108,6 +120,16 @@ endif %_defconfig: $(obj)/conf $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) +ifneq ($(KBUILD_DIFFCONFIG), ) + $(Q)cp -f .config .config.def + $(Q)$(srctree)/$(obj)/merge_config.sh -m .config \ + $(srctree)/arch/$(SRCARCH)/configs/diffconfig/common_diffconfig + $(Q)$< --olddefconfig $(Kconfig) + $(Q)cp -f .config .config.common + $(Q)$(srctree)/$(obj)/merge_config.sh -m .config \ + $(srctree)/arch/$(SRCARCH)/configs/diffconfig/$(KBUILD_DIFFCONFIG) + $(Q)$< --defconfig=.config $(Kconfig) +endif configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/configs/$@) diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index 8691e92f27e584dcf26f35628c61e408c08a77cb..6e31e646366ec9a1cc8d6586a86e97e20eb7a2f4 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -74,6 +74,16 @@ config SECURITY_SELINUX_AVC_STATS /selinux/avc/cache_stats, which may be monitored via tools such as avcstat. +config SECURITY_SELINUX_AVC_EXTRA_INFO + bool "SELinux extended log info" + depends on SECURITY_SELINUX + default n + help + This option enables extra log info providing information + about the audit result and the process under audit, such + as thread group and parent pid. Appended log tags are: + "ppid", "pcomm", "pgid", pgcomm" and "op_res". + config SECURITY_SELINUX_CHECKREQPROT_VALUE int "NSA SELinux checkreqprot default value" depends on SECURITY_SELINUX @@ -131,3 +141,12 @@ config SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE installed under /etc/selinux/$SELINUXTYPE/policy, where SELINUXTYPE is defined in your /etc/selinux/config. + +config SECURITY_SELINUX_TRAP + bool "SELinux error trap support" + depends on SECURITY_SELINUX + default n + help + This option enables the trap feature for SELinux errors. + User process will get SIGABRT so that system can generate core file + for more further invesitaion of SELinux errors. diff --git a/security/selinux/Makefile b/security/selinux/Makefile index ad5cd76ec231cd14f02b2fb15f07a3d8a069972f..212bb4b8d781e76565abd2e248f8e3916ab7b967 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -9,6 +9,8 @@ selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \ ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \ ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o +selinux-$(CONFIG_SECURITY_SELINUX_TRAP) += trap.o + selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o selinux-$(CONFIG_NETLABEL) += netlabel.o diff --git a/security/selinux/avc.c b/security/selinux/avc.c index e60c79de13e1c74ea6129cfb5431d5d2415cdc2d..c89d887c32419e96be42774a0f983eaa1ea53ae3 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -13,6 +13,11 @@ * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -33,9 +38,13 @@ #include "avc.h" #include "avc_ss.h" #include "classmap.h" +#ifdef CONFIG_SECURITY_SELINUX_TRAP +#include "trap.h" +const int secclass_map_size = ARRAY_SIZE(secclass_map); +#endif -#define AVC_CACHE_SLOTS 512 -#define AVC_DEF_CACHE_THRESHOLD 512 +#define AVC_CACHE_SLOTS 1024 +#define AVC_DEF_CACHE_THRESHOLD 2048 #define AVC_CACHE_RECLAIM 16 #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS @@ -718,6 +727,35 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a) audit_log_format(ab, " for "); } +/** + * avc_dump_extra_info - add extra info about task and audit result + * @ab: the audit buffer + * @ad: audit_data + */ +#ifdef CONFIG_SECURITY_SELINUX_AVC_EXTRA_INFO +static void avc_dump_extra_info(struct audit_buffer *ab, + struct common_audit_data *ad) +{ + struct task_struct *tsk = current; + + if (tsk && tsk->pid) { + audit_log_format(ab, " ppid=%d pcomm=", tsk->parent->pid); + audit_log_untrustedstring(ab, tsk->parent->comm); + + if (tsk->group_leader->pid != tsk->pid) { + audit_log_format(ab, " pgid=%d pgcomm=", + tsk->group_leader->pid); + audit_log_untrustedstring(ab, + tsk->group_leader->comm); + } else if (tsk->parent->group_leader->pid) { + audit_log_format(ab, " pgid=%d pgcomm=", + tsk->parent->group_leader->pid); + audit_log_untrustedstring(ab, + tsk->parent->group_leader->comm); + } + } +} +#endif /** * avc_audit_post_callback - SELinux specific information * will be called by generic audit code @@ -735,6 +773,14 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a) audit_log_format(ab, " permissive=%u", ad->selinux_audit_data->result ? 0 : 1); } + +#ifdef CONFIG_SECURITY_SELINUX_AVC_EXTRA_INFO + avc_dump_extra_info(ab, ad); +#endif +#ifdef CONFIG_SECURITY_SELINUX_TRAP + if (ad->selinux_audit_data->denied && ad->selinux_audit_data->result) + trap_selinux_error(ad); +#endif } /* This is the slow part of avc audit with big stack footprint */ diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h index d5c328452df0161acd82bd8ec295fecc00392b86..834f8f8f0faac81fa84a2421c940d23cc31ce647 100644 --- a/security/selinux/include/avc_ss.h +++ b/security/selinux/include/avc_ss.h @@ -3,6 +3,11 @@ * * Author : Stephen Smalley, */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _SELINUX_AVC_SS_H_ #define _SELINUX_AVC_SS_H_ @@ -17,7 +22,9 @@ struct security_class_mapping { }; extern struct security_class_mapping secclass_map[]; - +#ifdef CONFIG_SECURITY_SELINUX_TRAP +extern const int secclass_map_size; +#endif /* * The security server must be initialized before * any labeling or access decisions can be provided. diff --git a/security/selinux/include/trap.h b/security/selinux/include/trap.h new file mode 100644 index 0000000000000000000000000000000000000000..9732c29563d3d280fe20471b2de8ac9ea77e9ca1 --- /dev/null +++ b/security/selinux/include/trap.h @@ -0,0 +1,74 @@ +/* + * 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. + */ +/* + * Copyright (C) 2014 Sony Mobile Communications 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 _SELINUX_TRAP_H_ +#define _SELINUX_TRAP_H_ + +#include +#include +#include +#include +#include "avc.h" + +enum trap_mask_type { + TRAP_MASK_TYPE_POLARITY, + TRAP_MASK_TYPE_SCONTEXT, + TRAP_MASK_TYPE_TCONTEXT, + TRAP_MASK_TYPE_TCLASS, + TRAP_MASK_TYPE_PNAME, + TRAP_MASK_TYPE_PNAME_PARENT, + TRAP_MASK_TYPE_PNAME_PGL, + TRAP_MASK_TYPE_PATH, + TRAP_MASK_TYPE_NAME, + TRAP_MASK_TYPE_ACTION, + TRAP_MASK_TYPE_MAX +}; +enum trap_loglevel_type { + TRAP_LOGLEVEL_MINIMUM = 0, + TRAP_LOGLEVEL_NORMAL, + TRAP_LOGLEVEL_DEVELOPER, + TRAP_LOGLEVEL_MAX +}; + +#define TRAP_MASK_TYPE_BEGIN TRAP_MASK_TYPE_POLARITY + +struct selinux_trap_list { + char *item_array[TRAP_MASK_TYPE_MAX]; + struct list_head list; + struct rcu_head rcu; +}; + +struct selinux_trap_process_list { + struct list_head list; + pid_t pid; + char *msg; + struct inode *inode; + struct dentry *ldentry; +}; + +void trap_selinux_error(struct common_audit_data *ad); +extern int selinux_trap_enable; +extern int selinux_trap_debug; +extern struct selinux_trap_list selinux_trap_list_head; +extern struct selinux_trap_process_list selinux_trap_process_list_head; +extern struct semaphore selinux_trap_list_sem; + +#define trap_devel_log(fmt, ...) \ + do { /* Multhi-statement Macro for semicolon */ \ + if (selinux_trap_debug >= TRAP_LOGLEVEL_DEVELOPER) { \ + pr_devel(fmt, ##__VA_ARGS__); \ + } \ + } while (0) /* Multhi-statement Macro for semicolon */ + +#endif /* _SELINUX_TRAP_H_ */ diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index c02da25d7b631992aa7841ca98250b08b48d8477..55460e8d1e7875281c8e15be4fcfed5746aea400 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -13,6 +13,11 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -40,6 +45,9 @@ #include "security.h" #include "objsec.h" #include "conditional.h" +#ifdef CONFIG_SECURITY_SELINUX_TRAP +#include "trap.h" +#endif /* Policy capability filenames */ static char *policycap_names[] = { @@ -1429,6 +1437,476 @@ static const struct file_operations sel_avc_cache_stats_ops = { }; #endif +#ifdef CONFIG_SECURITY_SELINUX_TRAP +static void sel_trap_clear_list_entry(struct selinux_trap_list *entry) +{ + enum trap_mask_type type; + for (type = TRAP_MASK_TYPE_BEGIN; type != TRAP_MASK_TYPE_MAX; type++) + kfree(entry->item_array[type]); + kfree(entry); +} + +static void sel_trap_clear_list_entry_rcu(struct rcu_head *rcu) +{ + struct selinux_trap_list *entry = + container_of(rcu, struct selinux_trap_list, rcu); + sel_trap_clear_list_entry(entry); +} + +static struct selinux_trap_list *sel_trap_init_list_entry(char *buf) +{ + struct selinux_trap_list *entry; + enum trap_mask_type type = TRAP_MASK_TYPE_BEGIN; + char *token; + int buflen = strlen(buf); + int valid_count = 0; + + if (buflen <= 0) + return NULL; + + if (buf[buflen-1] == '\n') + buf[buflen-1] = '\0'; + + entry = kzalloc(sizeof(struct selinux_trap_list), GFP_KERNEL); + if (!entry) { + printk(KERN_ERR "SELinux: trap: kmalloc() failed\n"); + return NULL; + } + + while ((token = strsep(&buf, ",")) != NULL) { + if (type == TRAP_MASK_TYPE_MAX) { + sel_trap_clear_list_entry(entry); + return NULL; + } + if (strlen(token) > 0) { + entry->item_array[type] = + kzalloc(strlen(token)+1, GFP_KERNEL); + if (!entry->item_array[type]) { + sel_trap_clear_list_entry(entry); + return NULL; + } + strlcpy(entry->item_array[type], + token, strlen(token)+1); + valid_count++; + } + type++; + } + if (valid_count == 0 || type != TRAP_MASK_TYPE_MAX) { + sel_trap_clear_list_entry(entry); + return NULL; + } + return entry; +} + +static ssize_t sel_read_trap_enable(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char tmpbuf[2]; + ssize_t length; + + length = scnprintf(tmpbuf, sizeof(tmpbuf), "%d", selinux_trap_enable); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); +} + +static ssize_t sel_write_trap_enable(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) + +{ + char *page = NULL; + ssize_t length; + int new_value; + + length = -ENOMEM; + if (count >= PAGE_SIZE) + goto out; + + /* No partial writes. */ + length = EINVAL; + if (*ppos != 0) + goto out; + + length = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) + goto out; + + length = -EFAULT; + if (copy_from_user(page, buf, count)) + goto out; + + length = -EINVAL; + if (sscanf(page, "%d", &new_value) != 1) + goto out; + + if (new_value == 0 || new_value == 1) + selinux_trap_enable = new_value; + + length = count; +out: + free_page((unsigned long) page); + return length; +} + +static const struct file_operations sel_trap_enable_ops = { + .read = sel_read_trap_enable, + .write = sel_write_trap_enable, + .llseek = generic_file_llseek, +}; + +static int show_trap_exceptions(struct seq_file *m, void *v) +{ + struct selinux_trap_list *entry; + enum trap_mask_type type; + + seq_printf(m, + "{-|+},scontext,tcontext,tclass," + "pname,pname_parent,pname_pgl," + "path,name,types\n"); + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &selinux_trap_list_head.list, list) { + for (type = TRAP_MASK_TYPE_BEGIN; + type < TRAP_MASK_TYPE_MAX; type++) { + if (entry->item_array[type]) + seq_printf(m, "%s", entry->item_array[type]); + if (type != TRAP_MASK_TYPE_MAX-1) + seq_printf(m, ","); + } + seq_printf(m, "\n"); + } + rcu_read_unlock(); + return 0; +} + +static int sel_open_trap_exceptions(struct inode *inode, struct file *file) +{ + return single_open(file, show_trap_exceptions, NULL); +} + +static const struct file_operations sel_trap_exceptions_ops = { + .open = sel_open_trap_exceptions, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t sel_write_trap_add_exception(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + char *page = NULL; + ssize_t length; + struct selinux_trap_list *new_entry; + + length = -ENOMEM; + if (count >= PAGE_SIZE) + goto out; + + /* No partial writes. */ + length = EINVAL; + if (*ppos != 0) + goto out; + + /* string size error */ + length = EINVAL ; + if (count < 2) + goto out ; + + length = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) + goto out; + + length = -EFAULT; + if (copy_from_user(page, buf, count)) + goto out; + + new_entry = sel_trap_init_list_entry(page); + if (!new_entry) { + printk(KERN_ERR "SELinux: trap: sel_trap_init_list_entry failed\n"); + goto out; + } + down(&selinux_trap_list_sem); + list_add_rcu(&new_entry->list, &selinux_trap_list_head.list); + up(&selinux_trap_list_sem); + + length = count; +out: + free_page((unsigned long) page); + return length; +} + +static const struct file_operations sel_trap_add_exception_ops = { + .write = sel_write_trap_add_exception, + .llseek = generic_file_llseek, +}; + +static ssize_t sel_write_trap_clear_exceptions( + struct file *file, const char __user *buf, size_t count, loff_t *ppos) + +{ + char *page = NULL; + ssize_t length; + int value; + + length = -ENOMEM; + if (count >= PAGE_SIZE) + goto out; + + /* No partial writes. */ + length = EINVAL; + if (*ppos != 0) + goto out; + + length = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) + goto out; + + length = -EFAULT; + if (copy_from_user(page, buf, count)) + goto out; + + length = -EINVAL; + if (sscanf(page, "%d", &value) != 1) + goto out; + + if (value == 1) { + struct selinux_trap_list *entry, *n; + down(&selinux_trap_list_sem); + list_for_each_entry_safe(entry, + n, &selinux_trap_list_head.list, list) { + list_del_rcu(&entry->list); + call_rcu(&entry->rcu, sel_trap_clear_list_entry_rcu); + } + up(&selinux_trap_list_sem); + } + length = count; +out: + free_page((unsigned long) page); + return length; +} + +static const struct file_operations sel_trap_clear_exceptions_ops = { + .write = sel_write_trap_clear_exceptions, + .llseek = generic_file_llseek, +}; + +static ssize_t sel_read_trap_debug(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char tmpbuf[2]; + ssize_t length; + + length = scnprintf(tmpbuf, sizeof(tmpbuf), "%d", selinux_trap_debug); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); +} + +static ssize_t sel_write_trap_debug(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) + +{ + char *page = NULL; + ssize_t length; + int new_value; + + length = -ENOMEM; + if (count >= PAGE_SIZE) + goto out; + + /* No partial writes. */ + length = EINVAL; + if (*ppos != 0) + goto out; + + length = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) + goto out; + + length = -EFAULT; + if (copy_from_user(page, buf, count)) + goto out; + + length = -EINVAL; + if (sscanf(page, "%d", &new_value) != 1) + goto out; + + if (new_value >= TRAP_LOGLEVEL_MINIMUM && new_value < TRAP_LOGLEVEL_MAX) + selinux_trap_debug = new_value; + + length = count; +out: + free_page((unsigned long) page); + return length; +} + +static const struct file_operations sel_trap_debug_ops = { + .read = sel_read_trap_debug, + .write = sel_write_trap_debug, + .llseek = generic_file_llseek, +}; + +static struct dentry *trapped_dentry; + +static int sel_make_trap_files(struct dentry *dir) +{ + int i; + static struct tree_descr files[] = { + { "enable", + &sel_trap_enable_ops, S_IRUGO|S_IWUSR }, + { "exceptions", + &sel_trap_exceptions_ops, S_IRUGO }, + { "add_exception", + &sel_trap_add_exception_ops, S_IWUSR }, + { "clear_exceptions", + &sel_trap_clear_exceptions_ops, S_IWUSR }, + { "debug", + &sel_trap_debug_ops, S_IRUGO|S_IWUSR }, + }; + + for (i = 0; i < ARRAY_SIZE(files); i++) { + struct inode *inode; + struct dentry *dentry; + + dentry = d_alloc_name(dir, files[i].name); + if (!dentry) + return -ENOMEM; + + inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); + if (!inode) + return -ENOMEM; + + inode->i_fop = files[i].ops; + inode->i_ino = ++sel_last_ino; + d_add(dentry, inode); + } + + /* Create a new node named 'trapped' */ + trapped_dentry = sel_make_dir(dir, "trapped", &sel_last_ino); + if (IS_ERR(trapped_dentry)) { + return -ENOMEM; + } + + return 0; +} + +static struct selinux_trap_process_list *sel_trap_process_list_entry(pid_t pid, char *msg, struct inode *inode, struct dentry *ldentry) +{ + struct selinux_trap_process_list *entry; + + entry = kmalloc(sizeof(struct selinux_trap_process_list), GFP_KERNEL); + if (!entry) { + printk(KERN_ERR "SELinux: trap: node list kmalloc() failed\n"); + kfree(msg); + return NULL; + } + + entry->pid = pid; + entry->msg = msg; + entry->inode = inode; + entry->ldentry = ldentry; + + down(&selinux_trap_list_sem); + list_add_tail(&entry->list, &selinux_trap_process_list_head.list); + up(&selinux_trap_list_sem); + + return entry; +} + +static int sel_trap_process_list_entry_clear(struct selinux_trap_process_list *entry) +{ + if (entry->msg) + kfree(entry->msg); + list_del(&entry->list); + kfree(entry); + return 0; +} + +static int show_trap_trapped(struct seq_file *m, void *v) +{ + struct inode *inode = (struct inode *)m->private; + pid_t pid = inode->i_ino; + + rcu_read_lock(); + if (!list_empty(&selinux_trap_process_list_head.list)) { + struct selinux_trap_process_list *entry, *n; + down(&selinux_trap_list_sem); + list_for_each_entry_safe(entry, n, &selinux_trap_process_list_head.list, list) { + if (entry->pid == pid) { + seq_printf(m, "%s\n", entry->msg); + break; + } + } + up(&selinux_trap_list_sem); + } + rcu_read_unlock(); + return 0; +} + +static int sel_open_trap_trapped(struct inode *inode, struct file *file) +{ + return single_open(file, show_trap_trapped, inode); +} + +static const struct file_operations sel_trap_trapped_ops = { + .open = sel_open_trap_trapped, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void trapped_process_list_refresh(pid_t pid_num) +{ + if (!list_empty(&selinux_trap_process_list_head.list)) { + struct selinux_trap_process_list *entry, *n; + down(&selinux_trap_list_sem); + list_for_each_entry_safe(entry, n, &selinux_trap_process_list_head.list, list) { + if (!find_task_by_vpid(entry->pid) || entry->pid == pid_num) { + /* process not exist or process id hit */ + dget_dlock(entry->ldentry); + d_delete(entry->ldentry); + simple_unlink(entry->inode, entry->ldentry); + dput(entry->ldentry); + sel_trap_process_list_entry_clear(entry); + } + } + up(&selinux_trap_list_sem); + } +} + +static int trapped_pid_entry(pid_t pid_num, char *msg) +{ + struct inode *inode; + struct dentry *ldentry; + char pid_name_buf[10]; /* PID name */ + + snprintf(pid_name_buf, sizeof(pid_name_buf), "%d", pid_num); + + ldentry = d_alloc_name(trapped_dentry, pid_name_buf); + if (!ldentry) + return -ENOMEM; + + inode = sel_make_inode(trapped_dentry->d_sb, S_IFREG|S_IRUGO); + if (!inode) + return -ENOMEM; + + inode->i_fop = &sel_trap_trapped_ops; + inode->i_ino = pid_num; + d_add(ldentry, inode); + + sel_trap_process_list_entry(pid_num, msg, inode, ldentry); + + return 0 ; +} + +void trapped_node_entry(pid_t pid_num, char *msg) +{ + trapped_process_list_refresh(pid_num); + trapped_pid_entry(pid_num, msg); +} + +#endif /* CONFIG_SECURITY_SELINUX_TRAP */ + static int sel_make_avc_files(struct dentry *dir) { int i; @@ -1802,6 +2280,18 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) if (ret) goto err; +#ifdef CONFIG_SECURITY_SELINUX_TRAP + dentry = sel_make_dir(sb->s_root, "trap", &sel_last_ino); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto err; + } + + ret = sel_make_trap_files(dentry); + if (ret) + goto err; +#endif + dentry = sel_make_dir(sb->s_root, "initial_contexts", &sel_last_ino); if (IS_ERR(dentry)) { ret = PTR_ERR(dentry); diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index d946c9dc3c9ca6b2569ecd7624c3bd12f7789c77..3dab94ada3bb0ff5cd48973e6290d926f6ccbbc9 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -7,6 +7,11 @@ * * Author : Stephen Smalley, */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ /* Updated: Frank Mayer and Karl MacMillan * @@ -117,7 +122,7 @@ struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified void avtab_cache_init(void); void avtab_cache_destroy(void); -#define MAX_AVTAB_HASH_BITS 16 +#define MAX_AVTAB_HASH_BITS 18 #define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS) #endif /* _SS_AVTAB_H_ */ diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h index 84dc154d9389db3b6353443e7c6d3c1997e08a76..e5cc791e8f1aa763781eaf8a7710fb96bb2227b9 100644 --- a/security/selinux/ss/sidtab.h +++ b/security/selinux/ss/sidtab.h @@ -4,6 +4,11 @@ * * Author : Stephen Smalley, */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _SS_SIDTAB_H_ #define _SS_SIDTAB_H_ @@ -15,7 +20,7 @@ struct sidtab_node { struct sidtab_node *next; }; -#define SIDTAB_HASH_BITS 7 +#define SIDTAB_HASH_BITS 12 #define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS) #define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1) @@ -26,7 +31,7 @@ struct sidtab { unsigned int nel; /* number of elements */ unsigned int next_sid; /* next SID to allocate */ unsigned char shutdown; -#define SIDTAB_CACHE_LEN 3 +#define SIDTAB_CACHE_LEN 7 struct sidtab_node *cache[SIDTAB_CACHE_LEN]; spinlock_t lock; }; diff --git a/security/selinux/trap.c b/security/selinux/trap.c new file mode 100644 index 0000000000000000000000000000000000000000..f151d0463c9dea9cf74d7a55f00baf9ae3032a3d --- /dev/null +++ b/security/selinux/trap.c @@ -0,0 +1,866 @@ +/* + * 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. + */ +/* + * Copyright (C) 2014 Sony Mobile Communications 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. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include "avc_ss.h" +#include "trap.h" + +extern void trapped_node_entry(pid_t, char *); + +#define STRING_ALL_LEN_MAX PATH_MAX +#define STRING_PART_LEN_MAX PATH_MAX + +enum string_lsm_audit_data { + STRING_LSM_AUDIT_DATA_TERM = 0, + STRING_LSM_AUDIT_DATA_ACTION, + STRING_LSM_AUDIT_DATA_PID, + STRING_LSM_AUDIT_DATA_NONE, + STRING_LSM_AUDIT_DATA_IPC, + STRING_LSM_AUDIT_DATA_CAP, + STRING_LSM_AUDIT_DATA_PATH, + STRING_LSM_AUDIT_DATA_DENTRY, + STRING_LSM_AUDIT_DATA_INODE, + STRING_LSM_AUDIT_DATA_TASK, + STRING_LSM_AUDIT_DATA_NET, + STRING_LSM_AUDIT_DATA_KEY, + STRING_LSM_AUDIT_DATA_KMOD, + STRING_LSM_AUDIT_DATA_TCONTEXT, + STRING_LSM_AUDIT_DATA_SCONTEXT, + STRING_LSM_AUDIT_DATA_TCLASS, + STRING_LSM_AUDIT_DATA_PERMISSIVE, + STRING_LSM_AUDIT_DATA_MAX +}; + +struct _trapwork { + struct work_struct work; + struct task_struct *task; +}; + +int selinux_trap_enable; +int selinux_trap_debug; +struct selinux_trap_list selinux_trap_list_head; +struct selinux_trap_process_list selinux_trap_process_list_head; +struct semaphore selinux_trap_list_sem; +static char string_work[STRING_PART_LEN_MAX]; +static const char copy_table[] = { + STRING_LSM_AUDIT_DATA_ACTION, /* action */ + STRING_LSM_AUDIT_DATA_PID, /* pid comm */ + STRING_LSM_AUDIT_DATA_INODE, /* name dev ino */ + STRING_LSM_AUDIT_DATA_TCONTEXT, /* scontext */ + STRING_LSM_AUDIT_DATA_SCONTEXT, /* tcontext */ + STRING_LSM_AUDIT_DATA_TCLASS, /* tclass */ + STRING_LSM_AUDIT_DATA_PERMISSIVE, /* permissive ppid pgid pgcomm */ + STRING_LSM_AUDIT_DATA_TERM /* TERM */ +}; + +static int cmp_string(char *rule_str, const char *cmp_str, size_t len) +{ + size_t rule_str_len = strlen(rule_str); + if (rule_str[rule_str_len-1] == '*') + return strncmp(rule_str, cmp_str, rule_str_len-1); /* Wild card match ex) cmpStr = "*" , "xxxxx*" or etc... */ + return strncmp(rule_str, cmp_str, len); /* Exact match ex) ruleStr = "xxxxx" cmpStr ="xxxxx" */ +} + +/* return 0 = identical / not 0 = different */ +static int cmp_polarity(char *rule, int c) +{ + int rc; + + if (!rule) { + trap_devel_log("SELinux: trap: no rule for name\n"); + return 1; /* Only + or - string / not NULL */ + } + + trap_devel_log("SELinux: trap: compare rule '%s' with polarity '%c'\n", + rule, c); + + if (rule[0] == c) + rc = 0; /* Matched */ + else + rc = 1; /* Unmatched */ + + if (rc) + trap_devel_log("SELinux: trap: different\n"); + else + trap_devel_log("SELinux: trap: identical\n"); + + return rc; +} + +/* return 0 = identical / not 0 = different */ +static int cmp_scontext(char *rule, struct common_audit_data *ad) +{ + int rc; + char *scontext; + u32 scontext_len; + u32 ssid = ad->selinux_audit_data->ssid; + + if (!rule) { + trap_devel_log("SELinux: trap: no rule for scontext\n"); + return 0; + } + rc = security_sid_to_context(ssid, &scontext, &scontext_len); + if (rc) { + pr_err("SELinux: trap: error context for ssid:%d\n", + ssid); + return 0; + } + + trap_devel_log("SELinux: trap: compare rule '%s' with scontext '%s'\n", + rule, scontext); + + rc = cmp_string(rule, scontext, scontext_len); + if (rc) + trap_devel_log("SELinux: trap: different\n"); + else + trap_devel_log("SELinux: trap: identical\n"); + + kfree(scontext); + return rc; +} + +/* return 0 = identical / not 0 = different */ +static int cmp_tcontext(char *rule, struct common_audit_data *ad) +{ + int rc; + char *tcontext; + u32 tcontext_len; + u32 tsid = ad->selinux_audit_data->tsid; + + if (!rule) { + trap_devel_log("SELinux: trap: no rule for tcontext\n"); + return 0; + } + + rc = security_sid_to_context(tsid, &tcontext, &tcontext_len); + if (rc) { + pr_err("SELinux: trap: error context for tsid:%d\n", + tsid); + return 0; + } + + trap_devel_log("SELinux: trap: compare rule '%s' with tcontext '%s'\n", + rule, tcontext); + + rc = cmp_string(rule, tcontext, tcontext_len); + if (rc) + trap_devel_log("SELinux: trap: different\n"); + else + trap_devel_log("SELinux: trap: identical\n"); + + kfree(tcontext); + return rc; +} + +/* return 0 = identical / not 0 = different */ +static int cmp_tclass(char *rule, struct common_audit_data *ad) +{ + int rc; + u16 tclass = ad->selinux_audit_data->tclass; + + if (!rule) { + trap_devel_log("SELinux: trap: no rule for tclass\n"); + return 0; + } + + BUG_ON(tclass >= secclass_map_size); + + trap_devel_log("SELinux: trap: compare rule '%s' with tclass '%s'\n", + rule, secclass_map[tclass-1].name); + + rc = cmp_string(rule, secclass_map[tclass-1].name, + strlen(secclass_map[tclass-1].name)); + + if (rc) + trap_devel_log("SELinux: trap: different\n"); + else + trap_devel_log("SELinux: trap: identical\n"); + + return rc; +} + +/* return 0 = identical / not 0 = different */ +static int get_pname(struct task_struct *task, char *zeroed_page) +{ + int res = 0; + unsigned int arg_len; + struct mm_struct *mm = get_task_mm(task); + if (!mm) + return res; + if (!mm->arg_end) { + mmput(mm); + return res; + } + if (!down_read_trylock(&mm->mmap_sem)) { + mmput(mm); + return res; + } + arg_len = mm->arg_end - mm->arg_start; + + if (arg_len >= PAGE_SIZE) + arg_len = PAGE_SIZE-1; + + res = access_process_vm(task, mm->arg_start, zeroed_page, arg_len, 0); + + up_read(&mm->mmap_sem); + mmput(mm); + return res; +} + +/* return 0 = identical / not 0 = different */ +static int cmp_pname(char *rule, struct common_audit_data *ad) +{ + int rc; + char *pname = NULL; + + if (!rule) { + trap_devel_log("SELinux: trap: no rule for pname\n"); + return 0; + } + + pname = (char *)get_zeroed_page(GFP_KERNEL); + if (!pname) { + pr_err("SELinux: trap: get_zeroed_page failed\n"); + return 0; + } + + rcu_read_lock(); + rc = get_pname(current, pname); + rcu_read_unlock(); + + if (rc <= 0) { + pr_err("SELinux: trap: get_pname failed\n"); + free_page((unsigned long) pname); + return 0; + } + + trap_devel_log("SELinux: trap: compare rule '%s' with pname '%s'\n", + rule, pname); + + rc = cmp_string(rule, current->comm, strlen(current->comm)); + if (rc) + trap_devel_log("SELinux: trap: different\n"); + else + trap_devel_log("SELinux: trap: identical\n"); + + free_page((unsigned long) pname); + return rc; +} + +/* return 0 = identical / not 0 = different */ +static int cmp_pname_parent(char *rule, struct common_audit_data *ad) +{ + int rc; + char *pname = NULL; + + if (!rule) { + trap_devel_log("SELinux: trap: no rule for pname_parent\n"); + return 0; + } + + pname = (char *)get_zeroed_page(GFP_KERNEL); + if (!pname) { + pr_err("SELinux: trap: get_zeroed_page failed\n"); + return 0; + } + + rcu_read_lock(); + rc = get_pname(current->parent, pname); + rcu_read_unlock(); + + if (rc <= 0) { + pr_err("SELinux: trap: get_pname failed\n"); + free_page((unsigned long) pname); + return 0; + } + + trap_devel_log("SELinux: trap: compare rule '%s' with pname_parent '%s'\n", + rule, pname); + + rc = cmp_string(rule, current->comm, strlen(current->comm)); + if (rc) + trap_devel_log("SELinux: trap: different\n"); + else + trap_devel_log("SELinux: trap: identical\n"); + + free_page((unsigned long) pname); + return rc; +} + +# if 0 /* XXX: temporarily disabled to check process group leader */ +/* return 0 = identical / not 0 = different */ +static int cmp_pname_pgl(char *rule, struct common_audit_data *ad) +{ + int rc; + char *pname = NULL; + + if (!rule) { + trap_devel_log("SELinux: trap: no rule for pname_pgl\n"); + return 0; + } + + pname = (char *)get_zeroed_page(GFP_KERNEL); + if (!pname) { + pr_err("SELinux: trap: get_zeroed_page failed\n"); + return 0; + } + + + rcu_read_lock(); + if (current->group_leader->pid != current->pid) { + rc = get_pname(current->group_leader, pname); + } else { + rc = get_pname(current->parent->group_leader, pname); + } + rcu_read_unlock(); + + if (rc <= 0) { + pr_err("SELinux: trap: get_pname failed\n"); + free_page((unsigned long) pname); + return 0; + } + + trap_devel_log("SELinux: trap: compare rule '%s' with pname_pgl '%s'\n", + rule, pname); + + rc = cmp_string(rule, current->comm, strlen(current->comm)); + if (rc) + trap_devel_log("SELinux: trap: different\n"); + else + trap_devel_log("SELinux: trap: identical\n"); + + free_page((unsigned long) pname); + return rc; +} +#endif + +/* return 0 = identical / not 0 = different */ +static int cmp_path(char *rule, struct common_audit_data *ad) +{ + int rc; + struct path *path = &ad->u.path; + char *path_str, *pathname; + + if (!rule) { + trap_devel_log("SELinux: trap: no rule for path\n"); + return 0; + } + + if (ad->type != LSM_AUDIT_DATA_PATH) { + trap_devel_log("SELinux: trap: not support for path\n"); + return 0; + } + + pathname = kzalloc(PATH_MAX+11, GFP_KERNEL); + if (!pathname) { + pr_err("SELinux: trap: kzalloc failed\n"); + return 0; + } + + path_str = d_path(path, pathname, PATH_MAX+11); + if (IS_ERR(path_str)) { + kfree(pathname); + pathname = NULL; + pr_err("SELinux: trap: too long path name\n"); + return 0; + } + + trap_devel_log("SELinux: trap: compare rule '%s' with path '%s'\n", + rule, path_str); + + rc = cmp_string(rule, path_str, strlen(path_str)); + + if (rc) + trap_devel_log("SELinux: trap: different\n"); + else + trap_devel_log("SELinux: trap: identical\n"); + + kfree(pathname); + pathname = NULL; + return rc; +} + +/* return 0 = identical / not 0 = different */ +static int cmp_name(char *rule, struct common_audit_data *ad) +{ + int rc; + const char *name; + + if (!rule) { + trap_devel_log("SELinux: trap: no rule for name\n"); + return 0; + } + + if (ad->type != LSM_AUDIT_DATA_DENTRY) { + trap_devel_log("SELinux: trap: not support for name\n"); + return 0; + } + + name = ad->u.dentry->d_name.name; + + trap_devel_log("SELinux: trap: compare rule '%s' with name '%s'\n", + rule, name); + + rc = cmp_string(rule, name, strlen(name)); + + if (rc) + trap_devel_log("SELinux: trap: different\n"); + else + trap_devel_log("SELinux: trap: identical\n"); + + return rc; +} + +/* return 0 = identical / not 0 = different */ +static int cmp_action(char *rule, struct common_audit_data *ad) +{ + int rc; + const char **perms; + int i, perm; + u16 tclass = ad->selinux_audit_data->tclass; + u32 av = ad->selinux_audit_data->audited; + u32 av_rule = 0; + char *temp; + char *token; + + if (!rule) { + trap_devel_log("SELinux: trap: no rule for action\n"); + return 0; + } + + if (av == 0) + return 0; + + temp = kmalloc(strlen(rule)+1, GFP_KERNEL); + if (!temp) { + pr_err("SELinux: trap: kzalloc failed\n"); + return 0; + } + strlcpy(temp, rule, strlen(rule)+1); + + trap_devel_log("SELinux: trap: compare rule '%s' with action [", + rule); + + perms = secclass_map[tclass-1].perms; + + i = 0; + perm = 1; + while (i < (sizeof(av) * 8)) { + if ((perm & av) && perms[i]) + trap_devel_log("%s", perms[i]); + i++; + perm <<= 1; + } + trap_devel_log("]\n"); + + while ((token = strsep(&temp, " ")) != NULL) { + int len = strlen(token); + if (len > 0) { + i = 0; + while (perms[i]) { + trap_devel_log("compare %s - %s\n", token, perms[i]); + if (!strcmp(token, perms[i])) { + trap_devel_log("match\n"); + av_rule |= (1 << i); + break; + } + i++; + } + } + } + kfree(temp); + + trap_devel_log("av 0x%x\n", av); /* audited value */ + trap_devel_log("av_rule 0x%x\n", av_rule); /* result: string matched position */ + + rc = av & ~av_rule; + if (rc) + trap_devel_log("SELinux: trap: different\n"); + else + trap_devel_log("SELinux: trap: identical\n"); + + return rc; +} + +/* param in ad ... check item */ +/* return 0 = blacklist matched or alllist unmatched / 1 = whitelist matched */ +static int mask_trap(struct common_audit_data *ad) +{ + int ret = 0; + struct selinux_trap_list *entry; + rcu_read_lock(); + /* Blacklist matching */ + list_for_each_entry_rcu(entry, &selinux_trap_list_head.list, list) { + if (cmp_polarity( + entry->item_array[TRAP_MASK_TYPE_POLARITY], + '-')) + continue; /* different to next rule */ + else if (cmp_scontext( + entry->item_array[TRAP_MASK_TYPE_SCONTEXT], + ad)) + continue; /* different to next rule */ + else if (cmp_tcontext( + entry->item_array[TRAP_MASK_TYPE_TCONTEXT], + ad)) + continue; /* different to next rule */ + else if (cmp_tclass( + entry->item_array[TRAP_MASK_TYPE_TCLASS], + ad)) + continue; /* different to next rule */ + else if (cmp_pname(entry->item_array[TRAP_MASK_TYPE_PNAME], ad)) + continue; /* different to next rule */ + else if (cmp_pname_parent( + entry->item_array[TRAP_MASK_TYPE_PNAME_PARENT], + ad)) + continue; /* different to next rule */ +#if 0 /* XXX: temporarily disabled to check process group leader */ + else if (cmp_pname_pgl( + entry->item_array[TRAP_MASK_TYPE_PNAME_PGL], + ad)) + continue; /* different to next rule */ +#endif + else if (cmp_path( + entry->item_array[TRAP_MASK_TYPE_PATH], + ad)) + continue; /* different to next rule */ + else if (cmp_name( + entry->item_array[TRAP_MASK_TYPE_NAME], + ad)) + continue; /* different to next rule */ + else if (cmp_action( + entry->item_array[TRAP_MASK_TYPE_ACTION], + ad)) + continue; /* different to next rule */ + /* Rule matched. It requires forcible crash. */ + ret = 0; + goto out; + } + /* Whitelist matching */ + list_for_each_entry_rcu(entry, &selinux_trap_list_head.list, list) { + if (cmp_polarity( + entry->item_array[TRAP_MASK_TYPE_POLARITY], + '+')) + continue; /* different to next rule */ + else if (cmp_scontext( + entry->item_array[TRAP_MASK_TYPE_SCONTEXT], + ad)) + continue; /* different to next rule */ + else if (cmp_tcontext( + entry->item_array[TRAP_MASK_TYPE_TCONTEXT], + ad)) + continue; /* different to next rule */ + else if (cmp_tclass( + entry->item_array[TRAP_MASK_TYPE_TCLASS], + ad)) + continue; /* different to next rule */ + else if (cmp_pname(entry->item_array[TRAP_MASK_TYPE_PNAME], ad)) + continue; /* different to next rule */ + else if (cmp_pname_parent( + entry->item_array[TRAP_MASK_TYPE_PNAME_PARENT], + ad)) + continue; /* different to next rule */ +#if 0 /* XXX: temporarily disabled to check process group leader */ + else if (cmp_pname_pgl( + entry->item_array[TRAP_MASK_TYPE_PNAME_PGL], + ad)) + continue; /* different to next rule */ +#endif + else if (cmp_path( + entry->item_array[TRAP_MASK_TYPE_PATH], + ad)) + continue; /* different to next rule */ + else if (cmp_name( + entry->item_array[TRAP_MASK_TYPE_NAME], + ad)) + continue; /* different to next rule */ + else if (cmp_action( + entry->item_array[TRAP_MASK_TYPE_ACTION], + ad)) + continue; /* different to next rule */ + /* Rule matched. It's exception. */ + ret = 1; + goto out; + } + /* All list not matched ..."ret = 0" */ +out: + rcu_read_unlock(); + return ret; +} + +static void task_killer(struct work_struct *param) +{ + int ret; + struct siginfo info; + struct _trapwork *work = (struct _trapwork *)param; + + memset(&info, 0, sizeof(struct siginfo)); + info.si_signo = SIGABRT; + info.si_code = SI_KERNEL; + pr_info("SELinux: trap: send signal to pid:%d.\n", + work->task->pid); + ret = send_sig_info(SIGABRT, &info, work->task); + if (ret < 0) + pr_err("SELinux: trap: send_sig_info failed\n"); +} + +static void dump_common_audit_data_part(struct common_audit_data *ad, char type, char string[], int len, int maxlen) +{ + struct task_struct *tsk = current; + + /* string work Initialize */ + string_work[0] = '\0'; + + switch (type) { + case STRING_LSM_AUDIT_DATA_ACTION: { + const char **perms; + int i, perm; + u16 tclass = ad->selinux_audit_data->tclass; + u32 av = ad->selinux_audit_data->audited; + + if (av == 0) + break; + perms = secclass_map[tclass-1].perms; + i = 0; + perm = 1; + while (i < (sizeof(av) * 8)) { + if ((perm & av) && perms[i]) + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " { %s }", perms[i]); + i++; + perm <<= 1; + } + break; + } + case STRING_LSM_AUDIT_DATA_PID: + if (tsk && tsk->pid) { + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " pid=%d comm=\"%s\"", tsk->pid, tsk->comm); + } + break; + case STRING_LSM_AUDIT_DATA_NONE: + break; + case STRING_LSM_AUDIT_DATA_IPC: + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " key=%d ", ad->u.ipc_id); + break; + case STRING_LSM_AUDIT_DATA_CAP: + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " capability=%d ", ad->u.cap); + break; + case STRING_LSM_AUDIT_DATA_PATH: { + struct inode *inode; + if (ad->type != LSM_AUDIT_DATA_PATH) + break; + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " path=%s", ad->u.path.dentry->d_iname); + inode = ad->u.path.dentry->d_inode; + if (inode) { + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " dev=\"%s\"", inode->i_sb->s_id); + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " ino=%lu", inode->i_ino); + } + break; + } + case STRING_LSM_AUDIT_DATA_DENTRY: { + struct inode *inode; + if (ad->type != LSM_AUDIT_DATA_DENTRY) + break; + snprintf(string_work, STRING_PART_LEN_MAX, " name=%s", ad->u.dentry->d_name.name); + inode = ad->u.dentry->d_inode; + if (inode) { + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " dev=\"%s\"", inode->i_sb->s_id); + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " ino=%lu", inode->i_ino); + } + break; + } + case STRING_LSM_AUDIT_DATA_INODE: + break; + + case STRING_LSM_AUDIT_DATA_TASK: + tsk = ad->u.tsk; + if (tsk && tsk->pid) { + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " pid=%d comm=\"%s\"", tsk->pid, tsk->comm); + } + break; + case STRING_LSM_AUDIT_DATA_NET: + break; +#ifdef CONFIG_KEYS + case STRING_LSM_AUDIT_DATA_KEY: + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " key_serial=%u", ad->u.key_struct.key); + if (ad->u.key_struct.key_desc) { + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " key_desc=%s", ad->u.key_struct.key_desc); + } + break; +#endif + case STRING_LSM_AUDIT_DATA_KMOD: + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " kmod=%s", ad->u.kmod_name); + break; + + case STRING_LSM_AUDIT_DATA_SCONTEXT: { + int rc; + char *scontext; + u32 scontext_len; + u32 ssid = ad->selinux_audit_data->ssid; + rc = security_sid_to_context(ssid, &scontext, &scontext_len); + if (rc) { + break; + } + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " scontext=%s", scontext); + kfree(scontext); + break; + } + case STRING_LSM_AUDIT_DATA_TCONTEXT: { + int rc; + char *tcontext; + u32 tcontext_len; + u32 tsid = ad->selinux_audit_data->tsid; + rc = security_sid_to_context(tsid, &tcontext, &tcontext_len); + if (rc) { + break; + } + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " tcontext=%s", tcontext); + kfree(tcontext); + break; + } + case STRING_LSM_AUDIT_DATA_TCLASS: { + u16 tclass = ad->selinux_audit_data->tclass; + + BUG_ON(tclass >= secclass_map_size); + + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " tclass=%s", secclass_map[tclass-1].name); + break; + } + case STRING_LSM_AUDIT_DATA_PERMISSIVE: + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " permissive=%u", ad->selinux_audit_data->result ? 0 : 1); + if (tsk && tsk->pid) { + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " ppid=%d pcomm=\"%s\"", tsk->parent->pid, tsk->parent->comm); + if (tsk->group_leader->pid != tsk->pid) { + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " pgid=%d pgcomm=\"%s\"", tsk->group_leader->pid, tsk->group_leader->comm); + } else if (tsk->parent->group_leader->pid) { + snprintf(&string_work[strlen(string_work)], STRING_PART_LEN_MAX-strlen(string_work), " pgid=%d pgcomm=\"%s\"", tsk->parent->group_leader->pid, tsk->parent->group_leader->comm); + } + } + break; + } /* switch (ad->type) */ + /* parent buff size check */ + if (strlen(string_work) < maxlen - len) { + /* parent buff copy */ + strlcpy(&string[len], string_work, maxlen - len); + } +} + +static char *dump_audit_data(struct common_audit_data *ad) +{ + char *string = NULL; + int i; + + string = kmalloc(STRING_ALL_LEN_MAX, GFP_KERNEL); + if (!string) { + /* error */ + return NULL; + } + + /* string work Initialize */ + string[0] = '\0'; + + for (i = 0; STRING_LSM_AUDIT_DATA_TERM != copy_table[i]; i++) { + dump_common_audit_data_part(ad, copy_table[i], string, strlen(string), STRING_ALL_LEN_MAX); + } + return string; +} + +static void trapped_node_update(struct common_audit_data *ad) +{ + char *string = NULL; + struct task_struct *tsk = current; + + rcu_read_lock(); + + string = dump_audit_data(ad); + if (string) { + /* kmsg output */ + pr_info("SELinux: trap: caught%s", string); + /* Node Entry */ + trapped_node_entry(tsk->pid, string); + } + + rcu_read_unlock(); +} + +void trap_selinux_error(struct common_audit_data *ad) +{ + int ret; + struct _trapwork *work; + + if (!selinux_trap_enable) + return; + + /* black list and white list check */ + if (mask_trap(ad)) { + /* Ignore error type( No problem ) */ + if (selinux_trap_debug >= TRAP_LOGLEVEL_NORMAL) { + pr_info("SELinux: trap: Ignore SELinux violation(pid = %d).\n", ((struct task_struct *)current)->pid); + } + return; + } + + /* trapped Update & kmeg */ + trapped_node_update(ad); + + /* Violation( process Abort ) */ + work = kmalloc(sizeof(struct _trapwork), GFP_KERNEL); + if (work) { + INIT_WORK((struct work_struct *)work, task_killer); + work->task = current; + ret = schedule_work((struct work_struct *)work); + if (!ret) + pr_err("SELinux: trap: schedule_work failed\n"); + + pr_info("SELinux: trap: block process.\n"); + flush_work((struct work_struct *)work); + pr_info("SELinux: trap: show stack.\n"); + show_stack(work->task, NULL); + pr_info("SELinux: trap: process came back.\n"); + } else { + pr_err("SELinux: trap: kmalloc() failed\n"); + } + kfree((void *)work); +} + +static int selinux_trap_init(void) +{ + int ret = 0; + selinux_trap_debug = TRAP_LOGLEVEL_NORMAL; + trap_devel_log("SELinux: trap: Init module\n"); + sema_init(&selinux_trap_list_sem, 1); + INIT_LIST_HEAD(&selinux_trap_list_head.list); + INIT_LIST_HEAD(&selinux_trap_process_list_head.list); + return ret; +} + +static void selinux_trap_exit(void) +{ + trap_devel_log("SELinux: trap: Exit module\n"); +} + +module_init(selinux_trap_init); +module_exit(selinux_trap_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Naoya Hirota "); +MODULE_AUTHOR("Naoya Inoue "); +MODULE_DESCRIPTION("SELinux error trap extention"); diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 180261da33c9d585b2fc50830b2e49e4a28a5c36..554627511123e89c54c15d5af05522e6f4fefb93 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -978,6 +978,7 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, if (userbuf) mutex_lock(&runtime->realloc_mutex); + spin_lock_irqsave(&runtime->lock, flags); while (count > 0 && runtime->avail) { count1 = runtime->buffer_size - runtime->appl_ptr; @@ -1007,6 +1008,7 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, count -= count1; } spin_unlock_irqrestore(&runtime->lock, flags); + if (userbuf) mutex_unlock(&runtime->realloc_mutex); return result; diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 997a623033fbf23b551c44566c1447a436b50ddf..8f7e47b5f5f323355adac58d93e5831446d81bf6 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -44,18 +49,22 @@ #define SPECIAL_HS_DETECT_TIME_MS (2 * 1000) #define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 #define GND_MIC_SWAP_THRESHOLD 4 -#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 +#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 150 #define HS_VREF_MIN_VAL 1400 #define FW_READ_ATTEMPTS 15 #define FW_READ_TIMEOUT 4000000 -#define FAKE_REM_RETRY_ATTEMPTS 3 -#define MAX_IMPED 60000 +#define FAKE_REM_RETRY_ATTEMPTS 10 #define WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS 50 #define ANC_DETECT_RETRY_CNT 7 #define WCD_MBHC_SPL_HS_CNT 1 +static bool skip_impdet_retry; +static bool lineout_detected; + static int det_extn_cable_en; +static int is_anc_mic_insert = -1; + module_param(det_extn_cable_en, int, S_IRUGO | S_IWUSR | S_IWGRP); MODULE_PARM_DESC(det_extn_cable_en, "enable/disable extn cable detect"); @@ -67,6 +76,32 @@ enum wcd_mbhc_cs_mb_en_flag { WCD_MBHC_EN_NONE, }; +int wcd_mbhc_jack_is_anc_mic_insert(void) +{ + return is_anc_mic_insert; +} +static void wcd_mbhc_jack_anc_insert_update(u32/*enum snd_jack_type*/ hs_type) +{ + switch(hs_type) { + case SND_JACK_HEADPHONE: + is_anc_mic_insert = 0; + break; + case SND_JACK_HEADSET: + is_anc_mic_insert = 1; + break; + case SND_JACK_ANC_HEADPHONE: + is_anc_mic_insert = 2; + break; + case SND_JACK_STEREO_MICROPHONE: + is_anc_mic_insert = 3; + break; + default : + is_anc_mic_insert = -1; + break; + } + pr_debug("wcd_mbhc_jack_anc_insert_update is_anc_mic_insert:%d", is_anc_mic_insert); +} + static void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc, struct snd_soc_jack *jack, int status, int mask) { @@ -608,6 +643,7 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, { struct snd_soc_codec *codec = mbhc->codec; bool is_pa_on = false; + bool skip_report = false; WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); @@ -648,7 +684,10 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, } mbhc->hph_type = WCD_MBHC_HPH_NONE; - mbhc->zl = mbhc->zr = 0; + mbhc->extn_cable_inserted = false; + lineout_detected = false; + if (!skip_impdet_retry) + mbhc->zl = mbhc->zr = 0; pr_debug("%s: Reporting removal %d(%x)\n", __func__, jack_type, mbhc->hph_status); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, @@ -687,7 +726,9 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->micbias_enable = false; } mbhc->hph_type = WCD_MBHC_HPH_NONE; - mbhc->zl = mbhc->zr = 0; + lineout_detected = false; + if (!skip_impdet_retry) + mbhc->zl = mbhc->zr = 0; pr_debug("%s: Reporting removal (%x)\n", __func__, mbhc->hph_status); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, @@ -728,6 +769,8 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->jiffies_atreport = jiffies; } else if (jack_type == SND_JACK_LINEOUT) { mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + skip_report = true; + pr_debug("%s: extension cable detected\n", __func__); } else if (jack_type == SND_JACK_ANC_HEADPHONE) mbhc->current_plug = MBHC_PLUG_TYPE_ANC_HEADPHONE; @@ -738,16 +781,42 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->mbhc_cb->compute_impedance && (mbhc->mbhc_cfg->linein_th != 0) && (!is_pa_on)) { + if (!skip_impdet_retry) { mbhc->mbhc_cb->compute_impedance(mbhc, &mbhc->zl, &mbhc->zr); - if ((mbhc->zl > mbhc->mbhc_cfg->linein_th && - mbhc->zl < MAX_IMPED) && - (mbhc->zr > mbhc->mbhc_cfg->linein_th && - mbhc->zr < MAX_IMPED) && - (jack_type == SND_JACK_HEADPHONE)) { + pr_debug("%s: impedance L:%d R:%d\n", __func__, + mbhc->zl, mbhc->zr); + } else { + pr_debug("%s: skip impedance detection\n", + __func__); + } + + if (jack_type == SND_JACK_HEADPHONE) + skip_impdet_retry = true; + else + skip_impdet_retry = false; + + if (mbhc->zl > mbhc->mbhc_cfg->linein_th && + jack_type == SND_JACK_ANC_HEADPHONE) { + if(!wcd_mbhc_is_hph_pa_on(mbhc)) { + jack_type = SND_JACK_STEREO_MICROPHONE; + mbhc->current_plug = + MBHC_PLUG_TYPE_STEREO_MICROPHONE; + mbhc->hph_status &= ~SND_JACK_HEADPHONE; + pr_debug("%s: Stereo microphone detected\n", + __func__); + } else { + pr_debug("%s: Skip Stereo microphone reporting\n", + __func__); + } + } else if (mbhc->zl > mbhc->mbhc_cfg->linein_th && + mbhc->zr > mbhc->mbhc_cfg->linein_th && + jack_type == SND_JACK_HEADPHONE) { jack_type = SND_JACK_LINEOUT; mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; - if (mbhc->hph_status) { + lineout_detected = true; + if (mbhc->hph_status && + mbhc->hph_status != SND_JACK_LINEOUT) { mbhc->hph_status &= ~(SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_UNSUPPORTED); @@ -763,11 +832,16 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->hph_status |= jack_type; - pr_debug("%s: Reporting insertion %d(%x)\n", __func__, - jack_type, mbhc->hph_status); - wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, - (mbhc->hph_status | SND_JACK_MECHANICAL), - WCD_MBHC_JACK_MASK); + if (!skip_report) { + pr_debug("%s: Reporting insertion %d(%x)\n", __func__, + jack_type, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + (mbhc->hph_status | + SND_JACK_MECHANICAL), + WCD_MBHC_JACK_MASK); + } else { + pr_debug("%s: Skip reporting insertion\n", __func__); + } wcd_mbhc_clr_and_turnon_hph_padac(mbhc); } pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status); @@ -930,6 +1004,7 @@ static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, mbhc->current_plug, plug_type); } exit: + wcd_mbhc_jack_anc_insert_update(mbhc->hph_status);//hph_status is updated with latest info pr_debug("%s: leave\n", __func__); } @@ -1089,6 +1164,7 @@ static void wcd_mbhc_update_fsm_source(struct wcd_mbhc *mbhc, break; case MBHC_PLUG_TYPE_HEADSET: case MBHC_PLUG_TYPE_ANC_HEADPHONE: + case MBHC_PLUG_TYPE_STEREO_MICROPHONE: if (!mbhc->is_hs_recording && !micbias2) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); break; @@ -1211,6 +1287,10 @@ static void wcd_correct_swch_plug(struct work_struct *work) mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); codec = mbhc->codec; + /* Wait for debounce time 200ms for extension cable */ + if (mbhc->extn_cable_inserted) + msleep(200); + /* * Enable micbias/pullup for detection in correct work. * This work will get scheduled from detect_plug_type which @@ -1422,7 +1502,9 @@ correct_plug_type: if (((mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) && (mbhc->current_plug != - MBHC_PLUG_TYPE_ANC_HEADPHONE)) && + MBHC_PLUG_TYPE_ANC_HEADPHONE) && + (mbhc->current_plug != + MBHC_PLUG_TYPE_STEREO_MICROPHONE)) && !wcd_swch_level_remove(mbhc) && !mbhc->btn_press_intr) { pr_debug("%s: cable is %sheadset\n", @@ -1439,21 +1521,25 @@ correct_plug_type: if (!wrk_complete && mbhc->btn_press_intr) { pr_debug("%s: Can be slow insertion of headphone\n", __func__); wcd_cancel_btn_work(mbhc); - plug_type = MBHC_PLUG_TYPE_HEADPHONE; + if (lineout_detected) + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + else + plug_type = MBHC_PLUG_TYPE_HEADPHONE; } /* * If plug_tye is headset, we might have already reported either in * detect_plug-type or in above while loop, no need to report again */ if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) || - (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) { + (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE) || + (plug_type == MBHC_PLUG_TYPE_STEREO_MICROPHONE))) { pr_debug("%s: plug_type:0x%x already reported\n", __func__, mbhc->current_plug); goto enable_supply; } if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH && - (!det_extn_cable_en)) { + (!det_extn_cable_en) && (!lineout_detected)) { if (wcd_is_special_headset(mbhc)) { pr_debug("%s: Special headset found %d\n", __func__, plug_type); @@ -1532,6 +1618,8 @@ exit: if (mbhc->mbhc_cb->hph_pull_down_ctrl) mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true); + skip_impdet_retry = false; + mbhc->mbhc_cb->lock_sleep(mbhc, false); pr_debug("%s: leave\n", __func__); } @@ -1588,6 +1676,10 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) /* Set the detection type appropriately */ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE, !detection_type); + if (!detection_type) { + //reset headset type at hs removing + wcd_mbhc_jack_anc_insert_update(-1); + } pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__, mbhc->current_plug, detection_type); @@ -1685,6 +1777,15 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); wcd_mbhc_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE); + } else if (mbhc->current_plug == + MBHC_PLUG_TYPE_STEREO_MICROPHONE) { + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, false); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + wcd_mbhc_report_plug(mbhc, + 0, SND_JACK_STEREO_MICROPHONE); } } else if (!detection_type) { /* Disable external voltage source to micbias if present */ @@ -1694,7 +1795,6 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); } - mbhc->in_swch_irq_handler = false; WCD_MBHC_RSC_UNLOCK(mbhc); pr_debug("%s: leave\n", __func__); @@ -1825,6 +1925,7 @@ determine_plug: hphl_trigerred = 0; mic_trigerred = 0; mbhc->is_extn_cable = true; + mbhc->extn_cable_inserted = true; mbhc->btn_press_intr = false; mbhc->is_btn_press = false; wcd_mbhc_detect_plug_type(mbhc); @@ -2831,6 +2932,7 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, mbhc->btn_press_intr = false; mbhc->is_hs_recording = false; mbhc->is_extn_cable = false; + mbhc->extn_cable_inserted = false; mbhc->hph_type = WCD_MBHC_HPH_NONE; mbhc->wcd_mbhc_regs = wcd_mbhc_regs; diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index 09b9307ad61de5de9d85a0fd9b29dec01c08af48..8b53462e364037adb94e3b45d5ea885c458f5cb6 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef __WCD_MBHC_V2_H__ #define __WCD_MBHC_V2_H__ @@ -22,7 +27,7 @@ #define WCD_MBHC_DEF_BUTTONS 8 #define WCD_MBHC_KEYCODE_NUM 8 #define WCD_MBHC_USLEEP_RANGE_MARGIN_US 100 -#define WCD_MBHC_THR_HS_MICB_MV 2700 +#define WCD_MBHC_THR_HS_MICB_MV 2750 /* z value defined in Ohms */ #define WCD_MONO_HS_MIN_THR 2 #define WCD_MBHC_STRINGIFY(s) __stringify(s) @@ -82,6 +87,7 @@ enum wcd_mbhc_plug_type { MBHC_PLUG_TYPE_HIGH_HPH, MBHC_PLUG_TYPE_GND_MIC_SWAP, MBHC_PLUG_TYPE_ANC_HEADPHONE, + MBHC_PLUG_TYPE_STEREO_MICROPHONE, }; enum pa_dac_ack_flags { @@ -423,6 +429,7 @@ struct wcd_mbhc { bool btn_press_intr; bool is_hs_recording; bool is_extn_cable; + bool extn_cable_inserted; bool skip_imped_detection; bool is_btn_already_regd; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 2bc911e63e12f989cbc1d74d3076c7455fe450d8..4a20e352ca80f7cf61ad8d2f87346c707a549a45 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -869,8 +874,8 @@ static const struct tasha_reg_mask_val tasha_spkr_default[] = { {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80}, {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01}, {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01}, - {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50}, - {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x58}, }; static const struct tasha_reg_mask_val tasha_spkr_mode1[] = { @@ -1857,7 +1862,7 @@ static inline void tasha_mbhc_get_result_params(struct wcd9xxx *wcd9xxx, if ((c1 < 2) && x1) usleep_range(5000, 5050); - if (!c1 || !x1) { + if (!c1) { dev_dbg(wcd9xxx->dev, "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", __func__, c1, x1); @@ -5241,6 +5246,22 @@ static int tasha_codec_config_ear_spkr_gain(struct snd_soc_codec *codec, return 0; } +static void tasha_codec_set_offset_val(int *offset_val, int gain_offset, + int mult) +{ + switch (gain_offset) { + case RX_GAIN_OFFSET_M0P5_DB: + *offset_val = 1 * mult; + break; + case RX_GAIN_OFFSET_M1P5_DB: + *offset_val = 2 * mult; + break; + default: + pr_err("Improper gain offset\n"); + break; + } +} + static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -5288,7 +5309,7 @@ static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: - if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + if ((tasha->spkr_gain_offset != RX_GAIN_OFFSET_0_DB) && (tasha->comp_enabled[COMPANDER_7] || tasha->comp_enabled[COMPANDER_8]) && (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL || @@ -5303,7 +5324,8 @@ static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x01, 0x01); - offset_val = -2; + tasha_codec_set_offset_val(&offset_val, + tasha->spkr_gain_offset, -1); } val = snd_soc_read(codec, gain_reg); val += offset_val; @@ -5311,7 +5333,7 @@ static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w, tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); break; case SND_SOC_DAPM_POST_PMD: - if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + if ((tasha->spkr_gain_offset != RX_GAIN_OFFSET_0_DB) && (tasha->comp_enabled[COMPANDER_7] || tasha->comp_enabled[COMPANDER_8]) && (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL || @@ -5326,7 +5348,8 @@ static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x01, 0x00); - offset_val = 2; + tasha_codec_set_offset_val(&offset_val, + tasha->spkr_gain_offset, 1); val = snd_soc_read(codec, gain_reg); val += offset_val; snd_soc_write(codec, gain_reg, val); @@ -5516,7 +5539,7 @@ static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: tasha_config_compander(codec, w->shift, event); /* apply gain after int clk is enabled */ - if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + if ((tasha->spkr_gain_offset != RX_GAIN_OFFSET_0_DB) && (tasha->comp_enabled[COMPANDER_7] || tasha->comp_enabled[COMPANDER_8]) && (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || @@ -5531,7 +5554,8 @@ static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x01, 0x01); - offset_val = -2; + tasha_codec_set_offset_val(&offset_val, + tasha->spkr_gain_offset, -1); } val = snd_soc_read(codec, gain_reg); val += offset_val; @@ -5541,7 +5565,7 @@ static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMD: tasha_config_compander(codec, w->shift, event); tasha_codec_enable_prim_interpolator(codec, reg, event); - if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + if ((tasha->spkr_gain_offset != RX_GAIN_OFFSET_0_DB) && (tasha->comp_enabled[COMPANDER_7] || tasha->comp_enabled[COMPANDER_8]) && (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || @@ -5556,7 +5580,8 @@ static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x01, 0x00); - offset_val = 2; + tasha_codec_set_offset_val(&offset_val, + tasha->spkr_gain_offset, 1); val = snd_soc_read(codec, gain_reg); val += offset_val; snd_soc_write(codec, gain_reg, val); @@ -12533,8 +12558,8 @@ static const struct tasha_reg_mask_val tasha_codec_reg_init_common_val[] = { {WCD9335_CDC_CLSH_K2_MSB, 0x0F, 0x00}, {WCD9335_CDC_CLSH_K2_LSB, 0xFF, 0x60}, {WCD9335_CPE_SS_DMIC_CFG, 0x80, 0x00}, - {WCD9335_CDC_BOOST0_BOOST_CTL, 0x70, 0x50}, - {WCD9335_CDC_BOOST1_BOOST_CTL, 0x70, 0x50}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x58}, {WCD9335_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, {WCD9335_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, {WCD9335_ANA_LO_1_2, 0x3C, 0X3C}, @@ -13614,7 +13639,7 @@ static int tasha_codec_probe(struct snd_soc_codec *codec) for (i = 0; i < COMPANDER_MAX; i++) tasha->comp_enabled[i] = 0; - tasha->spkr_gain_offset = RX_GAIN_OFFSET_0_DB; + tasha->spkr_gain_offset = RX_GAIN_OFFSET_M0P5_DB; tasha->intf_type = wcd9xxx_get_intf_type(); tasha_update_reg_reset_values(codec); pr_debug("%s: MCLK Rate = %x\n", __func__, control->mclk_rate); diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h index 8d38399ba92f04c150b35fea6134ada48b35dc2f..f028f0526f53bf1c9b7ba2454239d0ed60addf59 100644 --- a/sound/soc/codecs/wcd9335.h +++ b/sound/soc/codecs/wcd9335.h @@ -172,6 +172,7 @@ enum { */ enum { RX_GAIN_OFFSET_M1P5_DB, + RX_GAIN_OFFSET_M0P5_DB, RX_GAIN_OFFSET_0_DB, }; diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index 337c25f869d6d0d2650755c82e033c366f930ade..7b69599bf9ad5350c66f7a507e28e7a6748178e1 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -830,6 +835,8 @@ static int wcd_cpe_enable(struct wcd_cpe_core *core, bool enable) { int ret = 0; + int timeout = 0; + int err_cnt = 0; if (enable) { /* Reset CPE first */ @@ -852,8 +859,19 @@ static int wcd_cpe_enable(struct wcd_cpe_core *core, if (ret) goto fail_boot; - /* Dload data section */ - ret = wcd_cpe_load_fw(core, ELF_FLAG_RW); + for (err_cnt = 0; err_cnt < 10; err_cnt++) { + /* Dload data section */ + ret = wcd_cpe_load_fw(core, ELF_FLAG_RW); + if (ret) { + pr_err("%s: wcd_cpe_load_fw error ret=%d. retry.\n", __func__, ret); + msleep(5); + } else { + if (err_cnt > 0) { + pr_err("%s: wcd_cpe_load_fw error count=%d.\n", __func__, err_cnt); + } + break; + } + } if (ret) { dev_err(core->dev, "%s: Failed to dload data section, err = %d\n", @@ -882,9 +900,18 @@ static int wcd_cpe_enable(struct wcd_cpe_core *core, dev_dbg(core->dev, "%s: waiting for CPE bootup\n", __func__); - +#if 0 wait_for_completion(&core->online_compl); - +#else + timeout = wait_for_completion_timeout(&core->online_compl, msecs_to_jiffies(1000)); + if (!timeout) { + dev_err(core->dev, + "%s: Timeout boot CPE.\n", + __func__); + ret = -ETIMEDOUT; + goto fail_boot; + } +#endif dev_dbg(core->dev, "%s: CPE bootup done\n", __func__); @@ -1197,6 +1224,9 @@ static irqreturn_t svass_exception_irq(int irq, void *data) dev_err(core->dev, "%s: CPE SSR event,err_status = 0x%02x\n", __func__, status); + core->ssr_entry.err_status = status; + core->ssr_entry.err_data_ready = 1; + wake_up(&core->ssr_entry.err_status_debug_q); wcd_cpe_ssr_event(core, WCD_CPE_SSR_EVENT); /* * If fatal interrupt is received, @@ -1681,6 +1711,44 @@ done: return ret; } +static ssize_t cpe_err_status_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + int r; + char buf[32]; + size_t size; + struct wcd_cpe_core *core = filp->private_data; + struct wcd_cpe_ssr_entry *ssr_entry = &core->ssr_entry; + + r = snprintf(buf, sizeof(buf), + "err_status = 0x%02x", ssr_entry->err_status); + size = simple_read_from_buffer(ubuf, cnt, ppos, buf, r); + if (*ppos == r) + ssr_entry->err_data_ready = 0; + + return size; +} + +static unsigned int cpe_err_status_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct wcd_cpe_core *core = filp->private_data; + struct wcd_cpe_ssr_entry *ssr_entry = &core->ssr_entry; + unsigned int mask = 0; + + if (ssr_entry->err_data_ready) + mask |= (POLLIN | POLLRDNORM); + + poll_wait(filp, &ssr_entry->err_status_debug_q, wait); + return mask; +} + +static const struct file_operations cpe_err_status_fops = { + .open = simple_open, + .read = cpe_err_status_read, + .poll = cpe_err_status_poll, +}; + static int wcd_cpe_debugfs_init(struct wcd_cpe_core *core) { int rc = 0; @@ -1716,6 +1784,18 @@ static int wcd_cpe_debugfs_init(struct wcd_cpe_core *core) goto err_create_entry; } + if (!debugfs_create_file("err_status", S_IRUGO, + dir, core, &cpe_err_status_fops)) { + dev_err(core->dev, "%s: Failed to create debugfs node %s\n", + __func__, "err_status"); + rc = -ENODEV; + goto err_create_entry; + } + + init_waitqueue_head(&core->ssr_entry.err_status_debug_q); + + return 0; + err_create_entry: debugfs_remove(dir); diff --git a/sound/soc/codecs/wcd_cpe_core.h b/sound/soc/codecs/wcd_cpe_core.h index 1ac39347119743a0246eb7ade0ce59a8b9de8c99..243cb0a9b35ecf9adfa83253a8d7e20d0f01e214 100644 --- a/sound/soc/codecs/wcd_cpe_core.h +++ b/sound/soc/codecs/wcd_cpe_core.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef WCD_CPE_CORE_H #define WCD_CPE_CORE_H @@ -95,6 +100,9 @@ struct wcd_cpe_ssr_entry { int offline; u32 offline_change; wait_queue_head_t offline_poll_wait; + int err_status; + int err_data_ready; + wait_queue_head_t err_status_debug_q; struct snd_info_entry *entry; }; diff --git a/sound/soc/codecs/wcd_cpe_services.c b/sound/soc/codecs/wcd_cpe_services.c index 58f02f1f35f5bfd181b35004acb4ede0b0310c05..559474e95038977d7ed79dd0123ff6990b995fd1 100644 --- a/sound/soc/codecs/wcd_cpe_services.c +++ b/sound/soc/codecs/wcd_cpe_services.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -2865,8 +2870,19 @@ static enum cpe_svc_result cpe_tgt_wcd9335_write_RAM(struct cpe_info *t_info, return CPE_SVC_FAILED; } +#if 0 cpe_register_write_repeat(WCD9335_CPE_SS_MEM_BANK_0, temp_ptr, to_write); +#else + rc = cpe_register_write_repeat(WCD9335_CPE_SS_MEM_BANK_0, + temp_ptr, to_write); + if (rc) { + pr_err("%s: cpe_register_write_repeat error rc=%d\n", __func__, rc); + cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + return rc; + } +#endif + temp_size += CHUNK_SIZE; temp_ptr += CHUNK_SIZE; } diff --git a/sound/soc/codecs/wcdcal-hwdep.c b/sound/soc/codecs/wcdcal-hwdep.c index 58e51542ed66d43b5398bb3157e4e8234882c48e..ce15f54db44c99c901714af8bab9ab8359d43138 100755 --- a/sound/soc/codecs/wcdcal-hwdep.c +++ b/sound/soc/codecs/wcdcal-hwdep.c @@ -11,6 +11,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -21,8 +26,10 @@ #include #include "wcdcal-hwdep.h" +extern int wcd_mbhc_jack_is_anc_mic_insert(void); + const int cal_size_info[WCD9XXX_MAX_CAL] = { - [WCD9XXX_ANC_CAL] = 16384, + [WCD9XXX_ANC_CAL] = 36864, [WCD9XXX_MBHC_CAL] = 4096, [WCD9XXX_MAD_CAL] = 4096, [WCD9XXX_VBAT_CAL] = 72, @@ -113,6 +120,18 @@ static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg; struct wcdcal_ioctl_buffer32 fw_user32; struct wcdcal_ioctl_buffer fw_user_compat; + int is_anc_insert = -1; + is_anc_insert = wcd_mbhc_jack_is_anc_mic_insert(); + pr_debug("%s cmd:%x\n", __func__, cmd); + if (cmd == SNDRV_CTL_IOCTL_HWDEP_IS_ANC_INSERT) { + pr_debug("%s SNDRV_CTL_IOCTL_HWDEP_IS_ANC_INSERT ioctl\n", __func__); + if (copy_to_user((void *)arg, &is_anc_insert, + sizeof(int))) { + pr_err("%s: Copy to user failed\n", __func__); + return -EFAULT; + } + return 0; + } if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) { pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd); @@ -136,7 +155,17 @@ static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, { struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg; struct wcdcal_ioctl_buffer fw_user; + int is_anc_insert = wcd_mbhc_jack_is_anc_mic_insert(); + if (cmd == SNDRV_CTL_IOCTL_HWDEP_IS_ANC_INSERT) { + pr_debug("%s SNDRV_CTL_IOCTL_HWDEP_IS_ANC_INSERT ioctl\n", __func__); + if (copy_to_user((void *)arg, &is_anc_insert, + sizeof(int))) { + pr_err("%s: Copy to user failed\n", __func__); + return -EFAULT; + } + return 0; + } if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) { pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd); return -ENOIOCTLCMD; diff --git a/sound/soc/codecs/wsa881x-regmap.c b/sound/soc/codecs/wsa881x-regmap.c index 20dc3508a5aff30e47139e35f9dd9595a85acdd0..909f63d53029bb520de4324fe458dd0ed93ce8eb 100644 --- a/sound/soc/codecs/wsa881x-regmap.c +++ b/sound/soc/codecs/wsa881x-regmap.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -174,7 +179,7 @@ static struct reg_sequence wsa881x_rev_2_0[] = { {WSA881X_SPKR_BIAS_INT, 0x5F, 0x00}, {WSA881X_SPKR_BIAS_PSRR, 0x44, 0x00}, {WSA881X_BOOST_PS_CTL, 0xA0, 0x00}, - {WSA881X_BOOST_PRESET_OUT1, 0xB7, 0x00}, + {WSA881X_BOOST_PRESET_OUT1, 0x37, 0x00}, {WSA881X_BOOST_LOOP_STABILITY, 0x8D, 0x00}, {WSA881X_SPKR_PROT_ATEST2, 0x02, 0x00}, {WSA881X_BONGO_RESRV_REG1, 0x5E, 0x00}, diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index e4f6df077d98f2095289488397d8499630ee3a02..be9d5e71e859e2ad04100a03bcec11da3a1cb961 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -76,6 +81,7 @@ struct swr_port { enum { WSA881X_DEV_DOWN, WSA881X_DEV_UP, + WSA881X_DEV_READY, }; /* @@ -99,6 +105,7 @@ struct wsa881x_priv { int version; struct mutex bg_lock; struct mutex res_lock; + struct mutex temp_lock; struct snd_info_entry *entry; struct snd_info_entry *version_entry; int state; @@ -464,6 +471,17 @@ static const struct file_operations codec_debug_ops = { .read = codec_debug_read, }; +static void wsa881x_regcache_sync(struct wsa881x_priv *wsa881x) +{ + mutex_lock(&wsa881x->res_lock); + if (wsa881x->state != WSA881X_DEV_READY) { + regcache_mark_dirty(wsa881x->regmap); + regcache_sync(wsa881x->regmap); + wsa881x->state = WSA881X_DEV_READY; + } + mutex_unlock(&wsa881x->res_lock); +} + static const struct reg_sequence wsa881x_pre_pmu_pa[] = { {WSA881X_SPKR_DRV_GAIN, 0x41, 0}, {WSA881X_SPKR_MISC_CTL1, 0x01, 0}, @@ -790,7 +808,9 @@ static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + mutex_lock(&wsa881x->temp_lock); wsa881x_resource_acquire(codec, ENABLE); + mutex_unlock(&wsa881x->temp_lock); wsa881x_boost_ctrl(codec, ENABLE); break; case SND_SOC_DAPM_POST_PMD: @@ -993,7 +1013,7 @@ static void wsa881x_init(struct snd_soc_codec *codec) 0x03, 0x00); if (snd_soc_read(codec, WSA881X_OTP_REG_0)) snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, - 0xF0, 0x70); + 0xFF, 0x7F); snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT2, 0xF0, 0x30); snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x08, 0x08); @@ -1042,13 +1062,8 @@ static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec, return -EINVAL; } } - mutex_lock(&wsa881x->res_lock); - if (!wsa881x->clk_cnt) { - regcache_mark_dirty(wsa881x->regmap); - regcache_sync(wsa881x->regmap); - } - mutex_unlock(&wsa881x->res_lock); - + wsa881x_regcache_sync(wsa881x); + mutex_lock(&wsa881x->temp_lock); wsa881x_resource_acquire(codec, ENABLE); snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00); @@ -1061,6 +1076,7 @@ static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec, wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4); wsa881x_resource_acquire(codec, DISABLE); + mutex_unlock(&wsa881x->temp_lock); return 0; } @@ -1076,7 +1092,6 @@ static int wsa881x_probe(struct snd_soc_codec *codec) dev = wsa881x->swr_slave; wsa881x->codec = codec; mutex_init(&wsa881x->bg_lock); - mutex_init(&wsa881x->res_lock); wsa881x_init(codec); snprintf(wsa881x->tz_pdata.name, sizeof(wsa881x->tz_pdata.name), "%s.%x", "wsatz", (u8)dev->addr); @@ -1098,7 +1113,6 @@ static int wsa881x_remove(struct snd_soc_codec *codec) if (wsa881x->tz_pdata.tz_dev) wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev); mutex_destroy(&wsa881x->bg_lock); - mutex_destroy(&wsa881x->res_lock); return 0; } @@ -1282,6 +1296,8 @@ static int wsa881x_swr_probe(struct swr_device *pdev) __func__); goto dev_err; } + mutex_init(&wsa881x->res_lock); + mutex_init(&wsa881x->temp_lock); return 0; @@ -1303,6 +1319,8 @@ static int wsa881x_swr_remove(struct swr_device *pdev) return -EINVAL; } debugfs_remove_recursive(debugfs_wsa881x_dent); + mutex_destroy(&wsa881x->res_lock); + mutex_destroy(&wsa881x->temp_lock); snd_soc_unregister_codec(&pdev->dev); if (wsa881x->pd_gpio) gpio_free(wsa881x->pd_gpio); @@ -1361,6 +1379,11 @@ static int wsa881x_swr_reset(struct swr_device *pdev) dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); return -EINVAL; } + if (wsa881x->state == WSA881X_DEV_READY) { + dev_dbg(&pdev->dev, "%s: device already active\n", __func__); + return 0; + } + wsa881x->bg_cnt = 0; wsa881x->clk_cnt = 0; while (swr_get_logical_dev_num(pdev, pdev->addr, &devnum) && retry--) { @@ -1368,8 +1391,8 @@ static int wsa881x_swr_reset(struct swr_device *pdev) usleep_range(1000, 1100); } pdev->dev_num = devnum; - regcache_mark_dirty(wsa881x->regmap); - regcache_sync(wsa881x->regmap); + wsa881x_regcache_sync(wsa881x); + return 0; } diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig index 18a5d924e282ac8d0c1b090d95fc6d3a887ba308..197fff2c480a4b136cb39f5cd108e996a68f963b 100644 --- a/sound/soc/msm/Kconfig +++ b/sound/soc/msm/Kconfig @@ -41,6 +41,25 @@ config SND_SOC_QDSP_DEBUG is inducing kernel panic upon encountering critical errors from DSP audio modules +config FORCE_24BIT_COPP + bool "Configure COPP in ADSP as 24-bit" + depends on SND_SOC_MSM_QDSP6V2_INTF + default n + help + To configure COPP in ADSP as 24bit for media playback. + This can avoid quantization error for 16-bit media playback + when IIR/MBDRC filters are enabled. + +config AHC + bool "Enable Automatic Headset Compensation" + depends on SND_SOC_MSM_QDSP6V2_INTF + help + To add support for Automatic Headset Compensation post processing. + This support is to configure the post processing parameters + to DSP. The configuration includes enabling and setting up + the IIR tuning filter and MBDRC in the DSP. + + config DOLBY_DS2 bool "Enable Dolby DS2" depends on SND_SOC_MSM_QDSP6V2_INTF diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c index 2d431a0183157127a49e203ca6f66dedc6dff361..08e449893bff9928cf4fe0b503f0c4f1986576f2 100644 --- a/sound/soc/msm/msm-cpe-lsm.c +++ b/sound/soc/msm/msm-cpe-lsm.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -2647,12 +2652,15 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, u_event_status32.payload_size; err = msm_cpe_lsm_ioctl_shared(substream, cmd, event_status); - if (err) + if (err) { dev_err(rtd->dev, "%s: %s failed, error = %d\n", __func__, "SNDRV_LSM_EVENT_STATUS_V3_32", err); + kfree(event_status); + goto done; + } } if (!err) { diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c index 1e97a0cd76ac659e0fd585f7fb3d7ff2f4ed78f5..f642774ddf7a8e8d44548c3ad2a4d2d2759b2818 100644 --- a/sound/soc/msm/msm8998.c +++ b/sound/soc/msm/msm8998.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -177,6 +182,7 @@ struct msm_pinctrl_info { struct msm_asoc_mach_data { u32 mclk_freq; int us_euro_gpio; /* used by gpio driver API */ + int ear_en_gpio; struct device_node *us_euro_gpio_p; /* used by pinctrl API */ struct device_node *hph_en1_gpio_p; /* used by pinctrl API */ struct device_node *hph_en0_gpio_p; /* used by pinctrl API */ @@ -397,6 +403,7 @@ static struct dev_config aux_pcm_tx_cfg[] = { }; static int msm_vi_feed_tx_ch = 2; +static int ear_enable_states; static const char *const slim_rx_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; @@ -442,6 +449,7 @@ static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; static const char *const hifi_text[] = {"Off", "On"}; +static const char *const ear_enable_states_text[] = {"Disable", "Enable"}; static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text); @@ -506,6 +514,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text); +static SOC_ENUM_SINGLE_EXT_DECL(ear_enable_state, ear_enable_states_text); static struct platform_device *spdev; static int msm_hifi_control; @@ -543,9 +552,9 @@ static struct wcd_mbhc_config wcd_mbhc_cfg = { .key_code[7] = 0, .linein_th = 5000, .moisture_en = true, - .mbhc_micbias = MIC_BIAS_2, - .anc_micbias = MIC_BIAS_2, - .enable_anc_mic_detect = false, + .mbhc_micbias = MIC_BIAS_2, + .anc_micbias = MIC_BIAS_3, + .enable_anc_mic_detect = true, }; static struct snd_soc_dapm_route wcd_audio_paths_tasha[] = { @@ -1496,6 +1505,61 @@ static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, return rc; } +static int ear_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int gpio_state = 0; + + switch (ear_enable_states) { + case 1: + gpio_state = 1; + break; + case 0: + default: + gpio_state = 0; + break; + } + + ucontrol->value.integer.value[0] = gpio_state; + pr_debug("%s: ear_enable_states = %d\n", __func__, + ear_enable_states); + + return 0; +} + +static int ear_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + struct snd_soc_card *card = platform_get_drvdata(spdev); + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + pr_debug("%s: ucontrol value = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + if (pdata->ear_en_gpio >= 0) { + ret = gpio_request(pdata->ear_en_gpio, "ear_en_gpio"); + if (ret) { + pr_err("%s: request ear_en_gpio failed, ret:%d\n", + __func__, ret); + return ret; + } + switch (ucontrol->value.integer.value[0]) { + case 1: + gpio_set_value(pdata->ear_en_gpio, 1); + break; + case 0: + default: + gpio_set_value(pdata->ear_en_gpio, 0); + break; + } + gpio_free(pdata->ear_en_gpio); + ear_enable_states = ucontrol->value.integer.value[0]; + } + + return 0; +} + static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol) { int idx; @@ -2907,6 +2971,9 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get, msm_hifi_put), + SOC_ENUM_EXT("Ear_Enable_States", ear_enable_state, + ear_enable_get, + ear_enable_put), }; static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, @@ -3864,7 +3931,7 @@ static void *def_tasha_mbhc_cal(void) (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); btn_high[0] = 75; - btn_high[1] = 150; + btn_high[1] = 137; btn_high[2] = 237; btn_high[3] = 500; btn_high[4] = 500; @@ -7577,19 +7644,14 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) pdev->dev.of_node->full_name); dev_dbg(&pdev->dev, "Jack type properties set to default"); } else { - if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) { - wcd_mbhc_cfg.enable_anc_mic_detect = false; + if (!strcmp(mbhc_audio_jack_type, "4-pole-jack")) dev_dbg(&pdev->dev, "This hardware has 4 pole jack"); - } else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) { - wcd_mbhc_cfg.enable_anc_mic_detect = true; + else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) dev_dbg(&pdev->dev, "This hardware has 5 pole jack"); - } else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) { - wcd_mbhc_cfg.enable_anc_mic_detect = true; + else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) dev_dbg(&pdev->dev, "This hardware has 6 pole jack"); - } else { - wcd_mbhc_cfg.enable_anc_mic_detect = false; + else dev_dbg(&pdev->dev, "Unknown value, set to default"); - } } /* * Parse US-Euro gpio info from DT. Report no error if us-euro @@ -7635,6 +7697,17 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) pr_err("%s: Audio notifier register failed ret = %d\n", __func__, ret); + /* Parse EAR_EN info for NX5L2750C */ + pdata->ear_en_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,ear-en-gpios", 0); + if (pdata->ear_en_gpio < 0) { + dev_err(&pdev->dev, "property %s not detected in node %s", + "qcom,ear-en-gpios", + pdev->dev.of_node->full_name); + ret = -ENODEV; + goto err; + } + return 0; err: if (pdata->us_euro_gpio > 0) { @@ -7643,6 +7716,11 @@ err: gpio_free(pdata->us_euro_gpio); pdata->us_euro_gpio = 0; } + if (pdata->ear_en_gpio > 0) { + dev_dbg(&pdev->dev, "%s initialize ear_en gpio %d\n", + __func__, pdata->ear_en_gpio); + pdata->ear_en_gpio = 0; + } msm_release_pinctrl(pdev); devm_kfree(&pdev->dev, pdata); return ret; diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile index 67d3d277404d86f9f1f64186bb27107a4e5363ad..5067606096a52616aeb34ecc9b5498026c5275ea 100644 --- a/sound/soc/msm/qdsp6v2/Makefile +++ b/sound/soc/msm/qdsp6v2/Makefile @@ -5,7 +5,8 @@ snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o \ msm-lsm-client.o msm-pcm-host-voice-v2.o \ msm-audio-effects-q6-v2.o msm-pcm-loopback-v2.o \ msm-transcode-loopback-q6-v2.o \ - adsp_err.o + adsp_err.o \ + sony-hweffect-params.o msm-sony-hweffect.o obj-$(CONFIG_SLIMBUS) += msm-dai-slim.o audio_slimslave.o obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \ msm-dai-stub-v2.o @@ -15,6 +16,7 @@ obj-$(CONFIG_SND_HWDEP) += msm-pcm-routing-devdep.o obj-$(CONFIG_DOLBY_DAP) += msm-dolby-dap-config.o obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o obj-$(CONFIG_DOLBY_LICENSE) += msm-ds2-dap-config.o +obj-$(CONFIG_AHC) += msm-ahc-config.o obj-$(CONFIG_DTS_SRS_TM) += msm-dts-srs-tm-config.o obj-$(CONFIG_QTI_PP) += msm-qti-pp-config.o obj-y += audio_calibration.o audio_cal_utils.o q6adm.o q6afe.o q6asm.o \ diff --git a/sound/soc/msm/qdsp6v2/msm-ahc-config.c b/sound/soc/msm/qdsp6v2/msm-ahc-config.c new file mode 100755 index 0000000000000000000000000000000000000000..04cbdeb2f7b23f2260e0411dcd489833c1dfc45a --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-ahc-config.c @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2014 Sony Mobile Communications 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm-ahc-config.h" + +enum { + AHC_IIR_TF_REG_ENABLE = 0, + AHC_IIR_TF_REG_ENABLE_CONFIG, + AHC_IIR_TF_REG_PRE_GAIN, + AHC_IIR_TF_REG_CONFIG_PARAMS, + AHC_IIR_TF_REG_CONFIG_ALL_PARAMS, + AHC_MBDRC_REG_ENABLE, + AHC_MBDRC_REG_ENABLE_CONFIG, +}; + +enum { + AHC_IIR_TF_SHIFT_BAND1 = 0, + AHC_IIR_TF_SHIFT_BAND2, + AHC_IIR_TF_SHIFT_BAND3, + AHC_IIR_TF_SHIFT_BAND4, + AHC_IIR_TF_SHIFT_BAND5, +}; + +#define AHC_BANDS_USED 5 + +#define IIR_COEFF_CHANNEL_LEFT 0 +#define IIR_COEFF_CHANNEL_CENTER 1 +#define IIR_COEFF_CHANNEL_RIGHT 2 + +/* 1073741824 is Q30 format for 1.0 */ +#define DEFAULT_COEFF {1073741824, 0, 0, 0, 0} +#define START_COEFF_STRUCT {.coeff = DEFAULT_COEFF, .num_shift_factor = 2, \ + .pan_setting = IIR_COEFF_CHANNEL_CENTER} + +struct audproc_common_enable { + uint32_t enable; +} __packed; + +struct iir_tf_5_config { + struct asm_iir_filter_config_params bands; + int32_t coeffs[5][5]; + int16_t num_shift_factor[5]; + uint16_t pan_setting[5]; +} __packed; + +struct iir_tf_coeff { + int32_t coeff[5]; + int16_t num_shift_factor; + uint16_t pan_setting; +}; + +static struct iir_tf_coeff iir_tf_coeffs[AHC_BANDS_USED] = { + START_COEFF_STRUCT, START_COEFF_STRUCT, START_COEFF_STRUCT, + START_COEFF_STRUCT, START_COEFF_STRUCT +}; + +static int32_t default_coeff[5] = DEFAULT_COEFF; + +static int ahc_copp_idx = -1; + +static int iir_tf_send_params(int port_id, uint32_t module_id, + uint32_t param_id, uint8_t *param, uint32_t param_len) +{ + uint8_t *params_value; + struct adm_param_data_v5 *p_hdr; + uint32_t pkt_len = sizeof(*p_hdr) + param_len; + int rc; + + pr_debug("%s: module_id 0x%X param_id 0x%X param_len %d\n", + __func__, module_id, param_id, param_len); + + params_value = kmalloc(pkt_len, GFP_KERNEL); + if (!params_value) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + + p_hdr = (struct adm_param_data_v5 *)params_value; + p_hdr->module_id = module_id; + p_hdr->param_id = param_id; + p_hdr->param_size = param_len; + p_hdr->reserved = 0; + memcpy(params_value + sizeof(*p_hdr), param, param_len); + + rc = adm_ahc_send_params(port_id, ahc_copp_idx, params_value, + pkt_len); + if (rc) + pr_err("%s: send ahc params failed %d\n", __func__, rc); + + kfree(params_value); + return rc; +} + +static int send_iir_config(void) +{ + struct iir_tf_5_config *params; + int rc = 0, i, j; + + params = kmalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + pr_err("%s: Could not allocate params\n", __func__); + return -ENOMEM; + } + + params->bands.num_biquad_stages = AHC_BANDS_USED; + params->bands.reserved = 0; + + for (i = 0; i < AHC_BANDS_USED; i++) { + for (j = 0; j < 5; j++) + params->coeffs[i][j] = iir_tf_coeffs[i].coeff[j]; + params->num_shift_factor[i] = + iir_tf_coeffs[i].num_shift_factor; + params->pan_setting[i] = iir_tf_coeffs[i].pan_setting; + } + + rc = iir_tf_send_params(AHC_PORT_ID, + ASM_MODULE_ID_IIRUNING_FILTER, + ASM_PARAM_ID_IIRUNING_FILTER_CONFIG_PARAMS, + (uint8_t *)params, sizeof(*params)); + + kfree(params); + return rc; +} + +static int get_params_for_get_cmd(int cmd, int band, int *param_len, + uint32_t *param_id, uint32_t *module_id) +{ + switch (cmd) { + case AHC_IIR_TF_REG_ENABLE: + pr_debug("%s: AHC_IIR_TF_REG_ENABLE\n", __func__); + *param_len = sizeof(struct audproc_common_enable); + *param_id = AUDPROC_PARAM_ID_ENABLE; + *module_id = ASM_MODULE_ID_IIRUNING_FILTER; + break; + + case AHC_IIR_TF_REG_ENABLE_CONFIG: + pr_debug("%s: AHC_IIR_TF_REG_ENABLE_CONFIG\n", __func__); + *param_len = sizeof(struct asm_iiruning_filter_enable); + *param_id = ASM_PARAM_ID_IIRUNING_FILTER_ENABLE_CONFIG; + *module_id = ASM_MODULE_ID_IIRUNING_FILTER; + break; + + case AHC_IIR_TF_REG_PRE_GAIN: + pr_debug("%s: AHC_IIR_TF_REG_PRE_GAIN\n", __func__); + *param_len = sizeof(struct asm_iiruning_filter_pregain); + *param_id = ASM_PARAM_ID_IIRUNING_FILTER_PRE_GAIN; + *module_id = ASM_MODULE_ID_IIRUNING_FILTER; + break; + + case AHC_IIR_TF_REG_CONFIG_PARAMS: + pr_debug("%s: AHC_IIR_TF_REG_CONFIG_PARAMS for band %d\n", + __func__, band); + *param_len = sizeof(struct iir_tf_5_config); + *param_id = ASM_PARAM_ID_IIRUNING_FILTER_CONFIG_PARAMS; + *module_id = ASM_MODULE_ID_IIRUNING_FILTER; + break; + + case AHC_IIR_TF_REG_CONFIG_ALL_PARAMS: + pr_debug("%s: AHC_IIR_TF_REG_CONFIG_ALL_PARAMS\n", __func__); + *param_len = sizeof(struct iir_tf_5_config); + *param_id = ASM_PARAM_ID_IIRUNING_FILTER_CONFIG_PARAMS; + *module_id = ASM_MODULE_ID_IIRUNING_FILTER; + break; + + case AHC_MBDRC_REG_ENABLE: + pr_debug("%s: AHC_MBDRC_REG_ENABLE\n", __func__); + *param_len = sizeof(struct audproc_common_enable); + *param_id = AUDPROC_PARAM_ID_ENABLE; + *module_id = ASM_MODULE_ID_MBDRCV3; + break; + + case AHC_MBDRC_REG_ENABLE_CONFIG: + pr_debug("%s: AHC_MBDRC_REG_ENABLE_CONFIG\n", __func__); + *param_len = sizeof(struct asm_mbdrc_enable); + *param_id = ASM_PARAM_ID_MBDRC_ENABLE; + *module_id = ASM_MODULE_ID_MBDRCV3; + break; + + default: + pr_err("%s: Received unknown command %d\n", __func__, cmd); + *param_len = 0; + *param_id = 0; + *module_id = 0; + return -EINVAL; + } + return 0; +} + +static int set_values_for_get_cmd(int cmd, int band, + struct snd_ctl_elem_value *ucontrol, uint8_t *params_value) +{ + switch (cmd) { + case AHC_IIR_TF_REG_ENABLE: /* FALLTHROUGH */ + case AHC_MBDRC_REG_ENABLE: { + struct audproc_common_enable *param = + (struct audproc_common_enable *)params_value; + ucontrol->value.integer.value[0] = param->enable; + pr_debug("%s: Val %ld returned\n", __func__, + ucontrol->value.integer.value[0]); + break; + } + + case AHC_IIR_TF_REG_ENABLE_CONFIG: { + struct asm_iiruning_filter_enable *param = + (struct asm_iiruning_filter_enable *)params_value; + ucontrol->value.integer.value[0] = param->enable_flag; + pr_debug("%s: Val %ld returned\n", __func__, + ucontrol->value.integer.value[0]); + break; + } + + case AHC_IIR_TF_REG_PRE_GAIN: { + struct asm_iiruning_filter_pregain *param = + (struct asm_iiruning_filter_pregain *)params_value; + ucontrol->value.integer.value[0] = param->pregain; + pr_debug("%s: Val %ld returned\n", __func__, + ucontrol->value.integer.value[0]); + break; + } + + case AHC_IIR_TF_REG_CONFIG_PARAMS: { + struct iir_tf_5_config *param = + (struct iir_tf_5_config *)params_value; + int32_t *p_coeff; + int i; + + if (band < param->bands.num_biquad_stages) + p_coeff = ¶m->coeffs[band][0]; + else + p_coeff = &default_coeff[0]; + + for (i = 0; i < 5; i++) { + ucontrol->value.integer.value[i] = p_coeff[i]; + pr_debug("%s: Val %ld returned\n", __func__, + ucontrol->value.integer.value[i]); + } + break; + } + + case AHC_IIR_TF_REG_CONFIG_ALL_PARAMS: { + struct iir_tf_5_config *param = + (struct iir_tf_5_config *)params_value; + int32_t *p_coeff; + int i; + int j; + int idx = 0; + + for (i = 0; i < AHC_BANDS_USED; i++) { + if (i < param->bands.num_biquad_stages) + p_coeff = ¶m->coeffs[i][0]; + else + p_coeff = &default_coeff[0]; + + for (j = 0; j < 5; j++) { + ucontrol->value.integer.value[idx] = p_coeff[j]; + pr_debug("%s: Val %ld returned\n", __func__, + ucontrol->value.integer.value[i]); + idx++; + } + } + break; + } + + case AHC_MBDRC_REG_ENABLE_CONFIG: { + struct asm_mbdrc_enable *param = + (struct asm_mbdrc_enable *)params_value; + ucontrol->value.integer.value[0] = param->enable_flag; + pr_debug("%s: Val %ld returned\n", __func__, + ucontrol->value.integer.value[0]); + break; + } + + default: + /* Will never happen. Checked in get_params_for_get_cmd */ + break; + } + return 0; +} + +static int handle_get_cmd(int cmd, int band, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + int param_len = 0; + uint32_t param_id = 0; + uint32_t module_id = 0; + uint8_t *params_value; + + if ((ahc_copp_idx < 0) || (ahc_copp_idx > MAX_COPPS_PER_PORT)) { + pr_err("%s: ahc_copp_idx not set\n", __func__); + return -EINVAL; + } + + rc = get_params_for_get_cmd(cmd, band, ¶m_len, ¶m_id, + &module_id); + if (rc) + return rc; + + /* Adjust size to 4-byte alignment. */ + param_len = (param_len + 3) & ~3; + + params_value = kzalloc(param_len + sizeof(struct adm_param_data_v5), GFP_KERNEL); + if (!params_value) { + pr_err("%s: params memory alloc failed\n", __func__); + return -ENOMEM; + } + + pr_debug("%s: Retrieving parameters len %d\n", __func__, param_len); + /* + * Retrieve the parameters. + * param_len must be increased with size of struct adm_param_data_v5 + * because adm_get_params() does not add this size to the return + * buffer allocation even though that header is always included + * before the actual parameter payload. + */ + rc = adm_get_params(AHC_PORT_ID, + ahc_copp_idx, + module_id, + param_id, + param_len + sizeof(struct adm_param_data_v5), + params_value); + if (rc) { + pr_err("%s: get parameters failed %d\n", __func__, rc); + goto exit; + } + + rc = set_values_for_get_cmd(cmd, band, ucontrol, params_value); + +exit: + kfree(params_value); + return rc; +} + +static int handle_put_cmd(int cmd, int band, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + + if ((ahc_copp_idx < 0) || (ahc_copp_idx > MAX_COPPS_PER_PORT)) { + pr_err("%s: ahc_copp_idx not set\n", __func__); + return -EINVAL; + } + + switch (cmd) { + case AHC_IIR_TF_REG_ENABLE: { + struct audproc_common_enable param; + + param.enable = ucontrol->value.integer.value[0]; + pr_debug("%s: AHC_IIR_TF_REG_ENABLE val: %u\n", + __func__, param.enable); + rc = iir_tf_send_params(AHC_PORT_ID, + ASM_MODULE_ID_IIRUNING_FILTER, + AUDPROC_PARAM_ID_ENABLE, + (uint8_t *)¶m, sizeof(param)); + break; + } + + case AHC_IIR_TF_REG_ENABLE_CONFIG: { + struct asm_iiruning_filter_enable param; + + param.enable_flag = ucontrol->value.integer.value[0]; + pr_debug("%s: AHC_IIR_TF_REG_ENABLE_CONFIG val: %u\n", + __func__, param.enable_flag); + rc = iir_tf_send_params(AHC_PORT_ID, + ASM_MODULE_ID_IIRUNING_FILTER, + ASM_PARAM_ID_IIRUNING_FILTER_ENABLE_CONFIG, + (uint8_t *)¶m, sizeof(param)); + break; + } + + case AHC_IIR_TF_REG_PRE_GAIN: { + struct asm_iiruning_filter_pregain param; + + pr_debug("%s: AHC_IIR_TF_REG_PRE_GAIN val: %ld\n", + __func__, ucontrol->value.integer.value[0]); + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > 0xFFFF) { + pr_err("%s: value %ld is not uint16_t", __func__, + ucontrol->value.integer.value[0]); + return -EINVAL; + } + param.pregain = ucontrol->value.integer.value[0]; + param.reserved = 0; + rc = iir_tf_send_params(AHC_PORT_ID, + ASM_MODULE_ID_IIRUNING_FILTER, + ASM_PARAM_ID_IIRUNING_FILTER_PRE_GAIN, + (uint8_t *)¶m, sizeof(param)); + break; + } + + case AHC_IIR_TF_REG_CONFIG_PARAMS: { + long *temp = ucontrol->value.integer.value; + int i; + + /* Two prints to avoid checkpatch warning */ + pr_debug("%s: AHC_IIR_TF_REG_CONFIG_PARAMS band %d:", + __func__, band); + pr_debug(" %ld %ld %ld %ld %ld\n", + temp[0], temp[1], temp[2], temp[3], temp[4]); + + for (i = 0; i < 5; i++) + iir_tf_coeffs[band].coeff[i] = temp[i]; + + rc = send_iir_config(); + break; + } + + case AHC_IIR_TF_REG_CONFIG_ALL_PARAMS: { + long *temp = ucontrol->value.integer.value; + int i; + int j; + + pr_debug("%s: AHC_IIR_TF_REG_CONFIG_ALL_PARAMS", __func__); + + for (i = 0; i < AHC_BANDS_USED; i++) + for (j = 0; j < 5; j++) + iir_tf_coeffs[i].coeff[j] = + temp[(i * 5) + j]; + + rc = send_iir_config(); + break; + } + + case AHC_MBDRC_REG_ENABLE: { + struct audproc_common_enable param; + + param.enable = ucontrol->value.integer.value[0]; + pr_debug("%s: AHC_MBDRC_REG_ENABLE recvd val: %u\n", + __func__, param.enable); + rc = iir_tf_send_params(AHC_PORT_ID, ASM_MODULE_ID_MBDRCV3, + AUDPROC_PARAM_ID_ENABLE, + (uint8_t *)¶m, sizeof(param)); + break; + } + + case AHC_MBDRC_REG_ENABLE_CONFIG: { + struct asm_mbdrc_enable param; + + param.enable_flag = ucontrol->value.integer.value[0]; + pr_debug("%s: AHC_MBDRC_REG_ENABLE_CONFIG recvd val: %u\n", + __func__, param.enable_flag); + rc = iir_tf_send_params(AHC_PORT_ID, ASM_MODULE_ID_MBDRCV3, + ASM_PARAM_ID_MBDRC_ENABLE, + (uint8_t *)¶m, sizeof(param)); + break; + } + + default: + pr_err("%s: Received unknown command %d\n", __func__, cmd); + rc = -EINVAL; + break; + } + + return rc; +} + +static int get_ahc_multi_param( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct soc_multi_mixer_control *mc = + (struct soc_multi_mixer_control *) + kcontrol->private_value; + + return handle_get_cmd(mc->reg, mc->shift, ucontrol); +} + +static int put_ahc_multi_param( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct soc_multi_mixer_control *mc = + (struct soc_multi_mixer_control *) + kcontrol->private_value; + + return handle_put_cmd(mc->reg, mc->shift, ucontrol); +} + +static const struct snd_kcontrol_new ahc_multi_controls[] = { + + /* IIR Tuning Filter */ + SOC_SINGLE_MULTI_EXT("AHC IIR Enable", AHC_IIR_TF_REG_ENABLE, + 0, 1, 0, 1, + get_ahc_multi_param, put_ahc_multi_param), + + SOC_SINGLE_MULTI_EXT("AHC IIR EnableConfig", + AHC_IIR_TF_REG_ENABLE_CONFIG, + 0, 1, 0, 1, + get_ahc_multi_param, put_ahc_multi_param), + + SOC_SINGLE_MULTI_EXT("AHC IIR PreGain", AHC_IIR_TF_REG_PRE_GAIN, + 0, 0x0000FFFF, 0, 1, + get_ahc_multi_param, put_ahc_multi_param), + + SOC_SINGLE_MULTI_EXT("AHC IIR Band1", AHC_IIR_TF_REG_CONFIG_PARAMS, + AHC_IIR_TF_SHIFT_BAND1, 0xFFFFFFFF, 0, 5, + get_ahc_multi_param, put_ahc_multi_param), + + SOC_SINGLE_MULTI_EXT("AHC IIR Band2", AHC_IIR_TF_REG_CONFIG_PARAMS, + AHC_IIR_TF_SHIFT_BAND2, 0xFFFFFFFF, 0, 5, + get_ahc_multi_param, put_ahc_multi_param), + + SOC_SINGLE_MULTI_EXT("AHC IIR Band3", AHC_IIR_TF_REG_CONFIG_PARAMS, + AHC_IIR_TF_SHIFT_BAND3, 0xFFFFFFFF, 0, 5, + get_ahc_multi_param, put_ahc_multi_param), + + SOC_SINGLE_MULTI_EXT("AHC IIR Band4", AHC_IIR_TF_REG_CONFIG_PARAMS, + AHC_IIR_TF_SHIFT_BAND4, 0xFFFFFFFF, 0, 5, + get_ahc_multi_param, put_ahc_multi_param), + + SOC_SINGLE_MULTI_EXT("AHC IIR Band5", AHC_IIR_TF_REG_CONFIG_PARAMS, + AHC_IIR_TF_SHIFT_BAND5, 0xFFFFFFFF, 0, 5, + get_ahc_multi_param, put_ahc_multi_param), + + SOC_SINGLE_MULTI_EXT("AHC IIR Bands", AHC_IIR_TF_REG_CONFIG_ALL_PARAMS, + 0, 0xFFFFFFFF, 0, 25, + get_ahc_multi_param, put_ahc_multi_param), + + /* MBDRC */ + SOC_SINGLE_MULTI_EXT("AHC MBDRC Enable", AHC_MBDRC_REG_ENABLE, + 0, 1, 0, 1, + get_ahc_multi_param, put_ahc_multi_param), + + SOC_SINGLE_MULTI_EXT("AHC MBDRC EnableConfig", + AHC_MBDRC_REG_ENABLE_CONFIG, + 0, 1, 0, 1, + get_ahc_multi_param, put_ahc_multi_param), +}; + +void msm_routing_ahc_set_copp_idx(int copp_idx) +{ + pr_debug("%s: copp_idx %d\n", __func__, copp_idx); + ahc_copp_idx = copp_idx; +} + +void msm_routing_ahc_add_controls(struct snd_soc_platform *platform) +{ + int rc; + + rc = snd_soc_add_platform_controls(platform, ahc_multi_controls, + ARRAY_SIZE(ahc_multi_controls)); + if (rc) + pr_err("%s: Failed to add multi controls %d\n", __func__, rc); +} diff --git a/sound/soc/msm/qdsp6v2/msm-ahc-config.h b/sound/soc/msm/qdsp6v2/msm-ahc-config.h new file mode 100644 index 0000000000000000000000000000000000000000..c732acc08db609d99bb9b8ad16b08894dc69453b --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-ahc-config.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 Sony Mobile Communications 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 _MSM_AHC_CONFIG_H_ +#define _MSM_AHC_CONFIG_H_ + +#define AHC_PORT_ID SLIMBUS_0_RX + +#ifdef CONFIG_AHC +void msm_routing_ahc_set_copp_idx(int copp_idx); +void msm_routing_ahc_add_controls(struct snd_soc_platform *platform); +#else +void msm_routing_ahc_set_copp_idx(int copp_idx) {} +void msm_routing_ahc_add_controls(struct snd_soc_platform *platform) {} +#endif + +#endif diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c old mode 100644 new mode 100755 index 69951e12ecb12eba31c5e0bc1bc50a462a845eef..f09d5c36eae78dcd53ba8943545f57448f718b3e --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include @@ -46,6 +51,8 @@ #include #include "msm-pcm-routing-v2.h" #include "msm-qti-pp-config.h" +#include "msm-sony-hweffect.h" +#include "sound/sony-hweffect-params.h" #define DSP_PP_BUFFERING_IN_MSEC 25 #define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150 @@ -100,6 +107,7 @@ struct msm_compr_pdata { struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX]; uint32_t volume[MSM_FRONTEND_DAI_MAX][2]; /* For both L & R */ struct msm_compr_audio_effects *audio_effects[MSM_FRONTEND_DAI_MAX]; + struct msm_compr_sony_hweffect *sony_hweffect[MSM_FRONTEND_DAI_MAX]; bool use_dsp_gapless_mode; bool use_legacy_api; /* indicates use older asm apis*/ struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX]; @@ -207,6 +215,10 @@ struct msm_compr_audio_effects { struct query_audio_effect query; }; +struct msm_compr_sony_hweffect { + struct sonybundle_params sonybundle; +}; + struct msm_compr_dec_params { struct snd_dec_ddp ddp_params; }; @@ -1602,13 +1614,23 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) ret = -ENOMEM; goto effect_err; } + pdata->sony_hweffect[rtd->dai_link->be_id] = + kzalloc(sizeof(struct msm_compr_sony_hweffect), GFP_KERNEL); + if (pdata->sony_hweffect[rtd->dai_link->be_id]== NULL) { + pr_err("%s: Could not allocate memory for effects\n", __func__); + goto param_err; + } + + init_sonybundle_params( + &(pdata->sony_hweffect[rtd->dai_link->be_id]->sonybundle)); + pdata->dec_params[rtd->dai_link->be_id] = kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL); if (pdata->dec_params[rtd->dai_link->be_id] == NULL) { pr_err("%s: Could not allocate memory for dec params\n", __func__); ret = -ENOMEM; - goto param_err; + goto param_err2; } prtd->codec = FORMAT_MP3; prtd->bytes_received = 0; @@ -1673,6 +1695,9 @@ map_err: ac_err: kfree(pdata->dec_params[rtd->dai_link->be_id]); pdata->dec_params[rtd->dai_link->be_id] = NULL; +param_err2: + kfree(pdata->sony_hweffect[rtd->dai_link->be_id]); + pdata->sony_hweffect[rtd->dai_link->be_id] = NULL; param_err: kfree(pdata->audio_effects[rtd->dai_link->be_id]); pdata->audio_effects[rtd->dai_link->be_id] = NULL; @@ -1846,12 +1871,16 @@ static int msm_compr_playback_free(struct snd_compr_stream *cstream) q6asm_audio_client_free(ac); msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); if (pdata->audio_effects[soc_prtd->dai_link->be_id] != NULL) { - kfree(pdata->audio_effects[soc_prtd->dai_link->be_id]); - pdata->audio_effects[soc_prtd->dai_link->be_id] = NULL; + kfree(pdata->audio_effects[soc_prtd->dai_link->be_id]); + pdata->audio_effects[soc_prtd->dai_link->be_id] = NULL; + } + if (pdata->sony_hweffect[soc_prtd->dai_link->be_id] != NULL) { + kfree(pdata->sony_hweffect[soc_prtd->dai_link->be_id]); + pdata->sony_hweffect[soc_prtd->dai_link->be_id] = NULL; } if (pdata->dec_params[soc_prtd->dai_link->be_id] != NULL) { - kfree(pdata->dec_params[soc_prtd->dai_link->be_id]); - pdata->dec_params[soc_prtd->dai_link->be_id] = NULL; + kfree(pdata->dec_params[soc_prtd->dai_link->be_id]); + pdata->dec_params[soc_prtd->dai_link->be_id] = NULL; } pdata->is_in_use[soc_prtd->dai_link->be_id] = false; kfree(prtd); @@ -3309,6 +3338,92 @@ static int msm_compr_audio_effects_config_get(struct snd_kcontrol *kcontrol, return 0; } + +static int msm_compr_sony_hweffect_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_sony_hweffect *sony_hweffect = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + int effects_module; + uint16_t bits_per_sample; + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + sony_hweffect = pdata->sony_hweffect[fe_id]; + if (!cstream || !sony_hweffect) { + pr_err("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + effects_module = *values++; + switch (effects_module) { + case SONYBUNDLE_MODULE: + pr_debug("%s: SONYBUNDLE_MODULE\n", __func__); + + if ((prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) || + (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_3LE)) + bits_per_sample = 24; + else if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S32_LE) + bits_per_sample = 32; + else + bits_per_sample = 16; + + msm_sony_hweffect_sonybundle_handler(prtd->audio_client, + &(sony_hweffect->sonybundle), + values, + prtd->sample_rate, + bits_per_sample, + prtd->num_channels); + break; + default: + pr_err("%s Invalid effects config module\n", __func__); + return -EINVAL; + } + return 0; +} + +static int msm_compr_sony_hweffect_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_sony_hweffect *sony_hweffect = NULL; + long *values = &(ucontrol->value.integer.value[0]); + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + sony_hweffect = pdata->sony_hweffect[fe_id]; + if (!sony_hweffect) { + pr_err("%s: effects inactive\n", __func__); + return -EINVAL; + } + *values++ = SONYBUNDLE_MODULE; + msm_sony_hweffect_sonybundle_get(&(sony_hweffect->sonybundle), + values); + return 0; +} + static int msm_compr_query_audio_effect_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -4040,6 +4155,35 @@ static const struct snd_kcontrol_new msm_compr_gapless_controls[] = { msm_compr_gapless_put), }; +static int sony_hweffect_put_params_size( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s\n", __func__); + + return 0; +} + +static int sony_hweffect_get_params_size( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + long *values = &(ucontrol->value.integer.value[0]); + + pr_debug("%s\n", __func__); + + sony_hweffect_params_getparam_size(values); + + return 0; +} + +static const struct snd_kcontrol_new sony_hweffect_params_controls[] = { + SOC_SINGLE_MULTI_EXT("Sony Audio Effect Param Size", + SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 4, + sony_hweffect_get_params_size, + sony_hweffect_put_params_size) +}; + static int msm_compr_probe(struct snd_soc_platform *platform) { struct msm_compr_pdata *pdata; @@ -4059,6 +4203,7 @@ static int msm_compr_probe(struct snd_soc_platform *platform) pdata->volume[i][0] = COMPRESSED_LR_VOL_MAX_STEPS; pdata->volume[i][1] = COMPRESSED_LR_VOL_MAX_STEPS; pdata->audio_effects[i] = NULL; + pdata->sony_hweffect[i] = NULL; pdata->dec_params[i] = NULL; pdata->cstream[i] = NULL; pdata->ch_map[i] = NULL; @@ -4068,6 +4213,9 @@ static int msm_compr_probe(struct snd_soc_platform *platform) snd_soc_add_platform_controls(platform, msm_compr_gapless_controls, ARRAY_SIZE(msm_compr_gapless_controls)); + snd_soc_add_platform_controls(platform, sony_hweffect_params_controls, + ARRAY_SIZE(sony_hweffect_params_controls)); + rc = of_property_read_string(platform->dev->of_node, "qcom,adsp-version", &qdsp_version); if (!rc) { @@ -4253,6 +4401,51 @@ static int msm_compr_add_audio_effects_control(struct snd_soc_pcm_runtime *rtd) return 0; } +static int msm_compr_add_sony_hweffect_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Sony Audio Effects Config"; + char *mixer_str = NULL; + int ctl_len = 64; + struct snd_kcontrol_new fe_sony_hweffect_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_audio_effects_config_info, + .get = msm_compr_sony_hweffect_config_get, + .put = msm_compr_sony_hweffect_config_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->be_id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) { + pr_err("failed to allocate mixer ctrl str of len %d", ctl_len); + return 0; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + + fe_sony_hweffect_config_control[0].name = mixer_str; + fe_sony_hweffect_config_control[0].private_value = rtd->dai_link->be_id; + pr_debug("Registering new mixer ctl %s\n", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_sony_hweffect_config_control, + ARRAY_SIZE(fe_sony_hweffect_config_control)); + kfree(mixer_str); + return 0; +} + static int msm_compr_add_query_audio_effect_control( struct snd_soc_pcm_runtime *rtd) { @@ -4817,6 +5010,11 @@ static int msm_compr_new(struct snd_soc_pcm_runtime *rtd) pr_err("%s: Could not add Compr Query Audio Effect Control\n", __func__); + rc = msm_compr_add_sony_hweffect_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Sony H/W Effects Control\n", + __func__); + rc = msm_compr_add_dec_runtime_params_control(rtd); if (rc) pr_err("%s: Could not add Compr Dec runtime params Control\n", diff --git a/sound/soc/msm/qdsp6v2/msm-dai-slim.c b/sound/soc/msm/qdsp6v2/msm-dai-slim.c index 64ecd7e031f005042e794baea0c6652fe3d26ddd..4ef2884e7f023092bd0702579c2e9e5096efe5c2 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-slim.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-slim.c @@ -10,6 +10,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2017 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -314,7 +319,7 @@ static int msm_dai_slim_prepare(struct snd_pcm_substream *substream, struct msm_slim_dai_data *dai_data = NULL; struct slim_ch prop; int rc; - u8 i, j; + u8 i; dai_data = msm_slim_get_dai_data(drv_data, dai); if (!dai_data) { @@ -351,6 +356,10 @@ static int msm_dai_slim_prepare(struct snd_pcm_substream *substream, } } + /* To decrement the channel ref count*/ + for (i = 0; i < dai_data->ch_cnt; i++) + slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[i]); + prop.prot = SLIM_AUTO_ISO; prop.baser = SLIM_RATE_4000HZ; prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; @@ -374,8 +383,6 @@ static int msm_dai_slim_prepare(struct snd_pcm_substream *substream, error_define_chan: error_chan_query: - for (j = 0; j < i; j++) - slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[j]); return rc; } @@ -385,7 +392,6 @@ static void msm_dai_slim_shutdown(struct snd_pcm_substream *stream, struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev); struct msm_slim_dma_data *dma_data = NULL; struct msm_slim_dai_data *dai_data; - int i, rc = 0; dai_data = msm_slim_get_dai_data(drv_data, dai); dma_data = snd_soc_dai_get_dma_data(dai, stream); @@ -404,15 +410,6 @@ static void msm_dai_slim_shutdown(struct snd_pcm_substream *stream, return; } - for (i = 0; i < dai_data->ch_cnt; i++) { - rc = slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[i]); - if (rc) { - dev_err(dai->dev, - "%s: dealloc_ch failed, err = %d\n", - __func__, rc); - } - } - snd_soc_dai_set_dma_data(dai, stream, NULL); /* clear prepared state for the dai */ CLR_DAI_STATE(dai_data->status, DAI_STATE_PREPARED); diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c index 6dda41cc85bb71e4a43bb010a8100f0b7197158f..d9846a30de39e43e14fe4ca35b6560fc330d7dbd 100644 --- a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c +++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c @@ -8,6 +8,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -354,14 +359,10 @@ int qti_set_custom_stereo_on(int port_id, int copp_idx, pr_debug("%s: port 0x%x, copp_idx %d, is_custom_stereo_on %d\n", __func__, port_id, copp_idx, is_custom_stereo_on); if (is_custom_stereo_on) { - op_FL_ip_FL_weight = - Q14_GAIN_ZERO_POINT_FIVE; - op_FL_ip_FR_weight = - Q14_GAIN_ZERO_POINT_FIVE; - op_FR_ip_FL_weight = - Q14_GAIN_ZERO_POINT_FIVE; - op_FR_ip_FR_weight = - Q14_GAIN_ZERO_POINT_FIVE; + op_FL_ip_FL_weight = 0; + op_FL_ip_FR_weight = Q14_GAIN_UNITY; + op_FR_ip_FL_weight = Q14_GAIN_UNITY; + op_FR_ip_FR_weight = 0; } else { op_FL_ip_FL_weight = Q14_GAIN_UNITY; op_FL_ip_FR_weight = 0; diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index d0dabcb4e337a089605a299c6355a1ec61e0f1c4..f8cfbaa7eda1c48c4c03f1377055da21b678acb5 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include @@ -39,6 +44,7 @@ #include #include +#include "msm-ahc-config.h" #include "msm-pcm-routing-v2.h" #include "msm-pcm-routing-devdep.h" #include "msm-qti-pp-config.h" @@ -84,6 +90,13 @@ static bool is_custom_stereo_on; static bool is_ds2_on; static bool swap_ch; static int msm_native_mode; +static int sony_custom_stereo_mode; + +static struct mute_params { + uint32_t duration; + uint32_t flag_ch1; + uint32_t flag_ch2; +} mute = {0, 0, 0}; #define WEIGHT_0_DB 0x4000 /* all the FEs which can support channel mixer */ @@ -228,6 +241,13 @@ static void msm_pcm_routing_cfg_pp(int port_id, int copp_idx, int topology, pr_err("%s: topo_id 0x%x, port %d, copp %d, rc %d\n", __func__, topology, port_id, copp_idx, rc); break; + case ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_SOMC_HP: + if (port_id == AHC_PORT_ID) { + pr_debug("%s: AHC supporting topology 0x%X\n", + __func__, topology); + msm_routing_ahc_set_copp_idx(copp_idx); + } + break; default: /* custom topology specific feature param handlers */ break; @@ -259,6 +279,13 @@ static void msm_pcm_routing_deinit_pp(int port_id, int topology) pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__); msm_qti_pp_asphere_deinit(port_id); break; + case ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_SOMC_HP: + if (port_id == AHC_PORT_ID) { + pr_debug("%s: AHC supporting topology 0x%X\n", + __func__, topology); + msm_routing_ahc_set_copp_idx(-1); + } + break; default: /* custom topology specific feature deinit handlers */ break; @@ -269,28 +296,39 @@ static void msm_pcm_routng_cfg_matrix_map_pp(struct route_payload payload, int path_type, int perf_mode) { int itr = 0, rc = 0; - if ((path_type == ADM_PATH_PLAYBACK) && - (perf_mode == LEGACY_PCM_MODE) && - is_custom_stereo_on) { + if ((path_type != ADM_PATH_PLAYBACK) || + (perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) || + !is_custom_stereo_on) + return; + for (itr = 0; itr < payload.num_copps; itr++) { if ((payload.port_id[itr] != SLIMBUS_0_RX) && (payload.port_id[itr] != RT_PROXY_PORT_001_RX)) { continue; } - rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd( - payload.port_id[itr], - payload.copp_idx[itr], - payload.session_id, - Q14_GAIN_ZERO_POINT_FIVE, - Q14_GAIN_ZERO_POINT_FIVE, - Q14_GAIN_ZERO_POINT_FIVE, - Q14_GAIN_ZERO_POINT_FIVE); + if (sony_custom_stereo_mode == SONY_CUSTOM_STEREO_MIX) + rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd( + payload.port_id[itr], + payload.copp_idx[itr], + payload.session_id, + Q14_GAIN_ZERO_POINT_FIVE, + Q14_GAIN_ZERO_POINT_FIVE, + Q14_GAIN_ZERO_POINT_FIVE, + Q14_GAIN_ZERO_POINT_FIVE); + else + rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd( + payload.port_id[itr], + payload.copp_idx[itr], + payload.session_id, + 0, + Q14_GAIN_UNITY, + Q14_GAIN_UNITY, + 0); if (rc < 0) pr_err("%s: err setting custom stereo\n", __func__); } - } } #define SLIMBUS_EXTPROC_RX AFE_PORT_INVALID @@ -1481,6 +1519,7 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, uint32_t passthr_mode = LEGACY_PCM; int ret = 0; u32 copp_token = 0; + bool is_copp_24bit = false; if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) { /* bad ID assigned in machine driver */ @@ -1525,6 +1564,8 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, bits_per_sample = msm_routing_get_bit_width( msm_bedais[i].format); + if (bits_per_sample == 24) + is_copp_24bit = true; app_type = fe_dai_app_type_cfg[fedai_id][session_type][i].app_type; @@ -1542,6 +1583,11 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, } else sample_rate = msm_bedais[i].sample_rate; + if (path_type == 2) { + if (is_copp_24bit == true) + bits_per_sample = 24; + } + acdb_dev_id = fe_dai_app_type_cfg[fedai_id][session_type][i] .acdb_dev_id; @@ -1685,7 +1731,9 @@ void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type) clear_bit(idx, &session_copp_map[fedai_id][session_type][i]); if ((DOLBY_ADM_COPP_TOPOLOGY_ID == topology || - DS2_ADM_COPP_TOPOLOGY_ID == topology) && + DS2_ADM_COPP_TOPOLOGY_ID == topology || + ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_SOMC_HP + == topology) && (fdai->perf_mode == LEGACY_PCM_MODE) && (msm_bedais[i].passthr_mode[fedai_id] == LEGACY_PCM)) @@ -1725,6 +1773,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) uint32_t passthr_mode; bool is_lsm; u32 copp_token = 0; + bool is_copp_24bit = false; pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set); @@ -1793,6 +1842,8 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) bits_per_sample = msm_routing_get_bit_width( msm_bedais[reg].format); + if (bits_per_sample == 24) + is_copp_24bit = true; app_type = fe_dai_app_type_cfg[val][session_type][reg].app_type; @@ -1817,6 +1868,10 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) .copp_token; } else sample_rate = msm_bedais[reg].sample_rate; + if (path_type == 2) { + if (is_copp_24bit == true) + bits_per_sample = 24; + } topology = msm_routing_get_adm_topology(val, session_type, @@ -11495,14 +11550,10 @@ static int msm_routing_put_stereo_to_custom_stereo_control( session_id = fe_dai_map[i][SESSION_TYPE_RX].strm_id; if (is_custom_stereo_on) { - op_FL_ip_FL_weight = - Q14_GAIN_ZERO_POINT_FIVE; - op_FL_ip_FR_weight = - Q14_GAIN_ZERO_POINT_FIVE; - op_FR_ip_FL_weight = - Q14_GAIN_ZERO_POINT_FIVE; - op_FR_ip_FR_weight = - Q14_GAIN_ZERO_POINT_FIVE; + op_FL_ip_FL_weight = 0; + op_FL_ip_FR_weight = Q14_GAIN_UNITY; + op_FR_ip_FL_weight = Q14_GAIN_UNITY; + op_FR_ip_FR_weight = 0; } else { op_FL_ip_FL_weight = Q14_GAIN_UNITY; op_FL_ip_FR_weight = 0; @@ -11554,6 +11605,117 @@ static const struct snd_kcontrol_new stereo_to_custom_stereo_controls[] = { msm_routing_put_stereo_to_custom_stereo_control), }; +static int msm_routing_get_sony_stereo_to_custom_stereo_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = sony_custom_stereo_mode; + return 0; +} + +static int msm_routing_put_sony_stereo_to_custom_stereo_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = 0, i = 0, rc = 0, idx = 0; + int be_index = 0, port_id, topo_id; + unsigned int session_id = 0; + uint16_t op_FL_ip_FL_weight; + uint16_t op_FL_ip_FR_weight; + uint16_t op_FR_ip_FL_weight; + uint16_t op_FR_ip_FR_weight; + mode = ucontrol->value.integer.value[0]; + pr_debug("%s E mode %d\n", __func__, mode); + + if (sony_custom_stereo_mode == mode) { + pr_err("%s: sony_custom_stereo_mode %d, mode %d\n", + __func__, sony_custom_stereo_mode, mode); + return 0; + } + sony_custom_stereo_mode = mode; + is_custom_stereo_on = (mode == SONY_CUSTOM_STEREO_NORMAL) ? false : true; + pr_debug("%s:sony_custom_stereo_mode %d\n", __func__, sony_custom_stereo_mode); + if (sony_custom_stereo_mode == SONY_CUSTOM_STEREO_MIX) { + op_FL_ip_FL_weight = Q14_GAIN_ZERO_POINT_FIVE; + op_FL_ip_FR_weight = Q14_GAIN_ZERO_POINT_FIVE; + op_FR_ip_FL_weight = Q14_GAIN_ZERO_POINT_FIVE; + op_FR_ip_FR_weight = Q14_GAIN_ZERO_POINT_FIVE; + } else if (sony_custom_stereo_mode == SONY_CUSTOM_STEREO_SWAP) { + op_FL_ip_FL_weight = 0; + op_FL_ip_FR_weight = Q14_GAIN_UNITY; + op_FR_ip_FL_weight = Q14_GAIN_UNITY; + op_FR_ip_FR_weight = 0; + } else { + op_FL_ip_FL_weight = Q14_GAIN_UNITY; + op_FL_ip_FR_weight = 0; + op_FR_ip_FL_weight = 0; + op_FR_ip_FR_weight = Q14_GAIN_UNITY; + } + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + port_id = msm_bedais[be_index].port_id; + if (!msm_bedais[be_index].active) + continue; + if ((port_id != SLIMBUS_0_RX) && + (port_id != RT_PROXY_PORT_001_RX)) + continue; + + for_each_set_bit(i, &msm_bedais[be_index].fe_sessions[0], + MSM_FRONTEND_DAI_MM_SIZE) { + if (fe_dai_map[i][SESSION_TYPE_RX].perf_mode == + ULTRA_LOW_LATENCY_PCM_MODE) { + pr_err("%s: pcm mode error:%d\n", __func__, + fe_dai_map[i][SESSION_TYPE_RX].perf_mode); + continue; + } + session_id = + fe_dai_map[i][SESSION_TYPE_RX].strm_id; + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + unsigned long copp = + session_copp_map[i] + [SESSION_TYPE_RX][be_index]; + if (!test_bit(idx, &copp)) + continue; + topo_id = adm_get_topology_for_port_copp_idx( + msm_bedais[be_index].port_id, idx); + if (topo_id < 0) + pr_debug("%s:Err:custom stereo topo %d", + __func__, topo_id); + pr_debug("idx %d\n", idx); + if (topo_id == DS2_ADM_COPP_TOPOLOGY_ID) + rc = msm_ds2_dap_set_custom_stereo_onoff + (msm_bedais[be_index].port_id, + idx, is_custom_stereo_on); + else if (topo_id == DOLBY_ADM_COPP_TOPOLOGY_ID) + rc = dolby_dap_set_custom_stereo_onoff( + msm_bedais[be_index].port_id, + idx, is_custom_stereo_on); + else + rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd + (msm_bedais[be_index].port_id, + idx, session_id, + op_FL_ip_FL_weight, + op_FL_ip_FR_weight, + op_FR_ip_FL_weight, + op_FR_ip_FR_weight); + if (rc < 0) + pr_err("%s: err setting custom stereo\n", + __func__); + } + + } + } + return 0; +} +static const char *const sony_custom_stereo_text[] = {"Off", "Mix", "Swap"}; +static const struct soc_enum sony_custom_stereo_channel_enum = + SOC_ENUM_SINGLE_EXT(3, sony_custom_stereo_text); +static const struct snd_kcontrol_new sony_stereo_to_custom_stereo_controls[] = { + SOC_ENUM_EXT("Set Custom Stereo", + sony_custom_stereo_channel_enum, + msm_routing_get_sony_stereo_to_custom_stereo_control, + msm_routing_put_sony_stereo_to_custom_stereo_control), +}; + static int msm_routing_get_app_type_cfg_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -11716,6 +11878,79 @@ static struct snd_kcontrol_new msm_voc_session_controls[] = { msm_voc_session_id_put), }; +static int msm_adm_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = mute.duration; + ucontrol->value.integer.value[1] = mute.flag_ch1; + ucontrol->value.integer.value[2] = mute.flag_ch2; + + pr_debug("%s: mute = {%d, %d, %d}\n", __func__, + mute.duration, mute.flag_ch1, mute.flag_ch2); + + return 0; +} + +static int msm_adm_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int be_index = 0; + int fedai_id = 0; + int port_id = 0; + int session_id = 0; + struct msm_pcm_routing_bdai_data *bedai; + + mute.duration = (uint32_t)ucontrol->value.integer.value[0]; + mute.flag_ch1 = (uint32_t)ucontrol->value.integer.value[1]; + mute.flag_ch2 = (uint32_t)ucontrol->value.integer.value[2]; + + if ((mute.duration > 0xffff) || + (mute.flag_ch1 > 1) || (mute.flag_ch2 > 1)) { + pr_err("%s Invalid arguments. mute={%x, %x, %x}", + __func__, mute.duration, mute.flag_ch1, mute.flag_ch2); + ret = -EINVAL; + goto done; + } + + for (fedai_id = 0; fedai_id < MSM_FRONTEND_DAI_MM_SIZE; fedai_id++) { + if (fe_dai_map[fedai_id][SESSION_TYPE_RX].strm_id != + INVALID_SESSION) { + session_id = fe_dai_map[fedai_id][SESSION_TYPE_RX].strm_id; + + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + if (!msm_bedais[be_index].active) + continue; + + bedai = &msm_bedais[be_index]; + pr_debug("%s: be_index=%d, bedai->fe_sessions=%#x\n", __func__, + be_index, (int)bedai->fe_sessions[0]); + + port_id = msm_bedais[be_index].port_id; + + pr_debug("%s: fedai_id=%d, strm_id=%d, port_id=%d, session_id=%d, mute={%d, %d, %d}\n", + __func__, fedai_id, + fe_dai_map[fedai_id][SESSION_TYPE_RX].strm_id, + port_id, session_id, mute.duration, + mute.flag_ch1, mute.flag_ch2); + adm_matrix_mute(port_id, session_id, mute.duration, + mute.flag_ch1, mute.flag_ch2); + } + } else { + ; /* do nothing */ + } + } + +done: + return ret; +} + +static struct snd_kcontrol_new msm_adm_mute_controls[] = { + SOC_SINGLE_MULTI_EXT("Matrix Mute", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 3, msm_adm_mute_get, + msm_adm_mute_put), +}; + static int msm_sound_focus_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -16997,6 +17232,10 @@ static int msm_routing_probe(struct snd_soc_platform *platform) snd_soc_add_platform_controls(platform, native_mode_controls, ARRAY_SIZE(native_mode_controls)); + snd_soc_add_platform_controls(platform, + sony_stereo_to_custom_stereo_controls, + ARRAY_SIZE(sony_stereo_to_custom_stereo_controls)); + msm_qti_pp_add_controls(platform); msm_dts_srs_tm_add_controls(platform); @@ -17007,10 +17246,15 @@ static int msm_routing_probe(struct snd_soc_platform *platform) use_ds1_or_ds2_controls, ARRAY_SIZE(use_ds1_or_ds2_controls)); + msm_routing_ahc_add_controls(platform); + snd_soc_add_platform_controls(platform, device_pp_params_mixer_controls, ARRAY_SIZE(device_pp_params_mixer_controls)); + snd_soc_add_platform_controls(platform, msm_adm_mute_controls, + ARRAY_SIZE(msm_adm_mute_controls)); + snd_soc_add_platform_controls(platform, msm_routing_be_dai_name_table_mixer_controls, ARRAY_SIZE(msm_routing_be_dai_name_table_mixer_controls)); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h index 531f83d752b09bdde48570f3b79303811bf74cea..d149b5a0de81273e5fac0de5470d05dbc81ee54a 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2016 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #ifndef _MSM_PCM_ROUTING_H #define _MSM_PCM_ROUTING_H #include @@ -392,6 +397,13 @@ enum { EXT_EC_REF_SLIM_1_TX, }; +enum { + SONY_CUSTOM_STEREO_NORMAL = 0, + SONY_CUSTOM_STEREO_MIX, + SONY_CUSTOM_STEREO_SWAP, + SONY_CUSTOM_STEREO_MAX, +}; + #define INVALID_SESSION -1 #define SESSION_TYPE_RX 0 #define SESSION_TYPE_TX 1 diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c index 1b8150e5a30f8033bdc8ae0c5b2b7b3cfa82c609..2615ee61db21e40d189846cf6c2985ba56c6e43a 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c @@ -60,6 +60,28 @@ static struct snd_pcm_hardware msm_pcm_hardware = { .fifo_size = 0, }; +static int is_valid_session_id(uint32_t session_id) +{ + int idx = 0; + + switch (session_id) { + case VOICE_SESSION_VSID: + case VOICE2_SESSION_VSID: + case VOLTE_SESSION_VSID: + case QCHAT_SESSION_VSID: + case VOWLAN_SESSION_VSID: + case VOICEMMODE1_VSID: + case VOICEMMODE2_VSID: + case ALL_SESSION_VSID: + idx = 1; + break; + default: + pr_debug("%s: Invalid session_id : %x\n", __func__, session_id); + break; + } + return idx; +} + static int get_idx_for_session(uint32_t session_id) { int idx = 0; @@ -684,6 +706,11 @@ static int msm_dtmf_detect_rx_vsid_cb_put(struct snd_kcontrol *kcontrol, uint32_t session_id = ucontrol->value.integer.value[0]; uint32_t enable = ucontrol->value.integer.value[1]; + if (!is_valid_session_id(session_id)) { + pr_err(" %s Invalid session_id : %x\n", __func__, session_id); + return -EINVAL; + } + if (enable) enable = 1; diff --git a/sound/soc/msm/qdsp6v2/msm-sony-hweffect.c b/sound/soc/msm/qdsp6v2/msm-sony-hweffect.c new file mode 100755 index 0000000000000000000000000000000000000000..cfbe9790b814b36198f729be6d40dc6eeb2bd85f --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-sony-hweffect.c @@ -0,0 +1,574 @@ +/* + * Author: Yoshio Yamamoto yoshio.xa.yamamoto@sonymobile.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; either version 2 + * of the License, or (at your option) any later version. + */ +/* + * Copyright (C) 2014 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include "msm-sony-hweffect.h" +#include "sound/sony-hweffect-params.h" + +#define MAX_SUPPORTED_SAMPLERATE 48000 +#define MAX_SUPPORTED_BITWIDTH 16 +#define MAX_SUPPORTED_CHANNEL_COUNT 2 + +struct mfc_output_media_fmt { + uint32_t sampling_rate; + uint16_t bits_per_sample; + uint16_t num_channels; + uint16_t channel_type[8]; +} __packed; + +static void set_mfc_output_format(struct mfc_output_media_fmt *mfc_params, + uint32_t sampling_rate, + uint16_t bits_per_sample, + uint16_t num_channels) +{ + mfc_params->sampling_rate = sampling_rate; + mfc_params->bits_per_sample = bits_per_sample; + mfc_params->num_channels = num_channels; + if (num_channels == 1) { + mfc_params->channel_type[0] = PCM_CHANNEL_FC; + } else if (num_channels == 2) { + mfc_params->channel_type[0] = PCM_CHANNEL_FL; + mfc_params->channel_type[1] = PCM_CHANNEL_FR; + } else if (num_channels == 3) { + mfc_params->channel_type[0] = PCM_CHANNEL_FL; + mfc_params->channel_type[1] = PCM_CHANNEL_FR; + mfc_params->channel_type[2] = PCM_CHANNEL_FC; + } else if (num_channels == 4) { + mfc_params->channel_type[0] = PCM_CHANNEL_FL; + mfc_params->channel_type[1] = PCM_CHANNEL_FR; + mfc_params->channel_type[2] = PCM_CHANNEL_LS; + mfc_params->channel_type[3] = PCM_CHANNEL_RS; + } else if (num_channels == 5) { + mfc_params->channel_type[0] = PCM_CHANNEL_FL; + mfc_params->channel_type[1] = PCM_CHANNEL_FR; + mfc_params->channel_type[2] = PCM_CHANNEL_FC; + mfc_params->channel_type[3] = PCM_CHANNEL_LS; + mfc_params->channel_type[4] = PCM_CHANNEL_RS; + } else if (num_channels == 6) { + mfc_params->channel_type[0] = PCM_CHANNEL_FL; + mfc_params->channel_type[1] = PCM_CHANNEL_FR; + mfc_params->channel_type[2] = PCM_CHANNEL_LFE; + mfc_params->channel_type[3] = PCM_CHANNEL_FC; + mfc_params->channel_type[4] = PCM_CHANNEL_LS; + mfc_params->channel_type[5] = PCM_CHANNEL_RS; + } else if (num_channels == 7) { + mfc_params->channel_type[0] = PCM_CHANNEL_FL; + mfc_params->channel_type[1] = PCM_CHANNEL_FR; + mfc_params->channel_type[2] = PCM_CHANNEL_FC; + mfc_params->channel_type[3] = PCM_CHANNEL_LFE; + mfc_params->channel_type[4] = PCM_CHANNEL_LB; + mfc_params->channel_type[5] = PCM_CHANNEL_RB; + mfc_params->channel_type[6] = PCM_CHANNEL_CS; + } else if (num_channels == 8) { + mfc_params->channel_type[0] = PCM_CHANNEL_FL; + mfc_params->channel_type[1] = PCM_CHANNEL_FR; + mfc_params->channel_type[2] = PCM_CHANNEL_LFE; + mfc_params->channel_type[3] = PCM_CHANNEL_FC; + mfc_params->channel_type[4] = PCM_CHANNEL_LS; + mfc_params->channel_type[5] = PCM_CHANNEL_RS; + mfc_params->channel_type[6] = PCM_CHANNEL_LB; + mfc_params->channel_type[7] = PCM_CHANNEL_RB; + } else { + pr_err("%s: invalid num_channels %d\n", __func__, + num_channels); + /* Fallback for unexpected channels. */ + mfc_params->num_channels = 2; + mfc_params->channel_type[0] = PCM_CHANNEL_FL; + mfc_params->channel_type[1] = PCM_CHANNEL_FR; + } + + pr_debug("%s: sampling_rate:%d, bits_per_sample:%d, num_channels:%d\n", + __func__, mfc_params->sampling_rate, + mfc_params->bits_per_sample, mfc_params->num_channels); + + return; +} + +/* + * Value array (max size:128) + * +---------------+---------------------------+ + * | array index | value | + * +---------------+---------------------------+ + * | 0 | output device (unused) | + * +---------------+---------------------------+ + * | 1 | num of commands | + * +---------------+---------------------------+ + * | 2 | 1st command | + * +---------------+---------------------------+ + * | 3 | config state | + * +---------------+---------------------------+ + * | 4 | 1st param offset | + * +---------------+---------------------------+ + * | 5 | 1st param length | + * +---------------+---------------------------+ + * | 6 to n | 1st param effect params | + * +---------------+---------------------------+ + * | n + 1 | 2nd command | + * +---------------+---------------------------+ + * | n + 2 | config state | + * +---------------+---------------------------+ + * | n + 3 | 2nd param offset | + * +---------------+---------------------------+ + * | n + 4 | 2nd param length | + * +---------------+---------------------------+ + * | (n + 5) to m | 2nd param effect params | + * +---------------+---------------------------+ + * . + * . + * . +*/ + +int msm_sony_hweffect_sonybundle_handler(struct audio_client *ac, + struct sonybundle_params *sb, + long *values, + uint32_t sampling_rate, + uint16_t bits_per_sample, + uint16_t num_channels) +{ + int devices = *values++; + int num_commands = *values++; + char *params, *effect; + int i, j; + uint32_t module_id, param_id; + int rc = 0; + + pr_debug("%s\n", __func__); + if (!ac) { + pr_err("%s: cannot set sonyeffect hw\n", __func__); + return -EINVAL; + } + params = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL); + if (!params) { + pr_err("%s, params memory alloc failed\n", __func__); + return -ENOMEM; + } + pr_debug("%s: device: %d, num_commands %d\n", + __func__, devices, num_commands); + + effect = params + sizeof(struct param_hdr_v1); + for (i = 0; i < num_commands; i++) { + uint32_t p_length = 0; + uint32_t command_id = *values++; + uint32_t command_config_state = *values++; + uint32_t index_offset = *values++; + uint32_t length = *values++; + switch (command_id) { + case SONYBUNDLE_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("SONYBUNDLE_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->common.enable_flag = *values++; + pr_debug("%s:SONYBUNDLE_ENABLE state:%d\n", __func__, + sb->common.enable_flag); + if (command_config_state == CONFIG_SET) { + struct common_params *cmn_params = + (struct common_params *)effect; + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_COMMON_USER_PARAM; + cmn_params->enable_flag = + sb->common.enable_flag; + p_length += sizeof(struct common_params); + } + break; + + case DYNAMIC_NORMALIZER_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("DYNAMIC_NORMALIZER_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->dynamic_normalizer.enable_flag = *values++; + pr_debug("%s:DYNAMIC_NORMALIZER_ENABLE state:%d\n", + __func__, sb->dynamic_normalizer.enable_flag); + if (command_config_state == CONFIG_SET) { + struct dynamic_normalizer_params *dn_params = + (struct dynamic_normalizer_params *)effect; + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = + PARAM_ID_SB_DYNAMICNORMALIZER_USER_PARAM; + dn_params->enable_flag = + sb->dynamic_normalizer.enable_flag; + p_length += + sizeof(struct dynamic_normalizer_params); + } + break; + + case SFORCE_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("SFORCE_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->sforce.enable_flag = *values++; + pr_debug("%s:SFORCE_ENABLE state:%d\n", __func__, + sb->sforce.enable_flag); + if (command_config_state == CONFIG_SET) { + struct sforce_params *sf_params = + (struct sforce_params *)effect; + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_SFORCE_USER_PARAM; + sf_params->enable_flag = + sb->sforce.enable_flag; + p_length += sizeof(struct sforce_params); + + if (!sb->sforce_tuning_update) { + int ret; + ret = sony_hweffect_send_tuning_params( + SFORCE_PARAM, + (void *)ac); + pr_debug("%s:sony_hweffect_send_tuning_params(S-Force) ret=%d\n", + __func__, ret); + if (ret < 0) { + pr_err("SFORCE_ENABLE: send tuning param error\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->sforce_tuning_update = true; + } + } + break; + + case VPT20_MODE: + if (length != 1 || index_offset != 0) { + pr_err("VPT20_MODE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->vpt20.mode = *values++; + pr_debug("%s:VPT20_MODE: %d\n", __func__, + sb->vpt20.mode); + if (command_config_state == CONFIG_SET) { + struct vpt20_params *vpt_params = + (struct vpt20_params *)effect; + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_VPT20_USER_PARAM; + vpt_params->mode = sb->vpt20.mode; + p_length += sizeof(struct vpt20_params); + } + break; + + case CLEARPHASE_HP_MODE: + if (length != 1 || index_offset != 0) { + pr_err("CLEARPHASE_HP_MODE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->clearphase_hp.mode = *values++; + pr_debug("%s:CLEARPHASE_HP_MODE: %d\n", __func__, + sb->clearphase_hp.mode); + if (command_config_state == CONFIG_SET) { + struct clearphase_hp_params *cp_hp_params = + (struct clearphase_hp_params *)effect; + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_CLEARPHASE_HP_USER_PARAM; + cp_hp_params->mode = sb->clearphase_hp.mode; + p_length += sizeof(struct clearphase_hp_params); + + { + int ret; + ret = sony_hweffect_send_tuning_params( + CLEARPHASE_HP_PARAM, + (void *)ac); + pr_debug("%s:sony_hweffect_send_tuning_params(CP_HP) ret=%d\n", + __func__, ret); + if (ret < 0) { + pr_err("CLEARPHASE_HP_MODE: send tuning param error\n"); + rc = -EINVAL; + goto invalid_config; + } + } + } + break; + + case CLEARAUDIO_CHSEP: + if (length != 1 || index_offset != 0) { + pr_err("CLEARAUDIO_CHSEP:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->clearaudio.chsep_coef = *values++; + pr_debug("%s:CLEARAUDIO_CHSEP: %d\n", __func__, + sb->clearaudio.chsep_coef); + if (command_config_state == CONFIG_SET) { + struct clearaudio_params *ca_params = + (struct clearaudio_params *)effect; + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_CLEARAUDIO_USER_PARAM; + ca_params->chsep_coef = + sb->clearaudio.chsep_coef; + for (j = 0; j < BAND_NUM; j++) + ca_params->eq_coef[j] = + sb->clearaudio.eq_coef[j]; + p_length += + sizeof(struct clearaudio_params); + } + break; + + case CLEARAUDIO_EQ_COEF: + if (length != 6 || index_offset != 0) { + pr_err("CLEARAUDIO_EQ_COEF:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + for (j = 0; j < BAND_NUM; j++) + sb->clearaudio.eq_coef[j] = + (int16_t)*values++; + pr_debug("%s:CLEARAUDIO_EQ_COEF: %d %d %d %d %d %d\n", + __func__, + sb->clearaudio.eq_coef[0], + sb->clearaudio.eq_coef[1], + sb->clearaudio.eq_coef[2], + sb->clearaudio.eq_coef[3], + sb->clearaudio.eq_coef[4], + sb->clearaudio.eq_coef[5]); + if (command_config_state == CONFIG_SET) { + struct clearaudio_params *ca_params = + (struct clearaudio_params *)effect; + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_CLEARAUDIO_USER_PARAM; + ca_params->chsep_coef = + sb->clearaudio.chsep_coef; + for (j = 0; j < BAND_NUM; j++) + ca_params->eq_coef[j] = + sb->clearaudio.eq_coef[j]; + p_length += + sizeof(struct clearaudio_params); + } + break; + + case CLEARAUDIO_VOLUME: + if (length != 1 || index_offset != 0) { + pr_err("CLEARAUDIO_VOLUME:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->ca_volume.gain = *values++; + pr_debug("%s:CLEARAUDIO_VOLUME: %d\n", __func__, + sb->ca_volume.gain); + if (command_config_state == CONFIG_SET) { + struct clearaudio_volume *ca_volume = + (struct clearaudio_volume *)effect; + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_CLEARAUDIO_VOLUME_PARAM; + ca_volume->gain = sb->ca_volume.gain; + p_length += + sizeof(struct clearaudio_volume); + } + break; + + case CLEARPHASE_SP_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("CLEARPHASE_SP_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->clearphase_sp.mode = *values++; + pr_debug("%s:CLEARPHASE_SP_ENABLE mode:%d\n", __func__, + sb->clearphase_sp.mode); + if (command_config_state == CONFIG_SET) { + struct clearphase_sp_params *cp_sp_params = + (struct clearphase_sp_params *)effect; + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_CLEARPHASE_SP_USER_PARAM; + cp_sp_params->mode = sb->clearphase_sp.mode; + p_length += + sizeof(struct clearphase_sp_params); + + if (!sb->clearphase_sp_tuning_update) { + int ret; + ret = sony_hweffect_send_tuning_params( + CLEARPHASE_SP_PARAM, + (void *)ac); + pr_debug("%s:sony_hweffect_send_tuning_params(CP_SP) ret=%d\n", + __func__, ret); + if (ret < 0) { + pr_err("CLEARPHASE_SP_ENABLE: send tuning param error\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->clearphase_sp_tuning_update = true; + } + } + break; + + case XLOUD_ENABLE: + if (length != 1 || index_offset != 0) { + pr_err("XLOUD_ENABLE:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->xloud.enable_flag = *values++; + pr_debug("%s:XLOUD_ENABLE state:%d\n", __func__, + sb->xloud.enable_flag); + if (command_config_state == CONFIG_SET) { + struct xloud_params *xl_params = + (struct xloud_params *)effect; + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_XLOUD_USER_PARAM; + xl_params->enable_flag = sb->xloud.enable_flag; + p_length += sizeof(struct xloud_params); + + if (!sb->xloud_tuning_update) { + int ret; + ret = sony_hweffect_send_tuning_params( + XLOUD_PARAM, + (void *)ac); + pr_debug("%s:sony_hweffect_send_tuning_params(XLOUD) ret=%d\n", + __func__, ret); + if (ret < 0) { + pr_err("XLOUD_ENABLE: send tuning param error\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->xloud_tuning_update = true; + } + } + break; + + case DOWN_CONVERT: + if (length != 1 || index_offset != 0) { + pr_err("DOWN_CONVERT:invalid params\n"); + rc = -EINVAL; + goto invalid_config; + } + sb->down_convert_enable = *values++; + pr_debug("%s:DOWN_CONVERT state:%d\n", __func__, + sb->down_convert_enable); + if (command_config_state == CONFIG_SET) { + struct mfc_output_media_fmt *mfc_params = + (struct mfc_output_media_fmt *)effect; + + module_id = AUDPROC_MODULE_ID_MFC; + param_id = AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT; + if (sb->down_convert_enable == 1) { + /* Convert to supported format. */ + set_mfc_output_format(mfc_params, + MAX_SUPPORTED_SAMPLERATE, + MAX_SUPPORTED_BITWIDTH, + MAX_SUPPORTED_CHANNEL_COUNT); + } else { + /* Specify same as input stream to disable down-convert. */ + set_mfc_output_format(mfc_params, + sampling_rate, + bits_per_sample, + num_channels); + } + p_length += sizeof(struct mfc_output_media_fmt); + } + break; + + default: + pr_err("%s: Invalid command to set config\n", __func__); + break; + } + + if (p_length) { + struct param_hdr_v1 *param_data; + int ret; + + param_data = + (struct param_hdr_v1 *)params; + param_data->module_id = module_id; + param_data->param_id = param_id; + param_data->param_size = (uint16_t)p_length; + param_data->reserved = 0; + p_length += + sizeof(struct param_hdr_v1); + + ret = q6asm_set_pp_params(ac, NULL, params, p_length); + if (ret < 0) { + pr_err("q6asm_set_pp_params " + "failed %d\n", ret); + rc = -EINVAL; + goto invalid_config; + } + } + } + +invalid_config: + kfree(params); + return rc; +} + +void msm_sony_hweffect_sonybundle_get( + struct sonybundle_params *sb, + long *values) +{ + int i; + + pr_debug("%s\n", __func__); + + *values++ = 0; //output device (unused) + *values++ = 11; //num of commands + + *values++ = SONYBUNDLE_ENABLE; + *values++ = sb->common.enable_flag; + + *values++ = DYNAMIC_NORMALIZER_ENABLE; + *values++ = sb->dynamic_normalizer.enable_flag; + + *values++ = SFORCE_ENABLE; + *values++ = sb->sforce.enable_flag; + + *values++ = VPT20_MODE; + *values++ = sb->vpt20.mode; + + *values++ = CLEARPHASE_HP_MODE; + *values++ = sb->clearphase_hp.mode; + + *values++ = CLEARAUDIO_CHSEP; + *values++ = sb->clearaudio.chsep_coef; + + *values++ = CLEARAUDIO_EQ_COEF; + for (i = 0; i < CLEARAUDIO_EQ_COEF_PARAM_LEN; i++) { + *values++ = sb->clearaudio.eq_coef[i]; + } + + *values++ = CLEARAUDIO_VOLUME; + *values++ = sb->ca_volume.gain; + + *values++ = CLEARPHASE_SP_ENABLE; + *values++ = sb->clearphase_sp.mode; + + *values++ = XLOUD_ENABLE; + *values++ = sb->xloud.enable_flag; + + *values++ = DOWN_CONVERT; + *values++ = sb->down_convert_enable; +} + +void init_sonybundle_params(struct sonybundle_params *sb) +{ + /* Initialization of effect having initial value except for 0 */ + sb->clearphase_hp.mode = CP_MODE_OFF; + sb->clearphase_sp.mode = CP_MODE_OFF; + sb->vpt20.mode = VPT_MODE_OFF; + + return; +} diff --git a/sound/soc/msm/qdsp6v2/msm-sony-hweffect.h b/sound/soc/msm/qdsp6v2/msm-sony-hweffect.h new file mode 100644 index 0000000000000000000000000000000000000000..d51fb0e8dcf59ab098744e8272e44eac620f0de5 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-sony-hweffect.h @@ -0,0 +1,38 @@ +/* + * Author: Yoshio Yamamoto yoshio.xa.yamamoto@sonymobile.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; either version 2 + * of the License, or (at your option) any later version. + */ +/* + * Copyright (C) 2014 Sony Mobile Communications 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 _MSM_SONY_HWEFFECT_H +#define _MSM_SONY_HWEFFECT_H + +#include + +#define CP_MODE_1 0 +#define CP_MODE_OFF 4 +#define VPT_MODE_OFF 4 + +int msm_sony_hweffect_sonybundle_handler(struct audio_client *ac, + struct sonybundle_params *sb, + long *values, + uint32_t sampling_rate, + uint16_t bits_per_sample, + uint16_t num_channels); + +void msm_sony_hweffect_sonybundle_get(struct sonybundle_params *sb, + long *values); + +void init_sonybundle_params(struct sonybundle_params *sb); + +#endif /*_MSM_SONYEFFECT_HW_H*/ diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index c6f3b5ba96ba34e4e1cb7b7ed796471209504248..88d810ae04887619371b0bd9c3a7244a8e438e72 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -9,6 +9,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -50,6 +55,11 @@ #define DS2_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFF #endif +#ifdef CONFIG_FORCE_24BIT_COPP +#define APPTYPE_GENERAL_PLAYBACK 0x00011130 +#define APPTYPE_SYSTEM_SOUNDS 0x00011131 +#endif + struct adm_copp { atomic_t id[AFE_MAX_PORTS][MAX_COPPS_PER_PORT]; @@ -1037,6 +1047,81 @@ done: } EXPORT_SYMBOL(adm_get_pp_params); +int adm_ahc_send_params(int port_id, int copp_idx, char *params, + uint32_t params_length) +{ + struct adm_cmd_set_pp_params_v5 *adm_params = NULL; + int sz, rc = 0; + int port_idx; + + pr_debug("%s:\n", __func__); + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + sz = sizeof(struct adm_cmd_set_pp_params_v5) + params_length; + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s, adm params memory alloc failed", __func__); + return -ENOMEM; + } + + memcpy(((u8 *)adm_params + sizeof(struct adm_cmd_set_pp_params_v5)), + params, params_length); + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.pkt_size = sz; + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->hdr.token = port_idx << 16 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->payload_size = params_length; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (rc < 0) { + pr_err("%s: Set params failed port = 0x%x rc %d\n", + __func__, port_id, rc); + rc = -EINVAL; + goto ahc_send_param_return; + } + /* Wait for the callback */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: Set params timed out port = 0x%x\n", + __func__, port_id); + rc = -EINVAL; + goto ahc_send_param_return; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto ahc_send_param_return; + } + rc = 0; +ahc_send_param_return: + kfree(adm_params); + return rc; +} + int adm_get_pp_topo_module_list_v2(int port_id, int copp_idx, int32_t param_length, int32_t *returned_params) @@ -1445,6 +1530,7 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) case ADM_CMD_DEVICE_OPEN_V6: case ADM_CMD_DEVICE_OPEN_V8: case ADM_CMD_SET_MTMX_STRTR_DEV_PARAMS_V1: + case ADM_CMD_MATRIX_MUTE_V5: pr_debug("%s: Basic callback received, wake up.\n", __func__); atomic_set(&this_adm.copp.stat[port_idx] @@ -2124,6 +2210,107 @@ static void send_adm_cal(int port_id, int copp_idx, int path, int perf_mode, return; } +int adm_matrix_mute(int port_id, int session_id, uint32_t ramp_duration, + uint32_t mute_flag_ch1, uint32_t mute_flag_ch2) +{ + struct adm_cmd_matrix_mute_v5 *apr = NULL; + + int cmd_size = 0; + int ret = 0; + void *matrix_mute = NULL; + int port_idx, copp_idx = 0; + + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + if (this_adm.apr == NULL) { + this_adm.apr = apr_register("ADSP", "ADM", adm_callback, + 0xFFFFFFFF, &this_adm); + if (this_adm.apr == NULL) { + pr_err("%s: Unable to register ADM\n", __func__); + ret = -ENODEV; + return ret; + } + rtac_set_adm_handle(this_adm.apr); + } + + cmd_size = sizeof(struct adm_cmd_matrix_mute_v5); + matrix_mute = kzalloc(cmd_size, GFP_KERNEL); + if (matrix_mute == NULL) { + pr_err("%s: Mem alloc failed\n", __func__); + ret = -EINVAL; + return ret; + } + apr = (struct adm_cmd_matrix_mute_v5 *)matrix_mute; + + pr_debug("%s: Port ID 0x%x, index %d\n", __func__, port_id, port_idx); + + /* APR header field */ + apr->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + apr->hdr.pkt_size = cmd_size; + apr->hdr.src_svc = APR_SVC_ADM; + apr->hdr.src_domain = APR_DOMAIN_APPS; + apr->hdr.src_port = 0; + apr->hdr.dest_svc = APR_SVC_ADM; + apr->hdr.dest_domain = APR_DOMAIN_ADSP; + apr->hdr.dest_port = atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + apr->hdr.token = port_idx << 16 | copp_idx; + apr->hdr.opcode = ADM_CMD_MATRIX_MUTE_V5; + /* APR message payload */ + apr->matrix_id = ADM_MATRIX_ID_AUDIO_RX; + apr->session_id = session_id; + apr->copp_id = ADM_CMD_MATRIX_MUTE_COPP_ID_ALL_CONNECTED_COPPS; + apr->mute_flag_ch_1 = mute_flag_ch1; + apr->mute_flag_ch_2 = mute_flag_ch2; + apr->mute_flag_ch_3 = 0; + apr->mute_flag_ch_4 = 0; + apr->mute_flag_ch_5 = 0; + apr->mute_flag_ch_6 = 0; + apr->mute_flag_ch_7 = 0; + apr->mute_flag_ch_8 = 0; + apr->ramp_duration = ramp_duration; + apr->reserved_for_align = 0; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)matrix_mute); + if (ret < 0) { + pr_err("%s: Set params failed port = %#x\n", + __func__, port_id); + ret = -EINVAL; + goto adm_matrix_mute_return; + } + /* Wait for the callback */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: Set params timed out port = %#x\n", + __func__, port_id); + ret = -EINVAL; + goto adm_matrix_mute_return; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto adm_matrix_mute_return; + } + ret = 0; + +adm_matrix_mute_return: + kfree(matrix_mute); + return ret; +} + int adm_connect_afe_port(int mode, int session_id, int port_id) { struct adm_cmd_connect_afe_port_v5 cmd; @@ -2849,6 +3036,20 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology, __func__, port_id, path, rate, channel_mode, perf_mode, topology); +#ifdef CONFIG_FORCE_24BIT_COPP + if ((topology == ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_SOMC_HP) && + (app_type == APPTYPE_GENERAL_PLAYBACK)) { + bit_width = 24; + pr_debug("%s: Force open adm in 24-bit for SOMC HP topology 0x%x\n", + __func__, topology); + } else if (((topology == ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_RX_MCH_IIR_COPP_MBDRC_V3) || + (topology == ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_RX_MCH_FIR_IIR_COPP_MBDRC_V3)) && + ((app_type == APPTYPE_GENERAL_PLAYBACK) || (app_type == APPTYPE_SYSTEM_SOUNDS))) { + bit_width = 24; + pr_debug("%s: Force open adm in 24-bit for SOMC Speaker topology 0x%x\n", + __func__, topology); + } +#endif port_id = q6audio_convert_virtual_to_portid(port_id); port_idx = adm_validate_and_get_port_index(port_id); if (port_idx < 0) { @@ -2893,7 +3094,9 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology, if ((topology == VPM_TX_SM_ECNS_COPP_TOPOLOGY) || (topology == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY) || - (topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY)) + (topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY) || + (topology == VOICE_TOPOLOGY_LVVEFQ_TX_SM) || + (topology == VOICE_TOPOLOGY_LVVEFQ_TX_DM)) rate = 16000; /* @@ -4663,6 +4866,128 @@ done: return ret; } +int adm_get_params_v2(int port_id, int copp_idx, uint32_t module_id, + uint32_t param_id, uint32_t params_length, + char *params, uint32_t client_id) +{ + struct adm_cmd_get_pp_params_v5 *adm_params = NULL; + int rc = 0, i = 0; + int port_idx, idx; + int *params_data = (int *)params; + uint64_t sz = 0; + + port_id = afe_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + sz = (uint64_t)sizeof(struct adm_cmd_get_pp_params_v5) + + (uint64_t)params_length; + /* + * Check if the value of "sz" (which is ultimately assigned to + * "hdr.pkt_size") crosses U16_MAX. + */ + if (sz > U16_MAX) { + pr_err("%s: Invalid params_length\n", __func__); + return -EINVAL; + } + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) { + pr_err("%s: adm params memory alloc failed", __func__); + return -ENOMEM; + } + + memcpy(((u8 *)adm_params + sizeof(struct adm_cmd_get_pp_params_v5)), + params, params_length); + adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + adm_params->hdr.pkt_size = sz; + adm_params->hdr.src_svc = APR_SVC_ADM; + adm_params->hdr.src_domain = APR_DOMAIN_APPS; + adm_params->hdr.src_port = port_id; + adm_params->hdr.dest_svc = APR_SVC_ADM; + adm_params->hdr.dest_domain = APR_DOMAIN_ADSP; + adm_params->hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + adm_params->hdr.token = port_idx << 16 | client_id << 8 | copp_idx; + adm_params->hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5; + adm_params->data_payload_addr_lsw = 0; + adm_params->data_payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->module_id = module_id; + adm_params->param_id = param_id; + adm_params->param_max_size = params_length; + adm_params->reserved = 0; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (rc < 0) { + pr_err("%s: Failed to Get Params on port_id 0x%x %d\n", + __func__, port_id, rc); + rc = -EINVAL; + goto adm_get_param_return; + } + /* Wait for the callback with copp id */ + rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!rc) { + pr_err("%s: get params timed out port_id = 0x%x\n", __func__, + port_id); + rc = -EINVAL; + goto adm_get_param_return; + } else if (atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto adm_get_param_return; + } + idx = ADM_GET_PARAMETER_LENGTH * copp_idx; + + if (adm_get_parameters[idx] < 0) { + pr_err("%s: Size is invalid %d\n", __func__, + adm_get_parameters[idx]); + rc = -EINVAL; + goto adm_get_param_return; + } + if ((params_data) && + (ARRAY_SIZE(adm_get_parameters) > + idx) && + (ARRAY_SIZE(adm_get_parameters) >= + 1+adm_get_parameters[idx]+idx) && + (params_length/sizeof(uint32_t) >= + adm_get_parameters[idx])) { + for (i = 0; i < adm_get_parameters[idx]; i++) + params_data[i] = adm_get_parameters[1+i+idx]; + + } else { + pr_err("%s: Get param data not copied! get_param array size %zd, index %d, params array size %zd, index %d\n", + __func__, ARRAY_SIZE(adm_get_parameters), + (1+adm_get_parameters[idx]+idx), + params_length/sizeof(int), + adm_get_parameters[idx]); + } + rc = 0; +adm_get_param_return: + kfree(adm_params); + + return rc; +} + +int adm_get_params(int port_id, int copp_idx, uint32_t module_id, + uint32_t param_id, uint32_t params_length, char *params) +{ + return adm_get_params_v2(port_id, copp_idx, module_id, param_id, + params_length, params, 0); +} + static int __init adm_init(void) { int i = 0, j; diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c old mode 100644 new mode 100755 index 2f048fddcb08bd1463b7290e76cf206ab4490b11..659a92d2f5c784a36de73f392e663efa594a9082 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -12,6 +12,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2014 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include #include @@ -43,6 +48,9 @@ #include #include +#include "sound/sony-hweffect.h" +#include "sound/sony-hweffect-params.h" + #define TRUE 0x01 #define FALSE 0x00 #define SESSION_MAX 9 @@ -86,6 +94,17 @@ enum { #define ASM_SET_BIT(n, x) (n |= 1 << x) #define ASM_TEST_BIT(n, x) ((n >> x) & 1) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif +#define ONCE_PARAM_SIZE 460 + +struct param_divide { + uint8_t divide_num; + uint8_t no; + uint16_t offset; +}; + /* TODO, combine them together */ static DEFINE_MUTEX(session_lock); struct asm_mmap { @@ -577,6 +596,136 @@ static void config_debug_fs_init(void) } #endif +/* SOMC effect control start */ + +int sony_hweffect_send_tuning_params(unsigned int effect_id, void *client) +{ + int rc = 0x00; + char *param, *tuning_param_s, *tuning_param_d; + uint32_t module_id, param_id; + uint32_t param_length; + struct param_hdr_v1 *param_data; + struct param_divide *divide; + uint32_t i, num, len, ret; + + pr_debug("%s: effect_id=%u\n", __func__, effect_id); + if (client == NULL) { + pr_err("%s: audio client is NULL\n", __func__); + return -EINVAL; + } + + param = kzalloc(MAX_INBAND_PARAM_SZ, GFP_KERNEL); + if (!param) { + pr_err("%s, param memory alloc failed\n", __func__); + return -ENOMEM; + } + + tuning_param_d = param + sizeof(struct param_hdr_v1); + + switch (effect_id) { + case SFORCE_PARAM: + tuning_param_s = sony_hweffect_params_getparam(SFORCE_PARAM); + if (tuning_param_s == NULL) { + pr_err("%s: sforce param is NULL\n", __func__); + rc = -EINVAL; + goto invalid_config; + } + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_SFORCE_TUNING; + param_length = sizeof(struct s_force_tuning_params); + pr_debug("%s: SFORCE_PARAM\n module_id=%u, param_id=%u, param_length=%u", + __func__, module_id, param_id, param_length); + break; + + case CLEARPHASE_HP_PARAM: + tuning_param_s = sony_hweffect_params_getparam( + CLEARPHASE_HP_PARAM); + if (tuning_param_s == NULL) { + pr_err("%s: clearphase_hp param is NULL\n", __func__); + rc = -EINVAL; + goto invalid_config; + } + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_CLEARPHASE_HP_TUNING; + param_length = sizeof(struct clearphase_hp_tuning_params); + pr_debug("%s: CLEARPHASE_HP_PARAM\n module_id=%u, param_id=%u, param_length=%u", + __func__, module_id, param_id, param_length); + break; + + case CLEARPHASE_SP_PARAM: + tuning_param_s = sony_hweffect_params_getparam( + CLEARPHASE_SP_PARAM); + if (tuning_param_s == NULL) { + pr_err("%s: clearphase_sp param is NULL\n", __func__); + rc = -EINVAL; + goto invalid_config; + } + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_CLEARPHASE_SP_TUNING; + param_length = sizeof(struct clearphase_sp_tuning_params); + pr_debug("%s: CLEARPHASE_SP_PARAM\n module_id=%u, param_id=%u, param_length=%u", + __func__, module_id, param_id, param_length); + break; + + case XLOUD_PARAM: + tuning_param_s = sony_hweffect_params_getparam(XLOUD_PARAM); + if (tuning_param_s == NULL) { + pr_err("%s: xloud param is NULL\n", __func__); + rc = -EINVAL; + goto invalid_config; + } + + module_id = ASM_MODULE_ID_SONYBUNDLE; + param_id = PARAM_ID_SB_XLOUD_TUNING; + param_length = sizeof(struct xloud_tuning_params); + pr_debug("%s: XLOUD_PARAM\n module_id=%u, param_id=%u, param_length=%u", + __func__, module_id, param_id, param_length); + break; + + default: + pr_err("%s: Invalid effect id(%u)\n", __func__, effect_id); + rc = -EINVAL; + goto invalid_config; + }; + + + num = (param_length + ONCE_PARAM_SIZE - 1) / ONCE_PARAM_SIZE; + for (i = 0; i < num; i++) { + len = MIN(ONCE_PARAM_SIZE, param_length + - (i * ONCE_PARAM_SIZE)); + divide = (struct param_divide *)tuning_param_d; + divide->divide_num = (uint8_t)num; + divide->no = (uint8_t)i; + divide->offset = (uint16_t)(i * ONCE_PARAM_SIZE); + memcpy(tuning_param_d + sizeof(struct param_divide), + tuning_param_s + divide->offset, len); + len += sizeof(struct param_divide); + param_data = (struct param_hdr_v1 *)param; + param_data->module_id = module_id; + param_data->param_id = param_id; + param_data->param_size = (uint16_t)len; + param_data->reserved = 0; + len += sizeof(struct param_hdr_v1); + ret = q6asm_set_pp_params( + (struct audio_client *)client, NULL, + (char *)param, len); + if (ret < 0) { + pr_err("%s: set-param failed ret[%d]\n", __func__, ret); + rc = -EINVAL; + goto invalid_config; + } + } + +invalid_config: + kfree(param); + return rc; +} + +/* SOMC effect control end */ + int q6asm_mmap_apr_dereg(void) { int c; diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/sound/soc/msm/qdsp6v2/rtac.c index 9fd71d798ad5121f2f60b482165c3d643e19b868..92d1bc1dd5e0094bb064b95772ddfc8adc740745 100644 --- a/sound/soc/msm/qdsp6v2/rtac.c +++ b/sound/soc/msm/qdsp6v2/rtac.c @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include #include diff --git a/sound/soc/msm/qdsp6v2/sony-hweffect-params.c b/sound/soc/msm/qdsp6v2/sony-hweffect-params.c new file mode 100644 index 0000000000000000000000000000000000000000..5bcfaee915d78178181a5b59976f2d822767aba9 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/sony-hweffect-params.c @@ -0,0 +1,238 @@ +/* + * Author: Yoshio Yamamoto yoshio.xa.yamamoto@sonymobile.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; either version 2 + * of the License, or (at your option) any later version. + */ +/* + * Copyright (C) 2014 Sony Mobile Communications 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. + */ + +#include +#include +#include +#include +#include +#include "sound/sony-hweffect-params.h" + + +static struct s_force_tuning_params s_force_coefs; + +static struct clearphase_hp_tuning_params clearphase_hp_coefs; + +static struct clearphase_sp_tuning_params clearphase_sp_coefs; + +static struct xloud_tuning_params xloud_coefs; + +static uint32_t s_force_coefs_size; + +static uint32_t clearphase_hp_coefs_size; + +static uint32_t clearphase_sp_coefs_size; + +static uint32_t xloud_coefs_size; + +void *sony_hweffect_params_getparam(unsigned int effect_id) +{ + void *hw_params; + + pr_debug("%s: effect id %u\n", __func__, effect_id); + + hw_params = NULL; + + switch (effect_id) { + case SFORCE_PARAM: + hw_params = (void *)&s_force_coefs; + break; + + case CLEARPHASE_HP_PARAM: + hw_params = (void *)&clearphase_hp_coefs; + break; + + case CLEARPHASE_SP_PARAM: + hw_params = (void *)&clearphase_sp_coefs; + break; + + case XLOUD_PARAM: + hw_params = (void *)&xloud_coefs; + break; + + default: + break; + }; + + return hw_params; +} + +int sony_hweffect_params_getparam_size(long *values) +{ + if (values == NULL) { + pr_err("%s: invald pointer", __func__); + return -EINVAL; + } + + values[0] = (long)s_force_coefs_size; + values[1] = (long)clearphase_hp_coefs_size; + values[2] = (long)clearphase_sp_coefs_size; + values[3] = (long)xloud_coefs_size; + + return 0; +} + +static int sony_hweffect_params_open(struct inode *inode, struct file *f) +{ + pr_debug("%s\n", __func__); + s_force_coefs_size = 0; + clearphase_hp_coefs_size = 0; + clearphase_sp_coefs_size = 0; + xloud_coefs_size = 0; + + return 0; +} + +static long sony_hweffect_params_ioctl_shared(struct file *f, + unsigned int cmd, void __user *arg) +{ + pr_debug("%s %d\n", __func__, cmd); + + switch (cmd) { + case SFORCE_PARAM: + if (copy_from_user(&s_force_coefs, (void *)arg, + sizeof(struct sforce_param_data))) { + pr_err("%s: fail to copy memory handle!\n", __func__); + return -EFAULT; + } + s_force_coefs_size = sizeof(s_force_coefs); + break; + + case CLEARPHASE_HP_PARAM: + if (copy_from_user(&clearphase_hp_coefs, (void *)arg, + sizeof(struct clearphase_hp_param_data))) { + pr_err("%s: fail to copy memory handle!\n", __func__); + return -EFAULT; + } + clearphase_hp_coefs_size = sizeof(clearphase_hp_coefs); + break; + + case CLEARPHASE_SP_PARAM: + if (copy_from_user(&clearphase_sp_coefs, (void *)arg, + sizeof(struct clearphase_sp_param_data))) { + pr_err("%s: fail to copy memory handle!\n", __func__); + return -EFAULT; + } + clearphase_sp_coefs_size = sizeof(clearphase_sp_coefs); + break; + + case XLOUD_PARAM: + if (copy_from_user(&xloud_coefs, (void *)arg, + sizeof(struct xloud_param_data))) { + pr_err("%s: fail to copy memory handle!\n", __func__); + return -EFAULT; + } + xloud_coefs_size = sizeof(xloud_coefs); + break; + + default: + return -EFAULT; + }; + return 0; +} + +static long sony_hweffect_params_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + int result = 0; + + pr_debug("%s %d\n", __func__, cmd); + + result = sony_hweffect_params_ioctl_shared(f, cmd, (void __user *)arg); + + return result; +} + +#ifdef CONFIG_COMPAT +#define SFORCE_PARAM_32 \ + _IOW(SONYEFFECT_HW_PARAMS_IOCTL_MAGIC, 0, compat_uptr_t) +#define CLEARPHASE_HP_PARAM_32 \ + _IOW(SONYEFFECT_HW_PARAMS_IOCTL_MAGIC, 1, compat_uptr_t) +#define CLEARPHASE_SP_PARAM_32 \ + _IOW(SONYEFFECT_HW_PARAMS_IOCTL_MAGIC, 2, compat_uptr_t) +#define XLOUD_PARAM_32 \ + _IOW(SONYEFFECT_HW_PARAMS_IOCTL_MAGIC, 3, compat_uptr_t) + +static long sony_hweffect_params_compat_ioctl(struct file *f, + unsigned int cmd, unsigned long arg) +{ + unsigned int cmd64; + int result = 0; + + switch (cmd) { + case SFORCE_PARAM_32: + cmd64 = SFORCE_PARAM; + break; + case CLEARPHASE_HP_PARAM_32: + cmd64 = CLEARPHASE_HP_PARAM; + break; + case CLEARPHASE_SP_PARAM_32: + cmd64 = CLEARPHASE_SP_PARAM; + break; + case XLOUD_PARAM_32: + cmd64 = XLOUD_PARAM; + break; + default: + result = -EINVAL; + pr_err("%s: Invalid IOCTL, command = %d!\n", + __func__, cmd); + break; + } + + if (!result) + result = sony_hweffect_params_ioctl_shared(f, cmd64, + compat_ptr(arg)); + + return result; +} +#else +#define sony_hweffect_params_compat_ioctl NULL +#endif + +static int sony_hweffect_params_release(struct inode *inode, struct file *f) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static const struct file_operations sony_hweffect_params_fops = { + .owner = THIS_MODULE, + .open = sony_hweffect_params_open, + .release = sony_hweffect_params_release, + .unlocked_ioctl = sony_hweffect_params_ioctl, + .compat_ioctl = sony_hweffect_params_compat_ioctl, +}; + +struct miscdevice sony_hweffect_params_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sony_hweffect_params", + .fops = &sony_hweffect_params_fops, +}; + +static int __init sony_hweffect_params_init(void) +{ + pr_info("%s\n", __func__); + misc_register(&sony_hweffect_params_misc); + return 0; +} + +static void __exit sony_hweffect_params_exit(void) +{ + pr_info("%s\n", __func__); +} + +module_init(sony_hweffect_params_init); +module_exit(sony_hweffect_params_exit); diff --git a/sound/usb/card.c b/sound/usb/card.c index 86096532dfbadd1c7a29eacbd81cf228764a959a..14a8faba64e274753d091faeeecd30fcc0ecdd00 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -31,6 +31,11 @@ * this type *correctly*. SB extigy looks as if it supports, but it's * indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream). */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2018 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #include @@ -681,7 +686,6 @@ static int usb_audio_probe(struct usb_interface *intf, chip->num_interfaces++; usb_set_intfdata(intf, chip); intf->needs_remote_wakeup = 1; - usb_enable_autosuspend(chip->dev); atomic_dec(&chip->active); mutex_unlock(®ister_mutex); return 0; diff --git a/tools/testing/selftests/size/get_size.c b/tools/testing/selftests/size/get_size.c index 2d1af7cca4631ff5c582cfe7c2f78a416a1d6c06..e5d56ccd858efed16078de8b80d6346d81de8f7a 100644 --- a/tools/testing/selftests/size/get_size.c +++ b/tools/testing/selftests/size/get_size.c @@ -20,6 +20,13 @@ * has large external dependencies) by implementing it's own * number output and print routines, and using __builtin_strlen() */ +/* + * Copyright (C) 2014 Sony Mobile Communications 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. + */ #include #include diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c index 5a6016224bb9c9c4bbe5f7b28c37738f0edec5a3..719311b5e873ce3cc05b13e09b270008758425a1 100644 --- a/tools/vm/page-types.c +++ b/tools/vm/page-types.c @@ -18,6 +18,11 @@ * * Authors: Wu Fengguang */ +/* + * NOTE: This file has been modified by Sony Mobile Communications Inc. + * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, + * and licensed under the license of the file. + */ #define _FILE_OFFSET_BITS 64 #define _GNU_SOURCE