diff options
111 files changed, 3321 insertions, 921 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/regulator/qpnp-oledb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt index 5d80a04c0b88..38f599ba5321 100644 --- a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt @@ -44,12 +44,12 @@ Required Node Structure Value type: <bool> Definition: Enables the voltage programming through SWIRE signal. - qcom,ext-pin-control +- qcom,ext-pin-control Usage: optional Value type: <bool> Definition: Configures the OLED module to be enabled by a external pin. - qcom,dynamic-ext-pinctl-config +- qcom,dynamic-ext-pinctl-config Usage: optional Value type: <bool> Definition: Used to dynamically enable/disable the OLEDB module @@ -57,13 +57,27 @@ Required Node Structure rail. This property is applicable only if qcom,ext-pin-ctl property is specified and it is specific to PM660A. - qcom,pbs-control +- qcom,force-pd-control + Usage: optional + Value type: <bool> + Definition: Used to enable the pull down control forcibly via SPMI by + disabling the pull down configuration done by hardware + automatically through SWIRE pulses. + +- qcom,pbs-client + Usage: optional + Value type: <phandle> + Definition: Used to send the PBS trigger to the specified PBS client. + This property is applicable only if qcom,force-pd-control + property is specified. + +- qcom,pbs-control Usage: optional Value type: <bool> Definition: PMIC PBS logic directly configures the output voltage update and pull down control. - qcom,oledb-init-voltage-mv +- qcom,oledb-init-voltage-mv Usage: optional Value type: <u32> Definition: Sets the AVDD bias voltage (in mV) when the module is @@ -71,53 +85,53 @@ Required Node Structure property is not specified. Supported values are from 5.0V to 8.1V with a step of 100mV. -qcom,oledb-default-voltage-mv +- qcom,oledb-default-voltage-mv Usage: optional Value type: <u32> Definition: Sets the default AVDD bias voltage (in mV) before module enable. Supported values are from 5.0V to 8.1V with the step of 100mV. -qcom,bias-gen-warmup-delay-ns +- qcom,bias-gen-warmup-delay-ns Usage: optional Value type: <u32> Definition: Bias generator warm-up time (ns). Supported values are 6700, 13300, 267000, 534000. -qcom,peak-curr-limit-ma +- qcom,peak-curr-limit-ma Usage: optional Value type: <u32> Definition: Peak current limit (in mA). Supported values are 115, 265, 415, 570, 720, 870, 1020, 1170. -qcom,pull-down-enable +- qcom,pull-down-enable Usage: optional Value type: <u32> Definition: Pull down configuration of OLEDB. 1 - Enable pull-down 0 - Disable pull-down -qcom,negative-curr-limit-enable +- qcom,negative-curr-limit-enable Usage: optional Value type: <u32> Definition: negative current limit enable/disable. 1 = enable negative current limit 0 = disable negative current limit -qcom,negative-curr-limit-ma +- qcom,negative-curr-limit-ma Usage: optional Value type: <u32> Definition: Negative current limit (in mA). Supported values are 170, 300, 420, 550. -qcom,enable-short-circuit +- qcom,enable-short-circuit Usage: optional Value type: <u32> Definition: Short circuit protection enable/disable. 1 = enable short circuit protection 0 = disable short circuit protection -qcom,short-circuit-dbnc-time +- qcom,short-circuit-dbnc-time usage: optional Value type: <u32> Definitioan: Short circuit debounce time (in Fsw). Supported @@ -126,26 +140,26 @@ qcom,short-circuit-dbnc-time Fast precharge properties: ------------------------- -qcom,fast-precharge-ppulse-enable +- qcom,fast-precharge-ppulse-enable usage: optional Value type: <u32> Definitioan: Fast precharge pfet pulsing enable/disable. 1 = enable fast precharge pfet pulsing 0 = disable fast precharge pfet pulsing -qcom,precharge-debounce-time-ms +- qcom,precharge-debounce-time-ms usage: optional Value type: <u32> Definitioan: Fast precharge debounce time (in ms). Supported values are 1, 2, 4, 8. -qcom,precharge-pulse-period-us +- qcom,precharge-pulse-period-us usage: optional Value type: <u32> Definitioan: Fast precharge pulse period (in us). Supported values are 3, 6, 9, 12. -qcom,precharge-pulse-on-time-us +- qcom,precharge-pulse-on-time-us usage: optional Value type: <u32> Definitioan: Fast precharge pulse on time (in ns). Supported @@ -154,20 +168,20 @@ qcom,precharge-pulse-on-time-us Pulse Skip Modulation (PSM) properties: -------------------------------------- -qcom,psm-enable +- qcom,psm-enable Usage: optional Value type: <u32> Definition: Pulse Skip Modulation mode. 1 - Enable PSM mode 0 - Disable PSM mode -qcom,psm-hys-mv +- qcom,psm-hys-mv Usage: optional Value type: <u32> Definition: PSM hysterysis voltage (in mV). Supported values are 13mV and 26mV. -qcom,psm-vref-mv +- qcom,psm-vref-mv Usage: optional Value type: <u32> Definition: Reference voltage(in mV) control for PSM comparator. @@ -177,26 +191,26 @@ qcom,psm-vref-mv Pulse Frequency Modulation (PFM) properties: ------------------------------------------- -qcom,pfm-enable +- qcom,pfm-enable Usage: optional Value type: <u32> Definition: Pulse Frequency Modulation mode. 1 - Enable PFM mode 0 - Disable PFM mode -qcom,pfm-hys-mv +- qcom,pfm-hys-mv Usage: optional Value type: <u32> Definition: PFM hysterysis voltage (in mV). Supported values are 13mV and 26mV. -qcom,pfm-curr-limit-ma +- qcom,pfm-curr-limit-ma Usage: optional Value type: <u32> Definition: PFM current limit (in mA). Supported values are 130, 200, 270, 340. -qcom,pfm-off-time-ns +- qcom,pfm-off-time-ns Usage: optional Value type: <u32> Definition: NFET off time at PFM (in ns). 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/msm-pm660l.dtsi b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi index 20016c7ac6ec..f39cc96ea9a6 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi @@ -26,6 +26,11 @@ reg = <0x100 0x100>; }; + pm660l_pbs: qcom,pbs@7300 { + compatible = "qcom,qpnp-pbs"; + reg = <0x7300 0x100>; + }; + qcom,power-on@800 { compatible = "qcom,qpnp-power-on"; reg = <0x800 0x100>; 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-sde-display.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi index 6cef416eb5b0..6098a96db206 100644 --- a/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi @@ -18,10 +18,20 @@ label = "wb_display"; }; + msm_ext_disp: qcom,msm_ext_disp { + compatible = "qcom,msm-ext-disp"; + + ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx { + compatible = "qcom,msm-ext-disp-audio-codec-rx"; + qcom,msm_ext_disp = <&msm_ext_disp>; + }; + }; + sde_hdmi: qcom,hdmi-display { compatible = "qcom,hdmi-display"; label = "sde_hdmi"; qcom,display-type = "secondary"; + qcom,msm_ext_disp = <&msm_ext_disp>; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi index 6db6ec2c3e92..ec70d0367160 100644 --- a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi @@ -193,8 +193,6 @@ qcom,enable-load = <0>; qcom,disable-load = <0>; - qcom,msm_ext_disp = <&msm_ext_disp>; - clocks = <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_mdss_ahb_clk>, <&clock_mmss clk_mmss_mdss_hdmi_clk>, 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 a53094c2e125..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 >, @@ -1767,9 +1767,9 @@ reg = <0x10 8>; }; - dload_type@18 { + dload_type@1c { compatible = "qcom,msm-imem-dload-type"; - reg = <0x18 4>; + reg = <0x1c 4>; }; restart_reason@65c { 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 9ba63be179ea..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 { @@ -1991,9 +2001,9 @@ reg = <0x10 8>; }; - dload_type@18 { + dload_type@1c { compatible = "qcom,msm-imem-dload-type"; - reg = <0x18 4>; + reg = <0x1c 4>; }; restart_reason@65c { diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig index cb20c31ccf1b..309d7a802d07 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 @@ -641,4 +642,8 @@ CONFIG_CRYPTO_DEV_QCEDEV=y CONFIG_CRYPTO_DEV_OTA_CRYPTO=y CONFIG_CRYPTO_DEV_QCOM_ICE=y CONFIG_SYSTEM_TRUSTED_KEYS="verity.x509.pem" +CONFIG_ARM_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM_NEON=y +CONFIG_CRYPTO_AES_ARM_BS=y +CONFIG_CRYPTO_AES_ARM_CE=y CONFIG_XZ_DEC=y diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig index 37ace58ef8d8..a6a831aba9dc 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 @@ -536,7 +537,6 @@ CONFIG_QCOM_WATCHDOG_V2=y CONFIG_QCOM_IRQ_HELPER=y CONFIG_QCOM_MEMORY_DUMP_V2=y CONFIG_ICNSS=y -CONFIG_ICNSS_DEBUG=y CONFIG_MSM_GLADIATOR_ERP_V2=y CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y CONFIG_MSM_GLADIATOR_HANG_DETECT=y @@ -681,4 +681,8 @@ CONFIG_CRYPTO_DEV_QCEDEV=y CONFIG_CRYPTO_DEV_OTA_CRYPTO=y CONFIG_CRYPTO_DEV_QCOM_ICE=y CONFIG_SYSTEM_TRUSTED_KEYS="verity.x509.pem" +CONFIG_ARM_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM_NEON=y +CONFIG_CRYPTO_AES_ARM_BS=y +CONFIG_CRYPTO_AES_ARM_CE=y CONFIG_XZ_DEC=y diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig index 967edf184f1b..27055fc4b9d5 100644 --- a/arch/arm64/configs/msmcortex_mediabox_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox_defconfig @@ -349,7 +349,6 @@ CONFIG_MFD_SPMI_PMIC=y CONFIG_MFD_I2C_PMIC=y CONFIG_WCD9335_CODEC=y CONFIG_WCD934X_CODEC=y -CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_RPM_SMD=y CONFIG_REGULATOR_QPNP=y @@ -408,14 +407,8 @@ CONFIG_DVB_MPQ_DEMUX=m CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX=y CONFIG_TSPP=m CONFIG_QCOM_KGSL=y -CONFIG_FB=y -CONFIG_FB_VIRTUAL=y -CONFIG_FB_MSM=y -CONFIG_FB_MSM_MDSS=y -CONFIG_FB_MSM_MDSS_WRITEBACK=y -CONFIG_FB_MSM_MDSS_HDMI_PANEL=y -CONFIG_FB_MSM_MDSS_DP_PANEL=y -CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y +CONFIG_DRM=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set @@ -497,6 +490,7 @@ CONFIG_STAGING=y CONFIG_ASHMEM=y CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y CONFIG_ION=y CONFIG_ION_MSM=y CONFIG_QPNP_REVID=y @@ -545,7 +539,6 @@ CONFIG_MSM_SYSMON_GLINK_COMM=y CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y CONFIG_MSM_GLINK_PKT=y CONFIG_MSM_SPM=y -CONFIG_QCOM_SCM=y CONFIG_QCOM_WATCHDOG_V2=y CONFIG_QCOM_IRQ_HELPER=y CONFIG_QCOM_MEMORY_DUMP_V2=y @@ -608,7 +601,6 @@ CONFIG_EXT4_FS_ICE_ENCRYPTION=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y -CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_EFIVAR_FS=y CONFIG_ECRYPT_FS=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 adfcc6c9531c..7b21409c3266 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 @@ -568,7 +569,6 @@ CONFIG_QCOM_WATCHDOG_V2=y CONFIG_QCOM_IRQ_HELPER=y CONFIG_QCOM_MEMORY_DUMP_V2=y CONFIG_ICNSS=y -CONFIG_ICNSS_DEBUG=y CONFIG_MSM_GLADIATOR_ERP_V2=y CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y CONFIG_MSM_GLADIATOR_HANG_DETECT=y @@ -687,6 +687,7 @@ CONFIG_PID_IN_CONTEXTIDR=y CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_DEBUG_RODATA=y CONFIG_FREE_PAGES_RDONLY=y +CONFIG_ARM64_STRICT_BREAK_BEFORE_MAKE=y CONFIG_CORESIGHT=y CONFIG_CORESIGHT_EVENT=y CONFIG_CORESIGHT_LINK_AND_SINK_TMC=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/clk/msm/clock-mmss-8998.c b/drivers/clk/msm/clock-mmss-8998.c index 1661878fc650..6ebb3ed6ed91 100644 --- a/drivers/clk/msm/clock-mmss-8998.c +++ b/drivers/clk/msm/clock-mmss-8998.c @@ -664,8 +664,8 @@ static struct rcg_clk byte0_clk_src = { .parent = &ext_byte0_clk_src.c, .ops = &clk_ops_byte_multiparent, .flags = CLKFLAG_NO_RATE_CACHE, - VDD_DIG_FMAX_MAP3(LOWER, 150000000, LOW, 240000000, - NOMINAL, 357140000), + VDD_DIG_FMAX_MAP3(LOWER, 131250000, LOW, 210000000, + NOMINAL, 312500000), CLK_INIT(byte0_clk_src.c), }, }; @@ -681,8 +681,8 @@ static struct rcg_clk byte1_clk_src = { .parent = &ext_byte1_clk_src.c, .ops = &clk_ops_byte_multiparent, .flags = CLKFLAG_NO_RATE_CACHE, - VDD_DIG_FMAX_MAP3(LOWER, 150000000, LOW, 240000000, - NOMINAL, 357140000), + VDD_DIG_FMAX_MAP3(LOWER, 131250000, LOW, 210000000, + NOMINAL, 312500000), CLK_INIT(byte1_clk_src.c), }, }; @@ -722,8 +722,8 @@ static struct rcg_clk pclk0_clk_src = { .parent = &ext_pclk0_clk_src.c, .ops = &clk_ops_pixel_multiparent, .flags = CLKFLAG_NO_RATE_CACHE, - VDD_DIG_FMAX_MAP3(LOWER, 184000000, LOW, 295000000, - NOMINAL, 610000000), + VDD_DIG_FMAX_MAP3(LOWER, 175000000, LOW, 280000000, + NOMINAL, 416670000), CLK_INIT(pclk0_clk_src.c), }, }; @@ -739,8 +739,8 @@ static struct rcg_clk pclk1_clk_src = { .parent = &ext_pclk1_clk_src.c, .ops = &clk_ops_pixel_multiparent, .flags = CLKFLAG_NO_RATE_CACHE, - VDD_DIG_FMAX_MAP3(LOWER, 184000000, LOW, 295000000, - NOMINAL, 610000000), + VDD_DIG_FMAX_MAP3(LOWER, 175000000, LOW, 280000000, + NOMINAL, 416670000), CLK_INIT(pclk1_clk_src.c), }, }; diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 62675198d6ac..5838545468f8 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -11,6 +11,7 @@ config DRM_MSM select TMPFS select QCOM_SCM select BACKLIGHT_CLASS_DEVICE + select MSM_EXT_DISPLAY default y help DRM/KMS driver for MSM/snapdragon. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index d81ec8918ce7..79ea5a9f90ea 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -100,7 +100,8 @@ msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \ msm_drm-$(CONFIG_DRM_SDE_HDMI) += \ hdmi-staging/sde_hdmi.o \ hdmi-staging/sde_hdmi_bridge.o \ - hdmi-staging/sde_hdmi_audio.o + hdmi-staging/sde_hdmi_audio.o \ + hdmi-staging/sde_hdmi_edid.o msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \ dsi/pll/dsi_pll_28nm.o diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 15e2d69827e7..347b78886b24 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -22,8 +22,10 @@ #include <linux/of.h> #include <linux/gpio.h> #include <linux/of_irq.h> +#include <linux/of_platform.h> #include "sde_kms.h" +#include "sde_connector.h" #include "msm_drv.h" #include "sde_hdmi.h" @@ -402,6 +404,13 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work) } connector = sde_hdmi->ctrl.ctrl->connector; + + if (sde_hdmi->connected) + sde_hdmi_get_edid(connector, sde_hdmi); + else + sde_hdmi_free_edid(sde_hdmi); + + sde_hdmi_notify_clients(connector, sde_hdmi->connected); drm_helper_hpd_irq_event(connector->dev); } @@ -431,7 +440,8 @@ static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi) hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT; hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl); - queue_work(hdmi->workq, &sde_hdmi->hpd_work); + if (!sde_hdmi->non_pluggable) + queue_work(hdmi->workq, &sde_hdmi->hpd_work); } } @@ -460,6 +470,148 @@ static irqreturn_t _sde_hdmi_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static int _sde_hdmi_audio_info_setup(struct platform_device *pdev, + struct msm_ext_disp_audio_setup_params *params) +{ + int rc = -EPERM; + struct sde_hdmi *display = NULL; + struct hdmi *hdmi = NULL; + + display = platform_get_drvdata(pdev); + + if (!display || !params) { + SDE_ERROR("invalid param(s), display %pK, params %pK\n", + display, params); + return -ENODEV; + } + + hdmi = display->ctrl.ctrl; + + if (hdmi->hdmi_mode) + rc = sde_hdmi_audio_on(hdmi, params); + + return rc; +} + +static int _sde_hdmi_get_audio_edid_blk(struct platform_device *pdev, + struct msm_ext_disp_audio_edid_blk *blk) +{ + struct sde_hdmi *display = platform_get_drvdata(pdev); + + if (!display || !blk) { + SDE_ERROR("invalid param(s), display %pK, blk %pK\n", + display, blk); + return -ENODEV; + } + + blk->audio_data_blk = display->edid.audio_data_block; + blk->audio_data_blk_size = display->edid.adb_size; + + blk->spk_alloc_data_blk = display->edid.spkr_alloc_data_block; + blk->spk_alloc_data_blk_size = display->edid.sadb_size; + + return 0; +} + +static int _sde_hdmi_get_cable_status(struct platform_device *pdev, u32 vote) +{ + struct sde_hdmi *display = NULL; + struct hdmi *hdmi = NULL; + + display = platform_get_drvdata(pdev); + + if (!display) { + SDE_ERROR("invalid param(s), display %pK\n", display); + return -ENODEV; + } + + hdmi = display->ctrl.ctrl; + + return hdmi->power_on && display->connected; +} + +static int _sde_hdmi_ext_disp_init(struct sde_hdmi *display) +{ + int rc = 0; + struct device_node *pd_np; + const char *phandle = "qcom,msm_ext_disp"; + + if (!display) { + SDE_ERROR("[%s]Invalid params\n", display->name); + return -EINVAL; + } + + display->ext_audio_data.type = EXT_DISPLAY_TYPE_HDMI; + display->ext_audio_data.pdev = display->pdev; + display->ext_audio_data.codec_ops.audio_info_setup = + _sde_hdmi_audio_info_setup; + display->ext_audio_data.codec_ops.get_audio_edid_blk = + _sde_hdmi_get_audio_edid_blk; + display->ext_audio_data.codec_ops.cable_status = + _sde_hdmi_get_cable_status; + + if (!display->pdev->dev.of_node) { + SDE_ERROR("[%s]cannot find sde_hdmi of_node\n", display->name); + return -ENODEV; + } + + pd_np = of_parse_phandle(display->pdev->dev.of_node, phandle, 0); + if (!pd_np) { + SDE_ERROR("[%s]cannot find %s device node\n", + display->name, phandle); + return -ENODEV; + } + + display->ext_pdev = of_find_device_by_node(pd_np); + if (!display->ext_pdev) { + SDE_ERROR("[%s]cannot find %s platform device\n", + display->name, phandle); + return -ENODEV; + } + + rc = msm_ext_disp_register_intf(display->ext_pdev, + &display->ext_audio_data); + if (rc) + SDE_ERROR("[%s]failed to register disp\n", display->name); + + return rc; +} + +void sde_hdmi_notify_clients(struct drm_connector *connector, + bool connected) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + int state = connected ? + EXT_DISPLAY_CABLE_CONNECT : EXT_DISPLAY_CABLE_DISCONNECT; + + if (display && display->ext_audio_data.intf_ops.hpd) { + struct hdmi *hdmi = display->ctrl.ctrl; + u32 flags = MSM_EXT_DISP_HPD_VIDEO; + + if (hdmi->hdmi_mode) + flags |= MSM_EXT_DISP_HPD_AUDIO; + + display->ext_audio_data.intf_ops.hpd(display->ext_pdev, + display->ext_audio_data.type, state, flags); + } +} + +void sde_hdmi_ack_state(struct drm_connector *connector, + enum drm_connector_status status) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + if (display) { + struct hdmi *hdmi = display->ctrl.ctrl; + + if (hdmi->hdmi_mode && display->ext_audio_data.intf_ops.notify) + display->ext_audio_data.intf_ops.notify( + display->ext_pdev, status); + } +} + void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on) { uint32_t ctrl = 0; @@ -645,13 +797,29 @@ sde_hdmi_connector_detect(struct drm_connector *connector, return status; } +int _sde_hdmi_update_modes(struct drm_connector *connector, + struct sde_hdmi *display) +{ + int rc = 0; + struct hdmi_edid_ctrl *edid_ctrl = &display->edid; + + if (edid_ctrl->edid) { + drm_mode_connector_update_edid_property(connector, + edid_ctrl->edid); + + rc = drm_add_edid_modes(connector, edid_ctrl->edid); + return rc; + } + + drm_mode_connector_update_edid_property(connector, NULL); + + return rc; +} + int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) { struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; - struct hdmi *hdmi; - struct edid *edid; struct drm_display_mode *mode, *m; - uint32_t hdmi_ctrl; int ret = 0; if (!connector || !display) { @@ -662,7 +830,6 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) SDE_DEBUG("\n"); - hdmi = hdmi_display->ctrl.ctrl; if (hdmi_display->non_pluggable) { list_for_each_entry(mode, &hdmi_display->mode_list, head) { m = drm_mode_duplicate(connector->dev, mode); @@ -675,21 +842,7 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) } ret = hdmi_display->num_of_modes; } else { - /* Read EDID */ - hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL); - hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE); - - edid = drm_get_edid(connector, hdmi->i2c); - - hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl); - - hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid); - drm_mode_connector_update_edid_property(connector, edid); - - if (edid) { - ret = drm_add_edid_modes(connector, edid); - kfree(edid); - } + ret = _sde_hdmi_update_modes(connector, display); } return ret; @@ -778,6 +931,20 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) if (rc) { SDE_ERROR("[%s]Debugfs init failed, rc=%d\n", display->name, rc); + goto debug_error; + } + + rc = _sde_hdmi_ext_disp_init(display); + if (rc) { + SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n", + display->name, rc); + goto error; + } + + rc = sde_hdmi_edid_init(display); + if (rc) { + SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n", + display->name, rc); goto error; } @@ -787,6 +954,8 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) display->drm_dev = drm; error: + (void)_sde_hdmi_debugfs_deinit(display); +debug_error: mutex_unlock(&display->display_lock); return rc; } @@ -809,6 +978,7 @@ static void sde_hdmi_unbind(struct device *dev, struct device *master, } mutex_lock(&display->display_lock); (void)_sde_hdmi_debugfs_deinit(display); + (void)sde_hdmi_edid_deinit(display); display->drm_dev = NULL; mutex_unlock(&display->display_lock); } diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 1c13d9f875f2..869d1bebf9db 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -25,6 +25,10 @@ #include <drm/drm_crtc.h> #include "hdmi.h" +#define MAX_NUMBER_ADB 5 +#define MAX_AUDIO_DATA_BLOCK_SIZE 30 +#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3 + /** * struct sde_hdmi_info - defines hdmi display properties * @display_type: Display type as defined by device tree. @@ -60,6 +64,14 @@ struct sde_hdmi_ctrl { u32 hdmi_ctrl_idx; }; +struct hdmi_edid_ctrl { + struct edid *edid; + u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE]; + int adb_size; + u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE]; + int sadb_size; +}; + /** * struct sde_hdmi - hdmi display information * @pdev: Pointer to platform device. @@ -88,6 +100,10 @@ struct sde_hdmi { struct sde_hdmi_ctrl ctrl; + struct platform_device *ext_pdev; + struct msm_ext_disp_init_data ext_audio_data; + struct hdmi_edid_ctrl edid; + bool non_pluggable; u32 num_of_modes; struct list_head mode_list; @@ -268,6 +284,61 @@ void sde_hdmi_audio_off(struct hdmi *hdmi); * Return: error code. */ int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set); + +/** + * sde_hdmi_notify_clients() - notify hdmi clients of the connection status. + * @connector: Handle to the drm_connector. + * @connected: connection status. + * + * Return: void. + */ +void sde_hdmi_notify_clients(struct drm_connector *connector, + bool connected); + +/** + * sde_hdmi_ack_state() - acknowledge the connection status. + * @connector: Handle to the drm_connector. + * @status: connection status. + * + * Return: void. + */ +void sde_hdmi_ack_state(struct drm_connector *connector, + enum drm_connector_status status); + +/** + * sde_hdmi_edid_init() - init edid structure. + * @display: Handle to the sde_hdmi. + * + * Return: error code. + */ +int sde_hdmi_edid_init(struct sde_hdmi *display); + +/** + * sde_hdmi_edid_deinit() - deinit edid structure. + * @display: Handle to the sde_hdmi. + * + * Return: error code. + */ +int sde_hdmi_edid_deinit(struct sde_hdmi *display); + +/** + * sde_hdmi_get_edid() - get edid info. + * @connector: Handle to the drm_connector. + * @display: Handle to the sde_hdmi. + * + * Return: void. + */ +void sde_hdmi_get_edid(struct drm_connector *connector, + struct sde_hdmi *display); + +/** + * sde_hdmi_free_edid() - free edid structure. + * @display: Handle to the sde_hdmi. + * + * Return: error code. + */ +int sde_hdmi_free_edid(struct sde_hdmi *display); + #else /*#ifdef CONFIG_DRM_SDE_HDMI*/ static inline u32 sde_hdmi_get_num_of_displays(void) diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index ecfff7e88689..681dca501f9b 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -18,6 +18,7 @@ #include "drm_edid.h" #include "sde_kms.h" +#include "sde_connector.h" #include "sde_hdmi.h" #include "hdmi.h" @@ -111,7 +112,6 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) if (!hdmi->power_on) { _sde_hdmi_bridge_power_on(bridge); hdmi->power_on = true; - hdmi_audio_update(hdmi); } if (phy) @@ -121,14 +121,42 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported) hdmi_hdcp_ctrl_on(hdmi->hdcp_ctrl); + + sde_hdmi_ack_state(hdmi->connector, EXT_DISPLAY_CABLE_CONNECT); +} + +static void sde_hdmi_force_update_audio(struct drm_connector *connector, + enum drm_connector_status status) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + if (display && display->non_pluggable) { + display->ext_audio_data.intf_ops.hpd(display->ext_pdev, + display->ext_audio_data.type, + status, + MSM_EXT_DISP_HPD_AUDIO); + } } static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge) { + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + + /* force update audio ops when there's no HPD event */ + sde_hdmi_force_update_audio(hdmi->connector, + EXT_DISPLAY_CABLE_CONNECT); } static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge) { + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + + /* force update audio ops when there's no HPD event */ + sde_hdmi_force_update_audio(hdmi->connector, + EXT_DISPLAY_CABLE_DISCONNECT); } static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) @@ -140,6 +168,8 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported) hdmi_hdcp_ctrl_off(hdmi->hdcp_ctrl); + sde_hdmi_audio_off(hdmi); + DRM_DEBUG("power down"); sde_hdmi_set_mode(hdmi, false); @@ -149,8 +179,9 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) if (hdmi->power_on) { _sde_hdmi_bridge_power_off(bridge); hdmi->power_on = false; - hdmi_audio_update(hdmi); } + + sde_hdmi_ack_state(hdmi->connector, EXT_DISPLAY_CABLE_DISCONNECT); } static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi, @@ -342,8 +373,6 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, _sde_hdmi_bridge_set_spd_infoframe(hdmi, mode); DRM_DEBUG("hdmi setup info frame\n"); } - - hdmi_audio_update(hdmi); } static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c new file mode 100644 index 000000000000..57c79e2aa812 --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c @@ -0,0 +1,227 @@ +/* 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. + */ + +#include <drm/drm_edid.h> + +#include "sde_kms.h" +#include "sde_hdmi.h" + +/* TODO: copy from drm_edid.c and mdss_hdmi_edid.c. remove if using ELD */ +#define DBC_START_OFFSET 4 +#define EDID_DTD_LEN 18 + +enum data_block_types { + RESERVED, + AUDIO_DATA_BLOCK, + VIDEO_DATA_BLOCK, + VENDOR_SPECIFIC_DATA_BLOCK, + SPEAKER_ALLOCATION_DATA_BLOCK, + VESA_DTC_DATA_BLOCK, + RESERVED2, + USE_EXTENDED_TAG +}; + +static u8 *_sde_hdmi_edid_find_cea_extension(struct edid *edid) +{ + u8 *edid_ext = NULL; + int i; + + /* No EDID or EDID extensions */ + if (edid == NULL || edid->extensions == 0) + return NULL; + + /* Find CEA extension */ + for (i = 0; i < edid->extensions; i++) { + edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); + if (edid_ext[0] == CEA_EXT) + break; + } + + if (i == edid->extensions) + return NULL; + + return edid_ext; +} + +static const u8 *_sde_hdmi_edid_find_block(const u8 *in_buf, u32 start_offset, + u8 type, u8 *len) +{ + /* the start of data block collection, start of Video Data Block */ + u32 offset = start_offset; + u32 dbc_offset = in_buf[2]; + + /* + * * edid buffer 1, byte 2 being 4 means no non-DTD/Data block + * collection present. + * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block + * collection present and no DTD data present. + */ + if ((dbc_offset == 0) || (dbc_offset == 4)) { + SDE_ERROR("EDID: no DTD or non-DTD data present\n"); + return NULL; + } + + while (offset < dbc_offset) { + u8 block_len = in_buf[offset] & 0x1F; + + if ((offset + block_len <= dbc_offset) && + (in_buf[offset] >> 5) == type) { + *len = block_len; + SDE_DEBUG("EDID: block=%d found @ 0x%x w/ len=%d\n", + type, offset, block_len); + + return in_buf + offset; + } + offset += 1 + block_len; + } + + return NULL; +} + +static void _sde_hdmi_extract_audio_data_blocks( + struct hdmi_edid_ctrl *edid_ctrl) +{ + u8 len = 0; + u8 adb_max = 0; + const u8 *adb = NULL; + u32 offset = DBC_START_OFFSET; + u8 *cea = NULL; + + if (!edid_ctrl) { + SDE_ERROR("invalid edid_ctrl\n"); + return; + } + + cea = _sde_hdmi_edid_find_cea_extension(edid_ctrl->edid); + if (!cea) { + SDE_DEBUG("CEA extension not found\n"); + return; + } + + edid_ctrl->adb_size = 0; + + memset(edid_ctrl->audio_data_block, 0, + sizeof(edid_ctrl->audio_data_block)); + + do { + len = 0; + adb = _sde_hdmi_edid_find_block(cea, offset, AUDIO_DATA_BLOCK, + &len); + + if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE || + adb_max >= MAX_NUMBER_ADB)) { + if (!edid_ctrl->adb_size) { + SDE_DEBUG("No/Invalid Audio Data Block\n"); + return; + } + + continue; + } + + memcpy(edid_ctrl->audio_data_block + edid_ctrl->adb_size, + adb + 1, len); + offset = (adb - cea) + 1 + len; + + edid_ctrl->adb_size += len; + adb_max++; + } while (adb); + +} + +static void _sde_hdmi_extract_speaker_allocation_data( + struct hdmi_edid_ctrl *edid_ctrl) +{ + u8 len; + const u8 *sadb = NULL; + u8 *cea = NULL; + + if (!edid_ctrl) { + SDE_ERROR("invalid edid_ctrl\n"); + return; + } + + cea = _sde_hdmi_edid_find_cea_extension(edid_ctrl->edid); + if (!cea) { + SDE_DEBUG("CEA extension not found\n"); + return; + } + + sadb = _sde_hdmi_edid_find_block(cea, DBC_START_OFFSET, + SPEAKER_ALLOCATION_DATA_BLOCK, &len); + if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)) { + SDE_DEBUG("No/Invalid Speaker Allocation Data Block\n"); + return; + } + + memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len); + edid_ctrl->sadb_size = len; + + SDE_DEBUG("EDID: speaker alloc data SP byte = %08x %s%s%s%s%s%s%s\n", + sadb[1], + (sadb[1] & BIT(0)) ? "FL/FR," : "", + (sadb[1] & BIT(1)) ? "LFE," : "", + (sadb[1] & BIT(2)) ? "FC," : "", + (sadb[1] & BIT(3)) ? "RL/RR," : "", + (sadb[1] & BIT(4)) ? "RC," : "", + (sadb[1] & BIT(5)) ? "FLC/FRC," : "", + (sadb[1] & BIT(6)) ? "RLC/RRC," : ""); +} + +int sde_hdmi_edid_init(struct sde_hdmi *display) +{ + int rc = 0; + + if (!display) { + SDE_ERROR("[%s]Invalid params\n", display->name); + return -EINVAL; + } + + memset(&display->edid, 0, sizeof(display->edid)); + + return rc; +} + +int sde_hdmi_free_edid(struct sde_hdmi *display) +{ + struct hdmi_edid_ctrl *edid_ctrl = &display->edid; + + kfree(edid_ctrl->edid); + edid_ctrl->edid = NULL; + + return 0; +} + +int sde_hdmi_edid_deinit(struct sde_hdmi *display) +{ + return sde_hdmi_free_edid(display); +} + +void sde_hdmi_get_edid(struct drm_connector *connector, + struct sde_hdmi *display) +{ + u32 hdmi_ctrl; + struct hdmi_edid_ctrl *edid_ctrl = &display->edid; + struct hdmi *hdmi = display->ctrl.ctrl; + + /* Read EDID */ + hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL); + hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE); + edid_ctrl->edid = drm_get_edid(connector, hdmi->i2c); + hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl); + + if (edid_ctrl->edid) { + hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid_ctrl->edid); + + _sde_hdmi_extract_audio_data_blocks(edid_ctrl); + _sde_hdmi_extract_speaker_allocation_data(edid_ctrl); + } +}; diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c index 42bbbdcab2c9..dc7827872276 100644 --- a/drivers/gpu/drm/msm/sde/sde_formats.c +++ b/drivers/gpu/drm/msm/sde/sde_formats.c @@ -102,169 +102,169 @@ flg, fm, np) \ static const struct sde_format sde_format_map[] = { INTERLEAVED_RGB_FMT(ARGB8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, true, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ABGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XBGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBA8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, true, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRA8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, true, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRX8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, false, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XRGB8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, false, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBX8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, false, 4, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGB888, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, + C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3, false, 3, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGR888, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3, + C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, false, 3, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGB565, 0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, + C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGR565, 0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3, + C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ARGB1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ABGR1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBA5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRA5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XRGB1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XBGR1555, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBX5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRX5551, COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ARGB4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ABGR4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBA4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRA4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, true, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XRGB4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XBGR4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBX4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRX4444, COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, false, 2, 0, SDE_FETCH_LINEAR, 1), @@ -366,13 +366,13 @@ static const struct sde_format sde_format_map[] = { PLANAR_YUV_FMT(YUV420, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C0_G_Y, C1_B_Cb, C2_R_Cr, + C2_R_Cr, C1_B_Cb, C0_G_Y, false, SDE_CHROMA_420, 1, SDE_FORMAT_FLAG_YUV, SDE_FETCH_LINEAR, 3), PLANAR_YUV_FMT(YVU420, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C0_G_Y, C2_R_Cr, C1_B_Cb, + C1_B_Cb, C2_R_Cr, C0_G_Y, false, SDE_CHROMA_420, 1, SDE_FORMAT_FLAG_YUV, SDE_FETCH_LINEAR, 3), }; @@ -384,19 +384,19 @@ static const struct sde_format sde_format_map[] = { * the data will be passed by user-space. */ static const struct sde_format sde_format_map_ubwc[] = { - INTERLEAVED_RGB_FMT(RGB565, + INTERLEAVED_RGB_FMT(BGR565, 0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, false, 2, 0, SDE_FETCH_UBWC, 2), - INTERLEAVED_RGB_FMT(RGBA8888, + INTERLEAVED_RGB_FMT(ABGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, 0, SDE_FETCH_UBWC, 2), - INTERLEAVED_RGB_FMT(RGBX8888, + INTERLEAVED_RGB_FMT(XBGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 4, 0, @@ -513,14 +513,15 @@ static int _sde_format_get_plane_sizes_ubwc( ALIGN(DIV_ROUND_UP(height / 2, uv_tile_height), 16), 4096); - } else if (fmt->base.pixel_format == DRM_FORMAT_RGBA8888 || - fmt->base.pixel_format == DRM_FORMAT_RGBX8888 || - fmt->base.pixel_format == DRM_FORMAT_RGBA1010102 || - fmt->base.pixel_format == DRM_FORMAT_RGBX1010102 || - fmt->base.pixel_format == DRM_FORMAT_RGB565) { + } else if (fmt->base.pixel_format == DRM_FORMAT_ABGR8888 || + fmt->base.pixel_format == DRM_FORMAT_XBGR8888 || + fmt->base.pixel_format == DRM_FORMAT_BGRA1010102 || + fmt->base.pixel_format == DRM_FORMAT_BGRX1010102 || + fmt->base.pixel_format == DRM_FORMAT_BGR565) { + uint32_t stride_alignment, aligned_bitstream_width; - if (fmt->base.pixel_format == DRM_FORMAT_RGB565) + if (fmt->base.pixel_format == DRM_FORMAT_BGR565) stride_alignment = 128; else stride_alignment = 64; 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/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c index 1caa673db6ff..7de43dd27ffe 100644 --- a/drivers/gpu/msm/kgsl_snapshot.c +++ b/drivers/gpu/msm/kgsl_snapshot.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 @@ -156,8 +156,10 @@ static size_t snapshot_os(struct kgsl_device *device, header->osid = KGSL_SNAPSHOT_OS_LINUX_V3; /* Get the kernel build information */ - strlcpy(header->release, utsname()->release, sizeof(header->release)); - strlcpy(header->version, utsname()->version, sizeof(header->version)); + strlcpy(header->release, init_utsname()->release, + sizeof(header->release)); + strlcpy(header->version, init_utsname()->version, + sizeof(header->version)); /* Get the Unix time for the timestamp */ header->seconds = get_seconds(); 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/hdcp.c b/drivers/misc/hdcp.c index bd21f8cca2aa..460ffc79f566 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -2103,7 +2103,8 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle) (rc == 0) && (rsp_buf->status == 0)) { pr_debug("Got Auth_Stream_Ready, nothing sent to rx\n"); - if (!hdcp_lib_enable_encryption(handle)) { + if (!handle->authenticated && + !hdcp_lib_enable_encryption(handle)) { handle->authenticated = true; cdata.cmd = HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS; 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/Kconfig b/drivers/platform/msm/Kconfig index 024c66ac8e57..66bdc593f811 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -217,4 +217,11 @@ config USB_BAM Enabling this option adds USB BAM Driver. USB BAM driver was added to supports SPS Peripheral-to-Peripheral transfers between the USB and other peripheral. + +config MSM_EXT_DISPLAY + bool "MSM External Display Driver" + help + Enabling this option adds MSM External Display Driver. + External Display driver was added to support the communication + between external display driver and its couterparts. endmenu diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index d5e87c209c21..d985aa81a3bb 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_SEEMP_CORE) += seemp_core/ obj-$(CONFIG_SSM) += ssm.o obj-$(CONFIG_USB_BAM) += usb_bam.o obj-$(CONFIG_MSM_MHI_DEV) += mhi_dev/ +obj-$(CONFIG_MSM_EXT_DISPLAY) += msm_ext_display.o 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/video/fbdev/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c index d74b1432ea71..bb1259e3cfa1 100644 --- a/drivers/video/fbdev/msm/msm_ext_display.c +++ b/drivers/platform/msm/msm_ext_display.c @@ -23,9 +23,6 @@ #include <linux/of_platform.h> #include <linux/msm_ext_display.h> -#include "mdss_hdmi_util.h" -#include "mdss_fb.h" - struct msm_ext_disp_list { struct msm_ext_disp_init_data *data; struct list_head list; @@ -48,7 +45,6 @@ struct msm_ext_disp { static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp, enum msm_ext_disp_type type, struct msm_ext_disp_init_data **data); -static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack); static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp, enum msm_ext_disp_type type, enum msm_ext_disp_cable_state state, u32 flags); @@ -103,128 +99,6 @@ end: return; } -static void msm_ext_disp_get_pdev_by_name(struct device *dev, - const char *phandle, struct platform_device **pdev) -{ - struct device_node *pd_np; - - if (!dev) { - pr_err("Invalid device\n"); - return; - } - - if (!dev->of_node) { - pr_err("Invalid of_node\n"); - return; - } - - pd_np = of_parse_phandle(dev->of_node, phandle, 0); - if (!pd_np) { - pr_err("Cannot find %s dev\n", phandle); - return; - } - - *pdev = of_find_device_by_node(pd_np); -} - -static void msm_ext_disp_get_fb_pdev(struct device *device, - struct platform_device **fb_pdev) -{ - struct msm_fb_data_type *mfd = NULL; - struct fb_info *fbi = dev_get_drvdata(device); - - if (!fbi) { - pr_err("fb_info is null\n"); - return; - } - - mfd = (struct msm_fb_data_type *)fbi->par; - - *fb_pdev = mfd->pdev; -} -static ssize_t msm_ext_disp_sysfs_wta_audio_cb(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int ack, ret = 0; - ssize_t size = strnlen(buf, PAGE_SIZE); - const char *ext_phandle = "qcom,msm_ext_disp"; - struct platform_device *ext_pdev = NULL; - const char *intf_phandle = "qcom,mdss-intf"; - struct platform_device *intf_pdev = NULL; - struct platform_device *fb_pdev = NULL; - - ret = kstrtoint(buf, 10, &ack); - if (ret) { - pr_err("kstrtoint failed. ret=%d\n", ret); - goto end; - } - - msm_ext_disp_get_fb_pdev(dev, &fb_pdev); - if (!fb_pdev) { - pr_err("failed to get fb pdev\n"); - goto end; - } - - msm_ext_disp_get_pdev_by_name(&fb_pdev->dev, intf_phandle, &intf_pdev); - if (!intf_pdev) { - pr_err("failed to get display intf pdev\n"); - goto end; - } - - msm_ext_disp_get_pdev_by_name(&intf_pdev->dev, ext_phandle, &ext_pdev); - if (!ext_pdev) { - pr_err("failed to get ext_pdev\n"); - goto end; - } - - ret = msm_ext_disp_audio_ack(ext_pdev, ack); - if (ret) - pr_err("Failed to process ack. ret=%d\n", ret); - -end: - return size; -} - -static DEVICE_ATTR(hdmi_audio_cb, S_IWUSR, NULL, - msm_ext_disp_sysfs_wta_audio_cb); - -static struct attribute *msm_ext_disp_fs_attrs[] = { - &dev_attr_hdmi_audio_cb.attr, - NULL, -}; - -static struct attribute_group msm_ext_disp_fs_attrs_group = { - .attrs = msm_ext_disp_fs_attrs, -}; - -static int msm_ext_disp_sysfs_create(struct msm_ext_disp_init_data *data) -{ - int ret = 0; - - if (!data || !data->kobj) { - pr_err("Invalid params\n"); - ret = -EINVAL; - goto end; - } - - ret = sysfs_create_group(data->kobj, &msm_ext_disp_fs_attrs_group); - if (ret) - pr_err("Failed, ret=%d\n", ret); - -end: - return ret; -} - -static void msm_ext_disp_sysfs_remove(struct msm_ext_disp_init_data *data) -{ - if (!data || !data->kobj) { - pr_err("Invalid params\n"); - return; - } - - sysfs_remove_group(data->kobj, &msm_ext_disp_fs_attrs_group); -} - static const char *msm_ext_disp_name(enum msm_ext_disp_type type) { switch (type) { @@ -293,31 +167,6 @@ end: return ret; } -static void msm_ext_disp_remove_intf_data(struct msm_ext_disp *ext_disp, - enum msm_ext_disp_type type) -{ - struct msm_ext_disp_list *node; - struct list_head *position = NULL; - struct list_head *temp = NULL; - - if (!ext_disp) { - pr_err("Invalid params\n"); - return; - } - - list_for_each_safe(position, temp, &ext_disp->display_list) { - node = list_entry(position, struct msm_ext_disp_list, list); - if (node->data->type == type) { - msm_ext_disp_sysfs_remove(node->data); - list_del(&node->list); - pr_debug("Removed display (%s)\n", - msm_ext_disp_name(type)); - kfree(node); - break; - } - } -} - static int msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp, enum msm_ext_disp_cable_state new_state) { @@ -661,6 +510,46 @@ static void msm_ext_disp_teardown_done(struct platform_device *pdev) complete_all(&ext_disp->hpd_comp); } +static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack) +{ + u32 ack_hpd; + int ret = 0; + struct msm_ext_disp *ext_disp = NULL; + + if (!pdev) { + pr_err("Invalid platform device\n"); + return -EINVAL; + } + + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("Invalid drvdata\n"); + return -EINVAL; + } + + if (ack & AUDIO_ACK_SET_ENABLE) { + ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ? + true : false; + + pr_debug("audio ack feature %s\n", + ext_disp->ack_enabled ? "enabled" : "disabled"); + goto end; + } + + if (!ext_disp->ack_enabled) + goto end; + + ack_hpd = ack & AUDIO_ACK_CONNECT; + + pr_debug("%s acknowledging audio (%d)\n", + msm_ext_disp_name(ext_disp->current_disp), ack_hpd); + + if (!ext_disp->audio_session_on) + complete_all(&ext_disp->hpd_comp); +end: + return ret; +} + static int msm_ext_disp_get_intf_id(struct platform_device *pdev) { int ret = 0; @@ -710,12 +599,14 @@ static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp, ops->cable_status = msm_ext_disp_cable_status; ops->get_intf_id = msm_ext_disp_get_intf_id; ops->teardown_done = msm_ext_disp_teardown_done; + ops->acknowledge = msm_ext_disp_audio_ack; } else { ops->audio_info_setup = NULL; ops->get_audio_edid_blk = NULL; ops->cable_status = NULL; ops->get_intf_id = NULL; ops->teardown_done = NULL; + ops->acknowledge = NULL; } end: return ret; @@ -755,46 +646,6 @@ end: return ret; } -static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack) -{ - u32 ack_hpd; - int ret = 0; - struct msm_ext_disp *ext_disp = NULL; - - if (!pdev) { - pr_err("Invalid platform device\n"); - return -EINVAL; - } - - ext_disp = platform_get_drvdata(pdev); - if (!ext_disp) { - pr_err("Invalid drvdata\n"); - return -EINVAL; - } - - if (ack & AUDIO_ACK_SET_ENABLE) { - ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ? - true : false; - - pr_debug("audio ack feature %s\n", - ext_disp->ack_enabled ? "enabled" : "disabled"); - goto end; - } - - if (!ext_disp->ack_enabled) - goto end; - - ack_hpd = ack & AUDIO_ACK_CONNECT; - - pr_debug("%s acknowledging audio (%d)\n", - msm_ext_disp_name(ext_disp->current_disp), ack_hpd); - - if (!ext_disp->audio_session_on) - complete_all(&ext_disp->hpd_comp); -end: - return ret; -} - int msm_hdmi_register_audio_codec(struct platform_device *pdev, struct msm_ext_disp_audio_codec_ops *ops) { @@ -849,11 +700,6 @@ static int msm_ext_disp_validate_intf(struct msm_ext_disp_init_data *init_data) return -EINVAL; } - if (!init_data->kobj) { - pr_err("Invalid display intf kobj\n"); - return -EINVAL; - } - if (!init_data->codec_ops.get_audio_edid_blk || !init_data->codec_ops.cable_status || !init_data->codec_ops.audio_info_setup) { @@ -899,10 +745,6 @@ int msm_ext_disp_register_intf(struct platform_device *pdev, if (ret) goto end; - ret = msm_ext_disp_sysfs_create(init_data); - if (ret) - goto sysfs_failure; - init_data->intf_ops.hpd = msm_ext_disp_hpd; init_data->intf_ops.notify = msm_ext_disp_notify; @@ -913,8 +755,6 @@ int msm_ext_disp_register_intf(struct platform_device *pdev, return ret; -sysfs_failure: - msm_ext_disp_remove_intf_data(ext_disp, init_data->type); end: mutex_unlock(&ext_disp->lock); @@ -1035,7 +875,7 @@ static void __exit msm_ext_disp_exit(void) platform_driver_unregister(&this_driver); } -module_init(msm_ext_disp_init); +subsys_initcall(msm_ext_disp_init); module_exit(msm_ext_disp_exit); MODULE_LICENSE("GPL v2"); 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/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c index 8d017fb55a3f..c012f373e80e 100644 --- a/drivers/regulator/qpnp-oledb-regulator.c +++ b/drivers/regulator/qpnp-oledb-regulator.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/notifier.h> #include <linux/of.h> #include <linux/regmap.h> #include <linux/spmi.h> @@ -24,6 +25,8 @@ #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> +#include <linux/regulator/qpnp-labibb-regulator.h> +#include <linux/qpnp/qpnp-pbs.h> #define QPNP_OLEDB_REGULATOR_DRIVER_NAME "qcom,qpnp-oledb-regulator" #define OLEDB_VOUT_STEP_MV 100 @@ -91,6 +94,12 @@ #define OLEDB_ENABLE_NLIMIT_BIT_SHIFT 7 #define OLEDB_NLIMIT_PGM_MASK GENMASK(1, 0) +#define OLEDB_SPARE_CTL 0xE9 +#define OLEDB_FORCE_PD_CTL_SPARE_BIT BIT(7) + +#define OLEDB_PD_PBS_TRIGGER_BIT BIT(0) + +#define OLEDB_SEC_UNLOCK_CODE 0xA5 #define OLEDB_PSM_HYS_CTRL_MIN 13 #define OLEDB_PSM_HYS_CTRL_MAX 26 @@ -150,6 +159,9 @@ struct qpnp_oledb { struct qpnp_oledb_psm_ctl psm_ctl; struct qpnp_oledb_pfm_ctl pfm_ctl; struct qpnp_oledb_fast_precharge_ctl fast_prechg_ctl; + struct notifier_block oledb_nb; + struct mutex bus_lock; + struct device_node *pbs_dev_node; u32 base; u8 mod_enable; @@ -168,6 +180,7 @@ struct qpnp_oledb { bool ext_pin_control; bool dynamic_ext_pinctl_config; bool pbs_control; + bool force_pd_control; }; static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400}; @@ -184,11 +197,13 @@ static int qpnp_oledb_read(struct qpnp_oledb *oledb, u32 address, int rc = 0; struct platform_device *pdev = oledb->pdev; + mutex_lock(&oledb->bus_lock); rc = regmap_bulk_read(oledb->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); + mutex_unlock(&oledb->bus_lock); return rc; } @@ -197,6 +212,7 @@ static int qpnp_oledb_masked_write(struct qpnp_oledb *oledb, { int rc; + mutex_lock(&oledb->bus_lock); rc = regmap_update_bits(oledb->regmap, address, mask, val); if (rc < 0) pr_err("Failed to write address 0x%04X, rc = %d\n", @@ -205,6 +221,31 @@ static int qpnp_oledb_masked_write(struct qpnp_oledb *oledb, pr_debug("Wrote 0x%02X to addr 0x%04X\n", val, address); + mutex_unlock(&oledb->bus_lock); + return rc; +} + +#define OLEDB_SEC_ACCESS 0xD0 +static int qpnp_oledb_sec_masked_write(struct qpnp_oledb *oledb, u16 address, + u8 mask, u8 val) +{ + int rc = 0; + u8 sec_val = OLEDB_SEC_UNLOCK_CODE; + u16 sec_reg_addr = (address & 0xFF00) | OLEDB_SEC_ACCESS; + + mutex_lock(&oledb->bus_lock); + rc = regmap_write(oledb->regmap, sec_reg_addr, sec_val); + if (rc < 0) { + pr_err("register %x failed rc = %d\n", sec_reg_addr, rc); + goto error; + } + + rc = regmap_update_bits(oledb->regmap, address, mask, val); + if (rc < 0) + pr_err("spmi write failed: addr=%03X, rc=%d\n", address, rc); + +error: + mutex_unlock(&oledb->bus_lock); return rc; } @@ -214,6 +255,7 @@ static int qpnp_oledb_write(struct qpnp_oledb *oledb, u16 address, u8 *val, int rc = 0; struct platform_device *pdev = oledb->pdev; + mutex_lock(&oledb->bus_lock); rc = regmap_bulk_write(oledb->regmap, address, val, count); if (rc) pr_err("Failed to write address=0x%02x sid=0x%02x rc=%d\n", @@ -222,7 +264,8 @@ static int qpnp_oledb_write(struct qpnp_oledb *oledb, u16 address, u8 *val, pr_debug("Wrote 0x%02X to addr 0x%04X\n", *val, address); - return 0; + mutex_unlock(&oledb->bus_lock); + return rc; } static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev) @@ -285,6 +328,8 @@ static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev) static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev) { int rc = 0; + u8 trigger_bitmap = OLEDB_PD_PBS_TRIGGER_BIT; + u8 val; struct qpnp_oledb *oledb = rdev_get_drvdata(rdev); @@ -314,6 +359,27 @@ static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev) pr_debug("Register-control mode, module disabled\n"); } + if (oledb->force_pd_control) { + rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_SPARE_CTL, + &val, 1); + if (rc < 0) { + pr_err("Failed to read OLEDB_SPARE_CTL rc=%d\n", rc); + return rc; + } + + if (val & OLEDB_FORCE_PD_CTL_SPARE_BIT) { + rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node, + trigger_bitmap); + if (rc < 0) { + pr_err("Failed to trigger the PBS sequence\n"); + return rc; + } + pr_debug("PBS event triggered\n"); + } else { + pr_debug("OLEDB_SPARE_CTL register bit not set\n"); + } + } + oledb->mod_enable = false; return rc; @@ -1034,6 +1100,18 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb) oledb->pbs_control = of_property_read_bool(of_node, "qcom,pbs-control"); + oledb->force_pd_control = + of_property_read_bool(of_node, "qcom,force-pd-control"); + + if (oledb->force_pd_control) { + oledb->pbs_dev_node = of_parse_phandle(of_node, + "qcom,pbs-client", 0); + if (!oledb->pbs_dev_node) { + pr_err("Missing qcom,pbs-client property\n"); + return -EINVAL; + } + } + oledb->current_voltage = -EINVAL; rc = of_property_read_u32(of_node, "qcom,oledb-init-voltage-mv", &oledb->current_voltage); @@ -1116,6 +1194,52 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb) return rc; } +static int qpnp_oledb_force_pulldown_config(struct qpnp_oledb *oledb) +{ + int rc = 0; + u8 val; + + rc = qpnp_oledb_sec_masked_write(oledb, oledb->base + + OLEDB_SPARE_CTL, OLEDB_FORCE_PD_CTL_SPARE_BIT, 0); + if (rc < 0) { + pr_err("Failed to write SPARE_CTL rc=%d\n", rc); + return rc; + } + + val = 1; + rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_PD_CTL, + &val, 1); + if (rc < 0) { + pr_err("Failed to write PD_CTL rc=%d\n", rc); + return rc; + } + + rc = qpnp_oledb_masked_write(oledb, oledb->base + + OLEDB_SWIRE_CONTROL, OLEDB_EN_SWIRE_PD_UPD_BIT, 0); + if (rc < 0) + pr_err("Failed to write SWIRE_CTL for pbs mode rc=%d\n", + rc); + + return rc; +} + +static int qpnp_labibb_notifier_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + int rc = 0; + struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb, + oledb_nb); + + if (action == LAB_VREG_OK) { + /* Disable SWIRE pull down control and enable via spmi mode */ + rc = qpnp_oledb_force_pulldown_config(oledb); + if (rc < 0) + return NOTIFY_STOP; + } + + return NOTIFY_OK; +} + static int qpnp_oledb_regulator_probe(struct platform_device *pdev) { int rc = 0; @@ -1143,6 +1267,7 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev) return rc; } + mutex_init(&(oledb->bus_lock)); oledb->base = val; rc = qpnp_oledb_parse_dt(oledb); if (rc < 0) { @@ -1156,18 +1281,47 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev) return rc; } + if (oledb->force_pd_control) { + oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb; + rc = qpnp_labibb_notifier_register(&oledb->oledb_nb); + if (rc < 0) { + pr_err("Failed to register qpnp_labibb_notifier_cb\n"); + return rc; + } + } + rc = qpnp_oledb_register_regulator(oledb); - if (!rc) - pr_info("OLEDB registered successfully, ext_pin_en=%d mod_en=%d cuurent_voltage=%d mV\n", + if (rc < 0) { + pr_err("Failed to register regulator rc=%d\n", rc); + goto out; + } + pr_info("OLEDB registered successfully, ext_pin_en=%d mod_en=%d current_voltage=%d mV\n", oledb->ext_pin_control, oledb->mod_enable, oledb->current_voltage); + return 0; + +out: + if (oledb->force_pd_control) { + rc = qpnp_labibb_notifier_unregister(&oledb->oledb_nb); + if (rc < 0) + pr_err("Failed to unregister lab_vreg_ok notifier\n"); + } return rc; } static int qpnp_oledb_regulator_remove(struct platform_device *pdev) { - return 0; + int rc = 0; + struct qpnp_oledb *oledb = platform_get_drvdata(pdev); + + if (oledb->force_pd_control) { + rc = qpnp_labibb_notifier_unregister(&oledb->oledb_nb); + if (rc < 0) + pr_err("Failed to unregister lab_vreg_ok notifier\n"); + } + + return rc; } const struct of_device_id qpnp_oledb_regulator_match_table[] = { 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/apr_tal_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c index 19974b61ec1c..d11ffdde23be 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c +++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c @@ -218,6 +218,7 @@ static void apr_tal_notify_remote_rx_intent(void *handle, const void *priv, */ pr_debug("%s: remote queued an intent\n", __func__); apr_ch->if_remote_intent_ready = true; + wake_up(&apr_ch->wait); } void apr_tal_notify_state(void *handle, const void *priv, unsigned event) 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/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 6a60e3624420..07610877f140 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -886,10 +886,11 @@ static int spcom_rx(struct spcom_channel *ch, if (timeleft == 0) { pr_err("rx_done timeout [%d] msec expired.\n", timeout_msec); - goto exit_err; + mutex_unlock(&ch->lock); + return -ETIMEDOUT; } else if (ch->rx_abort) { - pr_err("rx aborted.\n"); - goto exit_err; + mutex_unlock(&ch->lock); + return -ERESTART; /* probably SSR */ } else if (ch->actual_rx_size) { pr_debug("actual_rx_size is [%d].\n", ch->actual_rx_size); } else { @@ -1976,7 +1977,8 @@ static int spcom_handle_read_req_resp(struct spcom_channel *ch, ret = spcom_rx(ch, rx_buf, rx_buf_size, timeout_msec); if (ret < 0) { pr_err("rx error %d.\n", ret); - goto exit_err; + kfree(rx_buf); + return ret; } else { size = ret; /* actual_rx_size */ } @@ -2269,8 +2271,14 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, if (buf == NULL) return -ENOMEM; - actual_size = spcom_handle_read(ch, buf, size); - if ((actual_size <= 0) || (actual_size > size)) { + ret = spcom_handle_read(ch, buf, size); + if (ret < 0) { + pr_err("read error [%d].\n", ret); + kfree(buf); + return ret; + } + actual_size = ret; + if ((actual_size == 0) || (actual_size > size)) { pr_err("invalid actual_size [%d].\n", actual_size); kfree(buf); return -EFAULT; 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/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 97d86b6ac69b..e309dec68a75 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2289,8 +2289,11 @@ reset: } common->running = 0; - if (!new_fsg || rc) + if (!new_fsg || rc) { + /* allow usb LPM after eps are disabled */ + usb_gadget_autopm_put_async(common->gadget); return rc; + } common->fsg = new_fsg; fsg = common->fsg; @@ -2333,6 +2336,9 @@ reset: bh->outreq->complete = bulk_out_complete; } + /* prevents usb LPM until thread runs to completion */ + usb_gadget_autopm_get_noresume(common->gadget); + common->running = 1; for (i = 0; i < ARRAY_SIZE(common->luns); ++i) if (common->luns[i]) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index ea278781440c..935bd0778bfb 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -665,7 +665,7 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, rx_msg->type = PD_MSG_HDR_TYPE(header); rx_msg->len = PD_MSG_HDR_COUNT(header); - memcpy(&rx_msg->payload, buf, len); + memcpy(&rx_msg->payload, buf, min(len, sizeof(rx_msg->payload))); spin_lock_irqsave(&pd->rx_lock, flags); list_add_tail(&rx_msg->entry, &pd->rx_q); diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig index ef5c96214c19..03ee89ad0d99 100644 --- a/drivers/video/fbdev/msm/Kconfig +++ b/drivers/video/fbdev/msm/Kconfig @@ -63,6 +63,7 @@ config FB_MSM_MDSS_WRITEBACK config FB_MSM_MDSS_HDMI_PANEL depends on FB_MSM_MDSS + select MSM_EXT_DISPLAY bool "MDSS HDMI Tx Panel" default n ---help--- @@ -98,6 +99,7 @@ config FB_MSM_MDSS_DSI_CTRL_STATUS config FB_MSM_MDSS_DP_PANEL depends on FB_MSM_MDSS + select MSM_EXT_DISPLAY bool "MDSS DP Panel" ---help--- The MDSS DP Panel provides support for DP host controller driver diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index b905c0e855dd..e101b873f361 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -49,7 +49,6 @@ obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_aux.o obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_hdcp2p2.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o -obj-$(CONFIG_FB_MSM_MDSS) += msm_ext_display.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_panel.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp2p2.o 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_dp_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c index 3bcacf945761..5a677dfe7484 100644 --- a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c @@ -449,7 +449,7 @@ static struct attribute *dp_hdcp2p2_fs_attrs[] = { }; static struct attribute_group dp_hdcp2p2_fs_attr_group = { - .name = "dp_hdcp2p2", + .name = "hdcp2p2", .attrs = dp_hdcp2p2_fs_attrs, }; diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index f4c4c509410a..db27842eaccc 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -892,6 +892,12 @@ static ssize_t mdss_fb_get_persist_mode(struct device *dev, return ret; } +static ssize_t mdss_fb_idle_pc_notify(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "idle power collapsed\n"); +} + static DEVICE_ATTR(msm_fb_type, S_IRUGO, mdss_fb_get_type, NULL); static DEVICE_ATTR(msm_fb_split, S_IRUGO | S_IWUSR, mdss_fb_show_split, mdss_fb_store_split); @@ -912,6 +918,8 @@ static DEVICE_ATTR(measured_fps, S_IRUGO | S_IWUSR | S_IWGRP, mdss_fb_get_fps_info, NULL); static DEVICE_ATTR(msm_fb_persist_mode, S_IRUGO | S_IWUSR, mdss_fb_get_persist_mode, mdss_fb_change_persist_mode); +static DEVICE_ATTR(idle_power_collapse, S_IRUGO, mdss_fb_idle_pc_notify, NULL); + static struct attribute *mdss_fb_attrs[] = { &dev_attr_msm_fb_type.attr, &dev_attr_msm_fb_split.attr, @@ -925,6 +933,7 @@ static struct attribute *mdss_fb_attrs[] = { &dev_attr_msm_fb_dfps_mode.attr, &dev_attr_measured_fps.attr, &dev_attr_msm_fb_persist_mode.attr, + &dev_attr_idle_power_collapse.attr, NULL, }; @@ -4470,7 +4479,7 @@ err: static int __mdss_fb_copy_destscaler_data(struct fb_info *info, struct mdp_layer_commit *commit) { - int i; + int i = 0; int ret = 0; u32 data_size; struct mdp_destination_scaler_data __user *ds_data_user; @@ -4543,6 +4552,7 @@ static int __mdss_fb_copy_destscaler_data(struct fb_info *info, data_size); if (ret) { pr_err("scale data copy from user failed\n"); + kfree(scale_data); goto err; } } @@ -4552,7 +4562,7 @@ static int __mdss_fb_copy_destscaler_data(struct fb_info *info, err: if (ds_data) { - for (i = 0; i < commit->commit_v1.dest_scaler_cnt; i++) { + for (i--; i >= 0; i--) { scale_data = to_user_ptr(ds_data[i].scale); kfree(scale_data); } @@ -5176,3 +5186,16 @@ void mdss_fb_calc_fps(struct msm_fb_data_type *mfd) mfd->fps_info.frame_count = 0; } } + +void mdss_fb_idle_pc(struct msm_fb_data_type *mfd) +{ + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + + if (mdss_fb_is_power_off(mfd)) + return; + + if ((mfd->panel_info->type == MIPI_CMD_PANEL) && mdp5_data) { + pr_debug("Notify fb%d idle power collapsed\n", mfd->index); + sysfs_notify(&mfd->fbi->dev->kobj, NULL, "idle_power_collapse"); + } +} diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index 111d7cfc7c9a..d64580a35775 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -468,4 +468,5 @@ void mdss_fb_report_panel_dead(struct msm_fb_data_type *mfd); void mdss_panelinfo_to_fb_var(struct mdss_panel_info *pinfo, struct fb_var_screeninfo *var); void mdss_fb_calc_fps(struct msm_fb_data_type *mfd); +void mdss_fb_idle_pc(struct msm_fb_data_type *mfd); #endif /* MDSS_FB_H */ diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index d8d11f21f3b2..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); @@ -1355,6 +1385,68 @@ static inline void __mdss_mdp_reg_access_clk_enable( } } +/* + * __mdss_mdp_clk_control - Overall MDSS clock control for power on/off + */ +static void __mdss_mdp_clk_control(struct mdss_data_type *mdata, bool enable) +{ + int rc = 0; + unsigned long flags; + + if (enable) { + pm_runtime_get_sync(&mdata->pdev->dev); + + mdss_update_reg_bus_vote(mdata->reg_bus_clt, + VOTE_INDEX_LOW); + + rc = mdss_iommu_ctrl(1); + if (IS_ERR_VALUE(rc)) + pr_err("IOMMU attach failed\n"); + + /* Active+Sleep */ + msm_bus_scale_client_update_context(mdata->bus_hdl, + false, mdata->curr_bw_uc_idx); + + spin_lock_irqsave(&mdp_lock, flags); + mdata->clk_ena = enable; + spin_unlock_irqrestore(&mdp_lock, flags); + + mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 1); + mdss_mdp_clk_update(MDSS_CLK_AHB, 1); + mdss_mdp_clk_update(MDSS_CLK_AXI, 1); + mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 1); + mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, 1); + if (mdata->vsync_ena) + mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, 1); + } else { + spin_lock_irqsave(&mdp_lock, flags); + mdata->clk_ena = enable; + spin_unlock_irqrestore(&mdp_lock, flags); + + if (mdata->vsync_ena) + mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, 0); + + mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, 0); + mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 0); + mdss_mdp_clk_update(MDSS_CLK_AXI, 0); + mdss_mdp_clk_update(MDSS_CLK_AHB, 0); + mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 0); + + /* release iommu control */ + mdss_iommu_ctrl(0); + + /* Active-Only */ + msm_bus_scale_client_update_context(mdata->bus_hdl, + true, mdata->ao_bw_uc_idx); + + mdss_update_reg_bus_vote(mdata->reg_bus_clt, + VOTE_INDEX_DISABLE); + + pm_runtime_mark_last_busy(&mdata->pdev->dev); + pm_runtime_put_autosuspend(&mdata->pdev->dev); + } +} + int __mdss_mdp_vbif_halt(struct mdss_data_type *mdata, bool is_nrt) { int rc = 0; @@ -1646,9 +1738,7 @@ void mdss_mdp_clk_ctrl(int enable) { struct mdss_data_type *mdata = mdss_mdp_get_mdata(); static int mdp_clk_cnt; - unsigned long flags; int changed = 0; - int rc = 0; mutex_lock(&mdp_clk_lock); if (enable) { @@ -1672,49 +1762,8 @@ void mdss_mdp_clk_ctrl(int enable) __builtin_return_address(0), current->group_leader->comm, mdata->bus_ref_cnt, changed, enable); - if (changed) { - if (enable) { - pm_runtime_get_sync(&mdata->pdev->dev); - - mdss_update_reg_bus_vote(mdata->reg_bus_clt, - VOTE_INDEX_LOW); - - rc = mdss_iommu_ctrl(1); - if (IS_ERR_VALUE(rc)) - pr_err("IOMMU attach failed\n"); - - /* Active+Sleep */ - msm_bus_scale_client_update_context(mdata->bus_hdl, - false, mdata->curr_bw_uc_idx); - } - - spin_lock_irqsave(&mdp_lock, flags); - mdata->clk_ena = enable; - spin_unlock_irqrestore(&mdp_lock, flags); - - mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, enable); - mdss_mdp_clk_update(MDSS_CLK_AHB, enable); - mdss_mdp_clk_update(MDSS_CLK_AXI, enable); - mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, enable); - mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, enable); - if (mdata->vsync_ena) - mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, enable); - - if (!enable) { - /* release iommu control */ - mdss_iommu_ctrl(0); - - /* Active-Only */ - msm_bus_scale_client_update_context(mdata->bus_hdl, - true, mdata->ao_bw_uc_idx); - - mdss_update_reg_bus_vote(mdata->reg_bus_clt, - VOTE_INDEX_DISABLE); - - pm_runtime_mark_last_busy(&mdata->pdev->dev); - pm_runtime_put_autosuspend(&mdata->pdev->dev); - } - } + if (changed) + __mdss_mdp_clk_control(mdata, enable); if (enable && changed) mdss_mdp_idle_pc_restore(); @@ -4519,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; } @@ -5092,6 +5145,22 @@ vreg_set_voltage_fail: } /** + * mdss_mdp_notify_idle_pc() - Notify fb driver of idle power collapse + * @mdata: MDP private data + * + * This function is called if there are active overlays. + */ +static void mdss_mdp_notify_idle_pc(struct mdss_data_type *mdata) +{ + int i; + + for (i = 0; i < mdata->nctl; i++) + if ((mdata->ctl_off[i].ref_cnt) && + !mdss_mdp_ctl_is_power_off(&mdata->ctl_off[i])) + mdss_fb_idle_pc(mdata->ctl_off[i].mfd); +} + +/** * mdss_mdp_footswitch_ctrl() - Disable/enable MDSS GDSC and CX/Batfet rails * @mdata: MDP private data * @on: 1 to turn on footswitch, 0 to turn off footswitch @@ -5155,6 +5224,7 @@ static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on) mdss_mdp_memory_retention_ctrl(MEM_RETAIN_ON, PERIPH_RETAIN_OFF); mdata->idle_pc = true; + mdss_mdp_notify_idle_pc(mdata); pr_debug("idle pc. active overlays=%d\n", active_cnt); } else { @@ -5487,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_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index e258f258aeca..7b0207de101a 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -892,7 +892,7 @@ static u32 __calc_prefill_line_time_us(struct mdss_mdp_ctl *ctl) static u32 __get_min_prefill_line_time_us(struct mdss_mdp_ctl *ctl) { - u32 vbp_min = 0; + u32 vbp_min = UINT_MAX; int i; struct mdss_data_type *mdata; @@ -914,6 +914,9 @@ static u32 __get_min_prefill_line_time_us(struct mdss_mdp_ctl *ctl) } } + if (vbp_min == UINT_MAX) + vbp_min = 0; + return vbp_min; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 663d63092ebf..5173567a3420 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -1896,7 +1896,6 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, struct mdss_mdp_format_params *fmt; struct mdss_data_type *mdata = ctl->mdata; struct dsc_desc *dsc = NULL; - u32 hdmi_dp_core; ctx->ctl = ctl; ctx->intf_type = ctl->intf_type; @@ -2033,10 +2032,19 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, mdp_video_write(ctx, MDSS_MDP_REG_INTF_PANEL_FORMAT, ctl->dst_format); - hdmi_dp_core = (ctx->intf_type == MDSS_INTF_EDP) ? 1 : 0; - - writel_relaxed(hdmi_dp_core, mdata->mdp_base + + /* select HDMI or DP core usage */ + switch (ctx->intf_type) { + case MDSS_INTF_EDP: + writel_relaxed(0x1, mdata->mdp_base + + MDSS_MDP_HDMI_DP_CORE_SELECT); + break; + case MDSS_INTF_HDMI: + writel_relaxed(0x0, mdata->mdp_base + MDSS_MDP_HDMI_DP_CORE_SELECT); + break; + default: + break; + } return 0; } 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_ext_display.h b/include/linux/msm_ext_display.h index 4378080da0d9..d9831d7cbb4e 100644 --- a/include/linux/msm_ext_display.h +++ b/include/linux/msm_ext_display.h @@ -109,6 +109,7 @@ struct msm_ext_disp_intf_ops { * @get_audio_edid_blk: retrieve audio edid block * @cable_status: cable connected/disconnected * @get_intf_id: id of connected interface + * @acknowledge: acknowledge audio status */ struct msm_ext_disp_audio_codec_ops { int (*audio_info_setup)(struct platform_device *pdev, @@ -118,6 +119,7 @@ struct msm_ext_disp_audio_codec_ops { int (*cable_status)(struct platform_device *pdev, u32 vote); int (*get_intf_id)(struct platform_device *pdev); void (*teardown_done)(struct platform_device *pdev); + int (*acknowledge)(struct platform_device *pdev, u32 ack); }; /* 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/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index e04ccf0b6e8b..e050bc758b3b 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -70,7 +70,7 @@ struct snd_compr_tstamp { __u32 pcm_frames; __u32 pcm_io_frames; __u32 sampling_rate; - uint64_t timestamp; + __u64 timestamp; } __attribute__((packed, aligned(4))); /** @@ -128,8 +128,8 @@ struct snd_compr_codec_caps { * @reserved: reserved for furture use */ struct snd_compr_audio_info { - uint32_t frame_size; - uint32_t reserved[15]; + __u32 frame_size; + __u32 reserved[15]; } __attribute__((packed, aligned(4))); /** diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 6addbde34545..676c3b0335ef 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1094,7 +1094,6 @@ static int wsa881x_swr_startup(struct swr_device *swr_dev) { int ret = 0; u8 devnum = 0; - u8 retry = WSA881X_NUM_RETRY; struct wsa881x_priv *wsa881x; wsa881x = swr_get_dev_data(swr_dev); @@ -1109,16 +1108,12 @@ static int wsa881x_swr_startup(struct swr_device *swr_dev) * as per HW requirement. */ usleep_range(5000, 5010); - while (swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum) && - retry--) { - /* Retry after 1 msec delay */ - usleep_range(1000, 1100); - } - if (retry == 0) { - dev_err(&swr_dev->dev, + ret = swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum); + if (ret) { + dev_dbg(&swr_dev->dev, "%s get devnum %d for dev addr %lx failed\n", __func__, devnum, swr_dev->addr); - return -EINVAL; + goto err; } swr_dev->dev_num = devnum; diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c index b2008d6da2a1..48f8e22e7faa 100644 --- a/sound/soc/msm/msm-cpe-lsm.c +++ b/sound/soc/msm/msm-cpe-lsm.c @@ -2152,7 +2152,8 @@ static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if using topology\n", __func__, "LSM_REG_SND_MODEL_V2"); - return -EINVAL; + err = -EINVAL; + goto done; } if (copy_from_user(&snd_model, (void *)arg, @@ -2285,7 +2286,8 @@ static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if using topology\n", __func__, "SNDRV_LSM_SET_PARAMS"); - return -EINVAL; + err = -EINVAL; + goto done; } if (copy_from_user(&det_params, (void *) arg, @@ -2312,14 +2314,16 @@ static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if not using topology\n", __func__, "SET_MODULE_PARAMS"); - return -EINVAL; + err = -EINVAL; + goto done; } if (!arg) { dev_err(rtd->dev, "%s: %s: No Param data to set\n", __func__, "SET_MODULE_PARAMS"); - return -EINVAL; + err = -EINVAL; + goto done; } if (copy_from_user(&p_data, arg, @@ -2327,7 +2331,8 @@ static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: copy_from_user failed, size = %zd\n", __func__, "p_data", sizeof(p_data)); - return -EFAULT; + err = -EFAULT; + goto done; } if (p_data.num_params > LSM_PARAMS_MAX) { @@ -2335,7 +2340,8 @@ static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream, "%s: %s: Invalid num_params %d\n", __func__, "SET_MODULE_PARAMS", p_data.num_params); - return -EINVAL; + err = -EINVAL; + goto done; } p_size = p_data.num_params * @@ -2346,12 +2352,15 @@ static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream, "%s: %s: Invalid size %zd\n", __func__, "SET_MODULE_PARAMS", p_size); - return -EFAULT; + err = -EFAULT; + goto done; } params = kzalloc(p_size, GFP_KERNEL); - if (!params) - return -ENOMEM; + if (!params) { + err = -ENOMEM; + goto done; + } if (copy_from_user(params, p_data.params, p_data.data_size)) { @@ -2359,7 +2368,8 @@ static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream, "%s: %s: copy_from_user failed, size = %d\n", __func__, "params", p_data.data_size); kfree(params); - return -EFAULT; + err = -EFAULT; + goto done; } err = msm_cpe_lsm_process_params(substream, &p_data, params); @@ -2470,7 +2480,8 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if using topology\n", __func__, "LSM_REG_SND_MODEL_V2_32"); - return -EINVAL; + err = -EINVAL; + goto done; } dev_dbg(rtd->dev, @@ -2690,7 +2701,9 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if using topology\n", __func__, "SNDRV_LSM_SET_PARAMS32"); - return -EINVAL; + + err = -EINVAL; + goto done; } if (copy_from_user(&det_params32, arg, @@ -2734,7 +2747,8 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if not using topology\n", __func__, "SET_MODULE_PARAMS_32"); - return -EINVAL; + err = -EINVAL; + goto done; } if (copy_from_user(&p_data_32, arg, @@ -2743,7 +2757,8 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, "%s: %s: copy_from_user failed, size = %zd\n", __func__, "SET_MODULE_PARAMS_32", sizeof(p_data_32)); - return -EFAULT; + err = -EFAULT; + goto done; } p_data.params = compat_ptr(p_data_32.params); @@ -2755,7 +2770,8 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, "%s: %s: Invalid num_params %d\n", __func__, "SET_MODULE_PARAMS_32", p_data.num_params); - return -EINVAL; + err = -EINVAL; + goto done; } if (p_data.data_size != @@ -2764,21 +2780,25 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, "%s: %s: Invalid size %d\n", __func__, "SET_MODULE_PARAMS_32", p_data.data_size); - return -EINVAL; + err = -EINVAL; + goto done; } p_size = sizeof(struct lsm_params_info_32) * p_data.num_params; params32 = kzalloc(p_size, GFP_KERNEL); - if (!params32) - return -ENOMEM; + if (!params32) { + err = -ENOMEM; + goto done; + } p_size = sizeof(struct lsm_params_info) * p_data.num_params; params = kzalloc(p_size, GFP_KERNEL); if (!params) { kfree(params32); - return -ENOMEM; + err = -ENOMEM; + goto done; } if (copy_from_user(params32, p_data.params, @@ -2788,7 +2808,8 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream, __func__, "params32", p_data.data_size); kfree(params32); kfree(params); - return -EFAULT; + err = -EFAULT; + goto done; } p_info_32 = (struct lsm_params_info_32 *) params32; 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/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c index 3fa14d0113ef..ec4380036047 100644 --- a/sound/soc/msm/qdsp6v2/msm-lsm-client.c +++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c @@ -86,6 +86,7 @@ struct lsm_priv { atomic_t buf_count; atomic_t read_abort; wait_queue_head_t period_wait; + struct mutex lsm_api_lock; int appl_cnt; int dma_write; }; @@ -954,10 +955,18 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream, dev_dbg(rtd->dev, "%s: Get event status\n", __func__); atomic_set(&prtd->event_wait_stop, 0); + + /* + * Release the api lock before wait to allow + * other IOCTLs to be invoked while waiting + * for event + */ + mutex_unlock(&prtd->lsm_api_lock); rc = wait_event_freezable(prtd->event_wait, (cmpxchg(&prtd->event_avail, 1, 0) || (xchg = atomic_cmpxchg(&prtd->event_wait_stop, 1, 0)))); + mutex_lock(&prtd->lsm_api_lock); dev_dbg(rtd->dev, "%s: wait_event_freezable %d event_wait_stop %d\n", __func__, rc, xchg); if (!rc && !xchg) { @@ -1281,6 +1290,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, rtd = substream->private_data; prtd = runtime->private_data; + mutex_lock(&prtd->lsm_api_lock); + switch (cmd) { case SNDRV_LSM_EVENT_STATUS: { struct snd_lsm_event_status *user = NULL, userarg32; @@ -1288,7 +1299,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, if (copy_from_user(&userarg32, arg, sizeof(userarg32))) { dev_err(rtd->dev, "%s: err copyuser ioctl %s\n", __func__, "SNDRV_LSM_EVENT_STATUS"); - return -EFAULT; + err = -EFAULT; + goto done; } if (userarg32.payload_size > @@ -1296,7 +1308,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, pr_err("%s: payload_size %d is invalid, max allowed = %d\n", __func__, userarg32.payload_size, LISTEN_MAX_STATUS_PAYLOAD_SIZE); - return -EINVAL; + err = -EINVAL; + goto done; } size = sizeof(*user) + userarg32.payload_size; @@ -1305,7 +1318,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: Allocation failed event status size %d\n", __func__, size); - return -EFAULT; + err = -EFAULT; + goto done; } else { cmd = SNDRV_LSM_EVENT_STATUS; user->payload_size = userarg32.payload_size; @@ -1421,7 +1435,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if using topology\n", __func__, "REG_SND_MODEL_V2"); - return -EINVAL; + err = -EINVAL; + goto done; } if (copy_from_user(&snd_modelv232, arg, @@ -1462,7 +1477,7 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if using topology\n", __func__, "SET_PARAMS_32"); - return -EINVAL; + err = -EINVAL; } if (copy_from_user(&det_params32, arg, @@ -1505,7 +1520,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if not using topology\n", __func__, "SET_MODULE_PARAMS_32"); - return -EINVAL; + err = -EINVAL; + goto done; } if (copy_from_user(&p_data_32, arg, @@ -1514,7 +1530,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, "%s: %s: copy_from_user failed, size = %zd\n", __func__, "SET_MODULE_PARAMS_32", sizeof(p_data_32)); - return -EFAULT; + err = -EFAULT; + goto done; } p_data.params = compat_ptr(p_data_32.params); @@ -1526,7 +1543,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, "%s: %s: Invalid num_params %d\n", __func__, "SET_MODULE_PARAMS_32", p_data.num_params); - return -EINVAL; + err = -EINVAL; + goto done; } if (p_data.data_size != @@ -1535,7 +1553,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, "%s: %s: Invalid size %d\n", __func__, "SET_MODULE_PARAMS_32", p_data.data_size); - return -EINVAL; + err = -EINVAL; + goto done; } p_size = sizeof(struct lsm_params_info_32) * @@ -1546,7 +1565,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: no memory for params32, size = %zd\n", __func__, p_size); - return -ENOMEM; + err = -ENOMEM; + goto done; } p_size = sizeof(struct lsm_params_info) * p_data.num_params; @@ -1556,7 +1576,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, "%s: no memory for params, size = %zd\n", __func__, p_size); kfree(params32); - return -ENOMEM; + err = -ENOMEM; + goto done; } if (copy_from_user(params32, p_data.params, @@ -1566,7 +1587,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, __func__, "params32", p_data.data_size); kfree(params32); kfree(params); - return -EFAULT; + err = -EFAULT; + goto done; } p_info_32 = (struct lsm_params_info_32 *) params32; @@ -1609,6 +1631,8 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, err = msm_lsm_ioctl_shared(substream, cmd, arg); break; } +done: + mutex_unlock(&prtd->lsm_api_lock); return err; } #else @@ -1633,6 +1657,7 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, prtd = runtime->private_data; rtd = substream->private_data; + mutex_lock(&prtd->lsm_api_lock); switch (cmd) { case SNDRV_LSM_REG_SND_MODEL_V2: { struct snd_lsm_sound_model_v2 snd_model_v2; @@ -1641,7 +1666,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if using topology\n", __func__, "REG_SND_MODEL_V2"); - return -EINVAL; + err = -EINVAL; + goto done; } if (copy_from_user(&snd_model_v2, arg, sizeof(snd_model_v2))) { @@ -1668,7 +1694,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if using topology\n", __func__, "SET_PARAMS"); - return -EINVAL; + err = -EINVAL; + goto done; } pr_debug("%s: SNDRV_LSM_SET_PARAMS\n", __func__); @@ -1689,7 +1716,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: LSM_SET_PARAMS failed, err %d\n", __func__, err); - return err; + + goto done; } case SNDRV_LSM_SET_MODULE_PARAMS: { @@ -1701,7 +1729,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: not supported if not using topology\n", __func__, "SET_MODULE_PARAMS"); - return -EINVAL; + err = -EINVAL; + goto done; } if (copy_from_user(&p_data, arg, @@ -1709,7 +1738,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: %s: copy_from_user failed, size = %zd\n", __func__, "p_data", sizeof(p_data)); - return -EFAULT; + err = -EFAULT; + goto done; } if (p_data.num_params > LSM_PARAMS_MAX) { @@ -1717,7 +1747,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, "%s: %s: Invalid num_params %d\n", __func__, "SET_MODULE_PARAMS", p_data.num_params); - return -EINVAL; + err = -EINVAL; + goto done; } p_size = p_data.num_params * @@ -1728,7 +1759,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, "%s: %s: Invalid size %zd\n", __func__, "SET_MODULE_PARAMS", p_size); - return -EFAULT; + err = -EFAULT; + goto done; } params = kzalloc(p_size, GFP_KERNEL); @@ -1736,7 +1768,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: no memory for params\n", __func__); - return -ENOMEM; + err = -ENOMEM; + goto done; } if (copy_from_user(params, p_data.params, @@ -1745,7 +1778,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, "%s: %s: copy_from_user failed, size = %d\n", __func__, "params", p_data.data_size); kfree(params); - return -EFAULT; + err = -EFAULT; + goto done; } err = msm_lsm_process_params(substream, &p_data, params); @@ -1765,7 +1799,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: err copyuser event_status\n", __func__); - return -EFAULT; + err = -EFAULT; + goto done; } if (userarg.payload_size > @@ -1773,7 +1808,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, pr_err("%s: payload_size %d is invalid, max allowed = %d\n", __func__, userarg.payload_size, LISTEN_MAX_STATUS_PAYLOAD_SIZE); - return -EINVAL; + err = -EINVAL; + goto done; } size = sizeof(struct snd_lsm_event_status) + @@ -1783,7 +1819,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: Allocation failed event status size %d\n", __func__, size); - return -EFAULT; + err = -EFAULT; + goto done; } else { user->payload_size = userarg.payload_size; err = msm_lsm_ioctl_shared(substream, cmd, user); @@ -1806,7 +1843,7 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, if (err) dev_err(rtd->dev, "%s: lsmevent failed %d", __func__, err); - return err; + goto done; } case SNDRV_LSM_EVENT_STATUS_V3: { @@ -1873,6 +1910,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, err = msm_lsm_ioctl_shared(substream, cmd, arg); break; } +done: + mutex_unlock(&prtd->lsm_api_lock); return err; } @@ -1889,6 +1928,7 @@ static int msm_lsm_open(struct snd_pcm_substream *substream) __func__); return -ENOMEM; } + mutex_init(&prtd->lsm_api_lock); spin_lock_init(&prtd->event_lock); init_waitqueue_head(&prtd->event_wait); init_waitqueue_head(&prtd->period_wait); @@ -2048,6 +2088,7 @@ static int msm_lsm_close(struct snd_pcm_substream *substream) kfree(prtd->event_status); prtd->event_status = NULL; spin_unlock_irqrestore(&prtd->event_lock, flags); + mutex_destroy(&prtd->lsm_api_lock); kfree(prtd); runtime->private_data = NULL; diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 727bd6551018..3677a06c65ae 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -14701,6 +14701,7 @@ static void __exit msm_soc_routing_platform_exit(void) { msm_routing_delete_cal_data(); memset(&be_dai_name_table, 0, sizeof(be_dai_name_table)); + mutex_destroy(&routing_lock); platform_driver_unregister(&msm_routing_pcm_driver); } module_exit(msm_soc_routing_platform_exit); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c index c444a27c06e6..b2387a746f61 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.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 @@ -814,20 +814,25 @@ static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a, if (prtd->mode == MODE_PCM) { ret = copy_from_user(&buf_node->frame.voc_pkt, buf, count); + if (ret) { + pr_err("%s: copy from user failed %d\n", + __func__, ret); + return -EFAULT; + } buf_node->frame.pktlen = count; } else { ret = copy_from_user(&buf_node->frame, buf, count); + if (ret) { + pr_err("%s: copy from user failed %d\n", + __func__, ret); + return -EFAULT; + } if (buf_node->frame.pktlen >= count) buf_node->frame.pktlen = count - (sizeof(buf_node->frame.frm_hdr) + sizeof(buf_node->frame.pktlen)); } - if (ret) { - pr_err("%s: copy from user failed %d\n", - __func__, ret); - return -EFAULT; - } spin_lock_irqsave(&prtd->dsp_lock, dsp_flags); list_add_tail(&buf_node->list, &prtd->in_queue); spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags); 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]) |
