summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/msm/msm_hang_detect.txt1
-rw-r--r--Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt46
-rw-r--r--Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt18
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt39
-rw-r--r--Documentation/devicetree/bindings/usb/qpnp-pdphy.txt3
-rw-r--r--arch/arm/boot/dts/qcom/msm-gdsc-falcon.dtsi159
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-pm.dtsi46
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi15
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi29
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi23
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon.dtsi43
-rw-r--r--arch/arm/boot/dts/qcom/msmtriton.dtsi76
-rw-r--r--arch/arm/configs/msmfalcon_defconfig2
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig3
-rw-r--r--arch/arm64/configs/msmcortex_defconfig3
-rw-r--r--arch/arm64/configs/msmfalcon-perf_defconfig1
-rw-r--r--arch/arm64/configs/msmfalcon_defconfig1
-rw-r--r--drivers/char/adsprpc.c18
-rw-r--r--drivers/clk/clk.c20
-rw-r--r--drivers/clk/msm/clock-gcc-cobalt.c2
-rw-r--r--drivers/clk/msm/clock-osm.c33
-rw-r--r--drivers/clk/qcom/Makefile2
-rw-r--r--drivers/clk/qcom/clk-smd-rpm.c55
-rw-r--r--drivers/clk/qcom/clk-voter.c133
-rw-r--r--drivers/clk/qcom/clk-voter.h50
-rw-r--r--drivers/clk/qcom/common.c31
-rw-r--r--drivers/clk/qcom/common.h2
-rw-r--r--drivers/clk/qcom/gcc-msmfalcon.c39
-rw-r--r--drivers/devfreq/governor_memlat.c24
-rw-r--r--drivers/gpu/msm/adreno.c130
-rw-r--r--drivers/gpu/msm/adreno.h24
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c138
-rw-r--r--drivers/gpu/msm/adreno_a5xx_snapshot.c2
-rw-r--r--drivers/gpu/msm/adreno_perfcounter.c27
-rw-r--r--drivers/input/misc/Kconfig2
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/ots_pat9125/Kconfig14
-rw-r--r--drivers/input/misc/ots_pat9125/Makefile7
-rw-r--r--drivers/input/misc/ots_pat9125/pat9125_linux_driver.c231
-rw-r--r--drivers/input/misc/ots_pat9125/pixart_ots.h6
-rw-r--r--drivers/iommu/io-pgtable-arm.c8
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c107
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c105
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_dcvs.c8
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c18
-rw-r--r--drivers/mfd/wcd934x-regmap.c43
-rw-r--r--drivers/mfd/wcd9xxx-regmap.h7
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils.c1
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c20
-rw-r--r--drivers/platform/msm/ipa/ipa_api.c12
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_usb.c16
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c2
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_intf.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c12
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c2
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c24
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_intf.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c18
-rw-r--r--drivers/power/power_supply_sysfs.c4
-rw-r--r--drivers/power/qcom-charger/fg-core.h19
-rw-r--r--drivers/power/qcom-charger/fg-util.c38
-rw-r--r--drivers/power/qcom-charger/qpnp-fg-gen3.c376
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c14
-rw-r--r--drivers/power/qcom-charger/smb-lib.c40
-rw-r--r--drivers/power/qcom-charger/smb-lib.h6
-rw-r--r--drivers/power/qcom-charger/smb138x-charger.c17
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c19
-rw-r--r--drivers/scsi/ufs/ufshcd.c78
-rw-r--r--drivers/scsi/ufs/ufshcd.h9
-rw-r--r--drivers/soc/qcom/core_hang_detect.c24
-rw-r--r--drivers/soc/qcom/glink.c4
-rw-r--r--drivers/soc/qcom/icnss.c69
-rw-r--r--drivers/soc/qcom/msm_smem.c6
-rw-r--r--drivers/soc/qcom/qdsp6v2/voice_svc.c41
-rw-r--r--drivers/soc/qcom/qsee_ipc_irq_bridge.c12
-rw-r--r--drivers/soc/qcom/rpm-smd.c8
-rw-r--r--drivers/usb/dwc3/gadget.c22
-rw-r--r--drivers/usb/gadget/function/f_midi.c2
-rw-r--r--drivers/usb/pd/policy_engine.c756
-rw-r--r--drivers/video/fbdev/core/fbcmap.c8
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c22
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c138
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c39
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c170
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_util.c17
-rw-r--r--include/dt-bindings/clock/qcom,gcc-msmfalcon.h2
-rw-r--r--include/linux/clk-provider.h3
-rw-r--r--include/linux/diagchar.h18
-rw-r--r--include/linux/leds-qpnp-flash.h3
-rw-r--r--include/linux/mfd/wcd934x/registers.h2
-rw-r--r--include/linux/power_supply.h4
-rw-r--r--include/soc/qcom/smem.h2
-rw-r--r--include/sound/wcd-dsp-mgr.h18
-rw-r--r--kernel/sched/core.c39
-rw-r--r--kernel/sched/fair.c65
-rw-r--r--kernel/sched/hmp.c7
-rw-r--r--kernel/sched/sched.h2
-rw-r--r--kernel/sched/tune.c2
-rw-r--r--net/core/dev.c3
-rw-r--r--net/wireless/db.txt2
-rw-r--r--sound/soc/codecs/msm_hdmi_codec_rx.c7
-rw-r--r--sound/soc/codecs/wcd-dsp-mgr.c62
-rw-r--r--sound/soc/codecs/wcd-spi.c87
-rw-r--r--sound/soc/codecs/wcd9335.c4
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsd.c33
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsd.h1
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c120
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h7
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c240
-rw-r--r--sound/soc/codecs/wcd9xxx-common-v2.c5
-rw-r--r--sound/soc/codecs/wcd9xxx-resmgr-v2.c2
-rw-r--r--sound/soc/msm/msmcobalt.c6
115 files changed, 3401 insertions, 1242 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_hang_detect.txt b/Documentation/devicetree/bindings/arm/msm/msm_hang_detect.txt
index 7f23d9a3c6e8..1700d588fd46 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_hang_detect.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_hang_detect.txt
@@ -25,6 +25,7 @@ The device tree parameters for the core hang detection are:
Required properties:
- compatible : "qcom,core-hang-detect"
+- label: unique name used to created sysfs entry
- qcom,threshold-arr :
Array of APCS_ALIAS*_CORE_HANG_THRESHOLD register address
for each core.
diff --git a/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt
index 02f21835f870..54ba2be39f0c 100644
--- a/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt
+++ b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt
@@ -6,5 +6,49 @@ to the Host processor. The host processor reads the direction and number of
steps over I2C and passes the data to the rest of the system.
Required properties:
+ - compatible : should be "pixart,pat9125".
+ - reg : i2c slave address of the device.
+ - interrupt-parent : parent of interrupt.
+ - interrupts : interrupt to indicate motion of the rotating switch.
- - compatible : should be "pixart,pat9125".
+Optional properties:
+ - pixart,inverse-x : boolean, use this to invert the x data before sending it to input framework
+ - pixart,inverse-y : boolean, use this to invert the y data before sending it to input framework
+ - pixart,press-enabled : boolean, use this to enable detection of pressing the button
+ - pinctrl-names : This should be defined if a target uses pinctrl framework.
+ See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt
+ It should specify the names of the configs that pinctrl can
+ install in driver.
+ Following are the pinctrl configs that can be installed:
+ "pmx_rot_switch_active" : Active configuration of pins,
+ it should specify active config
+ defined in pin groups of
+ interrupt gpio.
+ "pmx_rot_switch_suspend" : Disabled configuration of
+ pins, it should specify sleep
+ config defined in pin groups
+ of interrupt gpio.
+ "pmx_rot_switch_release" : Release configuration of
+ pins, it should specify release
+ config defined in pin groups of
+ interrupt gpio.
+ - pixart,irq-gpio : This should be defined if a target doesn't use pinctrl framework.
+ irq gpio, which is to provide interrupts to host, same as "interrupts" node.
+
+Required properties if 'pixart,press-enabled' DT property is defined:
+ - pixart,press-keycode : keycode to be sent when press is detected by the driver.
+
+Example:
+ pixart_pat9125@75 {
+ compatible = "pixart,pat9125";
+ reg = <0x75>;
+ interrupt-parent = <&msm_gpio>;
+ interrupts = <98 0x2008>;
+ pixart,irq-gpio = <&msm_gpio 98 0x2008>;
+ pinctrl-names = "pmx_rot_switch_active",
+ "pmx_rot_switch_suspend",
+ "pmx_rot_switch_release";
+ pinctrl-0 = <&pix_int_active>;
+ pinctrl-1 = <&pix_int_suspend>;
+ pinctrl-2 = <&pix_release>;
+ };
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
index bd05c8bebfc8..8365762e520f 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
@@ -38,18 +38,28 @@ Optional properties:
Default value is 4500000 uA.
- qcom,rparasitic-uohm : Integer property for flash current predictive mitigation indicating
parasitic component of battery resistance. Default value is 0 uOhm.
-- qcom,lmh-ocv-threshold-uv : Required property for flash current preemptive mitigation.
+- qcom,lmh-ocv-threshold-uv : Required property for flash current preemptive LMH mitigation.
Default value is 3700000 uV.
-- qcom,lmh-rbatt-threshold-uohm : Required property for flash current preemptive mitigation.
+- qcom,lmh-rbatt-threshold-uohm : Required property for flash current preemptive LMH mitigation.
Default value is 400000 uOhm.
-- qcom,lmh-mitigation-sel : Optional property to configure flash current preemptive mitigation.
+- qcom,lmh-mitigation-sel : Optional property to configure flash current preemptive LMH mitigation.
Accepted values are:
0: MITIGATION_DISABLED
1: MITIGATION_BY_ILED_THRESHOLD
2: MITIGATION_BY_SW
Default value is 2.
-- qcom,lmh-level : Optional property to configure flash current preemptive mitigation.
+- qcom,chgr-mitigation-sel : Optional property to configure flash current preemptive charger mitigation.
+ Accepted values are:
+ 0: MITIGATION_DISABLED
+ 1: MITIGATION_BY_ILED_THRESHOLD
+ 2: MITIGATION_BY_SW
+ Default value is 2.
+- qcom,lmh-level : Optional property to configure flash current preemptive LMH mitigation.
Accepted values are 0, 1, and 3. Default value is 0.
+- qcom,iled-thrsh-ma : Optional property to configure the led current threshold at which HW
+ preemptive mitigation is triggered. Unit is mA. Default value is 1000.
+ Accepted values are in the range 0 - 3100, with steps of 100.
+ 0 disables autonomous HW mitigation.
- qcom,thermal-derate-en : Boolean property to enable flash current thermal mitigation.
- qcom,thermal-derate-current : Array of currrent limits for thermal mitigation. Required if
qcom,thermal-derate-en is specified. Unit is mA. Format is
diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
index 7841251c67fe..caabcd347a72 100644
--- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
@@ -216,6 +216,42 @@ First Level Node - FG Gen3 device
Definition: Battery temperature delta interrupt threshold. Possible
values are: 2, 4, 6 and 10. Unit is in Kelvin.
+- qcom,hold-soc-while-full:
+ Usage: optional
+ Value type: <bool>
+ Definition: A boolean property that when defined holds SOC at 100% when
+ the battery is full.
+
+- qcom,ki-coeff-soc-dischg:
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: Array of monotonic SOC threshold values to change the ki
+ coefficient for medium discharge current during discharge.
+ This should be defined in the ascending order and in the
+ range of 0-100. Array limit is set to 3.
+
+- qcom,ki-coeff-med-dischg:
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: Array of ki coefficient values for medium discharge current
+ during discharge. These values will be applied when the
+ monotonic SOC goes below the SOC threshold specified under
+ qcom,ki-coeff-soc-dischg. Array limit is set to 3. This
+ property should be specified if qcom,ki-coeff-soc-dischg
+ is specified to make it fully functional. Value has no
+ unit. Allowed range is 0 to 62200 in micro units.
+
+- qcom,ki-coeff-hi-dischg:
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: Array of ki coefficient values for high discharge current
+ during discharge. These values will be applied when the
+ monotonic SOC goes below the SOC threshold specified under
+ qcom,ki-coeff-soc-dischg. Array limit is set to 3. This
+ property should be specified if qcom,ki-coeff-soc-dischg
+ is specified to make it fully functional. Value has no
+ unit. Allowed range is 0 to 62200 in micro units.
+
==========================================================
Second Level Nodes - Peripherals managed by FG Gen3 driver
==========================================================
@@ -246,6 +282,9 @@ pmicobalt_fg: qpnp,fg {
qcom,pmic-revid = <&pmicobalt_revid>;
io-channels = <&pmicobalt_rradc 3>;
io-channel-names = "rradc_batt_id";
+ qcom,ki-coeff-soc-dischg = <30 60 90>;
+ qcom,ki-coeff-med-dischg = <800 1000 1400>;
+ qcom,ki-coeff-hi-dischg = <1200 1500 2100>;
status = "okay";
qcom,fg-batt-soc@4000 {
diff --git a/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt b/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt
index f5c6651affea..cd1386512bd3 100644
--- a/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt
+++ b/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt
@@ -40,6 +40,9 @@ Optional properties:
- vconn-supply: Regulator that enables VCONN source output. This will
be supplied on the USB CC line that is not used for
communication when Ra resistance is detected.
+- qcom,vconn-uses-external-source: Indicates whether VCONN supply is sourced
+ from an external regulator. If omitted, then it is
+ assumed it is connected to VBUS.
Example:
qcom,qpnp-pdphy@1700 {
diff --git a/arch/arm/boot/dts/qcom/msm-gdsc-falcon.dtsi b/arch/arm/boot/dts/qcom/msm-gdsc-falcon.dtsi
new file mode 100644
index 000000000000..6550ddcad86c
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm-gdsc-falcon.dtsi
@@ -0,0 +1,159 @@
+/*
+ * 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 {
+ /* GCC GDSCs */
+ gdsc_usb30: qcom,gdsc@10f004 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_usb30";
+ reg = <0x10f004 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_ufs: qcom,gdsc@175004 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_ufs";
+ reg = <0x175004 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_hlos1_vote_lpass_adsp: qcom,gdsc@17d034 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_hlos1_vote_lpass_adsp";
+ reg = <0x17d034 0x4>;
+ qcom,no-status-check-on-disable;
+ qcom,gds-timeout = <500>;
+ status = "disabled";
+ };
+
+ gdsc_hlos1_vote_turing_adsp: qcom,gdsc@17d04c {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_hlos1_vote_turing_adsp";
+ reg = <0x17d04c 0x4>;
+ qcom,no-status-check-on-disable;
+ qcom,gds-timeout = <500>;
+ status = "disabled";
+ };
+
+ gdsc_hlos2_vote_turing_adsp: qcom,gdsc@17e04c {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_hlos2_vote_turing_adsp";
+ reg = <0x17e04c 0x4>;
+ qcom,no-status-check-on-disable;
+ qcom,gds-timeout = <500>;
+ status = "disabled";
+ };
+
+ /* MMSS GDSCs */
+ bimc_smmu_hw_ctrl: syscon@c8ce024 {
+ compatible = "syscon";
+ reg = <0xc8ce024 0x4>;
+ };
+
+ gdsc_bimc_smmu: qcom,gdsc@c8ce020 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_bimc_smmu";
+ reg = <0xc8ce020 0x4>;
+ hw-ctrl-addr = <&bimc_smmu_hw_ctrl>;
+ qcom,no-status-check-on-disable;
+ qcom,gds-timeout = <500>;
+ status = "disabled";
+ };
+
+ gdsc_venus: qcom,gdsc@c8c1024 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_venus";
+ reg = <0xc8c1024 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_venus_core0: qcom,gdsc@c8c1040 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_venus_core0";
+ reg = <0xc8c1040 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_camss_top: qcom,gdsc@c8c34a0 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_camss_top";
+ reg = <0xc8c34a0 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_vfe0: qcom,gdsc@c8c3664 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_vfe0";
+ reg = <0xc8c3664 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_vfe1: qcom,gdsc@c8c3674 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_vfe1";
+ reg = <0xc8c3674 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_cpp: qcom,gdsc@c8c36d4 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_cpp";
+ reg = <0xc8c36d4 0x4>;
+ status = "disabled";
+ };
+
+ gdsc_mdss: qcom,gdsc@c8c2304 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_mdss";
+ reg = <0xc8c2304 0x4>;
+ status = "disabled";
+ };
+
+ /* GPU GDSCs */
+ gpu_cx_hw_ctrl: syscon@5066008 {
+ compatible = "syscon";
+ reg = <0x5066008 0x4>;
+ };
+
+ gdsc_gpu_cx: qcom,gdsc@5066004 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_gpu_cx";
+ reg = <0x5066004 0x4>;
+ hw-ctrl-addr = <&gpu_cx_hw_ctrl>;
+ qcom,no-status-check-on-disable;
+ qcom,gds-timeout = <2000>;
+ status = "disabled";
+ };
+
+ /* GPU GX GDSCs */
+ gpu_gx_domain_addr: syscon@5065130 {
+ compatible = "syscon";
+ reg = <0x5065130 0x4>;
+ };
+
+ gpu_gx_sw_reset: syscon@5066090 {
+ compatible = "syscon";
+ reg = <0x5066090 0x4>;
+ };
+
+ gdsc_gpu_gx: qcom,gdsc@5066094 {
+ compatible = "qcom,gdsc";
+ regulator-name = "gdsc_gpu_gx";
+ reg = <0x5066094 0x4>;
+ domain-addr = <&gpu_gx_domain_addr>;
+ sw-reset = <&gpu_gx_sw_reset>;
+ qcom,retain-periph;
+ qcom,reset-aon-logic;
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pm.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pm.dtsi
index a20d80fda72b..c6d7defbf35c 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-pm.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-pm.dtsi
@@ -279,6 +279,52 @@
qcom,sleep-stats-version = <2>;
};
+ qcom,rpm-rail-stats@200000 {
+ compatible = "qcom,rpm-rail-stats";
+ reg = <0x200000 0x100>,
+ <0x29000c 0x4>;
+ reg-names = "phys_addr_base",
+ "offset_addr";
+ };
+
+ qcom,rpm-log@200000 {
+ compatible = "qcom,rpm-log";
+ reg = <0x200000 0x4000>,
+ <0x290018 0x4>;
+ qcom,rpm-addr-phys = <0x200000>;
+ qcom,offset-version = <4>;
+ qcom,offset-page-buffer-addr = <36>;
+ qcom,offset-log-len = <40>;
+ qcom,offset-log-len-mask = <44>;
+ qcom,offset-page-indices = <56>;
+ };
+
+ qcom,rpm-master-stats@778150 {
+ compatible = "qcom,rpm-master-stats";
+ reg = <0x778150 0x5000>;
+ qcom,masters = "APSS", "MPSS", "ADSP", "SLPI";
+ qcom,master-stats-version = <2>;
+ qcom,master-offset = <4096>;
+ };
+
+ rpm_msg_ram: memory@0x200000 {
+ compatible = "qcom,rpm-msg-ram";
+ reg = <0x200000 0x1000>,
+ <0x290000 0x1000>;
+ };
+
+ rpm_code_ram: rpm-memory@0x778000 {
+ compatible = "qcom,rpm-code-ram";
+ reg = <0x778000 0x5000>;
+ };
+
+ qcom,system-stats {
+ compatible = "qcom,system-stats";
+ qcom,rpm-msg-ram = <&rpm_msg_ram>;
+ qcom,rpm-code-ram = <&rpm_code_ram>;
+ qcom,masters = "APSS", "MPSS", "ADSP", "SLPI";
+ };
+
qcom,mpm@7781b8 {
compatible = "qcom,mpm-v2";
reg = <0x7781b8 0x1000>, /* MSM_RPM_MPM_BASE 4K */
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi
index c028ea0eeab3..f8069856f3d8 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi
@@ -61,7 +61,7 @@
50000000 100000000 200000000>;
qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
- cd-gpios = <&tlmm 95 0x1>;
+ cd-gpios = <&tlmm 95 0x0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
index 9b791d6b7fb0..8016a3822a7f 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
@@ -669,11 +669,10 @@
qcom,load-freq-tbl =
/* Encoders */
<1105920 533000000 0x55555555>, /* 4kx2304@30 */ /*TURBO*/
- < 979200 444000000 0x55555555>, /* 1080p@120,1440p@60,
+ <1036800 444000000 0x55555555>, /* 720p@240, 1080p@120,1440p@60,
* UHD@30 */ /*NOMINAL*/
- < 939700 355200000 0x55555555>, /* 4kx2304@24 */ /*SVSL1*/
- < 489600 269330000 0x55555555>, /* 1080p@60, 2560x1440@30 */
- /* SVS */
+ < 829440 355200000 0x55555555>, /* UHD/4096x2160@30 SVSL1 */
+ < 489600 269330000 0x55555555>, /* 1080p@60 SVS */
< 432000 200000000 0x55555555>, /* 720p@120, 1080p@30 */
/* SVS2 */
@@ -685,7 +684,7 @@
<1675472 355200000 0xffffffff>, /* 4kx2304@44 */ /*SVSL1*/
<1105920 269330000 0xffffffff>, /* UHD/4k2304@30, 1080p@120 */
/* SVS */
- < 864000 200000000 0xffffffff>; /* 720p@240, 1080p@60 */
+ < 829440 200000000 0xffffffff>; /* 720p@120, 1080p@60 */
/* SVS2 */
qcom,imem-ab-tbl =
@@ -701,11 +700,11 @@
<1728000 1728000 2211840 0x3f00000c>,
/* Encoder */
/* Load > Nominal, Nominal <-> Turbo Eg. 4kx2304@30 */
- <972000 972000 1105920 0x04000004>,
+ <1036800 1036800 1105920 0x04000004>,
/* Load > SVSL1, SVSL1<-> Nominal Eg. 3840x2160@30 */
- <939700 939700 972000 0x04000004>,
+ < 829440 829440 1036800 0x04000004>,
/* Load > SVS , SVS <-> SVSL1 Eg. 4kx2304@24 */
- <489600 489600 939700 0x04000004>;
+ < 489600 489600 829440 0x04000004>;
qcom,dcvs-limit = /* Min Frame size, Min MBs/sec */
<32400 30>, /* Encoder 3840x2160@30 */
diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
index 96518dca0ec1..b21e2dcf8c1a 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
@@ -1135,13 +1135,21 @@
qcom,firmware-name = "ipa_fws";
};
- qcom,chd {
+ qcom,chd_silver {
compatible = "qcom,core-hang-detect";
+ label = "silver";
qcom,threshold-arr = <0x179880b0 0x179980b0
- 0x179a80b0 0x179b80b0 0x178880b0 0x178980b0
- 0x178a80b0 0x178b80b0>;
+ 0x179a80b0 0x179b80b0>;
qcom,config-arr = <0x179880b8 0x179980b8
- 0x179a80b8 0x179b80b8 0x178880b8 0x178980b8
+ 0x179a80b8 0x179b80b8>;
+ };
+
+ qcom,chd_gold {
+ compatible = "qcom,core-hang-detect";
+ label = "gold";
+ qcom,threshold-arr = <0x178880b0 0x178980b0
+ 0x178a80b0 0x178b80b0>;
+ qcom,config-arr = <0x178880b8 0x178980b8
0x178a80b8 0x178b80b8>;
};
@@ -1192,41 +1200,49 @@
label = "adsprpc-smd";
iommus = <&lpass_q6_smmu 2>;
qcom,secure-context-bank;
+ dma-coherent;
};
qcom,msm_fastrpc_compute_cb1 {
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&lpass_q6_smmu 8>;
+ dma-coherent;
};
qcom,msm_fastrpc_compute_cb2 {
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&lpass_q6_smmu 9>;
+ dma-coherent;
};
qcom,msm_fastrpc_compute_cb3 {
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&lpass_q6_smmu 10>;
+ dma-coherent;
};
qcom,msm_fastrpc_compute_cb4 {
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&lpass_q6_smmu 11>;
+ dma-coherent;
};
qcom,msm_fastrpc_compute_cb6 {
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&lpass_q6_smmu 5>;
+ dma-coherent;
};
qcom,msm_fastrpc_compute_cb7 {
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&lpass_q6_smmu 6>;
+ dma-coherent;
};
qcom,msm_fastrpc_compute_cb8 {
compatible = "qcom,msm-fastrpc-compute-cb";
label = "adsprpc-smd";
iommus = <&lpass_q6_smmu 7>;
+ dma-coherent;
};
};
@@ -2881,11 +2897,6 @@
vdd-3.3-ch0-supply = <&pmcobalt_l25_pin_ctrl>;
qcom,vdd-0.8-cx-mx-config = <800000 800000>;
qcom,vdd-3.3-ch0-config = <3104000 3312000>;
- qcom,msm-bus,name = "msm-icnss";
- qcom,msm-bus,num-cases = <2>;
- qcom,msm-bus,num-paths = <1>;
- qcom,msm-bus,vectors-KBps = <81 10065 0 0>,
- <81 10065 0 16000>;
qcom,icnss-vadc = <&pmcobalt_vadc>;
qcom,icnss-adc_tm = <&pmcobalt_adc_tm>;
};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi
index bdbcd9d7b6f9..e5db2766c553 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi
@@ -172,4 +172,27 @@
compatible = "qcom,smp2pgpio_test_smp2p_5_out";
gpios = <&smp2pgpio_smp2p_5_out 0 0>;
};
+
+ /* ssr - inbound entry from lpass */
+ smp2pgpio_ssr_smp2p_2_in: qcom,smp2pgpio-ssr-smp2p-2-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "slave-kernel";
+ qcom,remote-pid = <2>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ /* ssr - outbound entry to lpass */
+ smp2pgpio_ssr_smp2p_2_out: qcom,smp2pgpio-ssr-smp2p-2-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "master-kernel";
+ qcom,remote-pid = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
index df04d1a57683..2b2a201db8bc 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
@@ -670,12 +670,44 @@
<0 425 0>; /* CE11 */
qcom,wlan-msa-memory = <0x100000>;
};
+
+ qcom,lpass@15700000 {
+ compatible = "qcom,pil-tz-generic";
+ reg = <0x15700000 0x00100>;
+ interrupts = <0 162 1>;
+
+ vdd_cx-supply = <&pmfalcon_s3b_level>;
+ qcom,proxy-reg-names = "vdd_cx";
+ qcom,vdd_cx-uV-uA = <RPM_SMD_REGULATOR_LEVEL_TURBO 100000>;
+
+ clocks = <&clock_rpmcc CXO_PIL_LPASS_CLK>;
+ clock-names = "xo";
+ qcom,proxy-clock-names = "xo";
+
+ qcom,pas-id = <1>;
+ qcom,proxy-timeout-ms = <10000>;
+ qcom,smem-id = <423>;
+ qcom,sysmon-id = <1>;
+ qcom,ssctl-instance-id = <0x14>;
+ qcom,firmware-name = "adsp";
+ memory-region = <&adsp_fw_mem>;
+
+ /* GPIO inputs from lpass */
+ qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>;
+ qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>;
+ qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_2_in 3 0>;
+
+ /* GPIO output to lpass */
+ qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
+ status = "ok";
+ };
};
#include "msmfalcon-ion.dtsi"
#include "msmfalcon-bus.dtsi"
#include "msmfalcon-regulator.dtsi"
-#include "msm-gdsc-cobalt.dtsi"
+#include "msm-gdsc-falcon.dtsi"
&gdsc_usb30 {
clock-names = "core_clk";
@@ -699,6 +731,14 @@
status = "ok";
};
+&gdsc_hlos1_vote_turing_adsp {
+ status = "ok";
+};
+
+&gdsc_hlos2_vote_turing_adsp {
+ status = "ok";
+};
+
&gdsc_venus {
status = "ok";
};
@@ -749,5 +789,6 @@
&gdsc_gpu_cx {
status = "ok";
};
+
#include "msm-pmfalcon.dtsi"
#include "msm-pm2falcon.dtsi"
diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi
index 71b4da9bb2d0..09bb5f081602 100644
--- a/arch/arm/boot/dts/qcom/msmtriton.dtsi
+++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi
@@ -16,6 +16,7 @@
#include <dt-bindings/clock/qcom,mmcc-msmfalcon.h>
#include <dt-bindings/clock/qcom,rpmcc.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
/ {
model = "Qualcomm Technologies, Inc. MSMTRITON";
@@ -543,3 +544,78 @@
};
#include "msmtriton-ion.dtsi"
+#include "msmfalcon-regulator.dtsi"
+#include "msm-gdsc-falcon.dtsi"
+
+&gdsc_usb30 {
+ clock-names = "core_clk";
+ clocks = <&clock_gcc GCC_USB30_MASTER_CLK>;
+ status = "ok";
+};
+
+&gdsc_ufs {
+ status = "ok";
+};
+
+&gdsc_bimc_smmu {
+ clock-names = "bus_clk";
+ clocks = <&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>;
+ proxy-supply = <&gdsc_bimc_smmu>;
+ qcom,proxy-consumer-enable;
+ status = "ok";
+};
+
+&gdsc_hlos1_vote_lpass_adsp {
+ status = "ok";
+};
+
+&gdsc_venus {
+ status = "ok";
+};
+
+&gdsc_venus_core0 {
+ qcom,support-hw-trigger;
+ status = "ok";
+};
+
+&gdsc_camss_top {
+ status = "ok";
+};
+
+&gdsc_vfe0 {
+ parent-supply = <&gdsc_camss_top>;
+ status = "ok";
+};
+
+&gdsc_vfe1 {
+ parent-supply = <&gdsc_camss_top>;
+ status = "ok";
+};
+
+&gdsc_cpp {
+ parent-supply = <&gdsc_camss_top>;
+ status = "ok";
+};
+
+&gdsc_mdss {
+ clock-names = "bus_clk", "rot_clk";
+ clocks = <&clock_mmss MMSS_MDSS_AXI_CLK>,
+ <&clock_mmss MMSS_MDSS_ROT_CLK>;
+ proxy-supply = <&gdsc_mdss>;
+ qcom,proxy-consumer-enable;
+ status = "ok";
+};
+
+&gdsc_gpu_gx {
+ clock-names = "bimc_core_clk", "core_clk", "core_root_clk";
+ clocks = <&clock_gcc GCC_GPU_BIMC_GFX_CLK>,
+ <&clock_gfx GPUCC_GFX3D_CLK>,
+ <&clock_gfx GFX3D_CLK_SRC>;
+ qcom,force-enable-root-clk;
+ parent-supply = <&gfx_vreg_corner>;
+ status = "ok";
+};
+
+&gdsc_gpu_cx {
+ status = "ok";
+};
diff --git a/arch/arm/configs/msmfalcon_defconfig b/arch/arm/configs/msmfalcon_defconfig
index 64da50bb55b2..41572d54afcb 100644
--- a/arch/arm/configs/msmfalcon_defconfig
+++ b/arch/arm/configs/msmfalcon_defconfig
@@ -420,7 +420,7 @@ CONFIG_IPA3=y
CONFIG_RMNET_IPA3=y
CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
-CONFIG_MSM_MDSS_PLL=y
+CONFIG_QCOM_CLK_SMD_RPM=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_ARM_SMMU=y
CONFIG_IOMMU_DEBUG=y
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 036d6aa5c062..799e43f09a11 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -13,6 +13,7 @@ CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
CONFIG_CGROUP_FREEZER=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_SCHED_HMP=y
CONFIG_SCHED_HMP_CSTATE_AWARE=y
@@ -21,6 +22,7 @@ CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_PID_NS is not set
CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SCHED_TUNE=y
CONFIG_BLK_DEV_INITRD=y
# CONFIG_RD_XZ is not set
# CONFIG_RD_LZO is not set
@@ -435,6 +437,7 @@ CONFIG_USB_CONFIGFS_F_MTP=y
CONFIG_USB_CONFIGFS_F_PTP=y
CONFIG_USB_CONFIGFS_F_ACC=y
CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
CONFIG_USB_CONFIGFS_F_GSI=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index 77f0129776a3..dfd658d815fe 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -13,6 +13,7 @@ CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_SCHED_HMP=y
@@ -21,6 +22,7 @@ CONFIG_SCHED_CORE_CTL=y
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_PID_NS is not set
+CONFIG_SCHED_TUNE=y
CONFIG_BLK_DEV_INITRD=y
# CONFIG_RD_XZ is not set
# CONFIG_RD_LZO is not set
@@ -437,6 +439,7 @@ CONFIG_USB_CONFIGFS_F_MTP=y
CONFIG_USB_CONFIGFS_F_PTP=y
CONFIG_USB_CONFIGFS_F_ACC=y
CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
CONFIG_USB_CONFIGFS_F_GSI=y
diff --git a/arch/arm64/configs/msmfalcon-perf_defconfig b/arch/arm64/configs/msmfalcon-perf_defconfig
index 1bc352704893..5d271cad0aad 100644
--- a/arch/arm64/configs/msmfalcon-perf_defconfig
+++ b/arch/arm64/configs/msmfalcon-perf_defconfig
@@ -473,6 +473,7 @@ CONFIG_RMNET_IPA3=y
CONFIG_GPIO_USB_DETECT=y
CONFIG_SEEMP_CORE=y
CONFIG_USB_BAM=y
+CONFIG_QCOM_CLK_SMD_RPM=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_ARM_SMMU=y
diff --git a/arch/arm64/configs/msmfalcon_defconfig b/arch/arm64/configs/msmfalcon_defconfig
index 348c34a94119..707bc68c825f 100644
--- a/arch/arm64/configs/msmfalcon_defconfig
+++ b/arch/arm64/configs/msmfalcon_defconfig
@@ -483,6 +483,7 @@ CONFIG_RMNET_IPA3=y
CONFIG_GPIO_USB_DETECT=y
CONFIG_SEEMP_CORE=y
CONFIG_USB_BAM=y
+CONFIG_QCOM_CLK_SMD_RPM=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 63dc23387133..67c1207d35be 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -163,6 +163,7 @@ struct fastrpc_smmu {
int enabled;
int faults;
int secure;
+ int coherent;
};
struct fastrpc_session_ctx {
@@ -1129,6 +1130,8 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
for (oix = 0; oix < inbufs + outbufs; ++oix) {
int i = ctx->overps[oix]->raix;
struct fastrpc_mmap *map = ctx->maps[i];
+ if (ctx->fl->sctx->smmu.coherent)
+ continue;
if (map && map->uncached)
continue;
if (rpra[i].buf.len && ctx->overps[oix]->mstart)
@@ -1141,7 +1144,8 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
rpra[inh + i].buf.len = ctx->lpra[inh + i].buf.len;
rpra[inh + i].h = ctx->lpra[inh + i].h;
}
- dmac_flush_range((char *)rpra, (char *)rpra + ctx->used);
+ if (!ctx->fl->sctx->smmu.coherent)
+ dmac_flush_range((char *)rpra, (char *)rpra + ctx->used);
bail:
return err;
}
@@ -1372,13 +1376,15 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
goto bail;
}
- inv_args_pre(ctx);
- if (FASTRPC_MODE_SERIAL == mode)
- inv_args(ctx);
+ if (!fl->sctx->smmu.coherent) {
+ inv_args_pre(ctx);
+ if (mode == FASTRPC_MODE_SERIAL)
+ inv_args(ctx);
+ }
VERIFY(err, 0 == fastrpc_invoke_send(ctx, kernel, invoke->handle));
if (err)
goto bail;
- if (FASTRPC_MODE_PARALLEL == mode)
+ if (mode == FASTRPC_MODE_PARALLEL && !fl->sctx->smmu.coherent)
inv_args(ctx);
wait:
if (kernel)
@@ -2301,6 +2307,8 @@ static int fastrpc_cb_probe(struct device *dev)
sess = &chan->session[chan->sesscount];
sess->smmu.cb = iommuspec.args[0];
sess->used = 0;
+ sess->smmu.coherent = of_property_read_bool(dev->of_node,
+ "dma-coherent");
sess->smmu.secure = of_property_read_bool(dev->of_node,
"qcom,secure-context-bank");
if (sess->smmu.secure)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index ed763c7e98fc..1c625764133d 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -547,6 +547,26 @@ void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
EXPORT_SYMBOL_GPL(clk_hw_set_rate_range);
/*
+ * Aggregate the rate of all child nodes which are enabled and exclude the
+ * child node which requests for clk_aggregate_rate.
+ */
+unsigned long clk_aggregate_rate(struct clk_hw *hw,
+ const struct clk_core *parent)
+{
+ struct clk_core *child;
+ unsigned long aggre_rate = 0;
+
+ hlist_for_each_entry(child, &parent->children, child_node) {
+ if (child->enable_count &&
+ strcmp(child->name, hw->init->name))
+ aggre_rate = max(child->rate, aggre_rate);
+ }
+
+ return aggre_rate;
+}
+EXPORT_SYMBOL_GPL(clk_aggregate_rate);
+
+/*
* Helper for finding best parent to provide a given frequency. This can be used
* directly as a determine_rate callback (e.g. for a mux), or from a more
* complex clock that may combine a mux with other operations.
diff --git a/drivers/clk/msm/clock-gcc-cobalt.c b/drivers/clk/msm/clock-gcc-cobalt.c
index 05272118af16..46e791b3cb99 100644
--- a/drivers/clk/msm/clock-gcc-cobalt.c
+++ b/drivers/clk/msm/clock-gcc-cobalt.c
@@ -2374,7 +2374,7 @@ static struct mux_clk gcc_debug_mux = {
{ &debug_cpu_clk.c, 0x00c0 },
{ &snoc_clk.c, 0x0000 },
{ &cnoc_clk.c, 0x000e },
- { &bimc_clk.c, 0x00a9 },
+ { &bimc_clk.c, 0x014e },
{ &gcc_mmss_sys_noc_axi_clk.c, 0x001f },
{ &gcc_mmss_noc_cfg_ahb_clk.c, 0x0020 },
{ &gcc_usb30_master_clk.c, 0x003e },
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index d6cdbbc78827..d29fd60719c9 100644
--- a/drivers/clk/msm/clock-osm.c
+++ b/drivers/clk/msm/clock-osm.c
@@ -202,6 +202,8 @@ enum clk_osm_trace_packet_id {
#define TRACE_CTRL_EN_MASK BIT(0)
#define TRACE_CTRL_ENABLE 1
#define TRACE_CTRL_DISABLE 0
+#define TRACE_CTRL_ENABLE_WDOG_STATUS BIT(30)
+#define TRACE_CTRL_ENABLE_WDOG_STATUS_MASK BIT(30)
#define TRACE_CTRL_PACKET_TYPE_MASK BVAL(2, 1, 3)
#define TRACE_CTRL_PACKET_TYPE_SHIFT 1
#define TRACE_CTRL_PERIODIC_TRACE_EN_MASK BIT(3)
@@ -221,6 +223,11 @@ enum clk_osm_trace_packet_id {
#define PERFCL_EFUSE_SHIFT 29
#define PERFCL_EFUSE_MASK 0x7
+#define MSMCOBALTV1_PWRCL_BOOT_RATE 1478400000
+#define MSMCOBALTV1_PERFCL_BOOT_RATE 1536000000
+#define MSMCOBALTV2_PWRCL_BOOT_RATE 1555200000
+#define MSMCOBALTV2_PERFCL_BOOT_RATE 1728000000
+
static void __iomem *virt_base;
static void __iomem *debug_base;
@@ -2687,6 +2694,18 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
return rc;
}
+ if (msmcobalt_v2) {
+ /* Enable OSM WDOG registers */
+ clk_osm_masked_write_reg(&pwrcl_clk,
+ TRACE_CTRL_ENABLE_WDOG_STATUS,
+ TRACE_CTRL,
+ TRACE_CTRL_ENABLE_WDOG_STATUS_MASK);
+ clk_osm_masked_write_reg(&perfcl_clk,
+ TRACE_CTRL_ENABLE_WDOG_STATUS,
+ TRACE_CTRL,
+ TRACE_CTRL_ENABLE_WDOG_STATUS_MASK);
+ }
+
/*
* The hmss_gpll0 clock runs at 300 MHz. Ensure it is at the correct
* frequency before enabling OSM. LUT index 0 is always sourced from
@@ -2700,18 +2719,22 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
}
clk_prepare_enable(&sys_apcsaux_clk_gcc.c);
- /* Set 300MHz index */
- rc = clk_set_rate(&pwrcl_clk.c, init_rate);
+ /* Set boot rate */
+ rc = clk_set_rate(&pwrcl_clk.c, msmcobalt_v1 ?
+ MSMCOBALTV1_PWRCL_BOOT_RATE :
+ MSMCOBALTV2_PWRCL_BOOT_RATE);
if (rc) {
- dev_err(&pdev->dev, "Unable to set init rate on pwr cluster, rc=%d\n",
+ dev_err(&pdev->dev, "Unable to set boot rate on pwr cluster, rc=%d\n",
rc);
clk_disable_unprepare(&sys_apcsaux_clk_gcc.c);
return rc;
}
- rc = clk_set_rate(&perfcl_clk.c, init_rate);
+ rc = clk_set_rate(&perfcl_clk.c, msmcobalt_v1 ?
+ MSMCOBALTV1_PERFCL_BOOT_RATE :
+ MSMCOBALTV2_PERFCL_BOOT_RATE);
if (rc) {
- dev_err(&pdev->dev, "Unable to set init rate on perf cluster, rc=%d\n",
+ dev_err(&pdev->dev, "Unable to set boot rate on perf cluster, rc=%d\n",
rc);
clk_disable_unprepare(&sys_apcsaux_clk_gcc.c);
return rc;
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 7ee0294e9dc7..adebefd63e71 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -11,7 +11,7 @@ clk-qcom-y += clk-regmap-divider.o
clk-qcom-y += clk-regmap-mux.o
clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
clk-qcom-y += clk-hfpll.o
-clk-qcom-y += reset.o
+clk-qcom-y += reset.o clk-voter.o
clk-qcom-y += clk-dummy.o
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o gdsc-regulator.o
diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c
index ac007ec667bb..612e7b37a8d0 100644
--- a/drivers/clk/qcom/clk-smd-rpm.c
+++ b/drivers/clk/qcom/clk-smd-rpm.c
@@ -29,6 +29,8 @@
#include <dt-bindings/clock/qcom,rpmcc.h>
#include <dt-bindings/mfd/qcom-rpm.h>
+#include "clk-voter.h"
+
#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773
#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370
#define QCOM_RPM_SMD_KEY_RATE 0x007a484b
@@ -603,7 +605,7 @@ DEFINE_CLK_SMD_RPM(msmfalcon, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2);
DEFINE_CLK_SMD_RPM(msmfalcon, cnoc_periph_clk, cnoc_periph_a_clk,
QCOM_SMD_RPM_BUS_CLK, 0);
DEFINE_CLK_SMD_RPM(msmfalcon, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
-DEFINE_CLK_SMD_RPM(msmfalcon, mmssnoc_axi_rpm_clk, mmssnoc_axi_rpm_a_clk,
+DEFINE_CLK_SMD_RPM(msmfalcon, mmssnoc_axi_clk, mmssnoc_axi_a_clk,
QCOM_SMD_RPM_MMAXI_CLK, 0);
DEFINE_CLK_SMD_RPM(msmfalcon, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0);
DEFINE_CLK_SMD_RPM(msmfalcon, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0);
@@ -624,6 +626,27 @@ DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msmfalcon, ln_bb_clk2_pin,
ln_bb_clk2_pin_ao, 0x2);
DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msmfalcon, ln_bb_clk3_pin,
ln_bb_clk3_pin_ao, 0x3);
+/* Voter clocks */
+static DEFINE_CLK_VOTER(bimc_msmbus_clk, bimc_clk, LONG_MAX);
+static DEFINE_CLK_VOTER(bimc_msmbus_a_clk, bimc_a_clk, LONG_MAX);
+static DEFINE_CLK_VOTER(cnoc_msmbus_clk, cnoc_clk, LONG_MAX);
+static DEFINE_CLK_VOTER(cnoc_msmbus_a_clk, cnoc_a_clk, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_msmbus_clk, snoc_clk, LONG_MAX);
+static DEFINE_CLK_VOTER(snoc_msmbus_a_clk, snoc_a_clk, LONG_MAX);
+static DEFINE_CLK_VOTER(cnoc_periph_keepalive_a_clk, cnoc_periph_a_clk,
+ LONG_MAX);
+static DEFINE_CLK_VOTER(mcd_ce1_clk, ce1_clk, 85710000);
+static DEFINE_CLK_VOTER(qcedev_ce1_clk, ce1_clk, 85710000);
+static DEFINE_CLK_VOTER(qcrypto_ce1_clk, ce1_clk, 85710000);
+static DEFINE_CLK_VOTER(qseecom_ce1_clk, ce1_clk, 85710000);
+static DEFINE_CLK_VOTER(scm_ce1_clk, ce1_clk, 85710000);
+
+static DEFINE_CLK_BRANCH_VOTER(cxo_dwc3_clk, cxo);
+static DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, cxo);
+static DEFINE_CLK_BRANCH_VOTER(cxo_otg_clk, cxo);
+static DEFINE_CLK_BRANCH_VOTER(cxo_pil_lpass_clk, cxo);
+static DEFINE_CLK_BRANCH_VOTER(cxo_pil_cdsp_clk, cxo);
+
static struct clk_hw *msmfalcon_clks[] = {
[RPM_XO_CLK_SRC] = &msmfalcon_cxo.hw,
[RPM_XO_A_CLK_SRC] = &msmfalcon_cxo_a.hw,
@@ -639,8 +662,8 @@ static struct clk_hw *msmfalcon_clks[] = {
[RPM_AGGR2_NOC_A_CLK] = &msmfalcon_aggre2_noc_a_clk.hw,
[RPM_CNOC_CLK] = &msmfalcon_cnoc_clk.hw,
[RPM_CNOC_A_CLK] = &msmfalcon_cnoc_a_clk.hw,
- [RPM_MMAXI_CLK] = &msmfalcon_mmssnoc_axi_rpm_clk.hw,
- [RPM_MMAXI_A_CLK] = &msmfalcon_mmssnoc_axi_rpm_a_clk.hw,
+ [RPM_MMAXI_CLK] = &msmfalcon_mmssnoc_axi_clk.hw,
+ [RPM_MMAXI_A_CLK] = &msmfalcon_mmssnoc_axi_a_clk.hw,
[RPM_IPA_CLK] = &msmfalcon_ipa_clk.hw,
[RPM_IPA_A_CLK] = &msmfalcon_ipa_a_clk.hw,
[RPM_CE1_CLK] = &msmfalcon_ce1_clk.hw,
@@ -661,6 +684,25 @@ static struct clk_hw *msmfalcon_clks[] = {
[RPM_LN_BB_CLK3_PIN_AO] = &msmfalcon_ln_bb_clk3_pin_ao.hw,
[RPM_CNOC_PERIPH_CLK] = &msmfalcon_cnoc_periph_clk.hw,
[RPM_CNOC_PERIPH_A_CLK] = &msmfalcon_cnoc_periph_a_clk.hw,
+
+ /* Voter Clocks */
+ [BIMC_MSMBUS_CLK] = &bimc_msmbus_clk.hw,
+ [BIMC_MSMBUS_A_CLK] = &bimc_msmbus_a_clk.hw,
+ [CNOC_MSMBUS_CLK] = &cnoc_msmbus_clk.hw,
+ [CNOC_MSMBUS_A_CLK] = &cnoc_msmbus_a_clk.hw,
+ [MCD_CE1_CLK] = &mcd_ce1_clk.hw,
+ [QCEDEV_CE1_CLK] = &qcedev_ce1_clk.hw,
+ [QCRYPTO_CE1_CLK] = &qcrypto_ce1_clk.hw,
+ [QSEECOM_CE1_CLK] = &qseecom_ce1_clk.hw,
+ [SCM_CE1_CLK] = &scm_ce1_clk.hw,
+ [SNOC_MSMBUS_CLK] = &snoc_msmbus_clk.hw,
+ [SNOC_MSMBUS_A_CLK] = &snoc_msmbus_a_clk.hw,
+ [CXO_DWC3_CLK] = &cxo_dwc3_clk.hw,
+ [CXO_LPM_CLK] = &cxo_lpm_clk.hw,
+ [CXO_OTG_CLK] = &cxo_otg_clk.hw,
+ [CXO_PIL_LPASS_CLK] = &cxo_pil_lpass_clk.hw,
+ [CXO_PIL_CDSP_CLK] = &cxo_pil_cdsp_clk.hw,
+ [CNOC_PERIPH_KEEPALIVE_A_CLK] = &cnoc_periph_keepalive_a_clk.hw,
};
static const struct rpm_smd_clk_desc rpm_clk_msmfalcon = {
@@ -757,9 +799,14 @@ static int rpm_smd_clk_probe(struct platform_device *pdev)
/* Keep an active vote on CXO in case no other driver votes for it */
if (is_8996)
clk_prepare_enable(msm8996_cxo_a.hw.clk);
- else if (is_falcon)
+ else if (is_falcon) {
clk_prepare_enable(msmfalcon_cxo_a.hw.clk);
+ /* Hold an active set vote for the cnoc_periph resource */
+ clk_set_rate(cnoc_periph_keepalive_a_clk.hw.clk, 19200000);
+ clk_prepare_enable(cnoc_periph_keepalive_a_clk.hw.clk);
+ }
+
dev_info(&pdev->dev, "Registered RPM clocks\n");
return 0;
diff --git a/drivers/clk/qcom/clk-voter.c b/drivers/clk/qcom/clk-voter.c
new file mode 100644
index 000000000000..d3409b9e6b64
--- /dev/null
+++ b/drivers/clk/qcom/clk-voter.c
@@ -0,0 +1,133 @@
+/*
+ * 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 <linux/clk.h>
+
+#include "clk-voter.h"
+
+static int voter_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ int ret = 0;
+ struct clk_voter *v = to_clk_voter(hw);
+ unsigned long cur_rate, new_rate, other_rate = 0;
+
+ if (v->is_branch)
+ return ret;
+
+ if (v->enabled) {
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+
+ /*
+ * Get the aggregate rate without this clock's vote and update
+ * if the new rate is different than the current rate.
+ */
+ other_rate = clk_aggregate_rate(hw, parent->core);
+
+ cur_rate = max(other_rate, clk_get_rate(hw->clk));
+ new_rate = max(other_rate, rate);
+
+ if (new_rate != cur_rate) {
+ ret = clk_set_rate(parent->clk, new_rate);
+ if (ret)
+ return ret;
+ }
+ }
+ v->rate = rate;
+
+ return ret;
+}
+
+static int voter_clk_prepare(struct clk_hw *hw)
+{
+ int ret = 0;
+ unsigned long cur_rate;
+ struct clk_hw *parent;
+ struct clk_voter *v = to_clk_voter(hw);
+
+ parent = clk_hw_get_parent(hw);
+
+ if (v->is_branch) {
+ v->enabled = true;
+ return ret;
+ }
+
+ /*
+ * Increase the rate if this clock is voting for a higher rate
+ * than the current rate.
+ */
+ cur_rate = clk_aggregate_rate(hw, parent->core);
+
+ if (v->rate > cur_rate) {
+ ret = clk_set_rate(parent->clk, v->rate);
+ if (ret)
+ return ret;
+ }
+ v->enabled = true;
+
+ return ret;
+}
+
+static void voter_clk_unprepare(struct clk_hw *hw)
+{
+ unsigned long cur_rate, new_rate;
+ struct clk_hw *parent;
+ struct clk_voter *v = to_clk_voter(hw);
+
+
+ parent = clk_hw_get_parent(hw);
+
+ /*
+ * Decrease the rate if this clock was the only one voting for
+ * the highest rate.
+ */
+ v->enabled = false;
+ if (v->is_branch)
+ return;
+
+ new_rate = clk_aggregate_rate(hw, parent->core);
+ cur_rate = max(new_rate, v->rate);
+
+ if (new_rate < cur_rate)
+ clk_set_rate(parent->clk, new_rate);
+}
+
+static int voter_clk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_voter *v = to_clk_voter(hw);
+
+ return v->enabled;
+}
+
+static long voter_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return clk_hw_round_rate(clk_hw_get_parent(hw), rate);
+}
+
+static unsigned long voter_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_voter *v = to_clk_voter(hw);
+
+ return v->rate;
+}
+
+struct clk_ops clk_ops_voter = {
+ .prepare = voter_clk_prepare,
+ .unprepare = voter_clk_unprepare,
+ .set_rate = voter_clk_set_rate,
+ .is_enabled = voter_clk_is_enabled,
+ .round_rate = voter_clk_round_rate,
+ .recalc_rate = voter_clk_recalc_rate,
+};
diff --git a/drivers/clk/qcom/clk-voter.h b/drivers/clk/qcom/clk-voter.h
new file mode 100644
index 000000000000..27092ae7d131
--- /dev/null
+++ b/drivers/clk/qcom/clk-voter.h
@@ -0,0 +1,50 @@
+/*
+ * 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 __QCOM_CLK_VOTER_H__
+#define __QCOM_CLK_VOTER_H__
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+struct clk_voter {
+ int is_branch;
+ bool enabled;
+ struct clk_hw hw;
+ unsigned long rate;
+};
+
+extern struct clk_ops clk_ops_voter;
+
+#define to_clk_voter(_hw) container_of(_hw, struct clk_voter, hw)
+
+#define __DEFINE_CLK_VOTER(clk_name, _parent_name, _default_rate, _is_branch) \
+ struct clk_voter clk_name = { \
+ .is_branch = (_is_branch), \
+ .rate = _default_rate, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_ops_voter, \
+ .name = #clk_name, \
+ .parent_names = (const char *[]){ #_parent_name }, \
+ .num_parents = 1, \
+ }, \
+ }
+
+#define DEFINE_CLK_VOTER(clk_name, _parent_name, _default_rate) \
+ __DEFINE_CLK_VOTER(clk_name, _parent_name, _default_rate, 0)
+
+#define DEFINE_CLK_BRANCH_VOTER(clk_name, _parent_name) \
+ __DEFINE_CLK_VOTER(clk_name, _parent_name, 1000, 1)
+
+#endif
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index f7c226ab4307..423e975dffee 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -177,7 +177,7 @@ EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
int qcom_cc_really_probe(struct platform_device *pdev,
const struct qcom_cc_desc *desc, struct regmap *regmap)
{
- int i, ret;
+ int i = 0, ret, j = 0;
struct device *dev = &pdev->dev;
struct clk *clk;
struct clk_onecell_data *data;
@@ -187,8 +187,10 @@ int qcom_cc_really_probe(struct platform_device *pdev,
struct gdsc_desc *scd;
size_t num_clks = desc->num_clks;
struct clk_regmap **rclks = desc->clks;
+ struct clk_hw **hw_clks = desc->hwclks;
- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
+ cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) *
+ (num_clks + desc->num_hwclks),
GFP_KERNEL);
if (!cc)
return -ENOMEM;
@@ -196,17 +198,32 @@ int qcom_cc_really_probe(struct platform_device *pdev,
clks = cc->clks;
data = &cc->data;
data->clks = clks;
- data->clk_num = num_clks;
+ data->clk_num = num_clks + desc->num_hwclks;
- for (i = 0; i < num_clks; i++) {
- if (!rclks[i]) {
+ for (i = 0; i < desc->num_hwclks; i++) {
+ if (!hw_clks[i]) {
clks[i] = ERR_PTR(-ENOENT);
continue;
}
- clk = devm_clk_register_regmap(dev, rclks[i]);
+ clk = devm_clk_register(dev, hw_clks[i]);
if (IS_ERR(clk))
return PTR_ERR(clk);
clks[i] = clk;
+ pr_debug("Index for hw_clocks %d added %s\n", i,
+ __clk_get_name(clk));
+ }
+
+ for (j = i; j < num_clks; j++) {
+ if (!rclks[j]) {
+ clks[j] = ERR_PTR(-ENOENT);
+ continue;
+ }
+ clk = devm_clk_register_regmap(dev, rclks[j]);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ clks[j] = clk;
+ pr_debug("Index for Regmap clocks %d added %s\n", j,
+ __clk_get_name(clk));
}
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 10cabca921be..e3f450533470 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -25,7 +25,9 @@ struct parent_map;
struct qcom_cc_desc {
const struct regmap_config *config;
struct clk_regmap **clks;
+ struct clk_hw **hwclks;
size_t num_clks;
+ size_t num_hwclks;
const struct qcom_reset_map *resets;
size_t num_resets;
struct gdsc **gdscs;
diff --git a/drivers/clk/qcom/gcc-msmfalcon.c b/drivers/clk/qcom/gcc-msmfalcon.c
index d353cc9ade73..2cbc9dff047b 100644
--- a/drivers/clk/qcom/gcc-msmfalcon.c
+++ b/drivers/clk/qcom/gcc-msmfalcon.c
@@ -2527,6 +2527,32 @@ static struct clk_branch hlos1_vote_lpass_adsp_smmu_clk = {
},
};
+static struct clk_branch hlos1_vote_turing_adsp_smmu_clk = {
+ .halt_reg = 0x7d048,
+ .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE,
+ .clkr = {
+ .enable_reg = 0x7d048,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "hlos1_vote_turing_adsp_smmu_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch hlos2_vote_turing_adsp_smmu_clk = {
+ .halt_reg = 0x7e048,
+ .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE,
+ .clkr = {
+ .enable_reg = 0x7e048,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "hlos2_vote_turing_adsp_smmu_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
static struct clk_fixed_factor gcc_ce1_ahb_m_clk = {
.hw.init = &(struct clk_init_data){
.name = "gcc_ce1_ahb_m_clk",
@@ -2683,6 +2709,10 @@ static struct clk_regmap *gcc_falcon_clocks[] = {
[GCC_UFS_ICE_CORE_HW_CTL_CLK] = &gcc_ufs_ice_core_hw_ctl_clk.clkr,
[GCC_UFS_PHY_AUX_HW_CTL_CLK] = &gcc_ufs_phy_aux_hw_ctl_clk.clkr,
[GCC_UFS_UNIPRO_CORE_HW_CTL_CLK] = &gcc_ufs_unipro_core_hw_ctl_clk.clkr,
+ [HLOS1_VOTE_TURING_ADSP_SMMU_CLK] =
+ &hlos1_vote_turing_adsp_smmu_clk.clkr,
+ [HLOS2_VOTE_TURING_ADSP_SMMU_CLK] =
+ &hlos2_vote_turing_adsp_smmu_clk.clkr,
};
static const struct qcom_reset_map gcc_falcon_resets[] = {
@@ -2709,6 +2739,8 @@ static const struct qcom_cc_desc gcc_falcon_desc = {
.config = &gcc_falcon_regmap_config,
.clks = gcc_falcon_clocks,
.num_clks = ARRAY_SIZE(gcc_falcon_clocks),
+ .hwclks = gcc_msmfalcon_hws,
+ .num_hwclks = ARRAY_SIZE(gcc_msmfalcon_hws),
.resets = gcc_falcon_resets,
.num_resets = ARRAY_SIZE(gcc_falcon_resets),
};
@@ -2735,13 +2767,6 @@ static int gcc_falcon_probe(struct platform_device *pdev)
*/
regmap_update_bits(regmap, 0x52008, BIT(21), BIT(21));
- /* register hardware clocks */
- for (i = 0; i < ARRAY_SIZE(gcc_msmfalcon_hws); i++) {
- clk = devm_clk_register(&pdev->dev, gcc_msmfalcon_hws[i]);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
- }
-
vdd_dig.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig");
if (IS_ERR(vdd_dig.regulator[0])) {
if (!(PTR_ERR(vdd_dig.regulator[0]) == -EPROBE_DEFER))
diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c
index 010f9defe33e..a3c826e152e1 100644
--- a/drivers/devfreq/governor_memlat.c
+++ b/drivers/devfreq/governor_memlat.c
@@ -81,6 +81,29 @@ show_attr(__attr) \
store_attr(__attr, min, max) \
static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr)
+static ssize_t show_map(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct devfreq *df = to_devfreq(dev);
+ struct memlat_node *n = df->data;
+ struct core_dev_map *map = n->hw->freq_map;
+ unsigned int cnt = 0;
+
+ cnt += snprintf(buf, PAGE_SIZE, "Core freq (MHz)\tDevice BW\n");
+
+ while (map->core_mhz && cnt < PAGE_SIZE) {
+ cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "%15u\t%9u\n",
+ map->core_mhz, map->target_freq);
+ map++;
+ }
+ if (cnt < PAGE_SIZE)
+ cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\n");
+
+ return cnt;
+}
+
+static DEVICE_ATTR(freq_map, 0444, show_map, NULL);
+
static unsigned long core_to_dev_freq(struct memlat_node *node,
unsigned long coref)
{
@@ -247,6 +270,7 @@ gov_attr(ratio_ceil, 1U, 10000U);
static struct attribute *dev_attr[] = {
&dev_attr_ratio_ceil.attr,
+ &dev_attr_freq_map.attr,
NULL,
};
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index e9d16426d4a5..94d828027f20 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1243,86 +1243,6 @@ static bool regulators_left_on(struct kgsl_device *device)
return false;
}
-static void _setup_throttling_counters(struct adreno_device *adreno_dev)
-{
- int i, ret;
-
- if (!adreno_is_a540(adreno_dev))
- return;
-
- if (!ADRENO_FEATURE(adreno_dev, ADRENO_GPMU))
- return;
-
- for (i = 0; i < ADRENO_GPMU_THROTTLE_COUNTERS; i++) {
- /* reset throttled cycles ivalue */
- adreno_dev->busy_data.throttle_cycles[i] = 0;
-
- if (adreno_dev->gpmu_throttle_counters[i] != 0)
- continue;
- ret = adreno_perfcounter_get(adreno_dev,
- KGSL_PERFCOUNTER_GROUP_GPMU_PWR,
- ADRENO_GPMU_THROTTLE_COUNTERS_BASE_REG + i,
- &adreno_dev->gpmu_throttle_counters[i],
- NULL,
- PERFCOUNTER_FLAG_KERNEL);
- WARN_ONCE(ret, "Unable to get clock throttling counter %x\n",
- ADRENO_GPMU_THROTTLE_COUNTERS_BASE_REG + i);
- }
-}
-
-/* FW driven idle 10% throttle */
-#define IDLE_10PCT 0
-/* number of cycles when clock is throttled by 50% (CRC) */
-#define CRC_50PCT 1
-/* number of cycles when clock is throttled by more than 50% (CRC) */
-#define CRC_MORE50PCT 2
-/* number of cycles when clock is throttle by less than 50% (CRC) */
-#define CRC_LESS50PCT 3
-
-static uint64_t _read_throttling_counters(struct adreno_device *adreno_dev)
-{
- int i, adj;
- uint32_t th[ADRENO_GPMU_THROTTLE_COUNTERS];
- struct adreno_busy_data *busy = &adreno_dev->busy_data;
-
- if (!adreno_is_a540(adreno_dev))
- return 0;
-
- if (!ADRENO_FEATURE(adreno_dev, ADRENO_GPMU))
- return 0;
-
- if (!test_bit(ADRENO_THROTTLING_CTRL, &adreno_dev->pwrctrl_flag))
- return 0;
-
- for (i = 0; i < ADRENO_GPMU_THROTTLE_COUNTERS; i++) {
- if (!adreno_dev->gpmu_throttle_counters[i])
- return 0;
-
- th[i] = counter_delta(KGSL_DEVICE(adreno_dev),
- adreno_dev->gpmu_throttle_counters[i],
- &busy->throttle_cycles[i]);
- }
- adj = th[CRC_MORE50PCT] - th[IDLE_10PCT];
- adj = th[CRC_50PCT] + th[CRC_LESS50PCT] / 3 + (adj < 0 ? 0 : adj) * 3;
-
- trace_kgsl_clock_throttling(
- th[IDLE_10PCT], th[CRC_50PCT],
- th[CRC_MORE50PCT], th[CRC_LESS50PCT],
- adj);
- return adj;
-}
-
-static void _update_threshold_count(struct adreno_device *adreno_dev,
- uint64_t adj)
-{
- if (adreno_is_a530(adreno_dev))
- kgsl_regread(KGSL_DEVICE(adreno_dev),
- adreno_dev->lm_threshold_count,
- &adreno_dev->lm_threshold_cross);
- else if (adreno_is_a540(adreno_dev))
- adreno_dev->lm_threshold_cross = adj;
-}
-
static void _set_secvid(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
@@ -1419,8 +1339,8 @@ static int _adreno_start(struct adreno_device *adreno_dev)
}
}
- if (device->pwrctrl.bus_control) {
+ if (device->pwrctrl.bus_control) {
/* VBIF waiting for RAM */
if (adreno_dev->starved_ram_lo == 0) {
ret = adreno_perfcounter_get(adreno_dev,
@@ -1456,20 +1376,6 @@ static int _adreno_start(struct adreno_device *adreno_dev)
adreno_dev->busy_data.vbif_ram_cycles = 0;
adreno_dev->busy_data.vbif_starved_ram = 0;
- if (adreno_is_a530(adreno_dev) && ADRENO_FEATURE(adreno_dev, ADRENO_LM)
- && adreno_dev->lm_threshold_count == 0) {
-
- ret = adreno_perfcounter_get(adreno_dev,
- KGSL_PERFCOUNTER_GROUP_GPMU_PWR, 27,
- &adreno_dev->lm_threshold_count, NULL,
- PERFCOUNTER_FLAG_KERNEL);
- /* Ignore noncritical ret - used for debugfs */
- if (ret)
- adreno_dev->lm_threshold_count = 0;
- }
-
- _setup_throttling_counters(adreno_dev);
-
/* Restore performance counter registers with saved values */
adreno_perfcounter_restore(adreno_dev);
@@ -2576,27 +2482,6 @@ static inline s64 adreno_ticks_to_us(u32 ticks, u32 freq)
return ticks / freq;
}
-static unsigned int counter_delta(struct kgsl_device *device,
- unsigned int reg, unsigned int *counter)
-{
- unsigned int val;
- unsigned int ret = 0;
-
- /* Read the value */
- kgsl_regread(device, reg, &val);
-
- /* Return 0 for the first read */
- if (*counter != 0) {
- if (val < *counter)
- ret = (0xFFFFFFFF - *counter) + val;
- else
- ret = val - *counter;
- }
-
- *counter = val;
- return ret;
-}
-
/**
* adreno_power_stats() - Reads the counters needed for freq decisions
* @device: Pointer to device whose counters are read
@@ -2608,6 +2493,7 @@ static void adreno_power_stats(struct kgsl_device *device,
struct kgsl_power_stats *stats)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
struct adreno_busy_data *busy = &adreno_dev->busy_data;
uint64_t adj = 0;
@@ -2621,8 +2507,11 @@ static void adreno_power_stats(struct kgsl_device *device,
gpu_busy = counter_delta(device, adreno_dev->perfctr_pwr_lo,
&busy->gpu_busy);
- adj = _read_throttling_counters(adreno_dev);
- gpu_busy += adj;
+ if (gpudev->read_throttling_counters) {
+ adj = gpudev->read_throttling_counters(adreno_dev);
+ gpu_busy += adj;
+ }
+
stats->busy_time = adreno_ticks_to_us(gpu_busy,
kgsl_pwrctrl_active_freq(pwr));
}
@@ -2643,8 +2532,9 @@ static void adreno_power_stats(struct kgsl_device *device,
stats->ram_time = ram_cycles;
stats->ram_wait = starved_ram;
}
- if (adreno_dev->lm_threshold_count)
- _update_threshold_count(adreno_dev, adj);
+ if (adreno_dev->lm_threshold_count &&
+ gpudev->count_throttles)
+ gpudev->count_throttles(adreno_dev, adj);
}
static unsigned int adreno_gpuid(struct kgsl_device *device,
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 295a3d80d476..0f3403cb0095 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -756,6 +756,10 @@ struct adreno_gpudev {
void (*pwrlevel_change_settings)(struct adreno_device *,
unsigned int prelevel, unsigned int postlevel,
bool post);
+ uint64_t (*read_throttling_counters)(struct adreno_device *);
+ void (*count_throttles)(struct adreno_device *, uint64_t adj);
+ int (*enable_pwr_counters)(struct adreno_device *,
+ unsigned int counter);
unsigned int (*preemption_pre_ibsubmit)(struct adreno_device *,
struct adreno_ringbuffer *rb,
unsigned int *, struct kgsl_context *);
@@ -1466,4 +1470,24 @@ static inline void adreno_ringbuffer_set_pagetable(struct adreno_ringbuffer *rb,
spin_unlock_irqrestore(&rb->preempt_lock, flags);
}
+static inline unsigned int counter_delta(struct kgsl_device *device,
+ unsigned int reg, unsigned int *counter)
+{
+ unsigned int val;
+ unsigned int ret = 0;
+
+ /* Read the value */
+ kgsl_regread(device, reg, &val);
+
+ /* Return 0 for the first read */
+ if (*counter != 0) {
+ if (val < *counter)
+ ret = (0xFFFFFFFF - *counter) + val;
+ else
+ ret = val - *counter;
+ }
+
+ *counter = val;
+ return ret;
+}
#endif /*__ADRENO_H */
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index e67bb92c0c28..2891940b8f5b 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -27,6 +27,7 @@
#include "kgsl_sharedmem.h"
#include "kgsl_log.h"
#include "kgsl.h"
+#include "kgsl_trace.h"
#include "adreno_a5xx_packets.h"
static int zap_ucode_loaded;
@@ -1543,6 +1544,76 @@ static void a5xx_clk_set_options(struct adreno_device *adreno_dev,
}
}
+static void a5xx_count_throttles(struct adreno_device *adreno_dev,
+ uint64_t adj)
+{
+ if (adreno_is_a530(adreno_dev))
+ kgsl_regread(KGSL_DEVICE(adreno_dev),
+ adreno_dev->lm_threshold_count,
+ &adreno_dev->lm_threshold_cross);
+ else if (adreno_is_a540(adreno_dev))
+ adreno_dev->lm_threshold_cross = adj;
+}
+
+static int a5xx_enable_pwr_counters(struct adreno_device *adreno_dev,
+ unsigned int counter)
+{
+ /*
+ * On 5XX we have to emulate the PWR counters which are physically
+ * missing. Program countable 6 on RBBM_PERFCTR_RBBM_0 as a substitute
+ * for PWR:1. Don't emulate PWR:0 as nobody uses it and we don't want
+ * to take away too many of the generic RBBM counters.
+ */
+
+ if (counter == 0)
+ return -EINVAL;
+
+ kgsl_regwrite(KGSL_DEVICE(adreno_dev), A5XX_RBBM_PERFCTR_RBBM_SEL_0, 6);
+
+ return 0;
+}
+
+/* FW driven idle 10% throttle */
+#define IDLE_10PCT 0
+/* number of cycles when clock is throttled by 50% (CRC) */
+#define CRC_50PCT 1
+/* number of cycles when clock is throttled by more than 50% (CRC) */
+#define CRC_MORE50PCT 2
+/* number of cycles when clock is throttle by less than 50% (CRC) */
+#define CRC_LESS50PCT 3
+
+static uint64_t a5xx_read_throttling_counters(struct adreno_device *adreno_dev)
+{
+ int i, adj;
+ uint32_t th[ADRENO_GPMU_THROTTLE_COUNTERS];
+ struct adreno_busy_data *busy = &adreno_dev->busy_data;
+
+ if (!adreno_is_a540(adreno_dev))
+ return 0;
+
+ if (!ADRENO_FEATURE(adreno_dev, ADRENO_GPMU))
+ return 0;
+
+ if (!test_bit(ADRENO_THROTTLING_CTRL, &adreno_dev->pwrctrl_flag))
+ return 0;
+
+ for (i = 0; i < ADRENO_GPMU_THROTTLE_COUNTERS; i++) {
+ if (!adreno_dev->gpmu_throttle_counters[i])
+ return 0;
+
+ th[i] = counter_delta(KGSL_DEVICE(adreno_dev),
+ adreno_dev->gpmu_throttle_counters[i],
+ &busy->throttle_cycles[i]);
+ }
+ adj = th[CRC_MORE50PCT] - th[IDLE_10PCT];
+ adj = th[CRC_50PCT] + th[CRC_LESS50PCT] / 3 + (adj < 0 ? 0 : adj) * 3;
+
+ trace_kgsl_clock_throttling(
+ th[IDLE_10PCT], th[CRC_50PCT],
+ th[CRC_MORE50PCT], th[CRC_LESS50PCT],
+ adj);
+ return adj;
+}
static void a5xx_enable_64bit(struct adreno_device *adreno_dev)
{
@@ -1599,12 +1670,44 @@ static void a5xx_gpmu_reset(struct work_struct *work)
/* Soft reset of the GPMU block */
kgsl_regwrite(device, A5XX_RBBM_BLOCK_SW_RESET_CMD, BIT(16));
+ /* GPU comes up in secured mode, make it unsecured by default */
+ if (!ADRENO_FEATURE(adreno_dev, ADRENO_CONTENT_PROTECTION))
+ kgsl_regwrite(device, A5XX_RBBM_SECVID_TRUST_CNTL, 0x0);
+
+
a5xx_gpmu_init(adreno_dev);
out:
mutex_unlock(&device->mutex);
}
+static void _setup_throttling_counters(struct adreno_device *adreno_dev)
+{
+ int i, ret;
+
+ if (!adreno_is_a540(adreno_dev))
+ return;
+
+ if (!ADRENO_FEATURE(adreno_dev, ADRENO_GPMU))
+ return;
+
+ for (i = 0; i < ADRENO_GPMU_THROTTLE_COUNTERS; i++) {
+ /* reset throttled cycles ivalue */
+ adreno_dev->busy_data.throttle_cycles[i] = 0;
+
+ if (adreno_dev->gpmu_throttle_counters[i] != 0)
+ continue;
+ ret = adreno_perfcounter_get(adreno_dev,
+ KGSL_PERFCOUNTER_GROUP_GPMU_PWR,
+ ADRENO_GPMU_THROTTLE_COUNTERS_BASE_REG + i,
+ &adreno_dev->gpmu_throttle_counters[i],
+ NULL,
+ PERFCOUNTER_FLAG_KERNEL);
+ WARN_ONCE(ret, "Unable to get clock throttling counter %x\n",
+ ADRENO_GPMU_THROTTLE_COUNTERS_BASE_REG + i);
+ }
+}
+
/*
* a5xx_start() - Device start
* @adreno_dev: Pointer to adreno device
@@ -1616,6 +1719,21 @@ static void a5xx_start(struct adreno_device *adreno_dev)
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
unsigned int bit;
+ int ret;
+
+ if (adreno_is_a530(adreno_dev) && ADRENO_FEATURE(adreno_dev, ADRENO_LM)
+ && adreno_dev->lm_threshold_count == 0) {
+
+ ret = adreno_perfcounter_get(adreno_dev,
+ KGSL_PERFCOUNTER_GROUP_GPMU_PWR, 27,
+ &adreno_dev->lm_threshold_count, NULL,
+ PERFCOUNTER_FLAG_KERNEL);
+ /* Ignore noncritical ret - used for debugfs */
+ if (ret)
+ adreno_dev->lm_threshold_count = 0;
+ }
+
+ _setup_throttling_counters(adreno_dev);
adreno_vbif_start(adreno_dev, a5xx_vbif_platforms,
ARRAY_SIZE(a5xx_vbif_platforms));
@@ -1921,11 +2039,6 @@ static int a5xx_post_start(struct adreno_device *adreno_dev)
static int a5xx_gpmu_init(struct adreno_device *adreno_dev)
{
int ret;
- struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
-
- /* GPU comes up in secured mode, make it unsecured by default */
- if (!ADRENO_FEATURE(adreno_dev, ADRENO_CONTENT_PROTECTION))
- kgsl_regwrite(device, A5XX_RBBM_SECVID_TRUST_CNTL, 0x0);
/* Set up LM before initializing the GPMU */
a5xx_lm_init(adreno_dev);
@@ -2246,20 +2359,10 @@ static int a5xx_rb_start(struct adreno_device *adreno_dev,
if (ret)
return ret;
- /* Set up LM before initializing the GPMU */
- a5xx_lm_init(adreno_dev);
-
- /* Enable SPTP based power collapse before enabling GPMU */
- a5xx_enable_pc(adreno_dev);
-
- /* Program the GPMU */
- ret = a5xx_gpmu_start(adreno_dev);
+ ret = a5xx_gpmu_init(adreno_dev);
if (ret)
return ret;
- /* Enable limits management */
- a5xx_lm_enable(adreno_dev);
-
a5xx_post_start(adreno_dev);
return 0;
@@ -3421,6 +3524,9 @@ struct adreno_gpudev adreno_a5xx_gpudev = {
.regulator_enable = a5xx_regulator_enable,
.regulator_disable = a5xx_regulator_disable,
.pwrlevel_change_settings = a5xx_pwrlevel_change_settings,
+ .read_throttling_counters = a5xx_read_throttling_counters,
+ .count_throttles = a5xx_count_throttles,
+ .enable_pwr_counters = a5xx_enable_pwr_counters,
.preemption_pre_ibsubmit = a5xx_preemption_pre_ibsubmit,
.preemption_yield_enable =
a5xx_preemption_yield_enable,
diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c
index aeffeab2f6dc..c09d2f8c1947 100644
--- a/drivers/gpu/msm/adreno_a5xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c
@@ -410,8 +410,6 @@ static const unsigned int a5xx_registers[] = {
0xEA80, 0xEA80, 0xEA82, 0xEAA3, 0xEAA5, 0xEAC2,
/* GPMU */
0xA800, 0xA8FF, 0xAC60, 0xAC60,
- /* DPM */
- 0xB000, 0xB97F, 0xB9A0, 0xB9BF,
};
struct a5xx_hlsq_sp_tp_regs {
diff --git a/drivers/gpu/msm/adreno_perfcounter.c b/drivers/gpu/msm/adreno_perfcounter.c
index 8e354d71a291..42f8119ad8b4 100644
--- a/drivers/gpu/msm/adreno_perfcounter.c
+++ b/drivers/gpu/msm/adreno_perfcounter.c
@@ -598,28 +598,6 @@ int adreno_perfcounter_put(struct adreno_device *adreno_dev,
return -EINVAL;
}
-static int _perfcounter_enable_pwr(struct adreno_device *adreno_dev,
- unsigned int counter)
-{
- /* PWR counters enabled by default on A3XX/A4XX so nothing to do */
- if (adreno_is_a3xx(adreno_dev) || adreno_is_a4xx(adreno_dev))
- return 0;
-
- /*
- * On 5XX we have to emulate the PWR counters which are physically
- * missing. Program countable 6 on RBBM_PERFCTR_RBBM_0 as a substitute
- * for PWR:1. Don't emulate PWR:0 as nobody uses it and we don't want
- * to take away too many of the generic RBBM counters.
- */
-
- if (counter == 0)
- return -EINVAL;
-
- kgsl_regwrite(KGSL_DEVICE(adreno_dev), A5XX_RBBM_PERFCTR_RBBM_SEL_0, 6);
-
- return 0;
-}
-
static void _perfcounter_enable_vbif(struct adreno_device *adreno_dev,
struct adreno_perfcounters *counters, unsigned int counter,
unsigned int countable)
@@ -771,6 +749,7 @@ static int adreno_perfcounter_enable(struct adreno_device *adreno_dev,
unsigned int group, unsigned int counter, unsigned int countable)
{
struct adreno_perfcounters *counters = ADRENO_PERFCOUNTERS(adreno_dev);
+ struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
if (counters == NULL)
return -EINVAL;
@@ -786,7 +765,9 @@ static int adreno_perfcounter_enable(struct adreno_device *adreno_dev,
/* alwayson counter is global, so init value is 0 */
break;
case KGSL_PERFCOUNTER_GROUP_PWR:
- return _perfcounter_enable_pwr(adreno_dev, counter);
+ if (gpudev->enable_pwr_counters)
+ return gpudev->enable_pwr_counters(adreno_dev, counter);
+ return 0;
case KGSL_PERFCOUNTER_GROUP_VBIF:
if (countable > VBIF2_PERF_CNT_SEL_MASK)
return -EINVAL;
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 31369d8c0ef3..9c1380b65b77 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -823,4 +823,6 @@ config INPUT_DRV2667_HAPTICS
To compile this driver as a module, choose M here: the
module will be called drv2667-haptics.
+source "drivers/input/misc/ots_pat9125/Kconfig"
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 4019f19dd848..4e806ac056ce 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
+obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/
diff --git a/drivers/input/misc/ots_pat9125/Kconfig b/drivers/input/misc/ots_pat9125/Kconfig
new file mode 100644
index 000000000000..af82edd0faae
--- /dev/null
+++ b/drivers/input/misc/ots_pat9125/Kconfig
@@ -0,0 +1,14 @@
+#
+# PixArt OTS switch driver configuration
+#
+
+config INPUT_PIXART_OTS_PAT9125_SWITCH
+ tristate "PixArt PAT9125 Rotating Switch driver"
+ depends on INPUT && I2C && GPIOLIB
+ help
+ Say Y to enable support for the PixArt OTS pat9125
+ rotating switch driver.
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ots_pat9125.
diff --git a/drivers/input/misc/ots_pat9125/Makefile b/drivers/input/misc/ots_pat9125/Makefile
new file mode 100644
index 000000000000..a697caf69644
--- /dev/null
+++ b/drivers/input/misc/ots_pat9125/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the PixArt OST switch driver.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += pat9125_linux_driver.o pixart_ots.o
diff --git a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
index e5edaf5f908d..ac4caa48312d 100644
--- a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
+++ b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
@@ -11,13 +11,21 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of_gpio.h>
+#include <linux/delay.h>
#include "pixart_ots.h"
struct pixart_pat9125_data {
struct i2c_client *client;
struct input_dev *input;
int irq_gpio;
- u32 irq_flags;
+ u32 press_keycode;
+ bool press_en;
+ bool inverse_x;
+ bool inverse_y;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pinctrl_state_active;
+ struct pinctrl_state *pinctrl_state_suspend;
+ struct pinctrl_state *pinctrl_state_release;
};
static int pat9125_i2c_write(struct i2c_client *client, u8 reg, u8 *data,
@@ -70,7 +78,7 @@ static int pat9125_i2c_read(struct i2c_client *client, u8 reg, u8 *data)
return ret;
}
-unsigned char read_data(struct i2c_client *client, u8 addr)
+u8 read_data(struct i2c_client *client, u8 addr)
{
u8 data = 0xff;
@@ -83,8 +91,55 @@ void write_data(struct i2c_client *client, u8 addr, u8 data)
pat9125_i2c_write(client, addr, &data, 1);
}
-static irqreturn_t pixart_pat9125_irq(int irq, void *data)
+static irqreturn_t pat9125_irq(int irq, void *dev_data)
{
+ u8 delta_x = 0, delta_y = 0, motion;
+ struct pixart_pat9125_data *data = dev_data;
+ struct input_dev *ipdev = data->input;
+ struct device *dev = &data->client->dev;
+
+ motion = read_data(data->client, PIXART_PAT9125_MOTION_STATUS_REG);
+ do {
+ /* check if MOTION bit is set or not */
+ if (motion & PIXART_PAT9125_VALID_MOTION_DATA) {
+ delta_x = read_data(data->client,
+ PIXART_PAT9125_DELTA_X_LO_REG);
+ delta_y = read_data(data->client,
+ PIXART_PAT9125_DELTA_Y_LO_REG);
+
+ /* Inverse x depending upon the device orientation */
+ delta_x = (data->inverse_x) ? -delta_x : delta_x;
+ /* Inverse y depending upon the device orientation */
+ delta_y = (data->inverse_y) ? -delta_y : delta_y;
+ }
+
+ dev_dbg(dev, "motion = %x, delta_x = %x, delta_y = %x\n",
+ motion, delta_x, delta_y);
+
+ if (delta_x != 0) {
+ /* Send delta_x as REL_WHEEL for rotation */
+ input_report_rel(ipdev, REL_WHEEL, (s8) delta_x);
+ input_sync(ipdev);
+ }
+
+ if (data->press_en && delta_y != 0) {
+ if ((s8) delta_y > 0) {
+ /* Send DOWN event for press keycode */
+ input_report_key(ipdev, data->press_keycode, 1);
+ input_sync(ipdev);
+ } else {
+ /* Send UP event for press keycode */
+ input_report_key(ipdev, data->press_keycode, 0);
+ input_sync(ipdev);
+ }
+ }
+ usleep_range(PIXART_SAMPLING_PERIOD_US_MIN,
+ PIXART_SAMPLING_PERIOD_US_MAX);
+
+ motion = read_data(data->client,
+ PIXART_PAT9125_MOTION_STATUS_REG);
+ } while (motion & PIXART_PAT9125_VALID_MOTION_DATA);
+
return IRQ_HANDLED;
}
@@ -145,13 +200,77 @@ static struct attribute_group pat9125_attr_grp = {
.attrs = pat9125_attr_list,
};
+static int pixart_pinctrl_init(struct pixart_pat9125_data *data)
+{
+ int err;
+ struct device *dev = &data->client->dev;
+
+ data->pinctrl = devm_pinctrl_get(&(data->client->dev));
+ if (IS_ERR_OR_NULL(data->pinctrl)) {
+ err = PTR_ERR(data->pinctrl);
+ dev_err(dev, "Target does not use pinctrl %d\n", err);
+ return err;
+ }
+
+ data->pinctrl_state_active = pinctrl_lookup_state(data->pinctrl,
+ PINCTRL_STATE_ACTIVE);
+ if (IS_ERR_OR_NULL(data->pinctrl_state_active)) {
+ err = PTR_ERR(data->pinctrl_state_active);
+ dev_err(dev, "Can not lookup active pinctrl state %d\n", err);
+ return err;
+ }
+
+ data->pinctrl_state_suspend = pinctrl_lookup_state(data->pinctrl,
+ PINCTRL_STATE_SUSPEND);
+ if (IS_ERR_OR_NULL(data->pinctrl_state_suspend)) {
+ err = PTR_ERR(data->pinctrl_state_suspend);
+ dev_err(dev, "Can not lookup suspend pinctrl state %d\n", err);
+ return err;
+ }
+
+ data->pinctrl_state_release = pinctrl_lookup_state(data->pinctrl,
+ PINCTRL_STATE_RELEASE);
+ if (IS_ERR_OR_NULL(data->pinctrl_state_release)) {
+ err = PTR_ERR(data->pinctrl_state_release);
+ dev_err(dev, "Can not lookup release pinctrl state %d\n", err);
+ return err;
+ }
+ return 0;
+}
+
+static int pat9125_parse_dt(struct device *dev,
+ struct pixart_pat9125_data *data)
+{
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+ int ret;
+
+ data->inverse_x = of_property_read_bool(np, "pixart,inverse-x");
+ data->inverse_y = of_property_read_bool(np, "pixart,inverse-y");
+ data->press_en = of_property_read_bool(np, "pixart,press-enabled");
+ if (data->press_en) {
+ ret = of_property_read_u32(np, "pixart,press-keycode",
+ &temp_val);
+ if (!ret) {
+ data->press_keycode = temp_val;
+ } else {
+ dev_err(dev, "Unable to parse press-keycode\n");
+ return ret;
+ }
+ }
+
+ data->irq_gpio = of_get_named_gpio_flags(np, "pixart,irq-gpio",
+ 0, NULL);
+
+ return 0;
+}
+
static int pat9125_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err = 0;
struct pixart_pat9125_data *data;
struct input_dev *input;
- struct device_node *np;
struct device *dev = &client->dev;
err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE);
@@ -165,6 +284,11 @@ static int pat9125_i2c_probe(struct i2c_client *client,
GFP_KERNEL);
if (!data)
return -ENOMEM;
+ err = pat9125_parse_dt(dev, data);
+ if (err) {
+ dev_err(dev, "DT parsing failed, errno:%d\n", err);
+ return err;
+ }
} else {
data = client->dev.platform_data;
if (!data) {
@@ -180,6 +304,10 @@ static int pat9125_i2c_probe(struct i2c_client *client,
return -ENOMEM;
}
+ input_set_capability(input, EV_REL, REL_WHEEL);
+ if (data->press_en)
+ input_set_capability(input, EV_KEY, data->press_keycode);
+
i2c_set_clientdata(client, data);
input_set_drvdata(input, data);
input->name = PAT9125_DEV_NAME;
@@ -188,24 +316,39 @@ static int pat9125_i2c_probe(struct i2c_client *client,
err = input_register_device(data->input);
if (err < 0) {
dev_err(dev, "Failed to register input device\n");
- goto err_register_input_device;
- }
-
- if (!gpio_is_valid(data->irq_gpio)) {
- dev_err(dev, "invalid irq_gpio: %d\n", data->irq_gpio);
- return -EINVAL;
- }
-
- err = gpio_request(data->irq_gpio, "pixart_pat9125_irq_gpio");
- if (err) {
- dev_err(dev, "unable to request gpio %d\n", data->irq_gpio);
return err;
}
- err = gpio_direction_input(data->irq_gpio);
- if (err) {
- dev_err(dev, "unable to set dir for gpio %d\n", data->irq_gpio);
- goto free_gpio;
+ err = pixart_pinctrl_init(data);
+ if (!err && data->pinctrl) {
+ /*
+ * Pinctrl handle is optional. If pinctrl handle is found
+ * let pins to be configured in active state. If not
+ * found continue further without error.
+ */
+ err = pinctrl_select_state(data->pinctrl,
+ data->pinctrl_state_active);
+ if (err < 0)
+ dev_err(dev, "Could not set pin to active state %d\n",
+ err);
+ } else {
+ if (gpio_is_valid(data->irq_gpio)) {
+ err = devm_gpio_request(dev, data->irq_gpio,
+ "pixart_pat9125_irq_gpio");
+ if (err) {
+ dev_err(dev, "Couldn't request gpio %d\n", err);
+ return err;
+ }
+ err = gpio_direction_input(data->irq_gpio);
+ if (err) {
+ dev_err(dev, "Couldn't set dir for gpio %d\n",
+ err);
+ return err;
+ }
+ } else {
+ dev_err(dev, "Invalid gpio %d\n", data->irq_gpio);
+ return -EINVAL;
+ }
}
if (!ots_sensor_init(client)) {
@@ -213,8 +356,8 @@ static int pat9125_i2c_probe(struct i2c_client *client,
goto err_sensor_init;
}
- err = devm_request_threaded_irq(dev, client->irq, NULL,
- pixart_pat9125_irq, (unsigned long)data->irq_flags,
+ err = devm_request_threaded_irq(dev, client->irq, NULL, pat9125_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW,
"pixart_pat9125_irq", data);
if (err) {
dev_err(dev, "Req irq %d failed, errno:%d\n", client->irq, err);
@@ -232,33 +375,59 @@ static int pat9125_i2c_probe(struct i2c_client *client,
err_sysfs_create:
err_request_threaded_irq:
err_sensor_init:
-free_gpio:
- gpio_free(data->irq_gpio);
-err_register_input_device:
- input_free_device(data->input);
+ if (data->pinctrl)
+ if (pinctrl_select_state(data->pinctrl,
+ data->pinctrl_state_release) < 0)
+ dev_err(dev, "Couldn't set pin to release state\n");
+
return err;
}
static int pat9125_i2c_remove(struct i2c_client *client)
{
struct pixart_pat9125_data *data = i2c_get_clientdata(client);
+ struct device *dev = &data->client->dev;
- devm_free_irq(&client->dev, client->irq, data);
- if (gpio_is_valid(data->irq_gpio))
- gpio_free(data->irq_gpio);
- input_unregister_device(data->input);
- devm_kfree(&client->dev, data);
- data = NULL;
+ if (data->pinctrl)
+ if (pinctrl_select_state(data->pinctrl,
+ data->pinctrl_state_release) < 0)
+ dev_err(dev, "Couldn't set pin to release state\n");
return 0;
}
static int pat9125_suspend(struct device *dev)
{
+ int rc;
+ struct pixart_pat9125_data *data =
+ (struct pixart_pat9125_data *)dev->driver_data;
+
+ disable_irq(data->client->irq);
+ if (data->pinctrl) {
+ rc = pinctrl_select_state(data->pinctrl,
+ data->pinctrl_state_suspend);
+ if (rc < 0)
+ dev_err(dev, "Could not set pin to suspend state %d\n",
+ rc);
+ }
+
return 0;
}
static int pat9125_resume(struct device *dev)
{
+ int rc;
+ struct pixart_pat9125_data *data =
+ (struct pixart_pat9125_data *)dev->driver_data;
+
+ if (data->pinctrl) {
+ rc = pinctrl_select_state(data->pinctrl,
+ data->pinctrl_state_active);
+ if (rc < 0)
+ dev_err(dev, "Could not set pin to active state %d\n",
+ rc);
+ }
+ enable_irq(data->client->irq);
+
return 0;
}
diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.h b/drivers/input/misc/ots_pat9125/pixart_ots.h
index a66ded5c9d08..824d6bafd9bf 100644
--- a/drivers/input/misc/ots_pat9125/pixart_ots.h
+++ b/drivers/input/misc/ots_pat9125/pixart_ots.h
@@ -10,6 +10,9 @@
#define PAT9125_DEV_NAME "pixart_pat9125"
#define MAX_BUF_SIZE 20
#define RESET_DELAY_US 1000
+#define PINCTRL_STATE_ACTIVE "pmx_rot_switch_active"
+#define PINCTRL_STATE_SUSPEND "pmx_rot_switch_suspend"
+#define PINCTRL_STATE_RELEASE "pmx_rot_switch_release"
/* Register addresses */
#define PIXART_PAT9125_PRODUCT_ID1_REG 0x00
@@ -39,6 +42,9 @@
#define PIXART_PAT9125_LOW_VOLTAGE_SEGMENT 0x04
#define PIXART_PAT9125_VALID_MOTION_DATA 0x80
+#define PIXART_SAMPLING_PERIOD_US_MIN 4000
+#define PIXART_SAMPLING_PERIOD_US_MAX 8000
+
/* Export functions */
bool ots_sensor_init(struct i2c_client *);
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 5e47e2481300..3333f15f7f16 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -363,7 +363,7 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
else
pte |= ARM_LPAE_PTE_TYPE_BLOCK;
- pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_IS;
+ pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_OS;
pte |= pfn_to_iopte(paddr >> data->pg_shift, data);
*ptep = pte;
@@ -481,11 +481,9 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
pte |= (prot & IOMMU_PRIV) ? ARM_LPAE_PTE_AP_PRIV_RO
: ARM_LPAE_PTE_AP_RO;
- if (prot & IOMMU_CACHE) {
+ if (prot & IOMMU_CACHE)
pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
- pte |= ARM_LPAE_PTE_SH_OS;
- }
if (prot & IOMMU_DEVICE)
pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV <<
@@ -942,7 +940,7 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
(ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) |
(ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT);
else
- reg = (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) |
+ reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) |
(ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) |
(ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_ORGN0_SHIFT);
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 0cae5d2e5263..bc94dff08d21 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -48,6 +48,7 @@
#define FLASH_LED_REG_THERMAL_THRSH3(base) (base + 0x58)
#define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61)
#define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62)
+#define FLASH_LED_REG_ILED_GRT_THRSH(base) (base + 0x67)
#define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E)
#define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F)
#define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70)
@@ -62,22 +63,25 @@
#define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0)
#define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0)
#define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0)
+#define FLASH_LED_CHGR_MITIGATION_SEL_MASK GENMASK(5, 4)
#define FLASH_LED_LMH_MITIGATION_SEL_MASK GENMASK(1, 0)
+#define FLASH_LED_ILED_GRT_THRSH_MASK GENMASK(5, 0)
#define FLASH_LED_LMH_LEVEL_MASK GENMASK(1, 0)
#define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4)
#define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0)
#define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0)
#define FLASH_LED_THERMAL_OTST_MASK GENMASK(2, 0)
-#define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(2, 0)
#define FLASH_LED_MOD_CTRL_MASK BIT(7)
#define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2)
#define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4)
#define FLASH_LED_LMH_MITIGATION_EN_MASK BIT(0)
+#define FLASH_LED_CHGR_MITIGATION_EN_MASK BIT(4)
#define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8)
#define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25)
#define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25)
#define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000)
+#define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100)
#define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6
#define FLASH_LED_WARMUP_DELAY_DEFAULT 2
@@ -99,8 +103,13 @@
#define FLASH_LED_LMH_LEVEL_DEFAULT 0
#define FLASH_LED_LMH_MITIGATION_ENABLE 1
#define FLASH_LED_LMH_MITIGATION_DISABLE 0
-#define FLASH_LED_LMH_MITIGATION_SEL_DEFAULT 2
-#define FLASH_LED_LMH_MITIGATION_SEL_MAX 2
+#define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4)
+#define FLASH_LED_CHGR_MITIGATION_DISABLE 0
+#define FLASH_LED_MITIGATION_SEL_DEFAULT 2
+#define FLASH_LED_MITIGATION_SEL_MAX 2
+#define FLASH_LED_CHGR_MITIGATION_SEL_SHIFT 4
+#define FLASH_LED_MITIGATION_THRSH_DEFAULT 0xA
+#define FLASH_LED_MITIGATION_THRSH_MAX 0x1F
#define FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV 3700000
#define FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM 400000
#define FLASH_LED_IRES_BASE 3
@@ -199,7 +208,9 @@ struct flash_led_platform_data {
u8 vph_droop_hysteresis;
u8 vph_droop_debounce;
u8 lmh_mitigation_sel;
+ u8 chgr_mitigation_sel;
u8 lmh_level;
+ u8 iled_thrsh_val;
u8 hw_strobe_option;
bool hdrm_auto_mode_en;
bool thermal_derate_en;
@@ -222,6 +233,7 @@ struct qpnp_flash_led {
int enable;
u16 base;
bool trigger_lmh;
+ bool trigger_chgr;
};
static int
@@ -352,12 +364,26 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
return rc;
rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_MITIGATION_SEL(led->base),
+ FLASH_LED_CHGR_MITIGATION_SEL_MASK,
+ led->pdata->chgr_mitigation_sel);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LMH_LEVEL(led->base),
FLASH_LED_LMH_LEVEL_MASK,
led->pdata->lmh_level);
if (rc < 0)
return rc;
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_ILED_GRT_THRSH(led->base),
+ FLASH_LED_ILED_GRT_THRSH_MASK,
+ led->pdata->iled_thrsh_val);
+ if (rc < 0)
+ return rc;
+
return 0;
}
@@ -739,6 +765,18 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
}
}
+ if (!led->trigger_chgr) {
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_MITIGATION_SW(led->base),
+ FLASH_LED_CHGR_MITIGATION_EN_MASK,
+ FLASH_LED_CHGR_MITIGATION_DISABLE);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "disable chgr mitigation failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
led->enable--;
if (led->enable == 0) {
rc = qpnp_flash_led_masked_write(led,
@@ -892,6 +930,18 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
}
}
+ if (led->trigger_chgr) {
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_MITIGATION_SW(led->base),
+ FLASH_LED_CHGR_MITIGATION_EN_MASK,
+ FLASH_LED_CHGR_MITIGATION_ENABLE);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "trigger chgr mitigation failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_EN_LED_CTRL(led->base),
snode->led_mask, val);
@@ -951,6 +1001,10 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
*max_current = rc;
}
+ led->trigger_chgr = false;
+ if (options & PRE_FLASH)
+ led->trigger_chgr = true;
+
return 0;
}
@@ -959,17 +1013,24 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
{
struct flash_node_data *fnode = NULL;
struct flash_switch_data *snode = NULL;
- struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev);
+ struct qpnp_flash_led *led = NULL;
int rc;
if (!strncmp(led_cdev->name, "led:switch", strlen("led:switch"))) {
snode = container_of(led_cdev, struct flash_switch_data, cdev);
led = dev_get_drvdata(&snode->pdev->dev);
- } else {
+ } else if (!strncmp(led_cdev->name, "led:flash", strlen("led:flash")) ||
+ !strncmp(led_cdev->name, "led:torch",
+ strlen("led:torch"))) {
fnode = container_of(led_cdev, struct flash_node_data, cdev);
led = dev_get_drvdata(&fnode->pdev->dev);
}
+ if (!led) {
+ pr_err("Failed to get flash driver data\n");
+ return;
+ }
+
spin_lock(&led->lock);
if (snode) {
rc = qpnp_flash_led_switch_set(snode, value > 0);
@@ -1649,7 +1710,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
- led->pdata->lmh_mitigation_sel = FLASH_LED_LMH_MITIGATION_SEL_DEFAULT;
+ led->pdata->lmh_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT;
rc = of_property_read_u32(node, "qcom,lmh-mitigation-sel", &val);
if (!rc) {
led->pdata->lmh_mitigation_sel = val;
@@ -1659,11 +1720,43 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
- if (led->pdata->lmh_mitigation_sel > FLASH_LED_LMH_MITIGATION_SEL_MAX) {
+ if (led->pdata->lmh_mitigation_sel > FLASH_LED_MITIGATION_SEL_MAX) {
dev_err(&led->pdev->dev, "Invalid lmh_mitigation_sel specified\n");
return -EINVAL;
}
+ led->pdata->chgr_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT;
+ rc = of_property_read_u32(node, "qcom,chgr-mitigation-sel", &val);
+ if (!rc) {
+ led->pdata->chgr_mitigation_sel = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to parse chgr_mitigation_sel, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (led->pdata->chgr_mitigation_sel > FLASH_LED_MITIGATION_SEL_MAX) {
+ dev_err(&led->pdev->dev, "Invalid chgr_mitigation_sel specified\n");
+ return -EINVAL;
+ }
+
+ led->pdata->chgr_mitigation_sel <<= FLASH_LED_CHGR_MITIGATION_SEL_SHIFT;
+
+ led->pdata->iled_thrsh_val = FLASH_LED_MITIGATION_THRSH_DEFAULT;
+ rc = of_property_read_u32(node, "qcom,iled-thrsh-ma", &val);
+ if (!rc) {
+ led->pdata->iled_thrsh_val = MITIGATION_THRSH_MA_TO_VAL(val);
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to parse iled_thrsh_val, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (led->pdata->iled_thrsh_val > FLASH_LED_MITIGATION_THRSH_MAX) {
+ dev_err(&led->pdev->dev, "Invalid iled_thrsh_val specified\n");
+ return -EINVAL;
+ }
+
led->pdata->all_ramp_up_done_irq =
of_irq_get_byname(node, "all-ramp-up-done-irq");
if (led->pdata->all_ramp_up_done_irq < 0)
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 9c855e89e3ff..f071aae3ccab 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1400,6 +1400,49 @@ static struct msm_vidc_format venc_formats[] = {
},
};
+static void msm_venc_update_plane_count(struct msm_vidc_inst *inst, int type)
+{
+ struct v4l2_ctrl *ctrl = NULL;
+ u32 extradata = 0;
+
+ if (!inst)
+ return;
+
+ inst->fmts[type].num_planes = 1;
+
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
+ V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA);
+
+ if (ctrl)
+ extradata = v4l2_ctrl_g_ctrl(ctrl);
+
+ if (type == CAPTURE_PORT) {
+ switch (extradata) {
+ case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO:
+ case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB:
+ case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER:
+ case V4L2_MPEG_VIDC_EXTRADATA_LTR:
+ case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI:
+ inst->fmts[CAPTURE_PORT].num_planes = 2;
+ default:
+ break;
+ }
+ } else if (type == OUTPUT_PORT) {
+ switch (extradata) {
+ case V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP:
+ case V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM:
+ case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO:
+ case V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS:
+ case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP:
+ case V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO:
+ inst->fmts[OUTPUT_PORT].num_planes = 2;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
static int msm_venc_set_csc(struct msm_vidc_inst *inst);
static int msm_venc_queue_setup(struct vb2_queue *q,
@@ -1414,8 +1457,7 @@ static int msm_venc_queue_setup(struct vb2_queue *q,
enum hal_property property_id;
struct hfi_device *hdev;
struct hal_buffer_requirements *buff_req;
- struct v4l2_ctrl *ctrl = NULL;
- u32 extradata = 0, extra_idx = 0;
+ u32 extra_idx = 0;
struct hal_buffer_requirements *buff_req_buffer = NULL;
if (!q || !q->drv_priv) {
@@ -1471,21 +1513,8 @@ static int msm_venc_queue_setup(struct vb2_queue *q,
temp, *num_buffers);
}
- ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
- V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA);
- if (ctrl)
- extradata = v4l2_ctrl_g_ctrl(ctrl);
- switch (extradata) {
- case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO:
- case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB:
- case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER:
- case V4L2_MPEG_VIDC_EXTRADATA_LTR:
- case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI:
- *num_planes = *num_planes + 1;
- default:
- break;
- }
- inst->fmts[CAPTURE_PORT].num_planes = *num_planes;
+ msm_venc_update_plane_count(inst, CAPTURE_PORT);
+ *num_planes = inst->fmts[CAPTURE_PORT].num_planes;
for (i = 0; i < *num_planes; i++) {
int extra_idx = EXTRADATA_IDX(*num_planes);
@@ -1543,24 +1572,9 @@ static int msm_venc_queue_setup(struct vb2_queue *q,
dprintk(VIDC_DBG, "actual input buffer count set to fw = %d\n",
*num_buffers);
- ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
- V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA);
- if (ctrl)
- extradata = v4l2_ctrl_g_ctrl(ctrl);
- switch (extradata) {
- case V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP:
- case V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM:
- case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO:
- case V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS:
- case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP:
- case V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO:
- *num_planes = *num_planes + 1;
- break;
- default:
- break;
- }
+ msm_venc_update_plane_count(inst, OUTPUT_PORT);
+ *num_planes = inst->fmts[OUTPUT_PORT].num_planes;
- inst->fmts[OUTPUT_PORT].num_planes = *num_planes;
rc = call_hfi_op(hdev, session_set_property, inst->session,
property_id, &new_buf_count);
if (rc)
@@ -3629,6 +3643,9 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
memcpy(&inst->fmts[fmt->type], fmt,
sizeof(struct msm_vidc_format));
+ msm_venc_update_plane_count(inst, CAPTURE_PORT);
+ fmt->num_planes = inst->fmts[CAPTURE_PORT].num_planes;
+
rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
if (rc) {
dprintk(VIDC_ERR, "Failed to open instance\n");
@@ -3682,6 +3699,9 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
memcpy(&inst->fmts[fmt->type], fmt,
sizeof(struct msm_vidc_format));
+ msm_venc_update_plane_count(inst, OUTPUT_PORT);
+ fmt->num_planes = inst->fmts[OUTPUT_PORT].num_planes;
+
msm_comm_set_color_format(inst, HAL_BUFFER_INPUT, fmt->fourcc);
} else {
dprintk(VIDC_ERR, "%s - Unsupported buf type: %d\n",
@@ -3690,7 +3710,7 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
goto exit;
}
- f->fmt.pix_mp.num_planes = inst->fmts[fmt->type].num_planes;
+ f->fmt.pix_mp.num_planes = fmt->num_planes;
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
struct hal_frame_size frame_sz = {0};
@@ -3743,7 +3763,7 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
const struct msm_vidc_format *fmt = NULL;
int rc = 0;
int i;
- u32 height, width;
+ u32 height, width, num_planes;
unsigned int extra_idx = 0;
struct hal_buffer_requirements *bufreq = NULL;
@@ -3764,10 +3784,14 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
fmt = &inst->fmts[CAPTURE_PORT];
height = inst->prop.height[CAPTURE_PORT];
width = inst->prop.width[CAPTURE_PORT];
+ msm_venc_update_plane_count(inst, CAPTURE_PORT);
+ num_planes = inst->fmts[CAPTURE_PORT].num_planes;
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
fmt = &inst->fmts[OUTPUT_PORT];
height = inst->prop.height[OUTPUT_PORT];
width = inst->prop.width[OUTPUT_PORT];
+ msm_venc_update_plane_count(inst, OUTPUT_PORT);
+ num_planes = inst->fmts[OUTPUT_PORT].num_planes;
} else {
dprintk(VIDC_ERR, "Invalid type: %x\n", f->type);
return -ENOTSUPP;
@@ -3776,10 +3800,10 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
f->fmt.pix_mp.pixelformat = fmt->fourcc;
f->fmt.pix_mp.height = height;
f->fmt.pix_mp.width = width;
- f->fmt.pix_mp.num_planes = fmt->num_planes;
+ f->fmt.pix_mp.num_planes = num_planes;
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- for (i = 0; i < fmt->num_planes; ++i) {
+ for (i = 0; i < num_planes; ++i) {
f->fmt.pix_mp.plane_fmt[i].sizeimage =
fmt->get_frame_size(i, height, width);
}
@@ -3790,7 +3814,7 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
f->fmt.pix_mp.plane_fmt[0].sizeimage =
bufreq ? bufreq->buffer_size : 0;
}
- extra_idx = EXTRADATA_IDX(fmt->num_planes);
+ extra_idx = EXTRADATA_IDX(num_planes);
if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
bufreq = get_buff_req_buffer(inst,
@@ -3803,7 +3827,7 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
bufreq ? bufreq->buffer_size : 0;
}
- for (i = 0; i < fmt->num_planes; ++i) {
+ for (i = 0; i < num_planes; ++i) {
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
inst->bufq[OUTPUT_PORT].vb2_bufq.plane_sizes[i] =
f->fmt.pix_mp.plane_fmt[i].sizeimage;
@@ -4057,4 +4081,3 @@ int msm_venc_ctrl_init(struct msm_vidc_inst *inst)
return msm_comm_ctrl_init(inst, msm_venc_ctrls,
ARRAY_SIZE(msm_venc_ctrls), &msm_venc_ctrl_ops);
}
-
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
index 0e62811bf41b..3cd1c38f8f37 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
@@ -599,8 +599,8 @@ static bool msm_dcvs_check_supported(struct msm_vidc_inst *inst)
goto dcvs_decision_done;
}
if (msm_comm_turbo_session(inst) ||
- !IS_VALID_DCVS_SESSION(instance_load, dcvs_limit ||
- instance_count > 1))
+ !IS_VALID_DCVS_SESSION(instance_load, dcvs_limit) ||
+ instance_count > 1)
is_dcvs_supported = false;
}
if (inst->session_type == MSM_VIDC_ENCODER) {
@@ -617,8 +617,8 @@ static bool msm_dcvs_check_supported(struct msm_vidc_inst *inst)
goto dcvs_decision_done;
}
if (msm_comm_turbo_session(inst) ||
- !IS_VALID_DCVS_SESSION(instance_load, dcvs_limit ||
- instance_count > 1))
+ !IS_VALID_DCVS_SESSION(instance_load, dcvs_limit) ||
+ instance_count > 1)
is_dcvs_supported = false;
}
dcvs_decision_done:
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index e0fb31de38ff..8332c7f4db43 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -3336,7 +3336,6 @@ static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet)
{
bool local_packet = false;
enum vidc_msg_prio log_level = VIDC_FW;
- unsigned int pending_packet_count = 0;
if (!device) {
dprintk(VIDC_ERR, "%s: Invalid params\n", __func__);
@@ -3361,23 +3360,6 @@ static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet)
log_level = VIDC_ERR;
}
- /*
- * In FATAL situation, print all the pending messages in msg
- * queue. This is useful for debugging. At this time, message
- * queues may be corrupted. Hence don't trust them and just print
- * first max_packets packets.
- */
-
- if (local_packet) {
- dprintk(VIDC_ERR,
- "Printing all pending messages in message Queue\n");
- while (!__iface_msgq_read(device, packet) &&
- pending_packet_count < max_packets) {
- __dump_packet(packet, log_level);
- pending_packet_count++;
- }
- }
-
while (!__iface_dbgq_read(device, packet)) {
struct hfi_msg_sys_coverage_packet *pkt =
(struct hfi_msg_sys_coverage_packet *) packet;
diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c
index 02ddf3225af8..e07350a1e2ce 100644
--- a/drivers/mfd/wcd934x-regmap.c
+++ b/drivers/mfd/wcd934x-regmap.c
@@ -17,6 +17,18 @@
#include <linux/device.h>
#include "wcd9xxx-regmap.h"
+
+static const struct reg_sequence wcd934x_1_1_defaults[] = {
+ { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x01 },
+ { WCD934X_BIAS_VBG_FINE_ADJ, 0x75 },
+ { WCD934X_HPH_REFBUFF_LP_CTL, 0x0E },
+ { WCD934X_EAR_DAC_CTL_ATEST, 0x08 },
+ { WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x17 },
+ { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 },
+ { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81 },
+ { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81 },
+};
+
static const struct reg_default wcd934x_defaults[] = {
{ WCD934X_PAGE0_PAGE_REGISTER, 0x00 },
{ WCD934X_CODEC_RPM_CLK_BYPASS, 0x00 },
@@ -1803,6 +1815,37 @@ static const struct reg_default wcd934x_defaults[] = {
{ WCD934X_TEST_DEBUG_CODEC_DIAGS, 0x00 },
};
+/*
+ * wcd934x_regmap_register_patch: Update register defaults based on version
+ * @regmap: handle to wcd9xxx regmap
+ * @version: wcd934x version
+ *
+ * Returns error code in case of failure or 0 for success
+ */
+int wcd934x_regmap_register_patch(struct regmap *regmap, int revision)
+{
+ int rc = 0;
+
+ if (!regmap) {
+ pr_err("%s: regmap struct is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (revision) {
+ case TAVIL_VERSION_1_1:
+ case TAVIL_VERSION_WCD9340_1_1:
+ case TAVIL_VERSION_WCD9341_1_1:
+ regcache_cache_only(regmap, true);
+ rc = regmap_multi_reg_write(regmap, wcd934x_1_1_defaults,
+ ARRAY_SIZE(wcd934x_1_1_defaults));
+ regcache_cache_only(regmap, false);
+ break;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(wcd934x_regmap_register_patch);
+
static bool wcd934x_is_readable_register(struct device *dev, unsigned int reg)
{
u8 pg_num, reg_offset;
diff --git a/drivers/mfd/wcd9xxx-regmap.h b/drivers/mfd/wcd9xxx-regmap.h
index 62e4a620c71c..6db8fc55acae 100644
--- a/drivers/mfd/wcd9xxx-regmap.h
+++ b/drivers/mfd/wcd9xxx-regmap.h
@@ -21,6 +21,8 @@ typedef int (*regmap_patch_fptr)(struct regmap *, int);
#ifdef CONFIG_WCD934X_CODEC
extern struct regmap_config wcd934x_regmap_config;
+extern int wcd934x_regmap_register_patch(struct regmap *regmap,
+ int version);
#endif
#ifdef CONFIG_WCD9335_CODEC
@@ -71,6 +73,11 @@ static inline regmap_patch_fptr wcd9xxx_get_regmap_reg_patch(int type)
apply_patch = wcd9335_regmap_register_patch;
break;
#endif
+#ifdef CONFIG_WCD934X_CODEC
+ case WCD934X:
+ apply_patch = wcd934x_regmap_register_patch;
+ break;
+#endif
default:
apply_patch = NULL;
break;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.c b/drivers/misc/qcom/qdsp6v2/audio_utils.c
index 065b426ca6d0..840597314a5f 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils.c
@@ -601,6 +601,7 @@ long audio_in_compat_ioctl(struct file *file,
}
case AUDIO_GET_CONFIG_32: {
struct msm_audio_config32 cfg_32;
+ memset(&cfg_32, 0, sizeof(cfg_32));
cfg_32.buffer_size = audio->pcm_cfg.buffer_size;
cfg_32.buffer_count = audio->pcm_cfg.buffer_count;
cfg_32.channel_count = audio->pcm_cfg.channel_count;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
index 0c44f79549d4..567c948b0efe 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
@@ -570,6 +570,8 @@ int audio_aio_release(struct inode *inode, struct file *file)
struct q6audio_aio *audio = file->private_data;
pr_debug("%s[%p]\n", __func__, audio);
mutex_lock(&audio->lock);
+ mutex_lock(&audio->read_lock);
+ mutex_lock(&audio->write_lock);
audio->wflush = 1;
if (audio->wakelock_voted &&
(audio->audio_ws_mgr != NULL) &&
@@ -595,6 +597,8 @@ int audio_aio_release(struct inode *inode, struct file *file)
wake_up(&audio->event_wait);
audio_aio_reset_event_queue(audio);
q6asm_audio_client_free(audio->ac);
+ mutex_unlock(&audio->write_lock);
+ mutex_unlock(&audio->read_lock);
mutex_unlock(&audio->lock);
mutex_destroy(&audio->lock);
mutex_destroy(&audio->read_lock);
@@ -1745,7 +1749,11 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd,
__func__);
rc = -EFAULT;
} else {
+ mutex_lock(&audio->read_lock);
+ mutex_lock(&audio->write_lock);
rc = audio_aio_ion_add(audio, &info);
+ mutex_unlock(&audio->write_lock);
+ mutex_unlock(&audio->read_lock);
}
mutex_unlock(&audio->lock);
break;
@@ -1760,7 +1768,11 @@ static long audio_aio_ioctl(struct file *file, unsigned int cmd,
__func__);
rc = -EFAULT;
} else {
+ mutex_lock(&audio->read_lock);
+ mutex_lock(&audio->write_lock);
rc = audio_aio_ion_remove(audio, &info);
+ mutex_unlock(&audio->write_lock);
+ mutex_unlock(&audio->read_lock);
}
mutex_unlock(&audio->lock);
break;
@@ -2064,7 +2076,11 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
} else {
info.fd = info_32.fd;
info.vaddr = compat_ptr(info_32.vaddr);
+ mutex_lock(&audio->read_lock);
+ mutex_lock(&audio->write_lock);
rc = audio_aio_ion_add(audio, &info);
+ mutex_unlock(&audio->write_lock);
+ mutex_unlock(&audio->read_lock);
}
mutex_unlock(&audio->lock);
break;
@@ -2081,7 +2097,11 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
} else {
info.fd = info_32.fd;
info.vaddr = compat_ptr(info_32.vaddr);
+ mutex_lock(&audio->read_lock);
+ mutex_lock(&audio->write_lock);
rc = audio_aio_ion_remove(audio, &info);
+ mutex_unlock(&audio->write_lock);
+ mutex_unlock(&audio->read_lock);
}
mutex_unlock(&audio->lock);
break;
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index 05ce3969a5c7..75b193def36e 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -26,7 +26,8 @@
#define IPA_API_DISPATCH_RETURN(api, p...) \
do { \
if (!ipa_api_ctrl) { \
- pr_err("IPA HW is not supported on this target\n"); \
+ pr_err("%s:%d IPA HW is not supported\n", \
+ __func__, __LINE__); \
ret = -EPERM; \
} \
else { \
@@ -44,7 +45,8 @@
#define IPA_API_DISPATCH(api, p...) \
do { \
if (!ipa_api_ctrl) \
- pr_err("IPA HW is not supported on this target\n"); \
+ pr_err("%s:%d IPA HW is not supported\n", \
+ __func__, __LINE__); \
else { \
if (ipa_api_ctrl->api) { \
ipa_api_ctrl->api(p); \
@@ -59,7 +61,8 @@
#define IPA_API_DISPATCH_RETURN_PTR(api, p...) \
do { \
if (!ipa_api_ctrl) { \
- pr_err("IPA HW is not supported on this target\n"); \
+ pr_err("%s:%d IPA HW is not supported\n", \
+ __func__, __LINE__); \
ret = NULL; \
} \
else { \
@@ -77,7 +80,8 @@
#define IPA_API_DISPATCH_RETURN_BOOL(api, p...) \
do { \
if (!ipa_api_ctrl) { \
- pr_err("IPA HW is not supported on this target\n"); \
+ pr_err("%s:%d IPA HW is not supported\n", \
+ __func__, __LINE__); \
ret = false; \
} \
else { \
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index 838b78c1934d..d18308344431 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -2034,7 +2034,7 @@ static void ipa_usb_debugfs_init(void)
ipa3_usb_ctx->dent = debugfs_create_dir("ipa_usb", 0);
if (IS_ERR(ipa3_usb_ctx->dent)) {
- IPA_USB_ERR("fail to create folder in debug_fs.\n");
+ pr_err("fail to create folder in debug_fs.\n");
return;
}
@@ -2043,7 +2043,7 @@ static void ipa_usb_debugfs_init(void)
&ipa3_ipa_usb_ops);
if (!ipa3_usb_ctx->dfile_state_info ||
IS_ERR(ipa3_usb_ctx->dfile_state_info)) {
- IPA_USB_ERR("failed to create file for state_info\n");
+ pr_err("failed to create file for state_info\n");
goto fail;
}
@@ -2644,11 +2644,11 @@ static int __init ipa3_usb_init(void)
unsigned long flags;
int res;
- IPA_USB_DBG("entry\n");
+ pr_debug("entry\n");
ipa3_usb_ctx = kzalloc(sizeof(struct ipa3_usb_context), GFP_KERNEL);
if (ipa3_usb_ctx == NULL) {
- IPA_USB_ERR("failed to allocate memory\n");
- IPA_USB_ERR(":ipa_usb init failed\n");
+ pr_err("failed to allocate memory\n");
+ pr_err(":ipa_usb init failed\n");
return -EFAULT;
}
memset(ipa3_usb_ctx, 0, sizeof(struct ipa3_usb_context));
@@ -2680,19 +2680,19 @@ static int __init ipa3_usb_init(void)
ipa3_usb_ctx->wq = create_singlethread_workqueue("ipa_usb_wq");
if (!ipa3_usb_ctx->wq) {
- IPA_USB_ERR("failed to create workqueue\n");
+ pr_err("failed to create workqueue\n");
res = -EFAULT;
goto ipa_usb_workqueue_fail;
}
ipa_usb_debugfs_init();
- IPA_USB_INFO("exit: IPA_USB init success!\n");
+ pr_info("exit: IPA_USB init success!\n");
return 0;
ipa_usb_workqueue_fail:
- IPA_USB_ERR(":init failed (%d)\n", -res);
+ pr_err(":init failed (%d)\n", -res);
kfree(ipa3_usb_ctx);
return res;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
index 0eab77d27760..50c387ec785d 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
@@ -1420,6 +1420,7 @@ static ssize_t ipa_read_nat4(struct file *file,
u16 enable, tbl_entry, flag;
u32 no_entrys = 0;
+ mutex_lock(&ipa_ctx->nat_mem.lock);
value = ipa_ctx->nat_mem.public_ip_addr;
pr_err(
"Table IP Address:%d.%d.%d.%d\n",
@@ -1573,6 +1574,7 @@ static ssize_t ipa_read_nat4(struct file *file,
}
}
pr_err("Current No. Nat Entries: %d\n", no_entrys);
+ mutex_unlock(&ipa_ctx->nat_mem.lock);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 16f50030b960..3c2a6d4620ba 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -3152,23 +3152,23 @@ static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in,
} else if (in->client ==
IPA_CLIENT_APPS_WAN_CONS) {
sys->pyld_hdlr = ipa_wan_rx_pyld_hdlr;
- if (in->recycle_enabled) {
+ sys->rx_pool_sz = ipa_ctx->wan_rx_ring_size;
+ if (nr_cpu_ids > 1) {
sys->repl_hdlr =
- ipa_replenish_rx_cache_recycle;
- sys->rx_pool_sz =
- IPA_WAN_NAPI_CONS_RX_POOL_SZ;
+ ipa_fast_replenish_rx_cache;
+ sys->repl_trig_thresh =
+ sys->rx_pool_sz / 8;
} else {
- if (nr_cpu_ids > 1) {
- sys->repl_hdlr =
- ipa_fast_replenish_rx_cache;
- sys->repl_trig_thresh =
- sys->rx_pool_sz / 8;
- } else {
+ sys->repl_hdlr =
+ ipa_replenish_rx_cache;
+ }
+ if (in->napi_enabled) {
+ sys->rx_pool_sz =
+ IPA_WAN_NAPI_CONS_RX_POOL_SZ;
+ if (in->recycle_enabled) {
sys->repl_hdlr =
- ipa_replenish_rx_cache;
+ ipa_replenish_rx_cache_recycle;
}
- sys->rx_pool_sz =
- ipa_ctx->wan_rx_ring_size;
}
sys->ep->wakelock_client =
IPA_WAKELOCK_REF_CLIENT_WAN_RX;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
index 249de808ec5c..f5afb4b0141c 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
@@ -523,10 +523,9 @@ ssize_t ipa_read(struct file *filp, char __user *buf, size_t count,
start = buf;
while (1) {
- prepare_to_wait(&ipa_ctx->msg_waitq, &wait, TASK_INTERRUPTIBLE);
-
mutex_lock(&ipa_ctx->msg_lock);
locked = 1;
+ prepare_to_wait(&ipa_ctx->msg_waitq, &wait, TASK_INTERRUPTIBLE);
if (!list_empty(&ipa_ctx->msg_list)) {
msg = list_first_entry(&ipa_ctx->msg_list,
struct ipa_push_msg, link);
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index ebb93e246048..96003d7a16a0 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -1259,11 +1259,13 @@ static int handle_ingress_format(struct net_device *dev,
ipa_to_apps_ep_cfg.ipa_ep_cfg.aggr.aggr_pkt_limit =
in->u.ingress_format.agg_count;
- ipa_to_apps_ep_cfg.recycle_enabled = true;
- ep_cfg = (struct rmnet_phys_ep_conf_s *)
- rcu_dereference(dev->rx_handler_data);
- ep_cfg->recycle = ipa_recycle_wan_skb;
- pr_info("Wan Recycle Enabled\n");
+ if (ipa_rmnet_res.ipa_napi_enable) {
+ ipa_to_apps_ep_cfg.recycle_enabled = true;
+ ep_cfg = (struct rmnet_phys_ep_conf_s *)
+ rcu_dereference(dev->rx_handler_data);
+ ep_cfg->recycle = ipa_recycle_wan_skb;
+ pr_info("Wan Recycle Enabled\n");
+ }
}
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index 3915f652d87b..25e5e3b74f26 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -1478,6 +1478,7 @@ static ssize_t ipa3_read_nat4(struct file *file,
u16 enable, tbl_entry, flag;
u32 no_entrys = 0;
+ mutex_lock(&ipa3_ctx->nat_mem.lock);
value = ipa3_ctx->nat_mem.public_ip_addr;
pr_err(
"Table IP Address:%d.%d.%d.%d\n",
@@ -1631,6 +1632,7 @@ static ssize_t ipa3_read_nat4(struct file *file,
}
}
pr_err("Current No. Nat Entries: %d\n", no_entrys);
+ mutex_unlock(&ipa3_ctx->nat_mem.lock);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 643e40402499..94e8bba1fe01 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -3180,22 +3180,20 @@ static int ipa3_assign_policy(struct ipa_sys_connect_params *in,
IPA_CLIENT_APPS_WAN_CONS) {
sys->pyld_hdlr = ipa3_wan_rx_pyld_hdlr;
sys->free_rx_wrapper = ipa3_free_rx_wrapper;
- if (in->recycle_enabled) {
+ sys->rx_pool_sz = ipa3_ctx->wan_rx_ring_size;
+ if (nr_cpu_ids > 1) {
sys->repl_hdlr =
- ipa3_replenish_rx_cache_recycle;
- sys->rx_pool_sz =
- IPA_WAN_NAPI_CONS_RX_POOL_SZ;
+ ipa3_fast_replenish_rx_cache;
} else {
- if (nr_cpu_ids > 1) {
- sys->repl_hdlr =
- ipa3_fast_replenish_rx_cache;
- } else {
- sys->repl_hdlr =
- ipa3_replenish_rx_cache;
- }
- sys->rx_pool_sz =
- ipa3_ctx->wan_rx_ring_size;
+ sys->repl_hdlr =
+ ipa3_replenish_rx_cache;
}
+ if (in->napi_enabled)
+ sys->rx_pool_sz =
+ IPA_WAN_NAPI_CONS_RX_POOL_SZ;
+ if (in->napi_enabled && in->recycle_enabled)
+ sys->repl_hdlr =
+ ipa3_replenish_rx_cache_recycle;
in->ipa_ep_cfg.aggr.aggr_sw_eof_active
= true;
if (ipa3_ctx->
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
index 22756c1fb168..b9f57552533e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
@@ -528,12 +528,12 @@ ssize_t ipa3_read(struct file *filp, char __user *buf, size_t count,
start = buf;
while (1) {
+ mutex_lock(&ipa3_ctx->msg_lock);
+ locked = 1;
prepare_to_wait(&ipa3_ctx->msg_waitq,
&wait,
TASK_INTERRUPTIBLE);
- mutex_lock(&ipa3_ctx->msg_lock);
- locked = 1;
if (!list_empty(&ipa3_ctx->msg_list)) {
msg = list_first_entry(&ipa3_ctx->msg_list,
struct ipa3_push_msg, link);
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index a2fef45cc55f..f134852e046e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -1271,11 +1271,13 @@ static int handle3_ingress_format(struct net_device *dev,
ipa_wan_ep_cfg->ipa_ep_cfg.aggr.aggr_pkt_limit =
in->u.ingress_format.agg_count;
- ipa_wan_ep_cfg->recycle_enabled = true;
- ep_cfg = (struct rmnet_phys_ep_conf_s *)
- rcu_dereference(dev->rx_handler_data);
- ep_cfg->recycle = ipa_recycle_wan_skb;
- pr_info("Wan Recycle Enabled\n");
+ if (ipa_wan_ep_cfg->napi_enabled) {
+ ipa_wan_ep_cfg->recycle_enabled = true;
+ ep_cfg = (struct rmnet_phys_ep_conf_s *)
+ rcu_dereference(dev->rx_handler_data);
+ ep_cfg->recycle = ipa_recycle_wan_skb;
+ pr_info("Wan Recycle Enabled\n");
+ }
}
}
@@ -1969,9 +1971,9 @@ static int get_ipa_rmnet_dts_configuration(struct platform_device *pdev,
ipa_rmnet_drv_res->ipa_advertise_sg_support ? "True" : "False");
ipa_rmnet_drv_res->ipa_napi_enable =
- of_property_read_bool(pdev->dev.of_node,
- "qcom,napi");
- pr_info("IPA napi = %s\n",
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,ipa-napi-enable");
+ pr_info("IPA Napi Enable = %s\n",
ipa_rmnet_drv_res->ipa_napi_enable ? "True" : "False");
return 0;
}
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 545a1e684b25..0619b314b7de 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -267,8 +267,12 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(typec_power_role),
POWER_SUPPLY_ATTR(pd_allowed),
POWER_SUPPLY_ATTR(pd_active),
+ POWER_SUPPLY_ATTR(pd_in_hard_reset),
+ POWER_SUPPLY_ATTR(pd_current_max),
+ POWER_SUPPLY_ATTR(pd_usb_suspend_supported),
POWER_SUPPLY_ATTR(charger_temp),
POWER_SUPPLY_ATTR(charger_temp_max),
+ POWER_SUPPLY_ATTR(parallel_disable),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h
index 7e08b71e3b6a..adc640c7afe1 100644
--- a/drivers/power/qcom-charger/fg-core.h
+++ b/drivers/power/qcom-charger/fg-core.h
@@ -23,6 +23,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/string_helpers.h>
@@ -38,6 +39,7 @@
pr_debug(fmt, ##__VA_ARGS__); \
} while (0)
+/* Awake votable reasons */
#define SRAM_READ "fg_sram_read"
#define SRAM_WRITE "fg_sram_write"
#define PROFILE_LOAD "fg_profile_load"
@@ -59,6 +61,9 @@
#define BUCKET_COUNT 8
#define BUCKET_SOC_PCT (256 / BUCKET_COUNT)
+#define KI_COEFF_MAX 62200
+#define KI_COEFF_SOC_LEVELS 3
+
/* Debug flag definitions */
enum fg_debug_flag {
FG_IRQ = BIT(0), /* Show interrupts */
@@ -137,6 +142,8 @@ enum fg_sram_param_id {
FG_SRAM_CHG_TERM_CURR,
FG_SRAM_DELTA_SOC_THR,
FG_SRAM_RECHARGE_SOC_THR,
+ FG_SRAM_KI_COEFF_MED_DISCHG,
+ FG_SRAM_KI_COEFF_HI_DISCHG,
FG_SRAM_MAX,
};
@@ -173,6 +180,8 @@ struct fg_alg_flag {
/* DT parameters for FG device */
struct fg_dt_props {
+ bool force_load_profile;
+ bool hold_soc_while_full;
int cutoff_volt_mv;
int empty_volt_mv;
int vbatt_low_thr_mv;
@@ -185,7 +194,6 @@ struct fg_dt_props {
int esr_timer_charging;
int esr_timer_awake;
int esr_timer_asleep;
- bool force_load_profile;
int cl_start_soc;
int cl_max_temp;
int cl_min_temp;
@@ -195,6 +203,9 @@ struct fg_dt_props {
int cl_min_cap_limit;
int jeita_hyst_temp;
int batt_temp_delta;
+ int ki_coeff_soc[KI_COEFF_SOC_LEVELS];
+ int ki_coeff_med_dischg[KI_COEFF_SOC_LEVELS];
+ int ki_coeff_hi_dischg[KI_COEFF_SOC_LEVELS];
};
/* parameters from battery profile */
@@ -240,6 +251,8 @@ struct fg_chip {
struct dentry *dfs_root;
struct power_supply *fg_psy;
struct power_supply *batt_psy;
+ struct power_supply *usb_psy;
+ struct power_supply *dc_psy;
struct iio_channel *batt_id_chan;
struct fg_memif *sram;
struct fg_irq_info *irqs;
@@ -268,6 +281,9 @@ struct fg_chip {
bool profile_loaded;
bool battery_missing;
bool fg_restarting;
+ bool charge_full;
+ bool recharge_soc_adjusted;
+ bool ki_coeff_dischg_en;
struct completion soc_update;
struct completion soc_ready;
struct delayed_work profile_load_work;
@@ -321,4 +337,5 @@ extern int fg_debugfs_create(struct fg_chip *chip);
extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len);
extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos);
extern s64 fg_float_decode(u16 val);
+extern bool is_input_present(struct fg_chip *chip);
#endif
diff --git a/drivers/power/qcom-charger/fg-util.c b/drivers/power/qcom-charger/fg-util.c
index 790e56bd3dae..bbdbe48896d7 100644
--- a/drivers/power/qcom-charger/fg-util.c
+++ b/drivers/power/qcom-charger/fg-util.c
@@ -29,6 +29,43 @@ static struct fg_dbgfs dbgfs_data = {
},
};
+static bool is_usb_present(struct fg_chip *chip)
+{
+ union power_supply_propval pval = {0, };
+
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+
+ if (chip->usb_psy)
+ power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ else
+ return false;
+
+ return pval.intval != 0;
+}
+
+static bool is_dc_present(struct fg_chip *chip)
+{
+ union power_supply_propval pval = {0, };
+
+ if (!chip->dc_psy)
+ chip->dc_psy = power_supply_get_by_name("dc");
+
+ if (chip->dc_psy)
+ power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ else
+ return false;
+
+ return pval.intval != 0;
+}
+
+bool is_input_present(struct fg_chip *chip)
+{
+ return is_usb_present(chip) || is_dc_present(chip);
+}
+
#define EXPONENT_SHIFT 11
#define EXPONENT_OFFSET -9
#define MANTISSA_SIGN_BIT 10
@@ -98,6 +135,7 @@ int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset,
* This interrupt need to be enabled only when it is
* required. It will be kept disabled other times.
*/
+ reinit_completion(&chip->soc_update);
enable_irq(chip->irqs[SOC_UPDATE_IRQ].irq);
atomic_access = true;
} else {
diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c
index f8c1ad5963af..30408218b7e7 100644
--- a/drivers/power/qcom-charger/qpnp-fg-gen3.c
+++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c
@@ -17,7 +17,6 @@
#include <linux/of_platform.h>
#include <linux/of_batterydata.h>
#include <linux/platform_device.h>
-#include <linux/power_supply.h>
#include <linux/iio/consumer.h>
#include <linux/qpnp/qpnp-revid.h>
#include "fg-core.h"
@@ -37,6 +36,12 @@
#define SYS_TERM_CURR_OFFSET 0
#define VBATT_FULL_WORD 7
#define VBATT_FULL_OFFSET 0
+#define KI_COEFF_MED_DISCHG_WORD 9
+#define KI_COEFF_MED_DISCHG_OFFSET 3
+#define KI_COEFF_HI_DISCHG_WORD 10
+#define KI_COEFF_HI_DISCHG_OFFSET 0
+#define KI_COEFF_LOW_DISCHG_WORD 10
+#define KI_COEFF_LOW_DISCHG_OFFSET 2
#define DELTA_SOC_THR_WORD 12
#define DELTA_SOC_THR_OFFSET 3
#define RECHARGE_SOC_THR_WORD 14
@@ -65,6 +70,8 @@
#define PROFILE_INTEGRITY_OFFSET 3
#define BATT_SOC_WORD 91
#define BATT_SOC_OFFSET 0
+#define FULL_SOC_WORD 93
+#define FULL_SOC_OFFSET 2
#define MONOTONIC_SOC_WORD 94
#define MONOTONIC_SOC_OFFSET 2
#define CC_SOC_WORD 95
@@ -87,6 +94,12 @@
#define ALG_FLAGS_OFFSET 1
/* v2 SRAM address and offset in ascending order */
+#define KI_COEFF_LOW_DISCHG_v2_WORD 9
+#define KI_COEFF_LOW_DISCHG_v2_OFFSET 3
+#define KI_COEFF_MED_DISCHG_v2_WORD 10
+#define KI_COEFF_MED_DISCHG_v2_OFFSET 0
+#define KI_COEFF_HI_DISCHG_v2_WORD 10
+#define KI_COEFF_HI_DISCHG_v2_OFFSET 1
#define DELTA_SOC_THR_v2_WORD 13
#define DELTA_SOC_THR_v2_OFFSET 0
#define RECHARGE_SOC_THR_v2_WORD 14
@@ -106,8 +119,6 @@ static int fg_decode_value_16b(struct fg_sram_param *sp,
enum fg_sram_param_id id, int val);
static int fg_decode_default(struct fg_sram_param *sp,
enum fg_sram_param_id id, int val);
-static int fg_decode_batt_soc(struct fg_sram_param *sp,
- enum fg_sram_param_id id, int val);
static int fg_decode_cc_soc(struct fg_sram_param *sp,
enum fg_sram_param_id id, int value);
static void fg_encode_voltage(struct fg_sram_param *sp,
@@ -132,7 +143,7 @@ static void fg_encode_default(struct fg_sram_param *sp,
static struct fg_sram_param pmicobalt_v1_sram_params[] = {
PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL,
- fg_decode_batt_soc),
+ fg_decode_default),
PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141,
1000, 0, NULL, fg_decode_voltage_15b),
PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL,
@@ -174,11 +185,17 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = {
ESR_TIMER_CHG_MAX_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL),
PARAM(ESR_TIMER_CHG_INIT, ESR_TIMER_CHG_INIT_WORD,
ESR_TIMER_CHG_INIT_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL),
+ PARAM(KI_COEFF_MED_DISCHG, KI_COEFF_MED_DISCHG_WORD,
+ KI_COEFF_MED_DISCHG_OFFSET, 1, 1000, 244141, 0,
+ fg_encode_default, NULL),
+ PARAM(KI_COEFF_HI_DISCHG, KI_COEFF_HI_DISCHG_WORD,
+ KI_COEFF_HI_DISCHG_OFFSET, 1, 1000, 244141, 0,
+ fg_encode_default, NULL),
};
static struct fg_sram_param pmicobalt_v2_sram_params[] = {
PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL,
- fg_decode_batt_soc),
+ fg_decode_default),
PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141,
1000, 0, NULL, fg_decode_voltage_15b),
PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL,
@@ -223,6 +240,12 @@ static struct fg_sram_param pmicobalt_v2_sram_params[] = {
ESR_TIMER_CHG_MAX_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL),
PARAM(ESR_TIMER_CHG_INIT, ESR_TIMER_CHG_INIT_WORD,
ESR_TIMER_CHG_INIT_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL),
+ PARAM(KI_COEFF_MED_DISCHG, KI_COEFF_MED_DISCHG_v2_WORD,
+ KI_COEFF_MED_DISCHG_v2_OFFSET, 1, 1000, 244141, 0,
+ fg_encode_default, NULL),
+ PARAM(KI_COEFF_HI_DISCHG, KI_COEFF_HI_DISCHG_v2_WORD,
+ KI_COEFF_HI_DISCHG_v2_OFFSET, 1, 1000, 244141, 0,
+ fg_encode_default, NULL),
};
static struct fg_alg_flag pmicobalt_v1_alg_flags[] = {
@@ -335,19 +358,11 @@ static int fg_decode_value_16b(struct fg_sram_param *sp,
return sp[id].value;
}
-static int fg_decode_batt_soc(struct fg_sram_param *sp,
- enum fg_sram_param_id id, int value)
-{
- sp[id].value = (u32)value >> 24;
- pr_debug("id: %d raw value: %x decoded value: %x\n", id, value,
- sp[id].value);
- return sp[id].value;
-}
-
static int fg_decode_default(struct fg_sram_param *sp, enum fg_sram_param_id id,
int value)
{
- return value;
+ sp[id].value = value;
+ return sp[id].value;
}
static int fg_decode(struct fg_sram_param *sp, enum fg_sram_param_id id,
@@ -645,6 +660,11 @@ static int fg_get_prop_capacity(struct fg_chip *chip, int *val)
{
int rc, msoc;
+ if (chip->charge_full) {
+ *val = FULL_CAPACITY;
+ return 0;
+ }
+
rc = fg_get_msoc_raw(chip, &msoc);
if (rc < 0)
return rc;
@@ -1059,7 +1079,7 @@ static int fg_cap_learning_done(struct fg_chip *chip)
cc_soc_sw = CC_SOC_30BIT;
rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word,
chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw,
- chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_DEFAULT);
+ chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC);
if (rc < 0) {
pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
goto out;
@@ -1092,6 +1112,9 @@ static void fg_cap_learning_update(struct fg_chip *chip)
goto out;
}
+ /* We need only the most significant byte here */
+ batt_soc = (u32)batt_soc >> 24;
+
fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n",
chip->status, chip->cl.active, batt_soc);
@@ -1103,8 +1126,7 @@ static void fg_cap_learning_update(struct fg_chip *chip)
}
} else {
- if (chip->status == POWER_SUPPLY_STATUS_FULL &&
- chip->charge_done) {
+ if (chip->charge_done) {
rc = fg_cap_learning_done(chip);
if (rc < 0)
pr_err("Error in completing capacity learning, rc=%d\n",
@@ -1126,19 +1148,211 @@ out:
mutex_unlock(&chip->cl.lock);
}
+#define KI_COEFF_MED_DISCHG_DEFAULT 1500
+#define KI_COEFF_HI_DISCHG_DEFAULT 2200
+static int fg_adjust_ki_coeff_dischg(struct fg_chip *chip)
+{
+ int rc, i, msoc;
+ int ki_coeff_med = KI_COEFF_MED_DISCHG_DEFAULT;
+ int ki_coeff_hi = KI_COEFF_HI_DISCHG_DEFAULT;
+ u8 val;
+
+ if (!chip->ki_coeff_dischg_en)
+ return 0;
+
+ rc = fg_get_prop_capacity(chip, &msoc);
+ if (rc < 0) {
+ pr_err("Error in getting capacity, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ for (i = KI_COEFF_SOC_LEVELS - 1; i >= 0; i--) {
+ if (msoc < chip->dt.ki_coeff_soc[i]) {
+ ki_coeff_med = chip->dt.ki_coeff_med_dischg[i];
+ ki_coeff_hi = chip->dt.ki_coeff_hi_dischg[i];
+ }
+ }
+ }
+
+ fg_encode(chip->sp, FG_SRAM_KI_COEFF_MED_DISCHG, ki_coeff_med, &val);
+ rc = fg_sram_write(chip,
+ chip->sp[FG_SRAM_KI_COEFF_MED_DISCHG].addr_word,
+ chip->sp[FG_SRAM_KI_COEFF_MED_DISCHG].addr_byte, &val,
+ chip->sp[FG_SRAM_KI_COEFF_MED_DISCHG].len,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing ki_coeff_med, rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_encode(chip->sp, FG_SRAM_KI_COEFF_HI_DISCHG, ki_coeff_hi, &val);
+ rc = fg_sram_write(chip,
+ chip->sp[FG_SRAM_KI_COEFF_HI_DISCHG].addr_word,
+ chip->sp[FG_SRAM_KI_COEFF_HI_DISCHG].addr_byte, &val,
+ chip->sp[FG_SRAM_KI_COEFF_HI_DISCHG].len,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing ki_coeff_hi, rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_dbg(chip, FG_STATUS, "Wrote ki_coeff_med %d ki_coeff_hi %d\n",
+ ki_coeff_med, ki_coeff_hi);
+ return 0;
+}
+
+static int fg_charge_full_update(struct fg_chip *chip)
+{
+ union power_supply_propval prop = {0, };
+ int rc, msoc, bsoc, recharge_soc;
+ u8 full_soc[2] = {0xFF, 0xFF};
+
+ if (!chip->dt.hold_soc_while_full)
+ return 0;
+
+ if (!is_charger_available(chip))
+ return 0;
+
+ rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_HEALTH,
+ &prop);
+ if (rc < 0) {
+ pr_err("Error in getting battery health, rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->health = prop.intval;
+ recharge_soc = chip->dt.recharge_soc_thr;
+ recharge_soc = DIV_ROUND_CLOSEST(recharge_soc * FULL_SOC_RAW,
+ FULL_CAPACITY);
+ rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &bsoc);
+ if (rc < 0) {
+ pr_err("Error in getting BATT_SOC, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* We need 2 most significant bytes here */
+ bsoc = (u32)bsoc >> 16;
+ rc = fg_get_prop_capacity(chip, &msoc);
+ if (rc < 0) {
+ pr_err("Error in getting capacity, rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_dbg(chip, FG_STATUS, "msoc: %d health: %d status: %d\n", msoc,
+ chip->health, chip->status);
+ if (chip->charge_done) {
+ if (msoc >= 99 && chip->health == POWER_SUPPLY_HEALTH_GOOD)
+ chip->charge_full = true;
+ else
+ fg_dbg(chip, FG_STATUS, "Terminated charging @ SOC%d\n",
+ msoc);
+ } else if ((bsoc >> 8) <= recharge_soc) {
+ fg_dbg(chip, FG_STATUS, "bsoc: %d recharge_soc: %d\n",
+ bsoc >> 8, recharge_soc);
+ chip->charge_full = false;
+ }
+
+ if (!chip->charge_full)
+ return 0;
+
+ /*
+ * During JEITA conditions, charge_full can happen early. FULL_SOC
+ * and MONOTONIC_SOC needs to be updated to reflect the same. Write
+ * battery SOC to FULL_SOC and write a full value to MONOTONIC_SOC.
+ */
+ rc = fg_sram_write(chip, FULL_SOC_WORD, FULL_SOC_OFFSET, (u8 *)&bsoc, 2,
+ FG_IMA_ATOMIC);
+ if (rc < 0) {
+ pr_err("failed to write full_soc rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_sram_write(chip, MONOTONIC_SOC_WORD, MONOTONIC_SOC_OFFSET,
+ full_soc, 2, FG_IMA_ATOMIC);
+ if (rc < 0) {
+ pr_err("failed to write monotonic_soc rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_dbg(chip, FG_STATUS, "Set charge_full to true @ soc %d\n", msoc);
+ return 0;
+}
+
+static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc)
+{
+ u8 buf[4];
+ int rc;
+
+ fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, recharge_soc, buf);
+ rc = fg_sram_write(chip,
+ chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word,
+ chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_byte, buf,
+ chip->sp[FG_SRAM_RECHARGE_SOC_THR].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing recharge_soc_thr, rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int fg_adjust_recharge_soc(struct fg_chip *chip)
+{
+ int rc, msoc, recharge_soc, new_recharge_soc = 0;
+
+ recharge_soc = chip->dt.recharge_soc_thr;
+ /*
+ * If the input is present and charging had been terminated, adjust
+ * the recharge SOC threshold based on the monotonic SOC at which
+ * the charge termination had happened.
+ */
+ if (is_input_present(chip) && !chip->recharge_soc_adjusted
+ && chip->charge_done) {
+ /* Get raw monotonic SOC for calculation */
+ rc = fg_get_msoc_raw(chip, &msoc);
+ if (rc < 0) {
+ pr_err("Error in getting msoc, rc=%d\n", rc);
+ return rc;
+ }
+
+ msoc = DIV_ROUND_CLOSEST(msoc * FULL_CAPACITY, FULL_SOC_RAW);
+ /* Adjust the recharge_soc threshold */
+ new_recharge_soc = msoc - (FULL_CAPACITY - recharge_soc);
+ } else if (chip->recharge_soc_adjusted && (!is_input_present(chip)
+ || chip->health == POWER_SUPPLY_HEALTH_GOOD)) {
+ /* Restore the default value */
+ new_recharge_soc = recharge_soc;
+ }
+
+ if (new_recharge_soc > 0 && new_recharge_soc < FULL_CAPACITY) {
+ rc = fg_set_recharge_soc(chip, new_recharge_soc);
+ if (rc) {
+ pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->recharge_soc_adjusted = (new_recharge_soc !=
+ recharge_soc);
+ fg_dbg(chip, FG_STATUS, "resume soc set to %d\n",
+ new_recharge_soc);
+ }
+
+ return 0;
+}
+
static void status_change_work(struct work_struct *work)
{
struct fg_chip *chip = container_of(work,
struct fg_chip, status_change_work);
union power_supply_propval prop = {0, };
- int prev_status, rc;
+ int rc;
if (!is_charger_available(chip)) {
fg_dbg(chip, FG_STATUS, "Charger not available?!\n");
goto out;
}
- prev_status = chip->status;
rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS,
&prop);
if (rc < 0) {
@@ -1155,14 +1369,25 @@ static void status_change_work(struct work_struct *work)
}
chip->charge_done = prop.intval;
- fg_dbg(chip, FG_POWER_SUPPLY, "prev_status: %d curr_status:%d charge_done: %d\n",
- prev_status, chip->status, chip->charge_done);
- if (prev_status != chip->status) {
- if (chip->cyc_ctr.en)
- schedule_work(&chip->cycle_count_work);
- fg_cap_learning_update(chip);
- }
+ fg_dbg(chip, FG_POWER_SUPPLY, "curr_status:%d charge_done: %d\n",
+ chip->status, chip->charge_done);
+ if (chip->cyc_ctr.en)
+ schedule_work(&chip->cycle_count_work);
+
+ fg_cap_learning_update(chip);
+
+ rc = fg_charge_full_update(chip);
+ if (rc < 0)
+ pr_err("Error in charge_full_update, rc=%d\n", rc);
+
+ rc = fg_adjust_recharge_soc(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting recharge_soc, rc=%d\n", rc);
+
+ rc = fg_adjust_ki_coeff_dischg(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc);
out:
pm_relax(chip->dev);
}
@@ -1247,6 +1472,9 @@ static void cycle_count_work(struct work_struct *work)
goto out;
}
+ /* We need only the most significant byte here */
+ batt_soc = (u32)batt_soc >> 24;
+
if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
/* Find out which bucket the SOC falls in */
bucket = batt_soc / BUCKET_SOC_PCT;
@@ -1787,16 +2015,9 @@ static int fg_hw_init(struct fg_chip *chip)
}
if (chip->dt.recharge_soc_thr > 0 && chip->dt.recharge_soc_thr < 100) {
- fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR,
- chip->dt.recharge_soc_thr, buf);
- rc = fg_sram_write(chip,
- chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word,
- chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_byte,
- buf, chip->sp[FG_SRAM_RECHARGE_SOC_THR].len,
- FG_IMA_DEFAULT);
+ rc = fg_set_recharge_soc(chip, chip->dt.recharge_soc_thr);
if (rc < 0) {
- pr_err("Error in writing recharge_soc_thr, rc=%d\n",
- rc);
+ pr_err("Error in setting recharge_soc, rc=%d\n", rc);
return rc;
}
}
@@ -1982,6 +2203,7 @@ static irqreturn_t fg_soc_update_irq_handler(int irq, void *data)
static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data)
{
struct fg_chip *chip = data;
+ int rc;
if (chip->cyc_ctr.en)
schedule_work(&chip->cycle_count_work);
@@ -1994,6 +2216,14 @@ static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data)
if (chip->cl.active)
fg_cap_learning_update(chip);
+ rc = fg_charge_full_update(chip);
+ if (rc < 0)
+ pr_err("Error in charge_full_update, rc=%d\n", rc);
+
+ rc = fg_adjust_ki_coeff_dischg(chip);
+ if (rc < 0)
+ pr_err("Error in adjusting ki_coeff_dischg, rc=%d\n", rc);
+
return IRQ_HANDLED;
}
@@ -2154,6 +2384,73 @@ static int fg_register_interrupts(struct fg_chip *chip)
return 0;
}
+static int fg_parse_ki_coefficients(struct fg_chip *chip)
+{
+ struct device_node *node = chip->dev->of_node;
+ int rc, i;
+
+ rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-soc-dischg",
+ sizeof(u32));
+ if (rc != KI_COEFF_SOC_LEVELS)
+ return 0;
+
+ rc = of_property_read_u32_array(node, "qcom,ki-coeff-soc-dischg",
+ chip->dt.ki_coeff_soc, KI_COEFF_SOC_LEVELS);
+ if (rc < 0) {
+ pr_err("Error in reading ki-coeff-soc-dischg, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-med-dischg",
+ sizeof(u32));
+ if (rc != KI_COEFF_SOC_LEVELS)
+ return 0;
+
+ rc = of_property_read_u32_array(node, "qcom,ki-coeff-med-dischg",
+ chip->dt.ki_coeff_med_dischg, KI_COEFF_SOC_LEVELS);
+ if (rc < 0) {
+ pr_err("Error in reading ki-coeff-med-dischg, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-hi-dischg",
+ sizeof(u32));
+ if (rc != KI_COEFF_SOC_LEVELS)
+ return 0;
+
+ rc = of_property_read_u32_array(node, "qcom,ki-coeff-hi-dischg",
+ chip->dt.ki_coeff_hi_dischg, KI_COEFF_SOC_LEVELS);
+ if (rc < 0) {
+ pr_err("Error in reading ki-coeff-hi-dischg, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ for (i = 0; i < KI_COEFF_SOC_LEVELS; i++) {
+ if (chip->dt.ki_coeff_soc[i] < 0 ||
+ chip->dt.ki_coeff_soc[i] > FULL_CAPACITY) {
+ pr_err("Error in ki_coeff_soc_dischg values\n");
+ return -EINVAL;
+ }
+
+ if (chip->dt.ki_coeff_med_dischg[i] < 0 ||
+ chip->dt.ki_coeff_med_dischg[i] > KI_COEFF_MAX) {
+ pr_err("Error in ki_coeff_med_dischg values\n");
+ return -EINVAL;
+ }
+
+ if (chip->dt.ki_coeff_med_dischg[i] < 0 ||
+ chip->dt.ki_coeff_med_dischg[i] > KI_COEFF_MAX) {
+ pr_err("Error in ki_coeff_med_dischg values\n");
+ return -EINVAL;
+ }
+ }
+ chip->ki_coeff_dischg_en = true;
+ return 0;
+}
+
#define DEFAULT_CUTOFF_VOLT_MV 3200
#define DEFAULT_EMPTY_VOLT_MV 3100
#define DEFAULT_CHG_TERM_CURR_MA 100
@@ -2416,6 +2713,13 @@ static int fg_parse_dt(struct fg_chip *chip)
else if (temp > BTEMP_DELTA_LOW && temp <= BTEMP_DELTA_HIGH)
chip->dt.batt_temp_delta = temp;
+ chip->dt.hold_soc_while_full = of_property_read_bool(node,
+ "qcom,hold-soc-while-full");
+
+ rc = fg_parse_ki_coefficients(chip);
+ if (rc < 0)
+ pr_err("Error in parsing Ki coefficients, rc=%d\n", rc);
+
return 0;
}
diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c
index ee576d300054..8aaeb095db3c 100644
--- a/drivers/power/qcom-charger/qpnp-smb2.c
+++ b/drivers/power/qcom-charger/qpnp-smb2.c
@@ -342,6 +342,7 @@ static enum power_supply_property smb2_usb_props[] = {
POWER_SUPPLY_PROP_PD_ACTIVE,
POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
+ POWER_SUPPLY_PROP_PARALLEL_DISABLE,
};
static int smb2_usb_get_prop(struct power_supply *psy,
@@ -404,6 +405,10 @@ static int smb2_usb_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW:
rc = smblib_get_prop_usb_current_now(chg, val);
break;
+ case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
+ val->intval = get_client_vote(chg->pl_disable_votable,
+ USER_VOTER);
+ break;
default:
pr_err("get prop %d is not supported\n", psp);
rc = -EINVAL;
@@ -448,6 +453,9 @@ static int smb2_usb_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_PD_ACTIVE:
rc = smblib_set_prop_pd_active(chg, val);
break;
+ case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
+ vote(chg->pl_disable_votable, USER_VOTER, (bool)val->intval, 0);
+ break;
default:
pr_err("set prop %d is not supported\n", psp);
rc = -EINVAL;
@@ -462,6 +470,7 @@ static int smb2_usb_prop_is_writeable(struct power_supply *psy,
{
switch (psp) {
case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE:
+ case POWER_SUPPLY_PROP_PARALLEL_DISABLE:
return 1;
default:
break;
@@ -678,7 +687,7 @@ static int smb2_batt_get_prop(struct power_supply *psy,
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_CHARGE_DONE:
- val->intval = chg->chg_done;
+ rc = smblib_get_prop_batt_charge_done(chg, val);
break;
default:
pr_err("batt power supply prop %d not supported\n", psp);
@@ -710,9 +719,6 @@ static int smb2_batt_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CAPACITY:
rc = smblib_set_prop_batt_capacity(chg, val);
break;
- case POWER_SUPPLY_PROP_CHARGE_DONE:
- chg->chg_done = val->intval;
- break;
default:
rc = -EINVAL;
}
diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c
index 0067ec5c2ca2..ce76260be6f6 100644
--- a/drivers/power/qcom-charger/smb-lib.c
+++ b/drivers/power/qcom-charger/smb-lib.c
@@ -1165,6 +1165,24 @@ int smblib_get_prop_step_chg_step(struct smb_charger *chg,
return rc;
}
+int smblib_get_prop_batt_charge_done(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+ u8 stat;
+
+ rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ stat = stat & BATTERY_CHARGER_STATUS_MASK;
+ val->intval = (stat == TERMINATE_CHARGE);
+ return 0;
+}
+
/***********************
* BATTERY PSY SETTERS *
***********************/
@@ -1749,6 +1767,22 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
return rc;
}
+/************************
+ * PARALLEL PSY GETTERS *
+ ************************/
+
+int smblib_get_prop_slave_current_now(struct smb_charger *chg,
+ union power_supply_propval *pval)
+{
+ if (IS_ERR_OR_NULL(chg->iio.batt_i_chan))
+ chg->iio.batt_i_chan = iio_channel_get(chg->dev, "batt_i");
+
+ if (IS_ERR(chg->iio.batt_i_chan))
+ return PTR_ERR(chg->iio.batt_i_chan);
+
+ return iio_read_channel_processed(chg->iio.batt_i_chan, &pval->intval);
+}
+
/**********************
* INTERRUPT HANDLERS *
**********************/
@@ -1793,7 +1827,6 @@ static void smblib_pl_handle_chg_state_change(struct smb_charger *chg, u8 stat)
irqreturn_t smblib_handle_chg_state_change(int irq, void *data)
{
- union power_supply_propval pval = {0, };
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
u8 stat;
@@ -1810,9 +1843,6 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data)
stat = stat & BATTERY_CHARGER_STATUS_MASK;
smblib_pl_handle_chg_state_change(chg, stat);
- pval.intval = (stat == TERMINATE_CHARGE);
- power_supply_set_property(chg->batt_psy, POWER_SUPPLY_PROP_CHARGE_DONE,
- &pval);
power_supply_changed(chg->batt_psy);
return IRQ_HANDLED;
}
@@ -2440,6 +2470,8 @@ static void smblib_iio_deinit(struct smb_charger *chg)
iio_channel_release(chg->iio.usbin_i_chan);
if (!IS_ERR_OR_NULL(chg->iio.usbin_v_chan))
iio_channel_release(chg->iio.usbin_v_chan);
+ if (!IS_ERR_OR_NULL(chg->iio.batt_i_chan))
+ iio_channel_release(chg->iio.batt_i_chan);
}
int smblib_init(struct smb_charger *chg)
diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h
index 5b4c2016adc8..00975e6c1285 100644
--- a/drivers/power/qcom-charger/smb-lib.h
+++ b/drivers/power/qcom-charger/smb-lib.h
@@ -103,6 +103,7 @@ struct smb_iio {
struct iio_channel *temp_max_chan;
struct iio_channel *usbin_i_chan;
struct iio_channel *usbin_v_chan;
+ struct iio_channel *batt_i_chan;
};
struct smb_charger {
@@ -233,6 +234,8 @@ int smblib_get_prop_batt_status(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_batt_charge_type(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_batt_charge_done(struct smb_charger *chg,
+ union power_supply_propval *val);
int smblib_get_prop_batt_health(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_system_temp_level(struct smb_charger *chg,
@@ -301,6 +304,9 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg,
int smblib_set_prop_pd_active(struct smb_charger *chg,
const union power_supply_propval *val);
+int smblib_get_prop_slave_current_now(struct smb_charger *chg,
+ union power_supply_propval *val);
+
int smblib_init(struct smb_charger *chg);
int smblib_deinit(struct smb_charger *chg);
#endif /* __SMB2_CHARGER_H */
diff --git a/drivers/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c
index 33d759be9aeb..e8ec2f49f7eb 100644
--- a/drivers/power/qcom-charger/smb138x-charger.c
+++ b/drivers/power/qcom-charger/smb138x-charger.c
@@ -48,8 +48,8 @@ static struct smb_params v1_params = {
.name = "fast charge current",
.reg = FAST_CHARGE_CURRENT_CFG_REG,
.min_u = 0,
- .max_u = 5000000,
- .step_u = 50000,
+ .max_u = 4500000,
+ .step_u = 25000,
},
.fv = {
.name = "float voltage",
@@ -395,6 +395,7 @@ static enum power_supply_property smb138x_parallel_props[] = {
POWER_SUPPLY_PROP_INPUT_SUSPEND,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGER_TEMP,
POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
};
@@ -431,6 +432,9 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
rc = smblib_get_charge_param(chg, &chg->param.fcc,
&val->intval);
break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ rc = smblib_get_prop_slave_current_now(chg, val);
+ break;
case POWER_SUPPLY_PROP_CHARGER_TEMP:
rc = smblib_get_prop_charger_temp(chg, val);
break;
@@ -1125,6 +1129,15 @@ static int smb138x_slave_probe(struct smb138x *chip)
return rc;
}
+ /* enable parallel current sensing */
+ rc = smblib_masked_write(chg, CFG_REG,
+ VCHG_EN_CFG_BIT, VCHG_EN_CFG_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't enable parallel current sensing rc=%d\n",
+ rc);
+ return rc;
+ }
+
/* keep at the end of probe, ready to serve before notifying others */
rc = smb138x_init_parallel_psy(chip);
if (rc < 0) {
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index fbf6197de0f8..873b4615d4a9 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1498,10 +1498,14 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
/* M-PHY RMMI interface clocks can be turned off */
ufs_qcom_phy_disable_iface_clk(host->generic_phy);
- if (!ufs_qcom_is_link_active(hba)) {
- if (!is_gating_context)
- /* turn off UFS local PHY ref_clk */
- ufs_qcom_phy_disable_ref_clk(host->generic_phy);
+ /*
+ * If auto hibern8 is supported then the link will already
+ * be in hibern8 state and the ref clock can be gated.
+ */
+ if (ufshcd_is_auto_hibern8_supported(hba) ||
+ !ufs_qcom_is_link_active(hba)) {
+ /* turn off UFS local PHY ref_clk */
+ ufs_qcom_phy_disable_ref_clk(host->generic_phy);
/* disable device ref_clk */
ufs_qcom_dev_ref_clk_ctrl(host, false);
}
@@ -1956,13 +1960,6 @@ static int ufs_qcom_init(struct ufs_hba *hba)
host->hba = hba;
ufshcd_set_variant(hba, host);
- /*
- * voting/devoting device ref_clk source is time consuming hence
- * skip devoting it during aggressive clock gating. This clock
- * will still be gated off during runtime suspend.
- */
- hba->no_ref_clk_gating = true;
-
err = ufs_qcom_ice_get_dev(host);
if (err == -EPROBE_DEFER) {
/*
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 29d2f9562df3..862d56e78086 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1181,6 +1181,12 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
return ret;
}
+static inline void ufshcd_cancel_gate_work(struct ufs_hba *hba)
+{
+ hrtimer_cancel(&hba->clk_gating.gate_hrtimer);
+ cancel_work_sync(&hba->clk_gating.gate_work);
+}
+
static void ufshcd_ungate_work(struct work_struct *work)
{
int ret;
@@ -1188,7 +1194,7 @@ static void ufshcd_ungate_work(struct work_struct *work)
struct ufs_hba *hba = container_of(work, struct ufs_hba,
clk_gating.ungate_work);
- cancel_delayed_work_sync(&hba->clk_gating.gate_work);
+ ufshcd_cancel_gate_work(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
if (hba->clk_gating.state == CLKS_ON) {
@@ -1259,14 +1265,18 @@ start:
}
break;
case REQ_CLKS_OFF:
- if (cancel_delayed_work(&hba->clk_gating.gate_work)) {
+ /*
+ * If the timer was active but the callback was not running
+ * we have nothing to do, just change state and return.
+ */
+ if (hrtimer_try_to_cancel(&hba->clk_gating.gate_hrtimer) == 1) {
hba->clk_gating.state = CLKS_ON;
trace_ufshcd_clk_gating(dev_name(hba->dev),
hba->clk_gating.state);
break;
}
/*
- * If we here, it means gating work is either done or
+ * If we are here, it means gating work is either done or
* currently running. Hence, fall through to cancel gating
* work and to enable clocks.
*/
@@ -1306,7 +1316,7 @@ EXPORT_SYMBOL_GPL(ufshcd_hold);
static void ufshcd_gate_work(struct work_struct *work)
{
struct ufs_hba *hba = container_of(work, struct ufs_hba,
- clk_gating.gate_work.work);
+ clk_gating.gate_work);
unsigned long flags;
spin_lock_irqsave(hba->host->host_lock, flags);
@@ -1351,7 +1361,12 @@ static void ufshcd_gate_work(struct work_struct *work)
ufshcd_set_link_hibern8(hba);
}
- if (!ufshcd_is_link_active(hba) && !hba->no_ref_clk_gating)
+ /*
+ * If auto hibern8 is supported then the link will already
+ * be in hibern8 state and the ref clock can be gated.
+ */
+ if ((ufshcd_is_auto_hibern8_supported(hba) ||
+ !ufshcd_is_link_active(hba)) && !hba->no_ref_clk_gating)
ufshcd_disable_clocks(hba, true);
else
/* If link is active, device ref_clk can't be switched off */
@@ -1399,8 +1414,9 @@ static void __ufshcd_release(struct ufs_hba *hba, bool no_sched)
hba->clk_gating.state = REQ_CLKS_OFF;
trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state);
- schedule_delayed_work(&hba->clk_gating.gate_work,
- msecs_to_jiffies(hba->clk_gating.delay_ms));
+ hrtimer_start(&hba->clk_gating.gate_hrtimer,
+ ms_to_ktime(hba->clk_gating.delay_ms),
+ HRTIMER_MODE_REL);
}
void ufshcd_release(struct ufs_hba *hba, bool no_sched)
@@ -1528,6 +1544,17 @@ out:
return count;
}
+static enum hrtimer_restart ufshcd_clkgate_hrtimer_handler(
+ struct hrtimer *timer)
+{
+ struct ufs_hba *hba = container_of(timer, struct ufs_hba,
+ clk_gating.gate_hrtimer);
+
+ schedule_work(&hba->clk_gating.gate_work);
+
+ return HRTIMER_NORESTART;
+}
+
static void ufshcd_init_clk_gating(struct ufs_hba *hba)
{
struct ufs_clk_gating *gating = &hba->clk_gating;
@@ -1544,27 +1571,25 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba)
if (ufshcd_is_auto_hibern8_supported(hba))
hba->caps &= ~UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
- INIT_DELAYED_WORK(&gating->gate_work, ufshcd_gate_work);
+ INIT_WORK(&gating->gate_work, ufshcd_gate_work);
INIT_WORK(&gating->ungate_work, ufshcd_ungate_work);
+ /*
+ * Clock gating work must be executed only after auto hibern8
+ * timeout has expired in the hardware or after aggressive
+ * hibern8 on idle software timeout. Using jiffy based low
+ * resolution delayed work is not reliable to guarantee this,
+ * hence use a high resolution timer to make sure we schedule
+ * the gate work precisely more than hibern8 timeout.
+ *
+ * Always make sure gating->delay_ms > hibern8_on_idle->delay_ms
+ */
+ hrtimer_init(&gating->gate_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ gating->gate_hrtimer.function = ufshcd_clkgate_hrtimer_handler;
gating->is_enabled = true;
- /*
- * Scheduling the delayed work after 1 jiffies will make the work to
- * get schedule any time from 0ms to 1000/HZ ms which is not desirable
- * for hibern8 enter work as it may impact the performance if it gets
- * scheduled almost immediately. Hence make sure that hibern8 enter
- * work gets scheduled atleast after 2 jiffies (any time between
- * 1000/HZ ms to 2000/HZ ms).
- */
- gating->delay_ms_pwr_save = jiffies_to_msecs(
- max_t(unsigned long,
- msecs_to_jiffies(UFSHCD_CLK_GATING_DELAY_MS_PWR_SAVE),
- 2));
- gating->delay_ms_perf = jiffies_to_msecs(
- max_t(unsigned long,
- msecs_to_jiffies(UFSHCD_CLK_GATING_DELAY_MS_PERF),
- 2));
+ gating->delay_ms_pwr_save = UFSHCD_CLK_GATING_DELAY_MS_PWR_SAVE;
+ gating->delay_ms_perf = UFSHCD_CLK_GATING_DELAY_MS_PERF;
/* start with performance mode */
gating->delay_ms = gating->delay_ms_perf;
@@ -1621,8 +1646,8 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
device_remove_file(hba->dev, &hba->clk_gating.delay_attr);
}
device_remove_file(hba->dev, &hba->clk_gating.enable_attr);
+ ufshcd_cancel_gate_work(hba);
cancel_work_sync(&hba->clk_gating.ungate_work);
- cancel_delayed_work_sync(&hba->clk_gating.gate_work);
}
static void ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba, u32 delay)
@@ -1933,6 +1958,7 @@ static void ufshcd_init_hibern8_on_idle(struct ufs_hba *hba)
return;
if (ufshcd_is_auto_hibern8_supported(hba)) {
+ hba->hibern8_on_idle.delay_ms = 1;
hba->hibern8_on_idle.state = AUTO_HIBERN8;
/*
* Disable SW hibern8 enter on idle in case
@@ -1940,13 +1966,13 @@ static void ufshcd_init_hibern8_on_idle(struct ufs_hba *hba)
*/
hba->caps &= ~UFSHCD_CAP_HIBERN8_ENTER_ON_IDLE;
} else {
+ hba->hibern8_on_idle.delay_ms = 10;
INIT_DELAYED_WORK(&hba->hibern8_on_idle.enter_work,
ufshcd_hibern8_enter_work);
INIT_WORK(&hba->hibern8_on_idle.exit_work,
ufshcd_hibern8_exit_work);
}
- hba->hibern8_on_idle.delay_ms = 10;
hba->hibern8_on_idle.is_enabled = true;
hba->hibern8_on_idle.delay_attr.show =
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index a3e1353ffad9..c0714b7bea72 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -39,6 +39,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/hrtimer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -396,8 +397,9 @@ enum clk_gating_state {
/**
* struct ufs_clk_gating - UFS clock gating related info
- * @gate_work: worker to turn off clocks after some delay as specified in
- * delay_ms
+ * @gate_hrtimer: hrtimer to invoke @gate_work after some delay as
+ * specified in @delay_ms
+ * @gate_work: worker to turn off clocks
* @ungate_work: worker to turn on clocks that will be used in case of
* interrupt context
* @state: the current clocks state
@@ -415,7 +417,8 @@ enum clk_gating_state {
* completion before gating clocks.
*/
struct ufs_clk_gating {
- struct delayed_work gate_work;
+ struct hrtimer gate_hrtimer;
+ struct work_struct gate_work;
struct work_struct ungate_work;
enum clk_gating_state state;
unsigned long delay_ms;
diff --git a/drivers/soc/qcom/core_hang_detect.c b/drivers/soc/qcom/core_hang_detect.c
index e9b7f612dccc..c88d4c34eecf 100644
--- a/drivers/soc/qcom/core_hang_detect.c
+++ b/drivers/soc/qcom/core_hang_detect.c
@@ -245,7 +245,9 @@ static int msm_hang_detect_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
struct hang_detect *hang_det = NULL;
int cpu, ret, cpu_count = 0;
- u32 treg[NR_CPUS], creg[NR_CPUS];
+ const char *name;
+ u32 treg[NR_CPUS] = {0}, creg[NR_CPUS] = {0};
+ u32 num_reg = 0;
if (!pdev->dev.of_node || !enable)
return -ENODEV;
@@ -258,15 +260,28 @@ static int msm_hang_detect_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ name = of_get_property(node, "label", NULL);
+ if (!name) {
+ pr_err("Can't get label property\n");
+ return -EINVAL;
+ }
+
+ num_reg = of_property_count_u32_elems(node,
+ "qcom,threshold-arr");
+ if (num_reg < 0) {
+ pr_err("Can't get threshold-arr property\n");
+ return -EINVAL;
+ }
+
ret = of_property_read_u32_array(node, "qcom,threshold-arr",
- treg, num_possible_cpus());
+ treg, num_reg);
if (ret) {
pr_err("Can't get threshold-arr property\n");
return -EINVAL;
}
ret = of_property_read_u32_array(node, "qcom,config-arr",
- creg, num_possible_cpus());
+ creg, num_reg);
if (ret) {
pr_err("Can't get config-arr property\n");
return -EINVAL;
@@ -289,7 +304,8 @@ static int msm_hang_detect_probe(struct platform_device *pdev)
}
ret = kobject_init_and_add(&hang_det->kobj, &core_ktype,
- &cpu_subsys.dev_root->kobj, "%s", "hang_detect");
+ &cpu_subsys.dev_root->kobj, "%s_%s",
+ "hang_detect", name);
if (ret) {
pr_err("%s:Error in creation kobject_add\n", __func__);
goto out_put_kobj;
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index b7fa71dd0695..9cfca014c8ad 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -2583,6 +2583,7 @@ void *glink_open(const struct glink_open_config *cfg)
ctx->notify_tx_abort = cfg->notify_tx_abort;
ctx->notify_rx_tracer_pkt = cfg->notify_rx_tracer_pkt;
ctx->notify_remote_rx_intent = cfg->notify_remote_rx_intent;
+ ctx->magic_number = GLINK_CTX_CANARY;
if (!ctx->notify_rx_intent_req)
ctx->notify_rx_intent_req = glink_dummy_notify_rx_intent_req;
@@ -2618,7 +2619,6 @@ void *glink_open(const struct glink_open_config *cfg)
GLINK_INFO_CH(ctx, "%s: Created channel, sent OPEN command. ctx %p\n",
__func__, ctx);
- ctx->magic_number = GLINK_CTX_CANARY;
return ctx;
}
EXPORT_SYMBOL(glink_open);
@@ -5380,7 +5380,7 @@ static int glink_scheduler_tx(struct channel_ctx *ctx,
size_t txd_len = 0;
size_t tx_len = 0;
uint32_t num_pkts = 0;
- int ret;
+ int ret = 0;
spin_lock_irqsave(&ctx->tx_lists_lock_lhc3, flags);
while (txd_len < xprt_ctx->mtu &&
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 2aa588ba610b..f47d4a51fccd 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -33,7 +33,6 @@
#include <linux/dma-mapping.h>
#include <linux/qmi_encdec.h>
#include <linux/ipc_logging.h>
-#include <linux/msm-bus.h>
#include <linux/thread_info.h>
#include <linux/uaccess.h>
#include <linux/qpnp/qpnp-adc.h>
@@ -411,8 +410,6 @@ static struct icnss_priv {
size_t smmu_iova_len;
dma_addr_t smmu_iova_ipa_start;
size_t smmu_iova_ipa_len;
- struct msm_bus_scale_pdata *bus_scale_table;
- uint32_t bus_client;
struct qmi_handle *wlfw_clnt;
struct list_head event_list;
spinlock_t event_lock;
@@ -3410,62 +3407,6 @@ unsigned int icnss_socinfo_get_serial_number(struct device *dev)
}
EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
-static int icnss_bw_vote(struct icnss_priv *priv, int index)
-{
- int ret = 0;
-
- icnss_pr_dbg("Vote %d for msm_bus, state 0x%lx\n",
- index, priv->state);
- ret = msm_bus_scale_client_update_request(priv->bus_client, index);
- if (ret)
- icnss_pr_err("Fail to vote %d: ret %d, state 0x%lx\n",
- index, ret, priv->state);
-
- return ret;
-}
-
-static int icnss_bw_init(struct icnss_priv *priv)
-{
- int ret = 0;
-
- priv->bus_scale_table = msm_bus_cl_get_pdata(priv->pdev);
- if (!priv->bus_scale_table) {
- icnss_pr_err("Missing entry for msm_bus scale table\n");
- return -EINVAL;
- }
-
- priv->bus_client = msm_bus_scale_register_client(priv->bus_scale_table);
- if (!priv->bus_client) {
- icnss_pr_err("Fail to register with bus_scale client\n");
- ret = -EINVAL;
- goto out;
- }
-
- ret = icnss_bw_vote(priv, 1);
- if (ret)
- goto out;
-
- return 0;
-
-out:
- msm_bus_cl_clear_pdata(priv->bus_scale_table);
- return ret;
-}
-
-static void icnss_bw_deinit(struct icnss_priv *priv)
-{
- if (!priv)
- return;
-
- if (priv->bus_client) {
- icnss_bw_vote(priv, 0);
- msm_bus_scale_unregister_client(priv->bus_client);
- }
-
- if (priv->bus_scale_table)
- msm_bus_cl_clear_pdata(priv->bus_scale_table);
-}
-
static int icnss_smmu_init(struct icnss_priv *priv)
{
struct dma_iommu_mapping *mapping;
@@ -4419,10 +4360,6 @@ static int icnss_probe(struct platform_device *pdev)
priv->smmu_iova_len);
goto out;
}
-
- ret = icnss_bw_init(priv);
- if (ret)
- goto out_smmu_deinit;
}
spin_lock_init(&priv->event_lock);
@@ -4432,7 +4369,7 @@ static int icnss_probe(struct platform_device *pdev)
if (!priv->event_wq) {
icnss_pr_err("Workqueue creation failed\n");
ret = -EFAULT;
- goto out_bw_deinit;
+ goto out_smmu_deinit;
}
INIT_WORK(&priv->event_work, icnss_driver_event_work);
@@ -4460,8 +4397,6 @@ static int icnss_probe(struct platform_device *pdev)
out_destroy_wq:
destroy_workqueue(priv->event_wq);
-out_bw_deinit:
- icnss_bw_deinit(priv);
out_smmu_deinit:
icnss_smmu_deinit(priv);
out:
@@ -4487,8 +4422,6 @@ static int icnss_remove(struct platform_device *pdev)
if (penv->event_wq)
destroy_workqueue(penv->event_wq);
- icnss_bw_deinit(penv);
-
icnss_hw_power_off(penv);
dev_set_drvdata(&pdev->dev, NULL);
diff --git a/drivers/soc/qcom/msm_smem.c b/drivers/soc/qcom/msm_smem.c
index 881359d444fc..cd3d387645fd 100644
--- a/drivers/soc/qcom/msm_smem.c
+++ b/drivers/soc/qcom/msm_smem.c
@@ -79,6 +79,7 @@ static int spinlocks_initialized;
static void *smem_ramdump_dev;
static DEFINE_MUTEX(spinlock_init_lock);
static DEFINE_SPINLOCK(smem_init_check_lock);
+static struct device *smem_dev;
static int smem_module_inited;
static RAW_NOTIFIER_HEAD(smem_module_init_notifier_list);
static DEFINE_MUTEX(smem_module_init_notifier_lock);
@@ -1047,7 +1048,8 @@ static __init int modem_restart_late_init(void)
void *handle;
struct restart_notifier_block *nb;
- smem_ramdump_dev = create_ramdump_device("smem", NULL);
+ if (smem_dev)
+ smem_ramdump_dev = create_ramdump_device("smem", smem_dev);
if (IS_ERR_OR_NULL(smem_ramdump_dev)) {
LOG_ERR("%s: Unable to create smem ramdump device.\n",
__func__);
@@ -1444,7 +1446,7 @@ smem_targ_info_done:
SMEM_INFO("smem security enabled\n");
smem_init_security();
}
-
+ smem_dev = &pdev->dev;
probe_done = true;
ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c
index 67c58d1e6d4c..50dd9256b270 100644
--- a/drivers/soc/qcom/qdsp6v2/voice_svc.c
+++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c
@@ -223,8 +223,8 @@ static int voice_svc_send_req(struct voice_svc_cmd_request *apr_request,
} else if (!strcmp(apr_request->svc_name, VOICE_SVC_MVM_STR)) {
apr_handle = prtd->apr_q6_mvm;
} else {
- pr_err("%s: Invalid service %s\n", __func__,
- apr_request->svc_name);
+ pr_err("%s: Invalid service %.*s\n", __func__,
+ MAX_APR_SERVICE_NAME_LEN, apr_request->svc_name);
ret = -EINVAL;
goto done;
@@ -338,8 +338,8 @@ static int process_reg_cmd(struct voice_svc_register *apr_reg_svc,
svc = VOICE_SVC_CVS_STR;
handle = &prtd->apr_q6_cvs;
} else {
- pr_err("%s: Invalid Service: %s\n", __func__,
- apr_reg_svc->svc_name);
+ pr_err("%s: Invalid Service: %.*s\n", __func__,
+ MAX_APR_SERVICE_NAME_LEN, apr_reg_svc->svc_name);
ret = -EINVAL;
goto done;
}
@@ -365,7 +365,17 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
pr_debug("%s\n", __func__);
- data = kmalloc(count, GFP_KERNEL);
+ /*
+ * Check if enough memory is allocated to parse the message type.
+ * Will check there is enough to hold the payload later.
+ */
+ if (count >= sizeof(struct voice_svc_write_msg)) {
+ data = kmalloc(count, GFP_KERNEL);
+ } else {
+ pr_debug("%s: invalid data size\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
if (data == NULL) {
pr_err("%s: data kmalloc failed.\n", __func__);
@@ -383,7 +393,7 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
}
cmd = data->msg_type;
- prtd = (struct voice_svc_prvt *)file->private_data;
+ prtd = (struct voice_svc_prvt *) file->private_data;
if (prtd == NULL) {
pr_err("%s: prtd is NULL\n", __func__);
@@ -393,9 +403,13 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
switch (cmd) {
case MSG_REGISTER:
- if (count >=
- (sizeof(struct voice_svc_register) +
- sizeof(*data))) {
+ /*
+ * Check that count reflects the expected size to ensure
+ * sufficient memory was allocated. Since voice_svc_register
+ * has a static size, this should be exact.
+ */
+ if (count == (sizeof(struct voice_svc_write_msg) +
+ sizeof(struct voice_svc_register))) {
ret = process_reg_cmd(
(struct voice_svc_register *)data->payload, prtd);
if (!ret)
@@ -407,8 +421,13 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
}
break;
case MSG_REQUEST:
- if (count >= (sizeof(struct voice_svc_cmd_request) +
- sizeof(*data))) {
+ /*
+ * Check that count reflects the expected size to ensure
+ * sufficient memory was allocated. Since voice_svc_cmd_request
+ * has a variable size, check the minimum value count must be.
+ */
+ if (count >= (sizeof(struct voice_svc_write_msg) +
+ sizeof(struct voice_svc_cmd_request))) {
ret = voice_svc_send_req(
(struct voice_svc_cmd_request *)data->payload, prtd);
if (!ret)
diff --git a/drivers/soc/qcom/qsee_ipc_irq_bridge.c b/drivers/soc/qcom/qsee_ipc_irq_bridge.c
index ab43bbb7e86a..eee42d7ba314 100644
--- a/drivers/soc/qcom/qsee_ipc_irq_bridge.c
+++ b/drivers/soc/qcom/qsee_ipc_irq_bridge.c
@@ -115,10 +115,8 @@ static struct qiib_driver_data *qiib_info;
static int qiib_driver_data_init(void)
{
qiib_info = kzalloc(sizeof(*qiib_info), GFP_KERNEL);
- if (!qiib_info) {
- QIIB_ERR("Unable to allocate info pointer\n");
+ if (!qiib_info)
return -ENOMEM;
- }
INIT_LIST_HEAD(&qiib_info->list);
mutex_init(&qiib_info->list_lock);
@@ -356,6 +354,7 @@ static int qiib_parse_node(struct device_node *node, struct qiib_dev *devp)
const char *dev_name;
uint32_t irqtype;
uint32_t irq_clear[2];
+ struct irq_data *irqtype_data;
int ret = -ENODEV;
key = "qcom,dev-name";
@@ -374,7 +373,12 @@ static int qiib_parse_node(struct device_node *node, struct qiib_dev *devp)
}
QIIB_DBG("%s: %s = %d\n", __func__, key, devp->irq_line);
- irqtype = irqd_get_trigger_type(irq_get_irq_data(devp->irq_line));
+ irqtype_data = irq_get_irq_data(devp->irq_line);
+ if (!irqtype_data) {
+ QIIB_ERR("%s: get irqdata fail:%d\n", __func__, devp->irq_line);
+ goto missing_key;
+ }
+ irqtype = irqd_get_trigger_type(irqtype_data);
QIIB_DBG("%s: irqtype = %d\n", __func__, irqtype);
key = "label";
diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c
index 03a1591e5b09..242071f52811 100644
--- a/drivers/soc/qcom/rpm-smd.c
+++ b/drivers/soc/qcom/rpm-smd.c
@@ -967,8 +967,10 @@ static struct msm_rpm_request *msm_rpm_create_request_common(
cdata->client_buf = kzalloc(buf_size, GFP_FLAG(noirq));
- if (!cdata->client_buf)
- goto cdata_alloc_fail;
+ if (!cdata->client_buf) {
+ pr_warn("Cannot allocate memory for client_buf\n");
+ goto client_buf_alloc_fail;
+ }
set_set_type(cdata->client_buf, set);
set_rsc_type(cdata->client_buf, rsc_type);
@@ -997,6 +999,8 @@ static struct msm_rpm_request *msm_rpm_create_request_common(
buf_alloc_fail:
kfree(cdata->kvp);
kvp_alloc_fail:
+ kfree(cdata->client_buf);
+client_buf_alloc_fail:
kfree(cdata);
cdata_alloc_fail:
return NULL;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 4ad994972b19..805c5e1931e1 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -421,7 +421,16 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
if (dep->endpoint.ep_type == EP_TYPE_GSI)
return;
- if (dep->trb_pool && dep->trb_pool_dma) {
+ /*
+ * Clean up ep ring to avoid getting xferInProgress due to stale trbs
+ * with HWO bit set from previous composition when update transfer cmd
+ * is issued.
+ */
+ if (dep->number > 1 && dep->trb_pool && dep->trb_pool_dma) {
+ memset(&dep->trb_pool[0], 0,
+ sizeof(struct dwc3_trb) * dep->num_trbs);
+ dbg_event(dep->number, "Clr_TRB", 0);
+
dma_free_coherent(dwc->dev,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM, dep->trb_pool,
dep->trb_pool_dma);
@@ -723,17 +732,6 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
(dep->number & 1) ? "in" : "out");
}
- /*
- * Clean up ep ring of non-control endpoint to avoid getting xferInProgress
- * due to stale trbs with HWO bit set from previous composition when update
- * transfer cmd is issued.
- */
- if (dep->number > 1 && dep->trb_pool) {
- memset(&dep->trb_pool[0], 0,
- sizeof(struct dwc3_trb) * dep->num_trbs);
- dbg_event(dep->number, "Clr_TRB", 0);
- }
-
return 0;
}
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 8a0e7f988d25..0121bf7ca4ac 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -1111,7 +1111,7 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1;
opts->id = SNDRV_DEFAULT_STR1;
- opts->buflen = 256;
+ opts->buflen = 1024;
opts->qlen = 32;
opts->in_ports = 1;
opts->out_ports = 1;
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 12b98017beb2..26c91e0ce163 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -59,6 +59,7 @@ enum usbpd_state {
PE_PRS_SRC_SNK_SEND_SWAP,
PE_PRS_SRC_SNK_TRANSITION_TO_OFF,
PE_PRS_SRC_SNK_WAIT_SOURCE_ON,
+ PE_VCS_WAIT_FOR_VCONN,
};
static const char * const usbpd_state_strings[] = {
@@ -94,6 +95,7 @@ static const char * const usbpd_state_strings[] = {
"PRS_SRC_SNK_Send_Swap",
"PRS_SRC_SNK_Transition_to_off",
"PRS_SRC_SNK_Wait_Source_on",
+ "VCS_Wait_for_VCONN",
};
enum usbpd_control_msg_type {
@@ -168,7 +170,12 @@ static void *usbpd_ipc_log;
#define PS_HARD_RESET_TIME 25
#define PS_SOURCE_ON 400
#define PS_SOURCE_OFF 750
+#define SWAP_SOURCE_START_TIME 20
#define VDM_BUSY_TIME 50
+#define VCONN_ON_TIME 100
+
+/* tPSHardReset + tSafe0V + tSrcRecover + tSrcTurnOn */
+#define SNK_HARD_RESET_RECOVER_TIME (35 + 650 + 1000 + 275)
#define PD_CAPS_COUNT 50
@@ -191,7 +198,7 @@ static void *usbpd_ipc_log;
#define PD_RDO_MISMATCH(rdo) ((rdo) >> 26 & 1)
#define PD_RDO_USB_COMM(rdo) ((rdo) >> 25 & 1)
#define PD_RDO_NO_USB_SUSP(rdo) ((rdo) >> 24 & 1)
-#define PD_RDO_FIXED_CURR(rdo) ((rdo) >> 19 & 0x3FF)
+#define PD_RDO_FIXED_CURR(rdo) ((rdo) >> 10 & 0x3FF)
#define PD_RDO_FIXED_CURR_MINMAX(rdo) ((rdo) & 0x3FF)
#define PD_SRC_PDO_TYPE(pdo) (((pdo) >> 30) & 3)
@@ -219,9 +226,10 @@ static void *usbpd_ipc_log;
/* VDM header is the first 32-bit object following the 16-bit PD header */
#define VDM_HDR_SVID(hdr) ((hdr) >> 16)
-#define VDM_HDR_TYPE(hdr) ((hdr) & 0x8000)
-#define VDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3)
-#define VDM_HDR_CMD(hdr) ((hdr) & 0x1f)
+#define VDM_IS_SVDM(hdr) ((hdr) & 0x8000)
+#define SVDM_HDR_OBJ_POS(hdr) (((hdr) >> 8) & 0x7)
+#define SVDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3)
+#define SVDM_HDR_CMD(hdr) ((hdr) & 0x1f)
#define SVDM_HDR(svid, ver, obj, cmd_type, cmd) \
(((svid) << 16) | (1 << 15) | ((ver) << 13) \
@@ -249,15 +257,11 @@ static bool ss_dev = true;
module_param(ss_dev, bool, S_IRUSR | S_IWUSR);
static const u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */
-
-static const u32 default_snk_caps[] = { 0x2601905A, /* 5V @ 900mA */
- 0x0002D096, /* 9V @ 1.5A */
- 0x0003C064 }; /* 12V @ 1A */
+static const u32 default_snk_caps[] = { 0x2601905A }; /* 5V @ 900mA */
struct vdm_tx {
u32 data[7];
int size;
- struct list_head entry;
};
struct usbpd {
@@ -265,6 +269,7 @@ struct usbpd {
struct workqueue_struct *wq;
struct work_struct sm_work;
struct hrtimer timer;
+ bool sm_queued;
struct extcon_dev *extcon;
@@ -294,6 +299,7 @@ struct usbpd {
enum power_supply_typec_mode typec_mode;
enum power_supply_type psy_type;
bool vbus_present;
+ bool pd_allowed;
enum data_role current_dr;
enum power_role current_pr;
@@ -303,6 +309,7 @@ struct usbpd {
struct regulator *vbus;
struct regulator *vconn;
bool vconn_enabled;
+ bool vconn_is_external;
u8 tx_msgid;
u8 rx_msgid;
@@ -311,8 +318,9 @@ struct usbpd {
enum vdm_state vdm_state;
u16 *discovered_svids;
+ int num_svids;
+ struct vdm_tx *vdm_tx;
struct vdm_tx *vdm_tx_retry;
- struct list_head vdm_tx_queue;
struct list_head svid_handlers;
struct list_head instance;
@@ -436,6 +444,12 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos)
}
pd->requested_voltage = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000;
+
+ /* Can't sink more than 5V if VCONN is sourced from the VBUS input */
+ if (pd->vconn_enabled && !pd->vconn_is_external &&
+ pd->requested_voltage > 5000000)
+ return -ENOTSUPP;
+
pd->requested_current = curr;
pd->requested_pdo = pdo_pos;
pd->rdo = PD_RDO_FIXED(pdo_pos, 0, mismatch, 1, 1, curr / 10,
@@ -446,6 +460,7 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos)
static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps)
{
+ union power_supply_propval val;
u32 first_pdo = src_caps[0];
/* save the PDOs so userspace can further evaluate */
@@ -461,6 +476,10 @@ static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps)
pd->peer_pr_swap = PD_SRC_PDO_FIXED_PR_SWAP(first_pdo);
pd->peer_dr_swap = PD_SRC_PDO_FIXED_DR_SWAP(first_pdo);
+ val.intval = PD_SRC_PDO_FIXED_USB_SUSP(first_pdo);
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val);
+
/* Select the first PDO (vSafe5V) immediately. */
pd_select_pdo(pd, 1);
@@ -481,6 +500,17 @@ static void pd_send_hard_reset(struct usbpd *pd)
pd->hard_reset = true;
}
+static void kick_sm(struct usbpd *pd, int ms)
+{
+ pm_stay_awake(&pd->dev);
+ pd->sm_queued = true;
+
+ if (ms)
+ hrtimer_start(&pd->timer, ms_to_ktime(ms), HRTIMER_MODE_REL);
+ else
+ queue_work(pd->wq, &pd->sm_work);
+}
+
static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
{
if (type != HARD_RESET_SIG) {
@@ -493,7 +523,7 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
/* Force CC logic to source/sink to keep Rp/Rd unchanged */
set_power_role(pd, pd->current_pr);
pd->hard_reset = true;
- queue_work(pd->wq, &pd->sm_work);
+ kick_sm(pd, 0);
}
static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
@@ -530,6 +560,10 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
pd->rx_msgid = PD_MSG_HDR_ID(header);
+ /* discard Pings */
+ if (PD_MSG_HDR_TYPE(header) == MSG_PING && !len)
+ return;
+
/* check header's count field to see if it matches len */
if (PD_MSG_HDR_COUNT(header) != (len / 4)) {
usbpd_err(&pd->dev, "header count (%d) mismatch, len=%ld\n",
@@ -537,11 +571,18 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
return;
}
+ /* block until previous message has been consumed by usbpd_sm */
+ if (pd->rx_msg_type)
+ flush_work(&pd->sm_work);
+
pd->rx_msg_type = PD_MSG_HDR_TYPE(header);
pd->rx_msg_len = PD_MSG_HDR_COUNT(header);
memcpy(&pd->rx_payload, buf, len);
- queue_work(pd->wq, &pd->sm_work);
+ usbpd_dbg(&pd->dev, "received message: type(%d) len(%d)\n",
+ pd->rx_msg_type, pd->rx_msg_len);
+
+ kick_sm(pd, 0);
}
static void phy_shutdown(struct usbpd *pd)
@@ -583,16 +624,24 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->in_pr_swap = false;
set_power_role(pd, PR_NONE);
pd->typec_mode = POWER_SUPPLY_TYPEC_NONE;
- queue_work(pd->wq, &pd->sm_work);
+ kick_sm(pd, 0);
break;
/* Source states */
case PE_SRC_STARTUP:
if (pd->current_dr == DR_NONE) {
pd->current_dr = DR_DFP;
- /* Defer starting USB host mode until after PD */
+ /*
+ * Defer starting USB host mode until PE_SRC_READY or
+ * when PE_SRC_SEND_CAPABILITIES fails
+ */
}
+ /* Set CC back to DRP toggle for the next disconnect */
+ val.intval = POWER_SUPPLY_TYPEC_PR_DUAL;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val);
+
pd->rx_msg_len = 0;
pd->rx_msg_type = 0;
pd->rx_msgid = -1;
@@ -618,22 +667,24 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->pd_phy_opened = true;
}
- val.intval = 1;
- power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_ACTIVE, &val);
-
- pd->in_pr_swap = false;
pd->current_state = PE_SRC_SEND_CAPABILITIES;
- usbpd_dbg(&pd->dev, "Enter %s\n",
- usbpd_state_strings[pd->current_state]);
+ if (pd->in_pr_swap) {
+ kick_sm(pd, SWAP_SOURCE_START_TIME);
+ break;
+ }
+
/* fall-through */
case PE_SRC_SEND_CAPABILITIES:
- queue_work(pd->wq, &pd->sm_work);
+ kick_sm(pd, 0);
break;
case PE_SRC_NEGOTIATE_CAPABILITY:
- if (PD_RDO_OBJ_POS(pd->rdo) != 1) {
+ if (PD_RDO_OBJ_POS(pd->rdo) != 1 ||
+ PD_RDO_FIXED_CURR(pd->rdo) >
+ PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps) ||
+ PD_RDO_FIXED_CURR_MINMAX(pd->rdo) >
+ PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps)) {
/* send Reject */
ret = pd_send_msg(pd, MSG_REJECT, NULL, 0, SOP_MSG);
if (ret) {
@@ -703,6 +754,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
break;
case PE_SRC_TRANSITION_TO_DEFAULT:
+ pd->hard_reset = false;
+
if (pd->vconn_enabled)
regulator_disable(pd->vconn);
regulator_disable(pd->vbus);
@@ -727,13 +780,17 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
}
}
+ val.intval = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
usbpd_set_state(pd, PE_SRC_STARTUP);
break;
case PE_SRC_HARD_RESET:
case PE_SNK_HARD_RESET:
/* hard reset may sleep; handle it in the workqueue */
- queue_work(pd->wq, &pd->sm_work);
+ kick_sm(pd, 0);
break;
case PE_SRC_SEND_SOFT_RESET:
@@ -751,8 +808,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
}
/* wait for ACCEPT */
- hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME),
- HRTIMER_MODE_REL);
+ kick_sm(pd, SENDER_RESPONSE_TIME);
break;
/* Sink states */
@@ -772,9 +828,14 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
}
}
+ if (!pd->pd_allowed)
+ break;
+
+ /* Reset protocol layer */
+ pd->tx_msgid = 0;
+ pd->rx_msgid = -1;
pd->rx_msg_len = 0;
pd->rx_msg_type = 0;
- pd->rx_msgid = -1;
if (!pd->in_pr_swap) {
if (pd->pd_phy_opened) {
@@ -797,31 +858,16 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->pd_phy_opened = true;
}
- pd->in_pr_swap = false;
pd->current_voltage = 5000000;
- if (!pd->vbus_present) {
- /* can get here during a hard reset and we lost vbus */
- pd->current_state = PE_SNK_DISCOVERY;
- hrtimer_start(&pd->timer, ms_to_ktime(2000),
- HRTIMER_MODE_REL);
- break;
- }
-
- /*
- * If VBUS is already present go and skip ahead to
- * PE_SNK_WAIT_FOR_CAPABILITIES.
- */
pd->current_state = PE_SNK_WAIT_FOR_CAPABILITIES;
/* fall-through */
case PE_SNK_WAIT_FOR_CAPABILITIES:
if (pd->rx_msg_len && pd->rx_msg_type)
- queue_work(pd->wq, &pd->sm_work);
+ kick_sm(pd, 0);
else
- hrtimer_start(&pd->timer,
- ms_to_ktime(SINK_WAIT_CAP_TIME),
- HRTIMER_MODE_REL);
+ kick_sm(pd, SINK_WAIT_CAP_TIME);
break;
case PE_SNK_EVALUATE_CAPABILITY:
@@ -839,18 +885,19 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
case PE_SNK_SELECT_CAPABILITY:
ret = pd_send_msg(pd, MSG_REQUEST, &pd->rdo, 1, SOP_MSG);
- if (ret)
+ if (ret) {
usbpd_err(&pd->dev, "Error sending Request\n");
+ usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+ break;
+ }
/* wait for ACCEPT */
- hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME),
- HRTIMER_MODE_REL);
+ kick_sm(pd, SENDER_RESPONSE_TIME);
break;
case PE_SNK_TRANSITION_SINK:
/* wait for PS_RDY */
- hrtimer_start(&pd->timer, ms_to_ktime(PS_TRANSITION_TIME),
- HRTIMER_MODE_REL);
+ kick_sm(pd, PS_TRANSITION_TIME);
break;
case PE_SNK_READY:
@@ -859,6 +906,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
break;
case PE_SNK_TRANSITION_TO_DEFAULT:
+ pd->hard_reset = false;
+
if (pd->current_dr != DR_UFP) {
extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 0);
@@ -875,15 +924,30 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->vconn_enabled = false;
}
- pd->tx_msgid = 0;
-
val.intval = pd->requested_voltage; /* set range back to 5V */
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_VOLTAGE_MAX, &val);
pd->current_voltage = pd->requested_voltage;
- /* recursive call; go back to beginning state */
- usbpd_set_state(pd, PE_SNK_STARTUP);
+ /* max time for hard reset to toggle vbus off/on */
+ kick_sm(pd, SNK_HARD_RESET_RECOVER_TIME);
+ break;
+
+ case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
+ val.intval = pd->requested_current = 0; /* suspend charging */
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &val);
+
+ pd->in_explicit_contract = false;
+
+ /*
+ * need to update PR bit in message header so that
+ * proper GoodCRC is sent when receiving next PS_RDY
+ */
+ pd_phy_update_roles(pd->current_dr, PR_SRC);
+
+ /* wait for PS_RDY */
+ kick_sm(pd, PS_SOURCE_OFF);
break;
default:
@@ -907,10 +971,10 @@ int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr)
/* already connected with this SVID discovered? */
if (pd->vdm_state >= DISCOVERED_SVIDS) {
- u16 *psvid;
+ int i;
- for (psvid = pd->discovered_svids; *psvid; psvid++) {
- if (*psvid == hdlr->svid) {
+ for (i = 0; i < pd->num_svids; i++) {
+ if (pd->discovered_svids[i] == hdlr->svid) {
if (hdlr->connect)
hdlr->connect(hdlr);
break;
@@ -932,7 +996,7 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos)
{
struct vdm_tx *vdm_tx;
- if (!pd->in_explicit_contract)
+ if (!pd->in_explicit_contract || pd->vdm_tx)
return -EBUSY;
vdm_tx = kzalloc(sizeof(*vdm_tx), GFP_KERNEL);
@@ -945,8 +1009,10 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos)
vdm_tx->size = num_vdos + 1; /* include the header */
/* VDM will get sent in PE_SRC/SNK_READY state handling */
- list_add_tail(&vdm_tx->entry, &pd->vdm_tx_queue);
- queue_work(pd->wq, &pd->sm_work);
+ pd->vdm_tx = vdm_tx;
+
+ /* slight delay before queuing to prioritize handling of incoming VDM */
+ kick_sm(pd, 5);
return 0;
}
@@ -972,8 +1038,8 @@ static void handle_vdm_rx(struct usbpd *pd)
u16 svid = VDM_HDR_SVID(vdm_hdr);
u16 *psvid;
u8 i, num_vdos = pd->rx_msg_len - 1; /* num objects minus header */
- u8 cmd = VDM_HDR_CMD(vdm_hdr);
- u8 cmd_type = VDM_HDR_CMD_TYPE(vdm_hdr);
+ u8 cmd = SVDM_HDR_CMD(vdm_hdr);
+ u8 cmd_type = SVDM_HDR_CMD_TYPE(vdm_hdr);
struct usbpd_svid_handler *handler;
usbpd_dbg(&pd->dev, "VDM rx: svid:%x cmd:%x cmd_type:%x vdm_hdr:%x\n",
@@ -983,7 +1049,7 @@ static void handle_vdm_rx(struct usbpd *pd)
handler = find_svid_handler(pd, svid);
/* Unstructured VDM */
- if (!VDM_HDR_TYPE(vdm_hdr)) {
+ if (!VDM_IS_SVDM(vdm_hdr)) {
if (handler && handler->vdm_received)
handler->vdm_received(handler, vdm_hdr, vdos, num_vdos);
return;
@@ -994,7 +1060,19 @@ static void handle_vdm_rx(struct usbpd *pd)
switch (cmd_type) {
case SVDM_CMD_TYPE_INITIATOR:
- if (cmd == USBPD_SVDM_DISCOVER_IDENTITY) {
+ /*
+ * if this interrupts a previous exchange, abort the previous
+ * outgoing response
+ */
+ if (pd->vdm_tx) {
+ usbpd_dbg(&pd->dev, "Discarding previously queued SVDM tx (SVID:0x%04x)\n",
+ VDM_HDR_SVID(pd->vdm_tx->data[0]));
+
+ kfree(pd->vdm_tx);
+ pd->vdm_tx = NULL;
+ }
+
+ if (svid == USBPD_SID && cmd == USBPD_SVDM_DISCOVER_IDENTITY) {
u32 tx_vdos[3] = {
ID_HDR_USB_HOST | ID_HDR_USB_DEVICE |
ID_HDR_PRODUCT_PER_MASK | ID_HDR_VID,
@@ -1005,9 +1083,9 @@ static void handle_vdm_rx(struct usbpd *pd)
usbpd_send_svdm(pd, USBPD_SID, cmd,
SVDM_CMD_TYPE_RESP_ACK, 0, tx_vdos, 3);
- } else {
- usbpd_send_svdm(pd, USBPD_SID, cmd,
- SVDM_CMD_TYPE_RESP_NAK, 0, NULL, 0);
+ } else if (cmd != USBPD_SVDM_ATTENTION) {
+ usbpd_send_svdm(pd, svid, cmd, SVDM_CMD_TYPE_RESP_NAK,
+ SVDM_HDR_OBJ_POS(vdm_hdr), NULL, 0);
}
break;
@@ -1039,19 +1117,37 @@ static void handle_vdm_rx(struct usbpd *pd)
kfree(pd->vdm_tx_retry);
pd->vdm_tx_retry = NULL;
- kfree(pd->discovered_svids);
-
- /* TODO: handle > 12 SVIDs */
- pd->discovered_svids = kzalloc((2 * num_vdos + 1) *
- sizeof(u16),
- GFP_KERNEL);
if (!pd->discovered_svids) {
- usbpd_err(&pd->dev, "unable to allocate SVIDs\n");
- break;
+ pd->num_svids = 2 * num_vdos;
+ pd->discovered_svids = kcalloc(pd->num_svids,
+ sizeof(u16),
+ GFP_KERNEL);
+ if (!pd->discovered_svids) {
+ usbpd_err(&pd->dev, "unable to allocate SVIDs\n");
+ break;
+ }
+
+ psvid = pd->discovered_svids;
+ } else { /* handle > 12 SVIDs */
+ void *ptr;
+ size_t oldsize = pd->num_svids * sizeof(u16);
+ size_t newsize = oldsize +
+ (2 * num_vdos * sizeof(u16));
+
+ ptr = krealloc(pd->discovered_svids, newsize,
+ GFP_KERNEL);
+ if (!ptr) {
+ usbpd_err(&pd->dev, "unable to realloc SVIDs\n");
+ break;
+ }
+
+ pd->discovered_svids = ptr;
+ psvid = pd->discovered_svids + pd->num_svids;
+ memset(psvid, 0, (2 * num_vdos));
+ pd->num_svids += 2 * num_vdos;
}
/* convert 32-bit VDOs to list of 16-bit SVIDs */
- psvid = pd->discovered_svids;
for (i = 0; i < num_vdos * 2; i++) {
/*
* Within each 32-bit VDO,
@@ -1075,8 +1171,22 @@ static void handle_vdm_rx(struct usbpd *pd)
usbpd_dbg(&pd->dev, "Discovered SVID: 0x%04x\n",
svid);
*psvid++ = svid;
+ }
+ }
+
+ /* if more than 12 SVIDs, resend the request */
+ if (num_vdos == 6 && vdos[5] != 0) {
+ usbpd_send_svdm(pd, USBPD_SID,
+ USBPD_SVDM_DISCOVER_SVIDS,
+ SVDM_CMD_TYPE_INITIATOR, 0,
+ NULL, 0);
+ break;
+ }
- /* if SVID supported notify handler */
+ /* now that all SVIDs are discovered, notify handlers */
+ for (i = 0; i < pd->num_svids; i++) {
+ svid = pd->discovered_svids[i];
+ if (svid) {
handler = find_svid_handler(pd, svid);
if (handler && handler->connect)
handler->connect(handler);
@@ -1126,10 +1236,9 @@ static void handle_vdm_rx(struct usbpd *pd)
}
/* wait tVDMBusy, then retry */
- list_move(&pd->vdm_tx_retry->entry, &pd->vdm_tx_queue);
+ pd->vdm_tx = pd->vdm_tx_retry;
pd->vdm_tx_retry = NULL;
- hrtimer_start(&pd->timer, ms_to_ktime(VDM_BUSY_TIME),
- HRTIMER_MODE_REL);
+ kick_sm(pd, VDM_BUSY_TIME);
break;
default:
break;
@@ -1143,16 +1252,14 @@ static void handle_vdm_tx(struct usbpd *pd)
int ret;
/* only send one VDM at a time */
- if (!list_empty(&pd->vdm_tx_queue)) {
- struct vdm_tx *vdm_tx = list_first_entry(&pd->vdm_tx_queue,
- struct vdm_tx, entry);
- u32 vdm_hdr = vdm_tx->data[0];
+ if (pd->vdm_tx) {
+ u32 vdm_hdr = pd->vdm_tx->data[0];
- ret = pd_send_msg(pd, MSG_VDM, vdm_tx->data, vdm_tx->size,
- SOP_MSG);
+ ret = pd_send_msg(pd, MSG_VDM, pd->vdm_tx->data,
+ pd->vdm_tx->size, SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending VDM command %d\n",
- VDM_HDR_CMD(vdm_tx->data[0]));
+ SVDM_HDR_CMD(pd->vdm_tx->data[0]));
usbpd_set_state(pd, pd->current_pr == PR_SRC ?
PE_SRC_SEND_SOFT_RESET :
PE_SNK_SEND_SOFT_RESET);
@@ -1161,24 +1268,25 @@ static void handle_vdm_tx(struct usbpd *pd)
return;
}
- list_del(&vdm_tx->entry);
-
/*
* special case: keep initiated Discover ID/SVIDs
* around in case we need to re-try when receiving BUSY
*/
- if (VDM_HDR_TYPE(vdm_hdr) &&
- VDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR &&
- VDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) {
+ if (VDM_IS_SVDM(vdm_hdr) &&
+ SVDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR &&
+ SVDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) {
if (pd->vdm_tx_retry) {
usbpd_err(&pd->dev, "Previous Discover VDM command %d not ACKed/NAKed\n",
- VDM_HDR_CMD(pd->vdm_tx_retry->data[0]));
+ SVDM_HDR_CMD(
+ pd->vdm_tx_retry->data[0]));
kfree(pd->vdm_tx_retry);
}
- pd->vdm_tx_retry = vdm_tx;
+ pd->vdm_tx_retry = pd->vdm_tx;
} else {
- kfree(vdm_tx);
+ kfree(pd->vdm_tx);
}
+
+ pd->vdm_tx = NULL;
}
}
@@ -1194,13 +1302,9 @@ static void reset_vdm_state(struct usbpd *pd)
pd->vdm_tx_retry = NULL;
kfree(pd->discovered_svids);
pd->discovered_svids = NULL;
- while (!list_empty(&pd->vdm_tx_queue)) {
- struct vdm_tx *vdm_tx =
- list_first_entry(&pd->vdm_tx_queue,
- struct vdm_tx, entry);
- list_del(&vdm_tx->entry);
- kfree(vdm_tx);
- }
+ pd->num_svids = 0;
+ kfree(pd->vdm_tx);
+ pd->vdm_tx = NULL;
}
static void dr_swap(struct usbpd *pd)
@@ -1230,6 +1334,34 @@ static void dr_swap(struct usbpd *pd)
pd_phy_update_roles(pd->current_dr, pd->current_pr);
}
+
+static void vconn_swap(struct usbpd *pd)
+{
+ int ret;
+
+ if (pd->vconn_enabled) {
+ pd->current_state = PE_VCS_WAIT_FOR_VCONN;
+ kick_sm(pd, VCONN_ON_TIME);
+ } else {
+ ret = regulator_enable(pd->vconn);
+ if (ret) {
+ usbpd_err(&pd->dev, "Unable to enable vconn\n");
+ return;
+ }
+
+ pd->vconn_enabled = true;
+
+ ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG);
+ if (ret) {
+ usbpd_err(&pd->dev, "Error sending PS_RDY\n");
+ usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+ PE_SRC_SEND_SOFT_RESET :
+ PE_SNK_SEND_SOFT_RESET);
+ return;
+ }
+ }
+}
+
/* Handles current state and determines transitions */
static void usbpd_sm(struct work_struct *w)
{
@@ -1243,6 +1375,7 @@ static void usbpd_sm(struct work_struct *w)
usbpd_state_strings[pd->current_state]);
hrtimer_cancel(&pd->timer);
+ pd->sm_queued = false;
if (pd->rx_msg_len)
data_recvd = pd->rx_msg_type;
@@ -1250,11 +1383,11 @@ static void usbpd_sm(struct work_struct *w)
ctrl_recvd = pd->rx_msg_type;
/* Disconnect? */
- if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
+ if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE && !pd->in_pr_swap) {
if (pd->current_state == PE_UNKNOWN)
- return;
+ goto sm_done;
- usbpd_info(&pd->dev, "USB PD disconnect\n");
+ usbpd_info(&pd->dev, "USB Type-C disconnect\n");
if (pd->pd_phy_opened) {
pd_phy_close();
@@ -1274,14 +1407,21 @@ static void usbpd_sm(struct work_struct *w)
val.intval = 0;
power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED,
+ &val);
+
+ power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_ACTIVE, &val);
- if (pd->current_pr == PR_SRC) {
+ if (pd->current_pr == PR_SRC)
regulator_disable(pd->vbus);
- if (pd->vconn_enabled) {
- regulator_disable(pd->vconn);
- pd->vconn_enabled = false;
- }
+
+ if (pd->vconn_enabled) {
+ regulator_disable(pd->vconn);
+ pd->vconn_enabled = false;
}
if (pd->current_dr == DR_UFP)
@@ -1306,18 +1446,23 @@ static void usbpd_sm(struct work_struct *w)
pd->current_state = PE_UNKNOWN;
- return;
+ goto sm_done;
}
/* Hard reset? */
if (pd->hard_reset) {
+ val.intval = 1;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
reset_vdm_state(pd);
if (pd->current_pr == PR_SINK)
usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT);
else
usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT);
- pd->hard_reset = false;
+
+ goto sm_done;
}
/* Soft reset? */
@@ -1353,6 +1498,10 @@ static void usbpd_sm(struct work_struct *w)
}
break;
+ case PE_SRC_STARTUP:
+ usbpd_set_state(pd, PE_SRC_STARTUP);
+ break;
+
case PE_SRC_SEND_CAPABILITIES:
ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps,
ARRAY_SIZE(default_src_caps), SOP_MSG);
@@ -1378,30 +1527,31 @@ static void usbpd_sm(struct work_struct *w)
break;
}
- hrtimer_start(&pd->timer, ms_to_ktime(SRC_CAP_TIME),
- HRTIMER_MODE_REL);
+ kick_sm(pd, SRC_CAP_TIME);
break;
}
+ val.intval = 1;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_ACTIVE, &val);
+
/* transmit was successful if GoodCRC was received */
pd->caps_count = 0;
pd->hard_reset_count = 0;
pd->pd_connected = true; /* we know peer is PD capable */
- val.intval = POWER_SUPPLY_TYPE_USB_PD;
- power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_TYPE, &val);
-
/* wait for REQUEST */
pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT;
- hrtimer_start(&pd->timer, ms_to_ktime(SENDER_RESPONSE_TIME),
- HRTIMER_MODE_REL);
+ kick_sm(pd, SENDER_RESPONSE_TIME);
break;
case PE_SRC_SEND_CAPABILITIES_WAIT:
if (data_recvd == MSG_REQUEST) {
pd->rdo = pd->rx_payload[0];
usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY);
+ } else if (data_recvd || ctrl_recvd) {
+ usbpd_err(&pd->dev, "Unexpected message received\n");
+ usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
} else {
usbpd_set_state(pd, PE_SRC_HARD_RESET);
}
@@ -1417,6 +1567,14 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
break;
}
+ } else if (ctrl_recvd == MSG_GET_SINK_CAP) {
+ ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES,
+ default_snk_caps,
+ ARRAY_SIZE(default_snk_caps), SOP_MSG);
+ if (ret) {
+ usbpd_err(&pd->dev, "Error sending Sink Caps\n");
+ usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+ }
} else if (data_recvd == MSG_REQUEST) {
pd->rdo = pd->rx_payload[0];
usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY);
@@ -1436,6 +1594,9 @@ static void usbpd_sm(struct work_struct *w)
dr_swap(pd);
kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
} else if (ctrl_recvd == MSG_PR_SWAP) {
+ /* lock in current mode */
+ set_power_role(pd, pd->current_pr);
+
/* we'll happily accept Src->Sink requests anytime */
ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
if (ret) {
@@ -1445,8 +1606,17 @@ static void usbpd_sm(struct work_struct *w)
}
pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF;
- queue_work(pd->wq, &pd->sm_work);
+ kick_sm(pd, SRC_TRANSITION_TIME);
break;
+ } else if (ctrl_recvd == MSG_VCONN_SWAP) {
+ ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
+ if (ret) {
+ usbpd_err(&pd->dev, "Error sending Accept\n");
+ usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+ break;
+ }
+
+ vconn_swap(pd);
} else {
if (data_recvd == MSG_VDM)
handle_vdm_rx(pd);
@@ -1456,6 +1626,10 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SRC_HARD_RESET:
+ val.intval = 1;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
pd_send_hard_reset(pd);
pd->in_explicit_contract = false;
reset_vdm_state(pd);
@@ -1465,39 +1639,41 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT);
break;
- case PE_SNK_DISCOVERY:
- if (!pd->vbus_present) {
- /* Hard reset and VBUS didn't come back? */
- power_supply_get_property(pd->usb_psy,
- POWER_SUPPLY_PROP_TYPE, &val);
- if (val.intval == POWER_SUPPLY_TYPEC_NONE) {
- pd->typec_mode = POWER_SUPPLY_TYPEC_NONE;
- queue_work(pd->wq, &pd->sm_work);
- }
- break;
- }
-
- usbpd_set_state(pd, PE_SNK_WAIT_FOR_CAPABILITIES);
+ case PE_SNK_STARTUP:
+ usbpd_set_state(pd, PE_SNK_STARTUP);
break;
case PE_SNK_WAIT_FOR_CAPABILITIES:
if (data_recvd == MSG_SOURCE_CAPABILITIES) {
- val.intval = 1;
+ val.intval = 0;
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_ACTIVE, &val);
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
+ &val);
- val.intval = POWER_SUPPLY_TYPE_USB_PD;
+ val.intval = 1;
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_TYPE, &val);
+ POWER_SUPPLY_PROP_PD_ACTIVE, &val);
usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);
} else if (pd->hard_reset_count < 3) {
usbpd_set_state(pd, PE_SNK_HARD_RESET);
} else if (pd->pd_connected) {
usbpd_info(&pd->dev, "Sink hard reset count exceeded, forcing reconnect\n");
+
+ val.intval = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
+ &val);
+
usbpd_set_state(pd, PE_ERROR_RECOVERY);
} else {
usbpd_dbg(&pd->dev, "Sink hard reset count exceeded, disabling PD\n");
+
+ val.intval = 0;
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
+ &val);
+
val.intval = 0;
power_supply_set_property(pd->usb_psy,
POWER_SUPPLY_PROP_PD_ACTIVE, &val);
@@ -1514,9 +1690,14 @@ static void usbpd_sm(struct work_struct *w)
POWER_SUPPLY_PROP_VOLTAGE_MIN,
&val);
- val.intval = 0; /* suspend charging */
+ /*
+ * disable charging; technically we are allowed to
+ * charge up to pSnkStdby (2.5 W) during this
+ * transition, but disable it just for simplicity.
+ */
+ val.intval = 0;
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &val);
+ POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
pd->selected_pdo = pd->requested_pdo;
usbpd_set_state(pd, PE_SNK_TRANSITION_SINK);
@@ -1526,6 +1707,9 @@ static void usbpd_sm(struct work_struct *w)
else
usbpd_set_state(pd,
PE_SNK_WAIT_FOR_CAPABILITIES);
+ } else if (pd->rx_msg_type) {
+ usbpd_err(&pd->dev, "Invalid response to sink request\n");
+ usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
} else {
/* timed out; go to hard reset */
usbpd_set_state(pd, PE_SNK_HARD_RESET);
@@ -1544,7 +1728,7 @@ static void usbpd_sm(struct work_struct *w)
/* resume charging */
val.intval = pd->requested_current * 1000; /* mA->uA */
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &val);
+ POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
usbpd_set_state(pd, PE_SNK_READY);
} else {
@@ -1554,9 +1738,9 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SNK_READY:
- if (data_recvd == MSG_SOURCE_CAPABILITIES)
+ if (data_recvd == MSG_SOURCE_CAPABILITIES) {
usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);
- else if (ctrl_recvd == MSG_GET_SINK_CAP) {
+ } else if (ctrl_recvd == MSG_GET_SINK_CAP) {
ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES,
default_snk_caps,
ARRAY_SIZE(default_snk_caps), SOP_MSG);
@@ -1564,6 +1748,15 @@ static void usbpd_sm(struct work_struct *w)
usbpd_err(&pd->dev, "Error sending Sink Caps\n");
usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
}
+ } else if (ctrl_recvd == MSG_GET_SOURCE_CAP) {
+ ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES,
+ default_src_caps,
+ ARRAY_SIZE(default_src_caps), SOP_MSG);
+ if (ret) {
+ usbpd_err(&pd->dev, "Error sending SRC CAPs\n");
+ usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+ break;
+ }
} else if (ctrl_recvd == MSG_DR_SWAP) {
if (pd->vdm_state == MODE_ENTERED) {
usbpd_set_state(pd, PE_SNK_HARD_RESET);
@@ -1580,6 +1773,9 @@ static void usbpd_sm(struct work_struct *w)
dr_swap(pd);
kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
} else if (ctrl_recvd == MSG_PR_SWAP) {
+ /* lock in current mode */
+ set_power_role(pd, pd->current_pr);
+
/* TODO: should we Reject in certain circumstances? */
ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
if (ret) {
@@ -1589,20 +1785,34 @@ static void usbpd_sm(struct work_struct *w)
}
pd->in_pr_swap = true;
- pd->current_state = PE_PRS_SNK_SRC_TRANSITION_TO_OFF;
- /* turn off sink */
- pd->in_explicit_contract = false;
-
+ usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF);
+ break;
+ } else if (ctrl_recvd == MSG_VCONN_SWAP) {
/*
- * need to update PR bit in message header so that
- * proper GoodCRC is sent when receiving next PS_RDY
+ * if VCONN is connected to VBUS, make sure we are
+ * not in high voltage contract, otherwise reject.
*/
- pd->current_pr = PR_SRC;
- pd_phy_update_roles(pd->current_dr, pd->current_pr);
+ if (!pd->vconn_is_external &&
+ (pd->requested_voltage > 5000000)) {
+ ret = pd_send_msg(pd, MSG_REJECT, NULL, 0,
+ SOP_MSG);
+ if (ret) {
+ usbpd_err(&pd->dev, "Error sending Reject\n");
+ usbpd_set_state(pd,
+ PE_SNK_SEND_SOFT_RESET);
+ }
- hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_OFF),
- HRTIMER_MODE_REL);
- break;
+ break;
+ }
+
+ ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
+ if (ret) {
+ usbpd_err(&pd->dev, "Error sending Accept\n");
+ usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+ break;
+ }
+
+ vconn_swap(pd);
} else {
if (data_recvd == MSG_VDM)
handle_vdm_rx(pd);
@@ -1611,6 +1821,20 @@ static void usbpd_sm(struct work_struct *w)
}
break;
+ case PE_SNK_TRANSITION_TO_DEFAULT:
+ if (pd->vbus_present) {
+ usbpd_set_state(pd, PE_SNK_STARTUP);
+ } else {
+ /* Hard reset and VBUS didn't come back? */
+ power_supply_get_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_TYPEC_MODE, &val);
+ if (val.intval == POWER_SUPPLY_TYPEC_NONE) {
+ pd->typec_mode = POWER_SUPPLY_TYPEC_NONE;
+ kick_sm(pd, 0);
+ }
+ }
+ break;
+
case PE_SRC_SOFT_RESET:
case PE_SNK_SOFT_RESET:
/* Reset protocol layer */
@@ -1649,14 +1873,14 @@ static void usbpd_sm(struct work_struct *w)
/* prepare charger for VBUS change */
val.intval = 1;
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_ACTIVE, &val);
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
pd->requested_voltage = 5000000;
if (pd->requested_current) {
val.intval = pd->requested_current = 0;
power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &val);
+ POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
}
val.intval = pd->requested_voltage;
@@ -1684,16 +1908,23 @@ static void usbpd_sm(struct work_struct *w)
}
pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF;
- /* fall-through */
+ kick_sm(pd, SRC_TRANSITION_TIME);
+ break;
+
case PE_PRS_SRC_SNK_TRANSITION_TO_OFF:
pd->in_pr_swap = true;
pd->in_explicit_contract = false;
regulator_disable(pd->vbus);
- set_power_role(pd, PR_SINK); /* switch Rp->Rd */
+
+ /* PE_PRS_SRC_SNK_Assert_Rd */
pd->current_pr = PR_SINK;
+ set_power_role(pd, pd->current_pr);
pd_phy_update_roles(pd->current_dr, pd->current_pr);
+ /* allow time for Vbus discharge, must be < tSrcSwapStdby */
+ msleep(500);
+
ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending PS_RDY\n");
@@ -1702,8 +1933,7 @@ static void usbpd_sm(struct work_struct *w)
}
pd->current_state = PE_PRS_SRC_SNK_WAIT_SOURCE_ON;
- hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_ON),
- HRTIMER_MODE_REL);
+ kick_sm(pd, PS_SOURCE_ON);
break;
case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
@@ -1720,19 +1950,7 @@ static void usbpd_sm(struct work_struct *w)
}
pd->in_pr_swap = true;
- pd->current_state = PE_PRS_SNK_SRC_TRANSITION_TO_OFF;
- /* turn off sink */
- pd->in_explicit_contract = false;
-
- /*
- * need to update PR bit in message header so that
- * proper GoodCRC is sent when receiving next PS_RDY
- */
- pd->current_pr = PR_SRC;
- pd_phy_update_roles(pd->current_dr, pd->current_pr);
-
- hrtimer_start(&pd->timer, ms_to_ktime(PS_SOURCE_OFF),
- HRTIMER_MODE_REL);
+ usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF);
break;
case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
@@ -1741,14 +1959,20 @@ static void usbpd_sm(struct work_struct *w)
break;
}
+ /* PE_PRS_SNK_SRC_Assert_Rp */
+ pd->current_pr = PR_SRC;
+ set_power_role(pd, pd->current_pr);
pd->current_state = PE_PRS_SNK_SRC_SOURCE_ON;
+
/* fall-through */
+
case PE_PRS_SNK_SRC_SOURCE_ON:
- set_power_role(pd, PR_SRC);
ret = regulator_enable(pd->vbus);
if (ret)
usbpd_err(&pd->dev, "Unable to enable vbus\n");
+ msleep(200); /* allow time VBUS ramp-up, must be < tNewSrc */
+
ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG);
if (ret) {
usbpd_err(&pd->dev, "Error sending PS_RDY\n");
@@ -1759,6 +1983,26 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SRC_STARTUP);
break;
+ case PE_VCS_WAIT_FOR_VCONN:
+ if (ctrl_recvd == MSG_PS_RDY) {
+ /*
+ * hopefully redundant check but in case not enabled
+ * avoids unbalanced regulator disable count
+ */
+ if (pd->vconn_enabled)
+ regulator_disable(pd->vconn);
+ pd->vconn_enabled = false;
+
+ pd->current_state = pd->current_pr == PR_SRC ?
+ PE_SRC_READY : PE_SNK_READY;
+ } else {
+ /* timed out; go to hard reset */
+ usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+ PE_SRC_HARD_RESET : PE_SNK_HARD_RESET);
+ }
+
+ break;
+
default:
usbpd_err(&pd->dev, "Unhandled state %s\n",
usbpd_state_strings[pd->current_state]);
@@ -1767,6 +2011,10 @@ static void usbpd_sm(struct work_struct *w)
/* Rx message should have been consumed now */
pd->rx_msg_type = pd->rx_msg_len = 0;
+
+sm_done:
+ if (!pd->sm_queued)
+ pm_relax(&pd->dev);
}
static inline const char *src_current(enum power_supply_typec_mode typec_mode)
@@ -1787,9 +2035,8 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
{
struct usbpd *pd = container_of(nb, struct usbpd, psy_nb);
union power_supply_propval val;
- bool pd_allowed;
enum power_supply_typec_mode typec_mode;
- enum power_supply_type psy_type;
+ bool do_work = false;
int ret;
if (ptr != pd->usb_psy || evt != PSY_EVENT_PROP_CHANGED)
@@ -1803,7 +2050,9 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
return ret;
}
- pd_allowed = val.intval;
+ if (pd->pd_allowed != val.intval)
+ do_work = true;
+ pd->pd_allowed = val.intval;
ret = power_supply_get_property(pd->usb_psy,
POWER_SUPPLY_PROP_PRESENT, &val);
@@ -1823,37 +2072,6 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
typec_mode = val.intval;
- /*
- * Don't proceed if cable is connected but PD_ALLOWED is false.
- * It means the PMIC may still be in the middle of performing
- * charger type detection.
- */
- if (!pd_allowed && typec_mode != POWER_SUPPLY_TYPEC_NONE)
- return 0;
-
- /*
- * Workaround for PMIC HW bug.
- *
- * During hard reset or PR swap (sink to source) when VBUS goes to 0
- * the CC logic will report this as a disconnection. In those cases it
- * can be ignored, however the downside is that pd->hard_reset can be
- * momentarily true even when a non-PD capable source is attached, and
- * can't be distinguished from a physical disconnect. In that case,
- * allow for the common case of disconnecting from an SDP.
- *
- * The less common case is a PD-capable SDP which will result in a
- * hard reset getting treated like a disconnect. We can live with this
- * until the HW bug is fixed: in which disconnection won't be reported
- * on VBUS loss alone unless pullup is also removed from CC.
- */
- if ((pd->hard_reset || pd->in_pr_swap) &&
- typec_mode == POWER_SUPPLY_TYPEC_NONE &&
- pd->psy_type != POWER_SUPPLY_TYPE_USB) {
- usbpd_dbg(&pd->dev, "Ignoring disconnect due to %s\n",
- pd->hard_reset ? "hard reset" : "PR swap");
- return 0;
- }
-
ret = power_supply_get_property(pd->usb_psy,
POWER_SUPPLY_PROP_TYPE, &val);
if (ret) {
@@ -1861,59 +2079,91 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
return ret;
}
- psy_type = val.intval;
+ if (pd->psy_type != val.intval)
+ do_work = true;
+ pd->psy_type = val.intval;
- usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d\n", typec_mode,
- pd->vbus_present, psy_type);
+ usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d orientation:%d\n",
+ typec_mode, pd->vbus_present, pd->psy_type,
+ usbpd_get_plug_orientation(pd));
- /* any change? */
- if (pd->typec_mode == typec_mode && pd->psy_type == psy_type)
- return 0;
+ if (pd->typec_mode != typec_mode) {
+ pd->typec_mode = typec_mode;
+ do_work = true;
- pd->typec_mode = typec_mode;
- pd->psy_type = psy_type;
+ switch (typec_mode) {
+ /* Disconnect */
+ case POWER_SUPPLY_TYPEC_NONE:
+ if (pd->in_pr_swap) {
+ usbpd_dbg(&pd->dev, "Ignoring disconnect due to PR swap\n");
+ do_work = false;
+ }
- switch (typec_mode) {
- /* Disconnect */
- case POWER_SUPPLY_TYPEC_NONE:
- queue_work(pd->wq, &pd->sm_work);
- break;
+ /*
+ * Workaround for PMIC HW bug.
+ *
+ * During hard reset when VBUS goes to 0 the CC logic
+ * will report this as a disconnection. In those cases
+ * it can be ignored, however the downside is that
+ * pd->hard_reset can be momentarily true even when a
+ * non-PD capable source is attached, and can't be
+ * distinguished from a physical disconnect. In that
+ * case, allow for the common case of disconnecting
+ * from an SDP.
+ *
+ * The less common case is a PD-capable SDP which will
+ * result in a hard reset getting treated like a
+ * disconnect. We can live with this until the HW bug
+ * is fixed: in which disconnection won't be reported
+ * on VBUS loss alone unless pullup is also removed
+ * from CC.
+ */
+ if (pd->psy_type != POWER_SUPPLY_TYPE_USB &&
+ pd->current_state ==
+ PE_SNK_TRANSITION_TO_DEFAULT) {
+ usbpd_dbg(&pd->dev, "Ignoring disconnect due to hard reset\n");
+ do_work = false;
+ }
- /* Sink states */
- case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
- case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
- case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- usbpd_info(&pd->dev, "Type-C Source (%s) connected\n",
- src_current(typec_mode));
- if (pd->current_pr != PR_SINK) {
+ break;
+
+ /* Sink states */
+ case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
+ case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
+ case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
+ usbpd_info(&pd->dev, "Type-C Source (%s) connected\n",
+ src_current(typec_mode));
pd->current_pr = PR_SINK;
- queue_work(pd->wq, &pd->sm_work);
- }
- break;
+ pd->in_pr_swap = false;
+ break;
- /* Source states */
- case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE:
- case POWER_SUPPLY_TYPEC_SINK:
- usbpd_info(&pd->dev, "Type-C Sink%s connected\n",
- typec_mode == POWER_SUPPLY_TYPEC_SINK ?
- "" : " (powered)");
- if (pd->current_pr != PR_SRC) {
+ /* Source states */
+ case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE:
+ case POWER_SUPPLY_TYPEC_SINK:
+ usbpd_info(&pd->dev, "Type-C Sink%s connected\n",
+ typec_mode == POWER_SUPPLY_TYPEC_SINK ?
+ "" : " (powered)");
pd->current_pr = PR_SRC;
- queue_work(pd->wq, &pd->sm_work);
- }
- break;
+ pd->in_pr_swap = false;
+ break;
- case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY:
- usbpd_info(&pd->dev, "Type-C Debug Accessory connected\n");
- break;
- case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER:
- usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n");
- break;
- default:
- usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n", typec_mode);
- break;
+ case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY:
+ usbpd_info(&pd->dev, "Type-C Debug Accessory connected\n");
+ break;
+ case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER:
+ usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n");
+ break;
+ default:
+ usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n",
+ typec_mode);
+ break;
+ }
}
+ /* only queue state machine if CC state or PD_ALLOWED changes */
+ if (do_work)
+ kick_sm(pd, 0);
+
return 0;
}
@@ -2337,6 +2587,10 @@ struct usbpd *usbpd_create(struct device *parent)
if (ret)
goto free_pd;
+ ret = device_init_wakeup(&pd->dev, true);
+ if (ret)
+ goto free_pd;
+
ret = device_add(&pd->dev);
if (ret)
goto free_pd;
@@ -2392,11 +2646,13 @@ struct usbpd *usbpd_create(struct device *parent)
goto unreg_psy;
}
+ pd->vconn_is_external = device_property_present(parent,
+ "qcom,vconn-uses-external-source");
+
pd->current_pr = PR_NONE;
pd->current_dr = DR_NONE;
list_add_tail(&pd->instance, &_usbpd);
- INIT_LIST_HEAD(&pd->vdm_tx_queue);
INIT_LIST_HEAD(&pd->svid_handlers);
/* force read initial power_supply values */
diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c
index 6bf80e43cac5..021755f7f32d 100644
--- a/drivers/video/fbdev/core/fbcmap.c
+++ b/drivers/video/fbdev/core/fbcmap.c
@@ -187,8 +187,8 @@ int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to)
{
- int tooff = 0, fromoff = 0;
- int size;
+ u32 tooff = 0, fromoff = 0;
+ u32 size;
if (to->start > from->start)
fromoff = to->start - from->start;
@@ -198,10 +198,10 @@ int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to)
return -EINVAL;
size = to->len - tooff;
- if (size > (int) (from->len - fromoff))
+ if (size > (from->len - fromoff))
size = from->len - fromoff;
size *= sizeof(u16);
- if (!size)
+ if (size == 0)
return -EINVAL;
if (copy_to_user(to->red+tooff, from->red+fromoff, size))
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index 72b262e8171a..4e68952d33a9 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -1022,6 +1022,8 @@ static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic)
pinfo->lcdc.hsync_skew = 0;
pinfo->is_pluggable = true;
+ dp_drv->bpp = pinfo->bpp;
+
pr_debug("update res. vic= %d, pclk_rate = %llu\n",
dp_drv->vic, pinfo->clk_rate);
@@ -1786,16 +1788,15 @@ static void mdss_dp_do_link_train(struct mdss_dp_drv_pdata *dp)
static void mdss_dp_event_work(struct work_struct *work)
{
struct mdss_dp_drv_pdata *dp = NULL;
- struct delayed_work *dw = to_delayed_work(work);
unsigned long flag;
- u32 todo = 0, dp_config_pkt[2];
+ u32 todo = 0, config;
- if (!dw) {
+ if (!work) {
pr_err("invalid work structure\n");
return;
}
- dp = container_of(dw, struct mdss_dp_drv_pdata, dwork);
+ dp = container_of(work, struct mdss_dp_drv_pdata, work);
spin_lock_irqsave(&dp->event_lock, flag);
todo = dp->current_event;
@@ -1840,11 +1841,9 @@ static void mdss_dp_event_work(struct work_struct *work)
SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0);
break;
case EV_USBPD_DP_CONFIGURE:
- dp_config_pkt[0] = SVDM_HDR(USB_C_DP_SID, VDM_VERSION, 0x1,
- SVDM_CMD_TYPE_INITIATOR, DP_VDM_CONFIGURE);
- dp_config_pkt[1] = mdss_dp_usbpd_gen_config_pkt(dp);
+ config = mdss_dp_usbpd_gen_config_pkt(dp);
usbpd_send_svdm(dp->pd, USB_C_DP_SID, DP_VDM_CONFIGURE,
- SVDM_CMD_TYPE_INITIATOR, 0x1, dp_config_pkt, 0x2);
+ SVDM_CMD_TYPE_INITIATOR, 0x1, &config, 0x1);
break;
default:
pr_err("Unknown event:%d\n", todo);
@@ -1855,7 +1854,7 @@ static void dp_send_events(struct mdss_dp_drv_pdata *dp, u32 events)
{
spin_lock(&dp->event_lock);
dp->current_event = events;
- queue_delayed_work(dp->workq, &dp->dwork, HZ / 100);
+ queue_work(dp->workq, &dp->work);
spin_unlock(&dp->event_lock);
}
@@ -1931,7 +1930,7 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp)
return -EPERM;
}
- INIT_DELAYED_WORK(&dp->dwork, mdss_dp_event_work);
+ INIT_WORK(&dp->work, mdss_dp_event_work);
return 0;
}
@@ -2050,8 +2049,7 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
}
break;
case DP_VDM_CONFIGURE:
- if ((dp_drv->cable_connected == true)
- || (cmd_type == SVDM_CMD_TYPE_RESP_ACK)) {
+ if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) {
dp_drv->alt_mode.current_state = DP_CONFIGURE_DONE;
pr_debug("config USBPD to DP done\n");
mdss_dp_host_init(&dp_drv->panel_data);
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index ddadb7b6709c..6c391f6f7de0 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -428,7 +428,7 @@ struct mdss_dp_drv_pdata {
/* event */
struct workqueue_struct *workq;
- struct delayed_work dwork;
+ struct work_struct work;
u32 current_event;
spinlock_t event_lock;
spinlock_t lock;
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 27e982437961..119e2a2b05cf 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -374,7 +374,19 @@ static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
/*
* edid standard header bytes
*/
-static char edid_hdr[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
+static u8 edid_hdr[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
+
+static bool dp_edid_is_valid_header(u8 *buf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(edid_hdr); i++) {
+ if (buf[i] != edid_hdr[i])
+ return false;
+ }
+
+ return true;
+}
int dp_edid_buf_error(char *buf, int len)
{
@@ -396,11 +408,6 @@ int dp_edid_buf_error(char *buf, int len)
return -EINVAL;
}
- if (strncmp(buf, edid_hdr, strlen(edid_hdr))) {
- pr_err("Error: header\n");
- return -EINVAL;
- }
-
return 0;
}
@@ -708,10 +715,11 @@ static int dp_aux_chan_ready(struct mdss_dp_drv_pdata *ep)
int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
{
- struct edp_buf *rp;
- int cnt, rlen;
- int ret = 0;
- int blk_num = 0;
+ struct edp_buf *rp = &dp->rxp;
+ int rlen, ret = 0;
+ int edid_blk = 0, blk_num = 0, retries = 10;
+ bool edid_parsing_done = false;
+ const u8 cea_tag = 0x02;
ret = dp_aux_chan_ready(dp);
if (ret) {
@@ -719,70 +727,56 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
return ret;
}
- for (cnt = 5; cnt; cnt--) {
- rlen = dp_aux_read_buf
- (dp, EDID_START_ADDRESS, EDID_BLOCK_SIZE, 1);
- if (rlen > 0) {
- pr_debug("cnt=%d, block=%d, rlen=%d\n",
- cnt, blk_num, rlen);
-
- rp = &dp->rxp;
- if (!dp_edid_buf_error(rp->data, rp->len))
- break;
+ do {
+ rlen = dp_aux_read_buf(dp, EDID_START_ADDRESS +
+ (blk_num * EDID_BLOCK_SIZE),
+ EDID_BLOCK_SIZE, 1);
+ if (rlen != EDID_BLOCK_SIZE) {
+ pr_err("Read failed. rlen=%d\n", rlen);
+ continue;
}
- }
- if ((cnt <= 0) && (rlen != EDID_BLOCK_SIZE)) {
- pr_err("Read failed. rlen=%d\n", rlen);
- return -EINVAL;
- }
+ pr_debug("blk_num=%d, rlen=%d\n", blk_num, rlen);
- rp = &dp->rxp;
+ if (dp_edid_is_valid_header(rp->data)) {
+ if (dp_edid_buf_error(rp->data, rp->len))
+ continue;
- dp_extract_edid_manufacturer(&dp->edid, rp->data);
- dp_extract_edid_product(&dp->edid, rp->data);
- dp_extract_edid_version(&dp->edid, rp->data);
- dp_extract_edid_ext_block_cnt(&dp->edid, rp->data);
- dp_extract_edid_video_support(&dp->edid, rp->data);
- dp_extract_edid_feature(&dp->edid, rp->data);
- dp_extract_edid_detailed_timing_description(&dp->edid, rp->data);
- /* for the first block initialize the edid buffer size */
- dp->edid_buf_size = 0;
+ if (edid_parsing_done) {
+ blk_num++;
+ continue;
+ }
- pr_debug("edid extension = %d\n",
- dp->edid.ext_block_cnt);
+ dp_extract_edid_manufacturer(&dp->edid, rp->data);
+ dp_extract_edid_product(&dp->edid, rp->data);
+ dp_extract_edid_version(&dp->edid, rp->data);
+ dp_extract_edid_ext_block_cnt(&dp->edid, rp->data);
+ dp_extract_edid_video_support(&dp->edid, rp->data);
+ dp_extract_edid_feature(&dp->edid, rp->data);
+ dp_extract_edid_detailed_timing_description(&dp->edid,
+ rp->data);
- memcpy(dp->edid_buf, rp->data, EDID_BLOCK_SIZE);
- dp->edid_buf_size += EDID_BLOCK_SIZE;
+ edid_parsing_done = true;
+ } else {
+ edid_blk++;
+ blk_num++;
- if (!dp->edid.ext_block_cnt)
- return 0;
+ /* fix dongle byte shift issue */
+ if (edid_blk == 1 && rp->data[0] != cea_tag) {
+ u8 tmp[EDID_BLOCK_SIZE - 1];
- for (blk_num = 1; blk_num <= dp->edid.ext_block_cnt;
- blk_num++) {
- for (cnt = 5; cnt; cnt--) {
- rlen = dp_aux_read_buf
- (dp, EDID_START_ADDRESS +
- (blk_num * EDID_BLOCK_SIZE),
- EDID_BLOCK_SIZE, 1);
- if (rlen > 0) {
- pr_debug("cnt=%d, blk_num=%d, rlen=%d\n",
- cnt, blk_num, rlen);
- rp = &dp->rxp;
- if (!dp_edid_buf_error(rp->data, rp->len))
- break;
+ memcpy(tmp, rp->data, EDID_BLOCK_SIZE - 1);
+ rp->data[0] = cea_tag;
+ memcpy(rp->data + 1, tmp, EDID_BLOCK_SIZE - 1);
}
}
- if ((cnt <= 0) && (rlen != EDID_BLOCK_SIZE)) {
- pr_err("Read failed. rlen=%d\n", rlen);
- return -EINVAL;
- }
+ memcpy(dp->edid_buf + (edid_blk * EDID_BLOCK_SIZE),
+ rp->data, EDID_BLOCK_SIZE);
- memcpy(dp->edid_buf + (blk_num * EDID_BLOCK_SIZE),
- rp->data, EDID_BLOCK_SIZE);
- dp->edid_buf_size += EDID_BLOCK_SIZE;
- }
+ if (edid_blk == dp->edid.ext_block_cnt)
+ return 0;
+ } while (retries--);
return 0;
}
@@ -1220,7 +1214,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
{
- int tries;
+ int tries = 0;
int ret = 0;
int usleep_time;
char pattern;
@@ -1232,12 +1226,12 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
else
pattern = 0x02;
- dp_host_train_set(ep, pattern); /* train_2 */
- dp_voltage_pre_emphasise_set(ep);
dp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */
- tries = 0;
- while (1) {
+ do {
+ dp_voltage_pre_emphasise_set(ep);
+ dp_host_train_set(ep, pattern);
+
usleep_time = ep->dpcd.training_read_interval;
usleep_range(usleep_time, usleep_time);
@@ -1249,14 +1243,13 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
}
tries++;
- if (tries > 5) {
+ if (tries > 4) {
ret = -1;
break;
}
dp_sink_train_set_adjust(ep);
- dp_voltage_pre_emphasise_set(ep);
- }
+ } while (1);
return ret;
}
@@ -1328,7 +1321,6 @@ static void dp_clear_training_pattern(struct mdss_dp_drv_pdata *ep)
int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp)
{
int ret = 0;
- int usleep_time;
ret = dp_aux_chan_ready(dp);
if (ret) {
@@ -1349,8 +1341,6 @@ train_start:
mdss_dp_state_ctrl(&dp->ctrl_io, 0);
dp_clear_training_pattern(dp);
- usleep_time = dp->dpcd.training_read_interval;
- usleep_range(usleep_time, usleep_time);
ret = dp_start_link_train_1(dp);
if (ret < 0) {
@@ -1365,8 +1355,6 @@ train_start:
pr_debug("Training 1 completed successfully\n");
- mdss_dp_state_ctrl(&dp->ctrl_io, 0);
- dp_clear_training_pattern(dp);
ret = dp_start_link_train_2(dp);
if (ret < 0) {
if (dp_link_rate_down_shift(dp) == 0) {
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index f1245a024a88..92acb910e0c3 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -32,6 +32,29 @@
#define AUDIO_FREQ_48 48000
#define DP_AUDIO_FREQ_COUNT 3
+enum mdss_dp_pin_assignment {
+ PIN_ASSIGNMENT_A,
+ PIN_ASSIGNMENT_B,
+ PIN_ASSIGNMENT_C,
+ PIN_ASSIGNMENT_D,
+ PIN_ASSIGNMENT_E,
+ PIN_ASSIGNMENT_F,
+ PIN_ASSIGNMENT_MAX,
+};
+
+static const char *mdss_dp_pin_name(u8 pin)
+{
+ switch (pin) {
+ case PIN_ASSIGNMENT_A: return "PIN_ASSIGNMENT_A";
+ case PIN_ASSIGNMENT_B: return "PIN_ASSIGNMENT_B";
+ case PIN_ASSIGNMENT_C: return "PIN_ASSIGNMENT_C";
+ case PIN_ASSIGNMENT_D: return "PIN_ASSIGNMENT_D";
+ case PIN_ASSIGNMENT_E: return "PIN_ASSIGNMENT_E";
+ case PIN_ASSIGNMENT_F: return "PIN_ASSIGNMENT_F";
+ default: return "UNKNOWN";
+ }
+}
+
static const uint32_t naud_value[DP_AUDIO_FREQ_COUNT][DP_AUDIO_FREQ_COUNT] = {
{ 10125, 16875, 33750 },
{ 5625, 9375, 18750 },
@@ -477,9 +500,23 @@ void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status)
u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp)
{
+ u8 pin_cfg, pin;
u32 config = 0;
- config |= (dp->alt_mode.dp_cap.dlink_pin_config << 8);
+ pin_cfg = dp->alt_mode.dp_cap.dlink_pin_config;
+
+ for (pin = PIN_ASSIGNMENT_A; pin < PIN_ASSIGNMENT_MAX; pin++) {
+ if (pin_cfg & BIT(pin))
+ break;
+ }
+
+ if (pin == PIN_ASSIGNMENT_MAX)
+ pin = PIN_ASSIGNMENT_C;
+
+ pr_debug("pin assignment: %s\n", mdss_dp_pin_name(pin));
+
+ config |= BIT(pin) << 8;
+
config |= (0x1 << 2); /* configure for DPv1.3 */
config |= 0x2; /* Configuring for UFP_D */
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index 4f1435d006b2..2047a047b537 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -698,7 +698,6 @@ static ssize_t hdmi_edid_sysfs_rda_3d_modes(struct device *dev,
}
}
- DEV_DBG("%s: '%s'\n", __func__, buf);
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
return ret;
@@ -1567,7 +1566,9 @@ static void hdmi_edid_detail_desc(struct hdmi_edid_ctrl *edid_ctrl,
frame_data = (active_h + blank_h) * (active_v + blank_v);
if (frame_data) {
- int refresh_rate_khz = (pixel_clk * khz_to_hz) / frame_data;
+ u64 refresh_rate = (u64)pixel_clk * khz_to_hz * khz_to_hz;
+
+ do_div(refresh_rate, frame_data);
timing.active_h = active_h;
timing.front_porch_h = front_porch_h;
@@ -1582,19 +1583,24 @@ static void hdmi_edid_detail_desc(struct hdmi_edid_ctrl *edid_ctrl,
(front_porch_v + pulse_width_v);
timing.active_low_v = active_low_v;
timing.pixel_freq = pixel_clk;
- timing.refresh_rate = refresh_rate_khz * khz_to_hz;
+ timing.refresh_rate = refresh_rate;
timing.interlaced = interlaced;
timing.supported = true;
timing.ar = aspect_ratio_4_3 ? HDMI_RES_AR_4_3 :
(aspect_ratio_5_4 ? HDMI_RES_AR_5_4 :
HDMI_RES_AR_16_9);
- DEV_DBG("%s: new res: %dx%d%s@%dHz\n", __func__,
+ DEV_DBG("%s: new res: %dx%d%s@%d.%d%d%dHz\n", __func__,
timing.active_h, timing.active_v,
interlaced ? "i" : "p",
- timing.refresh_rate / khz_to_hz);
-
- rc = hdmi_set_resv_timing_info(&timing);
+ timing.refresh_rate / khz_to_hz,
+ (timing.refresh_rate % khz_to_hz) / 100,
+ (timing.refresh_rate % 100) / 10,
+ timing.refresh_rate % 10);
+
+ rc = hdmi_get_video_id_code(&timing, NULL);
+ if (rc < 0)
+ rc = hdmi_set_resv_timing_info(&timing);
} else {
DEV_ERR("%s: Invalid frame data\n", __func__);
rc = -EINVAL;
@@ -1642,6 +1648,7 @@ static void hdmi_edid_add_sink_video_format(struct hdmi_edid_ctrl *edid_ctrl,
u32 supported = hdmi_edid_is_mode_supported(edid_ctrl, &timing);
struct hdmi_edid_sink_data *sink_data = &edid_ctrl->sink_data;
struct disp_mode_info *disp_mode_list = sink_data->disp_mode_list;
+ u32 i = 0;
if (video_format >= HDMI_VFRMT_MAX) {
DEV_ERR("%s: video format: %s is not supported\n", __func__,
@@ -1653,6 +1660,15 @@ static void hdmi_edid_add_sink_video_format(struct hdmi_edid_ctrl *edid_ctrl,
video_format, msm_hdmi_mode_2string(video_format),
supported ? "Supported" : "Not-Supported");
+ for (i = 0; i < sink_data->num_of_elements; i++) {
+ u32 vic = disp_mode_list[i].video_format;
+
+ if (vic == video_format) {
+ DEV_DBG("%s: vic %d already added\n", __func__, vic);
+ return;
+ }
+ }
+
if (!ret && supported) {
/* todo: MHL */
disp_mode_list[sink_data->num_of_elements].video_format =
@@ -1970,6 +1986,7 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl)
const u8 *svd = NULL;
u32 has60hz_mode = false;
u32 has50hz_mode = false;
+ u32 desc_offset = 0;
bool read_block0_res = false;
struct hdmi_edid_sink_data *sink_data = NULL;
@@ -2033,103 +2050,66 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl)
if (video_format == HDMI_VFRMT_640x480p60_4_3)
has480p = true;
}
- } else if (!num_of_cea_blocks || read_block0_res) {
- /* Detailed timing descriptors */
- u32 desc_offset = 0;
- /*
- * * Maximum 4 timing descriptor in block 0 - No CEA
- * extension in this case
- * * EDID_FIRST_TIMING_DESC[0x36] - 1st detailed timing
- * descriptor
- * * EDID_DETAIL_TIMING_DESC_BLCK_SZ[0x12] - Each detailed
- * timing descriptor has block size of 18
- */
- while (4 > i && 0 != edid_blk0[0x36+desc_offset]) {
- hdmi_edid_detail_desc(edid_ctrl,
- edid_blk0+0x36+desc_offset,
- &video_format);
-
- DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n",
- __func__, __LINE__,
- msm_hdmi_mode_2string(video_format));
-
- hdmi_edid_add_sink_video_format(edid_ctrl,
- video_format);
-
- if (video_format == HDMI_VFRMT_640x480p60_4_3)
- has480p = true;
-
- /* Make a note of the preferred video format */
- if (i == 0) {
- sink_data->preferred_video_format =
- video_format;
- }
- desc_offset += 0x12;
- ++i;
- }
- } else if (1 == num_of_cea_blocks) {
- u32 desc_offset = 0;
-
- /*
- * Read from both block 0 and block 1
- * Read EDID block[0] as above
- */
- while (4 > i && 0 != edid_blk0[0x36+desc_offset]) {
- hdmi_edid_detail_desc(edid_ctrl,
- edid_blk0+0x36+desc_offset,
- &video_format);
+ }
- DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n",
- __func__, __LINE__,
- msm_hdmi_mode_2string(video_format));
+ i = 0;
+ /* Read DTD resolutions from block0 */
+ while (4 > i && 0 != edid_blk0[0x36+desc_offset]) {
+ hdmi_edid_detail_desc(edid_ctrl,
+ edid_blk0+0x36+desc_offset,
+ &video_format);
+
+ DEV_DBG("[%s:%d] Block-0 Adding vid fmt = [%s]\n",
+ __func__, __LINE__,
+ msm_hdmi_mode_2string(video_format));
- hdmi_edid_add_sink_video_format(edid_ctrl,
- video_format);
+ hdmi_edid_add_sink_video_format(edid_ctrl,
+ video_format);
- if (video_format == HDMI_VFRMT_640x480p60_4_3)
- has480p = true;
+ if (video_format == HDMI_VFRMT_640x480p60_4_3)
+ has480p = true;
- /* Make a note of the preferred video format */
- if (i == 0) {
- sink_data->preferred_video_format =
- video_format;
- }
- desc_offset += 0x12;
- ++i;
+ /* Make a note of the preferred video format */
+ if (i == 0) {
+ sink_data->preferred_video_format =
+ video_format;
}
+ desc_offset += 0x12;
+ ++i;
+ }
- /*
- * * Parse block 1 - CEA extension byte offset of first
- * detailed timing generation - offset is relevant to
- * the offset of block 1
- * * EDID_CEA_EXTENSION_FIRST_DESC[0x82]: Offset to CEA
- * extension first timing desc - indicate the offset of
- * the first detailed timing descriptor
- * * EDID_BLOCK_SIZE = 0x80 Each page size in the EDID ROM
- */
- desc_offset = edid_blk1[0x02];
- while (0 != edid_blk1[desc_offset]) {
- hdmi_edid_detail_desc(edid_ctrl,
- edid_blk1+desc_offset,
- &video_format);
-
- DEV_DBG("[%s:%d] Block-1 Adding vid fmt = [%s]\n",
- __func__, __LINE__,
- msm_hdmi_mode_2string(video_format));
+ /*
+ * * Parse block 1 - CEA extension byte offset of first
+ * detailed timing generation - offset is relevant to
+ * the offset of block 1
+ * * EDID_CEA_EXTENSION_FIRST_DESC[0x82]: Offset to CEA
+ * extension first timing desc - indicate the offset of
+ * the first detailed timing descriptor
+ * * EDID_BLOCK_SIZE = 0x80 Each page size in the EDID ROM
+ */
+ desc_offset = edid_blk1[0x02];
+ i = 0;
+ while (!edid_blk1[desc_offset]) {
+ hdmi_edid_detail_desc(edid_ctrl,
+ edid_blk1+desc_offset,
+ &video_format);
- hdmi_edid_add_sink_video_format(edid_ctrl,
- video_format);
- if (video_format == HDMI_VFRMT_640x480p60_4_3)
- has480p = true;
+ DEV_DBG("[%s:%d] Block-1 Adding vid fmt = [%s]\n",
+ __func__, __LINE__,
+ msm_hdmi_mode_2string(video_format));
- /* Make a note of the preferred video format */
- if (i == 0) {
- sink_data->preferred_video_format =
- video_format;
- }
- desc_offset += 0x12;
- ++i;
+ hdmi_edid_add_sink_video_format(edid_ctrl,
+ video_format);
+ if (video_format == HDMI_VFRMT_640x480p60_4_3)
+ has480p = true;
+
+ /* Make a note of the preferred video format */
+ if (i == 0) {
+ sink_data->preferred_video_format =
+ video_format;
}
+ desc_offset += 0x12;
+ ++i;
}
std_blk = 0;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c
index b3d929b15b44..c9fc8ba8bfdb 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_util.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c
@@ -639,7 +639,7 @@ int hdmi_get_video_id_code(struct msm_hdmi_mode_timing_info *timing_in,
{
int i, vic = -1;
struct msm_hdmi_mode_timing_info supported_timing = {0};
- u32 ret;
+ u32 ret, pclk_delta, pclk, fps_delta, fps;
if (!timing_in) {
pr_err("invalid input\n");
@@ -647,9 +647,16 @@ int hdmi_get_video_id_code(struct msm_hdmi_mode_timing_info *timing_in,
}
/* active_low_h, active_low_v and interlaced are not checked against */
- for (i = 0; i < HDMI_VFRMT_MAX; i++) {
+ for (i = 1; i < HDMI_VFRMT_MAX; i++) {
ret = hdmi_get_supported_mode(&supported_timing, ds_data, i);
+ pclk = supported_timing.pixel_freq;
+ fps = supported_timing.refresh_rate;
+
+ /* as per standard, 0.5% of deviation is allowed */
+ pclk_delta = (pclk / HDMI_KHZ_TO_HZ) * 5;
+ fps_delta = (fps / HDMI_KHZ_TO_HZ) * 5;
+
if (ret || !supported_timing.supported)
continue;
if (timing_in->active_h != supported_timing.active_h)
@@ -668,9 +675,11 @@ int hdmi_get_video_id_code(struct msm_hdmi_mode_timing_info *timing_in,
continue;
if (timing_in->back_porch_v != supported_timing.back_porch_v)
continue;
- if (timing_in->pixel_freq != supported_timing.pixel_freq)
+ if (timing_in->pixel_freq < (pclk - pclk_delta) ||
+ timing_in->pixel_freq > (pclk + pclk_delta))
continue;
- if (timing_in->refresh_rate != supported_timing.refresh_rate)
+ if (timing_in->refresh_rate < (fps - fps_delta) ||
+ timing_in->refresh_rate > (fps + fps_delta))
continue;
vic = (int)supported_timing.video_format;
diff --git a/include/dt-bindings/clock/qcom,gcc-msmfalcon.h b/include/dt-bindings/clock/qcom,gcc-msmfalcon.h
index 609a20422ed1..aa76fbad5083 100644
--- a/include/dt-bindings/clock/qcom,gcc-msmfalcon.h
+++ b/include/dt-bindings/clock/qcom,gcc-msmfalcon.h
@@ -195,6 +195,8 @@
#define GCC_UFS_ICE_CORE_HW_CTL_CLK 180
#define GCC_UFS_PHY_AUX_HW_CTL_CLK 181
#define GCC_UFS_UNIPRO_CORE_HW_CTL_CLK 182
+#define HLOS1_VOTE_TURING_ADSP_SMMU_CLK 183
+#define HLOS2_VOTE_TURING_ADSP_SMMU_CLK 184
/* Block resets */
#define GCC_QUSB2PHY_PRIM_BCR 0
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 23026ba6ff25..0de2d0c780d7 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -669,6 +669,9 @@ void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent);
void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
unsigned long max_rate);
+unsigned long clk_aggregate_rate(struct clk_hw *hw,
+ const struct clk_core *parent);
+
static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src)
{
dst->clk = src->clk;
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 768c44d9ea8b..0ae23ddbc528 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -144,10 +144,10 @@ the appropriate macros. */
/* This needs to be modified manually now, when we add
a new RANGE of SSIDs to the msg_mask_tbl */
#define MSG_MASK_TBL_CNT 25
-#define APPS_EVENT_LAST_ID 0x0B14
+#define APPS_EVENT_LAST_ID 0x0B2A
#define MSG_SSID_0 0
-#define MSG_SSID_0_LAST 118
+#define MSG_SSID_0_LAST 120
#define MSG_SSID_1 500
#define MSG_SSID_1_LAST 506
#define MSG_SSID_2 1000
@@ -163,7 +163,7 @@ the appropriate macros. */
#define MSG_SSID_7 4600
#define MSG_SSID_7_LAST 4615
#define MSG_SSID_8 5000
-#define MSG_SSID_8_LAST 5032
+#define MSG_SSID_8_LAST 5033
#define MSG_SSID_9 5500
#define MSG_SSID_9_LAST 5516
#define MSG_SSID_10 6000
@@ -193,7 +193,7 @@ the appropriate macros. */
#define MSG_SSID_22 10350
#define MSG_SSID_22_LAST 10377
#define MSG_SSID_23 10400
-#define MSG_SSID_23_LAST 10415
+#define MSG_SSID_23_LAST 10416
#define MSG_SSID_24 0xC000
#define MSG_SSID_24_LAST 0xC063
@@ -336,7 +336,9 @@ static const uint32_t msg_bld_masks_0[] = {
MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL,
MSG_LVL_MED,
MSG_LVL_MED,
- MSG_LVL_HIGH
+ MSG_LVL_HIGH,
+ MSG_LVL_LOW,
+ MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL
};
static const uint32_t msg_bld_masks_1[] = {
@@ -535,7 +537,8 @@ static const uint32_t msg_bld_masks_8[] = {
MSG_LVL_MED,
MSG_LVL_MED,
MSG_LVL_MED,
- MSG_LVL_MED
+ MSG_LVL_MED,
+ MSG_LVL_HIGH
};
static const uint32_t msg_bld_masks_9[] = {
@@ -848,13 +851,14 @@ static const uint32_t msg_bld_masks_23[] = {
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
+ MSG_LVL_LOW,
MSG_LVL_LOW
};
/* LOG CODES */
static const uint32_t log_code_last_tbl[] = {
0x0, /* EQUIP ID 0 */
- 0x1966, /* EQUIP ID 1 */
+ 0x1A02, /* EQUIP ID 1 */
0x0, /* EQUIP ID 2 */
0x0, /* EQUIP ID 3 */
0x4910, /* EQUIP ID 4 */
diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h
index 3df370a9e6d3..4b5a339970fa 100644
--- a/include/linux/leds-qpnp-flash.h
+++ b/include/linux/leds-qpnp-flash.h
@@ -18,6 +18,9 @@
#define ENABLE_REGULATOR BIT(0)
#define DISABLE_REGULATOR BIT(1)
#define QUERY_MAX_CURRENT BIT(2)
+#define PRE_FLASH BIT(3)
+
+#define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(3, 0)
int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
int *max_current);
diff --git a/include/linux/mfd/wcd934x/registers.h b/include/linux/mfd/wcd934x/registers.h
index 085e16d66bc4..a824875096e4 100644
--- a/include/linux/mfd/wcd934x/registers.h
+++ b/include/linux/mfd/wcd934x/registers.h
@@ -800,9 +800,11 @@ enum {
#define WCD934X_VBADC_NEW_ADC_DOUTLSB 0x0731
#define WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL 0x0732
#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL 0x0733
+#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L 0x0733
#define WCD934X_HPH_NEW_INT_RDAC_VREF_CTL 0x0734
#define WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL 0x0735
#define WCD934X_HPH_NEW_INT_RDAC_MISC1 0x0736
+#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R 0x0736
#define WCD934X_HPH_NEW_INT_PA_MISC1 0x0737
#define WCD934X_HPH_NEW_INT_PA_MISC2 0x0738
#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC 0x0739
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 03853d956b41..218cd875ee5a 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -216,8 +216,12 @@ enum power_supply_property {
POWER_SUPPLY_PROP_TYPEC_POWER_ROLE,
POWER_SUPPLY_PROP_PD_ALLOWED,
POWER_SUPPLY_PROP_PD_ACTIVE,
+ POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
+ POWER_SUPPLY_PROP_PD_CURRENT_MAX,
+ POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED,
POWER_SUPPLY_PROP_CHARGER_TEMP,
POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
+ POWER_SUPPLY_PROP_PARALLEL_DISABLE,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
index b5425dd7eaea..4117b0d47b0d 100644
--- a/include/soc/qcom/smem.h
+++ b/include/soc/qcom/smem.h
@@ -22,11 +22,11 @@ enum {
SMEM_DSPS,
SMEM_WCNSS,
SMEM_MODEM_Q6_FW,
+ SMEM_CDSP = SMEM_MODEM_Q6_FW,
SMEM_RPM,
SMEM_TZ,
SMEM_SPSS,
SMEM_HYP,
- SMEM_CDSP,
NUM_SMEM_SUBSYSTEMS,
};
diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h
index aa3b363e95e1..2beb9b38a46a 100644
--- a/include/sound/wcd-dsp-mgr.h
+++ b/include/sound/wcd-dsp-mgr.h
@@ -65,9 +65,14 @@ enum wdsp_event_type {
WDSP_EVENT_RESUME,
};
-enum wdsp_intr {
+enum wdsp_signal {
+ /* Hardware generated interrupts signalled to manager */
WDSP_IPC1_INTR,
WDSP_ERR_INTR,
+
+ /* Other signals */
+ WDSP_CDC_DOWN_SIGNAL,
+ WDSP_CDC_UP_SIGNAL,
};
/*
@@ -92,7 +97,7 @@ struct wdsp_img_section {
u8 *data;
};
-struct wdsp_err_intr_arg {
+struct wdsp_err_signal_arg {
bool mem_dumps_enabled;
u32 remote_start_addr;
size_t dump_size;
@@ -104,8 +109,9 @@ struct wdsp_err_intr_arg {
* their own ops to manager driver
* @get_dev_for_cmpnt: components can use this to get handle
* to struct device * of any other component
- * @intr_handler: callback to notify manager driver that interrupt
- * has occurred.
+ * @signal_handler: callback to notify manager driver that signal
+ * has occurred. Cannot be called from interrupt
+ * context as this can sleep
* @vote_for_dsp: notifies manager that dsp should be booted up
* @suspend: notifies manager that one component wants to suspend.
* Manager will make sure to suspend all components in order
@@ -120,8 +126,8 @@ struct wdsp_mgr_ops {
struct wdsp_cmpnt_ops *ops);
struct device *(*get_dev_for_cmpnt)(struct device *wdsp_dev,
enum wdsp_cmpnt_type type);
- int (*intr_handler)(struct device *wdsp_dev,
- enum wdsp_intr intr, void *arg);
+ int (*signal_handler)(struct device *wdsp_dev,
+ enum wdsp_signal signal, void *arg);
int (*vote_for_dsp)(struct device *wdsp_dev, bool vote);
int (*suspend)(struct device *wdsp_dev);
int (*resume)(struct device *wdsp_dev);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index ff7f6f35fc8f..024fb1007c78 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -8239,7 +8239,7 @@ void set_curr_task(int cpu, struct task_struct *p)
/* task_group_lock serializes the addition/removal of task groups */
static DEFINE_SPINLOCK(task_group_lock);
-static void free_sched_group(struct task_group *tg)
+static void sched_free_group(struct task_group *tg)
{
free_fair_sched_group(tg);
free_rt_sched_group(tg);
@@ -8265,7 +8265,7 @@ struct task_group *sched_create_group(struct task_group *parent)
return tg;
err:
- free_sched_group(tg);
+ sched_free_group(tg);
return ERR_PTR(-ENOMEM);
}
@@ -8285,27 +8285,24 @@ void sched_online_group(struct task_group *tg, struct task_group *parent)
}
/* rcu callback to free various structures associated with a task group */
-static void free_sched_group_rcu(struct rcu_head *rhp)
+static void sched_free_group_rcu(struct rcu_head *rhp)
{
/* now it should be safe to free those cfs_rqs */
- free_sched_group(container_of(rhp, struct task_group, rcu));
+ sched_free_group(container_of(rhp, struct task_group, rcu));
}
-/* Destroy runqueue etc associated with a task group */
void sched_destroy_group(struct task_group *tg)
{
/* wait for possible concurrent references to cfs_rqs complete */
- call_rcu(&tg->rcu, free_sched_group_rcu);
+ call_rcu(&tg->rcu, sched_free_group_rcu);
}
void sched_offline_group(struct task_group *tg)
{
unsigned long flags;
- int i;
/* end participation in shares distribution */
- for_each_possible_cpu(i)
- unregister_fair_sched_group(tg, i);
+ unregister_fair_sched_group(tg);
spin_lock_irqsave(&task_group_lock, flags);
list_del_rcu(&tg->list);
@@ -8756,31 +8753,26 @@ cpu_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
if (IS_ERR(tg))
return ERR_PTR(-ENOMEM);
+ sched_online_group(tg, parent);
+
return &tg->css;
}
-static int cpu_cgroup_css_online(struct cgroup_subsys_state *css)
+static void cpu_cgroup_css_released(struct cgroup_subsys_state *css)
{
struct task_group *tg = css_tg(css);
- struct task_group *parent = css_tg(css->parent);
- if (parent)
- sched_online_group(tg, parent);
- return 0;
+ sched_offline_group(tg);
}
static void cpu_cgroup_css_free(struct cgroup_subsys_state *css)
{
struct task_group *tg = css_tg(css);
- sched_destroy_group(tg);
-}
-
-static void cpu_cgroup_css_offline(struct cgroup_subsys_state *css)
-{
- struct task_group *tg = css_tg(css);
-
- sched_offline_group(tg);
+ /*
+ * Relies on the RCU grace period between css_released() and this.
+ */
+ sched_free_group(tg);
}
static void cpu_cgroup_fork(struct task_struct *task, void *private)
@@ -9147,9 +9139,8 @@ static struct cftype cpu_files[] = {
struct cgroup_subsys cpu_cgrp_subsys = {
.css_alloc = cpu_cgroup_css_alloc,
+ .css_released = cpu_cgroup_css_released,
.css_free = cpu_cgroup_css_free,
- .css_online = cpu_cgroup_css_online,
- .css_offline = cpu_cgroup_css_offline,
.fork = cpu_cgroup_fork,
.can_attach = cpu_cgroup_can_attach,
.attach = cpu_cgroup_attach,
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index df23b0365527..6362b864e2b1 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2619,7 +2619,7 @@ struct cluster_cpu_stats {
int best_idle_cpu, least_loaded_cpu;
int best_capacity_cpu, best_cpu, best_sibling_cpu;
int min_cost, best_sibling_cpu_cost;
- int best_cpu_cstate;
+ int best_cpu_wakeup_latency;
u64 min_load, best_load, best_sibling_cpu_load;
s64 highest_spare_capacity;
};
@@ -2827,19 +2827,19 @@ next_best_cluster(struct sched_cluster *cluster, struct cpu_select_env *env,
static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats,
struct cpu_select_env *env, int cpu_cost)
{
- int cpu_cstate;
+ int wakeup_latency;
int prev_cpu = env->prev_cpu;
- cpu_cstate = cpu_rq(cpu)->cstate;
+ wakeup_latency = cpu_rq(cpu)->wakeup_latency;
if (env->need_idle) {
stats->min_cost = cpu_cost;
if (idle_cpu(cpu)) {
- if (cpu_cstate < stats->best_cpu_cstate ||
- (cpu_cstate == stats->best_cpu_cstate &&
- cpu == prev_cpu)) {
+ if (wakeup_latency < stats->best_cpu_wakeup_latency ||
+ (wakeup_latency == stats->best_cpu_wakeup_latency &&
+ cpu == prev_cpu)) {
stats->best_idle_cpu = cpu;
- stats->best_cpu_cstate = cpu_cstate;
+ stats->best_cpu_wakeup_latency = wakeup_latency;
}
} else {
if (env->cpu_load < stats->min_load ||
@@ -2855,7 +2855,7 @@ static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats,
if (cpu_cost < stats->min_cost) {
stats->min_cost = cpu_cost;
- stats->best_cpu_cstate = cpu_cstate;
+ stats->best_cpu_wakeup_latency = wakeup_latency;
stats->best_load = env->cpu_load;
stats->best_cpu = cpu;
env->sbc_best_flag = SBC_FLAG_CPU_COST;
@@ -2864,11 +2864,11 @@ static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats,
/* CPU cost is the same. Start breaking the tie by C-state */
- if (cpu_cstate > stats->best_cpu_cstate)
+ if (wakeup_latency > stats->best_cpu_wakeup_latency)
return;
- if (cpu_cstate < stats->best_cpu_cstate) {
- stats->best_cpu_cstate = cpu_cstate;
+ if (wakeup_latency < stats->best_cpu_wakeup_latency) {
+ stats->best_cpu_wakeup_latency = wakeup_latency;
stats->best_load = env->cpu_load;
stats->best_cpu = cpu;
env->sbc_best_flag = SBC_FLAG_COST_CSTATE_TIE_BREAKER;
@@ -2883,8 +2883,8 @@ static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats,
}
if (stats->best_cpu != prev_cpu &&
- ((cpu_cstate == 0 && env->cpu_load < stats->best_load) ||
- (cpu_cstate > 0 && env->cpu_load > stats->best_load))) {
+ ((wakeup_latency == 0 && env->cpu_load < stats->best_load) ||
+ (wakeup_latency > 0 && env->cpu_load > stats->best_load))) {
stats->best_load = env->cpu_load;
stats->best_cpu = cpu;
env->sbc_best_flag = SBC_FLAG_CSTATE_LOAD;
@@ -2979,7 +2979,7 @@ static inline void init_cluster_cpu_stats(struct cluster_cpu_stats *stats)
stats->min_load = stats->best_sibling_cpu_load = ULLONG_MAX;
stats->highest_spare_capacity = 0;
stats->least_loaded_cpu = -1;
- stats->best_cpu_cstate = INT_MAX;
+ stats->best_cpu_wakeup_latency = INT_MAX;
/* No need to initialize stats->best_load */
}
@@ -9653,11 +9653,8 @@ void free_fair_sched_group(struct task_group *tg)
for_each_possible_cpu(i) {
if (tg->cfs_rq)
kfree(tg->cfs_rq[i]);
- if (tg->se) {
- if (tg->se[i])
- remove_entity_load_avg(tg->se[i]);
+ if (tg->se)
kfree(tg->se[i]);
- }
}
kfree(tg->cfs_rq);
@@ -9705,21 +9702,29 @@ err:
return 0;
}
-void unregister_fair_sched_group(struct task_group *tg, int cpu)
+void unregister_fair_sched_group(struct task_group *tg)
{
- struct rq *rq = cpu_rq(cpu);
unsigned long flags;
+ struct rq *rq;
+ int cpu;
- /*
- * Only empty task groups can be destroyed; so we can speculatively
- * check on_list without danger of it being re-added.
- */
- if (!tg->cfs_rq[cpu]->on_list)
- return;
+ for_each_possible_cpu(cpu) {
+ if (tg->se[cpu])
+ remove_entity_load_avg(tg->se[cpu]);
- raw_spin_lock_irqsave(&rq->lock, flags);
- list_del_leaf_cfs_rq(tg->cfs_rq[cpu]);
- raw_spin_unlock_irqrestore(&rq->lock, flags);
+ /*
+ * Only empty task groups can be destroyed; so we can speculatively
+ * check on_list without danger of it being re-added.
+ */
+ if (!tg->cfs_rq[cpu]->on_list)
+ continue;
+
+ rq = cpu_rq(cpu);
+
+ raw_spin_lock_irqsave(&rq->lock, flags);
+ list_del_leaf_cfs_rq(tg->cfs_rq[cpu]);
+ raw_spin_unlock_irqrestore(&rq->lock, flags);
+ }
}
void init_tg_cfs_entry(struct task_group *tg, struct cfs_rq *cfs_rq,
@@ -9801,7 +9806,7 @@ int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent)
return 1;
}
-void unregister_fair_sched_group(struct task_group *tg, int cpu) { }
+void unregister_fair_sched_group(struct task_group *tg) { }
#endif /* CONFIG_FAIR_GROUP_SCHED */
diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c
index a0686ea29243..3d5de8ba70a2 100644
--- a/kernel/sched/hmp.c
+++ b/kernel/sched/hmp.c
@@ -24,6 +24,8 @@
#include <trace/events/sched.h>
+#define CSTATE_LATENCY_GRANULARITY_SHIFT (6)
+
const char *task_event_names[] = {"PUT_PREV_TASK", "PICK_NEXT_TASK",
"TASK_WAKE", "TASK_MIGRATE", "TASK_UPDATE",
"IRQ_UPDATE"};
@@ -99,7 +101,10 @@ sched_set_cpu_cstate(int cpu, int cstate, int wakeup_energy, int wakeup_latency)
rq->cstate = cstate; /* C1, C2 etc */
rq->wakeup_energy = wakeup_energy;
- rq->wakeup_latency = wakeup_latency;
+ /* disregard small latency delta (64 us). */
+ rq->wakeup_latency = ((wakeup_latency >>
+ CSTATE_LATENCY_GRANULARITY_SHIFT) <<
+ CSTATE_LATENCY_GRANULARITY_SHIFT);
}
/*
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index ada5e580e968..27b28369440d 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -313,7 +313,7 @@ extern int tg_nop(struct task_group *tg, void *data);
extern void free_fair_sched_group(struct task_group *tg);
extern int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent);
-extern void unregister_fair_sched_group(struct task_group *tg, int cpu);
+extern void unregister_fair_sched_group(struct task_group *tg);
extern void init_tg_cfs_entry(struct task_group *tg, struct cfs_rq *cfs_rq,
struct sched_entity *se, int cpu,
struct sched_entity *parent);
diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c
index 3c964d6d3856..4f8182302e5e 100644
--- a/kernel/sched/tune.c
+++ b/kernel/sched/tune.c
@@ -29,7 +29,7 @@ struct schedtune {
static inline struct schedtune *css_st(struct cgroup_subsys_state *css)
{
- return css ? container_of(css, struct schedtune, css) : NULL;
+ return container_of(css, struct schedtune, css);
}
static inline struct schedtune *task_schedtune(struct task_struct *tsk)
diff --git a/net/core/dev.c b/net/core/dev.c
index a299c3956daa..a4c647893e52 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3544,9 +3544,6 @@ static int netif_rx_internal(struct sk_buff *skb)
trace_netif_rx(skb);
#ifdef CONFIG_RPS
- WARN_ONCE(skb_cloned(skb), "Cloned packet from dev %s\n",
- skb->dev->name);
-
if (static_key_false(&rps_needed)) {
struct rps_dev_flow voidflow, *rflow = &voidflow;
int cpu;
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index 3e47e0641780..23b7c76ff2d8 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -416,7 +416,7 @@ country EE: DFS-ETSI
(57240 - 65880 @ 2160), (40), NO-OUTDOOR
country EG: DFS-ETSI
- (2402 - 2482 @ 40), (20)
+ (2402 - 2482 @ 20), (20)
(5170 - 5250 @ 20), (23)
(5250 - 5330 @ 20), (23), DFS
diff --git a/sound/soc/codecs/msm_hdmi_codec_rx.c b/sound/soc/codecs/msm_hdmi_codec_rx.c
index dee66f231ceb..7d649ba2b505 100644
--- a/sound/soc/codecs/msm_hdmi_codec_rx.c
+++ b/sound/soc/codecs/msm_hdmi_codec_rx.c
@@ -318,8 +318,9 @@ static void msm_ext_disp_audio_codec_rx_dai_shutdown(
struct msm_ext_disp_audio_codec_rx_data *codec_data =
dev_get_drvdata(dai->codec->dev);
- if (!codec_data || !codec_data->ext_disp_ops.cable_status) {
- dev_err(dai->dev, "%s: codec data or cable_status is null\n",
+ if (!codec_data || !codec_data->ext_disp_ops.teardown_done ||
+ !codec_data->ext_disp_ops.cable_status) {
+ dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n",
__func__);
return;
}
@@ -332,6 +333,8 @@ static void msm_ext_disp_audio_codec_rx_dai_shutdown(
__func__);
}
+ codec_data->ext_disp_ops.teardown_done(
+ codec_data->ext_disp_core_pdev);
return;
}
diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c
index ee8b27dbec64..d9d413f0a80a 100644
--- a/sound/soc/codecs/wcd-dsp-mgr.c
+++ b/sound/soc/codecs/wcd-dsp-mgr.c
@@ -136,7 +136,7 @@ struct wdsp_ramdump_data {
void *rd_v_addr;
/* Data provided through error interrupt */
- struct wdsp_err_intr_arg err_data;
+ struct wdsp_err_signal_arg err_data;
};
struct wdsp_mgr_priv {
@@ -608,7 +608,7 @@ static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev,
static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp)
{
struct wdsp_img_section img_section;
- struct wdsp_err_intr_arg *data = &wdsp->dump_data.err_data;
+ struct wdsp_err_signal_arg *data = &wdsp->dump_data.err_data;
struct ramdump_segment rd_seg;
int ret = 0;
@@ -684,17 +684,18 @@ static void wdsp_ssr_work_fn(struct work_struct *work)
WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
- wdsp_collect_ramdumps(wdsp);
-
- /* In case of CDC_DOWN event, the DSP is already shutdown */
- if (wdsp->ssr_type != WDSP_SSR_TYPE_CDC_DOWN) {
+ /* Issue ramdumps and shutdown only if DSP is currently booted */
+ if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
+ wdsp_collect_ramdumps(wdsp);
ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
WDSP_EVENT_DO_SHUTDOWN, NULL);
if (IS_ERR_VALUE(ret))
WDSP_ERR(wdsp, "Failed WDSP shutdown, err = %d", ret);
+
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN,
+ NULL);
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
}
- wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL);
- WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
ret = wait_for_completion_timeout(&wdsp->ready_compl,
@@ -739,7 +740,7 @@ static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg,
enum wdsp_ssr_type ssr_type)
{
enum wdsp_ssr_type current_ssr_type;
- struct wdsp_err_intr_arg *err_data;
+ struct wdsp_err_signal_arg *err_data;
WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
@@ -750,7 +751,7 @@ static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg,
wdsp->ssr_type = ssr_type;
if (arg) {
- err_data = (struct wdsp_err_intr_arg *) arg;
+ err_data = (struct wdsp_err_signal_arg *) arg;
memcpy(&wdsp->dump_data.err_data, err_data,
sizeof(*err_data));
} else {
@@ -761,16 +762,29 @@ static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg,
switch (ssr_type) {
case WDSP_SSR_TYPE_WDSP_DOWN:
- case WDSP_SSR_TYPE_CDC_DOWN:
__wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY);
- if (ssr_type == WDSP_SSR_TYPE_CDC_DOWN)
- __wdsp_clr_ready_locked(wdsp,
- WDSP_SSR_STATUS_CDC_READY);
wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN,
NULL);
schedule_work(&wdsp->ssr_work);
break;
+ case WDSP_SSR_TYPE_CDC_DOWN:
+ __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY);
+ /*
+ * If DSP is booted when CDC_DOWN is received, it needs
+ * to be shutdown.
+ */
+ if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
+ __wdsp_clr_ready_locked(wdsp,
+ WDSP_SSR_STATUS_WDSP_READY);
+ wdsp_broadcast_event_downseq(wdsp,
+ WDSP_EVENT_PRE_SHUTDOWN,
+ NULL);
+ }
+
+ schedule_work(&wdsp->ssr_work);
+ break;
+
case WDSP_SSR_TYPE_CDC_UP:
__wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true);
break;
@@ -787,8 +801,8 @@ static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg,
return 0;
}
-static int wdsp_intr_handler(struct device *wdsp_dev,
- enum wdsp_intr intr, void *arg)
+static int wdsp_signal_handler(struct device *wdsp_dev,
+ enum wdsp_signal signal, void *arg)
{
struct wdsp_mgr_priv *wdsp;
int ret;
@@ -799,7 +813,9 @@ static int wdsp_intr_handler(struct device *wdsp_dev,
wdsp = dev_get_drvdata(wdsp_dev);
WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
- switch (intr) {
+ WDSP_DBG(wdsp, "Raised signal %d", signal);
+
+ switch (signal) {
case WDSP_IPC1_INTR:
ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC,
WDSP_EVENT_IPC1_INTR, NULL);
@@ -807,14 +823,20 @@ static int wdsp_intr_handler(struct device *wdsp_dev,
case WDSP_ERR_INTR:
ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN);
break;
+ case WDSP_CDC_DOWN_SIGNAL:
+ ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_DOWN);
+ break;
+ case WDSP_CDC_UP_SIGNAL:
+ ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_UP);
+ break;
default:
ret = -EINVAL;
break;
}
if (IS_ERR_VALUE(ret))
- WDSP_ERR(wdsp, "handling intr %d failed with error %d",
- intr, ret);
+ WDSP_ERR(wdsp, "handling signal %d failed with error %d",
+ signal, ret);
WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
return ret;
@@ -870,7 +892,7 @@ static int wdsp_resume(struct device *wdsp_dev)
static struct wdsp_mgr_ops wdsp_ops = {
.register_cmpnt_ops = wdsp_register_cmpnt_ops,
.get_dev_for_cmpnt = wdsp_get_dev_for_cmpnt,
- .intr_handler = wdsp_intr_handler,
+ .signal_handler = wdsp_signal_handler,
.vote_for_dsp = wdsp_vote_for_dsp,
.suspend = wdsp_suspend,
.resume = wdsp_resume,
diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c
index 60efcb174740..0a9c283c250c 100644
--- a/sound/soc/codecs/wcd-spi.c
+++ b/sound/soc/codecs/wcd-spi.c
@@ -319,7 +319,7 @@ static int wcd_spi_transfer_split(struct spi_device *spi,
u32 addr = data_msg->remote_addr;
u8 *data = data_msg->data;
int remain_size = data_msg->len;
- int to_xfer, loop_cnt, ret;
+ int to_xfer, loop_cnt, ret = 0;
/* Perform single writes until multi word alignment is met */
loop_cnt = 1;
@@ -631,6 +631,14 @@ static int wcd_spi_init(struct spi_device *spi)
if (IS_ERR_VALUE(ret))
goto err_wr_en;
+ /*
+ * In case spi_init is called after component deinit,
+ * it is possible hardware register state is also reset.
+ * Sync the regcache here so hardware state is updated
+ * to reflect the cache.
+ */
+ regcache_sync(wcd_spi->regmap);
+
regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG,
0x0F3D0800);
@@ -837,7 +845,7 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
void *data)
{
struct spi_device *spi = to_spi_device(dev);
- int ret;
+ int ret = 0;
dev_dbg(&spi->dev, "%s: event type %d\n",
__func__, event);
@@ -1093,46 +1101,12 @@ static struct regmap_config wcd_spi_regmap_cfg = {
static int wdsp_spi_init(struct device *dev, void *priv_data)
{
struct spi_device *spi = to_spi_device(dev);
- struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
int ret;
- wcd_spi->reg_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.reg_bits, 8);
- wcd_spi->val_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.val_bits, 8);
-
- wcd_spi->regmap = devm_regmap_init(&spi->dev, &wcd_spi_regmap_bus,
- &spi->dev, &wcd_spi_regmap_cfg);
- if (IS_ERR(wcd_spi->regmap)) {
- ret = PTR_ERR(wcd_spi->regmap);
- dev_err(&spi->dev, "%s: Failed to allocate regmap, err = %d\n",
- __func__, ret);
- goto err_regmap;
- }
-
- if (wcd_spi_debugfs_init(spi))
- dev_err(&spi->dev, "%s: Failed debugfs init\n", __func__);
-
- spi_message_init(&wcd_spi->msg1);
- spi_message_add_tail(&wcd_spi->xfer1, &wcd_spi->msg1);
-
- spi_message_init(&wcd_spi->msg2);
- spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2);
- spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2);
-
ret = wcd_spi_init(spi);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE(ret))
dev_err(&spi->dev, "%s: Init failed, err = %d\n",
__func__, ret);
- goto err_init;
- }
-
- return 0;
-
-err_init:
- spi_transfer_del(&wcd_spi->xfer1);
- spi_transfer_del(&wcd_spi->xfer2[0]);
- spi_transfer_del(&wcd_spi->xfer2[1]);
-
-err_regmap:
return ret;
}
@@ -1141,9 +1115,11 @@ static int wdsp_spi_deinit(struct device *dev, void *priv_data)
struct spi_device *spi = to_spi_device(dev);
struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
- spi_transfer_del(&wcd_spi->xfer1);
- spi_transfer_del(&wcd_spi->xfer2[0]);
- spi_transfer_del(&wcd_spi->xfer2[1]);
+ /*
+ * Deinit means the hardware is reset. Mark the cache
+ * as dirty here, so init will sync the cache
+ */
+ regcache_mark_dirty(wcd_spi->regmap);
return 0;
}
@@ -1170,9 +1146,34 @@ static int wcd_spi_component_bind(struct device *dev,
ret = wcd_spi->m_ops->register_cmpnt_ops(master, dev,
wcd_spi,
&wdsp_spi_ops);
- if (ret)
+ if (ret) {
dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n",
__func__, ret);
+ goto done;
+ }
+
+ wcd_spi->reg_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.reg_bits, 8);
+ wcd_spi->val_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.val_bits, 8);
+
+ wcd_spi->regmap = devm_regmap_init(&spi->dev, &wcd_spi_regmap_bus,
+ &spi->dev, &wcd_spi_regmap_cfg);
+ if (IS_ERR(wcd_spi->regmap)) {
+ ret = PTR_ERR(wcd_spi->regmap);
+ dev_err(&spi->dev, "%s: Failed to allocate regmap, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ if (wcd_spi_debugfs_init(spi))
+ dev_err(&spi->dev, "%s: Failed debugfs init\n", __func__);
+
+ spi_message_init(&wcd_spi->msg1);
+ spi_message_add_tail(&wcd_spi->xfer1, &wcd_spi->msg1);
+
+ spi_message_init(&wcd_spi->msg2);
+ spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2);
+ spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2);
+done:
return ret;
}
@@ -1185,6 +1186,10 @@ static void wcd_spi_component_unbind(struct device *dev,
wcd_spi->m_dev = NULL;
wcd_spi->m_ops = NULL;
+
+ spi_transfer_del(&wcd_spi->xfer1);
+ spi_transfer_del(&wcd_spi->xfer2[0]);
+ spi_transfer_del(&wcd_spi->xfer2[1]);
}
static const struct component_ops wcd_spi_component_ops = {
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 46b8e7f72eb8..9394ee52cad0 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -4596,9 +4596,11 @@ static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
if (!ret) {
wcd_clsh_imped_config(codec, impedl, false);
set_bit(CLASSH_CONFIG, &tasha->status_mask);
- } else
+ } else {
dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n",
__func__, ret);
+ ret = 0;
+ }
break;
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
index 55072466af55..4e3e769585e6 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-dsd.c
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
@@ -28,6 +28,9 @@
#define DSD_VOLUME_STEP_DELAY_US ((1000 * DSD_VOLUME_UPDATE_DELAY_MS) / \
(2 * DSD_VOLUME_STEPS))
+#define TAVIL_VERSION_1_0 0
+#define TAVIL_VERSION_1_1 1
+
static const DECLARE_TLV_DB_MINMAX(tavil_dsd_db_scale, DSD_VOLUME_MIN_M110dB,
DSD_VOLUME_MAX_0dB);
@@ -369,6 +372,14 @@ static void tavil_dsd_data_pull(struct snd_soc_codec *codec, int dsd_num,
}
}
+static void tavil_dsd_update_volume(struct tavil_dsd_config *dsd_conf)
+{
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0,
+ 0x01, 0x01);
+ snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0,
+ 0x01, 0x00);
+}
+
static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -429,6 +440,8 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
/* Apply Gain */
snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1,
dsd_conf->volume[DSD0]);
+ if (dsd_conf->version == TAVIL_VERSION_1_1)
+ tavil_dsd_update_volume(dsd_conf);
} else if (w->shift == DSD1) {
snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
@@ -440,6 +453,8 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
/* Apply Gain */
snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1,
dsd_conf->volume[DSD1]);
+ if (dsd_conf->version == TAVIL_VERSION_1_1)
+ tavil_dsd_update_volume(dsd_conf);
}
/* 10msec sleep required after DSD clock is set */
usleep_range(10000, 10100);
@@ -538,16 +553,23 @@ static int tavil_dsd_vol_put(struct snd_kcontrol *kcontrol,
snd_soc_write(codec,
WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx,
nv1);
+ if (dsd_conf->version == TAVIL_VERSION_1_1)
+ tavil_dsd_update_volume(dsd_conf);
+
/* sleep required after each volume step */
usleep_range(DSD_VOLUME_STEP_DELAY_US,
(DSD_VOLUME_STEP_DELAY_US +
DSD_VOLUME_USLEEP_MARGIN_US));
}
- if (nv1 != nv[dsd_idx])
+ if (nv1 != nv[dsd_idx]) {
snd_soc_write(codec,
WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx,
nv[dsd_idx]);
+ if (dsd_conf->version == TAVIL_VERSION_1_1)
+ tavil_dsd_update_volume(dsd_conf);
+ }
+
dsd_conf->volume[dsd_idx] = nv[dsd_idx];
}
@@ -629,9 +651,14 @@ struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec)
dsd_conf->codec = codec;
+ /* Read version */
+ dsd_conf->version = snd_soc_read(codec,
+ WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0);
/* DSD registers init */
- snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00);
- snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00);
+ if (dsd_conf->version == TAVIL_VERSION_1_0) {
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00);
+ }
/* DSD0: Mute EN */
snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04);
/* DSD1: Mute EN */
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/sound/soc/codecs/wcd934x/wcd934x-dsd.h
index c033795beb9b..21450c90a272 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-dsd.h
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.h
@@ -40,6 +40,7 @@ struct tavil_dsd_config {
u32 base_sample_rate[DSD_MAX];
int volume[DSD_MAX];
struct mutex vol_mutex;
+ int version;
};
#ifdef CONFIG_SND_SOC_WCD934X_DSD
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
index e649770297f1..8d2247176607 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
@@ -523,7 +523,9 @@ static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl)
ARRAY_SIZE(mem_enable_values),
mem_enable_values);
- snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0x05);
+ /* Make sure Deep sleep of memories is enabled for all banks */
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF);
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F);
done:
return ret;
}
@@ -533,6 +535,7 @@ static void wcd_cntl_disable_memory(struct wcd_dsp_cntl *cntl)
struct snd_soc_codec *codec = cntl->codec;
snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF);
+ snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F);
snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, 0xFF);
snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, 0xFF);
snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, 0x07);
@@ -646,9 +649,9 @@ static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data)
complete(&cntl->boot_complete);
if (cntl->m_dev && cntl->m_ops &&
- cntl->m_ops->intr_handler)
- ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_IPC1_INTR,
- NULL);
+ cntl->m_ops->signal_handler)
+ ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_IPC1_INTR,
+ NULL);
else
ret = -EINVAL;
@@ -663,7 +666,7 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data)
{
struct wcd_dsp_cntl *cntl = data;
struct snd_soc_codec *codec = cntl->codec;
- struct wdsp_err_intr_arg arg;
+ struct wdsp_err_signal_arg arg;
u16 status = 0;
u8 reg_val;
int ret = 0;
@@ -678,19 +681,19 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data)
__func__, status);
if ((status & cntl->irqs.fatal_irqs) &&
- (cntl->m_dev && cntl->m_ops && cntl->m_ops->intr_handler)) {
+ (cntl->m_dev && cntl->m_ops && cntl->m_ops->signal_handler)) {
arg.mem_dumps_enabled = cntl->ramdump_enable;
arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR;
arg.dump_size = WCD_934X_RAMDUMP_SIZE;
- ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_ERR_INTR,
- &arg);
+ ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_ERR_INTR,
+ &arg);
if (IS_ERR_VALUE(ret))
dev_err(cntl->codec->dev,
"%s: Failed to handle fatal irq 0x%x\n",
__func__, status & cntl->irqs.fatal_irqs);
wcd_cntl_change_online_state(cntl, 0);
} else {
- dev_err(cntl->codec->dev, "%s: Invalid intr_handler\n",
+ dev_err(cntl->codec->dev, "%s: Invalid signal_handler\n",
__func__);
}
@@ -833,8 +836,7 @@ static int wcd_control_init(struct device *dev, void *priv_data)
struct snd_soc_codec *codec = cntl->codec;
struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
- char wcd_cntl_dir_name[WCD_CNTL_DIR_NAME_LEN_MAX];
- int ret, ret1;
+ int ret;
bool err_irq_requested = false;
ret = wcd9xxx_request_irq(core_res,
@@ -876,25 +878,8 @@ static int wcd_control_init(struct device *dev, void *priv_data)
}
wcd_cntl_cpar_ctrl(cntl, true);
- snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX,
- "%s%d", "wdsp", cntl->dsp_instance);
- wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl);
- ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl);
- if (IS_ERR_VALUE(ret)) {
- dev_err(codec->dev,
- "%s: Failed to init sysfs %d\n",
- __func__, ret);
- goto err_sysfs_init;
- }
-
return 0;
-err_sysfs_init:
- wcd_cntl_cpar_ctrl(cntl, false);
- ret1 = wcd_cntl_clocks_disable(cntl);
- if (IS_ERR_VALUE(ret1))
- dev_err(codec->dev, "%s: Failed to disable clocks, err = %d\n",
- __func__, ret1);
err_clk_enable:
/* Mask all error interrupts */
snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF);
@@ -916,12 +901,6 @@ static int wcd_control_deinit(struct device *dev, void *priv_data)
struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
- /* Remove the sysfs entries */
- wcd_cntl_sysfs_remove(cntl);
-
- /* Remove the debugfs entries */
- wcd_cntl_debugfs_remove(cntl);
-
wcd_cntl_clocks_disable(cntl);
wcd_cntl_cpar_ctrl(cntl, false);
@@ -951,6 +930,7 @@ static int wcd_ctrl_component_bind(struct device *dev,
struct snd_card *card;
struct snd_info_entry *entry;
char proc_name[WCD_PROCFS_ENTRY_MAX_LEN];
+ char wcd_cntl_dir_name[WCD_CNTL_DIR_NAME_LEN_MAX];
int ret = 0;
if (!dev || !master || !data) {
@@ -982,6 +962,17 @@ static int wcd_ctrl_component_bind(struct device *dev,
goto done;
}
+ snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX,
+ "%s%d", "wdsp", cntl->dsp_instance);
+ ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(dev, "%s: sysfs_init failed, err = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl);
+
codec = cntl->codec;
card = codec->component.card->snd_card;
snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe",
@@ -1032,6 +1023,13 @@ static void wcd_ctrl_component_unbind(struct device *dev,
cntl->m_dev = NULL;
cntl->m_ops = NULL;
+
+ /* Remove the sysfs entries */
+ wcd_cntl_sysfs_remove(cntl);
+
+ /* Remove the debugfs entries */
+ wcd_cntl_debugfs_remove(cntl);
+
}
static const struct component_ops wcd_ctrl_component_ops = {
@@ -1040,6 +1038,60 @@ static const struct component_ops wcd_ctrl_component_ops = {
};
/*
+ * wcd_dsp_ssr_event: handle the SSR event raised by caller.
+ * @cntl: Handle to the wcd_dsp_cntl structure
+ * @event: The SSR event to be handled
+ *
+ * Notifies the manager driver about the SSR event.
+ * Returns 0 on success and negative error code on error.
+ */
+int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event)
+{
+ int ret = 0;
+
+ if (!cntl) {
+ pr_err("%s: Invalid handle to control\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!cntl->m_dev || !cntl->m_ops || !cntl->m_ops->signal_handler) {
+ dev_err(cntl->codec->dev,
+ "%s: Invalid signal_handler callback\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case WCD_CDC_DOWN_EVENT:
+ ret = cntl->m_ops->signal_handler(cntl->m_dev,
+ WDSP_CDC_DOWN_SIGNAL,
+ NULL);
+ if (IS_ERR_VALUE(ret))
+ dev_err(cntl->codec->dev,
+ "%s: WDSP_CDC_DOWN_SIGNAL failed, err = %d\n",
+ __func__, ret);
+ wcd_cntl_change_online_state(cntl, 0);
+ break;
+ case WCD_CDC_UP_EVENT:
+ ret = cntl->m_ops->signal_handler(cntl->m_dev,
+ WDSP_CDC_UP_SIGNAL,
+ NULL);
+ if (IS_ERR_VALUE(ret))
+ dev_err(cntl->codec->dev,
+ "%s: WDSP_CDC_UP_SIGNAL failed, err = %d\n",
+ __func__, ret);
+ break;
+ default:
+ dev_err(cntl->codec->dev, "%s: Invalid event %d\n",
+ __func__, event);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(wcd_dsp_ssr_event);
+
+/*
* wcd_dsp_cntl_init: Initialize the wcd-dsp control
* @codec: pointer to the codec handle
* @params: Parameters required to initialize wcd-dsp control
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h
index cd6697b3d641..83c59ed7b676 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h
@@ -17,6 +17,11 @@
#include <sound/soc.h>
#include <sound/wcd-dsp-mgr.h>
+enum cdc_ssr_event {
+ WCD_CDC_DOWN_EVENT,
+ WCD_CDC_UP_EVENT,
+};
+
struct wcd_dsp_cdc_cb {
/* Callback to enable codec clock */
int (*cdc_clk_en)(struct snd_soc_codec *, bool);
@@ -106,5 +111,5 @@ void wcd_dsp_cntl_init(struct snd_soc_codec *codec,
struct wcd_dsp_params *params,
struct wcd_dsp_cntl **cntl);
void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl);
-
+int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event);
#endif /* end __WCD_DSP_CONTROL_H__ */
diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c
index 9e18c17d6f1c..30c23df444d6 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.c
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -358,6 +358,15 @@ enum {
ASRC_MAX,
};
+enum {
+ CONV_88P2K_TO_384K,
+ CONV_96K_TO_352P8K,
+ CONV_352P8K_TO_384K,
+ CONV_384K_TO_352P8K,
+ CONV_384K_TO_384K,
+ CONV_96K_TO_384K,
+};
+
static struct afe_param_slimbus_slave_port_cfg tavil_slimbus_slave_port_cfg = {
.minor_version = 1,
.slimbus_dev_id = AFE_SLIMBUS_DEVICE_1,
@@ -577,7 +586,7 @@ struct tavil_priv {
/* num of slim ports required */
struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
/* Port values for Rx and Tx codec_dai */
- unsigned int rx_port_value;
+ unsigned int rx_port_value[WCD934X_RX_MAX];
unsigned int tx_port_value;
struct wcd9xxx_resmgr_v2 *resmgr;
@@ -616,6 +625,7 @@ struct tavil_priv {
int native_clk_users;
/* ASRC users count */
int asrc_users[ASRC_MAX];
+ int asrc_output_mode[ASRC_MAX];
/* Main path clock users count */
int main_clk_users[WCD934X_NUM_INTERPOLATORS];
struct tavil_dsd_config *dsd_config;
@@ -1298,7 +1308,8 @@ static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = tavil_p->rx_port_value;
+ ucontrol->value.enumerated.item[0] =
+ tavil_p->rx_port_value[widget->shift];
return 0;
}
@@ -1313,17 +1324,20 @@ static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct snd_soc_dapm_update *update = NULL;
+ unsigned int rx_port_value;
u32 port_id = widget->shift;
+ tavil_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0];
+ rx_port_value = tavil_p->rx_port_value[port_id];
+
mutex_lock(&tavil_p->codec_mutex);
- tavil_p->rx_port_value = ucontrol->value.enumerated.item[0];
dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n",
__func__, widget->name, ucontrol->id.name,
- tavil_p->rx_port_value, widget->shift,
+ rx_port_value, widget->shift,
ucontrol->value.integer.value[0]);
/* value need to match the Virtual port and AIF number */
- switch (tavil_p->rx_port_value) {
+ switch (rx_port_value) {
case 0:
list_del_init(&core->rx_chs[port_id].list);
break;
@@ -1372,13 +1386,13 @@ static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
&tavil_p->dai[AIF4_PB].wcd9xxx_ch_list);
break;
default:
- dev_err(codec->dev, "Unknown AIF %d\n", tavil_p->rx_port_value);
+ dev_err(codec->dev, "Unknown AIF %d\n", rx_port_value);
goto err;
}
rtn:
mutex_unlock(&tavil_p->codec_mutex);
snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
- tavil_p->rx_port_value, e, update);
+ rx_port_value, e, update);
return 0;
err:
@@ -1859,8 +1873,9 @@ 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));
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, (0x03 << 1));
set_bit(HPH_PA_DELAY, &tavil->status_mask);
if (dsd_conf &&
(snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) {
@@ -1922,8 +1937,9 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
blocking_notifier_call_chain(&tavil->mbhc->notifier,
WCD_EVENT_POST_HPHR_PA_OFF,
&tavil->mbhc->wcd_mbhc);
- snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
- 0x06, 0x0);
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, 0x0);
break;
};
@@ -1942,8 +1958,9 @@ 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));
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, (0x03 << 1));
set_bit(HPH_PA_DELAY, &tavil->status_mask);
if (dsd_conf &&
(snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) {
@@ -2004,8 +2021,9 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
blocking_notifier_call_chain(&tavil->mbhc->notifier,
WCD_EVENT_POST_HPHL_PA_OFF,
&tavil->mbhc->wcd_mbhc);
- snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
- 0x06, 0x0);
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, 0x0);
break;
};
@@ -2130,9 +2148,10 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
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 (TAVIL_IS_1_0(tavil->wcd9xxx))
+ 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))
hph_mode = CLS_H_HIFI;
@@ -2155,8 +2174,10 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
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);
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec,
+ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x0);
break;
default:
break;
@@ -2199,8 +2220,10 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
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 (TAVIL_IS_1_0(tavil->wcd9xxx))
+ 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;
@@ -2223,8 +2246,10 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
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);
+ if (TAVIL_IS_1_0(tavil->wcd9xxx))
+ snd_soc_update_bits(codec,
+ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x0);
break;
default:
break;
@@ -2581,6 +2606,45 @@ done:
return rc;
}
+static int tavil_get_asrc_mode(struct tavil_priv *tavil, int asrc,
+ u8 main_sr, u8 mix_sr)
+{
+ u8 asrc_output_mode;
+ int asrc_mode = CONV_88P2K_TO_384K;
+
+ if ((asrc < 0) || (asrc >= ASRC_MAX))
+ return 0;
+
+ asrc_output_mode = tavil->asrc_output_mode[asrc];
+
+ if (asrc_output_mode) {
+ /*
+ * If Mix sample rate is < 96KHz, use 96K to 352.8K
+ * conversion, or else use 384K to 352.8K conversion
+ */
+ if (mix_sr < 5)
+ asrc_mode = CONV_96K_TO_352P8K;
+ else
+ asrc_mode = CONV_384K_TO_352P8K;
+ } else {
+ /* Integer main and Fractional mix path */
+ if (main_sr < 8 && mix_sr > 9) {
+ asrc_mode = CONV_352P8K_TO_384K;
+ } else if (main_sr > 8 && mix_sr < 8) {
+ /* Fractional main and Integer mix path */
+ if (mix_sr < 5)
+ asrc_mode = CONV_96K_TO_352P8K;
+ else
+ asrc_mode = CONV_384K_TO_352P8K;
+ } else if (main_sr < 8 && mix_sr < 8) {
+ /* Integer main and Integer mix path */
+ asrc_mode = CONV_96K_TO_384K;
+ }
+ }
+
+ return asrc_mode;
+}
+
static int tavil_codec_enable_asrc(struct snd_soc_codec *codec,
int asrc_in, int event)
{
@@ -2647,19 +2711,8 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec,
main_sr = snd_soc_read(codec, ctl_reg) & 0x0F;
mix_ctl_reg = ctl_reg + 5;
mix_sr = snd_soc_read(codec, mix_ctl_reg) & 0x0F;
- /* Integer main and Fractional mix path */
- if (main_sr < 8 && mix_sr > 9) {
- asrc_mode = 2;
- } else if (main_sr > 8 && mix_sr < 8) {
- /* Fractional main and Integer mix path */
- if (mix_sr < 5)
- asrc_mode = 1;
- else
- asrc_mode = 3;
- } else if (main_sr < 8 && mix_sr < 8) {
- /* Integer main and Integer mix path */
- asrc_mode = 5;
- }
+ asrc_mode = tavil_get_asrc_mode(tavil, asrc,
+ main_sr, mix_sr);
dev_dbg(codec->dev, "%s: main_sr:%d mix_sr:%d asrc_mode %d\n",
__func__, main_sr, mix_sr, asrc_mode);
snd_soc_update_bits(codec, asrc_ctl, 0x07, asrc_mode);
@@ -2820,11 +2873,15 @@ static void tavil_codec_hphdelay_lutbypass(struct snd_soc_codec *codec,
}
}
-static void tavil_codec_hd2_control(struct snd_soc_codec *codec,
+static void tavil_codec_hd2_control(struct tavil_priv *priv,
u16 interp_idx, int event)
{
u16 hd2_scale_reg;
u16 hd2_enable_reg = 0;
+ struct snd_soc_codec *codec = priv->codec;
+
+ if (TAVIL_IS_1_1(priv->wcd9xxx))
+ return;
switch (interp_idx) {
case INTERP_HPHL:
@@ -3002,7 +3059,7 @@ int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
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_hd2_control(tavil, interp_idx, event);
tavil_codec_hphdelay_lutbypass(codec, interp_idx,
event);
tavil_config_compander(codec, interp_idx, event);
@@ -3017,7 +3074,7 @@ int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
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_hd2_control(tavil, interp_idx, event);
tavil_codec_idle_detect_control(codec, interp_idx,
event);
/* Clk Disable */
@@ -4737,6 +4794,46 @@ static int tavil_compander_put(struct snd_kcontrol *kcontrol,
return 0;
}
+static int tavil_hph_asrc_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);
+ int index = -EINVAL;
+
+ if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode"))
+ index = ASRC0;
+ if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode"))
+ index = ASRC1;
+
+ if (tavil && (index >= 0) && (index < ASRC_MAX))
+ tavil->asrc_output_mode[index] =
+ ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int tavil_hph_asrc_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);
+ int val = 0;
+ int index = -EINVAL;
+
+ if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode"))
+ index = ASRC0;
+ if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode"))
+ index = ASRC1;
+
+ if (tavil && (index >= 0) && (index < ASRC_MAX))
+ val = tavil->asrc_output_mode[index];
+
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+
static int tavil_hph_idle_detect_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -5131,6 +5228,10 @@ static const char * const hph_idle_detect_text[] = {
"OFF", "ON"
};
+static const char * const asrc_mode_text[] = {
+ "INT", "FRAC"
+};
+
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"
@@ -5146,6 +5247,7 @@ 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_EXT_DECL(asrc_mode_enum, asrc_mode_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,
@@ -5380,6 +5482,11 @@ 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("ASRC0 Output Mode", asrc_mode_enum,
+ tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put),
+ SOC_ENUM_EXT("ASRC1 Output Mode", asrc_mode_enum,
+ tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put),
+
SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum,
tavil_hph_idle_detect_get, tavil_hph_idle_detect_put),
@@ -7837,7 +7944,11 @@ static const struct wcd_resmgr_cb tavil_resmgr_cb = {
.cdc_rco_ctrl = __tavil_codec_internal_rco_ctrl,
};
-static const struct tavil_reg_mask_val tavil_codec_mclk2_defaults[] = {
+static const struct tavil_reg_mask_val tavil_codec_mclk2_1_1_defaults[] = {
+ {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20},
+};
+
+static const struct tavil_reg_mask_val tavil_codec_mclk2_1_0_defaults[] = {
/*
* PLL Settings:
* Clock Root: MCLK2,
@@ -7896,6 +8007,13 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = {
{WCD934X_HPH_R_TEST, 0x01, 0x01},
};
+static const struct tavil_reg_mask_val tavil_codec_reg_init_1_1_val[] = {
+ {WCD934X_CDC_COMPANDER1_CTL7, 0x1E, 0x06},
+ {WCD934X_CDC_COMPANDER2_CTL7, 0x1E, 0x06},
+ {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xFF, 0x84},
+ {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xFF, 0x84},
+};
+
static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {
{WCD934X_CDC_CLSH_K2_MSB, 0x0F, 0x00},
{WCD934X_CDC_CLSH_K2_LSB, 0xFF, 0x60},
@@ -7922,8 +8040,9 @@ static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {
{WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00},
};
-static void tavil_codec_init_reg(struct snd_soc_codec *codec)
+static void tavil_codec_init_reg(struct tavil_priv *priv)
{
+ struct snd_soc_codec *codec = priv->codec;
u32 i;
for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_common_val); i++)
@@ -7931,6 +8050,14 @@ static void tavil_codec_init_reg(struct snd_soc_codec *codec)
tavil_codec_reg_init_common_val[i].reg,
tavil_codec_reg_init_common_val[i].mask,
tavil_codec_reg_init_common_val[i].val);
+
+ if (TAVIL_IS_1_1(priv->wcd9xxx)) {
+ for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_1_1_val); i++)
+ snd_soc_update_bits(codec,
+ tavil_codec_reg_init_1_1_val[i].reg,
+ tavil_codec_reg_init_1_1_val[i].mask,
+ tavil_codec_reg_init_1_1_val[i].val);
+ }
}
static void tavil_update_reg_defaults(struct tavil_priv *tavil)
@@ -8367,11 +8494,22 @@ static void tavil_mclk2_reg_defaults(struct tavil_priv *tavil)
int i;
struct snd_soc_codec *codec = tavil->codec;
- /* MCLK2 configuration */
- for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_defaults); i++)
- snd_soc_update_bits(codec, tavil_codec_mclk2_defaults[i].reg,
- tavil_codec_mclk2_defaults[i].mask,
- tavil_codec_mclk2_defaults[i].val);
+ if (TAVIL_IS_1_0(tavil->wcd9xxx)) {
+ /* MCLK2 configuration */
+ for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_0_defaults); i++)
+ snd_soc_update_bits(codec,
+ tavil_codec_mclk2_1_0_defaults[i].reg,
+ tavil_codec_mclk2_1_0_defaults[i].mask,
+ tavil_codec_mclk2_1_0_defaults[i].val);
+ }
+ if (TAVIL_IS_1_1(tavil->wcd9xxx)) {
+ /* MCLK2 configuration */
+ for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_1_defaults); i++)
+ snd_soc_update_bits(codec,
+ tavil_codec_mclk2_1_1_defaults[i].reg,
+ tavil_codec_mclk2_1_1_defaults[i].mask,
+ tavil_codec_mclk2_1_1_defaults[i].val);
+ }
}
static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
@@ -8429,7 +8567,7 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
for (i = 0; i < COMPANDER_MAX; i++)
tavil->comp_enabled[i] = 0;
- tavil_codec_init_reg(codec);
+ tavil_codec_init_reg(tavil);
tavil_enable_sido_buck(codec);
pdata = dev_get_platdata(codec->dev->parent);
@@ -8749,6 +8887,9 @@ static int tavil_swrm_clock(void *handle, bool enable)
if (enable) {
tavil->swr.clk_users++;
if (tavil->swr.clk_users == 1) {
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_TEST_DEBUG_NPL_DLY_TEST_1,
+ 0x10, 0x00);
__tavil_cdc_mclk_enable(tavil, true);
regmap_update_bits(tavil->wcd9xxx->regmap,
WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL,
@@ -8761,6 +8902,9 @@ static int tavil_swrm_clock(void *handle, bool enable)
WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL,
0x01, 0x00);
__tavil_cdc_mclk_enable(tavil, false);
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_TEST_DEBUG_NPL_DLY_TEST_1,
+ 0x10, 0x10);
}
}
dev_dbg(tavil->dev, "%s: swrm clock users %d\n",
diff --git a/sound/soc/codecs/wcd9xxx-common-v2.c b/sound/soc/codecs/wcd9xxx-common-v2.c
index 63872bbf540c..47518ec92661 100644
--- a/sound/soc/codecs/wcd9xxx-common-v2.c
+++ b/sound/soc/codecs/wcd9xxx-common-v2.c
@@ -369,8 +369,9 @@ static inline void wcd_clsh_gm3_boost_disable(struct snd_soc_codec *codec,
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 */
+ if (TAVIL_IS_1_0(wcd9xxx))
+ 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 {
diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.c b/sound/soc/codecs/wcd9xxx-resmgr-v2.c
index 29718a8d7c04..39ca965e791e 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr-v2.c
+++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.c
@@ -307,7 +307,7 @@ static int wcd_resmgr_disable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr)
WCD9335_ANA_CLK_TOP,
0x04, 0x00);
wcd_resmgr_codec_reg_update_bits(resmgr,
- WCD934X_CLK_SYS_MCLK_PRG, 0x01, 0x0);
+ WCD934X_CLK_SYS_MCLK_PRG, 0x81, 0x00);
resmgr->clk_type = WCD_CLK_OFF;
}
diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c
index a2bd3be62175..524f737360d8 100644
--- a/sound/soc/msm/msmcobalt.c
+++ b/sound/soc/msm/msmcobalt.c
@@ -3180,8 +3180,10 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
* Send speaker configuration only for WSA8810.
* Defalut configuration is for WSA8815.
*/
+ pr_debug("%s: Number of aux devices: %d\n",
+ __func__, rtd->card->num_aux_devs);
if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
- if (rtd_aux && rtd_aux->component)
+ if (rtd->card->num_aux_devs && rtd_aux && rtd_aux->component)
if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
!strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
tavil_set_spkr_mode(rtd->codec, SPKR_MODE_1);
@@ -3200,7 +3202,7 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
pdata->codec_root = entry;
tavil_codec_info_create_codec_entry(pdata->codec_root, codec);
} else {
- if (rtd_aux && rtd_aux->component)
+ if (rtd->card->num_aux_devs && rtd_aux && rtd_aux->component)
if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
!strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1);