diff options
170 files changed, 6758 insertions, 1762 deletions
diff --git a/Documentation/devicetree/bindings/batterydata/batterydata.txt b/Documentation/devicetree/bindings/batterydata/batterydata.txt new file mode 100644 index 000000000000..39f9375a6c48 --- /dev/null +++ b/Documentation/devicetree/bindings/batterydata/batterydata.txt @@ -0,0 +1,221 @@ +Battery Profile Data + +Battery Data is a collection of battery profile data made available to +the QPNP Charger and BMS drivers via device tree. + +qcom,battery-data node required properties: +- qcom,rpull-up-kohm : The vadc pullup resistor's resistance value in kOhms. +- qcom,vref-batt-therm-uv : The vadc voltage used to make readings. + For Qualcomm Technologies, Inc. VADCs, this should be + 1800000uV. + +qcom,battery-data node optional properties: +- qcom,batt-id-range-pct : The area of variation between upper and lower bound + for which a given battery ID resistance is valid. This + value is expressed as a percentage of the specified kohm + resistance provided by qcom,batt-id-kohm. + +qcom,battery-data can also include any number of children nodes. These children +nodes will be treated as battery profile data nodes. + +Profile data node required properties: +- qcom,fcc-mah : Full charge count of the battery in milliamp-hours +- qcom,default-rbatt-mohm : The nominal battery resistance value +- qcom,rbatt-capacitive-mohm : The capacitive resistance of the battery. +- qcom,flat-ocv-threshold-uv : The threshold under which the battery can be + considered to be in the flat portion of the discharge + curve. +- qcom,max-voltage-uv : The maximum rated voltage of the battery +- qcom,v-cutoff-uv : The cutoff voltage of the battery at which the device + should shutdown gracefully. +- qcom,chg-term-ua : The termination charging current of the battery. +- qcom,batt-id-kohm : The battery id resistance of the battery. It can be + used as an array which could support multiple IDs for one battery + module when the ID resistance of some battery modules goes across + several ranges. +- qcom,battery-type : A string indicating the type of battery. +- qcom,fg-profile-data : An array of hexadecimal values used to configure more + complex fuel gauge peripherals which have a large amount + of coefficients used in hardware state machines and thus + influencing the final output of the state of charge read + by software. + +Profile data node optional properties: +- qcom,chg-rslow-comp-c1 : A constant for rslow compensation in the fuel gauge. + This will be provided by the profiling tool for + additional fuel gauge accuracy during charging. +- qcom,chg-rslow-comp-c2 : A constant for rslow compensation in the fuel gauge. + This will be provided by the profiling tool for + additional fuel gauge accuracy during charging. +- qcom,chg-rslow-comp-thr : A constant for rslow compensation in the fuel gauge. + This will be provided by the profiling tool for + additional fuel gauge accuracy during charging. +- qcom,chg-rs-to-rslow: A constant for rslow compensation in the fuel gauge. + This will be provided by the profiling tool for + additional fuel gauge accuracy during charging. +- qcom,fastchg-current-ma: Specifies the maximum fastcharge current. +- qcom,fg-cc-cv-threshold-mv: Voltage threshold in mV for transition from constant + charge (CC) to constant voltage (CV). This value should + be 10 mV less than the float voltage. + This property should only be specified if + "qcom,autoadjust-vfloat" property is specified in the + charger driver to ensure a proper operation. +- qcom,thermal-coefficients: Byte array of thermal coefficients for reading + battery thermistor. This should be exactly 6 bytes + in length. + Example: [01 02 03 04 05 06] + +Profile data node required subnodes: +- qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes + temperature to fcc lookup. The units for this lookup + table should be degrees celsius to milliamp-hours. +- qcom,pc-temp-ocv-lut : A 2-dimensional lookup table node that encodes + temperature and percent charge to open circuit voltage + lookup. The units for this lookup table should be + degrees celsius and percent to millivolts. +- qcom,rbatt-sf-lut : A 2-dimentional lookup table node that encodes + temperature and percent charge to battery internal + resistance lookup. The units for this lookup table + should be degrees celsius and percent to milliohms. + +Profile data node optional subnodes: +- qcom,ibat-acc-luit: A 2-dimentional lookup table that encodes temperature + and battery current to battery ACC (apparent charge + capacity). The units for this lookup table should be + temperature in degrees celsius, ibat in milli-amps + and ACC in milli-ampere-hour. + +Lookup table required properties: +- qcom,lut-col-legend : An array that encodes the legend of the lookup table's + columns. The length of this array will determine the + lookup table's width. +- qcom,lut-data : An array that encodes the lookup table's data. The size of this + array should be equal to the size of qcom,lut-col-legend + multiplied by 1 if it's a 1-dimensional table, or + the size of qcom,lut-row-legend if it's a 2-dimensional + table. The data should be in a flattened row-major + representation. + +Lookup table optional properties: +- qcom,lut-row-legend : An array that encodes the legend of the lookup table's rows. + If this property exists, then it is assumed that the + lookup table is a 2-dimensional table. + +Example: + +In msm8974-mtp.dtsi: + +mtp_batterydata: qcom,battery-data { + qcom,rpull-up-kohm = <100>; + qcom,vref-batt-therm-uv = <1800000>; + + /include/ "batterydata-palladium.dtsi" + /include/ "batterydata-mtp-3000mah.dtsi" +}; + +&pm8941_bms { + qcom,battery-data = <&mtp_batterydata>; +}; + +In batterydata-palladium.dtsi: + +qcom,palladium-batterydata { + qcom,fcc-mah = <1500>; + qcom,default-rbatt-mohm = <236>; + qcom,rbatt-capacitive-mohm = <50>; + qcom,flat-ocv-threshold-uv = <3800000>; + qcom,max-voltage-uv = <4200000>; + qcom,v-cutoff-uv = <3400000>; + qcom,chg-term-ua = <100000>; + qcom,batt-id-kohm = <75>; + qcom,battery-type = "palladium_1500mah"; + + qcom,fcc-temp-lut { + qcom,lut-col-legend = <(-20) 0 25 40 65>; + qcom,lut-data = <1492 1492 1493 1483 1502>; + }; + + qcom,pc-temp-ocv-lut { + qcom,lut-col-legend = <(-20) 0 25 40 65>; + qcom,lut-row-legend = <100 95 90 85 80 75 70>, + <65 60 55 50 45 40 35>, + <30 25 20 15 10 9 8>, + <7 6 5 4 3 2 1 0>; + qcom,lut-data = <4173 4167 4163 4156 4154>, + <4104 4107 4108 4102 4104>, + <4057 4072 4069 4061 4060>, + <3973 4009 4019 4016 4020>, + <3932 3959 3981 3982 3983>, + <3899 3928 3954 3950 3950>, + <3868 3895 3925 3921 3920>, + <3837 3866 3898 3894 3892>, + <3812 3841 3853 3856 3862>, + <3794 3818 3825 3823 3822>, + <3780 3799 3804 3804 3803>, + <3768 3787 3790 3788 3788>, + <3757 3779 3778 3775 3776>, + <3747 3772 3771 3766 3765>, + <3736 3763 3766 3760 3746>, + <3725 3749 3756 3747 3729>, + <3714 3718 3734 3724 3706>, + <3701 3703 3696 3689 3668>, + <3675 3695 3682 3675 3662>, + <3670 3691 3680 3673 3661>, + <3661 3686 3679 3672 3656>, + <3649 3680 3676 3669 3641>, + <3633 3669 3667 3655 3606>, + <3610 3647 3640 3620 3560>, + <3580 3607 3596 3572 3501>, + <3533 3548 3537 3512 3425>, + <3457 3468 3459 3429 3324>, + <3328 3348 3340 3297 3172>, + <3000 3000 3000 3000 3000>; + }; + + qcom,rbatt-sf-lut { + qcom,lut-col-legend = <(-20) 0 25 40 65>; + qcom,lut-row-legend = <100 95 90 85 80 75 70>, + <65 60 55 50 45 40 35>, + <30 25 20 15 10 9 8>, + <7 6 5 4 3 2 1 0>; + qcom,lut-data = <357 187 100 91 91>, + <400 208 105 94 94>, + <390 204 106 95 96>, + <391 201 108 98 98>, + <391 202 110 98 100>, + <390 200 110 99 102>, + <389 200 110 99 102>, + <393 202 101 93 100>, + <407 205 99 89 94>, + <428 208 100 91 96>, + <455 212 102 92 98>, + <495 220 104 93 101>, + <561 232 107 95 102>, + <634 245 112 98 98>, + <714 258 114 98 98>, + <791 266 114 97 100>, + <871 289 108 95 97>, + <973 340 124 108 105>, + <489 241 109 96 99>, + <511 246 110 96 99>, + <534 252 111 95 98>, + <579 263 112 96 96>, + <636 276 111 95 97>, + <730 294 109 96 99>, + <868 328 112 98 104>, + <1089 374 119 101 115>, + <1559 457 128 105 213>, + <12886 1026 637 422 3269>, + <170899 127211 98968 88907 77102>; + }; + + qcom,ibat-acc-lut { + qcom,lut-col-legend = <(-20) 0 25>; + qcom,lut-row-legend = <0 250 500 1000>; + qcom,lut-data = <1470 1470 1473>, + <1406 1406 1430>, + <1247 1247 1414>, + <764 764 1338>; + }; +}; + diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt index 87d3714b956a..f825a44e5911 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt @@ -12,6 +12,8 @@ Required properties : "qcom,rpmcc-msm8916", "qcom,rpmcc" "qcom,rpmcc-apq8064", "qcom,rpmcc" + "qcom,rpmcc-msm8996", "qcom,rpmcc" + "qcom,rpmcc-msmfalcon", "qcom,rpmcc" - #clock-cells : shall contain 1 @@ -35,3 +37,10 @@ Example: }; }; }; + + The below are applicable for MSM8996 & MSMFalcon. + + rpmcc: clock-controller { + compatible = "qcom,rpmcc-msm8996", "qcom,rpmcc"; + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index fffb8cc39d0f..ca58f0da07ef 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -139,6 +139,10 @@ Optional Properties: baseAddr - base address of the gpu channels in the qdss stm memory region size - size of the gpu stm region +- qcom,tsens-name: + Specify the name of GPU temperature sensor. This name will be used + to get the temperature from the thermal driver API. + GPU Quirks: - qcom,gpu-quirk-two-pass-use-wfi: Signal the GPU to set Set TWOPASSUSEWFI bit in 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 199b5c0857f8..bd358593fcb3 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt @@ -140,6 +140,11 @@ First Level Node - FG Gen3 device asleep and the battery is discharging. This option requires qcom,fg-esr-timer-awake to be defined. +- qcom,cycle-counter-en + Usage: optional + Value type: <bool> + Definition: Enables the cycle counter feature. + ========================================================== Second Level Nodes - Peripherals managed by FG Gen3 driver ========================================================== diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt index 1c5dd91891dc..21404dfc4b7b 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt @@ -21,6 +21,12 @@ Charger specific properties: Value type: <string> Definition: "qcom,qpnp-smb2". +- qcom,pmic-revid + Usage: required + Value type: phandle + Definition: Should specify the phandle of PMI's revid module. This is used to + identify the PMI subtype. + - qcom,batteryless-platform Usage: optional Value type: <empty> diff --git a/Documentation/devicetree/bindings/scheduler/sched_hmp.txt b/Documentation/devicetree/bindings/scheduler/sched_hmp.txt new file mode 100644 index 000000000000..ba1d4db9e407 --- /dev/null +++ b/Documentation/devicetree/bindings/scheduler/sched_hmp.txt @@ -0,0 +1,35 @@ +* HMP scheduler + +This file describes the bindings for an optional HMP scheduler +node (/sched-hmp). + +Required properties: + +Optional properties: + +- boost-policy: The HMP scheduler has two types of task placement boost +policies. + +(1) boost-on-big policy make use of all big CPUs up to their full capacity +before using the little CPUs. This improves performance on true b.L systems +where the big CPUs have higher efficiency compared to the little CPUs. + +(2) boost-on-all policy place the tasks on the CPU having the highest +spare capacity. This policy is optimal for SMP like systems. + +The scheduler sets the boost policy to boost-on-big on systems which has +CPUs of different efficiencies. However it is possible that CPUs of the +same micro architecture to have slight difference in efficiency due to +other factors like cache size. Selecting the boost-on-big policy based +on relative difference in efficiency is not optimal on such systems. +The boost-policy device tree property is introduced to specify the +required boost type and it overrides the default selection of boost +type in the scheduler. + +The possible values for this property are "boost-on-big" and "boost-on-all". + +Example: + +sched-hmp { + boost-policy = "boost-on-all" +} diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt index 684bea131405..7189edbf8c5c 100644 --- a/Documentation/devicetree/bindings/thermal/tsens.txt +++ b/Documentation/devicetree/bindings/thermal/tsens.txt @@ -32,6 +32,7 @@ Required properties: should be "qcom,msmcobalt-tsens" for cobalt TSENS driver. should be "qcom,msmhamster-tsens" for hamster TSENS driver. should be "qcom,msmfalcon-tsens" for falcon TSENS driver. + should be "qcom,msmtriton-tsens" for triton TSENS driver. The compatible property is used to identify the respective fusemap to use for the corresponding SoC. - reg : offset and length of the TSENS registers with associated property in reg-names diff --git a/Documentation/scheduler/sched-hmp.txt b/Documentation/scheduler/sched-hmp.txt index b400e053e55d..298064bc44d7 100644 --- a/Documentation/scheduler/sched-hmp.txt +++ b/Documentation/scheduler/sched-hmp.txt @@ -43,6 +43,7 @@ CONTENTS 8.8 sched_get_busy 8.9 sched_freq_alert 8.10 sched_set_boost +9. Device Tree bindings =============== 1. INTRODUCTION @@ -1447,3 +1448,10 @@ Logged when boost settings are being changed <task>-0 [004] d.h4 12700.711489: sched_set_boost: ref_count=1 - ref_count: A non-zero value indicates boost is in effect + +======================== +9. Device Tree bindings +======================== + +The device tree bindings for the HMP scheduler are defined in +Documentation/devicetree/bindings/sched/sched_hmp.txt diff --git a/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi new file mode 100644 index 000000000000..6c17bca64a86 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi @@ -0,0 +1,80 @@ +/* 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. + */ + +&mdss_mdp { + dsi_dual_jdi_a407_cmd: qcom,mdss_dsi_jdi_a407_wqhd_cmd { + qcom,mdss-dsi-panel-name = "JDI a407 wqhd cmd mode dsi panel"; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <720>; + qcom,mdss-dsi-panel-height = <2560>; + qcom,mdss-dsi-h-front-porch = <16>; + qcom,mdss-dsi-h-back-porch = <40>; + qcom,mdss-dsi-h-pulse-width = <4>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <20>; + qcom,mdss-dsi-v-front-porch = <7>; + qcom,mdss-dsi-v-pulse-width = <1>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-on-command = [ + 15 01 00 00 00 00 02 35 00 + 05 01 00 00 78 00 02 11 00 + 05 01 00 00 32 00 02 29 00]; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 32 00 02 28 00 + 05 01 00 00 78 00 02 10 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line = <0x2c>; + qcom,mdss-dsi-te-dcs-command = <1>; + + qcom,mdss-dsi-lp11-init; + qcom,adjust-timer-wakeup-ms = <1>; + qcom,mdss-dsi-reset-sequence = <1 20>, <0 10>, <1 20>; + + qcom,config-select = <&dsi_dual_jdi_a407_cmd_config0>; + + dsi_dual_jdi_a407_cmd_config0: config0 { + qcom,split-mode = "dualctl-split"; + }; + + dsi_dual_jdi_a407_cmd_config1: config1 { + qcom,split-mode = "pingpong-split"; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index ec3e9e89b0ae..8a8782f5f8b3 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -321,6 +321,7 @@ io-channel-names = "rradc_batt_id"; qcom,fg-esr-timer-awake = <64>; qcom,fg-esr-timer-asleep = <256>; + qcom,cycle-counter-en; status = "okay"; qcom,fg-batt-soc@4000 { diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index dc1bbcd13c36..912bdd88be68 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -1355,7 +1355,7 @@ gdsc-vdd-supply = <&gdsc_pcie_0>; vreg-1.8-supply = <&pm8994_l12>; vreg-0.9-supply = <&pm8994_l28>; - vreg-cx-supply = <&pm8994_s1_corner_ao>; + vreg-cx-supply = <&pm8994_s1_corner>; qcom,vreg-0.9-voltage-level = <925000 925000 24000>; qcom,vreg-cx-voltage-level = <7 4 0>; @@ -1510,7 +1510,7 @@ gdsc-vdd-supply = <&gdsc_pcie_1>; vreg-1.8-supply = <&pm8994_l12>; vreg-0.9-supply = <&pm8994_l28>; - vreg-cx-supply = <&pm8994_s1_corner_ao>; + vreg-cx-supply = <&pm8994_s1_corner>; qcom,vreg-0.9-voltage-level = <925000 925000 24000>; qcom,vreg-cx-voltage-level = <7 5 0>; @@ -1663,10 +1663,10 @@ gdsc-vdd-supply = <&gdsc_pcie_2>; vreg-1.8-supply = <&pm8994_l12>; vreg-0.9-supply = <&pm8994_l28>; - vreg-cx-supply = <&pm8994_s1_corner_ao>; + vreg-cx-supply = <&pm8994_s1_corner>; qcom,vreg-0.9-voltage-level = <925000 925000 24000>; - qcom,vreg-cx-voltage-level = <7 4 0>; + qcom,vreg-cx-voltage-level = <7 5 0>; qcom,l1-supported; qcom,l1ss-supported; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi index 445f32df8aa4..a1d80075abe0 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi @@ -157,6 +157,7 @@ qcom,audio-routing = "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", "AMIC2", "MIC BIAS2", "MIC BIAS2", "Headset Mic", "AMIC3", "MIC BIAS2", diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi index ed29dd9e1508..ed8eb8459e51 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi @@ -87,7 +87,7 @@ cam_vdig-supply = <&pmcobalt_s3>; qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; qcom,cam-vreg-min-voltage = <0 3312000 1352000>; - qcom,cam-vreg-max-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; qcom,cam-vreg-op-mode = <0 80000 105000>; qcom,gpio-no-mux = <0>; pinctrl-names = "cam_default", "cam_suspend"; @@ -132,7 +132,7 @@ cam_vana-supply = <&pmicobalt_bob>; qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; qcom,cam-vreg-min-voltage = <0 0 3312000>; - qcom,cam-vreg-max-voltage = <0 0 3312000>; + qcom,cam-vreg-max-voltage = <0 0 3600000>; qcom,cam-vreg-op-mode = <0 0 80000>; qcom,gpio-no-mux = <0>; pinctrl-names = "cam_default", "cam_suspend"; @@ -215,7 +215,7 @@ cam_vdig-supply = <&pmcobalt_s3>; qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; qcom,cam-vreg-min-voltage = <0 3312000 1352000>; - qcom,cam-vreg-max-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; qcom,cam-vreg-op-mode = <0 80000 105000>; qcom,gpio-no-mux = <0>; pinctrl-names = "cam_default", "cam_suspend"; @@ -259,7 +259,7 @@ cam_vana-supply = <&pmicobalt_bob>; qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; qcom,cam-vreg-min-voltage = <0 0 3312000>; - qcom,cam-vreg-max-voltage = <0 0 3312000>; + qcom,cam-vreg-max-voltage = <0 0 3600000>; qcom,cam-vreg-op-mode = <0 0 80000>; qcom,gpio-no-mux = <0>; pinctrl-names = "cam_default", "cam_suspend"; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi index 485bc560eef5..2be67ab52ba7 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi @@ -87,7 +87,7 @@ cam_vdig-supply = <&pmcobalt_s3>; qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; qcom,cam-vreg-min-voltage = <0 3312000 1352000>; - qcom,cam-vreg-max-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; qcom,cam-vreg-op-mode = <0 80000 105000>; qcom,gpio-no-mux = <0>; pinctrl-names = "cam_default", "cam_suspend"; @@ -132,7 +132,7 @@ cam_vana-supply = <&pmicobalt_bob>; qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; qcom,cam-vreg-min-voltage = <0 0 3312000>; - qcom,cam-vreg-max-voltage = <0 0 3312000>; + qcom,cam-vreg-max-voltage = <0 0 3600000>; qcom,cam-vreg-op-mode = <0 0 80000>; qcom,gpio-no-mux = <0>; pinctrl-names = "cam_default", "cam_suspend"; @@ -215,7 +215,7 @@ cam_vdig-supply = <&pmcobalt_s3>; qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; qcom,cam-vreg-min-voltage = <0 3312000 1352000>; - qcom,cam-vreg-max-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; qcom,cam-vreg-op-mode = <0 80000 105000>; qcom,gpio-no-mux = <0>; pinctrl-names = "cam_default", "cam_suspend"; @@ -259,7 +259,7 @@ cam_vana-supply = <&pmicobalt_bob>; qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; qcom,cam-vreg-min-voltage = <0 0 3312000>; - qcom,cam-vreg-max-voltage = <0 0 3312000>; + qcom,cam-vreg-max-voltage = <0 0 3600000>; qcom,cam-vreg-op-mode = <0 0 80000>; qcom,gpio-no-mux = <0>; pinctrl-names = "cam_default", "cam_suspend"; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi index 467b2cbfcd7d..a37fa26b1055 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi @@ -147,21 +147,20 @@ <&clock_mmss clk_mmss_camss_top_ahb_clk>, <&clock_mmss clk_mmss_camss_ispif_ahb_clk>, <&clock_mmss clk_csi0_clk_src>, + <&clock_mmss clk_csiphy_clk_src>, <&clock_mmss clk_mmss_camss_csi0_clk>, - <&clock_mmss clk_mmss_camss_csiphy0_clk>, <&clock_mmss clk_mmss_camss_csi0_ahb_clk>, <&clock_mmss clk_mmss_camss_csi0rdi_clk>, <&clock_mmss clk_mmss_camss_csi0pix_clk>, - <&clock_mmss clk_mmss_camss_cphy_csid0_clk>, - <&clock_mmss clk_csiphy_clk_src>; + <&clock_mmss clk_mmss_camss_cphy_csid0_clk>; clock-names = "mmssnoc_axi", "mnoc_ahb", "bmic_smmu_ahb", "bmic_smmu_axi", "camss_ahb_clk", "camss_top_ahb_clk", - "ispif_ahb_clk", "csi_src_clk", "csi_clk", - "csi_phy_clk", "csi_ahb_clk", "csi_rdi_clk", - "csi_pix_clk", "cphy_csid_clk", "cphy_clk_src"; - qcom,clock-rates = <0 0 0 0 0 0 0 256000000 0 0 0 0 0 0 - 256000000>; + "ispif_ahb_clk", "csi_src_clk", "csiphy_clk_src", + "csi_clk", "csi_ahb_clk", "csi_rdi_clk", + "csi_pix_clk", "cphy_csid_clk"; + qcom,clock-rates = <0 0 0 0 0 0 0 256000000 256000000 + 0 0 0 0 0>; status = "ok"; }; @@ -186,21 +185,20 @@ <&clock_mmss clk_mmss_camss_top_ahb_clk>, <&clock_mmss clk_mmss_camss_ispif_ahb_clk>, <&clock_mmss clk_csi1_clk_src>, + <&clock_mmss clk_csiphy_clk_src>, <&clock_mmss clk_mmss_camss_csi1_clk>, - <&clock_mmss clk_mmss_camss_csiphy1_clk>, <&clock_mmss clk_mmss_camss_csi1_ahb_clk>, <&clock_mmss clk_mmss_camss_csi1rdi_clk>, <&clock_mmss clk_mmss_camss_csi1pix_clk>, - <&clock_mmss clk_mmss_camss_cphy_csid1_clk>, - <&clock_mmss clk_csiphy_clk_src>; + <&clock_mmss clk_mmss_camss_cphy_csid1_clk>; clock-names = "mmssnoc_axi", "mnoc_ahb", "bmic_smmu_ahb", "bmic_smmu_axi", "camss_ahb_clk", "camss_top_ahb_clk", - "ispif_ahb_clk", "csi_src_clk", "csi_clk", - "csi_phy_clk", "csi_ahb_clk", "csi_rdi_clk", - "csi_pix_clk", "cphy_csid_clk", "cphy_clk_src"; - qcom,clock-rates = <0 0 0 0 0 0 0 256000000 0 0 0 0 0 0 - 256000000>; + "ispif_ahb_clk", "csi_src_clk", "csiphy_clk_src", + "csi_clk", "csi_ahb_clk", "csi_rdi_clk", + "csi_pix_clk", "cphy_csid_clk"; + qcom,clock-rates = <0 0 0 0 0 0 0 256000000 256000000 + 0 0 0 0 0>; status = "ok"; }; @@ -225,21 +223,20 @@ <&clock_mmss clk_mmss_camss_top_ahb_clk>, <&clock_mmss clk_mmss_camss_ispif_ahb_clk>, <&clock_mmss clk_csi2_clk_src>, + <&clock_mmss clk_csiphy_clk_src>, <&clock_mmss clk_mmss_camss_csi2_clk>, - <&clock_mmss clk_mmss_camss_csiphy2_clk>, <&clock_mmss clk_mmss_camss_csi2_ahb_clk>, <&clock_mmss clk_mmss_camss_csi2rdi_clk>, <&clock_mmss clk_mmss_camss_csi2pix_clk>, - <&clock_mmss clk_mmss_camss_cphy_csid2_clk>, - <&clock_mmss clk_csiphy_clk_src>; + <&clock_mmss clk_mmss_camss_cphy_csid2_clk>; clock-names = "mmssnoc_axi", "mnoc_ahb", "bmic_smmu_ahb", "bmic_smmu_axi", "camss_ahb_clk", "camss_top_ahb_clk", - "ispif_ahb_clk", "csi_src_clk", "csi_clk", - "csi_phy_clk", "csi_ahb_clk", "csi_rdi_clk", - "csi_pix_clk", "cphy_csid_clk", "cphy_clk_src"; - qcom,clock-rates = <0 0 0 0 0 0 0 256000000 0 0 0 0 0 0 - 256000000>; + "ispif_ahb_clk", "csi_src_clk", "csiphy_clk_src", + "csi_clk", "csi_ahb_clk", "csi_rdi_clk", + "csi_pix_clk", "cphy_csid_clk"; + qcom,clock-rates = <0 0 0 0 0 0 0 256000000 256000000 + 0 0 0 0 0>; status = "ok"; }; @@ -264,20 +261,20 @@ <&clock_mmss clk_mmss_camss_top_ahb_clk>, <&clock_mmss clk_mmss_camss_ispif_ahb_clk>, <&clock_mmss clk_csi3_clk_src>, + <&clock_mmss clk_csiphy_clk_src>, <&clock_mmss clk_mmss_camss_csi3_clk>, <&clock_mmss clk_mmss_camss_csi3_ahb_clk>, <&clock_mmss clk_mmss_camss_csi3rdi_clk>, <&clock_mmss clk_mmss_camss_csi3pix_clk>, - <&clock_mmss clk_mmss_camss_cphy_csid1_clk>, - <&clock_mmss clk_csiphy_clk_src>; + <&clock_mmss clk_mmss_camss_cphy_csid3_clk>; clock-names = "mmssnoc_axi", "mnoc_ahb", "bmic_smmu_ahb", "bmic_smmu_axi", "camss_ahb_clk", "camss_top_ahb_clk", - "ispif_ahb_clk", "csi_src_clk", "csi_clk", - "csi_ahb_clk", "csi_rdi_clk", - "csi_pix_clk", "cphy_csid_clk", "cphy_clk_src"; - qcom,clock-rates = <0 0 0 0 0 0 0 256000000 0 0 0 0 0 - 256000000>; + "ispif_ahb_clk", "csi_src_clk", "csiphy_clk_src", + "csi_clk", "csi_ahb_clk", "csi_rdi_clk", + "csi_pix_clk", "cphy_csid_clk"; + qcom,clock-rates = <0 0 0 0 0 0 0 256000000 256000000 + 0 0 0 0 0>; status = "ok"; }; @@ -333,7 +330,7 @@ camss-vdd-supply = <&gdsc_camss_top>; vdd-supply = <&gdsc_cpp>; qcom,vdd-names = "smmu-vdd", "camss-vdd", "vdd"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -344,7 +341,7 @@ <&clock_mmss clk_mmss_fd_ahb_clk>, <&clock_mmss clk_mmss_camss_cpp_axi_clk>, <&clock_mmss clk_mmss_camss_cpp_vbif_ahb_clk>; - clock-names = "mmss_mnoc_maxi_clk", + clock-names = "mmssnoc_axi", "mmss_mnoc_ahb_clk", "mmss_bimc_smmu_ahb_clk", "mmss_bimc_smmu_axi_clk", @@ -355,14 +352,18 @@ "mmss_fd_ahb_clk", "mmss_camss_cpp_axi_clk", "mmss_camss_cpp_vbif_ahb_clk"; - qcom,clock-rates = <0 0 0 0 0 0 200000000 0 0 0 0>; + qcom,clock-rates = + <0 0 0 0 0 0 404000000 0 0 0 0>, + <0 0 0 0 0 0 100000000 0 0 0 0>, + <0 0 0 0 0 0 404000000 0 0 0 0>, + <0 0 0 0 0 0 404000000 0 0 0 0>; qcom,msm-bus,name = "msm_camera_fd"; qcom,msm-bus,num-cases = <4>; qcom,msm-bus,num-paths = <1>; qcom,msm-bus,vectors-KBps = <106 512 0 0>, - <106 512 13000000 13000000>, - <106 512 45000000 45000000>, - <106 512 90000000 90000000>; + <106 512 1625 0>, + <106 512 2995 0>, + <106 512 7200 0>; qcom,fd-vbif-reg-settings = <0x20 0x10000000 0x30000000>, <0x24 0x10000000 0x30000000>, <0x28 0x10000000 0x30000000>, @@ -387,7 +388,6 @@ vdd-supply = <&gdsc_cpp>; qcom,vdd-names = "smmu-vdd", "camss-vdd", "vdd"; clocks = <&clock_gcc clk_mmssnoc_axi_clk>, - <&clock_mmss clk_mmss_mnoc_maxi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_camss_ahb_clk>, <&clock_mmss clk_mmss_camss_top_ahb_clk>, @@ -398,12 +398,12 @@ <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, <&clock_mmss clk_mmss_camss_cpp_vbif_ahb_clk>; clock-names = "mmssnoc_axi_clk", - "mnoc_maxi_clk", "mnoc_ahb_clk", + "mnoc_ahb_clk", "camss_ahb_clk", "camss_top_ahb_clk", "cpp_core_clk", "camss_cpp_ahb_clk", "camss_cpp_axi_clk", "micro_iface_clk", "mmss_smmu_axi_clk", "cpp_vbif_ahb_clk"; - qcom,clock-rates = <0 0 0 0 0 200000000 0 0 0 0 0>; + qcom,clock-rates = <0 0 0 0 200000000 0 0 0 0 0>; qcom,min-clock-rate = <200000000>; qcom,bus-master = <1>; qcom,vbif-qos-setting = <0x20 0x10000000>, @@ -455,7 +455,7 @@ qcom,vdd-names = "camss-vdd", "vfe0-vdd", "vfe1-vdd"; qcom,clock-cntl-support; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_camss_ahb_clk>, <&clock_mmss clk_mmss_camss_top_ahb_clk>, @@ -482,7 +482,7 @@ <&clock_mmss clk_mmss_camss_vfe1_clk>, <&clock_mmss clk_vfe1_clk_src>, <&clock_mmss clk_mmss_camss_csi_vfe1_clk>; - clock-names = "mnoc_maxi_clk", "mnoc_ahb_clk", + clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "camss_ahb_clk", "camss_top_ahb_clk", "ispif_ahb_clk", "csi0_src_clk", "csi1_src_clk", @@ -534,7 +534,7 @@ camss-vdd-supply = <&gdsc_camss_top>; smmu-vdd-supply = <&gdsc_bimc_smmu>; qcom,vdd-names = "vdd", "camss-vdd", "smmu-vdd"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -547,7 +547,7 @@ <&clock_mmss clk_mmss_camss_vfe_vbif_axi_clk>, <&clock_mmss clk_vfe0_clk_src>, <&clock_mmss clk_mmss_camss_csi_vfe0_clk>; - clock-names = "mnoc_maxi_clk", "mnoc_ahb_clk", + clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "bimc_smmu_ahb_clk", "bimc_smmu_axi_clk", "camss_ahb_clk", "camss_top_ahb_clk", "camss_vfe_clk", "camss_vfe_stream_clk", @@ -614,7 +614,7 @@ camss-vdd-supply = <&gdsc_camss_top>; smmu-vdd-supply = <&gdsc_bimc_smmu>; qcom,vdd-names = "vdd", "camss-vdd", "smmu-vdd"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -627,7 +627,7 @@ <&clock_mmss clk_mmss_camss_vfe_vbif_axi_clk>, <&clock_mmss clk_vfe1_clk_src>, <&clock_mmss clk_mmss_camss_csi_vfe1_clk>; - clock-names = "mnoc_maxi_clk", "mnoc_ahb_clk", + clock-names = "mmssnoc_axi", "mnoc_ahb_clk", "bimc_smmu_ahb_clk", "bimc_smmu_axi_clk", "camss_ahb_clk", "camss_top_ahb_clk", "camss_vfe_clk", "camss_vfe_stream_clk", @@ -752,7 +752,7 @@ smmu-vdd-supply = <&gdsc_bimc_smmu>; camss-vdd-supply = <&gdsc_camss_top>; qcom,vdd-names = "smmu-vdd", "camss-vdd"; - clock-names = "mmss_mnoc_maxi_clk", + clock-names = "mmssnoc_axi", "mmss_mnoc_ahb_clk", "mmss_bimc_smmu_ahb_clk", "mmss_bimc_smmu_axi_clk", @@ -761,7 +761,7 @@ "core_clk", "mmss_camss_jpeg_ahb_clk", "mmss_camss_jpeg_axi_clk"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -796,7 +796,7 @@ smmu-vdd-supply = <&gdsc_bimc_smmu>; camss-vdd-supply = <&gdsc_camss_top>; qcom,vdd-names = "smmu-vdd", "camss-vdd"; - clock-names = "mmss_mnoc_maxi_clk", + clock-names = "mmssnoc_axi", "mmss_mnoc_ahb_clk", "mmss_bimc_smmu_ahb_clk", "mmss_bimc_smmu_axi_clk", @@ -805,7 +805,7 @@ "core_clk", "mmss_camss_jpeg_ahb_clk", "mmss_camss_jpeg_axi_clk"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi index ad293d9827d1..085ca0187ee6 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi @@ -376,6 +376,8 @@ qcom,mdss-dsi-bl-max-level = <4095>; qcom,5v-boost-gpio = <&tlmm 51 0>; qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; + qcom,partial-update-enabled; + qcom,panel-roi-alignment = <4 2 4 2 20 20>; }; &dsi_sharp_1080_cmd { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi index 1267e578f9b4..e140074465ef 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi @@ -75,6 +75,8 @@ qcom,gpu-qdss-stm = <0x161c0000 0x40000>; // base addr, size + qcom,tsens-name = "tsens_tz_sensor12"; + clocks = <&clock_gfx clk_gpucc_gfx3d_clk>, <&clock_gcc clk_gcc_gpu_cfg_ahb_clk>, <&clock_gpu clk_gpucc_rbbmtimer_clk>, diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi index a4ba9a61cded..d973bc5ed84f 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi @@ -25,6 +25,7 @@ #include "dsi-panel-sharp-1080p-cmd.dtsi" #include "dsi-panel-jdi-1080p-video.dtsi" #include "dsi-panel-sharp-dualmipi-1080p-120hz.dtsi" +#include "dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi" &soc { dsi_panel_pwr_supply: dsi_panel_pwr_supply { @@ -156,3 +157,9 @@ qcom,mdss-dsi-t-clk-post = <0x7>; qcom,mdss-dsi-t-clk-pre = <0x26>; }; + +&dsi_dual_jdi_a407_cmd { + qcom,mdss-dsi-panel-timings = [00 16 05 05 09 0e 05 05 04 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x06>; + qcom,mdss-dsi-t-clk-pre = <0x22>; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi index 11571415c02e..af0eb60818fb 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi @@ -96,8 +96,10 @@ gdsc-supply = <&gdsc_mdss>; - clocks = <&clock_mmss clk_mmss_mdss_ahb_clk>; - clock-names = "iface_clk"; + clocks = <&clock_mmss clk_mmss_mdss_ahb_clk>, + <&clock_gcc clk_ln_bb_clk1>, + <&clock_gcc clk_gcc_usb3_clkref_clk>; + clock-names = "iface_clk", "ref_clk_src", "ref_clk"; clock-rate = <0>; qcom,platform-supply-entries { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi index 4fe694069011..ec38e46b1d89 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi @@ -477,15 +477,17 @@ <&clock_mmss clk_mmss_mdss_mdp_clk>, <&clock_mmss clk_mmss_mdss_hdmi_dp_ahb_clk>, <&clock_mmss clk_mmss_mdss_dp_aux_clk>, + <&clock_gcc clk_ln_bb_clk1>, + <&clock_gcc clk_gcc_usb3_clkref_clk>, <&clock_mmss clk_mmss_mdss_dp_link_clk>, <&clock_mmss clk_mmss_mdss_dp_link_intf_clk>, <&clock_mmss clk_mmss_mdss_dp_crypto_clk>, <&clock_mmss clk_mmss_mdss_dp_pixel_clk>; clock-names = "core_mnoc_clk", "core_iface_clk", "core_bus_clk", "core_mdp_core_clk", "core_alt_iface_clk", - "core_aux_clk", "ctrl_link_clk", - "ctrl_link_iface_clk", "ctrl_crypto_clk", - "ctrl_pixel_clk"; + "core_aux_clk", "core_ref_clk_src", "core_ref_clk", + "ctrl_link_clk", "ctrl_link_iface_clk", + "ctrl_crypto_clk", "ctrl_pixel_clk"; qcom,dp-usbpd-detection = <&pmicobalt_pdphy>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi index 1ae0ab804eac..5f89985db0a3 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi @@ -126,3 +126,70 @@ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft"; }; }; + +&pmx_mdss { + mdss_dsi_active: mdss_dsi_active { + mux { + pins = "gpio94"; + function = "gpio"; + }; + + config { + pins = "gpio94"; + drive-strength = <8>; /* 8 mA */ + bias-disable = <0>; /* no pull */ + output-high; + }; + }; + + mdss_dsi_suspend: mdss_dsi_suspend { + mux { + pins = "gpio94"; + function = "gpio"; + }; + + config { + pins = "gpio94"; + drive-strength = <2>; /* 2 mA */ + bias-pull-down; /* pull down */ + }; + }; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "split_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_dual_jdi_a407_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; +}; + +&mdss_dsi1 { + qcom,dsi-pref-prim-pan = <&dsi_dual_jdi_a407_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; +}; + +&labibb { + status = "ok"; + qpnp,qpnp-labibb-mode = "lcd"; +}; + +&dsi_dual_jdi_a407_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi index 99d80a3b3848..fcc4d6d8ee2d 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi @@ -19,7 +19,10 @@ reg-names = "csiphy"; interrupts = <0 78 0>; interrupt-names = "csiphy"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + gdscr-supply = <&gdsc_camss_top>; + bimc_smmu-supply = <&gdsc_bimc_smmu>; + qcom,cam-vreg-name = "gdscr", "bimc_smmu"; + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -33,13 +36,13 @@ <&clock_mmss clk_mmss_camss_ispif_ahb_clk>, <&clock_mmss clk_csiphy_clk_src>, <&clock_mmss clk_mmss_camss_csiphy0_clk>; - clock-names = "mnoc_maxi", "mnoc_ahb", + clock-names = "mmssnoc_axi", "mnoc_ahb", "bmic_smmu_ahb", "bmic_smmu_axi", "camss_ahb_clk", "camss_top_ahb_clk", "csi_src_clk", "csi_clk", "cphy_csid_clk", "csiphy_timer_src_clk", "csiphy_timer_clk", "camss_ispif_ahb_clk", "csiphy_clk_src", "csiphy_clk"; - qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 269333333 0 + qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 200000000 0 0 256000000 0>; status = "ok"; }; @@ -51,7 +54,10 @@ reg-names = "csiphy"; interrupts = <0 79 0>; interrupt-names = "csiphy"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + gdscr-supply = <&gdsc_camss_top>; + bimc_smmu-supply = <&gdsc_bimc_smmu>; + qcom,cam-vreg-name = "gdscr", "bimc_smmu"; + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -65,13 +71,13 @@ <&clock_mmss clk_mmss_camss_ispif_ahb_clk>, <&clock_mmss clk_csiphy_clk_src>, <&clock_mmss clk_mmss_camss_csiphy1_clk>; - clock-names = "mnoc_maxi", "mnoc_ahb", + clock-names = "mmssnoc_axi", "mnoc_ahb", "bmic_smmu_ahb", "bmic_smmu_axi", "camss_ahb_clk", "camss_top_ahb_clk", "csi_src_clk", "csi_clk", "cphy_csid_clk", "csiphy_timer_src_clk", "csiphy_timer_clk", "camss_ispif_ahb_clk", "csiphy_clk_src", "csiphy_clk"; - qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 269333333 0 + qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 200000000 0 0 256000000 0>; status = "ok"; }; @@ -83,7 +89,10 @@ reg-names = "csiphy"; interrupts = <0 80 0>; interrupt-names = "csiphy"; - clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>, + gdscr-supply = <&gdsc_camss_top>; + bimc_smmu-supply = <&gdsc_bimc_smmu>; + qcom,cam-vreg-name = "gdscr", "bimc_smmu"; + clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, @@ -97,13 +106,13 @@ <&clock_mmss clk_mmss_camss_ispif_ahb_clk>, <&clock_mmss clk_csiphy_clk_src>, <&clock_mmss clk_mmss_camss_csiphy2_clk>; - clock-names = "mnoc_maxi", "mnoc_ahb", + clock-names = "mmssnoc_axi", "mnoc_ahb", "bmic_smmu_ahb", "bmic_smmu_axi", "camss_ahb_clk", "camss_top_ahb_clk", "csi_src_clk", "csi_clk", "cphy_csid_clk", "csiphy_timer_src_clk", "csiphy_timer_clk", "camss_ispif_ahb_clk", "csiphy_clk_src", "csiphy_clk"; - qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 269333333 0 + qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 200000000 0 0 256000000 0>; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi index e19fb5f7c233..b255fca6a691 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi @@ -171,12 +171,17 @@ < 2496000 >; }; +&bwmon { + compatible = "qcom,bimc-bwmon4"; + qcom,hw-timer-hz = <19200000>; +}; + &devfreq_cpufreq { mincpubw-cpufreq { cpu-to-dev-map-0 = < 1900800 1525 >; cpu-to-dev-map-4 = - < 2419200 1525 >, + < 2112000 1525 >, < 2496000 5195 >; }; }; @@ -664,11 +669,11 @@ /* SVS2 */ qcom,imem-ab-tbl = - <200000000 1752000>, - <269330000 1752000>, - <355200000 2500000>, - <444000000 6000000>, - <533000000 6000000>; + <200000000 1560000>,/* imem @ svs2 freq 75 Mhz */ + <269330000 3570000>,/* imem @ svs freq 171 Mhz */ + <355200000 3570000>,/* imem @ svs freq 171 Mhz */ + <444000000 6750000>,/* imem @ nom freq 323 Mhz */ + <533000000 8490000>;/* imem @ turbo freq 406 Mhz */ }; /* GPU overrides */ diff --git a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi index c44e8f976710..f17be7570742 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi @@ -66,10 +66,10 @@ * corresponding video core frequency. */ qcom,imem-ab-tbl = - <100000000 1752000>, /* imem @ svs2 freq 75 Mhz */ - <186000000 1752000>, /* imem @ svs2 freq 75 Mhz */ - <360000000 2500000>, /* imem @ svs freq 171 Mhz */ - <465000000 6000000>; /* imem @ noimal freq 320 Mhz */ + <100000000 1560000>, /* imem @ svs2 freq 75 Mhz */ + <186000000 3570000>, /* imem @ svs freq 171 Mhz */ + <360000000 6750000>, /* imem @ nom freq 323 Mhz */ + <465000000 8490000>; /* imem @ turbo freq 406 Mhz */ /* Regulators */ smmu-vdd-supply = <&gdsc_bimc_smmu>; @@ -124,7 +124,7 @@ qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0_OCMEM>; qcom,bus-slave = <MSM_BUS_SLAVE_VMEM>; qcom,bus-governor = "msm-vidc-vmem+"; - qcom,bus-range-kbps = <1000 6776000>; + qcom,bus-range-kbps = <1000 8490000>; }; arm9_bus_ddr { diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index 27f9e2ace106..2600fa25b73f 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -541,14 +541,13 @@ < 13763 /* 1804 MHz */ >; }; - qcom,cpu-bwmon { - compatible = "qcom,bimc-bwmon4"; + bwmon: qcom,cpu-bwmon { + compatible = "qcom,bimc-bwmon3"; reg = <0x01008000 0x300>, <0x01001000 0x200>; reg-names = "base", "global_base"; interrupts = <0 183 4>; qcom,mport = <0>; qcom,target-dev = <&cpubw>; - qcom,hw-timer-hz = <19200000>; }; mincpubw: qcom,mincpubw { diff --git a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi index 352856965373..b60d4013dad8 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi @@ -26,6 +26,8 @@ arm,buffer-size = <0x400000>; arm,sg-enable; + coresight-ctis = <&cti0 &cti8>; + coresight-name = "coresight-tmc-etr"; clocks = <&clock_gcc RPM_QDSS_CLK>, @@ -76,6 +78,8 @@ coresight-name = "coresight-tmc-etf"; + coresight-ctis = <&cti0 &cti8>; + clocks = <&clock_gcc RPM_QDSS_CLK>, <&clock_gcc RPM_QDSS_A_CLK>; clock-names = "apb_pclk", "core_a_clk"; @@ -161,6 +165,14 @@ <&funnel_merg_in_funnel_in0>; }; }; + port@3 { + reg = <6>; + funnel_in0_in_funnel_qatb: endpoint { + slave-mode; + remote-endpoint = + <&funnel_qatb_out_funnel_in0>; + }; + }; port@4 { reg = <7>; funnel_in0_in_stm: endpoint { @@ -191,4 +203,294 @@ }; }; }; + + cti0: cti@6010000 { + compatible = "arm,coresight-cti"; + reg = <0x6010000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti0"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti1: cti@6011000 { + compatible = "arm,coresight-cti"; + reg = <0x6011000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti1"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti2: cti@6012000 { + compatible = "arm,coresight-cti"; + reg = <0x6012000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti2"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti3: cti@6013000 { + compatible = "arm,coresight-cti"; + reg = <0x6013000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti3"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti4: cti@6014000 { + compatible = "arm,coresight-cti"; + reg = <0x6014000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti4"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti5: cti@6015000 { + compatible = "arm,coresight-cti"; + reg = <0x6015000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti5"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti6: cti@6016000 { + compatible = "arm,coresight-cti"; + reg = <0x6016000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti6"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti7: cti@6017000 { + compatible = "arm,coresight-cti"; + reg = <0x6017000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti7"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti8: cti@6018000 { + compatible = "arm,coresight-cti"; + reg = <0x6018000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti8"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti9: cti@6019000 { + compatible = "arm,coresight-cti"; + reg = <0x6019000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti9"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti10: cti@601a000 { + compatible = "arm,coresight-cti"; + reg = <0x601a000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti10"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti11: cti@601b000 { + compatible = "arm,coresight-cti"; + reg = <0x601b000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti11"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti12: cti@601c000 { + compatible = "arm,coresight-cti"; + reg = <0x601c000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti12"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti13: cti@601d000 { + compatible = "arm,coresight-cti"; + reg = <0x601d000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti13"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti14: cti@601e000 { + compatible = "arm,coresight-cti"; + reg = <0x601e000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti14"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + cti15: cti@601f000 { + compatible = "arm,coresight-cti"; + reg = <0x601f000 0x1000>; + reg-names = "cti-base"; + + coresight-name = "coresight-cti15"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + }; + + funnel_qatb: funnel@6005000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; + + reg = <0x6005000 0x1000>; + reg-names = "funnel-base"; + + coresight-name = "coresight-funnel-qatb"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "apb_pclk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + funnel_qatb_out_funnel_in0: endpoint { + remote-endpoint = + <&funnel_in0_in_funnel_qatb>; + }; + }; + port@1 { + reg = <0>; + funnel_qatb_in_tpda: endpoint { + slave-mode; + remote-endpoint = + <&tpda_out_funnel_qatb>; + }; + }; + }; + }; + + tpda: tpda@6004000 { + compatible = "qcom,coresight-tpda"; + reg = <0x6004000 0x1000>; + reg-names = "tpda-base"; + + coresight-name = "coresight-tpda"; + + qcom,tpda-atid = <65>; + qcom,bc-elem-size = <7 32>, + <9 32>; + qcom,tc-elem-size = <3 32>, + <6 32>, + <9 32>; + qcom,dsb-elem-size = <7 32>, + <9 32>; + qcom,cmb-elem-size = <3 32>, + <4 32>, + <5 32>, + <9 64>; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + tpda_out_funnel_qatb: endpoint { + remote-endpoint = + <&funnel_qatb_in_tpda>; + }; + }; + port@2 { + reg = <5>; + tpda_in_tpdm_dcc: endpoint { + slave-mode; + remote-endpoint = + <&tpdm_dcc_out_tpda>; + }; + }; + }; + }; + + tpdm_dcc: tpdm@7054000 { + compatible = "qcom,coresight-tpdm"; + reg = <0x7054000 0x1000>; + reg-names = "tpdm-base"; + + coresight-name = "coresight-tpdm-dcc"; + + clocks = <&clock_gcc RPM_QDSS_CLK>, + <&clock_gcc RPM_QDSS_A_CLK>; + clock-names = "core_clk", "core_a_clk"; + + port{ + tpdm_dcc_out_tpda: endpoint { + remote-endpoint = <&tpda_in_tpdm_dcc>; + }; + }; + }; }; diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi index 4765a8cb7c79..67748d6683c0 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi @@ -252,6 +252,18 @@ status = "ok"; }; + wdog: qcom,wdt@17817000 { + status = "disabled"; + compatible = "qcom,msm-watchdog"; + reg = <0x17817000 0x1000>; + reg-names = "wdt-base"; + interrupts = <0 3 0>, <0 4 0>; + qcom,bark-time = <11000>; + qcom,pet-time = <10000>; + qcom,ipi-ping; + qcom,wakeup-enable; + }; + qcom,sps { compatible = "qcom,msm_sps_4k"; qcom,pipe-attr-ee; @@ -380,6 +392,16 @@ qcom,mpu-enabled; }; + dcc: dcc@10b3000 { + compatible = "qcom,dcc"; + reg = <0x10b3000 0x1000>, + <0x10b4000 0x800>; + reg-names = "dcc-base", "dcc-ram-base"; + + clocks = <&clock_gcc RPM_QDSS_CLK>; + clock-names = "dcc_clk"; + }; + qcom,glink-smem-native-xprt-modem@86000000 { compatible = "qcom,glink-smem-native-xprt"; reg = <0x86000000 0x200000>, diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi index 17551d94896b..3f0d4cc48696 100644 --- a/arch/arm/boot/dts/qcom/msmtriton.dtsi +++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi @@ -220,6 +220,15 @@ qcom,pipe-attr-ee; }; + tsens: tsens@10ad000 { + compatible = "qcom,msmtriton-tsens"; + reg = <0x10ad000 0x2000>; + reg-names = "tsens_physical"; + interrupts = <0 184 0>, <0 430 0>; + interrupt-names = "tsens-upper-lower", "tsens-critical"; + qcom,sensors = <12>; + }; + uartblsp1dm1: serial@0c170000 { compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; reg = <0xc170000 0x1000>; diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig index 2b35b0f12787..48507eebe9f3 100644 --- a/arch/arm/configs/msmcortex_defconfig +++ b/arch/arm/configs/msmcortex_defconfig @@ -261,8 +261,8 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_SECURE_TOUCH=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_KEYCHORD=y diff --git a/arch/arm/configs/msmfalcon_defconfig b/arch/arm/configs/msmfalcon_defconfig index 2b35b0f12787..db50dce9f9a4 100644 --- a/arch/arm/configs/msmfalcon_defconfig +++ b/arch/arm/configs/msmfalcon_defconfig @@ -261,7 +261,6 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y @@ -416,6 +415,9 @@ CONFIG_GPIO_USB_DETECT=y CONFIG_USB_BAM=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_ARM_SMMU=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y CONFIG_QCOM_COMMON_LOG=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c index 598323a1842e..e683d147816c 100644 --- a/arch/arm/kernel/topology.c +++ b/arch/arm/kernel/topology.c @@ -190,6 +190,13 @@ static int __init parse_cluster(struct device_node *cluster, int depth) return 0; } +static DEFINE_PER_CPU(unsigned long, cpu_efficiency) = SCHED_CAPACITY_SCALE; + +unsigned long arch_get_cpu_efficiency(int cpu) +{ + return per_cpu(cpu_efficiency, cpu); +} + #ifdef CONFIG_OF struct cpu_efficiency { const char *compatible; @@ -266,6 +273,7 @@ static int __init parse_dt_topology(void) for_each_possible_cpu(cpu) { const u32 *rate; int len; + u32 efficiency; /* too early to use cpu->of_node */ cn = of_get_cpu_node(cpu, NULL); @@ -274,12 +282,26 @@ static int __init parse_dt_topology(void) continue; } - for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++) - if (of_device_is_compatible(cn, cpu_eff->compatible)) - break; + /* + * The CPU efficiency value passed from the device tree + * overrides the value defined in the table_efficiency[] + */ + if (of_property_read_u32(cn, "efficiency", &efficiency) < 0) { + + for (cpu_eff = table_efficiency; + cpu_eff->compatible; cpu_eff++) - if (cpu_eff->compatible == NULL) - continue; + if (of_device_is_compatible(cn, + cpu_eff->compatible)) + break; + + if (cpu_eff->compatible == NULL) + continue; + + efficiency = cpu_eff->efficiency; + } + + per_cpu(cpu_efficiency, cpu) = efficiency; rate = of_get_property(cn, "clock-frequency", &len); if (!rate || len != 4) { @@ -288,7 +310,7 @@ static int __init parse_dt_topology(void) continue; } - capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency; + capacity = ((be32_to_cpup(rate)) >> 20) * efficiency; /* Save min capacity of the system */ if (capacity < min_capacity) diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig index d6ed9ac56bf1..d4d355531169 100644 --- a/arch/arm/mach-qcom/Kconfig +++ b/arch/arm/mach-qcom/Kconfig @@ -16,7 +16,9 @@ config ARCH_MSMFALCON select MULTI_IRQ_HANDLER select HAVE_ARM_ARCH_TIMER select MAY_HAVE_SPARSE_IRQ - select COMMON_CLK_MSM + select COMMON_CLK + select COMMON_CLK_QCOM + select QCOM_GDSC select PINCTRL_MSM_TLMM select USE_PINCTRL_IRQ select MSM_PM if PM @@ -31,6 +33,7 @@ config ARCH_MSMFALCON select MSM_QDSP6V2_CODECS select MSM_AUDIO_QDSP6V2 if SND_SOC select MSM_RPM_SMD + select GENERIC_IRQ_MIGRATION select MSM_JTAGV8 if CORESIGHT_ETMV4 help This enables support for the MSMFALCON chipset. If you do not diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index ee4efe58d0c8..94c0bf30c284 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -89,7 +89,9 @@ config ARCH_MSMHAMSTER config ARCH_MSMFALCON bool "Enable Support for Qualcomm Technologies Inc MSMFALCON" depends on ARCH_QCOM - select COMMON_CLK_MSM + select COMMON_CLK + select COMMON_CLK_QCOM + select QCOM_GDSC help This enables support for the MSMFALCON chipset. If you do not wish to build a kernel that runs @@ -98,7 +100,9 @@ config ARCH_MSMFALCON config ARCH_MSMTRITON bool "Enable Support for Qualcomm Technologies Inc MSMTRITON" depends on ARCH_QCOM - select COMMON_CLK_MSM + select COMMON_CLK + select COMMON_CLK_QCOM + select QCOM_GDSC help This enables support for the MSMTRITON chipset. If you do not wish to build a kernel that runs diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 7f31331933bb..5f8b02904d49 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -282,7 +282,6 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y CONFIG_TOUCHSCREEN_ATMEL_MXT=y CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=y @@ -548,6 +547,9 @@ CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT3_FS=y CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_EXT4_FS_ENCRYPTION=y +CONFIG_EXT4_FS_ICE_ENCRYPTION=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 89bee1463421..c1c0ae9da001 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -269,7 +269,6 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y CONFIG_TOUCHSCREEN_ATMEL_MXT=y CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=y @@ -555,6 +554,9 @@ CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT3_FS=y CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_EXT4_FS_ENCRYPTION=y +CONFIG_EXT4_FS_ICE_ENCRYPTION=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 50c8848bc6f9..036d6aa5c062 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -44,7 +44,6 @@ CONFIG_PARTITION_ADVANCED=y CONFIG_ARCH_QCOM=y CONFIG_ARCH_MSMCOBALT=y CONFIG_ARCH_MSMHAMSTER=y -CONFIG_ARCH_MSMFALCON=y CONFIG_PCI=y CONFIG_PCI_MSM=y CONFIG_SCHED_MC=y @@ -274,6 +273,7 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_SECURE_TOUCH=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_UINPUT=y @@ -555,6 +555,9 @@ CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT3_FS=y CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_EXT4_FS_ENCRYPTION=y +CONFIG_EXT4_FS_ICE_ENCRYPTION=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 3d1a01491c0c..77f0129776a3 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -45,7 +45,6 @@ CONFIG_PARTITION_ADVANCED=y CONFIG_ARCH_QCOM=y CONFIG_ARCH_MSMCOBALT=y CONFIG_ARCH_MSMHAMSTER=y -CONFIG_ARCH_MSMFALCON=y CONFIG_PCI=y CONFIG_PCI_MSM=y CONFIG_SCHED_MC=y @@ -275,6 +274,7 @@ CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_SECURE_TOUCH=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_KEYCHORD=y @@ -575,6 +575,9 @@ CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT3_FS=y CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_EXT4_FS_ENCRYPTION=y +CONFIG_EXT4_FS_ICE_ENCRYPTION=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y diff --git a/arch/arm64/configs/msmfalcon-perf_defconfig b/arch/arm64/configs/msmfalcon-perf_defconfig index 6ebd60b43c71..1bc352704893 100644 --- a/arch/arm64/configs/msmfalcon-perf_defconfig +++ b/arch/arm64/configs/msmfalcon-perf_defconfig @@ -41,8 +41,6 @@ CONFIG_MODULE_SIG_FORCE=y CONFIG_MODULE_SIG_SHA512=y CONFIG_PARTITION_ADVANCED=y CONFIG_ARCH_QCOM=y -CONFIG_ARCH_MSMCOBALT=y -CONFIG_ARCH_MSMHAMSTER=y CONFIG_ARCH_MSMFALCON=y CONFIG_ARCH_MSMTRITON=y CONFIG_PCI=y @@ -272,7 +270,6 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y @@ -476,7 +473,6 @@ CONFIG_RMNET_IPA3=y CONFIG_GPIO_USB_DETECT=y CONFIG_SEEMP_CORE=y CONFIG_USB_BAM=y -CONFIG_MSM_MDSS_PLL=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 01324d89e79e..34f0da3c37a4 100644 --- a/arch/arm64/configs/msmfalcon_defconfig +++ b/arch/arm64/configs/msmfalcon_defconfig @@ -42,8 +42,6 @@ CONFIG_MODULE_SIG_SHA512=y CONFIG_PARTITION_ADVANCED=y # CONFIG_IOSCHED_DEADLINE is not set CONFIG_ARCH_QCOM=y -CONFIG_ARCH_MSMCOBALT=y -CONFIG_ARCH_MSMHAMSTER=y CONFIG_ARCH_MSMFALCON=y CONFIG_ARCH_MSMTRITON=y CONFIG_PCI=y @@ -273,7 +271,6 @@ CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y @@ -486,7 +483,6 @@ CONFIG_RMNET_IPA3=y CONFIG_GPIO_USB_DETECT=y CONFIG_SEEMP_CORE=y CONFIG_USB_BAM=y -CONFIG_MSM_MDSS_PLL=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_IOMMU_IO_PGTABLE_FAST=y CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index e5389bc981ee..eff70892dada 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -49,6 +49,17 @@ static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot, return prot; } +static int __get_iommu_pgprot(struct dma_attrs *attrs, int prot, + bool coherent) +{ + if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs)) + prot |= IOMMU_NOEXEC; + if (coherent) + prot |= IOMMU_CACHE; + + return prot; +} + static struct gen_pool *atomic_pool; #define NO_KERNEL_MAPPING_DUMMY 0x2222 #define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K @@ -1153,7 +1164,7 @@ static int arm_dma_set_mask(struct device *dev, u64 dma_mask) /* IOMMU */ static void __dma_clear_buffer(struct page *page, size_t size, - struct dma_attrs *attrs) + struct dma_attrs *attrs, bool is_coherent) { /* * Ensure that the allocated pages are zeroed, and that any data @@ -1162,7 +1173,8 @@ static void __dma_clear_buffer(struct page *page, size_t size, void *ptr = page_address(page); if (!dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs)) memset(ptr, 0, size); - dmac_flush_range(ptr, ptr + size); + if (!is_coherent) + dmac_flush_range(ptr, ptr + size); } static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping, @@ -1212,6 +1224,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, size_t count = size >> PAGE_SHIFT; size_t array_size = count * sizeof(struct page *); int i = 0; + bool is_coherent = is_device_dma_coherent(dev); if (array_size <= PAGE_SIZE) pages = kzalloc(array_size, gfp); @@ -1228,7 +1241,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, if (!page) goto error; - __dma_clear_buffer(page, size, attrs); + __dma_clear_buffer(page, size, attrs, is_coherent); for (i = 0; i < count; i++) pages[i] = page + i; @@ -1257,7 +1270,8 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, pages[i + j] = pages[i] + j; } - __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs); + __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs, + is_coherent); i += 1 << order; count -= 1 << order; } @@ -1322,9 +1336,8 @@ static dma_addr_t __iommu_create_mapping(struct device *dev, dma_addr = __alloc_iova(mapping, size); if (dma_addr == DMA_ERROR_CODE) return dma_addr; - - if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs)) - prot |= IOMMU_NOEXEC; + prot = __get_iommu_pgprot(attrs, prot, + is_device_dma_coherent(dev)); iova = dma_addr; for (i = 0; i < count; ) { @@ -1404,6 +1417,7 @@ static void *__iommu_alloc_atomic(struct device *dev, size_t size, size_t array_size = count * sizeof(struct page *); int i; void *addr; + bool coherent = is_device_dma_coherent(dev); if (array_size <= PAGE_SIZE) pages = kzalloc(array_size, gfp); @@ -1413,7 +1427,13 @@ static void *__iommu_alloc_atomic(struct device *dev, size_t size, if (!pages) return NULL; - addr = __alloc_from_pool(size, &page, gfp); + if (coherent) { + page = alloc_pages(gfp, get_order(size)); + addr = page ? page_address(page) : NULL; + } else { + addr = __alloc_from_pool(size, &page, gfp); + } + if (!addr) goto err_free; @@ -1428,7 +1448,10 @@ static void *__iommu_alloc_atomic(struct device *dev, size_t size, return addr; err_mapping: - __free_from_pool(addr, size); + if (coherent) + __free_pages(page, get_order(size)); + else + __free_from_pool(addr, size); err_free: kvfree(pages); return NULL; @@ -1444,7 +1467,8 @@ static void __iommu_free_atomic(struct device *dev, void *cpu_addr, static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs) { - pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, false); + bool coherent = is_device_dma_coherent(dev); + pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, coherent); struct page **pages; void *addr = NULL; @@ -1495,8 +1519,10 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, unsigned long uaddr = vma->vm_start; unsigned long usize = vma->vm_end - vma->vm_start; struct page **pages = __iommu_get_pages(cpu_addr, attrs); + bool coherent = is_device_dma_coherent(dev); - vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, false); + vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, + coherent); if (!pages) return -ENXIO; @@ -1577,121 +1603,6 @@ static int __dma_direction_to_prot(enum dma_data_direction dir) return prot; } -/* - * Map a part of the scatter-gather list into contiguous io address space - */ -static int __map_sg_chunk(struct device *dev, struct scatterlist *sg, - size_t size, dma_addr_t *handle, - enum dma_data_direction dir, struct dma_attrs *attrs, - bool is_coherent) -{ - struct dma_iommu_mapping *mapping = dev->archdata.mapping; - dma_addr_t iova, iova_base; - int ret = 0; - unsigned int count; - struct scatterlist *s; - int prot; - - size = PAGE_ALIGN(size); - *handle = DMA_ERROR_CODE; - - iova_base = iova = __alloc_iova(mapping, size); - if (iova == DMA_ERROR_CODE) - return -ENOMEM; - - for (count = 0, s = sg; count < (size >> PAGE_SHIFT); s = sg_next(s)) { - phys_addr_t phys = page_to_phys(sg_page(s)); - unsigned int len = PAGE_ALIGN(s->offset + s->length); - - if (!is_coherent && - !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) - __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, - dir); - - prot = __dma_direction_to_prot(dir); - if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs)) - prot |= IOMMU_NOEXEC; - - ret = iommu_map(mapping->domain, iova, phys, len, prot); - if (ret < 0) - goto fail; - count += len >> PAGE_SHIFT; - iova += len; - } - *handle = iova_base; - - return 0; -fail: - iommu_unmap(mapping->domain, iova_base, count * PAGE_SIZE); - __free_iova(mapping, iova_base, size); - return ret; -} - -static int __iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents, - enum dma_data_direction dir, struct dma_attrs *attrs, - bool is_coherent) -{ - struct scatterlist *s = sg, *dma = sg, *start = sg; - int i, count = 0; - unsigned int offset = s->offset; - unsigned int size = s->offset + s->length; - unsigned int max = dma_get_max_seg_size(dev); - - for (i = 1; i < nents; i++) { - s = sg_next(s); - - s->dma_address = DMA_ERROR_CODE; - s->dma_length = 0; - - if (s->offset || (size & ~PAGE_MASK) - || size + s->length > max) { - if (__map_sg_chunk(dev, start, size, &dma->dma_address, - dir, attrs, is_coherent) < 0) - goto bad_mapping; - - dma->dma_address += offset; - dma->dma_length = size - offset; - - size = offset = s->offset; - start = s; - dma = sg_next(dma); - count += 1; - } - size += s->length; - } - if (__map_sg_chunk(dev, start, size, &dma->dma_address, dir, attrs, - is_coherent) < 0) - goto bad_mapping; - - dma->dma_address += offset; - dma->dma_length = size - offset; - - return count+1; - -bad_mapping: - for_each_sg(sg, s, count, i) - __iommu_remove_mapping(dev, sg_dma_address(s), sg_dma_len(s)); - return 0; -} - -/** - * arm_coherent_iommu_map_sg - map a set of SG buffers for streaming mode DMA - * @dev: valid struct device pointer - * @sg: list of buffers - * @nents: number of buffers to map - * @dir: DMA transfer direction - * - * Map a set of i/o coherent buffers described by scatterlist in streaming - * mode for DMA. The scatter gather list elements are merged together (if - * possible) and tagged with the appropriate dma address and length. They are - * obtained via sg_dma_{address,length}. - */ -int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir, struct dma_attrs *attrs) -{ - return __iommu_map_sg(dev, sg, nents, dir, attrs, true); -} - /** * arm_iommu_map_sg - map a set of SG buffers for streaming mode DMA * @dev: valid struct device pointer @@ -1722,9 +1633,8 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, dev_err(dev, "Couldn't allocate iova for sg %p\n", sg); return 0; } - - if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs)) - prot |= IOMMU_NOEXEC; + prot = __get_iommu_pgprot(attrs, prot, + is_device_dma_coherent(dev)); ret = iommu_map_sg(mapping->domain, iova, sg, nents, prot); if (ret != total_length) { @@ -1741,40 +1651,6 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, return nents; } -static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir, struct dma_attrs *attrs, - bool is_coherent) -{ - struct scatterlist *s; - int i; - - for_each_sg(sg, s, nents, i) { - if (sg_dma_len(s)) - __iommu_remove_mapping(dev, sg_dma_address(s), - sg_dma_len(s)); - if (!is_coherent && - !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) - __dma_page_dev_to_cpu(sg_page(s), s->offset, - s->length, dir); - } -} - -/** - * arm_coherent_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg - * @dev: valid struct device pointer - * @sg: list of buffers - * @nents: number of buffers to unmap (same as was passed to dma_map_sg) - * @dir: DMA transfer direction (same as was passed to dma_map_sg) - * - * Unmap a set of streaming mode DMA translations. Again, CPU access - * rules concerning calls here are the same as for dma_unmap_single(). - */ -void arm_coherent_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir, struct dma_attrs *attrs) -{ - __iommu_unmap_sg(dev, sg, nents, dir, attrs, true); -} - /** * arm_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg * @dev: valid struct device pointer @@ -1812,6 +1688,9 @@ void arm_iommu_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, struct scatterlist *s; int i; + if (is_device_dma_coherent(dev)) + return; + for_each_sg(sg, s, nents, i) __dma_page_dev_to_cpu(sg_page(s), s->offset, s->length, dir); @@ -1830,6 +1709,9 @@ void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *sg, struct scatterlist *s; int i; + if (is_device_dma_coherent(dev)) + return; + for_each_sg(sg, s, nents, i) __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir); } @@ -1858,8 +1740,8 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, return dma_addr; prot = __dma_direction_to_prot(dir); - if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs)) - prot |= IOMMU_NOEXEC; + prot = __get_iommu_pgprot(attrs, prot, + is_device_dma_coherent(dev)); ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len, prot); @@ -1886,38 +1768,14 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs) { - if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) + if (!is_device_dma_coherent(dev) && + !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) __dma_page_cpu_to_dev(page, offset, size, dir); return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs); } /** - * arm_coherent_iommu_unmap_page - * @dev: valid struct device pointer - * @handle: DMA address of buffer - * @size: size of buffer (same as passed to dma_map_page) - * @dir: DMA transfer direction (same as passed to dma_map_page) - * - * Coherent IOMMU aware version of arm_dma_unmap_page() - */ -static void arm_coherent_iommu_unmap_page(struct device *dev, dma_addr_t handle, - size_t size, enum dma_data_direction dir, - struct dma_attrs *attrs) -{ - struct dma_iommu_mapping *mapping = dev->archdata.mapping; - dma_addr_t iova = handle & PAGE_MASK; - int offset = handle & ~PAGE_MASK; - int len = PAGE_ALIGN(size + offset); - - if (!iova) - return; - - iommu_unmap(mapping->domain, iova, len); - __free_iova(mapping, iova, len); -} - -/** * arm_iommu_unmap_page * @dev: valid struct device pointer * @handle: DMA address of buffer @@ -1940,7 +1798,8 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle, if (!iova) return; - if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) + if (!(is_device_dma_coherent(dev) || + dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))) __dma_page_dev_to_cpu(page, offset, size, dir); iommu_unmap(mapping->domain, iova, len); @@ -1959,7 +1818,8 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev, if (!iova) return; - __dma_page_dev_to_cpu(page, offset, size, dir); + if (!is_device_dma_coherent(dev)) + __dma_page_dev_to_cpu(page, offset, size, dir); } static void arm_iommu_sync_single_for_device(struct device *dev, @@ -1974,7 +1834,8 @@ static void arm_iommu_sync_single_for_device(struct device *dev, if (!iova) return; - __dma_page_cpu_to_dev(page, offset, size, dir); + if (!is_device_dma_coherent(dev)) + __dma_page_cpu_to_dev(page, offset, size, dir); } static int arm_iommu_dma_supported(struct device *dev, u64 mask) @@ -2016,22 +1877,6 @@ const struct dma_map_ops iommu_ops = { .mapping_error = arm_iommu_mapping_error, }; -const struct dma_map_ops iommu_coherent_ops = { - .alloc = arm_iommu_alloc_attrs, - .free = arm_iommu_free_attrs, - .mmap = arm_iommu_mmap_attrs, - .get_sgtable = arm_iommu_get_sgtable, - - .map_page = arm_coherent_iommu_map_page, - .unmap_page = arm_coherent_iommu_unmap_page, - - .map_sg = arm_coherent_iommu_map_sg, - .unmap_sg = arm_coherent_iommu_unmap_sg, - - .set_dma_mask = arm_dma_set_mask, - .dma_supported = arm_iommu_dma_supported, -}; - /** * arm_iommu_create_mapping * @bus: pointer to the bus holding the client device (for IOMMU calls) diff --git a/drivers/char/diag/diagfwd_glink.c b/drivers/char/diag/diagfwd_glink.c index fea1b74aacae..a2ffabe43c86 100644 --- a/drivers/char/diag/diagfwd_glink.c +++ b/drivers/char/diag/diagfwd_glink.c @@ -413,19 +413,16 @@ static int diag_glink_write(void *ctxt, unsigned char *buf, int len) return -ENODEV; } - err = wait_event_interruptible(glink_info->wait_q, - atomic_read(&glink_info->tx_intent_ready)); - if (err) { - diagfwd_write_buffer_done(glink_info->fwd_ctxt, buf); - return -ERESTARTSYS; - } - - atomic_dec(&glink_info->tx_intent_ready); - err = glink_tx(glink_info->hdl, glink_info, buf, len, tx_flags); - if (!err) { - DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s wrote to glink, len: %d\n", - glink_info->name, len); - } + if (atomic_read(&glink_info->tx_intent_ready)) { + atomic_dec(&glink_info->tx_intent_ready); + err = glink_tx(glink_info->hdl, glink_info, buf, len, tx_flags); + if (!err) { + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "%s wrote to glink, len: %d\n", + glink_info->name, len); + } + } else + err = -ENOMEM; return err; diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index 066890aebf39..22b9e05086bd 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.c @@ -751,7 +751,9 @@ int diagfwd_write(uint8_t peripheral, uint8_t type, void *buf, int len) if (!err) fwd_info->write_bytes += len; - + else + if (fwd_info->transport == TRANSPORT_GLINK) + diagfwd_write_buffer_done(fwd_info, buf_ptr); return err; } diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index 68274765a6c1..0d733f49f184 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -460,7 +460,7 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate) } pr_debug("rate: %lu --> index %d\n", rate, index); - if (cpuclk->llm_sw_overr) { + if (cpuclk->llm_sw_overr[0]) { clk_osm_write_reg(cpuclk, cpuclk->llm_sw_overr[0], LLM_SW_OVERRIDE_REG); clk_osm_write_reg(cpuclk, cpuclk->llm_sw_overr[1], @@ -471,7 +471,7 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate) /* Choose index and send request to OSM hardware */ clk_osm_write_reg(cpuclk, index, DCVS_PERF_STATE_DESIRED_REG); - if (cpuclk->llm_sw_overr) { + if (cpuclk->llm_sw_overr[0]) { udelay(1); clk_osm_write_reg(cpuclk, cpuclk->llm_sw_overr[2], LLM_SW_OVERRIDE_REG); @@ -1882,8 +1882,8 @@ static void clk_osm_apm_vc_setup(struct clk_osm *c) } scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(72), c->apm_crossover_vc); - clk_osm_write_reg(c, c->apm_threshold_vc, - SEQ_REG(15)); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(15), + c->apm_threshold_vc); scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(31), c->apm_threshold_vc != 0 ? c->apm_threshold_vc - 1 : 0xff); diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c index d5d55a58bf7f..c4f77e01b682 100644 --- a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c +++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c @@ -145,7 +145,7 @@ static void hdmi_cobalt_get_div(struct cobalt_reg_cfg *cfg, unsigned long pclk) u32 const min_freq = 8000, max_freq = 12000; u32 const cmp_cnt = 1024; u32 const th_min = 500, th_max = 1000; - u64 bit_clk = pclk * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; + u64 bit_clk = ((u64)pclk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; u32 half_rate_mode = 0; u32 freq_optimal, list_elements; int optimal_index; @@ -161,7 +161,7 @@ find_optimal_index: for (i = 0; i < sz_ratio; i++) { for (j = 0; j < sz_band; j++) { - u64 freq = (bit_clk / (1 << half_rate_mode)); + u64 freq = div_u64(bit_clk, (1 << half_rate_mode)); freq *= (ratio_list[i] * (1 << band_list[j])); do_div(freq, (u64) HDMI_MHZ_TO_HZ); diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index e3760969848d..da02ab499bff 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -23,6 +23,7 @@ struct freq_tbl { u8 pre_div; u16 m; u16 n; + unsigned long src_freq; }; /** @@ -184,5 +185,6 @@ extern const struct clk_ops clk_byte_ops; extern const struct clk_ops clk_byte2_ops; extern const struct clk_ops clk_pixel_ops; extern const struct clk_ops clk_gfx3d_ops; +extern const struct clk_ops clk_gfx3d_src_ops; #endif diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 4d5081c2b6d1..a075859771d3 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -16,6 +16,7 @@ #include <linux/err.h> #include <linux/bug.h> #include <linux/export.h> +#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/delay.h> #include <linux/regmap.h> @@ -865,3 +866,80 @@ const struct clk_ops clk_gfx3d_ops = { .determine_rate = clk_gfx3d_determine_rate, }; EXPORT_SYMBOL_GPL(clk_gfx3d_ops); + +static int clk_gfx3d_src_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_rate_request parent_req = { }; + struct clk_hw *p1, *p3, *xo, *curr_p; + const struct freq_tbl *f; + int ret; + + xo = clk_hw_get_parent_by_index(hw, 0); + if (req->rate == clk_hw_get_rate(xo)) { + req->best_parent_hw = xo; + req->best_parent_rate = req->rate; + return 0; + } + + f = qcom_find_freq(rcg->freq_tbl, req->rate); + if (!f || (req->rate != f->freq)) + return -EINVAL; + + /* Indexes of source from the parent map */ + p1 = clk_hw_get_parent_by_index(hw, 1); + p3 = clk_hw_get_parent_by_index(hw, 2); + + curr_p = clk_hw_get_parent(hw); + parent_req.rate = f->src_freq; + + if (curr_p == xo || curr_p == p3) + req->best_parent_hw = p1; + else if (curr_p == p1) + req->best_parent_hw = p3; + + parent_req.best_parent_hw = req->best_parent_hw; + + ret = __clk_determine_rate(req->best_parent_hw, &parent_req); + if (ret) + return ret; + + req->best_parent_rate = parent_req.rate; + + return 0; +} + +static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + const struct freq_tbl *f; + u32 cfg; + int ret; + + cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; + + f = qcom_find_freq(rcg->freq_tbl, rate); + if (!f) + return -EINVAL; + + /* Update the RCG-DIV */ + cfg |= f->pre_div << CFG_SRC_DIV_SHIFT; + + ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg); + if (ret) + return ret; + + return update_config(rcg); +} + +const struct clk_ops clk_gfx3d_src_ops = { + .is_enabled = clk_rcg2_is_enabled, + .get_parent = clk_rcg2_get_parent, + .set_parent = clk_rcg2_set_parent, + .recalc_rate = clk_rcg2_recalc_rate, + .set_rate = clk_gfx3d_set_rate, + .set_rate_and_parent = clk_gfx3d_src_set_rate_and_parent, + .determine_rate = clk_gfx3d_src_determine_rate, +}; +EXPORT_SYMBOL_GPL(clk_gfx3d_src_ops); diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 8ed5115efc3b..ac007ec667bb 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -595,9 +595,84 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8996 = { .num_clks = ARRAY_SIZE(msm8996_clks), }; +/* msmfalcon */ +DEFINE_CLK_SMD_RPM_BRANCH(msmfalcon, cxo, cxo_a, QCOM_SMD_RPM_MISC_CLK, 0, + 19200000); +DEFINE_CLK_SMD_RPM(msmfalcon, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +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, + 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); +DEFINE_CLK_SMD_RPM(msmfalcon, aggre2_noc_clk, aggre2_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 2); +DEFINE_CLK_SMD_RPM_QDSS(msmfalcon, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msmfalcon, rf_clk2, rf_clk2_ao, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msmfalcon, div_clk1, div_clk1_ao, 0xb); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msmfalcon, ln_bb_clk1, ln_bb_clk1_ao, 0x1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msmfalcon, ln_bb_clk2, ln_bb_clk2_ao, 0x2); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msmfalcon, ln_bb_clk3, ln_bb_clk3_ao, 0x3); + +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msmfalcon, rf_clk2_pin, rf_clk2_a_pin, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msmfalcon, ln_bb_clk1_pin, + ln_bb_clk1_pin_ao, 0x1); +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); +static struct clk_hw *msmfalcon_clks[] = { + [RPM_XO_CLK_SRC] = &msmfalcon_cxo.hw, + [RPM_XO_A_CLK_SRC] = &msmfalcon_cxo_a.hw, + [RPM_SNOC_CLK] = &msmfalcon_snoc_clk.hw, + [RPM_SNOC_A_CLK] = &msmfalcon_snoc_a_clk.hw, + [RPM_BIMC_CLK] = &msmfalcon_bimc_clk.hw, + [RPM_BIMC_A_CLK] = &msmfalcon_bimc_a_clk.hw, + [RPM_QDSS_CLK] = &msmfalcon_qdss_clk.hw, + [RPM_QDSS_A_CLK] = &msmfalcon_qdss_a_clk.hw, + [RPM_RF_CLK2_PIN] = &msmfalcon_rf_clk2_pin.hw, + [RPM_RF_CLK2_A_PIN] = &msmfalcon_rf_clk2_a_pin.hw, + [RPM_AGGR2_NOC_CLK] = &msmfalcon_aggre2_noc_clk.hw, + [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_IPA_CLK] = &msmfalcon_ipa_clk.hw, + [RPM_IPA_A_CLK] = &msmfalcon_ipa_a_clk.hw, + [RPM_CE1_CLK] = &msmfalcon_ce1_clk.hw, + [RPM_CE1_A_CLK] = &msmfalcon_ce1_a_clk.hw, + [RPM_DIV_CLK1] = &msmfalcon_div_clk1.hw, + [RPM_DIV_CLK1_AO] = &msmfalcon_div_clk1_ao.hw, + [RPM_LN_BB_CLK1] = &msmfalcon_ln_bb_clk1.hw, + [RPM_LN_BB_CLK1] = &msmfalcon_ln_bb_clk1_ao.hw, + [RPM_LN_BB_CLK1_PIN] = &msmfalcon_ln_bb_clk1_pin.hw, + [RPM_LN_BB_CLK1_PIN_AO] = &msmfalcon_ln_bb_clk1_pin_ao.hw, + [RPM_LN_BB_CLK2] = &msmfalcon_ln_bb_clk2.hw, + [RPM_LN_BB_CLK2_AO] = &msmfalcon_ln_bb_clk2_ao.hw, + [RPM_LN_BB_CLK2_PIN] = &msmfalcon_ln_bb_clk2_pin.hw, + [RPM_LN_BB_CLK2_PIN_AO] = &msmfalcon_ln_bb_clk2_pin_ao.hw, + [RPM_LN_BB_CLK3] = &msmfalcon_ln_bb_clk3.hw, + [RPM_LN_BB_CLK3_AO] = &msmfalcon_ln_bb_clk3_ao.hw, + [RPM_LN_BB_CLK3_PIN] = &msmfalcon_ln_bb_clk3_pin.hw, + [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, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msmfalcon = { + .clks = msmfalcon_clks, + .num_rpm_clks = RPM_CNOC_PERIPH_A_CLK, + .num_clks = ARRAY_SIZE(msmfalcon_clks), +}; + static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916}, { .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996}, + { .compatible = "qcom,rpmcc-msmfalcon", .data = &rpm_clk_msmfalcon}, { } }; MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); @@ -608,17 +683,23 @@ static int rpm_smd_clk_probe(struct platform_device *pdev) struct clk *clk; struct rpm_cc *rcc; struct clk_onecell_data *data; - int ret, is_8996 = 0; + int ret, is_8996 = 0, is_falcon = 0; size_t num_clks, i; struct clk_hw **hw_clks; const struct rpm_smd_clk_desc *desc; is_8996 = of_device_is_compatible(pdev->dev.of_node, "qcom,rpmcc-msm8996"); + is_falcon = of_device_is_compatible(pdev->dev.of_node, + "qcom,rpmcc-msmfalcon"); if (is_8996) { ret = clk_vote_bimc(&msm8996_bimc_clk.hw, INT_MAX); if (ret < 0) return ret; + } else if (is_falcon) { + ret = clk_vote_bimc(&msmfalcon_bimc_clk.hw, INT_MAX); + if (ret < 0) + return ret; } desc = of_device_get_match_data(&pdev->dev); @@ -676,6 +757,8 @@ 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) + clk_prepare_enable(msmfalcon_cxo_a.hw.clk); dev_info(&pdev->dev, "Registered RPM clocks\n"); diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index f4ae70ac9315..b40231dd8dd1 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -38,34 +38,138 @@ static const struct lpm_type_str lpm_types[] = { {SUSPEND, "suspend_enabled"}, }; +static DEFINE_PER_CPU(uint32_t *, max_residency); +static DEFINE_PER_CPU(uint32_t *, min_residency); static struct lpm_level_avail *cpu_level_available[NR_CPUS]; static struct platform_device *lpm_pdev; -static void *get_avail_val(struct kobject *kobj, struct kobj_attribute *attr) +static void *get_enabled_ptr(struct kobj_attribute *attr, + struct lpm_level_avail *avail) { void *arg = NULL; + + if (!strcmp(attr->attr.name, lpm_types[IDLE].str)) + arg = (void *) &avail->idle_enabled; + else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str)) + arg = (void *) &avail->suspend_enabled; + + return arg; +} + +static struct lpm_level_avail *get_avail_ptr(struct kobject *kobj, + struct kobj_attribute *attr) +{ struct lpm_level_avail *avail = NULL; - if (!strcmp(attr->attr.name, lpm_types[IDLE].str)) { + if (!strcmp(attr->attr.name, lpm_types[IDLE].str)) avail = container_of(attr, struct lpm_level_avail, idle_enabled_attr); - arg = (void *) &avail->idle_enabled; - } else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str)) { + else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str)) avail = container_of(attr, struct lpm_level_avail, suspend_enabled_attr); - arg = (void *) &avail->suspend_enabled; + + return avail; +} + +static void set_optimum_cpu_residency(struct lpm_cpu *cpu, int cpu_id, + bool probe_time) +{ + int i, j; + bool mode_avail; + uint32_t *maximum_residency = per_cpu(max_residency, cpu_id); + uint32_t *minimum_residency = per_cpu(min_residency, cpu_id); + + for (i = 0; i < cpu->nlevels; i++) { + struct power_params *pwr = &cpu->levels[i].pwr; + + mode_avail = probe_time || + lpm_cpu_mode_allow(cpu_id, i, true); + + if (!mode_avail) { + maximum_residency[i] = 0; + minimum_residency[i] = 0; + continue; + } + + maximum_residency[i] = ~0; + for (j = i + 1; j < cpu->nlevels; j++) { + mode_avail = probe_time || + lpm_cpu_mode_allow(cpu_id, j, true); + + if (mode_avail && + (maximum_residency[i] > pwr->residencies[j]) && + (pwr->residencies[j] != 0)) + maximum_residency[i] = pwr->residencies[j]; + } + + minimum_residency[i] = pwr->time_overhead_us; + for (j = i-1; j >= 0; j--) { + if (probe_time || lpm_cpu_mode_allow(cpu_id, j, true)) { + minimum_residency[i] = maximum_residency[j] + 1; + break; + } + } } +} - return arg; +static void set_optimum_cluster_residency(struct lpm_cluster *cluster, + bool probe_time) +{ + int i, j; + bool mode_avail; + + for (i = 0; i < cluster->nlevels; i++) { + struct power_params *pwr = &cluster->levels[i].pwr; + + mode_avail = probe_time || + lpm_cluster_mode_allow(cluster, i, + true); + + if (!mode_avail) { + pwr->max_residency = 0; + pwr->min_residency = 0; + continue; + } + + pwr->max_residency = ~0; + for (j = i+1; j < cluster->nlevels; j++) { + mode_avail = probe_time || + lpm_cluster_mode_allow(cluster, j, + true); + if (mode_avail && + (pwr->max_residency > pwr->residencies[j]) && + (pwr->residencies[j] != 0)) + pwr->max_residency = pwr->residencies[j]; + } + + pwr->min_residency = pwr->time_overhead_us; + for (j = i-1; j >= 0; j--) { + if (probe_time || + lpm_cluster_mode_allow(cluster, j, true)) { + pwr->min_residency = + cluster->levels[j].pwr.max_residency + 1; + break; + } + } + } } +uint32_t *get_per_cpu_max_residency(int cpu) +{ + return per_cpu(max_residency, cpu); +} + +uint32_t *get_per_cpu_min_residency(int cpu) +{ + return per_cpu(min_residency, cpu); +} ssize_t lpm_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int ret = 0; struct kernel_param kp; - kp.arg = get_avail_val(kobj, attr); + kp.arg = get_enabled_ptr(attr, get_avail_ptr(kobj, attr)); ret = param_get_bool(buf, &kp); if (ret > 0) { strlcat(buf, "\n", PAGE_SIZE); @@ -80,15 +184,25 @@ ssize_t lpm_enable_store(struct kobject *kobj, struct kobj_attribute *attr, { int ret = 0; struct kernel_param kp; + struct lpm_level_avail *avail; - kp.arg = get_avail_val(kobj, attr); + avail = get_avail_ptr(kobj, attr); + if (WARN_ON(!avail)) + return -EINVAL; + kp.arg = get_enabled_ptr(attr, avail); ret = param_set_bool(buf, &kp); + if (avail->cpu_node) + set_optimum_cpu_residency(avail->data, avail->idx, false); + else + set_optimum_cluster_residency(avail->data, false); + return ret ? ret : len; } static int create_lvl_avail_nodes(const char *name, - struct kobject *parent, struct lpm_level_avail *avail) + struct kobject *parent, struct lpm_level_avail *avail, + void *data, int index, bool cpu_node) { struct attribute_group *attr_group = NULL; struct attribute **attr = NULL; @@ -139,6 +253,9 @@ static int create_lvl_avail_nodes(const char *name, avail->idle_enabled = true; avail->suspend_enabled = true; avail->kobj = kobj; + avail->data = data; + avail->idx = index; + avail->cpu_node = cpu_node; return ret; @@ -181,7 +298,8 @@ static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent) for (i = 0; i < p->cpu->nlevels; i++) { ret = create_lvl_avail_nodes(p->cpu->levels[i].name, - cpu_kobj[cpu_idx], &level_list[i]); + cpu_kobj[cpu_idx], &level_list[i], + (void *)p->cpu, cpu, true); if (ret) goto release_kobj; } @@ -215,7 +333,8 @@ int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj) for (i = 0; i < p->nlevels; i++) { ret = create_lvl_avail_nodes(p->levels[i].level_name, - cluster_kobj, &p->levels[i].available); + cluster_kobj, &p->levels[i].available, + (void *)p, 0, false); if (ret) return ret; } @@ -421,6 +540,9 @@ static int parse_power_params(struct device_node *node, key = "qcom,time-overhead"; ret = of_property_read_u32(node, key, &pwr->time_overhead_us); + if (ret) + goto fail; + fail: if (ret) pr_err("%s(): %s Error reading %s\n", __func__, node->name, @@ -615,11 +737,31 @@ static int get_cpumask_for_node(struct device_node *node, struct cpumask *mask) return 0; } +static int calculate_residency(struct power_params *base_pwr, + struct power_params *next_pwr) +{ + int32_t residency = (int32_t)(next_pwr->energy_overhead - + base_pwr->energy_overhead) - + ((int32_t)(next_pwr->ss_power * next_pwr->time_overhead_us) + - (int32_t)(base_pwr->ss_power * base_pwr->time_overhead_us)); + + residency /= (int32_t)(base_pwr->ss_power - next_pwr->ss_power); + + if (residency < 0) { + __WARN_printf("%s: Incorrect power attributes for LPM\n", + __func__); + return next_pwr->time_overhead_us; + } + + return residency < next_pwr->time_overhead_us ? + next_pwr->time_overhead_us : residency; +} + static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c) { struct device_node *n; int ret = -ENOMEM; - int i; + int i, j; char *key; c->cpu = devm_kzalloc(&lpm_pdev->dev, sizeof(*c->cpu), GFP_KERNEL); @@ -676,6 +818,22 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c) else if (ret) goto failed; } + for (i = 0; i < c->cpu->nlevels; i++) { + for (j = 0; j < c->cpu->nlevels; j++) { + if (i >= j) { + c->cpu->levels[i].pwr.residencies[j] = 0; + continue; + } + + c->cpu->levels[i].pwr.residencies[j] = + calculate_residency(&c->cpu->levels[i].pwr, + &c->cpu->levels[j].pwr); + + pr_err("%s: idx %d %u\n", __func__, j, + c->cpu->levels[i].pwr.residencies[j]); + } + } + return 0; failed: for (i = 0; i < c->cpu->nlevels; i++) { @@ -732,6 +890,7 @@ struct lpm_cluster *parse_cluster(struct device_node *node, struct device_node *n; char *key; int ret = 0; + int i, j; c = devm_kzalloc(&lpm_pdev->dev, sizeof(*c), GFP_KERNEL); if (!c) @@ -789,6 +948,22 @@ struct lpm_cluster *parse_cluster(struct device_node *node, goto failed_parse_cluster; c->aff_level = 1; + + for_each_cpu(i, &c->child_cpus) { + per_cpu(max_residency, i) = devm_kzalloc( + &lpm_pdev->dev, + sizeof(uint32_t) * c->cpu->nlevels, + GFP_KERNEL); + if (!per_cpu(max_residency, i)) + return ERR_PTR(-ENOMEM); + per_cpu(min_residency, i) = devm_kzalloc( + &lpm_pdev->dev, + sizeof(uint32_t) * c->cpu->nlevels, + GFP_KERNEL); + if (!per_cpu(min_residency, i)) + return ERR_PTR(-ENOMEM); + set_optimum_cpu_residency(c->cpu, i, true); + } } } @@ -797,6 +972,17 @@ struct lpm_cluster *parse_cluster(struct device_node *node, else c->last_level = c->nlevels-1; + for (i = 0; i < c->nlevels; i++) { + for (j = 0; j < c->nlevels; j++) { + if (i >= j) { + c->levels[i].pwr.residencies[j] = 0; + continue; + } + c->levels[i].pwr.residencies[j] = calculate_residency( + &c->levels[i].pwr, &c->levels[j].pwr); + } + } + set_optimum_cluster_residency(c, true); return c; failed_parse_cluster: diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 4f880fdd1478..ced95aa2b649 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -1,4 +1,6 @@ /* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2006-2007 Adam Belay <abelay@novell.com> + * Copyright (C) 2009 Intel Corporation * * 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 @@ -83,9 +85,37 @@ struct lpm_debug { struct lpm_cluster *lpm_root_node; +#define MAXSAMPLES 5 + +static bool lpm_prediction; +module_param_named(lpm_prediction, + lpm_prediction, bool, S_IRUGO | S_IWUSR | S_IWGRP); + +static uint32_t ref_stddev = 100; +module_param_named( + ref_stddev, ref_stddev, uint, S_IRUGO | S_IWUSR | S_IWGRP +); + +static uint32_t tmr_add = 100; +module_param_named( + tmr_add, tmr_add, uint, S_IRUGO | S_IWUSR | S_IWGRP +); + +struct lpm_history { + uint32_t resi[MAXSAMPLES]; + int mode[MAXSAMPLES]; + int nsamp; + uint32_t hptr; + uint32_t hinvalid; + uint32_t htmr_wkup; +}; + +static DEFINE_PER_CPU(struct lpm_history, hist); + static DEFINE_PER_CPU(struct lpm_cluster*, cpu_cluster); static bool suspend_in_progress; static struct hrtimer lpm_hrtimer; +static struct hrtimer histtimer; static struct lpm_debug *lpm_debug; static phys_addr_t lpm_debug_phys; static const int num_dbg_elements = 0x100; @@ -327,10 +357,37 @@ static enum hrtimer_restart lpm_hrtimer_cb(struct hrtimer *h) return HRTIMER_NORESTART; } +static void histtimer_cancel(void) +{ + if (!lpm_prediction) + return; + + hrtimer_try_to_cancel(&histtimer); +} + +static enum hrtimer_restart histtimer_fn(struct hrtimer *h) +{ + int cpu = raw_smp_processor_id(); + struct lpm_history *history = &per_cpu(hist, cpu); + + history->hinvalid = 1; + return HRTIMER_NORESTART; +} + +static void histtimer_start(uint32_t time_us) +{ + uint64_t time_ns = time_us * NSEC_PER_USEC; + ktime_t hist_ktime = ns_to_ktime(time_ns); + + histtimer.function = histtimer_fn; + hrtimer_start(&histtimer, hist_ktime, HRTIMER_MODE_REL_PINNED); +} + static void msm_pm_set_timer(uint32_t modified_time_us) { u64 modified_time_ns = modified_time_us * NSEC_PER_USEC; ktime_t modified_ktime = ns_to_ktime(modified_time_ns); + lpm_hrtimer.function = lpm_hrtimer_cb; hrtimer_start(&lpm_hrtimer, modified_ktime, HRTIMER_MODE_REL_PINNED); } @@ -415,22 +472,160 @@ static int set_device_mode(struct lpm_cluster *cluster, int ndevice, return -EINVAL; } +static uint64_t lpm_cpuidle_predict(struct cpuidle_device *dev, + struct lpm_cpu *cpu, int *idx_restrict, + uint32_t *idx_restrict_time) +{ + int i, j, divisor; + uint64_t max, avg, stddev; + int64_t thresh = LLONG_MAX; + struct lpm_history *history = &per_cpu(hist, dev->cpu); + uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu); + + if (!lpm_prediction) + return 0; + + /* + * Samples are marked invalid when woken-up due to timer, + * so donot predict. + */ + if (history->hinvalid) { + history->hinvalid = 0; + history->htmr_wkup = 1; + return 0; + } + + /* + * Predict only when all the samples are collected. + */ + if (history->nsamp < MAXSAMPLES) + return 0; + + /* + * Check if the samples are not much deviated, if so use the + * average of those as predicted sleep time. Else if any + * specific mode has more premature exits return the index of + * that mode. + */ + +again: + max = avg = divisor = stddev = 0; + for (i = 0; i < MAXSAMPLES; i++) { + int64_t value = history->resi[i]; + + if (value <= thresh) { + avg += value; + divisor++; + if (value > max) + max = value; + } + } + do_div(avg, divisor); + + for (i = 0; i < MAXSAMPLES; i++) { + int64_t value = history->resi[i]; + + if (value <= thresh) { + int64_t diff = value - avg; + + stddev += diff * diff; + } + } + do_div(stddev, divisor); + stddev = int_sqrt(stddev); + + /* + * If the deviation is less, return the average, else + * ignore one maximum sample and retry + */ + if (((avg > stddev * 6) && (divisor >= (MAXSAMPLES - 1))) + || stddev <= ref_stddev) { + return avg; + } else if (divisor > (MAXSAMPLES - 1)) { + thresh = max - 1; + goto again; + } + + /* + * Find the number of premature exits for each of the mode, + * excluding clockgating mode, and they are more than fifty + * percent restrict that and deeper modes. + */ + if (history->htmr_wkup != 1) { + for (j = 1; j < cpu->nlevels; j++) { + uint32_t failed = 0; + uint64_t total = 0; + + for (i = 0; i < MAXSAMPLES; i++) { + if ((history->mode[i] == j) && + (history->resi[i] < min_residency[j])) { + failed++; + total += history->resi[i]; + } + } + if (failed > (MAXSAMPLES/2)) { + *idx_restrict = j; + do_div(total, failed); + *idx_restrict_time = total; + break; + } + } + } + return 0; +} + +static inline void invalidate_predict_history(struct cpuidle_device *dev) +{ + struct lpm_history *history = &per_cpu(hist, dev->cpu); + + if (!lpm_prediction) + return; + + if (history->hinvalid) { + history->hinvalid = 0; + history->htmr_wkup = 1; + } +} + +static void clear_predict_history(void) +{ + struct lpm_history *history; + int i; + unsigned int cpu; + + if (!lpm_prediction) + return; + + for_each_possible_cpu(cpu) { + history = &per_cpu(hist, cpu); + for (i = 0; i < MAXSAMPLES; i++) { + history->resi[i] = 0; + history->mode[i] = -1; + history->hptr = 0; + history->nsamp = 0; + } + } +} + +static void update_history(struct cpuidle_device *dev, int idx); + static int cpu_power_select(struct cpuidle_device *dev, struct lpm_cpu *cpu) { int best_level = -1; - uint32_t best_level_pwr = ~0U; uint32_t latency_us = pm_qos_request_for_cpu(PM_QOS_CPU_DMA_LATENCY, dev->cpu); uint32_t sleep_us = (uint32_t)(ktime_to_us(tick_nohz_get_sleep_length())); uint32_t modified_time_us = 0; uint32_t next_event_us = 0; - uint32_t pwr; - int i; + int i, idx_restrict; uint32_t lvl_latency_us = 0; - uint32_t lvl_overhead_us = 0; - uint32_t lvl_overhead_energy = 0; + uint64_t predicted = 0; + uint32_t htime = 0, idx_restrict_time = 0; + uint32_t next_wakeup_us = sleep_us; + uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu); + uint32_t *max_residency = get_per_cpu_max_residency(dev->cpu); if (!cpu) return -EINVAL; @@ -438,12 +633,13 @@ static int cpu_power_select(struct cpuidle_device *dev, if (sleep_disabled) return 0; + idx_restrict = cpu->nlevels + 1; + next_event_us = (uint32_t)(ktime_to_us(get_next_event_time(dev->cpu))); for (i = 0; i < cpu->nlevels; i++) { struct lpm_cpu_level *level = &cpu->levels[i]; struct power_params *pwr_params = &level->pwr; - uint32_t next_wakeup_us = sleep_us; enum msm_pm_sleep_mode mode = level->mode; bool allow; @@ -454,56 +650,76 @@ static int cpu_power_select(struct cpuidle_device *dev, lvl_latency_us = pwr_params->latency_us; - lvl_overhead_us = pwr_params->time_overhead_us; - - lvl_overhead_energy = pwr_params->energy_overhead; - if (latency_us < lvl_latency_us) - continue; + break; if (next_event_us) { if (next_event_us < lvl_latency_us) - continue; + break; if (((next_event_us - lvl_latency_us) < sleep_us) || (next_event_us < sleep_us)) next_wakeup_us = next_event_us - lvl_latency_us; } - if (next_wakeup_us <= pwr_params->time_overhead_us) - continue; - - /* - * If wakeup time greater than overhead by a factor of 1000 - * assume that core steady state power dominates the power - * equation - */ - if ((next_wakeup_us >> 10) > lvl_overhead_us) { - pwr = pwr_params->ss_power; - } else { - pwr = pwr_params->ss_power; - pwr -= (lvl_overhead_us * pwr_params->ss_power) / - next_wakeup_us; - pwr += pwr_params->energy_overhead / next_wakeup_us; + if (!i) { + /* + * If the next_wake_us itself is not sufficient for + * deeper low power modes than clock gating do not + * call prediction. + */ + if (next_wakeup_us > max_residency[i]) { + predicted = lpm_cpuidle_predict(dev, cpu, + &idx_restrict, &idx_restrict_time); + if (predicted < min_residency[i]) + predicted = 0; + } else + invalidate_predict_history(dev); } - if (best_level_pwr >= pwr) { - best_level = i; - best_level_pwr = pwr; - if (next_event_us && next_event_us < sleep_us && - (mode != MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)) - modified_time_us - = next_event_us - lvl_latency_us; - else - modified_time_us = 0; - } + if (i >= idx_restrict) + break; + + best_level = i; + + if (next_event_us && next_event_us < sleep_us && + (mode != MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT)) + modified_time_us + = next_event_us - lvl_latency_us; + else + modified_time_us = 0; + + if (predicted ? (predicted <= max_residency[i]) + : (next_wakeup_us <= max_residency[i])) + break; } if (modified_time_us) msm_pm_set_timer(modified_time_us); + /* + * Start timer to avoid staying in shallower mode forever + * incase of misprediciton + */ + if ((predicted || (idx_restrict != (cpu->nlevels + 1))) + && ((best_level >= 0) + && (best_level < (cpu->nlevels-1)))) { + htime = predicted + tmr_add; + if (htime == tmr_add) + htime = idx_restrict_time; + else if (htime > max_residency[best_level]) + htime = max_residency[best_level]; + + if ((next_wakeup_us > htime) && + ((next_wakeup_us - htime) > max_residency[best_level])) + histtimer_start(htime); + } + trace_cpu_power_select(best_level, sleep_us, latency_us, next_event_us); + trace_cpu_pred_select(idx_restrict_time ? 2 : (predicted ? 1 : 0), + predicted, htime); + return best_level; } @@ -554,8 +770,6 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle) { int best_level = -1; int i; - uint32_t best_level_pwr = ~0U; - uint32_t pwr; struct cpumask mask; uint32_t latency_us = ~0U; uint32_t sleep_us; @@ -596,10 +810,10 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle) continue; if (from_idle && latency_us < pwr_params->latency_us) - continue; + break; if (sleep_us < pwr_params->time_overhead_us) - continue; + break; if (suspend_in_progress && from_idle && level->notify_rpm) continue; @@ -607,19 +821,10 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle) if (level->notify_rpm && msm_rpm_waiting_for_ack()) continue; - if ((sleep_us >> 10) > pwr_params->time_overhead_us) { - pwr = pwr_params->ss_power; - } else { - pwr = pwr_params->ss_power; - pwr -= (pwr_params->time_overhead_us * - pwr_params->ss_power) / sleep_us; - pwr += pwr_params->energy_overhead / sleep_us; - } + best_level = i; - if (best_level_pwr >= pwr) { - best_level = i; - best_level_pwr = pwr; - } + if (sleep_us <= pwr_params->max_residency) + break; } return best_level; @@ -675,6 +880,7 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx, } us = us + 1; + clear_predict_history(); do_div(us, USEC_PER_SEC/SCLK_HZ); msm_mpm_enter_sleep(us, from_idle, cpumask); } @@ -1009,6 +1215,39 @@ static int lpm_cpuidle_select(struct cpuidle_driver *drv, return idx; } +static void update_history(struct cpuidle_device *dev, int idx) +{ + struct lpm_history *history = &per_cpu(hist, dev->cpu); + uint32_t tmr = 0; + + if (!lpm_prediction) + return; + + if (history->htmr_wkup) { + if (!history->hptr) + history->hptr = MAXSAMPLES-1; + else + history->hptr--; + + history->resi[history->hptr] += dev->last_residency; + history->htmr_wkup = 0; + tmr = 1; + } else + history->resi[history->hptr] = dev->last_residency; + + history->mode[history->hptr] = idx; + + trace_cpu_pred_hist(history->mode[history->hptr], + history->resi[history->hptr], history->hptr, tmr); + + if (history->nsamp < MAXSAMPLES) + history->nsamp++; + + (history->hptr)++; + if (history->hptr >= MAXSAMPLES) + history->hptr = 0; +} + static int lpm_cpuidle_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int idx) { @@ -1043,12 +1282,13 @@ exit: cluster_unprepare(cluster, cpumask, idx, true, end_time); cpu_unprepare(cluster, idx, true); sched_set_cpu_cstate(smp_processor_id(), 0, 0, 0); - - trace_cpu_idle_exit(idx, success); end_time = ktime_to_ns(ktime_get()) - start_time; - dev->last_residency = do_div(end_time, 1000); + do_div(end_time, 1000); + dev->last_residency = end_time; + update_history(dev, idx); + trace_cpu_idle_exit(idx, success); local_irq_enable(); - + histtimer_cancel(); return idx; } @@ -1320,6 +1560,7 @@ static int lpm_probe(struct platform_device *pdev) */ suspend_set_ops(&lpm_suspend_ops); hrtimer_init(&lpm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_init(&histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ret = remote_spin_lock_init(&scm_handoff_lock, SCM_HANDOFF_LOCK_ID); if (ret) { diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h index 8e05336be21a..63fe0a0fbc08 100644 --- a/drivers/cpuidle/lpm-levels.h +++ b/drivers/cpuidle/lpm-levels.h @@ -27,6 +27,9 @@ struct power_params { uint32_t ss_power; /* Steady state power */ uint32_t energy_overhead; /* Enter + exit over head */ uint32_t time_overhead_us; /* Enter + exit overhead */ + uint32_t residencies[NR_LPM_LEVELS]; + uint32_t min_residency; + uint32_t max_residency; }; struct lpm_cpu_level { @@ -55,6 +58,9 @@ struct lpm_level_avail { struct kobject *kobj; struct kobj_attribute idle_enabled_attr; struct kobj_attribute suspend_enabled_attr; + void *data; + int idx; + bool cpu_node; }; struct lpm_cluster_level { @@ -119,7 +125,8 @@ bool lpm_cpu_mode_allow(unsigned int cpu, unsigned int mode, bool from_idle); bool lpm_cluster_mode_allow(struct lpm_cluster *cluster, unsigned int mode, bool from_idle); - +uint32_t *get_per_cpu_max_residency(int cpu); +uint32_t *get_per_cpu_min_residency(int cpu); extern struct lpm_cluster *lpm_root_node; #ifdef CONFIG_SMP diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h index a3b25b3d8dd1..3615be45b6d9 100644 --- a/drivers/gpu/msm/adreno-gpulist.h +++ b/drivers/gpu/msm/adreno-gpulist.h @@ -244,6 +244,28 @@ static const struct adreno_gpu_core adreno_gpulist[] = { .core = 5, .major = 4, .minor = 0, + .patchid = 0, + .features = ADRENO_PREEMPTION | ADRENO_64BIT | + ADRENO_CONTENT_PROTECTION | + ADRENO_GPMU | ADRENO_SPTP_PC, + .pm4fw_name = "a530_pm4.fw", + .pfpfw_name = "a530_pfp.fw", + .zap_name = "a540_zap", + .gpudev = &adreno_a5xx_gpudev, + .gmem_size = SZ_1M, + .num_protected_regs = 0x20, + .busy_mask = 0xFFFFFFFE, + .gpmufw_name = "a540_gpmu.fw2", + .gpmu_major = 3, + .gpmu_minor = 0, + .gpmu_tsens = 0x000C000D, + .max_power = 5448, + }, + { + .gpurev = ADRENO_REV_A540, + .core = 5, + .major = 4, + .minor = 0, .patchid = ANY_ID, .features = ADRENO_PREEMPTION | ADRENO_64BIT | ADRENO_CONTENT_PROTECTION | diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 498386903936..1356835d0e93 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -2799,6 +2799,18 @@ static void adreno_regulator_disable_poll(struct kgsl_device *device) adreno_iommu_sync(device, false); } +static void adreno_gpu_model(struct kgsl_device *device, char *str, + size_t bufsz) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + snprintf(str, bufsz, "Adreno%d%d%dv%d", + ADRENO_CHIPID_CORE(adreno_dev->chipid), + ADRENO_CHIPID_MAJOR(adreno_dev->chipid), + ADRENO_CHIPID_MINOR(adreno_dev->chipid), + ADRENO_CHIPID_PATCH(adreno_dev->chipid) + 1); +} + static const struct kgsl_functable adreno_functable = { /* Mandatory functions */ .regread = adreno_regread, @@ -2835,7 +2847,8 @@ static const struct kgsl_functable adreno_functable = { .regulator_disable = adreno_regulator_disable, .pwrlevel_change_settings = adreno_pwrlevel_change_settings, .regulator_disable_poll = adreno_regulator_disable_poll, - .clk_set_options = adreno_clk_set_options + .clk_set_options = adreno_clk_set_options, + .gpu_model = adreno_gpu_model, }; static struct platform_driver adreno_platform_driver = { diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 286f7d63c8fe..d4858f3f818e 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -1015,6 +1015,12 @@ static inline int adreno_is_a540v1(struct adreno_device *adreno_dev) (ADRENO_CHIPID_PATCH(adreno_dev->chipid) == 0); } +static inline int adreno_is_a540v2(struct adreno_device *adreno_dev) +{ + return (ADRENO_GPUREV(adreno_dev) == ADRENO_REV_A540) && + (ADRENO_CHIPID_PATCH(adreno_dev->chipid) == 1); +} + /* * adreno_checkreg_off() - Checks the validity of a register enum * @adreno_dev: Pointer to adreno device diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 1782d1d54946..8ac058a7c5b0 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -2147,9 +2147,11 @@ static int _me_init_ucode_workarounds(struct adreno_device *adreno_dev) case ADRENO_REV_A540: /* * WFI after every direct-render 3D mode draw and - * WFI after every 2D Mode 3 draw. + * WFI after every 2D Mode 3 draw. This is needed + * only on a540v1. */ - return 0x0000000A; + if (adreno_is_a540v1(adreno_dev)) + return 0x0000000A; default: return 0x00000000; /* No ucode workarounds enabled */ } diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 25f5de6ce645..7ac84b777051 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -579,4 +579,19 @@ static inline void __user *to_user_ptr(uint64_t address) return (void __user *)(uintptr_t)address; } +static inline void kgsl_gpu_sysfs_add_link(struct kobject *dst, + struct kobject *src, const char *src_name, + const char *dst_name) +{ + struct kernfs_node *old; + + if (dst == NULL || src == NULL) + return; + + old = sysfs_get_dirent(src->sd, src_name); + if (IS_ERR_OR_NULL(old)) + return; + + kernfs_create_link(dst->sd, dst_name, old); +} #endif /* __KGSL_H */ diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 0df6dd8628a5..24511a4de6f1 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -167,6 +167,8 @@ struct kgsl_functable { void (*regulator_disable_poll)(struct kgsl_device *device); void (*clk_set_options)(struct kgsl_device *device, const char *name, struct clk *clk); + void (*gpu_model)(struct kgsl_device *device, char *str, + size_t bufsz); }; struct kgsl_ioctl { @@ -261,6 +263,7 @@ struct kgsl_device { struct kgsl_snapshot *snapshot; u32 snapshot_faultcount; /* Total number of faults since boot */ + bool force_panic; /* Force panic after snapshot dump */ struct kobject snapshot_kobj; struct kobject ppd_kobj; @@ -281,6 +284,7 @@ struct kgsl_device { /* Number of active contexts seen globally for this device */ int active_context_count; + struct kobject *gpu_sysfs_kobj; }; #define KGSL_MMU_DEVICE(_mmu) \ diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 1f2178848664..d71c6a63f2d3 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -21,6 +21,7 @@ #include <linux/delay.h> #include <linux/msm_adreno_devfreq.h> #include <linux/of_device.h> +#include <linux/thermal.h> #include "kgsl.h" #include "kgsl_pwrscale.h" @@ -590,22 +591,10 @@ static ssize_t kgsl_pwrctrl_max_pwrlevel_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", pwr->max_pwrlevel); } -static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr; - int ret; - unsigned int level = 0; - - if (device == NULL) - return 0; - - pwr = &device->pwrctrl; - - ret = kgsl_sysfs_store(buf, &level); - if (ret) - return ret; +static void kgsl_pwrctrl_min_pwrlevel_set(struct kgsl_device *device, + int level) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; mutex_lock(&device->mutex); if (level > pwr->num_pwrlevels - 2) @@ -621,6 +610,24 @@ static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev, kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel); mutex_unlock(&device->mutex); +} + +static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + int ret; + unsigned int level = 0; + + if (device == NULL) + return 0; + + ret = kgsl_sysfs_store(buf, &level); + if (ret) + return ret; + + kgsl_pwrctrl_min_pwrlevel_set(device, level); return count; } @@ -664,24 +671,13 @@ static int _get_nearest_pwrlevel(struct kgsl_pwrctrl *pwr, unsigned int clock) return -ERANGE; } -static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static void kgsl_pwrctrl_max_clock_set(struct kgsl_device *device, int val) { - struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr; - unsigned int val = 0; - int level, ret; - - if (device == NULL) - return 0; + int level; pwr = &device->pwrctrl; - ret = kgsl_sysfs_store(buf, &val); - if (ret) - return ret; - mutex_lock(&device->mutex); level = _get_nearest_pwrlevel(pwr, val); /* If the requested power level is not supported by hw, try cycling */ @@ -715,21 +711,37 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, if (pwr->sysfs_pwr_limit) kgsl_pwr_limits_set_freq(pwr->sysfs_pwr_limit, pwr->pwrlevels[level].gpu_freq); - return count; + return; err: mutex_unlock(&device->mutex); - return count; } -static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct kgsl_device *device = kgsl_device_from_dev(dev); + unsigned int val = 0; + int ret; + + if (device == NULL) + return 0; + + ret = kgsl_sysfs_store(buf, &val); + if (ret) + return ret; + + kgsl_pwrctrl_max_clock_set(device, val); + + return count; +} + +static unsigned int kgsl_pwrctrl_max_clock_get(struct kgsl_device *device) +{ struct kgsl_pwrctrl *pwr; unsigned int freq; + if (device == NULL) return 0; pwr = &device->pwrctrl; @@ -743,7 +755,17 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev, (TH_HZ - pwr->thermal_timeout) * (hfreq / TH_HZ); } - return snprintf(buf, PAGE_SIZE, "%d\n", freq); + return freq; +} + +static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + kgsl_pwrctrl_max_clock_get(device)); } static ssize_t kgsl_pwrctrl_gpuclk_store(struct device *dev, @@ -903,9 +925,14 @@ static ssize_t kgsl_pwrctrl_gpu_available_frequencies_show( if (device == NULL) return 0; pwr = &device->pwrctrl; - for (index = 0; index < pwr->num_pwrlevels - 1; index++) - num_chars += snprintf(buf + num_chars, PAGE_SIZE, "%d ", - pwr->pwrlevels[index].gpu_freq); + for (index = 0; index < pwr->num_pwrlevels - 1; index++) { + num_chars += scnprintf(buf + num_chars, + PAGE_SIZE - num_chars - 1, + "%d ", pwr->pwrlevels[index].gpu_freq); + /* One space for trailing null and another for the newline */ + if (num_chars >= PAGE_SIZE - 2) + break; + } buf[num_chars++] = '\n'; return num_chars; } @@ -1171,6 +1198,195 @@ static ssize_t kgsl_popp_show(struct device *dev, test_bit(POPP_ON, &device->pwrscale.popp_state)); } +static ssize_t kgsl_pwrctrl_gpu_model_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + char model_str[32] = {0}; + + if (device == NULL) + return 0; + + device->ftbl->gpu_model(device, model_str, sizeof(model_str)); + + return snprintf(buf, PAGE_SIZE, "%s\n", model_str); +} + +static ssize_t kgsl_pwrctrl_gpu_busy_percentage_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_clk_stats *stats; + unsigned int busy_percent = 0; + + if (device == NULL) + return 0; + stats = &device->pwrctrl.clk_stats; + + if (stats->total_old != 0) + busy_percent = (stats->busy_old * 100) / stats->total_old; + + ret = snprintf(buf, PAGE_SIZE, "%d %%\n", busy_percent); + + /* Reset the stats if GPU is OFF */ + if (!test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) { + stats->busy_old = 0; + stats->total_old = 0; + } + return ret; +} + +static ssize_t kgsl_pwrctrl_min_clock_mhz_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr; + + if (device == NULL) + return 0; + pwr = &device->pwrctrl; + + return snprintf(buf, PAGE_SIZE, "%d\n", + pwr->pwrlevels[pwr->min_pwrlevel].gpu_freq / 1000000); +} + +static ssize_t kgsl_pwrctrl_min_clock_mhz_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + int level, ret; + unsigned int freq; + struct kgsl_pwrctrl *pwr; + + if (device == NULL) + return 0; + + pwr = &device->pwrctrl; + + ret = kgsl_sysfs_store(buf, &freq); + if (ret) + return ret; + + freq *= 1000000; + level = _get_nearest_pwrlevel(pwr, freq); + + if (level >= 0) + kgsl_pwrctrl_min_pwrlevel_set(device, level); + + return count; +} + +static ssize_t kgsl_pwrctrl_max_clock_mhz_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + unsigned int freq; + + if (device == NULL) + return 0; + + freq = kgsl_pwrctrl_max_clock_get(device); + + return snprintf(buf, PAGE_SIZE, "%d\n", freq / 1000000); +} + +static ssize_t kgsl_pwrctrl_max_clock_mhz_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + unsigned int val = 0; + int ret; + + if (device == NULL) + return 0; + + ret = kgsl_sysfs_store(buf, &val); + if (ret) + return ret; + + val *= 1000000; + kgsl_pwrctrl_max_clock_set(device, val); + + return count; +} + +static ssize_t kgsl_pwrctrl_clock_mhz_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + + if (device == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "%ld\n", + kgsl_pwrctrl_active_freq(&device->pwrctrl) / 1000000); +} + +static ssize_t kgsl_pwrctrl_freq_table_mhz_show( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr; + int index, num_chars = 0; + + if (device == NULL) + return 0; + + pwr = &device->pwrctrl; + for (index = 0; index < pwr->num_pwrlevels - 1; index++) { + num_chars += scnprintf(buf + num_chars, + PAGE_SIZE - num_chars - 1, + "%d ", pwr->pwrlevels[index].gpu_freq / 1000000); + /* One space for trailing null and another for the newline */ + if (num_chars >= PAGE_SIZE - 2) + break; + } + + buf[num_chars++] = '\n'; + + return num_chars; +} + +static ssize_t kgsl_pwrctrl_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr; + int ret, id = 0, temperature = 0; + + if (device == NULL) + goto done; + + pwr = &device->pwrctrl; + + if (!pwr->tsens_name) + goto done; + + id = sensor_get_id((char *)pwr->tsens_name); + if (id < 0) + goto done; + + ret = sensor_get_temp(id, &temperature); + if (ret) + goto done; + + return snprintf(buf, PAGE_SIZE, "%d\n", + temperature); +done: + return 0; +} + static DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store); static DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show, @@ -1222,6 +1438,17 @@ static DEVICE_ATTR(popp, 0644, kgsl_popp_show, kgsl_popp_store); static DEVICE_ATTR(force_no_nap, 0644, kgsl_pwrctrl_force_no_nap_show, kgsl_pwrctrl_force_no_nap_store); +static DEVICE_ATTR(gpu_model, 0444, kgsl_pwrctrl_gpu_model_show, NULL); +static DEVICE_ATTR(gpu_busy_percentage, 0444, + kgsl_pwrctrl_gpu_busy_percentage_show, NULL); +static DEVICE_ATTR(min_clock_mhz, 0644, kgsl_pwrctrl_min_clock_mhz_show, + kgsl_pwrctrl_min_clock_mhz_store); +static DEVICE_ATTR(max_clock_mhz, 0644, kgsl_pwrctrl_max_clock_mhz_show, + kgsl_pwrctrl_max_clock_mhz_store); +static DEVICE_ATTR(clock_mhz, 0444, kgsl_pwrctrl_clock_mhz_show, NULL); +static DEVICE_ATTR(freq_table_mhz, 0444, + kgsl_pwrctrl_freq_table_mhz_show, NULL); +static DEVICE_ATTR(temp, 0444, kgsl_pwrctrl_temp_show, NULL); static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_gpuclk, @@ -1243,12 +1470,50 @@ static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_bus_split, &dev_attr_default_pwrlevel, &dev_attr_popp, + &dev_attr_gpu_model, + &dev_attr_gpu_busy_percentage, + &dev_attr_min_clock_mhz, + &dev_attr_max_clock_mhz, + &dev_attr_clock_mhz, + &dev_attr_freq_table_mhz, + &dev_attr_temp, NULL }; +struct sysfs_link { + const char *src; + const char *dst; +}; + +static struct sysfs_link link_names[] = { + { "gpu_model", "gpu_model",}, + { "gpu_busy_percentage", "gpu_busy",}, + { "min_clock_mhz", "gpu_min_clock",}, + { "max_clock_mhz", "gpu_max_clock",}, + { "clock_mhz", "gpu_clock",}, + { "freq_table_mhz", "gpu_freq_table",}, + { "temp", "gpu_tmu",}, +}; + int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device) { - return kgsl_create_device_sysfs_files(device->dev, pwrctrl_attr_list); + int i, ret; + + ret = kgsl_create_device_sysfs_files(device->dev, pwrctrl_attr_list); + if (ret) + return ret; + + device->gpu_sysfs_kobj = kobject_create_and_add("gpu", kernel_kobj); + if (IS_ERR_OR_NULL(device->gpu_sysfs_kobj)) + return (device->gpu_sysfs_kobj == NULL) ? + -ENOMEM : PTR_ERR(device->gpu_sysfs_kobj); + + for (i = 0; i < ARRAY_SIZE(link_names); i++) + kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj, + &device->dev->kobj, link_names[i].src, + link_names[i].dst); + + return 0; } void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device) @@ -1860,6 +2125,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) kgsl_pwrctrl_vbif_init(); + /* temperature sensor name */ + of_property_read_string(pdev->dev.of_node, "qcom,tsens-name", + &pwr->tsens_name); + return result; } @@ -1913,20 +2182,38 @@ void kgsl_idle_check(struct work_struct *work) { struct kgsl_device *device = container_of(work, struct kgsl_device, idle_check_ws); + int ret = 0; + unsigned int requested_state; + WARN_ON(device == NULL); if (device == NULL) return; mutex_lock(&device->mutex); + requested_state = device->requested_state; + if (device->state == KGSL_STATE_ACTIVE || device->state == KGSL_STATE_NAP) { - if (!atomic_read(&device->active_cnt)) - kgsl_pwrctrl_change_state(device, + if (!atomic_read(&device->active_cnt)) { + ret = kgsl_pwrctrl_change_state(device, device->requested_state); + if (ret == -EBUSY) { + /* + * If the GPU is currently busy, restore + * the requested state and reschedule + * idle work. + */ + kgsl_pwrctrl_request_state(device, + requested_state); + kgsl_schedule_work(&device->idle_check_ws); + } + } + + if (!ret) + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); - kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); if (device->state == KGSL_STATE_ACTIVE) mod_timer(&device->idle_timer, jiffies + diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index ae21a274fada..2de42d87bcbe 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -152,6 +152,7 @@ struct kgsl_regulator { * @sysfs_pwr_limit - pointer to the sysfs limits node * isense_clk_indx - index of isense clock, 0 if no isense * isense_clk_on_level - isense clock rate is XO rate below this level. + * tsens_name - pointer to temperature sensor name of GPU temperature sensor */ struct kgsl_pwrctrl { @@ -204,6 +205,7 @@ struct kgsl_pwrctrl { struct kgsl_pwr_limit *sysfs_pwr_limit; unsigned int gpu_bimc_int_clk_freq; bool gpu_bimc_interface_enabled; + const char *tsens_name; }; int kgsl_pwrctrl_init(struct kgsl_device *device); diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index d90aec42f30a..01d3b74c16fd 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -910,6 +910,14 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor) pwrscale->history[i].type = i; } + /* Add links to the devfreq sysfs nodes */ + kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj, + &pwrscale->devfreqptr->dev.kobj, "governor", + "gpu_governor"); + kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj, + &pwrscale->devfreqptr->dev.kobj, + "available_governors", "gpu_available_governor"); + return 0; } EXPORT_SYMBOL(kgsl_pwrscale_init); diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c index 2c9f17f9e7a4..a2e4a909062f 100644 --- a/drivers/gpu/msm/kgsl_snapshot.c +++ b/drivers/gpu/msm/kgsl_snapshot.c @@ -810,6 +810,29 @@ static ssize_t faultcount_store(struct kgsl_device *device, const char *buf, return count; } +/* Show the force_panic request status */ +static ssize_t force_panic_show(struct kgsl_device *device, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", device->force_panic); +} + +/* Store the panic request value to force_panic */ +static ssize_t force_panic_store(struct kgsl_device *device, const char *buf, + size_t count) +{ + unsigned int val = 0; + int ret; + + if (device && count > 0) + device->force_panic = 0; + + ret = kgsl_sysfs_store(buf, &val); + + if (!ret && device) + device->force_panic = (bool)val; + + return (ssize_t) ret < 0 ? ret : count; +} /* Show the timestamp of the last collected snapshot */ static ssize_t timestamp_show(struct kgsl_device *device, char *buf) { @@ -835,6 +858,7 @@ struct kgsl_snapshot_attribute attr_##_name = { \ static SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL); static SNAPSHOT_ATTR(faultcount, 0644, faultcount_show, faultcount_store); +static SNAPSHOT_ATTR(force_panic, 0644, force_panic_show, force_panic_store); static ssize_t snapshot_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buf) @@ -914,6 +938,7 @@ int kgsl_device_snapshot_init(struct kgsl_device *device) device->snapshot = NULL; device->snapshot_faultcount = 0; + device->force_panic = 0; ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot, &device->dev->kobj, "snapshot"); @@ -929,7 +954,11 @@ int kgsl_device_snapshot_init(struct kgsl_device *device) goto done; ret = sysfs_create_file(&device->snapshot_kobj, &attr_faultcount.attr); + if (ret) + goto done; + ret = sysfs_create_file(&device->snapshot_kobj, + &attr_force_panic.attr); done: return ret; } @@ -954,6 +983,7 @@ void kgsl_device_snapshot_close(struct kgsl_device *device) device->snapshot_memory.ptr = NULL; device->snapshot_memory.size = 0; device->snapshot_faultcount = 0; + device->force_panic = 0; } EXPORT_SYMBOL(kgsl_device_snapshot_close); @@ -1032,6 +1062,7 @@ void kgsl_snapshot_save_frozen_objs(struct work_struct *work) { struct kgsl_snapshot *snapshot = container_of(work, struct kgsl_snapshot, work); + struct kgsl_device *device = kgsl_get_device(KGSL_DEVICE_3D0); struct kgsl_snapshot_object *obj, *tmp; size_t size = 0; void *ptr; @@ -1073,12 +1104,15 @@ done: snapshot->process = NULL; if (snapshot->ib1base && !snapshot->ib1dumped) - pr_warn("kgsl: snapshot: Active IB1:%016llx not dumped\n", + KGSL_DRV_ERR(device, + "snapshot: Active IB1:%016llx not dumped\n", snapshot->ib1base); else if (snapshot->ib2base && !snapshot->ib2dumped) - pr_warn("kgsl: snapshot: Active IB2:%016llx not dumped\n", + KGSL_DRV_ERR(device, + "snapshot: Active IB2:%016llx not dumped\n", snapshot->ib2base); complete_all(&snapshot->dump_gate); + BUG_ON(device->force_panic); return; } diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c index 787a9e5a15c8..d8ef5f59edcb 100644 --- a/drivers/hwmon/qpnp-adc-common.c +++ b/drivers/hwmon/qpnp-adc-common.c @@ -39,6 +39,8 @@ #define PMI_CHG_SCALE_2 391750000000 #define QPNP_VADC_HC_VREF_CODE 0x4000 #define QPNP_VADC_HC_VDD_REFERENCE_MV 1875 +/* Clamp negative ADC code to 0 */ +#define QPNP_VADC_HC_MAX_CODE 0x7FFF /* Units for temperature below (on x axis) is in 0.1DegC as required by the battery driver. Note the resolution used @@ -752,6 +754,8 @@ int32_t qpnp_adc_scale_pmic_therm(struct qpnp_vadc_chip *vadc, if (adc_properties->adc_hc) { /* (ADC code * vref_vadc (1.875V)) / 0x4000 */ + if (adc_code > QPNP_VADC_HC_MAX_CODE) + adc_code = 0; pmic_voltage = (int64_t) adc_code; pmic_voltage *= (int64_t) (adc_properties->adc_vdd_reference * 1000); @@ -862,6 +866,8 @@ int32_t qpnp_adc_tdkntcg_therm(struct qpnp_vadc_chip *chip, if (adc_properties->adc_hc) { /* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */ + if (adc_code > QPNP_VADC_HC_MAX_CODE) + adc_code = 0; xo_thm_voltage = (int64_t) adc_code; xo_thm_voltage *= (int64_t) (adc_properties->adc_vdd_reference * 1000); @@ -1059,6 +1065,8 @@ int32_t qpnp_adc_scale_therm_pu2(struct qpnp_vadc_chip *chip, if (adc_properties->adc_hc) { /* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */ + if (adc_code > QPNP_VADC_HC_MAX_CODE) + adc_code = 0; therm_voltage = (int64_t) adc_code; therm_voltage *= (int64_t) (adc_properties->adc_vdd_reference * 1000); @@ -1094,6 +1102,8 @@ int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *chip, if (adc_properties->adc_hc) { /* (ADC code * vref_vadc (1.875V)) / 0x4000 */ + if (reg > QPNP_VADC_HC_MAX_CODE) + reg = 0; adc_voltage = (int64_t) reg; adc_voltage *= QPNP_VADC_HC_VDD_REFERENCE_MV; adc_voltage = div64_s64(adc_voltage, @@ -1228,6 +1238,8 @@ int32_t qpnp_adc_scale_default(struct qpnp_vadc_chip *vadc, if (adc_properties->adc_hc) { /* (ADC code * vref_vadc (1.875V)) / 0x4000 */ + if (adc_code > QPNP_VADC_HC_MAX_CODE) + adc_code = 0; scale_voltage = (int64_t) adc_code; scale_voltage *= (adc_properties->adc_vdd_reference * 1000); scale_voltage = div64_s64(scale_voltage, diff --git a/drivers/hwtracing/coresight/coresight-csr.c b/drivers/hwtracing/coresight/coresight-csr.c index dfb2922b6f33..3c18d686091a 100644 --- a/drivers/hwtracing/coresight/coresight-csr.c +++ b/drivers/hwtracing/coresight/coresight-csr.c @@ -191,8 +191,6 @@ static int csr_probe(struct platform_device *pdev) drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - /* Store the driver data pointer for use in exported functions */ - csrdrvdata = drvdata; drvdata->dev = &pdev->dev; platform_set_drvdata(pdev, drvdata); @@ -220,6 +218,8 @@ static int csr_probe(struct platform_device *pdev) if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); + /* Store the driver data pointer for use in exported functions */ + csrdrvdata = drvdata; dev_info(dev, "CSR initialized\n"); return 0; } diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index a234d61802ce..fb2f27299417 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -774,8 +774,6 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - /* Store the driver data pointer for use in exported functions */ - stmdrvdata = drvdata; drvdata->dev = &adev->dev; dev_set_drvdata(dev, drvdata); @@ -846,6 +844,8 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) if (boot_enable) coresight_enable(drvdata->csdev); + /* Store the driver data pointer for use in exported functions */ + stmdrvdata = drvdata; return 0; err: coresight_unregister(drvdata->csdev); diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index a0fcad198f62..bb44b6d82ccd 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -418,9 +418,9 @@ static int rradc_post_process_gpio(struct rradc_chip *chip, .sts = _sts, \ }, \ -#define RR_ADC_CHAN_TEMP(_dname, _scale, _lsb, _msb, _sts) \ +#define RR_ADC_CHAN_TEMP(_dname, _scale, mask, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_TEMP, \ - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \ + mask, \ _scale, _lsb, _msb, _sts) \ #define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb, _sts) \ @@ -443,9 +443,11 @@ static const struct rradc_channels rradc_chans[] = { FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB, FG_ADC_RR_BATT_ID_STS) RR_ADC_CHAN_TEMP("batt_therm", &rradc_post_process_therm, + BIT(IIO_CHAN_INFO_RAW), FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB, FG_ADC_RR_BATT_THERM_STS) RR_ADC_CHAN_TEMP("skin_temp", &rradc_post_process_therm, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB, FG_ADC_RR_AUX_THERM_STS) RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr, @@ -461,24 +463,30 @@ static const struct rradc_channels rradc_chans[] = { FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB, FG_ADC_RR_DC_IN_V_STS) RR_ADC_CHAN_TEMP("die_temp", &rradc_post_process_die_temp, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB, FG_ADC_RR_PMI_DIE_TEMP_STS) RR_ADC_CHAN_TEMP("chg_temp", &rradc_post_process_chg_temp, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB, FG_ADC_RR_CHARGER_TEMP_STS) RR_ADC_CHAN_VOLT("gpio", &rradc_post_process_gpio, FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB, FG_ADC_RR_GPIO_STS) RR_ADC_CHAN_TEMP("chg_temp_hot", &rradc_post_process_chg_temp_hot, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), FG_ADC_RR_CHARGER_HOT, FG_ADC_RR_CHARGER_HOT, FG_ADC_RR_CHARGER_TEMP_STS) RR_ADC_CHAN_TEMP("chg_temp_too_hot", &rradc_post_process_chg_temp_hot, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), FG_ADC_RR_CHARGER_TOO_HOT, FG_ADC_RR_CHARGER_TOO_HOT, FG_ADC_RR_CHARGER_TEMP_STS) RR_ADC_CHAN_TEMP("skin_temp_hot", &rradc_post_process_skin_temp_hot, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), FG_ADC_RR_SKIN_HOT, FG_ADC_RR_SKIN_HOT, FG_ADC_RR_AUX_THERM_STS) RR_ADC_CHAN_TEMP("skin_temp_too_hot", &rradc_post_process_skin_temp_hot, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), FG_ADC_RR_SKIN_TOO_HOT, FG_ADC_RR_SKIN_TOO_HOT, FG_ADC_RR_AUX_THERM_STS) }; diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c index 4c341ffb6094..9d61eb110e2f 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c @@ -347,7 +347,7 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval; - unsigned char tmpbuf[count + 1]; + unsigned char *tmpbuf; struct rmidev_data *dev_data = filp->private_data; if (IS_ERR(dev_data)) { @@ -361,6 +361,10 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf, if (count > (REG_ADDR_LIMIT - *f_pos)) count = REG_ADDR_LIMIT - *f_pos; + tmpbuf = kzalloc(count + 1, GFP_KERNEL); + if (!tmpbuf) + return -ENOMEM; + mutex_lock(&(dev_data->file_mutex)); retval = synaptics_rmi4_reg_read(rmidev->rmi4_data, @@ -377,7 +381,7 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf, clean_up: mutex_unlock(&(dev_data->file_mutex)); - + kfree(tmpbuf); return retval; } @@ -393,7 +397,7 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval; - unsigned char tmpbuf[count + 1]; + unsigned char *tmpbuf; struct rmidev_data *dev_data = filp->private_data; if (IS_ERR(dev_data)) { @@ -407,9 +411,14 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf, if (count > (REG_ADDR_LIMIT - *f_pos)) count = REG_ADDR_LIMIT - *f_pos; - if (copy_from_user(tmpbuf, buf, count)) - return -EFAULT; + tmpbuf = kzalloc(count + 1, GFP_KERNEL); + if (!tmpbuf) + return -ENOMEM; + if (copy_from_user(tmpbuf, buf, count)) { + kfree(tmpbuf); + return -EFAULT; + } mutex_lock(&(dev_data->file_mutex)); retval = synaptics_rmi4_reg_write(rmidev->rmi4_data, @@ -420,7 +429,7 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf, *f_pos += retval; mutex_unlock(&(dev_data->file_mutex)); - + kfree(tmpbuf); return retval; } diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index a892b73a4288..4036997f49c7 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -935,9 +935,14 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) return NULL; /* TCR */ - reg = (ARM_LPAE_TCR_SH_IS << 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); + if (cfg->iommu_dev && cfg->iommu_dev->archdata.dma_coherent) + reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) | + (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) | + (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) | + (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_ORGN0_SHIFT); switch (1 << data->pg_shift) { case SZ_4K: diff --git a/drivers/iommu/msm_dma_iommu_mapping.c b/drivers/iommu/msm_dma_iommu_mapping.c index 334f4e95c068..0a8728ce36dc 100644 --- a/drivers/iommu/msm_dma_iommu_mapping.c +++ b/drivers/iommu/msm_dma_iommu_mapping.c @@ -376,6 +376,7 @@ int msm_dma_unmap_all_for_dev(struct device *dev) return ret; } +EXPORT_SYMBOL(msm_dma_unmap_all_for_dev); /* * Only to be called by ION code when a buffer is freed diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index b434161f5599..9a469abc56ca 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -248,13 +248,31 @@ static enum cam_ahb_clk_vote msm_isp47_get_cam_clk_vote( return 0; } -static int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev, +int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev, struct msm_isp_ahb_clk_cfg *ahb_cfg) { int rc = 0; enum cam_ahb_clk_vote vote; + enum cam_ahb_clk_vote src_clk_vote; + struct msm_isp_clk_rates clk_rates; - vote = msm_isp47_get_cam_clk_vote(ahb_cfg->vote); + if (ahb_cfg) + vote = msm_isp47_get_cam_clk_vote(ahb_cfg->vote); + else + vote = CAM_AHB_SVS_VOTE; + + vfe_dev->hw_info->vfe_ops.platform_ops.get_clk_rates(vfe_dev, + &clk_rates); + if (vfe_dev->msm_isp_vfe_clk_rate <= clk_rates.svs_rate) + src_clk_vote = CAM_AHB_SVS_VOTE; + else if (vfe_dev->msm_isp_vfe_clk_rate <= clk_rates.nominal_rate) + src_clk_vote = CAM_AHB_NOMINAL_VOTE; + else + src_clk_vote = CAM_AHB_TURBO_VOTE; + + /* vote for higher of the user requested or src clock matched vote */ + if (vote < src_clk_vote) + vote = src_clk_vote; if (vote && vfe_dev->ahb_vote != vote) { rc = cam_config_ahb_clk(NULL, 0, @@ -320,6 +338,7 @@ enable_regulators_failed: void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) { enum cam_ahb_clk_client id; + unsigned long rate = 0; /* when closing node, disable all irq */ vfe_dev->irq0_mask = 0; @@ -345,6 +364,8 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE; + vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(vfe_dev, &rate); + vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks( vfe_dev, 0); vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); @@ -378,8 +399,8 @@ void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev) /* BUS_CFG */ msm_camera_io_w(0x00000101, vfe_dev->vfe_base + 0x84); /* IRQ_MASK/CLEAR */ - msm_vfe47_config_irq(vfe_dev, 0x810000E0, 0xFFFFFF7E, - MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 0x810000E0, 0xFFFFFF7E, MSM_ISP_IRQ_ENABLE); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58); @@ -387,8 +408,8 @@ void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev) void msm_vfe47_clear_status_reg(struct vfe_device *vfe_dev) { - msm_vfe47_config_irq(vfe_dev, 0x80000000, 0x0, - MSM_ISP_IRQ_SET); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 0x80000000, 0x0, MSM_ISP_IRQ_SET); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58); @@ -536,7 +557,8 @@ void msm_vfe47_read_irq_status(struct vfe_device *vfe_dev, vfe_dev->error_info.camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x4A4); /* mask off camif error after first occurrance */ - msm_vfe47_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, 0, + (1 << 0), MSM_ISP_IRQ_DISABLE); } if (*irq_status1 & (1 << 7)) @@ -775,7 +797,8 @@ void msm_vfe47_axi_cfg_comp_mask(struct vfe_device *vfe_dev, stream_composite_mask << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - msm_vfe47_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << (comp_mask_index + 25), 0, MSM_ISP_IRQ_ENABLE); } @@ -790,7 +813,8 @@ void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev, comp_mask &= ~(0x7F << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - msm_vfe47_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + (1 << (comp_mask_index + 25)), 0, MSM_ISP_IRQ_DISABLE); } @@ -799,7 +823,8 @@ void msm_vfe47_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, { int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - msm_vfe47_config_irq(vfe_dev, 1 << (stream_info->wm[vfe_idx][0] + 8), 0, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << (stream_info->wm[vfe_idx][0] + 8), 0, MSM_ISP_IRQ_ENABLE); } @@ -808,7 +833,8 @@ void msm_vfe47_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, { int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - msm_vfe47_config_irq(vfe_dev, (1 << (stream_info->wm[vfe_idx][0] + 8)), + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + (1 << (stream_info->wm[vfe_idx][0] + 8)), 0, MSM_ISP_IRQ_DISABLE); } @@ -1060,7 +1086,8 @@ void msm_vfe47_cfg_fetch_engine(struct vfe_device *vfe_dev, temp |= (1 << 1); msm_camera_io_w(temp, vfe_dev->vfe_base + 0x84); - msm_vfe47_config_irq(vfe_dev, (1 << 24), 0, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + (1 << 24), 0, MSM_ISP_IRQ_ENABLE); temp = fe_cfg->fetch_height - 1; @@ -1390,7 +1417,8 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev, msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x64); msm_camera_io_w(0x81, vfe_dev->vfe_base + 0x68); msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58); - msm_vfe47_config_irq(vfe_dev, 0x15, 0x81, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 0x15, 0x81, MSM_ISP_IRQ_ENABLE); if ((vfe_dev->hvx_cmd > HVX_DISABLE) && @@ -1422,7 +1450,7 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev, if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) update_state = DISABLE_CAMIF; /* turn off camif violation and error irqs */ - msm_vfe47_config_irq(vfe_dev, 0, 0x81, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, 0, 0x81, MSM_ISP_IRQ_DISABLE); val = msm_camera_io_r(vfe_dev->vfe_base + 0x464); /* disable danger signal */ @@ -1447,7 +1475,8 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev, msm_camera_io_w(0, vfe_dev->vfe_base + 0x64); msm_camera_io_w(1 << 0, vfe_dev->vfe_base + 0x68); msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + vfe_dev->irq0_mask, vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); } @@ -1728,7 +1757,8 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev, msm_camera_io_w(val, vfe_dev->vfe_vbif_base + VFE47_VBIF_CLK_OFFSET); /* Keep only halt and reset mask */ - msm_vfe47_config_irq(vfe_dev, (1 << 31), (1 << 8), + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + (1 << 31), (1 << 8), MSM_ISP_IRQ_SET); /*Clear IRQ Status0, only leave reset irq mask*/ @@ -1777,7 +1807,8 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev, void msm_vfe47_axi_restart(struct vfe_device *vfe_dev, uint32_t blocking, uint32_t enable_camif) { - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + vfe_dev->irq0_mask, vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w(0xFFFFFEFF, vfe_dev->vfe_base + 0x68); @@ -1884,7 +1915,8 @@ void msm_vfe47_stats_cfg_comp_mask( comp_mask_reg |= stats_mask << (request_comp_index * 16); atomic_set(stats_comp_mask, stats_mask | atomic_read(stats_comp_mask)); - msm_vfe47_config_irq(vfe_dev, 1 << (29 + request_comp_index), + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << (29 + request_comp_index), 0, MSM_ISP_IRQ_ENABLE); } else { if (!(atomic_read(stats_comp_mask) & stats_mask)) @@ -1893,7 +1925,8 @@ void msm_vfe47_stats_cfg_comp_mask( atomic_set(stats_comp_mask, ~stats_mask & atomic_read(stats_comp_mask)); comp_mask_reg &= ~(stats_mask << (request_comp_index * 16)); - msm_vfe47_config_irq(vfe_dev, 1 << (29 + request_comp_index), + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << (29 + request_comp_index), 0, MSM_ISP_IRQ_DISABLE); } @@ -1916,32 +1949,41 @@ void msm_vfe47_stats_cfg_wm_irq_mask( switch (STATS_IDX(stream_info->stream_handle[vfe_idx])) { case STATS_COMP_IDX_AEC_BG: - msm_vfe47_config_irq(vfe_dev, 1 << 15, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 15, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_HDR_BE: - msm_vfe47_config_irq(vfe_dev, 1 << 16, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 16, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BG: - msm_vfe47_config_irq(vfe_dev, 1 << 17, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 17, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BF: - msm_vfe47_config_irq(vfe_dev, 1 << 18, 1 << 26, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 18, 1 << 26, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_HDR_BHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 19, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 19, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_RS: - msm_vfe47_config_irq(vfe_dev, 1 << 20, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 20, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_CS: - msm_vfe47_config_irq(vfe_dev, 1 << 21, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 21, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_IHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 22, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 22, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 23, 0, MSM_ISP_IRQ_ENABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 23, 0, MSM_ISP_IRQ_ENABLE); break; default: pr_err("%s: Invalid stats idx %d\n", __func__, @@ -1958,32 +2000,41 @@ void msm_vfe47_stats_clear_wm_irq_mask( switch (STATS_IDX(stream_info->stream_handle[vfe_idx])) { case STATS_COMP_IDX_AEC_BG: - msm_vfe47_config_irq(vfe_dev, 1 << 15, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 15, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_HDR_BE: - msm_vfe47_config_irq(vfe_dev, 1 << 16, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 16, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BG: - msm_vfe47_config_irq(vfe_dev, 1 << 17, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 17, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BF: - msm_vfe47_config_irq(vfe_dev, 1 << 18, 1 << 26, + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 18, 1 << 26, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_HDR_BHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 19, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 19, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_RS: - msm_vfe47_config_irq(vfe_dev, 1 << 20, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 20, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_CS: - msm_vfe47_config_irq(vfe_dev, 1 << 21, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 21, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_IHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 22, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 22, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BHIST: - msm_vfe47_config_irq(vfe_dev, 1 << 23, 0, MSM_ISP_IRQ_DISABLE); + vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, + 1 << 23, 0, MSM_ISP_IRQ_DISABLE); break; default: pr_err("%s: Invalid stats idx %d\n", __func__, @@ -2336,6 +2387,9 @@ int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate) return rc; *rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate); vfe_dev->msm_isp_vfe_clk_rate = *rate; + + if (vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg) + vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg(vfe_dev, NULL); return 0; } diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h index 8581373b3b71..3955196d1deb 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h @@ -193,4 +193,6 @@ int msm_vfe47_update_bandwidth( void msm_vfe47_config_irq(struct vfe_device *vfe_dev, uint32_t irq0_mask, uint32_t irq1_mask, enum msm_isp_irq_operation oper); +int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev, + struct msm_isp_ahb_clk_cfg *ahb_cfg); #endif /* __MSM_ISP47_H__ */ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c index a792404c243c..49520bb44ad8 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c @@ -246,6 +246,8 @@ struct msm_vfe_hardware_info vfe48_hw_info = { .num_iommu_secure_ctx = 0, .vfe_clk_idx = VFE48_SRC_CLK_DTSI_IDX, .runtime_axi_update = 1, + .min_ib = 100000000, + .min_ab = 100000000, .vfe_ops = { .irq_ops = { .read_irq_status = msm_vfe47_read_irq_status, @@ -306,6 +308,7 @@ struct msm_vfe_hardware_info vfe48_hw_info = { .process_error_status = msm_vfe47_process_error_status, .is_module_cfg_lock_needed = msm_vfe47_is_module_cfg_lock_needed, + .ahb_clk_cfg = msm_isp47_ahb_clk_cfg, }, .stats_ops = { .get_stats_idx = msm_vfe47_get_stats_idx, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 39a0845a886f..7488f371545b 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -1023,31 +1023,32 @@ static void msm_isp_calculate_bandwidth( struct msm_vfe_axi_stream *stream_info) { int bpp = 0; + struct vfe_device *vfe_dev; struct msm_vfe_axi_shared_data *axi_data; int i; - if (stream_info->stream_src < RDI_INTF_0) { - for (i = 0; i < stream_info->num_isp; i++) { - axi_data = &stream_info->vfe_dev[i]->axi_data; + for (i = 0; i < stream_info->num_isp; i++) { + vfe_dev = stream_info->vfe_dev[i]; + axi_data = &vfe_dev->axi_data; + if (stream_info->stream_src < RDI_INTF_0) { stream_info->bandwidth[i] = - (axi_data->src_info[VFE_PIX_0].pixel_clock / + (vfe_dev->msm_isp_vfe_clk_rate / axi_data->src_info[VFE_PIX_0].width) * stream_info->max_width[i]; stream_info->bandwidth[i] = (unsigned long)stream_info->bandwidth[i] * stream_info->format_factor / ISP_Q2; - } - } else { - int rdi = SRC_TO_INTF(stream_info->stream_src); - bpp = msm_isp_get_bit_per_pixel(stream_info->output_format); - if (rdi < VFE_SRC_MAX) { - for (i = 0; i < stream_info->num_isp; i++) { - axi_data = &stream_info->vfe_dev[i]->axi_data; + } else { + int rdi = SRC_TO_INTF(stream_info->stream_src); + + bpp = msm_isp_get_bit_per_pixel( + stream_info->output_format); + if (rdi < VFE_SRC_MAX) { stream_info->bandwidth[i] = - (axi_data->src_info[rdi].pixel_clock / 8) * bpp; + (vfe_dev->msm_isp_vfe_clk_rate / 8) * bpp; + } else { + pr_err("%s: Invalid rdi interface\n", __func__); } - } else { - pr_err("%s: Invalid rdi interface\n", __func__); } } } @@ -1103,7 +1104,6 @@ int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg) uint32_t io_format = 0; struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd = arg; struct msm_vfe_axi_stream *stream_info; - unsigned long flags; if (stream_cfg_cmd->stream_src >= VFE_AXI_SRC_MAX) { pr_err("%s:%d invalid stream_src %d\n", __func__, __LINE__, @@ -1113,12 +1113,9 @@ int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg) stream_info = msm_isp_get_stream_common_data(vfe_dev, stream_cfg_cmd->stream_src); - spin_lock_irqsave(&stream_info->lock, flags); - rc = msm_isp_axi_create_stream(vfe_dev, &vfe_dev->axi_data, stream_cfg_cmd, stream_info); if (rc) { - spin_unlock_irqrestore(&stream_info->lock, flags); pr_err("%s: create stream failed\n", __func__); return rc; } @@ -1127,7 +1124,6 @@ int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg) vfe_dev, stream_info, stream_cfg_cmd); if (rc) { msm_isp_axi_destroy_stream(vfe_dev, stream_info); - spin_unlock_irqrestore(&stream_info->lock, flags); pr_err("%s: Request validation failed\n", __func__); return rc; } @@ -1235,7 +1231,6 @@ done: msm_isp_axi_free_wm(vfe_dev, stream_info); msm_isp_axi_destroy_stream(vfe_dev, stream_info); } - spin_unlock_irqrestore(&stream_info->lock, flags); return rc; } @@ -1246,7 +1241,6 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg) struct msm_vfe_axi_stream *stream_info; struct msm_vfe_axi_stream_cfg_cmd stream_cfg; int vfe_idx; - unsigned long flags; if (HANDLE_TO_IDX(stream_release_cmd->stream_handle) >= VFE_AXI_SRC_MAX) { @@ -1256,13 +1250,10 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg) stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(stream_release_cmd->stream_handle)); - spin_lock_irqsave(&stream_info->lock, flags); - vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev, stream_info); if (vfe_idx == -ENOTTY || stream_release_cmd->stream_handle != stream_info->stream_handle[vfe_idx]) { - spin_unlock_irqrestore(&stream_info->lock, flags); pr_err("%s: Invalid stream %p handle %x/%x vfe_idx %d vfe_dev %d num_isp %d\n", __func__, stream_info, stream_release_cmd->stream_handle, @@ -1276,9 +1267,7 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg) stream_cfg.cmd = STOP_STREAM; stream_cfg.num_streams = 1; stream_cfg.stream_handle[0] = stream_release_cmd->stream_handle; - spin_unlock_irqrestore(&stream_info->lock, flags); msm_isp_cfg_axi_stream(vfe_dev, (void *) &stream_cfg); - spin_lock_irqsave(&stream_info->lock, flags); } for (i = 0; i < stream_info->num_planes; i++) { @@ -1296,7 +1285,6 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg) msm_isp_axi_free_wm(vfe_dev, stream_info); msm_isp_axi_destroy_stream(vfe_dev, stream_info); - spin_unlock_irqrestore(&stream_info->lock, flags); return rc; } @@ -1411,6 +1399,7 @@ static void __msm_isp_axi_stream_update( switch (stream_info->state) { case UPDATING: stream_info->state = ACTIVE; + complete_all(&stream_info->active_comp); break; case STOP_PENDING: msm_isp_axi_stream_enable_cfg(stream_info); @@ -2268,13 +2257,14 @@ int msm_isp_axi_halt(struct vfe_device *vfe_dev, int msm_isp_axi_reset(struct vfe_device *vfe_dev, struct msm_vfe_axi_reset_cmd *reset_cmd) { - int rc = 0, i, k; + int rc = 0, i, k, j; struct msm_vfe_axi_stream *stream_info; struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; uint32_t bufq_handle = 0, bufq_id = 0; struct msm_isp_timestamp timestamp; unsigned long flags; struct vfe_device *update_vfes[MAX_VFE] = {0, 0}; + int vfe_idx; if (!reset_cmd) { pr_err("%s: NULL pointer reset cmd %pK\n", __func__, reset_cmd); @@ -2345,6 +2335,20 @@ int msm_isp_axi_reset(struct vfe_device *vfe_dev, ISP_EVENT_BUF_FATAL_ERROR); return rc; } + if (stream_info->num_planes > 1) { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_comp_mask(vfe_dev, stream_info); + } else { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_wm_irq_mask(vfe_dev, stream_info); + } + vfe_idx = msm_isp_get_vfe_idx_for_stream( + vfe_dev, stream_info); + for (j = 0; j < stream_info->num_planes; j++) + vfe_dev->hw_info->vfe_ops.axi_ops. + enable_wm( + vfe_dev->vfe_base, + stream_info->wm[vfe_idx][j], 1); axi_data->src_info[SRC_TO_INTF(stream_info-> stream_src)].frame_id = @@ -2731,6 +2735,7 @@ static void __msm_isp_stop_axi_streams(struct msm_vfe_axi_stream **streams, ×tamp); msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG); msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG); + stream_info->undelivered_request_cnt = 0; for (k = 0; k < stream_info->num_isp; k++) { vfe_dev = stream_info->vfe_dev[k]; if (stream_info->num_planes > 1) @@ -2879,6 +2884,8 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, msm_isp_get_timestamp(×tamp); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (stream_cfg_cmd->stream_handle[i] == 0) + continue; stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX) @@ -3018,6 +3025,8 @@ static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev_ioctl, return -EINVAL; for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (stream_cfg_cmd->stream_handle[i] == 0) + continue; stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); @@ -3044,12 +3053,37 @@ int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg) { int rc = 0, ret; struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg; + uint32_t stream_idx[MAX_NUM_STREAM]; int i; + int vfe_idx; + struct msm_vfe_axi_stream *stream_info; + + memset(stream_idx, 0, sizeof(stream_idx)); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= VFE_AXI_SRC_MAX) return -EINVAL; + stream_info = msm_isp_get_stream_common_data(vfe_dev, + HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); + vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev, + stream_info); + if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] != + stream_cfg_cmd->stream_handle[i]) { + pr_err("%s: Invalid stream handle %x vfe_idx %d expected %x\n", + __func__, stream_cfg_cmd->stream_handle[i], + vfe_idx, + (vfe_idx != -ENOTTY) ? + stream_info->stream_handle[vfe_idx] : 0); + return -EINVAL; + } + /* check for duplicate stream handle */ + if (stream_idx[stream_info->stream_src] == + stream_cfg_cmd->stream_handle[i]) + stream_cfg_cmd->stream_handle[i] = 0; + else + stream_idx[stream_info->stream_src] = + stream_cfg_cmd->stream_handle[i]; } if (stream_cfg_cmd->cmd == START_STREAM) { msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 1); @@ -3387,8 +3421,10 @@ static void msm_isp_remove_buf_queue(struct vfe_device *vfe_dev, if (stream_info->bufq_handle[bufq_id]) { stream_info->bufq_handle[bufq_id] = 0; - if (stream_info->state == ACTIVE) + if (stream_info->state == ACTIVE) { + init_completion(&stream_info->active_comp); stream_info->state = UPDATING; + } } spin_unlock_irqrestore(&stream_info->lock, flags); if (stream_info->state == UPDATING) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index f851e8c9289e..e226f7e40a07 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -460,7 +460,6 @@ int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg) struct msm_vfe_stats_stream_request_cmd *stream_req_cmd = arg; struct msm_vfe_stats_stream *stream_info = NULL; uint32_t stats_idx; - unsigned long flags; stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops. get_stats_idx(stream_req_cmd->stats_type); @@ -472,11 +471,8 @@ int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg) stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, stats_idx); - spin_lock_irqsave(&stream_info->lock, flags); - rc = msm_isp_stats_create_stream(vfe_dev, stream_req_cmd, stream_info); if (rc < 0) { - spin_unlock_irqrestore(&stream_info->lock, flags); pr_err("%s: create stream failed\n", __func__); return rc; } @@ -491,7 +487,6 @@ int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg) msm_isp_stats_cfg_stream_scratch(stream_info, VFE_PONG_FLAG); } - spin_unlock_irqrestore(&stream_info->lock, flags); return rc; } @@ -505,7 +500,6 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg) int vfe_idx; int i; int k; - unsigned long flags; if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { pr_err("%s Invalid stats index %d", __func__, stats_idx); @@ -513,12 +507,10 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg) } stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, stats_idx); - spin_lock_irqsave(&stream_info->lock, flags); vfe_idx = msm_isp_get_vfe_idx_for_stats_stream_user( vfe_dev, stream_info); if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] != stream_release_cmd->stream_handle) { - spin_unlock_irqrestore(&stream_info->lock, flags); pr_err("%s: Invalid stream handle %x, expected %x\n", __func__, stream_release_cmd->stream_handle, vfe_idx != -ENOTTY ? @@ -526,7 +518,6 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg) return -EINVAL; } if (stream_info->state == STATS_AVAILABLE) { - spin_unlock_irqrestore(&stream_info->lock, flags); pr_err("%s: stream already release\n", __func__); return rc; } @@ -537,9 +528,7 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg) stream_cfg_cmd.num_streams = 1; stream_cfg_cmd.stream_handle[0] = stream_release_cmd->stream_handle; - spin_unlock_irqrestore(&stream_info->lock, flags); msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd); - spin_lock_irqsave(&stream_info->lock, flags); } for (i = vfe_idx, k = vfe_idx + 1; k < stream_info->num_isp; k++, i++) { @@ -556,7 +545,6 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg) if (stream_info->num_isp == 0) stream_info->state = STATS_AVAILABLE; - spin_unlock_irqrestore(&stream_info->lock, flags); return 0; } @@ -904,6 +892,10 @@ int msm_isp_stats_reset(struct vfe_device *vfe_dev) ISP_EVENT_BUF_FATAL_ERROR); return rc; } + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_irq_mask( + vfe_dev, stream_info); + vfe_dev->hw_info->vfe_ops.stats_ops.enable_module( + vfe_dev, BIT(i), 1); } } @@ -960,7 +952,9 @@ static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info; uint32_t idx; int vfe_idx; + uint32_t stats_idx[MSM_ISP_STATS_MAX]; + memset(stats_idx, 0, sizeof(stats_idx)); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); @@ -980,6 +974,11 @@ static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev, stream_info->stream_handle[vfe_idx]); return -EINVAL; } + /* remove duplicate handles */ + if (stats_idx[idx] == stream_cfg_cmd->stream_handle[i]) + stream_cfg_cmd->stream_handle[i] = 0; + else + stats_idx[idx] = stream_cfg_cmd->stream_handle[i]; } return 0; } @@ -1083,6 +1082,8 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, num_stats_comp_mask = vfe_dev_ioctl->hw_info->stats_hw_info->num_stats_comp_mask; for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (stream_cfg_cmd->stream_handle[i] == 0) + continue; idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); stream_info = msm_isp_get_stats_stream_common_data( vfe_dev_ioctl, idx); @@ -1169,7 +1170,8 @@ static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev, vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask; for (i = 0; i < stream_cfg_cmd->num_streams; i++) { - + if (stream_cfg_cmd->stream_handle[i] == 0) + continue; idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); stream_info = msm_isp_get_stats_stream_common_data( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index a4eb80f31984..71c907f2b381 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -20,6 +20,7 @@ #include "msm_isp_stats_util.h" #include "msm_camera_io_util.h" #include "cam_smmu_api.h" +#include "msm_isp48.h" #define MAX_ISP_V4l2_EVENTS 100 static DEFINE_MUTEX(bandwidth_mgr_mutex); @@ -482,11 +483,18 @@ int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg) } pixel_clock = input_cfg->input_pix_clk; - rc = vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(vfe_dev, - &pixel_clock); - if (rc < 0) { - pr_err("%s: clock set rate failed\n", __func__); - return rc; + /* + * Only set rate to higher, do not lower higher + * rate needed by another input + */ + if (pixel_clock > vfe_dev->msm_isp_vfe_clk_rate) { + rc = vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate( + vfe_dev, + &pixel_clock); + if (rc < 0) { + pr_err("%s: clock set rate failed\n", __func__); + return rc; + } } return rc; } @@ -1739,6 +1747,9 @@ static void msm_isp_process_overflow_irq( if (overflow_mask) { struct msm_isp_event_data error_event; struct msm_vfe_axi_halt_cmd halt_cmd; + uint32_t val = 0; + int i; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; if (vfe_dev->reset_pending == 1) { pr_err("%s:%d failed: overflow %x during reset\n", @@ -1747,6 +1758,16 @@ static void msm_isp_process_overflow_irq( *irq_status1 &= ~overflow_mask; return; } + if (msm_vfe_is_vfe48(vfe_dev)) + val = msm_camera_io_r(vfe_dev->vfe_base + 0xC94); + pr_err("%s: vfe %d overflow mask %x, bus_error %x\n", + __func__, vfe_dev->pdev->id, overflow_mask, val); + for (i = 0; i < axi_data->hw_info->num_wm; i++) { + if (!axi_data->free_wm[i]) + continue; + pr_err("%s: wm %d assigned to stream handle %x\n", + __func__, i, axi_data->free_wm[i]); + } halt_cmd.overflow_detected = 1; halt_cmd.stop_camif = 1; diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 0b3e4e1fcf04..bf3973888573 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -101,11 +101,6 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl, i2c_tbl = a_ctrl->i2c_reg_tbl; for (i = 0; i < size; i++) { - /* check that the index into i2c_tbl cannot grow larger that - the allocated size of i2c_tbl */ - if ((a_ctrl->total_steps + 1) < (a_ctrl->i2c_tbl_index)) - break; - if (write_arr[i].reg_write_type == MSM_ACTUATOR_WRITE_DAC) { value = (next_lens_position << write_arr[i].data_shift) | @@ -119,6 +114,11 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl, i2c_byte2 = value & 0xFF; CDBG("byte1:0x%x, byte2:0x%x\n", i2c_byte1, i2c_byte2); + if (a_ctrl->i2c_tbl_index > + a_ctrl->total_steps) { + pr_err("failed:i2c table index out of bound\n"); + break; + } i2c_tbl[a_ctrl->i2c_tbl_index]. reg_addr = i2c_byte1; i2c_tbl[a_ctrl->i2c_tbl_index]. @@ -139,6 +139,10 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl, i2c_byte2 = (hw_dword & write_arr[i].hw_mask) >> write_arr[i].hw_shift; } + if (a_ctrl->i2c_tbl_index > a_ctrl->total_steps) { + pr_err("failed: i2c table index out of bound\n"); + break; + } CDBG("i2c_byte1:0x%x, i2c_byte2:0x%x\n", i2c_byte1, i2c_byte2); i2c_tbl[a_ctrl->i2c_tbl_index].reg_addr = i2c_byte1; i2c_tbl[a_ctrl->i2c_tbl_index].reg_data = i2c_byte2; diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c index 8f911d362477..a4ee5041bfff 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c @@ -276,6 +276,12 @@ int32_t msm_camera_cci_i2c_write_seq_table( client_addr_type = client->addr_type; client->addr_type = write_setting->addr_type; + if (reg_setting->reg_data_size > I2C_SEQ_REG_DATA_MAX) { + pr_err("%s: number of bytes %u exceeding the max supported %d\n", + __func__, reg_setting->reg_data_size, I2C_SEQ_REG_DATA_MAX); + return rc; + } + for (i = 0; i < write_setting->size; i++) { rc = msm_camera_cci_i2c_write_seq(client, reg_setting->reg_addr, reg_setting->reg_data, reg_setting->reg_data_size); diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c index 3b101798edac..7a0fb97061d5 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c @@ -290,6 +290,12 @@ int32_t msm_camera_qup_i2c_write_seq_table(struct msm_camera_i2c_client *client, client_addr_type = client->addr_type; client->addr_type = write_setting->addr_type; + if (reg_setting->reg_data_size > I2C_SEQ_REG_DATA_MAX) { + pr_err("%s: number of bytes %u exceeding the max supported %d\n", + __func__, reg_setting->reg_data_size, I2C_SEQ_REG_DATA_MAX); + return rc; + } + for (i = 0; i < write_setting->size; i++) { rc = msm_camera_qup_i2c_write_seq(client, reg_setting->reg_addr, reg_setting->reg_data, reg_setting->reg_data_size); diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c index 43aadffa2983..86e7837cc02a 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c @@ -1167,6 +1167,7 @@ static int32_t msm_sensor_driver_parse(struct msm_sensor_ctrl_t *s_ctrl) if (!s_ctrl->msm_sensor_mutex) { pr_err("failed: no memory msm_sensor_mutex %pK", s_ctrl->msm_sensor_mutex); + rc = -ENOMEM; goto FREE_SENSOR_I2C_CLIENT; } diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c index eed177ea5bab..60c4c81eddf2 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c @@ -355,13 +355,6 @@ int sde_mdp_get_plane_sizes(struct sde_mdp_format_params *fmt, u32 w, u32 h, chroma_samp = fmt->chroma_sample; - if (rotation) { - if (chroma_samp == SDE_MDP_CHROMA_H2V1) - chroma_samp = SDE_MDP_CHROMA_H1V2; - else if (chroma_samp == SDE_MDP_CHROMA_H1V2) - chroma_samp = SDE_MDP_CHROMA_H2V1; - } - sde_mdp_get_v_h_subsample_rate(chroma_samp, &v_subsample, &h_subsample); diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index fdf6e1b1c5d0..d9f47978a081 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -626,6 +626,11 @@ static u32 get_frame_size_compressed(int plane, return (max_mbs_per_frame * size_per_mb * 3/2)/2; } +static u32 get_frame_size_nv12_ubwc_10bit(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_BPP10_UBWC, width, height); +} + static u32 get_frame_size(struct msm_vidc_inst *inst, const struct msm_vidc_format *fmt, int fmt_type, int plane) @@ -712,6 +717,14 @@ struct msm_vidc_format vdec_formats[] = { .type = CAPTURE_PORT, }, { + .name = "UBWC YCbCr Semiplanar 4:2:0 10bit", + .description = "UBWC Y/CbCr 4:2:0 10bit", + .fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC, + .num_planes = 2, + .get_frame_size = get_frame_size_nv12_ubwc_10bit, + .type = CAPTURE_PORT, + }, + { .name = "Mpeg4", .description = "Mpeg4 compressed format", .fourcc = V4L2_PIX_FMT_MPEG4, diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index ce115c98a72f..d3e420f1b26b 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1583,6 +1583,32 @@ static const struct file_operations fops_fw_capabilities = { .llseek = seq_lseek, }; +/*---------FW version------------*/ +static int wil_fw_version_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + + if (wil->fw_version[0]) + seq_printf(s, "%s\n", wil->fw_version); + else + seq_puts(s, "N/A\n"); + + return 0; +} + +static int wil_fw_version_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_fw_version_debugfs_show, + inode->i_private); +} + +static const struct file_operations fops_fw_version = { + .open = wil_fw_version_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + /*----------------*/ static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil, struct dentry *dbg) @@ -1634,6 +1660,7 @@ static const struct { {"led_cfg", S_IRUGO | S_IWUSR, &fops_led_cfg}, {"led_blink_time", S_IRUGO | S_IWUSR, &fops_led_blink_time}, {"fw_capabilities", S_IRUGO, &fops_fw_capabilities}, + {"fw_version", S_IRUGO, &fops_fw_version}, }; static void wil6210_debugfs_init_files(struct wil6210_priv *wil, @@ -1674,7 +1701,6 @@ static void wil6210_debugfs_init_isr(struct wil6210_priv *wil, static const struct dbg_off dbg_wil_off[] = { WIL_FIELD(privacy, S_IRUGO, doff_u32), WIL_FIELD(status[0], S_IRUGO | S_IWUSR, doff_ulong), - WIL_FIELD(fw_version, S_IRUGO, doff_u32), WIL_FIELD(hw_version, S_IRUGO, doff_x32), WIL_FIELD(recovery_count, S_IRUGO, doff_u32), WIL_FIELD(ap_isolate, S_IRUGO, doff_u32), diff --git a/drivers/net/wireless/ath/wil6210/fw.h b/drivers/net/wireless/ath/wil6210/fw.h index c3191c61832c..2f2b910501ba 100644 --- a/drivers/net/wireless/ath/wil6210/fw.h +++ b/drivers/net/wireless/ath/wil6210/fw.h @@ -102,6 +102,9 @@ struct wil_fw_record_verify { /* type == wil_fw_verify */ /* file header * First record of every file */ +/* the FW version prefix in the comment */ +#define WIL_FW_VERSION_PREFIX "FW version: " +#define WIL_FW_VERSION_PREFIX_LEN (sizeof(WIL_FW_VERSION_PREFIX) - 1) struct wil_fw_record_file_header { __le32 signature ; /* Wilocity signature */ __le32 reserved; diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c index 3860238840ba..8f40eb301924 100644 --- a/drivers/net/wireless/ath/wil6210/fw_inc.c +++ b/drivers/net/wireless/ath/wil6210/fw_inc.c @@ -223,6 +223,13 @@ static int fw_handle_file_header(struct wil6210_priv *wil, const void *data, wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, d->comment, sizeof(d->comment), true); + if (!memcmp(d->comment, WIL_FW_VERSION_PREFIX, + WIL_FW_VERSION_PREFIX_LEN)) + memcpy(wil->fw_version, + d->comment + WIL_FW_VERSION_PREFIX_LEN, + min(sizeof(d->comment) - WIL_FW_VERSION_PREFIX_LEN, + sizeof(wil->fw_version) - 1)); + return 0; } diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 88dfb93d0f3d..a509841c3187 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -892,6 +892,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) WIL_FW2_NAME); wil_halt_cpu(wil); + memset(wil->fw_version, 0, sizeof(wil->fw_version)); /* Loading f/w from the file */ rc = wil_request_firmware(wil, WIL_FW_NAME, true); if (rc) diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 81a4c6a684f5..f4fca9d4eedf 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -222,6 +222,8 @@ int wil_if_add(struct wil6210_priv *wil) wil_dbg_misc(wil, "entered"); + strlcpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version)); + rc = wiphy_register(wiphy); if (rc < 0) { wil_err(wil, "failed to register wiphy, err %d\n", rc); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 1e0536adb6e7..ce33e919d321 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -17,6 +17,7 @@ #ifndef __WIL6210_H__ #define __WIL6210_H__ +#include <linux/etherdevice.h> #include <linux/netdevice.h> #include <linux/wireless.h> #include <net/cfg80211.h> @@ -579,7 +580,7 @@ struct wil6210_priv { struct wireless_dev *wdev; void __iomem *csr; DECLARE_BITMAP(status, wil_status_last); - u32 fw_version; + u8 fw_version[ETHTOOL_FWVERS_LEN]; u32 hw_version; const char *hw_name; DECLARE_BITMAP(hw_capabilities, hw_capability_last); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 0c5db9584159..6ec3ddc5b6f1 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -312,14 +312,14 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) struct wireless_dev *wdev = wil->wdev; struct wmi_ready_event *evt = d; - wil->fw_version = le32_to_cpu(evt->sw_version); wil->n_mids = evt->numof_additional_mids; - wil_info(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version, + wil_info(wil, "FW ver. %s(SW %d); MAC %pM; %d MID's\n", + wil->fw_version, le32_to_cpu(evt->sw_version), evt->mac, wil->n_mids); /* ignore MAC address, we already have it from the boot loader */ - snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), - "%d", wil->fw_version); + strlcpy(wdev->wiphy->fw_version, wil->fw_version, + sizeof(wdev->wiphy->fw_version)); wil_set_recovery_state(wil, fw_recovery_idle); set_bit(wil_status_fwready, wil->status); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c index 08ed47f3cacf..d14f8da15595 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c @@ -150,10 +150,16 @@ int ipa2_register_ipa_ready_cb(void (*ipa_ready_cb)(void *), void *user_data) { int ret; + if (!ipa_ctx) { + IPAERR("IPA ctx is null\n"); + return -ENXIO; + } + ret = ipa2_uc_state_check(); if (ret) { ipa_ctx->uc_ntn_ctx.uc_ready_cb = ipa_ready_cb; ipa_ctx->uc_ntn_ctx.priv = user_data; + return 0; } return -EEXIST; diff --git a/drivers/platform/msm/sps/bam.c b/drivers/platform/msm/sps/bam.c index 3aef2060ab52..c94536398dac 100644 --- a/drivers/platform/msm/sps/bam.c +++ b/drivers/platform/msm/sps/bam.c @@ -1162,7 +1162,7 @@ void bam_output_register_content(void *base, u32 ee) print_bam_test_bus_reg(base, 0); - print_bam_selected_reg(dev->base, BAM_MAX_EES); + print_bam_selected_reg(base, BAM_MAX_EES); num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES); @@ -1174,11 +1174,11 @@ void bam_output_register_content(void *base, u32 ee) if (!enhd_pipe || !pipe_attr) for (i = 0; i < num_pipes; i++) - print_bam_pipe_selected_reg(dev->base, i); + print_bam_pipe_selected_reg(base, i); else { for (i = 0; i < num_pipes; i++) { if (pipe_attr & (1UL << i)) - print_bam_pipe_selected_reg(dev->base, i); + print_bam_pipe_selected_reg(base, i); } } } diff --git a/drivers/power/qcom-charger/Makefile b/drivers/power/qcom-charger/Makefile index aae6084c3c10..0126d2d0a18e 100644 --- a/drivers/power/qcom-charger/Makefile +++ b/drivers/power/qcom-charger/Makefile @@ -6,6 +6,6 @@ obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o obj-$(CONFIG_BATTERY_BCL) += battery_current_limit.o -obj-$(CONFIG_QPNP_SMB2) += qpnp-smb2.o smb-lib.o pmic-voter.o -obj-$(CONFIG_SMB138X_CHARGER) += smb138x-charger.o smb-lib.o pmic-voter.o +obj-$(CONFIG_QPNP_SMB2) += qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o +obj-$(CONFIG_SMB138X_CHARGER) += smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o diff --git a/drivers/power/qcom-charger/battery_current_limit.c b/drivers/power/qcom-charger/battery_current_limit.c index 2bda5ce1a8c4..951d0544efa0 100644 --- a/drivers/power/qcom-charger/battery_current_limit.c +++ b/drivers/power/qcom-charger/battery_current_limit.c @@ -174,6 +174,9 @@ struct bcl_context { struct qpnp_adc_tm_btm_param btm_vph_adc_param; /* Low temp min freq limit requested by thermal */ uint32_t thermal_freq_limit; + /* state of charge notifier */ + struct notifier_block psy_nb; + struct work_struct soc_mitig_work; /* BCL Peripheral monitor parameters */ struct bcl_threshold ibat_high_thresh; @@ -204,8 +207,6 @@ static DEFINE_MUTEX(bcl_hotplug_mutex); static bool bcl_hotplug_enabled; static uint32_t battery_soc_val = 100; static uint32_t soc_low_threshold; -static struct power_supply *bcl_psy; -static struct power_supply_desc bcl_psy_des; static const char bcl_psy_name[] = "bcl"; static void bcl_handle_hotplug(struct work_struct *work) @@ -277,22 +278,34 @@ static void update_cpu_freq(void) trace_bcl_sw_mitigation_event("End Frequency Mitigation"); } -static void power_supply_callback(struct power_supply *psy) +static void soc_mitigate(struct work_struct *work) { - static struct power_supply *bms_psy; + if (bcl_hotplug_enabled) + queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work); + update_cpu_freq(); +} + +static int power_supply_callback(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct power_supply *psy = data; + static struct power_supply *batt_psy; union power_supply_propval ret = {0,}; int battery_percentage; enum bcl_threshold_state prev_soc_state; if (gbcl->bcl_mode != BCL_DEVICE_ENABLED) { pr_debug("BCL is not enabled\n"); - return; + return NOTIFY_OK; } - if (!bms_psy) - bms_psy = power_supply_get_by_name("bms"); - if (bms_psy) { - battery_percentage = power_supply_get_property(bms_psy, + if (strcmp(psy->desc->name, "battery")) + return NOTIFY_OK; + + if (!batt_psy) + batt_psy = power_supply_get_by_name("battery"); + if (batt_psy) { + battery_percentage = power_supply_get_property(batt_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); battery_percentage = ret.intval; battery_soc_val = battery_percentage; @@ -302,15 +315,14 @@ static void power_supply_callback(struct power_supply *psy) bcl_soc_state = (battery_soc_val <= soc_low_threshold) ? BCL_LOW_THRESHOLD : BCL_HIGH_THRESHOLD; if (bcl_soc_state == prev_soc_state) - return; + return NOTIFY_OK; trace_bcl_sw_mitigation_event( (bcl_soc_state == BCL_LOW_THRESHOLD) ? "trigger SoC mitigation" : "clear SoC mitigation"); - if (bcl_hotplug_enabled) - queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work); - update_cpu_freq(); + schedule_work(&gbcl->soc_mitig_work); } + return NOTIFY_OK; } static int bcl_get_battery_voltage(int *vbatt_mv) @@ -624,7 +636,6 @@ static void bcl_periph_vbat_notify(enum bcl_trip_type type, int trip_temp, static void bcl_periph_mode_set(enum bcl_device_mode mode) { int ret = 0; - struct power_supply_config bcl_psy_cfg = {}; if (mode == BCL_DEVICE_ENABLED) { /* @@ -632,15 +643,11 @@ static void bcl_periph_mode_set(enum bcl_device_mode mode) * power state changes. Make sure we read the current SoC * and mitigate. */ - power_supply_callback(bcl_psy); - bcl_psy_cfg.num_supplicants = 0; - bcl_psy_cfg.drv_data = gbcl; - - bcl_psy = power_supply_register(gbcl->dev, &bcl_psy_des, - &bcl_psy_cfg); - if (IS_ERR(bcl_psy)) { - pr_err("Unable to register bcl_psy rc = %ld\n", - PTR_ERR(bcl_psy)); + power_supply_callback(&gbcl->psy_nb, 1, gbcl); + ret = power_supply_reg_notifier(&gbcl->psy_nb); + if (ret < 0) { + pr_err("Unable to register soc notifier rc = %d\n", + ret); return; } ret = msm_bcl_set_threshold(BCL_PARAM_CURRENT, BCL_HIGH_TRIP, @@ -678,7 +685,7 @@ static void bcl_periph_mode_set(enum bcl_device_mode mode) } gbcl->btm_mode = BCL_VPH_MONITOR_MODE; } else { - power_supply_unregister(bcl_psy); + power_supply_unreg_notifier(&gbcl->psy_nb); ret = msm_bcl_disable(); if (ret) { pr_err("Error disabling BCL\n"); @@ -1627,19 +1634,6 @@ btm_probe_exit: return ret; } -static int bcl_battery_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) -{ - return 0; -} -static int bcl_battery_set_property(struct power_supply *psy, - enum power_supply_property prop, - const union power_supply_propval *val) -{ - return 0; -} - static uint32_t get_mask_from_core_handle(struct platform_device *pdev, const char *key) { @@ -1725,12 +1719,8 @@ static int bcl_probe(struct platform_device *pdev) pr_err("Cannot create bcl sysfs\n"); return ret; } - bcl_psy_des.name = bcl_psy_name; - bcl_psy_des.type = POWER_SUPPLY_TYPE_BMS; - bcl_psy_des.get_property = bcl_battery_get_property; - bcl_psy_des.set_property = bcl_battery_set_property; - bcl_psy_des.num_properties = 0; - bcl_psy_des.external_power_changed = power_supply_callback; + INIT_WORK(&bcl->soc_mitig_work, soc_mitigate); + bcl->psy_nb.notifier_call = power_supply_callback; bcl->bcl_hotplug_wq = alloc_workqueue("bcl_hotplug_wq", WQ_HIGHPRI, 0); if (!bcl->bcl_hotplug_wq) { pr_err("Workqueue alloc failed\n"); @@ -1773,6 +1763,7 @@ static int bcl_remove(struct platform_device *pdev) int cpu; /* De-register KTM handle */ + power_supply_unreg_notifier(&gbcl->psy_nb); if (gbcl->hotplug_handle) devmgr_unregister_mitigation_client(&pdev->dev, gbcl->hotplug_handle); diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h index 4feaaa0e0c4e..515f31a44ce7 100644 --- a/drivers/power/qcom-charger/fg-core.h +++ b/drivers/power/qcom-charger/fg-core.h @@ -54,6 +54,8 @@ CHARS_PER_ITEM) + 1) \ #define FG_SRAM_ADDRESS_MAX 255 +#define BUCKET_COUNT 8 +#define BUCKET_SOC_PCT (256 / BUCKET_COUNT) /* Debug flag definitions */ enum fg_debug_flag { @@ -186,6 +188,15 @@ struct fg_batt_props { int batt_id_kohm; }; +struct fg_cyc_ctr_data { + bool en; + bool started[BUCKET_COUNT]; + u16 count[BUCKET_COUNT]; + u8 last_soc[BUCKET_COUNT]; + int id; + struct mutex lock; +}; + struct fg_irq_info { const char *name; const irq_handler_t handler; @@ -209,6 +220,7 @@ struct fg_chip { char *batt_profile; struct fg_dt_props dt; struct fg_batt_props bp; + struct fg_cyc_ctr_data cyc_ctr; struct notifier_block nb; struct mutex bus_lock; struct mutex sram_rw_lock; @@ -216,6 +228,8 @@ struct fg_chip { u32 batt_info_base; u32 mem_if_base; int nom_cap_uah; + int status; + int prev_status; bool batt_id_avail; bool profile_loaded; bool battery_missing; @@ -223,6 +237,7 @@ struct fg_chip { struct completion soc_ready; struct delayed_work profile_load_work; struct work_struct status_change_work; + struct work_struct cycle_count_work; struct fg_alg_flag *alg_flags; }; diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c index a163061156ba..7739952f3254 100644 --- a/drivers/power/qcom-charger/qpnp-fg-gen3.c +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -57,6 +57,8 @@ #define PROFILE_LOAD_OFFSET 0 #define NOM_CAP_WORD 58 #define NOM_CAP_OFFSET 0 +#define CYCLE_COUNT_WORD 75 +#define CYCLE_COUNT_OFFSET 0 #define PROFILE_INTEGRITY_WORD 79 #define PROFILE_INTEGRITY_OFFSET 3 #define BATT_SOC_WORD 91 @@ -92,6 +94,8 @@ 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 void fg_encode_voltage(struct fg_sram_param *sp, enum fg_sram_param_id id, int val, u8 *buf); static void fg_encode_current(struct fg_sram_param *sp, @@ -114,7 +118,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_default), + fg_decode_batt_soc), PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, 1000, 0, NULL, fg_decode_value_16b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL, @@ -152,7 +156,7 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = { 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_default), + fg_decode_batt_soc), PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, 1000, 0, NULL, fg_decode_value_16b), PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL, @@ -260,171 +264,6 @@ module_param_named( sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR ); -/* Other functions HERE */ - -static int fg_awake_cb(struct votable *votable, void *data, int awake, - const char *client) -{ - struct fg_chip *chip = data; - - if (awake) - pm_stay_awake(chip->dev); - else - pm_relax(chip->dev); - - pr_debug("client: %s awake: %d\n", client, awake); - return 0; -} - -static bool is_charger_available(struct fg_chip *chip) -{ - if (!chip->batt_psy) - chip->batt_psy = power_supply_get_by_name("battery"); - - if (!chip->batt_psy) - return false; - - return true; -} - -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, }; - - if (!is_charger_available(chip)) { - fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); - return; - } - - power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, - &prop); - switch (prop.intval) { - case POWER_SUPPLY_STATUS_CHARGING: - fg_dbg(chip, FG_POWER_SUPPLY, "Charging\n"); - break; - case POWER_SUPPLY_STATUS_DISCHARGING: - fg_dbg(chip, FG_POWER_SUPPLY, "Discharging\n"); - break; - case POWER_SUPPLY_STATUS_FULL: - fg_dbg(chip, FG_POWER_SUPPLY, "Full\n"); - break; - default: - break; - } -} - -#define PROFILE_LEN 224 -#define PROFILE_COMP_LEN 32 -#define SOC_READY_WAIT_MS 2000 -static void profile_load_work(struct work_struct *work) -{ - struct fg_chip *chip = container_of(work, - struct fg_chip, - profile_load_work.work); - int rc; - u8 buf[PROFILE_COMP_LEN], val; - bool tried_again = false, profiles_same = false; - - if (!chip->batt_id_avail) { - pr_err("batt_id not available\n"); - return; - } - - rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, - PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("failed to read profile integrity rc=%d\n", rc); - return; - } - - vote(chip->awake_votable, PROFILE_LOAD, true, 0); - if (val == 0x01) { - fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n"); - rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, - buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("Error in reading battery profile, rc:%d\n", rc); - goto out; - } - profiles_same = memcmp(chip->batt_profile, buf, - PROFILE_COMP_LEN) == 0; - if (profiles_same) { - fg_dbg(chip, FG_STATUS, "Battery profile is same\n"); - goto done; - } - fg_dbg(chip, FG_STATUS, "profiles are different?\n"); - } - - fg_dbg(chip, FG_STATUS, "profile loading started\n"); - rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); - if (rc < 0) { - pr_err("Error in writing to %04x, rc=%d\n", - BATT_SOC_RESTART(chip), rc); - goto out; - } - - /* load battery profile */ - rc = fg_sram_write(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, - chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC); - if (rc < 0) { - pr_err("Error in writing battery profile, rc:%d\n", rc); - goto out; - } - - rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, - RESTART_GO_BIT); - if (rc < 0) { - pr_err("Error in writing to %04x, rc=%d\n", - BATT_SOC_RESTART(chip), rc); - goto out; - } - -wait: - rc = wait_for_completion_interruptible_timeout(&chip->soc_ready, - msecs_to_jiffies(SOC_READY_WAIT_MS)); - - /* If we were interrupted wait again one more time. */ - if (rc == -ERESTARTSYS && !tried_again) { - tried_again = true; - goto wait; - } else if (rc <= 0) { - pr_err("wait for soc_ready timed out rc=%d\n", rc); - goto out; - } - - fg_dbg(chip, FG_STATUS, "SOC is ready\n"); - - /* Set the profile integrity bit */ - val = 0x1; - rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD, - PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("failed to write profile integrity rc=%d\n", rc); - goto out; - } - - fg_dbg(chip, FG_STATUS, "profile loaded successfully"); -done: - rc = fg_sram_read(chip, NOM_CAP_WORD, NOM_CAP_OFFSET, buf, 2, - FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("Error in reading %04x[%d] rc=%d\n", NOM_CAP_WORD, - NOM_CAP_OFFSET, rc); - goto out; - } - - chip->nom_cap_uah = (int)(buf[0] | buf[1] << 8) * 1000; - chip->profile_loaded = true; -out: - vote(chip->awake_votable, PROFILE_LOAD, false, 0); - rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); - if (rc < 0) - pr_err("Error in writing to %04x, rc=%d\n", - BATT_SOC_RESTART(chip), rc); -} - /* All getters HERE */ static int fg_decode_value_16b(struct fg_sram_param *sp, @@ -436,9 +275,18 @@ static int fg_decode_value_16b(struct fg_sram_param *sp, return sp[id].value; } -static int fg_decode_default(struct fg_sram_param *sp, +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; } @@ -752,6 +600,7 @@ static int fg_get_batt_id(struct fg_chip *chip, int *val) return 0; } +#define PROFILE_LEN 224 static int fg_get_batt_profile(struct fg_chip *chip) { struct device_node *node = chip->dev->of_node; @@ -863,6 +712,316 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging, return 0; } +/* Other functions HERE */ + +static int fg_awake_cb(struct votable *votable, void *data, int awake, + const char *client) +{ + struct fg_chip *chip = data; + + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); + + pr_debug("client: %s awake: %d\n", client, awake); + return 0; +} + +static bool is_charger_available(struct fg_chip *chip) +{ + if (!chip->batt_psy) + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) + return false; + + return true; +} + +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, }; + + if (!is_charger_available(chip)) { + fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); + return; + } + + power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, + &prop); + chip->prev_status = chip->status; + chip->status = prop.intval; + + if (chip->cyc_ctr.en && chip->prev_status != chip->status) + schedule_work(&chip->cycle_count_work); + + switch (prop.intval) { + case POWER_SUPPLY_STATUS_CHARGING: + fg_dbg(chip, FG_POWER_SUPPLY, "Charging\n"); + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + fg_dbg(chip, FG_POWER_SUPPLY, "Discharging\n"); + break; + case POWER_SUPPLY_STATUS_FULL: + fg_dbg(chip, FG_POWER_SUPPLY, "Full\n"); + break; + default: + break; + } +} + +static void restore_cycle_counter(struct fg_chip *chip) +{ + int rc = 0, i; + u8 data[2]; + + mutex_lock(&chip->cyc_ctr.lock); + for (i = 0; i < BUCKET_COUNT; i++) { + rc = fg_sram_read(chip, CYCLE_COUNT_WORD + (i / 2), + CYCLE_COUNT_OFFSET + (i % 2) * 2, data, 2, + FG_IMA_DEFAULT); + if (rc < 0) + pr_err("failed to read bucket %d rc=%d\n", i, rc); + else + chip->cyc_ctr.count[i] = data[0] | data[1] << 8; + } + mutex_unlock(&chip->cyc_ctr.lock); +} + +static void clear_cycle_counter(struct fg_chip *chip) +{ + int rc = 0, i; + + if (!chip->cyc_ctr.en) + return; + + mutex_lock(&chip->cyc_ctr.lock); + memset(chip->cyc_ctr.count, 0, sizeof(chip->cyc_ctr.count)); + for (i = 0; i < BUCKET_COUNT; i++) { + chip->cyc_ctr.started[i] = false; + chip->cyc_ctr.last_soc[i] = 0; + } + rc = fg_sram_write(chip, CYCLE_COUNT_WORD, CYCLE_COUNT_OFFSET, + (u8 *)&chip->cyc_ctr.count, + sizeof(chip->cyc_ctr.count) / sizeof(u8 *), + FG_IMA_DEFAULT); + if (rc < 0) + pr_err("failed to clear cycle counter rc=%d\n", rc); + + mutex_unlock(&chip->cyc_ctr.lock); +} + +static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket) +{ + int rc = 0; + u16 cyc_count; + u8 data[2]; + + if (bucket < 0 || (bucket > BUCKET_COUNT - 1)) + return 0; + + cyc_count = chip->cyc_ctr.count[bucket]; + cyc_count++; + data[0] = cyc_count & 0xFF; + data[1] = cyc_count >> 8; + + rc = fg_sram_write(chip, CYCLE_COUNT_WORD + (bucket / 2), + CYCLE_COUNT_OFFSET + (bucket % 2) * 2, data, 2, + FG_IMA_DEFAULT); + if (rc < 0) + pr_err("failed to write BATT_CYCLE[%d] rc=%d\n", + bucket, rc); + else + chip->cyc_ctr.count[bucket] = cyc_count; + return rc; +} + +static void cycle_count_work(struct work_struct *work) +{ + int rc = 0, bucket, i, batt_soc; + struct fg_chip *chip = container_of(work, + struct fg_chip, + cycle_count_work); + + mutex_lock(&chip->cyc_ctr.lock); + rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &batt_soc); + if (rc < 0) { + pr_err("Failed to read battery soc rc: %d\n", rc); + goto out; + } + + if (chip->status == POWER_SUPPLY_STATUS_CHARGING) { + /* Find out which bucket the SOC falls in */ + bucket = batt_soc / BUCKET_SOC_PCT; + pr_debug("batt_soc: %d bucket: %d\n", batt_soc, bucket); + + /* + * If we've started counting for the previous bucket, + * then store the counter for that bucket if the + * counter for current bucket is getting started. + */ + if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] && + !chip->cyc_ctr.started[bucket]) { + rc = fg_inc_store_cycle_ctr(chip, bucket - 1); + if (rc < 0) { + pr_err("Error in storing cycle_ctr rc: %d\n", + rc); + goto out; + } else { + chip->cyc_ctr.started[bucket - 1] = false; + chip->cyc_ctr.last_soc[bucket - 1] = 0; + } + } + if (!chip->cyc_ctr.started[bucket]) { + chip->cyc_ctr.started[bucket] = true; + chip->cyc_ctr.last_soc[bucket] = batt_soc; + } + } else { + for (i = 0; i < BUCKET_COUNT; i++) { + if (chip->cyc_ctr.started[i] && + batt_soc > chip->cyc_ctr.last_soc[i]) { + rc = fg_inc_store_cycle_ctr(chip, i); + if (rc < 0) + pr_err("Error in storing cycle_ctr rc: %d\n", + rc); + chip->cyc_ctr.last_soc[i] = 0; + } + chip->cyc_ctr.started[i] = false; + } + } +out: + mutex_unlock(&chip->cyc_ctr.lock); +} + +static int fg_get_cycle_count(struct fg_chip *chip) +{ + int count; + + if (!chip->cyc_ctr.en) + return 0; + + if ((chip->cyc_ctr.id <= 0) || (chip->cyc_ctr.id > BUCKET_COUNT)) + return -EINVAL; + + mutex_lock(&chip->cyc_ctr.lock); + count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1]; + mutex_unlock(&chip->cyc_ctr.lock); + return count; +} + +#define PROFILE_COMP_LEN 32 +#define SOC_READY_WAIT_MS 2000 +static void profile_load_work(struct work_struct *work) +{ + struct fg_chip *chip = container_of(work, + struct fg_chip, + profile_load_work.work); + int rc; + u8 buf[PROFILE_COMP_LEN], val; + bool tried_again = false, profiles_same = false; + + if (!chip->batt_id_avail) { + pr_err("batt_id not available\n"); + return; + } + + rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, + PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to read profile integrity rc=%d\n", rc); + return; + } + + vote(chip->awake_votable, PROFILE_LOAD, true, 0); + if (val == 0x01) { + fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n"); + rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, + buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading battery profile, rc:%d\n", rc); + goto out; + } + profiles_same = memcmp(chip->batt_profile, buf, + PROFILE_COMP_LEN) == 0; + if (profiles_same) { + fg_dbg(chip, FG_STATUS, "Battery profile is same\n"); + goto done; + } + fg_dbg(chip, FG_STATUS, "profiles are different?\n"); + } + + clear_cycle_counter(chip); + fg_dbg(chip, FG_STATUS, "profile loading started\n"); + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); + if (rc < 0) { + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); + goto out; + } + + /* load battery profile */ + rc = fg_sram_write(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, + chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("Error in writing battery profile, rc:%d\n", rc); + goto out; + } + + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, + RESTART_GO_BIT); + if (rc < 0) { + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); + goto out; + } + +wait: + rc = wait_for_completion_interruptible_timeout(&chip->soc_ready, + msecs_to_jiffies(SOC_READY_WAIT_MS)); + + /* If we were interrupted wait again one more time. */ + if (rc == -ERESTARTSYS && !tried_again) { + tried_again = true; + goto wait; + } else if (rc <= 0) { + pr_err("wait for soc_ready timed out rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_STATUS, "SOC is ready\n"); + + /* Set the profile integrity bit */ + val = 0x1; + rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD, + PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to write profile integrity rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_STATUS, "profile loaded successfully"); +done: + rc = fg_sram_read(chip, NOM_CAP_WORD, NOM_CAP_OFFSET, buf, 2, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading %04x[%d] rc=%d\n", NOM_CAP_WORD, + NOM_CAP_OFFSET, rc); + goto out; + } + + chip->nom_cap_uah = (int)(buf[0] | buf[1] << 8) * 1000; + chip->profile_loaded = true; +out: + vote(chip->awake_votable, PROFILE_LOAD, false, 0); + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); + if (rc < 0) + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); +} + /* PSY CALLBACKS STAY HERE */ static int fg_psy_get_property(struct power_supply *psy, @@ -902,6 +1061,11 @@ static int fg_psy_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: pval->intval = chip->bp.float_volt_uv; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + pval->intval = fg_get_cycle_count(chip); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: + pval->intval = chip->cyc_ctr.id; break; default: break; @@ -914,7 +1078,18 @@ static int fg_psy_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *pval) { + struct fg_chip *chip = power_supply_get_drvdata(psy); + switch (psp) { + case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: + if ((pval->intval > 0) && (pval->intval <= BUCKET_COUNT)) { + chip->cyc_ctr.id = pval->intval; + } else { + pr_err("rejecting invalid cycle_count_id = %d\n", + pval->intval); + return -EINVAL; + } + break; default: break; } @@ -926,6 +1101,8 @@ static int fg_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { + case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: + return 1; default: break; } @@ -965,6 +1142,8 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_BATTERY_TYPE, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CYCLE_COUNT_ID, }; static const struct power_supply_desc fg_psy_desc = { @@ -1121,6 +1300,9 @@ static int fg_hw_init(struct fg_chip *chip) } } + if (chip->cyc_ctr.en) + restore_cycle_counter(chip); + return 0; } @@ -1180,6 +1362,7 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data) if (chip->battery_missing) { chip->batt_id_avail = false; chip->profile_loaded = false; + clear_cycle_counter(chip); } else { rc = fg_batt_profile_init(chip); if (rc < 0) { @@ -1222,6 +1405,12 @@ static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) { struct fg_chip *chip = data; + if (chip->cyc_ctr.en) + schedule_work(&chip->cycle_count_work); + + if (is_charger_available(chip)) + power_supply_changed(chip->batt_psy); + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); return IRQ_HANDLED; } @@ -1230,6 +1419,9 @@ static irqreturn_t fg_empty_soc_irq_handler(int irq, void *data) { struct fg_chip *chip = data; + if (is_charger_available(chip)) + power_supply_changed(chip->batt_psy); + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); return IRQ_HANDLED; } @@ -1528,6 +1720,9 @@ static int fg_parse_dt(struct fg_chip *chip) else chip->dt.esr_timer_asleep = temp; + chip->cyc_ctr.en = of_property_read_bool(node, "qcom,cycle-counter-en"); + if (chip->cyc_ctr.en) + chip->cyc_ctr.id = 1; return 0; } @@ -1580,10 +1775,12 @@ static int fg_gen3_probe(struct platform_device *pdev) mutex_init(&chip->bus_lock); mutex_init(&chip->sram_rw_lock); + mutex_init(&chip->cyc_ctr.lock); init_completion(&chip->soc_update); init_completion(&chip->soc_ready); INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work); INIT_WORK(&chip->status_change_work, status_change_work); + INIT_WORK(&chip->cycle_count_work, cycle_count_work); rc = fg_memif_init(chip); if (rc < 0) { diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index d19e7827ed83..57f31d8c58e7 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -18,11 +18,13 @@ #include <linux/interrupt.h> #include <linux/of.h> #include <linux/of_irq.h> +#include <linux/qpnp/qpnp-revid.h> #include <linux/regulator/driver.h> #include <linux/regulator/of_regulator.h> #include <linux/regulator/machine.h> #include "smb-reg.h" #include "smb-lib.h" +#include "storm-watch.h" #include "pmic-voter.h" #define SMB2_DEFAULT_WPWR_UW 8000000 @@ -205,6 +207,7 @@ struct smb_dt_props { int wipower_max_uw; u32 step_soc_threshold[STEP_CHARGING_MAX_STEPS - 1]; s32 step_cc_delta[STEP_CHARGING_MAX_STEPS]; + struct device_node *revid_dev_node; }; struct smb2 { @@ -600,6 +603,8 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_STEP_CHARGING_STEP, }; static int smb2_batt_get_prop(struct power_supply *psy, @@ -639,6 +644,11 @@ static int smb2_batt_get_prop(struct power_supply *psy, break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: rc = smblib_get_prop_input_current_limited(chg, val); + case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: + val->intval = chg->step_chg_enabled; + break; + case POWER_SUPPLY_PROP_STEP_CHARGING_STEP: + rc = smblib_get_prop_step_chg_step(chg, val); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: rc = smblib_get_prop_batt_voltage_now(chg, val); @@ -1085,6 +1095,40 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } +static int smb2_setup_wa_flags(struct smb2 *chip) +{ + struct pmic_revid_data *pmic_rev_id; + struct device_node *revid_dev_node; + + revid_dev_node = of_parse_phandle(chip->chg.dev->of_node, + "qcom,pmic-revid", 0); + if (!revid_dev_node) { + pr_err("Missing qcom,pmic-revid property\n"); + return -EINVAL; + } + + pmic_rev_id = get_revid_data(revid_dev_node); + if (IS_ERR_OR_NULL(pmic_rev_id)) { + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + + switch (pmic_rev_id->pmic_subtype) { + case PMICOBALT_SUBTYPE: + break; + default: + pr_err("PMIC subtype %d not supported\n", + pmic_rev_id->pmic_subtype); + return -EINVAL; + } + + return 0; +} + /**************************** * DETERMINE INITIAL STATUS * ****************************/ @@ -1109,60 +1153,181 @@ static int smb2_determine_initial_status(struct smb2 *chip) **************************/ struct smb2_irq_info { - const char *name; - const irq_handler_t handler; - const bool wake; - int irq; + const char *name; + const irq_handler_t handler; + const bool wake; + const struct storm_watch storm_data; + int irq; }; static struct smb2_irq_info smb2_irqs[] = { /* CHARGER IRQs */ - { "chg-error", smblib_handle_debug }, - { "chg-state-change", smblib_handle_chg_state_change, true }, - { "step-chg-state-change", smblib_handle_step_chg_state_change, - true }, - { "step-chg-soc-update-fail", smblib_handle_step_chg_soc_update_fail, - true }, - { "step-chg-soc-update-request", - smblib_handle_step_chg_soc_update_request, true }, + { + .name = "chg-error", + .handler = smblib_handle_debug, + }, + { + .name = "chg-state-change", + .handler = smblib_handle_chg_state_change, + .wake = true, + }, + { + .name = "step-chg-state-change", + .handler = smblib_handle_step_chg_state_change, + .wake = true, + }, + { + .name = "step-chg-soc-update-fail", + .handler = smblib_handle_step_chg_soc_update_fail, + .wake = true, + }, + { + .name = "step-chg-soc-update-request", + .handler = smblib_handle_step_chg_soc_update_request, + .wake = true, + }, /* OTG IRQs */ - { "otg-fail", smblib_handle_debug }, - { "otg-overcurrent", smblib_handle_debug }, - { "otg-oc-dis-sw-sts", smblib_handle_debug }, - { "testmode-change-detect", smblib_handle_debug }, + { + .name = "otg-fail", + .handler = smblib_handle_debug, + }, + { + .name = "otg-overcurrent", + .handler = smblib_handle_debug, + }, + { + .name = "otg-oc-dis-sw-sts", + .handler = smblib_handle_debug, + }, + { + .name = "testmode-change-detect", + .handler = smblib_handle_debug, + }, /* BATTERY IRQs */ - { "bat-temp", smblib_handle_batt_temp_changed }, - { "bat-ocp", smblib_handle_batt_psy_changed }, - { "bat-ov", smblib_handle_batt_psy_changed }, - { "bat-low", smblib_handle_batt_psy_changed }, - { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed }, - { "bat-terminal-missing", smblib_handle_batt_psy_changed }, + { + .name = "bat-temp", + .handler = smblib_handle_batt_temp_changed, + }, + { + .name = "bat-ocp", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-ov", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-low", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-therm-or-id-missing", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-terminal-missing", + .handler = smblib_handle_batt_psy_changed, + }, /* USB INPUT IRQs */ - { "usbin-collapse", smblib_handle_debug }, - { "usbin-lt-3p6v", smblib_handle_debug }, - { "usbin-uv", smblib_handle_debug }, - { "usbin-ov", smblib_handle_debug }, - { "usbin-plugin", smblib_handle_usb_plugin, true }, - { "usbin-src-change", smblib_handle_usb_source_change, true }, - { "usbin-icl-change", smblib_handle_icl_change, true }, - { "type-c-change", smblib_handle_usb_typec_change, true }, + { + .name = "usbin-collapse", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-lt-3p6v", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-uv", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-ov", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-plugin", + .handler = smblib_handle_usb_plugin, + .wake = true, + }, + { + .name = "usbin-src-change", + .handler = smblib_handle_usb_source_change, + .wake = true, + }, + { + .name = "usbin-icl-change", + .handler = smblib_handle_icl_change, + .wake = true, + }, + { + .name = "type-c-change", + .handler = smblib_handle_usb_typec_change, + .wake = true, + }, /* DC INPUT IRQs */ - { "dcin-collapse", smblib_handle_debug }, - { "dcin-lt-3p6v", smblib_handle_debug }, - { "dcin-uv", smblib_handle_debug }, - { "dcin-ov", smblib_handle_debug }, - { "dcin-plugin", smblib_handle_debug }, - { "div2-en-dg", smblib_handle_debug }, - { "dcin-icl-change", smblib_handle_debug }, + { + .name = "dcin-collapse", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-lt-3p6v", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-uv", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-ov", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-plugin", + .handler = smblib_handle_debug, + }, + { + .name = "div2-en-dg", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-icl-change", + .handler = smblib_handle_debug, + }, /* MISCELLANEOUS IRQs */ - { "wdog-snarl", NULL }, - { "wdog-bark", NULL }, - { "aicl-fail", smblib_handle_debug }, - { "aicl-done", smblib_handle_debug }, - { "high-duty-cycle", smblib_handle_high_duty_cycle, true }, - { "input-current-limiting", smblib_handle_debug }, - { "temperature-change", smblib_handle_debug }, - { "switcher-power-ok", smblib_handle_debug }, + { + .name = "wdog-snarl", + .handler = NULL, + }, + { + .name = "wdog-bark", + .handler = NULL, + }, + { + .name = "aicl-fail", + .handler = smblib_handle_debug, + }, + { + .name = "aicl-done", + .handler = smblib_handle_debug, + }, + { + .name = "high-duty-cycle", + .handler = smblib_handle_high_duty_cycle, + .wake = true, + }, + { + .name = "input-current-limiting", + .handler = smblib_handle_debug, + }, + { + .name = "temperature-change", + .handler = smblib_handle_debug, + }, + { + .name = "switcher-power-ok", + .handler = smblib_handle_debug, + }, }; static int smb2_get_irq_index_byname(const char *irq_name) @@ -1205,6 +1370,7 @@ static int smb2_request_interrupt(struct smb2 *chip, irq_data->parent_data = chip; irq_data->name = irq_name; + irq_data->storm_data = smb2_irqs[irq_index].storm_data; rc = devm_request_threaded_irq(chg->dev, irq, NULL, smb2_irqs[irq_index].handler, @@ -1270,6 +1436,13 @@ static int smb2_probe(struct platform_device *pdev) return -EINVAL; } + rc = smb2_setup_wa_flags(chip); + if (rc < 0) { + if (rc != -EPROBE_DEFER) + pr_err("Couldn't setup wa flags rc=%d\n", rc); + return rc; + } + rc = smblib_init(chg); if (rc < 0) { pr_err("Smblib_init failed rc=%d\n", rc); diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index ed24e12e313d..e93d03788f11 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -18,6 +18,7 @@ #include <linux/irq.h> #include "smb-lib.h" #include "smb-reg.h" +#include "storm-watch.h" #include "pmic-voter.h" #define smblib_dbg(chg, reason, fmt, ...) \ @@ -102,7 +103,8 @@ static int smblib_get_step_charging_adjustment(struct smb_charger *chg, return rc; } - step_state = (stat & STEP_CHARGING_STATUS_MASK) >> 3; + step_state = (stat & STEP_CHARGING_STATUS_MASK) >> + STEP_CHARGING_STATUS_SHIFT; rc = smblib_get_charge_param(chg, &chg->param.step_cc_delta[step_state], cc_offset); @@ -779,13 +781,24 @@ int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev) int smblib_vconn_regulator_enable(struct regulator_dev *rdev) { struct smb_charger *chg = rdev_get_drvdata(rdev); + u8 stat; int rc = 0; + /* + * VCONN_EN_ORIENTATION is overloaded with overriding the CC pin used + * for Vconn, and it should be set with reverse polarity of CC_OUT. + */ + rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); + return rc; + } + stat = stat & CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT; rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - VCONN_EN_VALUE_BIT, VCONN_EN_VALUE_BIT); + VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT, + VCONN_EN_VALUE_BIT | stat); if (rc < 0) - dev_err(chg->dev, "Couldn't enable vconn regulator rc=%d\n", - rc); + dev_err(chg->dev, "Couldn't enable vconn setting rc=%d\n", rc); return rc; } @@ -1042,6 +1055,30 @@ int smblib_get_prop_batt_temp(struct smb_charger *chg, return rc; } +int smblib_get_prop_step_chg_step(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + u8 stat; + + if (!chg->step_chg_enabled) { + val->intval = -1; + return 0; + } + + 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; + } + + val->intval = (stat & STEP_CHARGING_STATUS_MASK) >> + STEP_CHARGING_STATUS_SHIFT; + + return rc; +} + /*********************** * BATTERY PSY SETTERS * ***********************/ @@ -1565,6 +1602,7 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, const union power_supply_propval *val) { int rc; + u8 stat; if (!get_effective_result(chg->pd_allowed_votable)) { dev_err(chg->dev, "PD is not allowed\n"); @@ -1582,6 +1620,40 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, vote(chg->pd_allowed_votable, PD_VOTER, val->intval, 0); + /* + * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line + * when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) is set + * or when VCONN_EN_VALUE_BIT is set. + */ + if (val->intval) { + rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read TYPE_C_STATUS_4 rc=%d\n", + rc); + return rc; + } + + stat &= CC_ORIENTATION_BIT; + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + VCONN_EN_ORIENTATION_BIT, + stat ? 0 : VCONN_EN_ORIENTATION_BIT); + if (rc < 0) + dev_err(chg->dev, + "Couldn't enable vconn on CC line rc=%d\n", rc); + } + + /* CC pin selection s/w override in PD session; h/w otherwise. */ + rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, + TYPEC_SPARE_CFG_BIT, + val->intval ? TYPEC_SPARE_CFG_BIT : 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't change cc_out ctrl to %s rc=%d\n", + val->intval ? "SW" : "HW", rc); + return rc; + } + chg->pd_active = (bool)val->intval; smblib_update_usb_type(chg); return rc; @@ -1597,7 +1669,6 @@ irqreturn_t smblib_handle_debug(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); - return IRQ_HANDLED; } @@ -1633,8 +1704,10 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data) dev_err(chg->dev, "Couldn't get batt status type rc=%d\n", rc); return IRQ_HANDLED; } - if (pval.intval == POWER_SUPPLY_STATUS_FULL) + if (pval.intval == POWER_SUPPLY_STATUS_FULL) { + power_supply_changed(chg->batt_psy); vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0); + } return IRQ_HANDLED; } @@ -1705,7 +1778,7 @@ irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data) struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; - smblib_handle_debug(irq, data); + smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); power_supply_changed(chg->batt_psy); return IRQ_HANDLED; } @@ -1715,7 +1788,7 @@ irqreturn_t smblib_handle_usb_psy_changed(int irq, void *data) struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; - smblib_handle_debug(irq, data); + smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); power_supply_changed(chg->usb_psy); return IRQ_HANDLED; } @@ -1768,6 +1841,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) } skip_dpdm_float: + power_supply_changed(chg->usb_psy); smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", irq_data->name, chg->vbus_present ? "attached" : "detached"); return IRQ_HANDLED; diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index 289fb1706e97..f5d9dda8330a 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -16,6 +16,7 @@ #include <linux/irqreturn.h> #include <linux/regulator/driver.h> #include <linux/regulator/consumer.h> +#include "storm-watch.h" enum print_reason { PR_INTERRUPT = BIT(0), @@ -46,8 +47,9 @@ struct smb_regulator { }; struct smb_irq_data { - void *parent_data; - const char *name; + void *parent_data; + const char *name; + struct storm_watch storm_data; }; struct smb_chg_param { @@ -161,6 +163,9 @@ struct smb_charger { bool step_chg_enabled; bool is_hdc; + + /* workaround flag */ + u32 wa_flags; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); @@ -228,6 +233,8 @@ int smblib_get_prop_batt_current_now(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_temp(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_step_chg_step(struct smb_charger *chg, + union power_supply_propval *val); int smblib_set_prop_input_suspend(struct smb_charger *chg, const union power_supply_propval *val); diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h index 0d5222ec08f8..c88d132fbf70 100644 --- a/drivers/power/qcom-charger/smb-reg.h +++ b/drivers/power/qcom-charger/smb-reg.h @@ -32,6 +32,7 @@ #define BATTERY_CHARGER_STATUS_1_REG (CHGR_BASE + 0x06) #define BVR_INITIAL_RAMP_BIT BIT(7) #define CC_SOFT_TERMINATE_BIT BIT(6) +#define STEP_CHARGING_STATUS_SHIFT 3 #define STEP_CHARGING_STATUS_MASK GENMASK(5, 3) #define BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0) enum { @@ -585,6 +586,7 @@ enum { #define FORCE_FLOAT_SDP_CFG_BIT BIT(0) #define TAPER_TIMER_SEL_CFG_REG (USBIN_BASE + 0x64) +#define TYPEC_SPARE_CFG_BIT BIT(7) #define TAPER_TIMER_SEL_MASK GENMASK(1, 0) #define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65) @@ -606,6 +608,8 @@ enum { #define TYPEC_VBUS_ASSERT_INT_EN_BIT BIT(0) #define TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG (USBIN_BASE + 0x68) +#define EXIT_SNK_BASED_ON_CC BIT(7) +#define VCONN_EN_ORIENTATION_BIT BIT(6) #define TYPEC_VCONN_OVERCURR_INT_EN_BIT BIT(5) #define VCONN_EN_SRC_BIT BIT(4) #define VCONN_EN_VALUE_BIT BIT(3) diff --git a/drivers/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c index 5b4e7bcccdce..33d759be9aeb 100644 --- a/drivers/power/qcom-charger/smb138x-charger.c +++ b/drivers/power/qcom-charger/smb138x-charger.c @@ -25,6 +25,7 @@ #include <linux/qpnp/qpnp-revid.h> #include "smb-reg.h" #include "smb-lib.h" +#include "storm-watch.h" #include "pmic-voter.h" #define SMB138X_DEFAULT_FCC_UA 1000000 @@ -748,55 +749,170 @@ static int smb138x_determine_initial_status(struct smb138x *chip) **************************/ struct smb138x_irq_info { - const char *name; - const irq_handler_t handler; + const char *name; + const irq_handler_t handler; + const struct storm_watch storm_data; }; static const struct smb138x_irq_info smb138x_irqs[] = { /* CHARGER IRQs */ - { "chg-error", smblib_handle_debug }, - { "chg-state-change", smblib_handle_debug }, - { "step-chg-state-change", smblib_handle_debug }, - { "step-chg-soc-update-fail", smblib_handle_debug }, - { "step-chg-soc-update-request", smblib_handle_debug }, + { + .name = "chg-error", + .handler = smblib_handle_debug, + }, + { + .name = "chg-state-change", + .handler = smblib_handle_debug, + }, + { + .name = "step-chg-state-change", + .handler = smblib_handle_debug, + }, + { + .name = "step-chg-soc-update-fail", + .handler = smblib_handle_debug, + }, + { + .name = "step-chg-soc-update-request", + .handler = smblib_handle_debug, + }, /* OTG IRQs */ - { "otg-fail", smblib_handle_debug }, - { "otg-overcurrent", smblib_handle_debug }, - { "otg-oc-dis-sw-sts", smblib_handle_debug }, - { "testmode-change-detect", smblib_handle_debug }, + { + .name = "otg-fail", + .handler = smblib_handle_debug, + }, + { + .name = "otg-overcurrent", + .handler = smblib_handle_debug, + }, + { + .name = "otg-oc-dis-sw-sts", + .handler = smblib_handle_debug, + }, + { + .name = "testmode-change-detect", + .handler = smblib_handle_debug, + }, /* BATTERY IRQs */ - { "bat-temp", smblib_handle_batt_psy_changed }, - { "bat-ocp", smblib_handle_batt_psy_changed }, - { "bat-ov", smblib_handle_batt_psy_changed }, - { "bat-low", smblib_handle_batt_psy_changed }, - { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed }, - { "bat-terminal-missing", smblib_handle_batt_psy_changed }, + { + .name = "bat-temp", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-ocp", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-ov", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-low", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-therm-or-id-missing", + .handler = smblib_handle_batt_psy_changed, + }, + { + .name = "bat-terminal-missing", + .handler = smblib_handle_batt_psy_changed, + }, /* USB INPUT IRQs */ - { "usbin-collapse", smblib_handle_debug }, - { "usbin-lt-3p6v", smblib_handle_debug }, - { "usbin-uv", smblib_handle_debug }, - { "usbin-ov", smblib_handle_debug }, - { "usbin-plugin", smblib_handle_usb_plugin }, - { "usbin-src-change", smblib_handle_usb_source_change }, - { "usbin-icl-change", smblib_handle_debug }, - { "type-c-change", smblib_handle_usb_typec_change }, + { + .name = "usbin-collapse", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-lt-3p6v", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-uv", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-ov", + .handler = smblib_handle_debug, + }, + { + .name = "usbin-plugin", + .handler = smblib_handle_usb_plugin, + }, + { + .name = "usbin-src-change", + .handler = smblib_handle_usb_source_change, + }, + { + .name = "usbin-icl-change", + .handler = smblib_handle_debug, + }, + { + .name = "type-c-change", + .handler = smblib_handle_usb_typec_change, + }, /* DC INPUT IRQs */ - { "dcin-collapse", smblib_handle_debug }, - { "dcin-lt-3p6v", smblib_handle_debug }, - { "dcin-uv", smblib_handle_debug }, - { "dcin-ov", smblib_handle_debug }, - { "dcin-plugin", smblib_handle_debug }, - { "div2-en-dg", smblib_handle_debug }, - { "dcin-icl-change", smblib_handle_debug }, + { + .name = "dcin-collapse", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-lt-3p6v", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-uv", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-ov", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-plugin", + .handler = smblib_handle_debug, + }, + { + .name = "div2-en-dg", + .handler = smblib_handle_debug, + }, + { + .name = "dcin-icl-change", + .handler = smblib_handle_debug, + }, /* MISCELLANEOUS IRQs */ - { "wdog-snarl", smblib_handle_debug }, - { "wdog-bark", smblib_handle_debug }, - { "aicl-fail", smblib_handle_debug }, - { "aicl-done", smblib_handle_debug }, - { "high-duty-cycle", smblib_handle_debug }, - { "input-current-limiting", smblib_handle_debug }, - { "temperature-change", smblib_handle_debug }, - { "switcher-power-ok", smblib_handle_debug }, + { + .name = "wdog-snarl", + .handler = smblib_handle_debug, + }, + { + .name = "wdog-bark", + .handler = smblib_handle_debug, + }, + { + .name = "aicl-fail", + .handler = smblib_handle_debug, + }, + { + .name = "aicl-done", + .handler = smblib_handle_debug, + }, + { + .name = "high-duty-cycle", + .handler = smblib_handle_debug, + }, + { + .name = "input-current-limiting", + .handler = smblib_handle_debug, + }, + { + .name = "temperature-change", + .handler = smblib_handle_debug, + }, + { + .name = "switcher-power-ok", + .handler = smblib_handle_debug, + }, }; static int smb138x_get_irq_index_byname(const char *irq_name) @@ -837,6 +953,7 @@ static int smb138x_request_interrupt(struct smb138x *chip, irq_data->parent_data = chip; irq_data->name = irq_name; + irq_data->storm_data = smb138x_irqs[irq_index].storm_data; rc = devm_request_threaded_irq(chg->dev, irq, NULL, smb138x_irqs[irq_index].handler, diff --git a/drivers/power/qcom-charger/storm-watch.c b/drivers/power/qcom-charger/storm-watch.c new file mode 100644 index 000000000000..90fec12bd742 --- /dev/null +++ b/drivers/power/qcom-charger/storm-watch.c @@ -0,0 +1,57 @@ +/* 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 "storm-watch.h" + +/** + * is_storming(): Check if an event is storming + * + * @data: Data for tracking an event storm + * + * The return value will be true if a storm has been detected and + * false if a storm was not detected. + */ +bool is_storming(struct storm_watch *data) +{ + ktime_t curr_kt, delta_kt; + bool is_storming = false; + + if (!data) + return false; + + if (!data->enabled) + return false; + + /* max storm count must be greater than 0 */ + if (data->max_storm_count <= 0) + return false; + + /* the period threshold must be greater than 0ms */ + if (data->storm_period_ms <= 0) + return false; + + curr_kt = ktime_get_boottime(); + delta_kt = ktime_sub(curr_kt, data->last_kt); + + if (ktime_to_ms(delta_kt) < data->storm_period_ms) + data->storm_count++; + else + data->storm_count = 0; + + if (data->storm_count > data->max_storm_count) { + is_storming = true; + data->storm_count = 0; + } + + data->last_kt = curr_kt; + return is_storming; +} diff --git a/drivers/power/qcom-charger/storm-watch.h b/drivers/power/qcom-charger/storm-watch.h new file mode 100644 index 000000000000..44b9d64d8a87 --- /dev/null +++ b/drivers/power/qcom-charger/storm-watch.h @@ -0,0 +1,36 @@ +/* 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 __STORM_WATCH_H +#define __STORM_WATCH_H +#include <linux/ktime.h> + +/** + * Data used to track an event storm. + * + * @storm_period_ms: The maximum time interval between two events. If this limit + * is exceeded then the event chain will be broken and removed + * from consideration for a storm. + * @max_storm_count: The number of chained events required to trigger a storm. + * @storm_count: The current number of chained events. + * @last_kt: Kernel time of the last event seen. + */ +struct storm_watch { + bool enabled; + int storm_period_ms; + int max_storm_count; + int storm_count; + ktime_t last_kt; +}; + +bool is_storming(struct storm_watch *data); +#endif diff --git a/drivers/power/qcom/msm-core.c b/drivers/power/qcom/msm-core.c index e990425bd63a..727a768e63eb 100644 --- a/drivers/power/qcom/msm-core.c +++ b/drivers/power/qcom/msm-core.c @@ -240,10 +240,10 @@ void trigger_cpu_pwr_stats_calc(void) if (cpu_node->sensor_id < 0) continue; - if (cpu_node->temp == prev_temp[cpu]) + if (cpu_node->temp == prev_temp[cpu]) { sensor_get_temp(cpu_node->sensor_id, &temp); - - cpu_node->temp = temp / scaling_factor; + cpu_node->temp = temp / scaling_factor; + } prev_temp[cpu] = cpu_node->temp; @@ -373,7 +373,7 @@ static int update_userspace_power(struct sched_params __user *argp) { int i; int ret; - int cpu; + int cpu = -1; struct cpu_activity_info *node; struct cpu_static_info *sp, *clear_sp; int cpumask, cluster, mpidr; @@ -396,7 +396,7 @@ static int update_userspace_power(struct sched_params __user *argp) } } - if (cpu >= num_possible_cpus()) + if ((cpu < 0) || (cpu >= num_possible_cpus())) return -EINVAL; node = &activity[cpu]; diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 1a1dd804ffb3..ad4b6ffef36e 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1498,14 +1498,10 @@ 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 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); + 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); /* disable device ref_clk */ ufs_qcom_dev_ref_clk_ctrl(host, false); } @@ -1960,6 +1956,13 @@ 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/ufs_test.c b/drivers/scsi/ufs/ufs_test.c index 8953722e8dad..e23dc3e8d9da 100644 --- a/drivers/scsi/ufs/ufs_test.c +++ b/drivers/scsi/ufs/ufs_test.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -689,13 +689,13 @@ static void scenario_free_end_io_fn(struct request *rq, int err) __blk_put_request(test_iosched->req_q, test_rq->rq); spin_unlock_irqrestore(&test_iosched->lock, flags); - test_iosched_free_test_req_data_buffer(test_rq); - kfree(test_rq); - if (err) pr_err("%s: request %d completed, err=%d", __func__, test_rq->req_id, err); + test_iosched_free_test_req_data_buffer(test_rq); + kfree(test_rq); + check_test_completion(test_iosched); } @@ -984,14 +984,14 @@ static void long_test_free_end_io_fn(struct request *rq, int err) return; } - test_iosched_free_test_req_data_buffer(test_rq); - kfree(test_rq); - utd->completed_req_count++; - if (err) pr_err("%s: request %d completed, err=%d", __func__, test_rq->req_id, err); + test_iosched_free_test_req_data_buffer(test_rq); + kfree(test_rq); + utd->completed_req_count++; + check_test_completion(test_iosched); } @@ -1007,7 +1007,7 @@ static void long_test_free_end_io_fn(struct request *rq, int err) static int run_long_test(struct test_iosched *test_iosched) { int ret = 0; - int direction, num_bios_per_request; + int direction, num_bios_per_request = 1; static unsigned int inserted_requests; u32 sector, seed, num_bios, seq_sector_delta; struct ufs_test_data *utd = test_iosched->blk_dev_test_data; @@ -1028,14 +1028,12 @@ static int run_long_test(struct test_iosched *test_iosched) /* Set test parameters */ switch (test_iosched->test_info.testcase) { case UFS_TEST_LONG_RANDOM_READ: - num_bios_per_request = 1; utd->long_test_num_reqs = (utd->sector_range * SECTOR_SIZE) / (LONG_RAND_TEST_REQ_RATIO * TEST_BIO_SIZE * num_bios_per_request); direction = READ; break; case UFS_TEST_LONG_RANDOM_WRITE: - num_bios_per_request = 1; utd->long_test_num_reqs = (utd->sector_range * SECTOR_SIZE) / (LONG_RAND_TEST_REQ_RATIO * TEST_BIO_SIZE * num_bios_per_request); diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 311a4ea24973..d478767ad3dd 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1346,12 +1346,7 @@ static void ufshcd_gate_work(struct work_struct *work) ufshcd_set_link_hibern8(hba); } - /* - * 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) + if (!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 */ diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index edaaa0b2d3c8..cdc07411b690 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -34,6 +34,7 @@ #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> #include <soc/qcom/memory_dump.h> @@ -41,6 +42,7 @@ #include <soc/qcom/msm_qmi_interface.h> #include <soc/qcom/secure_buffer.h> #include <soc/qcom/subsystem_notif.h> +#include <soc/qcom/subsystem_restart.h> #include <soc/qcom/service-locator.h> #include <soc/qcom/service-notifier.h> #include <soc/qcom/socinfo.h> @@ -270,6 +272,10 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_MAX, }; +struct icnss_event_pd_service_down_data { + bool crashed; +}; + struct icnss_driver_event { struct list_head list; enum icnss_driver_event_type type; @@ -285,13 +291,13 @@ enum icnss_driver_state { ICNSS_FW_READY, ICNSS_DRIVER_PROBED, ICNSS_FW_TEST_MODE, - ICNSS_SUSPEND, ICNSS_PM_SUSPEND, ICNSS_PM_SUSPEND_NOIRQ, ICNSS_SSR_ENABLED, ICNSS_PDR_ENABLED, ICNSS_PD_RESTART, ICNSS_MSA0_ASSIGNED, + ICNSS_WLFW_EXISTS, }; struct ce_irq_list { @@ -535,9 +541,9 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type, int gfp = GFP_KERNEL; int ret = 0; - icnss_pr_dbg("Posting event: %s(%d)%s, state: 0x%lx\n", - icnss_driver_event_to_str(type), type, - sync ? "-sync" : "", penv->state); + icnss_pr_dbg("Posting event: %s: %s%s(%d), state: 0x%lx\n", + current->comm, icnss_driver_event_to_str(type), + sync ? "-sync" : "", type, penv->state); if (type >= ICNSS_DRIVER_EVENT_MAX) { icnss_pr_err("Invalid Event type: %d, can't post", type); @@ -2212,6 +2218,8 @@ static int icnss_driver_event_server_arrive(void *data) if (!penv) return -ENODEV; + set_bit(ICNSS_WLFW_EXISTS, &penv->state); + penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv); if (!penv->wlfw_clnt) { icnss_pr_err("QMI client handle create failed\n"); @@ -2311,6 +2319,8 @@ static int icnss_call_driver_probe(struct icnss_priv *priv) if (!priv->ops || !priv->ops->probe) return 0; + icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state); + icnss_hw_power_on(priv); ret = priv->ops->probe(&priv->pdev->dev); @@ -2339,6 +2349,8 @@ static int icnss_call_driver_reinit(struct icnss_priv *priv) if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) goto out; + icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state); + icnss_hw_power_on(priv); ret = priv->ops->reinit(&priv->pdev->dev); @@ -2463,36 +2475,70 @@ out: return 0; } -static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, - void *data) +static int icnss_call_driver_remove(struct icnss_priv *priv) { - int ret = 0; + icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state); - if (test_bit(ICNSS_PD_RESTART, &priv->state)) { - icnss_pr_err("PD Down while recovery inprogress, state: 0x%lx\n", - priv->state); - ICNSS_ASSERT(0); - goto out; - } + clear_bit(ICNSS_FW_READY, &priv->state); + + if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) + return 0; + + if (!priv->ops || !priv->ops->remove) + return 0; + + penv->ops->remove(&priv->pdev->dev); + + clear_bit(ICNSS_DRIVER_PROBED, &priv->state); + + return 0; +} + +static int icnss_call_driver_shutdown(struct icnss_priv *priv) +{ + icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state); set_bit(ICNSS_PD_RESTART, &priv->state); clear_bit(ICNSS_FW_READY, &priv->state); + if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) + return 0; + if (!priv->ops || !priv->ops->shutdown) - goto out; + return 0; - if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) + priv->ops->shutdown(&priv->pdev->dev); + + return 0; +} + +static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, + void *data) +{ + int ret = 0; + struct icnss_event_pd_service_down_data *event_data = data; + + if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) + return 0; + + if (test_bit(ICNSS_PD_RESTART, &priv->state)) { + icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n", + event_data->crashed, priv->state); + ICNSS_ASSERT(0); goto out; + } - priv->ops->shutdown(&priv->pdev->dev); + if (event_data->crashed) + icnss_call_driver_shutdown(priv); + else + icnss_call_driver_remove(priv); out: icnss_remove_msa_permissions(priv); ret = icnss_hw_power_off(priv); - icnss_pr_dbg("PD down completed: %d, state: 0x%lx\n", - ret, priv->state); + kfree(data); return ret; } @@ -2533,7 +2579,8 @@ static void icnss_driver_event_work(struct work_struct *work) ret = icnss_driver_event_unregister_driver(event->data); break; case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN: - icnss_driver_event_pd_service_down(penv, event->data); + ret = icnss_driver_event_pd_service_down(penv, + event->data); break; default: icnss_pr_err("Invalid Event type: %d", event->type); @@ -2543,6 +2590,11 @@ static void icnss_driver_event_work(struct work_struct *work) penv->stats.events[event->type].processed++; + icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n", + icnss_driver_event_to_str(event->type), + event->sync ? "-sync" : "", event->type, ret, + penv->state); + spin_lock_irqsave(&penv->event_lock, flags); if (event->sync) { event->ret = ret; @@ -2590,23 +2642,31 @@ static struct notifier_block wlfw_clnt_nb = { .notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify, }; -static int icnss_modem_notifier_nb(struct notifier_block *this, +static int icnss_modem_notifier_nb(struct notifier_block *nb, unsigned long code, - void *ss_handle) + void *data) { + struct icnss_event_pd_service_down_data *event_data; + struct notif_data *notif = data; + struct icnss_priv *priv = container_of(nb, struct icnss_priv, + modem_ssr_nb); + icnss_pr_dbg("Modem-Notify: event %lu\n", code); - if (code == SUBSYS_AFTER_POWERUP) { - icnss_pr_dbg("Modem-Notify: Powerup\n"); - } else if (code == SUBSYS_BEFORE_SHUTDOWN) { - icnss_pr_info("Modem-Notify: Before shutdown\n"); - icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, - true, NULL); - } else if (code == SUBSYS_AFTER_SHUTDOWN) { - icnss_pr_info("Modem-Notify: After Shutdown\n"); - } else { - return NOTIFY_DONE; - } + if (code != SUBSYS_BEFORE_SHUTDOWN) + return NOTIFY_OK; + + icnss_pr_info("Modem went down, state: %lx\n", priv->state); + + event_data = kzalloc(sizeof(*data), GFP_KERNEL); + + if (event_data == NULL) + return notifier_from_errno(-ENOMEM); + + event_data->crashed = notif->crashed; + + icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, + true, event_data); return NOTIFY_OK; } @@ -2665,14 +2725,23 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, { struct icnss_priv *priv = container_of(nb, struct icnss_priv, service_notifier_nb); + enum pd_subsys_state *state = data; + struct icnss_event_pd_service_down_data *event_data; switch (notification) { case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01: - icnss_pr_info("Service down, state: 0x%lx\n", priv->state); + icnss_pr_info("Service down, data: 0x%p, state: 0x%lx\n", data, + priv->state); + event_data = kzalloc(sizeof(*data), GFP_KERNEL); + + if (event_data == NULL) + return notifier_from_errno(-ENOMEM); + + if (state == NULL || *state != SHUTDOWN) + event_data->crashed = true; + icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, - true, NULL); - icnss_pr_dbg("Service down completed, state: 0x%lx\n", - priv->state); + true, event_data); break; case SERVREG_NOTIF_SERVICE_STATE_UP_V01: icnss_pr_dbg("Service up, state: 0x%lx\n", priv->state); @@ -2818,8 +2887,6 @@ enable_pdr: if (ret) return ret; - icnss_modem_ssr_unregister_notifier(priv); - return 0; } @@ -3753,9 +3820,6 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_FW_TEST_MODE: seq_puts(s, "FW TEST MODE"); continue; - case ICNSS_SUSPEND: - seq_puts(s, "SUSPEND"); - continue; case ICNSS_PM_SUSPEND: seq_puts(s, "PM SUSPEND"); continue; @@ -3774,6 +3838,8 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_MSA0_ASSIGNED: seq_puts(s, "MSA0 ASSIGNED"); continue; + case ICNSS_WLFW_EXISTS: + continue; } seq_printf(s, "UNKNOWN-%d", i); @@ -4400,55 +4466,6 @@ static int icnss_remove(struct platform_device *pdev) return 0; } -static int icnss_suspend(struct platform_device *pdev, - pm_message_t state) -{ - int ret = 0; - - if (!penv) { - ret = -ENODEV; - goto out; - } - - icnss_pr_dbg("Driver suspending, state: 0x%lx\n", - penv->state); - - if (!penv->ops || !penv->ops->suspend || - !test_bit(ICNSS_DRIVER_PROBED, &penv->state)) - goto out; - - ret = penv->ops->suspend(&pdev->dev, state); - -out: - if (ret == 0) - set_bit(ICNSS_SUSPEND, &penv->state); - return ret; -} - -static int icnss_resume(struct platform_device *pdev) -{ - int ret = 0; - - if (!penv) { - ret = -ENODEV; - goto out; - } - - icnss_pr_dbg("Driver resuming, state: 0x%lx\n", - penv->state); - - if (!penv->ops || !penv->ops->resume || - !test_bit(ICNSS_DRIVER_PROBED, &penv->state)) - goto out; - - ret = penv->ops->resume(&pdev->dev); - -out: - if (ret == 0) - clear_bit(ICNSS_SUSPEND, &penv->state); - return ret; -} - #ifdef CONFIG_PM_SLEEP static int icnss_pm_suspend(struct device *dev) { @@ -4584,8 +4601,6 @@ MODULE_DEVICE_TABLE(of, icnss_dt_match); static struct platform_driver icnss_driver = { .probe = icnss_probe, .remove = icnss_remove, - .suspend = icnss_suspend, - .resume = icnss_resume, .driver = { .name = "icnss", .pm = &icnss_pm_ops, diff --git a/drivers/soc/qcom/jtag-fuse.c b/drivers/soc/qcom/jtag-fuse.c index 0b05ce9a22bb..0f347723e378 100644 --- a/drivers/soc/qcom/jtag-fuse.c +++ b/drivers/soc/qcom/jtag-fuse.c @@ -152,8 +152,6 @@ static int jtag_fuse_probe(struct platform_device *pdev) drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - /* Store the driver data pointer for use in exported functions */ - fusedrvdata = drvdata; drvdata->dev = &pdev->dev; platform_set_drvdata(pdev, drvdata); @@ -174,6 +172,8 @@ static int jtag_fuse_probe(struct platform_device *pdev) if (!drvdata->base) return -ENOMEM; + /* Store the driver data pointer for use in exported functions */ + fusedrvdata = drvdata; dev_info(dev, "JTag Fuse initialized\n"); return 0; } diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index 28b47ca6112a..504a3263253c 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -114,6 +114,7 @@ struct qmi_client_info { }; static LIST_HEAD(qmi_client_list); static DEFINE_MUTEX(qmi_list_lock); +static DEFINE_MUTEX(qmi_client_release_lock); static DEFINE_MUTEX(notif_add_lock); @@ -417,9 +418,11 @@ static void root_service_service_exit(struct qmi_client_info *data, * Destroy client handle and try connecting when * service comes up again. */ + mutex_lock(&qmi_client_release_lock); data->service_connected = false; qmi_handle_destroy(data->clnt_handle); data->clnt_handle = NULL; + mutex_unlock(&qmi_client_release_lock); } static void root_service_exit_work(struct work_struct *work) diff --git a/drivers/soc/qcom/system_stats.c b/drivers/soc/qcom/system_stats.c index 476d2f6dca27..ba35928a991b 100644 --- a/drivers/soc/qcom/system_stats.c +++ b/drivers/soc/qcom/system_stats.c @@ -154,7 +154,7 @@ static int rpm_stats_write_buf(struct seq_file *m) time = get_time_in_msec(time); seq_printf(m, "\ttime in last mode(msec):%llu\n", time); - time = arch_counter_get_cntpct() - rs.last_exited_at; + time = arch_counter_get_cntvct() - rs.last_exited_at; time = get_time_in_sec(time); seq_printf(m, "\ttime since last mode(sec):%llu\n", time); diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index a8c8e120c348..e0af922a0329 100755 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -1201,7 +1201,7 @@ static void ion_vm_open(struct vm_area_struct *vma) mutex_lock(&buffer->lock); list_add(&vma_list->list, &buffer->vmas); mutex_unlock(&buffer->lock); - pr_debug("%s: adding %p\n", __func__, vma); + pr_debug("%s: adding %pK\n", __func__, vma); } static void ion_vm_close(struct vm_area_struct *vma) @@ -1216,7 +1216,7 @@ static void ion_vm_close(struct vm_area_struct *vma) continue; list_del(&vma_list->list); kfree(vma_list); - pr_debug("%s: deleting %p\n", __func__, vma); + pr_debug("%s: deleting %pK\n", __func__, vma); break; } mutex_unlock(&buffer->lock); diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c index aaea7bed36e1..b2e1a4c1b170 100644 --- a/drivers/staging/android/ion/ion_cma_heap.c +++ b/drivers/staging/android/ion/ion_cma_heap.c @@ -94,7 +94,7 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, /* keep this for memory release */ buffer->priv_virt = info; - dev_dbg(dev, "Allocate buffer %p\n", buffer); + dev_dbg(dev, "Allocate buffer %pK\n", buffer); return 0; err: @@ -107,7 +107,7 @@ static void ion_cma_free(struct ion_buffer *buffer) struct device *dev = buffer->heap->priv; struct ion_cma_buffer_info *info = buffer->priv_virt; - dev_dbg(dev, "Release buffer %p\n", buffer); + dev_dbg(dev, "Release buffer %pK\n", buffer); /* release memory */ dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle); sg_free_table(info->table); @@ -123,7 +123,7 @@ static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer, struct device *dev = heap->priv; struct ion_cma_buffer_info *info = buffer->priv_virt; - dev_dbg(dev, "Return buffer %p physical address %pa\n", buffer, + dev_dbg(dev, "Return buffer %pK physical address %pa\n", buffer, &info->handle); *addr = info->handle; diff --git a/drivers/staging/android/ion/ion_cma_secure_heap.c b/drivers/staging/android/ion/ion_cma_secure_heap.c index d945b9251437..90ae7eb65b65 100644 --- a/drivers/staging/android/ion/ion_cma_secure_heap.c +++ b/drivers/staging/android/ion/ion_cma_secure_heap.c @@ -3,7 +3,7 @@ * * Copyright (C) Linaro 2012 * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson. - * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-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 @@ -501,7 +501,7 @@ retry: /* keep this for memory release */ buffer->priv_virt = info; - dev_dbg(sheap->dev, "Allocate buffer %p\n", buffer); + dev_dbg(sheap->dev, "Allocate buffer %pK\n", buffer); return info; err: @@ -634,7 +634,7 @@ retry: sg = sg_next(sg); } buffer->priv_virt = info; - dev_dbg(sheap->dev, "Allocate buffer %p\n", buffer); + dev_dbg(sheap->dev, "Allocate buffer %pK\n", buffer); return info; err2: @@ -721,7 +721,7 @@ static void ion_secure_cma_free(struct ion_buffer *buffer) struct ion_secure_cma_buffer_info *info = buffer->priv_virt; int ret = 0; - dev_dbg(sheap->dev, "Release buffer %p\n", buffer); + dev_dbg(sheap->dev, "Release buffer %pK\n", buffer); if (msm_secure_v2_is_supported()) ret = msm_unsecure_table(info->table); atomic_sub(buffer->size, &sheap->total_allocated); @@ -743,8 +743,8 @@ static int ion_secure_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer, container_of(heap, struct ion_cma_secure_heap, heap); struct ion_secure_cma_buffer_info *info = buffer->priv_virt; - dev_dbg(sheap->dev, "Return buffer %p physical address 0x%pa\n", buffer, - &info->phys); + dev_dbg(sheap->dev, "Return buffer %pK physical address 0x%pa\n", + buffer, &info->phys); *addr = info->phys; *len = buffer->size; diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c index fd4d45ad8db2..03b2b8a38991 100644 --- a/drivers/staging/android/ion/ion_system_heap.c +++ b/drivers/staging/android/ion/ion_system_heap.c @@ -204,11 +204,16 @@ static struct page *split_page_from_secure_pool(struct ion_system_heap *heap, split_page(page, order); break; } - /* Return the remaining order-0 pages to the pool */ - if (page) - for (j = 1; j < (1 << order); j++) + /* + * Return the remaining order-0 pages to the pool. + * SetPagePrivate flag to mark memory as secure. + */ + if (page) { + for (j = 1; j < (1 << order); j++) { + SetPagePrivate(page + j); free_buffer_page(heap, buffer, page + j, 0); - + } + } got_page: mutex_unlock(&heap->split_page_mutex); diff --git a/drivers/staging/android/ion/msm/msm_ion.c b/drivers/staging/android/ion/msm/msm_ion.c index 592c603b1780..176f22ba570c 100644 --- a/drivers/staging/android/ion/msm/msm_ion.c +++ b/drivers/staging/android/ion/msm/msm_ion.c @@ -711,7 +711,7 @@ long msm_ion_custom_ioctl(struct ion_client *client, } else { handle = ion_import_dma_buf(client, data.flush_data.fd); if (IS_ERR(handle)) { - pr_info("%s: Could not import handle: %p\n", + pr_info("%s: Could not import handle: %pK\n", __func__, handle); return -EINVAL; } @@ -724,8 +724,8 @@ long msm_ion_custom_ioctl(struct ion_client *client, + data.flush_data.length; if (start && check_vaddr_bounds(start, end)) { - pr_err("%s: virtual address %p is out of bounds\n", - __func__, data.flush_data.vaddr); + pr_err("%s: virtual address %pK is out of bounds\n", + __func__, data.flush_data.vaddr); ret = -EINVAL; } else { ret = ion_do_cache_op( diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index b6d76402b726..df3a638510c2 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -933,6 +933,9 @@ static struct of_device_id tsens_match[] = { { .compatible = "qcom,msmfalcon-tsens", .data = (void *)TSENS_CALIB_FUSE_MAP_NONE, }, + { .compatible = "qcom,msmtriton-tsens", + .data = (void *)TSENS_CALIB_FUSE_MAP_NONE, + }, {} }; @@ -5435,7 +5438,8 @@ static int get_device_tree_data(struct platform_device *pdev, tmdev->tsens_type = TSENS_TYPE3; else if (!strcmp(id->compatible, "qcom,msmtitanium-tsens") || (!strcmp(id->compatible, "qcom,msmfalcon-tsens") || - (!strcmp(id->compatible, "qcom,msmhamster-tsens")))) { + (!strcmp(id->compatible, "qcom,msmtriton-tsens") || + (!strcmp(id->compatible, "qcom,msmhamster-tsens"))))) { tmdev->tsens_type = TSENS_TYPE3; tsens_poll_check = 0; } else if (!strcmp(id->compatible, "qcom,msm8952-tsens") || @@ -5457,7 +5461,8 @@ static int get_device_tree_data(struct platform_device *pdev, (!strcmp(id->compatible, "qcom,msmtitanium-tsens")) || (!strcmp(id->compatible, "qcom,msmcobalt-tsens")) || (!strcmp(id->compatible, "qcom,msmfalcon-tsens") || - (!strcmp(id->compatible, "qcom,msmhamster-tsens")))) + (!strcmp(id->compatible, "qcom,msmtriton-tsens") || + (!strcmp(id->compatible, "qcom,msmhamster-tsens"))))) tmdev->tsens_valid_status_check = true; } @@ -5473,7 +5478,8 @@ static int get_device_tree_data(struct platform_device *pdev, (!strcmp(id->compatible, "qcom,msmcobalt-tsens")) || (!strcmp(id->compatible, "qcom,msmhamster-tsens")) || (!strcmp(id->compatible, "qcom,msmfalcon-tsens") || - (!strcmp(id->compatible, "qcom,msmtitanium-tsens")))) { + (!strcmp(id->compatible, "qcom,msmtriton-tsens") || + (!strcmp(id->compatible, "qcom,msmtitanium-tsens"))))) { tmdev->tsens_critical_irq = platform_get_irq_byname(pdev, "tsens-critical"); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index c89ae79763c6..9ef57e5d7d64 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -175,6 +175,9 @@ config USB_F_SUBSET config USB_F_RNDIS tristate +config USB_F_QCRNDIS + tristate + config USB_F_MASS_STORAGE tristate @@ -318,6 +321,14 @@ config USB_CONFIGFS_ECM_SUBSET On hardware that can't implement the full protocol, a simple CDC subset is used, placing fewer demands on USB. +config USB_CONFIGFS_QCRNDIS + bool "RNDIS" + depends on USB_CONFIGFS + depends on RNDIS_IPA + depends on NET + select USB_U_ETHER + select USB_F_QCRNDIS + config USB_CONFIGFS_RNDIS bool "RNDIS" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 9a27deac7978..a213cd4c8377 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -60,3 +60,5 @@ usb_f_cdev-y := f_cdev.o obj-$(CONFIG_USB_F_CDEV) += usb_f_cdev.o usb_f_qdss-y := f_qdss.o u_qdss.o obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o +usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o +obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c index bc319d4fe16c..316967415aa9 100644 --- a/drivers/usb/gadget/function/f_qc_rndis.c +++ b/drivers/usb/gadget/function/f_qc_rndis.c @@ -25,16 +25,17 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> #include <linux/atomic.h> #include "u_ether.h" -#include "u_qc_ether.h" #include "rndis.h" -#include "u_bam_data.h" +#include "u_data_ipa.h" #include <linux/rndis_ipa.h> +#include "configfs.h" unsigned int rndis_dl_max_xfer_size = 9216; module_param(rndis_dl_max_xfer_size, uint, S_IRUGO | S_IWUSR); @@ -86,7 +87,7 @@ MODULE_PARM_DESC(rndis_dl_max_xfer_size, */ struct f_rndis_qc { - struct qc_gether port; + struct usb_function func; u8 ctrl_id, data_id; u8 ethaddr[ETH_ALEN]; u32 vendorID; @@ -94,27 +95,27 @@ struct f_rndis_qc { u8 pkt_alignment_factor; u32 max_pkt_size; const char *manufacturer; - int config; + struct rndis_params *params; atomic_t ioctl_excl; atomic_t open_excl; struct usb_ep *notify; struct usb_request *notify_req; atomic_t notify_count; - struct data_port bam_port; - enum transport_type xport; + struct gadget_ipa_port bam_port; u8 port_num; + u16 cdc_filter; bool net_ready_trigger; }; static struct ipa_usb_init_params rndis_ipa_params; static spinlock_t rndis_lock; static bool rndis_ipa_supported; -static void rndis_qc_open(struct qc_gether *geth); +static void rndis_qc_open(struct f_rndis_qc *rndis); static inline struct f_rndis_qc *func_to_rndis_qc(struct usb_function *f) { - return container_of(f, struct f_rndis_qc, port.func); + return container_of(f, struct f_rndis_qc, func); } /* peak (theoretical) bulk transfer rate in bits-per-second */ @@ -322,10 +323,20 @@ static struct usb_endpoint_descriptor rndis_qc_ss_notify_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), + .wMaxPacketSize = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT), .bInterval = RNDIS_QC_LOG2_STATUS_INTERVAL_MSEC + 4, }; +static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = { + .bLength = sizeof(ss_intr_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT), +}; + static struct usb_ss_ep_comp_descriptor rndis_qc_ss_intr_comp_desc = { .bLength = sizeof(ss_intr_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -333,7 +344,16 @@ static struct usb_ss_ep_comp_descriptor rndis_qc_ss_intr_comp_desc = { /* the following 3 values can be tweaked if necessary */ /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */ - .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT), + .wBytesPerInterval = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT), +}; + +static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = { + .bLength = sizeof(ss_bulk_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ }; static struct usb_endpoint_descriptor rndis_qc_ss_in_desc = { @@ -407,7 +427,7 @@ struct f_rndis_qc *_rndis_qc; static inline int rndis_qc_lock(atomic_t *excl) { - if (atomic_inc_return(excl) == 1) { + if (atomic_inc_return(excl) == 1) return 0; atomic_dec(excl); @@ -421,46 +441,6 @@ static inline void rndis_qc_unlock(atomic_t *excl) /*-------------------------------------------------------------------------*/ -static struct sk_buff *rndis_qc_add_header(struct qc_gether *port, - struct sk_buff *skb) -{ - struct sk_buff *skb2; - - skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); - if (skb2) - rndis_add_hdr(skb2); - - dev_kfree_skb_any(skb); - return skb2; -} - -int rndis_qc_rm_hdr(struct qc_gether *port, - struct sk_buff *skb, - struct sk_buff_head *list) -{ - /* tmp points to a struct rndis_packet_msg_type */ - __le32 *tmp = (void *)skb->data; - - /* MessageType, MessageLength */ - if (cpu_to_le32(RNDIS_MSG_PACKET) - != get_unaligned(tmp++)) { - dev_kfree_skb_any(skb); - return -EINVAL; - } - tmp++; - - /* DataOffset, DataLength */ - if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) { - dev_kfree_skb_any(skb); - return -EOVERFLOW; - } - skb_trim(skb, get_unaligned_le32(tmp++)); - - skb_queue_tail(list, skb); - return 0; -} - - static void rndis_qc_response_available(void *_rndis) { struct f_rndis_qc *rndis = _rndis; @@ -496,12 +476,12 @@ static void rndis_qc_response_complete(struct usb_ep *ep, int status = req->status; struct usb_composite_dev *cdev; - if (!rndis->port.func.config || !rndis->port.func.config->cdev) { + if (!rndis->func.config || !rndis->func.config->cdev) { pr_err("%s(): cdev or config is NULL.\n", __func__); return; } - cdev = rndis->port.func.config->cdev; + cdev = rndis->func.config->cdev; /* after TX: * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) * - RNDIS_RESPONSE_AVAILABLE (status/irq) @@ -544,7 +524,7 @@ static void rndis_qc_command_complete(struct usb_ep *ep, u32 ul_max_xfer_size, dl_max_xfer_size; /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ - status = rndis_msg_parser(rndis->config, (u8 *) req->buf); + status = rndis_msg_parser(rndis->params, (u8 *) req->buf); if (status < 0) pr_err("RNDIS command error %d, %d/%d\n", status, req->actual, req->length); @@ -552,8 +532,8 @@ static void rndis_qc_command_complete(struct usb_ep *ep, buf = (rndis_init_msg_type *)req->buf; if (buf->MessageType == RNDIS_MSG_INIT) { - ul_max_xfer_size = rndis_get_ul_max_xfer_size(rndis->config); - u_bam_data_set_ul_max_xfer_size(ul_max_xfer_size); + ul_max_xfer_size = rndis_get_ul_max_xfer_size(rndis->params); + ipa_data_set_ul_max_xfer_size(ul_max_xfer_size); /* * For consistent data throughput from IPA, it is required to * fine tune aggregation byte limit as 7KB. RNDIS IPA driver @@ -565,11 +545,11 @@ static void rndis_qc_command_complete(struct usb_ep *ep, */ if (rndis_dl_max_xfer_size) dl_max_xfer_size = min_t(u32, rndis_dl_max_xfer_size, - rndis_get_dl_max_xfer_size(rndis->config)); + rndis_get_dl_max_xfer_size(rndis->params)); else dl_max_xfer_size = - rndis_get_dl_max_xfer_size(rndis->config); - u_bam_data_set_dl_max_xfer_size(dl_max_xfer_size); + rndis_get_dl_max_xfer_size(rndis->params); + ipa_data_set_dl_max_xfer_size(dl_max_xfer_size); } } @@ -612,11 +592,11 @@ rndis_qc_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) u32 n; /* return the result */ - buf = rndis_get_next_response(rndis->config, &n); + buf = rndis_get_next_response(rndis->params, &n); if (buf) { memcpy(req->buf, buf, n); req->complete = rndis_qc_response_complete; - rndis_free_response(rndis->config, buf); + rndis_free_response(rndis->params, buf); value = n; } /* else stalls ... spec says to avoid that */ @@ -647,11 +627,31 @@ invalid: return value; } +struct net_device *rndis_qc_get_net(const char *netname) +{ + struct net_device *net_dev; + + net_dev = dev_get_by_name(&init_net, netname); + if (!net_dev) + return ERR_PTR(-EINVAL); + + /* + * Decrement net_dev refcount as it was incremented in + * dev_get_by_name(). + */ + dev_put(net_dev); + return net_dev; +} static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_rndis_qc *rndis = func_to_rndis_qc(f); + struct f_rndis_qc_opts *opts; struct usb_composite_dev *cdev = f->config->cdev; + u8 src_connection_idx; + u8 dst_connection_idx; + enum usb_ctrl usb_bam_type; + int ret; /* we know alt == 0 */ @@ -672,35 +672,28 @@ static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct net_device *net; rndis->net_ready_trigger = false; - if (rndis->port.in_ep->driver_data) { + if (rndis->bam_port.in->driver_data) { DBG(cdev, "reset rndis\n"); - /* rndis->port is needed for disconnecting the BAM data + /* bam_port is needed for disconnecting the BAM data * path. Only after the BAM data path is disconnected, * we can disconnect the port from the network layer. */ - bam_data_disconnect(&rndis->bam_port, USB_FUNC_RNDIS, - rndis->port_num); - - if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA) - gether_qc_disconnect_name(&rndis->port, - "rndis0"); + ipa_data_disconnect(&rndis->bam_port, + USB_IPA_FUNC_RNDIS); } - if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) { + if (!rndis->bam_port.in->desc || !rndis->bam_port.out->desc) { DBG(cdev, "init rndis\n"); if (config_ep_by_speed(cdev->gadget, f, - rndis->port.in_ep) || + rndis->bam_port.in) || config_ep_by_speed(cdev->gadget, f, - rndis->port.out_ep)) { - rndis->port.in_ep->desc = NULL; - rndis->port.out_ep->desc = NULL; + rndis->bam_port.out)) { + rndis->bam_port.in->desc = NULL; + rndis->bam_port.out->desc = NULL; goto fail; } } - /* Avoid ZLPs; they can be troublesome. */ - rndis->port.is_zlp_ok = false; - /* RNDIS should be in the "RNDIS uninitialized" state, * either never activated or after rndis_uninit(). * @@ -713,30 +706,37 @@ static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt) * very long time. We need another call to the link layer * code -- gether_updown(...bool) maybe -- to do it right. */ - rndis->port.cdc_filter = 0; + rndis->cdc_filter = 0; rndis->bam_port.cdev = cdev; - rndis->bam_port.func = &rndis->port.func; - rndis->bam_port.in = rndis->port.in_ep; - rndis->bam_port.out = rndis->port.out_ep; - - if (bam_data_connect(&rndis->bam_port, rndis->xport, - rndis->port_num, USB_FUNC_RNDIS)) + rndis->bam_port.func = &rndis->func; + ipa_data_port_select(USB_IPA_FUNC_RNDIS); + usb_bam_type = usb_bam_get_bam_type(cdev->gadget->name); + + src_connection_idx = usb_bam_get_connection_idx(usb_bam_type, + IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, + rndis->port_num); + dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type, + IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, + rndis->port_num); + if (src_connection_idx < 0 || dst_connection_idx < 0) { + pr_err("%s: usb_bam_get_connection_idx failed\n", + __func__); + return ret; + } + if (ipa_data_connect(&rndis->bam_port, USB_IPA_FUNC_RNDIS, + src_connection_idx, dst_connection_idx)) goto fail; DBG(cdev, "RNDIS RX/TX early activation ...\n"); - if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA) { - net = gether_qc_connect_name(&rndis->port, "rndis0", - false); - } else { - rndis_qc_open(&rndis->port); - net = gether_qc_get_net("rndis0"); - } + rndis_qc_open(rndis); + net = rndis_qc_get_net("rndis0"); if (IS_ERR(net)) return PTR_ERR(net); + opts->net = net; - rndis_set_param_dev(rndis->config, net, - &rndis->port.cdc_filter); + rndis_set_param_dev(rndis->params, net, + &rndis->cdc_filter); } else goto fail; @@ -753,18 +753,13 @@ static void rndis_qc_disable(struct usb_function *f) if (!rndis->notify->driver_data) return; - pr_info("rndis deactivated\n"); + DBG(cdev, "rndis deactivated\n"); - rndis_uninit(rndis->config); - bam_data_disconnect(&rndis->bam_port, USB_FUNC_RNDIS, rndis->port_num); - if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA) - gether_qc_disconnect_name(&rndis->port, "rndis0"); + rndis_uninit(rndis->params); + ipa_data_disconnect(&rndis->bam_port, USB_IPA_FUNC_RNDIS); - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA && - gadget_is_dwc3(cdev->gadget)) { - msm_ep_unconfig(rndis->port.out_ep); - msm_ep_unconfig(rndis->port.in_ep); - } + msm_ep_unconfig(rndis->bam_port.out); + msm_ep_unconfig(rndis->bam_port.in); usb_ep_disable(rndis->notify); rndis->notify->driver_data = NULL; } @@ -789,11 +784,11 @@ static void rndis_qc_suspend(struct usb_function *f) * host case. In case of windows, this RNDIS state machine is * already updated due to receiving of PACKET_FILTER. */ - rndis_flow_control(rndis->config, true); + rndis_flow_control(rndis->params, true); pr_debug("%s(): Disconnecting\n", __func__); } - bam_data_suspend(&rndis->bam_port, rndis->port_num, USB_FUNC_RNDIS, + ipa_data_suspend(&rndis->bam_port, USB_IPA_FUNC_RNDIS, remote_wakeup_allowed); pr_debug("rndis suspended\n"); } @@ -816,12 +811,11 @@ static void rndis_qc_resume(struct usb_function *f) else remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; - bam_data_resume(&rndis->bam_port, rndis->port_num, USB_FUNC_RNDIS, - remote_wakeup_allowed); + ipa_data_resume(&rndis->bam_port, USB_IPA_FUNC_RNDIS, + remote_wakeup_allowed); if (!remote_wakeup_allowed) { - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) - rndis_qc_open(&rndis->port); + rndis_qc_open(rndis); /* * Linux Host doesn't sends RNDIS_MSG_INIT or non-zero value * set with RNDIS_MESSAGE_PACKET_FILTER after performing bus @@ -829,7 +823,7 @@ static void rndis_qc_resume(struct usb_function *f) * explicitly here. For Windows host case is also being * handle with RNDIS state machine. */ - rndis_flow_control(rndis->config, false); + rndis_flow_control(rndis->params, false); } pr_debug("%s: RNDIS resume completed\n", __func__); @@ -844,26 +838,23 @@ static void rndis_qc_resume(struct usb_function *f) * not used to tell whether the link should send packets or not. */ -static void rndis_qc_open(struct qc_gether *geth) +static void rndis_qc_open(struct f_rndis_qc *rndis) { - struct f_rndis_qc *rndis = func_to_rndis_qc(&geth->func); - struct usb_composite_dev *cdev = geth->func.config->cdev; + struct usb_composite_dev *cdev = rndis->func.config->cdev; DBG(cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, rndis_qc_bitrate(cdev->gadget) / 100); - rndis_signal_connect(rndis->config); + rndis_signal_connect(rndis->params); } -static void rndis_qc_close(struct qc_gether *geth) +void ipa_data_flow_control_enable(bool enable, struct rndis_params *param) { - struct f_rndis_qc *rndis = func_to_rndis_qc(&geth->func); - - DBG(geth->func.config->cdev, "%s\n", __func__); - - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_signal_disconnect(rndis->config); + if (enable) + ipa_data_stop_rndis_ipa(USB_IPA_FUNC_RNDIS); + else + ipa_data_start_rndis_ipa(USB_IPA_FUNC_RNDIS); } /*-------------------------------------------------------------------------*/ @@ -875,9 +866,35 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_rndis_qc *rndis = func_to_rndis_qc(f); + struct rndis_params *params; int status; struct usb_ep *ep; + /* maybe allocate device-global string IDs */ + if (rndis_qc_string_defs[0].id == 0) { + + /* control interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_qc_string_defs[0].id = status; + rndis_qc_control_intf.iInterface = status; + + /* data interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_qc_string_defs[1].id = status; + rndis_qc_data_intf.iInterface = status; + + /* IAD iFunction label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_qc_string_defs[2].id = status; + rndis_qc_iad_descriptor.iFunction = status; + } + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -902,13 +919,13 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f) ep = usb_ep_autoconfig(cdev->gadget, &rndis_qc_fs_in_desc); if (!ep) goto fail; - rndis->port.in_ep = ep; + rndis->bam_port.in = ep; ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &rndis_qc_fs_out_desc); if (!ep) goto fail; - rndis->port.out_ep = ep; + rndis->bam_port.out = ep; ep->driver_data = cdev; /* claim */ /* NOTE: a status/notification endpoint is, strictly speaking, @@ -972,33 +989,30 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f) goto fail; } - rndis->port.open = rndis_qc_open; - rndis->port.close = rndis_qc_close; - - status = rndis_register(rndis_qc_response_available, rndis, - bam_data_flow_control_enable); - if (status < 0) + params = rndis_register(rndis_qc_response_available, rndis, + ipa_data_flow_control_enable); + if (params < 0) goto fail; - rndis->config = status; + rndis->params = params; - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_set_host_mac(rndis->config, rndis->ethaddr); + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); + rndis_set_host_mac(rndis->params, rndis->ethaddr); if (rndis->manufacturer && rndis->vendorID && - rndis_set_param_vendor(rndis->config, rndis->vendorID, + rndis_set_param_vendor(rndis->params, rndis->vendorID, rndis->manufacturer)) goto fail; pr_debug("%s(): max_pkt_per_xfer:%d\n", __func__, rndis->ul_max_pkt_per_xfer); - rndis_set_max_pkt_xfer(rndis->config, rndis->ul_max_pkt_per_xfer); + rndis_set_max_pkt_xfer(rndis->params, rndis->ul_max_pkt_per_xfer); /* In case of aggregated packets QC device will request * aliment to 4 (2^2). */ pr_debug("%s(): pkt_alignment_factor:%d\n", __func__, rndis->pkt_alignment_factor); - rndis_set_pkt_alignment_factor(rndis->config, + rndis_set_pkt_alignment_factor(rndis->params, rndis->pkt_alignment_factor); /* NOTE: all that is done without knowing or caring about @@ -1009,7 +1023,7 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f) DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - rndis->port.in_ep->name, rndis->port.out_ep->name, + rndis->bam_port.in->name, rndis->bam_port.out->name, rndis->notify->name); return 0; @@ -1029,24 +1043,31 @@ fail: /* we might as well release our claims on endpoints */ if (rndis->notify) rndis->notify->driver_data = NULL; - if (rndis->port.out_ep->desc) - rndis->port.out_ep->driver_data = NULL; - if (rndis->port.in_ep->desc) - rndis->port.in_ep->driver_data = NULL; + if (rndis->bam_port.out->desc) + rndis->bam_port.out->driver_data = NULL; + if (rndis->bam_port.in->desc) + rndis->bam_port.in->driver_data = NULL; pr_err("%s: can't bind, err %d\n", f->name, status); return status; } +static void rndis_qc_free(struct usb_function *f) +{ + struct f_rndis_qc_opts *opts; + + opts = container_of(f->fi, struct f_rndis_qc_opts, func_inst); + opts->refcnt--; +} + static void rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rndis_qc *rndis = func_to_rndis_qc(f); - unsigned long flags; pr_debug("rndis_qc_unbind: free\n"); - rndis_deregister(rndis->config); + rndis_deregister(rndis->params); if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); @@ -1055,23 +1076,17 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f) kfree(rndis->notify_req->buf); usb_ep_free_request(rndis->notify, rndis->notify_req); - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) { - /* - * call flush_workqueue to make sure that any pending - * disconnect_work() from u_bam_data.c file is being - * flushed before calling this rndis_ipa_cleanup API - * as rndis ipa disconnect API is required to be - * called before this. - */ - bam_data_flush_workqueue(); - rndis_ipa_cleanup(rndis_ipa_params.private); - rndis_ipa_supported = false; - } + /* + * call flush_workqueue to make sure that any pending + * disconnect_work() from u_bam_data.c file is being + * flushed before calling this rndis_ipa_cleanup API + * as rndis ipa disconnect API is required to be + * called before this. + */ + ipa_data_flush_workqueue(); + rndis_ipa_cleanup(rndis_ipa_params.private); + rndis_ipa_supported = false; - spin_lock_irqsave(&rndis_lock, flags); - kfree(rndis); - _rndis_qc = NULL; - spin_unlock_irqrestore(&rndis_lock, flags); } void rndis_ipa_reset_trigger(void) @@ -1099,7 +1114,6 @@ void rndis_net_ready_notify(void) { struct f_rndis_qc *rndis; unsigned long flags; - int port_num; spin_lock_irqsave(&rndis_lock, flags); rndis = _rndis_qc; @@ -1117,19 +1131,7 @@ void rndis_net_ready_notify(void) pr_debug("%s: Set net_ready_trigger", __func__); rndis->net_ready_trigger = true; spin_unlock_irqrestore(&rndis_lock, flags); - port_num = (u_bam_data_func_to_port(USB_FUNC_RNDIS, - RNDIS_QC_ACTIVE_PORT)); - if (port_num < 0) - return; - bam_data_start_rx_tx(port_num); -} - - -/* Some controllers can't support RNDIS ... */ -static inline bool can_support_rndis_qc(struct usb_configuration *c) -{ - /* everything else is *presumably* fine */ - return true; + ipa_data_start_rx_tx(USB_IPA_FUNC_RNDIS); } /** @@ -1144,84 +1146,42 @@ static inline bool can_support_rndis_qc(struct usb_configuration *c) * Caller must have called @gether_setup(). Caller is also responsible * for calling @gether_cleanup() before module unload. */ -int -rndis_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) -{ - return rndis_qc_bind_config_vendor(c, ethaddr, 0, NULL, 1, 0, NULL); -} -int -rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer, - u8 max_pkt_per_xfer, - u8 pkt_alignment_factor, - char *xport_name) +static struct +usb_function *rndis_qc_bind_config_vendor(struct usb_function_instance *fi, + u32 vendorID, const char *manufacturer, + u8 max_pkt_per_xfer, u8 pkt_alignment_factor) { + struct f_rndis_qc_opts *opts = container_of(fi, + struct f_rndis_qc_opts, func_inst); struct f_rndis_qc *rndis; int status; - if (!can_support_rndis_qc(c) || !ethaddr) { - pr_debug("%s: invalid argument\n", __func__); - return -EINVAL; - } - - /* maybe allocate device-global string IDs */ - if (rndis_qc_string_defs[0].id == 0) { - - /* control interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rndis_qc_string_defs[0].id = status; - rndis_qc_control_intf.iInterface = status; - - /* data interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rndis_qc_string_defs[1].id = status; - rndis_qc_data_intf.iInterface = status; - - /* IAD iFunction label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rndis_qc_string_defs[2].id = status; - rndis_qc_iad_descriptor.iFunction = status; - } - /* allocate and initialize one new instance */ status = -ENOMEM; - rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); - if (!rndis) { - pr_err("%s: fail allocate and initialize new instance\n", - __func__); - goto fail; - } - rndis->xport = str_to_xport(xport_name); + opts = container_of(fi, struct f_rndis_qc_opts, func_inst); - /* export host's Ethernet address in CDC format */ - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) { - gether_qc_get_macs(rndis_ipa_params.device_ethaddr, - rndis_ipa_params.host_ethaddr); - pr_debug("setting host_ethaddr=%pM, device_ethaddr=%pM\n", - rndis_ipa_params.host_ethaddr, - rndis_ipa_params.device_ethaddr); - rndis_ipa_supported = true; - ether_addr_copy(rndis->ethaddr, &rndis_ipa_params.host_ethaddr); - rndis_ipa_params.device_ready_notify = rndis_net_ready_notify; - } else - ether_addr_copy(rndis->ethaddr, ethaddr); + opts->refcnt++; + rndis = opts->rndis; - rndis->vendorID = vendorID; - rndis->manufacturer = manufacturer; + rndis->vendorID = opts->vendor_id; + rndis->manufacturer = opts->manufacturer; + /* export host's Ethernet address in CDC format */ + random_ether_addr(rndis_ipa_params.host_ethaddr); + random_ether_addr(rndis_ipa_params.device_ethaddr); + pr_debug("setting host_ethaddr=%pM, device_ethaddr=%pM\n", + rndis_ipa_params.host_ethaddr, + rndis_ipa_params.device_ethaddr); + rndis_ipa_supported = true; + ether_addr_copy(rndis->ethaddr, rndis_ipa_params.host_ethaddr); + rndis_ipa_params.device_ready_notify = rndis_net_ready_notify; /* if max_pkt_per_xfer was not configured set to default value */ rndis->ul_max_pkt_per_xfer = max_pkt_per_xfer ? max_pkt_per_xfer : DEFAULT_MAX_PKT_PER_XFER; - u_bam_data_set_ul_max_pkt_num(rndis->ul_max_pkt_per_xfer); + ipa_data_set_ul_max_pkt_num(rndis->ul_max_pkt_per_xfer); /* * Check no RNDIS aggregation, and alignment if not mentioned, @@ -1241,47 +1201,35 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], DEFAULT_PKT_ALIGNMENT_FACTOR; /* RNDIS activates when the host changes this filter */ - rndis->port.cdc_filter = 0; + rndis->cdc_filter = 0; - /* RNDIS has special (and complex) framing */ - rndis->port.header_len = sizeof(struct rndis_packet_msg_type); - rndis->port.wrap = rndis_qc_add_header; - rndis->port.unwrap = rndis_qc_rm_hdr; - - rndis->port.func.name = "rndis"; - rndis->port.func.strings = rndis_qc_strings; + rndis->func.name = "rndis"; + rndis->func.strings = rndis_qc_strings; /* descriptors are per-instance copies */ - rndis->port.func.bind = rndis_qc_bind; - rndis->port.func.unbind = rndis_qc_unbind; - rndis->port.func.set_alt = rndis_qc_set_alt; - rndis->port.func.setup = rndis_qc_setup; - rndis->port.func.disable = rndis_qc_disable; - rndis->port.func.suspend = rndis_qc_suspend; - rndis->port.func.resume = rndis_qc_resume; + rndis->func.bind = rndis_qc_bind; + rndis->func.unbind = rndis_qc_unbind; + rndis->func.set_alt = rndis_qc_set_alt; + rndis->func.setup = rndis_qc_setup; + rndis->func.disable = rndis_qc_disable; + rndis->func.suspend = rndis_qc_suspend; + rndis->func.resume = rndis_qc_resume; + rndis->func.free_func = rndis_qc_free; _rndis_qc = rndis; - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) { - status = rndis_ipa_init(&rndis_ipa_params); - if (status) { - pr_err("%s: failed to init rndis_ipa\n", __func__); - goto fail; - } - } - - status = usb_add_function(c, &rndis->port.func); + status = rndis_ipa_init(&rndis_ipa_params); if (status) { - if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) - rndis_ipa_cleanup(rndis_ipa_params.private); - goto fail; + pr_err("%s: failed to init rndis_ipa\n", __func__); + kfree(rndis); + return ERR_PTR(status); } - return 0; + return &rndis->func; +} -fail: - kfree(rndis); - _rndis_qc = NULL; - return status; +static struct usb_function *qcrndis_alloc(struct usb_function_instance *fi) +{ + return rndis_qc_bind_config_vendor(fi, 0, NULL, 1, 0); } static int rndis_qc_open_dev(struct inode *ip, struct file *fp) @@ -1370,24 +1318,100 @@ static struct miscdevice rndis_qc_device = { .fops = &rndis_qc_fops, }; -static int rndis_qc_init(void) +static void qcrndis_free_inst(struct usb_function_instance *f) { + struct f_rndis_qc *rndis; + struct f_rndis_qc_opts *opts = container_of(f, + struct f_rndis_qc_opts, func_inst); + unsigned long flags; + + rndis = opts->rndis; + misc_deregister(&rndis_qc_device); + + ipa_data_free(USB_IPA_FUNC_RNDIS); + spin_lock_irqsave(&rndis_lock, flags); + kfree(rndis); + _rndis_qc = NULL; + kfree(opts->rndis); + kfree(opts); + spin_unlock_irqrestore(&rndis_lock, flags); +} + +static int qcrndis_set_inst_name(struct usb_function_instance *fi, + const char *name) +{ + struct f_rndis_qc_opts *opts = container_of(fi, + struct f_rndis_qc_opts, func_inst); + struct f_rndis_qc *rndis; + int name_len; int ret; - pr_info("initialize rndis QC instance\n"); + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + pr_debug("initialize rndis QC instance\n"); + rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); + if (!rndis) { + pr_err("%s: fail allocate and initialize new instance\n", + __func__); + return -ENOMEM; + } + + opts->rndis = rndis; ret = misc_register(&rndis_qc_device); if (ret) pr_err("rndis QC driver failed to register\n"); spin_lock_init(&rndis_lock); - ret = bam_data_setup(USB_FUNC_RNDIS, RNDIS_QC_NO_PORTS); + ret = ipa_data_setup(USB_IPA_FUNC_RNDIS); if (ret) { pr_err("bam_data_setup failed err: %d\n", ret); + kfree(rndis); return ret; } - return ret; + return 0; +} + +static inline +struct f_rndis_qc_opts *to_f_qc_rndis_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_rndis_qc_opts, + func_inst.group); +} + +static void qcrndis_attr_release(struct config_item *item) +{ + struct f_rndis_qc_opts *opts = to_f_qc_rndis_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations qcrndis_item_ops = { + .release = qcrndis_attr_release, +}; + +static struct config_item_type qcrndis_func_type = { + .ct_item_ops = &qcrndis_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct usb_function_instance *qcrndis_alloc_inst(void) +{ + struct f_rndis_qc_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.set_inst_name = qcrndis_set_inst_name; + opts->func_inst.free_func_inst = qcrndis_free_inst; + + config_group_init_type_name(&opts->func_inst.group, "", + &qcrndis_func_type); + + return &opts->func_inst; } static void rndis_qc_cleanup(void) @@ -1416,3 +1440,27 @@ bool rndis_qc_get_skip_ep_config(void) { return rndis_ipa_params.skip_ep_cfg; } + +DECLARE_USB_FUNCTION_INIT(qcrndis, qcrndis_alloc_inst, qcrndis_alloc); + +static int __init usb_qcrndis_init(void) +{ + int ret; + + ret = usb_function_register(&qcrndisusb_func); + if (ret) { + pr_err("%s: failed to register diag %d\n", __func__, ret); + return ret; + } + return ret; +} + +static void __exit usb_qcrndis_exit(void) +{ + usb_function_unregister(&qcrndisusb_func); + rndis_qc_cleanup(); +} + +module_init(usb_qcrndis_init); +module_exit(usb_qcrndis_exit); +MODULE_DESCRIPTION("USB RMNET Function Driver"); diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index b0e7b65b84bd..98ac1ff58323 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -596,6 +596,7 @@ static int rndis_init_response(struct rndis_params *params, resp->AFListOffset = cpu_to_le32(0); resp->AFListSize = cpu_to_le32(0); + params->ul_max_xfer_size = le32_to_cpu(resp->MaxTransferSize); params->resp_avail(params->v); return 0; } @@ -799,7 +800,7 @@ EXPORT_SYMBOL_GPL(rndis_set_host_mac); */ int rndis_msg_parser(struct rndis_params *params, u8 *buf) { - u32 MsgType, MsgLength; + u32 MsgType, MsgLength, major, minor, max_transfer_size; __le32 *tmp; if (!buf) @@ -822,6 +823,19 @@ int rndis_msg_parser(struct rndis_params *params, u8 *buf) case RNDIS_MSG_INIT: pr_debug("%s: RNDIS_MSG_INIT\n", __func__); + major = get_unaligned_le32(tmp++); + minor = get_unaligned_le32(tmp++); + max_transfer_size = get_unaligned_le32(tmp++); + + params->host_rndis_major_ver = major; + params->host_rndis_minor_ver = minor; + params->dl_max_xfer_size = max_transfer_size; + + pr_debug("%s(): RNDIS Host Major:%d Minor:%d version\n", + __func__, major, minor); + pr_debug("%s(): UL Max Transfer size:%x\n", __func__, + max_transfer_size); + params->state = RNDIS_INITIALIZED; return rndis_init_response(params, (rndis_init_msg_type *)buf); @@ -1013,6 +1027,18 @@ int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed) } EXPORT_SYMBOL_GPL(rndis_set_param_medium); +u32 rndis_get_dl_max_xfer_size(struct rndis_params *params) +{ + pr_debug("%s:\n", __func__); + return params->dl_max_xfer_size; +} + +u32 rndis_get_ul_max_xfer_size(struct rndis_params *params) +{ + pr_debug("%s:\n", __func__); + return params->ul_max_xfer_size; +} + void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer) { pr_debug("%s:\n", __func__); diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index 939c3bebe015..3d130b0576fc 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -199,6 +199,10 @@ typedef struct rndis_params void *v; struct list_head resp_queue; + u32 host_rndis_major_ver; + u32 host_rndis_minor_ver; + u32 ul_max_xfer_size; + u32 dl_max_xfer_size; } rndis_params; /* RNDIS Message parser and other useless functions */ @@ -213,6 +217,8 @@ int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed); void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer); +u32 rndis_get_ul_max_xfer_size(struct rndis_params *params); +u32 rndis_get_dl_max_xfer_size(struct rndis_params *params); void rndis_add_hdr(struct sk_buff *skb); int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, struct sk_buff_head *list); diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c index 3a5b1e2da2e6..56e7dea427ec 100644 --- a/drivers/usb/gadget/function/u_data_ipa.c +++ b/drivers/usb/gadget/function/u_data_ipa.c @@ -22,37 +22,49 @@ #include <linux/termios.h> #include <linux/usb_bam.h> -#include "usb_gadget_xport.h" +#include "u_data_ipa.h" -#define IPA_N_PORTS 4 struct ipa_data_ch_info { - struct usb_request *rx_req; - struct usb_request *tx_req; - unsigned long flags; - unsigned id; - enum transport_type trans; - enum gadget_type gtype; - bool is_connected; - unsigned port_num; - spinlock_t port_lock; - - struct work_struct connect_w; - struct work_struct disconnect_w; - struct work_struct suspend_w; - struct work_struct resume_w; - - u32 src_pipe_idx; - u32 dst_pipe_idx; - u8 src_connection_idx; - u8 dst_connection_idx; - enum usb_ctrl usb_bam_type; - struct gadget_ipa_port *port_usb; + struct usb_request *rx_req; + struct usb_request *tx_req; + unsigned long flags; + unsigned id; + enum ipa_func_type func_type; + bool is_connected; + unsigned port_num; + spinlock_t port_lock; + + struct work_struct connect_w; + struct work_struct disconnect_w; + struct work_struct suspend_w; + struct work_struct resume_w; + + u32 src_pipe_idx; + u32 dst_pipe_idx; + u8 src_connection_idx; + u8 dst_connection_idx; + enum usb_ctrl usb_bam_type; + struct gadget_ipa_port *port_usb; + struct usb_gadget *gadget; + atomic_t pipe_connect_notified; struct usb_bam_connect_ipa_params ipa_params; }; -static int n_ipa_ports; +struct rndis_data_ch_info { + /* this provides downlink (device->host i.e host) side configuration*/ + u32 dl_max_transfer_size; + /* this provides uplink (host->device i.e device) side configuration */ + u32 ul_max_transfer_size; + u32 ul_max_packets_number; + bool ul_aggregation_enable; + u32 prod_clnt_hdl; + u32 cons_clnt_hdl; + void *priv; +}; + static struct workqueue_struct *ipa_data_wq; struct ipa_data_ch_info *ipa_data_ports[IPA_N_PORTS]; +static struct rndis_data_ch_info *rndis_data; /** * ipa_data_endless_complete() - completion callback for endless TX/RX request * @ep: USB endpoint for which this completion happen @@ -132,6 +144,56 @@ static void ipa_data_stop_endless_xfer(struct ipa_data_ch_info *port, bool in) } } +/* + * Called when IPA triggers us that the network interface is up. + * Starts the transfers on bulk endpoints. + * (optimization reasons, the pipes and bam with IPA are already connected) + */ +void ipa_data_start_rx_tx(enum ipa_func_type func) +{ + struct ipa_data_ch_info *port; + unsigned long flags; + + pr_debug("%s: Triggered: starting tx, rx", __func__); + /* queue in & out requests */ + port = ipa_data_ports[func]; + if (!port) { + pr_err("%s: port is NULL, can't start tx, rx", __func__); + return; + } + + spin_lock_irqsave(&port->port_lock, flags); + + if (!port->port_usb || !port->port_usb->in || + !port->port_usb->out) { + pr_err("%s: Can't start tx, rx, ep not enabled", __func__); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + if (!port->rx_req || !port->tx_req) { + pr_err("%s: No request d->rx_req=%p, d->tx_req=%p", __func__, + port->rx_req, port->tx_req); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + if (!port->is_connected) { + pr_debug("%s: pipes are disconnected", __func__); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + spin_unlock_irqrestore(&port->port_lock, flags); + + /* queue in & out requests */ + pr_debug("%s: Starting rx", __func__); + if (port->port_usb->out) + ipa_data_start_endless_xfer(port, false); + + pr_debug("%s: Starting tx", __func__); + if (port->port_usb->in) + ipa_data_start_endless_xfer(port, true); +} /** * ipa_data_disconnect_work() - Perform USB IPA BAM disconnect * @w: disconnect work @@ -166,6 +228,23 @@ static void ipa_data_disconnect_work(struct work_struct *w) if (ret) pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret); + if (port->func_type == USB_IPA_FUNC_RNDIS) { + /* + * NOTE: it is required to disconnect USB and IPA BAM related + * pipes before calling IPA tethered function related disconnect + * API. IPA tethered function related disconnect API delete + * depedency graph with IPA RM which would results into IPA not + * pulling data although there is pending data on USB BAM + * producer pipe. + */ + if (atomic_xchg(&port->pipe_connect_notified, 0) == 1) { + void *priv; + + priv = rndis_qc_get_ipa_priv(); + rndis_ipa_pipe_disconnect_notify(priv); + } + } + if (port->ipa_params.prod_clnt_hdl) usb_bam_free_fifos(port->usb_bam_type, port->dst_connection_idx); @@ -173,6 +252,12 @@ static void ipa_data_disconnect_work(struct work_struct *w) usb_bam_free_fifos(port->usb_bam_type, port->src_connection_idx); + /* + * Decrement usage count which was incremented + * upon cable connect or cable disconnect in suspended state. + */ + usb_gadget_autopm_put_async(port->gadget); + pr_debug("%s(): disconnect work completed.\n", __func__); } @@ -186,15 +271,15 @@ static void ipa_data_disconnect_work(struct work_struct *w) * switch is being trigger. This API performs restoring USB endpoint operation * and disable USB endpoint used for accelerated path. */ -void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num) +void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func) { struct ipa_data_ch_info *port; unsigned long flags; struct usb_gadget *gadget = NULL; - pr_debug("dev:%p port number:%d\n", gp, port_num); - if (port_num >= n_ipa_ports) { - pr_err("invalid ipa portno#%d\n", port_num); + pr_debug("dev:%p port number:%d\n", gp, func); + if (func >= USB_IPA_NUM_FUNCS) { + pr_err("invalid ipa portno#%d\n", func); return; } @@ -203,9 +288,9 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num) return; } - port = ipa_data_ports[port_num]; + port = ipa_data_ports[func]; if (!port) { - pr_err("port %u is NULL", port_num); + pr_err("port %u is NULL", func); return; } @@ -223,8 +308,7 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num) * complete function will be called, where we try * to obtain the spinlock as well. */ - if (gadget_is_dwc3(gadget)) - msm_ep_unconfig(port->port_usb->in); + msm_ep_unconfig(port->port_usb->in); spin_unlock_irqrestore(&port->port_lock, flags); usb_ep_disable(port->port_usb->in); spin_lock_irqsave(&port->port_lock, flags); @@ -232,8 +316,7 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num) } if (port->port_usb->out) { - if (gadget_is_dwc3(gadget)) - msm_ep_unconfig(port->port_usb->out); + msm_ep_unconfig(port->port_usb->out); spin_unlock_irqrestore(&port->port_lock, flags); usb_ep_disable(port->port_usb->out); spin_lock_irqsave(&port->port_lock, flags); @@ -257,14 +340,14 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num) */ static void configure_fifo(enum usb_ctrl bam_type, u8 idx, struct usb_ep *ep) { - struct u_bam_data_connect_info bam_info; struct sps_mem_buffer data_fifo = {0}; + u32 usb_bam_pipe_idx; get_bam2bam_connection_info(bam_type, idx, - &bam_info.usb_bam_pipe_idx, + &usb_bam_pipe_idx, NULL, &data_fifo, NULL); msm_data_fifo_config(ep, data_fifo.phys_base, data_fifo.size, - bam_info.usb_bam_pipe_idx); + usb_bam_pipe_idx); } /** @@ -308,8 +391,21 @@ static void ipa_data_connect_work(struct work_struct *w) return; } + /* + * check if connect_w got called two times during RNDIS resume as + * explicit flow control is called to start data transfers after + * ipa_data_connect() + */ + if (port->is_connected) { + pr_debug("IPA connect is already done & Transfers started\n"); + spin_unlock_irqrestore(&port->port_lock, flags); + usb_gadget_autopm_put_async(port->gadget); + return; + } + gport->ipa_consumer_ep = -1; gport->ipa_producer_ep = -1; + if (gport->out) { port->rx_req = usb_ep_alloc_request(gport->out, GFP_ATOMIC); if (!port->rx_req) { @@ -341,8 +437,7 @@ static void ipa_data_connect_work(struct work_struct *w) /* update IPA Parameteres here. */ port->ipa_params.usb_connection_speed = gadget->speed; - if (gadget_is_dwc3(gadget)) - port->ipa_params.reset_pipe_after_lpm = + port->ipa_params.reset_pipe_after_lpm = msm_dwc3_reset_ep_after_lpm(gadget); port->ipa_params.skip_ep_cfg = true; port->ipa_params.keep_ipa_awake = true; @@ -354,49 +449,35 @@ static void ipa_data_connect_work(struct work_struct *w) usb_bam_alloc_fifos(port->usb_bam_type, port->src_connection_idx); - if (gadget_is_dwc3(gadget)) { - sps_params = MSM_SPS_MODE | MSM_DISABLE_WB - | MSM_PRODUCER | port->src_pipe_idx; - port->rx_req->length = 32*1024; - port->rx_req->udc_priv = sps_params; - configure_fifo(port->usb_bam_type, - port->src_connection_idx, - port->port_usb->out); - ret = msm_ep_config(gport->out, port->rx_req, - GFP_ATOMIC); - if (ret) { - pr_err("msm_ep_config() failed for OUT EP\n"); - usb_bam_free_fifos(port->usb_bam_type, - port->src_connection_idx); - goto free_rx_tx_req; - } - } else { - sps_params = (MSM_SPS_MODE | port->src_pipe_idx | - MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER; - port->rx_req->udc_priv = sps_params; + sps_params = MSM_SPS_MODE | MSM_DISABLE_WB + | MSM_PRODUCER | port->src_pipe_idx; + port->rx_req->length = 32*1024; + port->rx_req->udc_priv = sps_params; + configure_fifo(port->usb_bam_type, + port->src_connection_idx, + port->port_usb->out); + ret = msm_ep_config(gport->out); + if (ret) { + pr_err("msm_ep_config() failed for OUT EP\n"); + usb_bam_free_fifos(port->usb_bam_type, + port->src_connection_idx); + goto free_rx_tx_req; } } if (gport->in) { usb_bam_alloc_fifos(port->usb_bam_type, port->dst_connection_idx); - if (gadget_is_dwc3(gadget)) { - sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | - port->dst_pipe_idx; - port->tx_req->length = 32*1024; - port->tx_req->udc_priv = sps_params; - configure_fifo(port->usb_bam_type, - port->dst_connection_idx, gport->in); - ret = msm_ep_config(gport->in, port->tx_req, - GFP_ATOMIC); - if (ret) { - pr_err("msm_ep_config() failed for IN EP\n"); - goto unconfig_msm_ep_out; - } - } else { - sps_params = (MSM_SPS_MODE | port->dst_pipe_idx | - MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER; - port->tx_req->udc_priv = sps_params; + sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | + port->dst_pipe_idx; + port->tx_req->length = 32*1024; + port->tx_req->udc_priv = sps_params; + configure_fifo(port->usb_bam_type, + port->dst_connection_idx, gport->in); + ret = msm_ep_config(gport->in); + if (ret) { + pr_err("msm_ep_config() failed for IN EP\n"); + goto unconfig_msm_ep_out; } } @@ -410,13 +491,20 @@ static void ipa_data_connect_work(struct work_struct *w) if (gport->out) { pr_debug("configure bam ipa connect for USB OUT\n"); port->ipa_params.dir = USB_TO_PEER_PERIPHERAL; + + if (port->func_type == USB_IPA_FUNC_RNDIS) { + port->ipa_params.notify = rndis_qc_get_ipa_rx_cb(); + port->ipa_params.priv = rndis_qc_get_ipa_priv(); + port->ipa_params.skip_ep_cfg = + rndis_qc_get_skip_ep_config(); + } + ret = usb_bam_connect_ipa(port->usb_bam_type, &port->ipa_params); if (ret) { pr_err("usb_bam_connect_ipa out failed err:%d\n", ret); goto unconfig_msm_ep_in; } - gadget->bam2bam_func_enabled = true; gport->ipa_consumer_ep = port->ipa_params.ipa_cons_ep_idx; is_ipa_disconnected = false; @@ -425,30 +513,71 @@ static void ipa_data_connect_work(struct work_struct *w) if (gport->in) { pr_debug("configure bam ipa connect for USB IN\n"); port->ipa_params.dir = PEER_PERIPHERAL_TO_USB; - port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS; + + if (port->func_type == USB_IPA_FUNC_RNDIS) { + port->ipa_params.notify = rndis_qc_get_ipa_tx_cb(); + port->ipa_params.priv = rndis_qc_get_ipa_priv(); + port->ipa_params.skip_ep_cfg = + rndis_qc_get_skip_ep_config(); + } + + if (port->func_type == USB_IPA_FUNC_DPL) + port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS; ret = usb_bam_connect_ipa(port->usb_bam_type, &port->ipa_params); if (ret) { pr_err("usb_bam_connect_ipa IN failed err:%d\n", ret); goto disconnect_usb_bam_ipa_out; } - gadget->bam2bam_func_enabled = true; gport->ipa_producer_ep = port->ipa_params.ipa_prod_ep_idx; is_ipa_disconnected = false; } - pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n", - gport->ipa_producer_ep, - gport->ipa_consumer_ep); + /* For DPL need to update_ipa_pipes to qti */ + if (port->func_type == USB_IPA_FUNC_RNDIS) { + rndis_data->prod_clnt_hdl = + port->ipa_params.prod_clnt_hdl; + rndis_data->cons_clnt_hdl = + port->ipa_params.cons_clnt_hdl; + rndis_data->priv = port->ipa_params.priv; + + pr_debug("ul_max_transfer_size:%d\n", + rndis_data->ul_max_transfer_size); + pr_debug("ul_max_packets_number:%d\n", + rndis_data->ul_max_packets_number); + pr_debug("dl_max_transfer_size:%d\n", + rndis_data->dl_max_transfer_size); + + ret = rndis_ipa_pipe_connect_notify( + rndis_data->cons_clnt_hdl, + rndis_data->prod_clnt_hdl, + rndis_data->ul_max_transfer_size, + rndis_data->ul_max_packets_number, + rndis_data->dl_max_transfer_size, + rndis_data->priv); + if (ret) { + pr_err("%s: failed to connect IPA: err:%d\n", + __func__, ret); + return; + } + atomic_set(&port->pipe_connect_notified, 1); + } - gqti_ctrl_update_ipa_pipes(NULL, DPL_QTI_CTRL_PORT_NO, + pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n", gport->ipa_producer_ep, gport->ipa_consumer_ep); pr_debug("src_bam_idx:%d dst_bam_idx:%d\n", port->src_connection_idx, port->dst_connection_idx); + /* Don't queue the transfers yet, only after network stack is up */ + if (port->func_type == USB_IPA_FUNC_RNDIS) { + pr_debug("%s: Not starting now, waiting for network notify", + __func__); + return; + } + if (gport->out) ipa_data_start_endless_xfer(port, false); if (gport->in) @@ -496,7 +625,7 @@ free_rx_req: * initiate USB BAM IPA connection. This API is enabling accelerated endpoints * and schedule connect_work() which establishes USB IPA BAM communication. */ -int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, +int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, u8 src_connection_idx, u8 dst_connection_idx) { struct ipa_data_ch_info *port; @@ -504,10 +633,10 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, int ret; pr_debug("dev:%p port#%d src_connection_idx:%d dst_connection_idx:%d\n", - gp, port_num, src_connection_idx, dst_connection_idx); + gp, func, src_connection_idx, dst_connection_idx); - if (port_num >= n_ipa_ports) { - pr_err("invalid portno#%d\n", port_num); + if (func >= USB_IPA_NUM_FUNCS) { + pr_err("invalid portno#%d\n", func); ret = -ENODEV; goto err; } @@ -518,10 +647,11 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, goto err; } - port = ipa_data_ports[port_num]; + port = ipa_data_ports[func]; spin_lock_irqsave(&port->port_lock, flags); port->port_usb = gp; + port->gadget = gp->cdev->gadget; port->src_connection_idx = src_connection_idx; port->dst_connection_idx = dst_connection_idx; port->usb_bam_type = usb_bam_get_bam_type(gp->cdev->gadget->name); @@ -565,6 +695,19 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, goto err_usb_in; } + /* Wait for host to enable flow_control */ + if (port->func_type == USB_IPA_FUNC_RNDIS) { + ret = 0; + goto err_usb_in; + } + + /* + * Increment usage count upon cable connect. Decrement after IPA + * handshake is done in disconnect work (due to cable disconnect) + * or in suspend work. + */ + usb_gadget_autopm_get_noresume(port->gadget); + queue_work(ipa_data_wq, &port->connect_w); spin_unlock_irqrestore(&port->port_lock, flags); @@ -642,6 +785,12 @@ static void ipa_data_stop(void *param, enum usb_bam_pipe_dir dir) } } +void ipa_data_flush_workqueue(void) +{ + pr_debug("%s(): Flushing workqueue\n", __func__); + flush_workqueue(ipa_data_wq); +} + /** * ipa_data_suspend() - Initiate USB BAM IPA suspend functionality * @gp: Gadget IPA port @@ -650,15 +799,14 @@ static void ipa_data_stop(void *param, enum usb_bam_pipe_dir dir) * It is being used to initiate USB BAM IPA suspend functionality * for USB bus suspend functionality. */ -void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num) +void ipa_data_suspend(struct gadget_ipa_port *gp, enum ipa_func_type func, + bool remote_wakeup_enabled) { struct ipa_data_ch_info *port; - int ret; - - pr_debug("dev:%p port number:%d\n", gp, port_num); + unsigned long flags; - if (port_num >= n_ipa_ports) { - pr_err("invalid ipa portno#%d\n", port_num); + if (func >= USB_IPA_NUM_FUNCS) { + pr_err("invalid ipa portno#%d\n", func); return; } @@ -666,14 +814,61 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num) pr_err("data port is null\n"); return; } + pr_debug("%s: suspended port %d\n", __func__, func); - port = ipa_data_ports[port_num]; + port = ipa_data_ports[func]; if (!port) { - pr_err("port %u is NULL", port_num); + pr_err("%s(): Port is NULL.\n", __func__); + return; + } + + /* suspend with remote wakeup disabled */ + if (!remote_wakeup_enabled) { + /* + * When remote wakeup is disabled, IPA BAM is disconnected + * because it cannot send new data until the USB bus is resumed. + * Endpoint descriptors info is saved before it gets reset by + * the BAM disconnect API. This lets us restore this info when + * the USB bus is resumed. + */ + gp->in_ep_desc_backup = gp->in->desc; + gp->out_ep_desc_backup = gp->out->desc; + + pr_debug("in_ep_desc_backup = %p, out_ep_desc_backup = %p", + gp->in_ep_desc_backup, + gp->out_ep_desc_backup); + + ipa_data_disconnect(gp, func); return; } + spin_lock_irqsave(&port->port_lock, flags); + queue_work(ipa_data_wq, &port->suspend_w); + spin_unlock_irqrestore(&port->port_lock, flags); +} +static void bam2bam_data_suspend_work(struct work_struct *w) +{ + struct ipa_data_ch_info *port = container_of(w, struct ipa_data_ch_info, + connect_w); + unsigned long flags; + int ret; + pr_debug("%s: suspend started\n", __func__); + spin_lock_irqsave(&port->port_lock, flags); + + /* In case of RNDIS, host enables flow_control invoking connect_w. If it + * is delayed then we may end up having suspend_w run before connect_w. + * In this scenario, connect_w may or may not at all start if cable gets + * disconnected or if host changes configuration e.g. RNDIS --> MBIM + * For these cases don't do runtime_put as there was no _get yet, and + * detect this condition on disconnect to not do extra pm_runtme_get + * for SUSPEND --> DISCONNECT scenario. + */ + if (!port->is_connected) { + pr_err("%s: Not yet connected. SUSPEND pending.\n", __func__); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } ret = usb_bam_register_wake_cb(port->usb_bam_type, port->dst_connection_idx, NULL, port); if (ret) { @@ -685,7 +880,23 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num) usb_bam_register_start_stop_cbs(port->usb_bam_type, port->dst_connection_idx, ipa_data_start, ipa_data_stop, port); + /* + * release lock here because bam_data_start() or + * bam_data_stop() called from usb_bam_suspend() + * re-acquires port lock. + */ + spin_unlock_irqrestore(&port->port_lock, flags); usb_bam_suspend(port->usb_bam_type, &port->ipa_params); + spin_lock_irqsave(&port->port_lock, flags); + + /* + * Decrement usage count after IPA handshake is done + * to allow gadget parent to go to lpm. This counter was + * incremented upon cable connect. + */ + usb_gadget_autopm_put_async(port->gadget); + + spin_unlock_irqrestore(&port->port_lock, flags); } /** @@ -696,17 +907,20 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num) * It is being used to initiate USB resume functionality * for USB bus resume case. */ -void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num) +void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func, + bool remote_wakeup_enabled) { struct ipa_data_ch_info *port; unsigned long flags; struct usb_gadget *gadget = NULL; - int ret; + u8 src_connection_idx; + u8 dst_connection_idx; + enum usb_ctrl usb_bam_type; - pr_debug("dev:%p port number:%d\n", gp, port_num); + pr_debug("dev:%p port number:%d\n", gp, func); - if (port_num >= n_ipa_ports) { - pr_err("invalid ipa portno#%d\n", port_num); + if (func >= USB_IPA_NUM_FUNCS) { + pr_err("invalid ipa portno#%d\n", func); return; } @@ -715,12 +929,66 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num) return; } - port = ipa_data_ports[port_num]; + port = ipa_data_ports[func]; if (!port) { - pr_err("port %u is NULL", port_num); + pr_err("port %u is NULL", func); + return; + } + + gadget = gp->cdev->gadget; + /* resume with remote wakeup disabled */ + if (!remote_wakeup_enabled) { + /* Restore endpoint descriptors info. */ + gp->in->desc = gp->in_ep_desc_backup; + gp->out->desc = gp->out_ep_desc_backup; + + pr_debug("in_ep_desc_backup = %p, out_ep_desc_backup = %p", + gp->in_ep_desc_backup, + gp->out_ep_desc_backup); + usb_bam_type = usb_bam_get_bam_type(gadget->name); + src_connection_idx = usb_bam_get_connection_idx(usb_bam_type, + IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, + 0); + dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type, + IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, + 0); + ipa_data_connect(gp, func, + src_connection_idx, dst_connection_idx); return; } + spin_lock_irqsave(&port->port_lock, flags); + + /* + * Increment usage count here to disallow gadget + * parent suspend. This counter will decrement + * after IPA handshake is done in disconnect work + * (due to cable disconnect) or in bam_data_disconnect + * in suspended state. + */ + usb_gadget_autopm_get_noresume(port->gadget); + queue_work(ipa_data_wq, &port->resume_w); + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static void bam2bam_data_resume_work(struct work_struct *w) +{ + struct ipa_data_ch_info *port = container_of(w, struct ipa_data_ch_info, + connect_w); + struct usb_gadget *gadget; + unsigned long flags; + int ret; + + if (!port->port_usb->cdev) { + pr_err("!port->port_usb->cdev is NULL"); + goto exit; + } + + if (!port->port_usb->cdev->gadget) { + pr_err("!port->port_usb->cdev->gadget is NULL"); + goto exit; + } + pr_debug("%s: resume started\n", __func__); spin_lock_irqsave(&port->port_lock, flags); gadget = port->port_usb->cdev->gadget; @@ -750,6 +1018,7 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num) usb_bam_resume(port->usb_bam_type, &port->ipa_params); } +exit: spin_unlock_irqrestore(&port->port_lock, flags); } @@ -762,12 +1031,12 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num) * * Retrun: 0 in case of success, otherwise errno. */ -static int ipa_data_port_alloc(int portno) +static int ipa_data_port_alloc(enum ipa_func_type func) { struct ipa_data_ch_info *port = NULL; - if (ipa_data_ports[portno] != NULL) { - pr_debug("port %d already allocated.\n", portno); + if (ipa_data_ports[func] != NULL) { + pr_debug("port %d already allocated.\n", func); return 0; } @@ -775,29 +1044,29 @@ static int ipa_data_port_alloc(int portno) if (!port) return -ENOMEM; - ipa_data_ports[portno] = port; + ipa_data_ports[func] = port; - pr_debug("port:%p with portno:%d allocated\n", port, portno); + pr_debug("port:%p with portno:%d allocated\n", port, func); return 0; } /** * ipa_data_port_select() - Select particular port for BAM2BAM IPA mode * @portno: port number to be used by particular USB function - * @gtype: USB gadget function type + * @func_type: USB gadget function type * * It is being used by USB function driver to select which BAM2BAM IPA * port particular USB function wants to use. * */ -void ipa_data_port_select(int portno, enum gadget_type gtype) +void ipa_data_port_select(enum ipa_func_type func) { struct ipa_data_ch_info *port = NULL; - pr_debug("portno:%d\n", portno); + pr_debug("portno:%d\n", func); - port = ipa_data_ports[portno]; - port->port_num = portno; + port = ipa_data_ports[func]; + port->port_num = func; port->is_connected = false; spin_lock_init(&port->port_lock); @@ -808,14 +1077,30 @@ void ipa_data_port_select(int portno, enum gadget_type gtype) if (!work_pending(&port->disconnect_w)) INIT_WORK(&port->disconnect_w, ipa_data_disconnect_work); + INIT_WORK(&port->suspend_w, bam2bam_data_suspend_work); + INIT_WORK(&port->resume_w, bam2bam_data_resume_work); + port->ipa_params.src_client = IPA_CLIENT_USB_PROD; port->ipa_params.dst_client = IPA_CLIENT_USB_CONS; - port->gtype = gtype; + port->func_type = func; }; +void ipa_data_free(enum ipa_func_type func) +{ + pr_debug("freeing %d IPA BAM port", func); + + kfree(ipa_data_ports[func]); + ipa_data_ports[func] = NULL; + if (func == USB_IPA_FUNC_RNDIS) + kfree(rndis_data); + if (ipa_data_wq) { + destroy_workqueue(ipa_data_wq); + ipa_data_wq = NULL; + } +} + /** * ipa_data_setup() - setup BAM2BAM IPA port - * @no_ipa_port: total number of BAM2BAM IPA port to support * * Each USB function who wants to use BAM2BAM IPA port would * be counting number of IPA port to use and initialize those @@ -823,32 +1108,34 @@ void ipa_data_port_select(int portno, enum gadget_type gtype) * * Retrun: 0 in case of success, otherwise errno. */ -int ipa_data_setup(unsigned int no_ipa_port) +int ipa_data_setup(enum ipa_func_type func) { - int i, ret; + int ret; - pr_debug("requested %d IPA BAM ports", no_ipa_port); + pr_debug("requested %d IPA BAM port", func); - if (!no_ipa_port || no_ipa_port > IPA_N_PORTS) { - pr_err("Invalid num of ports count:%d\n", no_ipa_port); + if (func >= USB_IPA_NUM_FUNCS) { + pr_err("Invalid num of ports count:%d\n", func); return -EINVAL; } - for (i = 0; i < no_ipa_port; i++) { - n_ipa_ports++; - ret = ipa_data_port_alloc(i); - if (ret) { - n_ipa_ports--; - pr_err("Failed to alloc port:%d\n", i); + ret = ipa_data_port_alloc(func); + if (ret) { + pr_err("Failed to alloc port:%d\n", func); + return ret; + } + + if (func == USB_IPA_FUNC_RNDIS) { + rndis_data = kzalloc(sizeof(*rndis_data), GFP_KERNEL); + if (!rndis_data) { + pr_err("%s: fail allocate and initialize new instance\n", + __func__); goto free_ipa_ports; } } - - pr_debug("n_ipa_ports:%d\n", n_ipa_ports); - if (ipa_data_wq) { pr_debug("ipa_data_wq is already setup."); - return 0; + goto free_rndis_data; } ipa_data_wq = alloc_workqueue("k_usb_ipa_data", @@ -856,20 +1143,111 @@ int ipa_data_setup(unsigned int no_ipa_port) if (!ipa_data_wq) { pr_err("Failed to create workqueue\n"); ret = -ENOMEM; - goto free_ipa_ports; + goto free_rndis_data; } return 0; +free_rndis_data: + if (func == USB_IPA_FUNC_RNDIS) + kfree(rndis_data); free_ipa_ports: - for (i = 0; i < n_ipa_ports; i++) { - kfree(ipa_data_ports[i]); - ipa_data_ports[i] = NULL; - if (ipa_data_wq) { - destroy_workqueue(ipa_data_wq); - ipa_data_wq = NULL; - } - } + kfree(ipa_data_ports[func]); + ipa_data_ports[func] = NULL; return ret; } + +void ipa_data_set_ul_max_xfer_size(u32 max_transfer_size) +{ + if (!max_transfer_size) { + pr_err("%s: invalid parameters\n", __func__); + return; + } + rndis_data->ul_max_transfer_size = max_transfer_size; + pr_debug("%s(): ul_max_xfer_size:%d\n", __func__, max_transfer_size); +} + +void ipa_data_set_dl_max_xfer_size(u32 max_transfer_size) +{ + + if (!max_transfer_size) { + pr_err("%s: invalid parameters\n", __func__); + return; + } + rndis_data->dl_max_transfer_size = max_transfer_size; + pr_debug("%s(): dl_max_xfer_size:%d\n", __func__, max_transfer_size); +} + +void ipa_data_set_ul_max_pkt_num(u8 max_packets_number) +{ + if (!max_packets_number) { + pr_err("%s: invalid parameters\n", __func__); + return; + } + + rndis_data->ul_max_packets_number = max_packets_number; + + if (max_packets_number > 1) + rndis_data->ul_aggregation_enable = true; + else + rndis_data->ul_aggregation_enable = false; + + pr_debug("%s(): ul_aggregation enable:%d ul_max_packets_number:%d\n", + __func__, rndis_data->ul_aggregation_enable, + max_packets_number); +} + +void ipa_data_start_rndis_ipa(enum ipa_func_type func) +{ + struct ipa_data_ch_info *port; + + pr_debug("%s\n", __func__); + + port = ipa_data_ports[func]; + if (!port) { + pr_err("%s: port is NULL", __func__); + return; + } + + if (atomic_read(&port->pipe_connect_notified)) { + pr_debug("%s: Transfers already started?\n", __func__); + return; + } + /* + * Increment usage count upon cable connect. Decrement after IPA + * handshake is done in disconnect work due to cable disconnect + * or in suspend work. + */ + usb_gadget_autopm_get_noresume(port->gadget); + queue_work(ipa_data_wq, &port->connect_w); +} + +void ipa_data_stop_rndis_ipa(enum ipa_func_type func) +{ + struct ipa_data_ch_info *port; + unsigned long flags; + + pr_debug("%s\n", __func__); + + port = ipa_data_ports[func]; + if (!port) { + pr_err("%s: port is NULL", __func__); + return; + } + + if (!atomic_read(&port->pipe_connect_notified)) + return; + + rndis_ipa_reset_trigger(); + ipa_data_stop_endless_xfer(port, true); + ipa_data_stop_endless_xfer(port, false); + spin_lock_irqsave(&port->port_lock, flags); + /* check if USB cable is disconnected or not */ + if (port->port_usb) { + msm_ep_unconfig(port->port_usb->in); + msm_ep_unconfig(port->port_usb->out); + } + spin_unlock_irqrestore(&port->port_lock, flags); + queue_work(ipa_data_wq, &port->disconnect_w); +} diff --git a/drivers/usb/gadget/function/u_data_ipa.h b/drivers/usb/gadget/function/u_data_ipa.h index b7d47ab1bb04..a1c1055bd8ef 100644 --- a/drivers/usb/gadget/function/u_data_ipa.h +++ b/drivers/usb/gadget/function/u_data_ipa.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014,2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,23 +13,78 @@ #ifndef __U_DATA_IPA_H #define __U_DATA_IPA_H -#include "usb_gadget_xport.h" +#include <linux/usb/composite.h> +#include <linux/rndis_ipa.h> +#include <linux/usb/msm_hsusb.h> +#include <linux/miscdevice.h> +#include <linux/ipa_usb.h> +#include <linux/usb_bam.h> + +enum ipa_func_type { + USB_IPA_FUNC_ECM, + USB_IPA_FUNC_MBIM, + USB_IPA_FUNC_RMNET, + USB_IPA_FUNC_RNDIS, + USB_IPA_FUNC_DPL, + USB_IPA_NUM_FUNCS, +}; + +/* Max Number of IPA data ports supported */ +#define IPA_N_PORTS USB_IPA_NUM_FUNCS struct gadget_ipa_port { struct usb_composite_dev *cdev; struct usb_function *func; + int rx_buffer_size; struct usb_ep *in; struct usb_ep *out; int ipa_consumer_ep; int ipa_producer_ep; + const struct usb_endpoint_descriptor *in_ep_desc_backup; + const struct usb_endpoint_descriptor *out_ep_desc_backup; + +}; + +/* for configfs support */ +#define MAX_INST_NAME_LEN 40 + +struct f_rndis_qc_opts { + struct usb_function_instance func_inst; + struct f_rndis_qc *rndis; + u32 vendor_id; + const char *manufacturer; + struct net_device *net; + int refcnt; }; -void ipa_data_port_select(int portno, enum gadget_type gtype); -void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num); -int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, +void ipa_data_port_select(enum ipa_func_type func); +void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func); +int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, u8 src_connection_idx, u8 dst_connection_idx); -int ipa_data_setup(unsigned int no_ipa_port); -void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num); -void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num); +int ipa_data_setup(enum ipa_func_type func); +void ipa_data_free(enum ipa_func_type func); + +void ipa_data_flush_workqueue(void); +void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func, + bool remote_wakeup_enabled); +void ipa_data_suspend(struct gadget_ipa_port *gp, enum ipa_func_type func, + bool remote_wakeup_enabled); + +void ipa_data_set_ul_max_xfer_size(u32 ul_max_xfer_size); + +void ipa_data_set_dl_max_xfer_size(u32 dl_max_transfer_size); + +void ipa_data_set_ul_max_pkt_num(u8 ul_max_packets_number); + +void ipa_data_start_rx_tx(enum ipa_func_type func); + +void ipa_data_start_rndis_ipa(enum ipa_func_type func); + +void ipa_data_stop_rndis_ipa(enum ipa_func_type func); +void *rndis_qc_get_ipa_priv(void); +void *rndis_qc_get_ipa_rx_cb(void); +bool rndis_qc_get_skip_ep_config(void); +void *rndis_qc_get_ipa_tx_cb(void); +void rndis_ipa_reset_trigger(void); #endif diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index dd7669331d00..b30831ef4014 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -116,17 +116,20 @@ int xhci_halt(struct xhci_hcd *xhci) STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); if (!ret) { xhci->xhc_state |= XHCI_STATE_HALTED; - xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; - - if (timer_pending(&xhci->cmd_timer)) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Cleanup command queue"); - del_timer(&xhci->cmd_timer); - xhci_cleanup_command_queue(xhci); - } - } else + } else { xhci_warn(xhci, "Host not halted after %u microseconds.\n", XHCI_MAX_HALT_USEC); + } + + xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; + + if (timer_pending(&xhci->cmd_timer)) { + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Cleanup command queue"); + del_timer(&xhci->cmd_timer); + xhci_cleanup_command_queue(xhci); + } + return ret; } diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index a1451a2d4826..12b98017beb2 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -2267,12 +2267,15 @@ struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, const char *phandle) struct platform_device *pdev; struct device *pd_dev; + if (!usbpd_class.p) /* usbpd_init() not yet called */ + return ERR_PTR(-EAGAIN); + if (!dev->of_node) - return ERR_PTR(-ENODEV); + return ERR_PTR(-EINVAL); pd_np = of_parse_phandle(dev->of_node, phandle, 0); if (!pd_np) - return ERR_PTR(-ENODEV); + return ERR_PTR(-ENXIO); pdev = of_find_device_by_node(pd_np); if (!pdev) @@ -2282,7 +2285,8 @@ struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, const char *phandle) match_usbpd_device); if (!pd_dev) { platform_device_put(pdev); - return ERR_PTR(-ENODEV); + /* device was found but maybe hadn't probed yet, so defer */ + return ERR_PTR(-EPROBE_DEFER); } ptr = devres_alloc(devm_usbpd_put, sizeof(*ptr), GFP_KERNEL); @@ -2294,7 +2298,7 @@ struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, const char *phandle) pd = dev_get_drvdata(pd_dev); if (!pd) - return ERR_PTR(-ENODEV); + return ERR_PTR(-EPROBE_DEFER); *ptr = pd; devres_add(dev, ptr); diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 584d2edc364e..d9297a7af764 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -510,8 +510,8 @@ char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt) pr_debug("clk_rate=%llu, bpp= %d, lane_cnt=%d\n", pinfo->clk_rate, pinfo->bpp, lane_cnt); - min_link_rate = (pinfo->clk_rate * 10) / - (lane_cnt * encoding_factx10); + min_link_rate = (u32)div_u64((pinfo->clk_rate * 10), + (lane_cnt * encoding_factx10)); min_link_rate = (min_link_rate * pinfo->bpp) / (DP_LINK_RATE_MULTIPLIER); min_link_rate /= ln_to_link_ratio; diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index c145f72c3c70..66cd99720afa 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -357,7 +357,7 @@ static int mdss_dsi_panel_power_lp(struct mdss_panel_data *pdata, int enable) static int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata, int power_state) { - int ret; + int ret = 0; struct mdss_panel_info *pinfo; if (pdata == NULL) { @@ -383,7 +383,11 @@ static int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata, switch (power_state) { case MDSS_PANEL_POWER_OFF: - ret = mdss_dsi_panel_power_off(pdata); + case MDSS_PANEL_POWER_LCD_DISABLED: + /* if LCD has not been disabled, then disable it now */ + if ((pinfo->panel_power_state != MDSS_PANEL_POWER_LCD_DISABLED) + && (pinfo->panel_power_state != MDSS_PANEL_POWER_OFF)) + ret = mdss_dsi_panel_power_off(pdata); break; case MDSS_PANEL_POWER_ON: if (mdss_dsi_is_panel_on_lp(pdata)) @@ -2469,6 +2473,7 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, int power_state; u32 mode; struct mdss_panel_info *pinfo; + int ret; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); @@ -2529,6 +2534,20 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, rc = mdss_dsi_blank(pdata, power_state); rc = mdss_dsi_off(pdata, power_state); break; + case MDSS_EVENT_DISABLE_PANEL: + /* disable esd thread */ + disable_esd_thread(); + + /* disable backlight */ + ctrl_pdata->panel_data.set_backlight(pdata, 0); + + /* send the off commands */ + ctrl_pdata->off(pdata); + + /* disable panel power */ + ret = mdss_dsi_panel_power_ctrl(pdata, + MDSS_PANEL_POWER_LCD_DISABLED); + break; case MDSS_EVENT_CONT_SPLASH_FINISH: if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE) rc = mdss_dsi_blank(pdata, MDSS_PANEL_POWER_OFF); diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index bd1854092c6a..7091dc2f38b9 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -614,6 +614,7 @@ int mdss_dsi_wait_for_lane_idle(struct mdss_dsi_ctrl_pdata *ctrl); irqreturn_t mdss_dsi_isr(int irq, void *ptr); irqreturn_t hw_vsync_handler(int irq, void *data); +void disable_esd_thread(void); void mdss_dsi_irq_handler_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata); void mdss_dsi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata); diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index e8d68059581f..8ffba091e2b2 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -667,6 +667,11 @@ static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, * for the backlight brightness. If the brightness is less * than it, the controller can malfunction. */ + pr_debug("%s: bl_level:%d\n", __func__, bl_level); + + /* do not allow backlight to change when panel in disable mode */ + if (pdata->panel_disable_mode && (bl_level != 0)) + return; if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0)) bl_level = pdata->panel_info.bl_min; diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c index bf545ae311f2..4208c2c43efb 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_status.c +++ b/drivers/video/fbdev/msm/mdss_dsi_status.c @@ -101,6 +101,16 @@ irqreturn_t hw_vsync_handler(int irq, void *data) } /* + * disable_esd_thread() - Cancels work item for the esd check. + */ +void disable_esd_thread(void) +{ + if (pstatus_data && + cancel_delayed_work(&pstatus_data->check_status)) + pr_debug("esd thread killed\n"); +} + +/* * fb_event_callback() - Call back function for the fb_register_client() * notifying events * @self : notifier block diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 50c7015c6731..fc8d3898351e 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -1950,6 +1950,9 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) pdata->panel_info.is_lpm_mode = false; } + if (pdata->panel_disable_mode) + mdss_mdp_enable_panel_disable_mode(mfd, false); + return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); } diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 921391dc4bde..0085163ada52 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -394,6 +394,8 @@ struct mdss_mdp_ctl_intfs_ops { enum dynamic_switch_modes mode, bool pre); /* called before do any register programming from commit thread */ void (*pre_programming)(struct mdss_mdp_ctl *ctl); + /* called to do any interface programming for the panel disable mode */ + void (*panel_disable_cfg)(struct mdss_mdp_ctl *ctl, bool disable); /* to update lineptr, [1..yres] - enable, 0 - disable */ int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable); @@ -1863,6 +1865,8 @@ int mdss_mdp_cmd_set_autorefresh_mode(struct mdss_mdp_ctl *ctl, int frame_cnt); int mdss_mdp_cmd_get_autorefresh_mode(struct mdss_mdp_ctl *ctl); int mdss_mdp_ctl_cmd_set_autorefresh(struct mdss_mdp_ctl *ctl, int frame_cnt); int mdss_mdp_ctl_cmd_get_autorefresh(struct mdss_mdp_ctl *ctl); +int mdss_mdp_enable_panel_disable_mode(struct msm_fb_data_type *mfd, + bool disable_panel); int mdss_mdp_pp_get_version(struct mdp_pp_feature_version *version); int mdss_mdp_layer_pre_commit_cwb(struct msm_fb_data_type *mfd, struct mdp_layer_commit_v1 *commit); diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 2c2dc6f18fd9..72d6175686b7 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -295,9 +295,9 @@ static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer, __func__, pinfo->yres, vclks_line, te->sync_cfg_height, te->vsync_init_val, te->rd_ptr_irq, te->start_pos, te->wr_ptr_irq); - pr_debug("thrd_start =%d thrd_cont=%d pp_split=%d\n", + pr_debug("thrd_start =%d thrd_cont=%d pp_split=%d hw_vsync_mode:%d\n", te->sync_threshold_start, te->sync_threshold_continue, - ctx->pingpong_split_slave); + ctx->pingpong_split_slave, pinfo->mipi.hw_vsync_mode); pingpong_base = mixer->pingpong_base; @@ -2130,6 +2130,88 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl, } /* + * This function will be called from the sysfs node to tear down or restore + * any dependencies of the interface to disable the panel + */ +void mdss_mdp_cmd_panel_disable_cfg(struct mdss_mdp_ctl *ctl, + bool disable) +{ + struct mdss_panel_info *pinfo, *spinfo = NULL; + struct mdss_mdp_cmd_ctx *ctx, *sctx = NULL; + + pinfo = &ctl->panel_data->panel_info; + mutex_lock(&ctl->offlock); + + if ((pinfo->sim_panel_mode == SIM_MODE) || + ((!ctl->panel_data->panel_disable_mode) && + (pinfo->mipi.hw_vsync_mode == 0))) { + pr_err("te already in simulaiton mode\n"); + goto exit; + } + + ctx = (struct mdss_mdp_cmd_ctx *)ctl->intf_ctx[MASTER_CTX]; + if (is_pingpong_split(ctl->mfd)) { + sctx = (struct mdss_mdp_cmd_ctx *)ctl->intf_ctx[SLAVE_CTX]; + } else if (ctl->mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY) { + struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); + + if (sctl) { + sctx = (struct mdss_mdp_cmd_ctx *) + sctl->intf_ctx[MASTER_CTX]; + spinfo = &sctl->panel_data->panel_info; + } + } + + if (disable) { + /* cache the te params */ + memcpy(&pinfo->te_cached, &pinfo->te, + sizeof(struct mdss_mdp_pp_tear_check)); + pinfo->mipi.hw_vsync_mode = 0; + + if (spinfo) { + spinfo->mipi.hw_vsync_mode = 0; + memcpy(&spinfo->te_cached, &spinfo->te, + sizeof(struct mdss_mdp_pp_tear_check)); + } + + pr_debug("%s: update info\n", __func__); + /* update the te information to use sim mode */ + mdss_panel_override_te_params(pinfo); + if (spinfo) + mdss_panel_override_te_params(spinfo); + + pr_debug("%s: reconfig tear check\n", __func__); + /* reconfigure tear check, remove dependency to external te */ + if (mdss_mdp_cmd_tearcheck_setup(ctx, false)) { + pr_warn("%s: ctx%d tearcheck setup failed\n", __func__, + ctx->current_pp_num); + } else { + if (sctx && mdss_mdp_cmd_tearcheck_setup(sctx, false)) + pr_warn("%s: ctx%d tearcheck setup failed\n", + __func__, sctx->current_pp_num); + } + } else { + /* + * restore the information in the panel information, + * the actual programming will happen during restore + */ + pr_debug("%s: reset tear check\n", __func__); + memcpy(&pinfo->te, &pinfo->te_cached, + sizeof(struct mdss_mdp_pp_tear_check)); + pinfo->mipi.hw_vsync_mode = 1; + + if (spinfo) { + spinfo->mipi.hw_vsync_mode = 1; + memcpy(&spinfo->te, &spinfo->te_cached, + sizeof(struct mdss_mdp_pp_tear_check)); + } + } + +exit: + mutex_unlock(&ctl->offlock); +} + +/* * This function will be called from the sysfs node to enable and disable the * feature with master ctl only. */ @@ -3481,6 +3563,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl) ctl->ops.reconfigure = mdss_mdp_cmd_reconfigure; ctl->ops.pre_programming = mdss_mdp_cmd_pre_programming; ctl->ops.update_lineptr = mdss_mdp_cmd_update_lineptr; + ctl->ops.panel_disable_cfg = mdss_mdp_cmd_panel_disable_cfg; pr_debug("%s:-\n", __func__); return 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 9dda467e53cc..965d4a6cfb5e 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -3243,6 +3243,110 @@ static ssize_t mdss_mdp_dyn_pu_store(struct device *dev, return count; } + +static ssize_t mdss_mdp_panel_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_mdp_ctl *ctl; + struct mdss_panel_data *pdata; + + if (!mfd) { + pr_err("Invalid mfd structure\n"); + return -EINVAL; + } + + ctl = mfd_to_ctl(mfd); + if (!ctl) { + pr_err("Invalid ctl structure\n"); + return -EINVAL; + } + + pdata = dev_get_platdata(&mfd->pdev->dev); + + ret = snprintf(buf, PAGE_SIZE, "%d\n", + pdata->panel_disable_mode); + + return ret; +} + +int mdss_mdp_enable_panel_disable_mode(struct msm_fb_data_type *mfd, + bool disable_panel) +{ + struct mdss_mdp_ctl *ctl; + int ret = 0; + struct mdss_panel_data *pdata; + + ctl = mfd_to_ctl(mfd); + if (!ctl) { + pr_err("Invalid ctl structure\n"); + ret = -EINVAL; + return ret; + } + + pdata = dev_get_platdata(&mfd->pdev->dev); + + pr_debug("config panel %d\n", disable_panel); + if (disable_panel) { + /* first set the flag that we enter this mode */ + pdata->panel_disable_mode = true; + + /* + * setup any interface config that needs to change before + * disabling the panel + */ + if (ctl->ops.panel_disable_cfg) + ctl->ops.panel_disable_cfg(ctl, disable_panel); + + /* disable panel */ + ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DISABLE_PANEL, + NULL, CTL_INTF_EVENT_FLAG_DEFAULT); + if (ret) + pr_err("failed to disable panel! %d\n", ret); + } else { + /* restore any interface configuration */ + if (ctl->ops.panel_disable_cfg) + ctl->ops.panel_disable_cfg(ctl, disable_panel); + + /* + * no other action is needed when reconfiguring, since all the + * re-configuration will happen during restore + */ + pdata->panel_disable_mode = false; + } + + return ret; +} + +static ssize_t mdss_mdp_panel_disable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int disable_panel, rc; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + if (!mfd) { + pr_err("Invalid mfd structure\n"); + rc = -EINVAL; + return rc; + } + + rc = kstrtoint(buf, 10, &disable_panel); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + return rc; + } + + pr_debug("disable panel: %d ++\n", disable_panel); + /* we only support disabling the panel from sysfs */ + if (disable_panel) + mdss_mdp_enable_panel_disable_mode(mfd, true); + + return len; +} + static ssize_t mdss_mdp_cmd_autorefresh_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -3433,6 +3537,8 @@ static DEVICE_ATTR(msm_misr_en, S_IRUGO | S_IWUSR, mdss_mdp_misr_show, mdss_mdp_misr_store); static DEVICE_ATTR(msm_cmd_autorefresh_en, S_IRUGO | S_IWUSR, mdss_mdp_cmd_autorefresh_show, mdss_mdp_cmd_autorefresh_store); +static DEVICE_ATTR(msm_disable_panel, S_IRUGO | S_IWUSR, + mdss_mdp_panel_disable_show, mdss_mdp_panel_disable_store); static DEVICE_ATTR(vsync_event, S_IRUGO, mdss_mdp_vsync_show_event, NULL); static DEVICE_ATTR(lineptr_event, S_IRUGO, mdss_mdp_lineptr_show_event, NULL); static DEVICE_ATTR(lineptr_value, S_IRUGO | S_IWUSR | S_IWGRP, @@ -3454,6 +3560,7 @@ static struct attribute *mdp_overlay_sysfs_attrs[] = { &dev_attr_dyn_pu.attr, &dev_attr_msm_misr_en.attr, &dev_attr_msm_cmd_autorefresh_en.attr, + &dev_attr_msm_disable_panel.attr, &dev_attr_hist_event.attr, &dev_attr_bl_event.attr, &dev_attr_ad_event.attr, diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c index 48235c5b85ba..5bd627853c59 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c @@ -1570,7 +1570,7 @@ static int pp_pa_dither_cache_params_v1_7( res_data->strength = dither_data.strength; res_data->matrix_sz = MDP_DITHER_DATA_V1_7_SZ; ret = copy_from_user(res_data->matrix_data, - (u8 *)dither_data.matrix_data, + (u8 *)((unsigned long)dither_data.matrix_data), (MDP_DITHER_DATA_V1_7_SZ * sizeof(u32))); if (ret) { pr_err("failed to copy the dither matrix ret %d sz %zd", ret, @@ -1700,8 +1700,8 @@ static int pp_igc_lut_cache_params_v3(struct mdp_igc_lut_data *config, v3_cache_data->table_fmt = v3_kernel_data->table_fmt; } else { ret = copy_from_user(v3_cache_data->c0_c1_data, - (u8 *)v3_usr_config.c0_c1_data, - len * sizeof(u32)); + (u8 *)((unsigned long)v3_usr_config.c0_c1_data), + len * sizeof(u32)); if (ret) { pr_err("copy from user failed for c0_c1_data size %zd ret %d\n", len * sizeof(u32), ret); @@ -1709,8 +1709,8 @@ static int pp_igc_lut_cache_params_v3(struct mdp_igc_lut_data *config, goto igc_config_exit; } ret = copy_from_user(v3_cache_data->c2_data, - (u8 *)v3_usr_config.c2_data, - len * sizeof(u32)); + (u8 *)((unsigned long)v3_usr_config.c2_data), + len * sizeof(u32)); if (ret) { pr_err("copy from user failed for c2_data size %zd ret %d\n", len * sizeof(u32), ret); @@ -1846,7 +1846,7 @@ static int pp_igc_lut_cache_params_pipe_v3( } } else { if (copy_from_user(v3_cache_data->c0_c1_data, - (u8 *)v3_usr_config.c0_c1_data, + (u8 *)((unsigned long)v3_usr_config.c0_c1_data), IGC_LUT_ENTRIES * sizeof(u32))) { pr_err("error in copying the c0_c1_data of size %zd\n", IGC_LUT_ENTRIES * sizeof(u32)); @@ -1854,7 +1854,7 @@ static int pp_igc_lut_cache_params_pipe_v3( goto igc_config_exit; } if (copy_from_user(v3_cache_data->c2_data, - (u8 *)v3_usr_config.c2_data, + (u8 *)((unsigned long)v3_usr_config.c2_data), IGC_LUT_ENTRIES * sizeof(u32))) { pr_err("error in copying the c2_data of size %zd\n", IGC_LUT_ENTRIES * sizeof(u32)); diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index b2b647dcc017..a633528b5373 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -104,6 +104,7 @@ enum { MDSS_PANEL_POWER_ON, MDSS_PANEL_POWER_LP1, MDSS_PANEL_POWER_LP2, + MDSS_PANEL_POWER_LCD_DISABLED, }; enum { @@ -265,6 +266,7 @@ enum mdss_intf_events { MDSS_EVENT_DSI_RESET_WRITE_PTR, MDSS_EVENT_PANEL_TIMING_SWITCH, MDSS_EVENT_DEEP_COLOR, + MDSS_EVENT_DISABLE_PANEL, MDSS_EVENT_MAX, }; @@ -692,6 +694,7 @@ struct mdss_panel_info { char panel_name[MDSS_MAX_PANEL_LEN]; struct mdss_mdp_pp_tear_check te; + struct mdss_mdp_pp_tear_check te_cached; /* * Value of 2 only when single DSI is configured with 2 DSC @@ -789,6 +792,12 @@ struct mdss_panel_data { /* To store dsc cfg name passed by bootloader */ char dsc_cfg_np_name[MDSS_MAX_PANEL_LEN]; struct mdss_panel_data *next; + + /* + * Set when the power of the panel is disabled while dsi/mdp + * are still on; panel will recover after unblank + */ + bool panel_disable_mode; }; struct mdss_panel_debugfs_info { diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c index 9a00eff9ade9..b5da4ad1a86b 100644 --- a/drivers/video/fbdev/msm/mdss_smmu.c +++ b/drivers/video/fbdev/msm/mdss_smmu.c @@ -36,6 +36,8 @@ #include "mdss_smmu.h" #include "mdss_debug.h" +#define SZ_4G 0xF0000000 + static DEFINE_MUTEX(mdp_iommu_lock); void mdss_iommu_lock(void) @@ -536,13 +538,13 @@ int mdss_smmu_init(struct mdss_data_type *mdata, struct device *dev) } static struct mdss_smmu_domain mdss_mdp_unsec = { - "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128K, (SZ_1G - SZ_128K)}; + "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128K, (SZ_4G - SZ_128K)}; static struct mdss_smmu_domain mdss_rot_unsec = { - NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_1G - SZ_128K)}; + NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_4G - SZ_128K)}; static struct mdss_smmu_domain mdss_mdp_sec = { - "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_1G, SZ_2G}; + "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_128K, (SZ_4G - SZ_128K)}; static struct mdss_smmu_domain mdss_rot_sec = { - NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_1G, SZ_2G}; + NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_128K, (SZ_4G - SZ_128K)}; static const struct of_device_id mdss_smmu_dt_match[] = { { .compatible = "qcom,smmu_mdp_unsec", .data = &mdss_mdp_unsec}, diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index e8671942c2a0..785af63acabd 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -71,10 +71,12 @@ static ssize_t fuse_passthrough_read_write_iter(struct kiocb *iocb, struct fuse_file *ff; struct file *fuse_file, *passthrough_filp; struct inode *fuse_inode, *passthrough_inode; + struct fuse_conn *fc; ff = iocb->ki_filp->private_data; fuse_file = iocb->ki_filp; passthrough_filp = ff->passthrough_filp; + fc = ff->fc; /* lock passthrough file to prevent it from being released */ get_file(passthrough_filp); @@ -88,7 +90,9 @@ static ssize_t fuse_passthrough_read_write_iter(struct kiocb *iocb, ret_val = passthrough_filp->f_op->write_iter(iocb, iter); if (ret_val >= 0 || ret_val == -EIOCBQUEUED) { + spin_lock(&fc->lock); fsstack_copy_inode_size(fuse_inode, passthrough_inode); + spin_unlock(&fc->lock); fsstack_copy_attr_times(fuse_inode, passthrough_inode); } } else { diff --git a/include/linux/dma-mapping-fast.h b/include/linux/dma-mapping-fast.h index aa9fcfe73162..ddd126c0fd85 100644 --- a/include/linux/dma-mapping-fast.h +++ b/include/linux/dma-mapping-fast.h @@ -16,6 +16,8 @@ #include <linux/iommu.h> #include <linux/io-pgtable-fast.h> +struct dma_iommu_mapping; + struct dma_fast_smmu_mapping { struct device *dev; struct iommu_domain *domain; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 56855724271c..f4f5af978c7c 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -650,8 +650,8 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link) { } -static int iommu_dma_supported(struct iommu_domain *domain, struct device *dev, - u64 mask) +static inline int iommu_dma_supported(struct iommu_domain *domain, + struct device *dev, u64 mask) { return -EINVAL; } diff --git a/include/linux/msm_dma_iommu_mapping.h b/include/linux/msm_dma_iommu_mapping.h index 76451faa2073..73e69383b9b6 100644 --- a/include/linux/msm_dma_iommu_mapping.h +++ b/include/linux/msm_dma_iommu_mapping.h @@ -90,7 +90,7 @@ static inline void msm_dma_unmap_sg(struct device *dev, { } -int msm_dma_unmap_all_for_dev(struct device *dev) +static inline int msm_dma_unmap_all_for_dev(struct device *dev) { return 0; } diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index c2fa3ecb0dce..146efefde2a1 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -10,30 +10,96 @@ struct percpu_rw_semaphore { struct rcu_sync rss; - unsigned int __percpu *fast_read_ctr; + unsigned int __percpu *read_count; struct rw_semaphore rw_sem; - atomic_t slow_read_ctr; - wait_queue_head_t write_waitq; + wait_queue_head_t writer; + int readers_block; }; -extern void percpu_down_read(struct percpu_rw_semaphore *); -extern int percpu_down_read_trylock(struct percpu_rw_semaphore *); -extern void percpu_up_read(struct percpu_rw_semaphore *); +extern int __percpu_down_read(struct percpu_rw_semaphore *, int); +extern void __percpu_up_read(struct percpu_rw_semaphore *); + +static inline void percpu_down_read(struct percpu_rw_semaphore *sem) +{ + might_sleep(); + + rwsem_acquire_read(&sem->rw_sem.dep_map, 0, 0, _RET_IP_); + + preempt_disable(); + /* + * We are in an RCU-sched read-side critical section, so the writer + * cannot both change sem->state from readers_fast and start checking + * counters while we are here. So if we see !sem->state, we know that + * the writer won't be checking until we're past the preempt_enable() + * and that one the synchronize_sched() is done, the writer will see + * anything we did within this RCU-sched read-size critical section. + */ + __this_cpu_inc(*sem->read_count); + if (unlikely(!rcu_sync_is_idle(&sem->rss))) + __percpu_down_read(sem, false); /* Unconditional memory barrier */ + preempt_enable(); + /* + * The barrier() from preempt_enable() prevents the compiler from + * bleeding the critical section out. + */ +} + +static inline int percpu_down_read_trylock(struct percpu_rw_semaphore *sem) +{ + int ret = 1; + + preempt_disable(); + /* + * Same as in percpu_down_read(). + */ + __this_cpu_inc(*sem->read_count); + if (unlikely(!rcu_sync_is_idle(&sem->rss))) + ret = __percpu_down_read(sem, true); /* Unconditional memory barrier */ + preempt_enable(); + /* + * The barrier() from preempt_enable() prevents the compiler from + * bleeding the critical section out. + */ + + if (ret) + rwsem_acquire_read(&sem->rw_sem.dep_map, 0, 1, _RET_IP_); + + return ret; +} + +static inline void percpu_up_read(struct percpu_rw_semaphore *sem) +{ + /* + * The barrier() in preempt_disable() prevents the compiler from + * bleeding the critical section out. + */ + preempt_disable(); + /* + * Same as in percpu_down_read(). + */ + if (likely(rcu_sync_is_idle(&sem->rss))) + __this_cpu_dec(*sem->read_count); + else + __percpu_up_read(sem); /* Unconditional memory barrier */ + preempt_enable(); + + rwsem_release(&sem->rw_sem.dep_map, 1, _RET_IP_); +} extern void percpu_down_write(struct percpu_rw_semaphore *); extern void percpu_up_write(struct percpu_rw_semaphore *); extern int __percpu_init_rwsem(struct percpu_rw_semaphore *, const char *, struct lock_class_key *); + extern void percpu_free_rwsem(struct percpu_rw_semaphore *); -#define percpu_init_rwsem(brw) \ +#define percpu_init_rwsem(sem) \ ({ \ static struct lock_class_key rwsem_key; \ - __percpu_init_rwsem(brw, #brw, &rwsem_key); \ + __percpu_init_rwsem(sem, #sem, &rwsem_key); \ }) - #define percpu_rwsem_is_held(sem) lockdep_is_held(&(sem)->rw_sem) static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem, diff --git a/include/linux/rcu_sync.h b/include/linux/rcu_sync.h index a63a33e6196e..ece7ed9a4a70 100644 --- a/include/linux/rcu_sync.h +++ b/include/linux/rcu_sync.h @@ -59,6 +59,7 @@ static inline bool rcu_sync_is_idle(struct rcu_sync *rsp) } extern void rcu_sync_init(struct rcu_sync *, enum rcu_sync_type); +extern void rcu_sync_enter_start(struct rcu_sync *); extern void rcu_sync_enter(struct rcu_sync *); extern void rcu_sync_exit(struct rcu_sync *); extern void rcu_sync_dtor(struct rcu_sync *); diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h index 8704b2e7cfbc..473cb5fb375e 100644 --- a/include/soc/qcom/icnss.h +++ b/include/soc/qcom/icnss.h @@ -24,8 +24,6 @@ struct icnss_driver_ops { void (*shutdown)(struct device *dev); int (*reinit)(struct device *dev); void (*crash_shutdown)(void *pdev); - int (*suspend)(struct device *dev, pm_message_t state); - int (*resume)(struct device *dev); int (*pm_suspend)(struct device *dev); int (*pm_resume)(struct device *dev); int (*suspend_noirq)(struct device *dev); diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h index 5adcbcf660ba..aa3b363e95e1 100644 --- a/include/sound/wcd-dsp-mgr.h +++ b/include/sound/wcd-dsp-mgr.h @@ -36,6 +36,9 @@ enum wdsp_cmpnt_type { }; enum wdsp_event_type { + /* Initialization related */ + WDSP_EVENT_POST_INIT, + /* Image download related */ WDSP_EVENT_PRE_DLOAD_CODE, WDSP_EVENT_DLOAD_SECTION, @@ -44,6 +47,8 @@ enum wdsp_event_type { WDSP_EVENT_POST_DLOAD_DATA, WDSP_EVENT_DLOAD_FAILED, + WDSP_EVENT_READ_SECTION, + /* DSP boot related */ WDSP_EVENT_PRE_BOOTUP, WDSP_EVENT_DO_BOOT, @@ -62,6 +67,7 @@ enum wdsp_event_type { enum wdsp_intr { WDSP_IPC1_INTR, + WDSP_ERR_INTR, }; /* @@ -86,6 +92,12 @@ struct wdsp_img_section { u8 *data; }; +struct wdsp_err_intr_arg { + bool mem_dumps_enabled; + u32 remote_start_addr; + size_t dump_size; +}; + /* * wdsp_ops: ops/function callbacks for manager driver * @register_cmpnt_ops: components will use this to register @@ -109,7 +121,7 @@ struct wdsp_mgr_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); + enum wdsp_intr intr, 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/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h index 691df1b2689b..e14cab59e90a 100644 --- a/include/trace/events/trace_msm_low_power.h +++ b/include/trace/events/trace_msm_low_power.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2014-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -43,6 +43,54 @@ TRACE_EVENT(cpu_power_select, __entry->next_event_us) ); +TRACE_EVENT(cpu_pred_select, + + TP_PROTO(u32 predtype, u64 predicted, u32 tmr_time), + + TP_ARGS(predtype, predicted, tmr_time), + + TP_STRUCT__entry( + __field(u32, predtype) + __field(u64, predicted) + __field(u32, tmr_time) + ), + + TP_fast_assign( + __entry->predtype = predtype; + __entry->predicted = predicted; + __entry->tmr_time = tmr_time; + ), + + TP_printk("pred:%u time:%lu tmr_time:%u", + __entry->predtype, (unsigned long)__entry->predicted, + __entry->tmr_time) +); + +TRACE_EVENT(cpu_pred_hist, + + TP_PROTO(int idx, u32 resi, u32 sample, u32 tmr), + + TP_ARGS(idx, resi, sample, tmr), + + TP_STRUCT__entry( + __field(int, idx) + __field(u32, resi) + __field(u32, sample) + __field(u32, tmr) + ), + + TP_fast_assign( + __entry->idx = idx; + __entry->resi = resi; + __entry->sample = sample; + __entry->tmr = tmr; + ), + + TP_printk("idx:%d resi:%u sample:%u tmr:%u", + __entry->idx, __entry->resi, + __entry->sample, __entry->tmr) +); + TRACE_EVENT(cpu_idle_enter, TP_PROTO(int index), diff --git a/include/uapi/linux/msm_vidc_dec.h b/include/uapi/linux/msm_vidc_dec.h index f502c81665a4..48ce8e929fbf 100644 --- a/include/uapi/linux/msm_vidc_dec.h +++ b/include/uapi/linux/msm_vidc_dec.h @@ -486,10 +486,14 @@ enum vdec_interlaced_format { VDEC_InterlaceInterleaveFrameBottomFieldFirst = 0x4 }; +#define VDEC_YUV_FORMAT_NV12_TP10_UBWC \ + VDEC_YUV_FORMAT_NV12_TP10_UBWC + enum vdec_output_fromat { VDEC_YUV_FORMAT_NV12 = 0x1, VDEC_YUV_FORMAT_TILE_4x2 = 0x2, - VDEC_YUV_FORMAT_NV12_UBWC = 0x3 + VDEC_YUV_FORMAT_NV12_UBWC = 0x3, + VDEC_YUV_FORMAT_NV12_TP10_UBWC = 0x4 }; enum vdec_output_order { diff --git a/kernel/cgroup.c b/kernel/cgroup.c index ad4a12371069..cc6c7d0a6758 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -5376,6 +5376,12 @@ int __init cgroup_init(void) BUG_ON(cgroup_init_cftypes(NULL, cgroup_dfl_base_files)); BUG_ON(cgroup_init_cftypes(NULL, cgroup_legacy_base_files)); + /* + * The latency of the synchronize_sched() is too high for cgroups, + * avoid it at the cost of forcing all readers into the slow path. + */ + rcu_sync_enter_start(&cgroup_threadgroup_rwsem.rss); + mutex_lock(&cgroup_mutex); /* Add init_css_set to the hash table */ diff --git a/kernel/fork.c b/kernel/fork.c index c9eb86b646ab..e89d0bae6f20 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1375,7 +1375,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->real_start_time = ktime_get_boot_ns(); p->io_context = NULL; p->audit_context = NULL; - threadgroup_change_begin(current); cgroup_fork(p); #ifdef CONFIG_NUMA p->mempolicy = mpol_dup(p->mempolicy); @@ -1527,6 +1526,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, INIT_LIST_HEAD(&p->thread_group); p->task_works = NULL; + threadgroup_change_begin(current); /* * Ensure that the cgroup subsystem policies allow the new process to be * forked. It should be noted the the new process's css_set can be changed @@ -1627,6 +1627,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, bad_fork_cancel_cgroup: cgroup_cancel_fork(p, cgrp_ss_priv); bad_fork_free_pid: + threadgroup_change_end(current); if (pid != &init_struct_pid) free_pid(pid); bad_fork_cleanup_io: @@ -1657,7 +1658,6 @@ bad_fork_cleanup_policy: mpol_put(p->mempolicy); bad_fork_cleanup_threadgroup_lock: #endif - threadgroup_change_end(current); delayacct_tsk_free(p); bad_fork_cleanup_count: atomic_dec(&p->cred->user->processes); diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index f231e0bb311c..ce182599cf2e 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -8,151 +8,186 @@ #include <linux/sched.h> #include <linux/errno.h> -int __percpu_init_rwsem(struct percpu_rw_semaphore *brw, +int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, const char *name, struct lock_class_key *rwsem_key) { - brw->fast_read_ctr = alloc_percpu(int); - if (unlikely(!brw->fast_read_ctr)) + sem->read_count = alloc_percpu(int); + if (unlikely(!sem->read_count)) return -ENOMEM; /* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */ - __init_rwsem(&brw->rw_sem, name, rwsem_key); - rcu_sync_init(&brw->rss, RCU_SCHED_SYNC); - atomic_set(&brw->slow_read_ctr, 0); - init_waitqueue_head(&brw->write_waitq); + rcu_sync_init(&sem->rss, RCU_SCHED_SYNC); + __init_rwsem(&sem->rw_sem, name, rwsem_key); + init_waitqueue_head(&sem->writer); + sem->readers_block = 0; return 0; } EXPORT_SYMBOL_GPL(__percpu_init_rwsem); -void percpu_free_rwsem(struct percpu_rw_semaphore *brw) +void percpu_free_rwsem(struct percpu_rw_semaphore *sem) { /* * XXX: temporary kludge. The error path in alloc_super() * assumes that percpu_free_rwsem() is safe after kzalloc(). */ - if (!brw->fast_read_ctr) + if (!sem->read_count) return; - rcu_sync_dtor(&brw->rss); - free_percpu(brw->fast_read_ctr); - brw->fast_read_ctr = NULL; /* catch use after free bugs */ + rcu_sync_dtor(&sem->rss); + free_percpu(sem->read_count); + sem->read_count = NULL; /* catch use after free bugs */ } +EXPORT_SYMBOL_GPL(percpu_free_rwsem); -/* - * This is the fast-path for down_read/up_read. If it succeeds we rely - * on the barriers provided by rcu_sync_enter/exit; see the comments in - * percpu_down_write() and percpu_up_write(). - * - * If this helper fails the callers rely on the normal rw_semaphore and - * atomic_dec_and_test(), so in this case we have the necessary barriers. - */ -static bool update_fast_ctr(struct percpu_rw_semaphore *brw, unsigned int val) +int __percpu_down_read(struct percpu_rw_semaphore *sem, int try) { - bool success; + /* + * Due to having preemption disabled the decrement happens on + * the same CPU as the increment, avoiding the + * increment-on-one-CPU-and-decrement-on-another problem. + * + * If the reader misses the writer's assignment of readers_block, then + * the writer is guaranteed to see the reader's increment. + * + * Conversely, any readers that increment their sem->read_count after + * the writer looks are guaranteed to see the readers_block value, + * which in turn means that they are guaranteed to immediately + * decrement their sem->read_count, so that it doesn't matter that the + * writer missed them. + */ - preempt_disable(); - success = rcu_sync_is_idle(&brw->rss); - if (likely(success)) - __this_cpu_add(*brw->fast_read_ctr, val); - preempt_enable(); + smp_mb(); /* A matches D */ - return success; -} + /* + * If !readers_block the critical section starts here, matched by the + * release in percpu_up_write(). + */ + if (likely(!smp_load_acquire(&sem->readers_block))) + return 1; -/* - * Like the normal down_read() this is not recursive, the writer can - * come after the first percpu_down_read() and create the deadlock. - * - * Note: returns with lock_is_held(brw->rw_sem) == T for lockdep, - * percpu_up_read() does rwsem_release(). This pairs with the usage - * of ->rw_sem in percpu_down/up_write(). - */ -void percpu_down_read(struct percpu_rw_semaphore *brw) -{ - might_sleep(); - rwsem_acquire_read(&brw->rw_sem.dep_map, 0, 0, _RET_IP_); + /* + * Per the above comment; we still have preemption disabled and + * will thus decrement on the same CPU as we incremented. + */ + __percpu_up_read(sem); - if (likely(update_fast_ctr(brw, +1))) - return; + if (try) + return 0; - /* Avoid rwsem_acquire_read() and rwsem_release() */ - __down_read(&brw->rw_sem); - atomic_inc(&brw->slow_read_ctr); - __up_read(&brw->rw_sem); -} -EXPORT_SYMBOL_GPL(percpu_down_read); + /* + * We either call schedule() in the wait, or we'll fall through + * and reschedule on the preempt_enable() in percpu_down_read(). + */ + preempt_enable_no_resched(); -int percpu_down_read_trylock(struct percpu_rw_semaphore *brw) -{ - if (unlikely(!update_fast_ctr(brw, +1))) { - if (!__down_read_trylock(&brw->rw_sem)) - return 0; - atomic_inc(&brw->slow_read_ctr); - __up_read(&brw->rw_sem); - } - - rwsem_acquire_read(&brw->rw_sem.dep_map, 0, 1, _RET_IP_); + /* + * Avoid lockdep for the down/up_read() we already have them. + */ + __down_read(&sem->rw_sem); + this_cpu_inc(*sem->read_count); + __up_read(&sem->rw_sem); + + preempt_disable(); return 1; } +EXPORT_SYMBOL_GPL(__percpu_down_read); -void percpu_up_read(struct percpu_rw_semaphore *brw) +void __percpu_up_read(struct percpu_rw_semaphore *sem) { - rwsem_release(&brw->rw_sem.dep_map, 1, _RET_IP_); - - if (likely(update_fast_ctr(brw, -1))) - return; + smp_mb(); /* B matches C */ + /* + * In other words, if they see our decrement (presumably to aggregate + * zero, as that is the only time it matters) they will also see our + * critical section. + */ + __this_cpu_dec(*sem->read_count); - /* false-positive is possible but harmless */ - if (atomic_dec_and_test(&brw->slow_read_ctr)) - wake_up_all(&brw->write_waitq); + /* Prod writer to recheck readers_active */ + wake_up(&sem->writer); } -EXPORT_SYMBOL_GPL(percpu_up_read); +EXPORT_SYMBOL_GPL(__percpu_up_read); + +#define per_cpu_sum(var) \ +({ \ + typeof(var) __sum = 0; \ + int cpu; \ + compiletime_assert_atomic_type(__sum); \ + for_each_possible_cpu(cpu) \ + __sum += per_cpu(var, cpu); \ + __sum; \ +}) -static int clear_fast_ctr(struct percpu_rw_semaphore *brw) +/* + * Return true if the modular sum of the sem->read_count per-CPU variable is + * zero. If this sum is zero, then it is stable due to the fact that if any + * newly arriving readers increment a given counter, they will immediately + * decrement that same counter. + */ +static bool readers_active_check(struct percpu_rw_semaphore *sem) { - unsigned int sum = 0; - int cpu; + if (per_cpu_sum(*sem->read_count) != 0) + return false; + + /* + * If we observed the decrement; ensure we see the entire critical + * section. + */ - for_each_possible_cpu(cpu) { - sum += per_cpu(*brw->fast_read_ctr, cpu); - per_cpu(*brw->fast_read_ctr, cpu) = 0; - } + smp_mb(); /* C matches B */ - return sum; + return true; } -void percpu_down_write(struct percpu_rw_semaphore *brw) +void percpu_down_write(struct percpu_rw_semaphore *sem) { + /* Notify readers to take the slow path. */ + rcu_sync_enter(&sem->rss); + + down_write(&sem->rw_sem); + /* - * Make rcu_sync_is_idle() == F and thus disable the fast-path in - * percpu_down_read() and percpu_up_read(), and wait for gp pass. - * - * The latter synchronises us with the preceding readers which used - * the fast-past, so we can not miss the result of __this_cpu_add() - * or anything else inside their criticial sections. + * Notify new readers to block; up until now, and thus throughout the + * longish rcu_sync_enter() above, new readers could still come in. */ - rcu_sync_enter(&brw->rss); + WRITE_ONCE(sem->readers_block, 1); - /* exclude other writers, and block the new readers completely */ - down_write(&brw->rw_sem); + smp_mb(); /* D matches A */ - /* nobody can use fast_read_ctr, move its sum into slow_read_ctr */ - atomic_add(clear_fast_ctr(brw), &brw->slow_read_ctr); + /* + * If they don't see our writer of readers_block, then we are + * guaranteed to see their sem->read_count increment, and therefore + * will wait for them. + */ - /* wait for all readers to complete their percpu_up_read() */ - wait_event(brw->write_waitq, !atomic_read(&brw->slow_read_ctr)); + /* Wait for all now active readers to complete. */ + wait_event(sem->writer, readers_active_check(sem)); } EXPORT_SYMBOL_GPL(percpu_down_write); -void percpu_up_write(struct percpu_rw_semaphore *brw) +void percpu_up_write(struct percpu_rw_semaphore *sem) { - /* release the lock, but the readers can't use the fast-path */ - up_write(&brw->rw_sem); /* - * Enable the fast-path in percpu_down_read() and percpu_up_read() - * but only after another gp pass; this adds the necessary barrier - * to ensure the reader can't miss the changes done by us. + * Signal the writer is done, no fast path yet. + * + * One reason that we cannot just immediately flip to readers_fast is + * that new readers might fail to see the results of this writer's + * critical section. + * + * Therefore we force it through the slow path which guarantees an + * acquire and thereby guarantees the critical section's consistency. + */ + smp_store_release(&sem->readers_block, 0); + + /* + * Release the write lock, this will allow readers back in the game. + */ + up_write(&sem->rw_sem); + + /* + * Once this completes (at least one RCU-sched grace period hence) the + * reader fast path will be available again. Safe to use outside the + * exclusive write lock because its counting. */ - rcu_sync_exit(&brw->rss); + rcu_sync_exit(&sem->rss); } EXPORT_SYMBOL_GPL(percpu_up_write); diff --git a/kernel/rcu/sync.c b/kernel/rcu/sync.c index be922c9f3d37..e358313a0d6c 100644 --- a/kernel/rcu/sync.c +++ b/kernel/rcu/sync.c @@ -83,6 +83,18 @@ void rcu_sync_init(struct rcu_sync *rsp, enum rcu_sync_type type) } /** + * Must be called after rcu_sync_init() and before first use. + * + * Ensures rcu_sync_is_idle() returns false and rcu_sync_{enter,exit}() + * pairs turn into NO-OPs. + */ +void rcu_sync_enter_start(struct rcu_sync *rsp) +{ + rsp->gp_count++; + rsp->gp_state = GP_PASSED; +} + +/** * rcu_sync_enter() - Force readers onto slowpath * @rsp: Pointer to rcu_sync structure to use for synchronization * diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 4b5b0c3cef7a..ff7f6f35fc8f 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3390,16 +3390,17 @@ static void __sched notrace __schedule(bool preempt) update_rq_clock(rq); next = pick_next_task(rq, prev); - wallclock = sched_ktime_clock(); - update_task_ravg(prev, rq, PUT_PREV_TASK, wallclock, 0); - update_task_ravg(next, rq, PICK_NEXT_TASK, wallclock, 0); clear_tsk_need_resched(prev); clear_preempt_need_resched(); rq->clock_skip_update = 0; BUG_ON(task_cpu(next) != cpu_of(rq)); + wallclock = sched_ktime_clock(); if (likely(prev != next)) { + update_task_ravg(prev, rq, PUT_PREV_TASK, wallclock, 0); + update_task_ravg(next, rq, PICK_NEXT_TASK, wallclock, 0); + rq->nr_switches++; rq->curr = next; ++*switch_count; @@ -3410,6 +3411,7 @@ static void __sched notrace __schedule(bool preempt) rq = context_switch(rq, prev, next); /* unlocks the rq */ cpu = cpu_of(rq); } else { + update_task_ravg(prev, rq, TASK_UPDATE, wallclock, 0); lockdep_unpin_lock(&rq->lock); raw_spin_unlock_irq(&rq->lock); } @@ -7812,6 +7814,7 @@ void __init sched_init_smp(void) hotcpu_notifier(cpuset_cpu_inactive, CPU_PRI_CPUSET_INACTIVE); update_cluster_topology(); + init_sched_hmp_boost_policy(); init_hrtick(); @@ -7860,6 +7863,7 @@ void __init sched_init(void) BUG_ON(num_possible_cpus() > BITS_PER_LONG); + sched_hmp_parse_dt(); init_clusters(); #ifdef CONFIG_FAIR_GROUP_SCHED diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c index 61e352aeec00..a0686ea29243 100644 --- a/kernel/sched/hmp.c +++ b/kernel/sched/hmp.c @@ -17,6 +17,7 @@ #include <linux/cpufreq.h> #include <linux/list_sort.h> #include <linux/syscore_ops.h> +#include <linux/of.h> #include "sched.h" #include "core_ctl.h" @@ -225,6 +226,52 @@ fail: return ret; } +/* + * It is possible that CPUs of the same micro architecture can have slight + * difference in the efficiency due to other factors like cache size. The + * BOOST_ON_BIG policy may not be optimial for such systems. The required + * boost policy can be specified via device tree to handle this. + */ +static int __read_mostly sched_boost_policy = SCHED_BOOST_NONE; + +/* + * This should be called after clusters are populated and + * the respective efficiency values are initialized. + */ +void init_sched_hmp_boost_policy(void) +{ + /* + * Initialize the boost type here if it is not passed from + * device tree. + */ + if (sched_boost_policy == SCHED_BOOST_NONE) { + if (max_possible_efficiency != min_possible_efficiency) + sched_boost_policy = SCHED_BOOST_ON_BIG; + else + sched_boost_policy = SCHED_BOOST_ON_ALL; + } +} + +void sched_hmp_parse_dt(void) +{ + struct device_node *sn; + const char *boost_policy; + + if (!sched_enable_hmp) + return; + + sn = of_find_node_by_path("/sched-hmp"); + if (!sn) + return; + + if (!of_property_read_string(sn, "boost-policy", &boost_policy)) { + if (!strcmp(boost_policy, "boost-on-big")) + sched_boost_policy = SCHED_BOOST_ON_BIG; + else if (!strcmp(boost_policy, "boost-on-all")) + sched_boost_policy = SCHED_BOOST_ON_ALL; + } +} + unsigned int max_possible_efficiency = 1; unsigned int min_possible_efficiency = UINT_MAX; @@ -1169,12 +1216,9 @@ int task_load_will_fit(struct task_struct *p, u64 task_load, int cpu, enum sched_boost_type sched_boost_type(void) { - if (sched_boost()) { - if (min_possible_efficiency != max_possible_efficiency) - return SCHED_BOOST_ON_BIG; - else - return SCHED_BOOST_ON_ALL; - } + if (sched_boost()) + return sched_boost_policy; + return SCHED_BOOST_NONE; } @@ -2697,7 +2741,8 @@ static void update_task_demand(struct task_struct *p, struct rq *rq, void update_task_ravg(struct task_struct *p, struct rq *rq, int event, u64 wallclock, u64 irqtime) { - if (!rq->window_start || sched_disable_window_stats) + if (!rq->window_start || sched_disable_window_stats || + p->ravg.mark_start == wallclock) return; lockdep_assert_held(&rq->lock); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 1b641e60233e..ada5e580e968 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1399,6 +1399,8 @@ extern u64 cpu_upmigrate_discourage_read_u64(struct cgroup_subsys_state *css, struct cftype *cft); extern int cpu_upmigrate_discourage_write_u64(struct cgroup_subsys_state *css, struct cftype *cft, u64 upmigrate_discourage); +extern void sched_hmp_parse_dt(void); +extern void init_sched_hmp_boost_policy(void); #else /* CONFIG_SCHED_HMP */ @@ -1589,6 +1591,8 @@ static inline void post_big_task_count_change(void) { } static inline void set_hmp_defaults(void) { } static inline void clear_reserved(int cpu) { } +static inline void sched_hmp_parse_dt(void) {} +static inline void init_sched_hmp_boost_policy(void) {} #define trace_sched_cpu_load(...) #define trace_sched_cpu_load_lb(...) diff --git a/kernel/sched/sched_avg.c b/kernel/sched/sched_avg.c index c70e0466c36c..29d8a26a78ed 100644 --- a/kernel/sched/sched_avg.c +++ b/kernel/sched/sched_avg.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2015-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -60,17 +60,17 @@ void sched_get_nr_running_avg(int *avg, int *iowait_avg, int *big_avg) spin_lock_irqsave(&per_cpu(nr_lock, cpu), flags); curr_time = sched_clock(); + diff = curr_time - per_cpu(last_time, cpu); + BUG_ON((s64)diff < 0); + tmp_avg += per_cpu(nr_prod_sum, cpu); - tmp_avg += per_cpu(nr, cpu) * - (curr_time - per_cpu(last_time, cpu)); + tmp_avg += per_cpu(nr, cpu) * diff; tmp_big_avg += per_cpu(nr_big_prod_sum, cpu); - tmp_big_avg += nr_eligible_big_tasks(cpu) * - (curr_time - per_cpu(last_time, cpu)); + tmp_big_avg += nr_eligible_big_tasks(cpu) * diff; tmp_iowait += per_cpu(iowait_prod_sum, cpu); - tmp_iowait += nr_iowait_cpu(cpu) * - (curr_time - per_cpu(last_time, cpu)); + tmp_iowait += nr_iowait_cpu(cpu) * diff; per_cpu(last_time, cpu) = curr_time; @@ -107,14 +107,15 @@ EXPORT_SYMBOL(sched_get_nr_running_avg); */ void sched_update_nr_prod(int cpu, long delta, bool inc) { - int diff; - s64 curr_time; + u64 diff; + u64 curr_time; unsigned long flags, nr_running; spin_lock_irqsave(&per_cpu(nr_lock, cpu), flags); nr_running = per_cpu(nr, cpu); curr_time = sched_clock(); diff = curr_time - per_cpu(last_time, cpu); + BUG_ON((s64)diff < 0); per_cpu(last_time, cpu) = curr_time; per_cpu(nr, cpu) = nr_running + (inc ? delta : -delta); diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 36ea0d54e05b..902657d4cac5 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1020,7 +1020,7 @@ choice config DEBUG_SPINLOCK_BITE_ON_BUG bool "Cause a Watchdog Bite on Spinlock bug" - depends on MSM_WATCHDOG_V2 + depends on QCOM_WATCHDOG_V2 help On a spinlock bug, cause a watchdog bite so that we can get the precise state of the system captured at the time of spin dump. This is mutually diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c index 6d6893c7d99d..4e3a205551e0 100644 --- a/net/rmnet_data/rmnet_data_vnd.c +++ b/net/rmnet_data/rmnet_data_vnd.c @@ -928,7 +928,7 @@ int rmnet_vnd_add_tc_flow(uint32_t id, uint32_t map_flow, uint32_t tc_flow) list_add(&(itm->list), &(dev_conf->flow_head)); write_unlock_irqrestore(&dev_conf->flow_map_lock, flags); - LOGD("Created flow mapping [%s][0x%08X][0x%08X]@%p", + LOGD("Created flow mapping [%s][0x%08X][0x%08X]@%pK", dev->name, itm->map_flow_id, itm->tc_flow_id[0], itm); return RMNET_CONFIG_OK; diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f237a2188fe1..f580a1048d65 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -136,7 +136,9 @@ snd-soc-wcd9335-objs := wcd9335.o snd-soc-wcd934x-objs := wcd934x.o snd-soc-wcd9xxx-objs := wcd9xxx-resmgr.o wcd9xxx-mbhc.o wcd9xxx-common.o wcdcal-hwdep.o snd-soc-wcd9xxx-v2-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o -audio-ext-clock-objs := audio-ext-clk.o +ifeq ($(CONFIG_COMMON_CLK_MSM), y) + audio-ext-clock-objs := audio-ext-clk.o +endif snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o wsa881x-temp-sensor.o snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o @@ -344,7 +346,9 @@ obj-$(CONFIG_SND_SOC_WCD9320) += snd-soc-wcd9320.o obj-$(CONFIG_SND_SOC_WCD9330) += snd-soc-wcd9330.o obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o obj-$(CONFIG_SND_SOC_WCD934X) += wcd934x/ -obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clock.o +ifeq ($(CONFIG_COMMON_CLK_MSM), y) + obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clock.o +endif obj-$(CONFIG_SND_SOC_WCD9XXX) += snd-soc-wcd9xxx.o obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += snd-soc-wcd9xxx-v2.o obj-$(CONFIG_SND_SOC_WCD_CPE) += snd-soc-wcd-cpe.o diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c index 69246ac9cc87..ee8b27dbec64 100644 --- a/sound/soc/codecs/wcd-dsp-mgr.c +++ b/sound/soc/codecs/wcd-dsp-mgr.c @@ -16,6 +16,8 @@ #include <linux/stringify.h> #include <linux/of.h> #include <linux/component.h> +#include <linux/dma-mapping.h> +#include <soc/qcom/ramdump.h> #include <sound/wcd-dsp-mgr.h> #include "wcd-dsp-utils.h" @@ -75,6 +77,32 @@ static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type); #define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state) +/* SSR relate status macros */ +#define WDSP_SSR_STATUS_WDSP_READY BIT(0) +#define WDSP_SSR_STATUS_CDC_READY BIT(1) +#define WDSP_SSR_STATUS_READY \ + (WDSP_SSR_STATUS_WDSP_READY | WDSP_SSR_STATUS_CDC_READY) +#define WDSP_SSR_READY_WAIT_TIMEOUT (10 * HZ) + +enum wdsp_ssr_type { + + /* Init value, indicates there is no SSR in progress */ + WDSP_SSR_TYPE_NO_SSR = 0, + + /* + * Indicates WDSP crashed. The manager driver internally + * decides when to perform WDSP restart based on the + * users of wdsp. Hence there is no explicit WDSP_UP. + */ + WDSP_SSR_TYPE_WDSP_DOWN, + + /* Indicates codec hardware is down */ + WDSP_SSR_TYPE_CDC_DOWN, + + /* Indicates codec hardware is up, trigger to restart WDSP */ + WDSP_SSR_TYPE_CDC_UP, +}; + struct wdsp_cmpnt { /* OF node of the phandle */ @@ -96,6 +124,21 @@ struct wdsp_cmpnt { struct wdsp_cmpnt_ops *ops; }; +struct wdsp_ramdump_data { + + /* Ramdump device */ + void *rd_dev; + + /* DMA address of the dump */ + dma_addr_t rd_addr; + + /* Virtual address of the dump */ + void *rd_v_addr; + + /* Data provided through error interrupt */ + struct wdsp_err_intr_arg err_data; +}; + struct wdsp_mgr_priv { /* Manager driver's struct device pointer */ @@ -130,8 +173,35 @@ struct wdsp_mgr_priv { /* Lock for serializing ops called by components */ struct mutex api_mutex; + + struct wdsp_ramdump_data dump_data; + + /* SSR related */ + enum wdsp_ssr_type ssr_type; + struct mutex ssr_mutex; + struct work_struct ssr_work; + u16 ready_status; + struct completion ready_compl; }; +static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type) +{ + switch (type) { + case WDSP_SSR_TYPE_NO_SSR: + return "NO_SSR"; + case WDSP_SSR_TYPE_WDSP_DOWN: + return "WDSP_DOWN"; + case WDSP_SSR_TYPE_CDC_DOWN: + return "CDC_DOWN"; + case WDSP_SSR_TYPE_CDC_UP: + return "CDC_UP"; + default: + pr_err("%s: Invalid ssr_type %d\n", + __func__, type); + return "Invalid"; + } +} + static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type) { switch (type) { @@ -148,6 +218,26 @@ static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type) } } +static void __wdsp_clr_ready_locked(struct wdsp_mgr_priv *wdsp, + u16 value) +{ + wdsp->ready_status &= ~(value); + WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status); +} + +static void __wdsp_set_ready_locked(struct wdsp_mgr_priv *wdsp, + u16 value, bool mark_complete) +{ + wdsp->ready_status |= value; + WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status); + + if (mark_complete && + wdsp->ready_status == WDSP_SSR_STATUS_READY) { + WDSP_DBG(wdsp, "marking ready completion"); + complete(&wdsp->ready_compl); + } +} + static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp, enum wdsp_event_type event, void *data) @@ -199,6 +289,18 @@ static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp, return ret; } +static void wdsp_deinit_components(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data); + } +} + static int wdsp_init_components(struct wdsp_mgr_priv *wdsp) { struct wdsp_cmpnt *cmpnt; @@ -230,6 +332,8 @@ static int wdsp_init_components(struct wdsp_mgr_priv *wdsp) cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data); } + } else { + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_INIT, NULL); } return ret; @@ -272,6 +376,7 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, struct wdsp_cmpnt *ctl; struct wdsp_img_segment *seg = NULL; enum wdsp_event_type pre, post; + long status; int ret; ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL); @@ -279,9 +384,11 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, if (type == WDSP_ELF_FLAG_RE) { pre = WDSP_EVENT_PRE_DLOAD_CODE; post = WDSP_EVENT_POST_DLOAD_CODE; + status = WDSP_STATUS_CODE_DLOADED; } else if (type == WDSP_ELF_FLAG_WRITE) { pre = WDSP_EVENT_PRE_DLOAD_DATA; post = WDSP_EVENT_POST_DLOAD_DATA; + status = WDSP_STATUS_DATA_DLOADED; } else { WDSP_ERR(wdsp, "Invalid type %u", type); return -EINVAL; @@ -312,6 +419,8 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, } } + WDSP_SET_STATUS(wdsp, status); + /* Notify all components that image is downloaded */ wdsp_broadcast_event_downseq(wdsp, post, NULL); @@ -321,42 +430,47 @@ done: return ret; } -static void wdsp_load_fw_image(struct work_struct *work) +static int wdsp_init_and_dload_code_sections(struct wdsp_mgr_priv *wdsp) { - struct wdsp_mgr_priv *wdsp; - struct wdsp_cmpnt *cmpnt; - int ret, idx; - - wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work); - if (!wdsp) { - pr_err("%s: Invalid private_data\n", __func__); - goto done; - } + int ret; + bool is_initialized; - /* Initialize the components first */ - ret = wdsp_init_components(wdsp); - if (IS_ERR_VALUE(ret)) - goto done; + is_initialized = WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_INITIALIZED); - /* Set init done status */ - WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + if (!is_initialized) { + /* Components are not initialized yet, initialize them */ + ret = wdsp_init_components(wdsp); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "INIT failed, err = %d", ret); + goto done; + } + WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } /* Download the read-execute sections of image */ ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE); if (IS_ERR_VALUE(ret)) { WDSP_ERR(wdsp, "Error %d to download code sections", ret); - for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { - cmpnt = WDSP_GET_COMPONENT(wdsp, idx); - if (cmpnt->ops && cmpnt->ops->deinit) - cmpnt->ops->deinit(cmpnt->cdev, - cmpnt->priv_data); - } - WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + goto done; } - - WDSP_SET_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED); done: - return; + return ret; +} + +static void wdsp_load_fw_image(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + return; + } + + ret = wdsp_init_and_dload_code_sections(wdsp); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "dload code sections failed, err = %d", ret); } static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) @@ -377,8 +491,6 @@ static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) goto done; } - WDSP_SET_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); - wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL); ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, @@ -399,6 +511,21 @@ static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp) { int ret; + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + /* + * If Disable happened while SSR is in progress, then set the SSR + * ready status indicating WDSP is now ready. Ignore the disable + * event here and let the SSR handler go through shutdown. + */ + if (wdsp->ssr_type != WDSP_SSR_TYPE_NO_SSR) { + __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY, true); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + return 0; + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + /* Make sure wdsp is in good state */ if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status); @@ -478,8 +605,190 @@ static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev, return cmpnt->cdev; } +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 ramdump_segment rd_seg; + int ret = 0; + + if (wdsp->ssr_type != WDSP_SSR_TYPE_WDSP_DOWN || + !data->mem_dumps_enabled) { + WDSP_DBG(wdsp, "cannot dump memory, ssr_type %s, dumps %s", + wdsp_get_ssr_type_string(wdsp->ssr_type), + !(data->mem_dumps_enabled) ? "disabled" : "enabled"); + goto done; + } + + if (data->dump_size == 0 || + data->remote_start_addr < wdsp->base_addr) { + WDSP_ERR(wdsp, "Invalid start addr 0x%x or dump_size 0x%zx", + data->remote_start_addr, data->dump_size); + goto done; + } + + if (!wdsp->dump_data.rd_dev) { + WDSP_ERR(wdsp, "Ramdump device is not setup"); + goto done; + } + + WDSP_DBG(wdsp, "base_addr 0x%x, dump_start_addr 0x%x, dump_size 0x%zx", + wdsp->base_addr, data->remote_start_addr, data->dump_size); + + /* Allocate memory for dumps */ + wdsp->dump_data.rd_v_addr = dma_alloc_coherent(wdsp->mdev, + data->dump_size, + &wdsp->dump_data.rd_addr, + GFP_KERNEL); + if (!wdsp->dump_data.rd_v_addr) + goto done; + + img_section.addr = data->remote_start_addr - wdsp->base_addr; + img_section.size = data->dump_size; + img_section.data = wdsp->dump_data.rd_v_addr; + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT, + WDSP_EVENT_READ_SECTION, + &img_section); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to read dumps, size 0x%zx at addr 0x%x", + img_section.size, img_section.addr); + goto err_read_dumps; + } + + rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr; + rd_seg.size = img_section.size; + rd_seg.v_address = wdsp->dump_data.rd_v_addr; + + ret = do_ramdump(wdsp->dump_data.rd_dev, &rd_seg, 1); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "do_ramdump failed with error %d", ret); + +err_read_dumps: + dma_free_coherent(wdsp->mdev, data->dump_size, + wdsp->dump_data.rd_v_addr, wdsp->dump_data.rd_addr); +done: + return; +} + +static void wdsp_ssr_work_fn(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + wdsp = container_of(work, struct wdsp_mgr_priv, ssr_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + return; + } + + 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) { + 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_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + ret = wait_for_completion_timeout(&wdsp->ready_compl, + WDSP_SSR_READY_WAIT_TIMEOUT); + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + if (ret == 0) { + WDSP_ERR(wdsp, "wait_for_ready timed out, status = 0x%x", + wdsp->ready_status); + goto done; + } + + /* Data sections are to downloaded per WDSP boot */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + + /* + * Even though code section could possible be retained on DSP + * crash, go ahead and still re-download just to avoid any + * memory corruption from previous crash. + */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED); + + /* If codec went down, then all components must be re-initialized */ + if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_DOWN) { + wdsp_deinit_components(wdsp); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } + + ret = wdsp_init_and_dload_code_sections(wdsp); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to dload code sections err = %d", + ret); + goto done; + } + + /* SSR handling is finished, mark SSR type as NO_SSR */ + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); +} + +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; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + current_ssr_type = wdsp->ssr_type; + WDSP_DBG(wdsp, "Current ssr_type %s, handling ssr_type %s", + wdsp_get_ssr_type_string(current_ssr_type), + wdsp_get_ssr_type_string(ssr_type)); + wdsp->ssr_type = ssr_type; + + if (arg) { + err_data = (struct wdsp_err_intr_arg *) arg; + memcpy(&wdsp->dump_data.err_data, err_data, + sizeof(*err_data)); + } else { + memset(&wdsp->dump_data.err_data, 0, + sizeof(wdsp->dump_data.err_data)); + } + + 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_UP: + __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true); + break; + + default: + WDSP_ERR(wdsp, "undefined ssr_type %d\n", ssr_type); + /* Revert back the ssr_type for undefined events */ + wdsp->ssr_type = current_ssr_type; + break; + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + + return 0; +} + static int wdsp_intr_handler(struct device *wdsp_dev, - enum wdsp_intr intr) + enum wdsp_intr intr, void *arg) { struct wdsp_mgr_priv *wdsp; int ret; @@ -495,6 +804,9 @@ static int wdsp_intr_handler(struct device *wdsp_dev, ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC, WDSP_EVENT_IPC1_INTR, NULL); break; + case WDSP_ERR_INTR: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN); + break; default: ret = -EINVAL; break; @@ -585,6 +897,11 @@ static int wdsp_mgr_bind(struct device *dev) wdsp->ops = &wdsp_ops; + /* Setup ramdump device */ + wdsp->dump_data.rd_dev = create_ramdump_device("wdsp", dev); + if (!wdsp->dump_data.rd_dev) + dev_info(dev, "%s: create_ramdump_device failed\n", __func__); + ret = component_bind_all(dev, wdsp->ops); if (IS_ERR_VALUE(ret)) WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret); @@ -616,6 +933,11 @@ static void wdsp_mgr_unbind(struct device *dev) component_unbind_all(dev, wdsp->ops); + if (wdsp->dump_data.rd_dev) { + destroy_ramdump_device(wdsp->dump_data.rd_dev); + wdsp->dump_data.rd_dev = NULL; + } + /* Clear all status bits */ wdsp->status = 0x00; @@ -746,6 +1068,12 @@ static int wdsp_mgr_probe(struct platform_device *pdev) INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image); INIT_LIST_HEAD(wdsp->seg_list); mutex_init(&wdsp->api_mutex); + mutex_init(&wdsp->ssr_mutex); + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; + wdsp->ready_status = WDSP_SSR_STATUS_READY; + INIT_WORK(&wdsp->ssr_work, wdsp_ssr_work_fn); + init_completion(&wdsp->ready_compl); + arch_setup_dma_ops(wdsp->mdev, 0, 0, NULL, 0); dev_set_drvdata(mdev, wdsp); ret = component_master_add_with_match(mdev, &wdsp_master_ops, @@ -759,6 +1087,7 @@ static int wdsp_mgr_probe(struct platform_device *pdev) err_master_add: mutex_destroy(&wdsp->api_mutex); + mutex_destroy(&wdsp->ssr_mutex); err_dt_parse: devm_kfree(mdev, wdsp->seg_list); devm_kfree(mdev, wdsp); @@ -775,6 +1104,7 @@ static int wdsp_mgr_remove(struct platform_device *pdev) component_master_del(mdev, &wdsp_master_ops); mutex_destroy(&wdsp->api_mutex); + mutex_destroy(&wdsp->ssr_mutex); devm_kfree(mdev, wdsp->seg_list); devm_kfree(mdev, wdsp); dev_set_drvdata(mdev, NULL); diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index d207d9ccda34..3cbc1e7821cf 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -2318,7 +2318,7 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, schedule_delayed_work(&mbhc->mbhc_firmware_dwork, usecs_to_jiffies(FW_READ_TIMEOUT)); else - pr_err("%s: Skipping to read mbhc fw, 0x%p %p\n", + pr_err("%s: Skipping to read mbhc fw, 0x%pK %pK\n", __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); } pr_debug("%s: leave %d\n", __func__, rc); diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c index 3049d87c6c05..60efcb174740 100644 --- a/sound/soc/codecs/wcd-spi.c +++ b/sound/soc/codecs/wcd-spi.c @@ -639,12 +639,10 @@ static int wcd_spi_init(struct spi_device *spi) WCD_SPI_SLAVE_TRNS_LEN, 0xFFFF0000, (WCD_SPI_RW_MULTI_MAX_LEN / 4) << 16); -done: - return ret; - err_wr_en: wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, WCD_SPI_CLK_FLAG_IMMEDIATE); +done: return ret; } @@ -813,6 +811,27 @@ static int wdsp_spi_dload_section(struct spi_device *spi, return ret; } +static int wdsp_spi_read_section(struct spi_device *spi, void *data) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wdsp_img_section *sec = data; + struct wcd_spi_msg msg; + int ret; + + msg.remote_addr = sec->addr + wcd_spi->mem_base_addr; + msg.data = sec->data; + msg.len = sec->size; + + dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n", + __func__, msg.remote_addr, msg.len); + + ret = wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_READ); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n", + __func__, msg.remote_addr, msg.len); + return ret; +} + static int wdsp_spi_event_handler(struct device *dev, void *priv_data, enum wdsp_event_type event, void *data) @@ -824,6 +843,7 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data, __func__, event); switch (event) { + case WDSP_EVENT_PRE_DLOAD_CODE: case WDSP_EVENT_PRE_DLOAD_DATA: ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, WCD_SPI_CLK_FLAG_IMMEDIATE); @@ -846,6 +866,11 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data, case WDSP_EVENT_DLOAD_SECTION: ret = wdsp_spi_dload_section(spi, data); break; + + case WDSP_EVENT_READ_SECTION: + ret = wdsp_spi_read_section(spi, data); + break; + default: dev_dbg(&spi->dev, "%s: Unhandled event %d\n", __func__, event); diff --git a/sound/soc/codecs/wcd9330.c b/sound/soc/codecs/wcd9330.c index a8d6e0fa4732..fa396aa55ac9 100644 --- a/sound/soc/codecs/wcd9330.c +++ b/sound/soc/codecs/wcd9330.c @@ -5474,7 +5474,7 @@ static int tomtom_set_channel_map(struct snd_soc_dai *dai, struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec); struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent); if (!tx_slot || !rx_slot) { - pr_err("%s: Invalid tx_slot=%p, rx_slot=%p\n", + pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n", __func__, tx_slot, rx_slot); return -EINVAL; } @@ -5519,7 +5519,7 @@ static int tomtom_get_channel_map(struct snd_soc_dai *dai, case AIF2_PB: case AIF3_PB: if (!rx_slot || !rx_num) { - pr_err("%s: Invalid rx_slot %p or rx_num %p\n", + pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n", __func__, rx_slot, rx_num); return -EINVAL; } @@ -5538,7 +5538,7 @@ static int tomtom_get_channel_map(struct snd_soc_dai *dai, case AIF4_VIFEED: case AIF4_MAD_TX: if (!tx_slot || !tx_num) { - pr_err("%s: Invalid tx_slot %p or tx_num %p\n", + pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n", __func__, tx_slot, tx_num); return -EINVAL; } @@ -8228,7 +8228,7 @@ static void tomtom_compute_impedance(struct wcd9xxx_mbhc *mbhc, s16 *l, s16 *r, struct tomtom_priv *tomtom; if (!mbhc) { - pr_err("%s: Invalid parameters mbhc = %p\n", + pr_err("%s: Invalid parameters mbhc = %pK\n", __func__, mbhc); return; } @@ -8287,7 +8287,7 @@ static void tomtom_zdet_error_approx(struct wcd9xxx_mbhc *mbhc, uint32_t *zl, const int shift = TOMTOM_ZDET_ERROR_APPROX_SHIFT; if (!zl || !zr || !mbhc) { - pr_err("%s: Invalid parameters zl = %p zr = %p, mbhc = %p\n", + pr_err("%s: Invalid parameters zl = %pK zr = %pK, mbhc = %pK\n", __func__, zl, zr, mbhc); return; } @@ -8602,7 +8602,7 @@ static int tomtom_codec_fll_enable(struct snd_soc_codec *codec, struct wcd9xxx *wcd9xxx; if (!codec || !codec->control_data) { - pr_err("%s: Invalid codec handle, %p\n", + pr_err("%s: Invalid codec handle, %pK\n", __func__, codec); return -EINVAL; } diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 2cc895cf1d32..46b8e7f72eb8 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -11080,7 +11080,7 @@ static int tasha_get_channel_map(struct snd_soc_dai *dai, case AIF4_PB: case AIF_MIX1_PB: if (!rx_slot || !rx_num) { - pr_err("%s: Invalid rx_slot %p or rx_num %p\n", + pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n", __func__, rx_slot, rx_num); return -EINVAL; } @@ -11099,7 +11099,7 @@ static int tasha_get_channel_map(struct snd_soc_dai *dai, case AIF4_MAD_TX: case AIF4_VIFEED: if (!tx_slot || !tx_num) { - pr_err("%s: Invalid tx_slot %p or tx_num %p\n", + pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n", __func__, tx_slot, tx_num); return -EINVAL; } @@ -11137,7 +11137,7 @@ static int tasha_set_channel_map(struct snd_soc_dai *dai, core = dev_get_drvdata(dai->codec->dev->parent); if (!tx_slot || !rx_slot) { - pr_err("%s: Invalid tx_slot=%p, rx_slot=%p\n", + pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n", __func__, tx_slot, rx_slot); return -EINVAL; } diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c index 225b3a755f66..e649770297f1 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -28,19 +28,22 @@ #define WCD_MEM_ENABLE_MAX_RETRIES 20 #define WCD_DSP_BOOT_TIMEOUT_MS 3000 #define WCD_SYSFS_ENTRY_MAX_LEN 8 - -#define WCD_CNTL_MUTEX_LOCK(codec, lock) \ -{ \ - dev_dbg(codec->dev, "mutex_lock(%s)\n", \ - __func__); \ - mutex_lock(&lock); \ +#define WCD_PROCFS_ENTRY_MAX_LEN 16 +#define WCD_934X_RAMDUMP_START_ADDR 0x20100000 +#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128) + +#define WCD_CNTL_MUTEX_LOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ } -#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \ -{ \ - dev_dbg(codec->dev, "mutex_unlock(%s)\n",\ - __func__); \ - mutex_unlock(&lock); \ +#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ } struct wcd_cntl_attribute { @@ -147,6 +150,97 @@ static struct kobj_type wcd_cntl_ktype = { .sysfs_ops = &wcd_cntl_sysfs_ops, }; +static void wcd_cntl_change_online_state(struct wcd_dsp_cntl *cntl, + u8 online) +{ + struct wdsp_ssr_entry *ssr_entry = &cntl->ssr_entry; + unsigned long ret; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + ssr_entry->offline = !online; + /* Make sure the write is complete */ + wmb(); + ret = xchg(&ssr_entry->offline_change, 1); + wake_up_interruptible(&ssr_entry->offline_poll_wait); + dev_dbg(cntl->codec->dev, + "%s: requested %u, offline %u offline_change %u, ret = %ldn", + __func__, online, ssr_entry->offline, + ssr_entry->offline_change, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); +} + +static ssize_t wdsp_ssr_entry_read(struct snd_info_entry *entry, + void *file_priv_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + int len = 0; + char buffer[WCD_PROCFS_ENTRY_MAX_LEN]; + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + ssize_t ret; + u8 offline; + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + if (!cntl) { + pr_err("%s: Invalid private data for SSR procfs entry\n", + __func__); + return -EINVAL; + } + + ssr_entry = &cntl->ssr_entry; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + offline = ssr_entry->offline; + /* Make sure the read is complete */ + rmb(); + dev_dbg(cntl->codec->dev, "%s: offline = %s\n", __func__, + offline ? "true" : "false"); + len = snprintf(buffer, sizeof(buffer), "%s\n", + offline ? "OFFLINE" : "ONLINE"); + ret = simple_read_from_buffer(buf, count, &pos, buffer, len); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static unsigned int wdsp_ssr_entry_poll(struct snd_info_entry *entry, + void *private_data, struct file *file, + poll_table *wait) +{ + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + unsigned int ret = 0; + + if (!entry || !entry->private_data) { + pr_err("%s: %s is NULL\n", __func__, + (!entry) ? "entry" : "private_data"); + return -EINVAL; + } + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + ssr_entry = &cntl->ssr_entry; + + dev_dbg(cntl->codec->dev, "%s: Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + poll_wait(file, &ssr_entry->offline_poll_wait, wait); + dev_dbg(cntl->codec->dev, "%s: Woken up Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + if (xchg(&ssr_entry->offline_change, 0)) + ret = POLLIN | POLLPRI | POLLRDNORM; + dev_dbg(cntl->codec->dev, "%s: ret (%d) from poll_wait\n", + __func__, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static struct snd_info_entry_ops wdsp_ssr_entry_ops = { + .read = wdsp_ssr_entry_read, + .poll = wdsp_ssr_entry_poll, +}; + static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl) { struct snd_soc_codec *codec = cntl->codec; @@ -553,7 +647,8 @@ static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data) if (cntl->m_dev && cntl->m_ops && cntl->m_ops->intr_handler) - ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_IPC1_INTR); + ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_IPC1_INTR, + NULL); else ret = -EINVAL; @@ -568,8 +663,10 @@ 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; u16 status = 0; u8 reg_val; + int ret = 0; reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A); status = status | reg_val; @@ -580,6 +677,23 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data) dev_info(codec->dev, "%s: error interrupt status = 0x%x\n", __func__, status); + if ((status & cntl->irqs.fatal_irqs) && + (cntl->m_dev && cntl->m_ops && cntl->m_ops->intr_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); + 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", + __func__); + } + return IRQ_HANDLED; } @@ -591,10 +705,15 @@ static int wcd_control_handler(struct device *dev, void *priv_data, int ret = 0; switch (event) { + case WDSP_EVENT_POST_INIT: case WDSP_EVENT_POST_DLOAD_CODE: case WDSP_EVENT_DLOAD_FAILED: case WDSP_EVENT_POST_SHUTDOWN: + if (event == WDSP_EVENT_POST_DLOAD_CODE) + /* Mark DSP online since code download is complete */ + wcd_cntl_change_online_state(cntl, 1); + /* Disable CPAR */ wcd_cntl_cpar_ctrl(cntl, false); /* Disable all the clocks */ @@ -605,12 +724,8 @@ static int wcd_control_handler(struct device *dev, void *priv_data, __func__, ret); break; - case WDSP_EVENT_PRE_DLOAD_CODE: - - wcd_cntl_enable_memory(cntl); - break; - case WDSP_EVENT_PRE_DLOAD_DATA: + case WDSP_EVENT_PRE_DLOAD_CODE: /* Enable all the clocks */ ret = wcd_cntl_clocks_enable(cntl); @@ -623,6 +738,9 @@ static int wcd_control_handler(struct device *dev, void *priv_data, /* Enable CPAR */ wcd_cntl_cpar_ctrl(cntl, true); + + if (event == WDSP_EVENT_PRE_DLOAD_CODE) + wcd_cntl_enable_memory(cntl); break; case WDSP_EVENT_DO_BOOT: @@ -697,6 +815,8 @@ static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl) debugfs_create_u32("debug_mode", S_IRUGO | S_IWUSR, cntl->entry, &cntl->debug_mode); + debugfs_create_bool("ramdump_enable", S_IRUGO | S_IWUSR, + cntl->entry, &cntl->ramdump_enable); done: return; } @@ -827,6 +947,10 @@ static int wcd_ctrl_component_bind(struct device *dev, void *data) { struct wcd_dsp_cntl *cntl; + struct snd_soc_codec *codec; + struct snd_card *card; + struct snd_info_entry *entry; + char proc_name[WCD_PROCFS_ENTRY_MAX_LEN]; int ret = 0; if (!dev || !master || !data) { @@ -844,13 +968,47 @@ static int wcd_ctrl_component_bind(struct device *dev, cntl->m_dev = master; cntl->m_ops = data; - if (cntl->m_ops->register_cmpnt_ops) - ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, - &control_ops); + if (!cntl->m_ops->register_cmpnt_ops) { + dev_err(dev, "%s: invalid master callback register_cmpnt_ops\n", + __func__); + ret = -EINVAL; + goto done; + } - if (ret) + ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, &control_ops); + if (ret) { dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", __func__, ret); + goto done; + } + + codec = cntl->codec; + card = codec->component.card->snd_card; + snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe", + cntl->dsp_instance, "_state"); + entry = snd_info_create_card_entry(card, proc_name, card->proc_root); + if (!entry) { + /* Do not treat this as Fatal error */ + dev_err(dev, "%s: Failed to create procfs entry %s\n", + __func__, proc_name); + goto done; + } + + cntl->ssr_entry.entry = entry; + cntl->ssr_entry.offline = 1; + entry->size = WCD_PROCFS_ENTRY_MAX_LEN; + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->c.ops = &wdsp_ssr_entry_ops; + entry->private_data = cntl; + ret = snd_info_register(entry); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: Failed to register entry %s, err = %d\n", + __func__, proc_name, ret); + snd_info_free_entry(entry); + /* Let bind still happen even if creating the entry failed */ + ret = 0; + } +done: return ret; } @@ -929,6 +1087,8 @@ void wcd_dsp_cntl_init(struct snd_soc_codec *codec, memcpy(&control->irqs, ¶ms->irqs, sizeof(control->irqs)); init_completion(&control->boot_complete); mutex_init(&control->clk_mutex); + mutex_init(&control->ssr_mutex); + init_waitqueue_head(&control->ssr_entry.offline_poll_wait); /* * The default state of WDSP is in SVS mode. @@ -981,6 +1141,7 @@ void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl) component_del(codec->dev, &wcd_ctrl_component_ops); mutex_destroy(&control->clk_mutex); + mutex_destroy(&control->ssr_mutex); kfree(*cntl); *cntl = NULL; } diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h index 3d6db776a0b5..cd6697b3d641 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h @@ -54,6 +54,13 @@ struct wcd_dsp_params { u32 dsp_instance; }; +struct wdsp_ssr_entry { + u8 offline; + u8 offline_change; + wait_queue_head_t offline_poll_wait; + struct snd_info_entry *entry; +}; + struct wcd_dsp_cntl { /* Handle to codec */ struct snd_soc_codec *codec; @@ -77,6 +84,7 @@ struct wcd_dsp_cntl { /* Debugfs related */ struct dentry *entry; u32 debug_mode; + bool ramdump_enable; /* WDSP manager drivers data */ struct device *m_dev; @@ -88,6 +96,10 @@ struct wcd_dsp_cntl { /* Keep track of WDSP boot status */ bool is_wdsp_booted; + + /* SSR related */ + struct wdsp_ssr_entry ssr_entry; + struct mutex ssr_mutex; }; void wcd_dsp_cntl_init(struct snd_soc_codec *codec, diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h index ac3031ffe615..940fdf89d361 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-routing.h +++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h @@ -21,6 +21,7 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, /* Virtual input widget Mixer */ {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"}, @@ -65,6 +66,8 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11"}, {"AIF3_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + {"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"}, + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, @@ -121,6 +124,7 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"MAD_INP MUX", "MAD", "MAD_SEL MUX"}, {"MAD_INP MUX", "DEC1", "ADC MUX1"}, + {"MAD_BROADCAST", "Switch", "MAD_INP MUX"}, {"MAD_CPE1", "Switch", "MAD_INP MUX"}, {"MAD_CPE2", "Switch", "MAD_INP MUX"}, diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index b4a73d17c322..9e18c17d6f1c 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -186,6 +186,7 @@ enum { AIF3_CAP, AIF4_PB, AIF4_VIFEED, + AIF4_MAD_TX, NUM_CODEC_DAIS, }; @@ -301,13 +302,13 @@ static const struct wcd9xxx_ch tavil_tx_chs[WCD934X_TX_MAX] = { }; static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { - 0, /* AIF1_PB */ - BIT(AIF2_CAP) | BIT(AIF3_CAP), /* AIF1_CAP */ - 0, /* AIF2_PB */ - BIT(AIF1_CAP) | BIT(AIF3_CAP), /* AIF2_CAP */ - 0, /* AIF3_PB */ - BIT(AIF1_CAP) | BIT(AIF2_CAP), /* AIF3_CAP */ - 0, /* AIF4_PB */ + 0, /* AIF1_PB */ + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */ + 0, /* AIF2_PB */ + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */ + 0, /* AIF3_PB */ + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */ + 0, /* AIF4_PB */ }; /* Codec supports 2 IIR filters */ @@ -1271,6 +1272,8 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, return 0; } break; + case AIF4_MAD_TX: + break; default: dev_err(codec->dev, "Unknown AIF %d\n", dai_id); mutex_unlock(&tavil_p->codec_mutex); @@ -2474,7 +2477,7 @@ done: return ret; } -static int tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable) +static int __tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable) { int rc = 0; @@ -2519,6 +2522,29 @@ done: return rc; } +static int tavil_codec_ape_enable_mad(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x40); + rc = __tavil_codec_enable_mad(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x00); + __tavil_codec_enable_mad(codec, false); + break; + } + + dev_dbg(tavil->dev, "%s: event = %d\n", __func__, event); + return rc; +} + static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -2533,7 +2559,7 @@ static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w, goto done; snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x20); - rc = tavil_codec_enable_mad(codec, true); + rc = __tavil_codec_enable_mad(codec, true); if (IS_ERR_VALUE(rc)) { tavil->mad_switch_cnt--; goto done; @@ -2546,7 +2572,7 @@ static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w, goto done; snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x00); - tavil_codec_enable_mad(codec, false); + __tavil_codec_enable_mad(codec, false); break; } done: @@ -5789,6 +5815,11 @@ static const struct snd_kcontrol_new aif3_cap_mixer[] = { slim_tx_mixer_get, slim_tx_mixer_put), }; +static const struct snd_kcontrol_new aif4_mad_mixer[] = { + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text, slim_rx_mux_get, slim_rx_mux_put); WCD_DAPM_ENUM_EXT(slim_rx1, SND_SOC_NOPM, 0, slim_rx_mux_text, @@ -6110,6 +6141,9 @@ static const struct snd_kcontrol_new mad_cpe1_switch = static const struct snd_kcontrol_new mad_cpe2_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); +static const struct snd_kcontrol_new mad_brdcst_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + static const struct snd_kcontrol_new adc_us_mux0_switch = SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); @@ -6532,10 +6566,16 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, AIF4_VIFEED, 0, tavil_codec_enable_slimvi_feedback, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), SND_SOC_DAPM_INPUT("VIINPUT"), @@ -6670,6 +6710,10 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { WCD_DAPM_MUX("MAD_SEL MUX", 0, mad_sel), WCD_DAPM_MUX("MAD_INP MUX", 0, mad_inp_mux), + SND_SOC_DAPM_SWITCH_E("MAD_BROADCAST", SND_SOC_NOPM, 0, 0, + &mad_brdcst_switch, tavil_codec_ape_enable_mad, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH_E("MAD_CPE1", SND_SOC_NOPM, 0, 0, &mad_cpe1_switch, tavil_codec_cpe_mad_ctl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), @@ -6826,6 +6870,7 @@ static int tavil_get_channel_map(struct snd_soc_dai *dai, case AIF1_CAP: case AIF2_CAP: case AIF3_CAP: + case AIF4_MAD_TX: case AIF4_VIFEED: if (!tx_slot || !tx_num) { dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK or tx_num 0x%pK\n", @@ -6864,6 +6909,7 @@ static int tavil_set_channel_map(struct snd_soc_dai *dai, { struct tavil_priv *tavil; struct wcd9xxx *core; + struct wcd9xxx_codec_dai_data *dai_data = NULL; tavil = snd_soc_codec_get_drvdata(dai->codec); core = dev_get_drvdata(dai->codec->dev->parent); @@ -6878,6 +6924,12 @@ static int tavil_set_channel_map(struct snd_soc_dai *dai, wcd9xxx_init_slimslave(core, core->slim->laddr, tx_num, tx_slot, rx_num, rx_slot); + /* Reserve TX13 for MAD data channel */ + dai_data = &tavil->dai[AIF4_MAD_TX]; + if (dai_data) + list_add_tail(&core->tx_chs[WCD934X_TX13].list, + &dai_data->wcd9xxx_ch_list); + return 0; } @@ -7208,7 +7260,7 @@ static int tavil_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); - int ret; + int ret = 0; dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, dai->name, dai->id, params_rate(params), @@ -7238,7 +7290,9 @@ static int tavil_hw_params(struct snd_pcm_substream *substream, tavil->dai[dai->id].rate = params_rate(params); break; case SNDRV_PCM_STREAM_CAPTURE: - ret = tavil_set_decimator_rate(dai, params_rate(params)); + if (dai->id != AIF4_MAD_TX) + ret = tavil_set_decimator_rate(dai, + params_rate(params)); if (ret) { dev_err(tavil->dev, "%s: cannot set TX Decimator rate: %d\n", __func__, ret); @@ -7395,6 +7449,20 @@ static struct snd_soc_dai_driver tavil_dai[] = { }, .ops = &tavil_vi_dai_ops, }, + { + .name = "tavil_mad1", + .id = AIF4_MAD_TX, + .capture = { + .stream_name = "AIF4 MAD TX", + .rates = SNDRV_PCM_RATE_16000, + .formats = WCD934X_FORMATS_S16_LE, + .rate_min = 16000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &tavil_dai_ops, + }, }; static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil) diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c index 52ca82fba8e9..2012e4617ee1 100644 --- a/sound/soc/codecs/wcd9xxx-mbhc.c +++ b/sound/soc/codecs/wcd9xxx-mbhc.c @@ -4675,7 +4675,7 @@ int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc, schedule_delayed_work(&mbhc->mbhc_firmware_dwork, usecs_to_jiffies(FW_READ_TIMEOUT)); else - pr_debug("%s: Skipping to read mbhc fw, 0x%p %p\n", + pr_debug("%s: Skipping to read mbhc fw, 0x%pK %pK\n", __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); } @@ -5073,7 +5073,7 @@ static int wcd9xxx_remeasure_z_values(struct wcd9xxx_mbhc *mbhc, right = !!(r); dev_dbg(codec->dev, "%s: Remeasuring impedance values\n", __func__); - dev_dbg(codec->dev, "%s: l: %p, r: %p, left=%d, right=%d\n", __func__, + dev_dbg(codec->dev, "%s: l: %pK, r: %pK, left=%d, right=%d\n", __func__, l, r, left, right); /* Remeasure V2 values */ diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index e9f167fa643b..3aa9ac8d40b6 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -473,7 +473,7 @@ static int wcd_cpe_load_fw(struct wcd_cpe_core *core, bool load_segment; if (!core || !core->cpe_handle) { - pr_err("%s: Error CPE core %p\n", __func__, + pr_err("%s: Error CPE core %pK\n", __func__, core); return -EINVAL; } diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 28fd8930adb2..d7f4044b71ee 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -917,7 +917,7 @@ int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, u8 num_port, if (!port || !ch_mask || !ch_rate || (num_port > WSA881X_MAX_SWR_PORTS)) { dev_err(codec->dev, - "%s: Invalid port=%p, ch_mask=%p, ch_rate=%p\n", + "%s: Invalid port=%pK, ch_mask=%pK, ch_rate=%pK\n", __func__, port, ch_mask, ch_rate); return -EINVAL; } diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c index f0bf7a0db76f..5c8d91bfe400 100644 --- a/sound/soc/msm/msmcobalt.c +++ b/sound/soc/msm/msmcobalt.c @@ -71,6 +71,9 @@ #define WCN_CDC_SLIM_RX_CH_MAX 2 #define WCN_CDC_SLIM_TX_CH_MAX 3 +#define TDM_CHANNEL_MAX 8 +#define TDM_SLOT_OFFSET_MAX 8 + enum { SLIM_RX_0 = 0, SLIM_RX_1, @@ -170,6 +173,131 @@ struct msm_asoc_wcd93xx_codec { void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec); }; +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + +/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */ +static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */ +}; + /* Default configuration of slimbus channels */ static struct dev_config slim_rx_cfg[] = { [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, @@ -273,6 +401,12 @@ static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_96", "KHZ_192", "KHZ_384"}; static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", "KHZ_192"}; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_44P1", "KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", "KHZ_44P1", "KHZ_48", @@ -309,6 +443,12 @@ static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate, ext_disp_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); @@ -1359,6 +1499,45 @@ static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, return 1; } +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + static int aux_pcm_get_sample_rate(int value) { int sample_rate; @@ -1375,6 +1554,45 @@ static int aux_pcm_get_sample_rate(int value) return sample_rate; } +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + static int aux_pcm_get_sample_rate_val(int sample_rate) { int sample_rate_val; @@ -1391,6 +1609,361 @@ static int aux_pcm_get_sample_rate_val(int sample_rate) return sample_rate_val; } +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) { int idx; @@ -1789,6 +2362,24 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, ext_disp_rx_sample_rate_get, ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, aux_pcm_rx_sample_rate_get, aux_pcm_rx_sample_rate_put), @@ -2126,6 +2717,22 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = SAMPLING_RATE_48KHZ; break; + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + case MSM_BACKEND_DAI_AUXPCM_RX: rate->min = rate->max = aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; @@ -2643,7 +3250,7 @@ static void *def_tasha_mbhc_cal(void) return NULL; #define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y)) - S(v_hs_max, 1500); + S(v_hs_max, 1600); #undef S #define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y)) S(num_btn, WCD_MBHC_DEF_BUTTONS); @@ -3207,6 +3814,157 @@ static struct snd_soc_ops msm_aux_pcm_be_ops = { .shutdown = msm_aux_pcm_snd_shutdown, }; +static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width, + int slots) +{ + unsigned int slot_mask = 0; + int i, j; + unsigned int *slot_offset; + + for (i = TDM_0; i < TDM_PORT_MAX; i++) { + slot_offset = tdm_slot_offset[i]; + + for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + else + break; + } + } + + return slot_mask; +} + +static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channels HW config should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 32; + break; + default: + pr_err("%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + slots = 8; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, + slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + break; + default: + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slot_offset = tdm_slot_offset[TDM_0]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: slot offset not supported, offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + static struct snd_soc_ops msm_be_ops = { .hw_params = msm_snd_hw_params, }; @@ -3223,6 +3981,10 @@ static struct snd_soc_ops msm_wcn_ops = { .hw_params = msm_wcn_hw_params, }; +static struct snd_soc_ops msm_tdm_be_ops = { + .hw_params = msm_tdm_snd_hw_params +}; + /* Digital audio interface glue - connects codec <---> CPU */ static struct snd_soc_dai_link msm_common_dai_links[] = { /* FrontEnd DAI Links */ @@ -4023,6 +4785,34 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .be_hw_params_fixup = msm_be_hw_params_fixup, .ignore_suspend = 1, }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, }; static struct snd_soc_dai_link msm_tasha_be_dai_links[] = { diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index f0ad7daa8275..e661415a08ca 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -5832,11 +5832,6 @@ static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream, pr_debug("%s: dev_name: %s\n", __func__, dev_name(dai->dev)); - if (params_rate(params) != 48000) { - dev_err(dai->dev, "%s: invalid param rate %d\n", - __func__, params_rate(params)); - return -EINVAL; - } if ((params_channels(params) == 0) || (params_channels(params) > 8)) { dev_err(dai->dev, "%s: invalid param channels %d\n", diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index ffa78af72544..e30a4efa6e60 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -2173,69 +2173,74 @@ int adm_arrange_mch_map(struct adm_cmd_device_open_v5 *open, int path, int channel_mode) { int rc = 0, idx; - - memset(open->dev_channel_mapping, 0, - PCM_FORMAT_MAX_NUM_CHANNEL); - - if (channel_mode == 1) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FC; - } else if (channel_mode == 2) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - } else if (channel_mode == 3) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - open->dev_channel_mapping[2] = PCM_CHANNEL_FC; - } else if (channel_mode == 4) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - open->dev_channel_mapping[2] = PCM_CHANNEL_LS; - open->dev_channel_mapping[3] = PCM_CHANNEL_RS; - } else if (channel_mode == 5) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - open->dev_channel_mapping[2] = PCM_CHANNEL_FC; - open->dev_channel_mapping[3] = PCM_CHANNEL_LS; - open->dev_channel_mapping[4] = PCM_CHANNEL_RS; - } else if (channel_mode == 6) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; - open->dev_channel_mapping[3] = PCM_CHANNEL_FC; - open->dev_channel_mapping[4] = PCM_CHANNEL_LS; - open->dev_channel_mapping[5] = PCM_CHANNEL_RS; - } else if (channel_mode == 8) { - open->dev_channel_mapping[0] = PCM_CHANNEL_FL; - open->dev_channel_mapping[1] = PCM_CHANNEL_FR; - open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; - open->dev_channel_mapping[3] = PCM_CHANNEL_FC; - open->dev_channel_mapping[4] = PCM_CHANNEL_LS; - open->dev_channel_mapping[5] = PCM_CHANNEL_RS; - open->dev_channel_mapping[6] = PCM_CHANNEL_LB; - open->dev_channel_mapping[7] = PCM_CHANNEL_RB; - } else { - pr_err("%s: invalid num_chan %d\n", __func__, - channel_mode); - rc = -EINVAL; - goto inval_ch_mod; - } - + memset(open->dev_channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); switch (path) { case ADM_PATH_PLAYBACK: idx = ADM_MCH_MAP_IDX_PLAYBACK; break; case ADM_PATH_LIVE_REC: + case ADM_PATH_NONLIVE_REC: idx = ADM_MCH_MAP_IDX_REC; break; default: goto non_mch_path; - break; }; - - if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map) + if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map) { memcpy(open->dev_channel_mapping, - multi_ch_maps[idx].channel_mapping, - PCM_FORMAT_MAX_NUM_CHANNEL); + multi_ch_maps[idx].channel_mapping, + PCM_FORMAT_MAX_NUM_CHANNEL); + } else { + if (channel_mode == 1) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FC; + } else if (channel_mode == 2) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + } else if (channel_mode == 3) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + } else if (channel_mode == 4) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LS; + open->dev_channel_mapping[3] = PCM_CHANNEL_RS; + } else if (channel_mode == 5) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + open->dev_channel_mapping[3] = PCM_CHANNEL_LS; + open->dev_channel_mapping[4] = PCM_CHANNEL_RS; + } else if (channel_mode == 6) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[3] = PCM_CHANNEL_FC; + open->dev_channel_mapping[4] = PCM_CHANNEL_LS; + open->dev_channel_mapping[5] = PCM_CHANNEL_RS; + } else if (channel_mode == 7) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_FC; + open->dev_channel_mapping[3] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[4] = PCM_CHANNEL_LB; + open->dev_channel_mapping[5] = PCM_CHANNEL_RB; + open->dev_channel_mapping[6] = PCM_CHANNEL_CS; + } else if (channel_mode == 8) { + open->dev_channel_mapping[0] = PCM_CHANNEL_FL; + open->dev_channel_mapping[1] = PCM_CHANNEL_FR; + open->dev_channel_mapping[2] = PCM_CHANNEL_LFE; + open->dev_channel_mapping[3] = PCM_CHANNEL_FC; + open->dev_channel_mapping[4] = PCM_CHANNEL_LS; + open->dev_channel_mapping[5] = PCM_CHANNEL_RS; + open->dev_channel_mapping[6] = PCM_CHANNEL_LB; + open->dev_channel_mapping[7] = PCM_CHANNEL_RB; + } else { + pr_err("%s: invalid num_chan %d\n", __func__, + channel_mode); + rc = -EINVAL; + goto inval_ch_mod; + } + } non_mch_path: inval_ch_mod: |
