diff options
131 files changed, 7102 insertions, 1487 deletions
diff --git a/Documentation/devicetree/bindings/input/hbtp-input.txt b/Documentation/devicetree/bindings/input/hbtp-input.txt index f422d75e037e..ad28952c5e7e 100644 --- a/Documentation/devicetree/bindings/input/hbtp-input.txt +++ b/Documentation/devicetree/bindings/input/hbtp-input.txt @@ -20,6 +20,19 @@ Optional properties: - qcom,dig-vtg-max : Digital supply maximum voltage in uV - qcom,display-resolution : Display resolution - maxX, maxY - qcom,use-scale : boolean, enables the scaling for touch coordinates + - pinctrl-names : defines pinctrl names + "pmx_ts_active" : Required pinctrl name. + This should specify active config of TS RST gpio + "pmx_ts_suspend" : Required pinctrl name + This should specify suspend config of TS RST gpio + "ddic_rst_active" : Required pinctrl name + This should specify active config of DDIC RST gpio + "ddic_rst_suspend" : Required pinctrl name + This should specify suspend config of DDIC RST gpio + - pinctrl-0 : pin control to be used for TS active config + - pinctrl-1 : pin control to be used for TS suspend config + - pinctrl-2 : pin control to be used for DDIC active config + - pinctrl-3 : pin control to be used for DDIC suspend config Optional properties if qcom,use-scale DT property is defined: - qcom,def-maxx : default X-resolution of the touch panel. @@ -29,6 +42,20 @@ Optional properties if qcom,use-scale DT property is defined: - qcom,des-maxy : desired Y-resolution of the touch panel. (Above two properties should be defined in pairs only) +Optional Properties if pinctrl names are defined: + - qcom,pmx-ts-on-seq-delay-us : unsigned integer type for + delay after active TS RST gpio is changed + - qcom,fb-resume-delay-us : unsigned integer type for + delay in early resume framebuffer callback + - qcom,ddic-rst-on-seq-delay-us : array of unsigned integer type for + delay of each step in series of DDIC RST gpio control + +Optional Properties if qcom,afe-vtg and qcom,dig-vtg are defined + - qcom,afe-power-on-delay-us : unsigned integer type for + delay between turning on analog and digital power supply + - qcom,afe-power-off-delay-us : unsigned integer type for + delay between turning off digital and analog power supply + Example: &soc { hbtp { @@ -47,5 +74,16 @@ Example: qcom,default-max-y = <1920>; qcom,desired-max-x = <720>; qcom,desired-max-y = <1280>; + pinctrl-names = "pmx_ts_active","pmx_ts_suspend", + "ddic_rst_active", "ddic_rst_suspend"; + pinctrl-0 = <&ts_rst_active>; + pinctrl-1 = <&ts_rst_suspend>; + pinctrl-2 = <&ddic_rst_active>; + pinctrl-3 = <&ddic_rst_suspend>; + qcom,pmx-ts-on-seq-delay-us = <1000>; + qcom,ddic-rst-on-seq-delay-us = <10000 10000 10000 10000>; + qcom,fb-resume-delay-us = <90000>; + qcom,afe-power-on-delay-us = <1000>; + qcom,afe-power-off-delay-us = <6>; }; }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt index ff8fb76166a3..bde115155eba 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt @@ -57,6 +57,12 @@ Optional properties: - goodix,cfg-data5 : Touch screen controller config data group 5. Ask vendor to provide that. - goodix,fw-name : Touch screen controller firmware file name. + - goodix,slide-wakeup : To specify slide-wakeup property is enabled or not. + - goodix,dbl-clk-wakeup : To specify dbl-clk-wakeup property is enabled or not. + - goodix,change-x2y : To specify change-x2y property is enabled or not. + - goodix,driver-send-cfg : To specify driver-send-cfg property is enabled or not. + - goodix,have-touch-key : To specify have-touch-key property is enabled or not. + - goodix,with-pen : To specify with-pen property is enabled or not. Example: i2c@f9927000 { goodix@5d { @@ -92,5 +98,7 @@ i2c@f9927000 { FF FF FF FF FF FF FF 22 22 22 22 22 22 FF 07 01]; goodix,fw_name = "gtp_fw.bin"; + goodix,have-touch-key; + goodix,driver-send-cfg; }; }; diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt index 8389fe57898a..ebbcfe5b2fd0 100644 --- a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt @@ -12,6 +12,8 @@ Required properties: - reg-names : names associated with base addresses. It should be "qpnp-wled-ctrl-base", "qpnp-wled-sink-base", "qpnp-wled-ibb-base", "qpnp-wled-lab-base". +- qcom,pmic-revid : phandle of PMIC revid module. This is used to + identify the PMIC subtype. Optional properties for WLED: - interrupts : Specifies the interrupts associated with WLED. The available @@ -25,8 +27,21 @@ Optional properties for WLED: are "wled1", "wled2", "wled3", "wled4" and "auto". default is "auto". - qcom,vref-mv : maximum reference voltage in mv. default is 350. - qcom,switch-freq-khz : switch frequency in khz. default is 800. -- qcom,ovp-mv : over voltage protection value in mv. default is 17800. -- qcom,ilim-ma : maximum current limiter in ma. default is 980. +- qcom,ovp-mv : Over voltage protection threshold in mV. Default is + 29500. Supported values are: + - 31000, 29500, 19400, 17800 for pmi8994/8952/8996. + - 31100, 29600, 19600, 18100 for pmicobalt/pm2falcon. + Should only be used if qcom,disp-type-amoled is not + specified. +- qcom,ilim-ma : Current limit threshold in mA. + For pmi8994/8952/8996, default value for LCD is 980mA + and AMOLED is 385mA. + Supported values are: + - 105, 385, 660, 980, 1150, 1420, 1700, 1980. + For pmicobalt/pm2falcon, default value for LCD is + 970mA and AMOLED is 620mA. + Supported values are: + - 105, 280, 450, 620, 970, 1150, 1300, 1500. - qcom,boost-duty-ns : maximum boost duty cycle in ns. default is 104. - qcom,mod-freq-khz : modulation frequency in khz. default is 9600. - qcom,dim-mode : dimming mode. supporting dimming modes are "analog", @@ -54,7 +69,13 @@ Optional properties if 'qcom,disp-type-amoled' is mentioned in DT: - qcom,loop-ea-gm : control the gm for gm stage in control loop. default is 3. - qcom,loop-comp-res-kohm : control to select the compensation resistor in kohm. default is 320. - qcom,vref-psm-mv : reference psm voltage in mv. default for amoled is 450. -- qcom,avdd-trim-steps-from-center : The number of steps to trim the OVP threshold voltage. The possible values can be between -7 to 8. +- qcom,avdd-mode-spmi: Boolean property to enable AMOLED_VOUT programming via SPMI. If not specified, + AMOLED_VOUT is programmed via S-wire. This can be specified only for newer + PMICs like pmicobalt/pm2falcon. +- qcom,avdd-target-voltage-mv: The voltage required for AMOLED_VOUT. Accepted values are in the range + of 5650 to 7900 in steps of 150. Default value is 7600. Unit is in mV. + For old revisions, accepted values are: 7900, 7600, 7300, 6400, 6100, + 5800. Example: qcom,leds@d800 { diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt index a3dc40936e8e..54a3c5689b9c 100644 --- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt +++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt @@ -79,6 +79,7 @@ Optional properties: current issue. - qcom,qdsp6v61-1-1: Boolean- Present if the qdsp version is v61 1.1 - qcom,qdsp6v62-1-2: Boolean- Present if the qdsp version is v62 1.2 +- qcom,qdsp6v62-1-5: Boolean- Present if the qdsp version is v62 1.5 - qcom,mx-spike-wa: Boolean- Present if we need to assert QDSP6 I/O clamp, memory wordline clamp, and compiler memory clamp during MSS restart. - qcom,qdsp6v56-1-10: Boolean- Present if the qdsp version is v56 1.10 diff --git a/Documentation/devicetree/bindings/power/reset/reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt new file mode 100644 index 000000000000..de34f27d509e --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/reboot-mode.txt @@ -0,0 +1,25 @@ +Generic reboot mode core map driver + +This driver get reboot mode arguments and call the write +interface to store the magic value in special register +or ram. Then the bootloader can read it and take different +action according to the argument stored. + +All mode properties are vendor specific, it is a indication to tell +the bootloader what to do when the system reboots, and should be named +as mode-xxx = <magic> (xxx is mode name, magic should be a none-zero value). + +For example modes common on Android platform: +- mode-normal: Normal reboot mode, system reboot with command "reboot". +- mode-recovery: Android Recovery mode, it is a mode to format the device or update a new image. +- mode-bootloader: Android fastboot mode, it's a mode to re-flash partitions on the Android based device. +- mode-loader: A bootloader mode, it's a mode used to download image on Rockchip platform, + usually used in development. + +Example: + reboot-mode { + mode-normal = <BOOT_NORMAL>; + mode-recovery = <BOOT_RECOVERY>; + mode-bootloader = <BOOT_FASTBOOT>; + mode-loader = <BOOT_BL_DOWNLOAD>; + } diff --git a/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt b/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt new file mode 100644 index 000000000000..f7ce1d8af04a --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/syscon-reboot-mode.txt @@ -0,0 +1,35 @@ +SYSCON reboot mode driver + +This driver gets reboot mode magic value form reboot-mode driver +and stores it in a SYSCON mapped register. Then the bootloader +can read it and take different action according to the magic +value stored. + +This DT node should be represented as a sub-node of a "syscon", "simple-mfd" +node. + +Required properties: +- compatible: should be "syscon-reboot-mode" +- offset: offset in the register map for the storage register (in bytes) + +Optional property: +- mask: bits mask of the bits in the register to store the reboot mode magic value, + default set to 0xffffffff if missing. + +The rest of the properties should follow the generic reboot-mode description +found in reboot-mode.txt + +Example: + pmu: pmu@20004000 { + compatible = "rockchip,rk3066-pmu", "syscon", "simple-mfd"; + reg = <0x20004000 0x100>; + + reboot-mode { + compatible = "syscon-reboot-mode"; + offset = <0x40>; + mode-normal = <BOOT_NORMAL>; + mode-recovery = <BOOT_RECOVERY>; + mode-bootloader = <BOOT_FASTBOOT>; + mode-loader = <BOOT_BL_DOWNLOAD>; + }; + }; diff --git a/Documentation/devicetree/bindings/qbt1000/qbt1000.txt b/Documentation/devicetree/bindings/qbt1000/qbt1000.txt new file mode 100644 index 000000000000..c9861e4948f9 --- /dev/null +++ b/Documentation/devicetree/bindings/qbt1000/qbt1000.txt @@ -0,0 +1,54 @@ +Qualcomm Technologies, Inc. QBT1000 Specific Bindings + +QBT is a fingerprint sensor ASIC capable of performing fingerprint image scans +and detecting finger presence on the sensor using programmable firmware. + +======================= +Required Node Structure +======================= + +- compatible + Usage: required + Value type: <string> + Definition: "qcom,qbt1000". + +- clock-names + Usage: required + Value type: <stringlist> + Definition: List of clock names that need to be voted on/off. + +- clocks + Usage: required + Value type: <prop_encoded-array> + Definition: Property pair that represents the clock controller and the clock + id. This in combination with the clock-name is used to obtain + the handle for the clock that needs to be voted on/off. + +- clock-frequency + Usage: required + Value type: <u32> + Definition: Frequency of clock in Hz. + +- qcom,ipc-gpio + Usage: required + Value type: <phandle> + Definition: phandle for GPIO to be used for IPC. + +- qcom,finger-detect-gpio + Usage: required + Value type: <phandle> + Definition: phandle for GPIO to be used for finger detect. + +======= +Example +======= + +qcom,qbt1000 { + compatible = "qcom,qbt1000"; + clock-names = "core", "iface"; + clocks = <&clock_gcc clk_gcc_blsp2_qup6_spi_apps_clk>, + <&clock_gcc clk_gcc_blsp2_ahb_clk>; + clock-frequency = <15000000>; + qcom,ipc-gpio = <&tlmm 121 0>; + qcom,finger-detect-gpio = <&pmcobalt_gpios 2 0>; +}; diff --git a/Documentation/devicetree/bindings/qdsp/msm-ssc-sensors.txt b/Documentation/devicetree/bindings/qdsp/msm-ssc-sensors.txt index 165153dde994..ea671a1ff14a 100644 --- a/Documentation/devicetree/bindings/qdsp/msm-ssc-sensors.txt +++ b/Documentation/devicetree/bindings/qdsp/msm-ssc-sensors.txt @@ -1,11 +1,16 @@ -* msm-ssc-sensors +Qualcomm Technologies, Inc. SSC Driver + +msm-ssc-sensors driver implements the mechanism that allows to load SLPI firmware images. Required properties: - - compatible: "qcom,msm-ssc-sensors" + - compatible: This must be "qcom,msm-ssc-sensors" + - qcom,firmware-name: SLPI firmware name, must be "slpi_v1" or "slpi_v2" Example: + The following for msmcobalt version 1. - qcom,msm-ssc-sensors { - compatible = "qcom,msm-ssc-sensors"; - }; + qcom,msm-ssc-sensors { + compatible = "qcom,msm-ssc-sensors"; + qcom,firmware-name = "slpi_v1"; + }; diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index bffa21a06462..f884e0ece735 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -114,6 +114,7 @@ dtb-$(CONFIG_ARCH_MSMCOBALT) += msmcobalt-sim.dtb \ msmcobalt-v2-qrd.dtb \ msmcobalt-qrd-skuk.dtb \ msmcobalt-qrd-vr1.dtb \ + msmcobalt-v2-qrd-vr1.dtb \ apqcobalt-mtp.dtb \ apqcobalt-cdp.dtb \ apqcobalt-v2-mtp.dtb \ diff --git a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi index ba27e3912ee6..eb8a7a98b6b4 100644 --- a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi +++ b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi @@ -371,13 +371,84 @@ qcom,adsp-state = <0>; }; + qcom,msm-dai-tdm-pri-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37120>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36864>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + dai_pri_tdm_rx_0: qcom,msm-dai-q6-tdm-pri-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36864>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-pri-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37121>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36865>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + dai_pri_tdm_tx_0: qcom,msm-dai-q6-tdm-pri-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36865>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-sec-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37136>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36880>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + dai_sec_tdm_rx_0: qcom,msm-dai-q6-tdm-sec-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36880>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-sec-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37137>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36881>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + dai_sec_tdm_tx_0: qcom,msm-dai-q6-tdm-sec-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36881>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + qcom,msm-dai-tdm-tert-rx { compatible = "qcom,msm-dai-tdm"; qcom,msm-cpudai-tdm-group-id = <37152>; qcom,msm-cpudai-tdm-group-num-ports = <1>; qcom,msm-cpudai-tdm-group-port-id = <36896>; qcom,msm-cpudai-tdm-clk-rate = <1536000>; - pinctrl-names = "default", "sleep"; dai_tert_tdm_rx_0: qcom,msm-dai-q6-tdm-tert-rx-0 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36896>; @@ -396,7 +467,6 @@ qcom,msm-cpudai-tdm-group-num-ports = <1>; qcom,msm-cpudai-tdm-group-port-id = <36897 >; qcom,msm-cpudai-tdm-clk-rate = <1536000>; - pinctrl-names = "default", "sleep"; dai_tert_tdm_tx_0: qcom,msm-dai-q6-tdm-tert-tx-0 { compatible = "qcom,msm-dai-q6-tdm"; qcom,msm-cpudai-tdm-dev-id = <36897 >; @@ -408,4 +478,40 @@ qcom,msm-cpudai-tdm-data-align = <0>; }; }; + + qcom,msm-dai-tdm-quat-rx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37168>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36912>; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + dai_quat_tdm_rx_0: qcom,msm-dai-q6-tdm-quat-rx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36912>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; + + qcom,msm-dai-tdm-quat-tx { + compatible = "qcom,msm-dai-tdm"; + qcom,msm-cpudai-tdm-group-id = <37169>; + qcom,msm-cpudai-tdm-group-num-ports = <1>; + qcom,msm-cpudai-tdm-group-port-id = <36913 >; + qcom,msm-cpudai-tdm-clk-rate = <1536000>; + dai_quat_tdm_tx_0: qcom,msm-dai-q6-tdm-quat-tx-0 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36913 >; + qcom,msm-cpudai-tdm-sync-mode = <1>; + qcom,msm-cpudai-tdm-sync-src = <1>; + qcom,msm-cpudai-tdm-data-out = <0>; + qcom,msm-cpudai-tdm-invert-sync = <1>; + qcom,msm-cpudai-tdm-data-delay = <1>; + qcom,msm-cpudai-tdm-data-align = <0>; + }; + }; }; diff --git a/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi b/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi index 399892f52b6f..41589d02f6fc 100644 --- a/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi @@ -236,8 +236,8 @@ qcom,fdbk-output = "auto"; qcom,vref-mv = <350>; qcom,switch-freq-khz = <800>; - qcom,ovp-mv = <29500>; - qcom,ilim-ma = <980>; + qcom,ovp-mv = <29600>; + qcom,ilim-ma = <970>; qcom,boost-duty-ns = <26>; qcom,mod-freq-khz = <9600>; qcom,dim-mode = "hybrid"; @@ -248,6 +248,7 @@ qcom,en-phase-stag; qcom,led-strings-list = [00 01 02]; qcom,en-ext-pfet-sc-pro; + qcom,pmic-revid = <&pm2falcon_revid>; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/msm-pmcobalt-rpm-regulator.dtsi b/arch/arm/boot/dts/qcom/msm-pmcobalt-rpm-regulator.dtsi index 7a8e71d14291..7243a6b1d6d4 100644 --- a/arch/arm/boot/dts/qcom/msm-pmcobalt-rpm-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmcobalt-rpm-regulator.dtsi @@ -592,7 +592,7 @@ regulator-bob { compatible = "qcom,rpm-smd-regulator"; - regulator-name = "pmcobalt_bob"; + regulator-name = "pmicobalt_bob"; qcom,set = <3>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi index 96dfed8464e9..bba70329c819 100644 --- a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi @@ -537,6 +537,7 @@ qcom,en-phase-stag; qcom,led-strings-list = [00 01 02 03]; qcom,en-ext-pfet-sc-pro; + qcom,pmic-revid = <&pmi8994_revid>; }; pmi8994_haptics: qcom,haptic@c000 { diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index 28d230dfb6bf..a5243aff4282 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -596,8 +596,8 @@ qcom,fdbk-output = "auto"; qcom,vref-mv = <350>; qcom,switch-freq-khz = <800>; - qcom,ovp-mv = <29500>; - qcom,ilim-ma = <980>; + qcom,ovp-mv = <29600>; + qcom,ilim-ma = <970>; qcom,boost-duty-ns = <26>; qcom,mod-freq-khz = <9600>; qcom,dim-mode = "hybrid"; @@ -608,6 +608,7 @@ qcom,en-phase-stag; qcom,led-strings-list = [00 01 02 03]; qcom,en-ext-pfet-sc-pro; + qcom,pmic-revid = <&pmicobalt_revid>; }; pmicobalt_haptics: qcom,haptic@c000 { @@ -740,7 +741,7 @@ qcom,led-mask = <3>; qcom,default-led-trigger = "switch0_trigger"; reg0 { - regulator-name = "pmcobalt_bob"; + regulator-name = "pmicobalt_bob"; max-voltage-uv = <3600000>; }; }; @@ -751,7 +752,7 @@ qcom,led-mask = <4>; qcom,default-led-trigger = "switch1_trigger"; reg0 { - regulator-name = "pmcobalt_bob"; + regulator-name = "pmicobalt_bob"; max-voltage-uv = <3600000>; }; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi index 3681f3d34b0c..ca12a520c2aa 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi @@ -108,7 +108,10 @@ <&incall_music_2_rx>, <&sb_5_rx>, <&sb_6_rx>, <&sb_7_rx>, <&sb_7_tx>, <&sb_8_tx>, <&usb_audio_rx>, <&usb_audio_tx>, - <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>; + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>, + <&dai_sec_tdm_rx_0>, <&dai_sec_tdm_tx_0>, + <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>, + <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_tx_0>; asoc-cpu-names = "msm-dai-q6-hdmi.8", "msm-dai-q6-dp.24608", "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", @@ -127,7 +130,10 @@ "msm-dai-q6-dev.16396", "msm-dai-q6-dev.16398", "msm-dai-q6-dev.16399", "msm-dai-q6-dev.16401", "msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673", - "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897"; + "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36865", + "msm-dai-q6-tdm.36880", "msm-dai-q6-tdm.36881", + "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897", + "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36913"; asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>; asoc-codec-names = "msm-stub-codec.1", "msm-ext-disp-audio-codec-rx"; @@ -213,7 +219,10 @@ <&incall_music_2_rx>, <&sb_5_rx>, <&sb_6_rx>, <&sb_7_rx>, <&sb_7_tx>, <&sb_8_tx>, <&usb_audio_rx>, <&usb_audio_tx>, - <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>; + <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>, + <&dai_sec_tdm_rx_0>, <&dai_sec_tdm_tx_0>, + <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>, + <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_tx_0>; asoc-cpu-names = "msm-dai-q6-hdmi.8", "msm-dai-q6-dp.24608", "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1", "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", @@ -232,7 +241,10 @@ "msm-dai-q6-dev.16396", "msm-dai-q6-dev.16398", "msm-dai-q6-dev.16399", "msm-dai-q6-dev.16401", "msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673", - "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897"; + "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36865", + "msm-dai-q6-tdm.36880", "msm-dai-q6-tdm.36881", + "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897", + "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36913"; asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>; asoc-codec-names = "msm-stub-codec.1", "msm-ext-disp-audio-codec-rx"; @@ -371,6 +383,7 @@ qcom,cdc-slim-ifd-elemental-addr = [00 00 A0 01 17 02]; qcom,cdc-dmic-sample-rate = <4800000>; qcom,cdc-mad-dmic-rate = <600000>; + qcom,cdc-ecpp-dmic-rate = <1200000>; }; wcd934x_cdc: tavil_codec { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi index b64cd8eea19b..6afd593f9610 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi @@ -158,6 +158,8 @@ qcom,mdss-dsi-panel-timings = [00 17 05 05 09 0f 05 06 04 03 04 00]; qcom,mdss-dsi-t-clk-post = <0x06>; qcom,mdss-dsi-t-clk-pre = <0x23>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; }; &dsi_sharp_1080_cmd { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi index 5f89985db0a3..58471f6d0fd1 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi @@ -62,7 +62,7 @@ 50000000 100000000 200000000>; qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104"; - cd-gpios = <&tlmm 95 0x1>; + cd-gpios = <&tlmm 95 0x0>; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts index ee6a58a41b4f..e53912071502 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts @@ -21,32 +21,3 @@ compatible = "qcom,msmcobalt-qrd", "qcom,msmcobalt", "qcom,qrd"; qcom,board-id = <0x02000b 0x80>; }; - -&soc { - sound-tavil { - qcom,model = "msmcobalt-qvr-tavil-snd-card"; - qcom,audio-routing = - "RX_BIAS", "MCLK", - "MADINPUT", "MCLK", - "AMIC2", "MIC BIAS2", - "MIC BIAS2", "Headset Mic", - "DMIC0", "MIC BIAS1", - "MIC BIAS1", "Digital Mic0", - "DMIC1", "MIC BIAS1", - "MIC BIAS1", "Digital Mic1", - "DMIC2", "MIC BIAS3", - "MIC BIAS3", "Digital Mic2", - "DMIC4", "MIC BIAS4", - "MIC BIAS4", "Digital Mic4", - "SpkrLeft IN", "SPK1 OUT"; - - qcom,msm-mbhc-hphl-swh = <1>; - /delete-property/ qcom,us-euro-gpios; - /delete-property/ qcom,hph-en0-gpio; - /delete-property/ qcom,hph-en0-gpio; - - qcom,wsa-max-devs = <1>; - qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0213>; - qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft"; - }; -}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi index f8069856f3d8..f0607ac3a34a 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi @@ -99,4 +99,31 @@ debounce-interval = <15>; }; }; + + sound-tavil { + qcom,model = "msmcobalt-qvr-tavil-snd-card"; + qcom,audio-routing = + "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", + "DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "DMIC1", "MIC BIAS1", + "MIC BIAS1", "Digital Mic1", + "DMIC2", "MIC BIAS3", + "MIC BIAS3", "Digital Mic2", + "DMIC4", "MIC BIAS4", + "MIC BIAS4", "Digital Mic4", + "SpkrLeft IN", "SPK1 OUT"; + + qcom,msm-mbhc-hphl-swh = <1>; + /delete-property/ qcom,us-euro-gpios; + /delete-property/ qcom,hph-en0-gpio; + /delete-property/ qcom,hph-en0-gpio; + + qcom,wsa-max-devs = <1>; + qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0213>; + qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft"; + }; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi index 7d5509f0016c..682ea8a260ef 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi @@ -264,7 +264,7 @@ }; &pmicobalt_wled { - qcom,led-strings-list = [00 01]; + qcom,led-strings-list = [01 02]; }; &dsi_dual_nt35597_video { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi index 2a61cccad273..bb72cf3a0d2c 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi @@ -501,7 +501,7 @@ }; pmicobalt_bob_pin1: regulator-bob-pin1 { compatible = "qcom,rpm-smd-regulator"; - regulator-name = "pmcobalt_bob_pin1"; + regulator-name = "pmicobalt_bob_pin1"; qcom,set = <3>; regulator-min-microvolt = <3312000>; regulator-max-microvolt = <3600000>; @@ -509,7 +509,7 @@ }; pmicobalt_bob_pin2: regulator-bob-pin2 { compatible = "qcom,rpm-smd-regulator"; - regulator-name = "pmcobalt_bob_pin2"; + regulator-name = "pmicobalt_bob_pin2"; qcom,set = <3>; regulator-min-microvolt = <3312000>; regulator-max-microvolt = <3600000>; @@ -517,7 +517,7 @@ }; pmicobalt_bob_pin3: regulator-bob-pin3 { compatible = "qcom,rpm-smd-regulator"; - regulator-name = "pmcobalt_bob_pin3"; + regulator-name = "pmicobalt_bob_pin3"; qcom,set = <3>; regulator-min-microvolt = <3312000>; regulator-max-microvolt = <3600000>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-qrd-vr1.dts b/arch/arm/boot/dts/qcom/msmcobalt-v2-qrd-vr1.dts new file mode 100644 index 000000000000..15dd2d550b31 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-qrd-vr1.dts @@ -0,0 +1,23 @@ +/* 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. + */ + + +/dts-v1/; + +#include "msmcobalt-v2.dtsi" +#include "msmcobalt-qrd-vr1.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM COBALT V2 VR1 Board"; + compatible = "qcom,msmcobalt-qrd", "qcom,msmcobalt", "qcom,qrd"; + qcom,board-id = <0x02000b 0x80>; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi index cde74aa2866d..beecee843778 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi @@ -237,6 +237,84 @@ regulator-max-microvolt = <1056000>; }; +&pcie0 { + qcom,phy-sequence = <0x804 0x01 0x00 + 0x034 0x14 0x00 + 0x138 0x30 0x00 + 0x048 0x0f 0x00 + 0x15c 0x06 0x00 + 0x090 0x01 0x00 + 0x088 0x20 0x00 + 0x0f0 0x00 0x00 + 0x0f8 0x01 0x00 + 0x0f4 0xc9 0x00 + 0x11c 0xff 0x00 + 0x120 0x3f 0x00 + 0x164 0x01 0x00 + 0x154 0x00 0x00 + 0x148 0x0a 0x00 + 0x05C 0x19 0x00 + 0x038 0x90 0x00 + 0x0b0 0x82 0x00 + 0x0c0 0x03 0x00 + 0x0bc 0x55 0x00 + 0x0b8 0x55 0x00 + 0x0a0 0x00 0x00 + 0x09c 0x0d 0x00 + 0x098 0x04 0x00 + 0x13c 0x00 0x00 + 0x060 0x08 0x00 + 0x068 0x16 0x00 + 0x070 0x34 0x00 + 0x15c 0x06 0x00 + 0x138 0x33 0x00 + 0x03c 0x02 0x00 + 0x040 0x07 0x00 + 0x080 0x04 0x00 + 0x0dc 0x00 0x00 + 0x0d8 0x3f 0x00 + 0x00c 0x09 0x00 + 0x010 0x01 0x00 + 0x01c 0x40 0x00 + 0x020 0x01 0x00 + 0x014 0x02 0x00 + 0x018 0x00 0x00 + 0x024 0x7e 0x00 + 0x028 0x15 0x00 + 0x244 0x02 0x00 + 0x2a4 0x12 0x00 + 0x260 0x10 0x00 + 0x28c 0x06 0x00 + 0x504 0x03 0x00 + 0x500 0x1c 0x00 + 0x50c 0x14 0x00 + 0x4d4 0x0a 0x00 + 0x4d8 0x04 0x00 + 0x4dc 0x1a 0x00 + 0x434 0x4b 0x00 + 0x414 0x04 0x00 + 0x40c 0x04 0x00 + 0x4f8 0x00 0x00 + 0x4fc 0x80 0x00 + 0x51c 0x40 0x00 + 0x444 0x71 0x00 + 0x43c 0x40 0x00 + 0x854 0x04 0x00 + 0x62c 0x52 0x00 + 0x9ac 0x00 0x00 + 0x8a0 0x01 0x00 + 0x9e0 0x00 0x00 + 0x9dc 0x01 0x00 + 0x9a8 0x00 0x00 + 0x8a4 0x01 0x00 + 0x8a8 0x73 0x00 + 0x9d8 0x99 0x00 + 0x9b0 0x03 0x00 + 0x804 0x03 0x00 + 0x800 0x00 0x00 + 0x808 0x03 0x00>; +}; + &apc0_cpr { compatible = "qcom,cprh-msmcobalt-v2-kbss-regulator"; qcom,cpr-corner-switch-delay-time = <1042>; @@ -862,3 +940,7 @@ lanes-per-direction = <2>; }; + +&ssc_sensors { + qcom,firmware-name = "slpi_v2"; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index b21e2dcf8c1a..60b514c7ca20 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -2810,9 +2810,10 @@ }; }; - qcom,msm-ssc-sensors { + ssc_sensors: qcom,msm-ssc-sensors { compatible = "qcom,msm-ssc-sensors"; status = "ok"; + qcom,firmware-name = "slpi_v1"; }; dcc: dcc@10b3000 { diff --git a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi index 3826b00bf09e..2f1ef974811e 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi @@ -138,6 +138,14 @@ <&funnel_in0_out_funnel_merg>; }; }; + port@2 { + reg = <1>; + funnel_merg_in_funnel_in1:endpoint { + slave-mode; + remote-endpoint = + <&funnel_in1_out_funnel_merg>; + }; + }; }; }; @@ -183,6 +191,167 @@ }; }; + funnel_in1: funnel@6042000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6042000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-in1"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_in1_out_funnel_merg: endpoint { + remote-endpoint = + <&funnel_merg_in_funnel_in1>; + }; + }; + port@5 { + reg = <6>; + funnel_in1_in_funnel_apss_merg: endpoint { + slave-mode; + remote-endpoint = + <&funnel_apss_merg_out_funnel_in1>; + }; + }; + }; + }; + + funnel_apss_merg: funnel@7b70000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x7b70000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-apss-merg"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_apss_merg_out_funnel_in1: endpoint { + remote-endpoint = + <&funnel_in1_in_funnel_apss_merg>; + }; + }; + port@1 { + reg = <0>; + funnel_apss_merg_in_funnel_apss: endpoint { + slave-mode; + remote-endpoint = + <&funnel_apss_out_funnel_apss_merg>; + }; + }; + }; + }; + + funnel_apss: funnel@7b60000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x7b60000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-apss"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_apss_out_funnel_apss_merg: endpoint { + remote-endpoint = + <&funnel_apss_merg_in_funnel_apss>; + }; + }; + port@1 { + reg = <0>; + funnel_apss_in_etm0: endpoint { + slave-mode; + remote-endpoint = + <&etm0_out_funnel_apss>; + }; + }; + port@2 { + reg = <1>; + funnel_apss_in_etm1: endpoint { + slave-mode; + remote-endpoint = + <&etm1_out_funnel_apss>; + }; + }; + port@3 { + reg = <2>; + funnel_apss_in_etm2: endpoint { + slave-mode; + remote-endpoint = + <&etm2_out_funnel_apss>; + }; + }; + port@4 { + reg = <3>; + funnel_apss_in_etm3: endpoint { + slave-mode; + remote-endpoint = + <&etm3_out_funnel_apss>; + }; + }; + port@5 { + reg = <4>; + funnel_apss_in_etm4: endpoint { + slave-mode; + remote-endpoint = + <&etm4_out_funnel_apss>; + }; + }; + port@6 { + reg = <5>; + funnel_apss_in_etm5: endpoint { + slave-mode; + remote-endpoint = + <&etm5_out_funnel_apss>; + }; + }; + port@7 { + reg = <6>; + funnel_apss_in_etm6: endpoint { + slave-mode; + remote-endpoint = + <&etm6_out_funnel_apss>; + }; + }; + port@8 { + reg = <7>; + funnel_apss_in_etm7: endpoint { + slave-mode; + remote-endpoint = + <&etm7_out_funnel_apss>; + }; + }; + }; + }; + stm: stm@6002000 { compatible = "arm,primecell"; arm,primecell-periphid = <0x0003b962>; @@ -204,6 +373,166 @@ }; }; + etm0: etm@7840000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b95d>; + + reg = <0x7840000 0x1000>; + cpu = <&CPU0>; + + coresight-name = "coresight-etm0"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port{ + etm0_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm0>; + }; + }; + }; + + etm1: etm@7940000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b95d>; + + reg = <0x7940000 0x1000>; + cpu = <&CPU1>; + + coresight-name = "coresight-etm1"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port{ + etm1_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm1>; + }; + }; + }; + + etm2: etm@7a40000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b95d>; + + reg = <0x7a40000 0x1000>; + cpu = <&CPU2>; + + coresight-name = "coresight-etm2"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port{ + etm2_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm2>; + }; + }; + }; + + etm3: etm@7b40000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b95d>; + + reg = <0x7b40000 0x1000>; + cpu = <&CPU3>; + + coresight-name = "coresight-etm3"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port{ + etm3_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm3>; + }; + }; + }; + + etm4: etm@7c40000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b95d>; + + reg = <0x7c40000 0x1000>; + cpu = <&CPU4>; + + coresight-name = "coresight-etm4"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port{ + etm4_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm4>; + }; + }; + }; + + etm5: etm@7d40000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b95d>; + + reg = <0x7d40000 0x1000>; + cpu = <&CPU5>; + + coresight-name = "coresight-etm5"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port{ + etm5_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm5>; + }; + }; + }; + + etm6: etm@7e40000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b95d>; + + reg = <0x7e40000 0x1000>; + cpu = <&CPU6>; + + coresight-name = "coresight-etm6"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port{ + etm6_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm6>; + }; + }; + }; + + etm7: etm@7f40000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b95d>; + + reg = <0x7f40000 0x1000>; + cpu = <&CPU7>; + + coresight-name = "coresight-etm7"; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + port{ + etm7_out_funnel_apss: endpoint { + remote-endpoint = <&funnel_apss_in_etm7>; + }; + }; + }; + cti0: cti@6010000 { compatible = "arm,coresight-cti"; reg = <0x6010000 0x1000>; @@ -396,6 +725,110 @@ clock-names = "core_clk", "core_a_clk"; }; + cti_cpu0: cti@7820000 { + compatible = "arm,coresight-cti"; + reg = <0x7820000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu0"; + cpu = <&CPU0>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti_cpu1: cti@7920000 { + compatible = "arm,coresight-cti"; + reg = <0x7920000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu1"; + cpu = <&CPU1>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti_cpu2: cti@7a20000 { + compatible = "arm,coresight-cti"; + reg = <0x7a20000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu2"; + cpu = <&CPU2>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti_cpu3: cti@7b20000 { + compatible = "arm,coresight-cti"; + reg = <0x7b20000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu3"; + cpu = <&CPU3>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti_cpu4: cti@7c20000 { + compatible = "arm,coresight-cti"; + reg = <0x7c20000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu4"; + cpu = <&CPU4>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti_cpu5: cti@7d20000 { + compatible = "arm,coresight-cti"; + reg = <0x7d20000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu5"; + cpu = <&CPU5>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti_cpu6: cti@7e20000 { + compatible = "arm,coresight-cti"; + reg = <0x7e20000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu6"; + cpu = <&CPU6>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti_cpu7: cti@7f20000 { + compatible = "arm,coresight-cti"; + reg = <0x7f20000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti-cpu7"; + cpu = <&CPU7>; + + clocks = <&clock_rpmcc RPM_QDSS_CLK>, + <&clock_rpmcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + funnel_qatb: funnel@6005000 { compatible = "arm,primecell"; arm,primecell-periphid = <0x0003b908>; diff --git a/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi index e93067e3697c..b43fae954ec2 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi @@ -173,6 +173,29 @@ gpios = <&smp2pgpio_smp2p_5_out 0 0>; }; + /* ssr - inbound entry from mss */ + smp2pgpio_ssr_smp2p_1_in: qcom,smp2pgpio-ssr-smp2p-1-in { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "slave-kernel"; + qcom,remote-pid = <1>; + qcom,is-inbound; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + /* ssr - outbound entry to mss */ + smp2pgpio_ssr_smp2p_1_out: qcom,smp2pgpio-ssr-smp2p-1-out { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "master-kernel"; + qcom,remote-pid = <1>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + /* ssr - inbound entry from lpass */ smp2pgpio_ssr_smp2p_2_in: qcom,smp2pgpio-ssr-smp2p-2-in { compatible = "qcom,smp2pgpio"; diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi index 8e8c407734eb..7a5d83a12bfa 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi @@ -447,7 +447,7 @@ <0x10b4000 0x800>; reg-names = "dcc-base", "dcc-ram-base"; - clocks = <&clock_rpmcc RPM_QDSS_CLK>; + clocks = <&clock_rpmcc GCC_DCC_AHB_CLK>; clock-names = "dcc_clk"; }; @@ -754,6 +754,59 @@ qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_5_out 0 0>; status = "ok"; }; + + pil_modem: qcom,mss@4080000 { + compatible = "qcom,pil-q6v55-mss"; + reg = <0x4080000 0x100>, + <0x1f63000 0x008>, + <0x1f65000 0x008>, + <0x1f64000 0x008>, + <0x4180000 0x040>, + <0x00179000 0x004>; + reg-names = "qdsp6_base", "halt_q6", "halt_modem", + "halt_nc", "rmb_base", "restart_reg"; + + clocks = <&clock_rpmcc RPM_XO_CLK_SRC>, + <&clock_gcc GCC_MSS_CFG_AHB_CLK>, + <&clock_gcc GCC_BIMC_MSS_Q6_AXI_CLK>, + <&clock_gcc GCC_BOOT_ROM_AHB_CLK>, + <&clock_gcc GPLL0_OUT_MSSCC>, + <&clock_gcc GCC_MSS_SNOC_AXI_CLK>, + <&clock_gcc GCC_MSS_MNOC_BIMC_AXI_CLK>, + <&clock_rpmcc RPM_QDSS_CLK>; + clock-names = "xo", "iface_clk", "bus_clk", + "mem_clk", "gpll0_mss_clk", "snoc_axi_clk", + "mnoc_axi_clk", "qdss_clk"; + qcom,proxy-clock-names = "xo", "qdss_clk"; + qcom,active-clock-names = "iface_clk", "bus_clk", "mem_clk", + "gpll0_mss_clk", "snoc_axi_clk", + "mnoc_axi_clk"; + + interrupts = <0 448 1>; + vdd_cx-supply = <&pmfalcon_s3b_level>; + vdd_cx-voltage = <RPM_SMD_REGULATOR_LEVEL_TURBO>; + vdd_mx-supply = <&pmfalcon_s5b_level>; + vdd_mx-uV = <RPM_SMD_REGULATOR_LEVEL_TURBO>; + qcom,firmware-name = "modem"; + qcom,pil-self-auth; + qcom,sysmon-id = <0>; + qcom,ssctl-instance-id = <0x12>; + qcom,override-acc; + qcom,qdsp6v62-1-5; + memory-region = <&modem_fw_mem>; + qcom,mem-protect-id = <0xF>; + + /* GPIO inputs from mss */ + qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>; + qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_1_in 1 0>; + qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>; + qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_1_in 3 0>; + qcom,gpio-shutdown-ack = <&smp2pgpio_ssr_smp2p_1_in 7 0>; + + /* GPIO output to mss */ + qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>; + status = "ok"; + }; }; #include "msmfalcon-ion.dtsi" diff --git a/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi b/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi index 1a72414de094..b458bbb08dc2 100644 --- a/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi +++ b/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi @@ -134,6 +134,29 @@ gpios = <&smp2pgpio_sleepstate_2_out 0 0>; }; + /* ssr - inbound entry from mss */ + smp2pgpio_ssr_smp2p_1_in: qcom,smp2pgpio-ssr-smp2p-1-in { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "slave-kernel"; + qcom,remote-pid = <1>; + qcom,is-inbound; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + /* ssr - outbound entry to mss */ + smp2pgpio_ssr_smp2p_1_out: qcom,smp2pgpio-ssr-smp2p-1-out { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "master-kernel"; + qcom,remote-pid = <1>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + /* ssr - inbound entry from lpass */ smp2pgpio_ssr_smp2p_2_in: qcom,smp2pgpio-ssr-smp2p-2-in { compatible = "qcom,smp2pgpio"; diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi index 083c14af7839..807c40fcc46e 100644 --- a/arch/arm/boot/dts/qcom/msmtriton.dtsi +++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi @@ -609,6 +609,59 @@ memory-region = <&venus_fw_mem>; status = "ok"; }; + + pil_modem: qcom,mss@4080000 { + compatible = "qcom,pil-q6v55-mss"; + reg = <0x4080000 0x100>, + <0x1f63000 0x008>, + <0x1f65000 0x008>, + <0x1f64000 0x008>, + <0x4180000 0x040>, + <0x00179000 0x004>; + reg-names = "qdsp6_base", "halt_q6", "halt_modem", + "halt_nc", "rmb_base", "restart_reg"; + + clocks = <&clock_rpmcc RPM_XO_CLK_SRC>, + <&clock_gcc GCC_MSS_CFG_AHB_CLK>, + <&clock_gcc GCC_BIMC_MSS_Q6_AXI_CLK>, + <&clock_gcc GCC_BOOT_ROM_AHB_CLK>, + <&clock_gcc GPLL0_OUT_MSSCC>, + <&clock_gcc GCC_MSS_SNOC_AXI_CLK>, + <&clock_gcc GCC_MSS_MNOC_BIMC_AXI_CLK>, + <&clock_rpmcc RPM_QDSS_CLK>; + clock-names = "xo", "iface_clk", "bus_clk", + "mem_clk", "gpll0_mss_clk", "snoc_axi_clk", + "mnoc_axi_clk", "qdss_clk"; + qcom,proxy-clock-names = "xo", "qdss_clk"; + qcom,active-clock-names = "iface_clk", "bus_clk", "mem_clk", + "gpll0_mss_clk", "snoc_axi_clk", + "mnoc_axi_clk"; + + interrupts = <0 448 1>; + vdd_cx-supply = <&pmfalcon_s3b_level>; + vdd_cx-voltage = <RPM_SMD_REGULATOR_LEVEL_TURBO>; + vdd_mx-supply = <&pmfalcon_s5b_level>; + vdd_mx-uV = <RPM_SMD_REGULATOR_LEVEL_TURBO>; + qcom,firmware-name = "modem"; + qcom,pil-self-auth; + qcom,sysmon-id = <0>; + qcom,ssctl-instance-id = <0x12>; + qcom,override-acc; + qcom,qdsp6v62-1-5; + memory-region = <&modem_fw_mem>; + qcom,mem-protect-id = <0xF>; + + /* GPIO inputs from mss */ + qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>; + qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_1_in 1 0>; + qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>; + qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_1_in 3 0>; + qcom,gpio-shutdown-ack = <&smp2pgpio_ssr_smp2p_1_in 7 0>; + + /* GPIO output to mss */ + qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>; + status = "ok"; + }; }; #include "msmtriton-ion.dtsi" diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig index 48507eebe9f3..8be2b187a524 100644 --- a/arch/arm/configs/msmcortex_defconfig +++ b/arch/arm/configs/msmcortex_defconfig @@ -442,6 +442,7 @@ CONFIG_QCOM_SCM=y CONFIG_QCOM_WATCHDOG_V2=y CONFIG_QCOM_MEMORY_DUMP_V2=y CONFIG_ICNSS=y +CONFIG_ICNSS_DEBUG=y CONFIG_MSM_GLADIATOR_ERP_V2=y CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y CONFIG_MSM_GLADIATOR_HANG_DETECT=y diff --git a/arch/arm/configs/msmfalcon_defconfig b/arch/arm/configs/msmfalcon_defconfig index 069603eefe48..0788a03ed219 100644 --- a/arch/arm/configs/msmfalcon_defconfig +++ b/arch/arm/configs/msmfalcon_defconfig @@ -451,6 +451,7 @@ CONFIG_QCOM_SCM=y CONFIG_QCOM_WATCHDOG_V2=y CONFIG_QCOM_MEMORY_DUMP_V2=y CONFIG_ICNSS=y +CONFIG_ICNSS_DEBUG=y CONFIG_MSM_GLADIATOR_ERP_V2=y CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y CONFIG_MSM_GLADIATOR_HANG_DETECT=y diff --git a/arch/arm/mach-qcom/board-falcon.c b/arch/arm/mach-qcom/board-falcon.c index e9374050b2cb..aec16886308d 100644 --- a/arch/arm/mach-qcom/board-falcon.c +++ b/arch/arm/mach-qcom/board-falcon.c @@ -31,3 +31,20 @@ DT_MACHINE_START(MSMFALCON_DT, .init_machine = msmfalcon_init, .dt_compat = msmfalcon_dt_match, MACHINE_END + +static const char *msmtriton_dt_match[] __initconst = { + "qcom,msmtriton", + "qcom,apqtriton", + NULL +}; + +static void __init msmtriton_init(void) +{ + board_dt_populate(NULL); +} + +DT_MACHINE_START(MSMTRITON_DT, + "Qualcomm Technologies, Inc. MSM TRITON (Flattened Device Tree)") + .init_machine = msmtriton_init, + .dt_compat = msmtriton_dt_match, +MACHINE_END diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild index 861ed8acdc3f..d49e164867e1 100644 --- a/arch/arm64/include/asm/Kbuild +++ b/arch/arm64/include/asm/Kbuild @@ -2,7 +2,6 @@ generic-y += bug.h generic-y += bugs.h -generic-y += checksum.h generic-y += clkdev.h generic-y += cputime.h generic-y += current.h diff --git a/arch/arm64/include/asm/checksum.h b/arch/arm64/include/asm/checksum.h new file mode 100644 index 000000000000..09f65339d66d --- /dev/null +++ b/arch/arm64/include/asm/checksum.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef __ASM_CHECKSUM_H +#define __ASM_CHECKSUM_H + +#include <linux/types.h> + +static inline __sum16 csum_fold(__wsum csum) +{ + u32 sum = (__force u32)csum; + sum += (sum >> 16) | (sum << 16); + return ~(__force __sum16)(sum >> 16); +} +#define csum_fold csum_fold + +static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl) +{ + __uint128_t tmp; + u64 sum; + + tmp = *(const __uint128_t *)iph; + iph += 16; + ihl -= 4; + tmp += ((tmp >> 64) | (tmp << 64)); + sum = tmp >> 64; + do { + sum += *(const u32 *)iph; + iph += 4; + } while (--ihl); + + sum += ((sum >> 32) | (sum << 32)); + return csum_fold(sum >> 32); +} +#define ip_fast_csum ip_fast_csum + +#include <asm-generic/checksum.h> + +#endif /* __ASM_CHECKSUM_H */ diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c index 1317ddaa3c23..b05b999fbbdc 100644 --- a/drivers/bluetooth/bluetooth-power.c +++ b/drivers/bluetooth/bluetooth-power.c @@ -46,6 +46,7 @@ static const struct of_device_id bt_power_match_table[] = { static struct bluetooth_power_platform_data *bt_power_pdata; static struct platform_device *btpdev; static bool previous; +static int pwr_state; struct class *bt_class; static int bt_major; @@ -636,6 +637,7 @@ static int bt_power_probe(struct platform_device *pdev) memcpy(bt_power_pdata, pdev->dev.platform_data, sizeof(struct bluetooth_power_platform_data)); + pwr_state = 0; } else { BT_PWR_ERR("Failed to get platform data"); goto free_pdata; @@ -680,7 +682,7 @@ int bt_register_slimdev(struct device *dev) static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int ret; + int ret, pwr_cntrl = 0; switch (cmd) { case BT_CMD_SLIM_TEST: @@ -692,6 +694,18 @@ static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) bt_power_pdata->slim_dev->platform_data ); break; + case BT_CMD_PWR_CTRL: + pwr_cntrl = (int)arg; + BT_PWR_ERR("BT_CMD_PWR_CTRL pwr_cntrl:%d", pwr_cntrl); + if (pwr_state != pwr_cntrl) { + ret = bluetooth_power(pwr_cntrl); + if (!ret) + pwr_state = pwr_cntrl; + } else { + BT_PWR_ERR("BT chip state is already :%d no change d\n" + , pwr_state); + } + break; default: return -EINVAL; } @@ -711,6 +725,7 @@ static struct platform_driver bt_power_driver = { static const struct file_operations bt_dev_fops = { .owner = THIS_MODULE, .unlocked_ioctl = bt_ioctl, + .compat_ioctl = bt_ioctl, }; static int __init bluetooth_power_init(void) @@ -733,7 +748,7 @@ static int __init bluetooth_power_init(void) if (device_create(bt_class, NULL, MKDEV(bt_major, 0), - NULL, "pintest") == NULL) { + NULL, "btpower") == NULL) { BTFMSLIM_ERR("failed to allocate char dev\n"); goto chrdev_unreg; } diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 67c1207d35be..ef8aaac6e0a2 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1070,7 +1070,7 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) int idx = list[i].pgidx; if (map->attr & FASTRPC_ATTR_NOVA) { - offset = (uintptr_t)lpra[i].buf.pv; + offset = 0; } else { down_read(¤t->mm->mmap_sem); VERIFY(err, NULL != (vma = find_vma(current->mm, diff --git a/drivers/clk/msm/clock-gcc-cobalt.c b/drivers/clk/msm/clock-gcc-cobalt.c index 46e791b3cb99..f2a3f7402f67 100644 --- a/drivers/clk/msm/clock-gcc-cobalt.c +++ b/drivers/clk/msm/clock-gcc-cobalt.c @@ -1707,17 +1707,6 @@ static struct branch_clk gcc_gpu_bimc_gfx_clk = { }, }; -static struct branch_clk gcc_gpu_bimc_gfx_src_clk = { - .cbcr_reg = GCC_GPU_BIMC_GFX_SRC_CBCR, - .has_sibling = 1, - .base = &virt_base, - .c = { - .dbg_name = "gcc_gpu_bimc_gfx_src_clk", - .ops = &clk_ops_branch, - CLK_INIT(gcc_gpu_bimc_gfx_src_clk.c), - }, -}; - static struct branch_clk gcc_gpu_cfg_ahb_clk = { .cbcr_reg = GCC_GPU_CFG_AHB_CBCR, .has_sibling = 1, @@ -1731,17 +1720,6 @@ static struct branch_clk gcc_gpu_cfg_ahb_clk = { }, }; -static struct branch_clk gcc_gpu_snoc_dvm_gfx_clk = { - .cbcr_reg = GCC_GPU_SNOC_DVM_GFX_CBCR, - .has_sibling = 1, - .base = &virt_base, - .c = { - .dbg_name = "gcc_gpu_snoc_dvm_gfx_clk", - .ops = &clk_ops_branch, - CLK_INIT(gcc_gpu_snoc_dvm_gfx_clk.c), - }, -}; - static struct branch_clk gcc_gpu_iref_clk = { .cbcr_reg = GCC_GPU_IREF_EN, .has_sibling = 1, @@ -2454,7 +2432,6 @@ static struct mux_clk gcc_debug_mux = { { &gcc_mss_mnoc_bimc_axi_clk.c, 0x0120 }, { &gcc_mss_snoc_axi_clk.c, 0x0123 }, { &gcc_gpu_cfg_ahb_clk.c, 0x013b }, - { &gcc_gpu_bimc_gfx_src_clk.c, 0x013e }, { &gcc_gpu_bimc_gfx_clk.c, 0x013f }, { &gcc_qspi_ahb_clk.c, 0x0156 }, { &gcc_qspi_ref_clk.c, 0x0157 }, @@ -2649,9 +2626,7 @@ static struct clk_lookup msm_clocks_gcc_cobalt[] = { CLK_LIST(gcc_gp2_clk), CLK_LIST(gcc_gp3_clk), CLK_LIST(gcc_gpu_bimc_gfx_clk), - CLK_LIST(gcc_gpu_bimc_gfx_src_clk), CLK_LIST(gcc_gpu_cfg_ahb_clk), - CLK_LIST(gcc_gpu_snoc_dvm_gfx_clk), CLK_LIST(gcc_gpu_iref_clk), CLK_LIST(gcc_hmss_ahb_clk), CLK_LIST(gcc_hmss_dvm_bus_clk), diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 612e7b37a8d0..bc982c9bfa71 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -662,8 +662,6 @@ static struct clk_hw *msmfalcon_clks[] = { [RPM_AGGR2_NOC_A_CLK] = &msmfalcon_aggre2_noc_a_clk.hw, [RPM_CNOC_CLK] = &msmfalcon_cnoc_clk.hw, [RPM_CNOC_A_CLK] = &msmfalcon_cnoc_a_clk.hw, - [RPM_MMAXI_CLK] = &msmfalcon_mmssnoc_axi_clk.hw, - [RPM_MMAXI_A_CLK] = &msmfalcon_mmssnoc_axi_a_clk.hw, [RPM_IPA_CLK] = &msmfalcon_ipa_clk.hw, [RPM_IPA_A_CLK] = &msmfalcon_ipa_a_clk.hw, [RPM_CE1_CLK] = &msmfalcon_ce1_clk.hw, @@ -684,6 +682,8 @@ static struct clk_hw *msmfalcon_clks[] = { [RPM_LN_BB_CLK3_PIN_AO] = &msmfalcon_ln_bb_clk3_pin_ao.hw, [RPM_CNOC_PERIPH_CLK] = &msmfalcon_cnoc_periph_clk.hw, [RPM_CNOC_PERIPH_A_CLK] = &msmfalcon_cnoc_periph_a_clk.hw, + [MMSSNOC_AXI_CLK] = &msmfalcon_mmssnoc_axi_clk.hw, + [MMSSNOC_AXI_A_CLK] = &msmfalcon_mmssnoc_axi_a_clk.hw, /* Voter Clocks */ [BIMC_MSMBUS_CLK] = &bimc_msmbus_clk.hw, diff --git a/drivers/crypto/msm/qce.c b/drivers/crypto/msm/qce.c index 7ddbb1938400..4cf95b90a2df 100644 --- a/drivers/crypto/msm/qce.c +++ b/drivers/crypto/msm/qce.c @@ -1,6 +1,6 @@ /* Qualcomm Crypto Engine driver. * - * Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2010-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 @@ -1962,8 +1962,8 @@ int qce_aead_req(void *handle, struct qce_req *q_req) else q_req->cryptlen = areq->cryptlen - authsize; - if ((q_req->cryptlen > ULONG_MAX - ivsize) || - (q_req->cryptlen + ivsize > ULONG_MAX - areq->assoclen)) { + if ((q_req->cryptlen > UINT_MAX - ivsize) || + (q_req->cryptlen + ivsize > UINT_MAX - areq->assoclen)) { pr_err("Integer overflow on total aead req length.\n"); return -EINVAL; } diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 9940f7a7c2b7..6160aa567fbf 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -589,11 +589,21 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device) struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); struct adreno_irq *irq_params = gpudev->irq; irqreturn_t ret = IRQ_NONE; - unsigned int status = 0, tmp; + unsigned int status = 0, tmp, int_bit; int i; adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS, &status); + /* + * Clear all the interrupt bits but ADRENO_INT_RBBM_AHB_ERROR. Because + * even if we clear it here, it will stay high until it is cleared + * in its respective handler. Otherwise, the interrupt handler will + * fire again. + */ + int_bit = ADRENO_INT_BIT(adreno_dev, ADRENO_INT_RBBM_AHB_ERROR); + adreno_writereg(adreno_dev, ADRENO_REG_RBBM_INT_CLEAR_CMD, + status & ~int_bit); + /* Loop through all set interrupts and call respective handlers */ for (tmp = status; tmp != 0;) { i = fls(tmp) - 1; @@ -612,9 +622,14 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device) gpudev->irq_trace(adreno_dev, status); - if (status) + /* + * Clear ADRENO_INT_RBBM_AHB_ERROR bit after this interrupt has been + * cleared in its respective handler + */ + if (status & int_bit) adreno_writereg(adreno_dev, ADRENO_REG_RBBM_INT_CLEAR_CMD, - status); + int_bit); + return ret; } diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 0f3403cb0095..a2af26c81f50 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -198,6 +198,10 @@ struct adreno_gpudev; /* Time to allow preemption to complete (in ms) */ #define ADRENO_PREEMPT_TIMEOUT 10000 +#define ADRENO_INT_BIT(a, _bit) (((a)->gpucore->gpudev->int_bits) ? \ + (adreno_get_int(a, _bit) < 0 ? 0 : \ + BIT(adreno_get_int(a, _bit))) : 0) + /** * enum adreno_preempt_states * ADRENO_PREEMPT_NONE: No preemption is scheduled @@ -574,6 +578,11 @@ enum adreno_regs { ADRENO_REG_REGISTER_MAX, }; +enum adreno_int_bits { + ADRENO_INT_RBBM_AHB_ERROR, + ADRENO_INT_BITS_MAX, +}; + /** * adreno_reg_offsets: Holds array of register offsets * @offsets: Offset array of size defined by enum adreno_regs @@ -589,6 +598,7 @@ struct adreno_reg_offsets { #define ADRENO_REG_UNUSED 0xFFFFFFFF #define ADRENO_REG_SKIP 0xFFFFFFFE #define ADRENO_REG_DEFINE(_offset, _reg) [_offset] = _reg +#define ADRENO_INT_DEFINE(_offset, _val) ADRENO_REG_DEFINE(_offset, _val) /* * struct adreno_vbif_data - Describes vbif register value pair @@ -726,6 +736,7 @@ struct adreno_gpudev { * so define them in the structure and use them as variables. */ const struct adreno_reg_offsets *reg_offsets; + unsigned int *const int_bits; const struct adreno_ft_perf_counters *ft_perf_counters; unsigned int ft_perf_counters_count; @@ -1101,6 +1112,23 @@ static inline unsigned int adreno_getreg(struct adreno_device *adreno_dev, return gpudev->reg_offsets->offsets[offset_name]; } +/* + * adreno_get_int() - Returns the offset value of an interrupt bit from + * the interrupt bit array in the gpudev node + * @adreno_dev: Pointer to the the adreno device + * @bit_name: The interrupt bit enum whose bit is returned + */ +static inline unsigned int adreno_get_int(struct adreno_device *adreno_dev, + enum adreno_int_bits bit_name) +{ + struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); + + if (bit_name >= ADRENO_INT_BITS_MAX) + return -ERANGE; + + return gpudev->int_bits[bit_name]; +} + /** * adreno_gpu_fault() - Return the current state of the GPU * @adreno_dev: A pointer to the adreno_device to query diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c index 97e71464c2df..3f5a9c6318f6 100644 --- a/drivers/gpu/msm/adreno_a3xx.c +++ b/drivers/gpu/msm/adreno_a3xx.c @@ -1425,6 +1425,10 @@ static struct adreno_coresight a3xx_coresight = { .groups = a3xx_coresight_groups, }; +static unsigned int a3xx_int_bits[ADRENO_INT_BITS_MAX] = { + ADRENO_INT_DEFINE(ADRENO_INT_RBBM_AHB_ERROR, A3XX_INT_RBBM_AHB_ERROR), +}; + /* Register offset defines for A3XX */ static unsigned int a3xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_RAM_WADDR, A3XX_CP_ME_RAM_WADDR), @@ -1853,6 +1857,7 @@ int a3xx_microcode_load(struct adreno_device *adreno_dev, struct adreno_gpudev adreno_a3xx_gpudev = { .reg_offsets = &a3xx_reg_offsets, + .int_bits = a3xx_int_bits, .ft_perf_counters = a3xx_ft_perf_counters, .ft_perf_counters_count = ARRAY_SIZE(a3xx_ft_perf_counters), .perfcounters = &a3xx_perfcounters, diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c index bfbdb0e7ac1f..5ca04e522270 100644 --- a/drivers/gpu/msm/adreno_a4xx.c +++ b/drivers/gpu/msm/adreno_a4xx.c @@ -739,6 +739,10 @@ static void a4xx_err_callback(struct adreno_device *adreno_dev, int bit) } } +static unsigned int a4xx_int_bits[ADRENO_INT_BITS_MAX] = { + ADRENO_INT_DEFINE(ADRENO_INT_RBBM_AHB_ERROR, A4XX_INT_RBBM_AHB_ERROR), +}; + /* Register offset defines for A4XX, in order of enum adreno_regs */ static unsigned int a4xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_RAM_WADDR, A4XX_CP_ME_RAM_WADDR), @@ -1765,6 +1769,7 @@ static struct adreno_snapshot_data a4xx_snapshot_data = { struct adreno_gpudev adreno_a4xx_gpudev = { .reg_offsets = &a4xx_reg_offsets, + .int_bits = a4xx_int_bits, .ft_perf_counters = a4xx_ft_perf_counters, .ft_perf_counters_count = ARRAY_SIZE(a4xx_ft_perf_counters), .perfcounters = &a4xx_perfcounters, diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 2891940b8f5b..860f6d2925f1 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -2872,6 +2872,10 @@ static struct adreno_ft_perf_counters a5xx_ft_perf_counters[] = { {KGSL_PERFCOUNTER_GROUP_TSE, A5XX_TSE_INPUT_PRIM_NUM}, }; +static unsigned int a5xx_int_bits[ADRENO_INT_BITS_MAX] = { + ADRENO_INT_DEFINE(ADRENO_INT_RBBM_AHB_ERROR, A5XX_INT_RBBM_AHB_ERROR), +}; + /* Register offset defines for A5XX, in order of enum adreno_regs */ static unsigned int a5xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_CP_WFI_PEND_CTR, A5XX_CP_WFI_PEND_CTR), @@ -3504,6 +3508,7 @@ static struct adreno_coresight a5xx_coresight = { struct adreno_gpudev adreno_a5xx_gpudev = { .reg_offsets = &a5xx_reg_offsets, + .int_bits = a5xx_int_bits, .ft_perf_counters = a5xx_ft_perf_counters, .ft_perf_counters_count = ARRAY_SIZE(a5xx_ft_perf_counters), .coresight = &a5xx_coresight, diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c index 519e3db4d73e..f94ecf02d9cb 100644 --- a/drivers/input/misc/hbtp_input.c +++ b/drivers/input/misc/hbtp_input.c @@ -23,6 +23,12 @@ #include <linux/regulator/consumer.h> #include <uapi/linux/hbtp_input.h> #include "../input-compat.h" +#include <linux/ktime.h> +#include <linux/uaccess.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/delay.h> +#include <linux/completion.h> #if defined(CONFIG_FB) #include <linux/notifier.h> @@ -32,6 +38,10 @@ #define HBTP_INPUT_NAME "hbtp_input" #define DISP_COORDS_SIZE 2 +#define HBTP_PINCTRL_VALID_STATE_CNT (2) +#define HBTP_HOLD_DURATION_US (10) +#define HBTP_PINCTRL_DDIC_SEQ_NUM (4) + struct hbtp_data { struct platform_device *pdev; struct input_dev *input_dev; @@ -41,6 +51,20 @@ struct hbtp_data { #if defined(CONFIG_FB) struct notifier_block fb_notif; #endif + struct pinctrl *ts_pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + struct pinctrl_state *ddic_rst_state_active; + struct pinctrl_state *ddic_rst_state_suspend; + u32 ts_pinctrl_seq_delay; + u32 ddic_pinctrl_seq_delay[HBTP_PINCTRL_DDIC_SEQ_NUM]; + u32 fb_resume_seq_delay; + bool lcd_on; + bool power_suspended; + bool power_sync_enabled; + bool power_sig_enabled; + struct completion power_resume_sig; + struct completion power_suspend_sig; struct regulator *vcc_ana; struct regulator *vcc_dig; int afe_load_ua; @@ -59,31 +83,68 @@ struct hbtp_data { bool override_disp_coords; bool manage_afe_power_ana; bool manage_power_dig; + u32 power_on_delay; + u32 power_off_delay; + bool manage_pin_ctrl; }; static struct hbtp_data *hbtp; #if defined(CONFIG_FB) +static int hbtp_fb_suspend(struct hbtp_data *ts); +static int hbtp_fb_early_resume(struct hbtp_data *ts); +static int hbtp_fb_resume(struct hbtp_data *ts); +#endif + +#if defined(CONFIG_FB) static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { int blank; struct fb_event *evdata = data; struct hbtp_data *hbtp_data = - container_of(self, struct hbtp_data, fb_notif); - char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL}; + container_of(self, struct hbtp_data, fb_notif); - if (evdata && evdata->data && event == FB_EVENT_BLANK && - hbtp_data && hbtp_data->input_dev) { + if (evdata && evdata->data && hbtp_data && + (event == FB_EARLY_EVENT_BLANK || + event == FB_R_EARLY_EVENT_BLANK)) { blank = *(int *)(evdata->data); - if (blank == FB_BLANK_UNBLANK) - kobject_uevent_env(&hbtp_data->input_dev->dev.kobj, - KOBJ_ONLINE, envp); - else if (blank == FB_BLANK_POWERDOWN) - kobject_uevent_env(&hbtp_data->input_dev->dev.kobj, - KOBJ_OFFLINE, envp); + if (event == FB_EARLY_EVENT_BLANK) { + if (blank == FB_BLANK_UNBLANK) { + pr_debug("%s: receives EARLY_BLANK:UNBLANK\n", + __func__); + hbtp_data->lcd_on = true; + hbtp_fb_early_resume(hbtp_data); + } else if (blank == FB_BLANK_POWERDOWN) { + pr_debug("%s: receives EARLY_BLANK:POWERDOWN\n", + __func__); + hbtp_data->lcd_on = false; + } + } else if (event == FB_R_EARLY_EVENT_BLANK) { + if (blank == FB_BLANK_UNBLANK) { + pr_debug("%s: receives R_EARLY_BALNK:UNBLANK\n", + __func__); + hbtp_data->lcd_on = false; + hbtp_fb_suspend(hbtp_data); + } else if (blank == FB_BLANK_POWERDOWN) { + pr_debug("%s: receives R_EARLY_BALNK:POWERDOWN\n", + __func__); + hbtp_data->lcd_on = true; + } + } } + if (evdata && evdata->data && hbtp_data && + event == FB_EVENT_BLANK) { + blank = *(int *)(evdata->data); + if (blank == FB_BLANK_POWERDOWN) { + pr_debug("%s: receives BLANK:POWERDOWN\n", __func__); + hbtp_fb_suspend(hbtp_data); + } else if (blank == FB_BLANK_UNBLANK) { + pr_debug("%s: receives BLANK:UNBLANK\n", __func__); + hbtp_fb_resume(hbtp_data); + } + } return 0; } #endif @@ -111,6 +172,8 @@ static int hbtp_input_release(struct inode *inode, struct file *file) return -ENOTTY; } hbtp->count--; + if (hbtp->power_sig_enabled) + hbtp->power_sig_enabled = false; mutex_unlock(&hbtp->mutex); return 0; @@ -278,6 +341,14 @@ static int hbtp_pdev_power_on(struct hbtp_data *hbtp, bool on) return ret; } } + + if (hbtp->power_on_delay) { + pr_debug("%s: power-on-delay = %u\n", __func__, + hbtp->power_on_delay); + usleep_range(hbtp->power_on_delay, + hbtp->power_on_delay + HBTP_HOLD_DURATION_US); + } + if (hbtp->vcc_dig) { ret = reg_set_load_check(hbtp->vcc_dig, hbtp->dig_load_ua); @@ -299,17 +370,171 @@ static int hbtp_pdev_power_on(struct hbtp_data *hbtp, bool on) return 0; reg_off: - if (hbtp->vcc_ana) { - reg_set_load_check(hbtp->vcc_ana, 0); - regulator_disable(hbtp->vcc_ana); - } if (hbtp->vcc_dig) { reg_set_load_check(hbtp->vcc_dig, 0); regulator_disable(hbtp->vcc_dig); } + + if (hbtp->power_off_delay) { + pr_debug("%s: power-off-delay = %u\n", __func__, + hbtp->power_off_delay); + usleep_range(hbtp->power_off_delay, + hbtp->power_off_delay + HBTP_HOLD_DURATION_US); + } + + if (hbtp->vcc_ana) { + reg_set_load_check(hbtp->vcc_ana, 0); + regulator_disable(hbtp->vcc_ana); + } return 0; } +static int hbtp_gpio_select(struct hbtp_data *data, bool on) +{ + struct pinctrl_state *pins_state; + int ret = 0; + + pins_state = on ? data->gpio_state_active : data->gpio_state_suspend; + if (!IS_ERR_OR_NULL(pins_state)) { + ret = pinctrl_select_state(data->ts_pinctrl, pins_state); + if (ret) { + dev_err(&data->pdev->dev, + "can not set %s pins\n", + on ? "ts_active" : "ts_suspend"); + return ret; + } + + if (on) { + if (data->ts_pinctrl_seq_delay) { + usleep_range(data->ts_pinctrl_seq_delay, + data->ts_pinctrl_seq_delay + + HBTP_HOLD_DURATION_US); + dev_dbg(&data->pdev->dev, "ts_pinctrl_seq_delay = %u\n", + data->ts_pinctrl_seq_delay); + } + } + } else { + dev_warn(&data->pdev->dev, + "not a valid '%s' pinstate\n", + on ? "ts_active" : "ts_suspend"); + return ret; + } + + return ret; +} + +static int hbtp_ddic_rst_select(struct hbtp_data *data, bool on) +{ + struct pinctrl_state *active, *suspend; + int ret = 0; + + active = data->ddic_rst_state_active; + if (IS_ERR_OR_NULL(active)) { + dev_warn(&data->pdev->dev, + "not a valid ddic_rst_active pinstate\n"); + return ret; + } + + suspend = data->ddic_rst_state_suspend; + if (IS_ERR_OR_NULL(suspend)) { + dev_warn(&data->pdev->dev, + "not a valid ddic_rst_suspend pinstate\n"); + return ret; + } + + if (on) { + if (data->ddic_pinctrl_seq_delay[0]) { + usleep_range(data->ddic_pinctrl_seq_delay[0], + data->ddic_pinctrl_seq_delay[0] + + HBTP_HOLD_DURATION_US); + dev_dbg(&data->pdev->dev, "ddic_seq_delay[0] = %u\n", + data->ddic_pinctrl_seq_delay[0]); + } + + ret = pinctrl_select_state(data->ts_pinctrl, active); + if (ret) { + dev_err(&data->pdev->dev, + "can not set ddic_rst_active pins\n"); + return ret; + } + if (data->ddic_pinctrl_seq_delay[1]) { + usleep_range(data->ddic_pinctrl_seq_delay[1], + data->ddic_pinctrl_seq_delay[1] + + HBTP_HOLD_DURATION_US); + dev_dbg(&data->pdev->dev, "ddic_seq_delay[1] = %u\n", + data->ddic_pinctrl_seq_delay[1]); + } + ret = pinctrl_select_state(data->ts_pinctrl, suspend); + if (ret) { + dev_err(&data->pdev->dev, + "can not set ddic_rst_suspend pins\n"); + return ret; + } + + if (data->ddic_pinctrl_seq_delay[2]) { + usleep_range(data->ddic_pinctrl_seq_delay[2], + data->ddic_pinctrl_seq_delay[2] + + HBTP_HOLD_DURATION_US); + dev_dbg(&data->pdev->dev, "ddic_seq_delay[2] = %u\n", + data->ddic_pinctrl_seq_delay[2]); + } + + ret = pinctrl_select_state(data->ts_pinctrl, active); + if (ret) { + dev_err(&data->pdev->dev, + "can not set ddic_rst_active pins\n"); + return ret; + } + + if (data->ddic_pinctrl_seq_delay[3]) { + usleep_range(data->ddic_pinctrl_seq_delay[3], + data->ddic_pinctrl_seq_delay[3] + + HBTP_HOLD_DURATION_US); + dev_dbg(&data->pdev->dev, "ddic_seq_delay[3] = %u\n", + data->ddic_pinctrl_seq_delay[3]); + } + } else { + ret = pinctrl_select_state(data->ts_pinctrl, suspend); + if (ret) { + dev_err(&data->pdev->dev, + "can not set ddic_rst_suspend pins\n"); + return ret; + } + } + + return ret; +} + +static int hbtp_pinctrl_enable(struct hbtp_data *ts, bool on) +{ + int rc = 0; + + if (!ts->manage_pin_ctrl) { + pr_info("%s: pinctrl info is not available\n", __func__); + return 0; + } + + if (!on) + goto pinctrl_suspend; + + rc = hbtp_gpio_select(ts, true); + if (rc < 0) + return -EINVAL; + + rc = hbtp_ddic_rst_select(ts, true); + if (rc < 0) + goto err_ddic_rst_pinctrl_enable; + + return rc; + +pinctrl_suspend: + if (ts->ddic_rst_state_suspend) + hbtp_ddic_rst_select(ts, true); +err_ddic_rst_pinctrl_enable: + hbtp_gpio_select(ts, false); + return rc; +} + static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd, unsigned long arg, void __user *p) { @@ -318,6 +543,8 @@ static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd, struct hbtp_input_absinfo absinfo[ABS_MT_LAST - ABS_MT_FIRST + 1]; struct hbtp_input_key key_data; enum hbtp_afe_power_cmd power_cmd; + enum hbtp_afe_signal afe_signal; + enum hbtp_afe_power_ctrl afe_power_ctrl; switch (cmd) { case HBTP_SET_ABSPARAM: @@ -408,6 +635,112 @@ static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd, input_sync(hbtp->input_dev); break; + case HBTP_SET_SYNCSIGNAL: + if (!hbtp || !hbtp->input_dev) { + pr_err("%s: The input device hasn't been created\n", + __func__); + return -EFAULT; + } + + if (!hbtp->power_sig_enabled) { + pr_err("%s: power_signal is not enabled", __func__); + return -EPERM; + } + + if (copy_from_user(&afe_signal, (void *)arg, + sizeof(enum hbtp_afe_signal))) { + pr_err("%s: Error copying data\n", __func__); + return -EFAULT; + } + + pr_debug("%s: receives %d signal\n", __func__, afe_signal); + + switch (afe_signal) { + case HBTP_AFE_SIGNAL_ON_RESUME: + mutex_lock(&hbtp->mutex); + if (!hbtp->power_suspended) { + complete(&hbtp->power_resume_sig); + } else { + pr_err("%s: resume signal in wrong state\n", + __func__); + } + mutex_unlock(&hbtp->mutex); + break; + case HBTP_AFE_SIGNAL_ON_SUSPEND: + mutex_lock(&hbtp->mutex); + if (hbtp->power_suspended) { + complete(&hbtp->power_suspend_sig); + } else { + pr_err("%s: suspend signal in wrong state\n", + __func__); + } + mutex_unlock(&hbtp->mutex); + break; + default: + pr_err("%s: Unsupported command for afe signal, %d\n", + __func__, afe_signal); + return -EINVAL; + } + break; + case HBTP_SET_POWER_CTRL: + if (!hbtp || !hbtp->input_dev) { + pr_err("%s: The input device hasn't been created\n", + __func__); + return -EFAULT; + } + + if (copy_from_user(&afe_power_ctrl, (void *)arg, + sizeof(enum hbtp_afe_power_ctrl))) { + pr_err("%s: Error copying data\n", __func__); + return -EFAULT; + } + switch (afe_power_ctrl) { + case HBTP_AFE_POWER_ENABLE_SYNC: + pr_debug("%s: power_sync is enabled\n", __func__); + if (!hbtp->manage_pin_ctrl || !hbtp->manage_power_dig || + !hbtp->manage_afe_power_ana) { + pr_err("%s: power/pin is not available\n", + __func__); + return -EFAULT; + } + mutex_lock(&hbtp->mutex); + error = hbtp_pdev_power_on(hbtp, true); + if (error) { + mutex_unlock(&hbtp->mutex); + pr_err("%s: failed to power on\n", __func__); + return error; + } + error = hbtp_pinctrl_enable(hbtp, true); + if (error) { + mutex_unlock(&hbtp->mutex); + pr_err("%s: failed to enable pins\n", __func__); + hbtp_pdev_power_on(hbtp, false); + return error; + } + hbtp->power_sync_enabled = true; + mutex_unlock(&hbtp->mutex); + pr_debug("%s: power_sync option is enabled\n", + __func__); + break; + case HBTP_AFE_POWER_ENABLE_SYNC_SIGNAL: + if (!hbtp->power_sync_enabled) { + pr_err("%s: power_sync is not enabled\n", + __func__); + return -EFAULT; + } + mutex_lock(&hbtp->mutex); + init_completion(&hbtp->power_resume_sig); + init_completion(&hbtp->power_suspend_sig); + hbtp->power_sig_enabled = true; + mutex_unlock(&hbtp->mutex); + pr_err("%s: sync_signal option is enabled\n", __func__); + break; + default: + pr_err("%s: unsupported power ctrl, %d\n", + __func__, afe_power_ctrl); + return -EINVAL; + } + break; default: pr_err("%s: Unsupported ioctl command %u\n", __func__, cmd); error = -EINVAL; @@ -514,6 +847,25 @@ static int hbtp_parse_dt(struct device *dev) } } + if (hbtp->manage_power_dig && hbtp->manage_afe_power_ana) { + rc = of_property_read_u32(np, + "qcom,afe-power-on-delay-us", &temp_val); + if (!rc) + hbtp->power_on_delay = (u32)temp_val; + else + dev_info(dev, "Power-On Delay is not specified\n"); + + rc = of_property_read_u32(np, + "qcom,afe-power-off-delay-us", &temp_val); + if (!rc) + hbtp->power_off_delay = (u32)temp_val; + else + dev_info(dev, "Power-Off Delay is not specified\n"); + + dev_dbg(dev, "power-on-delay = %u, power-off-delay = %u\n", + hbtp->power_on_delay, hbtp->power_off_delay); + } + prop = of_find_property(np, "qcom,display-resolution", NULL); if (prop != NULL) { if (!prop->value) @@ -605,11 +957,292 @@ static int hbtp_parse_dt(struct device *dev) } #endif +static int hbtp_pinctrl_init(struct hbtp_data *data) +{ + const char *statename; + int rc; + int state_cnt, i; + struct device_node *np = data->pdev->dev.of_node; + bool pinctrl_state_act_found = false; + bool pinctrl_state_sus_found = false; + bool pinctrl_ddic_act_found = false; + bool pinctrl_ddic_sus_found = false; + int count = 0; + + data->ts_pinctrl = devm_pinctrl_get(&(data->pdev->dev)); + if (IS_ERR_OR_NULL(data->ts_pinctrl)) { + dev_err(&data->pdev->dev, + "Target does not use pinctrl\n"); + rc = PTR_ERR(data->ts_pinctrl); + data->ts_pinctrl = NULL; + return rc; + } + + state_cnt = of_property_count_strings(np, "pinctrl-names"); + if (state_cnt < HBTP_PINCTRL_VALID_STATE_CNT) { + /* + *if pinctrl names are not available then, + *power_sync can't be enabled + */ + dev_info(&data->pdev->dev, + "pinctrl names are not available\n"); + rc = -EINVAL; + goto error; + } + + for (i = 0; i < state_cnt; i++) { + rc = of_property_read_string_index(np, + "pinctrl-names", i, &statename); + if (rc) { + dev_err(&data->pdev->dev, + "failed to read pinctrl states by index\n"); + goto error; + } + + if (!strcmp(statename, "pmx_ts_active")) { + data->gpio_state_active + = pinctrl_lookup_state(data->ts_pinctrl, + statename); + if (IS_ERR_OR_NULL(data->gpio_state_active)) { + dev_err(&data->pdev->dev, + "Can not get ts default state\n"); + rc = PTR_ERR(data->gpio_state_active); + goto error; + } + pinctrl_state_act_found = true; + } else if (!strcmp(statename, "pmx_ts_suspend")) { + data->gpio_state_suspend + = pinctrl_lookup_state(data->ts_pinctrl, + statename); + if (IS_ERR_OR_NULL(data->gpio_state_suspend)) { + dev_err(&data->pdev->dev, + "Can not get ts sleep state\n"); + rc = PTR_ERR(data->gpio_state_suspend); + goto error; + } + pinctrl_state_sus_found = true; + } else if (!strcmp(statename, "ddic_rst_active")) { + data->ddic_rst_state_active + = pinctrl_lookup_state(data->ts_pinctrl, + statename); + if (IS_ERR(data->ddic_rst_state_active)) { + dev_err(&data->pdev->dev, + "Can not get DDIC rst act state\n"); + rc = PTR_ERR(data->ddic_rst_state_active); + goto error; + } + pinctrl_ddic_act_found = true; + } else if (!strcmp(statename, "ddic_rst_suspend")) { + data->ddic_rst_state_suspend + = pinctrl_lookup_state(data->ts_pinctrl, + statename); + if (IS_ERR(data->ddic_rst_state_suspend)) { + dev_err(&data->pdev->dev, + "Can not get DDIC rst sleep state\n"); + rc = PTR_ERR(data->ddic_rst_state_suspend); + goto error; + } + pinctrl_ddic_sus_found = true; + } else { + dev_err(&data->pdev->dev, "invalid pinctrl state\n"); + rc = -EINVAL; + goto error; + } + } + + if (!pinctrl_state_act_found || !pinctrl_state_sus_found) { + dev_err(&data->pdev->dev, + "missing required pinctrl states\n"); + rc = -EINVAL; + goto error; + } + + if (of_property_read_u32(np, "qcom,pmx-ts-on-seq-delay-us", + &data->ts_pinctrl_seq_delay)) { + dev_warn(&data->pdev->dev, "Can not find ts seq delay\n"); + } + + if (of_property_read_u32(np, "qcom,fb-resume-delay-us", + &data->fb_resume_seq_delay)) { + dev_warn(&data->pdev->dev, "Can not find fb resume seq delay\n"); + } + + if (pinctrl_ddic_act_found && pinctrl_ddic_sus_found) { + count = of_property_count_u32_elems(np, + "qcom,ddic-rst-on-seq-delay-us"); + if (count == HBTP_PINCTRL_DDIC_SEQ_NUM) { + of_property_read_u32_array(np, + "qcom,ddic-rst-on-seq-delay-us", + data->ddic_pinctrl_seq_delay, count); + } else { + dev_err(&data->pdev->dev, "count(%u) is not same as %u\n", + (u32)count, HBTP_PINCTRL_DDIC_SEQ_NUM); + } + } else { + dev_err(&data->pdev->dev, "ddic pinctrl act/sus not found\n"); + } + + data->manage_pin_ctrl = true; + return 0; + +error: + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + return rc; +} + +static int hbtp_fb_suspend(struct hbtp_data *ts) +{ + int rc; + char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL}; + + mutex_lock(&hbtp->mutex); + if (ts->pdev && ts->power_sync_enabled) { + pr_debug("%s: power_sync is enabled\n", __func__); + if (ts->power_suspended) { + mutex_unlock(&hbtp->mutex); + pr_err("%s: power is not resumed\n", __func__); + return 0; + } + rc = hbtp_pinctrl_enable(ts, false); + if (rc) { + pr_err("%s: failed to disable GPIO pins\n", __func__); + goto err_pin_disable; + } + + rc = hbtp_pdev_power_on(ts, false); + if (rc) { + pr_err("%s: failed to disable power\n", __func__); + goto err_power_disable; + } + ts->power_suspended = true; + } + + if (ts->input_dev) { + kobject_uevent_env(&ts->input_dev->dev.kobj, + KOBJ_OFFLINE, envp); + + if (ts->power_sig_enabled) { + pr_debug("%s: power_sig is enabled, wait for signal\n", + __func__); + mutex_unlock(&hbtp->mutex); + rc = wait_for_completion_interruptible( + &hbtp->power_suspend_sig); + if (rc != 0) { + pr_err("%s: wait for suspend is interrupted\n", + __func__); + } + mutex_lock(&hbtp->mutex); + pr_debug("%s: Wait is done for suspend\n", __func__); + } else { + pr_debug("%s: power_sig is NOT enabled", __func__); + } + } + + mutex_unlock(&hbtp->mutex); + return 0; +err_power_disable: + hbtp_pinctrl_enable(ts, true); +err_pin_disable: + mutex_unlock(&hbtp->mutex); + return rc; +} + +static int hbtp_fb_early_resume(struct hbtp_data *ts) +{ + char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL}; + int rc; + + mutex_lock(&hbtp->mutex); + + pr_debug("%s: hbtp_fb_early_resume\n", __func__); + + if (ts->pdev && ts->power_sync_enabled) { + pr_debug("%s: power_sync is enabled\n", __func__); + if (!ts->power_suspended) { + pr_err("%s: power is not suspended\n", __func__); + mutex_unlock(&hbtp->mutex); + return 0; + } + rc = hbtp_pdev_power_on(ts, true); + if (rc) { + pr_err("%s: failed to enable panel power\n", __func__); + goto err_power_on; + } + + rc = hbtp_pinctrl_enable(ts, true); + + if (rc) { + pr_err("%s: failed to enable pin\n", __func__); + goto err_pin_enable; + } + + ts->power_suspended = false; + + if (ts->input_dev) { + + kobject_uevent_env(&ts->input_dev->dev.kobj, + KOBJ_ONLINE, envp); + + if (ts->power_sig_enabled) { + pr_err("%s: power_sig is enabled, wait for signal\n", + __func__); + mutex_unlock(&hbtp->mutex); + rc = wait_for_completion_interruptible( + &hbtp->power_resume_sig); + if (rc != 0) { + pr_err("%s: wait for resume is interrupted\n", + __func__); + } + mutex_lock(&hbtp->mutex); + pr_debug("%s: wait is done\n", __func__); + } else { + pr_debug("%s: power_sig is NOT enabled\n", + __func__); + } + + if (ts->fb_resume_seq_delay) { + usleep_range(ts->fb_resume_seq_delay, + ts->fb_resume_seq_delay + + HBTP_HOLD_DURATION_US); + pr_err("%s: fb_resume_seq_delay = %u\n", + __func__, ts->fb_resume_seq_delay); + } + } + } + mutex_unlock(&hbtp->mutex); + return 0; + +err_pin_enable: + hbtp_pdev_power_on(ts, false); +err_power_on: + mutex_unlock(&hbtp->mutex); + return rc; +} + +static int hbtp_fb_resume(struct hbtp_data *ts) +{ + char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL}; + + mutex_lock(&hbtp->mutex); + if (!ts->power_sync_enabled) { + pr_debug("%s: power_sync is disabled, send uevent\n", __func__); + if (ts->input_dev) { + kobject_uevent_env(&ts->input_dev->dev.kobj, + KOBJ_ONLINE, envp); + } + } + mutex_unlock(&hbtp->mutex); + return 0; +} + static int hbtp_pdev_probe(struct platform_device *pdev) { int error; struct regulator *vcc_ana, *vcc_dig; + hbtp->pdev = pdev; + if (pdev->dev.of_node) { error = hbtp_parse_dt(&pdev->dev); if (error) { @@ -618,6 +1251,14 @@ static int hbtp_pdev_probe(struct platform_device *pdev) } } + platform_set_drvdata(pdev, hbtp); + + error = hbtp_pinctrl_init(hbtp); + if (error) { + pr_info("%s: pinctrl isn't available, rc=%d\n", __func__, + error); + } + if (hbtp->manage_afe_power_ana) { vcc_ana = regulator_get(&pdev->dev, "vcc_ana"); if (IS_ERR(vcc_ana)) { @@ -662,8 +1303,6 @@ static int hbtp_pdev_probe(struct platform_device *pdev) hbtp->vcc_dig = vcc_dig; } - hbtp->pdev = pdev; - return 0; } @@ -677,6 +1316,9 @@ static int hbtp_pdev_remove(struct platform_device *pdev) regulator_put(hbtp->vcc_dig); } + if (hbtp->ts_pinctrl) + devm_pinctrl_put(hbtp->ts_pinctrl); + return 0; } diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c index 99a29401b36f..1657f56558ce 100644 --- a/drivers/input/touchscreen/gt9xx/goodix_tool.c +++ b/drivers/input/touchscreen/gt9xx/goodix_tool.c @@ -236,14 +236,13 @@ static u8 relation(u8 src, u8 dst, u8 rlt) return ret; } -/******************************************************* +/* * Function: - * Comfirm function. + * Comfirm function. * Input: - * None. + * None. * Output: * Return write length. - ******************************************************* */ static u8 comfirm(void) { @@ -301,11 +300,11 @@ static s32 fill_update_info(char __user *user_buf, * Function: * Goodix tool write function. * Input: - * standard proc write function param. + * standard proc write function param. * Output: * Return write length. */ -static s32 goodix_tool_write(struct file *filp, const char __user *userbuf, +static ssize_t goodix_tool_write(struct file *filp, const char __user *userbuf, size_t count, loff_t *ppos) { s32 ret = 0; @@ -318,20 +317,22 @@ static s32 goodix_tool_write(struct file *filp, const char __user *userbuf, goto exit; } - dev_dbg(>_client->dev, "wr:0x%02x, flag:0x%02x, flag addr:0x%02x%02x, - flag val:0x%02x, flag rel:0x%02x,", cmd_headd.wr, - cmd_head.flag, cmd_head.flag_addr[0], - cmd_head.flag_addr[1], cmd_head.flag_val, - cmd_head.flag_relation); - dev_dbg(>_client->dev, "circle:%d, times:%d, retry:%d, delay:%d, - data len:%d, addr len:%d, addr:0x%02x%02x, write len: %d", - (s32)cmd_head.circle, (s32)cmd_head.times, (s32)cmd_head.retry, - (s32)cmd_head.delay, (s32)cmd_head.data_len, - (s32)cmd_head.addr_len, cmd_head.addr[0], cmd_head.addr[1], - (s32)count); + dev_dbg(>_client->dev, + "wr: 0x%02x, flag:0x%02x, flag addr:0x%02x%02x\n", cmd_head.wr, + cmd_head.flag, cmd_head.flag_addr[0], cmd_head.flag_addr[1]); + dev_dbg(>_client->dev, + "flag val:0x%02x, flag rel:0x%02x,\n", cmd_head.flag_val, + cmd_head.flag_relation); + dev_dbg(>_client->dev, "circle:%u, times:%u, retry:%u, delay:%u\n", + (s32) cmd_head.circle, (s32) cmd_head.times, + (s32) cmd_head.retry, (s32)cmd_head.delay); + dev_dbg(>_client->dev, + "data len:%u, addr len:%u, addr:0x%02x%02x, write len: %u\n", + (s32)cmd_head.data_len, (s32)cmd_head.addr_len, + cmd_head.addr[0], cmd_head.addr[1], (s32)count); if (cmd_head.data_len > (data_length - GTP_ADDR_LENGTH)) { - dev_err(>_client->dev, "data len %d > data buff %d, rejected\n", + dev_err(>_client->dev, "data len %u > data buff %d, rejected\n", cmd_head.data_len, (data_length - GTP_ADDR_LENGTH)); ret = -EINVAL; goto exit; @@ -383,7 +384,7 @@ static s32 goodix_tool_write(struct file *filp, const char __user *userbuf, if (cmd_head.data_len > sizeof(ic_type)) { dev_err(>_client->dev, - "data len %d > data buff %d, rejected\n", + "data len %u > data buff %zu, rejected\n", cmd_head.data_len, sizeof(ic_type)); ret = -EINVAL; goto exit; @@ -445,7 +446,7 @@ static s32 goodix_tool_write(struct file *filp, const char __user *userbuf, show_len = 0; total_len = 0; if (cmd_head.data_len + 1 > data_length) { - dev_err(>_client->dev, "data len %d > data buff %d, rejected\n", + dev_err(>_client->dev, "data len %u > data buff %d, rejected\n", cmd_head.data_len + 1, data_length); ret = -EINVAL; goto exit; @@ -470,11 +471,11 @@ exit: * Function: * Goodix tool read function. * Input: - * standard proc read function param. + * standard proc read function param. * Output: * Return read length. */ -static s32 goodix_tool_read(struct file *file, char __user *user_buf, +static ssize_t goodix_tool_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { u16 data_len = 0; diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c index 3b19a45922c4..ead935120624 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx.c +++ b/drivers/input/touchscreen/gt9xx/gt9xx.c @@ -48,6 +48,7 @@ #include <linux/module.h> #include <linux/input/mt.h> #include <linux/debugfs.h> +#include <linux/interrupt.h> #define GOODIX_DEV_NAME "Goodix-CTP" #define CFG_MAX_TOUCH_POINTS 5 @@ -65,6 +66,8 @@ #define RESET_DELAY_T3_US 200 /* T3: > 100us */ #define RESET_DELAY_T4 20 /* T4: > 5ms */ +#define SLEEP_DELAY_US 5000 +#define WAKE_UP_DELAY_US 5000 #define PHY_BUF_SIZE 32 #define PROP_NAME_SIZE 24 @@ -72,11 +75,6 @@ #define GTP_MAX_TOUCH 5 #define GTP_ESD_CHECK_CIRCLE_MS 2000 -#if GTP_HAVE_TOUCH_KEY -static const u16 touch_key_array[] = {KEY_MENU, KEY_HOMEPAGE, KEY_BACK}; - -#endif - static void gtp_int_sync(struct goodix_ts_data *ts, int ms); static int gtp_i2c_test(struct i2c_client *client); static int goodix_power_off(struct goodix_ts_data *ts); @@ -99,15 +97,14 @@ static void gtp_esd_check_func(struct work_struct *work); static int gtp_init_ext_watchdog(struct i2c_client *client); #endif -#if GTP_SLIDE_WAKEUP -enum doze_status { +enum doze { DOZE_DISABLED = 0, DOZE_ENABLED = 1, DOZE_WAKEUP = 2, }; -static enum doze_status = DOZE_DISABLED; +static enum doze doze_status = DOZE_DISABLED; static s8 gtp_enter_doze(struct goodix_ts_data *ts); -#endif + bool init_done; static u8 chip_gt9xxs; /* true if ic is gt9xxs, like gt915s */ u8 grp_cfg_version; @@ -157,11 +154,11 @@ int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len) dev_err(&client->dev, "I2C retry: %d\n", retries + 1); } if (retries == GTP_I2C_RETRY_5) { -#if GTP_SLIDE_WAKEUP - /* reset chip would quit doze mode */ - if (doze_status == DOZE_ENABLED) - return ret; -#endif + if (ts->pdata->slide_wakeup) + /* reset chip would quit doze mode */ + if (doze_status == DOZE_ENABLED) + return ret; + if (init_done) gtp_reset_guitar(ts, 10); else @@ -203,10 +200,10 @@ int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len) dev_err(&client->dev, "I2C retry: %d\n", retries + 1); } if (retries == GTP_I2C_RETRY_5) { -#if GTP_SLIDE_WAKEUP - if (doze_status == DOZE_ENABLED) - return ret; -#endif + if (ts->pdata->slide_wakeup) + if (doze_status == DOZE_ENABLED) + return ret; + if (init_done) gtp_reset_guitar(ts, 10); else @@ -270,24 +267,25 @@ Output: *********************************************************/ int gtp_send_cfg(struct goodix_ts_data *ts) { - int ret; -#if GTP_DRIVER_SEND_CFG - int retry = 0; + int ret = 0; + int retry; - if (ts->fixed_cfg) { - dev_dbg(&ts->client->dev, - "Ic fixed config, no config sent!"); - ret = 2; - } else { - for (retry = 0; retry < GTP_I2C_RETRY_5; retry++) { - ret = gtp_i2c_write(ts->client, - ts->config_data, - GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH); - if (ret > 0) - break; + if (ts->pdata->driver_send_cfg) { + if (ts->fixed_cfg) { + dev_dbg(&ts->client->dev, + "Ic fixed config, no config sent!"); + ret = 2; + } else { + for (retry = 0; retry < GTP_I2C_RETRY_5; retry++) { + ret = gtp_i2c_write(ts->client, + ts->config_data, + GTP_CONFIG_MAX_LENGTH + + GTP_ADDR_LENGTH); + if (ret > 0) + break; + } } } -#endif return ret; } @@ -347,9 +345,8 @@ Output: static void gtp_touch_down(struct goodix_ts_data *ts, int id, int x, int y, int w) { -#if GTP_CHANGE_X2Y - swap(x, y); -#endif + if (ts->pdata->change_x2y) + swap(x, y); input_mt_slot(ts->input_dev, id); input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); @@ -394,9 +391,7 @@ static void goodix_ts_work_func(struct work_struct *work) u8 finger = 0; static u16 pre_touch; static u8 pre_key; -#if GTP_WITH_PEN static u8 pre_pen; -#endif u8 key_value = 0; u8 *coor_data = NULL; s32 input_x = 0; @@ -406,10 +401,7 @@ static void goodix_ts_work_func(struct work_struct *work) s32 i = 0; int ret = -1; struct goodix_ts_data *ts = NULL; - -#if GTP_SLIDE_WAKEUP u8 doze_buf[3] = {0x81, 0x4B}; -#endif ts = container_of(work, struct goodix_ts_data, work); #ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE @@ -417,55 +409,59 @@ static void goodix_ts_work_func(struct work_struct *work) return; #endif -#if GTP_SLIDE_WAKEUP - if (doze_status == DOZE_ENABLED) { - ret = gtp_i2c_read(ts->client, doze_buf, 3); - if (ret > 0) { - if (doze_buf[2] == 0xAA) { - dev_dbg(&ts->client->dev, - "Slide(0xAA) To Light up the screen!"); - doze_status = DOZE_WAKEUP; - input_report_key( - ts->input_dev, KEY_POWER, 1); - input_sync(ts->input_dev); - input_report_key( - ts->input_dev, KEY_POWER, 0); - input_sync(ts->input_dev); - /* clear 0x814B */ - doze_buf[2] = 0x00; - gtp_i2c_write(ts->client, doze_buf, 3); - } else if (doze_buf[2] == 0xBB) { - dev_dbg(&ts->client->dev, - "Slide(0xBB) To Light up the screen!"); - doze_status = DOZE_WAKEUP; - input_report_key(ts->input_dev, KEY_POWER, 1); - input_sync(ts->input_dev); - input_report_key(ts->input_dev, KEY_POWER, 0); - input_sync(ts->input_dev); - /* clear 0x814B*/ - doze_buf[2] = 0x00; - gtp_i2c_write(ts->client, doze_buf, 3); - } else if (0xC0 == (doze_buf[2] & 0xC0)) { - dev_dbg(&ts->client->dev, - "double click to light up the screen!"); - doze_status = DOZE_WAKEUP; - input_report_key(ts->input_dev, KEY_POWER, 1); - input_sync(ts->input_dev); - input_report_key(ts->input_dev, KEY_POWER, 0); - input_sync(ts->input_dev); - /* clear 0x814B */ - doze_buf[2] = 0x00; - gtp_i2c_write(ts->client, doze_buf, 3); - } else { - gtp_enter_doze(ts); + if (ts->pdata->slide_wakeup) { + if (doze_status == DOZE_ENABLED) { + ret = gtp_i2c_read(ts->client, doze_buf, 3); + if (ret > 0) { + if (doze_buf[2] == 0xAA) { + dev_dbg(&ts->client->dev, + "Slide(0xAA) To Light up the screen!"); + doze_status = DOZE_WAKEUP; + input_report_key( + ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + input_report_key( + ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + /* clear 0x814B */ + doze_buf[2] = 0x00; + gtp_i2c_write(ts->client, doze_buf, 3); + } else if (doze_buf[2] == 0xBB) { + dev_dbg(&ts->client->dev, + "Slide(0xBB) To Light up the screen!"); + doze_status = DOZE_WAKEUP; + input_report_key(ts->input_dev, + KEY_POWER, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, + KEY_POWER, 0); + input_sync(ts->input_dev); + /* clear 0x814B*/ + doze_buf[2] = 0x00; + gtp_i2c_write(ts->client, doze_buf, 3); + } else if (0xC0 == (doze_buf[2] & 0xC0)) { + dev_dbg(&ts->client->dev, + "double click to light up the screen!"); + doze_status = DOZE_WAKEUP; + input_report_key(ts->input_dev, + KEY_POWER, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, + KEY_POWER, 0); + input_sync(ts->input_dev); + /* clear 0x814B */ + doze_buf[2] = 0x00; + gtp_i2c_write(ts->client, doze_buf, 3); + } else { + gtp_enter_doze(ts); + } } - } - if (ts->use_irq) - gtp_irq_enable(ts); + if (ts->use_irq) + gtp_irq_enable(ts); - return; + return; + } } -#endif ret = gtp_i2c_read(ts->client, point_data, 12); if (ret < 0) { @@ -506,15 +502,16 @@ static void goodix_ts_work_func(struct work_struct *work) pre_key = key_value; -#if GTP_WITH_PEN - if (pre_pen && (touch_num == 0)) { - dev_dbg(&ts->client->dev, "Pen touch UP(Slot)!"); - input_report_key(ts->input_dev, BTN_TOOL_PEN, 0); - input_mt_slot(ts->input_dev, 5); - input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1); - pre_pen = 0; + if (ts->pdata->with_pen) { + if (pre_pen && (touch_num == 0)) { + dev_dbg(&ts->client->dev, "Pen touch UP(Slot)!"); + input_report_key(ts->input_dev, BTN_TOOL_PEN, 0); + input_mt_slot(ts->input_dev, 5); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1); + pre_pen = 0; + } } -#endif + if (pre_touch || touch_num) { s32 pos = 0; u16 touch_index = 0; @@ -522,45 +519,45 @@ static void goodix_ts_work_func(struct work_struct *work) coor_data = &point_data[3]; if (touch_num) { id = coor_data[pos] & 0x0F; -#if GTP_WITH_PEN - id = coor_data[pos]; - if (id == 128) { - dev_dbg(&ts->client->dev, - "Pen touch DOWN(Slot)!"); - input_x = coor_data[pos + 1] - | (coor_data[pos + 2] << 8); - input_y = coor_data[pos + 3] - | (coor_data[pos + 4] << 8); - input_w = coor_data[pos + 5] - | (coor_data[pos + 6] << 8); - - input_report_key(ts->input_dev, - BTN_TOOL_PEN, 1); - input_mt_slot(ts->input_dev, 5); - input_report_abs(ts->input_dev, - ABS_MT_TRACKING_ID, 5); - input_report_abs(ts->input_dev, - ABS_MT_POSITION_X, input_x); - input_report_abs(ts->input_dev, - ABS_MT_POSITION_Y, input_y); - input_report_abs(ts->input_dev, - ABS_MT_TOUCH_MAJOR, input_w); - dev_dbg(&ts->client->dev, - "Pen/Stylus: (%d, %d)[%d]", - input_x, input_y, input_w); - pre_pen = 1; - pre_touch = 0; + if (ts->pdata->with_pen) { + id = coor_data[pos]; + if (id == 128) { + dev_dbg(&ts->client->dev, + "Pen touch DOWN(Slot)!"); + input_x = coor_data[pos + 1] + | (coor_data[pos + 2] << 8); + input_y = coor_data[pos + 3] + | (coor_data[pos + 4] << 8); + input_w = coor_data[pos + 5] + | (coor_data[pos + 6] << 8); + + input_report_key(ts->input_dev, + BTN_TOOL_PEN, 1); + input_mt_slot(ts->input_dev, 5); + input_report_abs(ts->input_dev, + ABS_MT_TRACKING_ID, 5); + input_report_abs(ts->input_dev, + ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, + ABS_MT_POSITION_Y, input_y); + input_report_abs(ts->input_dev, + ABS_MT_TOUCH_MAJOR, input_w); + dev_dbg(&ts->client->dev, + "Pen/Stylus: (%d, %d)[%d]", + input_x, input_y, input_w); + pre_pen = 1; + pre_touch = 0; + } } -#endif touch_index |= (0x01<<id); } for (i = 0; i < GTP_MAX_TOUCH; i++) { -#if GTP_WITH_PEN - if (pre_pen == 1) - break; -#endif + if (ts->pdata->with_pen) + if (pre_pen == 1) + break; + if (touch_index & (0x01<<i)) { input_x = coor_data[pos + 1] | coor_data[pos + 2] << 8; @@ -651,7 +648,7 @@ void gtp_reset_guitar(struct goodix_ts_data *ts, int ms) else gpio_direction_output(ts->pdata->irq_gpio, 0); - usleep(RESET_DELAY_T3_US); + usleep_range(RESET_DELAY_T3_US, RESET_DELAY_T3_US + 1); gpio_direction_output(ts->pdata->reset_gpio, 1); msleep(RESET_DELAY_T4); @@ -665,7 +662,6 @@ void gtp_reset_guitar(struct goodix_ts_data *ts, int ms) } #if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB) -#if GTP_SLIDE_WAKEUP /******************************************************* Function: Enter doze mode for sliding wakeup. @@ -682,9 +678,9 @@ static s8 gtp_enter_doze(struct goodix_ts_data *ts) (u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 8}; -#if GTP_DBL_CLK_WAKEUP - i2c_control_buf[2] = 0x09; -#endif + if (ts->pdata->dbl_clk_wakeup) + i2c_control_buf[2] = 0x09; + gtp_irq_disable(ts); while (retry++ < GTP_I2C_RETRY_3) { @@ -713,7 +709,6 @@ static s8 gtp_enter_doze(struct goodix_ts_data *ts) gtp_irq_enable(ts); return ret; } -#else /** * gtp_enter_sleep - Enter sleep mode * @ts: driver private data @@ -746,7 +741,7 @@ static u8 gtp_enter_sleep(struct goodix_ts_data *ts) } return 0; } - usleep(5000); + usleep_range(SLEEP_DELAY_US, SLEEP_DELAY_US + 1); while (retry++ < GTP_I2C_RETRY_5) { ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); if (ret == 1) { @@ -758,7 +753,6 @@ static u8 gtp_enter_sleep(struct goodix_ts_data *ts) dev_err(&ts->client->dev, "GTP send sleep cmd failed.\n"); return ret; } -#endif /* !GTP_SLIDE_WAKEUP */ /******************************************************* Function: @@ -804,33 +798,34 @@ static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts) "Wakeup sleep send config success."); } else { err_retry: -#if GTP_SLIDE_WAKEUP - /* wakeup not by slide */ - if (doze_status != DOZE_WAKEUP) - gtp_reset_guitar(ts, 10); - else - /* wakeup by slide */ - doze_status = DOZE_DISABLED; -#else - if (chip_gt9xxs == 1) { - gtp_reset_guitar(ts, 10); + if (ts->pdata->slide_wakeup) { /* wakeup not by slide */ + if (doze_status != DOZE_WAKEUP) + gtp_reset_guitar(ts, 10); + else + /* wakeup by slide */ + doze_status = DOZE_DISABLED; } else { - ret = gpio_direction_output(ts->pdata->irq_gpio, 1); - usleep(5000); + if (chip_gt9xxs == 1) { + gtp_reset_guitar(ts, 10); + } else { + ret = gpio_direction_output( + ts->pdata->irq_gpio, 1); + usleep_range(WAKE_UP_DELAY_US, + WAKE_UP_DELAY_US + 1); + } } -#endif ret = gtp_i2c_test(ts->client); if (ret == 2) { dev_dbg(&ts->client->dev, "GTP wakeup sleep."); -#if (!GTP_SLIDE_WAKEUP) - if (chip_gt9xxs == 0) { - gtp_int_sync(ts, 25); - msleep(20); + if (!ts->pdata->slide_wakeup) { + if (chip_gt9xxs == 0) { + gtp_int_sync(ts, 25); + msleep(20); #if GTP_ESD_PROTECT - gtp_init_ext_watchdog(ts->client); + gtp_init_ext_watchdog(ts->client); #endif + } } -#endif return ret; } gtp_reset_guitar(ts, 20); @@ -854,123 +849,126 @@ Output: static int gtp_init_panel(struct goodix_ts_data *ts) { struct i2c_client *client = ts->client; - unsigned char *config_data; + unsigned char *config_data = NULL; int ret = -EIO; - -#if GTP_DRIVER_SEND_CFG int i; u8 check_sum = 0; u8 opr_buf[16]; u8 sensor_id = 0; - for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) - dev_dbg(&client->dev, "Config Groups(%d) Lengths: %d", - i, ts->pdata->config_data_len[i]); - - ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1); - if (ret == SUCCESS) { - if (opr_buf[0] != 0xBE) { - ts->fw_error = 1; - dev_err(&client->dev, - "Firmware error, no config sent!"); - return -EINVAL; - } - } + if (ts->pdata->driver_send_cfg) { + for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) + dev_dbg(&client->dev, "Config Groups(%d) Lengths: %zu", + i, ts->pdata->config_data_len[i]); - for (i = 1; i < GOODIX_MAX_CFG_GROUP; i++) { - if (ts->pdata->config_data_len[i]) - break; - } - if (i == GOODIX_MAX_CFG_GROUP) { - sensor_id = 0; - } else { - ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, - &sensor_id, 1); + ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1); if (ret == SUCCESS) { - if (sensor_id >= GOODIX_MAX_CFG_GROUP) { + if (opr_buf[0] != 0xBE) { + ts->fw_error = 1; dev_err(&client->dev, - "Invalid sensor_id(0x%02X), No Config Sent!", - sensor_id); + "Firmware error, no config sent!"); return -EINVAL; } + } + + for (i = 1; i < GOODIX_MAX_CFG_GROUP; i++) { + if (ts->pdata->config_data_len[i]) + break; + } + + if (i == GOODIX_MAX_CFG_GROUP) { + sensor_id = 0; } else { - dev_err(&client->dev, - "Failed to get sensor_id, No config sent!"); - return -EINVAL; + ret = gtp_i2c_read_dbl_check(ts->client, + GTP_REG_SENSOR_ID, &sensor_id, 1); + if (ret == SUCCESS) { + if (sensor_id >= GOODIX_MAX_CFG_GROUP) { + dev_err(&client->dev, + "Invalid sensor_id(0x%02X), No Config Sent!", + sensor_id); + return -EINVAL; + } + } else { + dev_err(&client->dev, + "Failed to get sensor_id, No config sent!"); + return -EINVAL; + } } - } - dev_dbg(&client->dev, "Sensor ID selected: %d", sensor_id); + dev_info(&client->dev, "Sensor ID selected: %d", sensor_id); - if (ts->pdata->config_data_len[sensor_id] < GTP_CONFIG_MIN_LENGTH || - !ts->pdata->config_data[sensor_id]) { - dev_err(&client->dev, - "Sensor_ID(%d) matches with NULL or invalid config group!\n", - sensor_id); - return -EINVAL; - } + if (ts->pdata->config_data_len[sensor_id] < + GTP_CONFIG_MIN_LENGTH || + !ts->pdata->config_data[sensor_id]) { + dev_err(&client->dev, + "Sensor_ID(%d) matches with NULL or invalid config group!\n", + sensor_id); + return -EINVAL; + } - ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, - &opr_buf[0], 1); - if (ret == SUCCESS) { - if (opr_buf[0] < 90) { - /* backup group config version */ - grp_cfg_version = - ts->pdata->config_data[sensor_id][GTP_ADDR_LENGTH]; - ts->pdata->config_data[sensor_id][GTP_ADDR_LENGTH] = - 0x00; - ts->fixed_cfg = 0; + ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, + &opr_buf[0], 1); + if (ret == SUCCESS) { + if (opr_buf[0] < 90) { + /* backup group config version */ + grp_cfg_version = + ts->pdata-> + config_data[sensor_id][GTP_ADDR_LENGTH]; + ts->pdata-> + config_data[sensor_id][GTP_ADDR_LENGTH] + = 0x00; + ts->fixed_cfg = 0; + } else { + /* treated as fixed config, not send config */ + dev_warn(&client->dev, + "Ic fixed config with config version(%d, 0x%02X)", + opr_buf[0], opr_buf[0]); + ts->fixed_cfg = 1; + } } else { - /* treated as fixed config, not send config */ - dev_warn(&client->dev, - "Ic fixed config with config version(%d, 0x%02X)", - opr_buf[0], opr_buf[0]); - ts->fixed_cfg = 1; + dev_err(&client->dev, + "Failed to get ic config version!No config sent!"); + return -EINVAL; } - } else { - dev_err(&client->dev, - "Failed to get ic config version!No config sent!"); - return -EINVAL; - } - config_data = ts->pdata->config_data[sensor_id]; - ts->config_data = ts->pdata->config_data[sensor_id]; - ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id]; + config_data = ts->pdata->config_data[sensor_id]; + ts->config_data = ts->pdata->config_data[sensor_id]; + ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id]; #if GTP_CUSTOM_CFG - config_data[RESOLUTION_LOC] = - (unsigned char)(GTP_MAX_WIDTH && 0xFF); - config_data[RESOLUTION_LOC + 1] = - (unsigned char)(GTP_MAX_WIDTH >> 8); - config_data[RESOLUTION_LOC + 2] = - (unsigned char)(GTP_MAX_HEIGHT && 0xFF); - config_data[RESOLUTION_LOC + 3] = - (unsigned char)(GTP_MAX_HEIGHT >> 8); - - if (GTP_INT_TRIGGER == 0) - config_data[TRIGGER_LOC] &= 0xfe; - else if (GTP_INT_TRIGGER == 1) - config_data[TRIGGER_LOC] |= 0x01; + config_data[RESOLUTION_LOC] = + (unsigned char)(GTP_MAX_WIDTH && 0xFF); + config_data[RESOLUTION_LOC + 1] = + (unsigned char)(GTP_MAX_WIDTH >> 8); + config_data[RESOLUTION_LOC + 2] = + (unsigned char)(GTP_MAX_HEIGHT && 0xFF); + config_data[RESOLUTION_LOC + 3] = + (unsigned char)(GTP_MAX_HEIGHT >> 8); + + if (GTP_INT_TRIGGER == 0) + config_data[TRIGGER_LOC] &= 0xfe; + else if (GTP_INT_TRIGGER == 1) + config_data[TRIGGER_LOC] |= 0x01; #endif /* !GTP_CUSTOM_CFG */ - check_sum = 0; - for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) - check_sum += config_data[i]; + check_sum = 0; + for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) + check_sum += config_data[i]; - config_data[ts->gtp_cfg_len] = (~check_sum) + 1; + config_data[ts->gtp_cfg_len] = (~check_sum) + 1; -#else /* DRIVER NOT SEND CONFIG */ - ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH; - ret = gtp_i2c_read(ts->client, config_data, + } else { /* DRIVER NOT SEND CONFIG */ + ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH; + ret = gtp_i2c_read(ts->client, config_data, ts->gtp_cfg_len + GTP_ADDR_LENGTH); - if (ret < 0) { - dev_err(&client->dev, + if (ret < 0) { + dev_err(&client->dev, "Read Config Failed, Using DEFAULT Resolution & INT Trigger!\n"); - ts->abs_x_max = GTP_MAX_WIDTH; - ts->abs_y_max = GTP_MAX_HEIGHT; - ts->int_trigger_type = GTP_INT_TRIGGER; - } -#endif /* !DRIVER NOT SEND CONFIG */ + ts->abs_x_max = GTP_MAX_WIDTH; + ts->abs_y_max = GTP_MAX_HEIGHT; + ts->int_trigger_type = GTP_INT_TRIGGER; + } + } /* !DRIVER NOT SEND CONFIG */ if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) { ts->abs_x_max = (config_data[RESOLUTION_LOC + 1] << 8) @@ -1177,7 +1175,8 @@ static int gtp_request_irq(struct goodix_ts_data *ts) int ret; const u8 irq_table[] = GTP_IRQ_TAB; - GTP_DEBUG("INT trigger type:%x, irq=%d", ts->int_trigger_type, + dev_dbg(&ts->client->dev, "INT trigger type:%x, irq=%d", + ts->int_trigger_type, ts->client->irq); ret = request_threaded_irq(ts->client->irq, NULL, @@ -1206,9 +1205,7 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts) { int ret; char phys[PHY_BUF_SIZE]; -#if GTP_HAVE_TOUCH_KEY int index = 0; -#endif ts->input_dev = input_allocate_device(); if (ts->input_dev == NULL) { @@ -1224,26 +1221,24 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts) /* in case of "out of memory" */ input_mt_init_slots(ts->input_dev, 10, 0); - for (index = 0; index < ts->pdata->num_button; index++) { - input_set_capability(ts->input_dev, + if (ts->pdata->have_touch_key) { + for (index = 0; index < ts->pdata->num_button; index++) { + input_set_capability(ts->input_dev, EV_KEY, ts->pdata->button_map[index]); + } } + if (ts->pdata->slide_wakeup) + input_set_capability(ts->input_dev, EV_KEY, KEY_POWER); -#if GTP_SLIDE_WAKEUP - input_set_capability(ts->input_dev, EV_KEY, KEY_POWER); -#endif - -#if GTP_WITH_PEN - /* pen support */ - __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit); - __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); - __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit); -#endif + if (ts->pdata->with_pen) { /* pen support */ + __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); + __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit); + } -#if GTP_CHANGE_X2Y - swap(ts->abs_x_max, ts->abs_y_max); -#endif + if (ts->pdata->change_x2y) + swap(ts->abs_x_max, ts->abs_y_max); input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0); @@ -1283,7 +1278,7 @@ exit_free_inputdev: static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA) { return (regulator_count_voltages(reg) > 0) ? - regulator_set_optimum_mode(reg, load_uA) : 0; + regulator_set_load(reg, load_uA) : 0; } /** @@ -1828,7 +1823,7 @@ static int goodix_parse_dt(struct device *dev, u32 temp_val, num_buttons; u32 button_map[MAX_BUTTONS]; char prop_name[PROP_NAME_SIZE]; - int i, read_cfg_num; + int i, read_cfg_num, temp; rc = goodix_ts_get_dt_coords(dev, "goodix,panel-coords", pdata); if (rc && (rc != -EINVAL)) @@ -1846,6 +1841,25 @@ static int goodix_parse_dt(struct device *dev, pdata->enable_power_off = of_property_read_bool(np, "goodix,enable-power-off"); + + pdata->have_touch_key = of_property_read_bool(np, + "goodix,have-touch-key"); + + pdata->driver_send_cfg = of_property_read_bool(np, + "goodix,driver-send-cfg"); + + pdata->change_x2y = of_property_read_bool(np, + "goodix,change-x2y"); + + pdata->with_pen = of_property_read_bool(np, + "goodix,with-pen"); + + pdata->slide_wakeup = of_property_read_bool(np, + "goodix,slide-wakeup"); + + pdata->dbl_clk_wakeup = of_property_read_bool(np, + "goodix,dbl_clk_wakeup"); + /* reset, irq gpio info */ pdata->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &pdata->reset_gpio_flags); @@ -1891,14 +1905,15 @@ static int goodix_parse_dt(struct device *dev, read_cfg_num = 0; for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) { + temp = 0; snprintf(prop_name, sizeof(prop_name), "goodix,cfg-data%d", i); - prop = of_find_property(np, prop_name, - &pdata->config_data_len[i]); + prop = of_find_property(np, prop_name, &temp); if (!prop || !prop->value) { pdata->config_data_len[i] = 0; pdata->config_data[i] = NULL; continue; } + pdata->config_data_len[i] = temp; pdata->config_data[i] = devm_kzalloc(dev, GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH, GFP_KERNEL); @@ -2101,8 +2116,6 @@ exit_free_irq: #endif if (ts->use_irq) free_irq(client->irq, ts); - else - hrtimer_cancel(&ts->timer); cancel_work_sync(&ts->work); flush_workqueue(ts->goodix_wq); destroy_workqueue(ts->goodix_wq); @@ -2166,8 +2179,6 @@ static int goodix_ts_remove(struct i2c_client *client) if (ts) { if (ts->use_irq) free_irq(client->irq, ts); - else - hrtimer_cancel(&ts->timer); cancel_work_sync(&ts->work); flush_workqueue(ts->goodix_wq); @@ -2225,23 +2236,21 @@ static int goodix_ts_suspend(struct device *dev) gtp_esd_switch(ts->client, SWITCH_OFF); #endif -#if GTP_SLIDE_WAKEUP - ret = gtp_enter_doze(ts); -#else - if (ts->use_irq) - gtp_irq_disable(ts); - else - hrtimer_cancel(&ts->timer); + if (ts->pdata->slide_wakeup) { + ret = gtp_enter_doze(ts); + } else { + if (ts->use_irq) + gtp_irq_disable(ts); - for (i = 0; i < GTP_MAX_TOUCH; i++) - gtp_touch_up(ts, i); + for (i = 0; i < GTP_MAX_TOUCH; i++) + gtp_touch_up(ts, i); - input_sync(ts->input_dev); + input_sync(ts->input_dev); - ret = gtp_enter_sleep(ts); -#endif - if (ret < 0) - dev_err(&ts->client->dev, "GTP early suspend failed\n"); + ret = gtp_enter_sleep(ts); + if (ret < 0) + dev_err(&ts->client->dev, "GTP early suspend failed.\n"); + } /* to avoid waking up while not sleeping, * delay 48 + 10ms to ensure reliability */ @@ -2273,18 +2282,14 @@ static int goodix_ts_resume(struct device *dev) mutex_lock(&ts->lock); ret = gtp_wakeup_sleep(ts); -#if GTP_SLIDE_WAKEUP - doze_status = DOZE_DISABLED; -#endif + if (ts->pdata->slide_wakeup) + doze_status = DOZE_DISABLED; if (ret <= 0) dev_err(&ts->client->dev, "GTP resume failed\n"); if (ts->use_irq) gtp_irq_enable(ts); - else - hrtimer_start(&ts->timer, - ktime_set(1, 0), HRTIMER_MODE_REL); #if GTP_ESD_PROTECT gtp_esd_switch(ts->client, SWITCH_ON); diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h index 779a0ddd93f8..1e85e2fce276 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx.h +++ b/drivers/input/touchscreen/gt9xx/gt9xx.h @@ -60,6 +60,12 @@ struct goodix_ts_platform_data { u8 *config_data[GOODIX_MAX_CFG_GROUP]; u32 button_map[MAX_BUTTONS]; u8 num_button; + bool have_touch_key; + bool driver_send_cfg; + bool change_x2y; + bool with_pen; + bool slide_wakeup; + bool dbl_clk_wakeup; }; struct goodix_ts_data { spinlock_t irq_lock; @@ -69,6 +75,7 @@ struct goodix_ts_data { struct hrtimer timer; struct workqueue_struct *goodix_wq; struct work_struct work; + char fw_name[GTP_FW_NAME_MAXSIZE]; struct delayed_work goodix_update_work; s32 irq_is_disabled; s32 use_irq; @@ -107,17 +114,7 @@ extern u16 total_len; /***************************PART1:ON/OFF define*******************************/ #define GTP_CUSTOM_CFG 0 -#define GTP_CHANGE_X2Y 0 -#define GTP_DRIVER_SEND_CFG 1 -#define GTP_HAVE_TOUCH_KEY 1 - #define GTP_ESD_PROTECT 0 -#define GTP_WITH_PEN 0 - -/* This cannot work when enable-power-off is on */ -#define GTP_SLIDE_WAKEUP 0 -/* double-click wakeup, function together with GTP_SLIDE_WAKEUP */ -#define GTP_DBL_CLK_WAKEUP 0 #define GTP_IRQ_TAB {\ IRQ_TYPE_EDGE_RISING,\ diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c index a91256c576e3..6bc243492272 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c +++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c @@ -67,6 +67,8 @@ #define FAIL 0 #define SUCCESS 1 +#define RESET_DELAY_US 20000 + struct st_fw_head { u8 hw_info[4]; /* hardware info */ u8 pid[8]; /* product id */ @@ -390,7 +392,7 @@ s32 gup_enter_update_mode(struct i2c_client *client) /* step1:RST output low last at least 2ms */ gpio_direction_output(ts->pdata->reset_gpio, 0); - usleep(20000); + usleep_range(RESET_DELAY_US, RESET_DELAY_US + 1); /* step2:select I2C slave addr,INT:0--0xBA;1--0x28. */ gpio_direction_output(ts->pdata->irq_gpio, @@ -565,8 +567,8 @@ static s8 gup_update_config(struct i2c_client *client, !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3)) { chip_cfg_len = 228; } - pr_debug("config file ASCII len:%d", cfg->size); - pr_debug("need config binary len:%d", chip_cfg_len); + pr_debug("config file ASCII len: %zu", cfg->size); + pr_debug("need config binary len: %u", chip_cfg_len); if ((cfg->size + 5) < chip_cfg_len * 5) { pr_err("Config length error"); return -EINVAL; @@ -643,7 +645,7 @@ static s32 gup_get_firmware_file(struct i2c_client *client, return -EEXIST; } - dev_dbg(&client->dev, "Config File: %s size=%d", path, fw->size); + dev_dbg(&client->dev, "Config File: %s size: %zu", path, fw->size); msg->fw_data = devm_kzalloc(&client->dev, fw->size, GFP_KERNEL); if (!msg->fw_data) { diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index bc94dff08d21..325bdb35e8a3 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -82,6 +82,8 @@ #define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25) #define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000) #define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100) +#define CURRENT_MA_TO_REG_VAL(curr_ma, ires_ua) ((curr_ma * 1000) / ires_ua - 1) +#define SAFETY_TMR_TO_REG_VAL(duration_ms) ((duration_ms / 10) - 1) #define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6 #define FLASH_LED_WARMUP_DELAY_DEFAULT 2 @@ -97,8 +99,6 @@ #define FLASH_LED_VLED_MAX_DEFAULT_UV 3500000 #define FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA 4500000 #define FLASH_LED_RPARA_DEFAULT_UOHM 0 -#define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1 -#define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10 #define FLASH_LED_SAFETY_TMR_ENABLE BIT(7) #define FLASH_LED_LMH_LEVEL_DEFAULT 0 #define FLASH_LED_LMH_MITIGATION_ENABLE 1 @@ -738,7 +738,8 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) prgm_current_ma = min(prgm_current_ma, fnode->max_current); fnode->current_ma = prgm_current_ma; fnode->cdev.brightness = prgm_current_ma; - fnode->current_reg_val = prgm_current_ma * 1000 / fnode->ires_ua + 1; + fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma, + fnode->ires_ua); fnode->led_on = prgm_current_ma != 0; } @@ -1341,9 +1342,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 = (u8)(((val - - FLASH_LED_SAFETY_TMR_VAL_OFFSET) / - FLASH_LED_SAFETY_TMR_VAL_DIVISOR) | + fnode->duration = (u8)(SAFETY_TMR_TO_REG_VAL(val) | FLASH_LED_SAFETY_TMR_ENABLE); } else if (rc == -EINVAL) { if (fnode->type == FLASH_LED_TYPE_FLASH) { diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c index d9626c29ce76..894c1d88b3ef 100644 --- a/drivers/leds/leds-qpnp-wled.c +++ b/drivers/leds/leds-qpnp-wled.c @@ -25,6 +25,7 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/leds-qpnp-wled.h> +#include <linux/qpnp/qpnp-revid.h> #define QPNP_IRQ_FLAGS (IRQF_TRIGGER_RISING | \ IRQF_TRIGGER_FALLING | \ @@ -44,15 +45,19 @@ #define QPNP_WLED_SWITCH_FREQ_REG(b) (b + 0x4C) #define QPNP_WLED_OVP_REG(b) (b + 0x4D) #define QPNP_WLED_ILIM_REG(b) (b + 0x4E) +#define QPNP_WLED_AMOLED_VOUT_REG(b) (b + 0x4F) #define QPNP_WLED_SOFTSTART_RAMP_DLY(b) (b + 0x53) #define QPNP_WLED_VLOOP_COMP_RES_REG(b) (b + 0x55) #define QPNP_WLED_VLOOP_COMP_GM_REG(b) (b + 0x56) #define QPNP_WLED_PSM_CTRL_REG(b) (b + 0x5B) #define QPNP_WLED_SC_PRO_REG(b) (b + 0x5E) +#define QPNP_WLED_SWIRE_AVDD_REG(b) (b + 0x5F) +#define QPNP_WLED_CTRL_SPARE_REG(b) (b + 0xDF) #define QPNP_WLED_TEST1_REG(b) (b + 0xE2) #define QPNP_WLED_TEST4_REG(b) (b + 0xE5) #define QPNP_WLED_REF_7P7_TRIM_REG(b) (b + 0xF2) +#define QPNP_WLED_7P7_TRIM_MASK GENMASK(3, 0) #define QPNP_WLED_EN_MASK 0x7F #define QPNP_WLED_EN_SHIFT 7 #define QPNP_WLED_FDBK_OP_MASK 0xF8 @@ -79,16 +84,16 @@ #define QPNP_WLED_VREF_PSM_MAX_MV 750 #define QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV 450 #define QPNP_WLED_PSM_CTRL_OVERWRITE 0x80 -#define QPNP_WLED_AVDD_MIN_TRIM_VALUE -7 -#define QPNP_WLED_AVDD_MAX_TRIM_VALUE 8 -#define QPNP_WLED_AVDD_TRIM_CENTER_VALUE 7 - -#define QPNP_WLED_ILIM_MASK 0xF8 -#define QPNP_WLED_ILIM_MIN_MA 105 -#define QPNP_WLED_ILIM_MAX_MA 1980 -#define QPNP_WLED_ILIM_STEP_MA 280 -#define QPNP_WLED_DFLT_ILIM_MA 980 -#define QPNP_WLED_ILIM_OVERWRITE 0x80 + +#define QPNP_WLED_ILIM_MASK GENMASK(2, 0) +#define QPNP_WLED_ILIM_OVERWRITE BIT(7) +#define PMI8994_WLED_ILIM_MIN_MA 105 +#define PMI8994_WLED_ILIM_MAX_MA 1980 +#define PMI8994_WLED_DFLT_ILIM_MA 980 +#define PMI8994_AMOLED_DFLT_ILIM_MA 385 +#define PMICOBALT_WLED_ILIM_MAX_MA 1500 +#define PMICOBALT_WLED_DFLT_ILIM_MA 970 +#define PMICOBALT_AMOLED_DFLT_ILIM_MA 620 #define QPNP_WLED_BOOST_DUTY_MASK 0xFC #define QPNP_WLED_BOOST_DUTY_STEP_NS 52 #define QPNP_WLED_BOOST_DUTY_MIN_NS 26 @@ -98,11 +103,7 @@ #define QPNP_WLED_SWITCH_FREQ_800_KHZ 800 #define QPNP_WLED_SWITCH_FREQ_1600_KHZ 1600 #define QPNP_WLED_SWITCH_FREQ_OVERWRITE 0x80 -#define QPNP_WLED_OVP_MASK 0xFC -#define QPNP_WLED_OVP_17800_MV 17800 -#define QPNP_WLED_OVP_19400_MV 19400 -#define QPNP_WLED_OVP_29500_MV 29500 -#define QPNP_WLED_OVP_31000_MV 31000 +#define QPNP_WLED_OVP_MASK GENMASK(1, 0) #define QPNP_WLED_TEST4_EN_VREF_UP 0x32 #define QPNP_WLED_INT_EN_SET_OVP_EN 0x02 #define QPNP_WLED_OVP_FLT_SLEEP_US 10 @@ -198,6 +199,22 @@ #define QPNP_WLED_MIN_MSLEEP 20 #define QPNP_WLED_SC_DLY_MS 20 +#define NUM_SUPPORTED_AVDD_VOLTAGES 6 +#define QPNP_WLED_DFLT_AVDD_MV 7600 +#define QPNP_WLED_AVDD_MIN_MV 5650 +#define QPNP_WLED_AVDD_MAX_MV 7900 +#define QPNP_WLED_AVDD_STEP_MV 150 +#define QPNP_WLED_AVDD_MIN_TRIM_VAL 0x0 +#define QPNP_WLED_AVDD_MAX_TRIM_VAL 0xF +#define QPNP_WLED_AVDD_SEL_SPMI_BIT BIT(7) +#define QPNP_WLED_AVDD_SET_BIT BIT(4) + +#define NUM_SUPPORTED_OVP_THRESHOLDS 4 +#define NUM_SUPPORTED_ILIM_THRESHOLDS 8 + +#define QPNP_WLED_AVDD_MV_TO_REG(val) \ + ((val - QPNP_WLED_AVDD_MIN_MV) / QPNP_WLED_AVDD_STEP_MV) + /* output feedback mode */ enum qpnp_wled_fdbk_op { QPNP_WLED_FDBK_AUTO, @@ -230,10 +247,38 @@ static u8 qpnp_wled_sink_dbg_regs[] = { 0xe6, }; +static int qpnp_wled_avdd_target_voltages[NUM_SUPPORTED_AVDD_VOLTAGES] = { + 7900, 7600, 7300, 6400, 6100, 5800, +}; + +static u8 qpnp_wled_ovp_reg_settings[NUM_SUPPORTED_AVDD_VOLTAGES] = { + 0x0, 0x0, 0x1, 0x2, 0x2, 0x3, +}; + +static int qpnp_wled_avdd_trim_adjustments[NUM_SUPPORTED_AVDD_VOLTAGES] = { + 3, 0, -2, 7, 3, 3, +}; + +static int qpnp_wled_ovp_thresholds_pmi8994[NUM_SUPPORTED_OVP_THRESHOLDS] = { + 31000, 29500, 19400, 17800, +}; + +static int qpnp_wled_ovp_thresholds_pmicobalt[NUM_SUPPORTED_OVP_THRESHOLDS] = { + 31100, 29600, 19600, 18100, +}; + +static int qpnp_wled_ilim_settings_pmi8994[NUM_SUPPORTED_ILIM_THRESHOLDS] = { + 105, 385, 660, 980, 1150, 1420, 1700, 1980, +}; + +static int qpnp_wled_ilim_settings_pmicobalt[NUM_SUPPORTED_ILIM_THRESHOLDS] = { + 105, 280, 450, 620, 970, 1150, 1300, 1500, +}; + /** * qpnp_wled - wed data structure * @ cdev - led class device - * @ spmi - spmi device + * @ pdev - platform device * @ work - worker for led operation * @ lock - mutex lock for exclusive access * @ fdbk_op - output feedback mode @@ -241,7 +286,7 @@ static u8 qpnp_wled_sink_dbg_regs[] = { * @ ovp_irq - over voltage protection irq * @ sc_irq - short circuit irq * @ sc_cnt - short circuit irq count - * @ avdd_trim_steps_from_center - number of steps to trim from center value + * @ avdd_target_voltage_mv - target voltage for AVDD module in mV * @ ctrl_base - base address for wled ctrl * @ sink_base - base address for wled sink * @ ibb_base - base address for IBB(Inverting Buck Boost) @@ -264,6 +309,7 @@ static u8 qpnp_wled_sink_dbg_regs[] = { * @ cons_sync_write_delay_us - delay between two consecutive writes to SYNC * @ strings - supported list of strings * @ num_strings - number of strings + * @ avdd_mode_spmi - enable avdd programming via spmi * @ en_9b_dim_res - enable or disable 9bit dimming * @ en_phase_stag - enable or disable phase staggering * @ en_cabc - enable or disable cabc @@ -276,14 +322,16 @@ struct qpnp_wled { struct led_classdev cdev; struct platform_device *pdev; struct regmap *regmap; + struct pmic_revid_data *pmic_rev_id; struct work_struct work; struct mutex lock; + struct mutex bus_lock; enum qpnp_wled_fdbk_op fdbk_op; enum qpnp_wled_dim_mode dim_mode; int ovp_irq; int sc_irq; u32 sc_cnt; - u32 avdd_trim_steps_from_center; + u32 avdd_target_voltage_mv; u16 ctrl_base; u16 sink_base; u16 mod_freq_khz; @@ -304,6 +352,7 @@ struct qpnp_wled { u16 cons_sync_write_delay_us; u8 strings[QPNP_WLED_MAX_STRINGS]; u8 num_strings; + bool avdd_mode_spmi; bool en_9b_dim_res; bool en_phase_stag; bool en_cabc; @@ -319,39 +368,79 @@ static int qpnp_wled_read_reg(struct qpnp_wled *wled, u8 *data, u16 addr) uint val; rc = regmap_read(wled->regmap, addr, &val); - if (rc < 0) + if (rc < 0) { dev_err(&wled->pdev->dev, "Error reading address: %x(%d)\n", addr, rc); + return rc; + } + *data = (u8)val; - return rc; + return 0; } /* helper to write a pmic register */ -static int qpnp_wled_write_reg(struct qpnp_wled *wled, u8 *data, u16 addr) +static int qpnp_wled_write_reg(struct qpnp_wled *wled, u8 data, u16 addr) { int rc; - rc = regmap_write(wled->regmap, addr, *data); + mutex_lock(&wled->bus_lock); + rc = regmap_write(wled->regmap, addr, data); + if (rc < 0) { + dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n", + addr, rc); + goto out; + } + + dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data); +out: + mutex_unlock(&wled->bus_lock); + return rc; +} + +static int qpnp_wled_masked_write_reg(struct qpnp_wled *wled, u8 mask, u8 *data, + u16 addr) +{ + u8 reg; + int rc; + + rc = qpnp_wled_read_reg(wled, ®, addr); if (rc < 0) - dev_err(&wled->pdev->dev, - "Error writing address: %x(%d)\n", addr, rc); + return rc; + + reg &= ~mask; + reg |= *data & mask; - dev_dbg(&wled->pdev->dev, "write: WLED_0x%x = 0x%x\n", addr, *data); + rc = qpnp_wled_write_reg(wled, reg, addr); return rc; } -static int qpnp_wled_sec_access(struct qpnp_wled *wled, u16 base_addr) +static int qpnp_wled_sec_write_reg(struct qpnp_wled *wled, u8 data, u16 addr) { int rc; u8 reg = QPNP_WLED_SEC_UNLOCK; + u16 base_addr = addr & 0xFF00; - rc = qpnp_wled_write_reg(wled, ®, - QPNP_WLED_SEC_ACCESS_REG(base_addr)); - if (rc) - return rc; + mutex_lock(&wled->bus_lock); + rc = regmap_write(wled->regmap, QPNP_WLED_SEC_ACCESS_REG(base_addr), + reg); + if (rc < 0) { + dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n", + QPNP_WLED_SEC_ACCESS_REG(base_addr), rc); + goto out; + } - return 0; + rc = regmap_write(wled->regmap, addr, data); + if (rc < 0) { + dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n", + addr, rc); + goto out; + } + + dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data); +out: + mutex_unlock(&wled->bus_lock); + return rc; } static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled) @@ -361,7 +450,7 @@ static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled) /* sync */ reg = QPNP_WLED_SYNC; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_SYNC_REG(wled->sink_base)); if (rc < 0) return rc; @@ -371,7 +460,7 @@ static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled) wled->cons_sync_write_delay_us + 1); reg = QPNP_WLED_SYNC_RESET; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_SYNC_REG(wled->sink_base)); if (rc < 0) return rc; @@ -388,7 +477,7 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) /* set brightness registers */ for (i = 0; i < wled->num_strings; i++) { reg = level & QPNP_WLED_BRIGHT_LSB_MASK; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base, wled->strings[i])); if (rc < 0) @@ -396,7 +485,7 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT; reg = reg & QPNP_WLED_BRIGHT_MSB_MASK; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base, wled->strings[i])); if (rc < 0) @@ -421,7 +510,7 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled, /* disable OVP fault interrupt */ if (state) { reg = QPNP_WLED_INT_EN_SET_OVP_EN; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_INT_EN_CLR(base_addr)); if (rc) return rc; @@ -433,7 +522,7 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled, return rc; reg &= QPNP_WLED_MODULE_EN_MASK; reg |= (state << QPNP_WLED_MODULE_EN_SHIFT); - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_MODULE_EN_REG(base_addr)); if (rc) return rc; @@ -442,7 +531,7 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled, if (state && (wled->ovp_irq > 0)) { udelay(QPNP_WLED_OVP_FLT_SLEEP_US); reg = QPNP_WLED_INT_EN_SET_OVP_EN; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_INT_EN_SET(base_addr)); if (rc) return rc; @@ -678,7 +767,7 @@ static ssize_t qpnp_wled_dim_mode_store(struct device *dev, reg |= temp; } - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_MOD_REG(wled->sink_base)); if (rc) return rc; @@ -722,7 +811,7 @@ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev, reg &= QPNP_WLED_FS_CURR_MASK; temp = data / QPNP_WLED_FS_CURR_STEP_UA; reg |= temp; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_FS_CURR_REG(wled->sink_base, wled->strings[i])); if (rc) @@ -838,11 +927,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) reg &= QPNP_WLED_DISP_SEL_MASK; reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT); - rc = qpnp_wled_sec_access(wled, base_addr); - if (rc) - return rc; - - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_sec_write_reg(wled, reg, QPNP_WLED_DISP_SEL_REG(base_addr)); if (rc) return rc; @@ -863,7 +948,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/ QPNP_WLED_VREF_PSM_STEP_MV); reg |= QPNP_WLED_PSM_CTRL_OVERWRITE; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base)); if (rc) return rc; @@ -887,7 +972,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)/ QPNP_WLED_LOOP_COMP_RES_STEP_KOHM); reg |= QPNP_WLED_VLOOP_COMP_RES_OVERWRITE; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base)); if (rc) return rc; @@ -905,7 +990,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) reg &= QPNP_WLED_VLOOP_COMP_GM_MASK; reg |= (wled->loop_ea_gm | QPNP_WLED_VLOOP_COMP_GM_OVERWRITE); - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_VLOOP_COMP_GM_REG(wled->ctrl_base)); if (rc) return rc; @@ -916,12 +1001,8 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) if (rc < 0) return rc; - rc = qpnp_wled_sec_access(wled, base_addr); - if (rc) - return rc; - reg |= QPNP_WLED_TEST4_EN_IIND_UP; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_sec_write_reg(wled, reg, QPNP_WLED_TEST4_REG(base_addr)); if (rc) return rc; @@ -929,12 +1010,8 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) /* * enable VREF_UP to avoid false ovp on low brightness for LCD */ - rc = qpnp_wled_sec_access(wled, base_addr); - if (rc) - return rc; - reg = QPNP_WLED_TEST4_EN_VREF_UP; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_sec_write_reg(wled, reg, QPNP_WLED_TEST4_REG(base_addr)); if (rc) return rc; @@ -968,6 +1045,209 @@ static irqreturn_t qpnp_wled_sc_irq(int irq, void *_wled) return IRQ_HANDLED; } +static bool is_avdd_trim_adjustment_required(struct qpnp_wled *wled) +{ + int rc; + u8 reg = 0; + + /* + * AVDD trim adjustment is not required for pmicobalt/pm2falcon and not + * supported for pmi8994. + */ + if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE || + wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE || + wled->pmic_rev_id->pmic_subtype == PMI8994_SUBTYPE) + return false; + + /* + * Configure TRIM_REG only if disp_type_amoled and it has + * not already been programmed by bootloader. + */ + if (!wled->disp_type_amoled) + return false; + + rc = qpnp_wled_read_reg(wled, ®, + QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base)); + if (rc < 0) + return false; + + return !(reg & QPNP_WLED_AVDD_SET_BIT); +} + +static int qpnp_wled_ovp_config(struct qpnp_wled *wled) +{ + int rc, i, *ovp_table; + u8 reg; + + /* + * Configure the OVP register based on ovp_mv only if display type is + * not AMOLED. + */ + if (wled->disp_type_amoled) + return 0; + + if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE || + wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE) + ovp_table = qpnp_wled_ovp_thresholds_pmicobalt; + else + ovp_table = qpnp_wled_ovp_thresholds_pmi8994; + + for (i = 0; i < NUM_SUPPORTED_OVP_THRESHOLDS; i++) { + if (wled->ovp_mv == ovp_table[i]) + break; + } + + if (i == NUM_SUPPORTED_OVP_THRESHOLDS) { + dev_err(&wled->pdev->dev, + "Invalid ovp threshold specified in device tree\n"); + return -EINVAL; + } + + reg = i & QPNP_WLED_OVP_MASK; + rc = qpnp_wled_masked_write_reg(wled, QPNP_WLED_OVP_MASK, ®, + QPNP_WLED_OVP_REG(wled->ctrl_base)); + if (rc) + return rc; + + return 0; +} + +static int qpnp_wled_avdd_trim_config(struct qpnp_wled *wled) +{ + int rc, i; + u8 reg; + + for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) { + if (wled->avdd_target_voltage_mv == + qpnp_wled_avdd_target_voltages[i]) + break; + } + + if (i == NUM_SUPPORTED_AVDD_VOLTAGES) { + dev_err(&wled->pdev->dev, + "Invalid avdd target voltage specified in device tree\n"); + return -EINVAL; + } + + /* Update WLED_OVP register based on desired target voltage */ + reg = qpnp_wled_ovp_reg_settings[i]; + rc = qpnp_wled_masked_write_reg(wled, QPNP_WLED_OVP_MASK, ®, + QPNP_WLED_OVP_REG(wled->ctrl_base)); + if (rc) + return rc; + + /* Update WLED_TRIM register based on desired target voltage */ + rc = qpnp_wled_read_reg(wled, ®, + QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base)); + if (rc) + return rc; + + reg += qpnp_wled_avdd_trim_adjustments[i]; + if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL || + (s8)reg > QPNP_WLED_AVDD_MAX_TRIM_VAL) { + dev_dbg(&wled->pdev->dev, + "adjusted trim %d is not within range, capping it\n", + (s8)reg); + if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL) + reg = QPNP_WLED_AVDD_MIN_TRIM_VAL; + else + reg = QPNP_WLED_AVDD_MAX_TRIM_VAL; + } + + reg &= QPNP_WLED_7P7_TRIM_MASK; + rc = qpnp_wled_sec_write_reg(wled, reg, + QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base)); + if (rc < 0) + dev_err(&wled->pdev->dev, "Write to 7P7_TRIM register failed, rc=%d\n", + rc); + return rc; +} + +static int qpnp_wled_avdd_mode_config(struct qpnp_wled *wled) +{ + int rc; + u8 reg = 0; + + /* + * At present, configuring the mode to SPMI/SWIRE for controlling + * AVDD voltage is available only in pmicobalt/pm2falcon. + */ + if (wled->pmic_rev_id->pmic_subtype != PMICOBALT_SUBTYPE && + wled->pmic_rev_id->pmic_subtype != PM2FALCON_SUBTYPE) + return 0; + + /* AMOLED_VOUT should be configured for AMOLED */ + if (!wled->disp_type_amoled) + return 0; + + /* Configure avdd register */ + if (wled->avdd_target_voltage_mv > QPNP_WLED_AVDD_MAX_MV) { + dev_dbg(&wled->pdev->dev, "Capping avdd target voltage to %d\n", + QPNP_WLED_AVDD_MAX_MV); + wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MAX_MV; + } else if (wled->avdd_target_voltage_mv < QPNP_WLED_AVDD_MIN_MV) { + dev_info(&wled->pdev->dev, "Capping avdd target voltage to %d\n", + QPNP_WLED_AVDD_MIN_MV); + wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MIN_MV; + } + + reg = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv); + + if (wled->avdd_mode_spmi) { + reg |= QPNP_WLED_AVDD_SEL_SPMI_BIT; + rc = qpnp_wled_write_reg(wled, reg, + QPNP_WLED_AMOLED_VOUT_REG(wled->ctrl_base)); + } else { + rc = qpnp_wled_write_reg(wled, reg, + QPNP_WLED_SWIRE_AVDD_REG(wled->ctrl_base)); + } + + if (rc < 0) + dev_err(&wled->pdev->dev, "Write to VOUT/AVDD register failed, rc=%d\n", + rc); + return rc; +} + +static int qpnp_wled_ilim_config(struct qpnp_wled *wled) +{ + int rc, i, *ilim_table; + u8 reg; + + if (wled->ilim_ma < PMI8994_WLED_ILIM_MIN_MA) + wled->ilim_ma = PMI8994_WLED_ILIM_MIN_MA; + + if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE || + wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE) { + ilim_table = qpnp_wled_ilim_settings_pmicobalt; + if (wled->ilim_ma > PMICOBALT_WLED_ILIM_MAX_MA) + wled->ilim_ma = PMICOBALT_WLED_ILIM_MAX_MA; + } else { + ilim_table = qpnp_wled_ilim_settings_pmi8994; + if (wled->ilim_ma > PMI8994_WLED_ILIM_MAX_MA) + wled->ilim_ma = PMI8994_WLED_ILIM_MAX_MA; + } + + for (i = 0; i < NUM_SUPPORTED_ILIM_THRESHOLDS; i++) { + if (wled->ilim_ma == ilim_table[i]) + break; + } + + if (i == NUM_SUPPORTED_ILIM_THRESHOLDS) { + dev_err(&wled->pdev->dev, + "Invalid ilim threshold specified in device tree\n"); + return -EINVAL; + } + + reg = (i & QPNP_WLED_ILIM_MASK) | QPNP_WLED_ILIM_OVERWRITE; + rc = qpnp_wled_masked_write_reg(wled, + QPNP_WLED_ILIM_MASK | QPNP_WLED_ILIM_OVERWRITE, + ®, QPNP_WLED_ILIM_REG(wled->ctrl_base)); + if (rc < 0) + dev_err(&wled->pdev->dev, "Write to ILIM register failed, rc=%d\n", + rc); + return rc; +} + /* Configure WLED registers */ static int qpnp_wled_config(struct qpnp_wled *wled) { @@ -986,7 +1266,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled) return rc; reg &= QPNP_WLED_FDBK_OP_MASK; reg |= wled->fdbk_op; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base)); if (rc) return rc; @@ -1004,35 +1284,21 @@ static int qpnp_wled_config(struct qpnp_wled *wled) reg &= QPNP_WLED_VREF_MASK; temp = wled->vref_mv - QPNP_WLED_VREF_MIN_MV; reg |= (temp / QPNP_WLED_VREF_STEP_MV); - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_VREF_REG(wled->ctrl_base)); if (rc) return rc; /* Configure the ILIM register */ - if (wled->ilim_ma < QPNP_WLED_ILIM_MIN_MA) - wled->ilim_ma = QPNP_WLED_ILIM_MIN_MA; - else if (wled->ilim_ma > QPNP_WLED_ILIM_MAX_MA) - wled->ilim_ma = QPNP_WLED_ILIM_MAX_MA; - - rc = qpnp_wled_read_reg(wled, ®, - QPNP_WLED_ILIM_REG(wled->ctrl_base)); - if (rc < 0) + rc = qpnp_wled_ilim_config(wled); + if (rc < 0) { + pr_err("Error in configuring wled ilim, rc=%d\n", rc); return rc; - temp = (wled->ilim_ma / QPNP_WLED_ILIM_STEP_MA); - if (temp != (reg & ~QPNP_WLED_ILIM_MASK)) { - reg &= QPNP_WLED_ILIM_MASK; - reg |= temp; - reg |= QPNP_WLED_ILIM_OVERWRITE; - rc = qpnp_wled_write_reg(wled, ®, - QPNP_WLED_ILIM_REG(wled->ctrl_base)); - if (rc) - return rc; } /* Configure the Soft start Ramp delay: for AMOLED - 0,for LCD - 2 */ reg = (wled->disp_type_amoled) ? 0 : 2; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base)); if (rc) return rc; @@ -1049,7 +1315,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled) return rc; reg &= QPNP_WLED_BOOST_DUTY_MASK; reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS); - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base)); if (rc) return rc; @@ -1066,62 +1332,27 @@ static int qpnp_wled_config(struct qpnp_wled *wled) return rc; reg &= QPNP_WLED_SWITCH_FREQ_MASK; reg |= (temp | QPNP_WLED_SWITCH_FREQ_OVERWRITE); - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base)); if (rc) return rc; - /* Configure the OVP register */ - if (wled->ovp_mv <= QPNP_WLED_OVP_17800_MV) { - wled->ovp_mv = QPNP_WLED_OVP_17800_MV; - temp = 3; - } else if (wled->ovp_mv <= QPNP_WLED_OVP_19400_MV) { - wled->ovp_mv = QPNP_WLED_OVP_19400_MV; - temp = 2; - } else if (wled->ovp_mv <= QPNP_WLED_OVP_29500_MV) { - wled->ovp_mv = QPNP_WLED_OVP_29500_MV; - temp = 1; - } else { - wled->ovp_mv = QPNP_WLED_OVP_31000_MV; - temp = 0; - } - - rc = qpnp_wled_read_reg(wled, ®, - QPNP_WLED_OVP_REG(wled->ctrl_base)); - if (rc < 0) - return rc; - reg &= QPNP_WLED_OVP_MASK; - reg |= temp; - rc = qpnp_wled_write_reg(wled, ®, - QPNP_WLED_OVP_REG(wled->ctrl_base)); - if (rc) + rc = qpnp_wled_ovp_config(wled); + if (rc < 0) { + pr_err("Error in configuring OVP threshold, rc=%d\n", rc); return rc; + } - if (wled->disp_type_amoled) { - /* Configure avdd trim register */ - rc = qpnp_wled_sec_access(wled, wled->ctrl_base); - if (rc) - return rc; - - /* Check if wled->avdd_trim_steps_from_center is negative */ - if ((s32)wled->avdd_trim_steps_from_center < - QPNP_WLED_AVDD_MIN_TRIM_VALUE) { - wled->avdd_trim_steps_from_center = - QPNP_WLED_AVDD_MIN_TRIM_VALUE; - } else if ((s32)wled->avdd_trim_steps_from_center > - QPNP_WLED_AVDD_MAX_TRIM_VALUE) { - wled->avdd_trim_steps_from_center = - QPNP_WLED_AVDD_MAX_TRIM_VALUE; - } - reg = wled->avdd_trim_steps_from_center + - QPNP_WLED_AVDD_TRIM_CENTER_VALUE; - - rc = qpnp_wled_write_reg(wled, ®, - QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base)); - if (rc) + if (is_avdd_trim_adjustment_required(wled)) { + rc = qpnp_wled_avdd_trim_config(wled); + if (rc < 0) return rc; } + rc = qpnp_wled_avdd_mode_config(wled); + if (rc < 0) + return rc; + /* Configure the MODULATION register */ if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) { wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ; @@ -1166,7 +1397,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled) reg |= wled->dim_mode; } - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_MOD_REG(wled->sink_base)); if (rc) return rc; @@ -1184,7 +1415,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled) reg &= QPNP_WLED_HYB_THRES_MASK; temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1; reg |= temp; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_HYB_THRES_REG(wled->sink_base)); if (rc) return rc; @@ -1195,17 +1426,14 @@ static int qpnp_wled_config(struct qpnp_wled *wled) else reg = QPNP_WLED_SINK_TEST5_HYB; - rc = qpnp_wled_sec_access(wled, wled->sink_base); - if (rc) - return rc; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_sec_write_reg(wled, reg, QPNP_WLED_SINK_TEST5_REG(wled->sink_base)); if (rc) return rc; /* disable all current sinks and enable selected strings */ reg = 0x00; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_CURR_SINK_REG(wled->sink_base)); for (i = 0; i < wled->num_strings; i++) { @@ -1228,7 +1456,7 @@ 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, reg, QPNP_WLED_MOD_EN_REG(wled->sink_base, wled->strings[i])); if (rc) @@ -1246,7 +1474,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled) reg &= QPNP_WLED_SYNC_DLY_MASK; temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US; reg |= temp; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_SYNC_DLY_REG(wled->sink_base, wled->strings[i])); if (rc) @@ -1264,7 +1492,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled) reg &= QPNP_WLED_FS_CURR_MASK; temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA; reg |= temp; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_FS_CURR_REG(wled->sink_base, wled->strings[i])); if (rc) @@ -1278,7 +1506,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled) return rc; reg &= QPNP_WLED_CABC_MASK; reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT); - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_CABC_REG(wled->sink_base, wled->strings[i])); if (rc) @@ -1291,7 +1519,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled) return rc; temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT; reg |= (1 << temp); - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_CURR_SINK_REG(wled->sink_base)); if (rc) return rc; @@ -1348,18 +1576,14 @@ static int qpnp_wled_config(struct qpnp_wled *wled) if (wled->disp_type_amoled) reg |= QPNP_WLED_SC_PRO_EN_DSCHGR; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_SC_PRO_REG(wled->ctrl_base)); if (rc) return rc; if (wled->en_ext_pfet_sc_pro) { - rc = qpnp_wled_sec_access(wled, wled->ctrl_base); - if (rc) - return rc; - reg = QPNP_WLED_EXT_FET_DTEST2; - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_sec_write_reg(wled, reg, QPNP_WLED_TEST1_REG(wled->ctrl_base)); if (rc) return rc; @@ -1378,7 +1602,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled) temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB; reg |= (temp << 1); - rc = qpnp_wled_write_reg(wled, ®, + rc = qpnp_wled_write_reg(wled, reg, QPNP_WLED_SC_PRO_REG(wled->ctrl_base)); if (rc) return rc; @@ -1447,13 +1671,16 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) return rc; } - wled->avdd_trim_steps_from_center = 0; + wled->avdd_mode_spmi = of_property_read_bool(pdev->dev.of_node, + "qcom,avdd-mode-spmi"); + + wled->avdd_target_voltage_mv = QPNP_WLED_DFLT_AVDD_MV; rc = of_property_read_u32(pdev->dev.of_node, - "qcom,avdd-trim-steps-from-center", &temp_val); + "qcom,avdd-target-voltage-mv", &temp_val); if (!rc) { - wled->avdd_trim_steps_from_center = temp_val; + wled->avdd_target_voltage_mv = temp_val; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read avdd trim steps from center value\n"); + dev_err(&pdev->dev, "Unable to read avdd target voltage\n"); return rc; } } @@ -1507,17 +1734,33 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) return rc; } - wled->ovp_mv = QPNP_WLED_OVP_29500_MV; + if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE || + wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE) + wled->ovp_mv = 29600; + else + wled->ovp_mv = 29500; rc = of_property_read_u32(pdev->dev.of_node, "qcom,ovp-mv", &temp_val); if (!rc) { wled->ovp_mv = temp_val; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read vref\n"); + dev_err(&pdev->dev, "Unable to read ovp\n"); return rc; } - wled->ilim_ma = QPNP_WLED_DFLT_ILIM_MA; + if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE || + wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE) { + if (wled->disp_type_amoled) + wled->ilim_ma = PMICOBALT_AMOLED_DFLT_ILIM_MA; + else + wled->ilim_ma = PMICOBALT_WLED_DFLT_ILIM_MA; + } else { + if (wled->disp_type_amoled) + wled->ilim_ma = PMI8994_AMOLED_DFLT_ILIM_MA; + else + wled->ilim_ma = PMI8994_WLED_DFLT_ILIM_MA; + } + rc = of_property_read_u32(pdev->dev.of_node, "qcom,ilim-ma", &temp_val); if (!rc) { @@ -1638,6 +1881,7 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) static int qpnp_wled_probe(struct platform_device *pdev) { struct qpnp_wled *wled; + struct device_node *revid_node; int rc = 0, i; const __be32 *prop; @@ -1652,6 +1896,27 @@ static int qpnp_wled_probe(struct platform_device *pdev) wled->pdev = pdev; + revid_node = of_parse_phandle(pdev->dev.of_node, "qcom,pmic-revid", 0); + if (!revid_node) { + pr_err("Missing qcom,pmic-revid property - driver failed\n"); + return -EINVAL; + } + + wled->pmic_rev_id = get_revid_data(revid_node); + if (IS_ERR_OR_NULL(wled->pmic_rev_id)) { + pr_err("Unable to get pmic_revid rc=%ld\n", + PTR_ERR(wled->pmic_rev_id)); + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + + pr_debug("PMIC subtype %d Digital major %d\n", + wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4); + prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE, 0, 0); if (!prop) { @@ -1676,6 +1941,7 @@ static int qpnp_wled_probe(struct platform_device *pdev) return rc; } + mutex_init(&wled->bus_lock); rc = qpnp_wled_config(wled); if (rc) { dev_err(&pdev->dev, "wled config failed\n"); diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c index 594bac6c5902..0d8c6cb8f3f3 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c @@ -266,7 +266,7 @@ static int sde_rotator_update_clk(struct sde_rot_mgr *mgr) SDEROT_DBG("core_clk %lu\n", total_clk_rate); ATRACE_INT("core_clk", total_clk_rate); - sde_rotator_set_clk_rate(mgr, total_clk_rate, mgr->core_clk_idx); + sde_rotator_set_clk_rate(mgr, total_clk_rate, SDE_ROTATOR_CLK_ROT_CORE); return 0; } @@ -300,11 +300,34 @@ static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on) mgr->regulator_enable = on; } -int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable) +static int sde_rotator_enable_clk(struct sde_rot_mgr *mgr, int clk_idx) +{ + struct clk *clk; + int ret = 0; + + clk = sde_rotator_get_clk(mgr, clk_idx); + if (clk) { + ret = clk_prepare_enable(clk); + if (ret) + SDEROT_ERR("enable failed clk_idx %d\n", clk_idx); + } + + return ret; +} + +static void sde_rotator_disable_clk(struct sde_rot_mgr *mgr, int clk_idx) { struct clk *clk; + + clk = sde_rotator_get_clk(mgr, clk_idx); + if (clk) + clk_disable_unprepare(clk); +} + +int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable) +{ int ret = 0; - int i, changed = 0; + int changed = 0; if (enable) { if (mgr->rot_enable_clk_cnt == 0) @@ -323,32 +346,41 @@ int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable) if (changed) { SDEROT_EVTLOG(enable); SDEROT_DBG("Rotator clk %s\n", enable ? "enable" : "disable"); - for (i = 0; i < mgr->num_rot_clk; i++) { - clk = mgr->rot_clk[i].clk; - - if (!clk) - continue; - - if (enable) { - ret = clk_prepare_enable(clk); - if (ret) { - SDEROT_ERR( - "enable failed clk_idx %d\n", - i); - goto error; - } - } else { - clk_disable_unprepare(clk); - } - } if (enable) { + ret = sde_rotator_enable_clk(mgr, + SDE_ROTATOR_CLK_MNOC_AHB); + if (ret) + goto error_mnoc_ahb; + ret = sde_rotator_enable_clk(mgr, + SDE_ROTATOR_CLK_MDSS_AHB); + if (ret) + goto error_mdss_ahb; + ret = sde_rotator_enable_clk(mgr, + SDE_ROTATOR_CLK_MDSS_AXI); + if (ret) + goto error_mdss_axi; + ret = sde_rotator_enable_clk(mgr, + SDE_ROTATOR_CLK_ROT_CORE); + if (ret) + goto error_rot_core; + ret = sde_rotator_enable_clk(mgr, + SDE_ROTATOR_CLK_MDSS_ROT); + if (ret) + goto error_mdss_rot; + /* Active+Sleep */ msm_bus_scale_client_update_context( mgr->data_bus.bus_hdl, false, mgr->data_bus.curr_bw_uc_idx); trace_rot_bw_ao_as_context(0); } else { + sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_ROT); + sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_ROT_CORE); + sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AXI); + sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AHB); + sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MNOC_AHB); + /* Active Only */ msm_bus_scale_client_update_context( mgr->data_bus.bus_hdl, true, @@ -358,9 +390,15 @@ int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable) } return ret; -error: - for (i--; i >= 0; i--) - clk_disable_unprepare(mgr->rot_clk[i].clk); +error_mdss_rot: + sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_ROT_CORE); +error_rot_core: + sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AXI); +error_mdss_axi: + sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MDSS_AHB); +error_mdss_ahb: + sde_rotator_disable_clk(mgr, SDE_ROTATOR_CLK_MNOC_AHB); +error_mnoc_ahb: return ret; } @@ -2101,7 +2139,6 @@ static ssize_t sde_rotator_show_state(struct device *dev, SPRINT("footswitch_cnt=%d\n", mgr->res_ref_cnt); SPRINT("regulator_enable=%d\n", mgr->regulator_enable); SPRINT("enable_clk_cnt=%d\n", mgr->rot_enable_clk_cnt); - SPRINT("core_clk_idx=%d\n", mgr->core_clk_idx); for (i = 0; i < mgr->num_rot_clk; i++) if (mgr->rot_clk[i].clk) SPRINT("%s=%lu\n", mgr->rot_clk[i].clk_name, @@ -2301,17 +2338,39 @@ static int sde_rotator_bus_scale_register(struct sde_rot_mgr *mgr) return 0; } +static inline int sde_rotator_search_dt_clk(struct platform_device *pdev, + struct sde_rot_mgr *mgr, char *clk_name, int clk_idx) +{ + struct clk *tmp; + + if (clk_idx >= SDE_ROTATOR_CLK_MAX) { + SDEROT_ERR("invalid clk index %d\n", clk_idx); + return -EINVAL; + } + + tmp = devm_clk_get(&pdev->dev, clk_name); + if (IS_ERR(tmp)) { + SDEROT_ERR("unable to get clk: %s\n", clk_name); + return PTR_ERR(tmp); + } + + strlcpy(mgr->rot_clk[clk_idx].clk_name, clk_name, + sizeof(mgr->rot_clk[clk_idx].clk_name)); + + mgr->rot_clk[clk_idx].clk = tmp; + return 0; +} + static int sde_rotator_parse_dt_clk(struct platform_device *pdev, struct sde_rot_mgr *mgr) { - u32 i = 0, rc = 0; - const char *clock_name; + u32 rc = 0; int num_clk; num_clk = of_property_count_strings(pdev->dev.of_node, "clock-names"); - if (num_clk <= 0) { - SDEROT_ERR("clocks are not defined\n"); + if ((num_clk <= 0) || (num_clk > SDE_ROTATOR_CLK_MAX)) { + SDEROT_ERR("Number of clocks are out of range: %d\n", num_clk); goto clk_err; } @@ -2325,19 +2384,17 @@ static int sde_rotator_parse_dt_clk(struct platform_device *pdev, goto clk_err; } - for (i = 0; i < mgr->num_rot_clk; i++) { - u32 clock_rate = 0; - - of_property_read_string_index(pdev->dev.of_node, "clock-names", - i, &clock_name); - strlcpy(mgr->rot_clk[i].clk_name, clock_name, - sizeof(mgr->rot_clk[i].clk_name)); - - of_property_read_u32_index(pdev->dev.of_node, "clock-rate", - i, &clock_rate); - mgr->rot_clk[i].rate = clock_rate; - } - + if (sde_rotator_search_dt_clk(pdev, mgr, "mnoc_clk", + SDE_ROTATOR_CLK_MNOC_AHB) || + sde_rotator_search_dt_clk(pdev, mgr, "iface_clk", + SDE_ROTATOR_CLK_MDSS_AHB) || + sde_rotator_search_dt_clk(pdev, mgr, "axi_clk", + SDE_ROTATOR_CLK_MDSS_AXI) || + sde_rotator_search_dt_clk(pdev, mgr, "rot_core_clk", + SDE_ROTATOR_CLK_ROT_CORE) || + sde_rotator_search_dt_clk(pdev, mgr, "rot_clk", + SDE_ROTATOR_CLK_MDSS_ROT)) + rc = -EINVAL; clk_err: return rc; } @@ -2345,10 +2402,7 @@ clk_err: static int sde_rotator_register_clk(struct platform_device *pdev, struct sde_rot_mgr *mgr) { - int i, ret; - struct clk *clk; - struct sde_rot_clk *rot_clk; - int core_clk_idx = -1; + int ret; ret = sde_rotator_parse_dt_clk(pdev, mgr); if (ret) { @@ -2356,28 +2410,6 @@ static int sde_rotator_register_clk(struct platform_device *pdev, return -EINVAL; } - for (i = 0; i < mgr->num_rot_clk; i++) { - rot_clk = &mgr->rot_clk[i]; - - clk = devm_clk_get(&pdev->dev, rot_clk->clk_name); - if (IS_ERR(clk)) { - SDEROT_ERR("unable to get clk: %s\n", - rot_clk->clk_name); - return PTR_ERR(clk); - } - rot_clk->clk = clk; - - if (strcmp(rot_clk->clk_name, "rot_core_clk") == 0) - core_clk_idx = i; - } - - if (core_clk_idx < 0) { - SDEROT_ERR("undefined core clk\n"); - return -ENXIO; - } - - mgr->core_clk_idx = core_clk_idx; - return 0; } diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h index 781b03e1b974..e1b326b8eb1c 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h @@ -92,6 +92,15 @@ enum sde_rotator_ts { SDE_ROTATOR_TS_MAX }; +enum sde_rotator_clk_type { + SDE_ROTATOR_CLK_MDSS_AHB, + SDE_ROTATOR_CLK_MDSS_AXI, + SDE_ROTATOR_CLK_ROT_CORE, + SDE_ROTATOR_CLK_MDSS_ROT, + SDE_ROTATOR_CLK_MNOC_AHB, + SDE_ROTATOR_CLK_MAX +}; + struct sde_rotation_item { /* rotation request flag */ uint32_t flags; @@ -275,7 +284,6 @@ struct sde_rot_mgr { int rot_enable_clk_cnt; struct sde_rot_clk *rot_clk; int num_rot_clk; - int core_clk_idx; u32 rdot_limit; u32 wrot_limit; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c index d2bc76874c48..925b8497273a 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c @@ -2337,9 +2337,9 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr) goto error_hw_rev_init; /* set rotator CBCR to shutoff memory/periphery on clock off.*/ - clk_set_flags(mgr->rot_clk[mgr->core_clk_idx].clk, + clk_set_flags(mgr->rot_clk[SDE_ROTATOR_CLK_ROT_CORE].clk, CLKFLAG_NORETAIN_MEM); - clk_set_flags(mgr->rot_clk[mgr->core_clk_idx].clk, + clk_set_flags(mgr->rot_clk[SDE_ROTATOR_CLK_ROT_CORE].clk, CLKFLAG_NORETAIN_PERIPH); mdata->sde_rot_hw = rot; diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c index 3ed3d125f430..fbaf05e58aff 100644 --- a/drivers/mfd/wcd934x-regmap.c +++ b/drivers/mfd/wcd934x-regmap.c @@ -1922,6 +1922,10 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) case WCD934X_SIDO_NEW_VOUT_A_STARTUP: case WCD934X_SIDO_NEW_VOUT_D_STARTUP: case WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL: + case WCD934X_ANA_MBHC_MECH: + case WCD934X_ANA_MBHC_ELECT: + case WCD934X_ANA_MBHC_ZDET: + case WCD934X_ANA_MICB2: return true; } diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 644178a0cdfc..862d72cb86cf 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -3591,7 +3591,7 @@ static bool __qseecom_is_fw_image_valid(const struct firmware *fw_entry) return true; } -static int __qseecom_get_fw_size(char *appname, uint32_t *fw_size, +static int __qseecom_get_fw_size(const char *appname, uint32_t *fw_size, uint32_t *app_arch) { int ret = -1; @@ -3629,14 +3629,21 @@ static int __qseecom_get_fw_size(char *appname, uint32_t *fw_size, } pr_debug("QSEE %s app, arch %u\n", appname, *app_arch); release_firmware(fw_entry); + fw_entry = NULL; for (i = 0; i < num_images; i++) { memset(fw_name, 0, sizeof(fw_name)); snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i); ret = request_firmware(&fw_entry, fw_name, qseecom.pdev); if (ret) goto err; + if (*fw_size > U32_MAX - fw_entry->size) { + pr_err("QSEE %s app file size overflow\n", appname); + ret = -EINVAL; + goto err; + } *fw_size += fw_entry->size; release_firmware(fw_entry); + fw_entry = NULL; } return ret; @@ -3647,8 +3654,9 @@ err: return ret; } -static int __qseecom_get_fw_data(char *appname, u8 *img_data, - struct qseecom_load_app_ireq *load_req) +static int __qseecom_get_fw_data(const char *appname, u8 *img_data, + uint32_t fw_size, + struct qseecom_load_app_ireq *load_req) { int ret = -1; int i = 0, rc = 0; @@ -3668,6 +3676,12 @@ static int __qseecom_get_fw_data(char *appname, u8 *img_data, } load_req->img_len = fw_entry->size; + if (load_req->img_len > fw_size) { + pr_err("app %s size %zu is larger than buf size %u\n", + appname, fw_entry->size, fw_size); + ret = -EINVAL; + goto err; + } memcpy(img_data_ptr, fw_entry->data, fw_entry->size); img_data_ptr = img_data_ptr + fw_entry->size; load_req->mdt_len = fw_entry->size; /*Get MDT LEN*/ @@ -3686,6 +3700,7 @@ static int __qseecom_get_fw_data(char *appname, u8 *img_data, goto err; } release_firmware(fw_entry); + fw_entry = NULL; for (i = 0; i < num_images; i++) { snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", appname, i); ret = request_firmware(&fw_entry, fw_name, qseecom.pdev); @@ -3693,10 +3708,17 @@ static int __qseecom_get_fw_data(char *appname, u8 *img_data, pr_err("Failed to locate blob %s\n", fw_name); goto err; } + if ((fw_entry->size > U32_MAX - load_req->img_len) || + (fw_entry->size + load_req->img_len > fw_size)) { + pr_err("Invalid file size for %s\n", fw_name); + ret = -EINVAL; + goto err; + } memcpy(img_data_ptr, fw_entry->data, fw_entry->size); img_data_ptr = img_data_ptr + fw_entry->size; load_req->img_len += fw_entry->size; release_firmware(fw_entry); + fw_entry = NULL; } return ret; err: @@ -3801,7 +3823,7 @@ static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname) if (ret) return ret; - ret = __qseecom_get_fw_data(appname, img_data, &load_req); + ret = __qseecom_get_fw_data(appname, img_data, fw_size, &load_req); if (ret) { ret = -EIO; goto exit_free_img_data; @@ -3922,7 +3944,7 @@ static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data, if (ret) return -EIO; - ret = __qseecom_get_fw_data(cmnlib_name, img_data, &load_req); + ret = __qseecom_get_fw_data(cmnlib_name, img_data, fw_size, &load_req); if (ret) { ret = -EIO; goto exit_free_img_data; diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c index d18308344431..293371b88ab9 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c @@ -127,6 +127,7 @@ enum ipa3_usb_state { IPA_USB_SUSPEND_REQUESTED, IPA_USB_SUSPEND_IN_PROGRESS, IPA_USB_SUSPENDED, + IPA_USB_SUSPENDED_NO_RWAKEUP, IPA_USB_RESUME_IN_PROGRESS }; @@ -152,6 +153,12 @@ struct finish_suspend_work_context { u32 ul_clnt_hdl; }; +struct ipa3_usb_teth_prot_conn_params { + u32 usb_to_ipa_clnt_hdl; + u32 ipa_to_usb_clnt_hdl; + struct ipa_usb_teth_prot_params params; +}; + /** * Transport type - could be either data tethering or DPL * Each transport has it's own RM resources and statuses @@ -163,6 +170,7 @@ struct ipa3_usb_transport_type_ctx { enum ipa3_usb_state state; struct finish_suspend_work_context finish_suspend_work; struct ipa_usb_xdci_chan_params ch_params; + struct ipa3_usb_teth_prot_conn_params teth_conn_params; }; struct ipa3_usb_smmu_reg_map { @@ -189,14 +197,15 @@ struct ipa3_usb_context { }; enum ipa3_usb_op { - IPA_USB_INIT_TETH_PROT, - IPA_USB_REQUEST_CHANNEL, - IPA_USB_CONNECT, - IPA_USB_DISCONNECT, - IPA_USB_RELEASE_CHANNEL, - IPA_USB_DEINIT_TETH_PROT, - IPA_USB_SUSPEND, - IPA_USB_RESUME + IPA_USB_OP_INIT_TETH_PROT, + IPA_USB_OP_REQUEST_CHANNEL, + IPA_USB_OP_CONNECT, + IPA_USB_OP_DISCONNECT, + IPA_USB_OP_RELEASE_CHANNEL, + IPA_USB_OP_DEINIT_TETH_PROT, + IPA_USB_OP_SUSPEND, + IPA_USB_OP_SUSPEND_NO_RWAKEUP, + IPA_USB_OP_RESUME }; struct ipa3_usb_status_dbg_info { @@ -228,22 +237,24 @@ struct ipa3_usb_context *ipa3_usb_ctx; static char *ipa3_usb_op_to_string(enum ipa3_usb_op op) { switch (op) { - case IPA_USB_INIT_TETH_PROT: - return "IPA_USB_INIT_TETH_PROT"; - case IPA_USB_REQUEST_CHANNEL: - return "IPA_USB_REQUEST_CHANNEL"; - case IPA_USB_CONNECT: - return "IPA_USB_CONNECT"; - case IPA_USB_DISCONNECT: - return "IPA_USB_DISCONNECT"; - case IPA_USB_RELEASE_CHANNEL: - return "IPA_USB_RELEASE_CHANNEL"; - case IPA_USB_DEINIT_TETH_PROT: - return "IPA_USB_DEINIT_TETH_PROT"; - case IPA_USB_SUSPEND: - return "IPA_USB_SUSPEND"; - case IPA_USB_RESUME: - return "IPA_USB_RESUME"; + case IPA_USB_OP_INIT_TETH_PROT: + return "IPA_USB_OP_INIT_TETH_PROT"; + case IPA_USB_OP_REQUEST_CHANNEL: + return "IPA_USB_OP_REQUEST_CHANNEL"; + case IPA_USB_OP_CONNECT: + return "IPA_USB_OP_CONNECT"; + case IPA_USB_OP_DISCONNECT: + return "IPA_USB_OP_DISCONNECT"; + case IPA_USB_OP_RELEASE_CHANNEL: + return "IPA_USB_OP_RELEASE_CHANNEL"; + case IPA_USB_OP_DEINIT_TETH_PROT: + return "IPA_USB_OP_DEINIT_TETH_PROT"; + case IPA_USB_OP_SUSPEND: + return "IPA_USB_OP_SUSPEND"; + case IPA_USB_OP_SUSPEND_NO_RWAKEUP: + return "IPA_USB_OP_SUSPEND_NO_RWAKEUP"; + case IPA_USB_OP_RESUME: + return "IPA_USB_OP_RESUME"; } return "UNSUPPORTED"; @@ -266,6 +277,8 @@ static char *ipa3_usb_state_to_string(enum ipa3_usb_state state) return "IPA_USB_SUSPEND_IN_PROGRESS"; case IPA_USB_SUSPENDED: return "IPA_USB_SUSPENDED"; + case IPA_USB_SUSPENDED_NO_RWAKEUP: + return "IPA_USB_SUSPENDED_NO_RWAKEUP"; case IPA_USB_RESUME_IN_PROGRESS: return "IPA_USB_RESUME_IN_PROGRESS"; } @@ -312,6 +325,7 @@ static bool ipa3_usb_set_state(enum ipa3_usb_state new_state, bool err_permit, if (state == IPA_USB_INITIALIZED || state == IPA_USB_STOPPED || state == IPA_USB_RESUME_IN_PROGRESS || + state == IPA_USB_SUSPENDED_NO_RWAKEUP || /* * In case of failure during suspend request * handling, state is reverted to connected. @@ -327,7 +341,8 @@ static bool ipa3_usb_set_state(enum ipa3_usb_state new_state, bool err_permit, case IPA_USB_STOPPED: if (state == IPA_USB_SUSPEND_IN_PROGRESS || state == IPA_USB_CONNECTED || - state == IPA_USB_SUSPENDED) + state == IPA_USB_SUSPENDED || + state == IPA_USB_SUSPENDED_NO_RWAKEUP) state_legal = true; break; case IPA_USB_SUSPEND_REQUESTED: @@ -354,6 +369,10 @@ static bool ipa3_usb_set_state(enum ipa3_usb_state new_state, bool err_permit, (err_permit && state == IPA_USB_RESUME_IN_PROGRESS)) state_legal = true; break; + case IPA_USB_SUSPENDED_NO_RWAKEUP: + if (state == IPA_USB_CONNECTED) + state_legal = true; + break; case IPA_USB_RESUME_IN_PROGRESS: if (state == IPA_USB_SUSPEND_IN_PROGRESS || state == IPA_USB_SUSPENDED) @@ -418,32 +437,33 @@ static bool ipa3_usb_check_legal_op(enum ipa3_usb_op op, spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); state = ipa3_usb_ctx->ttype_ctx[ttype].state; switch (op) { - case IPA_USB_INIT_TETH_PROT: + case IPA_USB_OP_INIT_TETH_PROT: if (state == IPA_USB_INVALID || (!is_dpl && state == IPA_USB_INITIALIZED)) is_legal = true; break; - case IPA_USB_REQUEST_CHANNEL: + case IPA_USB_OP_REQUEST_CHANNEL: if (state == IPA_USB_INITIALIZED) is_legal = true; break; - case IPA_USB_CONNECT: + case IPA_USB_OP_CONNECT: if (state == IPA_USB_INITIALIZED || state == IPA_USB_STOPPED) is_legal = true; break; - case IPA_USB_DISCONNECT: + case IPA_USB_OP_DISCONNECT: if (state == IPA_USB_CONNECTED || state == IPA_USB_SUSPEND_IN_PROGRESS || - state == IPA_USB_SUSPENDED) + state == IPA_USB_SUSPENDED || + state == IPA_USB_SUSPENDED_NO_RWAKEUP) is_legal = true; break; - case IPA_USB_RELEASE_CHANNEL: + case IPA_USB_OP_RELEASE_CHANNEL: /* when releasing 1st channel state will be changed already */ if (state == IPA_USB_STOPPED || (!is_dpl && state == IPA_USB_INITIALIZED)) is_legal = true; break; - case IPA_USB_DEINIT_TETH_PROT: + case IPA_USB_OP_DEINIT_TETH_PROT: /* * For data tethering we should allow deinit an inited protocol * always. E.g. rmnet is inited and rndis is connected. @@ -453,13 +473,18 @@ static bool ipa3_usb_check_legal_op(enum ipa3_usb_op op, if (!is_dpl || state == IPA_USB_INITIALIZED) is_legal = true; break; - case IPA_USB_SUSPEND: + case IPA_USB_OP_SUSPEND: if (state == IPA_USB_CONNECTED) is_legal = true; break; - case IPA_USB_RESUME: + case IPA_USB_OP_SUSPEND_NO_RWAKEUP: + if (state == IPA_USB_CONNECTED) + is_legal = true; + break; + case IPA_USB_OP_RESUME: if (state == IPA_USB_SUSPENDED || - state == IPA_USB_SUSPEND_IN_PROGRESS) + state == IPA_USB_SUSPEND_IN_PROGRESS || + state == IPA_USB_SUSPENDED_NO_RWAKEUP) is_legal = true; break; default: @@ -638,6 +663,7 @@ static int ipa3_usb_cons_request_resource_cb_do( ipa3_usb_ctx->ttype_ctx[ttype].state)); switch (ipa3_usb_ctx->ttype_ctx[ttype].state) { case IPA_USB_CONNECTED: + case IPA_USB_SUSPENDED_NO_RWAKEUP: rm_ctx->cons_state = IPA_USB_CONS_GRANTED; result = 0; break; @@ -717,6 +743,7 @@ static int ipa3_usb_cons_release_resource_cb_do( break; case IPA_USB_STOPPED: case IPA_USB_RESUME_IN_PROGRESS: + case IPA_USB_SUSPENDED_NO_RWAKEUP: if (rm_ctx->cons_requested) rm_ctx->cons_requested = false; break; @@ -886,7 +913,7 @@ int ipa_usb_init_teth_prot(enum ipa_usb_teth_prot teth_prot, ttype = IPA3_USB_GET_TTYPE(teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_INIT_TETH_PROT, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_INIT_TETH_PROT, ttype)) { IPA_USB_ERR("Illegal operation.\n"); result = -EPERM; goto bad_params; @@ -1204,7 +1231,7 @@ static int ipa3_usb_request_xdci_channel( ttype = IPA3_USB_GET_TTYPE(params->teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_REQUEST_CHANNEL, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_REQUEST_CHANNEL, ttype)) { IPA_USB_ERR("Illegal operation\n"); return -EPERM; } @@ -1347,7 +1374,7 @@ static int ipa3_usb_release_xdci_channel(u32 clnt_hdl, return -EINVAL; } - if (!ipa3_usb_check_legal_op(IPA_USB_RELEASE_CHANNEL, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_RELEASE_CHANNEL, ttype)) { IPA_USB_ERR("Illegal operation.\n"); return -EPERM; } @@ -1511,81 +1538,79 @@ static int ipa3_usb_connect_dpl(void) return 0; } -static int ipa3_usb_connect_teth_prot( - struct ipa_usb_xdci_connect_params_internal *params, - enum ipa3_usb_transport_type ttype) +static int ipa3_usb_connect_teth_prot(enum ipa_usb_teth_prot teth_prot) { int result; struct teth_bridge_connect_params teth_bridge_params; + struct ipa3_usb_teth_prot_conn_params *teth_conn_params; + enum ipa3_usb_transport_type ttype; - IPA_USB_DBG("connecting protocol = %d\n", - params->teth_prot); - switch (params->teth_prot) { + IPA_USB_DBG("connecting protocol = %s\n", + ipa3_usb_teth_prot_to_string(teth_prot)); + + ttype = IPA3_USB_GET_TTYPE(teth_prot); + + teth_conn_params = &(ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params); + + switch (teth_prot) { case IPA_USB_RNDIS: if (ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS].state == IPA_USB_TETH_PROT_CONNECTED) { IPA_USB_DBG("%s is already connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; } ipa3_usb_ctx->ttype_ctx[ttype].user_data = ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS].user_data; result = rndis_ipa_pipe_connect_notify( - params->usb_to_ipa_clnt_hdl, - params->ipa_to_usb_clnt_hdl, - params->teth_prot_params.max_xfer_size_bytes_to_dev, - params->teth_prot_params.max_packet_number_to_dev, - params->teth_prot_params.max_xfer_size_bytes_to_host, + teth_conn_params->usb_to_ipa_clnt_hdl, + teth_conn_params->ipa_to_usb_clnt_hdl, + teth_conn_params->params.max_xfer_size_bytes_to_dev, + teth_conn_params->params.max_packet_number_to_dev, + teth_conn_params->params.max_xfer_size_bytes_to_host, ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS]. teth_prot_params.rndis.private); if (result) { IPA_USB_ERR("failed to connect %s.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); ipa3_usb_ctx->ttype_ctx[ttype].user_data = NULL; return result; } ipa3_usb_ctx->teth_prot_ctx[IPA_USB_RNDIS].state = IPA_USB_TETH_PROT_CONNECTED; IPA_USB_DBG("%s is connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; case IPA_USB_ECM: if (ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM].state == IPA_USB_TETH_PROT_CONNECTED) { IPA_USB_DBG("%s is already connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; } ipa3_usb_ctx->ttype_ctx[ttype].user_data = ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM].user_data; - result = ecm_ipa_connect(params->usb_to_ipa_clnt_hdl, - params->ipa_to_usb_clnt_hdl, + result = ecm_ipa_connect(teth_conn_params->usb_to_ipa_clnt_hdl, + teth_conn_params->ipa_to_usb_clnt_hdl, ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM]. teth_prot_params.ecm.private); if (result) { IPA_USB_ERR("failed to connect %s.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); ipa3_usb_ctx->ttype_ctx[ttype].user_data = NULL; return result; } ipa3_usb_ctx->teth_prot_ctx[IPA_USB_ECM].state = IPA_USB_TETH_PROT_CONNECTED; IPA_USB_DBG("%s is connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; case IPA_USB_RMNET: case IPA_USB_MBIM: - if (ipa3_usb_ctx->teth_prot_ctx[params->teth_prot].state == + if (ipa3_usb_ctx->teth_prot_ctx[teth_prot].state == IPA_USB_TETH_PROT_CONNECTED) { IPA_USB_DBG("%s is already connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; } result = ipa3_usb_init_teth_bridge(); @@ -1593,14 +1618,14 @@ static int ipa3_usb_connect_teth_prot( return result; ipa3_usb_ctx->ttype_ctx[ttype].user_data = - ipa3_usb_ctx->teth_prot_ctx[params->teth_prot]. + ipa3_usb_ctx->teth_prot_ctx[teth_prot]. user_data; teth_bridge_params.ipa_usb_pipe_hdl = - params->ipa_to_usb_clnt_hdl; + teth_conn_params->ipa_to_usb_clnt_hdl; teth_bridge_params.usb_ipa_pipe_hdl = - params->usb_to_ipa_clnt_hdl; + teth_conn_params->usb_to_ipa_clnt_hdl; teth_bridge_params.tethering_mode = - (params->teth_prot == IPA_USB_RMNET) ? + (teth_prot == IPA_USB_RMNET) ? (TETH_TETHERING_MODE_RMNET):(TETH_TETHERING_MODE_MBIM); teth_bridge_params.client_type = IPA_CLIENT_USB_PROD; result = ipa3_usb_connect_teth_bridge(&teth_bridge_params); @@ -1608,27 +1633,23 @@ static int ipa3_usb_connect_teth_prot( ipa3_usb_ctx->ttype_ctx[ttype].user_data = NULL; return result; } - ipa3_usb_ctx->teth_prot_ctx[params->teth_prot].state = + ipa3_usb_ctx->teth_prot_ctx[teth_prot].state = IPA_USB_TETH_PROT_CONNECTED; ipa3_usb_notify_do(ttype, IPA_USB_DEVICE_READY); IPA_USB_DBG("%s (%s) is connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot), - ipa3_usb_teth_bridge_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot), + ipa3_usb_teth_bridge_prot_to_string(teth_prot)); break; case IPA_USB_DIAG: if (ipa3_usb_ctx->teth_prot_ctx[IPA_USB_DIAG].state == IPA_USB_TETH_PROT_CONNECTED) { IPA_USB_DBG("%s is already connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; } ipa3_usb_ctx->ttype_ctx[ttype].user_data = - ipa3_usb_ctx->teth_prot_ctx[params->teth_prot]. - user_data; + ipa3_usb_ctx->teth_prot_ctx[teth_prot].user_data; result = ipa3_usb_connect_dpl(); if (result) { IPA_USB_ERR("Failed connecting DPL result=%d\n", @@ -1640,8 +1661,7 @@ static int ipa3_usb_connect_teth_prot( IPA_USB_TETH_PROT_CONNECTED; ipa3_usb_notify_do(ttype, IPA_USB_DEVICE_READY); IPA_USB_DBG("%s is connected.\n", - ipa3_usb_teth_prot_to_string( - params->teth_prot)); + ipa3_usb_teth_prot_to_string(teth_prot)); break; default: IPA_USB_ERR("Invalid tethering protocol\n"); @@ -1775,11 +1795,19 @@ static int ipa3_usb_xdci_connect_internal( ttype = (params->teth_prot == IPA_USB_DIAG) ? IPA_USB_TRANSPORT_DPL : IPA_USB_TRANSPORT_TETH; - if (!ipa3_usb_check_legal_op(IPA_USB_CONNECT, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_CONNECT, ttype)) { IPA_USB_ERR("Illegal operation.\n"); return -EPERM; } + ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params.ipa_to_usb_clnt_hdl + = params->ipa_to_usb_clnt_hdl; + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) + ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params. + usb_to_ipa_clnt_hdl = params->usb_to_ipa_clnt_hdl; + ipa3_usb_ctx->ttype_ctx[ttype].teth_conn_params.params + = params->teth_prot_params; + /* Set EE xDCI specific scratch */ result = ipa3_set_usb_max_packet_size(params->max_pkt_size); if (result) { @@ -1816,7 +1844,7 @@ static int ipa3_usb_xdci_connect_internal( if (params->teth_prot != IPA_USB_DIAG) { /* Start UL channel */ - result = ipa3_xdci_connect(params->usb_to_ipa_clnt_hdl, + result = ipa3_xdci_start(params->usb_to_ipa_clnt_hdl, params->usb_to_ipa_xferrscidx, params->usb_to_ipa_xferrscidx_valid); if (result) { @@ -1826,7 +1854,7 @@ static int ipa3_usb_xdci_connect_internal( } /* Start DL/DPL channel */ - result = ipa3_xdci_connect(params->ipa_to_usb_clnt_hdl, + result = ipa3_xdci_start(params->ipa_to_usb_clnt_hdl, params->ipa_to_usb_xferrscidx, params->ipa_to_usb_xferrscidx_valid); if (result) { @@ -1835,7 +1863,7 @@ static int ipa3_usb_xdci_connect_internal( } /* Connect tethering protocol */ - result = ipa3_usb_connect_teth_prot(params, ttype); + result = ipa3_usb_connect_teth_prot(params->teth_prot); if (result) { IPA_USB_ERR("failed to connect teth protocol\n"); goto connect_teth_prot_fail; @@ -2164,6 +2192,70 @@ static int ipa3_usb_check_disconnect_prot(enum ipa_usb_teth_prot teth_prot) return 0; } +/* Assumes lock already acquired */ +static int ipa_usb_xdci_dismiss_channels(u32 ul_clnt_hdl, u32 dl_clnt_hdl, + enum ipa_usb_teth_prot teth_prot) +{ + int result = 0; + enum ipa3_usb_transport_type ttype; + + ttype = IPA3_USB_GET_TTYPE(teth_prot); + + IPA_USB_DBG_LOW("entry\n"); + + /* Reset DL channel */ + result = ipa3_reset_gsi_channel(dl_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to reset DL channel.\n"); + return result; + } + + /* Reset DL event ring */ + result = ipa3_reset_gsi_event_ring(dl_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to reset DL event ring.\n"); + return result; + } + + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + /* Reset UL channel */ + result = ipa3_reset_gsi_channel(ul_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to reset UL channel.\n"); + return result; + } + + /* Reset UL event ring */ + result = ipa3_reset_gsi_event_ring(ul_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to reset UL event ring.\n"); + return result; + } + } + + /* Change state to STOPPED */ + if (!ipa3_usb_set_state(IPA_USB_STOPPED, false, ttype)) + IPA_USB_ERR("failed to change state to stopped\n"); + + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + result = ipa3_usb_release_xdci_channel(ul_clnt_hdl, ttype); + if (result) { + IPA_USB_ERR("failed to release UL channel.\n"); + return result; + } + } + + result = ipa3_usb_release_xdci_channel(dl_clnt_hdl, ttype); + if (result) { + IPA_USB_ERR("failed to release DL channel.\n"); + return result; + } + + IPA_USB_DBG_LOW("exit\n"); + + return 0; +} + int ipa_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, enum ipa_usb_teth_prot teth_prot) { @@ -2175,20 +2267,31 @@ int ipa_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, mutex_lock(&ipa3_usb_ctx->general_mutex); IPA_USB_DBG_LOW("entry\n"); - if (ipa3_usb_check_disconnect_prot(teth_prot)) { - result = -EINVAL; - goto bad_params; - } ttype = IPA3_USB_GET_TTYPE(teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_DISCONNECT, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_DISCONNECT, ttype)) { IPA_USB_ERR("Illegal operation.\n"); result = -EPERM; goto bad_params; } spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); + if (ipa3_usb_ctx->ttype_ctx[ttype].state == + IPA_USB_SUSPENDED_NO_RWAKEUP) { + spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); + result = ipa_usb_xdci_dismiss_channels(ul_clnt_hdl, dl_clnt_hdl, + teth_prot); + mutex_unlock(&ipa3_usb_ctx->general_mutex); + return result; + } + + if (ipa3_usb_check_disconnect_prot(teth_prot)) { + spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); + result = -EINVAL; + goto bad_params; + } + if (ipa3_usb_ctx->ttype_ctx[ttype].state != IPA_USB_SUSPENDED) { spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); /* Stop DL/DPL channel */ @@ -2227,53 +2330,10 @@ int ipa_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, } else spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); - /* Reset DL channel */ - result = ipa3_reset_gsi_channel(dl_clnt_hdl); - if (result) { - IPA_USB_ERR("failed to reset DL channel.\n"); - goto bad_params; - } - - /* Reset DL event ring */ - result = ipa3_reset_gsi_event_ring(dl_clnt_hdl); - if (result) { - IPA_USB_ERR("failed to reset DL event ring.\n"); - goto bad_params; - } - - if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { - /* Reset UL channel */ - result = ipa3_reset_gsi_channel(ul_clnt_hdl); - if (result) { - IPA_USB_ERR("failed to reset UL channel.\n"); - goto bad_params; - } - - /* Reset UL event ring */ - result = ipa3_reset_gsi_event_ring(ul_clnt_hdl); - if (result) { - IPA_USB_ERR("failed to reset UL event ring.\n"); - goto bad_params; - } - } - - /* Change state to STOPPED */ - if (!ipa3_usb_set_state(IPA_USB_STOPPED, false, ttype)) - IPA_USB_ERR("failed to change state to stopped\n"); - - if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { - result = ipa3_usb_release_xdci_channel(ul_clnt_hdl, ttype); - if (result) { - IPA_USB_ERR("failed to release UL channel.\n"); - goto bad_params; - } - } - - result = ipa3_usb_release_xdci_channel(dl_clnt_hdl, ttype); - if (result) { - IPA_USB_ERR("failed to release DL channel.\n"); + result = ipa_usb_xdci_dismiss_channels(ul_clnt_hdl, dl_clnt_hdl, + teth_prot); + if (result) goto bad_params; - } /* Disconnect tethering protocol */ result = ipa3_usb_disconnect_teth_prot(teth_prot); @@ -2315,7 +2375,7 @@ int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot) ttype = IPA3_USB_GET_TTYPE(teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_DEINIT_TETH_PROT, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_DEINIT_TETH_PROT, ttype)) { IPA_USB_ERR("Illegal operation.\n"); result = -EPERM; goto bad_params; @@ -2411,25 +2471,104 @@ bad_params: } EXPORT_SYMBOL(ipa_usb_deinit_teth_prot); -int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, +/* Assumes lock already acquired */ +static int ipa3_usb_suspend_no_remote_wakeup(u32 ul_clnt_hdl, u32 dl_clnt_hdl, enum ipa_usb_teth_prot teth_prot) { int result = 0; + enum ipa3_usb_transport_type ttype; + + ttype = IPA3_USB_GET_TTYPE(teth_prot); + + if (!ipa3_usb_check_legal_op(IPA_USB_OP_SUSPEND_NO_RWAKEUP, ttype)) { + IPA_USB_ERR("Illegal operation.\n"); + result = -EPERM; + goto fail_exit; + } + + IPA_USB_DBG("Start suspend with no remote wakeup sequence: %s\n", + IPA3_USB_IS_TTYPE_DPL(ttype) ? + "DPL channel":"Data Tethering channels"); + + if (ipa3_usb_check_disconnect_prot(teth_prot)) { + result = -EINVAL; + goto fail_exit; + } + + /* Stop DL/DPL channel */ + result = ipa3_xdci_disconnect(dl_clnt_hdl, false, -1); + if (result) { + IPA_USB_ERR("failed to disconnect DL/DPL channel.\n"); + goto fail_exit; + } + + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + /* Stop UL channel */ + result = ipa3_xdci_disconnect(ul_clnt_hdl, true, + ipa3_usb_ctx->qmi_req_id); + if (result) { + IPA_USB_ERR("failed disconnect UL channel\n"); + goto start_dl; + } + ipa3_usb_ctx->qmi_req_id++; + } + + /* Disconnect tethering protocol */ + result = ipa3_usb_disconnect_teth_prot(teth_prot); + if (result) + goto start_ul; + + result = ipa3_usb_release_prod(ttype); + if (result) { + IPA_USB_ERR("failed to release PROD.\n"); + goto connect_teth; + } + + /* Change ipa_usb state to SUSPENDED_NO_RWAKEUP */ + if (!ipa3_usb_set_state(IPA_USB_SUSPENDED_NO_RWAKEUP, false, ttype)) + IPA_USB_ERR("failed to change state to suspend no rwakeup\n"); + + IPA_USB_DBG_LOW("exit\n"); + return 0; + +connect_teth: + (void)ipa3_usb_connect_teth_prot(teth_prot); +start_ul: + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) + (void)ipa3_xdci_connect(ul_clnt_hdl); +start_dl: + (void)ipa3_xdci_connect(dl_clnt_hdl); +fail_exit: + return result; +} + +int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, + enum ipa_usb_teth_prot teth_prot, bool with_remote_wakeup) +{ + int result = 0; unsigned long flags; enum ipa3_usb_cons_state curr_cons_state; enum ipa3_usb_transport_type ttype; mutex_lock(&ipa3_usb_ctx->general_mutex); IPA_USB_DBG_LOW("entry\n"); + if (teth_prot > IPA_USB_MAX_TETH_PROT_SIZE) { IPA_USB_ERR("bad parameters.\n"); result = -EINVAL; goto bad_params; } + if (!with_remote_wakeup) { + result = ipa3_usb_suspend_no_remote_wakeup(ul_clnt_hdl, + dl_clnt_hdl, teth_prot); + mutex_unlock(&ipa3_usb_ctx->general_mutex); + return result; + } + ttype = IPA3_USB_GET_TTYPE(teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_SUSPEND, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_SUSPEND, ttype)) { IPA_USB_ERR("Illegal operation.\n"); result = -EPERM; goto bad_params; @@ -2538,6 +2677,72 @@ bad_params: } EXPORT_SYMBOL(ipa_usb_xdci_suspend); +/* Assumes lock already acquired */ +static int ipa3_usb_resume_no_remote_wakeup(u32 ul_clnt_hdl, u32 dl_clnt_hdl, + enum ipa_usb_teth_prot teth_prot) +{ + int result = -EFAULT; + enum ipa3_usb_transport_type ttype; + + ttype = IPA3_USB_GET_TTYPE(teth_prot); + + IPA_USB_DBG("Start resume with no remote wakeup sequence: %s\n", + IPA3_USB_IS_TTYPE_DPL(ttype) ? + "DPL channel":"Data Tethering channels"); + + /* Request USB_PROD */ + result = ipa3_usb_request_prod(ttype); + if (result) + goto fail_exit; + + /* Connect tethering protocol */ + result = ipa3_usb_connect_teth_prot(teth_prot); + if (result) { + IPA_USB_ERR("failed to connect teth protocol\n"); + goto release_prod; + } + + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + /* Start UL channel */ + result = ipa3_xdci_connect(ul_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to start UL channel.\n"); + goto disconn_teth; + } + } + + /* Start DL/DPL channel */ + result = ipa3_xdci_connect(dl_clnt_hdl); + if (result) { + IPA_USB_ERR("failed to start DL/DPL channel.\n"); + goto stop_ul; + } + + /* Change state to CONNECTED */ + if (!ipa3_usb_set_state(IPA_USB_CONNECTED, false, ttype)) { + IPA_USB_ERR("failed to change state to connected\n"); + result = -EFAULT; + goto stop_dl; + } + + return 0; + +stop_dl: + (void)ipa3_xdci_disconnect(dl_clnt_hdl, false, -1); +stop_ul: + if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { + (void)ipa3_xdci_disconnect(ul_clnt_hdl, true, + ipa3_usb_ctx->qmi_req_id); + ipa3_usb_ctx->qmi_req_id++; + } +disconn_teth: + (void)ipa3_usb_disconnect_teth_prot(teth_prot); +release_prod: + (void)ipa3_usb_release_prod(ttype); +fail_exit: + return result; +} + int ipa_usb_xdci_resume(u32 ul_clnt_hdl, u32 dl_clnt_hdl, enum ipa_usb_teth_prot teth_prot) { @@ -2557,19 +2762,25 @@ int ipa_usb_xdci_resume(u32 ul_clnt_hdl, u32 dl_clnt_hdl, ttype = IPA3_USB_GET_TTYPE(teth_prot); - if (!ipa3_usb_check_legal_op(IPA_USB_RESUME, ttype)) { + if (!ipa3_usb_check_legal_op(IPA_USB_OP_RESUME, ttype)) { IPA_USB_ERR("Illegal operation.\n"); result = -EPERM; goto bad_params; } - IPA_USB_DBG_LOW("Start resume sequence: %s\n", - IPA3_USB_IS_TTYPE_DPL(ttype) ? - "DPL channel" : "Data Tethering channels"); - spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); prev_state = ipa3_usb_ctx->ttype_ctx[ttype].state; spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); + if (prev_state == IPA_USB_SUSPENDED_NO_RWAKEUP) { + result = ipa3_usb_resume_no_remote_wakeup(ul_clnt_hdl, + dl_clnt_hdl, teth_prot); + mutex_unlock(&ipa3_usb_ctx->general_mutex); + return result; + } + + IPA_USB_DBG("Start resume sequence: %s\n", + IPA3_USB_IS_TTYPE_DPL(ttype) ? + "DPL channel" : "Data Tethering channels"); /* Change state to RESUME_IN_PROGRESS */ if (!ipa3_usb_set_state(IPA_USB_RESUME_IN_PROGRESS, false, ttype)) { diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c index c36ecfef66f1..d6e563b935b6 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -235,7 +235,7 @@ static int ipa_generate_flt_hw_rule(enum ipa_ip_type ip, * @ip: the ip address family type * @hdr_sz: header size * - * Returns: 0 on success, negative on failure + * Returns: size on success, negative on failure * * caller needs to hold any needed locks to ensure integrity * @@ -373,7 +373,12 @@ static int ipa_generate_flt_hw_tbl_common(enum ipa_ip_type ip, u8 *base, ((long)body & IPA_FLT_ENTRY_MEMORY_ALLIGNMENT)); } else { - WARN_ON(tbl->sz == 0); + if (tbl->sz == 0) { + IPAERR("tbl size is 0\n"); + WARN_ON(1); + goto proc_err; + } + /* allocate memory for the flt tbl */ flt_tbl_mem.size = tbl->sz; flt_tbl_mem.base = @@ -460,7 +465,12 @@ static int ipa_generate_flt_hw_tbl_common(enum ipa_ip_type ip, u8 *base, ((long)body & IPA_FLT_ENTRY_MEMORY_ALLIGNMENT)); } else { - WARN_ON(tbl->sz == 0); + if (tbl->sz == 0) { + IPAERR("tbl size is 0\n"); + WARN_ON(1); + goto proc_err; + } + /* allocate memory for the flt tbl */ flt_tbl_mem.size = tbl->sz; flt_tbl_mem.base = @@ -534,8 +544,15 @@ static int ipa_generate_flt_hw_tbl_v1_1(enum ipa_ip_type ip, u8 *hdr; u8 *body; u8 *base; + int res; + + res = ipa_get_flt_hw_tbl_size(ip, &hdr_sz); + if (res < 0) { + IPAERR("ipa_get_flt_hw_tbl_size failed %d\n", res); + return res; + } - mem->size = ipa_get_flt_hw_tbl_size(ip, &hdr_sz); + mem->size = res; mem->size = IPA_HW_TABLE_ALIGNMENT(mem->size); if (mem->size == 0) { @@ -720,6 +737,7 @@ static int ipa_generate_flt_hw_tbl_v2(enum ipa_ip_type ip, u32 *entr; u32 body_start_offset; u32 hdr_top; + int res; if (ip == IPA_IP_v4) body_start_offset = IPA_MEM_PART(apps_v4_flt_ofst) - @@ -756,7 +774,13 @@ static int ipa_generate_flt_hw_tbl_v2(enum ipa_ip_type ip, entr++; } - mem->size = ipa_get_flt_hw_tbl_size(ip, &hdr_sz); + res = ipa_get_flt_hw_tbl_size(ip, &hdr_sz); + if (res < 0) { + IPAERR("ipa_get_flt_hw_tbl_size failed %d\n", res); + goto body_err; + } + + mem->size = res; mem->size -= hdr_sz; mem->size = IPA_HW_TABLE_ALIGNMENT(mem->size); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index fec4d5484d28..73206abf9cfd 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -139,7 +139,7 @@ #define IPA_HW_TABLE_ALIGNMENT(start_ofst) \ (((start_ofst) + 127) & ~127) -#define IPA_RT_FLT_HW_RULE_BUF_SIZE (128) +#define IPA_RT_FLT_HW_RULE_BUF_SIZE (256) #define IPA_HDR_PROC_CTX_TABLE_ALIGNMENT_BYTE 8 #define IPA_HDR_PROC_CTX_TABLE_ALIGNMENT(start_ofst) \ diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c index 6202992c2c4e..314b09593026 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -25,6 +25,16 @@ #define IPA_NAT_SHARED_MEMORY 1 #define IPA_NAT_TEMP_MEM_SIZE 128 +enum nat_table_type { + IPA_NAT_BASE_TBL = 0, + IPA_NAT_EXPN_TBL = 1, + IPA_NAT_INDX_TBL = 2, + IPA_NAT_INDEX_EXPN_TBL = 3, +}; + +#define NAT_TABLE_ENTRY_SIZE_BYTE 32 +#define NAT_INTEX_TABLE_ENTRY_SIZE_BYTE 4 + static int ipa_nat_vma_fault_remap( struct vm_area_struct *vma, struct vm_fault *vmf) { @@ -568,6 +578,71 @@ int ipa2_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma) goto bail; } + for (cnt = 0; cnt < dma->entries; cnt++) { + if (dma->dma[cnt].table_index >= 1) { + IPAERR("Invalid table index %d\n", + dma->dma[cnt].table_index); + ret = -EPERM; + goto bail; + } + + switch (dma->dma[cnt].base_addr) { + case IPA_NAT_BASE_TBL: + if (dma->dma[cnt].offset >= + (ipa_ctx->nat_mem.size_base_tables + 1) * + NAT_TABLE_ENTRY_SIZE_BYTE) { + IPAERR("Invalid offset %d\n", + dma->dma[cnt].offset); + ret = -EPERM; + goto bail; + } + + break; + + case IPA_NAT_EXPN_TBL: + if (dma->dma[cnt].offset >= + ipa_ctx->nat_mem.size_expansion_tables * + NAT_TABLE_ENTRY_SIZE_BYTE) { + IPAERR("Invalid offset %d\n", + dma->dma[cnt].offset); + ret = -EPERM; + goto bail; + } + + break; + + case IPA_NAT_INDX_TBL: + if (dma->dma[cnt].offset >= + (ipa_ctx->nat_mem.size_base_tables + 1) * + NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) { + IPAERR("Invalid offset %d\n", + dma->dma[cnt].offset); + ret = -EPERM; + goto bail; + } + + break; + + case IPA_NAT_INDEX_EXPN_TBL: + if (dma->dma[cnt].offset >= + ipa_ctx->nat_mem.size_expansion_tables * + NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) { + IPAERR("Invalid offset %d\n", + dma->dma[cnt].offset); + ret = -EPERM; + goto bail; + } + + break; + + default: + IPAERR("Invalid base_addr %d\n", + dma->dma[cnt].base_addr); + ret = -EPERM; + goto bail; + } + } + size = sizeof(struct ipa_desc) * NUM_OF_DESC; desc = kzalloc(size, GFP_KERNEL); if (desc == NULL) { diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 15476f38cf44..5e7a5383334c 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -227,7 +227,7 @@ int __ipa_generate_rt_hw_rule_v2_6L(enum ipa_ip_type ip, * @hdr_sz: header size * @max_rt_idx: maximal index * - * Returns: 0 on success, negative on failure + * Returns: size on success, negative on failure * * caller needs to hold any needed locks to ensure integrity * @@ -356,7 +356,11 @@ static int ipa_generate_rt_hw_tbl_common(enum ipa_ip_type ip, u8 *base, u8 *hdr, ((long)body & IPA_RT_ENTRY_MEMORY_ALLIGNMENT)); } else { - WARN_ON(tbl->sz == 0); + if (tbl->sz == 0) { + IPAERR("cannot generate 0 size table\n"); + goto proc_err; + } + /* allocate memory for the RT tbl */ rt_tbl_mem.size = tbl->sz; rt_tbl_mem.base = @@ -429,8 +433,15 @@ static int ipa_generate_rt_hw_tbl_v1_1(enum ipa_ip_type ip, u8 *base; int max_rt_idx; int i; + int res; - mem->size = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx); + res = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx); + if (res < 0) { + IPAERR("ipa_get_rt_hw_tbl_size failed %d\n", res); + goto error; + } + + mem->size = res; mem->size = (mem->size + IPA_RT_TABLE_MEMORY_ALLIGNMENT) & ~IPA_RT_TABLE_MEMORY_ALLIGNMENT; @@ -603,6 +614,7 @@ static int ipa_generate_rt_hw_tbl_v2(enum ipa_ip_type ip, int num_index; u32 body_start_offset; u32 apps_start_idx; + int res; if (ip == IPA_IP_v4) { num_index = IPA_MEM_PART(v4_apps_rt_index_hi) - @@ -632,7 +644,13 @@ static int ipa_generate_rt_hw_tbl_v2(enum ipa_ip_type ip, entr++; } - mem->size = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx); + res = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx); + if (res < 0) { + IPAERR("ipa_get_rt_hw_tbl_size failed %d\n", res); + goto base_err; + } + + mem->size = res; mem->size -= hdr_sz; mem->size = (mem->size + IPA_RT_TABLE_MEMORY_ALLIGNMENT) & ~IPA_RT_TABLE_MEMORY_ALLIGNMENT; diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 96003d7a16a0..520f139ee38a 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -1053,6 +1053,8 @@ static int ipa_wwan_xmit(struct sk_buff *skb, struct net_device *dev) IPAWANDBG ("SW filtering out none QMAP packet received from %s", current->comm); + dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; return NETDEV_TX_OK; } @@ -1094,6 +1096,8 @@ send: if (ret) { pr_err("[%s] fatal: ipa rm timer request resource failed %d\n", dev->name, ret); + dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; return -EFAULT; } /* IPA_RM checking end */ @@ -1109,7 +1113,6 @@ send: if (ret) { ret = NETDEV_TX_BUSY; - dev->stats.tx_dropped++; goto out; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index 8326c3fdd9d1..d176552a3533 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -61,7 +61,7 @@ int ipa3_enable_data_path(u32 clnt_hdl) !ipa3_should_pipe_be_suspended(ep->client))) { memset(&ep_cfg_ctrl, 0 , sizeof(ep_cfg_ctrl)); ep_cfg_ctrl.ipa_ep_suspend = false; - ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); + res = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); } /* Assign the resource group for pipe */ @@ -101,7 +101,7 @@ int ipa3_disable_data_path(u32 clnt_hdl) if (IPA_CLIENT_IS_CONS(ep->client)) { memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl)); ep_cfg_ctrl.ipa_ep_suspend = true; - ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); + res = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl); } udelay(IPA_PKT_FLUSH_TO_US); @@ -1311,7 +1311,46 @@ int ipa3_set_usb_max_packet_size( return 0; } -int ipa3_xdci_connect(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid) +int ipa3_xdci_connect(u32 clnt_hdl) +{ + int result; + struct ipa3_ep_context *ep; + + IPADBG("entry\n"); + + if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || + ipa3_ctx->ep[clnt_hdl].valid == 0) { + IPAERR("Bad parameter.\n"); + return -EINVAL; + } + + ep = &ipa3_ctx->ep[clnt_hdl]; + IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl)); + + result = ipa3_start_gsi_channel(clnt_hdl); + if (result) { + IPAERR("failed to start gsi channel clnt_hdl=%u\n", clnt_hdl); + goto exit; + } + + result = ipa3_enable_data_path(clnt_hdl); + if (result) { + IPAERR("enable data path failed res=%d clnt_hdl=%d.\n", result, + clnt_hdl); + goto stop_ch; + } + + IPADBG("exit\n"); + goto exit; + +stop_ch: + (void)ipa3_stop_gsi_channel(clnt_hdl); +exit: + IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl)); + return result; +} + +int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid) { struct ipa3_ep_context *ep; int result = -EFAULT; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 33be22f98b9d..96cd550d7ef6 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1482,7 +1482,9 @@ int ipa3_reset_gsi_event_ring(u32 clnt_hdl); int ipa3_set_usb_max_packet_size( enum ipa_usb_max_usb_packet_size usb_max_packet_size); -int ipa3_xdci_connect(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid); +int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid); + +int ipa3_xdci_connect(u32 clnt_hdl); int ipa3_xdci_disconnect(u32 clnt_hdl, bool should_force_clear, u32 qmi_req_id); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c index 67e9b397a8b4..e7e5cf114242 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c @@ -24,6 +24,17 @@ #define IPA_NAT_TEMP_MEM_SIZE 128 +enum nat_table_type { + IPA_NAT_BASE_TBL = 0, + IPA_NAT_EXPN_TBL = 1, + IPA_NAT_INDX_TBL = 2, + IPA_NAT_INDEX_EXPN_TBL = 3, +}; + +#define NAT_TABLE_ENTRY_SIZE_BYTE 32 +#define NAT_INTEX_TABLE_ENTRY_SIZE_BYTE 4 + + static int ipa3_nat_vma_fault_remap( struct vm_area_struct *vma, struct vm_fault *vmf) { @@ -571,6 +582,71 @@ int ipa3_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma) goto bail; } + for (cnt = 0; cnt < dma->entries; cnt++) { + if (dma->dma[cnt].table_index >= 1) { + IPAERR("Invalid table index %d\n", + dma->dma[cnt].table_index); + ret = -EPERM; + goto bail; + } + + switch (dma->dma[cnt].base_addr) { + case IPA_NAT_BASE_TBL: + if (dma->dma[cnt].offset >= + (ipa3_ctx->nat_mem.size_base_tables + 1) * + NAT_TABLE_ENTRY_SIZE_BYTE) { + IPAERR("Invalid offset %d\n", + dma->dma[cnt].offset); + ret = -EPERM; + goto bail; + } + + break; + + case IPA_NAT_EXPN_TBL: + if (dma->dma[cnt].offset >= + ipa3_ctx->nat_mem.size_expansion_tables * + NAT_TABLE_ENTRY_SIZE_BYTE) { + IPAERR("Invalid offset %d\n", + dma->dma[cnt].offset); + ret = -EPERM; + goto bail; + } + + break; + + case IPA_NAT_INDX_TBL: + if (dma->dma[cnt].offset >= + (ipa3_ctx->nat_mem.size_base_tables + 1) * + NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) { + IPAERR("Invalid offset %d\n", + dma->dma[cnt].offset); + ret = -EPERM; + goto bail; + } + + break; + + case IPA_NAT_INDEX_EXPN_TBL: + if (dma->dma[cnt].offset >= + ipa3_ctx->nat_mem.size_expansion_tables * + NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) { + IPAERR("Invalid offset %d\n", + dma->dma[cnt].offset); + ret = -EPERM; + goto bail; + } + + break; + + default: + IPAERR("Invalid base_addr %d\n", + dma->dma[cnt].base_addr); + ret = -EPERM; + goto bail; + } + } + size = sizeof(struct ipa3_desc) * NUM_OF_DESC; desc = kzalloc(size, GFP_KERNEL); if (desc == NULL) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index c0a6e8b00d71..4ea68ae1e95c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -3522,7 +3522,7 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl) goto end_sequence; IPADBG("Inject a DMA_TASK with 1B packet to IPA and retry\n"); - /* Send a 1B packet DMA_RASK to IPA and try again*/ + /* Send a 1B packet DMA_TASK to IPA and try again */ res = ipa3_inject_dma_task_for_gsi(); if (res) { IPAERR("Failed to inject DMA TASk for GSI\n"); diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index f134852e046e..8f6c303d2867 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -1066,6 +1066,8 @@ static int ipa3_wwan_xmit(struct sk_buff *skb, struct net_device *dev) IPAWANDBG_LOW ("SW filtering out none QMAP packet received from %s", current->comm); + dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; return NETDEV_TX_OK; } @@ -1077,7 +1079,8 @@ static int ipa3_wwan_xmit(struct sk_buff *skb, struct net_device *dev) pr_err("[%s]Queue stop, send ctrl pkts\n", dev->name); goto send; } else { - pr_err("[%s]fatal: ipa_wwan_xmit stopped\n", dev->name); + pr_err("[%s]fatal: ipa3_wwan_xmit stopped\n", + dev->name); return NETDEV_TX_BUSY; } } @@ -1107,6 +1110,8 @@ send: if (ret) { pr_err("[%s] fatal: ipa rm timer request resource failed %d\n", dev->name, ret); + dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; return -EFAULT; } /* IPA_RM checking end */ @@ -1122,7 +1127,6 @@ send: if (ret) { ret = NETDEV_TX_BUSY; - dev->stats.tx_dropped++; goto out; } diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c index 78e685f789cd..1ef8ebe3ed7d 100644 --- a/drivers/platform/msm/qpnp-revid.c +++ b/drivers/platform/msm/qpnp-revid.c @@ -56,6 +56,8 @@ static const char *const pmic_names[] = { [PMICOBALT_SUBTYPE] = "PMICOBALT", [PM8005_SUBTYPE] = "PM8005", [PM8937_SUBTYPE] = "PM8937", + [PM2FALCON_SUBTYPE] = "PM2FALCON", + [PMFALCON_SUBTYPE] = "PMFALCON", [PMI8937_SUBTYPE] = "PMI8937", }; diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index 93965dbe99ae..f9d76c56aa2e 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -203,6 +203,13 @@ static struct smb_params v1_params = { .get_proc = smblib_mapping_cc_delta_to_field_value, .set_proc = smblib_mapping_cc_delta_from_field_value, }, + .freq_buck = { + .name = "buck switching frequency", + .reg = CFG_BUCKBOOST_FREQ_SELECT_BUCK_REG, + .min_u = 600, + .max_u = 2000, + .step_u = 200, + }, }; #define STEP_CHARGING_MAX_STEPS 5 diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index de4391024970..198e77469bbe 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -220,6 +220,34 @@ int smblib_get_usb_suspend(struct smb_charger *chg, int *suspend) return rc; } +#define FSW_600HZ_FOR_5V 600 +#define FSW_800HZ_FOR_6V_8V 800 +#define FSW_1MHZ_FOR_REMOVAL 1000 +#define FSW_1MHZ_FOR_9V 1000 +#define FSW_1P2MHZ_FOR_12V 1200 +static int smblib_set_opt_freq_buck(struct smb_charger *chg, int fsw_khz) +{ + union power_supply_propval pval = {0, }; + int rc = 0; + + rc = smblib_set_charge_param(chg, &chg->param.freq_buck, fsw_khz); + if (rc < 0) + dev_err(chg->dev, "Error in setting freq_buck rc=%d\n", rc); + + if (chg->mode == PARALLEL_MASTER && chg->pl.psy) { + pval.intval = fsw_khz; + rc = power_supply_set_property(chg->pl.psy, + POWER_SUPPLY_PROP_BUCK_FREQ, &pval); + if (rc < 0) { + dev_err(chg->dev, + "Could not set parallel buck_freq rc=%d\n", rc); + return rc; + } + } + + return rc; +} + struct apsd_result { const char * const name; const u8 bit; @@ -416,10 +444,13 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, if (min_allowed_uv == MICRO_5V && max_allowed_uv == MICRO_5V) { allowed_voltage = USBIN_ADAPTER_ALLOW_5V; + smblib_set_opt_freq_buck(chg, FSW_600HZ_FOR_5V); } else if (min_allowed_uv == MICRO_9V && max_allowed_uv == MICRO_9V) { allowed_voltage = USBIN_ADAPTER_ALLOW_9V; + smblib_set_opt_freq_buck(chg, FSW_1MHZ_FOR_9V); } else if (min_allowed_uv == MICRO_12V && max_allowed_uv == MICRO_12V) { allowed_voltage = USBIN_ADAPTER_ALLOW_12V; + smblib_set_opt_freq_buck(chg, FSW_1P2MHZ_FOR_12V); } else if (min_allowed_uv < MICRO_9V && max_allowed_uv <= MICRO_9V) { allowed_voltage = USBIN_ADAPTER_ALLOW_5V_TO_9V; } else if (min_allowed_uv < MICRO_9V && max_allowed_uv <= MICRO_12V) { @@ -2178,6 +2209,16 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) int rc; u8 stat; + rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); + return IRQ_HANDLED; + } + + chg->vbus_present = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); + smblib_set_opt_freq_buck(chg, + chg->vbus_present ? FSW_600HZ_FOR_5V : FSW_1MHZ_FOR_REMOVAL); + /* fetch the DPDM regulator */ if (!chg->dpdm_reg && of_get_property(chg->dev->of_node, "dpdm-supply", NULL)) { @@ -2192,14 +2233,6 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) if (!chg->dpdm_reg) goto skip_dpdm_float; - rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); - return IRQ_HANDLED; - } - - chg->vbus_present = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); - if (chg->vbus_present) { if (!regulator_is_enabled(chg->dpdm_reg)) { smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); @@ -2271,6 +2304,60 @@ static void smblib_handle_sdp_enumeration_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +#define QC3_PULSES_FOR_6V 5 +#define QC3_PULSES_FOR_9V 20 +#define QC3_PULSES_FOR_12V 35 +static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) +{ + int rc; + u8 stat; + int pulses; + + if (chg->usb_psy_desc.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); + return; + } + + switch (stat & QC_2P0_STATUS_MASK) { + case QC_5V_BIT: + smblib_set_opt_freq_buck(chg, FSW_600HZ_FOR_5V); + break; + case QC_9V_BIT: + smblib_set_opt_freq_buck(chg, FSW_1MHZ_FOR_9V); + break; + case QC_12V_BIT: + smblib_set_opt_freq_buck(chg, FSW_1P2MHZ_FOR_12V); + break; + default: + smblib_set_opt_freq_buck(chg, FSW_1MHZ_FOR_REMOVAL); + break; + } + } + + if (chg->usb_psy_desc.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); + return; + } + pulses = (stat & QC_PULSE_COUNT_MASK); + + if (pulses < QC3_PULSES_FOR_6V) + smblib_set_opt_freq_buck(chg, FSW_600HZ_FOR_5V); + else if (pulses < QC3_PULSES_FOR_9V) + smblib_set_opt_freq_buck(chg, FSW_800HZ_FOR_6V_8V); + else if (pulses < QC3_PULSES_FOR_12V) + smblib_set_opt_freq_buck(chg, FSW_1MHZ_FOR_9V); + else + smblib_set_opt_freq_buck(chg, FSW_1P2MHZ_FOR_12V); + + } +} + static void smblib_handle_adaptive_voltage_done(struct smb_charger *chg, bool rising) { @@ -2283,10 +2370,20 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, bool rising) { const struct apsd_result *apsd_result; + int rc; if (!rising) return; + /* + * Disable AUTH_IRQ_EN_CFG_BIT to receive adapter voltage + * change interrupt. + */ + rc = smblib_masked_write(chg, USBIN_SOURCE_CHANGE_INTRPT_ENB_REG, + AUTH_IRQ_EN_CFG_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't enable QC auth setting rc=%d\n", rc); + if (chg->mode == PARALLEL_MASTER) vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0); @@ -2392,6 +2489,8 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) smblib_handle_slow_plugin_timeout(chg, (bool)(stat & SLOW_PLUGIN_TIMEOUT_BIT)); + smblib_hvdcp_adaptive_voltage_change(chg); + power_supply_changed(chg->usb_psy); return IRQ_HANDLED; @@ -2410,6 +2509,12 @@ static void typec_source_removal(struct smb_charger *chg) cancel_delayed_work_sync(&chg->hvdcp_detect_work); + /* reset AUTH_IRQ_EN_CFG_BIT */ + rc = smblib_masked_write(chg, USBIN_SOURCE_CHANGE_INTRPT_ENB_REG, + AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT); + if (rc < 0) + smblib_err(chg, "Couldn't enable QC auth setting rc=%d\n", rc); + /* reconfigure allowed voltage for HVDCP */ rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h index ba501761c209..a74fcf730a8c 100644 --- a/drivers/power/qcom-charger/smb-reg.h +++ b/drivers/power/qcom-charger/smb-reg.h @@ -428,6 +428,7 @@ enum { #define USBIN_5V_TO_12V_BIT BIT(2) #define USBIN_5V_TO_9V_BIT BIT(1) #define USBIN_5V_BIT BIT(0) +#define QC_2P0_STATUS_MASK GENMASK(2, 0) #define APSD_STATUS_REG (USBIN_BASE + 0x07) #define APSD_STATUS_7_BIT BIT(7) diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 17c8a5b00843..7569e35b59e0 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -183,5 +183,19 @@ config POWER_RESET_ZX help Reboot support for ZTE SoCs. +config REBOOT_MODE + tristate + +config SYSCON_REBOOT_MODE + tristate "Generic SYSCON regmap reboot mode driver" + depends on OF + select REBOOT_MODE + select MFD_SYSCON + help + Say y here will enable reboot mode driver. This will + get reboot mode arguments and store it in SYSCON mapped + register, then the bootloader can read it to take different + action according to the mode. + endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 3904e7977d07..66568c4497a4 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -20,3 +20,5 @@ obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o +obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o +obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c new file mode 100644 index 000000000000..2dfbbce0f817 --- /dev/null +++ b/drivers/power/reset/reboot-mode.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/reboot.h> +#include "reboot-mode.h" + +#define PREFIX "mode-" + +struct mode_info { + const char *mode; + u32 magic; + struct list_head list; +}; + +static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot, + const char *cmd) +{ + const char *normal = "normal"; + int magic = 0; + struct mode_info *info; + + if (!cmd) + cmd = normal; + + list_for_each_entry(info, &reboot->head, list) { + if (!strcmp(info->mode, cmd)) { + magic = info->magic; + break; + } + } + + return magic; +} + +static int reboot_mode_notify(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct reboot_mode_driver *reboot; + unsigned int magic; + + reboot = container_of(this, struct reboot_mode_driver, reboot_notifier); + magic = get_reboot_mode_magic(reboot, cmd); + if (magic) + reboot->write(reboot, magic); + + return NOTIFY_DONE; +} + +/** + * reboot_mode_register - register a reboot mode driver + * @reboot: reboot mode driver + * + * Returns: 0 on success or a negative error code on failure. + */ +int reboot_mode_register(struct reboot_mode_driver *reboot) +{ + struct mode_info *info; + struct property *prop; + struct device_node *np = reboot->dev->of_node; + size_t len = strlen(PREFIX); + int ret; + + INIT_LIST_HEAD(&reboot->head); + + for_each_property_of_node(np, prop) { + if (strncmp(prop->name, PREFIX, len)) + continue; + + info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto error; + } + + if (of_property_read_u32(np, prop->name, &info->magic)) { + dev_err(reboot->dev, "reboot mode %s without magic number\n", + info->mode); + devm_kfree(reboot->dev, info); + continue; + } + + info->mode = kstrdup_const(prop->name + len, GFP_KERNEL); + if (!info->mode) { + ret = -ENOMEM; + goto error; + } else if (info->mode[0] == '\0') { + kfree_const(info->mode); + ret = -EINVAL; + dev_err(reboot->dev, "invalid mode name(%s): too short!\n", + prop->name); + goto error; + } + + list_add_tail(&info->list, &reboot->head); + } + + reboot->reboot_notifier.notifier_call = reboot_mode_notify; + register_reboot_notifier(&reboot->reboot_notifier); + + return 0; + +error: + list_for_each_entry(info, &reboot->head, list) + kfree_const(info->mode); + + return ret; +} +EXPORT_SYMBOL_GPL(reboot_mode_register); + +/** + * reboot_mode_unregister - unregister a reboot mode driver + * @reboot: reboot mode driver + */ +int reboot_mode_unregister(struct reboot_mode_driver *reboot) +{ + struct mode_info *info; + + unregister_reboot_notifier(&reboot->reboot_notifier); + + list_for_each_entry(info, &reboot->head, list) + kfree_const(info->mode); + + return 0; +} +EXPORT_SYMBOL_GPL(reboot_mode_unregister); + +MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com"); +MODULE_DESCRIPTION("System reboot mode core library"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/reset/reboot-mode.h b/drivers/power/reset/reboot-mode.h new file mode 100644 index 000000000000..2491bb71f591 --- /dev/null +++ b/drivers/power/reset/reboot-mode.h @@ -0,0 +1,14 @@ +#ifndef __REBOOT_MODE_H__ +#define __REBOOT_MODE_H__ + +struct reboot_mode_driver { + struct device *dev; + struct list_head head; + int (*write)(struct reboot_mode_driver *reboot, unsigned int magic); + struct notifier_block reboot_notifier; +}; + +int reboot_mode_register(struct reboot_mode_driver *reboot); +int reboot_mode_unregister(struct reboot_mode_driver *reboot); + +#endif diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c new file mode 100644 index 000000000000..9e1cba5dd58e --- /dev/null +++ b/drivers/power/reset/syscon-reboot-mode.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include "reboot-mode.h" + +struct syscon_reboot_mode { + struct regmap *map; + struct reboot_mode_driver reboot; + u32 offset; + u32 mask; +}; + +static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot, + unsigned int magic) +{ + struct syscon_reboot_mode *syscon_rbm; + int ret; + + syscon_rbm = container_of(reboot, struct syscon_reboot_mode, reboot); + + ret = regmap_update_bits(syscon_rbm->map, syscon_rbm->offset, + syscon_rbm->mask, magic); + if (ret < 0) + dev_err(reboot->dev, "update reboot mode bits failed\n"); + + return ret; +} + +static int syscon_reboot_mode_probe(struct platform_device *pdev) +{ + int ret; + struct syscon_reboot_mode *syscon_rbm; + + syscon_rbm = devm_kzalloc(&pdev->dev, sizeof(*syscon_rbm), GFP_KERNEL); + if (!syscon_rbm) + return -ENOMEM; + + syscon_rbm->reboot.dev = &pdev->dev; + syscon_rbm->reboot.write = syscon_reboot_mode_write; + syscon_rbm->mask = 0xffffffff; + + dev_set_drvdata(&pdev->dev, syscon_rbm); + + syscon_rbm->map = syscon_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(syscon_rbm->map)) + return PTR_ERR(syscon_rbm->map); + + if (of_property_read_u32(pdev->dev.of_node, "offset", + &syscon_rbm->offset)) + return -EINVAL; + + of_property_read_u32(pdev->dev.of_node, "mask", &syscon_rbm->mask); + + ret = reboot_mode_register(&syscon_rbm->reboot); + if (ret) + dev_err(&pdev->dev, "can't register reboot mode\n"); + + return ret; +} + +static int syscon_reboot_mode_remove(struct platform_device *pdev) +{ + struct syscon_reboot_mode *syscon_rbm = dev_get_drvdata(&pdev->dev); + + return reboot_mode_unregister(&syscon_rbm->reboot); +} + +static const struct of_device_id syscon_reboot_mode_of_match[] = { + { .compatible = "syscon-reboot-mode" }, + {} +}; + +static struct platform_driver syscon_reboot_mode_driver = { + .probe = syscon_reboot_mode_probe, + .remove = syscon_reboot_mode_remove, + .driver = { + .name = "syscon-reboot-mode", + .of_match_table = syscon_reboot_mode_of_match, + }, +}; +module_platform_driver(syscon_reboot_mode_driver); + +MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com"); +MODULE_DESCRIPTION("SYSCON reboot mode driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/sensors/sensors_ssc.c b/drivers/sensors/sensors_ssc.c index 4707f74404f2..0910ef34e777 100644 --- a/drivers/sensors/sensors_ssc.c +++ b/drivers/sensors/sensors_ssc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -69,6 +69,8 @@ static void slpi_loader_do(struct platform_device *pdev) { struct slpi_loader_private *priv = NULL; + int ret; + const char *firmware_name = NULL; if (!pdev) { dev_err(&pdev->dev, "%s: Platform device null\n", __func__); @@ -81,6 +83,13 @@ static void slpi_loader_do(struct platform_device *pdev) goto fail; } + ret = of_property_read_string(pdev->dev.of_node, + "qcom,firmware-name", &firmware_name); + if (ret < 0) { + pr_err("can't get fw name.\n"); + goto fail; + } + priv = platform_get_drvdata(pdev); if (!priv) { dev_err(&pdev->dev, @@ -88,7 +97,7 @@ static void slpi_loader_do(struct platform_device *pdev) goto fail; } - priv->pil_h = subsystem_get("slpi"); + priv->pil_h = subsystem_get_with_fwname("slpi", firmware_name); if (IS_ERR(priv->pil_h)) { dev_err(&pdev->dev, "%s: pil get failed,\n", __func__); diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 8c43effadc70..d3f967319d21 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -768,6 +768,15 @@ config MSM_SERVICE_NOTIFIER like audio, the identifier for which is provided by the service locator. +config MSM_QBT1000 + bool "QBT1000 Ultrasonic Fingerprint Sensor" + help + This driver provides services for configuring the fingerprint + sensor hardware and for communicating with the trusted app which + uses it. It enables clocks and provides commands for loading + trusted apps, unloading them and marshalling buffers to the + trusted fingerprint app. + config MSM_RPM_RBCPR_STATS_V2_LOG tristate "MSM Resource Power Manager RPBCPR Stat Driver" depends on DEBUG_FS diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 0105e03b082d..f56c6bf1539a 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_MSM_KERNEL_PROTECT) += kernel_protect.o obj-$(CONFIG_MSM_RTB) += msm_rtb-hotplug.o obj-$(CONFIG_QCOM_REMOTEQDSS) += remoteqdss.o obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o +obj-$(CONFIG_MSM_QBT1000) += qbt1000.o obj-$(CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG) += rpm_rbcpr_stats_v2.o obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o system_stats.o obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 36622e30fbe7..feeed645fc47 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -131,6 +131,9 @@ module_param(qmi_timeout, ulong, 0600); * Registers: WCSS_HM_A_PMM_PMM * Base Address: 0x18880000 */ +#define WCSS_HM_A_PMM_ROOT_CLK_ENABLE 0x80010 +#define PMM_TCXO_CLK_ENABLE BIT(13) + #define PMM_COMMON_IDLEREQ_CSR_OFFSET 0x80120 #define PMM_COMMON_IDLEREQ_CSR_SW_WNOC_IDLEREQ_SET BIT(16) #define PMM_COMMON_IDLEREQ_CSR_WNOC_IDLEACK BIT(26) @@ -1332,8 +1335,28 @@ static int icnss_hw_reset_rf_reset_cmd(struct icnss_priv *priv) static int icnss_hw_reset_switch_to_cxo(struct icnss_priv *priv) { + u32 rdata; + icnss_pr_dbg("RESET: Switch to CXO, state: 0x%lx\n", priv->state); + rdata = icnss_hw_read_reg(priv->mem_base_va, + WCSS_HM_A_PMM_ROOT_CLK_ENABLE); + + icnss_pr_dbg("RESET: PMM_TCXO_CLK_ENABLE : 0x%05lx\n", + rdata & PMM_TCXO_CLK_ENABLE); + + if ((rdata & PMM_TCXO_CLK_ENABLE) == 0) { + icnss_pr_dbg("RESET: Set PMM_TCXO_CLK_ENABLE to 1\n"); + + icnss_hw_write_reg_field(priv->mem_base_va, + WCSS_HM_A_PMM_ROOT_CLK_ENABLE, + PMM_TCXO_CLK_ENABLE, 1); + icnss_hw_poll_reg_field(priv->mem_base_va, + WCSS_HM_A_PMM_ROOT_CLK_ENABLE, + PMM_TCXO_CLK_ENABLE, 1, 10, + ICNSS_HW_REG_RETRY); + } + icnss_hw_write_reg_field(priv->mem_base_va, WCSS_CLK_CTL_NOC_CFG_RCGR_OFFSET, WCSS_CLK_CTL_NOC_CFG_RCGR_SRC_SEL, 0); @@ -1464,6 +1487,26 @@ static int icnss_hw_reset(struct icnss_priv *priv) icnss_hw_reset_switch_to_cxo(priv); + for (i = 0; i < ICNSS_HW_REG_RETRY; i++) { + rdata = icnss_hw_read_reg(priv->mem_base_va, SR_PMM_SR_MSB); + usleep_range(5, 10); + rdata1 = icnss_hw_read_reg(priv->mem_base_va, SR_PMM_SR_MSB); + + icnss_pr_dbg("RESET: SR_PMM_SR_MSB: 0x%08x/0x%08x, XO: 0x%05lx/0x%05lx, AHB: 0x%05lx/0x%05lx\n", + rdata, rdata1, + rdata & SR_PMM_SR_MSB_XO_CLOCK_MASK, + rdata1 & SR_PMM_SR_MSB_XO_CLOCK_MASK, + rdata & SR_PMM_SR_MSB_AHB_CLOCK_MASK, + rdata1 & SR_PMM_SR_MSB_AHB_CLOCK_MASK); + + if ((rdata & SR_PMM_SR_MSB_AHB_CLOCK_MASK) != + (rdata1 & SR_PMM_SR_MSB_AHB_CLOCK_MASK) && + (rdata & SR_PMM_SR_MSB_XO_CLOCK_MASK) != + (rdata1 & SR_PMM_SR_MSB_XO_CLOCK_MASK)) + break; + usleep_range(5, 10); + } + ret = icnss_hw_reset_xo_disable_cmd(priv); if (ret) goto top_level_reset; @@ -2612,10 +2655,10 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, icnss_call_driver_remove(priv); out: - icnss_remove_msa_permissions(priv); - ret = icnss_hw_power_off(priv); + icnss_remove_msa_permissions(priv); + kfree(data); return ret; @@ -4236,6 +4279,11 @@ static int icnss_get_vbatt_info(struct icnss_priv *priv) struct qpnp_vadc_chip *vadc_dev = NULL; int ret = 0; + if (test_bit(VBATT_DISABLE, &quirks)) { + icnss_pr_dbg("VBATT feature is disabled\n"); + return ret; + } + adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss"); if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) { icnss_pr_err("adc_tm_dev probe defer\n"); diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c index f8895e8a7b3d..5752aecb82bd 100644 --- a/drivers/soc/qcom/pil-q6v5.c +++ b/drivers/soc/qcom/pil-q6v5.c @@ -388,7 +388,7 @@ static int __pil_q6v55_reset(struct pil_desc *pil) mb(); udelay(1); - if (drv->qdsp6v62_1_2) { + if (drv->qdsp6v62_1_2 || drv->qdsp6v62_1_5) { for (i = BHS_CHECK_MAX_LOOPS; i > 0; i--) { if (readl_relaxed(drv->reg_base + QDSP6V62SS_BHS_STATUS) & QDSP6v55_BHS_EN_REST_ACK) @@ -488,7 +488,8 @@ static int __pil_q6v55_reset(struct pil_desc *pil) */ udelay(1); } - } else if (drv->qdsp6v61_1_1 || drv->qdsp6v62_1_2) { + } else if (drv->qdsp6v61_1_1 || drv->qdsp6v62_1_2 || + drv->qdsp6v62_1_5) { /* Deassert QDSP6 compiler memory clamp */ val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); val &= ~QDSP6v55_CLAMP_QMC_MEM; @@ -501,7 +502,13 @@ static int __pil_q6v55_reset(struct pil_desc *pil) /* Turn on L1, L2, ETB and JU memories 1 at a time */ val = readl_relaxed(drv->reg_base + QDSP6V6SS_MEM_PWR_CTL); - for (i = 28; i >= 0; i--) { + + if (drv->qdsp6v62_1_5) + i = 29; + else + i = 28; + + for ( ; i >= 0; i--) { val |= BIT(i); writel_relaxed(val, drv->reg_base + QDSP6V6SS_MEM_PWR_CTL); @@ -663,6 +670,9 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev) drv->qdsp6v62_1_2 = of_property_read_bool(pdev->dev.of_node, "qcom,qdsp6v62-1-2"); + drv->qdsp6v62_1_5 = of_property_read_bool(pdev->dev.of_node, + "qcom,qdsp6v62-1-5"); + drv->non_elf_image = of_property_read_bool(pdev->dev.of_node, "qcom,mba-image-is-not-elf"); diff --git a/drivers/soc/qcom/pil-q6v5.h b/drivers/soc/qcom/pil-q6v5.h index 6a59b06f7b6c..9e8b8511e69b 100644 --- a/drivers/soc/qcom/pil-q6v5.h +++ b/drivers/soc/qcom/pil-q6v5.h @@ -62,6 +62,7 @@ struct q6v5_data { bool qdsp6v56_1_10; bool qdsp6v61_1_1; bool qdsp6v62_1_2; + bool qdsp6v62_1_5; bool non_elf_image; bool restart_reg_sec; bool override_acc; diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c new file mode 100644 index 000000000000..dd543daca1b4 --- /dev/null +++ b/drivers/soc/qcom/qbt1000.c @@ -0,0 +1,1263 @@ +/* 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. + */ + +#define pr_fmt(fmt) "qbt1000:%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/debugfs.h> +#include <linux/types.h> +#include <linux/cdev.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/pm.h> +#include <linux/of.h> +#include <linux/mutex.h> +#include <linux/atomic.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/input.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <uapi/linux/qbt1000.h> +#include <soc/qcom/scm.h> +#include "qseecom_kernel.h" + +#define QBT1000_DEV "qbt1000" +#define QBT1000_IN_DEV_NAME "qbt1000_key_input" +#define QBT1000_IN_DEV_VERSION 0x0100 +#define MAX_FW_EVENTS 128 +#define FP_APP_CMD_RX_IPC 132 +#define FW_MAX_IPC_MSG_DATA_SIZE 0x500 +#define IPC_MSG_ID_CBGE_REQUIRED 29 + +/* + * shared buffer size - init with max value, + * user space will provide new value upon tz app load + */ +static uint32_t g_app_buf_size = SZ_256K; +static char const *const FP_APP_NAME = "fingerpr"; + +struct finger_detect_gpio { + int gpio; + int active_low; + int irq; + struct work_struct work; + unsigned int key_code; + int power_key_enabled; + int last_gpio_state; + int event_reported; +}; + +struct fw_event_desc { + enum qbt1000_fw_event ev; +}; + +struct fw_ipc_info { + int gpio; + int irq; +}; + +struct qbt1000_drvdata { + struct class *qbt1000_class; + struct cdev qbt1000_cdev; + struct device *dev; + char *qbt1000_node; + struct clk **clocks; + unsigned clock_count; + uint8_t clock_state; + unsigned root_clk_idx; + unsigned frequency; + atomic_t available; + struct mutex mutex; + struct mutex fw_events_mutex; + struct input_dev *in_dev; + struct fw_ipc_info fw_ipc; + struct finger_detect_gpio fd_gpio; + DECLARE_KFIFO(fw_events, struct fw_event_desc, MAX_FW_EVENTS); + wait_queue_head_t read_wait_queue; + struct qseecom_handle *fp_app_handle; +}; + +/* +* struct fw_ipc_cmd - +* used to store IPC commands to/from firmware +* @status - indicates whether sending/getting the IPC message was successful +* @msg_type - the type of IPC message +* @msg_len - the length of the message data +* @resp_needed - whether a response is needed for this message +* @msg_data - any extra data associated with the message +*/ +struct fw_ipc_cmd { + uint32_t status; + uint32_t msg_type; + uint32_t msg_len; + uint32_t resp_needed; + uint8_t msg_data[FW_MAX_IPC_MSG_DATA_SIZE]; +}; + +/* +* struct ipc_msg_type_to_fw_event - +* entry in mapping between an IPC message type to a firmware event +* @msg_type - IPC message type, as reported by firmware +* @fw_event - corresponding firmware event code to report to driver client +*/ +struct ipc_msg_type_to_fw_event { + uint32_t msg_type; + enum qbt1000_fw_event fw_event; +}; + +/* mapping between firmware IPC message types to HLOS firmware events */ +struct ipc_msg_type_to_fw_event g_msg_to_event[] = { + {IPC_MSG_ID_CBGE_REQUIRED, FW_EVENT_CBGE_REQUIRED} +}; + +/** + * get_cmd_rsp_buffers() - Function sets cmd & rsp buffer pointers and + * aligns buffer lengths + * @hdl: index of qseecom_handle + * @cmd: req buffer - set to qseecom_handle.sbuf + * @cmd_len: ptr to req buffer len + * @rsp: rsp buffer - set to qseecom_handle.sbuf + offset + * @rsp_len: ptr to rsp buffer len + * + * Return: 0 on success. Error code on failure. + */ +static int get_cmd_rsp_buffers(struct qseecom_handle *hdl, + void **cmd, + uint32_t *cmd_len, + void **rsp, + uint32_t *rsp_len) +{ + /* 64 bytes alignment for QSEECOM */ + *cmd_len = ALIGN(*cmd_len, 64); + *rsp_len = ALIGN(*rsp_len, 64); + + if ((*rsp_len + *cmd_len) > g_app_buf_size) { + pr_err("buffer too small to hold cmd=%d and rsp=%d\n", + *cmd_len, *rsp_len); + return -ENOMEM; + } + + *cmd = hdl->sbuf; + *rsp = hdl->sbuf + *cmd_len; + return 0; +} + +/** + * clocks_on() - Function votes for SPI and AHB clocks to be on and sets + * the clk rate to predetermined value for SPI. + * @drvdata: ptr to driver data + * + * Return: 0 on success. Error code on failure. + */ +static int clocks_on(struct qbt1000_drvdata *drvdata) +{ + int rc = 0; + int index; + + if (!drvdata->clock_state) { + for (index = 0; index < drvdata->clock_count; index++) { + pr_debug("set clock rate at idx:%d, freq: %u\n", + index, drvdata->frequency); + if (index == drvdata->root_clk_idx) { + rc = clk_set_rate(drvdata->clocks[index], + drvdata->frequency); + if (rc) { + pr_err("failure set clock rate at idx:%d\n", + index); + goto unprepare; + } + } + + rc = clk_prepare_enable(drvdata->clocks[index]); + if (rc) { + pr_err("failure to prepare clk at idx:%d\n", + index); + goto unprepare; + } + + + } + drvdata->clock_state = 1; + } + goto end; + +unprepare: + for (--index; index >= 0; index--) + clk_disable_unprepare(drvdata->clocks[index]); + +end: + return rc; +} + +/** + * clocks_off() - Function votes for SPI and AHB clocks to be off + * @drvdata: ptr to driver data + * + * Return: None + */ +static void clocks_off(struct qbt1000_drvdata *drvdata) +{ + int index; + + if (drvdata->clock_state) { + for (index = 0; index < drvdata->clock_count; index++) + clk_disable_unprepare(drvdata->clocks[index]); + drvdata->clock_state = 0; + } +} + +/** + * send_tz_cmd() - Function sends a command to TZ + * + * @drvdata: pointer to driver data + * @app_handle: handle to tz app + * @is_user_space: 1 if the cmd buffer is in user space, 0 + * otherwise + * @cmd: command buffer to send + * @cmd_len: length of the command buffer + * @rsp: output, will be set to location of response buffer + * @rsp_len: max size of response + * + * Return: 0 on success. + */ +static int send_tz_cmd(struct qbt1000_drvdata *drvdata, + struct qseecom_handle *app_handle, + int is_user_space, + void *cmd, uint32_t cmd_len, + void **rsp, uint32_t rsp_len) +{ + int rc = 0; + void *aligned_cmd; + void *aligned_rsp; + uint32_t aligned_cmd_len; + uint32_t aligned_rsp_len; + + /* init command and response buffers and align lengths */ + aligned_cmd_len = cmd_len; + aligned_rsp_len = rsp_len; + + rc = get_cmd_rsp_buffers(app_handle, + (void **)&aligned_cmd, + &aligned_cmd_len, + (void **)&aligned_rsp, + &aligned_rsp_len); + + if (rc != 0) + goto end; + + if (aligned_cmd - cmd + cmd_len > g_app_buf_size) { + rc = -ENOMEM; + goto end; + } + + if (is_user_space) { + rc = copy_from_user(aligned_cmd, (void __user *)cmd, + cmd_len); + if (rc != 0) { + pr_err("failure to copy user space buf %d\n", rc); + rc = -EFAULT; + goto end; + } + } else + memcpy(aligned_cmd, cmd, cmd_len); + + + /* vote for clocks before sending TZ command */ + rc = clocks_on(drvdata); + if (rc != 0) { + pr_err("failure to enable clocks %d\n", rc); + goto end; + } + + /* send cmd to TZ */ + rc = qseecom_send_command(app_handle, + aligned_cmd, + aligned_cmd_len, + aligned_rsp, + aligned_rsp_len); + + /* un-vote for clocks */ + clocks_off(drvdata); + + if (rc != 0) { + pr_err("failure to send tz cmd %d\n", rc); + goto end; + } + + *rsp = aligned_rsp; + +end: + return rc; +} + +/** + * qbt1000_open() - Function called when user space opens device. + * Successful if driver not currently open. + * @inode: ptr to inode object + * @file: ptr to file object + * + * Return: 0 on success. Error code on failure. + */ +static int qbt1000_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + struct qbt1000_drvdata *drvdata = container_of(inode->i_cdev, + struct qbt1000_drvdata, + qbt1000_cdev); + file->private_data = drvdata; + + pr_debug("qbt1000_open begin\n"); + /* disallowing concurrent opens */ + if (!atomic_dec_and_test(&drvdata->available)) { + atomic_inc(&drvdata->available); + rc = -EBUSY; + } + + pr_debug("qbt1000_open end : %d\n", rc); + return rc; +} + +/** + * qbt1000_release() - Function called when user space closes device. + + * @inode: ptr to inode object + * @file: ptr to file object + * + * Return: 0 on success. Error code on failure. + */ +static int qbt1000_release(struct inode *inode, struct file *file) +{ + struct qbt1000_drvdata *drvdata = file->private_data; + + atomic_inc(&drvdata->available); + return 0; +} + +/** + * qbt1000_ioctl() - Function called when user space calls ioctl. + * @file: struct file - not used + * @cmd: cmd identifier:QBT1000_LOAD_APP,QBT1000_UNLOAD_APP, + * QBT1000_SEND_TZCMD + * @arg: ptr to relevant structe: either qbt1000_app or + * qbt1000_send_tz_cmd depending on which cmd is passed + * + * Return: 0 on success. Error code on failure. + */ +static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) +{ + int rc = 0; + void __user *priv_arg = (void __user *)arg; + struct qbt1000_drvdata *drvdata; + + drvdata = file->private_data; + + mutex_lock(&drvdata->mutex); + + pr_debug("qbt1000_ioctl %d\n", cmd); + + switch (cmd) { + case QBT1000_LOAD_APP: + { + struct qbt1000_app app; + struct qseecom_handle *app_handle; + + if (copy_from_user(&app, priv_arg, + sizeof(app)) != 0) { + rc = -EFAULT; + pr_err("failed copy from user space-LOAD\n"); + goto end; + } + + if (!app.app_handle) { + dev_err(drvdata->dev, "%s: LOAD app_handle is null\n", + __func__); + rc = -EINVAL; + goto end; + } + + pr_debug("app %s load before\n", app.name); + + /* start the TZ app */ + rc = qseecom_start_app(&app_handle, app.name, app.size); + if (rc == 0) { + g_app_buf_size = app.size; + rc = qseecom_set_bandwidth(app_handle, + app.high_band_width == 1 ? true : false); + if (rc != 0) { + /* log error, allow to continue */ + pr_err("App %s failed to set bw\n", app.name); + } + } else { + pr_err("app %s failed to load\n", app.name); + goto end; + } + + /* copy the app handle to user */ + rc = copy_to_user((void __user *)app.app_handle, &app_handle, + sizeof(*app.app_handle)); + + if (rc != 0) { + dev_err(drvdata->dev, + "%s: Failed copy 2us LOAD rc:%d\n", + __func__, rc); + rc = -ENOMEM; + goto end; + } + + pr_debug("app %s load after\n", app.name); + + if (!strcmp(app.name, FP_APP_NAME)) + drvdata->fp_app_handle = app_handle; + + break; + } + case QBT1000_UNLOAD_APP: + { + struct qbt1000_app app; + struct qseecom_handle *app_handle; + + if (copy_from_user(&app, priv_arg, + sizeof(app)) != 0) { + rc = -ENOMEM; + pr_err("failed copy from user space-UNLOAD\n"); + goto end; + } + + if (!app.app_handle) { + dev_err(drvdata->dev, "%s: UNLOAD app_handle is null\n", + __func__); + rc = -EINVAL; + goto end; + } + + rc = copy_from_user(&app_handle, app.app_handle, + sizeof(app_handle)); + + if (rc != 0) { + dev_err(drvdata->dev, + "%s: Failed copy from user space-UNLOAD handle rc:%d\n", + __func__, rc); + rc = -ENOMEM; + goto end; + } + + /* if the app hasn't been loaded already, return err */ + if (!app_handle) { + pr_err("app not loaded\n"); + rc = -EINVAL; + goto end; + } + + if (drvdata->fp_app_handle == app_handle) + drvdata->fp_app_handle = 0; + + /* set bw & shutdown the TZ app */ + qseecom_set_bandwidth(app_handle, + app.high_band_width == 1 ? true : false); + rc = qseecom_shutdown_app(&app_handle); + if (rc != 0) { + pr_err("app failed to shutdown\n"); + goto end; + } + + /* copy the app handle (should be null) to user */ + rc = copy_to_user((void __user *)app.app_handle, &app_handle, + sizeof(*app.app_handle)); + + if (rc != 0) { + dev_err(drvdata->dev, + "%s: Failed copy 2us UNLOAD rc:%d\n", + __func__, rc); + rc = -ENOMEM; + goto end; + } + + break; + } + case QBT1000_SEND_TZCMD: + { + struct qbt1000_send_tz_cmd tzcmd; + void *rsp_buf; + + if (copy_from_user(&tzcmd, priv_arg, + sizeof(tzcmd)) + != 0) { + rc = -EFAULT; + pr_err("failed copy from user space %d\n", rc); + goto end; + } + + if (tzcmd.req_buf_len > g_app_buf_size || + tzcmd.rsp_buf_len > g_app_buf_size) { + rc = -ENOMEM; + pr_err("invalid cmd buf len, req=%d, rsp=%d\n", + tzcmd.req_buf_len, tzcmd.rsp_buf_len); + goto end; + } + + /* if the app hasn't been loaded already, return err */ + if (!tzcmd.app_handle) { + pr_err("app not loaded\n"); + rc = -EINVAL; + goto end; + } + + rc = send_tz_cmd(drvdata, + tzcmd.app_handle, 1, + tzcmd.req_buf, tzcmd.req_buf_len, + &rsp_buf, tzcmd.rsp_buf_len); + + if (rc < 0) { + pr_err("failure sending command to tz\n"); + goto end; + } + + /* copy rsp buf back to user space buffer */ + rc = copy_to_user((void __user *)tzcmd.rsp_buf, + rsp_buf, tzcmd.rsp_buf_len); + if (rc != 0) { + pr_err("failed copy 2us rc:%d bytes %d:\n", + rc, tzcmd.rsp_buf_len); + rc = -EFAULT; + goto end; + } + + break; + } + case QBT1000_SET_FINGER_DETECT_KEY: + { + struct qbt1000_set_finger_detect_key set_fd_key; + + if (copy_from_user(&set_fd_key, priv_arg, + sizeof(set_fd_key)) + != 0) { + rc = -EFAULT; + pr_err("failed copy from user space %d\n", rc); + goto end; + } + + drvdata->fd_gpio.key_code = set_fd_key.key_code; + + break; + } + case QBT1000_CONFIGURE_POWER_KEY: + { + struct qbt1000_configure_power_key power_key; + + if (copy_from_user(&power_key, priv_arg, + sizeof(power_key)) + != 0) { + rc = -EFAULT; + pr_err("failed copy from user space %d\n", rc); + goto end; + } + + drvdata->fd_gpio.power_key_enabled = power_key.enable; + + break; + } + default: + pr_err("invalid cmd %d\n", cmd); + rc = -ENOIOCTLCMD; + goto end; + } + +end: + mutex_unlock(&drvdata->mutex); + return rc; +} + +static int get_events_fifo_len_locked(struct qbt1000_drvdata *drvdata) +{ + int len; + + mutex_lock(&drvdata->fw_events_mutex); + len = kfifo_len(&drvdata->fw_events); + mutex_unlock(&drvdata->fw_events_mutex); + + return len; +} + +static ssize_t qbt1000_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct fw_event_desc fw_event; + struct qbt1000_drvdata *drvdata = filp->private_data; + + if (cnt < sizeof(fw_event.ev)) + return -EINVAL; + + mutex_lock(&drvdata->fw_events_mutex); + + while (kfifo_len(&drvdata->fw_events) == 0) { + mutex_unlock(&drvdata->fw_events_mutex); + + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + pr_debug("fw_events fifo: empty, waiting\n"); + + if (wait_event_interruptible(drvdata->read_wait_queue, + (get_events_fifo_len_locked(drvdata) > 0))) + return -ERESTARTSYS; + + mutex_lock(&drvdata->fw_events_mutex); + } + + if (!kfifo_get(&drvdata->fw_events, &fw_event)) { + pr_debug("fw_events fifo: unexpectedly empty\n"); + + mutex_unlock(&drvdata->fw_events_mutex); + return -EINVAL; + } + + mutex_unlock(&drvdata->fw_events_mutex); + + pr_debug("fw_event: %d\n", (int)fw_event.ev); + return copy_to_user(ubuf, &fw_event.ev, sizeof(fw_event.ev)); +} + +static unsigned int qbt1000_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct qbt1000_drvdata *drvdata = filp->private_data; + unsigned int mask = 0; + + poll_wait(filp, &drvdata->read_wait_queue, wait); + + if (kfifo_len(&drvdata->fw_events) > 0) + mask |= (POLLIN | POLLRDNORM); + + return mask; +} + +static const struct file_operations qbt1000_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = qbt1000_ioctl, + .open = qbt1000_open, + .release = qbt1000_release, + .read = qbt1000_read, + .poll = qbt1000_poll +}; + +static int qbt1000_dev_register(struct qbt1000_drvdata *drvdata) +{ + dev_t dev_no; + int ret = 0; + size_t node_size; + char *node_name = QBT1000_DEV; + struct device *dev = drvdata->dev; + struct device *device; + + node_size = strlen(node_name) + 1; + + drvdata->qbt1000_node = devm_kzalloc(dev, node_size, GFP_KERNEL); + if (!drvdata->qbt1000_node) { + ret = -ENOMEM; + goto err_alloc; + } + + strlcpy(drvdata->qbt1000_node, node_name, node_size); + + ret = alloc_chrdev_region(&dev_no, 0, 1, drvdata->qbt1000_node); + if (ret) { + pr_err("alloc_chrdev_region failed %d\n", ret); + goto err_alloc; + } + + cdev_init(&drvdata->qbt1000_cdev, &qbt1000_fops); + + drvdata->qbt1000_cdev.owner = THIS_MODULE; + ret = cdev_add(&drvdata->qbt1000_cdev, dev_no, 1); + if (ret) { + pr_err("cdev_add failed %d\n", ret); + goto err_cdev_add; + } + + drvdata->qbt1000_class = class_create(THIS_MODULE, + drvdata->qbt1000_node); + if (IS_ERR(drvdata->qbt1000_class)) { + ret = PTR_ERR(drvdata->qbt1000_class); + pr_err("class_create failed %d\n", ret); + goto err_class_create; + } + + device = device_create(drvdata->qbt1000_class, NULL, + drvdata->qbt1000_cdev.dev, drvdata, + drvdata->qbt1000_node); + if (IS_ERR(device)) { + ret = PTR_ERR(device); + pr_err("device_create failed %d\n", ret); + goto err_dev_create; + } + + return 0; +err_dev_create: + class_destroy(drvdata->qbt1000_class); +err_class_create: + cdev_del(&drvdata->qbt1000_cdev); +err_cdev_add: + unregister_chrdev_region(drvdata->qbt1000_cdev.dev, 1); +err_alloc: + return ret; +} + +/** + * qbt1000_create_input_device() - Function allocates an input + * device, configures it for key events and registers it + * + * @drvdata: ptr to driver data + * + * Return: 0 on success. Error code on failure. + */ +static int qbt1000_create_input_device(struct qbt1000_drvdata *drvdata) +{ + int rc = 0; + + drvdata->in_dev = input_allocate_device(); + if (drvdata->in_dev == NULL) { + dev_err(drvdata->dev, "%s: input_allocate_device() failed\n", + __func__); + rc = -ENOMEM; + goto end; + } + + drvdata->in_dev->name = QBT1000_IN_DEV_NAME; + drvdata->in_dev->phys = NULL; + drvdata->in_dev->id.bustype = BUS_HOST; + drvdata->in_dev->id.vendor = 0x0001; + drvdata->in_dev->id.product = 0x0001; + drvdata->in_dev->id.version = QBT1000_IN_DEV_VERSION; + + drvdata->in_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + drvdata->in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + drvdata->in_dev->keybit[BIT_WORD(KEY_HOMEPAGE)] |= + BIT_MASK(KEY_HOMEPAGE); + drvdata->in_dev->keybit[BIT_WORD(KEY_CAMERA)] |= + BIT_MASK(KEY_CAMERA); + drvdata->in_dev->keybit[BIT_WORD(KEY_POWER)] |= + BIT_MASK(KEY_POWER); + + input_set_abs_params(drvdata->in_dev, ABS_X, + 0, + 1000, + 0, 0); + input_set_abs_params(drvdata->in_dev, ABS_Y, + 0, + 1000, + 0, 0); + + rc = input_register_device(drvdata->in_dev); + if (rc) { + dev_err(drvdata->dev, "%s: input_reg_dev() failed %d\n", + __func__, rc); + goto end; + } + +end: + if (rc) + input_free_device(drvdata->in_dev); + return rc; +} + +static void purge_finger_events(struct qbt1000_drvdata *drvdata) +{ + int i, fifo_len; + struct fw_event_desc fw_event; + + fifo_len = kfifo_len(&drvdata->fw_events); + + for (i = 0; i < fifo_len; i++) { + if (!kfifo_get(&drvdata->fw_events, &fw_event)) + pr_err("fw events fifo: could not remove oldest item\n"); + else if (fw_event.ev != FW_EVENT_FINGER_DOWN + && fw_event.ev != FW_EVENT_FINGER_UP) + kfifo_put(&drvdata->fw_events, fw_event); + } +} + +static void qbt1000_gpio_report_event(struct qbt1000_drvdata *drvdata) +{ + int state; + struct fw_event_desc fw_event; + + state = (__gpio_get_value(drvdata->fd_gpio.gpio) ? 1 : 0) + ^ drvdata->fd_gpio.active_low; + + if (drvdata->fd_gpio.event_reported + && state == drvdata->fd_gpio.last_gpio_state) + return; + + pr_debug("gpio %d: report state %d\n", drvdata->fd_gpio.gpio, state); + + drvdata->fd_gpio.event_reported = 1; + drvdata->fd_gpio.last_gpio_state = state; + + if (drvdata->fd_gpio.key_code) { + input_event(drvdata->in_dev, EV_KEY, + drvdata->fd_gpio.key_code, !!state); + input_sync(drvdata->in_dev); + } + + if (state && drvdata->fd_gpio.power_key_enabled) { + input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 1); + input_sync(drvdata->in_dev); + input_event(drvdata->in_dev, EV_KEY, KEY_POWER, 0); + input_sync(drvdata->in_dev); + } + + fw_event.ev = (state ? FW_EVENT_FINGER_DOWN : FW_EVENT_FINGER_UP); + + mutex_lock(&drvdata->fw_events_mutex); + + if (kfifo_is_full(&drvdata->fw_events)) { + struct fw_event_desc dummy_fw_event; + + pr_warn("fw events fifo: full, dropping oldest item\n"); + if (!kfifo_get(&drvdata->fw_events, &dummy_fw_event)) + pr_err("fw events fifo: could not remove oldest item\n"); + } + + purge_finger_events(drvdata); + + if (!kfifo_put(&drvdata->fw_events, fw_event)) + pr_err("fw events fifo: error adding item\n"); + + mutex_unlock(&drvdata->fw_events_mutex); + wake_up_interruptible(&drvdata->read_wait_queue); +} + +static void qbt1000_gpio_work_func(struct work_struct *work) +{ + struct qbt1000_drvdata *drvdata = + container_of(work, struct qbt1000_drvdata, fd_gpio.work); + + qbt1000_gpio_report_event(drvdata); + + pm_relax(drvdata->dev); +} + +static irqreturn_t qbt1000_gpio_isr(int irq, void *dev_id) +{ + struct qbt1000_drvdata *drvdata = dev_id; + + if (irq != drvdata->fd_gpio.irq) { + pr_warn("invalid irq %d (expected %d)\n", + irq, drvdata->fd_gpio.irq); + return IRQ_HANDLED; + } + + pm_stay_awake(drvdata->dev); + schedule_work(&drvdata->fd_gpio.work); + + return IRQ_HANDLED; +} + +/** + * qbt1000_ipc_irq_handler() - function processes IPC + * interrupts on its own thread + * @irq: the interrupt that occurred + * @dev_id: pointer to the qbt1000_drvdata + * + * Return: IRQ_HANDLED when complete + */ +static irqreturn_t qbt1000_ipc_irq_handler(int irq, void *dev_id) +{ + struct fw_ipc_cmd *rx_cmd; + int i; + uint32_t rxipc = FP_APP_CMD_RX_IPC; + struct qbt1000_drvdata *drvdata = (struct qbt1000_drvdata *)dev_id; + int rc = 0; + + pm_stay_awake(drvdata->dev); + + mutex_lock(&drvdata->mutex); + + if (irq != drvdata->fw_ipc.irq) { + pr_warn("invalid irq %d (expected %d)\n", + irq, drvdata->fw_ipc.irq); + goto end; + } + + pr_debug("firmware interrupt received (irq %d)\n", irq); + + if (!drvdata->fp_app_handle) + goto end; + + /* + * send the TZ command to fetch the message from firmware + * TZ will process the message if it can + */ + rc = send_tz_cmd(drvdata, drvdata->fp_app_handle, 0, + &rxipc, sizeof(rxipc), + (void *)&rx_cmd, sizeof(*rx_cmd)); + + if (rc < 0) { + pr_err("failure sending tz cmd %d\n", rxipc); + goto end; + } + + if (rx_cmd->status != 0) { + pr_err("tz command failed to complete\n"); + goto end; + } + + /* + * given the IPC message type, search for a corresponding event for the + * driver client. If found, add to the events FIFO + */ + for (i = 0; i < ARRAY_SIZE(g_msg_to_event); i++) { + if (g_msg_to_event[i].msg_type == rx_cmd->msg_type) { + enum qbt1000_fw_event ev = g_msg_to_event[i].fw_event; + struct fw_event_desc fw_ev_desc; + + mutex_lock(&drvdata->fw_events_mutex); + pr_debug("fw events: add %d\n", (int) ev); + fw_ev_desc.ev = ev; + + if (!kfifo_put(&drvdata->fw_events, fw_ev_desc)) + pr_err("fw events: fifo full, drop event %d\n", + (int) ev); + + mutex_unlock(&drvdata->fw_events_mutex); + wake_up_interruptible(&drvdata->read_wait_queue); + break; + } + } + +end: + mutex_unlock(&drvdata->mutex); + pm_relax(drvdata->dev); + return IRQ_HANDLED; +} + +static int setup_fd_gpio_irq(struct platform_device *pdev, + struct qbt1000_drvdata *drvdata) +{ + int rc = 0; + int irq; + const char *desc = "qbt_finger_detect"; + + rc = devm_gpio_request_one(&pdev->dev, drvdata->fd_gpio.gpio, + GPIOF_IN, desc); + + if (rc < 0) { + pr_err("failed to request gpio %d, error %d\n", + drvdata->fd_gpio.gpio, rc); + goto end; + } + + irq = gpio_to_irq(drvdata->fd_gpio.gpio); + if (irq < 0) { + rc = irq; + pr_err("unable to get irq number for gpio %d, error %d\n", + drvdata->fd_gpio.gpio, rc); + goto end; + } + + drvdata->fd_gpio.irq = irq; + INIT_WORK(&drvdata->fd_gpio.work, qbt1000_gpio_work_func); + + rc = devm_request_any_context_irq(&pdev->dev, drvdata->fd_gpio.irq, + qbt1000_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + desc, drvdata); + + if (rc < 0) { + pr_err("unable to claim irq %d; error %d\n", + drvdata->fd_gpio.irq, rc); + goto end; + } + +end: + return rc; +} + +static int setup_ipc_irq(struct platform_device *pdev, + struct qbt1000_drvdata *drvdata) +{ + int rc = 0; + const char *desc = "qbt_ipc"; + + drvdata->fw_ipc.irq = gpio_to_irq(drvdata->fw_ipc.gpio); + if (drvdata->fw_ipc.irq < 0) { + rc = drvdata->fw_ipc.irq; + pr_err("no irq for gpio %d, error=%d\n", + drvdata->fw_ipc.gpio, rc); + goto end; + } + + rc = devm_gpio_request_one(&pdev->dev, drvdata->fw_ipc.gpio, + GPIOF_IN, desc); + + if (rc < 0) { + pr_err("failed to request gpio %d, error %d\n", + drvdata->fw_ipc.gpio, rc); + goto end; + } + + rc = devm_request_threaded_irq(&pdev->dev, + drvdata->fw_ipc.irq, + NULL, + qbt1000_ipc_irq_handler, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + desc, + drvdata); + + if (rc < 0) { + pr_err("failed to register for ipc irq %d, rc = %d\n", + drvdata->fw_ipc.irq, rc); + goto end; + } + +end: + return rc; +} + +/** + * qbt1000_read_device_tree() - Function reads device tree + * properties into driver data + * @pdev: ptr to platform device object + * @drvdata: ptr to driver data + * + * Return: 0 on success. Error code on failure. + */ +static int qbt1000_read_device_tree(struct platform_device *pdev, + struct qbt1000_drvdata *drvdata) +{ + int rc = 0; + uint8_t clkcnt = 0; + int index = 0; + uint32_t rate; + const char *clock_name; + int gpio; + enum of_gpio_flags flags; + + /* obtain number of clocks from hw config */ + clkcnt = of_property_count_strings(pdev->dev.of_node, "clock-names"); + if (IS_ERR_VALUE(drvdata->clock_count)) { + pr_err("failed to get clock names\n"); + rc = -EINVAL; + goto end; + } + + /* sanity check for max clock count */ + if (clkcnt > 16) { + pr_err("invalid clock count %d\n", clkcnt); + rc = -EINVAL; + goto end; + } + + /* alloc mem for clock array - auto free if probe fails */ + drvdata->clock_count = clkcnt; + pr_debug("clock count %d\n", clkcnt); + drvdata->clocks = devm_kzalloc(&pdev->dev, + sizeof(struct clk *) * drvdata->clock_count, GFP_KERNEL); + if (!drvdata->clocks) { + rc = -ENOMEM; + goto end; + } + + /* load clock names */ + for (index = 0; index < drvdata->clock_count; index++) { + of_property_read_string_index(pdev->dev.of_node, + "clock-names", + index, &clock_name); + pr_debug("getting clock %s\n", clock_name); + drvdata->clocks[index] = devm_clk_get(&pdev->dev, clock_name); + if (IS_ERR(drvdata->clocks[index])) { + rc = PTR_ERR(drvdata->clocks[index]); + if (rc != -EPROBE_DEFER) + pr_err("failed to get %s\n", clock_name); + goto end; + } + + if (!strcmp(clock_name, "iface_clk")) { + pr_debug("root index %d\n", index); + drvdata->root_clk_idx = index; + } + } + + /* read clock frequency */ + if (of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &rate) == 0) { + pr_debug("clk frequency %d\n", rate); + drvdata->frequency = rate; + } + + /* read IPC gpio */ + drvdata->fw_ipc.gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,ipc-gpio", 0); + if (drvdata->fw_ipc.gpio < 0) { + rc = drvdata->fw_ipc.gpio; + pr_err("ipc gpio not found, error=%d\n", rc); + goto end; + } + + /* read finger detect GPIO configuration */ + gpio = of_get_named_gpio_flags(pdev->dev.of_node, + "qcom,finger-detect-gpio", 0, &flags); + if (gpio < 0) { + pr_err("failed to get gpio flags\n"); + rc = gpio; + goto end; + } + + drvdata->fd_gpio.gpio = gpio; + drvdata->fd_gpio.active_low = flags & OF_GPIO_ACTIVE_LOW; + +end: + return rc; +} + +/** + * qbt1000_probe() - Function loads hardware config from device tree + * @pdev: ptr to platform device object + * + * Return: 0 on success. Error code on failure. + */ +static int qbt1000_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qbt1000_drvdata *drvdata; + int rc = 0; + + pr_debug("qbt1000_probe begin\n"); + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->dev = &pdev->dev; + platform_set_drvdata(pdev, drvdata); + + rc = qbt1000_read_device_tree(pdev, drvdata); + if (rc < 0) + goto end; + + atomic_set(&drvdata->available, 1); + + mutex_init(&drvdata->mutex); + mutex_init(&drvdata->fw_events_mutex); + + rc = qbt1000_dev_register(drvdata); + if (rc < 0) + goto end; + + INIT_KFIFO(drvdata->fw_events); + init_waitqueue_head(&drvdata->read_wait_queue); + + rc = qbt1000_create_input_device(drvdata); + if (rc < 0) + goto end; + + rc = setup_fd_gpio_irq(pdev, drvdata); + if (rc < 0) + goto end; + + rc = setup_ipc_irq(pdev, drvdata); + if (rc < 0) + goto end; + + rc = device_init_wakeup(&pdev->dev, 1); + if (rc < 0) + goto end; + +end: + pr_debug("qbt1000_probe end : %d\n", rc); + return rc; +} + +static int qbt1000_remove(struct platform_device *pdev) +{ + struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev); + + input_unregister_device(drvdata->in_dev); + + clocks_off(drvdata); + mutex_destroy(&drvdata->mutex); + mutex_destroy(&drvdata->fw_events_mutex); + + device_destroy(drvdata->qbt1000_class, drvdata->qbt1000_cdev.dev); + class_destroy(drvdata->qbt1000_class); + cdev_del(&drvdata->qbt1000_cdev); + unregister_chrdev_region(drvdata->qbt1000_cdev.dev, 1); + + device_init_wakeup(&pdev->dev, 0); + + return 0; +} + +static int qbt1000_suspend(struct platform_device *pdev, pm_message_t state) +{ + int rc = 0; + struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev); + + /* + * Returning an error code if driver currently making a TZ call. + * Note: The purpose of this driver is to ensure that the clocks are on + * while making a TZ call. Hence the clock check to determine if the + * driver will allow suspend to occur. + */ + if (!mutex_trylock(&drvdata->mutex)) + return -EBUSY; + + if (drvdata->clock_state) + rc = -EBUSY; + else { + enable_irq_wake(drvdata->fd_gpio.irq); + enable_irq_wake(drvdata->fw_ipc.irq); + } + + mutex_unlock(&drvdata->mutex); + + return rc; +} + +static int qbt1000_resume(struct platform_device *pdev) +{ + struct qbt1000_drvdata *drvdata = platform_get_drvdata(pdev); + + disable_irq_wake(drvdata->fd_gpio.irq); + disable_irq_wake(drvdata->fw_ipc.irq); + + return 0; +} + +static const struct of_device_id qbt1000_match[] = { + { .compatible = "qcom,qbt1000" }, + {} +}; + +static struct platform_driver qbt1000_plat_driver = { + .probe = qbt1000_probe, + .remove = qbt1000_remove, + .suspend = qbt1000_suspend, + .resume = qbt1000_resume, + .driver = { + .name = "qbt1000", + .owner = THIS_MODULE, + .of_match_table = qbt1000_match, + }, +}; + +module_platform_driver(qbt1000_plat_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. QBT1000 driver"); diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 08006d84fb38..add035269ae7 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -1651,9 +1651,10 @@ static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned event) /* * Below sequence is used when controller is working without - * having ssphy and only USB high speed is supported. + * having ssphy and only USB high/full speed is supported. */ - if (dwc->maximum_speed == USB_SPEED_HIGH) { + if (dwc->maximum_speed == USB_SPEED_HIGH || + dwc->maximum_speed == USB_SPEED_FULL) { dwc3_msm_write_reg(mdwc->base, QSCRATCH_GENERAL_CFG, dwc3_msm_read_reg(mdwc->base, QSCRATCH_GENERAL_CFG) @@ -2072,6 +2073,11 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc) clk_prepare_enable(mdwc->iface_clk); clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate); clk_prepare_enable(mdwc->core_clk); + + /* set Memory core: ON, Memory periphery: ON */ + clk_set_flags(mdwc->core_clk, CLKFLAG_RETAIN_MEM); + clk_set_flags(mdwc->core_clk, CLKFLAG_RETAIN_PERIPH); + clk_prepare_enable(mdwc->utmi_clk); if (mdwc->bus_aggr_clk) clk_prepare_enable(mdwc->bus_aggr_clk); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 805c5e1931e1..687d51e25d4b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -802,8 +802,8 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep) dwc = dep->dwc; if (!(dep->flags & DWC3_EP_ENABLED)) { - dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n", - dep->name); + dev_dbg(dwc->dev, "%s is already disabled\n", dep->name); + dbg_event(dep->number, "ALRDY DISABLED", dep->flags); return 0; } diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index c298c95d4ba0..738f20d935d6 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -497,7 +497,8 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port) log_event_dbg("%s: Calling xdci_suspend", __func__); ret = ipa_usb_xdci_suspend(gsi->d_port.out_channel_handle, - gsi->d_port.in_channel_handle, gsi->prot_id); + gsi->d_port.in_channel_handle, gsi->prot_id, + true); if (!ret) { d_port->sm_state = STATE_SUSPENDED; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 3e49861a09a2..5193bf5eb8c3 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -241,10 +241,14 @@ static int xhci_plat_probe(struct platform_device *pdev) if (ret) goto disable_usb_phy; + device_wakeup_enable(&hcd->self.root_hub->dev); + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED | IRQF_ONESHOT); if (ret) goto dealloc_usb2_hcd; + device_wakeup_enable(&xhci->shared_hcd->self.root_hub->dev); + ret = device_create_file(&pdev->dev, &dev_attr_config_imod); if (ret) dev_err(&pdev->dev, "%s: unable to create imod sysfs entry\n", diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 2bc70d1cf6fa..e193182af225 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -274,7 +274,7 @@ struct usbpd { struct extcon_dev *extcon; enum usbpd_state current_state; - bool hard_reset; + bool hard_reset_recvd; u8 rx_msg_type; u8 rx_msg_len; u32 rx_payload[7]; @@ -487,16 +487,12 @@ static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps) static void pd_send_hard_reset(struct usbpd *pd) { - int ret; - usbpd_dbg(&pd->dev, "send hard reset"); /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); pd->hard_reset_count++; - ret = pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */ - if (!ret) - pd->hard_reset = true; + pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */ pd->in_pr_swap = false; } @@ -522,7 +518,7 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); - pd->hard_reset = true; + pd->hard_reset_recvd = true; kick_sm(pd, 0); } @@ -753,40 +749,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); break; - case PE_SRC_TRANSITION_TO_DEFAULT: - pd->hard_reset = false; - - if (pd->vconn_enabled) - regulator_disable(pd->vconn); - regulator_disable(pd->vbus); - - if (pd->current_dr != DR_DFP) { - extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0); - pd->current_dr = DR_DFP; - pd_phy_update_roles(pd->current_dr, pd->current_pr); - } - - msleep(SRC_RECOVER_TIME); - - ret = regulator_enable(pd->vbus); - if (ret) - usbpd_err(&pd->dev, "Unable to enable vbus\n"); - - if (pd->vconn_enabled) { - ret = regulator_enable(pd->vconn); - if (ret) { - usbpd_err(&pd->dev, "Unable to enable vconn\n"); - pd->vconn_enabled = false; - } - } - - val.intval = 0; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); - - usbpd_set_state(pd, PE_SRC_STARTUP); - break; - case PE_SRC_HARD_RESET: case PE_SNK_HARD_RESET: /* hard reset may sleep; handle it in the workqueue */ @@ -1403,7 +1365,7 @@ static void usbpd_sm(struct work_struct *w) pd->in_pr_swap = false; pd->pd_connected = false; pd->in_explicit_contract = false; - pd->hard_reset = false; + pd->hard_reset_recvd = false; pd->caps_count = 0; pd->hard_reset_count = 0; pd->src_cap_id = 0; @@ -1456,7 +1418,9 @@ static void usbpd_sm(struct work_struct *w) } /* Hard reset? */ - if (pd->hard_reset) { + if (pd->hard_reset_recvd) { + pd->hard_reset_recvd = false; + val.intval = 1; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); @@ -1464,10 +1428,12 @@ static void usbpd_sm(struct work_struct *w) pd->in_pr_swap = false; reset_vdm_state(pd); - if (pd->current_pr == PR_SINK) + if (pd->current_pr == PR_SINK) { usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT); - else - usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT); + } else { + pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT; + kick_sm(pd, PS_HARD_RESET_TIME); + } goto sm_done; } @@ -1632,6 +1598,38 @@ static void usbpd_sm(struct work_struct *w) } break; + case PE_SRC_TRANSITION_TO_DEFAULT: + if (pd->vconn_enabled) + regulator_disable(pd->vconn); + regulator_disable(pd->vbus); + + if (pd->current_dr != DR_DFP) { + extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0); + pd->current_dr = DR_DFP; + pd_phy_update_roles(pd->current_dr, pd->current_pr); + } + + msleep(SRC_RECOVER_TIME); + + ret = regulator_enable(pd->vbus); + if (ret) + usbpd_err(&pd->dev, "Unable to enable vbus\n"); + + if (pd->vconn_enabled) { + ret = regulator_enable(pd->vconn); + if (ret) { + usbpd_err(&pd->dev, "Unable to enable vconn\n"); + pd->vconn_enabled = false; + } + } + + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + + usbpd_set_state(pd, PE_SRC_STARTUP); + break; + case PE_SRC_HARD_RESET: val.intval = 1; power_supply_set_property(pd->usb_psy, @@ -1641,9 +1639,8 @@ static void usbpd_sm(struct work_struct *w) pd->in_explicit_contract = false; reset_vdm_state(pd); - usleep_range(PS_HARD_RESET_TIME * USEC_PER_MSEC, - (PS_HARD_RESET_TIME + 5) * USEC_PER_MSEC); - usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT); + pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT; + kick_sm(pd, PS_HARD_RESET_TIME); break; case PE_SNK_STARTUP: @@ -1829,8 +1826,6 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_TRANSITION_TO_DEFAULT: - pd->hard_reset = false; - val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); @@ -2117,11 +2112,11 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) * During hard reset when VBUS goes to 0 the CC logic * will report this as a disconnection. In those cases * it can be ignored, however the downside is that - * pd->hard_reset can be momentarily true even when a - * non-PD capable source is attached, and can't be - * distinguished from a physical disconnect. In that - * case, allow for the common case of disconnecting - * from an SDP. + * we can also happen to be in the SNK_Transition_to_default + * state due to a hard reset attempt even with a non-PD + * capable source, in which a physical disconnect may get + * masked. In that case, allow for the common case of + * disconnecting from an SDP. * * The less common case is a PD-capable SDP which will * result in a hard reset getting treated like a diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c index 79980acc2201..8663797f1730 100644 --- a/drivers/video/fbdev/msm/mdss_debug.c +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -39,6 +39,9 @@ #define PANEL_CMD_MIN_TX_COUNT 2 #define PANEL_DATA_NODE_LEN 80 +/* Hex number + whitespace */ +#define NEXT_VALUE_OFFSET 3 + #define INVALID_XIN_ID 0xFF static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00}; @@ -81,7 +84,7 @@ static ssize_t panel_debug_base_offset_write(struct file *file, buf[count] = 0; /* end of string */ - if (sscanf(buf, "%x %d", &off, &cnt) != 2) + if (sscanf(buf, "%x %u", &off, &cnt) != 2) return -EFAULT; if (off > dbg->max_offset) @@ -129,7 +132,7 @@ static ssize_t panel_debug_base_reg_write(struct file *file, struct mdss_debug_base *dbg = file->private_data; char buf[PANEL_TX_MAX_BUF] = {0x0}; char reg[PANEL_TX_MAX_BUF] = {0x0}; - u32 len = 0, step = 0, value = 0; + u32 len = 0, value = 0; char *bufp; struct mdss_data_type *mdata = mdss_res; @@ -152,13 +155,21 @@ static ssize_t panel_debug_base_reg_write(struct file *file, buf[count] = 0; /* end of string */ bufp = buf; - while (sscanf(bufp, "%x%n", &value, &step) > 0) { + /* End of a hex value in given string */ + bufp[NEXT_VALUE_OFFSET - 1] = 0; + while (kstrtouint(bufp, 16, &value) == 0) { reg[len++] = value; if (len >= PANEL_TX_MAX_BUF) { pr_err("wrong input reg len\n"); return -EFAULT; } - bufp += step; + bufp += NEXT_VALUE_OFFSET; + if ((bufp >= (buf + count)) || (bufp < buf)) { + pr_warn("%s,buffer out-of-bounds\n", __func__); + break; + } + /* End of a hex value in given string */ + bufp[NEXT_VALUE_OFFSET - 1] = 0; } if (len < PANEL_CMD_MIN_TX_COUNT) { pr_err("wrong input reg len\n"); @@ -203,6 +214,7 @@ static ssize_t panel_debug_base_reg_read(struct file *file, struct mdss_panel_data *panel_data = ctl->panel_data; struct mdss_dsi_ctrl_pdata *ctrl_pdata = container_of(panel_data, struct mdss_dsi_ctrl_pdata, panel_data); + int rc = -EFAULT; if (!dbg) return -ENODEV; @@ -221,7 +233,8 @@ static ssize_t panel_debug_base_reg_read(struct file *file, if (!rx_buf || !panel_reg_buf) { pr_err("not enough memory to hold panel reg dump\n"); - return -ENOMEM; + rc = -ENOMEM; + goto read_reg_fail; } if (mdata->debug_inf.debug_enable_clock) @@ -260,8 +273,7 @@ static ssize_t panel_debug_base_reg_read(struct file *file, read_reg_fail: kfree(rx_buf); kfree(panel_reg_buf); - return -EFAULT; - + return rc; } static const struct file_operations panel_off_fops = { @@ -739,11 +751,11 @@ static ssize_t mdss_debug_factor_write(struct file *file, if (strnchr(buf, count, '/')) { /* Parsing buf as fraction */ - if (sscanf(buf, "%d/%d", &numer, &denom) != 2) + if (sscanf(buf, "%u/%u", &numer, &denom) != 2) return -EFAULT; } else { /* Parsing buf as percentage */ - if (sscanf(buf, "%d", &numer) != 1) + if (kstrtouint(buf, 0, &numer)) return -EFAULT; denom = 100; } @@ -1051,7 +1063,7 @@ static ssize_t mdss_debug_perf_bw_limit_write(struct file *file, if (strnchr(buf, count, ' ')) { /* Parsing buf */ - if (sscanf(buf, "%d %d", &mode, &val) != 2) + if (sscanf(buf, "%u %u", &mode, &val) != 2) return -EFAULT; } diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 516cbdc9192b..fa2e47e06503 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -898,8 +898,6 @@ static int dp_audio_info_setup(struct platform_device *pdev, } mdss_dp_audio_setup_sdps(&dp_ctrl->ctrl_io); - mdss_dp_audio_set_sample_rate(&dp_ctrl->ctrl_io, - dp_ctrl->link_rate, params->sample_rate_hz); mdss_dp_config_audio_acr_ctrl(&dp_ctrl->ctrl_io, dp_ctrl->link_rate); mdss_dp_set_safe_to_exit_level(&dp_ctrl->ctrl_io, dp_ctrl->lane_cnt); mdss_dp_audio_enable(&dp_ctrl->ctrl_io, true); @@ -1303,17 +1301,23 @@ int mdss_dp_on(struct mdss_panel_data *pdata) return mdss_dp_on_hpd(dp_drv); } -static void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp) +static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp) { dp->test_data = (const struct dpcd_test_request){ 0 }; } -static bool mdss_dp_is_link_training_requested(struct mdss_dp_drv_pdata *dp) +static inline bool mdss_dp_is_link_status_updated(struct mdss_dp_drv_pdata *dp) +{ + return dp->link_status.link_status_updated; +} + +static inline bool mdss_dp_is_link_training_requested( + struct mdss_dp_drv_pdata *dp) { return (dp->test_data.test_requested == TEST_LINK_TRAINING); } -static bool mdss_dp_soft_hpd_reset(struct mdss_dp_drv_pdata *dp) +static inline bool mdss_dp_soft_hpd_reset(struct mdss_dp_drv_pdata *dp) { return mdss_dp_is_link_training_requested(dp) && dp->alt_mode.dp_status.hpd_irq; @@ -2281,7 +2285,7 @@ end: * This function will send the test response to the sink but only after * any previous link training has been completed. */ -static void mdss_dp_send_test_response(struct mdss_dp_drv_pdata *dp) +static inline void mdss_dp_send_test_response(struct mdss_dp_drv_pdata *dp) { mutex_lock(&dp->train_mutex); mdss_dp_aux_send_test_response(dp); @@ -2318,11 +2322,76 @@ static int mdss_dp_hpd_irq_notify_clients(struct mdss_dp_drv_pdata *dp) } /** + * mdss_dp_link_retraining() - initiates link retraining + * @dp: Display Port Driver data + * + * This function will initiate link retraining by first notifying + * DP clients and triggering DP shutdown, and then enabling DP after + * notification is done successfully. + */ +static inline void mdss_dp_link_retraining(struct mdss_dp_drv_pdata *dp) +{ + if (mdss_dp_hpd_irq_notify_clients(dp)) + return; + + mdss_dp_on_irq(dp); +} + +/** + * mdss_dp_process_link_status_update() - processes link status updates + * @dp: Display Port Driver data + * + * This function will check for changes in the link status, e.g. clock + * recovery done on all lanes, and trigger link training if there is a + * failure/error on the link. + */ +static void mdss_dp_process_link_status_update(struct mdss_dp_drv_pdata *dp) +{ + if (!mdss_dp_is_link_status_updated(dp) || + (mdss_dp_aux_channel_eq_done(dp) && + mdss_dp_aux_clock_recovery_done(dp))) + return; + + pr_info("channel_eq_done = %d, clock_recovery_done = %d\n", + mdss_dp_aux_channel_eq_done(dp), + mdss_dp_aux_clock_recovery_done(dp)); + + mdss_dp_link_retraining(dp); +} + +/** + * mdss_dp_process_link_training_request() - processes new training requests + * @dp: Display Port Driver data + * + * This function will handle new link training requests that are initiated by + * the sink. In particular, it will update the requested lane count and link + * link rate, and then trigger the link retraining procedure. + */ +static void mdss_dp_process_link_training_request(struct mdss_dp_drv_pdata *dp) +{ + if (!mdss_dp_is_link_training_requested(dp)) + return; + + mdss_dp_send_test_response(dp); + + pr_info("%s link rate = 0x%x, lane count = 0x%x\n", + mdss_dp_get_test_name(TEST_LINK_TRAINING), + dp->test_data.test_link_rate, + dp->test_data.test_lane_count); + dp->dpcd.max_lane_count = + dp->test_data.test_lane_count; + dp->link_rate = dp->test_data.test_link_rate; + + mdss_dp_link_retraining(dp); +} + +/** * mdss_dp_process_hpd_irq_high() - handle HPD IRQ transition to HIGH * @dp: Display Port Driver data * - * This function will handle the HPD IRQ state transitions from HIGH to HIGH - * or LOW to HIGH, indicating the start of a new test request. + * This function will handle the HPD IRQ state transitions from LOW to HIGH + * (including cases when there are back to back HPD IRQ HIGH) indicating + * the start of a new link training request or sink status update. */ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) { @@ -2332,22 +2401,9 @@ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) mdss_dp_aux_parse_sink_status_field(dp); - if (mdss_dp_is_link_training_requested(dp)) { - mdss_dp_send_test_response(dp); - - pr_info("%s requested: link rate = 0x%x, lane count = 0x%x\n", - mdss_dp_get_test_name(TEST_LINK_TRAINING), - dp->test_data.test_link_rate, - dp->test_data.test_lane_count); - dp->dpcd.max_lane_count = - dp->test_data.test_lane_count; - dp->link_rate = dp->test_data.test_link_rate; + mdss_dp_process_link_training_request(dp); - if (mdss_dp_hpd_irq_notify_clients(dp)) - return; - - mdss_dp_on_irq(dp); - } + mdss_dp_process_link_status_update(dp); mdss_dp_reset_test_data(dp); diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index beeb4d4b1a91..4ba2d20d4261 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -585,5 +585,7 @@ int mdss_dp_aux_set_sink_power_state(struct mdss_dp_drv_pdata *ep, char state); void mdss_dp_aux_send_test_response(struct mdss_dp_drv_pdata *ep); void *mdss_dp_get_hdcp_data(struct device *dev); int mdss_dp_hdcp2p2_init(struct mdss_dp_drv_pdata *dp_drv); +bool mdss_dp_aux_clock_recovery_done(struct mdss_dp_drv_pdata *ep); +bool mdss_dp_aux_channel_eq_done(struct mdss_dp_drv_pdata *ep); #endif /* MDSS_DP_H */ diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 4d9a110cf6af..91066662e793 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -1238,7 +1238,7 @@ static int dp_train_pattern_set_write(struct mdss_dp_drv_pdata *ep, return dp_aux_write_buf(ep, 0x102, buf, 1, 0); } -static int dp_sink_clock_recovery_done(struct mdss_dp_drv_pdata *ep) +bool mdss_dp_aux_clock_recovery_done(struct mdss_dp_drv_pdata *ep) { u32 mask; u32 data; @@ -1259,12 +1259,12 @@ static int dp_sink_clock_recovery_done(struct mdss_dp_drv_pdata *ep) pr_debug("data=%x mask=%x\n", data, mask); data &= mask; if (data == mask) /* all done */ - return 1; + return true; - return 0; + return false; } -static int dp_sink_channel_eq_done(struct mdss_dp_drv_pdata *ep) +bool mdss_dp_aux_channel_eq_done(struct mdss_dp_drv_pdata *ep) { u32 mask; u32 data; @@ -1293,9 +1293,9 @@ static int dp_sink_channel_eq_done(struct mdss_dp_drv_pdata *ep) data &= mask; if (data == mask)/* all done */ - return 1; + return true; - return 0; + return false; } void dp_sink_train_set_adjust(struct mdss_dp_drv_pdata *ep) @@ -1446,7 +1446,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep) usleep_range(usleep_time, usleep_time); dp_link_status_read(ep, 6); - if (dp_sink_clock_recovery_done(ep)) { + if (mdss_dp_aux_clock_recovery_done(ep)) { ret = 0; break; } @@ -1499,7 +1499,7 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) dp_link_status_read(ep, 6); - if (dp_sink_channel_eq_done(ep)) { + if (mdss_dp_aux_channel_eq_done(ep)) { ret = 0; break; } diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c index b1eb8e0c9579..2d24d8499105 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.c +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -27,10 +27,6 @@ #define DP_LS_FREQ_162 162000000 #define DP_LS_FREQ_270 270000000 #define DP_LS_FREQ_540 540000000 -#define AUDIO_FREQ_32 32000 -#define AUDIO_FREQ_44_1 44100 -#define AUDIO_FREQ_48 48000 -#define DP_AUDIO_FREQ_COUNT 3 enum mdss_dp_pin_assignment { PIN_ASSIGNMENT_A, @@ -55,68 +51,12 @@ static const char *mdss_dp_pin_name(u8 pin) } } -static const uint32_t naud_value[DP_AUDIO_FREQ_COUNT][DP_AUDIO_FREQ_COUNT] = { - { 10125, 16875, 33750 }, - { 5625, 9375, 18750 }, - { 3375, 5625, 11250 } -}; - -static const uint32_t maud_rate[DP_AUDIO_FREQ_COUNT] = { 1024, 784, 512 }; - -static const uint32_t audio_timing_rbr[DP_AUDIO_FREQ_COUNT] = { - MMSS_DP_AUDIO_TIMING_RBR_32, - MMSS_DP_AUDIO_TIMING_RBR_44, - MMSS_DP_AUDIO_TIMING_RBR_48 -}; - -static const uint32_t std_audio_freq_list[DP_AUDIO_FREQ_COUNT] = { - AUDIO_FREQ_32, - AUDIO_FREQ_44_1, - AUDIO_FREQ_48 -}; - struct mdss_hw mdss_dp_hw = { .hw_ndx = MDSS_HW_EDP, .ptr = NULL, .irq_handler = dp_isr, }; -static int mdss_dp_get_rate_index(uint32_t rate) -{ - int index = 0; - - switch (rate) { - case DP_LS_FREQ_162: - case AUDIO_FREQ_32: - index = 0; - break; - case DP_LS_FREQ_270: - case AUDIO_FREQ_44_1: - index = 1; - break; - case DP_LS_FREQ_540: - case AUDIO_FREQ_48: - index = 2; - break; - default: - index = 0; - pr_err("unsupported rate\n"); - break; - } - - return index; -} - -static bool match_std_freq(uint32_t audio_freq, uint32_t std_freq) -{ - int quotient = audio_freq / std_freq; - - if (quotient & (quotient - 1)) - return false; - else - return true; -} - /* DP retrieve ctrl HW version */ u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io) { @@ -866,52 +806,6 @@ void mdss_dp_audio_setup_sdps(struct dss_io_data *ctrl_io) mdss_dp_audio_setup_isrc_sdp(ctrl_io); } -void mdss_dp_audio_set_sample_rate(struct dss_io_data *ctrl_io, - char dp_link_rate, uint32_t audio_freq) -{ - uint32_t link_rate; - uint32_t default_audio_freq = AUDIO_FREQ_32; - int i, multiplier = 1; - uint32_t maud_index, lrate_index, register_index, value; - - link_rate = (uint32_t)dp_link_rate * DP_LINK_RATE_MULTIPLIER; - - pr_debug("link_rate = %u, audio_freq = %u\n", link_rate, audio_freq); - - for (i = 0; i < DP_AUDIO_FREQ_COUNT; i++) { - if (audio_freq % std_audio_freq_list[i]) - continue; - - if (match_std_freq(audio_freq, std_audio_freq_list[i])) { - default_audio_freq = std_audio_freq_list[i]; - multiplier = audio_freq / default_audio_freq; - break; - } - } - - pr_debug("default_audio_freq = %u, multiplier = %d\n", - default_audio_freq, multiplier); - - lrate_index = mdss_dp_get_rate_index(link_rate); - maud_index = mdss_dp_get_rate_index(default_audio_freq); - - pr_debug("lrate_index = %u, maud_index = %u, maud = %u, naud = %u\n", - lrate_index, maud_index, - maud_rate[maud_index] * multiplier, - naud_value[maud_index][lrate_index]); - - register_index = mdss_dp_get_rate_index(default_audio_freq); - value = ((maud_rate[maud_index] * multiplier) << 16) | - naud_value[maud_index][lrate_index]; - - pr_debug("reg index = %d, offset = 0x%x, value = 0x%x\n", - (int)register_index, audio_timing_rbr[register_index], - value); - - writel_relaxed(value, ctrl_io->base + - audio_timing_rbr[register_index]); -} - void mdss_dp_set_safe_to_exit_level(struct dss_io_data *ctrl_io, uint32_t lane_cnt) { diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 66cd99720afa..cf7a398c13ce 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -995,6 +995,7 @@ static void mdss_dsi_debugfs_cleanup(struct mdss_dsi_ctrl_pdata *ctrl_pdata) struct mdss_dsi_debugfs_info *dfs = ctrl->debugfs_info; if (dfs && dfs->root) debugfs_remove_recursive(dfs->root); + kfree(dfs); pdata = pdata->next; } while (pdata); pr_debug("%s: Cleaned up mdss_dsi_debugfs_info\n", __func__); diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index fc8d3898351e..0316f4e86d39 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -615,8 +615,8 @@ static ssize_t mdss_fb_force_panel_dead(struct device *dev, return len; } - if (sscanf(buf, "%d", &pdata->panel_info.panel_force_dead) != 1) - pr_err("sccanf buf error!\n"); + if (kstrtouint(buf, 0, &pdata->panel_info.panel_force_dead)) + pr_err("kstrtouint buf error!\n"); return len; } @@ -729,8 +729,8 @@ static ssize_t mdss_fb_change_dfps_mode(struct device *dev, } pinfo = &pdata->panel_info; - if (sscanf(buf, "%d", &dfps_mode) != 1) { - pr_err("sccanf buf error!\n"); + if (kstrtouint(buf, 0, &dfps_mode)) { + pr_err("kstrtouint buf error!\n"); return len; } @@ -1280,6 +1280,7 @@ static int mdss_fb_remove(struct platform_device *pdev) return -EINVAL; mdss_fb_unregister_input_handler(mfd); + mdss_panel_debugfs_cleanup(mfd->panel_info); if (mdss_fb_suspend_sub(mfd)) pr_err("msm_fb_remove: can't stop the device %d\n", diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 2047a047b537..b90ac82049c6 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -831,14 +831,10 @@ static const u8 *hdmi_edid_find_block(const u8 *in_buf, u32 start_offset, u32 offset = start_offset; u32 dbc_offset = in_buf[2]; - if (dbc_offset >= EDID_BLOCK_SIZE - EDID_DTD_LEN) - return NULL; - *len = 0; - /* * * edid buffer 1, byte 2 being 4 means no non-DTD/Data block * collection present. - * * edid buffer 1, byte 2 being 0 menas no non-DTD/DATA block + * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block * collection present and no DTD data present. */ if ((dbc_offset == 0) || (dbc_offset == 4)) { @@ -858,8 +854,6 @@ static const u8 *hdmi_edid_find_block(const u8 *in_buf, u32 start_offset, } offset += 1 + block_len; } - DEV_WARN("%s: EDID: type=%d block not found in EDID block\n", - __func__, type); return NULL; } /* hdmi_edid_find_block */ @@ -1602,7 +1596,6 @@ static void hdmi_edid_detail_desc(struct hdmi_edid_ctrl *edid_ctrl, if (rc < 0) rc = hdmi_set_resv_timing_info(&timing); } else { - DEV_ERR("%s: Invalid frame data\n", __func__); rc = -EINVAL; } @@ -1611,7 +1604,6 @@ static void hdmi_edid_detail_desc(struct hdmi_edid_ctrl *edid_ctrl, DEV_DBG("%s: DTD mode found: %d\n", __func__, *disp_mode); } else { *disp_mode = HDMI_VFRMT_UNKNOWN; - DEV_ERR("%s: error adding mode from DTD: %d\n", __func__, rc); } } /* hdmi_edid_detail_desc */ @@ -1987,7 +1979,6 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl) u32 has60hz_mode = false; u32 has50hz_mode = false; u32 desc_offset = 0; - bool read_block0_res = false; struct hdmi_edid_sink_data *sink_data = NULL; if (!edid_ctrl) { @@ -2004,12 +1995,6 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl) hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET, VIDEO_DATA_BLOCK, &len) : NULL; - if (num_of_cea_blocks && (len == 0 || len > MAX_DATA_BLOCK_SIZE)) { - DEV_DBG("%s: fall back to block 0 res\n", __func__); - svd = NULL; - read_block0_res = true; - } - sink_data = &edid_ctrl->sink_data; sink_data->disp_multi_3d_mode_list_cnt = 0; @@ -2059,20 +2044,21 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl) edid_blk0+0x36+desc_offset, &video_format); - DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n", - __func__, __LINE__, - msm_hdmi_mode_2string(video_format)); + if (video_format != HDMI_VFRMT_UNKNOWN) { + DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n", + __func__, __LINE__, + msm_hdmi_mode_2string(video_format)); - hdmi_edid_add_sink_video_format(edid_ctrl, - video_format); + hdmi_edid_add_sink_video_format(edid_ctrl, + video_format); - if (video_format == HDMI_VFRMT_640x480p60_4_3) - has480p = true; + if (video_format == HDMI_VFRMT_640x480p60_4_3) + has480p = true; - /* Make a note of the preferred video format */ - if (i == 0) { - sink_data->preferred_video_format = - video_format; + /* Make a note of the preferred video format */ + if (i == 0) + sink_data->preferred_video_format = + video_format; } desc_offset += 0x12; ++i; @@ -2088,28 +2074,32 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl) * * EDID_BLOCK_SIZE = 0x80 Each page size in the EDID ROM */ desc_offset = edid_blk1[0x02]; - i = 0; - while (!edid_blk1[desc_offset]) { - hdmi_edid_detail_desc(edid_ctrl, - edid_blk1+desc_offset, - &video_format); - - DEV_DBG("[%s:%d] Block-1 Adding vid fmt = [%s]\n", - __func__, __LINE__, - msm_hdmi_mode_2string(video_format)); - - hdmi_edid_add_sink_video_format(edid_ctrl, - video_format); - if (video_format == HDMI_VFRMT_640x480p60_4_3) - has480p = true; - - /* Make a note of the preferred video format */ - if (i == 0) { - sink_data->preferred_video_format = - video_format; + if (desc_offset < (EDID_BLOCK_SIZE - EDID_DTD_LEN)) { + i = 0; + while (!edid_blk1[desc_offset]) { + hdmi_edid_detail_desc(edid_ctrl, + edid_blk1+desc_offset, + &video_format); + + if (video_format != HDMI_VFRMT_UNKNOWN) { + DEV_DBG("%s Block-1 Adding vid fmt = [%s]\n", + __func__, + msm_hdmi_mode_2string(video_format)); + + hdmi_edid_add_sink_video_format(edid_ctrl, + video_format); + if (video_format == HDMI_VFRMT_640x480p60_4_3) + has480p = true; + + /* Make a note of the preferred video format */ + if (i == 0) { + sink_data->preferred_video_format = + video_format; + } + } + desc_offset += 0x12; + ++i; } - desc_offset += 0x12; - ++i; } std_blk = 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index a0637109c7b3..04e8fa4ba576 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -2605,7 +2605,7 @@ static ssize_t mdss_mdp_store_max_limit_bw(struct device *dev, struct mdss_data_type *mdata = dev_get_drvdata(dev); u32 data = 0; - if (1 != sscanf(buf, "%d", &data)) { + if (kstrtouint(buf, 0, &data)) { pr_info("Not able scan to bw_mode_bitmap\n"); } else { mdata->bw_mode_bitmap = data; diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 8ac63aaaefce..a561fed80ce6 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -109,6 +109,9 @@ * QSEED3 parameters needs to be updated. * @DS_ENHANCER_UPDATE: Setting this bit indicates current Desitnation Scaler * QSEED3 Detial enhancer parameters need to be updated. + * @DS_VALIDATE: Indicate destination data structure parameters are validated + * and can be used for programming the HW and perform a flush. + * @DS_DIRTY_UPDATE: Mark for dirty update for Power resume usecase. */ #define DS_ENABLE BIT(0) #define DS_DUAL_MODE BIT(1) @@ -116,6 +119,8 @@ #define DS_RIGHT BIT(3) #define DS_SCALE_UPDATE BIT(4) #define DS_ENHANCER_UPDATE BIT(5) +#define DS_VALIDATE BIT(6) +#define DS_DIRTY_UPDATE BIT(7) /** * Destination Scaler DUAL mode overfetch pixel count @@ -370,6 +375,8 @@ struct mdss_mdp_destination_scaler { char __iomem *lut_base; u16 src_width; u16 src_height; + u16 last_mixer_width; + u16 last_mixer_height; u32 flags; struct mdp_scale_data_v2 scaler; }; @@ -404,7 +411,7 @@ struct mdss_mdp_ctl_intfs_ops { /* to update lineptr, [1..yres] - enable, 0 - disable */ int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable); - int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *); + int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *, bool enable); }; struct mdss_mdp_cwb { @@ -1706,6 +1713,7 @@ void mdss_mdp_pp_term(struct device *dev); int mdss_mdp_pp_overlay_init(struct msm_fb_data_type *mfd); int mdss_mdp_pp_resume(struct msm_fb_data_type *mfd); +void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl); int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl); int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index eb1e0b5c47a6..1d61653b76bb 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -3508,7 +3508,9 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) if (is_dest_scaling_enable(ctl->mixer_left)) { width = get_ds_input_width(ctl->mixer_left); height = get_ds_input_height(ctl->mixer_left); - if (ctl->panel_data->next && is_pingpong_split(ctl->mfd)) + if (is_dual_lm_single_display(ctl->mfd) || + (ctl->panel_data->next && + is_pingpong_split(ctl->mfd))) width *= 2; } else { width = get_panel_width(ctl); @@ -3548,9 +3550,13 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) } if (split_fb) { - width = ctl->mfd->split_fb_left; - width += (pinfo->lcdc.border_left + - pinfo->lcdc.border_right); + if (is_dest_scaling_enable(ctl->mixer_left)) { + width = get_ds_input_width(ctl->mixer_left); + } else { + width = ctl->mfd->split_fb_left; + width += (pinfo->lcdc.border_left + + pinfo->lcdc.border_right); + } } else if (width > max_mixer_width) { width /= 2; } @@ -3576,8 +3582,12 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) return 0; } - if (split_fb) - width = ctl->mfd->split_fb_right; + if (split_fb) { + if (is_dest_scaling_enable(ctl->mixer_left)) + width = get_ds_input_width(ctl->mixer_left); + else + width = ctl->mfd->split_fb_right; + } if (width < ctl->width) { if (ctl->mixer_right == NULL) { @@ -4038,6 +4048,7 @@ static void mdss_mdp_ctl_restore_sub(struct mdss_mdp_ctl *ctl) if (ctl->mfd && ctl->panel_data) { ctl->mfd->ipc_resume = true; mdss_mdp_pp_resume(ctl->mfd); + mdss_mdp_pp_dest_scaler_resume(ctl); if (is_dsc_compression(&ctl->panel_data->panel_info)) { /* @@ -5593,7 +5604,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); if (ctl->ops.avr_ctrl_fnc) { - ret = ctl->ops.avr_ctrl_fnc(ctl); + ret = ctl->ops.avr_ctrl_fnc(ctl, true); if (ret) { pr_err("error configuring avr ctrl registers ctl=%d err=%d\n", ctl->num, ret); @@ -5603,7 +5614,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, } if (sctl && sctl->ops.avr_ctrl_fnc) { - ret = sctl->ops.avr_ctrl_fnc(sctl); + ret = sctl->ops.avr_ctrl_fnc(sctl, true); if (ret) { pr_err("error configuring avr ctrl registers sctl=%d err=%d\n", sctl->num, ret); diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index d316ab6d263a..048e5fce30c6 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -108,6 +108,8 @@ static void mdss_mdp_fetch_end_config(struct mdss_mdp_video_ctx *ctx, static void early_wakeup_dfps_update_work(struct work_struct *work); +static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl, bool enable); + static inline void mdp_video_write(struct mdss_mdp_video_ctx *ctx, u32 reg, u32 val) { @@ -459,13 +461,15 @@ static int mdss_mdp_video_avr_trigger_setup(struct mdss_mdp_ctl *ctl) } static void mdss_mdp_video_avr_ctrl_setup(struct mdss_mdp_video_ctx *ctx, - struct mdss_mdp_avr_info *avr_info, bool is_master) + struct mdss_mdp_avr_info *avr_info, bool is_master, bool enable) { u32 avr_ctrl = 0; u32 avr_mode = 0; - avr_ctrl = avr_info->avr_enabled; - avr_mode = avr_info->avr_mode; + if (enable) { + avr_ctrl = avr_info->avr_enabled; + avr_mode = avr_info->avr_mode; + } /* Enable avr_vsync_clear_en bit to clear avr in next vsync */ if (avr_mode == MDSS_MDP_AVR_ONE_SHOT) @@ -1429,6 +1433,20 @@ static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps) } mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + + /* + * Need to disable AVR during DFPS update period. + * Next commit will restore the AVR settings. + */ + if (test_bit(MDSS_CAPS_AVR_SUPPORTED, + mdata->mdss_caps_map) && + ctl->avr_info.avr_enabled) { + mdss_mdp_video_avr_ctrl(ctl, false); + rc = mdss_mdp_video_dfps_wait4vsync(ctl); + if (rc < 0) + pr_err("Error in dfps_wait: %d\n", rc); + } + spin_lock_irqsave(&ctx->dfps_lock, flags); if (mdata->mdp_rev < MDSS_MDP_HW_REV_105) { @@ -2112,6 +2130,7 @@ static void early_wakeup_dfps_update_work(struct work_struct *work) struct mdss_panel_info *pinfo; struct msm_fb_data_type *mfd; struct mdss_mdp_ctl *ctl; + struct mdss_data_type *mdata; struct dynamic_fps_data data = {0}; int ret = 0; int dfps; @@ -2123,7 +2142,8 @@ static void early_wakeup_dfps_update_work(struct work_struct *work) ctl = ctx->ctl; - if (!ctl || !ctl->panel_data || !ctl->mfd || !ctl->mfd->fbi) { + if (!ctl || !ctl->panel_data || !ctl->mfd || !ctl->mfd->fbi || + !ctl->mdata) { pr_err("%s: invalid ctl\n", __func__); return; } @@ -2131,6 +2151,7 @@ static void early_wakeup_dfps_update_work(struct work_struct *work) pdata = ctl->panel_data; pinfo = &ctl->panel_data->panel_info; mfd = ctl->mfd; + mdata = ctl->mdata; if (!pinfo->dynamic_fps || !ctl->ops.config_fps_fnc || !pdata->panel_info.default_fps) { @@ -2138,6 +2159,17 @@ static void early_wakeup_dfps_update_work(struct work_struct *work) return; } + /* + * Bypass DFPS update when AVR is enabled because + * AVR will take control of the programmable fetch + */ + if (test_bit(MDSS_CAPS_AVR_SUPPORTED, + mdata->mdss_caps_map) && + ctl->avr_info.avr_enabled) { + pr_debug("Bypass DFPS update when AVR is enabled\n"); + return; + } + /* get the default fps that was cached before any dfps update */ dfps = pdata->panel_info.default_fps; @@ -2213,7 +2245,7 @@ static int mdss_mdp_video_early_wake_up(struct mdss_mdp_ctl *ctl) return 0; } -static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl) +static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl, bool enable) { struct mdss_mdp_video_ctx *ctx = NULL, *sctx = NULL; @@ -2222,7 +2254,8 @@ static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl) pr_err("invalid master ctx\n"); return -EINVAL; } - mdss_mdp_video_avr_ctrl_setup(ctx, &ctl->avr_info, ctl->is_master); + mdss_mdp_video_avr_ctrl_setup(ctx, &ctl->avr_info, ctl->is_master, + enable); if (is_pingpong_split(ctl->mfd)) { sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX]; @@ -2230,7 +2263,8 @@ static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl) pr_err("invalid slave ctx\n"); return -EINVAL; } - mdss_mdp_video_avr_ctrl_setup(sctx, &ctl->avr_info, false); + mdss_mdp_video_avr_ctrl_setup(sctx, &ctl->avr_info, false, + enable); } return 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index e26b3843d7b0..353d07ad64ac 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -67,13 +67,89 @@ static inline void *u64_to_ptr(uint64_t address) return (void *)(uintptr_t)address; } +static void mdss_mdp_disable_destination_scaler_setup(struct mdss_mdp_ctl *ctl) +{ + struct mdss_data_type *mdata = ctl->mdata; + struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info; + + if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map)) { + if (ctl->mixer_left && ctl->mixer_right && + ctl->mixer_left->ds && ctl->mixer_right->ds && + ctl->mixer_left->ds->scaler.enable && + ctl->mixer_right->ds->scaler.enable) { + /* + * DUAL mode disable + */ + ctl->mixer_left->width = get_panel_width(ctl); + ctl->mixer_left->height = get_panel_yres(pinfo); + ctl->mixer_left->width /= 2; + ctl->mixer_right->width = ctl->mixer_left->width; + ctl->mixer_right->height = ctl->mixer_left->height; + ctl->mixer_left->roi = (struct mdss_rect) { 0, 0, + ctl->mixer_left->width, + ctl->mixer_left->height }; + ctl->mixer_right->roi = (struct mdss_rect) { 0, 0, + ctl->mixer_right->width, + ctl->mixer_right->height }; + + /* + * Disable destination scaler by resetting the control + * flags and also need to disable in the QSEED3 + * settings. + */ + ctl->mixer_left->ds->flags = DS_SCALE_UPDATE | + DS_VALIDATE; + ctl->mixer_right->ds->flags = DS_SCALE_UPDATE | + DS_VALIDATE; + ctl->mixer_left->ds->scaler.enable = 0; + ctl->mixer_left->ds->scaler.detail_enhance.enable = 0; + ctl->mixer_right->ds->scaler.enable = 0; + ctl->mixer_right->ds->scaler.detail_enhance.enable = 0; + + pr_debug("DS-Left+Right disable: left:%dx%d, right:%dx%d\n", + ctl->mixer_left->width, + ctl->mixer_left->height, + ctl->mixer_right->width, + ctl->mixer_right->height); + MDSS_XLOG(ctl->mixer_left->width, + ctl->mixer_left->height, + ctl->mixer_right->width, + ctl->mixer_right->height); + } else if (ctl->mixer_left && ctl->mixer_left->ds && + ctl->mixer_left->ds->scaler.enable) { + /* + * Single DS disable + */ + ctl->mixer_left->width = get_panel_width(ctl); + ctl->mixer_left->height = get_panel_yres(pinfo); + ctl->mixer_left->roi = (struct mdss_rect) { 0, 0, + ctl->mixer_left->width, + ctl->mixer_left->height }; + + ctl->mixer_left->ds->flags = DS_SCALE_UPDATE | + DS_VALIDATE; + ctl->mixer_left->ds->scaler.enable = 0; + ctl->mixer_left->ds->scaler.detail_enhance.enable = 0; + + pr_debug("DS-left disable: wxh=%dx%d\n", + ctl->mixer_left->width, + ctl->mixer_left->height); + MDSS_XLOG(ctl->mixer_left->width, + ctl->mixer_left->height); + } + } +} + static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data, struct mdss_mdp_destination_scaler *ds, u32 max_input_width, u32 max_output_width) { struct mdp_scale_data_v2 *scale; - ds->flags = (ds_data->flags & MDP_DESTSCALER_ENABLE) ? DS_ENABLE : 0; + if (ds_data->flags & MDP_DESTSCALER_ENABLE) + ds->flags |= DS_ENABLE; + else + ds->flags &= ~DS_ENABLE; if (ds_data->flags & (MDP_DESTSCALER_SCALE_UPDATE | MDP_DESTSCALER_ENHANCER_UPDATE)) { @@ -101,8 +177,12 @@ static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data, ds->flags |= DS_SCALE_UPDATE; if (ds_data->flags & MDP_DESTSCALER_ENHANCER_UPDATE) ds->flags |= DS_ENHANCER_UPDATE; - ds->src_width = scale->src_width[0]; - ds->src_height = scale->src_height[0]; + + /* + * Update with LM resolution + */ + ds->src_width = ds_data->lm_width; + ds->src_height = ds_data->lm_height; } if (ds_data->flags == 0) { @@ -110,6 +190,11 @@ static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data, ds_data->dest_scaler_ndx); } + /* + * Confirm all check pass validation, and to be cleared in CTL after + * flush is issued. + */ + ds->flags |= DS_VALIDATE; return 0; } @@ -118,7 +203,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, { struct mdss_data_type *mdata; struct mdss_panel_info *pinfo; - + u16 mxleft_w = 0, mxleft_h = 0, mxright_w = 0, mxright_h = 0; mdata = ctl->mdata; /* @@ -134,7 +219,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, * height. */ pinfo = &ctl->panel_data->panel_info; - if ((ds_data->lm_width > get_panel_xres(pinfo)) || + if ((ds_data->lm_width > get_panel_width(ctl)) || (ds_data->lm_height > get_panel_yres(pinfo)) || (ds_data->lm_width == 0) || (ds_data->lm_height == 0)) { @@ -142,14 +227,8 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, ds_data->lm_width, ds_data->lm_height); return -EINVAL; } - - ctl->width = ds_data->lm_width; - ctl->height = ds_data->lm_height; - - ctl->mixer_left->width = ds_data->lm_width; - ctl->mixer_left->height = ds_data->lm_height; - pr_debug("Update mixer-left width/height: %dx%d\n", - ds_data->lm_width, ds_data->lm_width); + mxleft_w = ds_data->lm_width; + mxleft_h = ds_data->lm_height; } if (ctl->mixer_right && ctl->mixer_right->ds) { @@ -174,25 +253,51 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, * Split display both left and right should have the * same width and height */ - ctl->mixer_right->width = ds_data->lm_width; - ctl->mixer_right->height = ds_data->lm_height; - pr_debug("Update mixer-right width/height: %dx%d\n", - ds_data->lm_width, ds_data->lm_height); + mxright_w = ds_data->lm_width; + mxright_h = ds_data->lm_height; if (ctl->mixer_left && - ((ctl->mixer_right->width != - ctl->mixer_left->width) || - (ctl->mixer_right->height != - ctl->mixer_left->height))) { + ((mxright_w != mxleft_w) || + (mxright_h != mxleft_h))) { pr_err("Mismatch width/heigth in LM for split display\n"); return -EINVAL; } + } + + /* + * Update mixer and control dimension after successful + * pre-validation + */ + if (mxleft_w && mxleft_h) { + ctl->mixer_left->ds->last_mixer_width = + ctl->mixer_left->width; + ctl->mixer_left->ds->last_mixer_height = + ctl->mixer_left->height; + + ctl->width = mxleft_w; + ctl->height = mxleft_h; + ctl->mixer_left->width = mxleft_w; + ctl->mixer_left->height = mxleft_h; + pr_debug("Update mixer-left width/height: %dx%d\n", + mxleft_w, mxleft_h); + } + + if (mxright_w && mxright_h) { + ctl->mixer_right->ds->last_mixer_width = + ctl->mixer_right->width; + ctl->mixer_right->ds->last_mixer_height = + ctl->mixer_right->height; + + ctl->mixer_right->width = mxright_w; + ctl->mixer_right->height = mxright_h; + pr_debug("Update mixer-right width/height: %dx%d\n", + mxright_w, mxright_h); /* * For split display, CTL width should be equal to * whole panel size */ - ctl->width += ds_data->lm_width; + ctl->width += mxright_w; } pr_debug("Updated CTL width:%d, height:%d\n", @@ -236,19 +341,23 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd, MDSS_MDP_DS_OVERFETCH_SIZE, mdata->max_dest_scaler_output_width); if (ret) - return ret; + goto reset_mixer; ret = __dest_scaler_data_setup(&ds_data[1], ds_right, mdata->max_dest_scaler_input_width - MDSS_MDP_DS_OVERFETCH_SIZE, mdata->max_dest_scaler_output_width); if (ret) - return ret; + goto reset_mixer; ds_left->flags &= ~(DS_LEFT|DS_RIGHT); ds_left->flags |= DS_DUAL_MODE; ds_right->flags &= ~(DS_LEFT|DS_RIGHT); ds_right->flags |= DS_DUAL_MODE; + MDSS_XLOG(ds_left->num, ds_left->src_width, + ds_left->src_height, ds_left->flags, + ds_right->num, ds_right->src_width, + ds_right->src_height, ds_right->flags); break; case DS_LEFT: @@ -262,6 +371,11 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd, ret = __dest_scaler_data_setup(&ds_data[0], ds_left, mdata->max_dest_scaler_input_width, mdata->max_dest_scaler_output_width); + if (ret) + goto reset_mixer; + + MDSS_XLOG(ds_left->num, ds_left->src_width, + ds_left->src_height, ds_left->flags); break; case DS_RIGHT: @@ -276,6 +390,11 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd, ret = __dest_scaler_data_setup(&ds_data[0], ds_right, mdata->max_dest_scaler_input_width, mdata->max_dest_scaler_output_width); + if (ret) + goto reset_mixer; + + MDSS_XLOG(ds_right->num, ds_right->src_width, + ds_right->src_height, ds_right->flags); break; } @@ -312,6 +431,40 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd, pr_err("Invalid Dest-scaler output width/height: %d/%d\n", scaler_width, scaler_height); ret = -EINVAL; + goto reset_mixer; + } + + return ret; + +reset_mixer: + /* reverting mixer and control dimension */ + if (ctl->mixer_left && ctl->mixer_left->ds && + ctl->mixer_left->ds->last_mixer_width) { + ctl->width = ctl->mixer_left->ds->last_mixer_width; + ctl->height = ctl->mixer_left->ds->last_mixer_height; + ctl->mixer_left->width = + ctl->mixer_left->ds->last_mixer_width; + ctl->mixer_left->height = + ctl->mixer_left->ds->last_mixer_height; + if (ds_left) + ds_left->flags &= ~DS_ENABLE; + MDSS_XLOG(ctl->width, ctl->height, + ctl->mixer_left->width, + ctl->mixer_left->height); + } + + if (ctl->mixer_right && ctl->mixer_right->ds && + ctl->mixer_right->ds->last_mixer_width) { + ctl->width += ctl->mixer_right->ds->last_mixer_width; + ctl->mixer_right->width = + ctl->mixer_right->ds->last_mixer_width; + ctl->mixer_right->height = + ctl->mixer_right->ds->last_mixer_height; + if (ds_right) + ds_right->flags &= ~DS_ENABLE; + MDSS_XLOG(ctl->width, ctl->height, + ctl->mixer_right->width, + ctl->mixer_right->height); } return ret; @@ -1939,6 +2092,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd, enum layer_pipe_q pipe_q_type; enum layer_zorder_used zorder_used[MDSS_MDP_MAX_STAGE] = {0}; enum mdss_mdp_pipe_rect rect_num; + struct mdp_destination_scaler_data *ds_data; ret = mutex_lock_interruptible(&mdp5_data->ov_lock); if (ret) @@ -2194,11 +2348,10 @@ static int __validate_layers(struct msm_fb_data_type *mfd, layer->z_order -= MDSS_MDP_STAGE_0; } + ds_data = commit->dest_scaler; if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) && - commit->dest_scaler && + ds_data && (ds_data->flags & MDP_DESTSCALER_ENABLE) && commit->dest_scaler_cnt) { - struct mdp_destination_scaler_data *ds_data = - commit->dest_scaler; /* * Find out which DS block to use based on DS commit info @@ -2217,8 +2370,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd, } ret = mdss_mdp_validate_destination_scaler(mfd, - commit->dest_scaler, - ds_mode); + ds_data, ds_mode); if (ret) { pr_err("fail to validate destination scaler\n"); layer->error_code = ret; @@ -2472,6 +2624,7 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd, struct file *file, struct mdp_layer_commit_v1 *commit) { struct mdss_overlay_private *mdp5_data; + struct mdp_destination_scaler_data *ds_data; int rc = 0; if (!mfd || !commit) { @@ -2505,15 +2658,17 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd, } } - if (commit->dest_scaler && commit->dest_scaler_cnt) { + ds_data = commit->dest_scaler; + if (ds_data && commit->dest_scaler_cnt && + (ds_data->flags & MDP_DESTSCALER_ENABLE)) { rc = mdss_mdp_destination_scaler_pre_validate(mdp5_data->ctl, - commit->dest_scaler, - commit->dest_scaler_cnt); + ds_data, commit->dest_scaler_cnt); if (IS_ERR_VALUE(rc)) { pr_err("Destination scaler pre-validate failed\n"); return -EINVAL; } - } + } else + mdss_mdp_disable_destination_scaler_setup(mdp5_data->ctl); rc = mdss_mdp_avr_validate(mfd, commit); if (IS_ERR_VALUE(rc)) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 965d4a6cfb5e..9bdc66232dd5 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -2958,7 +2958,7 @@ static ssize_t dynamic_fps_sysfs_wta_dfps(struct device *dev, if (pdata->panel_info.dfps_update == DFPS_IMMEDIATE_MULTI_UPDATE_MODE_CLK_HFP) { - if (sscanf(buf, "%d %d %d %d %d", + if (sscanf(buf, "%u %u %u %u %u", &data.hfp, &data.hbp, &data.hpw, &data.clk_rate, &data.fps) != 5) { pr_err("could not read input\n"); diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index ee1cd8fd623e..47edc320233a 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -2480,6 +2480,28 @@ static int pp_dest_scaler_setup(struct mdss_mdp_mixer *mixer) if (!test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) || !ds) return 0; + /* + * Non-validated DS data will be related to PM event. It is required + * to send out last setup to match the mixer and panel configuration. + */ + if (!(ds->flags & DS_VALIDATE)) { + pr_debug("Apply old DS[%d] for non validate data\n", ds->num); + if (ds->flags & DS_ENABLE) + ds->flags |= (DS_SCALE_UPDATE | DS_ENHANCER_UPDATE); + ds->flags |= DS_VALIDATE; + } + + /* + * If mark for dirty update, force update to scaler and detail + * enhancer. + */ + if (ds->flags & DS_DIRTY_UPDATE) { + pr_debug("Scale dirty update requested\n"); + ds->flags |= (DS_SCALE_UPDATE | DS_ENHANCER_UPDATE | + DS_VALIDATE); + ds->flags &= ~DS_DIRTY_UPDATE; + } + ds_offset = ds->ds_base; op_mode = readl_relaxed(MDSS_MDP_REG_DEST_SCALER_OP_MODE + ds_offset); @@ -2519,12 +2541,37 @@ static int pp_dest_scaler_setup(struct mdss_mdp_mixer *mixer) } /* Destinations scaler shared the flush with DSPP in control */ - if (ds->flags & DS_ENABLE) + if (ds->flags & (DS_ENABLE | DS_VALIDATE)) { + pr_debug("FLUSH[%d]: flags:%X, op_mode:%x\n", + ds->num, ds->flags, op_mode); ctl->flush_bits |= BIT(13 + ds->num); + } + ds->flags &= ~DS_VALIDATE; return 0; } +void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl) +{ + if (!ctl || !ctl->mdata) { + pr_err("Invalid ctl\n"); + return; + } + + if (!test_bit(MDSS_CAPS_DEST_SCALER, ctl->mdata->mdss_caps_map)) + return; + + if (ctl->mixer_left && ctl->mixer_left->ds) { + ctl->mixer_left->ds->flags |= DS_DIRTY_UPDATE; + pr_debug("DS left mark dirty\n"); + } + + if (ctl->mixer_right && ctl->mixer_right->ds) { + ctl->mixer_right->ds->flags |= DS_DIRTY_UPDATE; + pr_debug("DS right mark dirty\n"); + } +} + int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl) { int ret = 0; diff --git a/drivers/video/fbdev/msm/mdss_panel.c b/drivers/video/fbdev/msm/mdss_panel.c index 16c2d4e6e92d..31cf74274131 100644 --- a/drivers/video/fbdev/msm/mdss_panel.c +++ b/drivers/video/fbdev/msm/mdss_panel.c @@ -455,10 +455,12 @@ int mdss_panel_debugfs_setup(struct mdss_panel_info *panel_info, struct dentry return -ENOMEM; } + debugfs_info->parent = parent; debugfs_info->root = debugfs_create_dir(intf_str, parent); if (IS_ERR_OR_NULL(debugfs_info->root)) { pr_err("Debugfs create dir failed with error: %ld\n", PTR_ERR(debugfs_info->root)); + kfree(debugfs_info); return -ENODEV; } @@ -503,6 +505,7 @@ int mdss_panel_debugfs_init(struct mdss_panel_info *panel_info, intf_str); if (rc) { pr_err("error in initilizing panel debugfs\n"); + mdss_panel_debugfs_cleanup(&pdata->panel_info); return rc; } pdata = pdata->next; @@ -516,13 +519,16 @@ void mdss_panel_debugfs_cleanup(struct mdss_panel_info *panel_info) { struct mdss_panel_data *pdata; struct mdss_panel_debugfs_info *debugfs_info; + struct dentry *parent = NULL; pdata = container_of(panel_info, struct mdss_panel_data, panel_info); do { debugfs_info = pdata->panel_info.debugfs_info; - if (debugfs_info && debugfs_info->root) - debugfs_remove_recursive(debugfs_info->root); + if (debugfs_info && !parent) + parent = debugfs_info->parent; + kfree(debugfs_info); pdata = pdata->next; } while (pdata); + debugfs_remove_recursive(parent); pr_debug("Cleaned up mdss_panel_debugfs_info\n"); } diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index be0491195263..18a93f9d3c3e 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -879,6 +879,7 @@ struct mdss_panel_data { struct mdss_panel_debugfs_info { struct dentry *root; + struct dentry *parent; struct mdss_panel_info panel_info; u32 override_flag; struct mdss_panel_debugfs_info *next; diff --git a/include/dt-bindings/clock/msm-clocks-cobalt.h b/include/dt-bindings/clock/msm-clocks-cobalt.h index 251b7e314238..4bacef303967 100644 --- a/include/dt-bindings/clock/msm-clocks-cobalt.h +++ b/include/dt-bindings/clock/msm-clocks-cobalt.h @@ -198,9 +198,7 @@ #define clk_gcc_gp1_clk 0x057f7b69 #define clk_gcc_gp2_clk 0x9bf83ffd #define clk_gcc_gp3_clk 0xec6539ee -#define clk_gcc_gpu_snoc_dvm_gfx_clk 0xc9147451 #define clk_gcc_gpu_bimc_gfx_clk 0x3909459b -#define clk_gcc_gpu_bimc_gfx_src_clk 0x377cb748 #define clk_gcc_gpu_cfg_ahb_clk 0x72f20a57 #define clk_gcc_gpu_iref_clk 0xfd82abad #define clk_gcc_hmss_ahb_clk 0x62818713 diff --git a/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h b/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h index 81d75bc9a8d6..f10afffc74b2 100644 --- a/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h +++ b/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h @@ -167,9 +167,7 @@ #define GCC_GP2_CBCR 0x65000 #define GCC_GP3_CBCR 0x66000 #define GCC_GPU_BIMC_GFX_CBCR 0x71010 -#define GCC_GPU_BIMC_GFX_SRC_CBCR 0x7100C #define GCC_GPU_CFG_AHB_CBCR 0x71004 -#define GCC_GPU_SNOC_DVM_GFX_CBCR 0x71018 #define GCC_GPU_IREF_EN 0x88010 #define GCC_HMSS_AHB_CBCR 0x48000 #define GCC_HMSS_DVM_BUS_CBCR 0x4808C diff --git a/include/linux/bluetooth-power.h b/include/linux/bluetooth-power.h index 7be94d298b88..a822ba8c07d1 100644 --- a/include/linux/bluetooth-power.h +++ b/include/linux/bluetooth-power.h @@ -85,4 +85,5 @@ struct bluetooth_power_platform_data { int bt_register_slimdev(struct device *dev); #define BT_CMD_SLIM_TEST 0xbfac +#define BT_CMD_PWR_CTRL 0xbfad #endif /* __LINUX_BLUETOOTH_POWER_H */ diff --git a/include/linux/clk/msm-clk.h b/include/linux/clk/msm-clk.h index 22587e8852e2..964909d25021 100644 --- a/include/linux/clk/msm-clk.h +++ b/include/linux/clk/msm-clk.h @@ -14,6 +14,16 @@ #include <linux/notifier.h> +#if defined(CONFIG_COMMON_CLK_QCOM) +enum branch_mem_flags { + CLKFLAG_RETAIN_PERIPH, + CLKFLAG_NORETAIN_PERIPH, + CLKFLAG_RETAIN_MEM, + CLKFLAG_NORETAIN_MEM, + CLKFLAG_PERIPH_OFF_SET, + CLKFLAG_PERIPH_OFF_CLEAR, +}; +#elif defined(CONFIG_COMMON_CLK_MSM) #define CLKFLAG_INVERT 0x00000001 #define CLKFLAG_NOINVERT 0x00000002 #define CLKFLAG_NONEST 0x00000004 @@ -32,6 +42,7 @@ #define CLKFLAG_EPROBE_DEFER 0x00010000 #define CLKFLAG_PERIPH_OFF_SET 0x00020000 #define CLKFLAG_PERIPH_OFF_CLEAR 0x00040000 +#endif struct clk_lookup; struct clk; diff --git a/include/linux/ipa_usb.h b/include/linux/ipa_usb.h index 0fe0e36c551f..de1163348c05 100644 --- a/include/linux/ipa_usb.h +++ b/include/linux/ipa_usb.h @@ -253,6 +253,7 @@ int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot); * @dl_clnt_hdl: client handle previously obtained from * ipa_usb_xdci_connect() for IN channel * @teth_prot: tethering protocol + * @with_remote_wakeup: Does host support remote wakeup? * * Note: Should not be called from atomic context * Note: for DPL, the ul will be ignored as irrelevant @@ -260,7 +261,8 @@ int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot); * @Return 0 on success, negative on failure */ int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, - enum ipa_usb_teth_prot teth_prot); + enum ipa_usb_teth_prot teth_prot, + bool with_remote_wakeup); /** * ipa_usb_xdci_resume - Peripheral should call this function to resume @@ -313,7 +315,8 @@ static inline int ipa_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot) } static inline int ipa_usb_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, - enum ipa_usb_teth_prot teth_prot) + enum ipa_usb_teth_prot teth_prot, + bool with_remote_wakeup) { return -EPERM; } diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h index 7c12823894df..652d68ac63bf 100644 --- a/include/linux/qpnp/qpnp-revid.h +++ b/include/linux/qpnp/qpnp-revid.h @@ -177,6 +177,10 @@ /* PMICOBALT */ #define PMICOBALT_SUBTYPE 0x15 +/* PMFALCON */ +#define PM2FALCON_SUBTYPE 0x1A +#define PMFALCON_SUBTYPE 0x1B + #define PMICOBALT_V1P0_REV1 0x00 #define PMICOBALT_V1P0_REV2 0x00 #define PMICOBALT_V1P0_REV3 0x00 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 801315d1d405..9076fd9f92b2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -695,6 +695,18 @@ struct cfg80211_acl_data { struct mac_address mac_addrs[]; }; +/* + * cfg80211_bitrate_mask - masks for bitrate control + */ +struct cfg80211_bitrate_mask { + struct { + u32 legacy; + u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN]; + u16 vht_mcs[NL80211_VHT_NSS_MAX]; + enum nl80211_txrate_gi gi; + } control[IEEE80211_NUM_BANDS]; +}; + /** * struct cfg80211_ap_settings - AP configuration * @@ -719,6 +731,7 @@ struct cfg80211_acl_data { * MAC address based access control * @pbss: If set, start as a PCP instead of AP. Relevant for DMG * networks. + * @beacon_rate: masks for setting user configured beacon tx rate. */ struct cfg80211_ap_settings { struct cfg80211_chan_def chandef; @@ -738,6 +751,7 @@ struct cfg80211_ap_settings { bool p2p_opp_ps; const struct cfg80211_acl_data *acl; bool pbss; + struct cfg80211_bitrate_mask beacon_rate; }; /** @@ -1945,17 +1959,6 @@ enum wiphy_params_flags { WIPHY_PARAM_DYN_ACK = 1 << 5, }; -/* - * cfg80211_bitrate_mask - masks for bitrate control - */ -struct cfg80211_bitrate_mask { - struct { - u32 legacy; - u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN]; - u16 vht_mcs[NL80211_VHT_NSS_MAX]; - enum nl80211_txrate_gi gi; - } control[IEEE80211_NUM_BANDS]; -}; /** * struct cfg80211_pmksa - PMK Security Association * diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 0bac6947a1cb..d6ff882ad6a7 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -381,6 +381,7 @@ header-y += prctl.h header-y += psci.h header-y += ptp_clock.h header-y += ptrace.h +header-y += qbt1000.h header-y += qcedev.h header-y += qcota.h header-y += qnx4_fs.h diff --git a/include/uapi/linux/hbtp_input.h b/include/uapi/linux/hbtp_input.h index 67692ed8e3b8..9173c2ab72ed 100644 --- a/include/uapi/linux/hbtp_input.h +++ b/include/uapi/linux/hbtp_input.h @@ -43,6 +43,17 @@ struct hbtp_input_key { __s32 value; }; +enum hbtp_afe_signal { + HBTP_AFE_SIGNAL_ON_RESUME, + HBTP_AFE_SIGNAL_ON_SUSPEND, +}; + +enum hbtp_afe_power_ctrl { + HBTP_AFE_POWER_ENABLE_SYNC, + HBTP_AFE_POWER_ENABLE_SYNC_SIGNAL, +}; + + /* ioctl */ #define HBTP_INPUT_IOCTL_BASE 'T' #define HBTP_SET_ABSPARAM _IOW(HBTP_INPUT_IOCTL_BASE, 201, \ @@ -53,6 +64,10 @@ struct hbtp_input_key { enum hbtp_afe_power_cmd) #define HBTP_SET_KEYDATA _IOW(HBTP_INPUT_IOCTL_BASE, 204, \ struct hbtp_input_key) +#define HBTP_SET_SYNCSIGNAL _IOW(HBTP_INPUT_IOCTL_BASE, 205, \ + enum hbtp_afe_signal) +#define HBTP_SET_POWER_CTRL _IOW(HBTP_INPUT_IOCTL_BASE, 206, \ + enum hbtp_afe_power_ctrl) #endif /* _UAPI_HBTP_INPUT_H */ diff --git a/include/uapi/linux/qbt1000.h b/include/uapi/linux/qbt1000.h new file mode 100644 index 000000000000..8a3be2c634d3 --- /dev/null +++ b/include/uapi/linux/qbt1000.h @@ -0,0 +1,99 @@ +#ifndef _UAPI_QBT1000_H_ +#define _UAPI_QBT1000_H_ + +#define MAX_NAME_SIZE 32 + +/* +* enum qbt1000_commands - +* enumeration of command options +* @QBT1000_LOAD_APP - cmd loads TZ app +* @QBT1000_UNLOAD_APP - cmd unloads TZ app +* @QBT1000_SEND_TZCMD - sends cmd to TZ app +* @QBT1000_SET_FINGER_DETECT_KEY - sets the input key to send on finger detect +* @QBT1000_CONFIGURE_POWER_KEY - enables/disables sending the power key on + finger down events +*/ +enum qbt1000_commands { + QBT1000_LOAD_APP = 100, + QBT1000_UNLOAD_APP = 101, + QBT1000_SEND_TZCMD = 102, + QBT1000_SET_FINGER_DETECT_KEY = 103, + QBT1000_CONFIGURE_POWER_KEY = 104 +}; + +/* +* enum qbt1000_fw_event - +* enumeration of firmware events +* @FW_EVENT_FINGER_DOWN - finger down detected +* @FW_EVENT_FINGER_UP - finger up detected +* @FW_EVENT_INDICATION - an indication IPC from the firmware is pending +*/ +enum qbt1000_fw_event { + FW_EVENT_FINGER_DOWN = 1, + FW_EVENT_FINGER_UP = 2, + FW_EVENT_CBGE_REQUIRED = 3, +}; + +/* +* struct qbt1000_app - +* used to load and unload apps in TZ +* @app_handle - qseecom handle for clients +* @name - Name of secure app to load +* @size - Size of requested buffer of secure app +* @high_band_width - 1 - for high bandwidth usage +* 0 - for normal bandwidth usage +*/ +struct qbt1000_app { + struct qseecom_handle **app_handle; + char name[MAX_NAME_SIZE]; + uint32_t size; + uint8_t high_band_width; +}; + +/* +* struct qbt1000_send_tz_cmd - +* used to cmds to TZ App +* @app_handle - qseecom handle for clients +* @req_buf - Buffer containing request for secure app +* @req_buf_len - Length of request buffer +* @rsp_buf - Buffer containing response from secure app +* @rsp_buf_len - Length of response buffer +*/ +struct qbt1000_send_tz_cmd { + struct qseecom_handle *app_handle; + uint8_t *req_buf; + uint32_t req_buf_len; + uint8_t *rsp_buf; + uint32_t rsp_buf_len; +}; + +/* +* struct qbt1000_erie_event - +* used to receive events from Erie +* @buf - Buffer containing event from Erie +* @buf_len - Length of buffer +*/ +struct qbt1000_erie_event { + uint8_t *buf; + uint32_t buf_len; +}; + +/* +* struct qbt1000_set_finger_detect_key - +* used to configure the input key which is sent on finger down/up event +* @key_code - Key code to send on finger down/up. 0 disables sending key events +*/ +struct qbt1000_set_finger_detect_key { + unsigned int key_code; +}; + +/* +* struct qbt1000_configure_power_key - +* used to configure whether the power key is sent on finger down +* @enable - if non-zero, power key is sent on finger down +*/ +struct qbt1000_configure_power_key { + unsigned int enable; +}; + +#endif /* _UAPI_QBT1000_H_ */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index e32d4d7903b0..e0f212743c77 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3525,7 +3525,7 @@ static void dec_throttled_cfs_rq_hmp_stats(struct hmp_sched_stats *stats, BUG_ON(stats->nr_big_tasks < 0 || (s64)stats->cumulative_runnable_avg < 0); - verify_pred_demands_sum(stats); + BUG_ON((s64)stats->pred_demands_sum < 0); } #else /* CONFIG_CFS_BANDWIDTH */ diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c index d220482f4dbc..1d55e226196f 100644 --- a/kernel/sched/hmp.c +++ b/kernel/sched/hmp.c @@ -1387,7 +1387,7 @@ void dec_rq_hmp_stats(struct rq *rq, struct task_struct *p, int change_cra) dec_cumulative_runnable_avg(&rq->hmp_stats, p); } -static void reset_hmp_stats(struct hmp_sched_stats *stats, int reset_cra) +void reset_hmp_stats(struct hmp_sched_stats *stats, int reset_cra) { stats->nr_big_tasks = 0; if (reset_cra) { @@ -3106,9 +3106,9 @@ static void reset_all_task_stats(void) read_lock(&tasklist_lock); do_each_thread(g, p) { - raw_spin_lock(&p->pi_lock); + raw_spin_lock_irq(&p->pi_lock); reset_task_stats(p); - raw_spin_unlock(&p->pi_lock); + raw_spin_unlock_irq(&p->pi_lock); } while_each_thread(g, p); read_unlock(&tasklist_lock); } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 471dc9faab35..4289bf6cd642 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1407,6 +1407,7 @@ extern void inc_rq_hmp_stats(struct rq *rq, struct task_struct *p, int change_cra); extern void dec_rq_hmp_stats(struct rq *rq, struct task_struct *p, int change_cra); +extern void reset_hmp_stats(struct hmp_sched_stats *stats, int reset_cra); extern int is_big_task(struct task_struct *p); extern int upmigrate_discouraged(struct task_struct *p); extern struct sched_cluster *rq_cluster(struct rq *rq); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 569b77de3cae..4d7281df26b6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3245,6 +3245,279 @@ static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info) return err; } +static u32 rateset_to_mask(struct ieee80211_supported_band *sband, + u8 *rates, u8 rates_len) +{ + u8 i; + u32 mask = 0; + + for (i = 0; i < rates_len; i++) { + int rate = (rates[i] & 0x7f) * 5; + int ridx; + + for (ridx = 0; ridx < sband->n_bitrates; ridx++) { + struct ieee80211_rate *srate = + &sband->bitrates[ridx]; + if (rate == srate->bitrate) { + mask |= 1 << ridx; + break; + } + } + if (ridx == sband->n_bitrates) + return 0; /* rate not found */ + } + + return mask; +} + +static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband, + u8 *rates, u8 rates_len, + u8 mcs[IEEE80211_HT_MCS_MASK_LEN]) +{ + u8 i; + + memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN); + + for (i = 0; i < rates_len; i++) { + int ridx, rbit; + + ridx = rates[i] / 8; + rbit = BIT(rates[i] % 8); + + /* check validity */ + if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN)) + return false; + + /* check availability */ + if (sband->ht_cap.mcs.rx_mask[ridx] & rbit) + mcs[ridx] |= rbit; + else + return false; + } + + return true; +} + +static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map) +{ + u16 mcs_mask = 0; + + switch (vht_mcs_map) { + case IEEE80211_VHT_MCS_NOT_SUPPORTED: + break; + case IEEE80211_VHT_MCS_SUPPORT_0_7: + mcs_mask = 0x00FF; + break; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + mcs_mask = 0x01FF; + break; + case IEEE80211_VHT_MCS_SUPPORT_0_9: + mcs_mask = 0x03FF; + break; + default: + break; + } + + return mcs_mask; +} + +static void vht_build_mcs_mask(u16 vht_mcs_map, + u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) +{ + u8 nss; + + for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) { + vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03); + vht_mcs_map >>= 2; + } +} + +static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband, + struct nl80211_txrate_vht *txrate, + u16 mcs[NL80211_VHT_NSS_MAX]) +{ + u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); + u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {}; + u8 i; + + if (!sband->vht_cap.vht_supported) + return false; + + memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX); + + /* Build vht_mcs_mask from VHT capabilities */ + vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask); + + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { + if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) + mcs[i] = txrate->mcs[i]; + else + return false; + } + + return true; +} + +static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { + [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_RATES }, + [NL80211_TXRATE_HT] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_HT_RATES }, + [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)}, + [NL80211_TXRATE_GI] = { .type = NLA_U8 }, +}; + +static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, + struct cfg80211_bitrate_mask *mask) +{ + struct nlattr *tb[NL80211_TXRATE_MAX + 1]; + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int rem, i; + struct nlattr *tx_rates; + struct ieee80211_supported_band *sband; + u16 vht_tx_mcs_map; + + memset(mask, 0, sizeof(*mask)); + /* Default to all rates enabled */ + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + sband = rdev->wiphy.bands[i]; + + if (!sband) + continue; + + mask->control[i].legacy = (1 << sband->n_bitrates) - 1; + memcpy(mask->control[i].ht_mcs, + sband->ht_cap.mcs.rx_mask, + sizeof(mask->control[i].ht_mcs)); + + if (!sband->vht_cap.vht_supported) + continue; + + vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); + vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs); + } + + /* if no rates are given set it back to the defaults */ + if (!info->attrs[NL80211_ATTR_TX_RATES]) + goto out; + + /* The nested attribute uses enum nl80211_band as the index. This maps + * directly to the enum nl80211_band values used in cfg80211. + */ + BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8); + nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) { + enum ieee80211_band band = nla_type(tx_rates); + int err; + + if (band < 0 || band >= IEEE80211_NUM_BANDS) + return -EINVAL; + sband = rdev->wiphy.bands[band]; + if (sband == NULL) + return -EINVAL; + err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), + nla_len(tx_rates), nl80211_txattr_policy); + if (err) + return err; + if (tb[NL80211_TXRATE_LEGACY]) { + mask->control[band].legacy = rateset_to_mask( + sband, + nla_data(tb[NL80211_TXRATE_LEGACY]), + nla_len(tb[NL80211_TXRATE_LEGACY])); + if ((mask->control[band].legacy == 0) && + nla_len(tb[NL80211_TXRATE_LEGACY])) + return -EINVAL; + } + if (tb[NL80211_TXRATE_HT]) { + if (!ht_rateset_to_mask( + sband, + nla_data(tb[NL80211_TXRATE_HT]), + nla_len(tb[NL80211_TXRATE_HT]), + mask->control[band].ht_mcs)) + return -EINVAL; + } + if (tb[NL80211_TXRATE_VHT]) { + if (!vht_set_mcs_mask( + sband, + nla_data(tb[NL80211_TXRATE_VHT]), + mask->control[band].vht_mcs)) + return -EINVAL; + } + if (tb[NL80211_TXRATE_GI]) { + mask->control[band].gi = + nla_get_u8(tb[NL80211_TXRATE_GI]); + if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI) + return -EINVAL; + } + + if (mask->control[band].legacy == 0) { + /* don't allow empty legacy rates if HT or VHT + * are not even supported. + */ + if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported || + rdev->wiphy.bands[band]->vht_cap.vht_supported)) + return -EINVAL; + + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) + if (mask->control[band].ht_mcs[i]) + goto out; + + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) + if (mask->control[band].vht_mcs[i]) + goto out; + + /* legacy and mcs rates may not be both empty */ + return -EINVAL; + } + } + +out: + return 0; +} + +static int validate_beacon_tx_rate(struct cfg80211_ap_settings *params) +{ + u32 rate, count_ht, count_vht, i; + enum nl80211_band band; + + band = params->chandef.chan->band; + rate = params->beacon_rate.control[band].legacy; + + /* Allow only one rate */ + if (hweight32(rate) > 1) + return -EINVAL; + + count_ht = 0; + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + if (hweight8(params->beacon_rate.control[band].ht_mcs[i]) > 1) { + return -EINVAL; + } else if (params->beacon_rate.control[band].ht_mcs[i]) { + count_ht++; + if (count_ht > 1) + return -EINVAL; + } + if (count_ht && rate) + return -EINVAL; + } + + count_vht = 0; + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { + if (hweight16(params->beacon_rate.control[band].vht_mcs[i]) > 1) { + return -EINVAL; + } else if (params->beacon_rate.control[band].vht_mcs[i]) { + count_vht++; + if (count_vht > 1) + return -EINVAL; + } + if (count_vht && rate) + return -EINVAL; + } + + if ((count_ht && count_vht) || (!rate && !count_ht && !count_vht)) + return -EINVAL; + + return 0; +} + static int nl80211_parse_beacon(struct nlattr *attrs[], struct cfg80211_beacon_data *bcn) { @@ -3474,6 +3747,16 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) wdev->iftype)) return -EINVAL; + if (info->attrs[NL80211_ATTR_TX_RATES]) { + err = nl80211_parse_tx_bitrate_mask(info, ¶ms.beacon_rate); + if (err) + return err; + + err = validate_beacon_tx_rate(¶ms); + if (err) + return err; + } + if (info->attrs[NL80211_ATTR_SMPS_MODE]) { params.smps_mode = nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]); @@ -8292,237 +8575,21 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, return rdev_cancel_remain_on_channel(rdev, wdev, cookie); } -static u32 rateset_to_mask(struct ieee80211_supported_band *sband, - u8 *rates, u8 rates_len) -{ - u8 i; - u32 mask = 0; - - for (i = 0; i < rates_len; i++) { - int rate = (rates[i] & 0x7f) * 5; - int ridx; - for (ridx = 0; ridx < sband->n_bitrates; ridx++) { - struct ieee80211_rate *srate = - &sband->bitrates[ridx]; - if (rate == srate->bitrate) { - mask |= 1 << ridx; - break; - } - } - if (ridx == sband->n_bitrates) - return 0; /* rate not found */ - } - - return mask; -} - -static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband, - u8 *rates, u8 rates_len, - u8 mcs[IEEE80211_HT_MCS_MASK_LEN]) -{ - u8 i; - - memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN); - - for (i = 0; i < rates_len; i++) { - int ridx, rbit; - - ridx = rates[i] / 8; - rbit = BIT(rates[i] % 8); - - /* check validity */ - if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN)) - return false; - - /* check availability */ - if (sband->ht_cap.mcs.rx_mask[ridx] & rbit) - mcs[ridx] |= rbit; - else - return false; - } - - return true; -} - -static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map) -{ - u16 mcs_mask = 0; - - switch (vht_mcs_map) { - case IEEE80211_VHT_MCS_NOT_SUPPORTED: - break; - case IEEE80211_VHT_MCS_SUPPORT_0_7: - mcs_mask = 0x00FF; - break; - case IEEE80211_VHT_MCS_SUPPORT_0_8: - mcs_mask = 0x01FF; - break; - case IEEE80211_VHT_MCS_SUPPORT_0_9: - mcs_mask = 0x03FF; - break; - default: - break; - } - - return mcs_mask; -} - -static void vht_build_mcs_mask(u16 vht_mcs_map, - u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) -{ - u8 nss; - - for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) { - vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03); - vht_mcs_map >>= 2; - } -} - -static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband, - struct nl80211_txrate_vht *txrate, - u16 mcs[NL80211_VHT_NSS_MAX]) -{ - u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); - u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {}; - u8 i; - - if (!sband->vht_cap.vht_supported) - return false; - - memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX); - - /* Build vht_mcs_mask from VHT capabilities */ - vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask); - - for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { - if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) - mcs[i] = txrate->mcs[i]; - else - return false; - } - - return true; -} - -static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { - [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, - .len = NL80211_MAX_SUPP_RATES }, - [NL80211_TXRATE_HT] = { .type = NLA_BINARY, - .len = NL80211_MAX_SUPP_HT_RATES }, - [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)}, - [NL80211_TXRATE_GI] = { .type = NLA_U8 }, -}; - static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, struct genl_info *info) { - struct nlattr *tb[NL80211_TXRATE_MAX + 1]; - struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_bitrate_mask mask; - int rem, i; + struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; - struct nlattr *tx_rates; - struct ieee80211_supported_band *sband; - u16 vht_tx_mcs_map; + int err; if (!rdev->ops->set_bitrate_mask) return -EOPNOTSUPP; - memset(&mask, 0, sizeof(mask)); - /* Default to all rates enabled */ - for (i = 0; i < IEEE80211_NUM_BANDS; i++) { - sband = rdev->wiphy.bands[i]; - - if (!sband) - continue; - - mask.control[i].legacy = (1 << sband->n_bitrates) - 1; - memcpy(mask.control[i].ht_mcs, - sband->ht_cap.mcs.rx_mask, - sizeof(mask.control[i].ht_mcs)); - - if (!sband->vht_cap.vht_supported) - continue; - - vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); - vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs); - } - - /* if no rates are given set it back to the defaults */ - if (!info->attrs[NL80211_ATTR_TX_RATES]) - goto out; - - /* - * The nested attribute uses enum nl80211_band as the index. This maps - * directly to the enum ieee80211_band values used in cfg80211. - */ - BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8); - nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) { - enum ieee80211_band band = nla_type(tx_rates); - int err; - - if (band < 0 || band >= IEEE80211_NUM_BANDS) - return -EINVAL; - sband = rdev->wiphy.bands[band]; - if (sband == NULL) - return -EINVAL; - err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), - nla_len(tx_rates), nl80211_txattr_policy); - if (err) - return err; - if (tb[NL80211_TXRATE_LEGACY]) { - mask.control[band].legacy = rateset_to_mask( - sband, - nla_data(tb[NL80211_TXRATE_LEGACY]), - nla_len(tb[NL80211_TXRATE_LEGACY])); - if ((mask.control[band].legacy == 0) && - nla_len(tb[NL80211_TXRATE_LEGACY])) - return -EINVAL; - } - if (tb[NL80211_TXRATE_HT]) { - if (!ht_rateset_to_mask( - sband, - nla_data(tb[NL80211_TXRATE_HT]), - nla_len(tb[NL80211_TXRATE_HT]), - mask.control[band].ht_mcs)) - return -EINVAL; - } - if (tb[NL80211_TXRATE_VHT]) { - if (!vht_set_mcs_mask( - sband, - nla_data(tb[NL80211_TXRATE_VHT]), - mask.control[band].vht_mcs)) - return -EINVAL; - } - if (tb[NL80211_TXRATE_GI]) { - mask.control[band].gi = - nla_get_u8(tb[NL80211_TXRATE_GI]); - if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI) - return -EINVAL; - } - - if (mask.control[band].legacy == 0) { - /* don't allow empty legacy rates if HT or VHT - * are not even supported. - */ - if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported || - rdev->wiphy.bands[band]->vht_cap.vht_supported)) - return -EINVAL; - - for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) - if (mask.control[band].ht_mcs[i]) - goto out; - - for (i = 0; i < NL80211_VHT_NSS_MAX; i++) - if (mask.control[band].vht_mcs[i]) - goto out; - - /* legacy and mcs rates may not be both empty */ - return -EINVAL; - } - } + err = nl80211_parse_tx_bitrate_mask(info, &mask); + if (err) + return err; -out: return rdev_set_bitrate_mask(rdev, dev, NULL, &mask); } diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c index d9d413f0a80a..f51301d1ab08 100644 --- a/sound/soc/codecs/wcd-dsp-mgr.c +++ b/sound/soc/codecs/wcd-dsp-mgr.c @@ -717,8 +717,8 @@ static void wdsp_ssr_work_fn(struct work_struct *work) */ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED); - /* If codec went down, then all components must be re-initialized */ - if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_DOWN) { + /* If codec restarted, then all components must be re-initialized */ + if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_UP) { wdsp_deinit_components(wdsp); WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED); } diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 3cbc1e7821cf..a1f23685beb5 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -2593,6 +2593,8 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) if (mbhc->mbhc_cb && mbhc->mbhc_cb->register_notifier) mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false); mutex_destroy(&mbhc->codec_resource_lock); + mutex_destroy(&mbhc->hphl_pa_lock); + mutex_destroy(&mbhc->hphr_pa_lock); } EXPORT_SYMBOL(wcd_mbhc_deinit); diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c index 4e3e769585e6..580591a32ba1 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsd.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c @@ -619,6 +619,53 @@ static const struct snd_soc_dapm_widget tavil_dsd_widgets[] = { }; /** + * tavil_dsd_post_ssr_init - DSD intialization after subsystem restart + * + * @codec: pointer to snd_soc_codec + * + * Returns 0 on success or error on failure + */ +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_conf) +{ + struct snd_soc_codec *codec; + + if (!dsd_conf || !dsd_conf->codec) + return -EINVAL; + + codec = dsd_conf->codec; + /* Disable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08); + + /* DSD registers init */ + if (dsd_conf->version == TAVIL_VERSION_1_0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); + } + /* DSD0: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); + /* DSD1: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07, + 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07, + 0x04); + + /* Enable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00); + + return 0; +} +EXPORT_SYMBOL(tavil_dsd_post_ssr_init); + +/** * tavil_dsd_init - DSD intialization * * @codec: pointer to snd_soc_codec diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/sound/soc/codecs/wcd934x/wcd934x-dsd.h index 21450c90a272..498288335b3b 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsd.h +++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.h @@ -55,6 +55,7 @@ void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port, u32 sample_rate, u8 sample_rate_val); struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec); void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config); +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config); #else int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf, int interp_num, int sw_value) @@ -88,5 +89,9 @@ struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec) void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config) { } +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config) +{ + return 0; +} #endif #endif diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c index 8d2247176607..7e4cd6ce55a7 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -46,6 +46,11 @@ mutex_unlock(&lock); \ } +enum wcd_mem_type { + WCD_MEM_TYPE_ALWAYS_ON, + WCD_MEM_TYPE_SWITCHABLE, +}; + struct wcd_cntl_attribute { struct attribute attr; ssize_t (*show)(struct wcd_dsp_cntl *cntl, char *buf); @@ -469,7 +474,8 @@ static void wcd_cntl_cpar_ctrl(struct wcd_dsp_cntl *cntl, snd_soc_write(codec, WCD934X_CPE_SS_CPAR_CTL, 0x00); } -static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl) +static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl, + enum wcd_mem_type mem_type) { struct snd_soc_codec *codec = cntl->codec; struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); @@ -477,77 +483,115 @@ static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl) u8 status; int ret = 0; - snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, - 0x04, 0x00); - snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL, - 0x80, 0x80); - snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, - 0x01, 0x01); - do { - loop_cnt++; - /* Time to enable the power domain for memory */ - usleep_range(100, 150); - status = snd_soc_read(codec, - WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL); - } while ((status & 0x02) != 0x02 && - loop_cnt != WCD_MEM_ENABLE_MAX_RETRIES); - - if ((status & 0x02) != 0x02) { - dev_err(cntl->codec->dev, - "%s: power domain not enabled, status = 0x%02x\n", - __func__, status); - ret = -EIO; - goto done; - } + switch (mem_type) { - /* 512KB of always on region */ - wcd9xxx_slim_write_repeat(wcd9xxx, - WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, - ARRAY_SIZE(mem_enable_values), - mem_enable_values); - wcd9xxx_slim_write_repeat(wcd9xxx, - WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, - ARRAY_SIZE(mem_enable_values), - mem_enable_values); - - snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, 0x05); - - /* Rest of the memory */ - wcd9xxx_slim_write_repeat(wcd9xxx, - WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, - ARRAY_SIZE(mem_enable_values), - mem_enable_values); - wcd9xxx_slim_write_repeat(wcd9xxx, - WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, - ARRAY_SIZE(mem_enable_values), - mem_enable_values); + case WCD_MEM_TYPE_ALWAYS_ON: + /* 512KB of always on region */ + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + break; + + case WCD_MEM_TYPE_SWITCHABLE: + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL, + 0x80, 0x80); + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x01, 0x01); + do { + loop_cnt++; + /* Time to enable the power domain for memory */ + usleep_range(100, 150); + status = snd_soc_read(codec, + WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL); + } while ((status & 0x02) != 0x02 && + loop_cnt != WCD_MEM_ENABLE_MAX_RETRIES); + + if ((status & 0x02) != 0x02) { + dev_err(cntl->codec->dev, + "%s: power domain not enabled, status = 0x%02x\n", + __func__, status); + ret = -EIO; + goto done; + } + + /* Rest of the memory */ + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + 0x05); + break; + + default: + dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n", + __func__, mem_type); + ret = -EINVAL; + break; + } +done: /* Make sure Deep sleep of memories is enabled for all banks */ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); -done: + return ret; } -static void wcd_cntl_disable_memory(struct wcd_dsp_cntl *cntl) +static void wcd_cntl_disable_memory(struct wcd_dsp_cntl *cntl, + enum wcd_mem_type mem_type) { struct snd_soc_codec *codec = cntl->codec; + u8 val; + + switch (mem_type) { + case WCD_MEM_TYPE_ALWAYS_ON: + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + 0xFF); + break; + case WCD_MEM_TYPE_SWITCHABLE: + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + 0x07); + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x01, 0x00); + val = snd_soc_read(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL); + if (val & 0x02) + dev_err(codec->dev, + "%s: Disable switchable failed, val = 0x%02x", + __func__, val); + + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL, + 0x80, 0x00); + break; + default: + dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n", + __func__, mem_type); + break; + } snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); - snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, 0xFF); - snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, 0xFF); - snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, 0x07); - snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, 0xFF); - snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, 0xFF); - - snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, - 0x01, 0x00); - snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL, - 0x80, 0x00); - snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, - 0x04, 0x04); } static void wcd_cntl_do_shutdown(struct wcd_dsp_cntl *cntl) @@ -743,7 +787,9 @@ static int wcd_control_handler(struct device *dev, void *priv_data, wcd_cntl_cpar_ctrl(cntl, true); if (event == WDSP_EVENT_PRE_DLOAD_CODE) - wcd_cntl_enable_memory(cntl); + wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_ALWAYS_ON); + else if (event == WDSP_EVENT_PRE_DLOAD_DATA) + wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE); break; case WDSP_EVENT_DO_BOOT: @@ -758,6 +804,7 @@ static int wcd_control_handler(struct device *dev, void *priv_data, case WDSP_EVENT_DO_SHUTDOWN: wcd_cntl_do_shutdown(cntl); + wcd_cntl_disable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE); break; default: @@ -1188,7 +1235,8 @@ void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl) * irrespective of DSP was booted up or not. */ wcd_cntl_do_shutdown(control); - wcd_cntl_disable_memory(control); + wcd_cntl_disable_memory(control, WCD_MEM_TYPE_SWITCHABLE); + wcd_cntl_disable_memory(control, WCD_MEM_TYPE_ALWAYS_ON); component_del(codec->dev, &wcd_ctrl_component_ops); diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c index b3a30eb10b92..d713edbbb355 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c +++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c @@ -912,6 +912,37 @@ void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec) EXPORT_SYMBOL(tavil_mbhc_hs_detect_exit); /* + * tavil_mbhc_post_ssr_init: initialize mbhc for tavil post subsystem restart + * @mbhc: poniter to wcd934x_mbhc structure + * @codec: handle to snd_soc_codec * + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + int ret; + + if (!mbhc || !codec) + return -EINVAL; + + wcd_mbhc_deinit(&mbhc->wcd_mbhc); + ret = wcd_mbhc_init(&mbhc->wcd_mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto done; + } + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01); + +done: + return ret; +} +EXPORT_SYMBOL(tavil_mbhc_post_ssr_init); + +/* * tavil_mbhc_init: initialize mbhc for tavil * @mbhc: poniter to wcd934x_mbhc struct pointer to store the configs * @codec: handle to snd_soc_codec * @@ -977,7 +1008,9 @@ void tavil_mbhc_deinit(struct snd_soc_codec *codec) { struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); - if (!wcd934x_mbhc) + if (wcd934x_mbhc) { + wcd_mbhc_deinit(&wcd934x_mbhc->wcd_mbhc); devm_kfree(codec->dev, wcd934x_mbhc); + } } EXPORT_SYMBOL(tavil_mbhc_deinit); diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h index 120a7b0f8177..3c88a12194af 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h +++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h @@ -42,6 +42,8 @@ extern void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec); extern int tavil_mbhc_hs_detect(struct snd_soc_codec *codec, struct wcd_mbhc_config *mbhc_cfg); extern void tavil_mbhc_deinit(struct snd_soc_codec *codec); +extern int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, + struct snd_soc_codec *codec); #endif /* __WCD934X_MBHC_H__ */ diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index 2f117159402e..0213d9ba4a59 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -657,6 +657,8 @@ static const struct tavil_reg_mask_val tavil_spkr_mode1[] = { {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44}, }; +static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil); + /* * wcd934x_get_codec_info: Get codec specific information * @@ -4159,7 +4161,8 @@ int tavil_micbias_control(struct snd_soc_codec *codec, snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); break; case MICB_PULLUP_DISABLE: - tavil->pullup_ref[micb_index]--; + if (tavil->pullup_ref[micb_index] > 0) + tavil->pullup_ref[micb_index]--; if ((tavil->pullup_ref[micb_index] == 0) && (tavil->micb_ref[micb_index] == 0)) snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); @@ -4179,7 +4182,8 @@ int tavil_micbias_control(struct snd_soc_codec *codec, post_dapm_on, &tavil->mbhc->wcd_mbhc); break; case MICB_DISABLE: - tavil->micb_ref[micb_index]--; + if (tavil->pullup_ref[micb_index] > 0) + tavil->micb_ref[micb_index]--; if ((tavil->micb_ref[micb_index] == 0) && (tavil->pullup_ref[micb_index] > 0)) snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); @@ -8337,6 +8341,7 @@ static void tavil_cleanup_irqs(struct tavil_priv *tavil) &wcd9xxx->core_res; wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tavil); + wcd9xxx_free_irq(core_res, WCD934X_IRQ_MISC, tavil); } /* @@ -8588,6 +8593,131 @@ static void tavil_mclk2_reg_defaults(struct tavil_priv *tavil) } } +static int tavil_device_down(struct wcd9xxx *wcd9xxx) +{ + struct snd_soc_codec *codec; + struct tavil_priv *priv; + int count; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + priv = snd_soc_codec_get_drvdata(codec); + swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_DOWN, NULL); + tavil_dsd_reset(priv->dsd_config); + snd_soc_card_change_online_state(codec->component.card, 0); + for (count = 0; count < NUM_CODEC_DAIS; count++) + priv->dai[count].bus_down_in_recovery = true; + wcd_dsp_ssr_event(priv->wdsp_cntl, WCD_CDC_DOWN_EVENT); + priv->resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + + return 0; +} + +static int tavil_post_reset_cb(struct wcd9xxx *wcd9xxx) +{ + int i, ret = 0; + struct wcd9xxx *control; + struct snd_soc_codec *codec; + struct tavil_priv *tavil; + struct wcd9xxx_pdata *pdata; + struct wcd_mbhc *mbhc; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + tavil = snd_soc_codec_get_drvdata(codec); + control = dev_get_drvdata(codec->dev->parent); + + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_lock(&tavil->codec_mutex); + /* + * Codec hardware by default comes up in SVS mode. + * Initialize the svs_ref_cnt to 1 to reflect the hardware + * state in the driver. + */ + tavil->svs_ref_cnt = 1; + + tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tavil_init_slim_slave_cfg(codec); + snd_soc_card_change_online_state(codec->component.card, 1); + + /* Class-H Init */ + wcd_clsh_init(&tavil->clsh_d); + /* Default HPH Mode to Class-H LOHiFi */ + tavil->hph_mode = CLS_H_LOHIFI; + + for (i = 0; i < TAVIL_MAX_MICBIAS; i++) + tavil->micb_ref[i] = 0; + + for (i = 0; i < COMPANDER_MAX; i++) + tavil->comp_enabled[i] = 0; + + dev_dbg(codec->dev, "%s: MCLK Rate = %x\n", + __func__, control->mclk_rate); + + if (control->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (control->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + wcd_resmgr_post_ssr_v2(tavil->resmgr); + tavil_update_reg_defaults(tavil); + tavil_codec_init_reg(tavil); + __tavil_enable_efuse_sensing(tavil); + tavil_mclk2_reg_defaults(tavil); + + __tavil_cdc_mclk_enable(tavil, true); + regcache_mark_dirty(codec->component.regmap); + regcache_sync(codec->component.regmap); + __tavil_cdc_mclk_enable(tavil, false); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tavil_handle_pdata(tavil, pdata); + if (IS_ERR_VALUE(ret)) + dev_err(codec->dev, "%s: invalid pdata\n", __func__); + + /* Initialize MBHC module */ + mbhc = &tavil->mbhc->wcd_mbhc; + ret = tavil_mbhc_post_ssr_init(tavil->mbhc, codec); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto done; + } else { + tavil_mbhc_hs_detect(codec, mbhc->mbhc_cfg); + } + + /* DSD initialization */ + ret = tavil_dsd_post_ssr_init(tavil->dsd_config); + if (ret) + dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__); + + tavil_cleanup_irqs(tavil); + ret = tavil_setup_irqs(tavil); + if (ret) { + dev_err(codec->dev, "%s: tavil irq setup failed %d\n", + __func__, ret); + goto done; + } + + tavil_set_spkr_mode(codec, tavil->swr.spkr_mode); + /* + * Once the codec initialization is completed, the svs vote + * can be released allowing the codec to go to SVS2. + */ + tavil_vote_svs(tavil, false); + wcd_dsp_ssr_event(tavil->wdsp_cntl, WCD_CDC_UP_EVENT); + +done: + mutex_unlock(&tavil->codec_mutex); + return ret; +} + static int tavil_soc_codec_probe(struct snd_soc_codec *codec) { struct wcd9xxx *control; @@ -8603,8 +8733,12 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) tavil = snd_soc_codec_get_drvdata(codec); tavil->intf_type = wcd9xxx_get_intf_type(); + control->dev_down = tavil_device_down; + control->post_reset = tavil_post_reset_cb; + control->ssr_priv = (void *)codec; + /* Resource Manager post Init */ - ret = wcd_resmgr_post_init(tavil->resmgr, NULL, codec); + ret = wcd_resmgr_post_init(tavil->resmgr, &tavil_resmgr_cb, codec); if (ret) { dev_err(codec->dev, "%s: wcd resmgr post init failed\n", __func__); @@ -9215,6 +9349,7 @@ err_mem: static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil) { int val, rc; + struct snd_soc_codec *codec; __tavil_cdc_mclk_enable(tavil, true); @@ -9231,8 +9366,17 @@ static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil) rc = regmap_read(tavil->wcd9xxx->regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, &val); if (rc || (!(val & 0x01))) - WARN(1, "%s: Efuse sense is not complete\n", __func__); + WARN(1, "%s: Efuse sense is not complete val=%x, ret=%d\n", + __func__, val, rc); + codec = tavil->codec; + if (!codec) { + pr_debug("%s: codec is not yet registered\n", __func__); + goto done; + } + tavil_enable_sido_buck(codec); + +done: __tavil_cdc_mclk_enable(tavil, false); return rc; diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c index c82e0ad13db3..05a0e27cb45a 100644 --- a/sound/soc/msm/msmcobalt.c +++ b/sound/soc/msm/msmcobalt.c @@ -511,13 +511,20 @@ static struct wcd_mbhc_config wcd_mbhc_cfg = { .moisture_en = true, }; -static struct snd_soc_dapm_route wcd_audio_paths[] = { +static struct snd_soc_dapm_route wcd_audio_paths_tasha[] = { {"MIC BIAS1", NULL, "MCLK TX"}, {"MIC BIAS2", NULL, "MCLK TX"}, {"MIC BIAS3", NULL, "MCLK TX"}, {"MIC BIAS4", NULL, "MCLK TX"}, }; +static struct snd_soc_dapm_route wcd_audio_paths[] = { + {"MIC BIAS1", NULL, "MCLK"}, + {"MIC BIAS2", NULL, "MCLK"}, + {"MIC BIAS3", NULL, "MCLK"}, + {"MIC BIAS4", NULL, "MCLK"}, +}; + static struct afe_clk_set mi2s_clk[MI2S_MAX] = { { AFE_API_VERSION_I2S_CONFIG, @@ -2362,6 +2369,42 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, ext_disp_rx_sample_rate_get, ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, tdm_rx_sample_rate_get, tdm_rx_sample_rate_put), @@ -2380,6 +2423,24 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, tdm_tx_ch_get, tdm_tx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, aux_pcm_rx_sample_rate_get, aux_pcm_rx_sample_rate_put), @@ -2751,6 +2812,38 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = SAMPLING_RATE_48KHZ; break; + case MSM_BACKEND_DAI_PRI_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_PRI_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_SEC_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + case MSM_BACKEND_DAI_TERT_TDM_RX_0: channels->min = channels->max = tdm_rx_cfg[TDM_TERT][TDM_0].channels; @@ -2767,6 +2860,22 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; break; + case MSM_BACKEND_DAI_QUAT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUAT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + case MSM_BACKEND_DAI_AUXPCM_RX: rate->min = rate->max = aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; @@ -3123,8 +3232,12 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, ARRAY_SIZE(msm_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, wcd_audio_paths, - ARRAY_SIZE(wcd_audio_paths)); + if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) + snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tasha, + ARRAY_SIZE(wcd_audio_paths_tasha)); + else + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); @@ -4836,6 +4949,62 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .ignore_suspend = 1, }, { + .name = LPASS_BE_PRI_TDM_RX_0, + .stream_name = "Primary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36864", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_PRI_TDM_TX_0, + .stream_name = "Primary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36865", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_RX_0, + .stream_name = "Secondary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36880", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_SEC_TDM_TX_0, + .stream_name = "Secondary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36881", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { .name = LPASS_BE_TERT_TDM_RX_0, .stream_name = "Tertiary TDM0 Playback", .cpu_dai_name = "msm-dai-q6-tdm.36896", @@ -4863,6 +5032,34 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .ops = &msm_tdm_be_ops, .ignore_suspend = 1, }, + { + .name = LPASS_BE_QUAT_TDM_RX_0, + .stream_name = "Quaternary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36912", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUAT_TDM_TX_0, + .stream_name = "Quaternary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36913", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, }; static struct snd_soc_dai_link msm_tasha_be_dai_links[] = { diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 547af163c5c0..13d4ba27d990 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -121,14 +121,14 @@ static int msm_routing_get_bit_width(unsigned int format) return bit_width; } -static bool msm_is_fractional_resample_needed(int input_sr, int output_sr) +static bool msm_is_resample_needed(int input_sr, int output_sr) { bool rc = false; - if ((input_sr % output_sr != 0) && (output_sr % input_sr != 0)) + if (input_sr != output_sr) rc = true; - pr_debug("performing fractional resample (%s) for copp rate (%d)afe rate (%d)", + pr_debug("perform resampling (%s) for copp rate (%d)afe rate (%d)", (rc ? "oh yes" : "not really"), input_sr, output_sr); @@ -901,7 +901,7 @@ int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode, set_bit(copp_idx, &session_copp_map[fe_id][session_type][i]); - if (msm_is_fractional_resample_needed( + if (msm_is_resample_needed( sample_rate, msm_bedais[i].sample_rate)) adm_copp_mfc_cfg( @@ -1057,7 +1057,7 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, set_bit(copp_idx, &session_copp_map[fedai_id][session_type][i]); - if (msm_is_fractional_resample_needed( + if (msm_is_resample_needed( sample_rate, msm_bedais[i].sample_rate)) adm_copp_mfc_cfg( @@ -1284,7 +1284,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) set_bit(copp_idx, &session_copp_map[val][session_type][reg]); - if (msm_is_fractional_resample_needed( + if (msm_is_resample_needed( sample_rate, msm_bedais[reg].sample_rate)) adm_copp_mfc_cfg( @@ -3628,6 +3628,210 @@ static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_tx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new sec_tdm_tx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_0 , MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, @@ -3679,6 +3883,57 @@ static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new tert_tdm_tx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_1 , MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, @@ -3883,6 +4138,57 @@ static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new quat_tdm_tx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_RX_1 , MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, @@ -8141,9 +8447,24 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_MIXER("QUIN_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, quinary_mi2s_rx_mixer_controls, ARRAY_SIZE(quinary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_0_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_tx_0_mixer_controls, + ARRAY_SIZE(pri_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_0_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_tx_0_mixer_controls, + ARRAY_SIZE(sec_tdm_tx_0_mixer_controls)), SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, tert_tdm_rx_0_mixer_controls, ARRAY_SIZE(tert_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_tx_0_mixer_controls, + ARRAY_SIZE(tert_tdm_tx_0_mixer_controls)), SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, tert_tdm_rx_1_mixer_controls, ARRAY_SIZE(tert_tdm_rx_1_mixer_controls)), @@ -8156,6 +8477,9 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, quat_tdm_rx_0_mixer_controls, ARRAY_SIZE(quat_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_tx_0_mixer_controls, + ARRAY_SIZE(quat_tdm_tx_0_mixer_controls)), SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, quat_tdm_rx_1_mixer_controls, ARRAY_SIZE(quat_tdm_rx_1_mixer_controls)), @@ -8775,6 +9099,78 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX Audio Mixer"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Audio Mixer"}, + + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_TX_0", NULL, "PRI_TDM_TX_0 Audio Mixer"}, + + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Audio Mixer"}, + + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_TX_0", NULL, "SEC_TDM_TX_0 Audio Mixer"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, @@ -8793,6 +9189,24 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Audio Mixer"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_TX_0", NULL, "TERT_TDM_TX_0 Audio Mixer"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, @@ -8865,6 +9279,24 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Audio Mixer"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUAT_TDM_TX_0", NULL, "QUAT_TDM_TX_0 Audio Mixer"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, @@ -9735,6 +10167,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_MI2S_RX", NULL, "TERT_MI2S_DL_HL"}, {"QUAT_MI2S_UL_HL", NULL, "QUAT_MI2S_TX"}, + {"PRI_TDM_TX_0_UL_HL", NULL, "PRI_TDM_TX_0"}, + {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0_DL_HL"}, + {"SEC_TDM_TX_0_UL_HL", NULL, "SEC_TDM_TX_0"}, + {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0_DL_HL"}, {"TERT_TDM_TX_0_UL_HL", NULL, "TERT_TDM_TX_0"}, {"TERT_TDM_TX_1_UL_HL", NULL, "TERT_TDM_TX_1"}, {"TERT_TDM_TX_2_UL_HL", NULL, "TERT_TDM_TX_2"}, @@ -10140,6 +10576,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"BE_OUT", NULL, "SLIMBUS_3_RX"}, {"BE_OUT", NULL, "VOICE_PLAYBACK_TX"}, {"BE_OUT", NULL, "VOICE2_PLAYBACK_TX"}, + {"BE_OUT", NULL, "PRI_TDM_RX_0"}, + {"BE_OUT", NULL, "SEC_TDM_RX_0"}, {"BE_OUT", NULL, "TERT_TDM_RX_0"}, {"BE_OUT", NULL, "TERT_TDM_RX_1"}, {"BE_OUT", NULL, "TERT_TDM_RX_2"}, @@ -10186,6 +10624,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_LCH_MUX"}, {"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_RCH_MUX"}, {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_VI_FB_MUX"}, + {"PRI_TDM_TX_0", NULL, "BE_IN"}, + {"SEC_TDM_TX_0", NULL, "BE_IN"}, {"TERT_TDM_TX_0", NULL, "BE_IN"}, {"TERT_TDM_TX_1", NULL, "BE_IN"}, {"TERT_TDM_TX_2", NULL, "BE_IN"}, @@ -10381,7 +10821,7 @@ static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream) set_bit(copp_idx, &session_copp_map[i][session_type][be_id]); - if (msm_is_fractional_resample_needed( + if (msm_is_resample_needed( sample_rate, bedai->sample_rate)) adm_copp_mfc_cfg( diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index e30a4efa6e60..564b67c9f76b 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -2508,8 +2508,6 @@ void adm_copp_mfc_cfg(int port_id, int copp_idx, int dst_sample_rate) mfc_cfg.sampling_rate = dst_sample_rate; mfc_cfg.bits_per_sample = atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]); - if (24 == mfc_cfg.bits_per_sample) - mfc_cfg.bits_per_sample = 32; open.dev_num_channel = mfc_cfg.num_channels = atomic_read(&this_adm.copp.channels[port_idx][copp_idx]); diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index 88c27339b299..0ea94cb52bfb 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -1221,8 +1221,9 @@ int q6asm_audio_client_buf_alloc(unsigned int dir, struct audio_buffer *buf; size_t len; - if (!(ac) || ((dir != IN) && (dir != OUT))) { - pr_err("%s: ac %pK dir %d\n", __func__, ac, dir); + if (!(ac) || !(bufsz) || ((dir != IN) && (dir != OUT))) { + pr_err("%s: ac %pK bufsz %d dir %d\n", __func__, ac, bufsz, + dir); return -EINVAL; } |
