diff options
112 files changed, 5023 insertions, 1584 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt b/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt index 56559a69eb46..296e5dd0e383 100644 --- a/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt +++ b/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt @@ -34,6 +34,7 @@ Optional properties: - qcom,ipi-ping : (boolean) send keep alive ping to other cpus if present - qcom,wakeup-enable : (boolean) enable non secure watchdog to freeze / unfreeze automatically across suspend / resume path. +- qcom,scandump-size : size of scan dump memory region Example: diff --git a/Documentation/devicetree/bindings/arm/msm/spss_utils.txt b/Documentation/devicetree/bindings/arm/msm/spss_utils.txt new file mode 100644 index 000000000000..21b96377e5e4 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/spss_utils.txt @@ -0,0 +1,27 @@ +Qualcomm Technologies, Inc. Secure Processor SubSystem Utilities (spss_utils) + +The Secure Processor SubSystem (SPSS) is a dedicated subsystem for security. +It has its own CPU, memories, and cryptographic engine. +It shall provide cryptographic services to other subsystems. +The SPSS firmware is loaded by PIL driver. +The communication with SPSS is done via spcom driver, using glink. + +The spss_utils driver selects the SPSS firmware file, +according to a dedicated fuse and the platform HW version. + +Required properties: +-compatible : should be "qcom,spss_utils" +-qcom,spss-fuse-addr: fuse register physical address +-qcom,spss-fuse-bit: fuse relevant bit +-qcom,spss-test-firmware-name: test firmware file name +-qcom,spss-prod-firmware-name: production firmware file name + +Example: + qcom,spss_utils { + compatible = "qcom,spss-utils"; + + qcom,spss-fuse-addr = <0x007841c4 0x4>; /* spss test fuse physical address */ + qcom,spss-fuse-bit = <27>; + qcom,spss-test-firmware-name = "spss1t"; /* 8 chars max */ + qcom,spss-prod-firmware-name = "spss1p"; /* 8 chars max */ + }; diff --git a/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt b/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt index d96c174d2581..c77f84b61eaf 100644 --- a/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt +++ b/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt @@ -5,8 +5,8 @@ can be used to measure the bandwidth of read/write traffic from the BIMC master ports. For example, the CPU subsystem sits on one BIMC master port. Required properties: -- compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2" or - "qcom,bimc-bwmon3" +- compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2", + "qcom,bimc-bwmon3" or "qcom,bimc-bwmon4" - reg: Pairs of physical base addresses and region sizes of memory mapped registers. - reg-names: Names of the bases for the above registers. Expected @@ -14,6 +14,8 @@ Required properties: - interrupts: Lists the threshold IRQ. - qcom,mport: The hardware master port that this device can monitor - qcom,target-dev: The DT device that corresponds to this master port +- qcom,hw-timer-hz: Hardware sampling rate in Hz. This field must be + specified for "qcom,bimc-bwmon4" Example: qcom,cpu-bwmon { @@ -23,4 +25,5 @@ Example: interrupts = <0 183 1>; qcom,mport = <0>; qcom,target-dev = <&cpubw>; + qcom,hw-timer-hz = <19200000>; }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt index af09840bb053..4de22947b333 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt @@ -12,7 +12,7 @@ Required properties: - interrupt-parent : Parent of interrupt. - interrupts : Configuration of touch panel controller interrupt GPIO. - - goodix,family-id : Family identification of the controller. + - goodix,product-id : Product identification of the controller. - interrupt-gpios : Interrupt gpio which is to provide interrupts to host, same as "interrupts" node. - reset-gpios : Reset gpio to control the reset of chip. @@ -37,10 +37,24 @@ Optional properties: - goodix,no-force-update : To specify force update is allowed. - goodix,button-map : Button map of key codes. The number of key codes depend on panel. - - goodix,cfg-data : Touchpanel controller configuration data, ask vendor - to provide that. Default configuration will be - used if this property is not present. - + - goodix,cfg-data0 : Touch screen controller config data group 0. Ask vendor + to provide that. + Driver supports maximum six config groups. If more than one + groups are defined, driver will select config group depending + on hardware configuration. If only config group 0 is defined, + it will be used for all hardware configurations. + Touch screen controller will use its onchip default config data + if this property is not present. + - goodix,cfg-data1 : Touch screen controller config data group 1. Ask vendor + to provide that. + - goodix,cfg-data2 : Touch screen controller config data group 2. Ask vendor + to provide that. + - goodix,cfg-data3 : Touch screen controller config data group 3. Ask vendor + to provide that. + - goodix,cfg-data4 : Touch screen controller config data group 4. Ask vendor + to provide that. + - goodix,cfg-data5 : Touch screen controller config data group 5. Ask vendor + to provide that. Example: i2c@f9927000 { goodix@5d { @@ -54,26 +68,26 @@ i2c@f9927000 { goodix,panel-coords = <0 0 720 1200>; goodix,display-coords = <0 0 720 1080>; goodix,button-map= <158 102 139>; - goodix,family-id = <0x0>; - goodix,cfg-data = [ - 41 D0 02 00 05 0A 05 01 01 08 - 12 58 50 41 03 05 00 00 00 00 - 00 00 00 00 00 00 00 8C 2E 0E - 28 24 73 13 00 00 00 83 03 1D - 40 02 00 00 00 03 64 32 00 00 - 00 1A 38 94 C0 02 00 00 00 04 - 9E 1C 00 8D 20 00 7A 26 00 6D - 2C 00 60 34 00 60 10 38 68 00 - F0 50 35 FF FF 27 00 00 00 00 - 00 01 1B 14 0C 14 00 00 01 00 - 00 00 00 00 00 00 00 00 00 00 - 00 00 02 04 06 08 0A 0C 0E 10 - 12 14 16 18 1A 1C FF FF FF FF - FF FF FF FF FF FF FF FF FF FF - FF FF 00 02 04 06 08 0A 0C 0F - 10 12 13 14 16 18 1C 1D 1E 1F - 20 21 22 24 26 28 29 2A FF FF - FF FF FF FF FF FF FF 22 22 22 - 22 22 22 FF 07 01]; + goodix,product-id = "915"; + goodix,cfg-data0 = [ + 41 D0 02 00 05 0A 05 01 01 08 + 12 58 50 41 03 05 00 00 00 00 + 00 00 00 00 00 00 00 8C 2E 0E + 28 24 73 13 00 00 00 83 03 1D + 40 02 00 00 00 03 64 32 00 00 + 00 1A 38 94 C0 02 00 00 00 04 + 9E 1C 00 8D 20 00 7A 26 00 6D + 2C 00 60 34 00 60 10 38 68 00 + F0 50 35 FF FF 27 00 00 00 00 + 00 01 1B 14 0C 14 00 00 01 00 + 00 00 00 00 00 00 00 00 00 00 + 00 00 02 04 06 08 0A 0C 0E 10 + 12 14 16 18 1A 1C FF FF FF FF + FF FF FF FF FF FF FF FF FF FF + FF FF 00 02 04 06 08 0A 0C 0F + 10 12 13 14 16 18 1C 1D 1E 1F + 20 21 22 24 26 28 29 2A FF FF + FF FF FF FF FF FF FF 22 22 22 + 22 22 22 FF 07 01]; }; }; diff --git a/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt b/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt index e22435bb0be6..8a6b3c4355af 100644 --- a/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt +++ b/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt @@ -13,6 +13,7 @@ Required properties: - "qcom,csiphy-v3.4.2" - "qcom,csiphy-v3.5" - "qcom,csiphy-v5.0" + - "qcom,csiphy-v5.01" - reg : offset and length of the register set for the device for the csiphy operating in compatible mode. - reg-names : should specify relevant names to each reg property defined. diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index 48fdd3e03947..bceee5e1747d 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -13,6 +13,9 @@ Required properties: - reg : <registers mapping> first entry should contain UFS host controller register address space (mandatory), second entry is the device ref. clock control register map (optional). +- reset : reset specifier pair consists of phandle for the reset provider + and reset lines used by this controller. +- reset-names : reset signal name strings sorted in the same order as the resets property. Optional properties: - phys : phandle to UFS PHY node @@ -76,6 +79,8 @@ Example: clocks = <&core 0>, <&ref 0>, <&iface 0>; clock-names = "core_clk", "ref_clk", "iface_clk"; freq-table-hz = <100000000 200000000>, <0 0>, <0 0>; + resets = <clock_gcc GCC_UFS_BCR>; + reset-names = "core_reset"; phys = <&ufsphy1>; phy-names = "ufsphy"; rpm-level = <3>; diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 9c48b84660f5..03e6aafd5b94 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -60,6 +60,7 @@ show up in /proc/sys/kernel: - panic_on_stackoverflow - panic_on_unrecovered_nmi - panic_on_warn +- perf_event_paranoid - pid_max - powersave-nap [ PPC only ] - printk @@ -599,19 +600,6 @@ This file shows up if CONFIG_DEBUG_STACKOVERFLOW is enabled. ============================================================== -panic_on_unrecovered_nmi: - -The default Linux behaviour on an NMI of either memory or unknown is -to continue operation. For many environments such as scientific -computing it is preferable that the box is taken out and the error -dealt with than an uncorrected parity/ECC error get propagated. - -A small number of systems do generate NMI's for bizarre random reasons -such as power management so the default is off. That sysctl works like -the existing panic controls already in that directory. - -============================================================== - panic_on_warn: Calls panic() in the WARN() path when set to 1. This is useful to avoid @@ -649,6 +637,32 @@ allowed to execute. ============================================================== +panic_on_unrecovered_nmi: + +The default Linux behaviour on an NMI of either memory or unknown is +to continue operation. For many environments such as scientific +computing it is preferable that the box is taken out and the error +dealt with than an uncorrected parity/ECC error get propagated. + +A small number of systems do generate NMI's for bizarre random reasons +such as power management so the default is off. That sysctl works like +the existing panic controls already in that directory. + +============================================================== + +perf_event_paranoid: + +Controls use of the performance events system by unprivileged +users (without CAP_SYS_ADMIN). The default value is 3 if +CONFIG_SECURITY_PERF_EVENTS_RESTRICT is set, or 1 otherwise. + + -1: Allow use of (almost) all events by all users +>=0: Disallow raw tracepoint access by users without CAP_IOC_LOCK +>=1: Disallow CPU event access by users without CAP_SYS_ADMIN +>=2: Disallow kernel profiling by users without CAP_SYS_ADMIN +>=3: Disallow all event access by users without CAP_SYS_ADMIN + +============================================================== pid_max: diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index f0c13096cf37..3f1ffd497f2c 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -154,6 +154,7 @@ "usbin_i", "usbin_v"; + qcom,wipower-max-uw = <5000000>; dpdm-supply = <&qusb_phy0>; qcom,thermal-mitigation diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index e0d84e423e88..4b1b9796ebe6 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -1246,6 +1246,9 @@ "HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1", "MAX"; + resets = <&clock_gcc UFS_BCR>; + reset-names = "core_reset"; + /* PM QoS */ qcom,pm-qos-cpu-groups = <0x03 0x0C>; qcom,pm-qos-cpu-group-latency-us = <70 70>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi index ec69d7ac895d..9b50bb96750a 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi @@ -231,6 +231,9 @@ clocks = <&clock_gcc clk_div_clk1>; qcom,node_has_rpm_clock; #clock-cells = <1>; + pinctrl-names = "sleep", "active"; + pinctrl-0 = <&spkr_i2s_clk_sleep>; + pinctrl-1 = <&spkr_i2s_clk_active>; }; clock_audio_lnbb: audio_ext_clk_lnbb { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi index 617e7ddd9730..1267e578f9b4 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi @@ -15,7 +15,7 @@ pil_gpu: qcom,kgsl-hyp { compatible = "qcom,pil-tz-generic"; qcom,pas-id = <13>; - qcom,firmware-name = "a530_zap"; + qcom,firmware-name = "a540_zap"; }; msm_bus: qcom,kgsl-busmon{ diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi index c0f465b0eba5..f4f47bc461fc 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi @@ -2715,5 +2715,33 @@ }; }; }; + + spkr_i2s_clk_pin { + spkr_i2s_clk_sleep: spkr_i2s_clk_sleep { + mux { + pins = "gpio69"; + function = "spkr_i2s"; + }; + + config { + pins = "gpio69"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* PULL DOWN */ + }; + }; + + spkr_i2s_clk_active: spkr_i2s_clk_active { + mux { + pins = "gpio69"; + function = "spkr_i2s"; + }; + + config { + pins = "gpio69"; + drive-strength = <8>; /* 8 mA */ + bias-disable; /* NO PULL */ + }; + }; + }; }; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi new file mode 100644 index 000000000000..99d80a3b3848 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi @@ -0,0 +1,111 @@ +/* + * 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. + */ + +&soc { + qcom,csiphy@ca34000 { + cell-index = <0>; + compatible = "qcom,csiphy-v5.01", "qcom,csiphy"; + reg = <0xca34000 0x1000>; + reg-names = "csiphy"; + interrupts = <0 78 0>; + interrupt-names = "csiphy"; + clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + <&clock_mmss clk_mmss_mnoc_ahb_clk>, + <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, + <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, + <&clock_mmss clk_mmss_camss_ahb_clk>, + <&clock_mmss clk_mmss_camss_top_ahb_clk>, + <&clock_mmss clk_csi0_clk_src>, + <&clock_mmss clk_mmss_camss_csi0_clk>, + <&clock_mmss clk_mmss_camss_cphy_csid0_clk>, + <&clock_mmss clk_csi0phytimer_clk_src>, + <&clock_mmss clk_mmss_camss_csi0phytimer_clk>, + <&clock_mmss clk_mmss_camss_ispif_ahb_clk>, + <&clock_mmss clk_csiphy_clk_src>, + <&clock_mmss clk_mmss_camss_csiphy0_clk>; + clock-names = "mnoc_maxi", "mnoc_ahb", + "bmic_smmu_ahb", "bmic_smmu_axi", + "camss_ahb_clk", "camss_top_ahb_clk", + "csi_src_clk", "csi_clk", "cphy_csid_clk", + "csiphy_timer_src_clk", "csiphy_timer_clk", + "camss_ispif_ahb_clk", "csiphy_clk_src", "csiphy_clk"; + qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 269333333 0 + 0 256000000 0>; + status = "ok"; + }; + + qcom,csiphy@ca35000 { + cell-index = <1>; + compatible = "qcom,csiphy-v5.01", "qcom,csiphy"; + reg = <0xca35000 0x1000>; + reg-names = "csiphy"; + interrupts = <0 79 0>; + interrupt-names = "csiphy"; + clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + <&clock_mmss clk_mmss_mnoc_ahb_clk>, + <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, + <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, + <&clock_mmss clk_mmss_camss_ahb_clk>, + <&clock_mmss clk_mmss_camss_top_ahb_clk>, + <&clock_mmss clk_csi1_clk_src>, + <&clock_mmss clk_mmss_camss_csi1_clk>, + <&clock_mmss clk_mmss_camss_cphy_csid1_clk>, + <&clock_mmss clk_csi1phytimer_clk_src>, + <&clock_mmss clk_mmss_camss_csi1phytimer_clk>, + <&clock_mmss clk_mmss_camss_ispif_ahb_clk>, + <&clock_mmss clk_csiphy_clk_src>, + <&clock_mmss clk_mmss_camss_csiphy1_clk>; + clock-names = "mnoc_maxi", "mnoc_ahb", + "bmic_smmu_ahb", "bmic_smmu_axi", + "camss_ahb_clk", "camss_top_ahb_clk", + "csi_src_clk", "csi_clk", "cphy_csid_clk", + "csiphy_timer_src_clk", "csiphy_timer_clk", + "camss_ispif_ahb_clk", "csiphy_clk_src", "csiphy_clk"; + qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 269333333 0 + 0 256000000 0>; + status = "ok"; + }; + + qcom,csiphy@ca36000 { + cell-index = <2>; + compatible = "qcom,csiphy-v5.01", "qcom,csiphy"; + reg = <0xca36000 0x1000>; + reg-names = "csiphy"; + interrupts = <0 80 0>; + interrupt-names = "csiphy"; + clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + <&clock_mmss clk_mmss_mnoc_ahb_clk>, + <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, + <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, + <&clock_mmss clk_mmss_camss_ahb_clk>, + <&clock_mmss clk_mmss_camss_top_ahb_clk>, + <&clock_mmss clk_csi2_clk_src>, + <&clock_mmss clk_mmss_camss_csi2_clk>, + <&clock_mmss clk_mmss_camss_cphy_csid2_clk>, + <&clock_mmss clk_csi2phytimer_clk_src>, + <&clock_mmss clk_mmss_camss_csi2phytimer_clk>, + <&clock_mmss clk_mmss_camss_ispif_ahb_clk>, + <&clock_mmss clk_csiphy_clk_src>, + <&clock_mmss clk_mmss_camss_csiphy2_clk>; + clock-names = "mnoc_maxi", "mnoc_ahb", + "bmic_smmu_ahb", "bmic_smmu_axi", + "camss_ahb_clk", "camss_top_ahb_clk", + "csi_src_clk", "csi_clk", "cphy_csid_clk", + "csiphy_timer_src_clk", "csiphy_timer_clk", + "camss_ispif_ahb_clk", "csiphy_clk_src", "csiphy_clk"; + qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 269333333 0 + 0 256000000 0>; + status = "ok"; + }; +}; + diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi index 48a23b44b5b2..db50038b297e 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi @@ -17,6 +17,7 @@ */ #include "msmcobalt.dtsi" +#include "msmcobalt-v2-camera.dtsi" / { model = "Qualcomm Technologies, Inc. MSMCOBALT v2"; @@ -746,6 +747,11 @@ }; }; +&spss_utils { + qcom,spss-test-firmware-name = "spss2t"; /* 8 chars max */ + qcom,spss-prod-firmware-name = "spss2p"; /* 8 chars max */ +}; + &ufs1 { clock-names = "core_clk", diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index cd01d5158fc4..a16894dd4765 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -542,12 +542,13 @@ }; qcom,cpu-bwmon { - compatible = "qcom,bimc-bwmon3"; + compatible = "qcom,bimc-bwmon4"; reg = <0x01008000 0x300>, <0x01001000 0x200>; reg-names = "base", "global_base"; interrupts = <0 183 4>; qcom,mport = <0>; qcom,target-dev = <&cpubw>; + qcom,hw-timer-hz = <19200000>; }; mincpubw: qcom,mincpubw { @@ -1635,6 +1636,16 @@ status = "ok"; }; + spss_utils: qcom,spss_utils { + compatible = "qcom,spss-utils"; + /* spss test fuse physical address */ + qcom,spss-fuse-addr = <0x007841c4 0x4>; + qcom,spss-fuse-bit = <27>; + qcom,spss-test-firmware-name = "spss"; /* default name */ + qcom,spss-prod-firmware-name = "spss1p"; /* 8 chars max */ + status = "ok"; + }; + sdhc_2: sdhci@c0a4900 { compatible = "qcom,sdhci-msm"; reg = <0xc0a4900 0x314>, <0xc0a4000 0x800>; @@ -1791,6 +1802,9 @@ qcom,pm-qos-cpu-group-latency-us = <70 70>; qcom,pm-qos-default-cpu = <0>; + resets = <&clock_gcc UFS_BCR>; + reset-names = "core_reset"; + status = "disabled"; }; @@ -2627,6 +2641,7 @@ qcom,pet-time = <10000>; qcom,ipi-ping; qcom,wakeup-enable; + qcom,scandump-size = <0x40000>; }; qcom,spss@1d00000 { diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi index fb97074f35a3..246a6cf5371e 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi @@ -554,10 +554,6 @@ }; &gdsc_ufs { - clock-names = "bus_clk", "ice_clk", "unipro_clk"; - clocks = <&clock_gcc GCC_UFS_AXI_CLK>, - <&clock_gcc GCC_UFS_ICE_CORE_CLK>, - <&clock_gcc GCC_UFS_UNIPRO_CORE_CLK>; status = "ok"; }; @@ -573,50 +569,30 @@ status = "ok"; }; -&gdsc_hlos1_vote_lpass_core { - status = "ok"; -}; - &gdsc_venus { - clock-names = "bus_clk", "core_clk"; - clocks = <&clock_mmss MMSS_VIDEO_AXI_CLK>, - <&clock_mmss MMSS_VIDEO_CORE_CLK>; status = "ok"; }; &gdsc_venus_core0 { - clock-names = "core0_clk"; - clocks = <&clock_mmss MMSS_VIDEO_SUBCORE0_CLK>; qcom,support-hw-trigger; status = "ok"; }; &gdsc_camss_top { - clock-names = "bus_clk", "vfe_axi"; - clocks = <&clock_mmss MMSS_CAMSS_CPP_AXI_CLK>, - <&clock_mmss MMSS_CAMSS_VFE_VBIF_AXI_CLK>; status = "ok"; }; &gdsc_vfe0 { - clock-names = "core0_clk" , "core0_stream_clk"; - clocks = <&clock_mmss MMSS_CAMSS_VFE0_CLK>, - <&clock_mmss MMSS_CAMSS_VFE0_STREAM_CLK>; parent-supply = <&gdsc_camss_top>; status = "ok"; }; &gdsc_vfe1 { - clock-names = "core1_clk" , "core1_stream_clk"; - clocks = <&clock_mmss MMSS_CAMSS_VFE1_CLK>, - <&clock_mmss MMSS_CAMSS_VFE1_STREAM_CLK>; parent-supply = <&gdsc_camss_top>; status = "ok"; }; &gdsc_cpp { - clock-names = "core_clk"; - clocks = <&clock_mmss MMSS_CAMSS_CPP_CLK>; parent-supply = <&gdsc_camss_top>; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi index 7b7501dceff3..17198e462024 100644 --- a/arch/arm/boot/dts/qcom/msmtriton.dtsi +++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi @@ -167,6 +167,11 @@ clock-frequency = <19200000>; }; + qcom,sps { + compatible = "qcom,msm_sps_4k"; + qcom,pipe-attr-ee; + }; + uartblsp1dm1: serial@0c170000 { compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; reg = <0xc170000 0x1000>; diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 5d76e41d4fed..f552ecb7ae54 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -317,6 +317,7 @@ CONFIG_MSM_BCL_PERIPHERAL_CTL=y CONFIG_BATTERY_BCL=y CONFIG_QPNP_SMB2=y CONFIG_SMB138X_CHARGER=y +CONFIG_QPNP_QNOVO=y CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y CONFIG_LIMITS_MONITOR=y CONFIG_LIMITS_LITE_HW=y @@ -484,7 +485,6 @@ CONFIG_ARM_SMMU=y CONFIG_IOMMU_DEBUG=y CONFIG_IOMMU_DEBUG_TRACKING=y CONFIG_IOMMU_TESTS=y -CONFIG_QCOM_COMMON_LOG=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y CONFIG_MSM_SMD=y @@ -494,6 +494,7 @@ CONFIG_MSM_GLINK_SMD_XPRT=y CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y CONFIG_MSM_GLINK_SPI_XPRT=y CONFIG_MSM_SPCOM=y +CONFIG_MSM_SPSS_UTILS=y CONFIG_MSM_SMEM_LOGGING=y CONFIG_MSM_SMP2P=y CONFIG_MSM_SMP2P_TEST=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 367822dd0a94..456979c31d84 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -320,6 +320,7 @@ CONFIG_MSM_BCL_PERIPHERAL_CTL=y CONFIG_BATTERY_BCL=y CONFIG_QPNP_SMB2=y CONFIG_SMB138X_CHARGER=y +CONFIG_QPNP_QNOVO=y CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y CONFIG_LIMITS_MONITOR=y CONFIG_LIMITS_LITE_HW=y @@ -506,6 +507,7 @@ CONFIG_MSM_GLINK_SMD_XPRT=y CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y CONFIG_MSM_GLINK_SPI_XPRT=y CONFIG_MSM_SPCOM=y +CONFIG_MSM_SPSS_UTILS=y CONFIG_MSM_SMEM_LOGGING=y CONFIG_MSM_SMP2P=y CONFIG_MSM_SMP2P_TEST=y diff --git a/block/blk-merge.c b/block/blk-merge.c index 6e189a49dca6..2b27747b46d3 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -6,7 +6,8 @@ #include <linux/bio.h> #include <linux/blkdev.h> #include <linux/scatterlist.h> -#include <linux/security.h> +#include <linux/pfk.h> +#include <linux/pft.h> #include "blk.h" @@ -729,6 +730,12 @@ static void blk_account_io_merge(struct request *req) } } +static bool crypto_not_mergeable(const struct bio *bio, const struct bio *nxt) +{ + return (!pft_allow_merge_bio(bio, nxt) || + !pfk_allow_merge_bio(bio, nxt)); +} + /* * Has to be called with the request spinlock acquired */ @@ -756,6 +763,9 @@ static int attempt_merge(struct request_queue *q, struct request *req, !blk_write_same_mergeable(req->bio, next->bio)) return 0; + if (crypto_not_mergeable(req->bio, next->bio)) + return 0; + /* * If we are allowed to merge, then append bio list * from next to rq and release next. merge_requests_fn @@ -860,11 +870,9 @@ bool blk_rq_merge_ok(struct request *rq, struct bio *bio) !blk_write_same_mergeable(rq->bio, bio)) return false; - /* Don't merge bios of files with different encryption */ - if (!security_allow_merge_bio(rq->bio, bio)) + if (crypto_not_mergeable(rq->bio, bio)) return false; - return true; } diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c index 9b42e5ae129a..3562de7fc967 100644 --- a/drivers/crypto/msm/qce50.c +++ b/drivers/crypto/msm/qce50.c @@ -94,7 +94,6 @@ enum qce_owner { struct dummy_request { struct qce_sha_req sreq; - uint8_t *in_buf; struct scatterlist sg; struct ahash_request areq; }; @@ -154,6 +153,7 @@ struct qce_device { atomic_t bunch_cmd_seq; atomic_t last_intr_seq; bool cadence_flag; + uint8_t *dummyreq_in_buf; }; static void print_notify_debug(struct sps_event_notify *notify); @@ -4351,8 +4351,6 @@ static int qce_setup_ce_sps_data(struct qce_device *pce_dev) (uintptr_t)vaddr; vaddr += pce_dev->ce_bam_info.ce_burst_size * 2; } - pce_dev->dummyreq.in_buf = (uint8_t *)vaddr; - vaddr += DUMMY_REQ_DATA_LEN; if ((vaddr - pce_dev->coh_vmem) > pce_dev->memsize || iovec_memsize < 0) panic("qce50: Not enough coherent memory. Allocate %x , need %lx\n", @@ -5887,8 +5885,8 @@ static int setup_dummy_req(struct qce_device *pce_dev) "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopqopqrpqrs"; int len = DUMMY_REQ_DATA_LEN; - memcpy(pce_dev->dummyreq.in_buf, input, len); - sg_set_buf(&pce_dev->dummyreq.sg, pce_dev->dummyreq.in_buf, len); + memcpy(pce_dev->dummyreq_in_buf, input, len); + sg_set_buf(&pce_dev->dummyreq.sg, pce_dev->dummyreq_in_buf, len); sg_mark_end(&pce_dev->dummyreq.sg); pce_dev->dummyreq.sreq.alg = QCE_HASH_SHA1; @@ -5957,6 +5955,10 @@ void *qce_open(struct platform_device *pdev, int *rc) if (pce_dev->iovec_vmem == NULL) goto err_mem; + pce_dev->dummyreq_in_buf = kzalloc(DUMMY_REQ_DATA_LEN, GFP_KERNEL); + if (pce_dev->dummyreq_in_buf == NULL) + goto err_mem; + *rc = __qce_init_clk(pce_dev); if (*rc) goto err_mem; @@ -5996,6 +5998,7 @@ err_enable_clk: __qce_deinit_clk(pce_dev); err_mem: + kfree(pce_dev->dummyreq_in_buf); kfree(pce_dev->iovec_vmem); if (pce_dev->coh_vmem) dma_free_coherent(pce_dev->pdev, pce_dev->memsize, @@ -6027,6 +6030,7 @@ int qce_close(void *handle) if (pce_dev->coh_vmem) dma_free_coherent(pce_dev->pdev, pce_dev->memsize, pce_dev->coh_vmem, pce_dev->coh_pmem); + kfree(pce_dev->dummyreq_in_buf); kfree(pce_dev->iovec_vmem); qce_disable_clk(pce_dev); diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c index 2b0bacdb5f6a..707a244e62e9 100644 --- a/drivers/devfreq/bimc-bwmon.c +++ b/drivers/devfreq/bimc-bwmon.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-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 @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/delay.h> +#include <linux/bitops.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/interrupt.h> @@ -40,10 +41,24 @@ #define MON_MASK(m) ((m)->base + 0x298) #define MON_MATCH(m) ((m)->base + 0x29C) +#define MON2_EN(m) ((m)->base + 0x2A0) +#define MON2_CLEAR(m) ((m)->base + 0x2A4) +#define MON2_SW(m) ((m)->base + 0x2A8) +#define MON2_THRES_HI(m) ((m)->base + 0x2AC) +#define MON2_THRES_MED(m) ((m)->base + 0x2B0) +#define MON2_THRES_LO(m) ((m)->base + 0x2B4) +#define MON2_ZONE_ACTIONS(m) ((m)->base + 0x2B8) +#define MON2_ZONE_CNT_THRES(m) ((m)->base + 0x2BC) +#define MON2_BYTE_CNT(m) ((m)->base + 0x2D0) +#define MON2_WIN_TIMER(m) ((m)->base + 0x2D4) +#define MON2_ZONE_CNT(m) ((m)->base + 0x2D8) +#define MON2_ZONE_MAX(m, zone) ((m)->base + 0x2E0 + 0x4 * zone) + struct bwmon_spec { bool wrap_on_thres; bool overflow; bool throt_adj; + bool hw_sampling; }; struct bwmon { @@ -54,24 +69,37 @@ struct bwmon { const struct bwmon_spec *spec; struct device *dev; struct bw_hwmon hw; + u32 hw_timer_hz; u32 throttle_adj; + u32 sample_size_ms; + u32 intr_status; }; #define to_bwmon(ptr) container_of(ptr, struct bwmon, hw) +#define has_hw_sampling(m) (m->spec->hw_sampling) #define ENABLE_MASK BIT(0) #define THROTTLE_MASK 0x1F #define THROTTLE_SHIFT 16 +#define INT_ENABLE_V1 0x1 +#define INT_STATUS_MASK 0x03 +#define INT_STATUS_MASK_HWS 0xF0 static DEFINE_SPINLOCK(glb_lock); static void mon_enable(struct bwmon *m) { - writel_relaxed((ENABLE_MASK | m->throttle_adj), MON_EN(m)); + if (has_hw_sampling(m)) + writel_relaxed((ENABLE_MASK | m->throttle_adj), MON2_EN(m)); + else + writel_relaxed((ENABLE_MASK | m->throttle_adj), MON_EN(m)); } static void mon_disable(struct bwmon *m) { - writel_relaxed(m->throttle_adj, MON_EN(m)); + if (has_hw_sampling(m)) + writel_relaxed(m->throttle_adj, MON2_EN(m)); + else + writel_relaxed(m->throttle_adj, MON_EN(m)); /* * mon_disable() and mon_irq_clear(), * If latter goes first and count happen to trigger irq, we would @@ -80,17 +108,46 @@ static void mon_disable(struct bwmon *m) mb(); } -static void mon_clear(struct bwmon *m) +#define MON_CLEAR_BIT 0x1 +#define MON_CLEAR_ALL_BIT 0x2 +static void mon_clear(struct bwmon *m, bool clear_all) { - writel_relaxed(0x1, MON_CLEAR(m)); + if (!has_hw_sampling(m)) { + writel_relaxed(MON_CLEAR_BIT, MON_CLEAR(m)); + goto out; + } + + if (clear_all) + writel_relaxed(MON_CLEAR_ALL_BIT, MON2_CLEAR(m)); + else + writel_relaxed(MON_CLEAR_BIT, MON2_CLEAR(m)); + /* * The counter clear and IRQ clear bits are not in the same 4KB * region. So, we need to make sure the counter clear is completed * before we try to clear the IRQ or do any other counter operations. */ +out: mb(); } +#define SAMPLE_WIN_LIM 0xFFFFF +static void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms) +{ + u32 rate; + + if (unlikely(sample_ms != m->sample_size_ms)) { + rate = mult_frac(sample_ms, m->hw_timer_hz, MSEC_PER_SEC); + m->sample_size_ms = sample_ms; + if (unlikely(rate > SAMPLE_WIN_LIM)) { + rate = SAMPLE_WIN_LIM; + pr_warn("Sample window %u larger than hw limit: %u\n", + rate, SAMPLE_WIN_LIM); + } + writel_relaxed(rate, MON2_SW(m)); + } +} + static void mon_irq_enable(struct bwmon *m) { u32 val; @@ -99,11 +156,11 @@ static void mon_irq_enable(struct bwmon *m) val = readl_relaxed(GLB_INT_EN(m)); val |= 1 << m->mport; writel_relaxed(val, GLB_INT_EN(m)); - spin_unlock(&glb_lock); val = readl_relaxed(MON_INT_EN(m)); - val |= 0x1; + val |= has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_ENABLE_V1; writel_relaxed(val, MON_INT_EN(m)); + spin_unlock(&glb_lock); /* * make Sure irq enable complete for local and global * to avoid race with other monitor calls @@ -119,11 +176,11 @@ static void mon_irq_disable(struct bwmon *m) val = readl_relaxed(GLB_INT_EN(m)); val &= ~(1 << m->mport); writel_relaxed(val, GLB_INT_EN(m)); - spin_unlock(&glb_lock); val = readl_relaxed(MON_INT_EN(m)); - val &= ~0x1; + val &= has_hw_sampling(m) ? ~INT_STATUS_MASK_HWS : ~INT_ENABLE_V1; writel_relaxed(val, MON_INT_EN(m)); + spin_unlock(&glb_lock); /* * make Sure irq disable complete for local and global * to avoid race with other monitor calls @@ -140,12 +197,18 @@ static unsigned int mon_irq_status(struct bwmon *m) dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval, readl_relaxed(GLB_INT_STATUS(m))); + mval &= has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_STATUS_MASK; + return mval; } static void mon_irq_clear(struct bwmon *m) { - writel_relaxed(0x3, MON_INT_CLR(m)); + u32 intclr; + + intclr = has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_STATUS_MASK; + + writel_relaxed(intclr, MON_INT_CLR(m)); mb(); writel_relaxed(1 << m->mport, GLB_INT_CLR(m)); mb(); @@ -171,6 +234,90 @@ static u32 mon_get_throttle_adj(struct bw_hwmon *hw) return m->throttle_adj >> THROTTLE_SHIFT; } +#define ZONE1_SHIFT 8 +#define ZONE2_SHIFT 16 +#define ZONE3_SHIFT 24 +#define ZONE0_ACTION 0x01 /* Increment zone 0 count */ +#define ZONE1_ACTION 0x09 /* Increment zone 1 & clear lower zones */ +#define ZONE2_ACTION 0x25 /* Increment zone 2 & clear lower zones */ +#define ZONE3_ACTION 0x95 /* Increment zone 3 & clear lower zones */ +static u32 calc_zone_actions(void) +{ + u32 zone_actions; + + zone_actions = ZONE0_ACTION; + zone_actions |= ZONE1_ACTION << ZONE1_SHIFT; + zone_actions |= ZONE2_ACTION << ZONE2_SHIFT; + zone_actions |= ZONE3_ACTION << ZONE3_SHIFT; + + return zone_actions; +} + +#define ZONE_CNT_LIM 0xFFU +#define UP_CNT_1 1 +static u32 calc_zone_counts(struct bw_hwmon *hw) +{ + u32 zone_counts; + + zone_counts = ZONE_CNT_LIM; + zone_counts |= min(hw->down_cnt, ZONE_CNT_LIM) << ZONE1_SHIFT; + zone_counts |= ZONE_CNT_LIM << ZONE2_SHIFT; + zone_counts |= UP_CNT_1 << ZONE3_SHIFT; + + return zone_counts; +} + +static unsigned int mbps_to_mb(unsigned long mbps, unsigned int ms) +{ + mbps *= ms; + mbps = DIV_ROUND_UP(mbps, MSEC_PER_SEC); + return mbps; +} + +/* + * Define the 4 zones using HI, MED & LO thresholds: + * Zone 0: byte count < THRES_LO + * Zone 1: THRES_LO < byte count < THRES_MED + * Zone 2: THRES_MED < byte count < THRES_HI + * Zone 3: byte count > THRES_HI + */ +#define THRES_LIM 0x7FFU +static void set_zone_thres(struct bwmon *m, unsigned int sample_ms) +{ + struct bw_hwmon *hw = &(m->hw); + u32 hi, med, lo; + + hi = mbps_to_mb(hw->up_wake_mbps, sample_ms); + med = mbps_to_mb(hw->down_wake_mbps, sample_ms); + lo = 0; + + if (unlikely((hi > THRES_LIM) || (med > hi) || (lo > med))) { + pr_warn("Zone thres larger than hw limit: hi:%u med:%u lo:%u\n", + hi, med, lo); + hi = min(hi, THRES_LIM); + med = min(med, hi - 1); + lo = min(lo, med-1); + } + + writel_relaxed(hi, MON2_THRES_HI(m)); + writel_relaxed(med, MON2_THRES_MED(m)); + writel_relaxed(lo, MON2_THRES_LO(m)); + dev_dbg(m->dev, "Thres: hi:%u med:%u lo:%u\n", hi, med, lo); +} + +static void mon_set_zones(struct bwmon *m, unsigned int sample_ms) +{ + struct bw_hwmon *hw = &(m->hw); + u32 zone_cnt_thres = calc_zone_counts(hw); + + mon_set_hw_sampling_window(m, sample_ms); + set_zone_thres(m, sample_ms); + /* Set the zone count thresholds for interrupts */ + writel_relaxed(zone_cnt_thres, MON2_ZONE_CNT_THRES(m)); + + dev_dbg(m->dev, "Zone Count Thres: %0x\n", zone_cnt_thres); +} + static void mon_set_limit(struct bwmon *m, u32 count) { writel_relaxed(count, MON_THRES(m)); @@ -203,6 +350,41 @@ static unsigned long mon_get_count(struct bwmon *m) return count; } +static unsigned int get_zone(struct bwmon *m) +{ + u32 zone_counts; + u32 zone; + + zone = get_bitmask_order((m->intr_status & INT_STATUS_MASK_HWS) >> 4); + if (zone) { + zone--; + } else { + zone_counts = readl_relaxed(MON2_ZONE_CNT(m)); + if (zone_counts) { + zone = get_bitmask_order(zone_counts) - 1; + zone /= 8; + } + } + + m->intr_status = 0; + return zone; +} + +static unsigned long mon_get_zone_stats(struct bwmon *m) +{ + unsigned int zone; + unsigned long count = 0; + + zone = get_zone(m); + + count = readl_relaxed(MON2_ZONE_MAX(m, zone)); + count *= SZ_1M; + + dev_dbg(m->dev, "Zone%d Max byte count: %08lx\n", zone, count); + + return count; +} + /* ********** CPUBW specific code ********** */ /* Returns MBps of read/writes for the sampling window. */ @@ -222,8 +404,8 @@ static unsigned long get_bytes_and_clear(struct bw_hwmon *hw) unsigned long count; mon_disable(m); - count = mon_get_count(m); - mon_clear(m); + count = has_hw_sampling(m) ? mon_get_zone_stats(m) : mon_get_count(m); + mon_clear(m, false); mon_irq_clear(m); mon_enable(m); @@ -238,7 +420,7 @@ static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes) mon_disable(m); count = mon_get_count(m); - mon_clear(m); + mon_clear(m, false); mon_irq_clear(m); if (likely(!m->spec->wrap_on_thres)) @@ -252,11 +434,26 @@ static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes) return count; } +static unsigned long set_hw_events(struct bw_hwmon *hw, unsigned sample_ms) +{ + struct bwmon *m = to_bwmon(hw); + + mon_disable(m); + mon_clear(m, false); + mon_irq_clear(m); + + mon_set_zones(m, sample_ms); + mon_enable(m); + + return 0; +} + static irqreturn_t bwmon_intr_handler(int irq, void *dev) { struct bwmon *m = dev; - if (!mon_irq_status(m)) + m->intr_status = mon_irq_status(m); + if (!m->intr_status) return IRQ_NONE; if (bw_hwmon_sample_end(&m->hw) > 0) @@ -277,6 +474,7 @@ static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps) { struct bwmon *m = to_bwmon(hw); u32 limit; + u32 zone_actions = calc_zone_actions(); int ret; ret = request_threaded_irq(m->irq, bwmon_intr_handler, @@ -291,10 +489,16 @@ static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps) mon_disable(m); + mon_clear(m, true); limit = mbps_to_bytes(mbps, hw->df->profile->polling_ms, 0); - mon_set_limit(m, limit); + if (has_hw_sampling(m)) { + mon_set_zones(m, hw->df->profile->polling_ms); + /* Set the zone actions to increment appropriate counters */ + writel_relaxed(zone_actions, MON2_ZONE_ACTIONS(m)); + } else { + mon_set_limit(m, limit); + } - mon_clear(m); mon_irq_clear(m); mon_irq_enable(m); mon_enable(m); @@ -309,7 +513,7 @@ static void stop_bw_hwmon(struct bw_hwmon *hw) mon_irq_disable(m); free_irq(m->irq, m); mon_disable(m); - mon_clear(m); + mon_clear(m, true); mon_irq_clear(m); } @@ -330,7 +534,7 @@ static int resume_bw_hwmon(struct bw_hwmon *hw) struct bwmon *m = to_bwmon(hw); int ret; - mon_clear(m); + mon_clear(m, false); ret = request_threaded_irq(m->irq, bwmon_intr_handler, bwmon_intr_thread, IRQF_ONESHOT | IRQF_SHARED, @@ -350,15 +554,21 @@ static int resume_bw_hwmon(struct bw_hwmon *hw) /*************************************************************************/ static const struct bwmon_spec spec[] = { - { .wrap_on_thres = true, .overflow = false, .throt_adj = false}, - { .wrap_on_thres = false, .overflow = true, .throt_adj = false}, - { .wrap_on_thres = false, .overflow = true, .throt_adj = true}, + { .wrap_on_thres = true, .overflow = false, .throt_adj = false, + .hw_sampling = false}, + { .wrap_on_thres = false, .overflow = true, .throt_adj = false, + .hw_sampling = false}, + { .wrap_on_thres = false, .overflow = true, .throt_adj = true, + .hw_sampling = false}, + { .wrap_on_thres = false, .overflow = true, .throt_adj = true, + .hw_sampling = true}, }; static struct of_device_id match_table[] = { { .compatible = "qcom,bimc-bwmon", .data = &spec[0] }, { .compatible = "qcom,bimc-bwmon2", .data = &spec[1] }, { .compatible = "qcom,bimc-bwmon3", .data = &spec[2] }, + { .compatible = "qcom,bimc-bwmon4", .data = &spec[3] }, {} }; @@ -390,6 +600,16 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) } m->spec = id->data; + if (has_hw_sampling(m)) { + ret = of_property_read_u32(dev->of_node, + "qcom,hw-timer-hz", &data); + if (ret) { + dev_err(dev, "HW sampling rate not specified!\n"); + return ret; + } + m->hw_timer_hz = data; + } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); if (!res) { dev_err(dev, "base not found!\n"); @@ -426,7 +646,9 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) m->hw.suspend_hwmon = &suspend_bw_hwmon; m->hw.resume_hwmon = &resume_bw_hwmon; m->hw.get_bytes_and_clear = &get_bytes_and_clear; - m->hw.set_thres = &set_thres; + m->hw.set_thres = &set_thres; + if (has_hw_sampling(m)) + m->hw.set_hw_events = &set_hw_events; if (m->spec->throt_adj) { m->hw.set_throttle_adj = &mon_set_throttle_adj; m->hw.get_throttle_adj = &mon_get_throttle_adj; diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h index 778c76f52d0b..a3b25b3d8dd1 100644 --- a/drivers/gpu/msm/adreno-gpulist.h +++ b/drivers/gpu/msm/adreno-gpulist.h @@ -250,7 +250,7 @@ static const struct adreno_gpu_core adreno_gpulist[] = { ADRENO_GPMU | ADRENO_SPTP_PC, .pm4fw_name = "a530_pm4.fw", .pfpfw_name = "a530_pfp.fw", - .zap_name = "a530_zap", + .zap_name = "a540_zap", .gpudev = &adreno_a5xx_gpudev, .gmem_size = SZ_1M, .num_protected_regs = 0x20, diff --git a/drivers/gpu/msm/adreno_a5xx_preempt.c b/drivers/gpu/msm/adreno_a5xx_preempt.c index ffd7acbbe1f9..4baee4a5c0b1 100644 --- a/drivers/gpu/msm/adreno_a5xx_preempt.c +++ b/drivers/gpu/msm/adreno_a5xx_preempt.c @@ -311,6 +311,8 @@ void a5xx_preempt_callback(struct adreno_device *adreno_dev, int bit) adreno_dev->cur_rb->dispatch_q.expires); adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); + + a5xx_preemption_trigger(adreno_dev); } void a5xx_preemption_schedule(struct adreno_device *adreno_dev) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index f9eb080d903b..6e9abc99bcc4 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -4519,7 +4519,7 @@ int kgsl_device_platform_probe(struct kgsl_device *device) } device->events_wq = alloc_workqueue("kgsl-events", - WQ_UNBOUND | WQ_MEM_RECLAIM, 0); + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0); /* Initalize the snapshot engine */ kgsl_device_snapshot_init(device); @@ -4662,7 +4662,8 @@ static int __init kgsl_core_init(void) INIT_LIST_HEAD(&kgsl_driver.pagetable_list); - kgsl_driver.workqueue = create_singlethread_workqueue("kgsl-workqueue"); + kgsl_driver.workqueue = alloc_workqueue("kgsl-workqueue", + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0); kgsl_driver.mem_workqueue = alloc_workqueue("kgsl-mementry", WQ_UNBOUND | WQ_MEM_RECLAIM, 0); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index d48d8485f979..306465ededf9 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -95,6 +95,7 @@ #define TMC_ETR_BAM_PIPE_INDEX 0 #define TMC_ETR_BAM_NR_PIPES 2 +#define TMC_ETFETB_DUMP_MAGIC_V2 (0x42445953) #define TMC_REG_DUMP_MAGIC_V2 (0x42445953) #define TMC_REG_DUMP_VER (1) @@ -178,6 +179,7 @@ struct tmc_drvdata { spinlock_t spinlock; int read_count; bool reading; + bool aborting; char *buf; dma_addr_t paddr; void __iomem *vaddr; @@ -883,11 +885,15 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) for (i = 0; i < memwords; i++) { read_data = readl_relaxed(drvdata->base + TMC_RRD); if (read_data == 0xFFFFFFFF) - return; + goto out; memcpy(bufp, &read_data, 4); bufp += 4; } } + +out: + if (drvdata->aborting) + drvdata->buf_data.magic = TMC_ETFETB_DUMP_MAGIC_V2; } static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) @@ -1070,6 +1076,8 @@ static void tmc_abort(struct coresight_device *csdev) unsigned long flags; enum tmc_mode mode; + drvdata->aborting = true; + spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) goto out0; @@ -1728,7 +1736,7 @@ static void __tmc_reg_dump(struct tmc_drvdata *drvdata) if (!drvdata->reg_buf) return; - else if (!drvdata->dump_reg) + else if (!drvdata->aborting && !drvdata->dump_reg) return; drvdata->reg_data.version = TMC_REG_DUMP_VER; diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index ae2df4f7ff0d..a0fcad198f62 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -11,6 +11,8 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) "RRADC: %s: " fmt, __func__ + #include <linux/iio/iio.h> #include <linux/kernel.h> #include <linux/math64.h> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b9956170b909..49df5e0afbfb 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1217,4 +1217,15 @@ config TOUCHSCREEN_IT7260_I2C To compile this driver as a module, choose M here: the module will be called it7258_ts_i2c. +config TOUCHSCREEN_GT9XX + bool "Goodix touchpanel GT9xx series" + depends on I2C + help + Say Y here if you have a Goodix GT9xx touchscreen. + Gt9xx controllers are multi touch controllers which can + report 5 touches at a time. + + If unsure, say N. + +source "drivers/input/touchscreen/gt9xx/Kconfig" endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f448df5b234c..06953a69123f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -99,3 +99,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o obj-$(CONFIG_TOUCHSCREEN_MSTAR21XX) += msg21xx_ts.o +obj-$(CONFIG_TOUCHSCREEN_GT9XX) += gt9xx/ diff --git a/drivers/input/touchscreen/gt9xx/Kconfig b/drivers/input/touchscreen/gt9xx/Kconfig new file mode 100644 index 000000000000..2e1b5ba567a0 --- /dev/null +++ b/drivers/input/touchscreen/gt9xx/Kconfig @@ -0,0 +1,51 @@ +# +# Goodix GT9xx Touchscreen driver +# + +config GT9XX_TOUCHPANEL_DRIVER + tristate "Goodix GT9xx touchpanel driver" + depends on TOUCHSCREEN_GT9XX + default n + help + This is the main file for touchpanel driver for Goodix GT9xx + touchscreens. + + Say Y here if you have a Goodix GT9xx touchscreen connected + to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called gt9xx. + +config GT9XX_TOUCHPANEL_UPDATE + tristate "Goodix GT9xx touchpanel auto update support" + depends on GT9XX_TOUCHPANEL_DRIVER + default n + help + This enables support for firmware update for Goodix GT9xx + touchscreens. + + Say Y here if you have a Goodix GT9xx touchscreen connected + to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called gt9xx_update. + +config GT9XX_TOUCHPANEL_DEBUG + tristate "Goodix GT9xx Tools for debuging" + depends on GT9XX_TOUCHPANEL_DRIVER + default n + help + This is application debug interface support for Goodix GT9xx + touchscreens. + + Say Y here if you want to have a Android app debug interface + to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called gt9xx_tool. diff --git a/drivers/input/touchscreen/gt9xx/Makefile b/drivers/input/touchscreen/gt9xx/Makefile new file mode 100644 index 000000000000..482d869a2d37 --- /dev/null +++ b/drivers/input/touchscreen/gt9xx/Makefile @@ -0,0 +1,8 @@ +#gt915 touchpanel driver + + +obj-$(CONFIG_GT9XX_TOUCHPANEL_DRIVER) += gt9xx.o +#gt915 update file +obj-$(CONFIG_GT9XX_TOUCHPANEL_UPDATE) += gt9xx_update.o +#debug tool +obj-$(CONFIG_GT9XX_TOUCHPANEL_DEBUG) += goodix_tool.o diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c index bef080671ab0..c67c4c8f1207 100644 --- a/drivers/input/touchscreen/gt9xx/goodix_tool.c +++ b/drivers/input/touchscreen/gt9xx/goodix_tool.c @@ -22,6 +22,7 @@ */ #include "gt9xx.h" +#include <linux/mutex.h> #define DATA_LENGTH_UINT 512 #define CMD_HEAD_LENGTH (sizeof(st_cmd_head) - sizeof(u8 *)) @@ -53,6 +54,8 @@ static struct i2c_client *gt_client; static struct proc_dir_entry *goodix_proc_entry; +static struct mutex lock; + static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data); static s32 goodix_tool_read(char *page, char **start, off_t off, int count, @@ -88,18 +91,21 @@ static void tool_set_proc_name(char *procname) static s32 tool_i2c_read_no_extra(u8 *buf, u16 len) { s32 ret = -1; - s32 i = 0; - struct i2c_msg msgs[2]; - - msgs[0].flags = !I2C_M_RD; - msgs[0].addr = gt_client->addr; - msgs[0].len = cmd_head.addr_len; - msgs[0].buf = &buf[0]; - - msgs[1].flags = I2C_M_RD; - msgs[1].addr = gt_client->addr; - msgs[1].len = len; - msgs[1].buf = &buf[GTP_ADDR_LENGTH]; + u8 i = 0; + struct i2c_msg msgs[2] = { + { + .flags = !I2C_M_RD, + .addr = gt_client->addr, + .len = cmd_head.addr_len, + .buf = &buf[0], + }, + { + .flags = I2C_M_RD, + .addr = gt_client->addr, + .len = len, + .buf = &buf[GTP_ADDR_LENGTH], + }, + }; for (i = 0; i < cmd_head.retry; i++) { ret = i2c_transfer(gt_client->adapter, msgs, 2); @@ -107,25 +113,35 @@ static s32 tool_i2c_read_no_extra(u8 *buf, u16 len) break; } + if (i == cmd_head.retry) { + dev_err(&client->dev, "I2C read retry limit over.\n"); + ret = -EIO; + } + return ret; } static s32 tool_i2c_write_no_extra(u8 *buf, u16 len) { s32 ret = -1; - s32 i = 0; - struct i2c_msg msg; - - msg.flags = !I2C_M_RD; - msg.addr = gt_client->addr; - msg.len = len; - msg.buf = buf; + u8 i = 0; + struct i2c_msg msg = { + .flags = !I2C_M_RD, + .addr = gt_client->addr, + .len = len, + .buf = buf, + }; for (i = 0; i < cmd_head.retry; i++) { ret = i2c_transfer(gt_client->adapter, &msg, 1); if (ret > 0) break; - } + } + + if (i == cmd_head.retry) { + dev_err(&client->dev, "I2C write retry limit over.\n"); + ret = -EIO; + } return ret; } @@ -181,7 +197,7 @@ static void unregister_i2c_func(void) s32 init_wr_node(struct i2c_client *client) { - s32 i; + u8 i; gt_client = client; memset(&cmd_head, 0, sizeof(cmd_head)); @@ -189,14 +205,15 @@ s32 init_wr_node(struct i2c_client *client) i = 5; while ((!cmd_head.data) && i) { - cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL); - if (cmd_head.data != NULL) + cmd_head.data = devm_kzalloc(&client->dev, + i * DATA_LENGTH_UINT, GFP_KERNEL); + if (cmd_head.data) break; i--; } if (i) { - DATA_LENGTH = i * DATA_LENGTH_UINT + GTP_ADDR_LENGTH; - GTP_INFO("Applied memory size:%d.", DATA_LENGTH); + DATA_LENGTH = i * DATA_LENGTH_UINT; + dev_dbg(&client->dev, "Applied memory size:%d.", DATA_LENGTH); } else { GTP_ERROR("Apply for memory failed."); return FAIL; @@ -207,8 +224,9 @@ s32 init_wr_node(struct i2c_client *client) register_i2c_func(); + mutex_init(&lock); tool_set_proc_name(procname); - goodix_proc_entry = create_proc_entry(procname, 0666, NULL); + goodix_proc_entry = create_proc_entry(procname, 0660, NULL); if (goodix_proc_entry == NULL) { GTP_ERROR("Couldn't create proc entry!"); return FAIL; @@ -222,7 +240,6 @@ s32 init_wr_node(struct i2c_client *client) void uninit_wr_node(void) { - kfree(cmd_head.data); cmd_head.data = NULL; unregister_i2c_func(); remove_proc_entry(procname, NULL); @@ -330,9 +347,13 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff, GTP_DEBUG_FUNC(); GTP_DEBUG_ARRAY((u8 *)buff, len); + mutex_lock(&lock); ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH); - if (ret) + if (ret) { GTP_ERROR("copy_from_user failed."); + ret = -EACCES; + goto exit; + } GTP_DEBUG("wr :0x%02x.", cmd_head.wr); GTP_DEBUG("flag:0x%02x.", cmd_head.flag); @@ -350,6 +371,19 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff, GTP_DEBUG("len:%d.", (s32)len); GTP_DEBUG("buf[20]:0x%02x.", buff[CMD_HEAD_LENGTH]); + if (cmd_head.data_len > (DATA_LENGTH - GTP_ADDR_LENGTH)) { + pr_err("data len %d > data buff %d, rejected!\n", + cmd_head.data_len, (DATA_LENGTH - GTP_ADDR_LENGTH)); + ret = -EINVAL; + goto exit; + } + if (cmd_head.addr_len > GTP_ADDR_LENGTH) { + pr_err(" addr len %d > data buff %d, rejected!\n", + cmd_head.addr_len, GTP_ADDR_LENGTH); + ret = -EINVAL; + goto exit; + } + if (cmd_head.wr == 1) { /* copy_from_user(&cmd_head.data[cmd_head.addr_len], * &buff[CMD_HEAD_LENGTH], cmd_head.data_len); @@ -370,7 +404,8 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff, if (cmd_head.flag == 1) { if (comfirm() == FAIL) { GTP_ERROR("[WRITE]Comfirm fail!"); - return FAIL; + ret = -EINVAL; + goto exit; } } else if (cmd_head.flag == 2) { /* Need interrupt! */ @@ -379,7 +414,8 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff, &cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], cmd_head.data_len + cmd_head.addr_len) <= 0) { GTP_ERROR("[WRITE]Write data failed!"); - return FAIL; + ret = -EIO; + goto exit; } GTP_DEBUG_ARRAY( @@ -388,7 +424,8 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff, if (cmd_head.delay) msleep(cmd_head.delay); - return cmd_head.data_len + CMD_HEAD_LENGTH; + ret = cmd_head.data_len + CMD_HEAD_LENGTH; + goto exit; } else if (cmd_head.wr == 3) { /* Write ic type */ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], @@ -396,30 +433,40 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff, if (ret) GTP_ERROR("copy_from_user failed."); + if (cmd_head.data_len > sizeof(IC_TYPE)) { + pr_err("<<-GTP->> data len %d > data buff %d, rejected!\n", + cmd_head.data_len, sizeof(IC_TYPE)); + ret = -EINVAL; + goto exit; + } memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len); register_i2c_func(); - return cmd_head.data_len + CMD_HEAD_LENGTH; - } else if (cmd_head.wr == 3) { + ret = cmd_head.data_len + CMD_HEAD_LENGTH; + goto exit; + } else if (cmd_head.wr == 5) { /* memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len); */ - return cmd_head.data_len + CMD_HEAD_LENGTH; + ret = cmd_head.data_len + CMD_HEAD_LENGTH; + goto exit; } else if (cmd_head.wr == 7) { /* disable irq! */ gtp_irq_disable(i2c_get_clientdata(gt_client)); #if GTP_ESD_PROTECT gtp_esd_switch(gt_client, SWITCH_OFF); #endif - return CMD_HEAD_LENGTH; + ret = CMD_HEAD_LENGTH; + goto exit; } else if (cmd_head.wr == 9) { /* enable irq! */ gtp_irq_enable(i2c_get_clientdata(gt_client)); #if GTP_ESD_PROTECT gtp_esd_switch(gt_client, SWITCH_ON); #endif - return CMD_HEAD_LENGTH; + ret = CMD_HEAD_LENGTH; + goto exit; } else if (cmd_head.wr == 17) { struct goodix_ts_data *ts = i2c_get_clientdata(gt_client); @@ -434,27 +481,40 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff, ts->gtp_rawdiff_mode = false; GTP_DEBUG("gtp leave rawdiff."); } - return CMD_HEAD_LENGTH; + ret = CMD_HEAD_LENGTH; + goto exit; } #ifdef UPDATE_FUNCTIONS else if (cmd_head.wr == 11) { /* Enter update mode! */ if (gup_enter_update_mode(gt_client) == FAIL) - return FAIL; + ret = -EBUSY; + goto exit; } else if (cmd_head.wr == 13) { /* Leave update mode! */ gup_leave_update_mode(); } else if (cmd_head.wr == 15) { /* Update firmware! */ show_len = 0; total_len = 0; + if (cmd_head.data_len + 1 > DATA_LENGTH) { + pr_err("<<-GTP->> data len %d > data buff %d, rejected!\n", + cmd_head.data_len + 1, DATA_LENGTH); + ret = -EINVAL; + goto exit; + } memset(cmd_head.data, 0, cmd_head.data_len + 1); memcpy(cmd_head.data, &buff[CMD_HEAD_LENGTH], cmd_head.data_len); - if (gup_update_proc((void *)cmd_head.data) == FAIL) - return FAIL; + if (gup_update_proc((void *)cmd_head.data) == FAIL) { + ret = -EBUSY; + goto exit; + } } #endif + ret = CMD_HEAD_LENGTH; - return CMD_HEAD_LENGTH; +exit: + mutex_unlock(&lock); + return ret; } /******************************************************* @@ -469,10 +529,14 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff, static s32 goodix_tool_read(char *page, char **start, off_t off, int count, int *eof, void *data) { + s32 ret; GTP_DEBUG_FUNC(); + mutex_lock(&lock); if (cmd_head.wr % 2) { - return FAIL; + pr_err("<< [READ]command head wrong\n"); + ret = -EINVAL; + goto exit; } else if (!cmd_head.wr) { u16 len = 0; s16 data_len = 0; @@ -481,7 +545,8 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count, if (cmd_head.flag == 1) { if (comfirm() == FAIL) { GTP_ERROR("[READ]Comfirm fail!"); - return FAIL; + ret = -EINVAL; + goto exit; } } else if (cmd_head.flag == 2) { /* Need interrupt! */ @@ -504,11 +569,12 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count, else len = data_len; - data_len -= DATA_LENGTH; + data_len -= len; if (tool_i2c_read(cmd_head.data, len) <= 0) { GTP_ERROR("[READ]Read data failed!"); - return FAIL; + ret = -EINVAL; + goto exit; } memcpy(&page[loc], &cmd_head.data[GTP_ADDR_LENGTH], len); @@ -525,15 +591,14 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count, GTP_DEBUG("Return ic type:%s len:%d.", page, (s32)cmd_head.data_len); - return cmd_head.data_len; + ret = cmd_head.data_len; + goto exit; /* return sizeof(IC_TYPE_NAME); */ } else if (cmd_head.wr == 4) { page[0] = show_len >> 8; page[1] = show_len & 0xff; page[2] = total_len >> 8; page[3] = total_len & 0xff; - - return cmd_head.data_len; } else if (cmd_head.wr == 6) { /* Read error code! */ } else if (cmd_head.wr == 8) { /*Read driver version */ @@ -546,6 +611,9 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count, memcpy(page, GTP_DRIVER_VERSION, tmp_len); page[tmp_len] = 0; } + ret = cmd_head.data_len; - return cmd_head.data_len; +exit: + mutex_unlock(&lock); + return ret; } diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c index ebb9ce7ba6a4..6c3747108d75 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx.c +++ b/drivers/input/touchscreen/gt9xx/gt9xx.c @@ -45,11 +45,9 @@ #include <linux/of_gpio.h> -#if GTP_ICS_SLOT_REPORT #include <linux/input/mt.h> -#endif -#define GOODIX_DEV_NAME "Goodix Capacitive TouchScreen" +#define GOODIX_DEV_NAME "Goodix-CTP" #define CFG_MAX_TOUCH_POINTS 5 #define GOODIX_COORDS_ARR_SIZE 4 #define MAX_BUTTONS 4 @@ -70,7 +68,8 @@ #define RESET_DELAY_T3_US 200 /* T3: > 100us */ #define RESET_DELAY_T4 20 /* T4: > 5ms */ -#define PHY_BUF_SIZE 32 +#define PHY_BUF_SIZE 32 +#define PROP_NAME_SIZE 24 #define GTP_MAX_TOUCH 5 #define GTP_ESD_CHECK_CIRCLE_MS 2000 @@ -136,40 +135,40 @@ Output: int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len) { struct goodix_ts_data *ts = i2c_get_clientdata(client); - struct i2c_msg msgs[2]; int ret = -EIO; - int retries = 0; - - GTP_DEBUG_FUNC(); - - msgs[0].flags = !I2C_M_RD; - msgs[0].addr = client->addr; - msgs[0].len = GTP_ADDR_LENGTH; - msgs[0].buf = &buf[0]; - - msgs[1].flags = I2C_M_RD; - msgs[1].addr = client->addr; - msgs[1].len = len - GTP_ADDR_LENGTH; - msgs[1].buf = &buf[GTP_ADDR_LENGTH]; - - while (retries < 5) { + u8 retries; + struct i2c_msg msgs[2] = { + { + .flags = !I2C_M_RD, + .addr = client->addr, + .len = GTP_ADDR_LENGTH, + .buf = &buf[0], + }, + { + .flags = I2C_M_RD, + .addr = client->addr, + .len = len - GTP_ADDR_LENGTH, + .buf = &buf[GTP_ADDR_LENGTH], + }, + }; + + for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) { ret = i2c_transfer(client->adapter, msgs, 2); if (ret == 2) break; - retries++; + dev_err(&client->dev, "I2C retry: %d\n", retries + 1); } - if (retries >= 5) { + if (retries == GTP_I2C_RETRY_5) { #if GTP_SLIDE_WAKEUP /* reset chip would quit doze mode */ if (doze_status == DOZE_ENABLED) return ret; #endif - GTP_DEBUG("I2C communication timeout, resetting chip..."); if (init_done) gtp_reset_guitar(ts, 10); else dev_warn(&client->dev, - "<GTP> gtp_reset_guitar exit init_done=%d:\n", + "gtp_reset_guitar exit init_done=%d:\n", init_done); } return ret; @@ -190,38 +189,36 @@ Output: int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len) { struct goodix_ts_data *ts = i2c_get_clientdata(client); - struct i2c_msg msg; int ret = -EIO; - int retries = 0; - - GTP_DEBUG_FUNC(); - - msg.flags = !I2C_M_RD; - msg.addr = client->addr; - msg.len = len; - msg.buf = buf; - - while (retries < 5) { + u8 retries; + struct i2c_msg msg = { + .flags = !I2C_M_RD, + .addr = client->addr, + .len = len, + .buf = buf, + }; + + for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) { ret = i2c_transfer(client->adapter, &msg, 1); if (ret == 1) break; - retries++; + dev_err(&client->dev, "I2C retry: %d\n", retries + 1); } - if ((retries >= 5)) { + if (retries == GTP_I2C_RETRY_5) { #if GTP_SLIDE_WAKEUP if (doze_status == DOZE_ENABLED) return ret; #endif - GTP_DEBUG("I2C communication timeout, resetting chip..."); if (init_done) gtp_reset_guitar(ts, 10); else dev_warn(&client->dev, - "<GTP> gtp_reset_guitar exit init_done=%d:\n", + "gtp_reset_guitar exit init_done=%d:\n", init_done); } return ret; } + /******************************************************* Function: i2c read twice, compare the results @@ -241,7 +238,7 @@ int gtp_i2c_read_dbl_check(struct i2c_client *client, u8 confirm_buf[16] = {0}; u8 retry = 0; - while (retry++ < 3) { + while (retry++ < GTP_I2C_RETRY_3) { memset(buf, 0xAA, 16); buf[0] = (u8)(addr >> 8); buf[1] = (u8)(addr & 0xFF); @@ -255,7 +252,7 @@ int gtp_i2c_read_dbl_check(struct i2c_client *client, if (!memcmp(buf, confirm_buf, len + 2)) break; } - if (retry < 3) { + if (retry < GTP_I2C_RETRY_3) { memcpy(rxbuf, confirm_buf + 2, len); return SUCCESS; } @@ -284,7 +281,7 @@ static int gtp_send_cfg(struct goodix_ts_data *ts) "Ic fixed config, no config sent!"); ret = 2; } else { - for (retry = 0; retry < 5; retry++) { + 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); @@ -309,8 +306,6 @@ void gtp_irq_disable(struct goodix_ts_data *ts) { unsigned long irqflags; - GTP_DEBUG_FUNC(); - spin_lock_irqsave(&ts->irq_lock, irqflags); if (!ts->irq_is_disabled) { ts->irq_is_disabled = true; @@ -331,8 +326,6 @@ void gtp_irq_enable(struct goodix_ts_data *ts) { unsigned long irqflags = 0; - GTP_DEBUG_FUNC(); - spin_lock_irqsave(&ts->irq_lock, irqflags); if (ts->irq_is_disabled) { enable_irq(ts->client->irq); @@ -357,26 +350,15 @@ static void gtp_touch_down(struct goodix_ts_data *ts, int id, int x, int y, int w) { #if GTP_CHANGE_X2Y - GTP_SWAP(x, y); + swap(x, y); #endif -#if GTP_ICS_SLOT_REPORT input_mt_slot(ts->input_dev, id); - input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x); input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y); input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w); input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); -#else - input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x); - input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y); - input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w); - input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); - input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id); - input_mt_sync(ts->input_dev); -#endif - - GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w); } /******************************************************* @@ -389,15 +371,8 @@ Output: *********************************************************/ static void gtp_touch_up(struct goodix_ts_data *ts, int id) { -#if GTP_ICS_SLOT_REPORT input_mt_slot(ts->input_dev, id); - input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1); - GTP_DEBUG("Touch id[%2d] release!", id); -#else - input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); - input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); - input_mt_sync(ts->input_dev); -#endif + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); } @@ -438,8 +413,6 @@ static void goodix_ts_work_func(struct work_struct *work) u8 doze_buf[3] = {0x81, 0x4B}; #endif - GTP_DEBUG_FUNC(); - ts = container_of(work, struct goodix_ts_data, work); #ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE if (ts->enter_update) @@ -449,7 +422,6 @@ static void goodix_ts_work_func(struct work_struct *work) #if GTP_SLIDE_WAKEUP if (doze_status == DOZE_ENABLED) { ret = gtp_i2c_read(ts->client, doze_buf, 3); - GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]); if (ret > 0) { if (doze_buf[2] == 0xAA) { dev_dbg(&ts->client->dev, @@ -547,12 +519,9 @@ static void goodix_ts_work_func(struct work_struct *work) #endif pre_key = key_value; - GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger); - -#if GTP_ICS_SLOT_REPORT #if GTP_WITH_PEN if (pre_pen && (touch_num == 0)) { - GTP_DEBUG("Pen touch UP(Slot)!"); + 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); @@ -569,7 +538,8 @@ static void goodix_ts_work_func(struct work_struct *work) #if GTP_WITH_PEN id = coor_data[pos]; if (id == 128) { - GTP_DEBUG("Pen touch DOWN(Slot)!"); + 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] @@ -588,7 +558,8 @@ static void goodix_ts_work_func(struct work_struct *work) ABS_MT_POSITION_Y, input_y); input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); - GTP_DEBUG("Pen/Stylus: (%d, %d)[%d]", + dev_dbg(&ts->client->dev, + "Pen/Stylus: (%d, %d)[%d]", input_x, input_y, input_w); pre_pen = 1; pre_touch = 0; @@ -598,8 +569,6 @@ static void goodix_ts_work_func(struct work_struct *work) touch_index |= (0x01<<id); } - GTP_DEBUG("id = %d,touch_index = 0x%x, pre_touch = 0x%x\n", - id, touch_index, pre_touch); for (i = 0; i < GTP_MAX_TOUCH; i++) { #if GTP_WITH_PEN if (pre_pen == 1) @@ -626,42 +595,6 @@ static void goodix_ts_work_func(struct work_struct *work) } } } -#else - input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value)); - if (touch_num) { - for (i = 0; i < touch_num; i++) { - coor_data = &point_data[i * 8 + 3]; - - id = coor_data[0]; - input_x = coor_data[1] | coor_data[2] << 8; - input_y = coor_data[3] | coor_data[4] << 8; - input_w = coor_data[5] | coor_data[6] << 8; -#if GTP_WITH_PEN - if (id == 128) { - GTP_DEBUG("Pen touch DOWN!"); - input_report_key(ts->input_dev, - BTN_TOOL_PEN, 1); - pre_pen = 1; - id = 0; - } -#endif - gtp_touch_down(ts, id, input_x, input_y, input_w); - } - } else if (pre_touch) { -#if GTP_WITH_PEN - if (pre_pen == 1) { - GTP_DEBUG("Pen touch UP!"); - input_report_key(ts->input_dev, BTN_TOOL_PEN, 0); - pre_pen = 0; - } -#endif - GTP_DEBUG("Touch Released!"); - gtp_touch_up(ts, 0); - } - - pre_touch = touch_num; -#endif - input_sync(ts->input_dev); exit_work_func: @@ -691,8 +624,6 @@ static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer) struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer); - GTP_DEBUG_FUNC(); - queue_work(ts->goodix_wq, &ts->work); hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME + 6) * 1000000), HRTIMER_MODE_REL); @@ -713,8 +644,6 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) { struct goodix_ts_data *ts = dev_id; - GTP_DEBUG_FUNC(); - gtp_irq_disable(ts); queue_work(ts->goodix_wq, &ts->work); @@ -746,8 +675,6 @@ Output: *******************************************************/ static void gtp_reset_guitar(struct goodix_ts_data *ts, int ms) { - GTP_DEBUG_FUNC(); - /* This reset sequence will selcet I2C slave address */ gpio_direction_output(ts->pdata->reset_gpio, 0); msleep(ms); @@ -788,20 +715,17 @@ static s8 gtp_enter_doze(struct goodix_ts_data *ts) (u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 8}; - GTP_DEBUG_FUNC(); - #if GTP_DBL_CLK_WAKEUP i2c_control_buf[2] = 0x09; #endif gtp_irq_disable(ts); - GTP_DEBUG("entering doze mode..."); - while (retry++ < 5) { + while (retry++ < GTP_I2C_RETRY_3) { i2c_control_buf[0] = 0x80; i2c_control_buf[1] = 0x46; ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); if (ret < 0) { - GTP_DEBUG( + dev_err(&ts->client->dev, "failed to set doze flag into 0x8046, %d", retry); continue; @@ -840,11 +764,9 @@ static s8 gtp_enter_sleep(struct goodix_ts_data *ts) (u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 5}; - GTP_DEBUG_FUNC(); - ret = gpio_direction_output(ts->pdata->irq_gpio, 0); usleep(5000); - while (retry++ < 5) { + while (retry++ < GTP_I2C_RETRY_5) { ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); if (ret > 0) { dev_dbg(&ts->client->dev, @@ -872,8 +794,6 @@ static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts) u8 retry = 0; s8 ret = -1; - GTP_DEBUG_FUNC(); - #if GTP_POWER_CTRL_SLEEP gtp_reset_guitar(ts, 20); @@ -884,7 +804,7 @@ static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts) return 1; } #else - while (retry++ < 10) { + while (retry++ < GTP_I2C_RETRY_10) { #if GTP_SLIDE_WAKEUP /* wakeup not by slide */ if (doze_status != DOZE_WAKEUP) @@ -944,26 +864,9 @@ static int gtp_init_panel(struct goodix_ts_data *ts) u8 opr_buf[16]; u8 sensor_id = 0; - u8 cfg_info_group1[] = CTP_CFG_GROUP1; - u8 cfg_info_group2[] = CTP_CFG_GROUP2; - u8 cfg_info_group3[] = CTP_CFG_GROUP3; - u8 cfg_info_group4[] = CTP_CFG_GROUP4; - u8 cfg_info_group5[] = CTP_CFG_GROUP5; - u8 cfg_info_group6[] = CTP_CFG_GROUP6; - u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, - cfg_info_group3, cfg_info_group4, - cfg_info_group5, cfg_info_group6}; - - u8 cfg_info_len[] = {ARRAY_SIZE(cfg_info_group1), - ARRAY_SIZE(cfg_info_group2), - ARRAY_SIZE(cfg_info_group3), - ARRAY_SIZE(cfg_info_group4), - ARRAY_SIZE(cfg_info_group5), - ARRAY_SIZE(cfg_info_group6)}; - - GTP_DEBUG("Config Groups\' Lengths: %d, %d, %d, %d, %d, %d", - cfg_info_len[0], cfg_info_len[1], cfg_info_len[2], - cfg_info_len[3], cfg_info_len[4], cfg_info_len[5]); + 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) { @@ -974,14 +877,18 @@ static int gtp_init_panel(struct goodix_ts_data *ts) return -EINVAL; } } - if ((!cfg_info_len[1]) && (!cfg_info_len[2]) && (!cfg_info_len[3]) - && (!cfg_info_len[4]) && (!cfg_info_len[5])) { + + 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); if (ret == SUCCESS) { - if (sensor_id >= 0x06) { + if (sensor_id >= GOODIX_MAX_CFG_GROUP) { dev_err(&client->dev, "Invalid sensor_id(0x%02X), No Config Sent!", sensor_id); @@ -993,24 +900,26 @@ static int gtp_init_panel(struct goodix_ts_data *ts) return -EINVAL; } } - GTP_DEBUG("Sensor_ID: %d", sensor_id); - ts->gtp_cfg_len = cfg_info_len[sensor_id]; + dev_dbg(&client->dev, "Sensor ID selected: %d", sensor_id); - if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) { + 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! NO Config Sent! You need to check you header file CFG_GROUP section!\n", + "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 = send_cfg_buf[sensor_id][0]; - send_cfg_buf[sensor_id][0] = 0x00; + 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 */ @@ -1025,27 +934,9 @@ static int gtp_init_panel(struct goodix_ts_data *ts) return -EINVAL; } - if (ts->pdata->gtp_cfg_len) { - config_data = ts->pdata->config_data; - ts->config_data = ts->pdata->config_data; - ts->gtp_cfg_len = ts->pdata->gtp_cfg_len; - } else { - config_data = devm_kzalloc(&client->dev, - GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH, - GFP_KERNEL); - if (!config_data) { - dev_err(&client->dev, - "Not enough memory for panel config data\n"); - return -ENOMEM; - } - - ts->config_data = config_data; - config_data[0] = GTP_REG_CONFIG_DATA >> 8; - config_data[1] = GTP_REG_CONFIG_DATA & 0xff; - memset(&config_data[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); - memcpy(&config_data[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], - ts->gtp_cfg_len); - } + 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] = @@ -1082,7 +973,6 @@ static int gtp_init_panel(struct goodix_ts_data *ts) } #endif /* !DRIVER NOT SEND CONFIG */ - GTP_DEBUG_FUNC(); if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) { ts->abs_x_max = (config_data[RESOLUTION_LOC + 1] << 8) + config_data[RESOLUTION_LOC]; @@ -1094,49 +984,79 @@ static int gtp_init_panel(struct goodix_ts_data *ts) if (ret < 0) dev_err(&client->dev, "%s: Send config error.\n", __func__); - GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x", - ts->abs_x_max, ts->abs_y_max, - ts->int_trigger_type); - msleep(20); return ret; } /******************************************************* Function: - Read chip version. + Read firmware version Input: client: i2c device version: buffer to keep ic firmware version Output: read operation return. - 2: succeed, otherwise: failed + 0: succeed, otherwise: failed *******************************************************/ -int gtp_read_version(struct i2c_client *client, u16 *version) +static int gtp_read_fw_version(struct i2c_client *client, u16 *version) { - int ret = -EIO; - u8 buf[8] = { GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff }; - - GTP_DEBUG_FUNC(); + int ret = 0; + u8 buf[GTP_FW_VERSION_BUFFER_MAXSIZE] = { + GTP_REG_FW_VERSION >> 8, GTP_REG_FW_VERSION & 0xff }; ret = gtp_i2c_read(client, buf, sizeof(buf)); if (ret < 0) { dev_err(&client->dev, "GTP read version failed.\n"); - return ret; + return -EIO; } if (version) - *version = (buf[7] << 8) | buf[6]; + *version = (buf[3] << 8) | buf[2]; + + return ret; +} +/* + * Function: + * Read and check chip id. + * Input: + * client: i2c device + * Output: + * read operation return. + * 0: succeed, otherwise: failed + */ +static int gtp_check_product_id(struct i2c_client *client) +{ + int ret = 0; + char product_id[GTP_PRODUCT_ID_MAXSIZE]; + struct goodix_ts_data *ts = i2c_get_clientdata(client); + /* 04 bytes are used for the Product-id in the register space.*/ + u8 buf[GTP_PRODUCT_ID_BUFFER_MAXSIZE] = { + GTP_REG_PRODUCT_ID >> 8, GTP_REG_PRODUCT_ID & 0xff }; + + ret = gtp_i2c_read(client, buf, sizeof(buf)); + if (ret < 0) { + dev_err(&client->dev, "GTP read product_id failed.\n"); + return -EIO; + } if (buf[5] == 0x00) { - dev_dbg(&client->dev, "IC Version: %c%c%c_%02x%02x\n", buf[2], - buf[3], buf[4], buf[7], buf[6]); + /* copy (GTP_PRODUCT_ID_MAXSIZE - 1) from buffer. Ex: 915 */ + strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE - 1); } else { if (buf[5] == 'S' || buf[5] == 's') chip_gt9xxs = 1; - dev_dbg(&client->dev, "IC Version: %c%c%c%c_%02x%02x\n", buf[2], - buf[3], buf[4], buf[5], buf[7], buf[6]); + /* copy GTP_PRODUCT_ID_MAXSIZE from buffer. Ex: 915s */ + strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE); } + + dev_info(&client->dev, "Goodix Product ID = %s\n", product_id); + + if (!IS_ERR(ts->pdata->product_id)) + ret = strcmp(product_id, ts->pdata->product_id); + + if (ret != 0) + return -EINVAL; + return ret; } @@ -1152,11 +1072,9 @@ Output: static int gtp_i2c_test(struct i2c_client *client) { u8 buf[3] = { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff }; - int retry = 5; + int retry = GTP_I2C_RETRY_5; int ret = -EIO; - GTP_DEBUG_FUNC(); - while (retry--) { ret = gtp_i2c_read(client, buf, 3); if (ret > 0) @@ -1185,50 +1103,66 @@ static int gtp_request_io_port(struct goodix_ts_data *ts) if (gpio_is_valid(pdata->irq_gpio)) { ret = gpio_request(pdata->irq_gpio, "goodix_ts_irq_gpio"); if (ret) { - dev_err(&client->dev, "irq gpio request failed\n"); - goto pwr_off; + dev_err(&client->dev, "Unable to request irq gpio [%d]\n", + pdata->irq_gpio); + goto err_pwr_off; } ret = gpio_direction_input(pdata->irq_gpio); if (ret) { - dev_err(&client->dev, - "set_direction for irq gpio failed\n"); - goto free_irq_gpio; + dev_err(&client->dev, "Unable to set direction for irq gpio [%d]\n", + pdata->irq_gpio); + goto err_free_irq_gpio; } } else { - dev_err(&client->dev, "irq gpio is invalid!\n"); + dev_err(&client->dev, "Invalid irq gpio [%d]!\n", + pdata->irq_gpio); ret = -EINVAL; - goto free_irq_gpio; + goto err_pwr_off; } if (gpio_is_valid(pdata->reset_gpio)) { - ret = gpio_request(pdata->reset_gpio, "goodix_ts__reset_gpio"); + ret = gpio_request(pdata->reset_gpio, "goodix_ts_reset_gpio"); if (ret) { - dev_err(&client->dev, "reset gpio request failed\n"); - goto free_irq_gpio; + dev_err(&client->dev, "Unable to request reset gpio [%d]\n", + pdata->reset_gpio); + goto err_free_irq_gpio; } ret = gpio_direction_output(pdata->reset_gpio, 0); if (ret) { - dev_err(&client->dev, - "set_direction for reset gpio failed\n"); - goto free_reset_gpio; + dev_err(&client->dev, "Unable to set direction for reset gpio [%d]\n", + pdata->reset_gpio); + goto err_free_reset_gpio; } } else { - dev_err(&client->dev, "reset gpio is invalid!\n"); + dev_err(&client->dev, "Invalid irq gpio [%d]!\n", + pdata->reset_gpio); ret = -EINVAL; - goto free_reset_gpio; - } - gpio_direction_input(pdata->reset_gpio); + goto err_free_irq_gpio; + } + /* IRQ GPIO is an input signal, but we are setting it to output + * direction and pulling it down, to comply with power up timing + * requirements, mentioned in power up timing section of device + * datasheet. + */ + ret = gpio_direction_output(pdata->irq_gpio, 0); + if (ret) + dev_warn(&client->dev, + "pull down interrupt gpio failed\n"); + ret = gpio_direction_output(pdata->reset_gpio, 0); + if (ret) + dev_warn(&client->dev, + "pull down reset gpio failed\n"); return ret; -free_reset_gpio: +err_free_reset_gpio: if (gpio_is_valid(pdata->reset_gpio)) gpio_free(pdata->reset_gpio); -free_irq_gpio: +err_free_irq_gpio: if (gpio_is_valid(pdata->irq_gpio)) gpio_free(pdata->irq_gpio); -pwr_off: +err_pwr_off: return ret; } @@ -1246,9 +1180,6 @@ 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, - ts->client->irq); - ret = request_irq(ts->client->irq, goodix_ts_irq_handler, irq_table[ts->int_trigger_type], ts->client->name, ts); @@ -1287,8 +1218,6 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts) int index = 0; #endif - GTP_DEBUG_FUNC(); - ts->input_dev = input_allocate_device(); if (ts->input_dev == NULL) { dev_err(&ts->client->dev, @@ -1298,12 +1227,10 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts) ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); -#if GTP_ICS_SLOT_REPORT + set_bit(BTN_TOOL_FINGER, ts->input_dev->keybit); __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); - input_mt_init_slots(ts->input_dev, 10);/* in case of "out of memory" */ -#else - ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); -#endif + /* in case of "out of memory" */ + input_mt_init_slots(ts->input_dev, 10, 0); #if GTP_HAVE_TOUCH_KEY for (index = 0; index < ARRAY_SIZE(touch_key_array); index++) { @@ -1324,7 +1251,7 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts) #endif #if GTP_CHANGE_X2Y - GTP_SWAP(ts->abs_x_max, ts->abs_y_max); + swap(ts->abs_x_max, ts->abs_y_max); #endif input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, @@ -1606,6 +1533,8 @@ static int goodix_parse_dt(struct device *dev, struct property *prop; u32 temp_val, num_buttons; u32 button_map[MAX_BUTTONS]; + char prop_name[PROP_NAME_SIZE]; + int i, read_cfg_num; rc = goodix_ts_get_dt_coords(dev, "goodix,panel-coords", pdata); if (rc && (rc != -EINVAL)) @@ -1631,10 +1560,9 @@ static int goodix_parse_dt(struct device *dev, if (pdata->irq_gpio < 0) return pdata->irq_gpio; - rc = of_property_read_u32(np, "goodix,family-id", &temp_val); - if (!rc) - pdata->family_id = temp_val; - else + rc = of_property_read_string(np, "goodix,product-id", + &pdata->product_id); + if (rc < 0 || strlen(pdata->product_id) > GTP_PRODUCT_ID_MAXSIZE) return rc; prop = of_find_property(np, "goodix,button-map", NULL); @@ -1652,24 +1580,32 @@ static int goodix_parse_dt(struct device *dev, } } - prop = of_find_property(np, "goodix,cfg-data", &pdata->gtp_cfg_len); - if (prop && prop->value) { - pdata->config_data = devm_kzalloc(dev, - GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH, GFP_KERNEL); - if (!pdata->config_data) + read_cfg_num = 0; + for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) { + snprintf(prop_name, sizeof(prop_name), "goodix,cfg-data%d", i); + prop = of_find_property(np, prop_name, + &pdata->config_data_len[i]); + if (!prop || !prop->value) { + pdata->config_data_len[i] = 0; + pdata->config_data[i] = NULL; + continue; + } + pdata->config_data[i] = devm_kzalloc(dev, + GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH, + GFP_KERNEL); + if (!pdata->config_data[i]) { + dev_err(dev, + "Not enough memory for panel config data %d\n", + i); return -ENOMEM; - - pdata->config_data[0] = GTP_REG_CONFIG_DATA >> 8; - pdata->config_data[1] = GTP_REG_CONFIG_DATA & 0xff; - memset(&pdata->config_data[GTP_ADDR_LENGTH], 0, - GTP_CONFIG_MAX_LENGTH); - memcpy(&pdata->config_data[GTP_ADDR_LENGTH], - prop->value, pdata->gtp_cfg_len); - } else { - dev_err(dev, - "Unable to get configure data, default will be used.\n"); - pdata->gtp_cfg_len = 0; + } + pdata->config_data[i][0] = GTP_REG_CONFIG_DATA >> 8; + pdata->config_data[i][1] = GTP_REG_CONFIG_DATA & 0xff; + memcpy(&pdata->config_data[i][GTP_ADDR_LENGTH], + prop->value, pdata->config_data_len[i]); + read_cfg_num++; } + dev_dbg(dev, "%d config data read from device tree.\n", read_cfg_num); return 0; } @@ -1721,7 +1657,7 @@ static int goodix_ts_probe(struct i2c_client *client, return -ENODEV; } - ts = kzalloc(sizeof(*ts), GFP_KERNEL); + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); if (!ts) return -ENOMEM; @@ -1735,10 +1671,16 @@ static int goodix_ts_probe(struct i2c_client *client, i2c_set_clientdata(client, ts); ts->gtp_rawdiff_mode = 0; + ret = gtp_request_io_port(ts); + if (ret) { + dev_err(&client->dev, "GTP request IO port failed.\n"); + goto exit_free_client_data; + } + ret = goodix_power_init(ts); if (ret) { dev_err(&client->dev, "GTP power init failed\n"); - goto exit_free_client_data; + goto exit_free_io_port; } ret = goodix_power_on(ts); @@ -1747,18 +1689,12 @@ static int goodix_ts_probe(struct i2c_client *client, goto exit_deinit_power; } - ret = gtp_request_io_port(ts); - if (ret) { - dev_err(&client->dev, "GTP request IO port failed.\n"); - goto exit_power_off; - } - gtp_reset_guitar(ts, 20); ret = gtp_i2c_test(client); if (ret != 2) { dev_err(&client->dev, "I2C communication ERROR!\n"); - goto exit_free_io_port; + goto exit_power_off; } #if GTP_AUTO_UPDATE @@ -1807,9 +1743,13 @@ static int goodix_ts_probe(struct i2c_client *client, else dev_info(&client->dev, "GTP works in interrupt mode.\n"); - ret = gtp_read_version(client, &version_info); - if (ret != 2) { - dev_err(&client->dev, "Read version failed.\n"); + ret = gtp_read_fw_version(client, &version_info); + if (ret != 2) + dev_err(&client->dev, "GTP firmware version read failed.\n"); + + ret = gtp_check_product_id(client); + if (ret != 0) { + dev_err(&client->dev, "GTP Product id doesn't match.\n"); goto exit_free_irq; } if (ts->use_irq) @@ -1847,18 +1787,17 @@ exit_free_irq: } exit_free_inputdev: kfree(ts->config_data); +exit_power_off: + goodix_power_off(ts); +exit_deinit_power: + goodix_power_deinit(ts); exit_free_io_port: if (gpio_is_valid(pdata->reset_gpio)) gpio_free(pdata->reset_gpio); if (gpio_is_valid(pdata->irq_gpio)) gpio_free(pdata->irq_gpio); -exit_power_off: - goodix_power_off(ts); -exit_deinit_power: - goodix_power_deinit(ts); exit_free_client_data: i2c_set_clientdata(client, NULL); - kfree(ts); return ret; } @@ -1874,7 +1813,6 @@ static int goodix_ts_remove(struct i2c_client *client) { struct goodix_ts_data *ts = i2c_get_clientdata(client); - GTP_DEBUG_FUNC(); #if defined(CONFIG_FB) if (fb_unregister_client(&ts->fb_notif)) dev_err(&client->dev, @@ -1908,7 +1846,6 @@ static int goodix_ts_remove(struct i2c_client *client) input_free_device(ts->input_dev); ts->input_dev = NULL; } - kfree(ts->config_data); if (gpio_is_valid(ts->pdata->reset_gpio)) gpio_free(ts->pdata->reset_gpio); @@ -1918,7 +1855,6 @@ static int goodix_ts_remove(struct i2c_client *client) goodix_power_off(ts); goodix_power_deinit(ts); i2c_set_clientdata(client, NULL); - kfree(ts); } return 0; @@ -1935,9 +1871,7 @@ Output: *******************************************************/ static void goodix_ts_suspend(struct goodix_ts_data *ts) { - int ret = -1; - - GTP_DEBUG_FUNC(); + int ret = -1, i; #if GTP_ESD_PROTECT ts->gtp_is_suspend = 1; @@ -1951,6 +1885,12 @@ static void goodix_ts_suspend(struct goodix_ts_data *ts) gtp_irq_disable(ts); else hrtimer_cancel(&ts->timer); + + for (i = 0; i < GTP_MAX_TOUCH; i++) + gtp_touch_up(ts, i); + + input_sync(ts->input_dev); + ret = gtp_enter_sleep(ts); #endif if (ret < 0) @@ -1973,8 +1913,6 @@ static void goodix_ts_resume(struct goodix_ts_data *ts) { int ret = -1; - GTP_DEBUG_FUNC(); - ret = gtp_wakeup_sleep(ts); #if GTP_SLIDE_WAKEUP @@ -2101,21 +2039,18 @@ static int gtp_init_ext_watchdog(struct i2c_client *client) int ret; int retries = 0; - GTP_DEBUG("Init external watchdog..."); - GTP_DEBUG_FUNC(); - msg.flags = !I2C_M_RD; msg.addr = client->addr; msg.len = 4; msg.buf = opr_buffer; - while (retries < 5) { + while (retries < GTP_I2C_RETRY_5) { ret = i2c_transfer(client->adapter, &msg, 1); if (ret == 1) return 1; retries++; } - if (retries >= 5) + if (retries == GTP_I2C_RETRY_5) dev_err(&client->dev, "init external watchdog failed!"); return 0; } @@ -2131,13 +2066,11 @@ Output: *******************************************************/ static void gtp_esd_check_func(struct work_struct *work) { - s32 i; + s32 retry; s32 ret = -1; struct goodix_ts_data *ts = NULL; u8 test[4] = {0x80, 0x40}; - GTP_DEBUG_FUNC(); - ts = i2c_get_clientdata(i2c_connect_client); if (ts->gtp_is_suspend) { @@ -2150,17 +2083,16 @@ static void gtp_esd_check_func(struct work_struct *work) return; #endif - for (i = 0; i < 3; i++) { + for (retry = 0; retry < GTP_I2C_RETRY_3; retry++) { ret = gtp_i2c_read(ts->client, test, 4); - GTP_DEBUG("0x8040 = 0x%02X, 0x8041 = 0x%02X", test[2], test[3]); if ((ret < 0)) { /* IC works abnormally..*/ continue; } else { if ((test[2] == 0xAA) || (test[3] != 0xAA)) { /* IC works abnormally..*/ - i = 3; + retry = GTP_I2C_RETRY_3; break; } /* IC works normally, Write 0x8040 0xAA*/ @@ -2169,7 +2101,7 @@ static void gtp_esd_check_func(struct work_struct *work) break; } } - if (i >= 3) { + if (retry == GTP_I2C_RETRY_3) { dev_err(&ts->client->dev, "IC Working ABNORMALLY, Resetting Guitar...\n"); gtp_reset_guitar(ts, 50); @@ -2224,7 +2156,6 @@ static int __init goodix_ts_init(void) { int ret; - GTP_DEBUG_FUNC(); #if GTP_ESD_PROTECT INIT_DELAYED_WORK(>p_esd_check_work, gtp_esd_check_func); gtp_esd_check_workqueue = create_workqueue("gtp_esd_check"); @@ -2243,7 +2174,6 @@ Output: ********************************************************/ static void __exit goodix_ts_exit(void) { - GTP_DEBUG_FUNC(); i2c_del_driver(&goodix_ts_driver); } diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h index 185927c6d2b5..843e3d6c05b2 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx.h +++ b/drivers/input/touchscreen/gt9xx/gt9xx.h @@ -42,12 +42,13 @@ #define GOODIX_SUSPEND_LEVEL 1 #endif +#define GOODIX_MAX_CFG_GROUP 6 struct goodix_ts_platform_data { int irq_gpio; u32 irq_gpio_flags; int reset_gpio; u32 reset_gpio_flags; - u32 family_id; + const char *product_id; u32 x_max; u32 y_max; u32 x_min; @@ -58,8 +59,8 @@ struct goodix_ts_platform_data { u32 panel_maxy; bool no_force_update; bool i2c_pull_up; - int gtp_cfg_len; - u8 *config_data; + size_t config_data_len[GOODIX_MAX_CFG_GROUP]; + u8 *config_data[GOODIX_MAX_CFG_GROUP]; }; struct goodix_ts_data { spinlock_t irq_lock; @@ -104,7 +105,6 @@ extern u16 total_len; #define GTP_DRIVER_SEND_CFG 1 #define GTP_HAVE_TOUCH_KEY 1 #define GTP_POWER_CTRL_SLEEP 0 -#define GTP_ICS_SLOT_REPORT 1 /* auto updated by .bin file as default */ #define GTP_AUTO_UPDATE 0 @@ -121,7 +121,7 @@ extern u16 total_len; /* double-click wakeup, function together with GTP_SLIDE_WAKEUP */ #define GTP_DBL_CLK_WAKEUP 0 -#define GTP_DEBUG_ON 1 +#define GTP_DEBUG_ON 0 #define GTP_DEBUG_ARRAY_ON 0 #define GTP_DEBUG_FUNC_ON 0 @@ -135,56 +135,7 @@ extern u16 total_len; * GND NC/300K 3 * VDDIO NC/300K 4 * NC NC/300K 5 -*/ -/* Define your own default or for Sensor_ID == 0 config here */ -/* The predefined one is just a sample config, - * which is not suitable for your tp in most cases. */ -#define CTP_CFG_GROUP1 {\ - 0x41, 0x1C, 0x02, 0xC0, 0x03, 0x0A, 0x05, 0x01, 0x01, 0x0F,\ - 0x23, 0x0F, 0x5F, 0x41, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00,\ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x0A,\ - 0x28, 0x00, 0xB8, 0x0B, 0x00, 0x00, 0x00, 0x9A, 0x03, 0x25,\ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x64, 0x32, 0x00, 0x00,\ - 0x00, 0x32, 0x8C, 0x94, 0x05, 0x01, 0x05, 0x00, 0x00, 0x96,\ - 0x0C, 0x22, 0xD8, 0x0E, 0x23, 0x56, 0x11, 0x25, 0xFF, 0x13,\ - 0x28, 0xA7, 0x15, 0x2E, 0x00, 0x00, 0x10, 0x30, 0x48, 0x00,\ - 0x56, 0x4A, 0x3A, 0xFF, 0xFF, 0x16, 0x00, 0x00, 0x00, 0x00,\ - 0x00, 0x01, 0x1B, 0x14, 0x0D, 0x19, 0x00, 0x00, 0x01, 0x00,\ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ - 0x00, 0x00, 0x1A, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0E, 0x0C,\ - 0x0A, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\ - 0xFF, 0xFF, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24, 0x28, 0x29,\ - 0x0C, 0x0A, 0x08, 0x00, 0x02, 0x04, 0x05, 0x06, 0x0E, 0xFF,\ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\ - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x01\ - } - -/* Define your config for Sensor_ID == 1 here, if needed */ -#define CTP_CFG_GROUP2 {\ - } - -/* Define your config for Sensor_ID == 2 here, if needed */ -#define CTP_CFG_GROUP3 {\ - } - -/* Define your config for Sensor_ID == 3 here, if needed */ -#define CTP_CFG_GROUP4 {\ - } - -/* Define your config for Sensor_ID == 4 here, if needed */ -#define CTP_CFG_GROUP5 {\ - } - -/* Define your config for Sensor_ID == 5 here, if needed */ -#define CTP_CFG_GROUP6 {\ - } #define GTP_IRQ_TAB {\ IRQ_TYPE_EDGE_RISING,\ @@ -206,30 +157,38 @@ extern u16 total_len; #define GTP_INT_TRIGGER GTP_IRQ_TAB_FALLING #endif -#define GTP_MAX_TOUCH 5 -#define GTP_ESD_CHECK_CIRCLE 2000 /* jiffy: ms */ +#define GTP_PRODUCT_ID_MAXSIZE 5 +#define GTP_PRODUCT_ID_BUFFER_MAXSIZE 6 +#define GTP_FW_VERSION_BUFFER_MAXSIZE 4 +#define GTP_MAX_TOUCH 5 +#define GTP_ESD_CHECK_CIRCLE 2000 /* jiffy: ms */ /***************************PART3:OTHER define*********************************/ -#define GTP_DRIVER_VERSION "V1.8<2013/06/08>" -#define GTP_I2C_NAME "Goodix-TS" -#define GTP_POLL_TIME 10 /* jiffy: ms*/ -#define GTP_ADDR_LENGTH 2 +#define GTP_DRIVER_VERSION "V1.8.1<2013/09/01>" +#define GTP_I2C_NAME "Goodix-TS" +#define GTP_POLL_TIME 10 /* jiffy: ms*/ +#define GTP_ADDR_LENGTH 2 #define GTP_CONFIG_MIN_LENGTH 186 #define GTP_CONFIG_MAX_LENGTH 240 -#define FAIL 0 -#define SUCCESS 1 -#define SWITCH_OFF 0 -#define SWITCH_ON 1 +#define FAIL 0 +#define SUCCESS 1 +#define SWITCH_OFF 0 +#define SWITCH_ON 1 /* Registers define */ -#define GTP_READ_COOR_ADDR 0x814E -#define GTP_REG_SLEEP 0x8040 -#define GTP_REG_SENSOR_ID 0x814A -#define GTP_REG_CONFIG_DATA 0x8047 -#define GTP_REG_VERSION 0x8140 - -#define RESOLUTION_LOC 3 -#define TRIGGER_LOC 8 +#define GTP_READ_COOR_ADDR 0x814E +#define GTP_REG_SLEEP 0x8040 +#define GTP_REG_SENSOR_ID 0x814A +#define GTP_REG_CONFIG_DATA 0x8047 +#define GTP_REG_FW_VERSION 0x8144 +#define GTP_REG_PRODUCT_ID 0x8140 + +#define GTP_I2C_RETRY_3 3 +#define GTP_I2C_RETRY_5 5 +#define GTP_I2C_RETRY_10 10 + +#define RESOLUTION_LOC 3 +#define TRIGGER_LOC 8 /* Log define */ #define GTP_DEBUG(fmt, arg...) do {\ diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c index 29459b6d9ad1..9fcf7f0bef86 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c +++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c @@ -109,24 +109,25 @@ Output: *********************************************************/ s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len) { - struct i2c_msg msgs[2]; s32 ret = -1; - s32 retries = 0; + u8 retries = 0; + struct i2c_msg msgs[2] = { + { + .flags = !I2C_M_RD, + .addr = client->addr, + .len = GTP_ADDR_LENGTH, + .buf = &buf[0], + }, + { + .flags = I2C_M_RD, + .addr = client->addr, + .len = len - GTP_ADDR_LENGTH, + .buf = &buf[GTP_ADDR_LENGTH], + }, + }; GTP_DEBUG_FUNC(); - msgs[0].flags = !I2C_M_RD; - msgs[0].addr = client->addr; - msgs[0].len = GTP_ADDR_LENGTH; - msgs[0].buf = &buf[0]; - /* msgs[0].scl_rate = 300 * 1000; (for Rockchip) */ - - msgs[1].flags = I2C_M_RD; - msgs[1].addr = client->addr; - msgs[1].len = len - GTP_ADDR_LENGTH; - msgs[1].buf = &buf[GTP_ADDR_LENGTH]; - /* msgs[1].scl_rate = 300 * 1000; */ - while (retries < 5) { ret = i2c_transfer(client->adapter, msgs, 2); if (ret == 2) @@ -134,6 +135,11 @@ s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len) retries++; } + if (retries == 5) { + dev_err(&client->dev, "I2C read retry limit over.\n"); + ret = -EIO; + } + return ret; } @@ -151,18 +157,17 @@ Output: *********************************************************/ s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len) { - struct i2c_msg msg; s32 ret = -1; - s32 retries = 0; + u8 retries = 0; + struct i2c_msg msg = { + .flags = !I2C_M_RD, + .addr = client->addr, + .len = len, + .buf = buf, + }; GTP_DEBUG_FUNC(); - msg.flags = !I2C_M_RD; - msg.addr = client->addr; - msg.len = len; - msg.buf = buf; - /* msg.scl_rate = 300 * 1000; (for Rockchip) */ - while (retries < 5) { ret = i2c_transfer(client->adapter, &msg, 1); if (ret == 1) @@ -170,6 +175,11 @@ s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len) retries++; } + if (retries == 5) { + dev_err(&client->dev, "I2C write retry limit over.\n"); + ret = -EIO; + } + return ret; } @@ -288,7 +298,7 @@ static s32 gup_init_panel(struct goodix_ts_data *ts) static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len) { - s32 i = 0; + u8 i = 0; msg[0] = (addr >> 8) & 0xff; msg[1] = addr & 0xff; @@ -307,12 +317,12 @@ static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len) static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val) { - s32 i = 0; - u8 msg[3]; - - msg[0] = (addr >> 8) & 0xff; - msg[1] = addr & 0xff; - msg[2] = val; + u8 i = 0; + u8 msg[3] = { + (addr >> 8) & 0xff, + addr & 0xff, + val, + }; for (i = 0; i < 5; i++) if (gup_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0) @@ -411,7 +421,7 @@ static u8 gup_get_ic_fw_msg(struct i2c_client *client) s32 gup_enter_update_mode(struct i2c_client *client) { s32 ret = -1; - s32 retry = 0; + u8 retry = 0; u8 rd_buf[3]; /* step1:RST output low last at least 2ms */ @@ -575,11 +585,11 @@ static u8 ascii2hex(u8 a) static s8 gup_update_config(struct i2c_client *client) { - s32 file_len = 0; + u32 file_len = 0; s32 ret = 0; s32 i = 0; s32 file_cfg_len = 0; - s32 chip_cfg_len = 0; + u32 chip_cfg_len = 0; s32 count = 0; u8 *buf; u8 *pre_buf; @@ -615,9 +625,19 @@ static s8 gup_update_config(struct i2c_client *client) return -EINVAL; } - buf = kzalloc(file_len, GFP_KERNEL); - pre_buf = kzalloc(file_len, GFP_KERNEL); - file_config = kzalloc(chip_cfg_len + GTP_ADDR_LENGTH, GFP_KERNEL); + buf = devm_kzalloc(&client->dev, file_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pre_buf = devm_kzalloc(&client->dev, file_len, GFP_KERNEL); + if (!pre_buf) + return -ENOMEM; + + file_config = devm_kzalloc(&client->dev, chip_cfg_len + GTP_ADDR_LENGTH, + GFP_KERNEL); + if (!file_config) + return -ENOMEM; + update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_SET); GTP_DEBUG("[update_cfg]Read config from file."); @@ -625,7 +645,7 @@ static s8 gup_update_config(struct i2c_client *client) (char *)pre_buf, file_len, &update_msg.cfg_file->f_pos); if (ret < 0) { GTP_ERROR("[update_cfg]Read config file failed."); - goto update_cfg_file_failed; + return ret; } GTP_DEBUG("[update_cfg]Delete illegal character."); @@ -650,13 +670,13 @@ static s8 gup_update_config(struct i2c_client *client) if ((high == 0xFF) || (low == 0xFF)) { ret = 0; GTP_ERROR("[update_cfg]Illegal config file."); - goto update_cfg_file_failed; + return ret; } file_config[file_cfg_len++] = (high<<4) + low; } else { ret = 0; GTP_ERROR("[update_cfg]Illegal config file."); - goto update_cfg_file_failed; + return ret; } } @@ -680,10 +700,6 @@ static s8 gup_update_config(struct i2c_client *client) GTP_ERROR("[update_cfg]Send config i2c error."); } -update_cfg_file_failed: - kfree(pre_buf); - kfree(buf); - kfree(file_config); return ret; } @@ -787,16 +803,22 @@ static u8 gup_check_update_file(struct i2c_client *client, st_fw_head *fw_head, sizeof(UPDATE_FILE_PATH_2)); u8 cfp_len = max(sizeof(CONFIG_FILE_PATH_1), sizeof(CONFIG_FILE_PATH_2)); - u8 *search_update_path = kzalloc(fp_len, GFP_KERNEL); - u8 *search_cfg_path = kzalloc(cfp_len, GFP_KERNEL); + + u8 *search_update_path = devm_kzalloc(&client->dev, fp_len, + GFP_KERNEL); + if (!search_update_path) + goto load_failed; + + u8 *search_cfg_path = devm_kzalloc(&client->dev, cfp_len, + GFP_KERNEL); + if (!search_cfg_path) + goto load_failed; /* Begin to search update file,the config file & firmware * file must be in the same path,single or double. */ searching_file = 1; for (i = 0; i < GUP_SEARCH_FILE_TIMES; i++) { if (searching_file == 0) { - kfree(search_update_path); - kfree(search_cfg_path); GTP_INFO(".bin/.cfg update file search ", "forcely terminated!"); return FAIL; @@ -843,8 +865,6 @@ static u8 gup_check_update_file(struct i2c_client *client, st_fw_head *fw_head, } searching_file = 0; - kfree(search_update_path); - kfree(search_cfg_path); if (!got_file_flag) { GTP_ERROR("Can't find update file."); @@ -1168,7 +1188,8 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) /* step1:alloc memory */ GTP_DEBUG("[burn_dsp_isp]step1:alloc memory"); while (retry++ < 5) { - fw_dsp_isp = kzalloc(FW_DSP_ISP_LENGTH, GFP_KERNEL); + fw_dsp_isp = devm_kzalloc(&client->dev, FW_DSP_ISP_LENGTH, + GFP_KERNEL); if (fw_dsp_isp == NULL) { continue; } else { @@ -1177,7 +1198,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) break; } } - if (retry >= 5) { + if (retry == 5) { GTP_ERROR("[burn_dsp_isp]Alloc memory fail,exit."); return FAIL; } @@ -1188,7 +1209,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) FW_DSP_LENGTH + FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_dsp_isp]load firmware dsp_isp fail."); - goto exit_burn_dsp_isp; + return FAIL; } /* step3:disable wdt,clear cache enable */ @@ -1196,14 +1217,12 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00); if (ret <= 0) { GTP_ERROR("[burn_dsp_isp]disable wdt fail."); - ret = FAIL; - goto exit_burn_dsp_isp; + return FAIL; } ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00); if (ret <= 0) { GTP_ERROR("[burn_dsp_isp]clear cache enable fail."); - ret = FAIL; - goto exit_burn_dsp_isp; + return FAIL; } /* step4:hold ss51 & dsp */ @@ -1211,8 +1230,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); if (ret <= 0) { GTP_ERROR("[burn_dsp_isp]hold ss51 & dsp fail."); - ret = FAIL; - goto exit_burn_dsp_isp; + return FAIL; } /* step5:set boot from sram */ @@ -1220,8 +1238,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02); if (ret <= 0) { GTP_ERROR("[burn_dsp_isp]set boot from sram fail."); - ret = FAIL; - goto exit_burn_dsp_isp; + return FAIL; } /* step6:software reboot */ @@ -1229,8 +1246,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01); if (ret <= 0) { GTP_ERROR("[burn_dsp_isp]software reboot fail."); - ret = FAIL; - goto exit_burn_dsp_isp; + return FAIL; } /* step7:select bank2 */ @@ -1238,8 +1254,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02); if (ret <= 0) { GTP_ERROR("[burn_dsp_isp]select bank2 fail."); - ret = FAIL; - goto exit_burn_dsp_isp; + return FAIL; } /* step8:enable accessing code */ @@ -1247,8 +1262,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); if (ret <= 0) { GTP_ERROR("[burn_dsp_isp]enable accessing code fail."); - ret = FAIL; - goto exit_burn_dsp_isp; + return FAIL; } /* step9:burn 4k dsp_isp */ @@ -1256,7 +1270,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_dsp_isp]burn dsp_isp fail."); - goto exit_burn_dsp_isp; + return FAIL; } /* step10:set scramble */ @@ -1264,14 +1278,10 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); if (ret <= 0) { GTP_ERROR("[burn_dsp_isp]set scramble fail."); - ret = FAIL; - goto exit_burn_dsp_isp; + return FAIL; } - ret = SUCCESS; -exit_burn_dsp_isp: - kfree(fw_dsp_isp); - return ret; + return SUCCESS; } static u8 gup_burn_fw_ss51(struct i2c_client *client) @@ -1285,7 +1295,8 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) /* step1:alloc memory */ GTP_DEBUG("[burn_fw_ss51]step1:alloc memory"); while (retry++ < 5) { - fw_ss51 = kzalloc(FW_SECTION_LENGTH, GFP_KERNEL); + fw_ss51 = devm_kzalloc(&client->dev, FW_SECTION_LENGTH, + GFP_KERNEL); if (fw_ss51 == NULL) { continue; } else { @@ -1294,7 +1305,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) break; } } - if (retry >= 5) { + if (retry == 5) { GTP_ERROR("[burn_fw_ss51]Alloc memory fail,exit."); return FAIL; } @@ -1304,7 +1315,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 1 fail."); - goto exit_burn_fw_ss51; + return FAIL; } /* step3:clear control flag */ @@ -1312,8 +1323,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00); if (ret <= 0) { GTP_ERROR("[burn_fw_ss51]clear control flag fail."); - ret = FAIL; - goto exit_burn_fw_ss51; + return FAIL; } /* step4:burn ss51 firmware section 1 */ @@ -1321,7 +1331,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01); if (ret == FAIL) { GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 1 fail."); - goto exit_burn_fw_ss51; + return FAIL; } /* step5:load ss51 firmware section 2 file data */ @@ -1330,7 +1340,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) FW_SECTION_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 2 fail."); - goto exit_burn_fw_ss51; + return FAIL; } /* step6:burn ss51 firmware section 2 */ @@ -1338,7 +1348,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02); if (ret == FAIL) { GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 2 fail."); - goto exit_burn_fw_ss51; + return FAIL; } /* step7:load ss51 firmware section 3 file data */ @@ -1347,7 +1357,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) FW_SECTION_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 3 fail."); - goto exit_burn_fw_ss51; + return FAIL; } /* step8:burn ss51 firmware section 3 */ @@ -1355,7 +1365,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13); if (ret == FAIL) { GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 3 fail."); - goto exit_burn_fw_ss51; + return FAIL; } /* step9:load ss51 firmware section 4 file data */ @@ -1364,7 +1374,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) FW_SECTION_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 4 fail."); - goto exit_burn_fw_ss51; + return FAIL; } /* step10:burn ss51 firmware section 4 */ @@ -1372,14 +1382,10 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client) ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14); if (ret == FAIL) { GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 4 fail."); - goto exit_burn_fw_ss51; + return FAIL; } - ret = SUCCESS; - -exit_burn_fw_ss51: - kfree(fw_ss51); - return ret; + return SUCCESS; } static u8 gup_burn_fw_dsp(struct i2c_client *client) @@ -1393,7 +1399,8 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client) /* step1:alloc memory */ GTP_DEBUG("[burn_fw_dsp]step1:alloc memory"); while (retry++ < 5) { - fw_dsp = kzalloc(FW_DSP_LENGTH, GFP_KERNEL); + fw_dsp = devm_kzalloc(&client->dev, FW_DSP_LENGTH, + GFP_KERNEL); if (fw_dsp == NULL) { continue; } else { @@ -1402,7 +1409,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client) break; } } - if (retry >= 5) { + if (retry == 5) { GTP_ERROR("[burn_fw_dsp]Alloc memory fail,exit."); return FAIL; } @@ -1412,7 +1419,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client) ret = gup_load_section_file(fw_dsp, 4*FW_SECTION_LENGTH, FW_DSP_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_fw_dsp]load firmware dsp fail."); - goto exit_burn_fw_dsp; + return ret; } /* step3:select bank3 */ @@ -1420,8 +1427,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client) ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); if (ret <= 0) { GTP_ERROR("[burn_fw_dsp]select bank3 fail."); - ret = FAIL; - goto exit_burn_fw_dsp; + return FAIL; } /* Step4:hold ss51 & dsp */ @@ -1429,8 +1435,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); if (ret <= 0) { GTP_ERROR("[burn_fw_dsp]hold ss51 & dsp fail."); - ret = FAIL; - goto exit_burn_fw_dsp; + return FAIL; } /* step5:set scramble */ @@ -1438,8 +1443,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); if (ret <= 0) { GTP_ERROR("[burn_fw_dsp]set scramble fail."); - ret = FAIL; - goto exit_burn_fw_dsp; + return FAIL; } /* step6:release ss51 & dsp */ @@ -1447,8 +1451,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); if (ret <= 0) { GTP_ERROR("[burn_fw_dsp]release ss51 & dsp fail."); - ret = FAIL; - goto exit_burn_fw_dsp; + return FAIL; } /* must delay */ msleep(20); @@ -1458,7 +1461,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client) ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_fw_dsp]burn fw_section fail."); - goto exit_burn_fw_dsp; + return ret; } /* step8:send burn cmd to move data to flash from sram */ @@ -1467,14 +1470,14 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05); if (ret <= 0) { GTP_ERROR("[burn_fw_dsp]send burn cmd fail."); - goto exit_burn_fw_dsp; + return ret; } GTP_DEBUG("[burn_fw_dsp]Wait for the burn is complete......"); do { ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); if (ret <= 0) { GTP_ERROR("[burn_fw_dsp]Get burn state fail"); - goto exit_burn_fw_dsp; + return ret; } msleep(20); /* GTP_DEBUG("[burn_fw_dsp]Get burn state:%d.", @@ -1487,14 +1490,10 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client) ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_fw_dsp]recall check 4k dsp firmware fail."); - goto exit_burn_fw_dsp; + return ret; } ret = SUCCESS; - -exit_burn_fw_dsp: - kfree(fw_dsp); - return ret; } static u8 gup_burn_fw_boot(struct i2c_client *client) @@ -1509,7 +1508,8 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) /* step1:Alloc memory */ GTP_DEBUG("[burn_fw_boot]step1:Alloc memory"); while (retry++ < 5) { - fw_boot = kzalloc(FW_BOOT_LENGTH, GFP_KERNEL); + fw_boot = devm_kzalloc(&client->dev, FW_BOOT_LENGTH, + GFP_KERNEL); if (fw_boot == NULL) { continue; } else { @@ -1518,7 +1518,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) break; } } - if (retry >= 5) { + if (retry == 5) { GTP_ERROR("[burn_fw_boot]Alloc memory fail,exit."); return FAIL; } @@ -1529,7 +1529,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) FW_DSP_LENGTH), FW_BOOT_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_fw_boot]load firmware dsp fail."); - goto exit_burn_fw_boot; + return ret; } /* step3:hold ss51 & dsp */ @@ -1537,8 +1537,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); if (ret <= 0) { GTP_ERROR("[burn_fw_boot]hold ss51 & dsp fail."); - ret = FAIL; - goto exit_burn_fw_boot; + return FAIL; } /* step4:set scramble */ @@ -1546,8 +1545,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); if (ret <= 0) { GTP_ERROR("[burn_fw_boot]set scramble fail."); - ret = FAIL; - goto exit_burn_fw_boot; + return FAIL; } /* step5:release ss51 & dsp */ @@ -1555,8 +1553,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); if (ret <= 0) { GTP_ERROR("[burn_fw_boot]release ss51 & dsp fail."); - ret = FAIL; - goto exit_burn_fw_boot; + return FAIL; } /* must delay */ msleep(20); @@ -1566,8 +1563,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); if (ret <= 0) { GTP_ERROR("[burn_fw_boot]select bank3 fail."); - ret = FAIL; - goto exit_burn_fw_boot; + return FAIL; } /* step7:burn 2k bootloader firmware */ @@ -1575,7 +1571,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_fw_boot]burn fw_section fail."); - goto exit_burn_fw_boot; + return ret; } /* step7:send burn cmd to move data to flash from sram */ @@ -1584,14 +1580,14 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06); if (ret <= 0) { GTP_ERROR("[burn_fw_boot]send burn cmd fail."); - goto exit_burn_fw_boot; + return ret; } GTP_DEBUG("[burn_fw_boot]Wait for the burn is complete......"); do { ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); if (ret <= 0) { GTP_ERROR("[burn_fw_boot]Get burn state fail"); - goto exit_burn_fw_boot; + return ret; } msleep(20); /* GTP_DEBUG("[burn_fw_boot]Get burn state:%d.", @@ -1604,7 +1600,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH); if (ret == FAIL) { GTP_ERROR("[burn_fw_boot]recall check 4k dsp firmware fail."); - goto exit_burn_fw_boot; + return ret; } /* step9:enable download DSP code */ @@ -1612,8 +1608,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99); if (ret <= 0) { GTP_ERROR("[burn_fw_boot]enable download DSP code fail."); - ret = FAIL; - goto exit_burn_fw_boot; + return FAIL; } /* step10:release ss51 & hold dsp */ @@ -1621,15 +1616,10 @@ static u8 gup_burn_fw_boot(struct i2c_client *client) ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08); if (ret <= 0) { GTP_ERROR("[burn_fw_boot]release ss51 & hold dsp fail."); - ret = FAIL; - goto exit_burn_fw_boot; + return FAIL; } - ret = SUCCESS; - -exit_burn_fw_boot: - kfree(fw_boot); - return ret; + return SUCCESS; } s32 gup_update_proc(void *dir) diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c index 88b1d3c013c7..1a2afd1c1117 100644 --- a/drivers/input/touchscreen/it7258_ts_i2c.c +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -1383,7 +1383,7 @@ static int it7260_ts_chip_identify(struct it7260_ts_data *ts_data) 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; } static int it7260_regulator_configure(struct it7260_ts_data *ts_data, bool on) @@ -1950,7 +1950,7 @@ static int it7260_ts_probe(struct i2c_client *client, dev_err(&client->dev, "Unable to register fb_notifier %d\n", ret); #endif - + it7260_i2c_write_no_ready_check(ts_data, BUF_COMMAND, cmd_start, sizeof(cmd_start)); msleep(pdata->reset_delay); diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h new file mode 100644 index 000000000000..79d7d94582c5 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h @@ -0,0 +1,160 @@ +/* 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. + */ + +#ifndef MSM_CSIPHY_5_0_1_HWREG_H +#define MSM_CSIPHY_5_0_1_HWREG_H + +#define ULPM_WAKE_UP_TIMER_MODE 2 + +#include <sensor/csiphy/msm_csiphy.h> + +struct csiphy_reg_parms_t csiphy_v5_0_1 = { + .mipi_csiphy_interrupt_status0_addr = 0x8B0, + .mipi_csiphy_interrupt_clear0_addr = 0x858, + .mipi_csiphy_glbl_irq_cmd_addr = 0x828, + .combo_clk_mask = 0x10, +}; + +struct csiphy_reg_3ph_parms_t csiphy_v5_0_1_3ph = { + /*MIPI CSI PHY registers*/ + {0x814, 0xD5}, + {0x818, 0x1}, + {0x188, 0x7F}, + {0x18C, 0x7F}, + {0x190, 0x0}, + {0x104, 0x6}, + {0x108, 0x1}, + {0x10c, 0x12}, + {0x114, 0x20}, + {0x118, 0x3E}, + {0x11c, 0x41}, + {0x120, 0x41}, + {0x124, 0x7F}, + {0x128, 0x0}, + {0x12c, 0x0}, + {0x130, 0x1}, + {0x134, 0x0}, + {0x138, 0x0}, + {0x13C, 0x10}, + {0x140, 0x1}, + {0x144, 0x12}, + {0x148, 0xFE}, + {0x14C, 0x1}, + {0x154, 0x0}, + {0x15C, 0x23}, + {0x160, ULPM_WAKE_UP_TIMER_MODE}, + {0x164, 0x00}, + {0x168, 0xA0}, + {0x16C, 0x25}, + {0x170, 0x41}, + {0x174, 0x41}, + {0x178, 0x3E}, + {0x17C, 0x0}, + {0x180, 0x0}, + {0x184, 0x7F}, + {0x1cc, 0x41}, + {0x81c, 0x2}, + {0x82c, 0xFF}, + {0x830, 0xFF}, + {0x834, 0xFB}, + {0x838, 0xFF}, + {0x83c, 0x7F}, + {0x840, 0xFF}, + {0x844, 0xFF}, + {0x848, 0xEF}, + {0x84c, 0xFF}, + {0x850, 0xFF}, + {0x854, 0xFF}, + {0x28, 0x0}, + {0x800, 0x0}, + {0x4, 0xC}, + {0x8, 0x14}, + {0x8, 0x14}, + {0x10, 0x52}, + {0x14, 0x60}, + {0x14, 0x60}, + {0x1C, 0xa}, + {0x1c, 0xa}, + {0x38, 0x1}, + {0x3C, 0xB8}, + {0x3C, 0xB8}, + {0x14, 0x0}, + {0x14, 0x0}, + {0x700, 0xC0}, + {0x150, 0}, + {0x1dc, 0x51}, + {0x2C, 0x1}, + {0x34, 0xf}, + {0x728, 0x4}, + {0x0, 0x91}, + {0x70C, 0xA5}, + {0x38, 0xFE}, + {0x81c, 0x6}, +}; + +struct csiphy_settings_t csiphy_combo_mode_v5_0_1 = { + { + {0x818, 0x1}, + {0x81c, 0x2}, + {0x004, 0x08}, + {0x704, 0x08}, + {0x204, 0x08}, + {0x404, 0x08}, + {0x604, 0x08}, + {0x02c, 0x1}, + {0x22c, 0x1}, + {0x42c, 0x1}, + {0x62c, 0x1}, + {0x72c, 0x1}, + {0x034, 0x0f}, + {0x234, 0x0f}, + {0x434, 0x0f}, + {0x634, 0x0f}, + {0x734, 0x0f}, + {0x01c, 0x0a}, + {0x21c, 0x0a}, + {0x41c, 0x0a}, + {0x61c, 0x0a}, + {0x71c, 0x0a}, + {0x014, 0x60}, + {0x214, 0x60}, + {0x414, 0x60}, + {0x614, 0x60}, + {0x714, 0x60}, + {0x728, 0x4}, + {0x428, 0x0a}, + {0x628, 0x0e}, + {0x03c, 0xb8}, + {0x73c, 0xb8}, + {0x23c, 0xb8}, + {0x43c, 0xb8}, + {0x63c, 0xb8}, + {0x000, 0x91}, + {0x700, 0x80}, + {0x200, 0x91}, + {0x400, 0x91}, + {0x600, 0x80}, + {0x70c, 0xA5}, + {0x60c, 0xA5}, + {0x010, 0x52}, + {0x710, 0x52}, + {0x210, 0x52}, + {0x410, 0x52}, + {0x610, 0x52}, + {0x038, 0xfe}, + {0x738, 0x1f}, + {0x238, 0xfe}, + {0x438, 0xfe}, + {0x638, 0x1f}, + } +}; +#endif diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c index bdee620e8d45..6a1b385c3d8b 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c @@ -26,6 +26,7 @@ #include "include/msm_csiphy_3_4_2_hwreg.h" #include "include/msm_csiphy_3_5_hwreg.h" #include "include/msm_csiphy_5_0_hwreg.h" +#include "include/msm_csiphy_5_0_1_hwreg.h" #include "cam_hw_ops.h" #define DBG_CSIPHY 0 @@ -40,7 +41,8 @@ #define CSIPHY_VERSION_V32 0x32 #define CSIPHY_VERSION_V342 0x342 #define CSIPHY_VERSION_V35 0x35 -#define CSIPHY_VERSION_V50 0x50 +#define CSIPHY_VERSION_V50 0x500 +#define CSIPHY_VERSION_V501 0x501 #define MSM_CSIPHY_DRV_NAME "msm_csiphy" #define CLK_LANE_OFFSET 1 #define NUM_LANES_OFFSET 4 @@ -766,7 +768,7 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev, if (csiphy_dev->hw_version >= CSIPHY_VERSION_V30 && csiphy_dev->clk_mux_base != NULL && - csiphy_dev->hw_version != CSIPHY_VERSION_V50) { + csiphy_dev->hw_version < CSIPHY_VERSION_V50) { val = msm_camera_io_r(csiphy_dev->clk_mux_base); if (csiphy_params->combo_mode && (csiphy_params->lane_mask & 0x18) == 0x18) { @@ -789,7 +791,7 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev, rc = msm_camera_clk_enable(&csiphy_dev->pdev->dev, csiphy_dev->csiphy_3p_clk_info, csiphy_dev->csiphy_3p_clk, 2, true); - if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V50) + if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50) rc = msm_csiphy_3phase_lane_config_v50( csiphy_dev, csiphy_params); else @@ -797,7 +799,7 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev, csiphy_params); csiphy_dev->num_irq_registers = 20; } else { - if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V50) + if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50) rc = msm_csiphy_2phase_lane_config_v50( csiphy_dev, csiphy_params); else @@ -1201,7 +1203,7 @@ static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg) msm_camera_io_w(0x0, csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg. mipi_csiphy_3ph_cmn_ctrl6.addr); - if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V50) + if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50) msm_camera_io_w(0x0, csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg. @@ -1312,7 +1314,7 @@ static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg) msm_camera_io_w(0x0, csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg. mipi_csiphy_3ph_cmn_ctrl6.addr); - if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V50) + if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50) msm_camera_io_w(0x0, csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg. @@ -1694,6 +1696,14 @@ static int csiphy_probe(struct platform_device *pdev) new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW; new_csiphy_dev->ctrl_reg->csiphy_combo_mode_settings = csiphy_combo_mode_v5_0; + } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node, + "qcom,csiphy-v5.01")) { + new_csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_v5_0_1_3ph; + new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v5_0_1; + new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V501; + new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW; + new_csiphy_dev->ctrl_reg->csiphy_combo_mode_settings = + csiphy_combo_mode_v5_0_1; } else { pr_err("%s:%d, invalid hw version : 0x%x\n", __func__, __LINE__, new_csiphy_dev->hw_dts_version); 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 548131393835..e9d2dd5ec972 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c @@ -356,8 +356,6 @@ static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot) REGDMA_CSR_REGDMA_INVALID_CMD_RAM_OFFSET), SDE_ROTREG_READ(rot->mdss_base, REGDMA_CSR_REGDMA_FSM_STATE)); - - SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus", "panic"); } /** @@ -1096,6 +1094,9 @@ static u32 sde_hw_rotator_wait_done_regdma( sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0; + if (status & ROT_ERROR_BIT) + SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus", "panic"); + return sts; } diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c index b0e3bec683ed..b102264ca8fd 100644 --- a/drivers/mfd/wcd934x-regmap.c +++ b/drivers/mfd/wcd934x-regmap.c @@ -1848,6 +1848,20 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) if (reg_tbl && reg_tbl[reg_offset] == WCD934X_READ) return true; + /* IIR Coeff registers are not cacheable */ + if ((reg >= WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) && + (reg <= WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)) + return true; + + if ((reg >= WCD934X_CDC_ANC0_IIR_COEFF_1_CTL) && + (reg <= WCD934X_CDC_ANC0_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD934X_CDC_ANC1_IIR_COEFF_1_CTL) && + (reg <= WCD934X_CDC_ANC1_FB_GAIN_CTL)) + return true; + + /* * Need to mark volatile for registers that are writable but * only few bits are read-only diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c index a1c927c5ec0f..249de808ec5c 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -348,6 +348,11 @@ int ipa_query_intf_ext_props(struct ipa_ioc_query_intf_ext_props *ext) return result; } +static void ipa2_send_msg_free(void *buff, u32 len, u32 type) +{ + kfree(buff); +} + /** * ipa2_send_msg() - Send "message" from kernel client to IPA driver * @meta: [in] message meta-data @@ -367,6 +372,7 @@ int ipa2_send_msg(struct ipa_msg_meta *meta, void *buff, ipa_msg_free_fn callback) { struct ipa_push_msg *msg; + void *data = NULL; if (unlikely(!ipa_ctx)) { IPAERR("IPA driver was not initialized\n"); @@ -392,8 +398,17 @@ int ipa2_send_msg(struct ipa_msg_meta *meta, void *buff, } msg->meta = *meta; - msg->buff = buff; - msg->callback = callback; + if (meta->msg_len > 0 && buff) { + data = kmalloc(meta->msg_len, GFP_KERNEL); + if (data == NULL) { + IPAERR("fail to alloc data container\n"); + kfree(msg); + return -ENOMEM; + } + memcpy(data, buff, meta->msg_len); + msg->buff = data; + msg->callback = ipa2_send_msg_free; + } mutex_lock(&ipa_ctx->msg_lock); list_add_tail(&msg->link, &ipa_ctx->msg_list); @@ -401,6 +416,8 @@ int ipa2_send_msg(struct ipa_msg_meta *meta, void *buff, IPA_STATS_INC_CNT(ipa_ctx->stats.msg_w[meta->msg_type]); wake_up(&ipa_ctx->msg_waitq); + if (buff) + callback(buff, meta->msg_len, meta->msg_type); return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c index 32c5004dda95..22756c1fb168 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c @@ -358,6 +358,11 @@ int ipa3_query_intf_ext_props(struct ipa_ioc_query_intf_ext_props *ext) return result; } +static void ipa3_send_msg_free(void *buff, u32 len, u32 type) +{ + kfree(buff); +} + /** * ipa3_send_msg() - Send "message" from kernel client to IPA driver * @meta: [in] message meta-data @@ -377,6 +382,7 @@ int ipa3_send_msg(struct ipa_msg_meta *meta, void *buff, ipa_msg_free_fn callback) { struct ipa3_push_msg *msg; + void *data = NULL; if (meta == NULL || (buff == NULL && callback != NULL) || (buff != NULL && callback == NULL)) { @@ -397,8 +403,17 @@ int ipa3_send_msg(struct ipa_msg_meta *meta, void *buff, } msg->meta = *meta; - msg->buff = buff; - msg->callback = callback; + if (meta->msg_len > 0 && buff) { + data = kmalloc(meta->msg_len, GFP_KERNEL); + if (data == NULL) { + IPAERR("fail to alloc data container\n"); + kfree(msg); + return -ENOMEM; + } + memcpy(data, buff, meta->msg_len); + msg->buff = data; + msg->callback = ipa3_send_msg_free; + } mutex_lock(&ipa3_ctx->msg_lock); list_add_tail(&msg->link, &ipa3_ctx->msg_list); @@ -406,6 +421,8 @@ int ipa3_send_msg(struct ipa_msg_meta *meta, void *buff, IPA_STATS_INC_CNT(ipa3_ctx->stats.msg_w[meta->msg_type]); wake_up(&ipa3_ctx->msg_waitq); + if (buff) + callback(buff, meta->msg_len, meta->msg_type); return 0; } diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c index f34242f29e2b..38a6474dd06f 100644 --- a/drivers/platform/msm/sps/sps_bam.c +++ b/drivers/platform/msm/sps/sps_bam.c @@ -1083,6 +1083,7 @@ int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index) { struct sps_pipe *pipe; int result; + unsigned long flags; if (pipe_index >= dev->props.num_pipes) { SPS_ERR(dev, "sps:Invalid BAM %pa pipe: %d\n", BAM_ID(dev), @@ -1094,8 +1095,10 @@ int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index) pipe = dev->pipes[pipe_index]; if (BAM_PIPE_IS_ASSIGNED(pipe)) { if ((dev->pipe_active_mask & (1UL << pipe_index))) { + spin_lock_irqsave(&dev->isr_lock, flags); list_del(&pipe->list); dev->pipe_active_mask &= ~(1UL << pipe_index); + spin_unlock_irqrestore(&dev->isr_lock, flags); } dev->pipe_remote_mask &= ~(1UL << pipe_index); if (pipe->connect.options & SPS_O_NO_DISABLE) diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index f83b641bcc4f..7caa9548308a 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -279,10 +279,10 @@ static int smb2_parse_dt(struct smb2 *chip) if (rc < 0) chip->dt.dc_icl_ua = -EINVAL; - rc = of_property_read_u32(node, - "qcom,wipower-max-uw", &chip->dt.wipower_max_uw); + rc = of_property_read_u32(node, "qcom,wipower-max-uw", + &chip->dt.wipower_max_uw); if (rc < 0) - chip->dt.wipower_max_uw = SMB2_DEFAULT_WPWR_UW; + chip->dt.wipower_max_uw = -EINVAL; if (of_find_property(node, "qcom,thermal-mitigation", &byte_len)) { chg->thermal_mitigation = devm_kzalloc(chg->dev, byte_len, @@ -392,7 +392,11 @@ static int smb2_usb_get_prop(struct power_supply *psy, rc = -EINVAL; break; } - return rc; + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + return 0; } static int smb2_usb_set_prop(struct power_supply *psy, @@ -506,8 +510,11 @@ static int smb2_dc_get_prop(struct power_supply *psy, default: return -EINVAL; } - - return rc; + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + return 0; } static int smb2_dc_set_prop(struct power_supply *psy, @@ -633,8 +640,11 @@ static int smb2_batt_get_prop(struct power_supply *psy, pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; } - - return rc; + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + return 0; } static int smb2_batt_set_prop(struct power_supply *psy, @@ -866,6 +876,9 @@ static int smb2_config_wipower_input_power(struct smb2 *chip, int uw) struct smb_charger *chg = &chip->chg; s64 nw = (s64)uw * 1000; + if (uw < 0) + return 0; + ua = div_s64(nw, ZIN_ICL_PT_MAX_MV); rc = smblib_set_charge_param(chg, &chg->param.dc_icl_pt_lv, ua); if (rc < 0) { diff --git a/drivers/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c index a77b0bbc193c..5b4e7bcccdce 100644 --- a/drivers/power/qcom-charger/smb138x-charger.c +++ b/drivers/power/qcom-charger/smb138x-charger.c @@ -184,8 +184,11 @@ static int smb138x_usb_get_prop(struct power_supply *psy, pr_err("get prop %d is not supported\n", prop); return -EINVAL; } - - return rc; + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", prop, rc); + return -ENODATA; + } + return 0; } static int smb138x_usb_set_prop(struct power_supply *psy, @@ -305,12 +308,14 @@ static int smb138x_batt_get_prop(struct power_supply *psy, rc = smblib_get_prop_charger_temp_max(chg, val); break; default: - pr_err("batt power supply get prop %d not supported\n", - prop); + pr_err("batt power supply get prop %d not supported\n", prop); return -EINVAL; } - - return rc; + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", prop, rc); + return -ENODATA; + } + return 0; } static int smb138x_batt_set_prop(struct power_supply *psy, @@ -436,8 +441,11 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, prop); return -EINVAL; } - - return rc; + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", prop, rc); + return -ENODATA; + } + return 0; } static int smb138x_parallel_set_prop(struct power_supply *psy, diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d49d8606da15..27a5deb1213e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -150,7 +150,7 @@ static void regulator_lock_supply(struct regulator_dev *rdev) { int i; - for (i = 0; rdev->supply; rdev = rdev_get_supply(rdev), i++) + for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++) mutex_lock_nested(&rdev->mutex, i); } diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index 232373092746..cd02debc37aa 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -3362,7 +3362,8 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl, if (rc) { cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n", rc); - goto cleanup; + kfree(quot_delta_results); + return rc; } } diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index fde0d408d27e..ad4b6ffef36e 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -764,21 +764,29 @@ out: static int ufs_qcom_full_reset(struct ufs_hba *hba) { - struct ufs_clk_info *clki; int ret = -ENOTSUPP; - list_for_each_entry(clki, &hba->clk_list_head, list) { - if (!strcmp(clki->name, "core_clk")) { - ret = clk_reset(clki->clk, CLK_RESET_ASSERT); - if (ret) - goto out; - /* Very small delay, per the documented requirement */ - usleep_range(1, 2); - - ret = clk_reset(clki->clk, CLK_RESET_DEASSERT); - break; - } + if (!hba->core_reset) { + dev_err(hba->dev, "%s: failed, err = %d\n", __func__, + ret); + goto out; } + + ret = reset_control_assert(hba->core_reset); + if (ret) { + dev_err(hba->dev, "%s: core_reset assert failed, err = %d\n", + __func__, ret); + goto out; + } + + /* Very small delay, per the documented requirement */ + usleep_range(1, 2); + + ret = reset_control_deassert(hba->core_reset); + if (ret) + dev_err(hba->dev, "%s: core_reset deassert failed, err = %d\n", + __func__, ret); + out: return ret; } diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 19c378c72083..5a9564326099 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -40,6 +40,22 @@ #include "ufshcd.h" #include "ufshcd-pltfrm.h" +static int ufshcd_parse_reset_info(struct ufs_hba *hba) +{ + int ret = 0; + + hba->core_reset = devm_reset_control_get(hba->dev, + "core_reset"); + if (IS_ERR(hba->core_reset)) { + ret = PTR_ERR(hba->core_reset); + dev_err(hba->dev, "core_reset unavailable,err = %d\n", + ret); + hba->core_reset = NULL; + } + + return ret; +} + static int ufshcd_parse_clock_info(struct ufs_hba *hba) { int ret = 0; @@ -338,6 +354,13 @@ int ufshcd_pltfrm_init(struct platform_device *pdev, goto dealloc_host; } + err = ufshcd_parse_reset_info(hba); + if (err) { + dev_err(&pdev->dev, "%s: reset parse failed %d\n", + __func__, err); + goto dealloc_host; + } + ufshcd_parse_pm_levels(hba); if (!dev->dma_mask) diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index b79bebb58dcd..a6298f614a0b 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -55,6 +55,7 @@ #include <linux/clk.h> #include <linux/completion.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include "unipro.h" #include <asm/irq.h> @@ -893,6 +894,7 @@ struct ufs_hba { struct rw_semaphore clk_scaling_lock; + struct reset_control *core_reset; /* If set, don't gate device ref_clk during clock gating */ bool no_ref_clk_gating; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index f884f829b51c..9c27344165be 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -127,6 +127,17 @@ config MSM_SPCOM spcom provides clients/server API, although currently only one client or server is allowed per logical channel. +config MSM_SPSS_UTILS + depends on MSM_PIL + bool "Secure Processor Utilities" + help + spss-utils driver selects Secure Processor firmware file name. + The firmware file name for test or production is selected based + on a test fuse. + Different file name is used for differnt SPSS HW versions, + because the SPSS firmware size is too small to support multiple + HW versions. + config MSM_SMEM_LOGGING depends on MSM_SMEM bool "MSM Shared Memory Logger" diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index fb8437d7a7c3..d9134a558be6 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_MSM_IPC_ROUTER_HSIC_XPRT) += ipc_router_hsic_xprt.o obj-$(CONFIG_MSM_IPC_ROUTER_MHI_XPRT) += ipc_router_mhi_xprt.o obj-$(CONFIG_MSM_IPC_ROUTER_GLINK_XPRT) += ipc_router_glink_xprt.o obj-$(CONFIG_MSM_SPCOM) += spcom.o +obj-$(CONFIG_MSM_SPSS_UTILS) += spss_utils.o obj-y += qdsp6v2/ obj-$(CONFIG_MSM_SYSTEM_HEALTH_MONITOR) += system_health_monitor_v01.o obj-$(CONFIG_MSM_SYSTEM_HEALTH_MONITOR) += system_health_monitor.o diff --git a/drivers/soc/qcom/common_log.c b/drivers/soc/qcom/common_log.c index d2d877ef427b..f4c69d624342 100644 --- a/drivers/soc/qcom/common_log.c +++ b/drivers/soc/qcom/common_log.c @@ -23,7 +23,6 @@ #define PMIC_DUMP_DATA_LEN 4096 #define VSENSE_DUMP_DATA_LEN 4096 #define RPM_DUMP_DATA_LEN (160 * 1024) -#define SCAN_DUMP_DATA_LEN (256 * 1024) void register_misc_dump(void) { @@ -154,38 +153,6 @@ err0: } } -void register_scan_dump(void) -{ - static void *dump_addr; - int ret; - struct msm_dump_entry dump_entry; - struct msm_dump_data *dump_data; - - if (MSM_DUMP_MAJOR(msm_dump_table_version()) > 1) { - dump_data = kzalloc(sizeof(struct msm_dump_data), GFP_KERNEL); - if (!dump_data) - return; - dump_addr = kzalloc(SCAN_DUMP_DATA_LEN, GFP_KERNEL); - if (!dump_addr) - goto err0; - - dump_data->addr = virt_to_phys(dump_addr); - dump_data->len = SCAN_DUMP_DATA_LEN; - dump_entry.id = MSM_DUMP_DATA_SCANDUMP; - dump_entry.addr = virt_to_phys(dump_data); - ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); - if (ret) { - pr_err("Registering scandump region failed\n"); - goto err1; - } - return; -err1: - kfree(dump_addr); -err0: - kfree(dump_data); - } -} - static void __init common_log_register_log_buf(void) { char **log_bufp; @@ -257,7 +224,6 @@ static int __init msm_common_log_init(void) register_pmic_dump(); register_vsense_dump(); register_rpm_dump(); - register_scan_dump(); return 0; } late_initcall(msm_common_log_init); diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c index f00570aa5fe8..5612075ba60c 100644 --- a/drivers/soc/qcom/glink.c +++ b/drivers/soc/qcom/glink.c @@ -5376,7 +5376,7 @@ static int glink_scheduler_tx(struct channel_ctx *ctx, struct glink_core_xprt_ctx *xprt_ctx) { unsigned long flags; - struct glink_core_tx_pkt *tx_info; + struct glink_core_tx_pkt *tx_info, *temp_tx_info; size_t txd_len = 0; size_t tx_len = 0; uint32_t num_pkts = 0; @@ -5411,6 +5411,20 @@ static int glink_scheduler_tx(struct channel_ctx *ctx, ctx->lcid, tx_info); } spin_lock_irqsave(&ctx->tx_lists_lock_lhc3, flags); + if (!list_empty(&ctx->tx_active)) { + /* + * Verify if same tx_info still exist in tx_active + * list and is not removed during tx operation. + * It can happen if SSR and tx done both happen + * before tx_lists_lock_lhc3 is taken. + */ + temp_tx_info = list_first_entry(&ctx->tx_active, + struct glink_core_tx_pkt, list_node); + if (temp_tx_info != tx_info) + continue; + } else { + break; + } if (ret == -EAGAIN) { /* * transport unable to send at the moment and will call @@ -5437,6 +5451,7 @@ static int glink_scheduler_tx(struct channel_ctx *ctx, * Break out of the loop so that the scheduler can * continue with the next channel. */ + rwref_put(&tx_info->pkt_ref); break; } else { txd_len += tx_len; @@ -5445,8 +5460,8 @@ static int glink_scheduler_tx(struct channel_ctx *ctx, if (!tx_info->size_remaining) { num_pkts++; list_del_init(&tx_info->list_node); - rwref_put(&tx_info->pkt_ref); } + rwref_put(&tx_info->pkt_ref); } ctx->txd_len += txd_len; @@ -5495,6 +5510,7 @@ static void tx_func(struct kthread_work *work) glink_pm_qos_vote(xprt_ptr); ch_ptr = list_first_entry(&xprt_ptr->prio_bin[prio].tx_ready, struct channel_ctx, tx_ready_list_node); + rwref_get(&ch_ptr->ch_state_lhb2); spin_unlock_irqrestore(&xprt_ptr->tx_ready_lock_lhb3, flags); if (tx_ready_head == NULL || tx_ready_head_prio < prio) { @@ -5506,6 +5522,7 @@ static void tx_func(struct kthread_work *work) GLINK_ERR_XPRT(xprt_ptr, "%s: Unable to send data on this transport.\n", __func__); + rwref_put(&ch_ptr->ch_state_lhb2); break; } transmitted_successfully = false; @@ -5516,6 +5533,7 @@ static void tx_func(struct kthread_work *work) * transport unable to send at the moment and will call * tx_resume() when it can send again. */ + rwref_put(&ch_ptr->ch_state_lhb2); break; } else if (ret < 0) { /* @@ -5528,6 +5546,7 @@ static void tx_func(struct kthread_work *work) GLINK_ERR_XPRT(xprt_ptr, "%s: unrecoverable xprt failure %d\n", __func__, ret); + rwref_put(&ch_ptr->ch_state_lhb2); break; } else if (!ret) { /* @@ -5539,6 +5558,7 @@ static void tx_func(struct kthread_work *work) list_rotate_left(&xprt_ptr->prio_bin[prio].tx_ready); spin_unlock_irqrestore(&xprt_ptr->tx_ready_lock_lhb3, flags); + rwref_put(&ch_ptr->ch_state_lhb2); continue; } @@ -5556,6 +5576,7 @@ static void tx_func(struct kthread_work *work) tx_ready_head = NULL; transmitted_successfully = true; + rwref_put(&ch_ptr->ch_state_lhb2); } glink_pm_qos_unvote(xprt_ptr); GLINK_PERF("%s: worker exiting\n", __func__); diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 05a9ff4aeb1c..e56030e3b116 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -281,6 +281,7 @@ enum icnss_driver_state { ICNSS_SSR_ENABLED, ICNSS_PDR_ENABLED, ICNSS_PD_RESTART, + ICNSS_MSA0_ASSIGNED, }; struct ce_irq_list { @@ -1320,7 +1321,7 @@ int icnss_hw_reset(struct icnss_priv *priv) MPM_WCSSAON_CONFIG_FORCE_ACTIVE, 1); icnss_hw_poll_reg_field(priv->mem_base_va, SR_WCSSAON_SR_LSB_OFFSET, - SR_WCSSAON_SR_LSB_RETENTION_STATUS, 1, 100, + SR_WCSSAON_SR_LSB_RETENTION_STATUS, 1, 200, ICNSS_HW_REG_RETRY); for (i = 0; i < ICNSS_HW_REG_RETRY; i++) { @@ -1348,11 +1349,13 @@ int icnss_hw_reset(struct icnss_priv *priv) } if (i >= ICNSS_HW_REG_RETRY) - ICNSS_ASSERT(false); + goto top_level_reset; icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET, MPM_WCSSAON_CONFIG_DISCONNECT_CLR, 0x1); + usleep_range(200, 300); + icnss_hw_reset_wlan_ss_power_down(priv); icnss_hw_reset_common_ss_power_down(priv); @@ -1482,7 +1485,7 @@ int icnss_power_off(struct device *dev) } EXPORT_SYMBOL(icnss_power_off); -int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) +static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) { int ret = 0; phys_addr_t addr; @@ -1520,7 +1523,7 @@ out: } -int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) +static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) { int ret = 0; phys_addr_t addr; @@ -1558,7 +1561,10 @@ out: static int icnss_setup_msa_permissions(struct icnss_priv *priv) { - int ret = 0; + int ret; + + if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state)) + return 0; ret = icnss_map_msa_permissions(priv, 0); if (ret) @@ -1568,6 +1574,8 @@ static int icnss_setup_msa_permissions(struct icnss_priv *priv) if (ret) goto err_map_msa; + set_bit(ICNSS_MSA0_ASSIGNED, &priv->state); + return ret; err_map_msa: @@ -1577,8 +1585,13 @@ err_map_msa: static void icnss_remove_msa_permissions(struct icnss_priv *priv) { + if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state)) + return; + icnss_unmap_msa_permissions(priv, 0); icnss_unmap_msa_permissions(priv, 1); + + clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state); } static int wlfw_msa_mem_info_send_sync_msg(void) @@ -2435,12 +2448,17 @@ out: return 0; } -static int icnss_qmi_pd_event_service_down(struct icnss_priv *priv, void *data) +static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, + void *data) { int ret = 0; - if (test_bit(ICNSS_PD_RESTART, &priv->state)) + if (test_bit(ICNSS_PD_RESTART, &priv->state)) { + icnss_pr_err("PD Down while recovery inprogress, state: 0x%lx\n", + priv->state); + ICNSS_ASSERT(0); goto out; + } set_bit(ICNSS_PD_RESTART, &priv->state); clear_bit(ICNSS_FW_READY, &priv->state); @@ -2458,7 +2476,7 @@ out: ret = icnss_hw_power_off(priv); - icnss_pr_dbg("Shutdown completed: %d, state: 0x%lx\n", + icnss_pr_dbg("PD down completed: %d, state: 0x%lx\n", ret, priv->state); return ret; @@ -2500,7 +2518,7 @@ static void icnss_driver_event_work(struct work_struct *work) ret = icnss_driver_event_unregister_driver(event->data); break; case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN: - icnss_qmi_pd_event_service_down(penv, event->data); + icnss_driver_event_pd_service_down(penv, event->data); break; default: icnss_pr_err("Invalid Event type: %d", event->type); @@ -3685,7 +3703,7 @@ static ssize_t icnss_stats_write(struct file *fp, const char __user *buf, static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) { - int i; + enum icnss_driver_state i; int skip = 0; unsigned long state; @@ -3714,11 +3732,14 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_FW_TEST_MODE: seq_puts(s, "FW TEST MODE"); continue; + case ICNSS_SUSPEND: + seq_puts(s, "SUSPEND"); + continue; case ICNSS_PM_SUSPEND: seq_puts(s, "PM SUSPEND"); continue; case ICNSS_PM_SUSPEND_NOIRQ: - seq_puts(s, "PM SUSPEND_NOIRQ"); + seq_puts(s, "PM SUSPEND NOIRQ"); continue; case ICNSS_SSR_ENABLED: seq_puts(s, "SSR ENABLED"); @@ -3729,6 +3750,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_PD_RESTART: seq_puts(s, "PD RESTART"); continue; + case ICNSS_MSA0_ASSIGNED: + seq_puts(s, "MSA0 ASSIGNED"); + continue; } seq_printf(s, "UNKNOWN-%d", i); diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index 81dde8ca1ae8..981f78491ecf 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -386,7 +386,8 @@ static void root_service_service_arrive(struct work_struct *work) mutex_unlock(¬if_add_lock); } -static void root_service_service_exit(struct qmi_client_info *data) +static void root_service_service_exit(struct qmi_client_info *data, + enum pd_subsys_state state) { struct service_notif_info *service_notif = NULL; int rc; @@ -401,7 +402,7 @@ static void root_service_service_exit(struct qmi_client_info *data) if (service_notif->instance_id == data->instance_id) { rc = service_notif_queue_notification(service_notif, SERVREG_NOTIF_SERVICE_STATE_DOWN_V01, - NULL); + &state); if (rc & NOTIFY_STOP_MASK) pr_err("Notifier callback aborted for %s with error %d\n", service_notif->service_path, rc); @@ -425,7 +426,7 @@ static void root_service_exit_work(struct work_struct *work) { struct qmi_client_info *data = container_of(work, struct qmi_client_info, svc_exit); - root_service_service_exit(data); + root_service_service_exit(data, UNKNOWN); } static int service_event_notify(struct notifier_block *this, @@ -456,10 +457,15 @@ static int ssr_event_notify(struct notifier_block *this, { struct qmi_client_info *info = container_of(this, struct qmi_client_info, ssr_notifier); + struct notif_data *notif = data; switch (code) { case SUBSYS_BEFORE_SHUTDOWN: - pr_debug("Root PD service Down (SSR notification)\n"); - root_service_service_exit(info); + pr_debug("Root PD DOWN(SSR notification), crashed?%d\n", + notif->crashed); + if (notif->crashed) + root_service_service_exit(info, CRASHED); + else + root_service_service_exit(info, SHUTDOWN); break; default: break; diff --git a/drivers/soc/qcom/spss_utils.c b/drivers/soc/qcom/spss_utils.c new file mode 100644 index 000000000000..9f799dfd9003 --- /dev/null +++ b/drivers/soc/qcom/spss_utils.c @@ -0,0 +1,294 @@ +/* + * 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. + */ + +/* + * Secure-Processor-SubSystem (SPSS) utilities. + * + * This driver provides utilities for the Secure Processor (SP). + * + * The SP daemon needs to load different SPSS images based on: + * + * 1. Test/Production key used to sign the SPSS image (read fuse). + * 2. SPSS HW version (selected via Device Tree). + * + */ + +#define pr_fmt(fmt) "spss_utils [%s]: " fmt, __func__ + +#include <linux/kernel.h> /* min() */ +#include <linux/module.h> /* MODULE_LICENSE */ +#include <linux/device.h> /* class_create() */ +#include <linux/slab.h> /* kzalloc() */ +#include <linux/fs.h> /* file_operations */ +#include <linux/cdev.h> /* cdev_add() */ +#include <linux/errno.h> /* EINVAL, ETIMEDOUT */ +#include <linux/printk.h> /* pr_err() */ +#include <linux/bitops.h> /* BIT(x) */ +#include <linux/platform_device.h> /* platform_driver_register() */ +#include <linux/of.h> /* of_property_count_strings() */ +#include <linux/io.h> /* ioremap_nocache() */ + +#include <soc/qcom/subsystem_restart.h> + +/* driver name */ +#define DEVICE_NAME "spss-utils" + +static bool is_test_fuse_set; +static const char *test_firmware_name; +static const char *prod_firmware_name; +static const char *firmware_name; +static struct device *spss_dev; + +/*==========================================================================*/ +/* Device Sysfs */ +/*==========================================================================*/ + +static ssize_t firmware_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + + if (!dev || !attr || !buf) { + pr_err("invalid param.\n"); + return -EINVAL; + } + + if (firmware_name == NULL) + ret = snprintf(buf, PAGE_SIZE, "%s\n", "unknown"); + else + ret = snprintf(buf, PAGE_SIZE, "%s\n", firmware_name); + + return ret; +} + +static ssize_t firmware_name_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + pr_err("set firmware name is not allowed.\n"); + + return -EINVAL; +} + +static DEVICE_ATTR(firmware_name, 0444, + firmware_name_show, firmware_name_store); + +static ssize_t test_fuse_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + + if (!dev || !attr || !buf) { + pr_err("invalid param.\n"); + return -EINVAL; + } + + if (is_test_fuse_set) + ret = snprintf(buf, PAGE_SIZE, "%s\n", "test"); + else + ret = snprintf(buf, PAGE_SIZE, "%s\n", "prod"); + + return ret; +} + +static ssize_t test_fuse_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + pr_err("set test fuse state is not allowed.\n"); + + return -EINVAL; +} + +static DEVICE_ATTR(test_fuse_state, 0444, + test_fuse_state_show, test_fuse_state_store); + +static int spss_create_sysfs(struct device *dev) +{ + int ret; + + ret = device_create_file(dev, &dev_attr_firmware_name); + if (ret < 0) { + pr_err("failed to create sysfs file for firmware_name.\n"); + return ret; + } + + ret = device_create_file(dev, &dev_attr_test_fuse_state); + if (ret < 0) { + pr_err("failed to create sysfs file for test_fuse_state.\n"); + return ret; + } + + return 0; +} + +/*==========================================================================*/ +/* Device Tree */ +/*==========================================================================*/ + +/** + * spss_parse_dt() - Parse Device Tree info. + */ +static int spss_parse_dt(struct device_node *node) +{ + int ret; + u32 spss_fuse_addr = 0; + u32 spss_fuse_bit = 0; + u32 spss_fuse_mask = 0; + void __iomem *spss_fuse_reg = NULL; + u32 val = 0; + + ret = of_property_read_string(node, "qcom,spss-test-firmware-name", + &test_firmware_name); + if (ret < 0) { + pr_err("can't get test fw name.\n"); + return -EFAULT; + } + + ret = of_property_read_string(node, "qcom,spss-prod-firmware-name", + &prod_firmware_name); + if (ret < 0) { + pr_err("can't get prod fw name.\n"); + return -EFAULT; + } + + ret = of_property_read_u32(node, "qcom,spss-fuse-addr", + &spss_fuse_addr); + if (ret < 0) { + pr_err("can't get fuse addr.\n"); + return -EFAULT; + } + + ret = of_property_read_u32(node, "qcom,spss-fuse-bit", + &spss_fuse_bit); + if (ret < 0) { + pr_err("can't get fuse bit.\n"); + return -EFAULT; + } + + spss_fuse_mask = BIT(spss_fuse_bit); + + pr_debug("spss_fuse_addr [0x%x] , spss_fuse_bit [%d] .\n", + (int) spss_fuse_addr, (int) spss_fuse_bit); + + spss_fuse_reg = ioremap_nocache(spss_fuse_addr, sizeof(u32)); + + if (!spss_fuse_reg) { + pr_err("can't map fuse addr.\n"); + return -EFAULT; + } + + val = readl_relaxed(spss_fuse_reg); + + pr_debug("spss fuse register value [0x%x].\n", (int) val); + + if (val & spss_fuse_mask) + is_test_fuse_set = true; + + iounmap(spss_fuse_reg); + + return 0; +} + +/** + * spss_probe() - initialization sequence + */ +static int spss_probe(struct platform_device *pdev) +{ + int ret = 0; + struct device_node *np = NULL; + struct device *dev = NULL; + + if (!pdev) { + pr_err("invalid pdev.\n"); + return -ENODEV; + } + + np = pdev->dev.of_node; + if (!np) { + pr_err("invalid DT node.\n"); + return -EINVAL; + } + + dev = &pdev->dev; + spss_dev = dev; + + if (dev == NULL) { + pr_err("invalid dev.\n"); + return -EINVAL; + } + + platform_set_drvdata(pdev, dev); + + ret = spss_parse_dt(np); + if (ret < 0) { + pr_err("fail to parse device tree.\n"); + return -EFAULT; + } + + if (is_test_fuse_set) + firmware_name = test_firmware_name; + else + firmware_name = prod_firmware_name; + + ret = subsystem_set_fwname("spss", firmware_name); + if (ret < 0) { + pr_err("fail to set fw name.\n"); + return -EFAULT; + } + + spss_create_sysfs(dev); + + pr_info("Initialization completed ok, firmware_name [%s].\n", + firmware_name); + + return 0; +} + +static const struct of_device_id spss_match_table[] = { + { .compatible = "qcom,spss-utils", }, + { }, +}; + +static struct platform_driver spss_driver = { + .probe = spss_probe, + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(spss_match_table), + }, +}; + +/*==========================================================================*/ +/* Driver Init/Exit */ +/*==========================================================================*/ +static int __init spss_init(void) +{ + int ret = 0; + + pr_info("spss-utils driver Ver 1.0 12-Sep-2016.\n"); + + ret = platform_driver_register(&spss_driver); + if (ret) + pr_err("register platform driver failed, ret [%d]\n", ret); + + return 0; +} +late_initcall(spss_init); /* start after PIL driver */ + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Secure Processor Utilities"); diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index 6a1a87ead6e4..ae175e176aa3 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -518,8 +518,7 @@ err: static void disable_unprepare_clocks(struct clk **clks, int clk_count) { int i; - - for (i = 0; i < clk_count; i++) + for (i = --clk_count; i >= 0; i--) clk_disable_unprepare(clks[i]); } diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c index 65eda2de9586..aa20705b9adc 100644 --- a/drivers/soc/qcom/watchdog_v2.c +++ b/drivers/soc/qcom/watchdog_v2.c @@ -90,6 +90,7 @@ struct msm_watchdog_data { bool timer_expired; bool user_pet_complete; + unsigned int scandump_size; }; /* @@ -501,6 +502,39 @@ static irqreturn_t wdog_ppi_bark(int irq, void *dev_id) return wdog_bark_handler(irq, wdog_dd); } +void register_scan_dump(struct msm_watchdog_data *wdog_dd) +{ + static void *dump_addr; + int ret; + struct msm_dump_entry dump_entry; + struct msm_dump_data *dump_data; + + if (!wdog_dd->scandump_size) + return; + + dump_data = kzalloc(sizeof(struct msm_dump_data), GFP_KERNEL); + if (!dump_data) + return; + dump_addr = kzalloc(wdog_dd->scandump_size, GFP_KERNEL); + if (!dump_addr) + goto err0; + + dump_data->addr = virt_to_phys(dump_addr); + dump_data->len = wdog_dd->scandump_size; + dump_entry.id = MSM_DUMP_DATA_SCANDUMP; + dump_entry.addr = virt_to_phys(dump_data); + ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); + if (ret) { + pr_err("Registering scandump region failed\n"); + goto err1; + } + return; +err1: + kfree(dump_addr); +err0: + kfree(dump_data); +} + static void configure_bark_dump(struct msm_watchdog_data *wdog_dd) { int ret; @@ -582,6 +616,8 @@ static void configure_bark_dump(struct msm_watchdog_data *wdog_dd) if (ret) pr_err("cpu %d reg dump setup failed\n", cpu); } + + register_scan_dump(wdog_dd); } return; @@ -770,6 +806,11 @@ static int msm_wdog_dt_to_pdata(struct platform_device *pdev, pdata->wakeup_irq_enable = of_property_read_bool(node, "qcom,wakeup-enable"); + if (of_property_read_u32(node, "qcom,scandump-size", + &pdata->scandump_size)) + dev_info(&pdev->dev, + "No need to allocate memory for scandumps\n"); + pdata->irq_ppi = irq_is_percpu(pdata->bark_irq); dump_pdata(pdata); return 0; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d90df256796c..a798c4fa8812 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -539,7 +539,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, dep->stream_capable = true; } - if (!usb_endpoint_xfer_control(desc)) + if (usb_endpoint_xfer_isoc(desc)) params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN; /* @@ -891,11 +891,19 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; else trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS; + + if (!req->request.no_interrupt && !chain) + trb->ctrl |= DWC3_TRB_CTRL_IOC; break; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: trb->ctrl = DWC3_TRBCTL_NORMAL; + if (req->request.num_mapped_sgs > 0) { + if (!last && !chain && + !req->request.no_interrupt) + trb->ctrl |= DWC3_TRB_CTRL_IOC; + } break; default: /* @@ -905,9 +913,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, BUG(); } - if (!req->request.no_interrupt && !chain) - trb->ctrl |= DWC3_TRB_CTRL_IOC; - if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; trb->ctrl |= DWC3_TRB_CTRL_CSP; @@ -2477,6 +2482,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, return 1; } + if ((event->status & DEPEVT_STATUS_IOC) && + (trb->ctrl & DWC3_TRB_CTRL_IOC)) + return 0; return 1; } @@ -2564,6 +2572,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, break; case DWC3_DEPEVT_XFERINPROGRESS: dep->dbg_ep_events.xferinprogress++; + if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n", + dep->name); + return; + } + dwc3_endpoint_transfer_complete(dwc, dep, event); break; case DWC3_DEPEVT_XFERNOTREADY: diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index 2f6f393ba7c9..bc1c34f05fa3 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -629,8 +629,6 @@ static ssize_t acc_read(struct file *fp, char __user *buf, if (count > BULK_BUFFER_SIZE) count = BULK_BUFFER_SIZE; - len = ALIGN(count, dev->ep_out->maxpacket); - /* we will block until we're online */ pr_debug("acc_read: waiting for online\n"); ret = wait_event_interruptible(dev->read_wq, dev->online); @@ -639,6 +637,8 @@ static ssize_t acc_read(struct file *fp, char __user *buf, goto done; } + len = ALIGN(count, dev->ep_out->maxpacket); + if (dev->rx_done) { // last req cancelled. try to get it. req = dev->rx_req[0]; diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 80c9928e948e..c298c95d4ba0 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -1380,12 +1380,6 @@ static int gsi_ctrl_send_notification(struct f_gsi *gsi, return -ENODEV; } - if (atomic_inc_return(&gsi->c_port.notify_count) != 1) { - log_event_dbg("delay ep_queue: notify req is busy %d", - atomic_read(&gsi->c_port.notify_count)); - return 0; - } - event = req->buf; switch (state) { @@ -1441,6 +1435,12 @@ static int gsi_ctrl_send_notification(struct f_gsi *gsi, log_event_dbg("send Notify type %02x", event->bNotificationType); + if (atomic_inc_return(&gsi->c_port.notify_count) != 1) { + log_event_dbg("delay ep_queue: notify req is busy %d", + atomic_read(&gsi->c_port.notify_count)); + return 0; + } + return queue_notification_request(gsi); } @@ -1490,8 +1490,7 @@ static void gsi_rndis_response_available(void *_rndis) { struct f_gsi *gsi = _rndis; - gsi_ctrl_send_notification(gsi, - GSI_CTRL_NOTIFY_RESPONSE_AVAILABLE); + gsi_ctrl_send_notification(gsi, GSI_CTRL_NOTIFY_RESPONSE_AVAILABLE); } static void gsi_rndis_command_complete(struct usb_ep *ep, @@ -1959,14 +1958,14 @@ static int gsi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (gsi->prot_id == IPA_USB_ECM) gsi->d_port.cdc_filter = DEFAULT_FILTER; - post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS); /* * For RNDIS the event is posted from the flow control * handler which is invoked when the host sends the * GEN_CURRENT_PACKET_FILTER message. */ if (gsi->prot_id != IPA_USB_RNDIS) - post_event(&gsi->d_port, EVT_HOST_READY); + post_event(&gsi->d_port, + EVT_CONNECT_IN_PROGRESS); queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); } @@ -2078,8 +2077,6 @@ static void gsi_suspend(struct usb_function *f) queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); } - log_event_dbg("%s: notify_count = %d\n", - __func__, atomic_read(&gsi->c_port.notify_count)); log_event_dbg("gsi suspended"); } @@ -2104,21 +2101,6 @@ static void gsi_resume(struct usb_function *f) else remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; - if (gsi->c_port.notify && !gsi->c_port.notify->desc) - config_ep_by_speed(cdev->gadget, f, gsi->c_port.notify); - - log_event_dbg("%s: notify_count = %d\n", - __func__, atomic_read(&gsi->c_port.notify_count)); - - /* Send notification to host for RMNET, RNDIS and MBIM Interface */ - if ((gsi->prot_id == IPA_USB_MBIM || - gsi->prot_id == IPA_USB_RNDIS || - gsi->prot_id == IPA_USB_RMNET) && - (atomic_read(&gsi->c_port.notify_count) >= 1)) { - log_event_dbg("%s: force_queue\n", __func__); - queue_notification_request(gsi); - } - if (!remote_wakeup_allowed) { /* Configure EPs for GSI */ @@ -2149,6 +2131,11 @@ static void gsi_resume(struct usb_function *f) post_event(&gsi->d_port, EVT_RESUMED); queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); + + if (gsi->c_port.notify && !gsi->c_port.notify->desc) + config_ep_by_speed(cdev->gadget, f, gsi->c_port.notify); + + atomic_set(&gsi->c_port.notify_count, 0); log_event_dbg("%s: completed", __func__); } diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index b46e9fc64196..95a49ef2781a 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -117,10 +117,16 @@ config EXT4_ENCRYPTION decrypted pages in the page cache. config EXT4_FS_ENCRYPTION - bool - default y + bool "Ext4 FS Encryption" + default n depends on EXT4_ENCRYPTION +config EXT4_FS_ICE_ENCRYPTION + bool "Ext4 Encryption with ICE support" + default n + depends on EXT4_FS_ENCRYPTION + depends on PFK + config EXT4_DEBUG bool "EXT4 debugging support" depends on EXT4_FS diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index f52cf54f0cbc..1cabbd9a9229 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile @@ -14,3 +14,5 @@ ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o crypto.o \ crypto_key.o crypto_fname.o + +ext4-$(CONFIG_EXT4_FS_ICE_ENCRYPTION) += ext4_ice.o diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c index 032d0b9bb324..1bb67391225a 100644 --- a/fs/ext4/crypto.c +++ b/fs/ext4/crypto.c @@ -458,7 +458,8 @@ errout: bool ext4_valid_contents_enc_mode(uint32_t mode) { - return (mode == EXT4_ENCRYPTION_MODE_AES_256_XTS); + return (mode == EXT4_ENCRYPTION_MODE_AES_256_XTS || + mode == EXT4_ENCRYPTION_MODE_PRIVATE); } /** diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index 9a16d1e75a49..15342bfff70d 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -15,6 +15,7 @@ #include <uapi/linux/keyctl.h> #include "ext4.h" +#include "ext4_ice.h" #include "xattr.h" static void derive_crypt_complete(struct crypto_async_request *req, int rc) @@ -111,6 +112,12 @@ void ext4_free_encryption_info(struct inode *inode, ext4_free_crypt_info(ci); } +static int ext4_default_data_encryption_mode(void) +{ + return ext4_is_ice_enabled() ? EXT4_ENCRYPTION_MODE_PRIVATE : + EXT4_ENCRYPTION_MODE_AES_256_XTS; +} + int _ext4_get_encryption_info(struct inode *inode) { struct ext4_inode_info *ei = EXT4_I(inode); @@ -124,8 +131,8 @@ int _ext4_get_encryption_info(struct inode *inode) struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct crypto_ablkcipher *ctfm; const char *cipher_str; - char raw_key[EXT4_MAX_KEY_SIZE]; - char mode; + int for_fname = 0; + int mode; int res; if (!ext4_read_workqueue) { @@ -150,7 +157,8 @@ retry: if (res < 0) { if (!DUMMY_ENCRYPTION_ENABLED(sbi)) return res; - ctx.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS; + ctx.contents_encryption_mode = + ext4_default_data_encryption_mode(); ctx.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS; ctx.flags = 0; @@ -169,12 +177,12 @@ retry: crypt_info->ci_keyring_key = NULL; memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, sizeof(crypt_info->ci_master_key)); - if (S_ISREG(inode->i_mode)) - mode = crypt_info->ci_data_mode; - else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) - mode = crypt_info->ci_filename_mode; - else + if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + for_fname = 1; + else if (!S_ISREG(inode->i_mode)) BUG(); + mode = for_fname ? crypt_info->ci_filename_mode : + crypt_info->ci_data_mode; switch (mode) { case EXT4_ENCRYPTION_MODE_AES_256_XTS: cipher_str = "xts(aes)"; @@ -182,6 +190,9 @@ retry: case EXT4_ENCRYPTION_MODE_AES_256_CTS: cipher_str = "cts(cbc(aes))"; break; + case EXT4_ENCRYPTION_MODE_PRIVATE: + cipher_str = "bugon"; + break; default: printk_once(KERN_WARNING "ext4: unsupported key mode %d (ino %u)\n", @@ -190,7 +201,7 @@ retry: goto out; } if (DUMMY_ENCRYPTION_ENABLED(sbi)) { - memset(raw_key, 0x42, EXT4_AES_256_XTS_KEY_SIZE); + memset(crypt_info->ci_raw_key, 0x42, EXT4_AES_256_XTS_KEY_SIZE); goto got_key; } memcpy(full_key_descriptor, EXT4_KEY_DESC_PREFIX, @@ -232,28 +243,36 @@ retry: goto out; } res = ext4_derive_key_aes(ctx.nonce, master_key->raw, - raw_key); + crypt_info->ci_raw_key); up_read(&keyring_key->sem); if (res) goto out; got_key: - ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); - if (!ctfm || IS_ERR(ctfm)) { - res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; - printk(KERN_DEBUG - "%s: error %d (inode %u) allocating crypto tfm\n", - __func__, res, (unsigned) inode->i_ino); + if (for_fname || + (crypt_info->ci_data_mode != EXT4_ENCRYPTION_MODE_PRIVATE)) { + ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); + if (!ctfm || IS_ERR(ctfm)) { + res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; + pr_debug("%s: error %d (inode %u) allocating crypto tfm\n", + __func__, res, (unsigned) inode->i_ino); + goto out; + } + crypt_info->ci_ctfm = ctfm; + crypto_ablkcipher_clear_flags(ctfm, ~0); + crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm), + CRYPTO_TFM_REQ_WEAK_KEY); + res = crypto_ablkcipher_setkey(ctfm, crypt_info->ci_raw_key, + ext4_encryption_key_size(mode)); + if (res) + goto out; + memzero_explicit(crypt_info->ci_raw_key, + sizeof(crypt_info->ci_raw_key)); + } else if (!ext4_is_ice_enabled()) { + pr_warn("%s: ICE support not available\n", + __func__); + res = -EINVAL; goto out; } - crypt_info->ci_ctfm = ctfm; - crypto_ablkcipher_clear_flags(ctfm, ~0); - crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm), - CRYPTO_TFM_REQ_WEAK_KEY); - res = crypto_ablkcipher_setkey(ctfm, raw_key, - ext4_encryption_key_size(mode)); - if (res) - goto out; - memzero_explicit(raw_key, sizeof(raw_key)); if (cmpxchg(&ei->i_crypt_info, NULL, crypt_info) != NULL) { ext4_free_crypt_info(crypt_info); goto retry; @@ -263,8 +282,9 @@ got_key: out: if (res == -ENOKEY) res = 0; + memzero_explicit(crypt_info->ci_raw_key, + sizeof(crypt_info->ci_raw_key)); ext4_free_crypt_info(crypt_info); - memzero_explicit(raw_key, sizeof(raw_key)); return res; } diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 785bc29e4f14..f287ddfd17f1 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -588,6 +588,7 @@ enum { #define EXT4_ENCRYPTION_MODE_AES_256_GCM 2 #define EXT4_ENCRYPTION_MODE_AES_256_CBC 3 #define EXT4_ENCRYPTION_MODE_AES_256_CTS 4 +#define EXT4_ENCRYPTION_MODE_PRIVATE 127 #include "ext4_crypto.h" @@ -2328,6 +2329,19 @@ int _ext4_get_encryption_info(struct inode *inode); #ifdef CONFIG_EXT4_FS_ENCRYPTION int ext4_has_encryption_key(struct inode *inode); +static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) +{ + return EXT4_I(inode)->i_crypt_info; +} + +static inline int ext4_using_hardware_encryption(struct inode *inode) +{ + struct ext4_crypt_info *ci = ext4_encryption_info(inode); + + return S_ISREG(inode->i_mode) && ci && + ci->ci_data_mode == EXT4_ENCRYPTION_MODE_PRIVATE; +} + static inline int ext4_get_encryption_info(struct inode *inode) { struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; @@ -2341,11 +2355,6 @@ static inline int ext4_get_encryption_info(struct inode *inode) return 0; } -static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) -{ - return EXT4_I(inode)->i_crypt_info; -} - #else static inline int ext4_has_encryption_key(struct inode *inode) { @@ -2359,6 +2368,10 @@ static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) { return NULL; } +static inline int ext4_using_hardware_encryption(struct inode *inode) +{ + return 0; +} #endif diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h index ac7d4e813796..95cbc9bc1995 100644 --- a/fs/ext4/ext4_crypto.h +++ b/fs/ext4/ext4_crypto.h @@ -12,6 +12,7 @@ #define _EXT4_CRYPTO_H #include <linux/fs.h> +#include <linux/pfk.h> #define EXT4_KEY_DESCRIPTOR_SIZE 8 @@ -61,6 +62,7 @@ struct ext4_encryption_context { #define EXT4_AES_256_CBC_KEY_SIZE 32 #define EXT4_AES_256_CTS_KEY_SIZE 32 #define EXT4_AES_256_XTS_KEY_SIZE 64 +#define EXT4_PRIVATE_KEY_SIZE 64 #define EXT4_MAX_KEY_SIZE 64 #define EXT4_KEY_DESC_PREFIX "ext4:" @@ -80,8 +82,11 @@ struct ext4_crypt_info { struct crypto_ablkcipher *ci_ctfm; struct key *ci_keyring_key; char ci_master_key[EXT4_KEY_DESCRIPTOR_SIZE]; + char ci_raw_key[EXT4_MAX_KEY_SIZE]; }; + + #define EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 #define EXT4_WRITE_PATH_FL 0x00000002 @@ -114,6 +119,7 @@ static inline int ext4_encryption_key_size(int mode) { switch (mode) { case EXT4_ENCRYPTION_MODE_AES_256_XTS: + case EXT4_ENCRYPTION_MODE_PRIVATE: return EXT4_AES_256_XTS_KEY_SIZE; case EXT4_ENCRYPTION_MODE_AES_256_GCM: return EXT4_AES_256_GCM_KEY_SIZE; diff --git a/fs/ext4/ext4_ice.c b/fs/ext4/ext4_ice.c new file mode 100644 index 000000000000..d85bcb8ea1ba --- /dev/null +++ b/fs/ext4/ext4_ice.c @@ -0,0 +1,109 @@ +/* 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. + */ + +#include "ext4_ice.h" +#include "ext4_crypto.h" + + +/* + * Retrieves encryption key from the inode + */ +char *ext4_get_ice_encryption_key(const struct inode *inode) +{ + struct ext4_crypt_info *ci = NULL; + + if (!inode) + return NULL; + + ci = ext4_encryption_info((struct inode *)inode); + if (!ci) + return NULL; + + return &(ci->ci_raw_key[0]); +} + +/* + * Retrieves encryption salt from the inode + */ +char *ext4_get_ice_encryption_salt(const struct inode *inode) +{ + struct ext4_crypt_info *ci = NULL; + + if (!inode) + return NULL; + + ci = ext4_encryption_info((struct inode *)inode); + if (!ci) + return NULL; + + return &(ci->ci_raw_key[ext4_get_ice_encryption_key_size(inode)]); +} + +/* + * returns true if the cipher mode in inode is AES XTS + */ +int ext4_is_aes_xts_cipher(const struct inode *inode) +{ + struct ext4_crypt_info *ci = NULL; + + ci = ext4_encryption_info((struct inode *)inode); + if (!ci) + return 0; + + return (ci->ci_data_mode == EXT4_ENCRYPTION_MODE_PRIVATE); +} + +/* + * returns true if encryption info in both inodes is equal + */ +int ext4_is_ice_encryption_info_equal(const struct inode *inode1, + const struct inode *inode2) +{ + char *key1 = NULL; + char *key2 = NULL; + char *salt1 = NULL; + char *salt2 = NULL; + + if (!inode1 || !inode2) + return 0; + + if (inode1 == inode2) + return 1; + + /* both do not belong to ice, so we don't care, they are equal for us */ + if (!ext4_should_be_processed_by_ice(inode1) && + !ext4_should_be_processed_by_ice(inode2)) + return 1; + + /* one belongs to ice, the other does not -> not equal */ + if (ext4_should_be_processed_by_ice(inode1) ^ + ext4_should_be_processed_by_ice(inode2)) + return 0; + + key1 = ext4_get_ice_encryption_key(inode1); + key2 = ext4_get_ice_encryption_key(inode2); + salt1 = ext4_get_ice_encryption_salt(inode1); + salt2 = ext4_get_ice_encryption_salt(inode2); + + /* key and salt should not be null by this point */ + if (!key1 || !key2 || !salt1 || !salt2 || + (ext4_get_ice_encryption_key_size(inode1) != + ext4_get_ice_encryption_key_size(inode2)) || + (ext4_get_ice_encryption_salt_size(inode1) != + ext4_get_ice_encryption_salt_size(inode2))) + return 0; + + return ((memcmp(key1, key2, + ext4_get_ice_encryption_key_size(inode1)) == 0) && + (memcmp(salt1, salt2, + ext4_get_ice_encryption_salt_size(inode1)) == 0)); +} diff --git a/fs/ext4/ext4_ice.h b/fs/ext4/ext4_ice.h new file mode 100644 index 000000000000..5257edabd6b2 --- /dev/null +++ b/fs/ext4/ext4_ice.h @@ -0,0 +1,104 @@ +/* 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. + */ + +#ifndef _EXT4_ICE_H +#define _EXT4_ICE_H + +#include "ext4.h" +#include "ext4_crypto.h" + +#ifdef CONFIG_EXT4_FS_ICE_ENCRYPTION +static inline int ext4_should_be_processed_by_ice(const struct inode *inode) +{ + if (!ext4_encrypted_inode((struct inode *)inode)) + return 0; + + return ext4_using_hardware_encryption((struct inode *)inode); +} + +static inline int ext4_is_ice_enabled(void) +{ + return 1; +} + +int ext4_is_aes_xts_cipher(const struct inode *inode); + +char *ext4_get_ice_encryption_key(const struct inode *inode); +char *ext4_get_ice_encryption_salt(const struct inode *inode); + +int ext4_is_ice_encryption_info_equal(const struct inode *inode1, + const struct inode *inode2); + +static inline size_t ext4_get_ice_encryption_key_size( + const struct inode *inode) +{ + return EXT4_AES_256_XTS_KEY_SIZE / 2; +} + +static inline size_t ext4_get_ice_encryption_salt_size( + const struct inode *inode) +{ + return EXT4_AES_256_XTS_KEY_SIZE / 2; +} + +#else +static inline int ext4_should_be_processed_by_ice(const struct inode *inode) +{ + return 0; +} +static inline int ext4_is_ice_enabled(void) +{ + return 0; +} + +static inline char *ext4_get_ice_encryption_key(const struct inode *inode) +{ + return NULL; +} + +static inline char *ext4_get_ice_encryption_salt(const struct inode *inode) +{ + return NULL; +} + +static inline size_t ext4_get_ice_encryption_key_size( + const struct inode *inode) +{ + return 0; +} + +static inline size_t ext4_get_ice_encryption_salt_size( + const struct inode *inode) +{ + return 0; +} + +static inline int ext4_is_xts_cipher(const struct inode *inode) +{ + return 0; +} + +static inline int ext4_is_ice_encryption_info_equal( + const struct inode *inode1, + const struct inode *inode2) +{ + return 0; +} + +static inline int ext4_is_aes_xts_cipher(const struct inode *inode) +{ + return 0; +} + +#endif + +#endif /* _EXT4_ICE_H */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 06bda0361e7c..b15e6edb8f2c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -42,6 +42,7 @@ #include "xattr.h" #include "acl.h" #include "truncate.h" +#include "ext4_ice.h" #include <trace/events/ext4.h> @@ -979,7 +980,8 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, ll_rw_block(READ, 1, &bh); *wait_bh++ = bh; decrypt = ext4_encrypted_inode(inode) && - S_ISREG(inode->i_mode); + S_ISREG(inode->i_mode) && + !ext4_is_ice_enabled(); } } /* @@ -3459,7 +3461,8 @@ static int __ext4_block_zero_page_range(handle_t *handle, if (!buffer_uptodate(bh)) goto unlock; if (S_ISREG(inode->i_mode) && - ext4_encrypted_inode(inode)) { + ext4_encrypted_inode(inode) && + !ext4_using_hardware_encryption(inode)) { /* We expect the key to be set. */ BUG_ON(!ext4_has_encryption_key(inode)); BUG_ON(blocksize != PAGE_CACHE_SIZE); diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 5c72ae5d62a6..1a6a8ca4de3a 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -28,6 +28,7 @@ #include "ext4_jbd2.h" #include "xattr.h" #include "acl.h" +#include "ext4_ice.h" static struct kmem_cache *io_end_cachep; @@ -489,7 +490,11 @@ int ext4_bio_write_page(struct ext4_io_submit *io, gfp_t gfp_flags = GFP_NOFS; retry_encrypt: - data_page = ext4_encrypt(inode, page, gfp_flags); + + if (!ext4_using_hardware_encryption(inode)) + data_page = ext4_encrypt(inode, page, gfp_flags); + + if (IS_ERR(data_page)) { ret = PTR_ERR(data_page); if (ret == ENOMEM && wbc->sync_mode == WB_SYNC_ALL) { diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index bc7642f57dc8..dd98270d0b21 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -45,6 +45,7 @@ #include <linux/cleancache.h> #include "ext4.h" +#include "ext4_ice.h" /* * Call ext4_decrypt on every single page, reusing the encryption @@ -62,12 +63,17 @@ static void completion_pages(struct work_struct *work) bio_for_each_segment_all(bv, bio, i) { struct page *page = bv->bv_page; - int ret = ext4_decrypt(page); - if (ret) { - WARN_ON_ONCE(1); - SetPageError(page); - } else + if (ext4_is_ice_enabled()) { SetPageUptodate(page); + } else { + int ret = ext4_decrypt(page); + + if (ret) { + WARN_ON_ONCE(1); + SetPageError(page); + } else + SetPageUptodate(page); + } unlock_page(page); } ext4_release_crypto_ctx(ctx); @@ -324,5 +330,6 @@ int ext4_mpage_readpages(struct address_space *mapping, BUG_ON(pages && !list_empty(pages)); if (bio) submit_bio(READ, bio); + return 0; } diff --git a/include/linux/ecryptfs.h b/include/linux/ecryptfs.h index 33d35ea4f937..489b99e37128 100644 --- a/include/linux/ecryptfs.h +++ b/include/linux/ecryptfs.h @@ -130,7 +130,7 @@ struct ecryptfs_events { size_t (*get_salt_key_size_cb)(const void *ecrytpfs_data); }; - +#ifdef CONFIG_ECRYPT_FS int ecryptfs_register_to_events(const struct ecryptfs_events *ops); int ecryptfs_unregister_from_events(int user_handle); @@ -151,4 +151,55 @@ bool ecryptfs_is_page_in_metadata(const void *ecrytpfs_data, pgoff_t offset); bool ecryptfs_is_data_equal(const void *ecrytpfs_data1, const void *ecrytpfs_data2); +#else +static inline int ecryptfs_register_to_events( + const struct ecryptfs_events *ops) +{ + return 1; /* dummy handle */ +} + +static int ecryptfs_unregister_from_events(int user_handle) +{ + return 0; +} + +static inline const unsigned char *ecryptfs_get_key(const void *ecrytpfs_data) +{ + return NULL; +} + +static inline size_t ecryptfs_get_key_size(const void *ecrytpfs_data) +{ + return 0; +} + +static inline const unsigned char *ecryptfs_get_salt(const void *ecrytpfs_data) +{ + return NULL; +} + +static inline size_t ecryptfs_get_salt_size(const void *ecrytpfs_data) +{ + return 0; +} + +static inline bool ecryptfs_cipher_match(const void *ecrytpfs_data, + const unsigned char *cipher, size_t cipher_size) +{ + return false; +} + +bool ecryptfs_is_page_in_metadata(const void *ecrytpfs_data, pgoff_t offset) +{ + return false; +} + +bool ecryptfs_is_data_equal(const void *ecrytpfs_data1, + const void *ecrytpfs_data2) +{ + return false; +} + +#endif /* CONFIG_ECRYPT_FS */ + #endif /* _LINUX_ECRYPTFS_H */ diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 1919b06f28f4..cf4832db2b29 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1443,7 +1443,6 @@ union security_list_options { int (*file_receive)(struct file *file); int (*file_open)(struct file *file, const struct cred *cred); int (*file_close)(struct file *file); - bool (*allow_merge_bio)(struct bio *bio1, struct bio *bio2); int (*task_create)(unsigned long clone_flags); void (*task_free)(struct task_struct *task); @@ -1708,7 +1707,6 @@ struct security_hook_heads { struct list_head file_receive; struct list_head file_open; struct list_head file_close; - struct list_head allow_merge_bio; struct list_head task_create; struct list_head task_free; struct list_head cred_alloc_blank; diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h index f595275e9d42..11e8d89c337b 100644 --- a/include/linux/mfd/wcd9xxx/core.h +++ b/include/linux/mfd/wcd9xxx/core.h @@ -53,12 +53,52 @@ #define TASHA_VERSION_1_0 0 #define TASHA_VERSION_1_1 1 #define TASHA_VERSION_2_0 2 -#define TASHA_IS_1_0(ver) \ - ((ver == TASHA_VERSION_1_0) ? 1 : 0) -#define TASHA_IS_1_1(ver) \ - ((ver == TASHA_VERSION_1_1) ? 1 : 0) -#define TASHA_IS_2_0(ver) \ - ((ver == TASHA_VERSION_2_0) ? 1 : 0) + +#define TASHA_IS_1_0(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_1_0) ? 1 : 0) : 0) + +#define TASHA_IS_1_1(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_1_1) ? 1 : 0) : 0) + +#define TASHA_IS_2_0(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_2_0) ? 1 : 0) : 0) + +/* + * As fine version info cannot be retrieved before tavil probe. + * Define three coarse versions for possible future use before tavil probe. + */ +#define TAVIL_VERSION_1_0 0 +#define TAVIL_VERSION_1_1 1 +#define TAVIL_VERSION_WCD9340_1_0 2 +#define TAVIL_VERSION_WCD9341_1_0 3 +#define TAVIL_VERSION_WCD9340_1_1 4 +#define TAVIL_VERSION_WCD9341_1_1 5 + +#define TAVIL_IS_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_1_0 || \ + wcd->version == TAVIL_VERSION_WCD9340_1_0 || \ + wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_1_1 || \ + wcd->version == TAVIL_VERSION_WCD9340_1_1 || \ + wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9340_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9340_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9341_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9340_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9340_1_1) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9341_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0) #define IS_CODEC_TYPE(wcd, wcdtype) \ ((wcd->type == wcdtype) ? true : false) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 95fd207e63ca..4f28b91f49c5 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -992,6 +992,11 @@ extern int perf_cpu_time_max_percent_handler(struct ctl_table *table, int write, loff_t *ppos); +static inline bool perf_paranoid_any(void) +{ + return sysctl_perf_event_paranoid > 2; +} + static inline bool perf_paranoid_tracepoint_raw(void) { return sysctl_perf_event_paranoid > -1; diff --git a/include/linux/pfk.h b/include/linux/pfk.h index a7e8ecbea8f5..2fc64442b8ee 100644 --- a/include/linux/pfk.h +++ b/include/linux/pfk.h @@ -23,7 +23,7 @@ int pfk_load_key_start(const struct bio *bio, struct ice_crypto_setting *ice_setting, bool *is_pfe, bool); int pfk_load_key_end(const struct bio *bio, bool *is_pfe); int pfk_remove_key(const unsigned char *key, size_t key_size); -bool pfk_allow_merge_bio(struct bio *bio1, struct bio *bio2); +bool pfk_allow_merge_bio(const struct bio *bio1, const struct bio *bio2); #else static inline int pfk_load_key_start(const struct bio *bio, @@ -48,10 +48,6 @@ static inline bool pfk_allow_merge_bio(const struct bio *bio1, return true; } -static inline void pfk_remove_all_keys(void) -{ -} - #endif /* CONFIG_PFK */ #endif /* PFK_H */ diff --git a/include/linux/pft.h b/include/linux/pft.h index f2173b89a2a0..818383c73476 100644 --- a/include/linux/pft.h +++ b/include/linux/pft.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* 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 @@ -55,7 +55,8 @@ static inline int pft_get_key_index(struct bio *bio, u32 *key_index, bool *is_encrypted, bool *is_inplace) { return -ENODEV; } -static inline bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2) +static inline bool pft_allow_merge_bio(const struct bio *bio1, + const struct bio *bio2) { return true; } static inline int pft_file_permission(struct file *file, int mask) diff --git a/include/linux/security.h b/include/linux/security.h index 3de0302aecf2..e3b5efc0eb4b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -293,7 +293,6 @@ int security_file_send_sigiotask(struct task_struct *tsk, int security_file_receive(struct file *file); int security_file_open(struct file *file, const struct cred *cred); int security_file_close(struct file *file); -bool security_allow_merge_bio(struct bio *bio1, struct bio *bio2); int security_task_create(unsigned long clone_flags); void security_task_free(struct task_struct *task); int security_cred_alloc_blank(struct cred *cred, gfp_t gfp); @@ -826,11 +825,6 @@ static inline int security_file_close(struct file *file) return 0; } -static inline int security_allow_merge_bio(struct bio *bio1, struct bio *bio2) -{ - return true; -} - static inline int security_task_create(unsigned long clone_flags) { return 0; diff --git a/include/soc/qcom/service-notifier.h b/include/soc/qcom/service-notifier.h index 598c91f7c9e2..eae879786d59 100644 --- a/include/soc/qcom/service-notifier.h +++ b/include/soc/qcom/service-notifier.h @@ -24,6 +24,11 @@ enum qmi_servreg_notif_service_state_enum_type_v01 { SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01 = 0x7FFFFFFF, }; +enum pd_subsys_state { + CRASHED, + SHUTDOWN, + UNKNOWN, +}; #if defined(CONFIG_MSM_SERVICE_NOTIFIER) /* service_notif_register_notifier() - Register a notifier for a service diff --git a/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h index a9fe10d8cd6e..7902cfbafad8 100644 --- a/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h +++ b/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h @@ -352,4 +352,10 @@ #define WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL (0xB70) #define WCD9XXX_CDC_RX2_RX_PATH_SEC1 (0xB72) +/* Class-H registers for codecs from and above WCD934X */ +#define WCD9XXX_HPH_CNP_WG_CTL (0x06cc) +#define WCD9XXX_FLYBACK_VNEG_CTRL_4 (0x06a8) +#define WCD9XXX_HPH_NEW_INT_PA_MISC2 (0x0738) +#define WCD9XXX_RX_BIAS_HPH_LOWPOWER (0x06bf) +#define WCD9XXX_HPH_PA_CTL1 (0x06d1) #endif diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 69d25607d41b..a7ec545308a6 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -326,8 +326,7 @@ static struct file_system_type cpuset_fs_type = { /* * Return in pmask the portion of a cpusets's cpus_allowed that * are online. If none are online, walk up the cpuset hierarchy - * until we find one that does have some online cpus. The top - * cpuset always has some cpus online. + * until we find one that does have some online cpus. * * One way or another, we guarantee to return some non-empty subset * of cpu_online_mask. @@ -336,8 +335,20 @@ static struct file_system_type cpuset_fs_type = { */ static void guarantee_online_cpus(struct cpuset *cs, struct cpumask *pmask) { - while (!cpumask_intersects(cs->effective_cpus, cpu_online_mask)) + while (!cpumask_intersects(cs->effective_cpus, cpu_online_mask)) { cs = parent_cs(cs); + if (unlikely(!cs)) { + /* + * The top cpuset doesn't have any online cpu as a + * consequence of a race between cpuset_hotplug_work + * and cpu hotplug notifier. But we know the top + * cpuset's effective_cpus is on its way to to be + * identical to cpu_online_mask. + */ + cpumask_copy(pmask, cpu_online_mask); + return; + } + } cpumask_and(pmask, cs->effective_cpus, cpu_online_mask); } diff --git a/kernel/events/core.c b/kernel/events/core.c index 32e2617d654f..d6ec580584b6 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -176,9 +176,12 @@ static struct srcu_struct pmus_srcu; * 0 - disallow raw tracepoint access for unpriv * 1 - disallow cpu events for unpriv * 2 - disallow kernel profiling for unpriv + * 3 - disallow all unpriv perf event use */ #ifdef CONFIG_PERF_EVENTS_USERMODE int sysctl_perf_event_paranoid __read_mostly = -1; +#elif defined CONFIG_SECURITY_PERF_EVENTS_RESTRICT +int sysctl_perf_event_paranoid __read_mostly = 3; #else int sysctl_perf_event_paranoid __read_mostly = 1; #endif @@ -8325,6 +8328,9 @@ SYSCALL_DEFINE5(perf_event_open, if (flags & ~PERF_FLAG_ALL) return -EINVAL; + if (perf_paranoid_any() && !capable(CAP_SYS_ADMIN)) + return -EACCES; + err = perf_copy_attr(attr_uptr, &attr); if (err) return err; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index ca966f7de351..87b91ffbdec3 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1175,14 +1175,16 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb, static int rtnl_fill_link_ifmap(struct sk_buff *skb, struct net_device *dev) { - struct rtnl_link_ifmap map = { - .mem_start = dev->mem_start, - .mem_end = dev->mem_end, - .base_addr = dev->base_addr, - .irq = dev->irq, - .dma = dev->dma, - .port = dev->if_port, - }; + struct rtnl_link_ifmap map; + + memset(&map, 0, sizeof(map)); + map.mem_start = dev->mem_start; + map.mem_end = dev->mem_end; + map.base_addr = dev->base_addr; + map.irq = dev->irq; + map.dma = dev->dma; + map.port = dev->if_port; + if (nla_put(skb, IFLA_MAP, sizeof(map), &map)) return -EMSGSIZE; diff --git a/security/Kconfig b/security/Kconfig index 18568c21e564..c4f83485bc1f 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -23,6 +23,15 @@ config SECURITY_DMESG_RESTRICT If you are unsure how to answer this question, answer N. +config SECURITY_PERF_EVENTS_RESTRICT + bool "Restrict unprivileged use of performance events" + depends on PERF_EVENTS + help + If you say Y here, the kernel.perf_event_paranoid sysctl + will be set to 3 by default, and no unprivileged use of the + perf_event_open syscall will be permitted unless it is + changed. + config SECURITY bool "Enable different security models" depends on SYSFS diff --git a/security/pfe/Kconfig b/security/pfe/Kconfig index b951861bfc84..207be713f800 100644 --- a/security/pfe/Kconfig +++ b/security/pfe/Kconfig @@ -15,7 +15,6 @@ config PFT config PFK bool "Per-File-Key driver" depends on SECURITY - depends on ECRYPT_FS default n help This driver is used for storing eCryptfs information diff --git a/security/pfe/Makefile b/security/pfe/Makefile index 983eedb86170..f7badf74c73f 100644 --- a/security/pfe/Makefile +++ b/security/pfe/Makefile @@ -3,6 +3,7 @@ # ccflags-y += -Isecurity/selinux -Isecurity/selinux/include -Ifs/ecryptfs +ccflags-y += -Ifs/ext4 obj-$(CONFIG_PFT) += pft.o -obj-$(CONFIG_PFK) += pfk.o pfk_kc.o pfk_ice.o +obj-$(CONFIG_PFK) += pfk.o pfk_kc.o pfk_ice.o pfk_ext4.o pfk_ecryptfs.o diff --git a/security/pfe/pfk.c b/security/pfe/pfk.c index 5cfb3758d3cf..7e38c9fbf171 100644 --- a/security/pfe/pfk.c +++ b/security/pfe/pfk.c @@ -14,24 +14,27 @@ /* * Per-File-Key (PFK). * - * This driver is used for storing eCryptfs information (mainly file - * encryption key) in file node as part of eCryptfs hardware enhanced solution - * provided by Qualcomm Technologies, Inc. + * This driver is responsible for overall management of various + * Per File Encryption variants that work on top of or as part of different + * file systems. * - * The information is stored in node when file is first opened (eCryptfs - * will fire a callback notifying PFK about this event) and will be later - * accessed by Block Device Driver to actually load the key to encryption hw. + * The driver has the following purpose : + * 1) Define priorities between PFE's if more than one is enabled + * 2) Extract key information from inode + * 3) Load and manage various keys in ICE HW engine + * 4) It should be invoked from various layers in FS/BLOCK/STORAGE DRIVER + * that need to take decision on HW encryption management of the data + * Some examples: + * BLOCK LAYER: when it takes decision on whether 2 chunks can be united + * to one encryption / decryption request sent to the HW * - * PFK exposes API's for loading and removing keys from encryption hw - * and also API to determine whether 2 adjacent blocks can be agregated by - * Block Layer in one request to encryption hw. - * PFK is only supposed to be used by eCryptfs, except the below. + * UFS DRIVER: when it need to configure ICE HW with a particular key slot + * to be used for encryption / decryption + * + * PFE variants can differ on particular way of storing the cryptographic info + * inside inode, actions to be taken upon file operations, etc., but the common + * properties are described above * - * Please note, the only API that uses EXPORT_SYMBOL() is pfk_remove_key, - * this is intentionally, as it is the only API that is intended to be used - * by any kernel module, including dynamically loaded ones. All other API's, - * as mentioned above are only supposed to be used by eCryptfs which is - * a static module. */ @@ -45,7 +48,6 @@ #include <linux/printk.h> #include <linux/bio.h> #include <linux/security.h> -#include <linux/lsm_hooks.h> #include <crypto/ice.h> #include <linux/pfk.h> @@ -55,87 +57,120 @@ #include "objsec.h" #include "ecryptfs_kernel.h" #include "pfk_ice.h" +#include "pfk_ext4.h" +#include "pfk_ecryptfs.h" +#include "pfk_internal.h" +#include "ext4.h" -static DEFINE_MUTEX(pfk_lock); static bool pfk_ready; -static int g_events_handle; /* might be replaced by a table when more than one cipher is supported */ -#define PFK_SUPPORTED_CIPHER "aes_xts" #define PFK_SUPPORTED_KEY_SIZE 32 #define PFK_SUPPORTED_SALT_SIZE 32 +/* Various PFE types and function tables to support each one of them */ +enum pfe_type {ECRYPTFS_PFE, EXT4_CRYPT_PFE, INVALID_PFE}; -/** - * inode_to_filename() - get the filename from inode pointer. - * @inode: inode pointer - * - * it is used for debug prints. - * - * Return: filename string or "unknown". - */ -static char *inode_to_filename(struct inode *inode) -{ - struct dentry *dentry = NULL; - char *filename = NULL; +typedef int (*pfk_parse_inode_type)(const struct bio *bio, + const struct inode *inode, + struct pfk_key_info *key_info, + enum ice_cryto_algo_mode *algo, + bool *is_pfe); - if (hlist_empty(&inode->i_dentry)) - return "unknown"; +typedef bool (*pfk_allow_merge_bio_type)(const struct bio *bio1, + const struct bio *bio2, const struct inode *inode1, + const struct inode *inode2); - dentry = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias); - filename = dentry->d_iname; +static const pfk_parse_inode_type pfk_parse_inode_ftable[] = { + /* ECRYPTFS_PFE */ &pfk_ecryptfs_parse_inode, + /* EXT4_CRYPT_PFE */ &pfk_ext4_parse_inode, +}; - return filename; +static const pfk_allow_merge_bio_type pfk_allow_merge_bio_ftable[] = { + /* ECRYPTFS_PFE */ &pfk_ecryptfs_allow_merge_bio, + /* EXT4_CRYPT_PFE */ &pfk_ext4_allow_merge_bio, +}; + +static void __exit pfk_exit(void) +{ + pfk_ready = false; + pfk_ext4_deinit(); + pfk_ecryptfs_deinit(); + pfk_kc_deinit(); } -static int pfk_inode_alloc_security(struct inode *inode) +static int __init pfk_init(void) { - struct inode_security_struct *i_sec = NULL; - if (inode == NULL) - return -EINVAL; + int ret = 0; - i_sec = kzalloc(sizeof(*i_sec), GFP_KERNEL); + ret = pfk_ecryptfs_init(); + if (ret != 0) + goto fail; + + ret = pfk_ext4_init(); + if (ret != 0) { + pfk_ecryptfs_deinit(); + goto fail; + } - if (i_sec == NULL) - return -ENOMEM; + ret = pfk_kc_init(); + if (ret != 0) { + pr_err("could init pfk key cache, error %d\n", ret); + pfk_ext4_deinit(); + pfk_ecryptfs_deinit(); + goto fail; + } - inode->i_security = i_sec; + pfk_ready = true; + pr_info("Driver initialized successfully\n"); return 0; + +fail: + pr_err("Failed to init driver\n"); + return -ENODEV; } -static void pfk_inode_free_security(struct inode *inode) +/* + * If more than one type is supported simultaneously, this function will also + * set the priority between them + */ +static enum pfe_type pfk_get_pfe_type(const struct inode *inode) { - if (inode == NULL) - return; + if (!inode) + return INVALID_PFE; - kzfree(inode->i_security); -} + if (pfk_is_ecryptfs_type(inode)) + return ECRYPTFS_PFE; -static struct security_hook_list pfk_hooks[] = { - LSM_HOOK_INIT(inode_alloc_security, pfk_inode_alloc_security), - LSM_HOOK_INIT(inode_free_security, pfk_inode_free_security), - LSM_HOOK_INIT(allow_merge_bio, pfk_allow_merge_bio), -}; + if (pfk_is_ext4_type(inode)) + return EXT4_CRYPT_PFE; -static int __init pfk_lsm_init(void) + return INVALID_PFE; +} + +/** + * inode_to_filename() - get the filename from inode pointer. + * @inode: inode pointer + * + * it is used for debug prints. + * + * Return: filename string or "unknown". + */ +char *inode_to_filename(const struct inode *inode) { - /* Check if PFK is the chosen lsm via security_module_enable() */ - if (security_module_enable("pfk")) { - security_add_hooks(pfk_hooks, ARRAY_SIZE(pfk_hooks)); - pr_debug("pfk is the chosen lsm, registered successfully !\n"); - } else { - pr_debug("pfk is not the chosen lsm.\n"); - if (!selinux_is_enabled()) { - pr_err("se linux is not enabled.\n"); - return -ENODEV; - } + struct dentry *dentry = NULL; + char *filename = NULL; - } + if (hlist_empty(&inode->i_dentry)) + return "unknown"; - return 0; + dentry = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias); + filename = dentry->d_iname; + + return filename; } /** @@ -149,37 +184,6 @@ static inline bool pfk_is_ready(void) } /** - * pfk_get_page_index() - get the inode from a bio. - * @bio: Pointer to BIO structure. - * - * Walk the bio struct links to get the inode. - * Please note, that in general bio may consist of several pages from - * several files, but in our case we always assume that all pages come - * from the same file, since our logic ensures it. That is why we only - * walk through the first page to look for inode. - * - * Return: pointer to the inode struct if successful, or NULL otherwise. - * - */ -static int pfk_get_page_index(const struct bio *bio, pgoff_t *page_index) -{ - if (!bio || !page_index) - return -EINVAL; - - if (!bio_has_data((struct bio *)bio)) - return -EINVAL; - - if (!bio->bi_io_vec) - return -EINVAL; - if (!bio->bi_io_vec->bv_page) - return -EINVAL; - - *page_index = bio->bi_io_vec->bv_page->index; - - return 0; -} - -/** * pfk_bio_get_inode() - get the inode from a bio. * @bio: Pointer to BIO structure. * @@ -196,10 +200,8 @@ static struct inode *pfk_bio_get_inode(const struct bio *bio) { if (!bio) return NULL; - if (!bio_has_data((struct bio *)bio)) return NULL; - if (!bio->bi_io_vec) return NULL; if (!bio->bi_io_vec->bv_page) @@ -225,96 +227,13 @@ static struct inode *pfk_bio_get_inode(const struct bio *bio) } /** - * pfk_get_ecryptfs_data() - retrieves ecryptfs data stored inside node - * @inode: inode - * - * Return the data or NULL if there isn't any or in case of error - * Should be invoked under lock - */ -static void *pfk_get_ecryptfs_data(const struct inode *inode) -{ - struct inode_security_struct *isec = NULL; - - if (!inode) - return NULL; - - isec = inode->i_security; - - if (!isec) { - pr_debug("i_security is NULL, could be irrelevant file\n"); - return NULL; - } - - return isec->pfk_data; -} - -/** - * pfk_set_ecryptfs_data() - stores ecryptfs data inside node - * @inode: inode to update - * @data: data to put inside the node - * - * Returns 0 in case of success, error otherwise - * Should be invoked under lock - */ -static int pfk_set_ecryptfs_data(struct inode *inode, void *ecryptfs_data) -{ - struct inode_security_struct *isec = NULL; - - if (!inode) - return -EINVAL; - - isec = inode->i_security; - - if (!isec) { - pr_err("i_security is NULL, not ready yet\n"); - return -EINVAL; - } - - isec->pfk_data = ecryptfs_data; - - return 0; -} - - -/** - * pfk_parse_cipher() - parse cipher from ecryptfs to enum - * @ecryptfs_data: ecrypfs data - * @algo: pointer to store the output enum (can be null) - * - * return 0 in case of success, error otherwise (i.e not supported cipher) - */ -static int pfk_parse_cipher(const void *ecryptfs_data, - enum ice_cryto_algo_mode *algo) -{ - /* - * currently only AES XTS algo is supported - * in the future, table with supported ciphers might - * be introduced - */ - - if (!ecryptfs_data) - return -EINVAL; - - if (!ecryptfs_cipher_match(ecryptfs_data, - PFK_SUPPORTED_CIPHER, sizeof(PFK_SUPPORTED_CIPHER))) { - pr_debug("ecryptfs alghoritm is not supported by pfk\n"); - return -EINVAL; - } - - if (algo) - *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS; - - return 0; -} - -/** * pfk_key_size_to_key_type() - translate key size to key size enum * @key_size: key size in bytes * @key_size_type: pointer to store the output enum (can be null) * * return 0 in case of success, error otherwise (i.e not supported key size) */ -static int pfk_key_size_to_key_type(size_t key_size, +int pfk_key_size_to_key_type(size_t key_size, enum ice_crpto_key_size *key_size_type) { /* @@ -334,100 +253,38 @@ static int pfk_key_size_to_key_type(size_t key_size, return 0; } -static int pfk_bio_to_key(const struct bio *bio, unsigned char const **key, - size_t *key_size, unsigned char const **salt, size_t *salt_size, - bool *is_pfe, bool start) +/* + * Retrieves filesystem type from inode's superblock + */ +bool pfe_is_inode_filesystem_type(const struct inode *inode, + const char *fs_type) { - struct inode *inode = NULL; - int ret = 0; - void *ecryptfs_data = NULL; - pgoff_t offset; - bool is_metadata = false; - - /* - * only a few errors below can indicate that - * this function was not invoked within PFE context, - * otherwise we will consider it PFE - */ - *is_pfe = true; - - - if (!bio) - return -EINVAL; - - if (!key || !salt || !key_size || !salt_size) - return -EINVAL; - - inode = pfk_bio_get_inode(bio); - if (!inode) { - *is_pfe = false; - return -EINVAL; - } - - ecryptfs_data = pfk_get_ecryptfs_data(inode); - if (!ecryptfs_data) { - *is_pfe = false; - return -EPERM; - } - - pr_debug("loading key for file %s, start %d\n", - inode_to_filename(inode), start); - - ret = pfk_get_page_index(bio, &offset); - if (ret != 0) { - pr_err("could not get page index from bio, probably bug %d\n", - ret); - return -EINVAL; - } - - is_metadata = ecryptfs_is_page_in_metadata(ecryptfs_data, offset); - if (is_metadata == true) { - pr_debug("ecryptfs metadata, bypassing ICE\n"); - *is_pfe = false; - return -EPERM; - } - - *key = ecryptfs_get_key(ecryptfs_data); - if (!key) { - pr_err("could not parse key from ecryptfs\n"); - return -EINVAL; - } - - *key_size = ecryptfs_get_key_size(ecryptfs_data); - if (!(*key_size)) { - pr_err("could not parse key size from ecryptfs\n"); - return -EINVAL; - } + if (!inode || !fs_type) + return false; - *salt = ecryptfs_get_salt(ecryptfs_data); - if (!salt) { - pr_err("could not parse salt from ecryptfs\n"); - return -EINVAL; - } + if (!inode->i_sb) + return false; - *salt_size = ecryptfs_get_salt_size(ecryptfs_data); - if (!(*salt_size)) { - pr_err("could not parse salt size from ecryptfs\n"); - return -EINVAL; - } + if (!inode->i_sb->s_type) + return false; - return 0; + return (strcmp(inode->i_sb->s_type->name, fs_type) == 0); } + /** * pfk_load_key_start() - loads PFE encryption key to the ICE - * Can also be invoked from non - * PFE context, than it is not - * relevant and is_pfe flag is - * set to true + * Can also be invoked from non + * PFE context, in this case it + * is not relevant and is_pfe + * flag is set to false + * * @bio: Pointer to the BIO structure * @ice_setting: Pointer to ice setting structure that will be filled with * ice configuration values, including the index to which the key was loaded - * @is_pfe: Pointer to is_pfe flag, which will be true if function was invoked - * from PFE context + * @is_pfe: will be false if inode is not relevant to PFE, in such a case + * it should be treated as non PFE by the block layer * - * Via bio gets access to ecryptfs key stored in auxiliary structure inside - * inode and loads it to encryption hw. * Returns the index where the key is stored in encryption hw and additional * information that will be used later for configuration of the encryption hw. * @@ -439,15 +296,12 @@ int pfk_load_key_start(const struct bio *bio, bool async) { int ret = 0; - const unsigned char *key = NULL; - const unsigned char *salt = NULL; - size_t key_size = 0; - size_t salt_size = 0; - enum ice_cryto_algo_mode algo_mode = 0; + struct pfk_key_info key_info = {0}; + enum ice_cryto_algo_mode algo_mode = ICE_CRYPTO_ALGO_MODE_AES_XTS; enum ice_crpto_key_size key_size_type = 0; - void *ecryptfs_data = NULL; u32 key_index = 0; struct inode *inode = NULL; + enum pfe_type which_pfe = INVALID_PFE; if (!is_pfe) { pr_err("is_pfe is NULL\n"); @@ -469,35 +323,32 @@ int pfk_load_key_start(const struct bio *bio, return -EINVAL; } - ret = pfk_bio_to_key(bio, &key, &key_size, &salt, &salt_size, is_pfe, - true); - if (ret != 0) - return ret; - inode = pfk_bio_get_inode(bio); if (!inode) { *is_pfe = false; return -EINVAL; } - ecryptfs_data = pfk_get_ecryptfs_data(inode); - if (!ecryptfs_data) { + which_pfe = pfk_get_pfe_type(inode); + if (which_pfe == INVALID_PFE) { *is_pfe = false; return -EPERM; } - ret = pfk_parse_cipher(ecryptfs_data, &algo_mode); - if (ret != 0) { - pr_err("not supported cipher\n"); + pr_debug("parsing file %s with PFE %d\n", + inode_to_filename(inode), which_pfe); + + ret = (*(pfk_parse_inode_ftable[which_pfe])) + (bio, inode, &key_info, &algo_mode, is_pfe); + if (ret != 0) return ret; - } - ret = pfk_key_size_to_key_type(key_size, &key_size_type); + ret = pfk_key_size_to_key_type(key_info.key_size, &key_size_type); if (ret != 0) return ret; - ret = pfk_kc_load_key_start(key, key_size, salt, salt_size, &key_index, - async); + ret = pfk_kc_load_key_start(key_info.key, key_info.key_size, + key_info.salt, key_info.salt_size, &key_index, async); if (ret) { if (ret != -EBUSY && ret != -EAGAIN) pr_err("start: could not load key into pfk key cache, error %d\n", @@ -512,30 +363,29 @@ int pfk_load_key_start(const struct bio *bio, ice_setting->key_mode = ICE_CRYPTO_USE_LUT_SW_KEY; ice_setting->key_index = key_index; + pr_debug("loaded key for file %s key_index %d\n", + inode_to_filename(inode), key_index); + return 0; } /** * pfk_load_key_end() - marks the PFE key as no longer used by ICE - * Can also be invoked from non - * PFE context, than it is not - * relevant and is_pfe flag is - * set to true + * Can also be invoked from non + * PFE context, in this case it is not + * relevant and is_pfe flag is + * set to false + * * @bio: Pointer to the BIO structure * @is_pfe: Pointer to is_pfe flag, which will be true if function was invoked * from PFE context - * - * Via bio gets access to ecryptfs key stored in auxiliary structure inside - * inode and loads it to encryption hw. - * */ int pfk_load_key_end(const struct bio *bio, bool *is_pfe) { int ret = 0; - const unsigned char *key = NULL; - const unsigned char *salt = NULL; - size_t key_size = 0; - size_t salt_size = 0; + struct pfk_key_info key_info = {0}; + enum pfe_type which_pfe = INVALID_PFE; + struct inode *inode = NULL; if (!is_pfe) { pr_err("is_pfe is NULL\n"); @@ -551,42 +401,31 @@ int pfk_load_key_end(const struct bio *bio, bool *is_pfe) if (!pfk_is_ready()) return -ENODEV; - ret = pfk_bio_to_key(bio, &key, &key_size, &salt, &salt_size, is_pfe, - false); - if (ret != 0) - return ret; - - pfk_kc_load_key_end(key, key_size, salt, salt_size); - - return 0; -} + inode = pfk_bio_get_inode(bio); + if (!inode) { + *is_pfe = false; + return -EINVAL; + } -/** - * pfk_remove_key() - removes key from hw - * @key: pointer to the key - * @key_size: key size - * - * Will be used by external clients to remove a particular key for security - * reasons. - * The only API that can be used by dynamically loaded modules, - * see explanations above at the beginning of this file. - * The key is removed securely (by memsetting the previous value) - */ -int pfk_remove_key(const unsigned char *key, size_t key_size) -{ - int ret = 0; + which_pfe = pfk_get_pfe_type(inode); + if (which_pfe == INVALID_PFE) { + *is_pfe = false; + return -EPERM; + } - if (!pfk_is_ready()) - return -ENODEV; + ret = (*(pfk_parse_inode_ftable[which_pfe])) + (bio, inode, &key_info, NULL, is_pfe); + if (ret != 0) + return ret; - if (!key) - return -EINVAL; + pfk_kc_load_key_end(key_info.key, key_info.key_size, + key_info.salt, key_info.salt_size); - ret = pfk_kc_remove_key(key, key_size); + pr_debug("finished using key for file %s\n", + inode_to_filename(inode)); - return ret; + return 0; } -EXPORT_SYMBOL(pfk_remove_key); /** * pfk_allow_merge_bio() - Check if 2 BIOs can be merged. @@ -603,252 +442,39 @@ EXPORT_SYMBOL(pfk_remove_key); * Return: true if the BIOs allowed to be merged, false * otherwise. */ -bool pfk_allow_merge_bio(struct bio *bio1, struct bio *bio2) +bool pfk_allow_merge_bio(const struct bio *bio1, const struct bio *bio2) { - int ret; - void *ecryptfs_data1 = NULL; - void *ecryptfs_data2 = NULL; - pgoff_t offset1, offset2; - bool res = false; + struct inode *inode1 = NULL; + struct inode *inode2 = NULL; + enum pfe_type which_pfe1 = INVALID_PFE; + enum pfe_type which_pfe2 = INVALID_PFE; - /* if there is no pfk, don't disallow merging blocks */ if (!pfk_is_ready()) - return true; + return false; if (!bio1 || !bio2) return false; - ecryptfs_data1 = pfk_get_ecryptfs_data(pfk_bio_get_inode(bio1)); - ecryptfs_data2 = pfk_get_ecryptfs_data(pfk_bio_get_inode(bio2)); - - /* - * if we have 2 different encrypted files or 1 encrypted and 1 regular, - * merge is forbidden - */ - if (!ecryptfs_is_data_equal(ecryptfs_data1, ecryptfs_data2)) { - res = false; - goto end; - } - - /* - * if both are equall in their NULLINNESS, we have 2 unencrypted files, - * allow merge - */ - if (!ecryptfs_data1) { - res = true; - goto end; - } - - /* - * at this point both bio's are in the same file which is probably - * encrypted, last thing to check is header vs data - * We are assuming that we are not working in O_DIRECT mode, - * since it is not currently supported by eCryptfs - */ - ret = pfk_get_page_index(bio1, &offset1); - if (ret != 0) { - pr_err("could not get page index from bio1, probably bug %d\n", - ret); - res = false; - goto end; - } - - ret = pfk_get_page_index(bio2, &offset2); - if (ret != 0) { - pr_err("could not get page index from bio2, bug %d\n", ret); - res = false; - goto end; - } - - res = (ecryptfs_is_page_in_metadata(ecryptfs_data1, offset1) == - ecryptfs_is_page_in_metadata(ecryptfs_data2, offset2)); - - /* fall through */ - -end: - - return res; -} - -/** - * pfk_open_cb() - callback function for file open event - * @inode: file inode - * @data: data provided by eCryptfs - * - * Will be invoked from eCryptfs in case of file open event - */ -static void pfk_open_cb(struct inode *inode, void *ecryptfs_data) -{ - size_t key_size; - - if (!pfk_is_ready()) - return; - - if (!inode) { - pr_err("inode is null\n"); - return; - } - - key_size = ecryptfs_get_key_size(ecryptfs_data); - if (!(key_size)) { - pr_err("could not parse key size from ecryptfs\n"); - return; - } - - if (0 != pfk_parse_cipher(ecryptfs_data, NULL)) { - pr_debug("open_cb: not supported cipher\n"); - return; - } - - - if (0 != pfk_key_size_to_key_type(key_size, NULL)) - return; - - mutex_lock(&pfk_lock); - pfk_set_ecryptfs_data(inode, ecryptfs_data); - mutex_unlock(&pfk_lock); -} - -/** - * pfk_release_cb() - callback function for file release event - * @inode: file inode - * - * Will be invoked from eCryptfs in case of file release event - */ -static void pfk_release_cb(struct inode *inode) -{ - const unsigned char *key = NULL; - const unsigned char *salt = NULL; - size_t key_size = 0; - size_t salt_size = 0; - void *data = NULL; - - if (!pfk_is_ready()) - return; - - if (!inode) { - pr_err("inode is null\n"); - return; - } - - data = pfk_get_ecryptfs_data(inode); - if (!data) { - pr_debug("could not get ecryptfs data from inode\n"); - return; - } - - key = ecryptfs_get_key(data); - if (!key) { - pr_err("could not parse key from ecryptfs\n"); - return; - } - - key_size = ecryptfs_get_key_size(data); - if (!(key_size)) { - pr_err("could not parse key size from ecryptfs\n"); - return; - } - - salt = ecryptfs_get_salt(data); - if (!salt) { - pr_err("could not parse salt from ecryptfs\n"); - return; - } - - salt_size = ecryptfs_get_salt_size(data); - if (!salt_size) { - pr_err("could not parse salt size from ecryptfs\n"); - return; - } + if (bio1 == bio2) + return true; - pfk_kc_remove_key_with_salt(key, key_size, salt, salt_size); + inode1 = pfk_bio_get_inode(bio1); + inode2 = pfk_bio_get_inode(bio2); - mutex_lock(&pfk_lock); - pfk_set_ecryptfs_data(inode, NULL); - mutex_unlock(&pfk_lock); -} + which_pfe1 = pfk_get_pfe_type(inode1); + which_pfe2 = pfk_get_pfe_type(inode2); -static bool pfk_is_cipher_supported_cb(const void *ecryptfs_data) -{ - if (!pfk_is_ready()) + /* nodes with different encryption, do not merge */ + if (which_pfe1 != which_pfe2) return false; - if (!ecryptfs_data) - return false; - - return (pfk_parse_cipher(ecryptfs_data, NULL)) == 0; -} - -static bool pfk_is_hw_crypt_cb(void) -{ - if (!pfk_is_ready()) - return false; - - return true; -} - -static size_t pfk_get_salt_key_size_cb(const void *ecryptfs_data) -{ - if (!pfk_is_ready()) - return 0; - - if (!pfk_is_cipher_supported_cb(ecryptfs_data)) - return 0; - - return PFK_SUPPORTED_SALT_SIZE; -} - - -static void __exit pfk_exit(void) -{ - pfk_ready = false; - ecryptfs_unregister_from_events(g_events_handle); - pfk_kc_deinit(); -} - -static int __init pfk_init(void) -{ - - int ret = 0; - struct ecryptfs_events events = {0}; - - events.open_cb = pfk_open_cb; - events.release_cb = pfk_release_cb; - events.is_cipher_supported_cb = pfk_is_cipher_supported_cb; - events.is_hw_crypt_cb = pfk_is_hw_crypt_cb; - events.get_salt_key_size_cb = pfk_get_salt_key_size_cb; - - g_events_handle = ecryptfs_register_to_events(&events); - if (0 == g_events_handle) { - pr_err("could not register with eCryptfs, error %d\n", ret); - goto fail; - } - - ret = pfk_kc_init(); - if (ret != 0) { - pr_err("could init pfk key cache, error %d\n", ret); - ecryptfs_unregister_from_events(g_events_handle); - goto fail; - } - - ret = pfk_lsm_init(); - if (ret != 0) { - pr_debug("neither pfk nor se-linux sec modules are enabled\n"); - pr_debug("not an error, just don't enable pfk\n"); - pfk_kc_deinit(); - ecryptfs_unregister_from_events(g_events_handle); - return 0; - } - - pfk_ready = true; - pr_info("Driver initialized successfully\n"); - - return 0; + /* both nodes do not have encryption, allow merge */ + if (which_pfe1 == INVALID_PFE) + return true; -fail: - pr_err("Failed to init driver\n"); - return -ENODEV; + return (*(pfk_allow_merge_bio_ftable[which_pfe1]))(bio1, bio2, + inode1, inode2); } module_init(pfk_init); diff --git a/security/pfe/pfk_ecryptfs.c b/security/pfe/pfk_ecryptfs.c new file mode 100644 index 000000000000..1d6a2eeaf6fc --- /dev/null +++ b/security/pfe/pfk_ecryptfs.c @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * Per-File-Key (PFK) - eCryptfs. + * + * This driver is used for storing eCryptfs information (mainly file + * encryption key) in file node as part of eCryptfs hardware enhanced solution + * provided by Qualcomm Technologies, Inc. + * + * The information is stored in node when file is first opened (eCryptfs + * will fire a callback notifying PFK about this event) and will be later + * accessed by Block Device Driver to actually load the key to encryption hw. + * + * PFK exposes API's for loading and removing keys from encryption hw + * and also API to determine whether 2 adjacent blocks can be agregated by + * Block Layer in one request to encryption hw. + * PFK is only supposed to be used by eCryptfs, except the below. + * + */ + + +/* Uncomment the line below to enable debug messages */ +/* #define DEBUG 1 */ +#define pr_fmt(fmt) "pfk_ecryptfs [%s]: " fmt, __func__ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/printk.h> +#include <linux/bio.h> +#include <linux/security.h> +#include <linux/lsm_hooks.h> +#include <crypto/ice.h> + +#include <linux/pfk.h> +#include <linux/ecryptfs.h> + +#include "pfk_ecryptfs.h" +#include "pfk_kc.h" +#include "objsec.h" +#include "ecryptfs_kernel.h" +#include "pfk_ice.h" + +static DEFINE_MUTEX(pfk_ecryptfs_lock); +static bool pfk_ecryptfs_ready; +static int g_events_handle; + + +/* might be replaced by a table when more than one cipher is supported */ +#define PFK_SUPPORTED_CIPHER "aes_xts" +#define PFK_SUPPORTED_SALT_SIZE 32 + +static void *pfk_ecryptfs_get_data(const struct inode *inode); +static void pfk_ecryptfs_open_cb(struct inode *inode, void *ecryptfs_data); +static void pfk_ecryptfs_release_cb(struct inode *inode); +static bool pfk_ecryptfs_is_cipher_supported_cb(const void *ecryptfs_data); +static size_t pfk_ecryptfs_get_salt_key_size_cb(const void *ecryptfs_data); +static bool pfk_ecryptfs_is_hw_crypt_cb(void); + + +/** + * pfk_is_ecryptfs_type() - return true if inode belongs to ICE ecryptfs PFE + * @inode: inode pointer + */ +bool pfk_is_ecryptfs_type(const struct inode *inode) +{ + void *ecryptfs_data = NULL; + + /* + * the actual filesystem of an inode is still ext4, eCryptfs never + * reaches bio + */ + if (!pfe_is_inode_filesystem_type(inode, "ext4")) + return false; + + ecryptfs_data = pfk_ecryptfs_get_data(inode); + + if (!ecryptfs_data) + return false; + + return true; +} + +static int pfk_ecryptfs_inode_alloc_security(struct inode *inode) +{ + struct inode_security_struct *i_sec = NULL; + + if (inode == NULL) + return -EINVAL; + + i_sec = kzalloc(sizeof(*i_sec), GFP_KERNEL); + + if (i_sec == NULL) + return -ENOMEM; + + inode->i_security = i_sec; + + return 0; +} + +static void pfk_ecryptfs_inode_free_security(struct inode *inode) +{ + if (inode == NULL) + return; + + kzfree(inode->i_security); +} + +static struct security_hook_list pfk_ecryptfs_hooks[] = { + LSM_HOOK_INIT(inode_alloc_security, pfk_ecryptfs_inode_alloc_security), + LSM_HOOK_INIT(inode_free_security, pfk_ecryptfs_inode_free_security), +}; + +/* + * pfk_ecryptfs_lsm_init() - makes sure either se-linux or pfk_ecryptfs are + * registered as security module. + * + * This is required because ecryptfs uses a field inside security struct in + * inode to store its info + */ +static int __init pfk_ecryptfs_lsm_init(void) +{ + /* Check if PFK is the chosen lsm via security_module_enable() */ + if (security_module_enable("pfk_ecryptfs")) { + security_add_hooks(pfk_ecryptfs_hooks, + ARRAY_SIZE(pfk_ecryptfs_hooks)); + pr_debug("pfk_ecryptfs is the chosen lsm, registered successfully !\n"); + } else { + pr_debug("pfk_ecryptfs is not the chosen lsm.\n"); + if (!selinux_is_enabled()) { + pr_err("se linux is not enabled.\n"); + return -ENODEV; + } + } + + return 0; +} + +/* + * pfk_ecryptfs_deinit() - Deinit function, should be invoked by upper PFK layer + */ +void __exit pfk_ecryptfs_deinit(void) +{ + pfk_ecryptfs_ready = false; + ecryptfs_unregister_from_events(g_events_handle); +} + +/* + * pfk_ecryptfs_init() - Init function, should be invoked by upper PFK layer + */ +int __init pfk_ecryptfs_init(void) +{ + int ret = 0; + struct ecryptfs_events events = {0}; + + events.open_cb = pfk_ecryptfs_open_cb; + events.release_cb = pfk_ecryptfs_release_cb; + events.is_cipher_supported_cb = pfk_ecryptfs_is_cipher_supported_cb; + events.is_hw_crypt_cb = pfk_ecryptfs_is_hw_crypt_cb; + events.get_salt_key_size_cb = pfk_ecryptfs_get_salt_key_size_cb; + + g_events_handle = ecryptfs_register_to_events(&events); + if (g_events_handle == 0) { + pr_err("could not register with eCryptfs, error %d\n", ret); + goto fail; + } + + ret = pfk_ecryptfs_lsm_init(); + if (ret != 0) { + pr_debug("neither pfk nor se-linux sec modules are enabled\n"); + pr_debug("not an error, just don't enable PFK ecryptfs\n"); + ecryptfs_unregister_from_events(g_events_handle); + return 0; + } + + pfk_ecryptfs_ready = true; + pr_info("PFK ecryptfs inited successfully\n"); + + return 0; + +fail: + pr_err("Failed to init PFK ecryptfs\n"); + return -ENODEV; +} + +/** + * pfk_ecryptfs_is_ready() - driver is initialized and ready. + * + * Return: true if the driver is ready. + */ +static inline bool pfk_ecryptfs_is_ready(void) +{ + return pfk_ecryptfs_ready; +} + +/** + * pfk_ecryptfs_get_page_index() - get the inode from a bio. + * @bio: Pointer to BIO structure. + * + * Walk the bio struct links to get the inode. + * Please note, that in general bio may consist of several pages from + * several files, but in our case we always assume that all pages come + * from the same file, since our logic ensures it. That is why we only + * walk through the first page to look for inode. + * + * Return: pointer to the inode struct if successful, or NULL otherwise. + * + */ +static int pfk_ecryptfs_get_page_index(const struct bio *bio, + pgoff_t *page_index) +{ + if (!bio || !page_index) + return -EINVAL; + if (!bio_has_data((struct bio *)bio)) + return -EINVAL; + if (!bio->bi_io_vec) + return -EINVAL; + if (!bio->bi_io_vec->bv_page) + return -EINVAL; + + *page_index = bio->bi_io_vec->bv_page->index; + + return 0; +} + +/** + * pfk_ecryptfs_get_data() - retrieves ecryptfs data stored inside node + * @inode: inode + * + * Return the data or NULL if there isn't any or in case of error + * Should be invoked under lock + */ +static void *pfk_ecryptfs_get_data(const struct inode *inode) +{ + struct inode_security_struct *isec = NULL; + + if (!inode) + return NULL; + + isec = inode->i_security; + + if (!isec) { + pr_debug("i_security is NULL, could be irrelevant file\n"); + return NULL; + } + + return isec->pfk_data; +} + +/** + * pfk_ecryptfs_set_data() - stores ecryptfs data inside node + * @inode: inode to update + * @data: data to put inside the node + * + * Returns 0 in case of success, error otherwise + * Should be invoked under lock + */ +static int pfk_ecryptfs_set_data(struct inode *inode, void *ecryptfs_data) +{ + struct inode_security_struct *isec = NULL; + + if (!inode) + return -EINVAL; + + isec = inode->i_security; + + if (!isec) { + pr_err("i_security is NULL, not ready yet\n"); + return -EINVAL; + } + + isec->pfk_data = ecryptfs_data; + + return 0; +} + + +/** + * pfk_ecryptfs_parse_cipher() - parse cipher from ecryptfs to enum + * @ecryptfs_data: ecrypfs data + * @algo: pointer to store the output enum (can be null) + * + * return 0 in case of success, error otherwise (i.e not supported cipher) + */ +static int pfk_ecryptfs_parse_cipher(const void *ecryptfs_data, + enum ice_cryto_algo_mode *algo) +{ + /* + * currently only AES XTS algo is supported + * in the future, table with supported ciphers might + * be introduced + */ + + if (!ecryptfs_data) + return -EINVAL; + + if (!ecryptfs_cipher_match(ecryptfs_data, + PFK_SUPPORTED_CIPHER, sizeof(PFK_SUPPORTED_CIPHER))) { + pr_debug("ecryptfs alghoritm is not supported by pfk\n"); + return -EINVAL; + } + + if (algo) + *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS; + + return 0; +} + +/* + * pfk_ecryptfs_parse_inode() - parses key and algo information from inode + * + * Should be invoked by upper pfk layer + * @bio: bio + * @inode: inode to be parsed + * @key_info: out, key and salt information to be stored + * @algo: out, algorithm to be stored (can be null) + * @is_pfe: out, will be false if inode is not relevant to PFE, in such a case + * it should be treated as non PFE by the block layer + */ +int pfk_ecryptfs_parse_inode(const struct bio *bio, + const struct inode *inode, + struct pfk_key_info *key_info, + enum ice_cryto_algo_mode *algo, + bool *is_pfe) +{ + int ret = 0; + void *ecryptfs_data = NULL; + pgoff_t offset; + bool is_metadata = false; + + if (!is_pfe) + return -EINVAL; + + /* + * only a few errors below can indicate that + * this function was not invoked within PFE context, + * otherwise we will consider it PFE + */ + *is_pfe = true; + + if (!pfk_ecryptfs_is_ready()) + return -ENODEV; + + if (!inode) + return -EINVAL; + + if (!key_info) + return -EINVAL; + + ecryptfs_data = pfk_ecryptfs_get_data(inode); + if (!ecryptfs_data) { + pr_err("internal error, no ecryptfs data\n"); + return -EINVAL; + } + + ret = pfk_ecryptfs_get_page_index(bio, &offset); + if (ret != 0) { + pr_err("could not get page index from bio, probably bug %d\n", + ret); + return -EINVAL; + } + + is_metadata = ecryptfs_is_page_in_metadata(ecryptfs_data, offset); + if (is_metadata == true) { + pr_debug("ecryptfs metadata, bypassing ICE\n"); + *is_pfe = false; + return -EPERM; + } + + key_info->key = ecryptfs_get_key(ecryptfs_data); + if (!key_info->key) { + pr_err("could not parse key from ecryptfs\n"); + return -EINVAL; + } + + key_info->key_size = ecryptfs_get_key_size(ecryptfs_data); + if (!key_info->key_size) { + pr_err("could not parse key size from ecryptfs\n"); + return -EINVAL; + } + + key_info->salt = ecryptfs_get_salt(ecryptfs_data); + if (!key_info->salt) { + pr_err("could not parse salt from ecryptfs\n"); + return -EINVAL; + } + + key_info->salt_size = ecryptfs_get_salt_size(ecryptfs_data); + if (!key_info->salt_size) { + pr_err("could not parse salt size from ecryptfs\n"); + return -EINVAL; + } + + ret = pfk_ecryptfs_parse_cipher(ecryptfs_data, algo); + if (ret != 0) { + pr_err("not supported cipher\n"); + return ret; + } + + return 0; +} + +/** + * pfk_ecryptfs_allow_merge_bio() - Check if 2 bios can be merged. + * + * Should be invoked by upper pfk layer + * + * @bio1: Pointer to first BIO structure. + * @bio2: Pointer to second BIO structure. + * @inode1: Pointer to inode from first bio + * @inode2: Pointer to inode from second bio + * + * Prevent merging of BIOs from encrypted and non-encrypted + * files, or files encrypted with different key. + * Also prevent non encrypted and encrypted data from the same file + * to be merged (ecryptfs header if stored inside file should be non + * encrypted) + * + * Return: true if the BIOs allowed to be merged, false + * otherwise. + */ +bool pfk_ecryptfs_allow_merge_bio(const struct bio *bio1, + const struct bio *bio2, const struct inode *inode1, + const struct inode *inode2) +{ + int ret; + void *ecryptfs_data1 = NULL; + void *ecryptfs_data2 = NULL; + pgoff_t offset1, offset2; + + /* if there is no ecryptfs pfk, don't disallow merging blocks */ + if (!pfk_ecryptfs_is_ready()) + return true; + + if (!inode1 || !inode2) + return false; + + ecryptfs_data1 = pfk_ecryptfs_get_data(inode1); + ecryptfs_data2 = pfk_ecryptfs_get_data(inode2); + + if (!ecryptfs_data1 || !ecryptfs_data2) { + pr_err("internal error, ecryptfs data should not be null"); + return false; + } + + /* + * if we have 2 different encrypted files merge is not allowed + */ + if (!ecryptfs_is_data_equal(ecryptfs_data1, ecryptfs_data2)) + return false; + + /* + * at this point both bio's are in the same file which is probably + * encrypted, last thing to check is header vs data + * We are assuming that we are not working in O_DIRECT mode, + * since it is not currently supported by eCryptfs + */ + ret = pfk_ecryptfs_get_page_index(bio1, &offset1); + if (ret != 0) { + pr_err("could not get page index from bio1, probably bug %d\n", + ret); + return false; + } + + ret = pfk_ecryptfs_get_page_index(bio2, &offset2); + if (ret != 0) { + pr_err("could not get page index from bio2, bug %d\n", ret); + return false; + } + + return (ecryptfs_is_page_in_metadata(ecryptfs_data1, offset1) == + ecryptfs_is_page_in_metadata(ecryptfs_data2, offset2)); +} + +/** + * pfk_ecryptfs_open_cb() - callback function for file open event + * @inode: file inode + * @data: data provided by eCryptfs + * + * Will be invoked from eCryptfs in case of file open event + */ +static void pfk_ecryptfs_open_cb(struct inode *inode, void *ecryptfs_data) +{ + size_t key_size; + + if (!pfk_ecryptfs_is_ready()) + return; + + if (!inode) { + pr_err("inode is null\n"); + return; + } + + key_size = ecryptfs_get_key_size(ecryptfs_data); + if (!(key_size)) { + pr_err("could not parse key size from ecryptfs\n"); + return; + } + + if (pfk_ecryptfs_parse_cipher(ecryptfs_data, NULL) != 0) { + pr_debug("open_cb: not supported cipher\n"); + return; + } + + if (pfk_key_size_to_key_type(key_size, NULL) != 0) + return; + + mutex_lock(&pfk_ecryptfs_lock); + pfk_ecryptfs_set_data(inode, ecryptfs_data); + mutex_unlock(&pfk_ecryptfs_lock); +} + +/** + * pfk_ecryptfs_release_cb() - callback function for file release event + * @inode: file inode + * + * Will be invoked from eCryptfs in case of file release event + */ +static void pfk_ecryptfs_release_cb(struct inode *inode) +{ + const unsigned char *key = NULL; + const unsigned char *salt = NULL; + size_t key_size = 0; + size_t salt_size = 0; + void *data = NULL; + + if (!pfk_ecryptfs_is_ready()) + return; + + if (!inode) { + pr_err("inode is null\n"); + return; + } + + data = pfk_ecryptfs_get_data(inode); + if (!data) { + pr_debug("could not get ecryptfs data from inode\n"); + return; + } + + key = ecryptfs_get_key(data); + if (!key) { + pr_err("could not parse key from ecryptfs\n"); + return; + } + + key_size = ecryptfs_get_key_size(data); + if (!(key_size)) { + pr_err("could not parse key size from ecryptfs\n"); + return; + } + + salt = ecryptfs_get_salt(data); + if (!salt) { + pr_err("could not parse salt from ecryptfs\n"); + return; + } + + salt_size = ecryptfs_get_salt_size(data); + if (!salt_size) { + pr_err("could not parse salt size from ecryptfs\n"); + return; + } + + pfk_kc_remove_key_with_salt(key, key_size, salt, salt_size); + + mutex_lock(&pfk_ecryptfs_lock); + pfk_ecryptfs_set_data(inode, NULL); + mutex_unlock(&pfk_ecryptfs_lock); +} + +/* + * pfk_ecryptfs_is_cipher_supported_cb() - callback function to determine + * whether a particular cipher (stored in ecryptfs_data) is cupported by pfk + * + * Ecryptfs should invoke this callback whenever it needs to determine whether + * pfk supports the particular cipher mode + * + * @ecryptfs_data: ecryptfs data + */ +static bool pfk_ecryptfs_is_cipher_supported_cb(const void *ecryptfs_data) +{ + if (!pfk_ecryptfs_is_ready()) + return false; + + if (!ecryptfs_data) + return false; + + return (pfk_ecryptfs_parse_cipher(ecryptfs_data, NULL)) == 0; +} + +/* + * pfk_ecryptfs_is_hw_crypt_cb() - callback function that implements a query + * by ecryptfs whether PFK supports HW encryption + */ +static bool pfk_ecryptfs_is_hw_crypt_cb(void) +{ + if (!pfk_ecryptfs_is_ready()) + return false; + + return true; +} + +/* + * pfk_ecryptfs_get_salt_key_size_cb() - callback function to determine + * what is the salt size supported by PFK + * + * @ecryptfs_data: ecryptfs data + */ +static size_t pfk_ecryptfs_get_salt_key_size_cb(const void *ecryptfs_data) +{ + if (!pfk_ecryptfs_is_ready()) + return 0; + + if (!pfk_ecryptfs_is_cipher_supported_cb(ecryptfs_data)) + return 0; + + return PFK_SUPPORTED_SALT_SIZE; +} diff --git a/security/pfe/pfk_ecryptfs.h b/security/pfe/pfk_ecryptfs.h new file mode 100644 index 000000000000..1d9942c4c09b --- /dev/null +++ b/security/pfe/pfk_ecryptfs.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _PFK_ECRYPTFS_H_ +#define _PFK_ECRYPTFS_H_ + +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <crypto/ice.h> +#include "pfk_internal.h" + + +bool pfk_is_ecryptfs_type(const struct inode *inode); + +int pfk_ecryptfs_parse_inode(const struct bio *bio, + const struct inode *inode, + struct pfk_key_info *key_info, + enum ice_cryto_algo_mode *algo, + bool *is_pfe); + +bool pfk_ecryptfs_allow_merge_bio(const struct bio *bio1, + const struct bio *bio2, const struct inode *inode1, + const struct inode *inode2); + +int __init pfk_ecryptfs_init(void); + +void __exit pfk_ecryptfs_deinit(void); + +#endif /* _PFK_ECRYPTFS_H_ */ diff --git a/security/pfe/pfk_ext4.c b/security/pfe/pfk_ext4.c new file mode 100644 index 000000000000..07e2c7ab829c --- /dev/null +++ b/security/pfe/pfk_ext4.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * Per-File-Key (PFK) - EXT4 + * + * This driver is used for working with EXT4 crypt extension + * + * The key information is stored in node by EXT4 when file is first opened + * and will be later accessed by Block Device Driver to actually load the key + * to encryption hw. + * + * PFK exposes API's for loading and removing keys from encryption hw + * and also API to determine whether 2 adjacent blocks can be agregated by + * Block Layer in one request to encryption hw. + * + */ + + +/* Uncomment the line below to enable debug messages */ +/* #define DEBUG 1 */ +#define pr_fmt(fmt) "pfk_ext4 [%s]: " fmt, __func__ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/printk.h> + +#include "ext4_ice.h" +#include "pfk_ext4.h" + +static bool pfk_ext4_ready; + +/* + * pfk_ext4_deinit() - Deinit function, should be invoked by upper PFK layer + */ +void __exit pfk_ext4_deinit(void) +{ + pfk_ext4_ready = false; +} + +/* + * pfk_ecryptfs_init() - Init function, should be invoked by upper PFK layer + */ +int __init pfk_ext4_init(void) +{ + pfk_ext4_ready = true; + pr_info("PFK EXT4 inited successfully\n"); + + return 0; +} + +/** + * pfk_ecryptfs_is_ready() - driver is initialized and ready. + * + * Return: true if the driver is ready. + */ +static inline bool pfk_ext4_is_ready(void) +{ + return pfk_ext4_ready; +} + +/** + * pfk_ext4_dump_inode() - dumps all interesting info about inode to the screen + * + * + */ +/* + * static void pfk_ext4_dump_inode(const struct inode* inode) + * { + * struct ext4_crypt_info *ci = ext4_encryption_info((struct inode*)inode); + * + * pr_debug("dumping inode with address 0x%p\n", inode); + * pr_debug("S_ISREG is %d\n", S_ISREG(inode->i_mode)); + * pr_debug("EXT4_INODE_ENCRYPT flag is %d\n", + * ext4_test_inode_flag((struct inode*)inode, EXT4_INODE_ENCRYPT)); + * if (ci) { + * pr_debug("crypt_info address 0x%p\n", ci); + * pr_debug("ci->ci_data_mode %d\n", ci->ci_data_mode); + * } else { + * pr_debug("crypt_info is NULL\n"); + * } + * } +*/ + +/** + * pfk_is_ext4_type() - return true if inode belongs to ICE EXT4 PFE + * @inode: inode pointer + */ +bool pfk_is_ext4_type(const struct inode *inode) +{ + if (!pfe_is_inode_filesystem_type(inode, "ext4")) + return false; + + return ext4_should_be_processed_by_ice(inode); +} + +/** + * pfk_ext4_parse_cipher() - parse cipher from inode to enum + * @inode: inode + * @algo: pointer to store the output enum (can be null) + * + * return 0 in case of success, error otherwise (i.e not supported cipher) + */ +static int pfk_ext4_parse_cipher(const struct inode *inode, + enum ice_cryto_algo_mode *algo) +{ + /* + * currently only AES XTS algo is supported + * in the future, table with supported ciphers might + * be introduced + */ + + if (!inode) + return -EINVAL; + + if (!ext4_is_aes_xts_cipher(inode)) { + pr_err("ext4 alghoritm is not supported by pfk\n"); + return -EINVAL; + } + + if (algo) + *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS; + + return 0; +} + + +int pfk_ext4_parse_inode(const struct bio *bio, + const struct inode *inode, + struct pfk_key_info *key_info, + enum ice_cryto_algo_mode *algo, + bool *is_pfe) +{ + int ret = 0; + + if (!is_pfe) + return -EINVAL; + + /* + * only a few errors below can indicate that + * this function was not invoked within PFE context, + * otherwise we will consider it PFE + */ + *is_pfe = true; + + if (!pfk_ext4_is_ready()) + return -ENODEV; + + if (!inode) + return -EINVAL; + + if (!key_info) + return -EINVAL; + + key_info->key = ext4_get_ice_encryption_key(inode); + if (!key_info->key) { + pr_err("could not parse key from ext4\n"); + return -EINVAL; + } + + key_info->key_size = ext4_get_ice_encryption_key_size(inode); + if (!key_info->key_size) { + pr_err("could not parse key size from ext4\n"); + return -EINVAL; + } + + key_info->salt = ext4_get_ice_encryption_salt(inode); + if (!key_info->salt) { + pr_err("could not parse salt from ext4\n"); + return -EINVAL; + } + + key_info->salt_size = ext4_get_ice_encryption_salt_size(inode); + if (!key_info->salt_size) { + pr_err("could not parse salt size from ext4\n"); + return -EINVAL; + } + + ret = pfk_ext4_parse_cipher(inode, algo); + if (ret != 0) { + pr_err("not supported cipher\n"); + return ret; + } + + return 0; +} + +bool pfk_ext4_allow_merge_bio(const struct bio *bio1, + const struct bio *bio2, const struct inode *inode1, + const struct inode *inode2) +{ + /* if there is no ext4 pfk, don't disallow merging blocks */ + if (!pfk_ext4_is_ready()) + return true; + + if (!inode1 || !inode2) + return false; + + return ext4_is_ice_encryption_info_equal(inode1, inode2); +} + diff --git a/security/pfe/pfk_ext4.h b/security/pfe/pfk_ext4.h new file mode 100644 index 000000000000..6b367b428e50 --- /dev/null +++ b/security/pfe/pfk_ext4.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _PFK_EXT4_H_ +#define _PFK_EXT4_H_ + +#include <linux/types.h> +#include <linux/fs.h> +#include <crypto/ice.h> +#include "pfk_internal.h" + +bool pfk_is_ext4_type(const struct inode *inode); + +int pfk_ext4_parse_inode(const struct bio *bio, + const struct inode *inode, + struct pfk_key_info *key_info, + enum ice_cryto_algo_mode *algo, + bool *is_pfe); + +bool pfk_ext4_allow_merge_bio(const struct bio *bio1, + const struct bio *bio2, const struct inode *inode1, + const struct inode *inode2); + +int __init pfk_ext4_init(void); + +void __exit pfk_ext4_deinit(void); + +#endif /* _PFK_EXT4_H_ */ diff --git a/security/pfe/pfk_ice.h b/security/pfe/pfk_ice.h index 1d6339a575be..fe3f2ad3950c 100644 --- a/security/pfe/pfk_ice.h +++ b/security/pfe/pfk_ice.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* 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 @@ -22,7 +22,6 @@ #include <linux/types.h> - int pfk_ice_init(void); int pfk_ice_deinit(void); diff --git a/security/pfe/pfk_internal.h b/security/pfe/pfk_internal.h new file mode 100644 index 000000000000..abdd0b325b39 --- /dev/null +++ b/security/pfe/pfk_internal.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _PFK_INTERNAL_H_ +#define _PFK_INTERNAL_H_ + +#include <linux/types.h> +#include <crypto/ice.h> + +struct pfk_key_info { + const unsigned char *key; + const unsigned char *salt; + size_t key_size; + size_t salt_size; +}; + +int pfk_key_size_to_key_type(size_t key_size, + enum ice_crpto_key_size *key_size_type); + +bool pfe_is_inode_filesystem_type(const struct inode *inode, + const char *fs_type); + +char *inode_to_filename(const struct inode *inode); + +#endif /* _PFK_INTERNAL_H_ */ diff --git a/security/pfe/pft.c b/security/pfe/pft.c index e4bbba7b6295..74433c4fe0ff 100644 --- a/security/pfe/pft.c +++ b/security/pfe/pft.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * 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 @@ -746,7 +746,7 @@ EXPORT_SYMBOL(pft_get_key_index); * Return: true if the BIOs allowed to be merged, false * otherwise. */ -bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2) +bool pft_allow_merge_bio(const struct bio *bio1, const struct bio *bio2) { u32 key_index1 = 0, key_index2 = 0; bool is_encrypted1 = false, is_encrypted2 = false; diff --git a/security/security.c b/security/security.c index 81a555c14a35..7d1de1f6299f 100644 --- a/security/security.c +++ b/security/security.c @@ -857,11 +857,6 @@ int security_file_close(struct file *file) return call_int_hook(file_close, 0, file); } -bool security_allow_merge_bio(struct bio *bio1, struct bio *bio2) -{ - return call_int_hook(allow_merge_bio, 1, bio1, bio2); -} - int security_task_create(unsigned long clone_flags) { return call_int_hook(task_create, 0, clone_flags); @@ -1693,7 +1688,6 @@ struct security_hook_heads security_hook_heads = { .file_receive = LIST_HEAD_INIT(security_hook_heads.file_receive), .file_open = LIST_HEAD_INIT(security_hook_heads.file_open), .file_close = LIST_HEAD_INIT(security_hook_heads.file_close), - .allow_merge_bio = LIST_HEAD_INIT(security_hook_heads.allow_merge_bio), .task_create = LIST_HEAD_INIT(security_hook_heads.task_create), .task_free = LIST_HEAD_INIT(security_hook_heads.task_free), .cred_alloc_blank = diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 33d1262704ec..89fea87feafb 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3585,12 +3585,6 @@ static int selinux_file_close(struct file *file) return pft_file_close(file); } -static bool selinux_allow_merge_bio(struct bio *bio1, struct bio *bio2) -{ - return pft_allow_merge_bio(bio1, bio2) && - pfk_allow_merge_bio(bio1, bio2); -} - /* task security operations */ static int selinux_task_create(unsigned long clone_flags) @@ -6000,7 +5994,6 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(file_open, selinux_file_open), LSM_HOOK_INIT(file_close, selinux_file_close), - LSM_HOOK_INIT(allow_merge_bio, selinux_allow_merge_bio), LSM_HOOK_INIT(task_create, selinux_task_create), LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank), diff --git a/sound/soc/codecs/audio-ext-clk.c b/sound/soc/codecs/audio-ext-clk.c index c422267dbf2c..34b1d9481457 100755..100644 --- a/sound/soc/codecs/audio-ext-clk.c +++ b/sound/soc/codecs/audio-ext-clk.c @@ -193,7 +193,13 @@ static int audio_get_pinctrl(struct platform_device *pdev) int ret; pnctrl_info = &audio_ap_clk2.pnctrl_info; - pnctrl_info->pinctrl = NULL; + + if (pnctrl_info->pinctrl) { + dev_dbg(&pdev->dev, "%s: already requested before\n", + __func__); + return -EINVAL; + } + pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR_OR_NULL(pinctrl)) { dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n", diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index b5126351dda0..a4d92fdd3e7c 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -1114,7 +1114,7 @@ static void tasha_cdc_sido_ccl_enable(struct tasha_priv *tasha, bool ccl_flag) if (!codec) return; - if (!TASHA_IS_2_0(tasha->wcd9xxx->version)) { + if (!TASHA_IS_2_0(tasha->wcd9xxx)) { dev_dbg(codec->dev, "%s: tasha version < 2p0, return\n", __func__); return; @@ -1139,7 +1139,7 @@ static void tasha_cdc_sido_ccl_enable(struct tasha_priv *tasha, bool ccl_flag) static bool tasha_cdc_is_svs_enabled(struct tasha_priv *tasha) { - if (TASHA_IS_2_0(tasha->wcd9xxx->version) && + if (TASHA_IS_2_0(tasha->wcd9xxx) && svs_scaling_enabled) return true; @@ -1269,7 +1269,7 @@ int tasha_enable_efuse_sensing(struct snd_soc_codec *codec) tasha_cdc_mclk_enable(codec, true, false); - if (!TASHA_IS_2_0(priv->wcd9xxx->version)) + if (!TASHA_IS_2_0(priv->wcd9xxx)) snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x02); snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, @@ -1282,7 +1282,7 @@ int tasha_enable_efuse_sensing(struct snd_soc_codec *codec) if (!(snd_soc_read(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS) & 0x01)) WARN(1, "%s: Efuse sense is not complete\n", __func__); - if (TASHA_IS_2_0(priv->wcd9xxx->version)) { + if (TASHA_IS_2_0(priv->wcd9xxx)) { if (!(snd_soc_read(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0) & 0x40)) snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, @@ -1502,7 +1502,7 @@ static void tasha_mbhc_hph_l_pull_up_control(struct snd_soc_codec *codec, dev_dbg(codec->dev, "%s: HS pull up current:%d\n", __func__, pull_up_cur); - if (TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (TASHA_IS_2_0(tasha->wcd9xxx)) snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL, 0xC0, pull_up_cur << 6); else @@ -1980,7 +1980,7 @@ static void tasha_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, }; s16 *d1 = NULL; - if (!TASHA_IS_2_0(wcd9xxx->version)) { + if (!TASHA_IS_2_0(wcd9xxx)) { dev_dbg(codec->dev, "%s: Z-det is not supported for this codec version\n", __func__); *zl = 0; @@ -2168,13 +2168,13 @@ static void tasha_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec, if (enable) { snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x40); - if (TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (TASHA_IS_2_0(tasha->wcd9xxx)) snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x10, 0x10); } else { snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x00); - if (TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (TASHA_IS_2_0(tasha->wcd9xxx)) snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x10, 0x00); } @@ -2276,6 +2276,7 @@ static int tasha_put_anc_func(struct snd_kcontrol *kcontrol, snd_soc_dapm_enable_pin(dapm, "ANC HPHL"); snd_soc_dapm_enable_pin(dapm, "ANC EAR PA"); snd_soc_dapm_enable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); snd_soc_dapm_disable_pin(dapm, "LINEOUT2"); snd_soc_dapm_disable_pin(dapm, "LINEOUT2 PA"); snd_soc_dapm_disable_pin(dapm, "LINEOUT1"); @@ -2297,6 +2298,7 @@ static int tasha_put_anc_func(struct snd_kcontrol *kcontrol, snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); snd_soc_dapm_enable_pin(dapm, "LINEOUT2"); snd_soc_dapm_enable_pin(dapm, "LINEOUT2 PA"); snd_soc_dapm_enable_pin(dapm, "LINEOUT1"); @@ -2838,23 +2840,19 @@ static const struct snd_kcontrol_new aif4_mad_mixer[] = { }; static const struct snd_kcontrol_new rx_int1_spline_mix_switch[] = { - SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0), - SOC_DAPM_SINGLE("HPHL Native Switch", SND_SOC_NOPM, 0, 1, 0) + SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new rx_int2_spline_mix_switch[] = { - SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0), - SOC_DAPM_SINGLE("HPHR Native Switch", SND_SOC_NOPM, 0, 1, 0) + SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new rx_int3_spline_mix_switch[] = { - SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0), - SOC_DAPM_SINGLE("LO1 Native Switch", SND_SOC_NOPM, 0, 1, 0) + SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new rx_int4_spline_mix_switch[] = { - SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0), - SOC_DAPM_SINGLE("LO2 Native Switch", SND_SOC_NOPM, 0, 1, 0) + SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0) }; static const struct snd_kcontrol_new rx_int5_spline_mix_switch[] = { @@ -3667,7 +3665,7 @@ static int tasha_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_PRE_PMU: tasha->rx_bias_count++; if (tasha->rx_bias_count == 1) { - if (TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (TASHA_IS_2_0(tasha->wcd9xxx)) tasha_codec_init_flyback(codec); snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES, 0x01, 0x01); @@ -3826,7 +3824,8 @@ static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w, i = 0; anc_cal_size = anc_writes_size; - if (!strcmp(w->name, "RX INT0 DAC")) + if (!strcmp(w->name, "RX INT0 DAC") || + !strcmp(w->name, "ANC SPK1 PA")) tasha_realign_anc_coeff(codec, WCD9335_CDC_ANC0_IIR_COEFF_1_CTL, WCD9335_CDC_ANC0_IIR_COEFF_2_CTL); @@ -3875,8 +3874,9 @@ static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMD: if (!strcmp(w->name, "ANC HPHL PA") || - !strcmp(w->name, "ANC EAR PA") || - !strcmp(w->name, "ANC LINEOUT1 PA")) { + !strcmp(w->name, "ANC EAR PA") || + !strcmp(w->name, "ANC SPK1 PA") || + !strcmp(w->name, "ANC LINEOUT1 PA")) { snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_1_CTL, 0x30, 0x00); msleep(50); @@ -3889,7 +3889,7 @@ static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x00); } else if (!strcmp(w->name, "ANC HPHR PA") || - !strcmp(w->name, "ANC LINEOUT2 PA")) { + !strcmp(w->name, "ANC LINEOUT2 PA")) { snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_1_CTL, 0x30, 0x00); msleep(50); @@ -3933,7 +3933,7 @@ static void tasha_codec_hph_post_pa_config(struct tasha_priv *tasha, { u8 scale_val = 0; - if (!TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (!TASHA_IS_2_0(tasha->wcd9xxx)) return; switch (event) { @@ -4267,6 +4267,35 @@ static int tasha_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, return ret; } +static int tasha_codec_enable_spk_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d %d\n", __func__, w->name, event, + tasha->anc_func); + + if (!tasha->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tasha_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x00); + ret = tasha_codec_enable_anc(w, kcontrol, event); + break; + } + return ret; +} + static int tasha_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -4418,7 +4447,7 @@ static void tasha_codec_hph_mode_config(struct snd_soc_codec *codec, { struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); - if (!TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (!TASHA_IS_2_0(tasha->wcd9xxx)) return; switch (mode) { @@ -4482,14 +4511,14 @@ static int tasha_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, /* 1000us required as per HW requirement */ usleep_range(1000, 1100); if ((hph_mode == CLS_H_LP) && - (TASHA_IS_1_1(wcd9xxx->version))) { + (TASHA_IS_1_1(wcd9xxx))) { snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, 0x03, 0x03); } break; case SND_SOC_DAPM_PRE_PMD: if ((hph_mode == CLS_H_LP) && - (TASHA_IS_1_1(wcd9xxx->version))) { + (TASHA_IS_1_1(wcd9xxx))) { snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, 0x03, 0x00); } @@ -4572,14 +4601,14 @@ static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, /* 1000us required as per HW requirement */ usleep_range(1000, 1100); if ((hph_mode == CLS_H_LP) && - (TASHA_IS_1_1(wcd9xxx->version))) { + (TASHA_IS_1_1(wcd9xxx))) { snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, 0x03, 0x03); } break; case SND_SOC_DAPM_PRE_PMD: if ((hph_mode == CLS_H_LP) && - (TASHA_IS_1_1(wcd9xxx->version))) { + (TASHA_IS_1_1(wcd9xxx))) { snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, 0x03, 0x00); } @@ -4804,7 +4833,7 @@ static void tasha_codec_hd2_control(struct snd_soc_codec *codec, u16 hd2_scale_reg; u16 hd2_enable_reg = 0; - if (!TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (!TASHA_IS_2_0(tasha->wcd9xxx)) return; if (prim_int_reg == WCD9335_CDC_RX1_RX_PATH_CTL) { @@ -6822,7 +6851,9 @@ static const struct snd_soc_dapm_route audio_map[] = { {"SPL SRC0 MUX", "SRC_IN_HPHL", "RX INT1_1 MIX1"}, {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 MIX1"}, {"RX INT1 SPLINE MIX", "HPHL Switch", "SPL SRC0 MUX"}, - {"RX INT1 SPLINE MIX", "HPHL Native Switch", "RX INT1 NATIVE SUPPLY"}, + {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"}, + {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 NATIVE MUX"}, + {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, {"RX INT1 SEC MIX", NULL, "RX INT1 SPLINE MIX"}, {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"}, {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"}, @@ -6836,7 +6867,9 @@ static const struct snd_soc_dapm_route audio_map[] = { {"SPL SRC1 MUX", "SRC_IN_HPHR", "RX INT2_1 MIX1"}, {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 MIX1"}, {"RX INT2 SPLINE MIX", "HPHR Switch", "SPL SRC1 MUX"}, - {"RX INT2 SPLINE MIX", "HPHR Native Switch", "RX INT2 NATIVE SUPPLY"}, + {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"}, + {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 NATIVE MUX"}, + {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, {"RX INT2 SEC MIX", NULL, "RX INT2 SPLINE MIX"}, {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"}, {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"}, @@ -6850,7 +6883,9 @@ static const struct snd_soc_dapm_route audio_map[] = { {"SPL SRC0 MUX", "SRC_IN_LO1", "RX INT3_1 MIX1"}, {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 MIX1"}, {"RX INT3 SPLINE MIX", "LO1 Switch", "SPL SRC0 MUX"}, - {"RX INT3 SPLINE MIX", "LO1 Native Switch", "RX INT3 NATIVE SUPPLY"}, + {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"}, + {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 NATIVE MUX"}, + {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, {"RX INT3 SEC MIX", NULL, "RX INT3 SPLINE MIX"}, {"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"}, {"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"}, @@ -6863,7 +6898,9 @@ static const struct snd_soc_dapm_route audio_map[] = { {"SPL SRC1 MUX", "SRC_IN_LO2", "RX INT4_1 MIX1"}, {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 MIX1"}, {"RX INT4 SPLINE MIX", "LO2 Switch", "SPL SRC1 MUX"}, - {"RX INT4 SPLINE MIX", "LO2 Native Switch", "RX INT4 NATIVE SUPPLY"}, + {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"}, + {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 NATIVE MUX"}, + {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, {"RX INT4 SEC MIX", NULL, "RX INT4 SPLINE MIX"}, {"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"}, {"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"}, @@ -6919,6 +6956,10 @@ static const struct snd_soc_dapm_route audio_map[] = { {"RX INT7 CHAIN", NULL, "RX_BIAS"}, {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"}, + {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"}, + {"SPK1 OUT", NULL, "ANC SPK1 PA"}, + {"SPL SRC3 MUX", "SRC_IN_SPKRR", "RX INT8_1 MIX1"}, {"RX INT8 SPLINE MIX", NULL, "RX INT8_1 MIX1"}, {"RX INT8 SPLINE MIX", "SPKRR Switch", "SPL SRC3 MUX"}, @@ -6951,6 +6992,10 @@ static const struct snd_soc_dapm_route audio_map[] = { {"ANC EAR Enable", "Switch", "ADC MUX11"}, {"RX INT0 MIX2", NULL, "ANC EAR Enable"}, + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"}, + {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"}, + {"ANC LINEOUT1 Enable", "Switch", "ADC MUX10"}, {"ANC LINEOUT1 Enable", "Switch", "ADC MUX11"}, {"RX INT3 MIX2", NULL, "ANC LINEOUT1 Enable"}, @@ -8003,7 +8048,7 @@ static void wcd_vbat_adc_out_config(struct wcd_vbat *vbat, if (!vbat->adc_config) { tasha_cdc_mclk_enable(codec, true, false); - if (TASHA_IS_2_0(wcd9xxx->version)) + if (TASHA_IS_2_0(wcd9xxx)) wcd_vbat_adc_out_config_2_0(vbat, codec); else wcd_vbat_adc_out_config_1_x(vbat, codec); @@ -9059,6 +9104,10 @@ static const char * const anc1_fb_mux_text[] = { "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2" }; +static const char * const native_mux_text[] = { + "OFF", "ON", +}; + static const struct soc_enum spl_src0_mux_chain_enum = SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 0, 3, spl_src0_mux_text); @@ -9111,6 +9160,22 @@ static const struct soc_enum rx_int8_2_mux_chain_enum = SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9, rx_int_mix_mux_text); +static const struct soc_enum int1_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int2_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int3_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int4_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + static const struct soc_enum rx_int0_1_mix_inp0_chain_enum = SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13, rx_prim_mix_text); @@ -9643,6 +9708,18 @@ static const struct snd_kcontrol_new rx_int7_2_mux = static const struct snd_kcontrol_new rx_int8_2_mux = SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum); +static const struct snd_kcontrol_new int1_1_native_mux = + SOC_DAPM_ENUM("RX INT1_1 NATIVE MUX Mux", int1_1_native_enum); + +static const struct snd_kcontrol_new int2_1_native_mux = + SOC_DAPM_ENUM("RX INT2_1 NATIVE MUX Mux", int2_1_native_enum); + +static const struct snd_kcontrol_new int3_1_native_mux = + SOC_DAPM_ENUM("RX INT3_1 NATIVE MUX Mux", int3_1_native_enum); + +static const struct snd_kcontrol_new int4_1_native_mux = + SOC_DAPM_ENUM("RX INT4_1 NATIVE MUX Mux", int4_1_native_enum); + static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux = SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum); @@ -10023,12 +10100,18 @@ static const struct snd_kcontrol_new anc_hphr_switch = static const struct snd_kcontrol_new anc_ear_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); +static const struct snd_kcontrol_new anc_ear_spkr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + static const struct snd_kcontrol_new anc_lineout1_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); static const struct snd_kcontrol_new anc_lineout2_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); +static const struct snd_kcontrol_new anc_spkr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + static const struct snd_kcontrol_new adc_us_mux0_switch = SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); @@ -10648,6 +10731,14 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { tasha_codec_configure_cpe_input, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("RX INT1_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int1_1_native_mux), + SND_SOC_DAPM_MUX("RX INT2_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int2_1_native_mux), + SND_SOC_DAPM_MUX("RX INT3_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int3_1_native_mux), + SND_SOC_DAPM_MUX("RX INT4_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int4_1_native_mux), SND_SOC_DAPM_MUX("RX MIX TX0 MUX", SND_SOC_NOPM, 0, 0, &rx_mix_tx0_mux), SND_SOC_DAPM_MUX("RX MIX TX1 MUX", SND_SOC_NOPM, 0, 0, @@ -10794,6 +10885,9 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { tasha_codec_enable_lineout_pa, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_codec_enable_spk_anc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_OUTPUT("HPHL"), SND_SOC_DAPM_OUTPUT("HPHR"), @@ -10855,10 +10949,14 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { &anc_hphr_switch), SND_SOC_DAPM_SWITCH("ANC EAR Enable", SND_SOC_NOPM, 0, 0, &anc_ear_switch), + SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_spkr_switch), SND_SOC_DAPM_SWITCH("ANC LINEOUT1 Enable", SND_SOC_NOPM, 0, 0, &anc_lineout1_switch), SND_SOC_DAPM_SWITCH("ANC LINEOUT2 Enable", SND_SOC_NOPM, 0, 0, &anc_lineout2_switch), + SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0, + &anc_spkr_pa_switch), }; static int tasha_get_channel_map(struct snd_soc_dai *dai, @@ -10948,7 +11046,7 @@ static int tasha_set_channel_map(struct snd_soc_dai *dai, /* Reserve TX12/TX13 for MAD data channel */ dai_data = &tasha->dai[AIF4_MAD_TX]; if (dai_data) { - if (TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (TASHA_IS_2_0(tasha->wcd9xxx)) list_add_tail(&core->tx_chs[TASHA_TX13].list, &dai_data->wcd9xxx_ch_list); else @@ -11899,9 +11997,9 @@ static ssize_t tasha_codec_version_read(struct snd_info_entry *entry, wcd9xxx = tasha->wcd9xxx; if (wcd9xxx->codec_type->id_major == TASHA_MAJOR) { - if (TASHA_IS_1_0(wcd9xxx->version)) + if (TASHA_IS_1_0(wcd9xxx)) len = snprintf(buffer, sizeof(buffer), "WCD9335_1_0\n"); - else if (TASHA_IS_1_1(wcd9xxx->version)) + else if (TASHA_IS_1_1(wcd9xxx)) len = snprintf(buffer, sizeof(buffer), "WCD9335_1_1\n"); else snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); @@ -12243,7 +12341,7 @@ static void tasha_update_reg_reset_values(struct snd_soc_codec *codec) u32 i; struct wcd9xxx *tasha_core = dev_get_drvdata(codec->dev->parent); - if (TASHA_IS_1_1(tasha_core->version)) { + if (TASHA_IS_1_1(tasha_core)) { for (i = 0; i < ARRAY_SIZE(tasha_reg_update_reset_val_1_1); i++) snd_soc_write(codec, @@ -12263,27 +12361,27 @@ static void tasha_codec_init_reg(struct snd_soc_codec *codec) tasha_codec_reg_init_common_val[i].mask, tasha_codec_reg_init_common_val[i].val); - if (TASHA_IS_1_1(wcd9xxx->version) || - TASHA_IS_1_0(wcd9xxx->version)) + if (TASHA_IS_1_1(wcd9xxx) || + TASHA_IS_1_0(wcd9xxx)) for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_1_x_val); i++) snd_soc_update_bits(codec, tasha_codec_reg_init_1_x_val[i].reg, tasha_codec_reg_init_1_x_val[i].mask, tasha_codec_reg_init_1_x_val[i].val); - if (TASHA_IS_1_1(wcd9xxx->version)) { + if (TASHA_IS_1_1(wcd9xxx)) { for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_1); i++) snd_soc_update_bits(codec, tasha_codec_reg_init_val_1_1[i].reg, tasha_codec_reg_init_val_1_1[i].mask, tasha_codec_reg_init_val_1_1[i].val); - } else if (TASHA_IS_1_0(wcd9xxx->version)) { + } else if (TASHA_IS_1_0(wcd9xxx)) { for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_0); i++) snd_soc_update_bits(codec, tasha_codec_reg_init_val_1_0[i].reg, tasha_codec_reg_init_val_1_0[i].mask, tasha_codec_reg_init_val_1_0[i].val); - } else if (TASHA_IS_2_0(wcd9xxx->version)) { + } else if (TASHA_IS_2_0(wcd9xxx)) { for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_2_0); i++) snd_soc_update_bits(codec, tasha_codec_reg_init_val_2_0[i].reg, @@ -12770,7 +12868,7 @@ static int tasha_codec_cpe_fll_enable(struct snd_soc_codec *codec, } } - if (TASHA_IS_1_0(wcd9xxx->version)) { + if (TASHA_IS_1_0(wcd9xxx)) { tasha_cdc_mclk_enable(codec, true, false); clk_sel_reg_val = 0x02; } @@ -12809,7 +12907,7 @@ static int tasha_codec_cpe_fll_enable(struct snd_soc_codec *codec, snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0, 0x01, 0x00); - if (TASHA_IS_1_0(wcd9xxx->version)) + if (TASHA_IS_1_0(wcd9xxx)) tasha_cdc_mclk_enable(codec, false, false); /* @@ -12938,7 +13036,7 @@ static int tasha_cpe_err_irq_control(struct snd_soc_codec *codec, struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); u8 irq_bits; - if (TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (TASHA_IS_2_0(tasha->wcd9xxx)) irq_bits = 0xFF; else irq_bits = 0x3F; @@ -13251,7 +13349,7 @@ static int tasha_codec_probe(struct snd_soc_codec *codec) } /* Initialize MBHC module */ - if (TASHA_IS_2_0(tasha->wcd9xxx->version)) { + if (TASHA_IS_2_0(tasha->wcd9xxx)) { wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].reg = WCD9335_MBHC_FSM_STATUS; wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].mask = 0x01; @@ -13365,6 +13463,7 @@ static int tasha_codec_probe(struct snd_soc_codec *codec) snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); mutex_unlock(&tasha->codec_mutex); snd_soc_dapm_sync(dapm); @@ -13636,7 +13735,7 @@ static int tasha_swrm_clock(void *handle, bool enable) if (enable) { tasha->swr_clk_users++; if (tasha->swr_clk_users == 1) { - if (TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (TASHA_IS_2_0(tasha->wcd9xxx)) regmap_update_bits( tasha->wcd9xxx->regmap, WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, @@ -13653,7 +13752,7 @@ static int tasha_swrm_clock(void *handle, bool enable) WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL, 0x01, 0x00); __tasha_cdc_mclk_enable(tasha, false); - if (TASHA_IS_2_0(tasha->wcd9xxx->version)) + if (TASHA_IS_2_0(tasha->wcd9xxx)) regmap_update_bits( tasha->wcd9xxx->regmap, WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h index 8b20805de6f9..ac3031ffe615 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-routing.h +++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h @@ -312,6 +312,43 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"ADC MUX13", "DMIC", "DMIC MUX13"}, {"ADC MUX13", "AMIC", "AMIC MUX13"}, + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"}, + {"DMIC MUX0", "DMIC0", "DMIC0"}, {"DMIC MUX0", "DMIC1", "DMIC1"}, {"DMIC MUX0", "DMIC2", "DMIC2"}, @@ -860,6 +897,29 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"RX INT8 CHAIN", NULL, "RX_BIAS"}, {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + /* ANC Routing */ + {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"}, + + {"ANC OUT EAR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR Enable", "Switch", "ADC MUX11"}, + {"RX INT0 MIX2", NULL, "ANC OUT EAR Enable"}, + + {"ANC EAR PA", NULL, "RX INT0 DAC"}, + {"ANC EAR", NULL, "ANC EAR PA"}, + + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"}, + {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"}, + + {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"}, + {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"}, + {"SPK1 OUT", NULL, "ANC SPK1 PA"}, + /* * SRC0, SRC1 inputs to Sidetone RX Mixer * on RX0, RX1, RX2, RX3, RX4 and RX7 chains diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index 84e5c09494c6..28914ed3f937 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -66,6 +66,15 @@ #define WCD934X_FORMATS_S16_LE (SNDRV_PCM_FMTBIT_S16_LE) +/* Macros for packing register writes into a U32 */ +#define WCD934X_PACKED_REG_SIZE sizeof(u32) +#define WCD934X_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0) + #define STRING(name) #name #define WCD_DAPM_ENUM(name, reg, offset, text) \ static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ @@ -91,6 +100,7 @@ static const struct snd_kcontrol_new name##_mux = \ #define WCD934X_MCLK_CLK_12P288MHZ 12288000 #define WCD934X_MCLK_CLK_9P6MHZ 9600000 +#define WCD934X_INTERP_MUX_NUM_INPUTS 3 #define WCD934X_NUM_INTERPOLATORS 9 #define WCD934X_NUM_DECIMATORS 9 #define WCD934X_RX_PATH_CTL_OFFSET 20 @@ -129,6 +139,8 @@ static const struct snd_kcontrol_new name##_mux = \ #define WCD934X_MAD_AUDIO_FIRMWARE_PATH "wcd934x/wcd934x_mad_audio.bin" +#define TAVIL_VERSION_ENTRY_SIZE 17 + enum { VI_SENSE_1, VI_SENSE_2, @@ -177,6 +189,16 @@ enum { INTn_2_INP_SEL_PROXIMITY, }; +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +struct tavil_idle_detect_config { + u8 hph_idle_thr; + u8 hph_idle_detect_en; +}; + static const struct intr_data wcd934x_intr_table[] = { {WCD9XXX_IRQ_SLIMBUS, false}, {WCD934X_IRQ_MBHC_SW_DET, true}, @@ -378,6 +400,24 @@ static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = { (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE), SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1 }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FF_GAIN_ADAPTIVE, 0x4, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_FF_A_GAIN_CTL), + AANC_GAIN_CONTROL, 0xFF, WCD934X_REG_BITS, 0 + }, }; static struct afe_param_cdc_reg_cfg_data tavil_audio_reg_cfg = { @@ -385,6 +425,11 @@ static struct afe_param_cdc_reg_cfg_data tavil_audio_reg_cfg = { .reg_data = audio_reg_cfg, }; +static struct afe_param_id_cdc_aanc_version tavil_cdc_aanc_version = { + .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION, + .aanc_hw_version = AANC_HW_BLOCK_VERSION_2, +}; + static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); @@ -451,8 +496,14 @@ struct tavil_priv { s32 micb_ref[TAVIL_MAX_MICBIAS]; s32 pullup_ref[TAVIL_MAX_MICBIAS]; + /* ANC related */ + u32 anc_slot; + bool anc_func; + /* compander */ int comp_enabled[COMPANDER_MAX]; + int ear_spkr_gain; + /* class h specific data */ struct wcd_clsh_cdc_data clsh_d; /* Tavil Interpolator Mode Select for EAR, HPH_L and HPH_R */ @@ -461,6 +512,9 @@ struct tavil_priv { /* Mad switch reference count */ int mad_switch_cnt; + /* track tavil interface type */ + u8 intf_type; + /* to track the status */ unsigned long status_mask; @@ -494,6 +548,10 @@ struct tavil_priv { /* cal info for codec */ struct fw_info *fw_data; + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + /* SVS voting related */ struct mutex svs_mutex; int svs_ref_cnt; @@ -504,6 +562,7 @@ struct tavil_priv { /* Main path clock users count */ int main_clk_users[WCD934X_NUM_INTERPOLATORS]; struct tavil_dsd_config *dsd_config; + struct tavil_idle_detect_config idle_det_cfg; }; static const struct tavil_reg_mask_val tavil_spkr_default[] = { @@ -537,7 +596,7 @@ int wcd934x_get_codec_info(struct wcd9xxx *wcd9xxx, { u16 id_minor, id_major; struct regmap *wcd_regmap; - int rc, version = 0; + int rc, version = -1; if (!wcd9xxx || !wcd_type) return -EINVAL; @@ -561,8 +620,22 @@ int wcd934x_get_codec_info(struct wcd9xxx *wcd9xxx, dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n", __func__, id_major, id_minor); - /* Version detection */ - version = 1.0; + if (id_major != TAVIL_MAJOR) + goto version_unknown; + + /* + * As fine version info cannot be retrieved before tavil probe. + * Assign coarse versions for possible future use before tavil probe. + */ + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_1_1; + +version_unknown: + if (version < 0) + dev_err(wcd9xxx->dev, "%s: wcd934x version unknown\n", + __func__); /* Fill codec type info */ wcd_type->id_major = id_major; @@ -720,7 +793,7 @@ void *tavil_get_afe_config(struct snd_soc_codec *codec, case AFE_SLIMBUS_SLAVE_PORT_CONFIG: return &tavil_slimbus_slave_port_cfg; case AFE_AANC_VERSION: - return NULL; + return &tavil_cdc_aanc_version; case AFE_CDC_REGISTER_PAGE_CONFIG: return &tavil_cdc_reg_page_cfg; default: @@ -731,6 +804,37 @@ void *tavil_get_afe_config(struct snd_soc_codec *codec, } EXPORT_SYMBOL(tavil_get_afe_config); +static bool is_tavil_playback_dai(int dai_id) +{ + if ((dai_id == AIF1_PB) || (dai_id == AIF2_PB) || + (dai_id == AIF3_PB) || (dai_id == AIF4_PB)) + return true; + + return false; +} + +static int tavil_find_playback_dai_id_for_port(int port_id, + struct tavil_priv *tavil) +{ + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx_ch *ch; + int i, slv_port_id; + + for (i = AIF1_PB; i < NUM_CODEC_DAIS; i++) { + if (!is_tavil_playback_dai(i)) + continue; + + dai = &tavil->dai[i]; + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + slv_port_id = wcd9xxx_get_slave_port(ch->ch_num); + if ((slv_port_id > 0) && (slv_port_id == port_id)) + return i; + } + } + + return -EINVAL; +} + static void tavil_vote_svs(struct tavil_priv *tavil, bool vote) { struct wcd9xxx *wcd9xxx; @@ -761,6 +865,212 @@ done: mutex_unlock(&tavil->svs_mutex); } +static int tavil_get_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->anc_slot; + return 0; +} + +static int tavil_put_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + tavil->anc_slot = ucontrol->value.integer.value[0]; + return 0; +} + +static int tavil_get_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = (tavil->anc_func == true ? 1 : 0); + return 0; +} + +static int tavil_put_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + mutex_lock(&tavil->codec_mutex); + tavil->anc_func = (!ucontrol->value.integer.value[0] ? false : true); + dev_dbg(codec->dev, "%s: anc_func %x", __func__, tavil->anc_func); + + if (tavil->anc_func == true) { + snd_soc_dapm_enable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_disable_pin(dapm, "EAR PA"); + snd_soc_dapm_disable_pin(dapm, "EAR"); + } else { + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_enable_pin(dapm, "EAR PA"); + snd_soc_dapm_enable_pin(dapm, "EAR"); + } + mutex_unlock(&tavil->codec_mutex); + + snd_soc_dapm_sync(dapm); + return 0; +} + +static int tavil_codec_enable_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + const char *filename; + const struct firmware *fw; + int i; + int ret = 0; + int num_anc_slots; + struct wcd9xxx_anc_header *anc_head; + struct firmware_cal *hwdep_cal = NULL; + u32 anc_writes_size = 0; + u32 anc_cal_size = 0; + int anc_size_remaining; + u32 *anc_ptr; + u16 reg; + u8 mask, val; + size_t cal_size; + const void *data; + + if (!tavil->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_ANC_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration, cal_size %zd", + __func__, cal_size); + } else { + filename = "WCD934X/WCD934X_anc.bin"; + ret = request_firmware(&fw, filename, codec->dev); + if (IS_ERR_VALUE(ret)) { + dev_err(codec->dev, "%s: Failed to acquire ANC data: %d\n", + __func__, ret); + return ret; + } + if (!fw) { + dev_err(codec->dev, "%s: Failed to get anc fw\n", + __func__); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + if (cal_size < sizeof(struct wcd9xxx_anc_header)) { + dev_err(codec->dev, "%s: Invalid cal_size %zd\n", + __func__, cal_size); + ret = -EINVAL; + goto err; + } + /* First number is the number of register writes */ + anc_head = (struct wcd9xxx_anc_header *)(data); + anc_ptr = (u32 *)(data + sizeof(struct wcd9xxx_anc_header)); + anc_size_remaining = cal_size - + sizeof(struct wcd9xxx_anc_header); + num_anc_slots = anc_head->num_anc_slots; + + if (tavil->anc_slot >= num_anc_slots) { + dev_err(codec->dev, "%s: Invalid ANC slot selected\n", + __func__); + ret = -EINVAL; + goto err; + } + for (i = 0; i < num_anc_slots; i++) { + if (anc_size_remaining < WCD934X_PACKED_REG_SIZE) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + anc_writes_size = (u32)(*anc_ptr); + anc_size_remaining -= sizeof(u32); + anc_ptr += 1; + + if ((anc_writes_size * WCD934X_PACKED_REG_SIZE) > + anc_size_remaining) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + + if (tavil->anc_slot == i) + break; + + anc_size_remaining -= (anc_writes_size * + WCD934X_PACKED_REG_SIZE); + anc_ptr += anc_writes_size; + } + if (i == num_anc_slots) { + dev_err(codec->dev, "%s: Selected ANC slot not present\n", + __func__); + ret = -EINVAL; + goto err; + } + + anc_cal_size = anc_writes_size; + for (i = 0; i < anc_writes_size; i++) { + WCD934X_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val); + snd_soc_write(codec, reg, (val & mask)); + } + + if (!hwdep_cal) + release_firmware(fw); + break; + case SND_SOC_DAPM_POST_PMU: + /* Remove ANC Rx from reset */ + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_CLK_RESET_CTL, + 0x08, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + if (!strcmp(w->name, "ANC EAR PA") || + !strcmp(w->name, "ANC SPK1 PA")) { + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL, + 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x38); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x07, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x00); + } + break; + } + + return 0; +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1353,10 +1663,40 @@ static int tavil_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, return 0; } +static int tavil_codec_enable_spkr_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil->anc_func) + return 0; + + dev_dbg(codec->dev, "%s: w: %s event: %d anc: %d\n", __func__, + w->name, event, tavil->anc_func); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tavil_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x00); + ret = tavil_codec_enable_anc(w, kcontrol, event); + break; + } + return ret; +} + static int tavil_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + int ret = 0; struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); @@ -1377,11 +1717,43 @@ static int tavil_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, WCD934X_CDC_RX0_RX_PATH_MIX_CTL, 0x10, 0x00); break; - default: + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + + if (!(strcmp(w->name, "ANC EAR PA"))) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0, + 0x10, 0x00); + } break; }; - return 0; + return ret; +} + +static void tavil_codec_override(struct snd_soc_codec *codec, int mode, + int event) +{ + if (mode == CLS_AB || mode == CLS_AB_HIFI) { + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!(snd_soc_read(codec, + WCD934X_CDC_RX2_RX_PATH_CTL) & 0x10) && + (!(snd_soc_read(codec, + WCD934X_CDC_RX1_RX_PATH_CTL) & 0x10))) + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00); + break; + } + } } static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, @@ -1396,6 +1768,8 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, (0x03 << 1)); set_bit(HPH_PA_DELAY, &tavil->status_mask); break; case SND_SOC_DAPM_POST_PMU: @@ -1410,6 +1784,9 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, /* Remove mute */ snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x00); + /* Enable GM3 boost */ + snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL, + 0x80, 0x80); /* Enable AutoChop timer at the end of power up */ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x02); @@ -1427,6 +1804,7 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x00); } + tavil_codec_override(codec, tavil->hph_mode, event); break; case SND_SOC_DAPM_PRE_PMD: /* Enable DSD Mute before PA disable */ @@ -1436,6 +1814,9 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, 0x04, 0x04); break; case SND_SOC_DAPM_POST_PMD: + tavil_codec_override(codec, tavil->hph_mode, event); + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, 0x0); /* 5ms sleep is required after PA disable */ usleep_range(5000, 5100); break; @@ -1456,6 +1837,8 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, (0x03 << 1)); set_bit(HPH_PA_DELAY, &tavil->status_mask); break; case SND_SOC_DAPM_POST_PMU: @@ -1470,6 +1853,12 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, /* Remove Mute on primary path */ snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x00); + /* Enable GM3 boost */ + snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL, + 0x80, 0x80); + /* Enable AutoChop timer at the end of power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); /* Remove mix path mute if it is enabled */ if ((snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_MIX_CTL)) & 0x10) @@ -1484,6 +1873,7 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x00); } + tavil_codec_override(codec, tavil->hph_mode, event); break; case SND_SOC_DAPM_PRE_PMD: /* Enable DSD Mute before PA disable */ @@ -1493,6 +1883,9 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, 0x04, 0x04); break; case SND_SOC_DAPM_POST_PMD: + tavil_codec_override(codec, tavil->hph_mode, event); + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, 0x0); /* 5ms sleep is required after PA disable */ usleep_range(5000, 5100); break; @@ -1550,6 +1943,7 @@ static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + int ret = 0; struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); @@ -1560,10 +1954,17 @@ static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w, /* Disable AutoChop timer during power up */ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00); + + if (tavil->anc_func) + ret = tavil_codec_enable_anc(w, kcontrol, event); + wcd_clsh_fsm(codec, &tavil->clsh_d, WCD_CLSH_EVENT_PRE_DAC, WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + if (tavil->anc_func) + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0, + 0x10, 0x10); break; case SND_SOC_DAPM_POST_PMD: wcd_clsh_fsm(codec, &tavil->clsh_d, @@ -1575,7 +1976,7 @@ static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w, break; }; - return 0; + return ret; } static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, @@ -1602,9 +2003,17 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, __func__, hph_mode); return -EINVAL; } + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control enable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x01); /* Disable AutoChop timer during power up */ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, - 0x02, 0x00); + 0x02, 0x00); + /* Set RDAC gain */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x40); if (dsd_conf && (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) @@ -1613,8 +2022,7 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, wcd_clsh_fsm(codec, &tavil->clsh_d, WCD_CLSH_EVENT_PRE_DAC, WCD_CLSH_STATE_HPHR, - ((hph_mode == CLS_H_LOHIFI) ? - CLS_H_HIFI : hph_mode)); + hph_mode); break; case SND_SOC_DAPM_POST_PMD: /* 1000us required as per HW requirement */ @@ -1622,8 +2030,15 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, wcd_clsh_fsm(codec, &tavil->clsh_d, WCD_CLSH_EVENT_POST_PA, WCD_CLSH_STATE_HPHR, - ((hph_mode == CLS_H_LOHIFI) ? - CLS_H_HIFI : hph_mode)); + hph_mode); + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control disable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x0); + /* Re-set RDAC gain */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x0); break; default: break; @@ -1657,6 +2072,17 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, __func__, hph_mode); return -EINVAL; } + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control enable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x01); + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + /* Set RDAC gain */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x40); if (dsd_conf && (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) hph_mode = CLS_H_HIFI; @@ -1664,8 +2090,7 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, wcd_clsh_fsm(codec, &tavil->clsh_d, WCD_CLSH_EVENT_PRE_DAC, WCD_CLSH_STATE_HPHL, - ((hph_mode == CLS_H_LOHIFI) ? - CLS_H_HIFI : hph_mode)); + hph_mode); break; case SND_SOC_DAPM_POST_PMD: /* 1000us required as per HW requirement */ @@ -1673,8 +2098,15 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, wcd_clsh_fsm(codec, &tavil->clsh_d, WCD_CLSH_EVENT_POST_PA, WCD_CLSH_STATE_HPHL, - ((hph_mode == CLS_H_LOHIFI) ? - CLS_H_HIFI : hph_mode)); + hph_mode); + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control disable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x0); + /* Re-set RDAC gain */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x0); break; default: break; @@ -2207,6 +2639,46 @@ static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w, return 0; } +static void tavil_codec_hphdelay_lutbypass(struct snd_soc_codec *codec, + u16 interp_idx, int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u8 hph_dly_mask; + u16 hph_lut_bypass_reg = 0; + u16 hph_comp_ctrl7 = 0; + + + switch (interp_idx) { + case INTERP_HPHL: + hph_dly_mask = 1; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT; + hph_comp_ctrl7 = WCD934X_CDC_COMPANDER1_CTL7; + break; + case INTERP_HPHR: + hph_dly_mask = 2; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT; + hph_comp_ctrl7 = WCD934X_CDC_COMPANDER2_CTL7; + break; + default: + break; + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, 0x0); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x80); + if (tavil->hph_mode == CLS_H_ULP) + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x20); + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, hph_dly_mask); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00); + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x0); + } +} + static void tavil_codec_hd2_control(struct snd_soc_codec *codec, u16 interp_idx, int event) { @@ -2225,18 +2697,68 @@ static void tavil_codec_hd2_control(struct snd_soc_codec *codec, } if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { - snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10); - snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x14); snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); } if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); - snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00); snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); } } +static int tavil_codec_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg) +{ + int comp_gain_offset, val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (tavil->swr.spkr_mode) { + /* Compander gain in SPKR_MODE1 case is 12 dB */ + case WCD934X_SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (tavil->comp_enabled[COMPANDER_7] && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) && + (tavil->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + tavil->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX7 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (tavil->comp_enabled[COMPANDER_7] && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) && + (tavil->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n, int event) { @@ -2267,7 +2789,9 @@ static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n, } if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); @@ -2276,6 +2800,36 @@ static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n, return 0; } +static void tavil_codec_idle_detect_control(struct snd_soc_codec *codec, + int interp, int event) +{ + int reg = 0, mask, val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil->idle_det_cfg.hph_idle_detect_en) + return; + + if (interp == INTERP_HPHL) { + reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL; + mask = 0x01; + val = 0x01; + } + if (interp == INTERP_HPHR) { + reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL; + mask = 0x02; + val = 0x02; + } + + if (reg && SND_SOC_DAPM_EVENT_ON(event)) + snd_soc_update_bits(codec, reg, mask, val); + + if (reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, reg, mask, 0x00); + tavil->idle_det_cfg.hph_idle_thr = 0; + snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, 0x0); + } +} + /** * tavil_codec_enable_interp_clk - Enable main path Interpolator * clock. @@ -2305,7 +2859,11 @@ int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, snd_soc_update_bits(codec, main_reg, 0x10, 0x10); /* Clk enable */ snd_soc_update_bits(codec, main_reg, 0x20, 0x20); + tavil_codec_idle_detect_control(codec, interp_idx, + event); tavil_codec_hd2_control(codec, interp_idx, event); + tavil_codec_hphdelay_lutbypass(codec, interp_idx, + event); tavil_config_compander(codec, interp_idx, event); } tavil->main_clk_users[interp_idx]++; @@ -2316,7 +2874,11 @@ int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, if (tavil->main_clk_users[interp_idx] <= 0) { tavil->main_clk_users[interp_idx] = 0; tavil_config_compander(codec, interp_idx, event); + tavil_codec_hphdelay_lutbypass(codec, interp_idx, + event); tavil_codec_hd2_control(codec, interp_idx, event); + tavil_codec_idle_detect_control(codec, interp_idx, + event); /* Clk Disable */ snd_soc_update_bits(codec, main_reg, 0x20, 0x00); /* Reset enable and disable */ @@ -2334,6 +2896,110 @@ int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, } EXPORT_SYMBOL(tavil_codec_enable_interp_clk); +static int tavil_codec_set_idle_detect_thr(struct snd_soc_codec *codec, + int interp, int path_type) +{ + int port_id[4] = { 0, 0, 0, 0 }; + int *port_ptr, num_ports; + int bit_width = 0, i; + int mux_reg, mux_reg_val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int dai_id, idle_thr; + + if ((interp != INTERP_HPHL) && (interp != INTERP_HPHR)) + return 0; + + if (!tavil->idle_det_cfg.hph_idle_detect_en) + return 0; + + port_ptr = &port_id[0]; + num_ports = 0; + + /* + * Read interpolator MUX input registers and find + * which slimbus port is connected and store the port + * numbers in port_id array. + */ + if (path_type == INTERP_MIX_PATH) { + mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1 + + 2 * (interp - 1); + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + + if ((mux_reg_val >= INTn_2_INP_SEL_RX0) && + (mux_reg_val < INTn_2_INP_SEL_PROXIMITY)) { + *port_ptr++ = mux_reg_val + + WCD934X_RX_PORT_START_NUMBER - 1; + num_ports++; + } + } + + if (path_type == INTERP_MAIN_PATH) { + mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0 + + 2 * (interp - 1); + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + i = WCD934X_INTERP_MUX_NUM_INPUTS; + + while (i) { + if ((mux_reg_val >= INTn_1_INP_SEL_RX0) && + (mux_reg_val <= INTn_1_INP_SEL_RX7)) { + *port_ptr++ = mux_reg_val + + WCD934X_RX_PORT_START_NUMBER - + INTn_1_INP_SEL_RX0; + num_ports++; + } + mux_reg_val = (snd_soc_read(codec, mux_reg) & + 0xf0) >> 4; + mux_reg += 1; + i--; + } + } + + dev_dbg(codec->dev, "%s: num_ports: %d, ports[%d %d %d %d]\n", + __func__, num_ports, port_id[0], port_id[1], + port_id[2], port_id[3]); + + i = 0; + while (num_ports) { + dai_id = tavil_find_playback_dai_id_for_port(port_id[i++], + tavil); + + if ((dai_id >= 0) && (dai_id < NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: dai_id: %d bit_width: %d\n", + __func__, dai_id, + tavil->dai[dai_id].bit_width); + + if (tavil->dai[dai_id].bit_width > bit_width) + bit_width = tavil->dai[dai_id].bit_width; + } + + num_ports--; + } + + switch (bit_width) { + case 16: + idle_thr = 0xff; /* F16 */ + break; + case 24: + case 32: + idle_thr = 0x03; /* F22 */ + break; + default: + idle_thr = 0x00; + break; + } + + dev_dbg(codec->dev, "%s: (new) idle_thr: %d, (cur) idle_thr: %d\n", + __func__, idle_thr, tavil->idle_det_cfg.hph_idle_thr); + + if ((tavil->idle_det_cfg.hph_idle_thr == 0) || + (idle_thr < tavil->idle_det_cfg.hph_idle_thr)) { + snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, idle_thr); + tavil->idle_det_cfg.hph_idle_thr = idle_thr; + } + + return 0; +} + static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -2361,6 +3027,8 @@ static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_idle_detect_thr(codec, w->shift, + INTERP_MIX_PATH); tavil_codec_enable_interp_clk(codec, event, w->shift); /* Clk enable */ snd_soc_update_bits(codec, mix_reg, 0x20, 0x20); @@ -2387,6 +3055,7 @@ static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w, val = snd_soc_read(codec, gain_reg); val += offset_val; snd_soc_write(codec, gain_reg, val); + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); break; case SND_SOC_DAPM_POST_PMD: /* Clk Disable */ @@ -2417,6 +3086,7 @@ static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w, val += offset_val; snd_soc_write(codec, gain_reg, val); } + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); break; }; dev_dbg(codec->dev, "%s event %d name %s\n", __func__, event, w->name); @@ -2474,6 +3144,8 @@ static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_idle_detect_thr(codec, w->shift, + INTERP_MAIN_PATH); tavil_codec_enable_interp_clk(codec, event, w->shift); break; case SND_SOC_DAPM_POST_PMU: @@ -2499,6 +3171,7 @@ static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w, val = snd_soc_read(codec, gain_reg); val += offset_val; snd_soc_write(codec, gain_reg, val); + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); break; case SND_SOC_DAPM_POST_PMD: tavil_codec_enable_interp_clk(codec, event, w->shift); @@ -2524,6 +3197,7 @@ static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w, val += offset_val; snd_soc_write(codec, gain_reg, val); } + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); break; }; @@ -2898,6 +3572,9 @@ static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMD: snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); break; }; out: @@ -3349,6 +4026,58 @@ static int __tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w, return 0; } +/* + * tavil_codec_enable_standalone_micbias - enable micbias standalone + * @codec: pointer to codec instance + * @micb_num: number of micbias to be enabled + * @enable: true to enable micbias or false to disable + * + * This function is used to enable micbias (1, 2, 3 or 4) during + * standalone independent of whether TX use-case is running or not + * + * Return: error code in case of failure or 0 for success + */ +int tavil_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable) +{ + const char * const micb_names[] = { + DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE, + DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE + }; + int micb_index = micb_num - 1; + int rc; + + if (!codec) { + pr_err("%s: Codec memory is NULL\n", __func__); + return -EINVAL; + } + + if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + if (enable) + rc = snd_soc_dapm_force_enable_pin_unlocked( + snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + else + rc = snd_soc_dapm_disable_pin_unlocked( + snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: micbias%d force %s pin failed\n", + __func__, micb_num, (enable ? "enable" : "disable")); + + return rc; +} +EXPORT_SYMBOL(tavil_codec_enable_standalone_micbias); + static int tavil_codec_force_enable_micbias(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -3630,37 +4359,49 @@ static int tavil_compander_put(struct snd_kcontrol *kcontrol, return 0; } +static int tavil_hph_idle_detect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int val = 0; + + if (tavil) + val = tavil->idle_det_cfg.hph_idle_detect_en; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tavil_hph_idle_detect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (tavil) + tavil->idle_det_cfg.hph_idle_detect_en = + ucontrol->value.integer.value[0]; + + return 0; +} + static int tavil_dmic_pin_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - u16 ctl_reg; + u16 dmic_pin; u8 reg_val, pinctl_position; pinctl_position = ((struct soc_multi_mixer_control *) kcontrol->private_value)->shift; - switch (pinctl_position >> 3) { - case 0: - ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_0; - break; - case 1: - ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_1; - break; - case 2: - ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_2; - break; - case 3: - ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_3; - break; - default: - dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", - __func__, pinctl_position); - return -EINVAL; - } - reg_val = snd_soc_read(codec, ctl_reg); - reg_val = (reg_val >> (pinctl_position & 0x07)) & 0x1; - ucontrol->value.integer.value[0] = reg_val; + dmic_pin = pinctl_position & 0x07; + reg_val = snd_soc_read(codec, + WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1); + + ucontrol->value.integer.value[0] = !!reg_val; return 0; } @@ -3669,10 +4410,11 @@ static int tavil_dmic_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - u16 ctl_reg, cfg_reg; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 ctl_reg, cfg_reg, dmic_pin; u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask; - /* 1- high or low; 0- high Z */ + /* 0- high or low; 1- high Z */ pinctl_mode = ucontrol->value.integer.value[0]; pinctl_position = ((struct soc_multi_mixer_control *) kcontrol->private_value)->shift; @@ -3696,16 +4438,20 @@ static int tavil_dmic_pin_mode_put(struct snd_kcontrol *kcontrol, return -EINVAL; } - ctl_val = pinctl_mode << (pinctl_position & 0x07); + ctl_val = ~(pinctl_mode << (pinctl_position & 0x07)); mask = 1 << (pinctl_position & 0x07); snd_soc_update_bits(codec, ctl_reg, mask, ctl_val); - cfg_reg = WCD934X_TLMM_BIST_MODE_PINCFG + pinctl_position; - if (!pinctl_mode) - cfg_val = 0x4; - else + dmic_pin = pinctl_position & 0x07; + cfg_reg = WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1; + if (pinctl_mode) { + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + cfg_val = 0x6; + else + cfg_val = 0xD; + } else cfg_val = 0; - snd_soc_update_bits(codec, cfg_reg, 0x07, cfg_val); + snd_soc_update_bits(codec, cfg_reg, 0x1F, cfg_val); dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n", __func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val); @@ -3919,6 +4665,76 @@ static int tavil_ear_pa_gain_put(struct snd_kcontrol *kcontrol, return 0; } +static int tavil_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tavil_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + tavil->ear_spkr_gain = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: gain = %d\n", __func__, tavil->ear_spkr_gain); + + return 0; +} + +static int tavil_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->hph_mode; + return 0; +} + +static int tavil_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (mode_val == 0) { + dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H LOHiFi\n", + __func__); + mode_val = CLS_H_LOHIFI; + } + tavil->hph_mode = mode_val; + return 0; +} + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", + "CLS_H_ULP", "CLS_AB_HIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const char *const tavil_anc_func_text[] = {"OFF", "ON"}; +static const struct soc_enum tavil_anc_func_enum = + SOC_ENUM_SINGLE_EXT(2, tavil_anc_func_text); + /* Cutoff frequency for high pass filter */ static const char * const cf_text[] = { "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" @@ -3933,13 +4749,25 @@ static const char * const amic_pwr_lvl_text[] = { "LOW_PWR", "DEFAULT", "HIGH_PERF" }; +static const char * const hph_idle_detect_text[] = { + "OFF", "ON" +}; + static const char * const tavil_ear_pa_gain_text[] = { "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB" }; +static const char * const tavil_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_pa_gain_enum, tavil_ear_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_spkr_pa_gain_enum, + tavil_ear_spkr_pa_gain_text); static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text); +static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text); static SOC_ENUM_SINGLE_DECL(cf_dec0_enum, WCD934X_CDC_TX0_TX_PATH_CFG0, 5, cf_text); static SOC_ENUM_SINGLE_DECL(cf_dec1_enum, WCD934X_CDC_TX1_TX_PATH_CFG0, 5, @@ -3990,6 +4818,8 @@ static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 2, static const struct snd_kcontrol_new tavil_snd_controls[] = { SOC_ENUM_EXT("EAR PA Gain", tavil_ear_pa_gain_enum, tavil_ear_pa_gain_get, tavil_ear_pa_gain_put), + SOC_ENUM_EXT("EAR SPKR PA Gain", tavil_ear_spkr_pa_gain_enum, + tavil_ear_spkr_pa_gain_get, tavil_ear_spkr_pa_gain_put), SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 20, 1, line_gain), SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 20, 1, line_gain), SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER, @@ -4070,6 +4900,11 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tavil_get_anc_slot, + tavil_put_anc_slot), + SOC_ENUM_EXT("ANC Function", tavil_anc_func_enum, tavil_get_anc_func, + tavil_put_anc_func), + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), @@ -4095,6 +4930,9 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = { SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum), SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum), + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + tavil_rx_hph_mode_get, tavil_rx_hph_mode_put), + SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0, tavil_iir_enable_audio_mixer_get, tavil_iir_enable_audio_mixer_put), @@ -4160,6 +4998,9 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = { SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, tavil_compander_get, tavil_compander_put), + SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum, + tavil_hph_idle_detect_get, tavil_hph_idle_detect_put), + SOC_ENUM_EXT("MAD Input", tavil_conn_mad_enum, tavil_mad_input_get, tavil_mad_input_put), @@ -4435,6 +5276,15 @@ static const char * const amic4_5_sel_text[] = { "AMIC4", "AMIC5" }; +static const char * const anc0_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR", + "ANC_IN_LO1" +}; + +static const char * const anc1_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2" +}; + static const char * const rx_echo_mux_text[] = { "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4", "RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8" @@ -4886,6 +5736,18 @@ WCD_DAPM_ENUM(int4_2_native, SND_SOC_NOPM, 0, native_mux_text); WCD_DAPM_ENUM(int7_2_native, SND_SOC_NOPM, 0, native_mux_text); WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(anc0_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text); +WCD_DAPM_ENUM(anc1_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text); + +static const struct snd_kcontrol_new anc_ear_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_spkr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_spkr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + static const struct snd_kcontrol_new mad_cpe1_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); @@ -5256,6 +6118,9 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { WCD_DAPM_MUX("AMIC4_5 SEL", 0, tx_amic4_5), + WCD_DAPM_MUX("ANC0 FB MUX", 0, anc0_fb), + WCD_DAPM_MUX("ANC1 FB MUX", 0, anc1_fb), + SND_SOC_DAPM_INPUT("AMIC1"), SND_SOC_DAPM_INPUT("AMIC2"), SND_SOC_DAPM_INPUT("AMIC3"), @@ -5487,6 +6352,12 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0, tavil_codec_enable_lineout_pa, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0, + tavil_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tavil_codec_enable_spkr_anc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_OUTPUT("EAR"), SND_SOC_DAPM_OUTPUT("HPHL"), @@ -5495,6 +6366,14 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("LINEOUT2"), SND_SOC_DAPM_OUTPUT("SPK1 OUT"), SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + + SND_SOC_DAPM_SWITCH("ANC OUT EAR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_switch), + SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_spkr_switch), + SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0, + &anc_spkr_pa_switch), SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, tavil_codec_enable_rx_bias, @@ -6222,6 +7101,104 @@ static int __tavil_cdc_mclk_enable(struct tavil_priv *tavil, return ret; } +static ssize_t tavil_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + char buffer[TAVIL_VERSION_ENTRY_SIZE]; + int len = 0; + + tavil = (struct tavil_priv *) entry->private_data; + if (!tavil) { + pr_err("%s: tavil priv is null\n", __func__); + return -EINVAL; + } + + wcd9xxx = tavil->wcd9xxx; + + switch (wcd9xxx->version) { + case TAVIL_VERSION_WCD9340_1_0: + len = snprintf(buffer, sizeof(buffer), "WCD9340_1_0\n"); + break; + case TAVIL_VERSION_WCD9341_1_0: + len = snprintf(buffer, sizeof(buffer), "WCD9341_1_0\n"); + break; + case TAVIL_VERSION_WCD9340_1_1: + len = snprintf(buffer, sizeof(buffer), "WCD9340_1_1\n"); + break; + case TAVIL_VERSION_WCD9341_1_1: + len = snprintf(buffer, sizeof(buffer), "WCD9341_1_1\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops tavil_codec_info_ops = { + .read = tavil_codec_version_read, +}; + +/* + * tavil_codec_info_create_codec_entry - creates wcd934x module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wcd934x module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int tavil_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct tavil_priv *tavil; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + tavil = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + tavil->entry = snd_register_module_info(codec_root->module, + "tavil", + codec_root); + if (!tavil->entry) { + dev_dbg(codec->dev, "%s: failed to create wcd934x entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + tavil->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wcd934x version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = tavil; + version_entry->size = TAVIL_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &tavil_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + tavil->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(tavil_codec_info_create_codec_entry); + /** * tavil_cdc_mclk_enable - Enable/disable codec mclk * @@ -6327,7 +7304,6 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = { {WCD934X_BIAS_VBG_FINE_ADJ, 0xFF, 0x75}, {WCD934X_CODEC_CPR_SVS_CX_VDD, 0xFF, 0x7C}, /* value in svs mode */ {WCD934X_CODEC_CPR_SVS2_CX_VDD, 0xFF, 0x58}, /* value in svs2 mode */ - {WCD934X_SIDO_NEW_VOUT_D_FREQ2, 0x01, 0x01}, {WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, {WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, {WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, @@ -6343,6 +7319,15 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = { {WCD934X_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12}, {WCD934X_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08}, {WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x1F, 0x09}, + {WCD934X_CDC_TX0_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX1_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX2_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX3_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX4_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX5_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00}, }; static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { @@ -6608,6 +7593,7 @@ static int tavil_handle_pdata(struct tavil_priv *tavil, { struct snd_soc_codec *codec = tavil->codec; u8 mad_dmic_ctl_val; + u8 anc_ctl_value; u32 def_dmic_rate, dmic_clk_drv; int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; int rc = 0; @@ -6713,6 +7699,20 @@ static int tavil_handle_pdata(struct tavil_priv *tavil, snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC2_CTL, 0x0E, mad_dmic_ctl_val << 1); + if (dmic_clk_drv == WCD934X_DMIC_CLK_DIV_2) + anc_ctl_value = WCD934X_ANC_DMIC_X2_FULL_RATE; + else + anc_ctl_value = WCD934X_ANC_DMIC_X2_HALF_RATE; + + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + done: return rc; } @@ -6821,6 +7821,7 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) dev_info(codec->dev, "%s()\n", __func__); tavil = snd_soc_codec_get_drvdata(codec); + tavil->intf_type = wcd9xxx_get_intf_type(); /* Resource Manager post Init */ ret = wcd_resmgr_post_init(tavil->resmgr, NULL, codec); @@ -6831,8 +7832,8 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) } /* Class-H Init */ wcd_clsh_init(&tavil->clsh_d); - /* Default HPH Mode to Class-H HiFi */ - tavil->hph_mode = CLS_H_HIFI; + /* Default HPH Mode to Class-H Low HiFi */ + tavil->hph_mode = CLS_H_LOHIFI; tavil->fw_data = devm_kzalloc(codec->dev, sizeof(*(tavil->fw_data)), GFP_KERNEL); @@ -6926,6 +7927,12 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) if (IS_ERR_OR_NULL(tavil->dsd_config)) dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__); + mutex_lock(&tavil->codec_mutex); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + mutex_unlock(&tavil->codec_mutex); + snd_soc_dapm_sync(dapm); tavil_wdsp_initialize(codec); @@ -7433,6 +8440,45 @@ static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil) return rc; } +static void ___tavil_get_codec_fine_version(struct tavil_priv *tavil) +{ + int val1, val2, version; + struct regmap *regmap; + u16 id_minor; + u32 version_mask = 0; + + regmap = tavil->wcd9xxx->regmap; + version = tavil->wcd9xxx->version; + id_minor = tavil->wcd9xxx->codec_type->id_minor; + + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1); + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2); + + dev_dbg(tavil->dev, "%s: chip version :0x%x 0x:%x\n", + __func__, val1, val2); + + version_mask |= (!!((u8)val1 & 0x80)) << DSD_DISABLED_MASK; + version_mask |= (!!((u8)val2 & 0x01)) << SLNQ_DISABLED_MASK; + + switch (version_mask) { + case DSD_DISABLED | SLNQ_DISABLED: + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_WCD9340_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_WCD9340_1_1; + break; + case SLNQ_DISABLED: + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_WCD9341_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_WCD9341_1_1; + break; + } + + tavil->wcd9xxx->version = version; + tavil->wcd9xxx->codec_type->version = version; +} + /* * tavil_get_wcd_dsp_cntl: Get the reference to wcd_dsp_cntl * @dev: Device pointer for codec device @@ -7462,7 +8508,6 @@ static int tavil_probe(struct platform_device *pdev) struct tavil_priv *tavil; struct clk *wcd_ext_clk; struct wcd9xxx_resmgr_v2 *resmgr; - int val1, val2, val3, val4; tavil = devm_kzalloc(&pdev->dev, sizeof(struct tavil_priv), GFP_KERNEL); @@ -7507,6 +8552,7 @@ static int tavil_probe(struct platform_device *pdev) tavil->swr.plat_data.bulk_write = tavil_swrm_bulk_write; tavil->swr.plat_data.clk = tavil_swrm_clock; tavil->swr.plat_data.handle_irq = tavil_swrm_handle_irq; + tavil->swr.spkr_gain_offset = WCD934X_RX_GAIN_OFFSET_0_DB; /* Register for Clock */ wcd_ext_clk = clk_get(tavil->wcd9xxx->dev, "wcd_clk"); @@ -7530,17 +8576,7 @@ static int tavil_probe(struct platform_device *pdev) 0x03, 0x01); tavil_update_reg_defaults(tavil); __tavil_enable_efuse_sensing(tavil); - - regmap_read(tavil->wcd9xxx->regmap, - WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val1); - regmap_read(tavil->wcd9xxx->regmap, - WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val2); - regmap_read(tavil->wcd9xxx->regmap, - WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val3); - regmap_read(tavil->wcd9xxx->regmap, - WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9, &val4); - dev_dbg(&pdev->dev, "%s: chip version :0x%x 0x:%x 0x:%x 0x:%x\n", - __func__, val1, val2, val3, val4); + ___tavil_get_codec_fine_version(tavil); /* Register with soc framework */ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/sound/soc/codecs/wcd934x/wcd934x.h index 9cffa168c298..1f5fda88c4f9 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.h +++ b/sound/soc/codecs/wcd934x/wcd934x.h @@ -32,6 +32,9 @@ #define WCD934X_DMIC_CLK_DIV_16 0x5 #define WCD934X_DMIC_CLK_DRIVE_DEFAULT 0x02 +#define WCD934X_ANC_DMIC_X2_FULL_RATE 1 +#define WCD934X_ANC_DMIC_X2_HALF_RATE 0 + #define TAVIL_MAX_MICBIAS 4 #define TAVIL_NUM_INTERPOLATORS 9 #define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 @@ -39,6 +42,13 @@ /* Convert from vout ctl to micbias voltage in mV */ #define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) +/* Feature masks to distinguish codec version */ +#define DSD_DISABLED_MASK 0 +#define SLNQ_DISABLED_MASK 1 + +#define DSD_DISABLED (1 << DSD_DISABLED_MASK) +#define SLNQ_DISABLED (1 << SLNQ_DISABLED_MASK) + /* Number of input and output Slimbus port */ enum { WCD934X_RX0 = 0, @@ -180,4 +190,6 @@ extern struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec); extern int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, int event, int intp_idx); extern struct tavil_dsd_config *tavil_get_dsd_config(struct snd_soc_codec *); +extern int tavil_codec_info_create_codec_entry(struct snd_info_entry *, + struct snd_soc_codec *); #endif diff --git a/sound/soc/codecs/wcd9xxx-common-v2.c b/sound/soc/codecs/wcd9xxx-common-v2.c index f1b147aafd84..63872bbf540c 100644 --- a/sound/soc/codecs/wcd9xxx-common-v2.c +++ b/sound/soc/codecs/wcd9xxx-common-v2.c @@ -237,10 +237,16 @@ static const char *mode_to_str(int mode) return "CLS_H_NORMAL"; case CLS_H_HIFI: return "CLS_H_HIFI"; + case CLS_H_LOHIFI: + return "CLS_H_LOHIFI"; case CLS_H_LP: return "CLS_H_LP"; + case CLS_H_ULP: + return "CLS_H_ULP"; case CLS_AB: return "CLS_AB"; + case CLS_AB_HIFI: + return "CLS_AB_HIFI"; default: return "CLS_H_INVALID"; }; @@ -332,7 +338,8 @@ static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_data *clsh_d, static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec, int mode) { - if (mode == CLS_H_HIFI) + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, 0x08, 0x08); /* set to HIFI */ else @@ -343,7 +350,8 @@ static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec, static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec, int mode) { - if (mode == CLS_H_HIFI) + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, 0x04, 0x04); /* set to HIFI */ else @@ -351,6 +359,55 @@ static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec, 0x04, 0x00); /* set to Default */ } +static inline void wcd_clsh_gm3_boost_disable(struct snd_soc_codec *codec, + int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!IS_CODEC_TYPE(wcd9xxx, WCD934X)) + return; + + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) { + snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL, + 0x80, 0x0); /* disable GM3 Boost */ + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x80); + } else { + snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL, + 0x80, 0x80); /* set to Default */ + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x70); + } +} + + +static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec, + int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!IS_CODEC_TYPE(wcd9xxx, WCD934X)) + return; + + if (mode == CLS_H_LOHIFI || mode == CLS_AB) { + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x20); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0xC0); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x02); + } else { + + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x0); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0x80); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x06); + } +} + static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec, struct wcd_clsh_cdc_data *clsh_d, int mode, @@ -386,7 +443,7 @@ static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec, (1 << 6), (enable << 6)); /* 100usec delay is needed as per HW requirement */ usleep_range(100, 110); - if (enable && (TASHA_IS_1_1(wcd9xxx->version))) { + if (enable && (TASHA_IS_1_1(wcd9xxx))) { wcd_clsh_set_flyback_mode(codec, CLS_H_HIFI); snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN, 0x60, 0x40); @@ -427,7 +484,7 @@ static void wcd_clsh_set_gain_path(struct snd_soc_codec *codec, u8 val = 0; struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); - if (!TASHA_IS_2_0(wcd9xxx->version)) + if (!TASHA_IS_2_0(wcd9xxx)) return; switch (mode) { @@ -470,11 +527,26 @@ static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec, gain = DAC_GAIN_0DB; ipeak = DELTA_I_50MA; break; + case CLS_AB_HIFI: + val = 0x08; + break; case CLS_H_HIFI: val = 0x08; gain = DAC_GAIN_M0P2DB; ipeak = DELTA_I_50MA; break; + case CLS_H_LOHIFI: + val = 0x00; + if ((IS_CODEC_TYPE(wcd9xxx, WCD9335)) || + (IS_CODEC_TYPE(wcd9xxx, WCD9326))) { + val = 0x08; + gain = DAC_GAIN_M0P2DB; + ipeak = DELTA_I_50MA; + } + break; + case CLS_H_ULP: + val = 0x0C; + break; case CLS_H_LP: val = 0x04; ipeak = DELTA_I_30MA; @@ -483,8 +555,16 @@ static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec, return; }; + /* + * For tavil set mode to Lower_power for + * CLS_H_LOHIFI and CLS_AB + */ + if ((IS_CODEC_TYPE(wcd9xxx, WCD934X)) && + (mode == CLS_H_LOHIFI || mode == CLS_AB)) + val = 0x04; + snd_soc_update_bits(codec, WCD9XXX_A_ANA_HPH, 0x0C, val); - if (TASHA_IS_2_0(wcd9xxx->version)) { + if (TASHA_IS_2_0(wcd9xxx)) { snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_VCL_2, 0x30, (res_val << 4)); if (mode != CLS_H_LP) @@ -515,7 +595,7 @@ static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode) { struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); - if (!TASHA_IS_2_0(wcd9xxx->version)) + if (!TASHA_IS_2_0(wcd9xxx)) return; snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A); @@ -538,7 +618,7 @@ static void wcd_clsh_state_lo(struct snd_soc_codec *codec, dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), is_enable ? "enable" : "disable"); - if (mode != CLS_AB) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { dev_err(codec->dev, "%s: LO cannot be in this mode: %d\n", __func__, mode); return; @@ -586,7 +666,8 @@ static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec, WCD_CLSH_STATE_HPHR); else return; - if (hph_mode != CLS_AB && !is_native_44_1_active(codec)) + if (hph_mode != CLS_AB && hph_mode != CLS_AB_HIFI + && !is_native_44_1_active(codec)) snd_soc_update_bits(codec, WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, 0x40, 0x40); @@ -795,6 +876,7 @@ static void wcd_clsh_state_hph_lo(struct snd_soc_codec *codec, hph_mode); if ((hph_mode == CLS_AB) || + (hph_mode == CLS_AB_HIFI) || (hph_mode == CLS_NONE)) goto end; @@ -843,7 +925,7 @@ static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec, dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), is_enable ? "enable" : "disable"); - if (mode == CLS_AB) + if (mode == CLS_AB || mode == CLS_AB_HIFI) return; if (is_enable) { @@ -881,7 +963,7 @@ static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec, } if (is_enable) { - if (mode != CLS_AB) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { wcd_enable_clsh_block(codec, clsh_d, true); /* * These K1 values depend on the Headphone Impedance @@ -897,6 +979,8 @@ static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec, } wcd_clsh_set_buck_regulator_mode(codec, mode); wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_gm3_boost_disable(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode); wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); wcd_clsh_set_flyback_current(codec, mode); wcd_clsh_set_buck_mode(codec, mode); @@ -906,7 +990,7 @@ static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec, } else { wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); - if (mode != CLS_AB) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { snd_soc_update_bits(codec, WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, 0x40, 0x00); @@ -915,6 +999,8 @@ static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec, /* buck and flyback set to default mode and disable */ wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL); + wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL); wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); @@ -935,7 +1021,7 @@ static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec, } if (is_enable) { - if (mode != CLS_AB) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { wcd_enable_clsh_block(codec, clsh_d, true); /* * These K1 values depend on the Headphone Impedance @@ -951,6 +1037,8 @@ static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec, } wcd_clsh_set_buck_regulator_mode(codec, mode); wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_gm3_boost_disable(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode); wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); wcd_clsh_set_flyback_current(codec, mode); wcd_clsh_set_buck_mode(codec, mode); @@ -960,7 +1048,7 @@ static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec, } else { wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); - if (mode != CLS_AB) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { snd_soc_update_bits(codec, WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, 0x40, 0x00); @@ -969,6 +1057,8 @@ static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec, /* set buck and flyback to Default Mode */ wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL); + wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL); wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); diff --git a/sound/soc/codecs/wcd9xxx-common-v2.h b/sound/soc/codecs/wcd9xxx-common-v2.h index d3c52e73a7da..ee7e587b3f24 100644 --- a/sound/soc/codecs/wcd9xxx-common-v2.h +++ b/sound/soc/codecs/wcd9xxx-common-v2.h @@ -61,8 +61,10 @@ enum { CLS_H_NORMAL = 0, /* Class-H Default */ CLS_H_HIFI, /* Class-H HiFi */ CLS_H_LP, /* Class-H Low Power */ - CLS_AB, /* Class-AB */ + CLS_AB, /* Class-AB Low HIFI*/ CLS_H_LOHIFI, /* LoHIFI */ + CLS_H_ULP, /* Ultra Low power */ + CLS_AB_HIFI, /* Class-AB */ CLS_NONE, /* None of the above modes */ }; diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index 0b4bd3c15127..e9f167fa643b 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -2051,6 +2051,7 @@ struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, goto schedule_dload_work; } + arch_setup_dma_ops(core->dev, 0, 0, NULL, 0); core->cpe_dump_v_addr = dma_alloc_coherent(core->dev, core->hw_info.dram_size, &core->cpe_dump_addr, diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c index 523cd1ce4140..63367ce07f5a 100644 --- a/sound/soc/msm/msmcobalt.c +++ b/sound/soc/msm/msmcobalt.c @@ -191,7 +191,9 @@ static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_88P2", "KHZ_96", "KHZ_176P4", "KHZ_192", "KHZ_352P8", "KHZ_384"}; static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"}; -static const char *const usb_ch_text[] = {"One", "Two"}; +static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", + "Eight"}; static char const *ch_text[] = {"Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", @@ -1866,6 +1868,17 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) tavil_set_spkr_gain_offset(rtd->codec, RX_GAIN_OFFSET_M1P5_DB); } + card = rtd->card->snd_card; + entry = snd_register_module_info(card->module, "codecs", + card->proc_root); + if (!entry) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + pdata->codec_root = NULL; + goto done; + } + pdata->codec_root = entry; + tavil_codec_info_create_codec_entry(pdata->codec_root, codec); } else { if (rtd_aux && rtd_aux->component) if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) || @@ -1886,6 +1899,7 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) pdata->codec_root = entry; tasha_codec_info_create_codec_entry(pdata->codec_root, codec); } +done: codec_reg_done = true; return 0; diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 70ed8dd3e038..841bb5bce13f 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -1762,7 +1762,12 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) prtd->app_pointer = 0; prtd->first_buffer = 1; prtd->last_buffer = 0; - prtd->gapless_state.gapless_transition = 1; + /* + * Set gapless transition flag only if EOS hasn't been + * acknowledged already. + */ + if (atomic_read(&prtd->eos)) + prtd->gapless_state.gapless_transition = 1; prtd->marker_timestamp = 0; /* diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index a898532643ae..5fa3b6fb0885 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -2502,7 +2502,7 @@ static struct snd_soc_dai_driver msm_dai_q6_usb_rx_dai = { SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rate_max = 192000, .rate_min = 8000, }, @@ -2523,7 +2523,7 @@ static struct snd_soc_dai_driver msm_dai_q6_usb_tx_dai = { SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rate_max = 192000, .rate_min = 8000, }, diff --git a/sound/usb/card.c b/sound/usb/card.c index 524688e4c144..1f6c247f773a 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -673,6 +673,9 @@ static void usb_audio_disconnect(struct usb_interface *intf) card = chip->card; + if (chip->disconnect_cb) + chip->disconnect_cb(chip); + mutex_lock(®ister_mutex); if (atomic_inc_return(&chip->shutdown) == 1) { struct snd_usb_stream *as; @@ -707,8 +710,6 @@ static void usb_audio_disconnect(struct usb_interface *intf) if (chip->num_interfaces <= 0) { usb_chip[chip->index] = NULL; mutex_unlock(®ister_mutex); - if (chip->disconnect_cb) - chip->disconnect_cb(chip); snd_card_free_when_closed(card); } else { mutex_unlock(®ister_mutex); diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c index fa0206cc14c6..8337d11bad12 100644 --- a/sound/usb/usb_audio_qmi_svc.c +++ b/sound/usb/usb_audio_qmi_svc.c @@ -134,6 +134,12 @@ static struct msg_desc uaudio_stream_resp_desc = { .ei_array = qmi_uaudio_stream_resp_msg_v01_ei, }; +static struct msg_desc uaudio_stream_ind_desc = { + .max_msg_len = QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN, + .msg_id = QMI_UADUIO_STREAM_IND_V01, + .ei_array = qmi_uaudio_stream_ind_msg_v01_ei, +}; + enum mem_type { MEM_EVENT_RING, MEM_DCBA, @@ -643,29 +649,47 @@ void uaudio_disconnect_cb(struct snd_usb_audio *chip) int ret, if_idx; struct uaudio_dev *dev; int card_num = chip->card_num; + struct uaudio_qmi_svc *svc = uaudio_svc; + struct qmi_uaudio_stream_ind_msg_v01 disconnect_ind = {0}; pr_debug("%s: for card# %d\n", __func__, card_num); - mutex_lock(&chip->dev_lock); if (card_num >= SNDRV_CARDS) { pr_err("%s: invalid card number\n", __func__); - goto done; + return; } + mutex_lock(&chip->dev_lock); dev = &uadev[card_num]; + + /* clean up */ + if (!dev->udev) { + pr_debug("%s: no clean up required\n", __func__); + goto done; + } + if (atomic_read(&dev->in_use)) { + mutex_unlock(&chip->dev_lock); + + pr_debug("%s: sending qmi indication disconnect\n", __func__); + disconnect_ind.dev_event = USB_AUDIO_DEV_DISCONNECT_V01; + disconnect_ind.slot_id = dev->udev->slot_id; + ret = qmi_send_ind(svc->uaudio_svc_hdl, svc->curr_conn, + &uaudio_stream_ind_desc, &disconnect_ind, + sizeof(disconnect_ind)); + if (ret < 0) { + pr_err("%s: qmi send failed wiht err: %d\n", + __func__, ret); + return; + } + ret = wait_event_interruptible(dev->disconnect_wq, !atomic_read(&dev->in_use)); if (ret < 0) { pr_debug("%s: failed with ret %d\n", __func__, ret); - goto done; + return; } - } - - /* clean up */ - if (!dev->udev) { - pr_debug("%s: no clean up required\n", __func__); - goto done; + mutex_lock(&chip->dev_lock); } /* free xfer buffer and unmap xfer ring and buf per interface */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 397fb4ed3c97..d4913a46ee1c 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2313,12 +2313,15 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, case EPERM: case EACCES: return scnprintf(msg, size, - "You may not have permission to collect %sstats.\n" - "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" - " -1 - Not paranoid at all\n" - " 0 - Disallow raw tracepoint access for unpriv\n" - " 1 - Disallow cpu events for unpriv\n" - " 2 - Disallow kernel profiling for unpriv", + "You may not have permission to collect %sstats.\n\n" + "Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n" + "which controls use of the performance events system by\n" + "unprivileged users (without CAP_SYS_ADMIN).\n\n" + "The default value is 1:\n\n" + " -1: Allow use of (almost) all events by all users\n" + ">= 0: Disallow raw tracepoint access by users without CAP_IOC_LOCK\n" + ">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n" + ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN", target->system_wide ? "system-wide " : ""); case ENOENT: return scnprintf(msg, size, "The %s event is not supported.", |
