summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt1
-rw-r--r--Documentation/devicetree/bindings/arm/msm/spss_utils.txt27
-rw-r--r--Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt7
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt66
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-csi-phy.txt1
-rw-r--r--Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt5
-rw-r--r--Documentation/sysctl/kernel.txt40
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi28
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi111
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi17
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon.dtsi24
-rw-r--r--arch/arm/boot/dts/qcom/msmtriton.dtsi5
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig3
-rw-r--r--arch/arm64/configs/msmcortex_defconfig2
-rw-r--r--block/blk-merge.c16
-rw-r--r--drivers/crypto/msm/qce50.c14
-rw-r--r--drivers/devfreq/bimc-bwmon.c266
-rw-r--r--drivers/gpu/msm/adreno-gpulist.h2
-rw-r--r--drivers/gpu/msm/adreno_a5xx_preempt.c2
-rw-r--r--drivers/gpu/msm/kgsl.c5
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.c12
-rw-r--r--drivers/iio/adc/qcom-rradc.c2
-rw-r--r--drivers/input/touchscreen/Kconfig11
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/gt9xx/Kconfig51
-rw-r--r--drivers/input/touchscreen/gt9xx/Makefile8
-rw-r--r--drivers/input/touchscreen/gt9xx/goodix_tool.c166
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.c516
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.h103
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_update.c254
-rw-r--r--drivers/input/touchscreen/it7258_ts_i2c.c4
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h160
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c22
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c5
-rw-r--r--drivers/mfd/wcd934x-regmap.c14
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_intf.c23
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_intf.c21
-rw-r--r--drivers/platform/msm/sps/sps_bam.c3
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c29
-rw-r--r--drivers/power/qcom-charger/smb138x-charger.c24
-rw-r--r--drivers/regulator/core.c2
-rw-r--r--drivers/regulator/cpr3-regulator.c3
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c32
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c23
-rw-r--r--drivers/scsi/ufs/ufshcd.h2
-rw-r--r--drivers/soc/qcom/Kconfig11
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/common_log.c34
-rw-r--r--drivers/soc/qcom/glink.c25
-rw-r--r--drivers/soc/qcom/icnss.c46
-rw-r--r--drivers/soc/qcom/service-notifier.c16
-rw-r--r--drivers/soc/qcom/spss_utils.c294
-rw-r--r--drivers/soc/qcom/subsys-pil-tz.c3
-rw-r--r--drivers/soc/qcom/watchdog_v2.c41
-rw-r--r--drivers/usb/dwc3/gadget.c22
-rw-r--r--drivers/usb/gadget/function/f_accessory.c4
-rw-r--r--drivers/usb/gadget/function/f_gsi.c41
-rw-r--r--fs/ext4/Kconfig10
-rw-r--r--fs/ext4/Makefile2
-rw-r--r--fs/ext4/crypto.c3
-rw-r--r--fs/ext4/crypto_key.c72
-rw-r--r--fs/ext4/ext4.h23
-rw-r--r--fs/ext4/ext4_crypto.h6
-rw-r--r--fs/ext4/ext4_ice.c109
-rw-r--r--fs/ext4/ext4_ice.h104
-rw-r--r--fs/ext4/inode.c7
-rw-r--r--fs/ext4/page-io.c7
-rw-r--r--fs/ext4/readpage.c17
-rw-r--r--include/linux/ecryptfs.h53
-rw-r--r--include/linux/lsm_hooks.h2
-rw-r--r--include/linux/mfd/wcd9xxx/core.h52
-rw-r--r--include/linux/perf_event.h5
-rw-r--r--include/linux/pfk.h6
-rw-r--r--include/linux/pft.h5
-rw-r--r--include/linux/security.h6
-rw-r--r--include/soc/qcom/service-notifier.h5
-rw-r--r--include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h6
-rw-r--r--kernel/cpuset.c17
-rw-r--r--kernel/events/core.c6
-rw-r--r--net/core/rtnetlink.c18
-rw-r--r--security/Kconfig9
-rw-r--r--security/pfe/Kconfig1
-rw-r--r--security/pfe/Makefile3
-rw-r--r--security/pfe/pfk.c746
-rw-r--r--security/pfe/pfk_ecryptfs.c630
-rw-r--r--security/pfe/pfk_ecryptfs.h39
-rw-r--r--security/pfe/pfk_ext4.c212
-rw-r--r--security/pfe/pfk_ext4.h37
-rw-r--r--security/pfe/pfk_ice.h3
-rw-r--r--security/pfe/pfk_internal.h34
-rw-r--r--security/pfe/pft.c4
-rw-r--r--security/security.c6
-rw-r--r--security/selinux/hooks.c7
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/audio-ext-clk.c8
-rw-r--r--sound/soc/codecs/wcd9335.c195
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-routing.h60
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c1164
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.h12
-rw-r--r--sound/soc/codecs/wcd9xxx-common-v2.c116
-rw-r--r--sound/soc/codecs/wcd9xxx-common-v2.h4
-rw-r--r--sound/soc/codecs/wcd_cpe_core.c1
-rw-r--r--sound/soc/msm/msmcobalt.c16
-rw-r--r--sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c7
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c4
-rw-r--r--sound/usb/card.c5
-rw-r--r--sound/usb/usb_audio_qmi_svc.c42
-rw-r--r--tools/perf/util/evsel.c15
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(&gtp_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(&notif_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(&register_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(&register_mutex);
- if (chip->disconnect_cb)
- chip->disconnect_cb(chip);
snd_card_free_when_closed(card);
} else {
mutex_unlock(&register_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.",