diff options
74 files changed, 2188 insertions, 471 deletions
diff --git a/Documentation/devicetree/bindings/cnss/icnss.txt b/Documentation/devicetree/bindings/cnss/icnss.txt index a1cbf480890a..4b70e670798d 100644 --- a/Documentation/devicetree/bindings/cnss/icnss.txt +++ b/Documentation/devicetree/bindings/cnss/icnss.txt @@ -12,9 +12,17 @@ Required properties: - reg-names: Names of the memory regions defined in reg entry - interrupts: Copy engine interrupt table - qcom,wlan-msa-memory: MSA memory size + - clocks: List of clock phandles + - clock-names: List of clock names corresponding to the "clocks" property - iommus: SMMUs and corresponding Stream IDs needed by WLAN - qcom,wlan-smmu-iova-address: I/O virtual address range as <start length> format to be used for allocations associated between WLAN and SMMU + - <supply-name>-supply: phandle to the regulator device tree node + Required "supply-name" is "vdd-0.8-cx-mx". + - qcom,<supply>-config: Specifies voltage levels for supply. Should be + specified in pairs (min, max), units uV. There can + be optional load in uA and Regulator settle delay in + uS. Optional properties: - qcom,icnss-vadc: VADC handle for vph_pwr read APIs. @@ -27,6 +35,8 @@ Example: compatible = "qcom,icnss"; reg = <0x0a000000 0x1000000>; reg-names = "membase"; + clocks = <&clock_gcc clk_aggre2_noc_clk>; + clock-names = "smmu_aggre2_noc_clk"; iommus = <&anoc2_smmu 0x1900>, <&anoc2_smmu 0x1901>; qcom,wlan-smmu-iova-address = <0 0x10000000>; @@ -45,4 +55,6 @@ Example: <0 141 0 /* CE11 */ >; qcom,wlan-msa-memory = <0x200000>; qcom,smmu-s1-bypass; + vdd-0.8-cx-mx-supply = <&pm8998_l5>; + qcom,vdd-0.8-cx-mx-config = <800000 800000 2400 1000>; }; diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index 453223dc195a..1ca89b587077 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -110,6 +110,17 @@ Optional Properties: - qcom,l2pc-cpu-mask-latency: The CPU mask latency in microseconds to avoid L2PC on masked CPUs. + +- qcom,gpu-cx-ipeak: + To handle Cx peak current limit. + <phandle bit> + phandle - phandle of cx ipeak device node + bit - bit number of client in relevant register +- qcom,gpu-cx-ipeak-clk: + GPU clock threshold for Cx Ipeak voting. KGSL votes + to Cx Ipeak driver when GPU clock crosses this threshold. + Cx Ipeak can limit peak current based on voting from other clients. + - qcom,force-32bit: Force the GPU to use 32 bit data sizes even if it is capable of doing 64 bit. diff --git a/Documentation/devicetree/bindings/media/video/laser-sensor.txt b/Documentation/devicetree/bindings/media/video/laser-sensor.txt new file mode 100644 index 000000000000..1bcb0b93cb10 --- /dev/null +++ b/Documentation/devicetree/bindings/media/video/laser-sensor.txt @@ -0,0 +1,28 @@ +Laser Sensor Device Tree Bindings. +======================================== + +Boards with the Laser Sensor connected to CCI shall have the following +properties: + +Required node properties: + - cell-index: cci hardware core index + - compatible: + - "st,stmvl53l0" : STMiecroelectronics VL53L0 Laser sensor. + - reg : offset and length of the register set for the device + - qcom, cci-master: cci master the sensor connected to + - cam_cci-supply : cci voltage regulator used + - cam_laser-supply: laser sensor voltage regulator + - qcom,cam-vreg-name: voltage regulators name + - qcom, cam-vreg-min-voltage: specify minimum voltage level for + regulators used + - qcom, cam-vreg-max-voltage: specify maximum voltage level for + regulators used + - pinctrl-names : should specify the pin control groups followed by + the definition of each group + - gpios : should contain phandle to gpio controller node and array of + #gpio-cells specifying specific gpio (controller specific) + - qcom,gpio-req-tbl-num : contains index to gpios specific to the sensor + - qcom,gpio-req-tbl-flags : should contain direction of gpios present in + qcom,gpio-req-tbl-num property (in the same order) + - qcom,gpio-req-tbl-label : should contain name of gpios present in + qcom,gpio-req-tbl-num property (in the same order) diff --git a/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt b/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt index fe94e40a27cd..a7848153f83c 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt @@ -66,6 +66,40 @@ Optional properties when qcom,actuator-type is "lra" "none", "opt1", "opt2" and "opt3" (default) - qcom,lra-res-cal-period : Auto resonance calibration period. The values range from 4 to 32(default) + - qcom,perform-lra-auto-resonance-search : boolean, define this property if: + a) the underlying PMI chip does not have a register in the MISC block to + read the error percentage in RC clock + b) the actuator type is LRA + Defining this causes the auto resonance search algorithm to be be performed + for such devices. + c) This property is not defined by default. + +- qcom,drive-period-code-max-limit-percent-variation: RATE_CFG1 and RATE_CFG2 registers will + be updated with the values from AUTO_RES_LO and AUTO_RES_HI registers + only if the variation from the resonant frequency is within the value + mentioned by this property on the higher side. + The default value is 25, which means if the drive period code resulting + from AUTO_RES register's is more than 25 percent of the existing drive + period code, then driver does not update RATE_CFG registers. +- qcom,drive-period-code-min-limit-percent-variation: RATE_CFG1 and RATE_CFG2 registers will + be updated with the values from AUTO_RES_LO and AUTO_RES_HI registers + only if the variation from the resonant frequency is within the value + mentioned by this property on the lower side. + The default value is 25, which means if the drive period code resulting + from AUTO_RES register's is less than 25 percent of the existing drive + period code, then driver does not update RATE_CFG registers. + +Optional properties when qcom,lra-auto-res-mode is "qwd" + - qcom,time-required-to-generate-back-emf-us: Time period required to generate sufficient + back-emf (in case of QWD mode only) in us. For auto resonance + detection to work properly,sufficient back-emf has to be + generated. In general, back-emf takes some time to build up. + When the auto resonance mode is chosen as QWD, high-z will + be applied for every LRA cycle and hence there won't be + enough back-emf at the start-up. So we need to drive the + motor for a few LRA cycles. Hence, auto resonance detection + is enabled after this delay period after the PLAY bit is + asserted. The default value is 20000us. Example: qcom,haptic@c000 { @@ -94,4 +128,5 @@ Example: qcom,lra-high-z = "opt1"; qcom,lra-auto-res-mode = "qwd"; qcom,lra-res-cal-period = <4>; + qcom,time-required-to-generate-back-emf-us = <20000>; }; diff --git a/Documentation/devicetree/bindings/soc/qcom/qpnp-pbs.txt b/Documentation/devicetree/bindings/soc/qcom/qpnp-pbs.txt new file mode 100644 index 000000000000..d7aefbf9c19c --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qpnp-pbs.txt @@ -0,0 +1,30 @@ +QPNP PBS + +QPNP (Qualcomm Technologies, Inc. Plug N Play) PBS is programmable boot sequence +and this driver is for helping the client drivers triggering such sequence +to be configured in PMIC. + +This document describes the bindings for QPNP PBS driver. + +======================= +Required Node Structure +======================= + +- compatible + Usage: required + Value type: <string> + Definition: should be "qcom,qpnp-pbs". + +- reg + Usage: required + Value type: <prop-encoded-array> + Definition: Base address of the PBS registers. + + +======= +Example +======= + pm660l_pbs: qcom,pbs@7300 { + compatible = "qcom,qpnp-pbs"; + reg = <0x7300 0x100>; + }; diff --git a/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/arch/arm/boot/dts/qcom/msm-pm660.dtsi index 131ad649ef8b..b8b0f7e26dd9 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660.dtsi @@ -270,6 +270,18 @@ qcom,fast-avg-setup = <0>; }; + chan@4e { + label = "emmc_therm"; + reg = <0x4e>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + qcom,vadc-thermal-node; + }; + chan@1d { label = "drax_temp"; reg = <0x1d>; diff --git a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi index 2cb08e1709a5..e7a61f42dff1 100644 --- a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -78,6 +78,26 @@ status = "disabled"; }; + tof0:qcom,tof@0{ + cell-index = <0>; + reg = <0x29>; + compatible = "st,stmvl53l0"; + qcom,cci-master = <0>; + cam_cci-supply = <&pm8998_lvs1>; + cam_laser-supply = <&pmi8998_bob>; + qcom,cam-vreg-name = "cam_cci", "cam_laser"; + qcom,cam-vreg-min-voltage = <0 0>; + qcom,cam-vreg-max-voltage = <0 3600000>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_tof_active>; + pinctrl-1 = <&cam_tof_suspend>; + gpios = <&tlmm 27 0>, + <&tlmm 126 0>; + qcom,gpio-req-tbl-num = <0 1>; + qcom,gpio-req-tbl-flags = <0 0>; + qcom,gpio-req-tbl-label = "CAM_TOF", "CAM_CE"; + }; + eeprom0: qcom,eeprom@0 { cell-index = <0>; reg = <0>; diff --git a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi index 0a41383ba874..c5384eaf17a1 100644 --- a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -61,7 +61,24 @@ pinctrl-0 = <&cam_actuator_vaf_active>; pinctrl-1 = <&cam_actuator_vaf_suspend>; }; - + tof0:qcom,tof@0{ + cell-index = <0>; + reg = <0x29>; + compatible = "st,stmvl53l0"; + qcom,cci-master = <0>; + cam_cci-supply = <&pm8998_lvs1>; + cam_laser-supply = <&pmi8998_bob>; + qcom,cam-vreg-name = "cam_cci", "cam_laser"; + qcom,cam-vreg-min-voltage = <0 0>; + qcom,cam-vreg-max-voltage = <0 3600000>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_tof_active>; + pinctrl-1 = <&cam_tof_suspend>; + gpios = <&tlmm 27 0>, <&tlmm 126 0>; + qcom,gpio-req-tbl-num = <0 1>; + qcom,gpio-req-tbl-flags = <0 0>; + qcom,gpio-req-tbl-label = "CAM_TOF", "CAM_CE"; + }; ois0: qcom,ois@0 { cell-index = <0>; reg = <0x0>; diff --git a/arch/arm/boot/dts/qcom/msm8998-interposer-pm660.dtsi b/arch/arm/boot/dts/qcom/msm8998-interposer-pm660.dtsi index c9522154ae7d..f4ff7c401a98 100644 --- a/arch/arm/boot/dts/qcom/msm8998-interposer-pm660.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-interposer-pm660.dtsi @@ -55,6 +55,13 @@ /delete-property/gpios; }; +&tof0 { + /delete-property/cam_cci-supply; + /delete-property/cam_laser-supply; + /delete-property/gpios; +}; + + &cci { /delete-node/qcom,camera@1; /delete-node/qcom,camera@2; diff --git a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi index 19b227f1b60f..4914363b414a 100644 --- a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi @@ -954,6 +954,32 @@ }; }; + cam_tof_active: cam_tof_active { + mux { + pins = "gpio27", "gpio126"; + function = "gpio"; + }; + + config { + pins = "gpio27", "gpio126"; + bias-disable; + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_tof_suspend: cam_tof_suspend { + mux { + pins = "gpio27", "gpio126"; + function = "gpio"; + }; + + config { + pins = "gpio27", "gpio126"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + cam_sensor_mclk0_active: cam_sensor_mclk0_active { /* MCLK0 */ mux { diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index f33b8bc2a8a8..0e014a171156 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -3079,6 +3079,8 @@ <0xa0000000 0x10000000>, <0xb0000000 0x10000>; reg-names = "membase", "smmu_iova_base", "smmu_iova_ipa"; + clocks = <&clock_gcc clk_rf_clk2_pin>; + clock-names = "cxo_ref_clk_pin"; iommus = <&anoc2_smmu 0x1900>, <&anoc2_smmu 0x1901>; interrupts = <0 413 0 /* CE0 */ >, @@ -3094,6 +3096,12 @@ <0 424 0 /* CE10 */ >, <0 425 0 /* CE11 */ >; qcom,wlan-msa-memory = <0x100000>; + vdd-0.8-cx-mx-supply = <&pm8998_l5>; + vdd-1.8-xo-supply = <&pm8998_l7_pin_ctrl>; + vdd-1.3-rfa-supply = <&pm8998_l17_pin_ctrl>; + vdd-3.3-ch0-supply = <&pm8998_l25_pin_ctrl>; + qcom,vdd-0.8-cx-mx-config = <800000 800000>; + qcom,vdd-3.3-ch0-config = <3104000 3312000>; qcom,icnss-vadc = <&pm8998_vadc>; qcom,icnss-adc_tm = <&pm8998_adc_tm>; }; diff --git a/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi b/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi index e9c8e0456abc..2448e1894387 100644 --- a/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi @@ -56,8 +56,9 @@ label = "kgsl-3d0"; compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d"; status = "ok"; - reg = <0x5000000 0x40000>; - reg-names = "kgsl_3d0_reg_memory"; + reg = <0x5000000 0x40000 + 0x780000 0x6220>; + reg-names = "kgsl_3d0_reg_memory", "qfprom_memory"; interrupts = <0 300 0>; interrupt-names = "kgsl_3d0_irq"; qcom,id = <0>; @@ -129,6 +130,8 @@ /* Context aware jump target power level */ qcom,ca-target-pwrlevel = <4>; + qcom,gpu-speed-bin = <0x41a0 0x1fe00000 21>; + /* GPU Mempools */ qcom,gpu-mempools { #address-cells= <1>; @@ -149,92 +152,349 @@ }; }; - /* Power levels */ - qcom,gpu-pwrlevels { + /* + * Speed-bin zero is default speed bin. + * For rest of the speed bins, speed-bin value + * is calulated as FMAX/4.8 MHz round up to zero + * decimal places. + */ + qcom,gpu-pwrlevel-bins { #address-cells = <1>; #size-cells = <0>; - compatible = "qcom,gpu-pwrlevels"; - - /* TURBO */ - qcom,gpu-pwrlevel@0 { - reg = <0>; - qcom,gpu-freq = <775000000>; - qcom,bus-freq = <11>; - qcom,bus-min = <10>; - qcom,bus-max = <11>; - }; - - /* TURBO */ - qcom,gpu-pwrlevel@1 { - reg = <1>; - qcom,gpu-freq = <700000000>; - qcom,bus-freq = <10>; - qcom,bus-min = <9>; - qcom,bus-max = <11>; - }; - - /* NOM_L1 */ - qcom,gpu-pwrlevel@2 { - reg = <2>; - qcom,gpu-freq = <647000000>; - qcom,bus-freq = <9>; - qcom,bus-min = <8>; - qcom,bus-max = <9>; - }; - - /* NOM */ - qcom,gpu-pwrlevel@3 { - reg = <3>; - qcom,gpu-freq = <588000000>; - qcom,bus-freq = <9>; - qcom,bus-min = <7>; - qcom,bus-max = <9>; - }; - - /* SVS_L1 */ - qcom,gpu-pwrlevel@4 { - reg = <4>; - qcom,gpu-freq = <465000000>; - qcom,bus-freq = <8>; - qcom,bus-min = <6>; - qcom,bus-max = <9>; - }; - - /* SVS */ - qcom,gpu-pwrlevel@5 { - reg = <5>; - qcom,gpu-freq = <370000000>; - qcom,bus-freq = <5>; - qcom,bus-min = <4>; - qcom,bus-max = <7>; + compatible="qcom,gpu-pwrlevel-bins"; + + qcom,gpu-pwrlevels-0 { + #address-cells = <1>; + #size-cells = <0>; + + qcom,speed-bin = <0>; + + qcom,initial-pwrlevel = <5>; + + /* TURBO */ + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <775000000>; + qcom,bus-freq = <11>; + qcom,bus-min = <10>; + qcom,bus-max = <11>; + }; + + /* TURBO */ + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <700000000>; + qcom,bus-freq = <10>; + qcom,bus-min = <9>; + qcom,bus-max = <11>; + }; + + /* NOM_L1 */ + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <647000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <8>; + qcom,bus-max = <9>; + }; + + /* NOM */ + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <588000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <7>; + qcom,bus-max = <9>; + }; + + /* SVS_L1 */ + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <465000000>; + qcom,bus-freq = <8>; + qcom,bus-min = <6>; + qcom,bus-max = <9>; + }; + + /* SVS */ + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <370000000>; + qcom,bus-freq = <5>; + qcom,bus-min = <4>; + qcom,bus-max = <7>; + }; + + /* Low SVS */ + qcom,gpu-pwrlevel@6 { + reg = <6>; + qcom,gpu-freq = <240000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + /* Min SVS */ + qcom,gpu-pwrlevel@7 { + reg = <7>; + qcom,gpu-freq = <160000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <2>; + qcom,bus-max = <4>; + }; + + /* XO */ + qcom,gpu-pwrlevel@8 { + reg = <8>; + qcom,gpu-freq = <19200000>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; }; - /* Low SVS */ - qcom,gpu-pwrlevel@6 { - reg = <6>; - qcom,gpu-freq = <240000000>; - qcom,bus-freq = <3>; - qcom,bus-min = <3>; - qcom,bus-max = <5>; + qcom,gpu-pwrlevels-1 { + #address-cells = <1>; + #size-cells = <0>; + + qcom,speed-bin = <162>; + + qcom,initial-pwrlevel = <5>; + + /* TURBO */ + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <775000000>; + qcom,bus-freq = <11>; + qcom,bus-min = <10>; + qcom,bus-max = <11>; + }; + + /* TURBO */ + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <700000000>; + qcom,bus-freq = <10>; + qcom,bus-min = <9>; + qcom,bus-max = <11>; + }; + + /* NOM_L1 */ + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <647000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <8>; + qcom,bus-max = <9>; + }; + + /* NOM */ + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <588000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <7>; + qcom,bus-max = <9>; + }; + + /* SVS_L1 */ + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <465000000>; + qcom,bus-freq = <8>; + qcom,bus-min = <6>; + qcom,bus-max = <9>; + }; + + /* SVS */ + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <370000000>; + qcom,bus-freq = <5>; + qcom,bus-min = <4>; + qcom,bus-max = <7>; + }; + + /* Low SVS */ + qcom,gpu-pwrlevel@6 { + reg = <6>; + qcom,gpu-freq = <240000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + /* Min SVS */ + qcom,gpu-pwrlevel@7 { + reg = <7>; + qcom,gpu-freq = <160000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <2>; + qcom,bus-max = <4>; + }; + + /* XO */ + qcom,gpu-pwrlevel@8 { + reg = <8>; + qcom,gpu-freq = <19200000>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; }; - /* Min SVS */ - qcom,gpu-pwrlevel@7 { - reg = <7>; - qcom,gpu-freq = <160000000>; - qcom,bus-freq = <3>; - qcom,bus-min = <2>; - qcom,bus-max = <4>; + qcom,gpu-pwrlevels-2 { + #address-cells = <1>; + #size-cells = <0>; + + qcom,speed-bin = <146>; + + qcom,initial-pwrlevel = <4>; + + /* TURBO */ + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <700000000>; + qcom,bus-freq = <11>; + qcom,bus-min = <10>; + qcom,bus-max = <11>; + }; + + /* NOM_L1 */ + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <647000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <8>; + qcom,bus-max = <9>; + }; + + /* NOM */ + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <588000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <7>; + qcom,bus-max = <9>; + }; + + /* SVS_L1 */ + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <465000000>; + qcom,bus-freq = <8>; + qcom,bus-min = <6>; + qcom,bus-max = <9>; + }; + + /* SVS */ + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <370000000>; + qcom,bus-freq = <5>; + qcom,bus-min = <4>; + qcom,bus-max = <7>; + }; + + /* Low SVS */ + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <240000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + /* Min SVS */ + qcom,gpu-pwrlevel@6 { + reg = <6>; + qcom,gpu-freq = <160000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <2>; + qcom,bus-max = <4>; + }; + + /* XO */ + qcom,gpu-pwrlevel@7 { + reg = <7>; + qcom,gpu-freq = <19200000>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; }; - /* XO */ - qcom,gpu-pwrlevel@8 { - reg = <8>; - qcom,gpu-freq = <19200000>; - qcom,bus-freq = <0>; - qcom,bus-min = <0>; - qcom,bus-max = <0>; + qcom,gpu-pwrlevels-3 { + #address-cells = <1>; + #size-cells = <0>; + + qcom,speed-bin = <135>; + + qcom,initial-pwrlevel = <3>; + + /* NOM_L1 */ + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <647000000>; + qcom,bus-freq = <11>; + qcom,bus-min = <10>; + qcom,bus-max = <11>; + }; + + /* NOM */ + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <588000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <7>; + qcom,bus-max = <9>; + }; + + /* SVS_L1 */ + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <465000000>; + qcom,bus-freq = <8>; + qcom,bus-min = <6>; + qcom,bus-max = <9>; + }; + + /* SVS */ + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <370000000>; + qcom,bus-freq = <5>; + qcom,bus-min = <4>; + qcom,bus-max = <7>; + }; + + /* Low SVS */ + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <240000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + /* Min SVS */ + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <160000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <2>; + qcom,bus-max = <4>; + }; + + /* XO */ + qcom,gpu-pwrlevel@6 { + reg = <6>; + qcom,gpu-freq = <19200000>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; }; }; }; diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi index 4161d2e000bf..cd895a067f65 100644 --- a/arch/arm/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630.dtsi @@ -1118,7 +1118,7 @@ qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>; qcom,target-dev = <&memlat_cpu0>; qcom,core-dev-table = - < 787200 762 >, + < 1113600 762 >, < 1344000 2086 >, < 1670400 2929 >, < 2150400 3879 >, diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi index dc57ee62a867..d4461395891a 100644 --- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi @@ -467,10 +467,10 @@ qcom,devfreq,freq-table = <50000000 200000000>; qcom,pm-qos-irq-type = "affine_irq"; - qcom,pm-qos-irq-latency = <26 81>; + qcom,pm-qos-irq-latency = <43 377>; qcom,pm-qos-cpu-groups = <0x0f 0xf0>; - qcom,pm-qos-cmdq-latency-us = <26 81>, <26 81>; - qcom,pm-qos-legacy-latency-us = <26 81>, <26 81>; + qcom,pm-qos-cmdq-latency-us = <43 377>, <40 325>; + qcom,pm-qos-legacy-latency-us = <43 377>, <40 325>; qcom,msm-bus,name = "sdhc1"; qcom,msm-bus,num-cases = <9>; @@ -531,9 +531,9 @@ qcom,devfreq,freq-table = <50000000 200000000>; qcom,pm-qos-irq-type = "affine_irq"; - qcom,pm-qos-irq-latency = <26 81>; + qcom,pm-qos-irq-latency = <43 377>; qcom,pm-qos-cpu-groups = <0x0f 0xf0>; - qcom,pm-qos-legacy-latency-us = <26 81>, <26 81>; + qcom,pm-qos-legacy-latency-us = <43 377>, <40 325>; clocks = <&clock_gcc GCC_SDCC2_AHB_CLK>, <&clock_gcc GCC_SDCC2_APPS_CLK>; diff --git a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi index e5cf0b1534ec..a47a788874fa 100644 --- a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi @@ -119,8 +119,8 @@ vdd-supply = <&gdsc_gpu_gx>; /* CPU latency parameter */ - qcom,pm-qos-active-latency = <349>; - qcom,pm-qos-wakeup-latency = <349>; + qcom,pm-qos-active-latency = <518>; + qcom,pm-qos-wakeup-latency = <518>; /* Quirks */ qcom,gpu-quirk-dp2clockgating-disable; diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi index a7fe3185ac09..2d44c69ea827 100644 --- a/arch/arm/boot/dts/qcom/sdm660.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660.dtsi @@ -320,10 +320,16 @@ reg = <0x0 0x92a00000 0x0 0x1e00000>; }; - cdsp_fw_mem: cdsp_fw_region@94800000 { + pil_mba_mem: pil_mba_region@94800000 { compatible = "removed-dma-pool"; no-map; - reg = <0x0 0x94800000 0x0 0x600000>; + reg = <0x0 0x94800000 0x0 0x200000>; + }; + + cdsp_fw_mem: cdsp_fw_region@94a00000 { + compatible = "removed-dma-pool"; + no-map; + reg = <0x0 0x94a00000 0x0 0x600000>; }; venus_fw_mem: venus_fw_region { @@ -1966,6 +1972,10 @@ /* GPIO output to mss */ qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>; status = "ok"; + qcom,mba-mem@0 { + compatible = "qcom,pil-mba-mem"; + memory-region = <&pil_mba_mem>; + }; }; qcom,msm-rtb { diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig index cb20c31ccf1b..515aab3ccefb 100644 --- a/arch/arm/configs/sdm660-perf_defconfig +++ b/arch/arm/configs/sdm660-perf_defconfig @@ -510,6 +510,7 @@ CONFIG_IOMMU_TESTS=y CONFIG_QCOM_COMMON_LOG=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y +CONFIG_QPNP_PBS=y CONFIG_MSM_SMD=y CONFIG_MSM_SMD_DEBUG=y CONFIG_MSM_GLINK=y diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig index 37ace58ef8d8..574f3808704b 100644 --- a/arch/arm/configs/sdm660_defconfig +++ b/arch/arm/configs/sdm660_defconfig @@ -510,6 +510,7 @@ CONFIG_IOMMU_TESTS=y CONFIG_QCOM_COMMON_LOG=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y +CONFIG_QPNP_PBS=y CONFIG_MSM_SMD=y CONFIG_MSM_SMD_DEBUG=y CONFIG_MSM_GLINK=y diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig index 29b2c75e70f3..b0bdabf58d62 100644 --- a/arch/arm64/configs/sdm660-perf_defconfig +++ b/arch/arm64/configs/sdm660-perf_defconfig @@ -527,6 +527,7 @@ CONFIG_IOMMU_DEBUG_TRACKING=y CONFIG_IOMMU_TESTS=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y +CONFIG_QPNP_PBS=y CONFIG_MSM_SMD=y CONFIG_MSM_GLINK=y CONFIG_MSM_GLINK_LOOPBACK_SERVER=y diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig index a0d832f94de1..f6599db26231 100644 --- a/arch/arm64/configs/sdm660_defconfig +++ b/arch/arm64/configs/sdm660_defconfig @@ -541,6 +541,7 @@ CONFIG_IOMMU_TESTS=y CONFIG_QCOM_COMMON_LOG=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y +CONFIG_QPNP_PBS=y CONFIG_MSM_SMD=y CONFIG_MSM_SMD_DEBUG=y CONFIG_MSM_GLINK=y diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index 85aea381fbf6..cb3eec8e8e50 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -69,6 +69,8 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) frame->fp = *(unsigned long *)(fp); frame->pc = *(unsigned long *)(fp + 8); + kasan_enable_current(); + #ifdef CONFIG_FUNCTION_GRAPH_TRACER if (tsk && tsk->ret_stack && (frame->pc == (unsigned long)return_to_handler)) { @@ -112,8 +114,6 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) } } - kasan_enable_current(); - return 0; } diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 87a48268b663..1c6e4da01e69 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -307,8 +307,7 @@ static const char * const fw_path[] = { "/lib/firmware/updates/" UTS_RELEASE, "/lib/firmware/updates", "/lib/firmware/" UTS_RELEASE, - "/lib/firmware", - "/firmware/image" + "/lib/firmware" }; /* diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 88bd6afdeea5..9ca46ae54ce3 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1951,6 +1951,8 @@ static void fastrpc_channel_close(struct kref *kref) cid = ctx - &gcinfo[0]; fastrpc_glink_close(ctx->chan, cid); ctx->chan = 0; + glink_unregister_link_state_cb(ctx->link.link_notify_handle); + ctx->link.link_notify_handle = 0; mutex_unlock(&me->smd_mutex); pr_info("'closed /dev/%s c %d %d'\n", gcinfo[cid].name, MAJOR(me->dev_no), cid); diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 973884c2c5e7..b58391adf3ab 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -161,6 +161,7 @@ static const struct { { adreno_is_a530, a530_efuse_speed_bin }, { adreno_is_a505, a530_efuse_speed_bin }, { adreno_is_a512, a530_efuse_speed_bin }, + { adreno_is_a508, a530_efuse_speed_bin }, }; static void a5xx_check_features(struct adreno_device *adreno_dev) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 601e7a23101b..1de8e212a703 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -4753,6 +4753,7 @@ error_close_mmu: error_pwrctrl_close: kgsl_pwrctrl_close(device); error: + kgsl_device_debugfs_close(device); _unregister_device(device); return status; } @@ -4782,6 +4783,7 @@ void kgsl_device_platform_remove(struct kgsl_device *device) kgsl_pwrctrl_close(device); + kgsl_device_debugfs_close(device); _unregister_device(device); } EXPORT_SYMBOL(kgsl_device_platform_remove); diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c index 7758fc956055..37d92428f02c 100644 --- a/drivers/gpu/msm/kgsl_debugfs.c +++ b/drivers/gpu/msm/kgsl_debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2008-2017, 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 @@ -87,6 +87,11 @@ void kgsl_device_debugfs_init(struct kgsl_device *device) &pwr_log_fops); } +void kgsl_device_debugfs_close(struct kgsl_device *device) +{ + debugfs_remove_recursive(device->d_debugfs); +} + struct type_entry { int type; const char *str; diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h index 34875954bb8b..949aed81581c 100644 --- a/drivers/gpu/msm/kgsl_debugfs.h +++ b/drivers/gpu/msm/kgsl_debugfs.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2011,2013,2015 The Linux Foundation. +/* Copyright (c) 2002,2008-2011,2013,2015,2017 The Linux Foundation. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -23,6 +23,7 @@ void kgsl_core_debugfs_init(void); void kgsl_core_debugfs_close(void); void kgsl_device_debugfs_init(struct kgsl_device *device); +void kgsl_device_debugfs_close(struct kgsl_device *device); extern struct dentry *kgsl_debugfs_dir; static inline struct dentry *kgsl_get_debugfs_dir(void) @@ -34,6 +35,7 @@ void kgsl_process_init_debugfs(struct kgsl_process_private *); #else static inline void kgsl_core_debugfs_init(void) { } static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { } +static inline void kgsl_device_debugfs_close(struct kgsl_device *device) { } static inline void kgsl_core_debugfs_close(void) { } static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; } static inline void kgsl_process_init_debugfs(struct kgsl_process_private *priv) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index fe6aa45901d0..e639e197de93 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -361,6 +361,26 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, if (new_level == old_level) return; + if (pwr->gpu_cx_ipeak) { + unsigned int old_freq = pwr->pwrlevels[old_level].gpu_freq; + unsigned int new_freq = pwr->pwrlevels[new_level].gpu_freq; + + /* + * Set Cx ipeak vote for GPU if it tries to cross + * threshold frequency. + */ + if (old_freq < pwr->gpu_cx_ipeak_clk && + new_freq >= pwr->gpu_cx_ipeak_clk) { + int ret = cx_ipeak_update(pwr->gpu_cx_ipeak, true); + + if (ret) { + KGSL_PWR_ERR(device, + "cx_ipeak_update failed %d\n", ret); + return; + } + } + } + kgsl_pwrscale_update_stats(device); /* @@ -422,6 +442,24 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, /* Timestamp the frequency change */ device->pwrscale.freq_change_time = ktime_to_ms(ktime_get()); + + if (pwr->gpu_cx_ipeak) { + unsigned int old_freq = pwr->pwrlevels[old_level].gpu_freq; + unsigned int new_freq = pwr->pwrlevels[new_level].gpu_freq; + + /* + * Reset Cx ipeak vote for GPU if it goes below + * threshold frequency. + */ + if (old_freq >= pwr->gpu_cx_ipeak_clk && + new_freq < pwr->gpu_cx_ipeak_clk) { + int ret = cx_ipeak_update(pwr->gpu_cx_ipeak, false); + + if (ret) + KGSL_PWR_ERR(device, + "cx_ipeak_update failed %d\n", ret); + } + } } EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change); @@ -2217,8 +2255,37 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) of_property_read_string(pdev->dev.of_node, "qcom,tsens-name", &pwr->tsens_name); + /* Cx ipeak client support */ + if (of_find_property(pdev->dev.of_node, "qcom,gpu-cx-ipeak", NULL)) { + if (!of_property_read_u32(pdev->dev.of_node, + "qcom,gpu-cx-ipeak-clk", &pwr->gpu_cx_ipeak_clk)) { + pwr->gpu_cx_ipeak = cx_ipeak_register(pdev->dev.of_node, + "qcom,gpu-cx-ipeak"); + } else { + KGSL_PWR_ERR(device, "failed to get gpu cxip clk\n"); + result = -EINVAL; + goto error_cleanup_pwr_limit; + } + + if (IS_ERR(pwr->gpu_cx_ipeak)) { + result = PTR_ERR(pwr->gpu_cx_ipeak); + KGSL_PWR_ERR(device, + "Failed to register Cx ipeak client %d\n", + result); + goto error_cleanup_pwr_limit; + } + } return result; +error_cleanup_pwr_limit: + pwr->power_flags = 0; + + if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) { + list_del(&pwr->sysfs_pwr_limit->node); + kfree(pwr->sysfs_pwr_limit); + pwr->sysfs_pwr_limit = NULL; + } + kfree(pwr->bus_ib); error_cleanup_pcl: _close_pcl(pwr); error_cleanup_ocmem_pcl: @@ -2238,6 +2305,8 @@ void kgsl_pwrctrl_close(struct kgsl_device *device) KGSL_PWR_INFO(device, "close device %d\n", device->id); + cx_ipeak_unregister(pwr->gpu_cx_ipeak); + pwr->power_flags = 0; if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) { diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 2de42d87bcbe..42f918b80fcd 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, 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 @@ -14,6 +14,7 @@ #define __KGSL_PWRCTRL_H #include <linux/pm_qos.h> +#include <soc/qcom/cx_ipeak.h> /***************************************************************************** ** power flags @@ -153,6 +154,8 @@ struct kgsl_regulator { * 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 + * gpu_cx_ipeak - pointer to cx ipeak client used by GPU + * gpu_cx_ipeak_clk - GPU threshold frequency to call cx ipeak driver API */ struct kgsl_pwrctrl { @@ -206,6 +209,8 @@ struct kgsl_pwrctrl { unsigned int gpu_bimc_int_clk_freq; bool gpu_bimc_interface_enabled; const char *tsens_name; + struct cx_ipeak_client *gpu_cx_ipeak; + unsigned int gpu_cx_ipeak_clk; }; int kgsl_pwrctrl_init(struct kgsl_device *device); diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index 3a11b061e5b0..596a36ed7dba 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, 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 @@ -3725,12 +3725,6 @@ static int tpdm_probe(struct platform_device *pdev) clk_disable_unprepare(drvdata->clk); - ret = tpdm_datasets_alloc(drvdata); - if (ret) - return ret; - - tpdm_init_default_data(drvdata); - drvdata->traceid = traceid++; desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c index 04b1b62f85c3..bf2a1dd7cf15 100644 --- a/drivers/i2c/busses/i2c-msm-v2.c +++ b/drivers/i2c/busses/i2c-msm-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, 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 @@ -50,6 +50,8 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl, static int i2c_msm_pm_resume(struct device *dev); static void i2c_msm_pm_suspend(struct device *dev); static void i2c_msm_clk_path_init(struct i2c_msm_ctrl *ctrl); +static void i2c_msm_pm_pinctrl_state(struct i2c_msm_ctrl *ctrl, + bool runtime_active); /* string table for enum i2c_msm_xfer_mode_id */ const char * const i2c_msm_mode_str_tbl[] = { @@ -2157,27 +2159,54 @@ static bool i2c_msm_xfer_next_buf(struct i2c_msm_ctrl *ctrl) return true; } -static void i2c_msm_pm_clk_disable_unprepare(struct i2c_msm_ctrl *ctrl) +static void i2c_msm_pm_clk_unprepare(struct i2c_msm_ctrl *ctrl) { - clk_disable_unprepare(ctrl->rsrcs.core_clk); - clk_disable_unprepare(ctrl->rsrcs.iface_clk); + clk_unprepare(ctrl->rsrcs.core_clk); + clk_unprepare(ctrl->rsrcs.iface_clk); } -static int i2c_msm_pm_clk_prepare_enable(struct i2c_msm_ctrl *ctrl) +static int i2c_msm_pm_clk_prepare(struct i2c_msm_ctrl *ctrl) { int ret; - ret = clk_prepare_enable(ctrl->rsrcs.iface_clk); + ret = clk_prepare(ctrl->rsrcs.iface_clk); if (ret) { dev_err(ctrl->dev, - "error on clk_prepare_enable(iface_clk):%d\n", ret); + "error on clk_prepare(iface_clk):%d\n", ret); return ret; } - ret = clk_prepare_enable(ctrl->rsrcs.core_clk); + ret = clk_prepare(ctrl->rsrcs.core_clk); + if (ret) { + clk_unprepare(ctrl->rsrcs.iface_clk); + dev_err(ctrl->dev, + "error clk_prepare(core_clk):%d\n", ret); + } + return ret; +} + +static void i2c_msm_pm_clk_disable(struct i2c_msm_ctrl *ctrl) +{ + clk_disable(ctrl->rsrcs.core_clk); + clk_disable(ctrl->rsrcs.iface_clk); +} + +static int i2c_msm_pm_clk_enable(struct i2c_msm_ctrl *ctrl) +{ + int ret; + + ret = clk_enable(ctrl->rsrcs.iface_clk); + if (ret) { + dev_err(ctrl->dev, + "error on clk_enable(iface_clk):%d\n", ret); + i2c_msm_pm_clk_unprepare(ctrl); + return ret; + } + ret = clk_enable(ctrl->rsrcs.core_clk); if (ret) { - clk_disable_unprepare(ctrl->rsrcs.iface_clk); + clk_disable(ctrl->rsrcs.iface_clk); + i2c_msm_pm_clk_unprepare(ctrl); dev_err(ctrl->dev, - "error clk_prepare_enable(core_clk):%d\n", ret); + "error clk_enable(core_clk):%d\n", ret); } return ret; } @@ -2198,6 +2227,7 @@ static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl) return -EIO; } + i2c_msm_pm_pinctrl_state(ctrl, true); pm_runtime_get_sync(ctrl->dev); /* * if runtime PM callback was not invoked (when both runtime-pm @@ -2208,7 +2238,7 @@ static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl) i2c_msm_pm_resume(ctrl->dev); } - ret = i2c_msm_pm_clk_prepare_enable(ctrl); + ret = i2c_msm_pm_clk_enable(ctrl); if (ret) { mutex_unlock(&ctrl->xfer.mtx); return ret; @@ -2235,13 +2265,14 @@ static void i2c_msm_pm_xfer_end(struct i2c_msm_ctrl *ctrl) if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_DMA) i2c_msm_dma_free_channels(ctrl); - i2c_msm_pm_clk_disable_unprepare(ctrl); + i2c_msm_pm_clk_disable(ctrl); if (!pm_runtime_enabled(ctrl->dev)) i2c_msm_pm_suspend(ctrl->dev); pm_runtime_mark_last_busy(ctrl->dev); pm_runtime_put_autosuspend(ctrl->dev); + i2c_msm_pm_pinctrl_state(ctrl, false); mutex_unlock(&ctrl->xfer.mtx); } @@ -2663,7 +2694,7 @@ static void i2c_msm_pm_suspend(struct device *dev) return; } i2c_msm_dbg(ctrl, MSM_DBG, "suspending..."); - i2c_msm_pm_pinctrl_state(ctrl, false); + i2c_msm_pm_clk_unprepare(ctrl); i2c_msm_clk_path_unvote(ctrl); /* @@ -2690,7 +2721,7 @@ static int i2c_msm_pm_resume(struct device *dev) i2c_msm_dbg(ctrl, MSM_DBG, "resuming..."); i2c_msm_clk_path_vote(ctrl); - i2c_msm_pm_pinctrl_state(ctrl, true); + i2c_msm_pm_clk_prepare(ctrl); ctrl->pwr_state = I2C_MSM_PM_RT_ACTIVE; return 0; } @@ -2870,9 +2901,13 @@ static int i2c_msm_probe(struct platform_device *pdev) /* vote for clock to enable reading the version number off the HW */ i2c_msm_clk_path_vote(ctrl); - ret = i2c_msm_pm_clk_prepare_enable(ctrl); + ret = i2c_msm_pm_clk_prepare(ctrl); + if (ret) + goto clk_err; + + ret = i2c_msm_pm_clk_enable(ctrl); if (ret) { - dev_err(ctrl->dev, "error in enabling clocks:%d\n", ret); + i2c_msm_pm_clk_unprepare(ctrl); goto clk_err; } @@ -2884,7 +2919,8 @@ static int i2c_msm_probe(struct platform_device *pdev) if (ret) dev_err(ctrl->dev, "error error on qup software reset\n"); - i2c_msm_pm_clk_disable_unprepare(ctrl); + i2c_msm_pm_clk_disable(ctrl); + i2c_msm_pm_clk_unprepare(ctrl); i2c_msm_clk_path_unvote(ctrl); ret = i2c_msm_rsrcs_gpio_pinctrl_init(ctrl); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c index f7eb0f8ac5a8..8d66232dbda1 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c @@ -196,6 +196,13 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr, __func__, stream_id); return -EINVAL; } + + if (qbuf_buf->num_planes > MAX_PLANES_PER_STREAM) { + pr_err("%s: Invalid num_planes %d , stream id %x\n", + __func__, qbuf_buf->num_planes, stream_id); + return -EINVAL; + } + for (i = 0; i < qbuf_buf->num_planes; i++) { mapped_info = &buf_info->mapped_info[i]; mapped_info->buf_fd = qbuf_buf->planes[i].addr; @@ -249,6 +256,12 @@ static void msm_isp_unprepare_v4l2_buf( return; } + if (buf_info->num_planes > VIDEO_MAX_PLANES) { + pr_err("%s: Invalid num_planes %d , stream id %x\n", + __func__, buf_info->num_planes, stream_id); + return; + } + bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle); if (!bufq) { pr_err("%s: Invalid bufq, stream id %x\n", diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c index 840d84388a17..bb3f0dca9d92 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 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 @@ -588,6 +588,12 @@ int vfe_hw_probe(struct platform_device *pdev) } vfe_dev->hw_info = (struct msm_vfe_hardware_info *) match_dev->data; + /* Cx ipeak support */ + if (of_find_property(pdev->dev.of_node, + "qcom,vfe_cx_ipeak", NULL)) { + vfe_dev->vfe_cx_ipeak = cx_ipeak_register( + pdev->dev.of_node, "qcom,vfe_cx_ipeak"); + } } else { vfe_dev->hw_info = (struct msm_vfe_hardware_info *) platform_get_device_id(pdev)->driver_data; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index f6fabc61620d..aca8e99650ba 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -29,6 +29,7 @@ #include "msm_buf_mgr.h" #include "cam_hw_ops.h" +#include <soc/qcom/cx_ipeak.h> #define VFE40_8974V1_VERSION 0x10000018 #define VFE40_8974V2_VERSION 0x1001001A @@ -767,6 +768,8 @@ struct vfe_device { size_t num_hvx_clk; size_t num_norm_clk; enum cam_ahb_clk_vote ahb_vote; + bool turbo_vote; + struct cx_ipeak_client *vfe_cx_ipeak; /* Sync variables*/ struct completion reset_complete; 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 c7f3b97c83c9..57373c1fc74c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -331,6 +331,7 @@ int msm_vfe47_init_hardware(struct vfe_device *vfe_dev) goto ahb_vote_fail; } vfe_dev->ahb_vote = CAM_AHB_SVS_VOTE; + vfe_dev->turbo_vote = 0; vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = vfe_dev->vfe_base; @@ -763,7 +764,7 @@ long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev, } if (blocking_call) { - rc = wait_for_completion_timeout( + rc = wait_for_completion_interruptible_timeout( &vfe_dev->reset_complete, msecs_to_jiffies(100)); if (rc <= 0) { pr_err("%s:%d failed: reset timeout\n", __func__, @@ -1930,7 +1931,7 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev, init_completion(&vfe_dev->halt_complete); /* Halt AXI Bus Bridge */ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x400); - rc = wait_for_completion_timeout( + rc = wait_for_completion_interruptible_timeout( &vfe_dev->halt_complete, msecs_to_jiffies(500)); if (rc <= 0) pr_err("%s:VFE%d halt timeout rc=%d\n", __func__, @@ -2556,6 +2557,7 @@ int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate) { int rc = 0; int clk_idx = vfe_dev->hw_info->vfe_clk_idx; + int ret; rc = msm_camera_clk_set_rate(&vfe_dev->pdev->dev, vfe_dev->vfe_clk[clk_idx], *rate); @@ -2563,7 +2565,26 @@ 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->vfe_cx_ipeak) { + if (vfe_dev->msm_isp_vfe_clk_rate >= + vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_TURBO] + [vfe_dev->hw_info->vfe_clk_idx] && + vfe_dev->turbo_vote == 0) { + ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, true); + if (ret) + pr_debug("%s: cx_ipeak_update failed %d\n", + __func__, ret); + else + vfe_dev->turbo_vote = 1; + } else if (vfe_dev->turbo_vote == 1) { + ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, false); + if (ret) + pr_debug("%s: cx_ipeak_update failed %d\n", + __func__, ret); + else + vfe_dev->turbo_vote = 0; + } + } 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/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index 5264bba57c8d..5aa8a59128a8 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 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 @@ -443,7 +443,7 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif) msm_camera_io_w(ISPIF_RST_CMD_MASK, ispif->base + ISPIF_RST_CMD_ADDR); - timeout = wait_for_completion_timeout( + timeout = wait_for_completion_interruptible_timeout( &ispif->reset_complete[VFE0], msecs_to_jiffies(500)); CDBG("%s: VFE0 done\n", __func__); @@ -457,7 +457,7 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif) atomic_set(&ispif->reset_trig[VFE1], 1); msm_camera_io_w(ISPIF_RST_CMD_1_MASK, ispif->base + ISPIF_RST_CMD_1_ADDR); - timeout = wait_for_completion_timeout( + timeout = wait_for_completion_interruptible_timeout( &ispif->reset_complete[VFE1], msecs_to_jiffies(500)); CDBG("%s: VFE1 done\n", __func__); @@ -1120,7 +1120,7 @@ static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif, /* initiate reset of ISPIF */ msm_camera_io_w(ISPIF_RST_CMD_MASK_RESTART, ispif->base + ISPIF_RST_CMD_ADDR); - timeout = wait_for_completion_timeout( + timeout = wait_for_completion_interruptible_timeout( &ispif->reset_complete[VFE0], msecs_to_jiffies(500)); if (timeout <= 0) { pr_err("%s: VFE0 reset wait timeout\n", __func__); @@ -1133,7 +1133,7 @@ static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif, atomic_set(&ispif->reset_trig[VFE1], 1); msm_camera_io_w(ISPIF_RST_CMD_1_MASK_RESTART, ispif->base + ISPIF_RST_CMD_1_ADDR); - timeout = wait_for_completion_timeout( + timeout = wait_for_completion_interruptible_timeout( &ispif->reset_complete[VFE1], msecs_to_jiffies(500)); if (timeout <= 0) { diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c index 3b38882c4c45..88d90d0a7c08 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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 @@ -72,6 +72,18 @@ static const struct msm_jpegdma_block msm_jpegdma_block_sel[] = { }; /* +* jpegdma_do_div - long division. +* @num: dividend +* @den: divisor +* returns quotient value. +*/ +static inline long long jpegdma_do_div(long long num, long long den) +{ + do_div(num, den); + return num; +} + +/* * msm_jpegdma_hw_read_reg - dma read from register. * @dma: Pointer to dma device. * @base_idx: dma memory resource index. @@ -819,9 +831,9 @@ static int msm_jpegdma_hw_calc_speed(struct msm_jpegdma_device *dma, } speed->bus_ab = calc_rate * 2; - speed->bus_ib = (real_clock * - (MSM_JPEGDMA_BW_NUM + MSM_JPEGDMA_BW_DEN - 1)) / - MSM_JPEGDMA_BW_DEN; + speed->bus_ib = jpegdma_do_div((real_clock * + (MSM_JPEGDMA_BW_NUM + MSM_JPEGDMA_BW_DEN - 1)), + MSM_JPEGDMA_BW_DEN); speed->core_clock = real_clock; dev_dbg(dma->dev, "Speed core clk %llu ab %llu ib %llu fps %d\n", speed->core_clock, speed->bus_ab, speed->bus_ib, size->fps); @@ -923,13 +935,15 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg, in_width = size_cfg->in_size.width; out_width = size_cfg->out_size.width; - scale_hor = (in_width * MSM_JPEGDMA_SCALE_UNI) / out_width; + scale_hor = jpegdma_do_div((in_width * MSM_JPEGDMA_SCALE_UNI), + out_width); if (scale_hor != MSM_JPEGDMA_SCALE_UNI) config->scale_cfg.enable = 1; in_height = size_cfg->in_size.height; out_height = size_cfg->out_size.height; - scale_ver = (in_height * MSM_JPEGDMA_SCALE_UNI) / out_height; + scale_ver = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI), + out_height); if (scale_ver != MSM_JPEGDMA_SCALE_UNI) config->scale_cfg.enable = 1; @@ -946,23 +960,23 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg, config->block_cfg.block = msm_jpegdma_block_sel[i]; if (plane->active_pipes > 1) { - phase = (out_height * scale_ver + (plane->active_pipes - 1)) / - plane->active_pipes; + phase = jpegdma_do_div((out_height * scale_ver + + (plane->active_pipes - 1)), plane->active_pipes); phase &= (MSM_JPEGDMA_SCALE_UNI - 1); - out_height = (out_height + (plane->active_pipes - 1)) / - plane->active_pipes; + out_height = jpegdma_do_div((out_height + + (plane->active_pipes - 1)), plane->active_pipes); in_height = (out_height * scale_ver) / MSM_JPEGDMA_SCALE_UNI; } - config->block_cfg.blocks_per_row = out_width / - config->block_cfg.block.width; + config->block_cfg.blocks_per_row = (uint32_t) jpegdma_do_div(out_width, + config->block_cfg.block.width); config->block_cfg.blocks_per_col = out_height; config->block_cfg.h_step = config->block_cfg.block.width; - - config->block_cfg.h_step_last = out_width % - config->block_cfg.block.width; + config->size_cfg.out_size.width = out_width; + config->block_cfg.h_step_last = (uint32_t) do_div(out_width, + config->block_cfg.block.width); if (!config->block_cfg.h_step_last) config->block_cfg.h_step_last = config->block_cfg.h_step; else @@ -974,7 +988,6 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg, config->size_cfg = *size_cfg; config->size_cfg.in_size.width = in_width; config->size_cfg.in_size.height = in_height; - config->size_cfg.out_size.width = out_width; config->size_cfg.out_size.height = out_height; config->in_offset = 0; config->out_offset = 0; @@ -1013,14 +1026,16 @@ int msm_jpegdma_hw_check_config(struct msm_jpegdma_device *dma, in_width = size_cfg->in_size.width; out_width = size_cfg->out_size.width; - scale = ((in_width * MSM_JPEGDMA_SCALE_UNI)) / out_width; + scale = jpegdma_do_div(((in_width * MSM_JPEGDMA_SCALE_UNI)), + out_width); if (scale < MSM_JPEGDMA_SCALE_UNI) return -EINVAL; in_height = size_cfg->in_size.height; out_height = size_cfg->out_size.height; - scale = (in_height * MSM_JPEGDMA_SCALE_UNI) / out_height; + scale = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI), + out_height); if (scale < MSM_JPEGDMA_SCALE_UNI) return -EINVAL; @@ -1827,7 +1842,7 @@ int msm_jpegdma_hw_map_buffer(struct msm_jpegdma_device *dma, int fd, buf->fd = fd; ret = cam_smmu_get_phy_addr(dma->iommu_hndl, buf->fd, - CAM_SMMU_MAP_RW, &buf->addr, &buf->size); + CAM_SMMU_MAP_RW, &buf->addr, (size_t *)&buf->size); if (ret < 0) { dev_err(dma->dev, "Can not get physical address\n"); goto error_get_phy; diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index 730f8b32ff1a..76a7c6942c68 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 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 @@ -149,10 +149,7 @@ static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev, list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) { if ((bufs->session_id == buf_info->session_id) && (bufs->stream_id == buf_info->stream_id) && - (bufs->vb2_v4l2_buf->vb2_buf.index == - buf_info->index)) { - bufs->vb2_v4l2_buf->sequence = buf_info->frame_id; - bufs->vb2_v4l2_buf->timestamp = buf_info->timestamp; + (bufs->index == buf_info->index)) { ret = buf_mngr_dev->vb2_ops.buf_done (bufs->vb2_v4l2_buf, buf_info->session_id, @@ -181,7 +178,7 @@ static int32_t msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev, list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) { if ((bufs->session_id == buf_info->session_id) && (bufs->stream_id == buf_info->stream_id) && - (bufs->vb2_v4l2_buf->vb2_buf.index == buf_info->index)) { + (bufs->index == buf_info->index)) { ret = buf_mngr_dev->vb2_ops.put_buf(bufs->vb2_v4l2_buf, buf_info->session_id, buf_info->stream_id); list_del_init(&bufs->entry); @@ -214,7 +211,7 @@ static int32_t msm_generic_buf_mngr_flush( buf_info->session_id, buf_info->stream_id, 0, &ts, 0); pr_err("Bufs not flushed: str_id = %d buf_index = %d ret = %d\n", - buf_info->stream_id, bufs->vb2_v4l2_buf->vb2_buf.index, + buf_info->stream_id, bufs->index, ret); list_del_init(&bufs->entry); kfree(bufs); diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h index 07d522cb01bb..9120a4cc85ca 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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 @@ -99,6 +99,7 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_1_3ph = { {0x70C, 0xA5}, {0x38, 0xFE}, {0x81c, 0x2}, + {0x700, 0x80}, }; struct csiphy_settings_t csiphy_combo_mode_v5_0_1 = { diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h index 198d130b24fc..8591f0646080 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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 @@ -100,6 +100,7 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_3ph = { {0x70C, 0x16}, {0x38, 0xFE}, {0x81c, 0x6}, + {0x700, 0x80}, }; struct csiphy_settings_t csiphy_combo_mode_v5_0 = { diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c index a7cd44636d1d..be266641a105 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, 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 @@ -46,6 +46,7 @@ #define MSM_CSIPHY_DRV_NAME "msm_csiphy" #define CLK_LANE_OFFSET 1 #define NUM_LANES_OFFSET 4 +#define CLOCK_LANE 0x02 #define CSI_3PHASE_HW 1 #define MAX_DPHY_DATA_LN 4 @@ -683,12 +684,21 @@ static int msm_csiphy_2phase_lane_config_v50( csiphybase + csiphy_dev->ctrl_reg-> csiphy_3ph_reg. mipi_csiphy_2ph_lnn_ctrl15.addr + offset); - msm_camera_io_w(csiphy_dev->ctrl_reg-> - csiphy_3ph_reg. - mipi_csiphy_2ph_lnn_ctrl0.data, - csiphybase + csiphy_dev->ctrl_reg-> - csiphy_3ph_reg. - mipi_csiphy_2ph_lnn_ctrl0.addr + offset); + if (mask == CLOCK_LANE) + msm_camera_io_w(csiphy_dev->ctrl_reg-> + csiphy_3ph_reg. + mipi_csiphy_2ph_lnck_ctrl0.data, + csiphybase + csiphy_dev->ctrl_reg-> + csiphy_3ph_reg. + mipi_csiphy_2ph_lnck_ctrl0.addr); + else + msm_camera_io_w(csiphy_dev->ctrl_reg-> + csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_ctrl0.data, + csiphybase + csiphy_dev->ctrl_reg-> + csiphy_3ph_reg. + mipi_csiphy_2ph_lnn_ctrl0.addr + + offset); msm_camera_io_w(csiphy_dev->ctrl_reg-> csiphy_3ph_reg. mipi_csiphy_2ph_lnn_cfg1.data, diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h index 70462dcd3b12..c1a9748e8af5 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, 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 @@ -141,6 +141,7 @@ struct csiphy_reg_3ph_parms_t { struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl3; struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl14; struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl7_cphy; + struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl0; }; struct csiphy_ctrl_t { diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c index a2da663e2046..f41382b5b20c 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c @@ -990,11 +990,14 @@ static int sde_rotator_debug_base_release(struct inode *inode, { struct sde_rotator_debug_base *dbg = file->private_data; - if (dbg && dbg->buf) { + if (dbg) { + mutex_lock(&dbg->buflock); kfree(dbg->buf); dbg->buf_len = 0; dbg->buf = NULL; + mutex_unlock(&dbg->buflock); } + return 0; } @@ -1026,8 +1029,10 @@ static ssize_t sde_rotator_debug_base_offset_write(struct file *file, if (cnt > (dbg->max_offset - off)) cnt = dbg->max_offset - off; + mutex_lock(&dbg->buflock); dbg->off = off; dbg->cnt = cnt; + mutex_unlock(&dbg->buflock); SDEROT_DBG("offset=%x cnt=%x\n", off, cnt); @@ -1047,7 +1052,10 @@ static ssize_t sde_rotator_debug_base_offset_read(struct file *file, if (*ppos) return 0; /* the end */ + mutex_lock(&dbg->buflock); len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt); + mutex_unlock(&dbg->buflock); + if (len < 0 || len >= sizeof(buf)) return 0; @@ -1086,6 +1094,8 @@ static ssize_t sde_rotator_debug_base_reg_write(struct file *file, if (off >= dbg->max_offset) return -EFAULT; + mutex_lock(&dbg->buflock); + /* Enable Clock for register access */ sde_rotator_clk_ctrl(dbg->mgr, true); @@ -1094,6 +1104,8 @@ static ssize_t sde_rotator_debug_base_reg_write(struct file *file, /* Disable Clock after register access */ sde_rotator_clk_ctrl(dbg->mgr, false); + mutex_unlock(&dbg->buflock); + SDEROT_DBG("addr=%zx data=%x\n", off, data); return count; @@ -1104,12 +1116,14 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file, { struct sde_rotator_debug_base *dbg = file->private_data; size_t len; + int rc = 0; if (!dbg) { SDEROT_ERR("invalid handle\n"); return -ENODEV; } + mutex_lock(&dbg->buflock); if (!dbg->buf) { char dump_buf[64]; char *ptr; @@ -1121,7 +1135,8 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file, if (!dbg->buf) { SDEROT_ERR("not enough memory to hold reg dump\n"); - return -ENOMEM; + rc = -ENOMEM; + goto debug_read_error; } ptr = dbg->base + dbg->off; @@ -1151,18 +1166,26 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file, dbg->buf_len = tot; } - if (*ppos >= dbg->buf_len) - return 0; /* done reading */ + if (*ppos >= dbg->buf_len) { + rc = 0; /* done reading */ + goto debug_read_error; + } len = min(count, dbg->buf_len - (size_t) *ppos); if (copy_to_user(user_buf, dbg->buf + *ppos, len)) { SDEROT_ERR("failed to copy to user\n"); - return -EFAULT; + rc = -EFAULT; + goto debug_read_error; } *ppos += len; /* increase offset */ + mutex_unlock(&dbg->buflock); return len; + +debug_read_error: + mutex_unlock(&dbg->buflock); + return rc; } static const struct file_operations sde_rotator_off_fops = { @@ -1196,6 +1219,9 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev, if (!dbg) return -ENOMEM; + mutex_init(&dbg->buflock); + mutex_lock(&dbg->buflock); + if (name) strlcpy(dbg->name, name, sizeof(dbg->name)); dbg->base = io_data->base; @@ -1217,6 +1243,7 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev, dbg->base += rot_dev->mdata->regdump ? rot_dev->mdata->regdump[0].offset : 0; } + mutex_unlock(&dbg->buflock); strlcpy(dbgname + prefix_len, "off", sizeof(dbgname) - prefix_len); ent_off = debugfs_create_file(dbgname, 0644, debugfs_root, dbg, @@ -1234,7 +1261,9 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev, goto reg_fail; } + mutex_lock(&dbg->buflock); dbg->mgr = rot_dev->mgr; + mutex_unlock(&dbg->buflock); return 0; reg_fail: diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h index c2c6f9775602..c6d0151d37de 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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 @@ -53,6 +53,7 @@ struct sde_rotator_debug_base { char *buf; size_t buf_len; struct sde_rot_mgr *mgr; + struct mutex buflock; }; #if defined(CONFIG_DEBUG_FS) diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 134995c9cd3c..8d03c36858b3 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -7043,7 +7043,11 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg) break; } pr_debug("SET_MEM_PARAM: qseecom addr = 0x%pK\n", data); + mutex_lock(&app_access_lock); + atomic_inc(&data->ioctl_count); ret = qseecom_set_client_mem_param(data, argp); + atomic_dec(&data->ioctl_count); + mutex_unlock(&app_access_lock); if (ret) pr_err("failed Qqseecom_set_mem_param request: %d\n", ret); diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index 23b0428bcf34..f48182cc04df 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -22,7 +22,7 @@ #include "gsi_reg.h" #define GSI_CMD_TIMEOUT (5*HZ) -#define GSI_STOP_CMD_TIMEOUT_MS 1 +#define GSI_STOP_CMD_TIMEOUT_MS 10 #define GSI_MAX_CH_LOW_WEIGHT 15 #define GSI_MHI_ER_START 10 #define GSI_MHI_ER_END 16 diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index e3dfe8927682..81eae05d7ed9 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -1777,7 +1777,8 @@ dealloc_chan_fail: int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, bool should_force_clear, u32 qmi_req_id, bool is_dpl) { - struct ipa3_ep_context *ul_ep, *dl_ep; + struct ipa3_ep_context *ul_ep = NULL; + struct ipa3_ep_context *dl_ep; int result = -EFAULT; u32 source_pipe_bitmask = 0; bool dl_data_pending = true; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c index 483b2ca118fa..06f65906841d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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 @@ -853,6 +853,10 @@ void ipa3_dma_async_memcpy_notify_cb(void *priv mem_info = (struct ipa_mem_buffer *)data; ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS); + if (ep_idx < 0) { + IPADMA_ERR("IPA Client mapping failed\n"); + return; + } sys = ipa3_ctx->ep[ep_idx].sys; spin_lock_irqsave(&ipa3_dma_ctx->async_lock, flags); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 5c678f1cfc28..8ec0974711a4 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -337,7 +337,7 @@ int ipa3_send_one(struct ipa3_sys_context *sys, struct ipa3_desc *desc, int result; u16 sps_flags = SPS_IOVEC_FLAG_EOT; dma_addr_t dma_address; - u16 len; + u16 len = 0; u32 mem_flag = GFP_ATOMIC; if (unlikely(!in_atomic)) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index c3a12dd0b17c..362294b0f695 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c @@ -14,7 +14,6 @@ #include "ipahal/ipahal.h" #include "ipahal/ipahal_fltrt.h" -#define IPA_FLT_TABLE_INDEX_NOT_FOUND (-1) #define IPA_FLT_STATUS_OF_ADD_FAILED (-1) #define IPA_FLT_STATUS_OF_DEL_FAILED (-1) #define IPA_FLT_STATUS_OF_MDFY_FAILED (-1) @@ -1001,7 +1000,7 @@ error: static int __ipa_add_flt_get_ep_idx(enum ipa_client_type ep, int *ipa_ep_idx) { *ipa_ep_idx = ipa3_get_ep_mapping(ep); - if (*ipa_ep_idx == IPA_FLT_TABLE_INDEX_NOT_FOUND) { + if (*ipa_ep_idx < 0) { IPAERR("ep not valid ep=%d\n", ep); return -EINVAL; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c index 4ef1a96c8450..9e2ffe70170c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, 2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2015, 2017 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 @@ -153,10 +153,16 @@ int ipa3_mhi_reset_channel_internal(enum ipa_client_type client) int ipa3_mhi_start_channel_internal(enum ipa_client_type client) { int res; + int ipa_ep_idx; IPA_MHI_FUNC_ENTRY(); - res = ipa3_enable_data_path(ipa3_get_ep_mapping(client)); + ipa_ep_idx = ipa3_get_ep_mapping(client); + if (ipa_ep_idx < 0) { + IPA_MHI_ERR("Invalid client %d\n", client); + return -EINVAL; + } + res = ipa3_enable_data_path(ipa_ep_idx); if (res) { IPA_MHI_ERR("ipa3_enable_data_path failed %d\n", res); return res; @@ -521,6 +527,10 @@ int ipa3_mhi_resume_channels_internal(enum ipa_client_type client, IPA_MHI_FUNC_ENTRY(); ipa_ep_idx = ipa3_get_ep_mapping(client); + if (ipa_ep_idx < 0) { + IPA_MHI_ERR("Invalid client %d\n", client); + return -EINVAL; + } ep = &ipa3_ctx->ep[ipa_ep_idx]; if (brstmode_enabled && !LPTransitionRejected) { @@ -557,11 +567,14 @@ int ipa3_mhi_query_ch_info(enum ipa_client_type client, IPA_MHI_FUNC_ENTRY(); ipa_ep_idx = ipa3_get_ep_mapping(client); - + if (ipa_ep_idx < 0) { + IPA_MHI_ERR("Invalid client %d\n", client); + return -EINVAL; + } ep = &ipa3_ctx->ep[ipa_ep_idx]; res = gsi_query_channel_info(ep->gsi_chan_hdl, ch_info); if (res) { - IPAERR("gsi_query_channel_info failed\n"); + IPA_MHI_ERR("gsi_query_channel_info failed\n"); return res; } @@ -596,7 +609,10 @@ int ipa3_mhi_destroy_channel(enum ipa_client_type client) struct ipa3_ep_context *ep; ipa_ep_idx = ipa3_get_ep_mapping(client); - + if (ipa_ep_idx < 0) { + IPA_MHI_ERR("Invalid client %d\n", client); + return -EINVAL; + } ep = &ipa3_ctx->ep[ipa_ep_idx]; IPA_MHI_DBG("reset event ring (hdl: %lu, ep: %d)\n", diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c index 21ce28204069..c1d1d9659850 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -498,7 +498,7 @@ static int ipa3_uc_send_cmd_64b_param(u32 cmd_lo, u32 cmd_hi, u32 opcode, { int index; union IpaHwCpuCmdCompletedResponseData_t uc_rsp; - unsigned long flags; + unsigned long flags = 0; int retries = 0; send_cmd_lock: @@ -775,7 +775,7 @@ int ipa3_uc_send_cmd(u32 cmd, u32 opcode, u32 expected_status, void ipa3_uc_register_handlers(enum ipa3_hw_features feature, struct ipa3_uc_hdlrs *hdlrs) { - unsigned long flags; + unsigned long flags = 0; if (0 > feature || IPA_HW_FEATURE_MAX <= feature) { IPAERR("Feature %u is invalid, not registering hdlrs\n", diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 9f38af1b520b..2f28ba673d5a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -929,7 +929,7 @@ int ipa3_get_ep_mapping(enum ipa_client_type client) if (client >= IPA_CLIENT_MAX || client < 0) { IPAERR("Bad client number! client =%d\n", client); - return -EINVAL; + return IPA_EP_NOT_ALLOCATED; } ipa_ep_idx = ipa3_ep_mapping[ipa3_get_hw_type_index()][client].pipe_num; @@ -3446,6 +3446,11 @@ void ipa3_suspend_apps_pipes(bool suspend) cfg.ipa_ep_suspend = suspend; ipa_ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS); + if (ipa_ep_idx < 0) { + IPAERR("IPA client mapping failed\n"); + ipa_assert(); + return; + } ep = &ipa3_ctx->ep[ipa_ep_idx]; if (ep->valid) { IPADBG("%s pipe %d\n", suspend ? "suspend" : "unsuspend", diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index f9784630c327..5d4b46469e9c 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -381,13 +381,13 @@ static int smblib_set_opt_freq_buck(struct smb_charger *chg, int fsw_khz) if (chg->mode == PARALLEL_MASTER && chg->pl.psy) { pval.intval = fsw_khz; - rc = power_supply_set_property(chg->pl.psy, + /* + * Some parallel charging implementations may not have + * PROP_BUCK_FREQ property - they could be running + * with a fixed frequency + */ + power_supply_set_property(chg->pl.psy, POWER_SUPPLY_PROP_BUCK_FREQ, &pval); - if (rc < 0) { - dev_err(chg->dev, - "Could not set parallel buck_freq rc=%d\n", rc); - return rc; - } } return rc; diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index 37dc15494761..1c7c1e78699f 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -44,6 +44,8 @@ #define SMB2CHG_DC_TM_SREFGEN (DCIN_BASE + 0xE2) #define STACKED_DIODE_EN_BIT BIT(2) +#define TDIE_AVG_COUNT 10 + enum { OOB_COMP_WA_BIT = BIT(0), }; @@ -118,6 +120,27 @@ irqreturn_t smb138x_handle_slave_chg_state_change(int irq, void *data) return IRQ_HANDLED; } +static int smb138x_get_prop_charger_temp(struct smb138x *chip, + union power_supply_propval *val) +{ + union power_supply_propval pval; + int rc = 0, avg = 0, i; + struct smb_charger *chg = &chip->chg; + + for (i = 0; i < TDIE_AVG_COUNT; i++) { + pval.intval = 0; + rc = smblib_get_prop_charger_temp(chg, &pval); + if (rc < 0) { + pr_err("Couldnt read chg temp at %dth iteration rc = %d\n", + i + 1, rc); + return rc; + } + avg += pval.intval; + } + val->intval = avg / TDIE_AVG_COUNT; + return rc; +} + static int smb138x_parse_dt(struct smb138x *chip) { struct smb_charger *chg = &chip->chg; @@ -343,7 +366,7 @@ static int smb138x_batt_get_prop(struct power_supply *psy, rc = smblib_get_prop_batt_capacity(chg, val); break; case POWER_SUPPLY_PROP_CHARGER_TEMP: - rc = smblib_get_prop_charger_temp(chg, val); + rc = smb138x_get_prop_charger_temp(chip, val); break; case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX: rc = smblib_get_prop_charger_temp_max(chg, val); @@ -547,7 +570,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, rc = smblib_get_prop_slave_current_now(chg, val); break; case POWER_SUPPLY_PROP_CHARGER_TEMP: - rc = smblib_get_prop_charger_temp(chg, val); + rc = smb138x_get_prop_charger_temp(chip, val); break; case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX: rc = smblib_get_prop_charger_temp_max(chg, val); @@ -621,10 +644,6 @@ static int smb138x_parallel_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: rc = smblib_set_charge_param(chg, &chg->param.fcc, val->intval); break; - case POWER_SUPPLY_PROP_BUCK_FREQ: - rc = smblib_set_charge_param(chg, &chg->param.freq_buck, - val->intval); - break; case POWER_SUPPLY_PROP_SET_SHIP_MODE: /* Not in ship mode as long as the device is active */ if (!val->intval) @@ -632,7 +651,7 @@ static int smb138x_parallel_set_prop(struct power_supply *psy, rc = smblib_set_prop_ship_mode(chg, val); break; default: - pr_err("parallel power supply set prop %d not supported\n", + pr_debug("parallel power supply set prop %d not supported\n", prop); return -EINVAL; } @@ -911,6 +930,13 @@ static int smb138x_init_hw(struct smb138x *chip) chg->dcp_icl_ua = chip->dt.usb_icl_ua; + /* configure to a fixed 700khz freq to avoid tdie errors */ + rc = smblib_set_charge_param(chg, &chg->param.freq_buck, 700); + if (rc < 0) { + pr_err("Couldn't configure 700Khz switch freq rc=%d\n", rc); + return rc; + } + /* configure charge enable for software control; active high */ rc = smblib_masked_write(chg, CHGR_CFG2_REG, CHG_EN_POLARITY_BIT | CHG_EN_SRC_BIT, 0); diff --git a/drivers/sensors/sensors_ssc.c b/drivers/sensors/sensors_ssc.c index 0910ef34e777..0e299efece94 100644 --- a/drivers/sensors/sensors_ssc.c +++ b/drivers/sensors/sensors_ssc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -26,6 +26,8 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/sysfs.h> +#include <linux/workqueue.h> + #include <soc/qcom/subsystem_restart.h> #define IMAGE_LOAD_CMD 1 @@ -64,10 +66,11 @@ static struct attribute *attrs[] = { }; static struct platform_device *slpi_private; +static struct work_struct slpi_ldr_work; -static void slpi_loader_do(struct platform_device *pdev) +static void slpi_load_fw(struct work_struct *slpi_ldr_work) { - + struct platform_device *pdev = slpi_private; struct slpi_loader_private *priv = NULL; int ret; const char *firmware_name = NULL; @@ -111,6 +114,12 @@ fail: dev_err(&pdev->dev, "%s: SLPI image loading failed\n", __func__); } +static void slpi_loader_do(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s: scheduling work to load SLPI fw\n", __func__); + schedule_work(&slpi_ldr_work); +} + static void slpi_loader_unload(struct platform_device *pdev) { struct slpi_loader_private *priv = NULL; @@ -336,6 +345,8 @@ static int sensors_ssc_probe(struct platform_device *pdev) goto cdev_add_err; } + INIT_WORK(&slpi_ldr_work, slpi_load_fw); + return 0; cdev_add_err: diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index d3f1050499f3..75bebf66376d 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -46,6 +46,15 @@ config QPNP_HAPTIC module provides haptic feedback for user actions such as a long press on the touch screen. It uses the Android timed-output framework. +config QPNP_PBS + tristate "PBS trigger support for QPNP PMIC" + depends on SPMI + help + This driver supports configuring software PBS trigger event through PBS + RAM on Qualcomm Technologies, Inc. QPNP PMICs. This module provides + the APIs to the client drivers that wants to send the PBS trigger + event to the PBS RAM. + config MSM_SMD depends on MSM_SMEM bool "MSM Shared Memory Driver (SMD)" diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 2605107e2dbd..fa350d122384 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -52,6 +52,7 @@ endif obj-$(CONFIG_QPNP_HAPTIC) += qpnp-haptic.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_CPUSS_DUMP) += cpuss_dump.o +obj-$(CONFIG_QPNP_PBS) += qpnp-pbs.o obj-$(CONFIG_QCOM_PM) += spm.o obj-$(CONFIG_QCOM_SMD) += smd.o obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 768871ffa9a7..8562ada73c1d 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -195,6 +195,38 @@ struct ce_irq_list { irqreturn_t (*handler)(int, void *); }; +struct icnss_vreg_info { + struct regulator *reg; + const char *name; + u32 min_v; + u32 max_v; + u32 load_ua; + unsigned long settle_delay; + bool required; +}; + +struct icnss_clk_info { + struct clk *handle; + const char *name; + u32 freq; + bool required; +}; + +static struct icnss_vreg_info icnss_vreg_info[] = { + {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, true}, + {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false}, + {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false}, + {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false}, +}; + +#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info) + +static struct icnss_clk_info icnss_clk_info[] = { + {NULL, "cxo_ref_clk_pin", 0, false}, +}; + +#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info) + struct icnss_stats { struct { uint32_t posted; @@ -248,6 +280,7 @@ struct icnss_stats { uint32_t rejuvenate_ack_req; uint32_t rejuvenate_ack_resp; uint32_t rejuvenate_ack_err; + uint32_t trigger_recovery; }; #define MAX_NO_OF_MAC_ADDR 4 @@ -267,6 +300,8 @@ static struct icnss_priv { struct platform_device *pdev; struct icnss_driver_ops *ops; struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS]; + struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE]; + struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE]; u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS]; phys_addr_t mem_base_pa; void __iomem *mem_base_va; @@ -664,41 +699,220 @@ out: return ret; } +static int icnss_vreg_on(struct icnss_priv *priv) +{ + int ret = 0; + struct icnss_vreg_info *vreg_info; + int i; + + for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) { + vreg_info = &priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + icnss_pr_dbg("Regulator %s being enabled\n", vreg_info->name); + + ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v, + vreg_info->max_v); + if (ret) { + icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n", + vreg_info->name, vreg_info->min_v, + vreg_info->max_v, ret); + break; + } + + if (vreg_info->load_ua) { + ret = regulator_set_load(vreg_info->reg, + vreg_info->load_ua); + if (ret < 0) { + icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n", + vreg_info->name, + vreg_info->load_ua, ret); + break; + } + } + + ret = regulator_enable(vreg_info->reg); + if (ret) { + icnss_pr_err("Regulator %s, can't enable: %d\n", + vreg_info->name, ret); + break; + } + + if (vreg_info->settle_delay) + udelay(vreg_info->settle_delay); + } + + if (!ret) + return 0; + + for (; i >= 0; i--) { + vreg_info = &priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + regulator_disable(vreg_info->reg); + regulator_set_load(vreg_info->reg, 0); + regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v); + } + + return ret; +} + +static int icnss_vreg_off(struct icnss_priv *priv) +{ + int ret = 0; + struct icnss_vreg_info *vreg_info; + int i; + + for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) { + vreg_info = &priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + icnss_pr_dbg("Regulator %s being disabled\n", vreg_info->name); + + ret = regulator_disable(vreg_info->reg); + if (ret) + icnss_pr_err("Regulator %s, can't disable: %d\n", + vreg_info->name, ret); + + ret = regulator_set_load(vreg_info->reg, 0); + if (ret < 0) + icnss_pr_err("Regulator %s, can't set load: %d\n", + vreg_info->name, ret); + + ret = regulator_set_voltage(vreg_info->reg, 0, + vreg_info->max_v); + if (ret) + icnss_pr_err("Regulator %s, can't set voltage: %d\n", + vreg_info->name, ret); + } + + return ret; +} + +static int icnss_clk_init(struct icnss_priv *priv) +{ + struct icnss_clk_info *clk_info; + int i; + int ret = 0; + + for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) { + clk_info = &priv->clk_info[i]; + + if (!clk_info->handle) + continue; + + icnss_pr_dbg("Clock %s being enabled\n", clk_info->name); + + if (clk_info->freq) { + ret = clk_set_rate(clk_info->handle, clk_info->freq); + + if (ret) { + icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n", + clk_info->name, clk_info->freq, + ret); + break; + } + } + + ret = clk_prepare_enable(clk_info->handle); + if (ret) { + icnss_pr_err("Clock %s, can't enable: %d\n", + clk_info->name, ret); + break; + } + } + + if (ret == 0) + return 0; + + for (; i >= 0; i--) { + clk_info = &priv->clk_info[i]; + + if (!clk_info->handle) + continue; + + clk_disable_unprepare(clk_info->handle); + } + + return ret; +} + +static int icnss_clk_deinit(struct icnss_priv *priv) +{ + struct icnss_clk_info *clk_info; + int i; + + for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) { + clk_info = &priv->clk_info[i]; + + if (!clk_info->handle) + continue; + + icnss_pr_dbg("Clock %s being disabled\n", clk_info->name); + + clk_disable_unprepare(clk_info->handle); + } + + return 0; +} + static int icnss_hw_power_on(struct icnss_priv *priv) { int ret = 0; - unsigned long flags; icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state); - spin_lock_irqsave(&priv->on_off_lock, flags); + spin_lock(&priv->on_off_lock); if (test_bit(ICNSS_POWER_ON, &priv->state)) { - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); return ret; } set_bit(ICNSS_POWER_ON, &priv->state); - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); + + ret = icnss_vreg_on(priv); + if (ret) + goto out; + + ret = icnss_clk_init(priv); + if (ret) + goto vreg_off; + + return ret; +vreg_off: + icnss_vreg_off(priv); +out: + clear_bit(ICNSS_POWER_ON, &priv->state); return ret; } static int icnss_hw_power_off(struct icnss_priv *priv) { int ret = 0; - unsigned long flags; if (test_bit(HW_ALWAYS_ON, &quirks)) return 0; icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state); - spin_lock_irqsave(&priv->on_off_lock, flags); + spin_lock(&priv->on_off_lock); if (!test_bit(ICNSS_POWER_ON, &priv->state)) { - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); return ret; } clear_bit(ICNSS_POWER_ON, &priv->state); - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); + + icnss_clk_deinit(priv); + + ret = icnss_vreg_off(priv); return ret; } @@ -1895,6 +2109,8 @@ static int icnss_call_driver_remove(struct icnss_priv *priv) clear_bit(ICNSS_DRIVER_PROBED, &priv->state); + icnss_hw_power_off(penv); + return 0; } @@ -1947,8 +2163,6 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, icnss_call_driver_remove(priv); out: - ret = icnss_hw_power_off(priv); - kfree(data); return ret; @@ -2923,11 +3137,16 @@ int icnss_trigger_recovery(struct device *dev) } if (!priv->service_notifier[0].handle) { - icnss_pr_err("Invalid handle during recovery\n"); + icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n", + priv->state); ret = -EINVAL; goto out; } + icnss_pr_dbg("Initiate PD restart at WLAN FW, state: 0x%lx\n", + priv->state); + priv->stats.trigger_recovery++; + /* * Initiate PDR, required only for the first instance */ @@ -3005,6 +3224,114 @@ static void icnss_smmu_deinit(struct icnss_priv *priv) priv->smmu_mapping = NULL; } +static int icnss_get_vreg_info(struct device *dev, + struct icnss_vreg_info *vreg_info) +{ + int ret = 0; + char prop_name[MAX_PROP_SIZE]; + struct regulator *reg; + const __be32 *prop; + int len = 0; + int i; + + reg = devm_regulator_get_optional(dev, vreg_info->name); + if (PTR_ERR(reg) == -EPROBE_DEFER) { + icnss_pr_err("EPROBE_DEFER for regulator: %s\n", + vreg_info->name); + ret = PTR_ERR(reg); + goto out; + } + + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + + if (vreg_info->required) { + icnss_pr_err("Regulator %s doesn't exist: %d\n", + vreg_info->name, ret); + goto out; + } else { + icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n", + vreg_info->name, ret); + goto done; + } + } + + vreg_info->reg = reg; + + snprintf(prop_name, MAX_PROP_SIZE, + "qcom,%s-config", vreg_info->name); + + prop = of_get_property(dev->of_node, prop_name, &len); + + icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n", + prop_name, len); + + if (!prop || len < (2 * sizeof(__be32))) { + icnss_pr_dbg("Property %s %s\n", prop_name, + prop ? "invalid format" : "doesn't exist"); + goto done; + } + + for (i = 0; (i * sizeof(__be32)) < len; i++) { + switch (i) { + case 0: + vreg_info->min_v = be32_to_cpup(&prop[0]); + break; + case 1: + vreg_info->max_v = be32_to_cpup(&prop[1]); + break; + case 2: + vreg_info->load_ua = be32_to_cpup(&prop[2]); + break; + case 3: + vreg_info->settle_delay = be32_to_cpup(&prop[3]); + break; + default: + icnss_pr_dbg("Property %s, ignoring value at %d\n", + prop_name, i); + break; + } + } + +done: + icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n", + vreg_info->name, vreg_info->min_v, vreg_info->max_v, + vreg_info->load_ua, vreg_info->settle_delay); + + return 0; + +out: + return ret; +} + +static int icnss_get_clk_info(struct device *dev, + struct icnss_clk_info *clk_info) +{ + struct clk *handle; + int ret = 0; + + handle = devm_clk_get(dev, clk_info->name); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + if (clk_info->required) { + icnss_pr_err("Clock %s isn't available: %d\n", + clk_info->name, ret); + goto out; + } else { + icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name, + ret); + ret = 0; + goto out; + } + } + + icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq); + + clk_info->handle = handle; +out: + return ret; +} + static int icnss_fw_debug_show(struct seq_file *s, void *data) { struct icnss_priv *priv = s->private; @@ -3370,6 +3697,7 @@ static int icnss_stats_show(struct seq_file *s, void *data) ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err); + ICNSS_STATS_DUMP(s, priv, trigger_recovery); seq_puts(s, "\n<------------------ PM stats ------------------->\n"); ICNSS_STATS_DUMP(s, priv, pm_suspend); @@ -3715,6 +4043,21 @@ static int icnss_probe(struct platform_device *pdev) if (ret == -EPROBE_DEFER) goto out; + memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info)); + for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) { + ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]); + + if (ret) + goto out; + } + + memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info)); + for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) { + ret = icnss_get_clk_info(dev, &priv->clk_info[i]); + if (ret) + goto out; + } + if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass")) priv->bypass_s1_smmu = true; diff --git a/drivers/soc/qcom/qdsp6v2/adsp-loader.c b/drivers/soc/qcom/qdsp6v2/adsp-loader.c index 51539a36a74f..b45eac88ed12 100644 --- a/drivers/soc/qcom/qdsp6v2/adsp-loader.c +++ b/drivers/soc/qcom/qdsp6v2/adsp-loader.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2014, 2017, 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 @@ -20,6 +20,8 @@ #include <linux/qdsp6v2/apr.h> #include <linux/of_device.h> #include <linux/sysfs.h> +#include <linux/workqueue.h> + #include <soc/qcom/subsystem_restart.h> #define Q6_PIL_GET_DELAY_MS 100 @@ -44,12 +46,13 @@ static struct attribute *attrs[] = { NULL, }; +static struct work_struct adsp_ldr_work; static struct platform_device *adsp_private; static void adsp_loader_unload(struct platform_device *pdev); -static void adsp_loader_do(struct platform_device *pdev) +static void adsp_load_fw(struct work_struct *adsp_ldr_work) { - + struct platform_device *pdev = adsp_private; struct adsp_loader_private *priv = NULL; const char *adsp_dt = "qcom,adsp-state"; @@ -146,6 +149,11 @@ fail: return; } +static void adsp_loader_do(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s: scheduling work to load ADSP fw\n", __func__); + schedule_work(&adsp_ldr_work); +} static ssize_t adsp_boot_store(struct kobject *kobj, struct kobj_attribute *attr, @@ -270,6 +278,8 @@ static int adsp_loader_probe(struct platform_device *pdev) return ret; } + INIT_WORK(&adsp_ldr_work, adsp_load_fw); + return 0; } diff --git a/drivers/soc/qcom/qdsp6v2/cdsp-loader.c b/drivers/soc/qcom/qdsp6v2/cdsp-loader.c index 0b801c5cd7dd..cae26e3913d6 100644 --- a/drivers/soc/qcom/qdsp6v2/cdsp-loader.c +++ b/drivers/soc/qcom/qdsp6v2/cdsp-loader.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014,2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2014, 2017, 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 @@ -19,6 +19,8 @@ #include <linux/platform_device.h> #include <linux/of_device.h> #include <linux/sysfs.h> +#include <linux/workqueue.h> + #include <soc/qcom/subsystem_restart.h> #define BOOT_CMD 1 @@ -47,11 +49,12 @@ static struct attribute *attrs[] = { static u32 cdsp_state = CDSP_SUBSYS_DOWN; static struct platform_device *cdsp_private; +static struct work_struct cdsp_ldr_work; static void cdsp_loader_unload(struct platform_device *pdev); -static int cdsp_loader_do(struct platform_device *pdev) +static void cdsp_load_fw(struct work_struct *cdsp_ldr_work) { - + struct platform_device *pdev = cdsp_private; struct cdsp_loader_private *priv = NULL; int rc = 0; @@ -101,14 +104,19 @@ static int cdsp_loader_do(struct platform_device *pdev) } dev_dbg(&pdev->dev, "%s: CDSP image is loaded\n", __func__); - return rc; + return; } fail: dev_err(&pdev->dev, "%s: CDSP image loading failed\n", __func__); - return rc; + return; } +static void cdsp_loader_do(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s: scheduling work to load CDSP fw\n", __func__); + schedule_work(&cdsp_ldr_work); +} static ssize_t cdsp_boot_store(struct kobject *kobj, struct kobj_attribute *attr, @@ -126,7 +134,7 @@ static ssize_t cdsp_boot_store(struct kobject *kobj, pr_debug("%s: going to call cdsp_loader_do\n", __func__); cdsp_loader_do(cdsp_private); } else if (boot == IMAGE_UNLOAD_CMD) { - pr_debug("%s: going to call adsp_unloader\n", __func__); + pr_debug("%s: going to call cdsp_unloader\n", __func__); cdsp_loader_unload(cdsp_private); } return count; @@ -238,6 +246,8 @@ static int cdsp_loader_probe(struct platform_device *pdev) return ret; } + INIT_WORK(&cdsp_ldr_work, cdsp_load_fw); + return 0; } diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c index 39070561d7e4..cf0b7ff25201 100644 --- a/drivers/soc/qcom/qpnp-haptic.c +++ b/drivers/soc/qcom/qpnp-haptic.c @@ -141,9 +141,7 @@ #define QPNP_HAP_CYCLS 5 #define QPNP_TEST_TIMER_MS 5 -#define AUTO_RES_ENABLE_TIMEOUT 20000 -#define AUTO_RES_ERR_CAPTURE_RES 5 -#define AUTO_RES_ERR_MAX 15 +#define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000 #define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5 #define MISC_SEC_ACCESS 0x09D0 @@ -152,8 +150,22 @@ #define POLL_TIME_AUTO_RES_ERR_NS (5 * NSEC_PER_MSEC) -#define LRA_POS_FREQ_COUNT 6 -int lra_play_rate_code[LRA_POS_FREQ_COUNT]; +#define MAX_POSITIVE_VARIATION_LRA_FREQ 30 +#define MAX_NEGATIVE_VARIATION_LRA_FREQ -30 +#define FREQ_VARIATION_STEP 5 +#define AUTO_RES_ERROR_CAPTURE_RES 5 +#define AUTO_RES_ERROR_MAX 30 +#define ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE \ + ((MAX_POSITIVE_VARIATION_LRA_FREQ - MAX_NEGATIVE_VARIATION_LRA_FREQ) \ + / FREQ_VARIATION_STEP) +#define LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent) \ + (hap->init_drive_period_code = (hap->init_drive_period_code * \ + (1000 + rc_clk_err_percent_x10)) / 1000) +#define LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent) \ + (hap->init_drive_period_code = (hap->init_drive_period_code * \ + (1000 - rc_clk_err_percent_x10)) / 1000) + +u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE]; /* haptic debug register set */ static u8 qpnp_hap_dbg_regs[] = { @@ -246,10 +258,21 @@ struct qpnp_pwm_info { * @ pwm_info - pwm info * @ lock - mutex lock * @ wf_lock - mutex lock for waveform + * @ init_drive_period_code - the initial lra drive period code + * @ drive_period_code_max_limit_percent_variation - maximum limit of + percentage variation of drive period code + * @ drive_period_code_min_limit_percent_variation - minimum limit og + percentage variation of drive period code + * @ drive_period_code_max_limit - calculated drive period code with + percentage variation on the higher side. + * @ drive_period_code_min_limit - calculated drive period code with + percentage variation on the lower side * @ play_mode - play mode * @ auto_res_mode - auto resonace mode * @ lra_high_z - high z option line * @ timeout_ms - max timeout in ms + * @ time_required_to_generate_back_emf_us - the time required for sufficient + back-emf to be generated for auto resonance to be successful * @ vmax_mv - max voltage in mv * @ ilim_ma - limiting current in ma * @ sc_deb_cycles - short circuit debounce cycles @@ -280,6 +303,8 @@ struct qpnp_pwm_info { * @ sup_brake_pat - support custom brake pattern * @ correct_lra_drive_freq - correct LRA Drive Frequency * @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present + * @ perform_lra_auto_resonance_search - whether lra auto resonance search + * algorithm should be performed or not. */ struct qpnp_hap { struct platform_device *pdev; @@ -300,7 +325,9 @@ struct qpnp_hap { enum qpnp_hap_mode play_mode; enum qpnp_hap_auto_res_mode auto_res_mode; enum qpnp_hap_high_z lra_high_z; + u32 init_drive_period_code; u32 timeout_ms; + u32 time_required_to_generate_back_emf_us; u32 vmax_mv; u32 ilim_ma; u32 sc_deb_cycles; @@ -312,16 +339,21 @@ struct qpnp_hap { u32 play_irq; u32 sc_irq; u16 base; + u16 drive_period_code_max_limit; + u16 drive_period_code_min_limit; + u8 drive_period_code_max_limit_percent_variation; + u8 drive_period_code_min_limit_percent_variation; u8 act_type; u8 wave_shape; - u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; - u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; - u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; + u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; + u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; + u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; u8 reg_en_ctl; u8 reg_play; u8 lra_res_cal_period; u8 sc_duration; u8 ext_pwm_dtest_line; + bool vcc_pon_enabled; bool state; bool use_play_irq; bool use_sc_irq; @@ -333,6 +365,7 @@ struct qpnp_hap { bool sup_brake_pat; bool correct_lra_drive_freq; bool misc_trim_error_rc19p2_clk_reg_present; + bool perform_lra_auto_resonance_search; }; static struct qpnp_hap *ghap; @@ -1314,29 +1347,62 @@ static struct device_attribute qpnp_hap_attrs[] = { qpnp_hap_min_max_test_data_store), }; -static void calculate_lra_code(struct qpnp_hap *hap) +static int calculate_lra_code(struct qpnp_hap *hap) { - u8 play_rate_code_lo, play_rate_code_hi; - int play_rate_code, neg_idx = 0, pos_idx = LRA_POS_FREQ_COUNT-1; - int lra_init_freq, freq_variation, start_variation = AUTO_RES_ERR_MAX; + u8 lra_drive_period_code_lo = 0, lra_drive_period_code_hi = 0; + u32 lra_drive_period_code, lra_drive_frequency_hz, freq_variation; + u8 start_variation = AUTO_RES_ERROR_MAX, i; + u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1; + int rc = 0; - qpnp_hap_read_reg(hap, &play_rate_code_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); - qpnp_hap_read_reg(hap, &play_rate_code_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_lo, + QPNP_HAP_RATE_CFG1_REG(hap->base)); + if (rc) { + dev_err(&hap->pdev->dev, + "Error while reading RATE_CFG1 register\n"); + return rc; + } + + rc = qpnp_hap_read_reg(hap, &lra_drive_period_code_hi, + QPNP_HAP_RATE_CFG2_REG(hap->base)); + if (rc) { + dev_err(&hap->pdev->dev, + "Error while reading RATE_CFG2 register\n"); + return rc; + } - play_rate_code = (play_rate_code_hi << 8) | (play_rate_code_lo & 0xff); + if (!lra_drive_period_code_lo && !lra_drive_period_code_hi) { + dev_err(&hap->pdev->dev, + "Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n"); + return -EINVAL; + } - lra_init_freq = 200000 / play_rate_code; + lra_drive_period_code = + (lra_drive_period_code_hi << 8) | (lra_drive_period_code_lo & 0xff); + lra_drive_frequency_hz = 200000 / lra_drive_period_code; - while (start_variation >= AUTO_RES_ERR_CAPTURE_RES) { - freq_variation = (lra_init_freq * start_variation) / 100; - lra_play_rate_code[neg_idx++] = 200000 / (lra_init_freq - - freq_variation); - lra_play_rate_code[pos_idx--] = 200000 / (lra_init_freq + - freq_variation); - start_variation -= AUTO_RES_ERR_CAPTURE_RES; + while (start_variation >= AUTO_RES_ERROR_CAPTURE_RES) { + freq_variation = + (lra_drive_frequency_hz * start_variation) / 100; + adjusted_lra_play_rate_code[neg_idx++] = + 200000 / (lra_drive_frequency_hz - freq_variation); + adjusted_lra_play_rate_code[pos_idx--] = + 200000 / (lra_drive_frequency_hz + freq_variation); + start_variation -= AUTO_RES_ERROR_CAPTURE_RES; } + + dev_dbg(&hap->pdev->dev, + "lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n" + "lra_drive_period_code = 0x%x, lra_drive_frequency_hz = 0x%x\n" + "Calculated play rate code values are :\n", + lra_drive_period_code_lo, lra_drive_period_code_hi, + lra_drive_period_code, lra_drive_frequency_hz); + + for (i = 0; i < ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; ++i) + dev_dbg(&hap->pdev->dev, + " 0x%x", adjusted_lra_play_rate_code[i]); + + return 0; } static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) @@ -1369,20 +1435,37 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) static void update_lra_frequency(struct qpnp_hap *hap) { u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0; + u32 play_rate_code; qpnp_hap_read_reg(hap, &lra_auto_res_lo, QPNP_HAP_LRA_AUTO_RES_LO(hap->base)); qpnp_hap_read_reg(hap, &lra_auto_res_hi, QPNP_HAP_LRA_AUTO_RES_HI(hap->base)); - if (lra_auto_res_lo && lra_auto_res_hi) { - qpnp_hap_write_reg(hap, &lra_auto_res_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + play_rate_code = + (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF); - lra_auto_res_hi = lra_auto_res_hi >> 4; - qpnp_hap_write_reg(hap, &lra_auto_res_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); - } + dev_dbg(&hap->pdev->dev, + "lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n", + lra_auto_res_lo, lra_auto_res_hi, play_rate_code); + + /* + * If the drive period code read from AUTO RES_LO and AUTO_RES_HI + * registers is more than the max limit percent variation read from + * DT or less than the min limit percent variation read from DT, then + * RATE_CFG registers are not uptdated. + */ + + if ((play_rate_code <= hap->drive_period_code_min_limit) || + (play_rate_code >= hap->drive_period_code_max_limit)) + return; + + qpnp_hap_write_reg(hap, &lra_auto_res_lo, + QPNP_HAP_RATE_CFG1_REG(hap->base)); + + lra_auto_res_hi = lra_auto_res_hi >> 4; + qpnp_hap_write_reg(hap, &lra_auto_res_hi, + QPNP_HAP_RATE_CFG2_REG(hap->base)); } static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) @@ -1412,36 +1495,38 @@ static void correct_auto_res_error(struct work_struct *auto_res_err_work) struct qpnp_hap, auto_res_err_work); u8 lra_code_lo, lra_code_hi, disable_hap = 0x00; - static int lra_freq_index; - ktime_t currtime, remaining_time; - int temp, rem = 0, index = lra_freq_index % LRA_POS_FREQ_COUNT; + static u8 lra_freq_index; + ktime_t currtime = ktime_set(0, 0), remaining_time = ktime_set(0, 0); - if (hrtimer_active(&hap->hap_timer)) { + if (hrtimer_active(&hap->hap_timer)) remaining_time = hrtimer_get_remaining(&hap->hap_timer); - rem = (int)ktime_to_us(remaining_time); - } qpnp_hap_play(hap, 0); qpnp_hap_write_reg(hap, &disable_hap, QPNP_HAP_EN_CTL_REG(hap->base)); - lra_code_lo = lra_play_rate_code[index] & QPNP_HAP_RATE_CFG1_MASK; - qpnp_hap_write_reg(hap, &lra_code_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + if (hap->perform_lra_auto_resonance_search) { + lra_code_lo = + adjusted_lra_play_rate_code[lra_freq_index] + & QPNP_HAP_RATE_CFG1_MASK; - qpnp_hap_read_reg(hap, &lra_code_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + qpnp_hap_write_reg(hap, &lra_code_lo, + QPNP_HAP_RATE_CFG1_REG(hap->base)); - lra_code_hi &= QPNP_HAP_RATE_CFG2_MASK; - temp = lra_play_rate_code[index] >> QPNP_HAP_RATE_CFG2_SHFT; - lra_code_hi |= temp; + lra_code_hi = adjusted_lra_play_rate_code[lra_freq_index] + >> QPNP_HAP_RATE_CFG2_SHFT; - qpnp_hap_write_reg(hap, &lra_code_hi, + qpnp_hap_write_reg(hap, &lra_code_hi, QPNP_HAP_RATE_CFG2_REG(hap->base)); - lra_freq_index++; + lra_freq_index = (lra_freq_index+1) % + ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; + } - if (rem > 0) { + dev_dbg(&hap->pdev->dev, "Remaining time is %lld\n", + ktime_to_us(remaining_time)); + + if ((ktime_to_us(remaining_time)) > 0) { currtime = ktime_get(); hap->state = 1; hrtimer_forward(&hap->hap_timer, currtime, remaining_time); @@ -1455,6 +1540,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) int rc = 0; u8 val = 0; unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS; + u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us; if (hap->play_mode == QPNP_HAP_PWM) { if (on) @@ -1464,8 +1550,21 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) } else if (hap->play_mode == QPNP_HAP_BUFFER || hap->play_mode == QPNP_HAP_DIRECT) { if (on) { - if (hap->correct_lra_drive_freq || - hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) + /* + * For auto resonance detection to work properly, + * sufficient back-emf has to be generated. In general, + * back-emf takes some time to build up. When the auto + * resonance mode is chosen as QWD, high-z will be + * applied for every LRA cycle and hence there won't be + * enough back-emf at the start-up. Hence, the motor + * needs to vibrate for few LRA cycles after the PLAY + * bit is asserted. So disable the auto resonance here + * and enable it after the sleep of + * 'time_required_to_generate_back_emf_us' is completed. + */ + if ((hap->act_type == QPNP_HAP_LRA) && + (hap->correct_lra_drive_freq || + hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) qpnp_hap_auto_res_enable(hap, 0); rc = qpnp_hap_mod_enable(hap, on); @@ -1474,17 +1573,18 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) rc = qpnp_hap_play(hap, on); - if ((hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq) || - hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { - usleep_range(AUTO_RES_ENABLE_TIMEOUT, - (AUTO_RES_ENABLE_TIMEOUT + 1)); + if ((hap->act_type == QPNP_HAP_LRA) && + (hap->correct_lra_drive_freq || + hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) { + usleep_range(back_emf_delay_us, + (back_emf_delay_us + 1)); rc = qpnp_hap_auto_res_enable(hap, 1); if (rc < 0) return rc; } - if (hap->correct_lra_drive_freq) { + if (hap->act_type == QPNP_HAP_LRA && + hap->correct_lra_drive_freq) { /* * Start timer to poll Auto Resonance error bit */ @@ -1500,7 +1600,8 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) if (rc < 0) return rc; - if (hap->correct_lra_drive_freq) { + if (hap->act_type == QPNP_HAP_LRA && + hap->correct_lra_drive_freq) { rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); if (!(val & AUTO_RES_ERR_BIT)) @@ -1511,7 +1612,6 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { hrtimer_cancel(&hap->auto_res_err_poll_timer); - calculate_lra_code(hap); } } } @@ -1619,13 +1719,15 @@ static void qpnp_hap_worker(struct work_struct *work) struct qpnp_hap *hap = container_of(work, struct qpnp_hap, work); u8 val = 0x00; - int rc, reg_en = 0; + int rc; - if (hap->vcc_pon) { - reg_en = regulator_enable(hap->vcc_pon); - if (reg_en) - pr_err("%s: could not enable vcc_pon regulator\n", - __func__); + if (hap->vcc_pon && hap->state && !hap->vcc_pon_enabled) { + rc = regulator_enable(hap->vcc_pon); + if (rc < 0) + pr_err("%s: could not enable vcc_pon regulator rc=%d\n", + __func__, rc); + else + hap->vcc_pon_enabled = true; } /* Disable haptics module if the duration of short circuit @@ -1640,11 +1742,13 @@ static void qpnp_hap_worker(struct work_struct *work) qpnp_hap_set(hap, hap->state); } - if (hap->vcc_pon && !reg_en) { + if (hap->vcc_pon && !hap->state && hap->vcc_pon_enabled) { rc = regulator_disable(hap->vcc_pon); if (rc) - pr_err("%s: could not disable vcc_pon regulator\n", - __func__); + pr_err("%s: could not disable vcc_pon regulator rc=%d\n", + __func__, rc); + else + hap->vcc_pon_enabled = false; } } @@ -1706,10 +1810,16 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL); /* Configuration api for haptics registers */ static int qpnp_hap_config(struct qpnp_hap *hap) { - u8 reg = 0, unlock_val, error_value; - int rc, i, temp; + u8 reg = 0, unlock_val; + u32 temp; + int rc, i; uint error_code = 0; + /* + * This denotes the percentage error in rc clock multiplied by 10 + */ + u8 rc_clk_err_percent_x10; + /* Configure the ACTUATOR TYPE register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); if (rc < 0) @@ -1838,16 +1948,22 @@ static int qpnp_hap_config(struct qpnp_hap *hap) else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX) hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX; - temp = hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US; + hap->init_drive_period_code = + hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US; /* - * The frequency of 19.2Mzhz RC clock is subject to variation. - * In PMI8950, TRIM_ERROR_RC19P2_CLK register in MISC module - * holds the frequency error in 19.2Mhz RC clock + * The frequency of 19.2Mzhz RC clock is subject to variation. Currently + * a few PMI modules have MISC_TRIM_ERROR_RC19P2_CLK register + * present in their MISC block. This register holds the frequency error + * in 19.2Mhz RC clock. */ if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq && hap->misc_trim_error_rc19p2_clk_reg_present) { unlock_val = MISC_SEC_UNLOCK; + /* + * This SID value may change depending on the PMI chip where + * the MISC block is present. + */ rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val); if (rc) dev_err(&hap->pdev->dev, @@ -1855,36 +1971,69 @@ static int qpnp_hap_config(struct qpnp_hap *hap) regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK, &error_code); + dev_dbg(&hap->pdev->dev, "TRIM register = 0x%x\n", error_code); - error_value = (error_code & 0x0F) * 7; + /* + * Extract the 4 LSBs and multiply by 7 to get + * the %error in RC clock multiplied by 10 + */ + rc_clk_err_percent_x10 = (error_code & 0x0F) * 7; - if (error_code & 0x80) - temp = (temp * (1000 - error_value)) / 1000; + /* + * If the TRIM register holds value less than 0x80, + * then there is a positive error in the RC clock. + * If the TRIM register holds value greater than or equal to + * 0x80, then there is a negative error in the RC clock. + * + * The adjusted play rate code is calculated as follows: + * LRA drive period code (RATE_CFG) = + * 200KHz * 1 / LRA drive frequency * ( 1 + %error/ 100) + * + * This can be rewritten as: + * LRA drive period code (RATE_CFG) = + * 200KHz * 1/LRA drive frequency *( 1 + %error * 10/1000) + * + * Since 200KHz * 1/LRA drive frequency is already calculated + * above we only do rest of the scaling here. + */ + if (error_code >= 128) + LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10); else - temp = (temp * (1000 + error_value)) / 1000; + LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10); } - reg = temp & QPNP_HAP_RATE_CFG1_MASK; + dev_dbg(&hap->pdev->dev, + "Play rate code 0x%x\n", hap->init_drive_period_code); + + reg = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_RATE_CFG1_REG(hap->base)); if (rc) return rc; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_RATE_CFG2_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_RATE_CFG2_MASK; - temp = temp >> QPNP_HAP_RATE_CFG2_SHFT; - reg |= temp; + reg = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_RATE_CFG2_REG(hap->base)); if (rc) return rc; - if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq) + if (hap->act_type == QPNP_HAP_LRA && + hap->perform_lra_auto_resonance_search) calculate_lra_code(hap); + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { + hap->drive_period_code_max_limit = + (hap->init_drive_period_code * 100) / + (100 - hap->drive_period_code_max_limit_percent_variation); + hap->drive_period_code_min_limit = + (hap->init_drive_period_code * 100) / + (100 + hap->drive_period_code_min_limit_percent_variation); + dev_dbg(&hap->pdev->dev, "Drive period code max limit %x\n" + "Drive period code min limit %x\n", + hap->drive_period_code_max_limit, + hap->drive_period_code_min_limit); + } + /* Configure BRAKE register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); if (rc < 0) @@ -2031,13 +2180,44 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) return rc; } + hap->perform_lra_auto_resonance_search = + of_property_read_bool(pdev->dev.of_node, + "qcom,perform-lra-auto-resonance-search"); + hap->correct_lra_drive_freq = of_property_read_bool(pdev->dev.of_node, "qcom,correct-lra-drive-freq"); + hap->drive_period_code_max_limit_percent_variation = 25; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,drive-period-code-max-limit-percent-variation", &temp); + if (!rc) + hap->drive_period_code_max_limit_percent_variation = + (u8) temp; + + hap->drive_period_code_min_limit_percent_variation = 25; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,drive-period-code-min-limit-percent-variation", &temp); + if (!rc) + hap->drive_period_code_min_limit_percent_variation = + (u8) temp; + hap->misc_trim_error_rc19p2_clk_reg_present = of_property_read_bool(pdev->dev.of_node, "qcom,misc-trim-error-rc19p2-clk-reg-present"); + + if (hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { + hap->time_required_to_generate_back_emf_us = + QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,time-required-to-generate-back-emf-us", + &temp); + if (!rc) + hap->time_required_to_generate_back_emf_us = + temp; + } else { + hap->time_required_to_generate_back_emf_us = 0; + } } rc = of_property_read_string(pdev->dev.of_node, diff --git a/drivers/soc/qcom/qpnp-pbs.c b/drivers/soc/qcom/qpnp-pbs.c new file mode 100644 index 000000000000..287c8a25b391 --- /dev/null +++ b/drivers/soc/qcom/qpnp-pbs.c @@ -0,0 +1,361 @@ +/* Copyright (c) 2017, 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. + */ + +#define pr_fmt(fmt) "PBS: %s: " fmt, __func__ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spmi.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/qpnp/qpnp-pbs.h> + +#define QPNP_PBS_DEV_NAME "qcom,qpnp-pbs" + +#define PBS_CLIENT_TRIG_CTL 0x42 +#define PBS_CLIENT_SW_TRIG_BIT BIT(7) +#define PBS_CLIENT_SCRATCH1 0x50 +#define PBS_CLIENT_SCRATCH2 0x51 + +static LIST_HEAD(pbs_dev_list); +static DEFINE_MUTEX(pbs_list_lock); + +struct qpnp_pbs { + struct platform_device *pdev; + struct device *dev; + struct device_node *dev_node; + struct regmap *regmap; + struct mutex pbs_lock; + struct list_head link; + + u32 base; +}; + +static int qpnp_pbs_read(struct qpnp_pbs *pbs, u32 address, + u8 *val, int count) +{ + int rc = 0; + struct platform_device *pdev = pbs->pdev; + + rc = regmap_bulk_read(pbs->regmap, address, val, count); + if (rc) + pr_err("Failed to read address=0x%02x sid=0x%02x rc=%d\n", + address, to_spmi_device(pdev->dev.parent)->usid, rc); + + return rc; +} + +static int qpnp_pbs_write(struct qpnp_pbs *pbs, u16 address, + u8 *val, int count) +{ + int rc = 0; + struct platform_device *pdev = pbs->pdev; + + rc = regmap_bulk_write(pbs->regmap, address, val, count); + if (rc < 0) + pr_err("Failed to write address =0x%02x sid=0x%02x rc=%d\n", + address, to_spmi_device(pdev->dev.parent)->usid, rc); + else + pr_debug("Wrote 0x%02X to addr 0x%04x\n", *val, address); + + return rc; +} + +static int qpnp_pbs_masked_write(struct qpnp_pbs *pbs, u16 address, + u8 mask, u8 val) +{ + int rc; + + rc = regmap_update_bits(pbs->regmap, address, mask, val); + if (rc < 0) + pr_err("Failed to write address 0x%04X, rc = %d\n", + address, rc); + else + pr_debug("Wrote 0x%02X to addr 0x%04X\n", + val, address); + + return rc; +} + +static struct qpnp_pbs *get_pbs_client_node(struct device_node *dev_node) +{ + struct qpnp_pbs *pbs; + + mutex_lock(&pbs_list_lock); + list_for_each_entry(pbs, &pbs_dev_list, link) { + if (dev_node == pbs->dev_node) { + mutex_unlock(&pbs_list_lock); + return pbs; + } + } + + mutex_unlock(&pbs_list_lock); + return ERR_PTR(-EINVAL); +} + +static int qpnp_pbs_wait_for_ack(struct qpnp_pbs *pbs, u8 bit_pos) +{ + int rc = 0; + u16 retries = 2000, dly = 1000; + u8 val; + + while (retries--) { + rc = qpnp_pbs_read(pbs, pbs->base + + PBS_CLIENT_SCRATCH2, &val, 1); + if (rc < 0) { + pr_err("Failed to read register %x rc = %d\n", + PBS_CLIENT_SCRATCH2, rc); + return rc; + } + + if (val == 0xFF) { + /* PBS error - clear SCRATCH2 register */ + rc = qpnp_pbs_write(pbs, pbs->base + + PBS_CLIENT_SCRATCH2, 0, 1); + if (rc < 0) { + pr_err("Failed to clear register %x rc=%d\n", + PBS_CLIENT_SCRATCH2, rc); + return rc; + } + + pr_err("NACK from PBS for bit %d\n", bit_pos); + return -EINVAL; + } + + if (val & BIT(bit_pos)) { + pr_debug("PBS sequence for bit %d executed!\n", + bit_pos); + break; + } + + usleep_range(dly, dly + 100); + } + + if (!retries) { + pr_err("Timeout for PBS ACK/NACK for bit %d\n", bit_pos); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * qpnp_pbs_trigger_event - Trigger the PBS RAM sequence + * + * Returns = 0 If the PBS RAM sequence executed successfully. + * + * Returns < 0 for errors. + * + * This function is used to trigger the PBS RAM sequence to be + * executed by the client driver. + * + * The PBS trigger sequence involves + * 1. setting the PBS sequence bit in PBS_CLIENT_SCRATCH1 + * 2. Initiating the SW PBS trigger + * 3. Checking the equivalent bit in PBS_CLIENT_SCRATCH2 for the + * completion of the sequence. + * 4. If PBS_CLIENT_SCRATCH2 == 0xFF, the PBS sequence failed to execute + */ +int qpnp_pbs_trigger_event(struct device_node *dev_node, u8 bitmap) +{ + struct qpnp_pbs *pbs; + int rc = 0; + u16 bit_pos = 0; + u8 val, mask = 0; + + if (!dev_node) + return -EINVAL; + + if (!bitmap) { + pr_err("Invalid bitmap passed by client\n"); + return -EINVAL; + } + + pbs = get_pbs_client_node(dev_node); + if (IS_ERR_OR_NULL(pbs)) { + pr_err("Unable to find the PBS dev_node\n"); + return -EINVAL; + } + + mutex_lock(&pbs->pbs_lock); + rc = qpnp_pbs_read(pbs, pbs->base + PBS_CLIENT_SCRATCH2, &val, 1); + if (rc < 0) { + pr_err("read register %x failed rc = %d\n", + PBS_CLIENT_SCRATCH2, rc); + goto out; + } + + if (val == 0xFF) { + /* PBS error - clear SCRATCH2 register */ + rc = qpnp_pbs_write(pbs, pbs->base + PBS_CLIENT_SCRATCH2, 0, 1); + if (rc < 0) { + pr_err("Failed to clear register %x rc=%d\n", + PBS_CLIENT_SCRATCH2, rc); + goto out; + } + } + + for (bit_pos = 0; bit_pos < 8; bit_pos++) { + if (bitmap & BIT(bit_pos)) { + /* + * Clear the PBS sequence bit position in + * PBS_CLIENT_SCRATCH2 mask register. + */ + rc = qpnp_pbs_masked_write(pbs, pbs->base + + PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0); + if (rc < 0) { + pr_err("Failed to clear %x reg bit rc=%d\n", + PBS_CLIENT_SCRATCH2, rc); + goto error; + } + + /* + * Set the PBS sequence bit position in + * PBS_CLIENT_SCRATCH1 register. + */ + val = mask = BIT(bit_pos); + rc = qpnp_pbs_masked_write(pbs, pbs->base + + PBS_CLIENT_SCRATCH1, mask, val); + if (rc < 0) { + pr_err("Failed to set %x reg bit rc=%d\n", + PBS_CLIENT_SCRATCH1, rc); + goto error; + } + + /* Initiate the SW trigger */ + val = mask = PBS_CLIENT_SW_TRIG_BIT; + rc = qpnp_pbs_masked_write(pbs, pbs->base + + PBS_CLIENT_TRIG_CTL, mask, val); + if (rc < 0) { + pr_err("Failed to write register %x rc=%d\n", + PBS_CLIENT_TRIG_CTL, rc); + goto error; + } + + rc = qpnp_pbs_wait_for_ack(pbs, bit_pos); + if (rc < 0) { + pr_err("Error during wait_for_ack\n"); + goto error; + } + + /* + * Clear the PBS sequence bit position in + * PBS_CLIENT_SCRATCH1 register. + */ + rc = qpnp_pbs_masked_write(pbs, pbs->base + + PBS_CLIENT_SCRATCH1, BIT(bit_pos), 0); + if (rc < 0) { + pr_err("Failed to clear %x reg bit rc=%d\n", + PBS_CLIENT_SCRATCH1, rc); + goto error; + } + + /* + * Clear the PBS sequence bit position in + * PBS_CLIENT_SCRATCH2 mask register. + */ + rc = qpnp_pbs_masked_write(pbs, pbs->base + + PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0); + if (rc < 0) { + pr_err("Failed to clear %x reg bit rc=%d\n", + PBS_CLIENT_SCRATCH2, rc); + goto error; + } + + } + } + +error: + /* Clear all the requested bitmap */ + rc = qpnp_pbs_masked_write(pbs, pbs->base + PBS_CLIENT_SCRATCH1, + bitmap, 0); + if (rc < 0) + pr_err("Failed to clear %x reg bit rc=%d\n", + PBS_CLIENT_SCRATCH1, rc); +out: + mutex_unlock(&pbs->pbs_lock); + + return rc; +} +EXPORT_SYMBOL(qpnp_pbs_trigger_event); + +static int qpnp_pbs_probe(struct platform_device *pdev) +{ + int rc = 0; + u32 val = 0; + struct qpnp_pbs *pbs; + + pbs = devm_kzalloc(&pdev->dev, sizeof(*pbs), GFP_KERNEL); + if (!pbs) + return -ENOMEM; + + pbs->pdev = pdev; + pbs->dev = &pdev->dev; + pbs->dev_node = pdev->dev.of_node; + pbs->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!pbs->regmap) { + dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + + rc = of_property_read_u32(pdev->dev.of_node, "reg", &val); + if (rc < 0) { + dev_err(&pdev->dev, + "Couldn't find reg in node = %s rc = %d\n", + pdev->dev.of_node->full_name, rc); + return rc; + } + + pbs->base = val; + mutex_init(&pbs->pbs_lock); + + dev_set_drvdata(&pdev->dev, pbs); + + mutex_lock(&pbs_list_lock); + list_add(&pbs->link, &pbs_dev_list); + mutex_unlock(&pbs_list_lock); + + return 0; +} + +static const struct of_device_id qpnp_pbs_match_table[] = { + { .compatible = QPNP_PBS_DEV_NAME }, + {} +}; + +static struct platform_driver qpnp_pbs_driver = { + .driver = { + .name = QPNP_PBS_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = qpnp_pbs_match_table, + }, + .probe = qpnp_pbs_probe, +}; + +static int __init qpnp_pbs_init(void) +{ + return platform_driver_register(&qpnp_pbs_driver); +} +arch_initcall(qpnp_pbs_init); + +static void __exit qpnp_pbs_exit(void) +{ + return platform_driver_unregister(&qpnp_pbs_driver); +} +module_exit(qpnp_pbs_exit); + +MODULE_DESCRIPTION("QPNP PBS DRIVER"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" QPNP_PBS_DEV_NAME); diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 2b708732760f..8581ed587ead 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, 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 @@ -111,6 +111,7 @@ static void service_locator_svc_arrive(struct work_struct *work) qmi_handle_create(service_locator_clnt_notify, NULL); if (!service_locator.clnt_handle) { service_locator.clnt_handle = NULL; + complete_all(&service_locator.service_available); mutex_unlock(&service_locator.service_mutex); pr_err("Service locator QMI client handle alloc failed!\n"); return; @@ -123,6 +124,7 @@ static void service_locator_svc_arrive(struct work_struct *work) if (rc) { qmi_handle_destroy(service_locator.clnt_handle); service_locator.clnt_handle = NULL; + complete_all(&service_locator.service_available); mutex_unlock(&service_locator.service_mutex); pr_err("Unable to connnect to service rc:%d\n", rc); return; @@ -138,6 +140,7 @@ static void service_locator_svc_exit(struct work_struct *work) mutex_lock(&service_locator.service_mutex); qmi_handle_destroy(service_locator.clnt_handle); service_locator.clnt_handle = NULL; + complete_all(&service_locator.service_available); mutex_unlock(&service_locator.service_mutex); pr_info("Connection with service locator lost\n"); } diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 2f08a6c9d476..e46edc83430c 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -40,7 +40,6 @@ MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data"); static struct workqueue_struct *ipa_usb_wq; -static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis); static void ipa_disconnect_handler(struct gsi_data_port *d_port); static int gsi_ctrl_send_notification(struct f_gsi *gsi); static int gsi_alloc_trb_buffer(struct f_gsi *gsi); @@ -48,6 +47,20 @@ static void gsi_free_trb_buffer(struct f_gsi *gsi); static struct gsi_ctrl_pkt *gsi_ctrl_pkt_alloc(unsigned len, gfp_t flags); static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt); +static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f) +{ + bool remote_wakeup_allowed; + + if (f->config->cdev->gadget->speed == USB_SPEED_SUPER) + remote_wakeup_allowed = f->func_wakeup_allowed; + else + remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; + + log_event_dbg("%s: remote_wakeup_allowed:%s", __func__, + remote_wakeup_allowed ? "true" : "false"); + return remote_wakeup_allowed; +} + void post_event(struct gsi_data_port *port, u8 event) { unsigned long flags; @@ -477,16 +490,22 @@ static void ipa_disconnect_handler(struct gsi_data_port *d_port) log_event_dbg("%s: EP Disable for data", __func__); - /* Block doorbell to GSI to avoid USB wrapper from - * ringing doorbell in case IPA clocks are OFF - */ - usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, + if (gsi->d_port.in_ep) { + /* + * Block doorbell to GSI to avoid USB wrapper from + * ringing doorbell in case IPA clocks are OFF. + */ + usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, GSI_EP_OP_SET_CLR_BLOCK_DBL); + gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc; + usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE); + } - usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE); - - if (gsi->d_port.out_ep) + if (gsi->d_port.out_ep) { + gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc; usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_DISABLE); + } + gsi->d_port.net_ready_trigger = false; } @@ -523,19 +542,21 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port) int ret = 0; bool block_db, f_suspend; struct f_gsi *gsi = d_port_to_gsi(d_port); + struct usb_function *f = &gsi->function; + + f_suspend = f->func_wakeup_allowed; + log_event_dbg("%s: f_suspend:%d", __func__, f_suspend); - f_suspend = gsi->function.func_wakeup_allowed; if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend, GSI_EP_OP_CHECK_FOR_SUSPEND)) { ret = -EFAULT; goto done; } - log_event_dbg("%s: Calling xdci_suspend", __func__); + log_event_dbg("%s: Calling xdci_suspend", __func__); ret = ipa_usb_xdci_suspend(gsi->d_port.out_channel_handle, gsi->d_port.in_channel_handle, gsi->prot_id, - true); - + usb_gsi_remote_wakeup_allowed(f)); if (!ret) { d_port->sm_state = STATE_SUSPENDED; log_event_dbg("%s: STATE SUSPENDED", __func__); @@ -553,10 +574,8 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port) log_event_err("%s: Error %d for %d", __func__, ret, gsi->prot_id); } - - log_event_dbg("%s: xdci_suspend ret %d", __func__, ret); - done: + log_event_dbg("%s: xdci_suspend ret %d", __func__, ret); return ret; } @@ -591,7 +610,6 @@ static void ipa_work_handler(struct work_struct *w) struct device *dev; struct device *gad_dev; struct f_gsi *gsi; - bool block_db; event = read_event(d_port); @@ -653,6 +671,29 @@ static void ipa_work_handler(struct work_struct *w) __func__); break; } + + /* + * Update desc and reconfigure USB GSI OUT and IN + * endpoint for RNDIS Adaptor enable case. + */ + if (d_port->out_ep && !d_port->out_ep->desc && + gsi->out_ep_desc_backup) { + d_port->out_ep->desc = gsi->out_ep_desc_backup; + d_port->out_ep->ep_intr_num = 1; + log_event_dbg("%s: OUT ep_op_config", __func__); + usb_gsi_ep_op(d_port->out_ep, + &d_port->out_request, GSI_EP_OP_CONFIG); + } + + if (d_port->in_ep && !d_port->in_ep->desc && + gsi->in_ep_desc_backup) { + d_port->in_ep->desc = gsi->in_ep_desc_backup; + d_port->in_ep->ep_intr_num = 2; + log_event_dbg("%s: IN ep_op_config", __func__); + usb_gsi_ep_op(d_port->in_ep, + &d_port->in_request, GSI_EP_OP_CONFIG); + } + ipa_connect_channels(d_port); ipa_data_path_enable(d_port); d_port->sm_state = STATE_CONNECTED; @@ -714,15 +755,7 @@ static void ipa_work_handler(struct work_struct *w) if (event == EVT_HOST_NRDY) { log_event_dbg("%s: ST_CON_HOST_NRDY\n", __func__); - block_db = true; - /* stop USB ringing doorbell to GSI(OUT_EP) */ - usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, - GSI_EP_OP_SET_CLR_BLOCK_DBL); - gsi_rndis_ipa_reset_trigger(gsi); - usb_gsi_ep_op(d_port->in_ep, NULL, - GSI_EP_OP_ENDXFER); - usb_gsi_ep_op(d_port->out_ep, NULL, - GSI_EP_OP_ENDXFER); + ipa_disconnect_handler(d_port); } ipa_disconnect_work_handler(d_port); @@ -1086,9 +1119,9 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf, list_add_tail(&cpkt->list, &c_port->cpkt_resp_q); spin_unlock_irqrestore(&c_port->lock, flags); - ret = gsi_ctrl_send_notification(gsi); + if (!gsi_ctrl_send_notification(gsi)) + c_port->modem_to_host++; - c_port->modem_to_host++; log_event_dbg("Exit %zu", count); return ret ? ret : count; @@ -1345,26 +1378,6 @@ static void gsi_rndis_open(struct f_gsi *rndis) rndis_signal_connect(rndis->params); } -static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis) -{ - unsigned long flags; - - if (!rndis) { - log_event_err("%s: gsi prot ctx is %pK", __func__, rndis); - return; - } - - spin_lock_irqsave(&rndis->d_port.lock, flags); - if (!rndis) { - log_event_err("%s: No RNDIS instance", __func__); - spin_unlock_irqrestore(&rndis->d_port.lock, flags); - return; - } - - rndis->d_port.net_ready_trigger = false; - spin_unlock_irqrestore(&rndis->d_port.lock, flags); -} - void gsi_rndis_flow_ctrl_enable(bool enable, struct rndis_params *param) { struct f_gsi *rndis = param->v; @@ -1392,33 +1405,18 @@ static int queue_notification_request(struct f_gsi *gsi) { int ret; unsigned long flags; - struct usb_cdc_notification *event; - struct gsi_ctrl_pkt *cpkt; ret = usb_func_ep_queue(&gsi->function, gsi->c_port.notify, gsi->c_port.notify_req, GFP_ATOMIC); - if (ret == -ENOTSUPP || (ret < 0 && ret != -EAGAIN)) { + if (ret < 0) { spin_lock_irqsave(&gsi->c_port.lock, flags); gsi->c_port.notify_req_queued = false; - /* check if device disconnected while we dropped lock */ - if (atomic_read(&gsi->connected) && - !list_empty(&gsi->c_port.cpkt_resp_q)) { - cpkt = list_first_entry(&gsi->c_port.cpkt_resp_q, - struct gsi_ctrl_pkt, list); - list_del(&cpkt->list); - log_event_err("%s: drop ctrl pkt of len %d error %d", - __func__, cpkt->len, ret); - gsi_ctrl_pkt_free(cpkt); - } - gsi->c_port.cpkt_drop_cnt++; spin_unlock_irqrestore(&gsi->c_port.lock, flags); - } else { - ret = 0; - event = gsi->c_port.notify_req->buf; - log_event_dbg("%s: Queued Notify type %02x", __func__, - event->bNotificationType); } + log_event_dbg("%s: ret:%d req_queued:%d", + __func__, ret, gsi->c_port.notify_req_queued); + return ret; } @@ -2130,7 +2128,6 @@ static void gsi_suspend(struct usb_function *f) { bool block_db; struct f_gsi *gsi = func_to_gsi(f); - bool remote_wakeup_allowed; /* Check if function is already suspended in gsi_func_suspend() */ if (f->func_is_suspended) { @@ -2138,49 +2135,17 @@ static void gsi_suspend(struct usb_function *f) return; } - if (f->config->cdev->gadget->speed == USB_SPEED_SUPER) - remote_wakeup_allowed = f->func_wakeup_allowed; - else - remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; - - log_event_info("%s: remote_wakeup_allowed %d", - __func__, remote_wakeup_allowed); - - if (!remote_wakeup_allowed) { - if (gsi->prot_id == IPA_USB_RNDIS) - rndis_flow_control(gsi->params, true); - /* - * When remote wakeup is disabled, IPA 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. - */ - if (gsi->d_port.in_ep) - gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc; - if (gsi->d_port.out_ep) - gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc; - - ipa_disconnect_handler(&gsi->d_port); - - post_event(&gsi->d_port, EVT_DISCONNECTED); - queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); - log_event_dbg("%s: Disconnecting", __func__); - } else { - block_db = true; - usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db, - GSI_EP_OP_SET_CLR_BLOCK_DBL); - post_event(&gsi->d_port, EVT_SUSPEND); - queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); - } - + block_db = true; + usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db, + GSI_EP_OP_SET_CLR_BLOCK_DBL); + post_event(&gsi->d_port, EVT_SUSPEND); + queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); log_event_dbg("gsi suspended"); } static void gsi_resume(struct usb_function *f) { struct f_gsi *gsi = func_to_gsi(f); - bool remote_wakeup_allowed; struct usb_composite_dev *cdev = f->config->cdev; log_event_dbg("%s", __func__); @@ -2193,49 +2158,24 @@ static void gsi_resume(struct usb_function *f) f->func_is_suspended) return; - if (f->config->cdev->gadget->speed == USB_SPEED_SUPER) - remote_wakeup_allowed = f->func_wakeup_allowed; - else - remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; - if (gsi->c_port.notify && !gsi->c_port.notify->desc) config_ep_by_speed(cdev->gadget, f, gsi->c_port.notify); /* Check any pending cpkt, and queue immediately on resume */ gsi_ctrl_send_notification(gsi); - if (!remote_wakeup_allowed) { - - /* Configure EPs for GSI */ - if (gsi->d_port.out_ep) { - gsi->d_port.out_ep->desc = gsi->out_ep_desc_backup; - gsi->d_port.out_ep->ep_intr_num = 1; - usb_gsi_ep_op(gsi->d_port.out_ep, - &gsi->d_port.out_request, GSI_EP_OP_CONFIG); - } - gsi->d_port.in_ep->desc = gsi->in_ep_desc_backup; - if (gsi->prot_id != IPA_USB_DIAG) - gsi->d_port.in_ep->ep_intr_num = 2; - else - gsi->d_port.in_ep->ep_intr_num = 3; - - usb_gsi_ep_op(gsi->d_port.in_ep, &gsi->d_port.in_request, - GSI_EP_OP_CONFIG); - post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS); - - /* - * Linux host does not send RNDIS_MSG_INIT or non-zero - * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume. - * Trigger state machine explicitly on resume. - */ - if (gsi->prot_id == IPA_USB_RNDIS) - rndis_flow_control(gsi->params, false); - } else - post_event(&gsi->d_port, EVT_RESUMED); + /* + * Linux host does not send RNDIS_MSG_INIT or non-zero + * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume. + * Trigger state machine explicitly on resume. + */ + if (gsi->prot_id == IPA_USB_RNDIS && + !usb_gsi_remote_wakeup_allowed(f)) + rndis_flow_control(gsi->params, false); + post_event(&gsi->d_port, EVT_RESUMED); queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); - log_event_dbg("%s: completed", __func__); } @@ -3132,7 +3072,7 @@ MODULE_DESCRIPTION("GSI function driver"); static int fgsi_init(void) { ipa_usb_wq = alloc_workqueue("k_ipa_usb", - WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1); if (!ipa_usb_wq) { log_event_err("Failed to create workqueue for IPA"); return -ENOMEM; diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index bf4dc39f57ee..d9a4bd91f3eb 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -27,6 +27,7 @@ #include <linux/msm-bus.h> #include <linux/file.h> #include <linux/dma-direction.h> +#include <soc/qcom/cx_ipeak.h> #include "mdss_panel.h" @@ -535,6 +536,7 @@ struct mdss_data_type { u32 sec_cam_en; u32 sec_session_cnt; wait_queue_head_t secure_waitq; + struct cx_ipeak_client *mdss_cx_ipeak; }; extern struct mdss_data_type *mdss_res; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 21728c5dee45..3cadfa4ecef7 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1173,6 +1173,31 @@ irqreturn_t mdss_mdp_isr(int irq, void *ptr) return IRQ_HANDLED; } +static void mdss_mdp_cxipeak_vote(bool set_vote, unsigned long new_rate, + unsigned long prev_rate) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + int ret = 0; + + if (!mdata->mdss_cx_ipeak) + return; + + /* fmax threshold for mdp in sdm660 is max MDP clk */ + if (set_vote) { + if ((new_rate >= mdata->max_mdp_clk_rate) && + (prev_rate < mdata->max_mdp_clk_rate)) + ret = cx_ipeak_update(mdata->mdss_cx_ipeak, true); + } else { + if ((new_rate < mdata->max_mdp_clk_rate) && + (prev_rate >= mdata->max_mdp_clk_rate)) + ret = cx_ipeak_update(mdata->mdss_cx_ipeak, false); + } + if (ret) { + pr_err("cxipeak api fail ret:%d set_vote :%d new_rate:%lu prev_rate:%lu\n", + ret, (int)set_vote, new_rate, prev_rate); + } +} + static int mdss_mdp_clk_update(u32 clk_idx, u32 enable) { int ret = -ENODEV; @@ -1234,7 +1259,7 @@ void mdss_mdp_set_clk_rate(unsigned long rate, bool locked) struct mdss_data_type *mdata = mdss_res; unsigned long clk_rate; struct clk *clk = mdss_mdp_get_clk(MDSS_CLK_MDP_CORE); - unsigned long min_clk_rate; + unsigned long min_clk_rate, curr_clk_rate; min_clk_rate = max(rate, mdata->perf_tune.min_mdp_clk); @@ -1246,15 +1271,20 @@ void mdss_mdp_set_clk_rate(unsigned long rate, bool locked) clk_rate = clk_round_rate(clk, min_clk_rate); else clk_rate = mdata->max_mdp_clk_rate; + + curr_clk_rate = clk_get_rate(clk); if (IS_ERR_VALUE(clk_rate)) { pr_err("unable to round rate err=%ld\n", clk_rate); - } else if (clk_rate != clk_get_rate(clk)) { - + } else if (clk_rate != curr_clk_rate) { + mdss_mdp_cxipeak_vote(true, clk_rate, curr_clk_rate); mdata->mdp_clk_rate = clk_rate; - if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate))) + if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate))) { pr_err("clk_set_rate failed\n"); - else + } else { + mdss_mdp_cxipeak_vote(false, clk_rate, + curr_clk_rate); pr_debug("mdp clk rate=%lu\n", clk_rate); + } } if (!locked) mutex_unlock(&mdp_clk_lock); @@ -4538,6 +4568,10 @@ static int mdss_mdp_parse_dt_misc(struct platform_device *pdev) pr_debug("max pipe width not specified. Using default value\n"); mdata->max_pipe_width = DEFAULT_MDP_PIPE_WIDTH; } + + if (of_find_property(pdev->dev.of_node, "qcom,mdss-cx-ipeak", NULL)) + mdata->mdss_cx_ipeak = cx_ipeak_register(pdev->dev.of_node, + "qcom,mdss-cx-ipeak"); return 0; } @@ -5523,6 +5557,8 @@ static int mdss_mdp_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); mdss_mdp_pp_term(&pdev->dev); mdss_mdp_bus_scale_unregister(mdata); + if (mdata->mdss_cx_ipeak) + cx_ipeak_unregister(mdata->mdss_cx_ipeak); mdss_debugfs_remove(mdata); if (mdata->regulator_notif_register) regulator_unregister_notifier(mdata->fs, &(mdata->gdsc_cb)); diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 5e98de043e55..36a866685f21 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -1842,7 +1842,7 @@ int mdss_mdp_calib_mode(struct msm_fb_data_type *mfd, int mdss_mdp_pipe_handoff(struct mdss_mdp_pipe *pipe); int mdss_mdp_smp_handoff(struct mdss_data_type *mdata); struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer, - u32 type, struct mdss_mdp_pipe *left_blend_pipe); + u32 off, u32 type, struct mdss_mdp_pipe *left_blend_pipe); struct mdss_mdp_pipe *mdss_mdp_pipe_get(u32 ndx, enum mdss_mdp_pipe_rect rect_num); struct mdss_mdp_pipe *mdss_mdp_pipe_search(struct mdss_data_type *mdata, diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index da08917d334b..5d80c80ebcef 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -609,6 +609,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, bool is_vig_needed = false; u32 left_lm_w = left_lm_w_from_mfd(mfd); u32 flags = 0; + u32 off = 0; if (mdp5_data->ctl == NULL) return -ENODEV; @@ -692,18 +693,29 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, break; case PIPE_TYPE_AUTO: default: - if (req->flags & MDP_OV_PIPE_FORCE_DMA) + if (req->flags & MDP_OV_PIPE_FORCE_DMA) { pipe_type = MDSS_MDP_PIPE_TYPE_DMA; - else if (fmt->is_yuv || + /* + * For paths using legacy API's for pipe + * allocation, use offset of 2 for allocating + * right pipe for pipe type DMA. This is + * because from SDM 3.x.x. onwards one DMA + * pipe has two instances for multirect. + */ + off = (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) + ? 2 : 0; + } else if (fmt->is_yuv || (req->flags & MDP_OV_PIPE_SHARE) || - is_vig_needed) + is_vig_needed) { pipe_type = MDSS_MDP_PIPE_TYPE_VIG; - else + } else { pipe_type = MDSS_MDP_PIPE_TYPE_RGB; + } break; } - pipe = mdss_mdp_pipe_alloc(mixer, pipe_type, left_blend_pipe); + pipe = mdss_mdp_pipe_alloc(mixer, off, + pipe_type, left_blend_pipe); /* RGB pipes can be used instead of DMA */ if (IS_ERR_OR_NULL(pipe) && @@ -712,7 +724,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, pr_debug("giving RGB pipe for fb%d. flags:0x%x\n", mfd->index, req->flags); pipe_type = MDSS_MDP_PIPE_TYPE_RGB; - pipe = mdss_mdp_pipe_alloc(mixer, pipe_type, + pipe = mdss_mdp_pipe_alloc(mixer, off, pipe_type, left_blend_pipe); } @@ -723,7 +735,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, pr_debug("giving ViG pipe for fb%d. flags:0x%x\n", mfd->index, req->flags); pipe_type = MDSS_MDP_PIPE_TYPE_VIG; - pipe = mdss_mdp_pipe_alloc(mixer, pipe_type, + pipe = mdss_mdp_pipe_alloc(mixer, off, pipe_type, left_blend_pipe); } diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c index 965a6533dfcb..8d7bd60318ad 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c @@ -1250,11 +1250,12 @@ cursor_done: } struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer, - u32 type, struct mdss_mdp_pipe *left_blend_pipe) + u32 off, u32 type, struct mdss_mdp_pipe *left_blend_pipe) { struct mdss_mdp_pipe *pipe; + mutex_lock(&mdss_mdp_sspp_lock); - pipe = mdss_mdp_pipe_init(mixer, type, 0, left_blend_pipe); + pipe = mdss_mdp_pipe_init(mixer, type, off, left_blend_pipe); mutex_unlock(&mdss_mdp_sspp_lock); return pipe; } diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 68cfe76e8652..5991485cdea4 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -3,11 +3,32 @@ #include <uapi/linux/msm_kgsl.h> +#ifdef CONFIG_QCOM_KGSL /* Limits mitigations APIs */ void *kgsl_pwr_limits_add(enum kgsl_deviceid id); void kgsl_pwr_limits_del(void *limit); int kgsl_pwr_limits_set_freq(void *limit, unsigned int freq); void kgsl_pwr_limits_set_default(void *limit); unsigned int kgsl_pwr_limits_get_freq(enum kgsl_deviceid id); +#else +static inline void *kgsl_pwr_limits_add(enum kgsl_deviceid id) +{ + return NULL; +} + +static inline void kgsl_pwr_limits_del(void *limit) { } + +static inline int kgsl_pwr_limits_set_freq(void *limit, unsigned int freq) +{ + return -EINVAL; +} + +static inline void kgsl_pwr_limits_set_default(void *limit) { } + +static inline unsigned int kgsl_pwr_limits_get_freq(enum kgsl_deviceid id) +{ + return 0; +} +#endif #endif /* _MSM_KGSL_H */ diff --git a/include/linux/qpnp/qpnp-pbs.h b/include/linux/qpnp/qpnp-pbs.h new file mode 100644 index 000000000000..39497ac4b552 --- /dev/null +++ b/include/linux/qpnp/qpnp-pbs.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2017, 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 _QPNP_PBS_H +#define _QPNP_PBS_H + +#ifdef CONFIG_QPNP_PBS +int qpnp_pbs_trigger_event(struct device_node *dev_node, u8 bitmap); +#else +static inline int qpnp_pbs_trigger_event(struct device_node *dev_node, + u8 bitmap) { + return -ENODEV; +} +#endif + +#endif diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c index 46e2f7109b5a..26b7f3f26b26 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c @@ -434,8 +434,10 @@ static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = { .playback = { .stream_name = "HDMI Playback", .aif_name = "HDMI", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .channels_min = 2, .channels_max = 8, @@ -453,7 +455,9 @@ static struct snd_soc_dai_driver msm_dai_q6_display_port_rx_dai[] = { .playback = { .stream_name = "Display Port Playback", .aif_name = "DISPLAY_PORT", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, diff --git a/sound/soc/msm/sdm660-external.c b/sound/soc/msm/sdm660-external.c index 3fe327ad0442..191db6c2fa9d 100644 --- a/sound/soc/msm/sdm660-external.c +++ b/sound/soc/msm/sdm660-external.c @@ -676,7 +676,7 @@ static int msm_ext_get_spk(struct snd_kcontrol *kcontrol, static int msm_ext_set_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); pr_debug("%s()\n", __func__); if (msm_ext_spk_control == ucontrol->value.integer.value[0]) |
