diff options
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); |
