summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/batterydata/batterydata.txt221
-rw-r--r--Documentation/devicetree/bindings/clock/qcom,rpmcc.txt9
-rw-r--r--Documentation/devicetree/bindings/gpu/adreno.txt4
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt5
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt6
-rw-r--r--Documentation/devicetree/bindings/scheduler/sched_hmp.txt35
-rw-r--r--Documentation/devicetree/bindings/thermal/tsens.txt1
-rw-r--r--Documentation/scheduler/sched-hmp.txt8
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi80
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi100
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi7
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi67
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi27
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi17
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi10
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi5
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi302
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon.dtsi22
-rw-r--r--arch/arm/boot/dts/qcom/msmtriton.dtsi9
-rw-r--r--arch/arm/configs/msmcortex_defconfig2
-rw-r--r--arch/arm/configs/msmfalcon_defconfig4
-rw-r--r--arch/arm/kernel/topology.c34
-rw-r--r--arch/arm/mach-qcom/Kconfig5
-rw-r--r--arch/arm64/Kconfig.platforms8
-rw-r--r--arch/arm64/configs/msm-perf_defconfig4
-rw-r--r--arch/arm64/configs/msm_defconfig4
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig5
-rw-r--r--arch/arm64/configs/msmcortex_defconfig5
-rw-r--r--arch/arm64/configs/msmfalcon-perf_defconfig4
-rw-r--r--arch/arm64/configs/msmfalcon_defconfig4
-rw-r--r--arch/arm64/mm/dma-mapping.c265
-rw-r--r--drivers/char/diag/diagfwd_glink.c23
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c4
-rw-r--r--drivers/clk/msm/clock-osm.c8
-rw-r--r--drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c4
-rw-r--r--drivers/clk/qcom/clk-rcg.h2
-rw-r--r--drivers/clk/qcom/clk-rcg2.c78
-rw-r--r--drivers/clk/qcom/clk-smd-rpm.c85
-rw-r--r--drivers/cpuidle/lpm-levels-of.c210
-rw-r--r--drivers/cpuidle/lpm-levels.c355
-rw-r--r--drivers/cpuidle/lpm-levels.h9
-rw-r--r--drivers/gpu/msm/adreno-gpulist.h22
-rw-r--r--drivers/gpu/msm/adreno.c15
-rw-r--r--drivers/gpu/msm/adreno.h6
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c6
-rw-r--r--drivers/gpu/msm/kgsl.h15
-rw-r--r--drivers/gpu/msm/kgsl_device.h4
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c373
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.h2
-rw-r--r--drivers/gpu/msm/kgsl_pwrscale.c8
-rw-r--r--drivers/gpu/msm/kgsl_snapshot.c38
-rw-r--r--drivers/hwmon/qpnp-adc-common.c12
-rw-r--r--drivers/hwtracing/coresight/coresight-csr.c4
-rw-r--r--drivers/hwtracing/coresight/coresight-stm.c4
-rw-r--r--drivers/iio/adc/qcom-rradc.c12
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c21
-rw-r--r--drivers/iommu/io-pgtable-arm.c11
-rw-r--r--drivers/iommu/msm_dma_iommu_mapping.c1
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c128
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.h2
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp48.c3
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c94
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c28
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c31
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c14
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c1
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_util.c7
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c13
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c28
-rw-r--r--drivers/net/wireless/ath/wil6210/fw.h3
-rw-r--r--drivers/net/wireless/ath/wil6210/fw_inc.c7
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c1
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h3
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c8
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c6
-rw-r--r--drivers/platform/msm/sps/bam.c6
-rw-r--r--drivers/power/qcom-charger/Makefile4
-rw-r--r--drivers/power/qcom-charger/battery_current_limit.c77
-rw-r--r--drivers/power/qcom-charger/fg-core.h15
-rw-r--r--drivers/power/qcom-charger/qpnp-fg-gen3.c533
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c263
-rw-r--r--drivers/power/qcom-charger/smb-lib.c90
-rw-r--r--drivers/power/qcom-charger/smb-lib.h11
-rw-r--r--drivers/power/qcom-charger/smb-reg.h4
-rw-r--r--drivers/power/qcom-charger/smb138x-charger.c197
-rw-r--r--drivers/power/qcom-charger/storm-watch.c57
-rw-r--r--drivers/power/qcom-charger/storm-watch.h36
-rw-r--r--drivers/power/qcom/msm-core.c10
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c19
-rw-r--r--drivers/scsi/ufs/ufs_test.c20
-rw-r--r--drivers/scsi/ufs/ufshcd.c7
-rw-r--r--drivers/soc/qcom/icnss.c199
-rw-r--r--drivers/soc/qcom/jtag-fuse.c4
-rw-r--r--drivers/soc/qcom/service-notifier.c3
-rw-r--r--drivers/soc/qcom/system_stats.c2
-rwxr-xr-xdrivers/staging/android/ion/ion.c4
-rw-r--r--drivers/staging/android/ion/ion_cma_heap.c6
-rw-r--r--drivers/staging/android/ion/ion_cma_secure_heap.c12
-rw-r--r--drivers/staging/android/ion/ion_system_heap.c13
-rw-r--r--drivers/staging/android/ion/msm/msm_ion.c6
-rw-r--r--drivers/thermal/msm-tsens.c12
-rw-r--r--drivers/usb/gadget/Kconfig11
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_qc_rndis.c592
-rw-r--r--drivers/usb/gadget/function/rndis.c28
-rw-r--r--drivers/usb/gadget/function/rndis.h6
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.c656
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.h71
-rw-r--r--drivers/usb/host/xhci.c21
-rw-r--r--drivers/usb/pd/policy_engine.c12
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c23
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_status.c10
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c87
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c107
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c14
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h9
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.c10
-rw-r--r--fs/fuse/passthrough.c4
-rw-r--r--include/linux/dma-mapping-fast.h2
-rw-r--r--include/linux/iommu.h4
-rw-r--r--include/linux/msm_dma_iommu_mapping.h2
-rw-r--r--include/linux/percpu-rwsem.h84
-rw-r--r--include/linux/rcu_sync.h1
-rw-r--r--include/soc/qcom/icnss.h2
-rw-r--r--include/sound/wcd-dsp-mgr.h14
-rw-r--r--include/trace/events/trace_msm_low_power.h50
-rw-r--r--include/uapi/linux/msm_vidc_dec.h6
-rw-r--r--kernel/cgroup.c6
-rw-r--r--kernel/fork.c4
-rw-r--r--kernel/locking/percpu-rwsem.c229
-rw-r--r--kernel/rcu/sync.c12
-rw-r--r--kernel/sched/core.c10
-rw-r--r--kernel/sched/hmp.c59
-rw-r--r--kernel/sched/sched.h4
-rw-r--r--kernel/sched/sched_avg.c19
-rw-r--r--lib/Kconfig.debug2
-rw-r--r--net/rmnet_data/rmnet_data_vnd.c2
-rw-r--r--sound/soc/codecs/Makefile8
-rw-r--r--sound/soc/codecs/wcd-dsp-mgr.c388
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c2
-rw-r--r--sound/soc/codecs/wcd-spi.c31
-rw-r--r--sound/soc/codecs/wcd9330.c12
-rw-r--r--sound/soc/codecs/wcd9335.c6
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c203
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h12
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-routing.h4
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c92
-rw-r--r--sound/soc/codecs/wcd9xxx-mbhc.c4
-rw-r--r--sound/soc/codecs/wcd_cpe_core.c2
-rw-r--r--sound/soc/codecs/wsa881x.c2
-rw-r--r--sound/soc/msm/msmcobalt.c792
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c5
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c109
170 files changed, 6758 insertions, 1762 deletions
diff --git a/Documentation/devicetree/bindings/batterydata/batterydata.txt b/Documentation/devicetree/bindings/batterydata/batterydata.txt
new file mode 100644
index 000000000000..39f9375a6c48
--- /dev/null
+++ b/Documentation/devicetree/bindings/batterydata/batterydata.txt
@@ -0,0 +1,221 @@
+Battery Profile Data
+
+Battery Data is a collection of battery profile data made available to
+the QPNP Charger and BMS drivers via device tree.
+
+qcom,battery-data node required properties:
+- qcom,rpull-up-kohm : The vadc pullup resistor's resistance value in kOhms.
+- qcom,vref-batt-therm-uv : The vadc voltage used to make readings.
+ For Qualcomm Technologies, Inc. VADCs, this should be
+ 1800000uV.
+
+qcom,battery-data node optional properties:
+- qcom,batt-id-range-pct : The area of variation between upper and lower bound
+ for which a given battery ID resistance is valid. This
+ value is expressed as a percentage of the specified kohm
+ resistance provided by qcom,batt-id-kohm.
+
+qcom,battery-data can also include any number of children nodes. These children
+nodes will be treated as battery profile data nodes.
+
+Profile data node required properties:
+- qcom,fcc-mah : Full charge count of the battery in milliamp-hours
+- qcom,default-rbatt-mohm : The nominal battery resistance value
+- qcom,rbatt-capacitive-mohm : The capacitive resistance of the battery.
+- qcom,flat-ocv-threshold-uv : The threshold under which the battery can be
+ considered to be in the flat portion of the discharge
+ curve.
+- qcom,max-voltage-uv : The maximum rated voltage of the battery
+- qcom,v-cutoff-uv : The cutoff voltage of the battery at which the device
+ should shutdown gracefully.
+- qcom,chg-term-ua : The termination charging current of the battery.
+- qcom,batt-id-kohm : The battery id resistance of the battery. It can be
+ used as an array which could support multiple IDs for one battery
+ module when the ID resistance of some battery modules goes across
+ several ranges.
+- qcom,battery-type : A string indicating the type of battery.
+- qcom,fg-profile-data : An array of hexadecimal values used to configure more
+ complex fuel gauge peripherals which have a large amount
+ of coefficients used in hardware state machines and thus
+ influencing the final output of the state of charge read
+ by software.
+
+Profile data node optional properties:
+- qcom,chg-rslow-comp-c1 : A constant for rslow compensation in the fuel gauge.
+ This will be provided by the profiling tool for
+ additional fuel gauge accuracy during charging.
+- qcom,chg-rslow-comp-c2 : A constant for rslow compensation in the fuel gauge.
+ This will be provided by the profiling tool for
+ additional fuel gauge accuracy during charging.
+- qcom,chg-rslow-comp-thr : A constant for rslow compensation in the fuel gauge.
+ This will be provided by the profiling tool for
+ additional fuel gauge accuracy during charging.
+- qcom,chg-rs-to-rslow: A constant for rslow compensation in the fuel gauge.
+ This will be provided by the profiling tool for
+ additional fuel gauge accuracy during charging.
+- qcom,fastchg-current-ma: Specifies the maximum fastcharge current.
+- qcom,fg-cc-cv-threshold-mv: Voltage threshold in mV for transition from constant
+ charge (CC) to constant voltage (CV). This value should
+ be 10 mV less than the float voltage.
+ This property should only be specified if
+ "qcom,autoadjust-vfloat" property is specified in the
+ charger driver to ensure a proper operation.
+- qcom,thermal-coefficients: Byte array of thermal coefficients for reading
+ battery thermistor. This should be exactly 6 bytes
+ in length.
+ Example: [01 02 03 04 05 06]
+
+Profile data node required subnodes:
+- qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes
+ temperature to fcc lookup. The units for this lookup
+ table should be degrees celsius to milliamp-hours.
+- qcom,pc-temp-ocv-lut : A 2-dimensional lookup table node that encodes
+ temperature and percent charge to open circuit voltage
+ lookup. The units for this lookup table should be
+ degrees celsius and percent to millivolts.
+- qcom,rbatt-sf-lut : A 2-dimentional lookup table node that encodes
+ temperature and percent charge to battery internal
+ resistance lookup. The units for this lookup table
+ should be degrees celsius and percent to milliohms.
+
+Profile data node optional subnodes:
+- qcom,ibat-acc-luit: A 2-dimentional lookup table that encodes temperature
+ and battery current to battery ACC (apparent charge
+ capacity). The units for this lookup table should be
+ temperature in degrees celsius, ibat in milli-amps
+ and ACC in milli-ampere-hour.
+
+Lookup table required properties:
+- qcom,lut-col-legend : An array that encodes the legend of the lookup table's
+ columns. The length of this array will determine the
+ lookup table's width.
+- qcom,lut-data : An array that encodes the lookup table's data. The size of this
+ array should be equal to the size of qcom,lut-col-legend
+ multiplied by 1 if it's a 1-dimensional table, or
+ the size of qcom,lut-row-legend if it's a 2-dimensional
+ table. The data should be in a flattened row-major
+ representation.
+
+Lookup table optional properties:
+- qcom,lut-row-legend : An array that encodes the legend of the lookup table's rows.
+ If this property exists, then it is assumed that the
+ lookup table is a 2-dimensional table.
+
+Example:
+
+In msm8974-mtp.dtsi:
+
+mtp_batterydata: qcom,battery-data {
+ qcom,rpull-up-kohm = <100>;
+ qcom,vref-batt-therm-uv = <1800000>;
+
+ /include/ "batterydata-palladium.dtsi"
+ /include/ "batterydata-mtp-3000mah.dtsi"
+};
+
+&pm8941_bms {
+ qcom,battery-data = <&mtp_batterydata>;
+};
+
+In batterydata-palladium.dtsi:
+
+qcom,palladium-batterydata {
+ qcom,fcc-mah = <1500>;
+ qcom,default-rbatt-mohm = <236>;
+ qcom,rbatt-capacitive-mohm = <50>;
+ qcom,flat-ocv-threshold-uv = <3800000>;
+ qcom,max-voltage-uv = <4200000>;
+ qcom,v-cutoff-uv = <3400000>;
+ qcom,chg-term-ua = <100000>;
+ qcom,batt-id-kohm = <75>;
+ qcom,battery-type = "palladium_1500mah";
+
+ qcom,fcc-temp-lut {
+ qcom,lut-col-legend = <(-20) 0 25 40 65>;
+ qcom,lut-data = <1492 1492 1493 1483 1502>;
+ };
+
+ qcom,pc-temp-ocv-lut {
+ qcom,lut-col-legend = <(-20) 0 25 40 65>;
+ qcom,lut-row-legend = <100 95 90 85 80 75 70>,
+ <65 60 55 50 45 40 35>,
+ <30 25 20 15 10 9 8>,
+ <7 6 5 4 3 2 1 0>;
+ qcom,lut-data = <4173 4167 4163 4156 4154>,
+ <4104 4107 4108 4102 4104>,
+ <4057 4072 4069 4061 4060>,
+ <3973 4009 4019 4016 4020>,
+ <3932 3959 3981 3982 3983>,
+ <3899 3928 3954 3950 3950>,
+ <3868 3895 3925 3921 3920>,
+ <3837 3866 3898 3894 3892>,
+ <3812 3841 3853 3856 3862>,
+ <3794 3818 3825 3823 3822>,
+ <3780 3799 3804 3804 3803>,
+ <3768 3787 3790 3788 3788>,
+ <3757 3779 3778 3775 3776>,
+ <3747 3772 3771 3766 3765>,
+ <3736 3763 3766 3760 3746>,
+ <3725 3749 3756 3747 3729>,
+ <3714 3718 3734 3724 3706>,
+ <3701 3703 3696 3689 3668>,
+ <3675 3695 3682 3675 3662>,
+ <3670 3691 3680 3673 3661>,
+ <3661 3686 3679 3672 3656>,
+ <3649 3680 3676 3669 3641>,
+ <3633 3669 3667 3655 3606>,
+ <3610 3647 3640 3620 3560>,
+ <3580 3607 3596 3572 3501>,
+ <3533 3548 3537 3512 3425>,
+ <3457 3468 3459 3429 3324>,
+ <3328 3348 3340 3297 3172>,
+ <3000 3000 3000 3000 3000>;
+ };
+
+ qcom,rbatt-sf-lut {
+ qcom,lut-col-legend = <(-20) 0 25 40 65>;
+ qcom,lut-row-legend = <100 95 90 85 80 75 70>,
+ <65 60 55 50 45 40 35>,
+ <30 25 20 15 10 9 8>,
+ <7 6 5 4 3 2 1 0>;
+ qcom,lut-data = <357 187 100 91 91>,
+ <400 208 105 94 94>,
+ <390 204 106 95 96>,
+ <391 201 108 98 98>,
+ <391 202 110 98 100>,
+ <390 200 110 99 102>,
+ <389 200 110 99 102>,
+ <393 202 101 93 100>,
+ <407 205 99 89 94>,
+ <428 208 100 91 96>,
+ <455 212 102 92 98>,
+ <495 220 104 93 101>,
+ <561 232 107 95 102>,
+ <634 245 112 98 98>,
+ <714 258 114 98 98>,
+ <791 266 114 97 100>,
+ <871 289 108 95 97>,
+ <973 340 124 108 105>,
+ <489 241 109 96 99>,
+ <511 246 110 96 99>,
+ <534 252 111 95 98>,
+ <579 263 112 96 96>,
+ <636 276 111 95 97>,
+ <730 294 109 96 99>,
+ <868 328 112 98 104>,
+ <1089 374 119 101 115>,
+ <1559 457 128 105 213>,
+ <12886 1026 637 422 3269>,
+ <170899 127211 98968 88907 77102>;
+ };
+
+ qcom,ibat-acc-lut {
+ qcom,lut-col-legend = <(-20) 0 25>;
+ qcom,lut-row-legend = <0 250 500 1000>;
+ qcom,lut-data = <1470 1470 1473>,
+ <1406 1406 1430>,
+ <1247 1247 1414>,
+ <764 764 1338>;
+ };
+};
+
diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
index 87d3714b956a..f825a44e5911 100644
--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt
@@ -12,6 +12,8 @@ Required properties :
"qcom,rpmcc-msm8916", "qcom,rpmcc"
"qcom,rpmcc-apq8064", "qcom,rpmcc"
+ "qcom,rpmcc-msm8996", "qcom,rpmcc"
+ "qcom,rpmcc-msmfalcon", "qcom,rpmcc"
- #clock-cells : shall contain 1
@@ -35,3 +37,10 @@ Example:
};
};
};
+
+ The below are applicable for MSM8996 & MSMFalcon.
+
+ rpmcc: clock-controller {
+ compatible = "qcom,rpmcc-msm8996", "qcom,rpmcc";
+ #clock-cells = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index fffb8cc39d0f..ca58f0da07ef 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -139,6 +139,10 @@ Optional Properties:
baseAddr - base address of the gpu channels in the qdss stm memory region
size - size of the gpu stm region
+- qcom,tsens-name:
+ Specify the name of GPU temperature sensor. This name will be used
+ to get the temperature from the thermal driver API.
+
GPU Quirks:
- qcom,gpu-quirk-two-pass-use-wfi:
Signal the GPU to set Set TWOPASSUSEWFI bit in
diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
index 199b5c0857f8..bd358593fcb3 100644
--- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
@@ -140,6 +140,11 @@ First Level Node - FG Gen3 device
asleep and the battery is discharging. This option requires
qcom,fg-esr-timer-awake to be defined.
+- qcom,cycle-counter-en
+ Usage: optional
+ Value type: <bool>
+ Definition: Enables the cycle counter feature.
+
==========================================================
Second Level Nodes - Peripherals managed by FG Gen3 driver
==========================================================
diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
index 1c5dd91891dc..21404dfc4b7b 100644
--- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
@@ -21,6 +21,12 @@ Charger specific properties:
Value type: <string>
Definition: "qcom,qpnp-smb2".
+- qcom,pmic-revid
+ Usage: required
+ Value type: phandle
+ Definition: Should specify the phandle of PMI's revid module. This is used to
+ identify the PMI subtype.
+
- qcom,batteryless-platform
Usage: optional
Value type: <empty>
diff --git a/Documentation/devicetree/bindings/scheduler/sched_hmp.txt b/Documentation/devicetree/bindings/scheduler/sched_hmp.txt
new file mode 100644
index 000000000000..ba1d4db9e407
--- /dev/null
+++ b/Documentation/devicetree/bindings/scheduler/sched_hmp.txt
@@ -0,0 +1,35 @@
+* HMP scheduler
+
+This file describes the bindings for an optional HMP scheduler
+node (/sched-hmp).
+
+Required properties:
+
+Optional properties:
+
+- boost-policy: The HMP scheduler has two types of task placement boost
+policies.
+
+(1) boost-on-big policy make use of all big CPUs up to their full capacity
+before using the little CPUs. This improves performance on true b.L systems
+where the big CPUs have higher efficiency compared to the little CPUs.
+
+(2) boost-on-all policy place the tasks on the CPU having the highest
+spare capacity. This policy is optimal for SMP like systems.
+
+The scheduler sets the boost policy to boost-on-big on systems which has
+CPUs of different efficiencies. However it is possible that CPUs of the
+same micro architecture to have slight difference in efficiency due to
+other factors like cache size. Selecting the boost-on-big policy based
+on relative difference in efficiency is not optimal on such systems.
+The boost-policy device tree property is introduced to specify the
+required boost type and it overrides the default selection of boost
+type in the scheduler.
+
+The possible values for this property are "boost-on-big" and "boost-on-all".
+
+Example:
+
+sched-hmp {
+ boost-policy = "boost-on-all"
+}
diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt
index 684bea131405..7189edbf8c5c 100644
--- a/Documentation/devicetree/bindings/thermal/tsens.txt
+++ b/Documentation/devicetree/bindings/thermal/tsens.txt
@@ -32,6 +32,7 @@ Required properties:
should be "qcom,msmcobalt-tsens" for cobalt TSENS driver.
should be "qcom,msmhamster-tsens" for hamster TSENS driver.
should be "qcom,msmfalcon-tsens" for falcon TSENS driver.
+ should be "qcom,msmtriton-tsens" for triton TSENS driver.
The compatible property is used to identify the respective fusemap to use
for the corresponding SoC.
- reg : offset and length of the TSENS registers with associated property in reg-names
diff --git a/Documentation/scheduler/sched-hmp.txt b/Documentation/scheduler/sched-hmp.txt
index b400e053e55d..298064bc44d7 100644
--- a/Documentation/scheduler/sched-hmp.txt
+++ b/Documentation/scheduler/sched-hmp.txt
@@ -43,6 +43,7 @@ CONTENTS
8.8 sched_get_busy
8.9 sched_freq_alert
8.10 sched_set_boost
+9. Device Tree bindings
===============
1. INTRODUCTION
@@ -1447,3 +1448,10 @@ Logged when boost settings are being changed
<task>-0 [004] d.h4 12700.711489: sched_set_boost: ref_count=1
- ref_count: A non-zero value indicates boost is in effect
+
+========================
+9. Device Tree bindings
+========================
+
+The device tree bindings for the HMP scheduler are defined in
+Documentation/devicetree/bindings/sched/sched_hmp.txt
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi
new file mode 100644
index 000000000000..6c17bca64a86
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi
@@ -0,0 +1,80 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_dual_jdi_a407_cmd: qcom,mdss_dsi_jdi_a407_wqhd_cmd {
+ qcom,mdss-dsi-panel-name = "JDI a407 wqhd cmd mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <720>;
+ qcom,mdss-dsi-panel-height = <2560>;
+ qcom,mdss-dsi-h-front-porch = <16>;
+ qcom,mdss-dsi-h-back-porch = <40>;
+ qcom,mdss-dsi-h-pulse-width = <4>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <20>;
+ qcom,mdss-dsi-v-front-porch = <7>;
+ qcom,mdss-dsi-v-pulse-width = <1>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 15 01 00 00 00 00 02 35 00
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 32 00 02 29 00];
+ qcom,mdss-dsi-off-command = [
+ 05 01 00 00 32 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line = <0x2c>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+
+ qcom,mdss-dsi-lp11-init;
+ qcom,adjust-timer-wakeup-ms = <1>;
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 10>, <1 20>;
+
+ qcom,config-select = <&dsi_dual_jdi_a407_cmd_config0>;
+
+ dsi_dual_jdi_a407_cmd_config0: config0 {
+ qcom,split-mode = "dualctl-split";
+ };
+
+ dsi_dual_jdi_a407_cmd_config1: config1 {
+ qcom,split-mode = "pingpong-split";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
index ec3e9e89b0ae..8a8782f5f8b3 100644
--- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
@@ -321,6 +321,7 @@
io-channel-names = "rradc_batt_id";
qcom,fg-esr-timer-awake = <64>;
qcom,fg-esr-timer-asleep = <256>;
+ qcom,cycle-counter-en;
status = "okay";
qcom,fg-batt-soc@4000 {
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index dc1bbcd13c36..912bdd88be68 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.dtsi
@@ -1355,7 +1355,7 @@
gdsc-vdd-supply = <&gdsc_pcie_0>;
vreg-1.8-supply = <&pm8994_l12>;
vreg-0.9-supply = <&pm8994_l28>;
- vreg-cx-supply = <&pm8994_s1_corner_ao>;
+ vreg-cx-supply = <&pm8994_s1_corner>;
qcom,vreg-0.9-voltage-level = <925000 925000 24000>;
qcom,vreg-cx-voltage-level = <7 4 0>;
@@ -1510,7 +1510,7 @@
gdsc-vdd-supply = <&gdsc_pcie_1>;
vreg-1.8-supply = <&pm8994_l12>;
vreg-0.9-supply = <&pm8994_l28>;
- vreg-cx-supply = <&pm8994_s1_corner_ao>;
+ vreg-cx-supply = <&pm8994_s1_corner>;
qcom,vreg-0.9-voltage-level = <925000 925000 24000>;
qcom,vreg-cx-voltage-level = <7 5 0>;
@@ -1663,10 +1663,10 @@
gdsc-vdd-supply = <&gdsc_pcie_2>;
vreg-1.8-supply = <&pm8994_l12>;
vreg-0.9-supply = <&pm8994_l28>;
- vreg-cx-supply = <&pm8994_s1_corner_ao>;
+ vreg-cx-supply = <&pm8994_s1_corner>;
qcom,vreg-0.9-voltage-level = <925000 925000 24000>;
- qcom,vreg-cx-voltage-level = <7 4 0>;
+ qcom,vreg-cx-voltage-level = <7 5 0>;
qcom,l1-supported;
qcom,l1ss-supported;
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
index 445f32df8aa4..a1d80075abe0 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
@@ -157,6 +157,7 @@
qcom,audio-routing =
"RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
"AMIC2", "MIC BIAS2",
"MIC BIAS2", "Headset Mic",
"AMIC3", "MIC BIAS2",
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi
index ed29dd9e1508..ed8eb8459e51 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi
@@ -87,7 +87,7 @@
cam_vdig-supply = <&pmcobalt_s3>;
qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
qcom,cam-vreg-min-voltage = <0 3312000 1352000>;
- qcom,cam-vreg-max-voltage = <0 3312000 1352000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1352000>;
qcom,cam-vreg-op-mode = <0 80000 105000>;
qcom,gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
@@ -132,7 +132,7 @@
cam_vana-supply = <&pmicobalt_bob>;
qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
qcom,cam-vreg-min-voltage = <0 0 3312000>;
- qcom,cam-vreg-max-voltage = <0 0 3312000>;
+ qcom,cam-vreg-max-voltage = <0 0 3600000>;
qcom,cam-vreg-op-mode = <0 0 80000>;
qcom,gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
@@ -215,7 +215,7 @@
cam_vdig-supply = <&pmcobalt_s3>;
qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
qcom,cam-vreg-min-voltage = <0 3312000 1352000>;
- qcom,cam-vreg-max-voltage = <0 3312000 1352000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1352000>;
qcom,cam-vreg-op-mode = <0 80000 105000>;
qcom,gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
@@ -259,7 +259,7 @@
cam_vana-supply = <&pmicobalt_bob>;
qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
qcom,cam-vreg-min-voltage = <0 0 3312000>;
- qcom,cam-vreg-max-voltage = <0 0 3312000>;
+ qcom,cam-vreg-max-voltage = <0 0 3600000>;
qcom,cam-vreg-op-mode = <0 0 80000>;
qcom,gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi
index 485bc560eef5..2be67ab52ba7 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi
@@ -87,7 +87,7 @@
cam_vdig-supply = <&pmcobalt_s3>;
qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
qcom,cam-vreg-min-voltage = <0 3312000 1352000>;
- qcom,cam-vreg-max-voltage = <0 3312000 1352000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1352000>;
qcom,cam-vreg-op-mode = <0 80000 105000>;
qcom,gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
@@ -132,7 +132,7 @@
cam_vana-supply = <&pmicobalt_bob>;
qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
qcom,cam-vreg-min-voltage = <0 0 3312000>;
- qcom,cam-vreg-max-voltage = <0 0 3312000>;
+ qcom,cam-vreg-max-voltage = <0 0 3600000>;
qcom,cam-vreg-op-mode = <0 0 80000>;
qcom,gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
@@ -215,7 +215,7 @@
cam_vdig-supply = <&pmcobalt_s3>;
qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
qcom,cam-vreg-min-voltage = <0 3312000 1352000>;
- qcom,cam-vreg-max-voltage = <0 3312000 1352000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1352000>;
qcom,cam-vreg-op-mode = <0 80000 105000>;
qcom,gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
@@ -259,7 +259,7 @@
cam_vana-supply = <&pmicobalt_bob>;
qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
qcom,cam-vreg-min-voltage = <0 0 3312000>;
- qcom,cam-vreg-max-voltage = <0 0 3312000>;
+ qcom,cam-vreg-max-voltage = <0 0 3600000>;
qcom,cam-vreg-op-mode = <0 0 80000>;
qcom,gpio-no-mux = <0>;
pinctrl-names = "cam_default", "cam_suspend";
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi
index 467b2cbfcd7d..a37fa26b1055 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi
@@ -147,21 +147,20 @@
<&clock_mmss clk_mmss_camss_top_ahb_clk>,
<&clock_mmss clk_mmss_camss_ispif_ahb_clk>,
<&clock_mmss clk_csi0_clk_src>,
+ <&clock_mmss clk_csiphy_clk_src>,
<&clock_mmss clk_mmss_camss_csi0_clk>,
- <&clock_mmss clk_mmss_camss_csiphy0_clk>,
<&clock_mmss clk_mmss_camss_csi0_ahb_clk>,
<&clock_mmss clk_mmss_camss_csi0rdi_clk>,
<&clock_mmss clk_mmss_camss_csi0pix_clk>,
- <&clock_mmss clk_mmss_camss_cphy_csid0_clk>,
- <&clock_mmss clk_csiphy_clk_src>;
+ <&clock_mmss clk_mmss_camss_cphy_csid0_clk>;
clock-names = "mmssnoc_axi", "mnoc_ahb",
"bmic_smmu_ahb", "bmic_smmu_axi",
"camss_ahb_clk", "camss_top_ahb_clk",
- "ispif_ahb_clk", "csi_src_clk", "csi_clk",
- "csi_phy_clk", "csi_ahb_clk", "csi_rdi_clk",
- "csi_pix_clk", "cphy_csid_clk", "cphy_clk_src";
- qcom,clock-rates = <0 0 0 0 0 0 0 256000000 0 0 0 0 0 0
- 256000000>;
+ "ispif_ahb_clk", "csi_src_clk", "csiphy_clk_src",
+ "csi_clk", "csi_ahb_clk", "csi_rdi_clk",
+ "csi_pix_clk", "cphy_csid_clk";
+ qcom,clock-rates = <0 0 0 0 0 0 0 256000000 256000000
+ 0 0 0 0 0>;
status = "ok";
};
@@ -186,21 +185,20 @@
<&clock_mmss clk_mmss_camss_top_ahb_clk>,
<&clock_mmss clk_mmss_camss_ispif_ahb_clk>,
<&clock_mmss clk_csi1_clk_src>,
+ <&clock_mmss clk_csiphy_clk_src>,
<&clock_mmss clk_mmss_camss_csi1_clk>,
- <&clock_mmss clk_mmss_camss_csiphy1_clk>,
<&clock_mmss clk_mmss_camss_csi1_ahb_clk>,
<&clock_mmss clk_mmss_camss_csi1rdi_clk>,
<&clock_mmss clk_mmss_camss_csi1pix_clk>,
- <&clock_mmss clk_mmss_camss_cphy_csid1_clk>,
- <&clock_mmss clk_csiphy_clk_src>;
+ <&clock_mmss clk_mmss_camss_cphy_csid1_clk>;
clock-names = "mmssnoc_axi", "mnoc_ahb",
"bmic_smmu_ahb", "bmic_smmu_axi",
"camss_ahb_clk", "camss_top_ahb_clk",
- "ispif_ahb_clk", "csi_src_clk", "csi_clk",
- "csi_phy_clk", "csi_ahb_clk", "csi_rdi_clk",
- "csi_pix_clk", "cphy_csid_clk", "cphy_clk_src";
- qcom,clock-rates = <0 0 0 0 0 0 0 256000000 0 0 0 0 0 0
- 256000000>;
+ "ispif_ahb_clk", "csi_src_clk", "csiphy_clk_src",
+ "csi_clk", "csi_ahb_clk", "csi_rdi_clk",
+ "csi_pix_clk", "cphy_csid_clk";
+ qcom,clock-rates = <0 0 0 0 0 0 0 256000000 256000000
+ 0 0 0 0 0>;
status = "ok";
};
@@ -225,21 +223,20 @@
<&clock_mmss clk_mmss_camss_top_ahb_clk>,
<&clock_mmss clk_mmss_camss_ispif_ahb_clk>,
<&clock_mmss clk_csi2_clk_src>,
+ <&clock_mmss clk_csiphy_clk_src>,
<&clock_mmss clk_mmss_camss_csi2_clk>,
- <&clock_mmss clk_mmss_camss_csiphy2_clk>,
<&clock_mmss clk_mmss_camss_csi2_ahb_clk>,
<&clock_mmss clk_mmss_camss_csi2rdi_clk>,
<&clock_mmss clk_mmss_camss_csi2pix_clk>,
- <&clock_mmss clk_mmss_camss_cphy_csid2_clk>,
- <&clock_mmss clk_csiphy_clk_src>;
+ <&clock_mmss clk_mmss_camss_cphy_csid2_clk>;
clock-names = "mmssnoc_axi", "mnoc_ahb",
"bmic_smmu_ahb", "bmic_smmu_axi",
"camss_ahb_clk", "camss_top_ahb_clk",
- "ispif_ahb_clk", "csi_src_clk", "csi_clk",
- "csi_phy_clk", "csi_ahb_clk", "csi_rdi_clk",
- "csi_pix_clk", "cphy_csid_clk", "cphy_clk_src";
- qcom,clock-rates = <0 0 0 0 0 0 0 256000000 0 0 0 0 0 0
- 256000000>;
+ "ispif_ahb_clk", "csi_src_clk", "csiphy_clk_src",
+ "csi_clk", "csi_ahb_clk", "csi_rdi_clk",
+ "csi_pix_clk", "cphy_csid_clk";
+ qcom,clock-rates = <0 0 0 0 0 0 0 256000000 256000000
+ 0 0 0 0 0>;
status = "ok";
};
@@ -264,20 +261,20 @@
<&clock_mmss clk_mmss_camss_top_ahb_clk>,
<&clock_mmss clk_mmss_camss_ispif_ahb_clk>,
<&clock_mmss clk_csi3_clk_src>,
+ <&clock_mmss clk_csiphy_clk_src>,
<&clock_mmss clk_mmss_camss_csi3_clk>,
<&clock_mmss clk_mmss_camss_csi3_ahb_clk>,
<&clock_mmss clk_mmss_camss_csi3rdi_clk>,
<&clock_mmss clk_mmss_camss_csi3pix_clk>,
- <&clock_mmss clk_mmss_camss_cphy_csid1_clk>,
- <&clock_mmss clk_csiphy_clk_src>;
+ <&clock_mmss clk_mmss_camss_cphy_csid3_clk>;
clock-names = "mmssnoc_axi", "mnoc_ahb",
"bmic_smmu_ahb", "bmic_smmu_axi",
"camss_ahb_clk", "camss_top_ahb_clk",
- "ispif_ahb_clk", "csi_src_clk", "csi_clk",
- "csi_ahb_clk", "csi_rdi_clk",
- "csi_pix_clk", "cphy_csid_clk", "cphy_clk_src";
- qcom,clock-rates = <0 0 0 0 0 0 0 256000000 0 0 0 0 0
- 256000000>;
+ "ispif_ahb_clk", "csi_src_clk", "csiphy_clk_src",
+ "csi_clk", "csi_ahb_clk", "csi_rdi_clk",
+ "csi_pix_clk", "cphy_csid_clk";
+ qcom,clock-rates = <0 0 0 0 0 0 0 256000000 256000000
+ 0 0 0 0 0>;
status = "ok";
};
@@ -333,7 +330,7 @@
camss-vdd-supply = <&gdsc_camss_top>;
vdd-supply = <&gdsc_cpp>;
qcom,vdd-names = "smmu-vdd", "camss-vdd", "vdd";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
@@ -344,7 +341,7 @@
<&clock_mmss clk_mmss_fd_ahb_clk>,
<&clock_mmss clk_mmss_camss_cpp_axi_clk>,
<&clock_mmss clk_mmss_camss_cpp_vbif_ahb_clk>;
- clock-names = "mmss_mnoc_maxi_clk",
+ clock-names = "mmssnoc_axi",
"mmss_mnoc_ahb_clk",
"mmss_bimc_smmu_ahb_clk",
"mmss_bimc_smmu_axi_clk",
@@ -355,14 +352,18 @@
"mmss_fd_ahb_clk",
"mmss_camss_cpp_axi_clk",
"mmss_camss_cpp_vbif_ahb_clk";
- qcom,clock-rates = <0 0 0 0 0 0 200000000 0 0 0 0>;
+ qcom,clock-rates =
+ <0 0 0 0 0 0 404000000 0 0 0 0>,
+ <0 0 0 0 0 0 100000000 0 0 0 0>,
+ <0 0 0 0 0 0 404000000 0 0 0 0>,
+ <0 0 0 0 0 0 404000000 0 0 0 0>;
qcom,msm-bus,name = "msm_camera_fd";
qcom,msm-bus,num-cases = <4>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps = <106 512 0 0>,
- <106 512 13000000 13000000>,
- <106 512 45000000 45000000>,
- <106 512 90000000 90000000>;
+ <106 512 1625 0>,
+ <106 512 2995 0>,
+ <106 512 7200 0>;
qcom,fd-vbif-reg-settings = <0x20 0x10000000 0x30000000>,
<0x24 0x10000000 0x30000000>,
<0x28 0x10000000 0x30000000>,
@@ -387,7 +388,6 @@
vdd-supply = <&gdsc_cpp>;
qcom,vdd-names = "smmu-vdd", "camss-vdd", "vdd";
clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
- <&clock_mmss clk_mmss_mnoc_maxi_clk>,
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
<&clock_mmss clk_mmss_camss_ahb_clk>,
<&clock_mmss clk_mmss_camss_top_ahb_clk>,
@@ -398,12 +398,12 @@
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
<&clock_mmss clk_mmss_camss_cpp_vbif_ahb_clk>;
clock-names = "mmssnoc_axi_clk",
- "mnoc_maxi_clk", "mnoc_ahb_clk",
+ "mnoc_ahb_clk",
"camss_ahb_clk", "camss_top_ahb_clk",
"cpp_core_clk", "camss_cpp_ahb_clk",
"camss_cpp_axi_clk", "micro_iface_clk",
"mmss_smmu_axi_clk", "cpp_vbif_ahb_clk";
- qcom,clock-rates = <0 0 0 0 0 200000000 0 0 0 0 0>;
+ qcom,clock-rates = <0 0 0 0 200000000 0 0 0 0 0>;
qcom,min-clock-rate = <200000000>;
qcom,bus-master = <1>;
qcom,vbif-qos-setting = <0x20 0x10000000>,
@@ -455,7 +455,7 @@
qcom,vdd-names = "camss-vdd", "vfe0-vdd",
"vfe1-vdd";
qcom,clock-cntl-support;
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
<&clock_mmss clk_mmss_camss_ahb_clk>,
<&clock_mmss clk_mmss_camss_top_ahb_clk>,
@@ -482,7 +482,7 @@
<&clock_mmss clk_mmss_camss_vfe1_clk>,
<&clock_mmss clk_vfe1_clk_src>,
<&clock_mmss clk_mmss_camss_csi_vfe1_clk>;
- clock-names = "mnoc_maxi_clk", "mnoc_ahb_clk",
+ clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"camss_ahb_clk",
"camss_top_ahb_clk", "ispif_ahb_clk",
"csi0_src_clk", "csi1_src_clk",
@@ -534,7 +534,7 @@
camss-vdd-supply = <&gdsc_camss_top>;
smmu-vdd-supply = <&gdsc_bimc_smmu>;
qcom,vdd-names = "vdd", "camss-vdd", "smmu-vdd";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
@@ -547,7 +547,7 @@
<&clock_mmss clk_mmss_camss_vfe_vbif_axi_clk>,
<&clock_mmss clk_vfe0_clk_src>,
<&clock_mmss clk_mmss_camss_csi_vfe0_clk>;
- clock-names = "mnoc_maxi_clk", "mnoc_ahb_clk",
+ clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"bimc_smmu_ahb_clk", "bimc_smmu_axi_clk",
"camss_ahb_clk", "camss_top_ahb_clk",
"camss_vfe_clk", "camss_vfe_stream_clk",
@@ -614,7 +614,7 @@
camss-vdd-supply = <&gdsc_camss_top>;
smmu-vdd-supply = <&gdsc_bimc_smmu>;
qcom,vdd-names = "vdd", "camss-vdd", "smmu-vdd";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
@@ -627,7 +627,7 @@
<&clock_mmss clk_mmss_camss_vfe_vbif_axi_clk>,
<&clock_mmss clk_vfe1_clk_src>,
<&clock_mmss clk_mmss_camss_csi_vfe1_clk>;
- clock-names = "mnoc_maxi_clk", "mnoc_ahb_clk",
+ clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"bimc_smmu_ahb_clk", "bimc_smmu_axi_clk",
"camss_ahb_clk", "camss_top_ahb_clk",
"camss_vfe_clk", "camss_vfe_stream_clk",
@@ -752,7 +752,7 @@
smmu-vdd-supply = <&gdsc_bimc_smmu>;
camss-vdd-supply = <&gdsc_camss_top>;
qcom,vdd-names = "smmu-vdd", "camss-vdd";
- clock-names = "mmss_mnoc_maxi_clk",
+ clock-names = "mmssnoc_axi",
"mmss_mnoc_ahb_clk",
"mmss_bimc_smmu_ahb_clk",
"mmss_bimc_smmu_axi_clk",
@@ -761,7 +761,7 @@
"core_clk",
"mmss_camss_jpeg_ahb_clk",
"mmss_camss_jpeg_axi_clk";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
@@ -796,7 +796,7 @@
smmu-vdd-supply = <&gdsc_bimc_smmu>;
camss-vdd-supply = <&gdsc_camss_top>;
qcom,vdd-names = "smmu-vdd", "camss-vdd";
- clock-names = "mmss_mnoc_maxi_clk",
+ clock-names = "mmssnoc_axi",
"mmss_mnoc_ahb_clk",
"mmss_bimc_smmu_ahb_clk",
"mmss_bimc_smmu_axi_clk",
@@ -805,7 +805,7 @@
"core_clk",
"mmss_camss_jpeg_ahb_clk",
"mmss_camss_jpeg_axi_clk";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
index ad293d9827d1..085ca0187ee6 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
@@ -376,6 +376,8 @@
qcom,mdss-dsi-bl-max-level = <4095>;
qcom,5v-boost-gpio = <&tlmm 51 0>;
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,partial-update-enabled;
+ qcom,panel-roi-alignment = <4 2 4 2 20 20>;
};
&dsi_sharp_1080_cmd {
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi
index 1267e578f9b4..e140074465ef 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi
@@ -75,6 +75,8 @@
qcom,gpu-qdss-stm = <0x161c0000 0x40000>; // base addr, size
+ qcom,tsens-name = "tsens_tz_sensor12";
+
clocks = <&clock_gfx clk_gpucc_gfx3d_clk>,
<&clock_gcc clk_gcc_gpu_cfg_ahb_clk>,
<&clock_gpu clk_gpucc_rbbmtimer_clk>,
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi
index a4ba9a61cded..d973bc5ed84f 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi
@@ -25,6 +25,7 @@
#include "dsi-panel-sharp-1080p-cmd.dtsi"
#include "dsi-panel-jdi-1080p-video.dtsi"
#include "dsi-panel-sharp-dualmipi-1080p-120hz.dtsi"
+#include "dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi"
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
@@ -156,3 +157,9 @@
qcom,mdss-dsi-t-clk-post = <0x7>;
qcom,mdss-dsi-t-clk-pre = <0x26>;
};
+
+&dsi_dual_jdi_a407_cmd {
+ qcom,mdss-dsi-panel-timings = [00 16 05 05 09 0e 05 05 04 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x06>;
+ qcom,mdss-dsi-t-clk-pre = <0x22>;
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi
index 11571415c02e..af0eb60818fb 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi
@@ -96,8 +96,10 @@
gdsc-supply = <&gdsc_mdss>;
- clocks = <&clock_mmss clk_mmss_mdss_ahb_clk>;
- clock-names = "iface_clk";
+ clocks = <&clock_mmss clk_mmss_mdss_ahb_clk>,
+ <&clock_gcc clk_ln_bb_clk1>,
+ <&clock_gcc clk_gcc_usb3_clkref_clk>;
+ clock-names = "iface_clk", "ref_clk_src", "ref_clk";
clock-rate = <0>;
qcom,platform-supply-entries {
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
index 4fe694069011..ec38e46b1d89 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
@@ -477,15 +477,17 @@
<&clock_mmss clk_mmss_mdss_mdp_clk>,
<&clock_mmss clk_mmss_mdss_hdmi_dp_ahb_clk>,
<&clock_mmss clk_mmss_mdss_dp_aux_clk>,
+ <&clock_gcc clk_ln_bb_clk1>,
+ <&clock_gcc clk_gcc_usb3_clkref_clk>,
<&clock_mmss clk_mmss_mdss_dp_link_clk>,
<&clock_mmss clk_mmss_mdss_dp_link_intf_clk>,
<&clock_mmss clk_mmss_mdss_dp_crypto_clk>,
<&clock_mmss clk_mmss_mdss_dp_pixel_clk>;
clock-names = "core_mnoc_clk", "core_iface_clk", "core_bus_clk",
"core_mdp_core_clk", "core_alt_iface_clk",
- "core_aux_clk", "ctrl_link_clk",
- "ctrl_link_iface_clk", "ctrl_crypto_clk",
- "ctrl_pixel_clk";
+ "core_aux_clk", "core_ref_clk_src", "core_ref_clk",
+ "ctrl_link_clk", "ctrl_link_iface_clk",
+ "ctrl_crypto_clk", "ctrl_pixel_clk";
qcom,dp-usbpd-detection = <&pmicobalt_pdphy>;
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi
index 1ae0ab804eac..5f89985db0a3 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi
@@ -126,3 +126,70 @@
qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft";
};
};
+
+&pmx_mdss {
+ mdss_dsi_active: mdss_dsi_active {
+ mux {
+ pins = "gpio94";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio94";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable = <0>; /* no pull */
+ output-high;
+ };
+ };
+
+ mdss_dsi_suspend: mdss_dsi_suspend {
+ mux {
+ pins = "gpio94";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio94";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "split_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_dual_jdi_a407_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 94 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&mdss_dsi1 {
+ qcom,dsi-pref-prim-pan = <&dsi_dual_jdi_a407_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ qcom,platform-reset-gpio = <&tlmm 94 0>;
+ qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&labibb {
+ status = "ok";
+ qpnp,qpnp-labibb-mode = "lcd";
+};
+
+&dsi_dual_jdi_a407_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi
index 99d80a3b3848..fcc4d6d8ee2d 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi
@@ -19,7 +19,10 @@
reg-names = "csiphy";
interrupts = <0 78 0>;
interrupt-names = "csiphy";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ gdscr-supply = <&gdsc_camss_top>;
+ bimc_smmu-supply = <&gdsc_bimc_smmu>;
+ qcom,cam-vreg-name = "gdscr", "bimc_smmu";
+ clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
@@ -33,13 +36,13 @@
<&clock_mmss clk_mmss_camss_ispif_ahb_clk>,
<&clock_mmss clk_csiphy_clk_src>,
<&clock_mmss clk_mmss_camss_csiphy0_clk>;
- clock-names = "mnoc_maxi", "mnoc_ahb",
+ clock-names = "mmssnoc_axi", "mnoc_ahb",
"bmic_smmu_ahb", "bmic_smmu_axi",
"camss_ahb_clk", "camss_top_ahb_clk",
"csi_src_clk", "csi_clk", "cphy_csid_clk",
"csiphy_timer_src_clk", "csiphy_timer_clk",
"camss_ispif_ahb_clk", "csiphy_clk_src", "csiphy_clk";
- qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 269333333 0
+ qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 200000000 0
0 256000000 0>;
status = "ok";
};
@@ -51,7 +54,10 @@
reg-names = "csiphy";
interrupts = <0 79 0>;
interrupt-names = "csiphy";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ gdscr-supply = <&gdsc_camss_top>;
+ bimc_smmu-supply = <&gdsc_bimc_smmu>;
+ qcom,cam-vreg-name = "gdscr", "bimc_smmu";
+ clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
@@ -65,13 +71,13 @@
<&clock_mmss clk_mmss_camss_ispif_ahb_clk>,
<&clock_mmss clk_csiphy_clk_src>,
<&clock_mmss clk_mmss_camss_csiphy1_clk>;
- clock-names = "mnoc_maxi", "mnoc_ahb",
+ clock-names = "mmssnoc_axi", "mnoc_ahb",
"bmic_smmu_ahb", "bmic_smmu_axi",
"camss_ahb_clk", "camss_top_ahb_clk",
"csi_src_clk", "csi_clk", "cphy_csid_clk",
"csiphy_timer_src_clk", "csiphy_timer_clk",
"camss_ispif_ahb_clk", "csiphy_clk_src", "csiphy_clk";
- qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 269333333 0
+ qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 200000000 0
0 256000000 0>;
status = "ok";
};
@@ -83,7 +89,10 @@
reg-names = "csiphy";
interrupts = <0 80 0>;
interrupt-names = "csiphy";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ gdscr-supply = <&gdsc_camss_top>;
+ bimc_smmu-supply = <&gdsc_bimc_smmu>;
+ qcom,cam-vreg-name = "gdscr", "bimc_smmu";
+ clocks = <&clock_gcc clk_mmssnoc_axi_clk>,
<&clock_mmss clk_mmss_mnoc_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
@@ -97,13 +106,13 @@
<&clock_mmss clk_mmss_camss_ispif_ahb_clk>,
<&clock_mmss clk_csiphy_clk_src>,
<&clock_mmss clk_mmss_camss_csiphy2_clk>;
- clock-names = "mnoc_maxi", "mnoc_ahb",
+ clock-names = "mmssnoc_axi", "mnoc_ahb",
"bmic_smmu_ahb", "bmic_smmu_axi",
"camss_ahb_clk", "camss_top_ahb_clk",
"csi_src_clk", "csi_clk", "cphy_csid_clk",
"csiphy_timer_src_clk", "csiphy_timer_clk",
"camss_ispif_ahb_clk", "csiphy_clk_src", "csiphy_clk";
- qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 269333333 0
+ qcom,clock-rates = <0 0 0 0 0 0 256000000 0 0 200000000 0
0 256000000 0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
index e19fb5f7c233..b255fca6a691 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
@@ -171,12 +171,17 @@
< 2496000 >;
};
+&bwmon {
+ compatible = "qcom,bimc-bwmon4";
+ qcom,hw-timer-hz = <19200000>;
+};
+
&devfreq_cpufreq {
mincpubw-cpufreq {
cpu-to-dev-map-0 =
< 1900800 1525 >;
cpu-to-dev-map-4 =
- < 2419200 1525 >,
+ < 2112000 1525 >,
< 2496000 5195 >;
};
};
@@ -664,11 +669,11 @@
/* SVS2 */
qcom,imem-ab-tbl =
- <200000000 1752000>,
- <269330000 1752000>,
- <355200000 2500000>,
- <444000000 6000000>,
- <533000000 6000000>;
+ <200000000 1560000>,/* imem @ svs2 freq 75 Mhz */
+ <269330000 3570000>,/* imem @ svs freq 171 Mhz */
+ <355200000 3570000>,/* imem @ svs freq 171 Mhz */
+ <444000000 6750000>,/* imem @ nom freq 323 Mhz */
+ <533000000 8490000>;/* imem @ turbo freq 406 Mhz */
};
/* GPU overrides */
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi
index c44e8f976710..f17be7570742 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi
@@ -66,10 +66,10 @@
* corresponding video core frequency.
*/
qcom,imem-ab-tbl =
- <100000000 1752000>, /* imem @ svs2 freq 75 Mhz */
- <186000000 1752000>, /* imem @ svs2 freq 75 Mhz */
- <360000000 2500000>, /* imem @ svs freq 171 Mhz */
- <465000000 6000000>; /* imem @ noimal freq 320 Mhz */
+ <100000000 1560000>, /* imem @ svs2 freq 75 Mhz */
+ <186000000 3570000>, /* imem @ svs freq 171 Mhz */
+ <360000000 6750000>, /* imem @ nom freq 323 Mhz */
+ <465000000 8490000>; /* imem @ turbo freq 406 Mhz */
/* Regulators */
smmu-vdd-supply = <&gdsc_bimc_smmu>;
@@ -124,7 +124,7 @@
qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0_OCMEM>;
qcom,bus-slave = <MSM_BUS_SLAVE_VMEM>;
qcom,bus-governor = "msm-vidc-vmem+";
- qcom,bus-range-kbps = <1000 6776000>;
+ qcom,bus-range-kbps = <1000 8490000>;
};
arm9_bus_ddr {
diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
index 27f9e2ace106..2600fa25b73f 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
@@ -541,14 +541,13 @@
< 13763 /* 1804 MHz */ >;
};
- qcom,cpu-bwmon {
- compatible = "qcom,bimc-bwmon4";
+ bwmon: qcom,cpu-bwmon {
+ compatible = "qcom,bimc-bwmon3";
reg = <0x01008000 0x300>, <0x01001000 0x200>;
reg-names = "base", "global_base";
interrupts = <0 183 4>;
qcom,mport = <0>;
qcom,target-dev = <&cpubw>;
- qcom,hw-timer-hz = <19200000>;
};
mincpubw: qcom,mincpubw {
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi
index 352856965373..b60d4013dad8 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi
@@ -26,6 +26,8 @@
arm,buffer-size = <0x400000>;
arm,sg-enable;
+ coresight-ctis = <&cti0 &cti8>;
+
coresight-name = "coresight-tmc-etr";
clocks = <&clock_gcc RPM_QDSS_CLK>,
@@ -76,6 +78,8 @@
coresight-name = "coresight-tmc-etf";
+ coresight-ctis = <&cti0 &cti8>;
+
clocks = <&clock_gcc RPM_QDSS_CLK>,
<&clock_gcc RPM_QDSS_A_CLK>;
clock-names = "apb_pclk", "core_a_clk";
@@ -161,6 +165,14 @@
<&funnel_merg_in_funnel_in0>;
};
};
+ port@3 {
+ reg = <6>;
+ funnel_in0_in_funnel_qatb: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_qatb_out_funnel_in0>;
+ };
+ };
port@4 {
reg = <7>;
funnel_in0_in_stm: endpoint {
@@ -191,4 +203,294 @@
};
};
};
+
+ cti0: cti@6010000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6010000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti0";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti1: cti@6011000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6011000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti1";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti2: cti@6012000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6012000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti2";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti3: cti@6013000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6013000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti3";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti4: cti@6014000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6014000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti4";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti5: cti@6015000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6015000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti5";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti6: cti@6016000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6016000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti6";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti7: cti@6017000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6017000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti7";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti8: cti@6018000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6018000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti8";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti9: cti@6019000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6019000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti9";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti10: cti@601a000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x601a000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti10";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti11: cti@601b000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x601b000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti11";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti12: cti@601c000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x601c000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti12";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti13: cti@601d000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x601d000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti13";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti14: cti@601e000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x601e000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti14";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ cti15: cti@601f000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x601f000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti15";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+ };
+
+ funnel_qatb: funnel@6005000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6005000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-qatb";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_qatb_out_funnel_in0: endpoint {
+ remote-endpoint =
+ <&funnel_in0_in_funnel_qatb>;
+ };
+ };
+ port@1 {
+ reg = <0>;
+ funnel_qatb_in_tpda: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpda_out_funnel_qatb>;
+ };
+ };
+ };
+ };
+
+ tpda: tpda@6004000 {
+ compatible = "qcom,coresight-tpda";
+ reg = <0x6004000 0x1000>;
+ reg-names = "tpda-base";
+
+ coresight-name = "coresight-tpda";
+
+ qcom,tpda-atid = <65>;
+ qcom,bc-elem-size = <7 32>,
+ <9 32>;
+ qcom,tc-elem-size = <3 32>,
+ <6 32>,
+ <9 32>;
+ qcom,dsb-elem-size = <7 32>,
+ <9 32>;
+ qcom,cmb-elem-size = <3 32>,
+ <4 32>,
+ <5 32>,
+ <9 64>;
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ tpda_out_funnel_qatb: endpoint {
+ remote-endpoint =
+ <&funnel_qatb_in_tpda>;
+ };
+ };
+ port@2 {
+ reg = <5>;
+ tpda_in_tpdm_dcc: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_dcc_out_tpda>;
+ };
+ };
+ };
+ };
+
+ tpdm_dcc: tpdm@7054000 {
+ compatible = "qcom,coresight-tpdm";
+ reg = <0x7054000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-dcc";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+
+ port{
+ tpdm_dcc_out_tpda: endpoint {
+ remote-endpoint = <&tpda_in_tpdm_dcc>;
+ };
+ };
+ };
};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
index 4765a8cb7c79..67748d6683c0 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
@@ -252,6 +252,18 @@
status = "ok";
};
+ wdog: qcom,wdt@17817000 {
+ status = "disabled";
+ compatible = "qcom,msm-watchdog";
+ reg = <0x17817000 0x1000>;
+ reg-names = "wdt-base";
+ interrupts = <0 3 0>, <0 4 0>;
+ qcom,bark-time = <11000>;
+ qcom,pet-time = <10000>;
+ qcom,ipi-ping;
+ qcom,wakeup-enable;
+ };
+
qcom,sps {
compatible = "qcom,msm_sps_4k";
qcom,pipe-attr-ee;
@@ -380,6 +392,16 @@
qcom,mpu-enabled;
};
+ dcc: dcc@10b3000 {
+ compatible = "qcom,dcc";
+ reg = <0x10b3000 0x1000>,
+ <0x10b4000 0x800>;
+ reg-names = "dcc-base", "dcc-ram-base";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>;
+ clock-names = "dcc_clk";
+ };
+
qcom,glink-smem-native-xprt-modem@86000000 {
compatible = "qcom,glink-smem-native-xprt";
reg = <0x86000000 0x200000>,
diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi
index 17551d94896b..3f0d4cc48696 100644
--- a/arch/arm/boot/dts/qcom/msmtriton.dtsi
+++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi
@@ -220,6 +220,15 @@
qcom,pipe-attr-ee;
};
+ tsens: tsens@10ad000 {
+ compatible = "qcom,msmtriton-tsens";
+ reg = <0x10ad000 0x2000>;
+ reg-names = "tsens_physical";
+ interrupts = <0 184 0>, <0 430 0>;
+ interrupt-names = "tsens-upper-lower", "tsens-critical";
+ qcom,sensors = <12>;
+ };
+
uartblsp1dm1: serial@0c170000 {
compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
reg = <0xc170000 0x1000>;
diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig
index 2b35b0f12787..48507eebe9f3 100644
--- a/arch/arm/configs/msmcortex_defconfig
+++ b/arch/arm/configs/msmcortex_defconfig
@@ -261,8 +261,8 @@ CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y
-CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y
+CONFIG_SECURE_TOUCH=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_KEYCHORD=y
diff --git a/arch/arm/configs/msmfalcon_defconfig b/arch/arm/configs/msmfalcon_defconfig
index 2b35b0f12787..db50dce9f9a4 100644
--- a/arch/arm/configs/msmfalcon_defconfig
+++ b/arch/arm/configs/msmfalcon_defconfig
@@ -261,7 +261,6 @@ CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y
-CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
@@ -416,6 +415,9 @@ CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_ARM_SMMU=y
+CONFIG_IOMMU_DEBUG=y
+CONFIG_IOMMU_DEBUG_TRACKING=y
+CONFIG_IOMMU_TESTS=y
CONFIG_QCOM_COMMON_LOG=y
CONFIG_MSM_SMEM=y
CONFIG_QPNP_HAPTIC=y
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 598323a1842e..e683d147816c 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -190,6 +190,13 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
return 0;
}
+static DEFINE_PER_CPU(unsigned long, cpu_efficiency) = SCHED_CAPACITY_SCALE;
+
+unsigned long arch_get_cpu_efficiency(int cpu)
+{
+ return per_cpu(cpu_efficiency, cpu);
+}
+
#ifdef CONFIG_OF
struct cpu_efficiency {
const char *compatible;
@@ -266,6 +273,7 @@ static int __init parse_dt_topology(void)
for_each_possible_cpu(cpu) {
const u32 *rate;
int len;
+ u32 efficiency;
/* too early to use cpu->of_node */
cn = of_get_cpu_node(cpu, NULL);
@@ -274,12 +282,26 @@ static int __init parse_dt_topology(void)
continue;
}
- for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++)
- if (of_device_is_compatible(cn, cpu_eff->compatible))
- break;
+ /*
+ * The CPU efficiency value passed from the device tree
+ * overrides the value defined in the table_efficiency[]
+ */
+ if (of_property_read_u32(cn, "efficiency", &efficiency) < 0) {
+
+ for (cpu_eff = table_efficiency;
+ cpu_eff->compatible; cpu_eff++)
- if (cpu_eff->compatible == NULL)
- continue;
+ if (of_device_is_compatible(cn,
+ cpu_eff->compatible))
+ break;
+
+ if (cpu_eff->compatible == NULL)
+ continue;
+
+ efficiency = cpu_eff->efficiency;
+ }
+
+ per_cpu(cpu_efficiency, cpu) = efficiency;
rate = of_get_property(cn, "clock-frequency", &len);
if (!rate || len != 4) {
@@ -288,7 +310,7 @@ static int __init parse_dt_topology(void)
continue;
}
- capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency;
+ capacity = ((be32_to_cpup(rate)) >> 20) * efficiency;
/* Save min capacity of the system */
if (capacity < min_capacity)
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index d6ed9ac56bf1..d4d355531169 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -16,7 +16,9 @@ config ARCH_MSMFALCON
select MULTI_IRQ_HANDLER
select HAVE_ARM_ARCH_TIMER
select MAY_HAVE_SPARSE_IRQ
- select COMMON_CLK_MSM
+ select COMMON_CLK
+ select COMMON_CLK_QCOM
+ select QCOM_GDSC
select PINCTRL_MSM_TLMM
select USE_PINCTRL_IRQ
select MSM_PM if PM
@@ -31,6 +33,7 @@ config ARCH_MSMFALCON
select MSM_QDSP6V2_CODECS
select MSM_AUDIO_QDSP6V2 if SND_SOC
select MSM_RPM_SMD
+ select GENERIC_IRQ_MIGRATION
select MSM_JTAGV8 if CORESIGHT_ETMV4
help
This enables support for the MSMFALCON chipset. If you do not
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index ee4efe58d0c8..94c0bf30c284 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -89,7 +89,9 @@ config ARCH_MSMHAMSTER
config ARCH_MSMFALCON
bool "Enable Support for Qualcomm Technologies Inc MSMFALCON"
depends on ARCH_QCOM
- select COMMON_CLK_MSM
+ select COMMON_CLK
+ select COMMON_CLK_QCOM
+ select QCOM_GDSC
help
This enables support for the MSMFALCON chipset.
If you do not wish to build a kernel that runs
@@ -98,7 +100,9 @@ config ARCH_MSMFALCON
config ARCH_MSMTRITON
bool "Enable Support for Qualcomm Technologies Inc MSMTRITON"
depends on ARCH_QCOM
- select COMMON_CLK_MSM
+ select COMMON_CLK
+ select COMMON_CLK_QCOM
+ select QCOM_GDSC
help
This enables support for the MSMTRITON chipset.
If you do not wish to build a kernel that runs
diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig
index 7f31331933bb..5f8b02904d49 100644
--- a/arch/arm64/configs/msm-perf_defconfig
+++ b/arch/arm64/configs/msm-perf_defconfig
@@ -282,7 +282,6 @@ CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y
-CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=y
@@ -548,6 +547,9 @@ CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=y
CONFIG_FUSE_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig
index 89bee1463421..c1c0ae9da001 100644
--- a/arch/arm64/configs/msm_defconfig
+++ b/arch/arm64/configs/msm_defconfig
@@ -269,7 +269,6 @@ CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y
-CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=y
@@ -555,6 +554,9 @@ CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=y
CONFIG_FUSE_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 50c8848bc6f9..036d6aa5c062 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -44,7 +44,6 @@ CONFIG_PARTITION_ADVANCED=y
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSMCOBALT=y
CONFIG_ARCH_MSMHAMSTER=y
-CONFIG_ARCH_MSMFALCON=y
CONFIG_PCI=y
CONFIG_PCI_MSM=y
CONFIG_SCHED_MC=y
@@ -274,6 +273,7 @@ CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y
+CONFIG_SECURE_TOUCH=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_UINPUT=y
@@ -555,6 +555,9 @@ CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=y
CONFIG_FUSE_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index 3d1a01491c0c..77f0129776a3 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -45,7 +45,6 @@ CONFIG_PARTITION_ADVANCED=y
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSMCOBALT=y
CONFIG_ARCH_MSMHAMSTER=y
-CONFIG_ARCH_MSMFALCON=y
CONFIG_PCI=y
CONFIG_PCI_MSM=y
CONFIG_SCHED_MC=y
@@ -275,6 +274,7 @@ CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y
+CONFIG_SECURE_TOUCH=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_KEYCHORD=y
@@ -575,6 +575,9 @@ CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=y
CONFIG_FUSE_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
diff --git a/arch/arm64/configs/msmfalcon-perf_defconfig b/arch/arm64/configs/msmfalcon-perf_defconfig
index 6ebd60b43c71..1bc352704893 100644
--- a/arch/arm64/configs/msmfalcon-perf_defconfig
+++ b/arch/arm64/configs/msmfalcon-perf_defconfig
@@ -41,8 +41,6 @@ CONFIG_MODULE_SIG_FORCE=y
CONFIG_MODULE_SIG_SHA512=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_ARCH_QCOM=y
-CONFIG_ARCH_MSMCOBALT=y
-CONFIG_ARCH_MSMHAMSTER=y
CONFIG_ARCH_MSMFALCON=y
CONFIG_ARCH_MSMTRITON=y
CONFIG_PCI=y
@@ -272,7 +270,6 @@ CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y
-CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
@@ -476,7 +473,6 @@ CONFIG_RMNET_IPA3=y
CONFIG_GPIO_USB_DETECT=y
CONFIG_SEEMP_CORE=y
CONFIG_USB_BAM=y
-CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_ARM_SMMU=y
diff --git a/arch/arm64/configs/msmfalcon_defconfig b/arch/arm64/configs/msmfalcon_defconfig
index 01324d89e79e..34f0da3c37a4 100644
--- a/arch/arm64/configs/msmfalcon_defconfig
+++ b/arch/arm64/configs/msmfalcon_defconfig
@@ -42,8 +42,6 @@ CONFIG_MODULE_SIG_SHA512=y
CONFIG_PARTITION_ADVANCED=y
# CONFIG_IOSCHED_DEADLINE is not set
CONFIG_ARCH_QCOM=y
-CONFIG_ARCH_MSMCOBALT=y
-CONFIG_ARCH_MSMHAMSTER=y
CONFIG_ARCH_MSMFALCON=y
CONFIG_ARCH_MSMTRITON=y
CONFIG_PCI=y
@@ -273,7 +271,6 @@ CONFIG_KEYBOARD_GPIO=y
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y
-CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21=y
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
@@ -486,7 +483,6 @@ CONFIG_RMNET_IPA3=y
CONFIG_GPIO_USB_DETECT=y
CONFIG_SEEMP_CORE=y
CONFIG_USB_BAM=y
-CONFIG_MSM_MDSS_PLL=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index e5389bc981ee..eff70892dada 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -49,6 +49,17 @@ static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
return prot;
}
+static int __get_iommu_pgprot(struct dma_attrs *attrs, int prot,
+ bool coherent)
+{
+ if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs))
+ prot |= IOMMU_NOEXEC;
+ if (coherent)
+ prot |= IOMMU_CACHE;
+
+ return prot;
+}
+
static struct gen_pool *atomic_pool;
#define NO_KERNEL_MAPPING_DUMMY 0x2222
#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K
@@ -1153,7 +1164,7 @@ static int arm_dma_set_mask(struct device *dev, u64 dma_mask)
/* IOMMU */
static void __dma_clear_buffer(struct page *page, size_t size,
- struct dma_attrs *attrs)
+ struct dma_attrs *attrs, bool is_coherent)
{
/*
* Ensure that the allocated pages are zeroed, and that any data
@@ -1162,7 +1173,8 @@ static void __dma_clear_buffer(struct page *page, size_t size,
void *ptr = page_address(page);
if (!dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs))
memset(ptr, 0, size);
- dmac_flush_range(ptr, ptr + size);
+ if (!is_coherent)
+ dmac_flush_range(ptr, ptr + size);
}
static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping,
@@ -1212,6 +1224,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
size_t count = size >> PAGE_SHIFT;
size_t array_size = count * sizeof(struct page *);
int i = 0;
+ bool is_coherent = is_device_dma_coherent(dev);
if (array_size <= PAGE_SIZE)
pages = kzalloc(array_size, gfp);
@@ -1228,7 +1241,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
if (!page)
goto error;
- __dma_clear_buffer(page, size, attrs);
+ __dma_clear_buffer(page, size, attrs, is_coherent);
for (i = 0; i < count; i++)
pages[i] = page + i;
@@ -1257,7 +1270,8 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
pages[i + j] = pages[i] + j;
}
- __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs);
+ __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs,
+ is_coherent);
i += 1 << order;
count -= 1 << order;
}
@@ -1322,9 +1336,8 @@ static dma_addr_t __iommu_create_mapping(struct device *dev,
dma_addr = __alloc_iova(mapping, size);
if (dma_addr == DMA_ERROR_CODE)
return dma_addr;
-
- if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs))
- prot |= IOMMU_NOEXEC;
+ prot = __get_iommu_pgprot(attrs, prot,
+ is_device_dma_coherent(dev));
iova = dma_addr;
for (i = 0; i < count; ) {
@@ -1404,6 +1417,7 @@ static void *__iommu_alloc_atomic(struct device *dev, size_t size,
size_t array_size = count * sizeof(struct page *);
int i;
void *addr;
+ bool coherent = is_device_dma_coherent(dev);
if (array_size <= PAGE_SIZE)
pages = kzalloc(array_size, gfp);
@@ -1413,7 +1427,13 @@ static void *__iommu_alloc_atomic(struct device *dev, size_t size,
if (!pages)
return NULL;
- addr = __alloc_from_pool(size, &page, gfp);
+ if (coherent) {
+ page = alloc_pages(gfp, get_order(size));
+ addr = page ? page_address(page) : NULL;
+ } else {
+ addr = __alloc_from_pool(size, &page, gfp);
+ }
+
if (!addr)
goto err_free;
@@ -1428,7 +1448,10 @@ static void *__iommu_alloc_atomic(struct device *dev, size_t size,
return addr;
err_mapping:
- __free_from_pool(addr, size);
+ if (coherent)
+ __free_pages(page, get_order(size));
+ else
+ __free_from_pool(addr, size);
err_free:
kvfree(pages);
return NULL;
@@ -1444,7 +1467,8 @@ static void __iommu_free_atomic(struct device *dev, void *cpu_addr,
static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
{
- pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, false);
+ bool coherent = is_device_dma_coherent(dev);
+ pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, coherent);
struct page **pages;
void *addr = NULL;
@@ -1495,8 +1519,10 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
unsigned long uaddr = vma->vm_start;
unsigned long usize = vma->vm_end - vma->vm_start;
struct page **pages = __iommu_get_pages(cpu_addr, attrs);
+ bool coherent = is_device_dma_coherent(dev);
- vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, false);
+ vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot,
+ coherent);
if (!pages)
return -ENXIO;
@@ -1577,121 +1603,6 @@ static int __dma_direction_to_prot(enum dma_data_direction dir)
return prot;
}
-/*
- * Map a part of the scatter-gather list into contiguous io address space
- */
-static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
- size_t size, dma_addr_t *handle,
- enum dma_data_direction dir, struct dma_attrs *attrs,
- bool is_coherent)
-{
- struct dma_iommu_mapping *mapping = dev->archdata.mapping;
- dma_addr_t iova, iova_base;
- int ret = 0;
- unsigned int count;
- struct scatterlist *s;
- int prot;
-
- size = PAGE_ALIGN(size);
- *handle = DMA_ERROR_CODE;
-
- iova_base = iova = __alloc_iova(mapping, size);
- if (iova == DMA_ERROR_CODE)
- return -ENOMEM;
-
- for (count = 0, s = sg; count < (size >> PAGE_SHIFT); s = sg_next(s)) {
- phys_addr_t phys = page_to_phys(sg_page(s));
- unsigned int len = PAGE_ALIGN(s->offset + s->length);
-
- if (!is_coherent &&
- !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
- __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length,
- dir);
-
- prot = __dma_direction_to_prot(dir);
- if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs))
- prot |= IOMMU_NOEXEC;
-
- ret = iommu_map(mapping->domain, iova, phys, len, prot);
- if (ret < 0)
- goto fail;
- count += len >> PAGE_SHIFT;
- iova += len;
- }
- *handle = iova_base;
-
- return 0;
-fail:
- iommu_unmap(mapping->domain, iova_base, count * PAGE_SIZE);
- __free_iova(mapping, iova_base, size);
- return ret;
-}
-
-static int __iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
- enum dma_data_direction dir, struct dma_attrs *attrs,
- bool is_coherent)
-{
- struct scatterlist *s = sg, *dma = sg, *start = sg;
- int i, count = 0;
- unsigned int offset = s->offset;
- unsigned int size = s->offset + s->length;
- unsigned int max = dma_get_max_seg_size(dev);
-
- for (i = 1; i < nents; i++) {
- s = sg_next(s);
-
- s->dma_address = DMA_ERROR_CODE;
- s->dma_length = 0;
-
- if (s->offset || (size & ~PAGE_MASK)
- || size + s->length > max) {
- if (__map_sg_chunk(dev, start, size, &dma->dma_address,
- dir, attrs, is_coherent) < 0)
- goto bad_mapping;
-
- dma->dma_address += offset;
- dma->dma_length = size - offset;
-
- size = offset = s->offset;
- start = s;
- dma = sg_next(dma);
- count += 1;
- }
- size += s->length;
- }
- if (__map_sg_chunk(dev, start, size, &dma->dma_address, dir, attrs,
- is_coherent) < 0)
- goto bad_mapping;
-
- dma->dma_address += offset;
- dma->dma_length = size - offset;
-
- return count+1;
-
-bad_mapping:
- for_each_sg(sg, s, count, i)
- __iommu_remove_mapping(dev, sg_dma_address(s), sg_dma_len(s));
- return 0;
-}
-
-/**
- * arm_coherent_iommu_map_sg - map a set of SG buffers for streaming mode DMA
- * @dev: valid struct device pointer
- * @sg: list of buffers
- * @nents: number of buffers to map
- * @dir: DMA transfer direction
- *
- * Map a set of i/o coherent buffers described by scatterlist in streaming
- * mode for DMA. The scatter gather list elements are merged together (if
- * possible) and tagged with the appropriate dma address and length. They are
- * obtained via sg_dma_{address,length}.
- */
-int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg,
- int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
-{
- return __iommu_map_sg(dev, sg, nents, dir, attrs, true);
-}
-
/**
* arm_iommu_map_sg - map a set of SG buffers for streaming mode DMA
* @dev: valid struct device pointer
@@ -1722,9 +1633,8 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg,
dev_err(dev, "Couldn't allocate iova for sg %p\n", sg);
return 0;
}
-
- if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs))
- prot |= IOMMU_NOEXEC;
+ prot = __get_iommu_pgprot(attrs, prot,
+ is_device_dma_coherent(dev));
ret = iommu_map_sg(mapping->domain, iova, sg, nents, prot);
if (ret != total_length) {
@@ -1741,40 +1651,6 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg,
return nents;
}
-static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
- int nents, enum dma_data_direction dir, struct dma_attrs *attrs,
- bool is_coherent)
-{
- struct scatterlist *s;
- int i;
-
- for_each_sg(sg, s, nents, i) {
- if (sg_dma_len(s))
- __iommu_remove_mapping(dev, sg_dma_address(s),
- sg_dma_len(s));
- if (!is_coherent &&
- !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
- __dma_page_dev_to_cpu(sg_page(s), s->offset,
- s->length, dir);
- }
-}
-
-/**
- * arm_coherent_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg
- * @dev: valid struct device pointer
- * @sg: list of buffers
- * @nents: number of buffers to unmap (same as was passed to dma_map_sg)
- * @dir: DMA transfer direction (same as was passed to dma_map_sg)
- *
- * Unmap a set of streaming mode DMA translations. Again, CPU access
- * rules concerning calls here are the same as for dma_unmap_single().
- */
-void arm_coherent_iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
- int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
-{
- __iommu_unmap_sg(dev, sg, nents, dir, attrs, true);
-}
-
/**
* arm_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg
* @dev: valid struct device pointer
@@ -1812,6 +1688,9 @@ void arm_iommu_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
struct scatterlist *s;
int i;
+ if (is_device_dma_coherent(dev))
+ return;
+
for_each_sg(sg, s, nents, i)
__dma_page_dev_to_cpu(sg_page(s), s->offset, s->length, dir);
@@ -1830,6 +1709,9 @@ void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
struct scatterlist *s;
int i;
+ if (is_device_dma_coherent(dev))
+ return;
+
for_each_sg(sg, s, nents, i)
__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
}
@@ -1858,8 +1740,8 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev,
return dma_addr;
prot = __dma_direction_to_prot(dir);
- if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs))
- prot |= IOMMU_NOEXEC;
+ prot = __get_iommu_pgprot(attrs, prot,
+ is_device_dma_coherent(dev));
ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len,
prot);
@@ -1886,38 +1768,14 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
- if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+ if (!is_device_dma_coherent(dev) &&
+ !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
__dma_page_cpu_to_dev(page, offset, size, dir);
return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs);
}
/**
- * arm_coherent_iommu_unmap_page
- * @dev: valid struct device pointer
- * @handle: DMA address of buffer
- * @size: size of buffer (same as passed to dma_map_page)
- * @dir: DMA transfer direction (same as passed to dma_map_page)
- *
- * Coherent IOMMU aware version of arm_dma_unmap_page()
- */
-static void arm_coherent_iommu_unmap_page(struct device *dev, dma_addr_t handle,
- size_t size, enum dma_data_direction dir,
- struct dma_attrs *attrs)
-{
- struct dma_iommu_mapping *mapping = dev->archdata.mapping;
- dma_addr_t iova = handle & PAGE_MASK;
- int offset = handle & ~PAGE_MASK;
- int len = PAGE_ALIGN(size + offset);
-
- if (!iova)
- return;
-
- iommu_unmap(mapping->domain, iova, len);
- __free_iova(mapping, iova, len);
-}
-
-/**
* arm_iommu_unmap_page
* @dev: valid struct device pointer
* @handle: DMA address of buffer
@@ -1940,7 +1798,8 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle,
if (!iova)
return;
- if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+ if (!(is_device_dma_coherent(dev) ||
+ dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)))
__dma_page_dev_to_cpu(page, offset, size, dir);
iommu_unmap(mapping->domain, iova, len);
@@ -1959,7 +1818,8 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev,
if (!iova)
return;
- __dma_page_dev_to_cpu(page, offset, size, dir);
+ if (!is_device_dma_coherent(dev))
+ __dma_page_dev_to_cpu(page, offset, size, dir);
}
static void arm_iommu_sync_single_for_device(struct device *dev,
@@ -1974,7 +1834,8 @@ static void arm_iommu_sync_single_for_device(struct device *dev,
if (!iova)
return;
- __dma_page_cpu_to_dev(page, offset, size, dir);
+ if (!is_device_dma_coherent(dev))
+ __dma_page_cpu_to_dev(page, offset, size, dir);
}
static int arm_iommu_dma_supported(struct device *dev, u64 mask)
@@ -2016,22 +1877,6 @@ const struct dma_map_ops iommu_ops = {
.mapping_error = arm_iommu_mapping_error,
};
-const struct dma_map_ops iommu_coherent_ops = {
- .alloc = arm_iommu_alloc_attrs,
- .free = arm_iommu_free_attrs,
- .mmap = arm_iommu_mmap_attrs,
- .get_sgtable = arm_iommu_get_sgtable,
-
- .map_page = arm_coherent_iommu_map_page,
- .unmap_page = arm_coherent_iommu_unmap_page,
-
- .map_sg = arm_coherent_iommu_map_sg,
- .unmap_sg = arm_coherent_iommu_unmap_sg,
-
- .set_dma_mask = arm_dma_set_mask,
- .dma_supported = arm_iommu_dma_supported,
-};
-
/**
* arm_iommu_create_mapping
* @bus: pointer to the bus holding the client device (for IOMMU calls)
diff --git a/drivers/char/diag/diagfwd_glink.c b/drivers/char/diag/diagfwd_glink.c
index fea1b74aacae..a2ffabe43c86 100644
--- a/drivers/char/diag/diagfwd_glink.c
+++ b/drivers/char/diag/diagfwd_glink.c
@@ -413,19 +413,16 @@ static int diag_glink_write(void *ctxt, unsigned char *buf, int len)
return -ENODEV;
}
- err = wait_event_interruptible(glink_info->wait_q,
- atomic_read(&glink_info->tx_intent_ready));
- if (err) {
- diagfwd_write_buffer_done(glink_info->fwd_ctxt, buf);
- return -ERESTARTSYS;
- }
-
- atomic_dec(&glink_info->tx_intent_ready);
- err = glink_tx(glink_info->hdl, glink_info, buf, len, tx_flags);
- if (!err) {
- DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s wrote to glink, len: %d\n",
- glink_info->name, len);
- }
+ if (atomic_read(&glink_info->tx_intent_ready)) {
+ atomic_dec(&glink_info->tx_intent_ready);
+ err = glink_tx(glink_info->hdl, glink_info, buf, len, tx_flags);
+ if (!err) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "%s wrote to glink, len: %d\n",
+ glink_info->name, len);
+ }
+ } else
+ err = -ENOMEM;
return err;
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 066890aebf39..22b9e05086bd 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -751,7 +751,9 @@ int diagfwd_write(uint8_t peripheral, uint8_t type, void *buf, int len)
if (!err)
fwd_info->write_bytes += len;
-
+ else
+ if (fwd_info->transport == TRANSPORT_GLINK)
+ diagfwd_write_buffer_done(fwd_info, buf_ptr);
return err;
}
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index 68274765a6c1..0d733f49f184 100644
--- a/drivers/clk/msm/clock-osm.c
+++ b/drivers/clk/msm/clock-osm.c
@@ -460,7 +460,7 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate)
}
pr_debug("rate: %lu --> index %d\n", rate, index);
- if (cpuclk->llm_sw_overr) {
+ if (cpuclk->llm_sw_overr[0]) {
clk_osm_write_reg(cpuclk, cpuclk->llm_sw_overr[0],
LLM_SW_OVERRIDE_REG);
clk_osm_write_reg(cpuclk, cpuclk->llm_sw_overr[1],
@@ -471,7 +471,7 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate)
/* Choose index and send request to OSM hardware */
clk_osm_write_reg(cpuclk, index, DCVS_PERF_STATE_DESIRED_REG);
- if (cpuclk->llm_sw_overr) {
+ if (cpuclk->llm_sw_overr[0]) {
udelay(1);
clk_osm_write_reg(cpuclk, cpuclk->llm_sw_overr[2],
LLM_SW_OVERRIDE_REG);
@@ -1882,8 +1882,8 @@ static void clk_osm_apm_vc_setup(struct clk_osm *c)
}
scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(72),
c->apm_crossover_vc);
- clk_osm_write_reg(c, c->apm_threshold_vc,
- SEQ_REG(15));
+ scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(15),
+ c->apm_threshold_vc);
scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(31),
c->apm_threshold_vc != 0 ?
c->apm_threshold_vc - 1 : 0xff);
diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c
index d5d55a58bf7f..c4f77e01b682 100644
--- a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c
+++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c
@@ -145,7 +145,7 @@ static void hdmi_cobalt_get_div(struct cobalt_reg_cfg *cfg, unsigned long pclk)
u32 const min_freq = 8000, max_freq = 12000;
u32 const cmp_cnt = 1024;
u32 const th_min = 500, th_max = 1000;
- u64 bit_clk = pclk * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
+ u64 bit_clk = ((u64)pclk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
u32 half_rate_mode = 0;
u32 freq_optimal, list_elements;
int optimal_index;
@@ -161,7 +161,7 @@ find_optimal_index:
for (i = 0; i < sz_ratio; i++) {
for (j = 0; j < sz_band; j++) {
- u64 freq = (bit_clk / (1 << half_rate_mode));
+ u64 freq = div_u64(bit_clk, (1 << half_rate_mode));
freq *= (ratio_list[i] * (1 << band_list[j]));
do_div(freq, (u64) HDMI_MHZ_TO_HZ);
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index e3760969848d..da02ab499bff 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -23,6 +23,7 @@ struct freq_tbl {
u8 pre_div;
u16 m;
u16 n;
+ unsigned long src_freq;
};
/**
@@ -184,5 +185,6 @@ extern const struct clk_ops clk_byte_ops;
extern const struct clk_ops clk_byte2_ops;
extern const struct clk_ops clk_pixel_ops;
extern const struct clk_ops clk_gfx3d_ops;
+extern const struct clk_ops clk_gfx3d_src_ops;
#endif
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 4d5081c2b6d1..a075859771d3 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -16,6 +16,7 @@
#include <linux/err.h>
#include <linux/bug.h>
#include <linux/export.h>
+#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/regmap.h>
@@ -865,3 +866,80 @@ const struct clk_ops clk_gfx3d_ops = {
.determine_rate = clk_gfx3d_determine_rate,
};
EXPORT_SYMBOL_GPL(clk_gfx3d_ops);
+
+static int clk_gfx3d_src_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_rate_request parent_req = { };
+ struct clk_hw *p1, *p3, *xo, *curr_p;
+ const struct freq_tbl *f;
+ int ret;
+
+ xo = clk_hw_get_parent_by_index(hw, 0);
+ if (req->rate == clk_hw_get_rate(xo)) {
+ req->best_parent_hw = xo;
+ req->best_parent_rate = req->rate;
+ return 0;
+ }
+
+ f = qcom_find_freq(rcg->freq_tbl, req->rate);
+ if (!f || (req->rate != f->freq))
+ return -EINVAL;
+
+ /* Indexes of source from the parent map */
+ p1 = clk_hw_get_parent_by_index(hw, 1);
+ p3 = clk_hw_get_parent_by_index(hw, 2);
+
+ curr_p = clk_hw_get_parent(hw);
+ parent_req.rate = f->src_freq;
+
+ if (curr_p == xo || curr_p == p3)
+ req->best_parent_hw = p1;
+ else if (curr_p == p1)
+ req->best_parent_hw = p3;
+
+ parent_req.best_parent_hw = req->best_parent_hw;
+
+ ret = __clk_determine_rate(req->best_parent_hw, &parent_req);
+ if (ret)
+ return ret;
+
+ req->best_parent_rate = parent_req.rate;
+
+ return 0;
+}
+
+static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate, u8 index)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ const struct freq_tbl *f;
+ u32 cfg;
+ int ret;
+
+ cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
+
+ f = qcom_find_freq(rcg->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ /* Update the RCG-DIV */
+ cfg |= f->pre_div << CFG_SRC_DIV_SHIFT;
+
+ ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg);
+ if (ret)
+ return ret;
+
+ return update_config(rcg);
+}
+
+const struct clk_ops clk_gfx3d_src_ops = {
+ .is_enabled = clk_rcg2_is_enabled,
+ .get_parent = clk_rcg2_get_parent,
+ .set_parent = clk_rcg2_set_parent,
+ .recalc_rate = clk_rcg2_recalc_rate,
+ .set_rate = clk_gfx3d_set_rate,
+ .set_rate_and_parent = clk_gfx3d_src_set_rate_and_parent,
+ .determine_rate = clk_gfx3d_src_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_gfx3d_src_ops);
diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c
index 8ed5115efc3b..ac007ec667bb 100644
--- a/drivers/clk/qcom/clk-smd-rpm.c
+++ b/drivers/clk/qcom/clk-smd-rpm.c
@@ -595,9 +595,84 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8996 = {
.num_clks = ARRAY_SIZE(msm8996_clks),
};
+/* msmfalcon */
+DEFINE_CLK_SMD_RPM_BRANCH(msmfalcon, cxo, cxo_a, QCOM_SMD_RPM_MISC_CLK, 0,
+ 19200000);
+DEFINE_CLK_SMD_RPM(msmfalcon, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
+DEFINE_CLK_SMD_RPM(msmfalcon, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2);
+DEFINE_CLK_SMD_RPM(msmfalcon, cnoc_periph_clk, cnoc_periph_a_clk,
+ QCOM_SMD_RPM_BUS_CLK, 0);
+DEFINE_CLK_SMD_RPM(msmfalcon, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
+DEFINE_CLK_SMD_RPM(msmfalcon, mmssnoc_axi_rpm_clk, mmssnoc_axi_rpm_a_clk,
+ QCOM_SMD_RPM_MMAXI_CLK, 0);
+DEFINE_CLK_SMD_RPM(msmfalcon, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0);
+DEFINE_CLK_SMD_RPM(msmfalcon, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0);
+DEFINE_CLK_SMD_RPM(msmfalcon, aggre2_noc_clk, aggre2_noc_a_clk,
+ QCOM_SMD_RPM_AGGR_CLK, 2);
+DEFINE_CLK_SMD_RPM_QDSS(msmfalcon, qdss_clk, qdss_a_clk,
+ QCOM_SMD_RPM_MISC_CLK, 1);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msmfalcon, rf_clk2, rf_clk2_ao, 5);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msmfalcon, div_clk1, div_clk1_ao, 0xb);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msmfalcon, ln_bb_clk1, ln_bb_clk1_ao, 0x1);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msmfalcon, ln_bb_clk2, ln_bb_clk2_ao, 0x2);
+DEFINE_CLK_SMD_RPM_XO_BUFFER(msmfalcon, ln_bb_clk3, ln_bb_clk3_ao, 0x3);
+
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msmfalcon, rf_clk2_pin, rf_clk2_a_pin, 5);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msmfalcon, ln_bb_clk1_pin,
+ ln_bb_clk1_pin_ao, 0x1);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msmfalcon, ln_bb_clk2_pin,
+ ln_bb_clk2_pin_ao, 0x2);
+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msmfalcon, ln_bb_clk3_pin,
+ ln_bb_clk3_pin_ao, 0x3);
+static struct clk_hw *msmfalcon_clks[] = {
+ [RPM_XO_CLK_SRC] = &msmfalcon_cxo.hw,
+ [RPM_XO_A_CLK_SRC] = &msmfalcon_cxo_a.hw,
+ [RPM_SNOC_CLK] = &msmfalcon_snoc_clk.hw,
+ [RPM_SNOC_A_CLK] = &msmfalcon_snoc_a_clk.hw,
+ [RPM_BIMC_CLK] = &msmfalcon_bimc_clk.hw,
+ [RPM_BIMC_A_CLK] = &msmfalcon_bimc_a_clk.hw,
+ [RPM_QDSS_CLK] = &msmfalcon_qdss_clk.hw,
+ [RPM_QDSS_A_CLK] = &msmfalcon_qdss_a_clk.hw,
+ [RPM_RF_CLK2_PIN] = &msmfalcon_rf_clk2_pin.hw,
+ [RPM_RF_CLK2_A_PIN] = &msmfalcon_rf_clk2_a_pin.hw,
+ [RPM_AGGR2_NOC_CLK] = &msmfalcon_aggre2_noc_clk.hw,
+ [RPM_AGGR2_NOC_A_CLK] = &msmfalcon_aggre2_noc_a_clk.hw,
+ [RPM_CNOC_CLK] = &msmfalcon_cnoc_clk.hw,
+ [RPM_CNOC_A_CLK] = &msmfalcon_cnoc_a_clk.hw,
+ [RPM_MMAXI_CLK] = &msmfalcon_mmssnoc_axi_rpm_clk.hw,
+ [RPM_MMAXI_A_CLK] = &msmfalcon_mmssnoc_axi_rpm_a_clk.hw,
+ [RPM_IPA_CLK] = &msmfalcon_ipa_clk.hw,
+ [RPM_IPA_A_CLK] = &msmfalcon_ipa_a_clk.hw,
+ [RPM_CE1_CLK] = &msmfalcon_ce1_clk.hw,
+ [RPM_CE1_A_CLK] = &msmfalcon_ce1_a_clk.hw,
+ [RPM_DIV_CLK1] = &msmfalcon_div_clk1.hw,
+ [RPM_DIV_CLK1_AO] = &msmfalcon_div_clk1_ao.hw,
+ [RPM_LN_BB_CLK1] = &msmfalcon_ln_bb_clk1.hw,
+ [RPM_LN_BB_CLK1] = &msmfalcon_ln_bb_clk1_ao.hw,
+ [RPM_LN_BB_CLK1_PIN] = &msmfalcon_ln_bb_clk1_pin.hw,
+ [RPM_LN_BB_CLK1_PIN_AO] = &msmfalcon_ln_bb_clk1_pin_ao.hw,
+ [RPM_LN_BB_CLK2] = &msmfalcon_ln_bb_clk2.hw,
+ [RPM_LN_BB_CLK2_AO] = &msmfalcon_ln_bb_clk2_ao.hw,
+ [RPM_LN_BB_CLK2_PIN] = &msmfalcon_ln_bb_clk2_pin.hw,
+ [RPM_LN_BB_CLK2_PIN_AO] = &msmfalcon_ln_bb_clk2_pin_ao.hw,
+ [RPM_LN_BB_CLK3] = &msmfalcon_ln_bb_clk3.hw,
+ [RPM_LN_BB_CLK3_AO] = &msmfalcon_ln_bb_clk3_ao.hw,
+ [RPM_LN_BB_CLK3_PIN] = &msmfalcon_ln_bb_clk3_pin.hw,
+ [RPM_LN_BB_CLK3_PIN_AO] = &msmfalcon_ln_bb_clk3_pin_ao.hw,
+ [RPM_CNOC_PERIPH_CLK] = &msmfalcon_cnoc_periph_clk.hw,
+ [RPM_CNOC_PERIPH_A_CLK] = &msmfalcon_cnoc_periph_a_clk.hw,
+};
+
+static const struct rpm_smd_clk_desc rpm_clk_msmfalcon = {
+ .clks = msmfalcon_clks,
+ .num_rpm_clks = RPM_CNOC_PERIPH_A_CLK,
+ .num_clks = ARRAY_SIZE(msmfalcon_clks),
+};
+
static const struct of_device_id rpm_smd_clk_match_table[] = {
{ .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916},
{ .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996},
+ { .compatible = "qcom,rpmcc-msmfalcon", .data = &rpm_clk_msmfalcon},
{ }
};
MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table);
@@ -608,17 +683,23 @@ static int rpm_smd_clk_probe(struct platform_device *pdev)
struct clk *clk;
struct rpm_cc *rcc;
struct clk_onecell_data *data;
- int ret, is_8996 = 0;
+ int ret, is_8996 = 0, is_falcon = 0;
size_t num_clks, i;
struct clk_hw **hw_clks;
const struct rpm_smd_clk_desc *desc;
is_8996 = of_device_is_compatible(pdev->dev.of_node,
"qcom,rpmcc-msm8996");
+ is_falcon = of_device_is_compatible(pdev->dev.of_node,
+ "qcom,rpmcc-msmfalcon");
if (is_8996) {
ret = clk_vote_bimc(&msm8996_bimc_clk.hw, INT_MAX);
if (ret < 0)
return ret;
+ } else if (is_falcon) {
+ ret = clk_vote_bimc(&msmfalcon_bimc_clk.hw, INT_MAX);
+ if (ret < 0)
+ return ret;
}
desc = of_device_get_match_data(&pdev->dev);
@@ -676,6 +757,8 @@ static int rpm_smd_clk_probe(struct platform_device *pdev)
/* Keep an active vote on CXO in case no other driver votes for it */
if (is_8996)
clk_prepare_enable(msm8996_cxo_a.hw.clk);
+ else if (is_falcon)
+ clk_prepare_enable(msmfalcon_cxo_a.hw.clk);
dev_info(&pdev->dev, "Registered RPM clocks\n");
diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c
index f4ae70ac9315..b40231dd8dd1 100644
--- a/drivers/cpuidle/lpm-levels-of.c
+++ b/drivers/cpuidle/lpm-levels-of.c
@@ -38,34 +38,138 @@ static const struct lpm_type_str lpm_types[] = {
{SUSPEND, "suspend_enabled"},
};
+static DEFINE_PER_CPU(uint32_t *, max_residency);
+static DEFINE_PER_CPU(uint32_t *, min_residency);
static struct lpm_level_avail *cpu_level_available[NR_CPUS];
static struct platform_device *lpm_pdev;
-static void *get_avail_val(struct kobject *kobj, struct kobj_attribute *attr)
+static void *get_enabled_ptr(struct kobj_attribute *attr,
+ struct lpm_level_avail *avail)
{
void *arg = NULL;
+
+ if (!strcmp(attr->attr.name, lpm_types[IDLE].str))
+ arg = (void *) &avail->idle_enabled;
+ else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str))
+ arg = (void *) &avail->suspend_enabled;
+
+ return arg;
+}
+
+static struct lpm_level_avail *get_avail_ptr(struct kobject *kobj,
+ struct kobj_attribute *attr)
+{
struct lpm_level_avail *avail = NULL;
- if (!strcmp(attr->attr.name, lpm_types[IDLE].str)) {
+ if (!strcmp(attr->attr.name, lpm_types[IDLE].str))
avail = container_of(attr, struct lpm_level_avail,
idle_enabled_attr);
- arg = (void *) &avail->idle_enabled;
- } else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str)) {
+ else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str))
avail = container_of(attr, struct lpm_level_avail,
suspend_enabled_attr);
- arg = (void *) &avail->suspend_enabled;
+
+ return avail;
+}
+
+static void set_optimum_cpu_residency(struct lpm_cpu *cpu, int cpu_id,
+ bool probe_time)
+{
+ int i, j;
+ bool mode_avail;
+ uint32_t *maximum_residency = per_cpu(max_residency, cpu_id);
+ uint32_t *minimum_residency = per_cpu(min_residency, cpu_id);
+
+ for (i = 0; i < cpu->nlevels; i++) {
+ struct power_params *pwr = &cpu->levels[i].pwr;
+
+ mode_avail = probe_time ||
+ lpm_cpu_mode_allow(cpu_id, i, true);
+
+ if (!mode_avail) {
+ maximum_residency[i] = 0;
+ minimum_residency[i] = 0;
+ continue;
+ }
+
+ maximum_residency[i] = ~0;
+ for (j = i + 1; j < cpu->nlevels; j++) {
+ mode_avail = probe_time ||
+ lpm_cpu_mode_allow(cpu_id, j, true);
+
+ if (mode_avail &&
+ (maximum_residency[i] > pwr->residencies[j]) &&
+ (pwr->residencies[j] != 0))
+ maximum_residency[i] = pwr->residencies[j];
+ }
+
+ minimum_residency[i] = pwr->time_overhead_us;
+ for (j = i-1; j >= 0; j--) {
+ if (probe_time || lpm_cpu_mode_allow(cpu_id, j, true)) {
+ minimum_residency[i] = maximum_residency[j] + 1;
+ break;
+ }
+ }
}
+}
- return arg;
+static void set_optimum_cluster_residency(struct lpm_cluster *cluster,
+ bool probe_time)
+{
+ int i, j;
+ bool mode_avail;
+
+ for (i = 0; i < cluster->nlevels; i++) {
+ struct power_params *pwr = &cluster->levels[i].pwr;
+
+ mode_avail = probe_time ||
+ lpm_cluster_mode_allow(cluster, i,
+ true);
+
+ if (!mode_avail) {
+ pwr->max_residency = 0;
+ pwr->min_residency = 0;
+ continue;
+ }
+
+ pwr->max_residency = ~0;
+ for (j = i+1; j < cluster->nlevels; j++) {
+ mode_avail = probe_time ||
+ lpm_cluster_mode_allow(cluster, j,
+ true);
+ if (mode_avail &&
+ (pwr->max_residency > pwr->residencies[j]) &&
+ (pwr->residencies[j] != 0))
+ pwr->max_residency = pwr->residencies[j];
+ }
+
+ pwr->min_residency = pwr->time_overhead_us;
+ for (j = i-1; j >= 0; j--) {
+ if (probe_time ||
+ lpm_cluster_mode_allow(cluster, j, true)) {
+ pwr->min_residency =
+ cluster->levels[j].pwr.max_residency + 1;
+ break;
+ }
+ }
+ }
}
+uint32_t *get_per_cpu_max_residency(int cpu)
+{
+ return per_cpu(max_residency, cpu);
+}
+
+uint32_t *get_per_cpu_min_residency(int cpu)
+{
+ return per_cpu(min_residency, cpu);
+}
ssize_t lpm_enable_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
int ret = 0;
struct kernel_param kp;
- kp.arg = get_avail_val(kobj, attr);
+ kp.arg = get_enabled_ptr(attr, get_avail_ptr(kobj, attr));
ret = param_get_bool(buf, &kp);
if (ret > 0) {
strlcat(buf, "\n", PAGE_SIZE);
@@ -80,15 +184,25 @@ ssize_t lpm_enable_store(struct kobject *kobj, struct kobj_attribute *attr,
{
int ret = 0;
struct kernel_param kp;
+ struct lpm_level_avail *avail;
- kp.arg = get_avail_val(kobj, attr);
+ avail = get_avail_ptr(kobj, attr);
+ if (WARN_ON(!avail))
+ return -EINVAL;
+ kp.arg = get_enabled_ptr(attr, avail);
ret = param_set_bool(buf, &kp);
+ if (avail->cpu_node)
+ set_optimum_cpu_residency(avail->data, avail->idx, false);
+ else
+ set_optimum_cluster_residency(avail->data, false);
+
return ret ? ret : len;
}
static int create_lvl_avail_nodes(const char *name,
- struct kobject *parent, struct lpm_level_avail *avail)
+ struct kobject *parent, struct lpm_level_avail *avail,
+ void *data, int index, bool cpu_node)
{
struct attribute_group *attr_group = NULL;
struct attribute **attr = NULL;
@@ -139,6 +253,9 @@ static int create_lvl_avail_nodes(const char *name,
avail->idle_enabled = true;
avail->suspend_enabled = true;
avail->kobj = kobj;
+ avail->data = data;
+ avail->idx = index;
+ avail->cpu_node = cpu_node;
return ret;
@@ -181,7 +298,8 @@ static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent)
for (i = 0; i < p->cpu->nlevels; i++) {
ret = create_lvl_avail_nodes(p->cpu->levels[i].name,
- cpu_kobj[cpu_idx], &level_list[i]);
+ cpu_kobj[cpu_idx], &level_list[i],
+ (void *)p->cpu, cpu, true);
if (ret)
goto release_kobj;
}
@@ -215,7 +333,8 @@ int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj)
for (i = 0; i < p->nlevels; i++) {
ret = create_lvl_avail_nodes(p->levels[i].level_name,
- cluster_kobj, &p->levels[i].available);
+ cluster_kobj, &p->levels[i].available,
+ (void *)p, 0, false);
if (ret)
return ret;
}
@@ -421,6 +540,9 @@ static int parse_power_params(struct device_node *node,
key = "qcom,time-overhead";
ret = of_property_read_u32(node, key, &pwr->time_overhead_us);
+ if (ret)
+ goto fail;
+
fail:
if (ret)
pr_err("%s(): %s Error reading %s\n", __func__, node->name,
@@ -615,11 +737,31 @@ static int get_cpumask_for_node(struct device_node *node, struct cpumask *mask)
return 0;
}
+static int calculate_residency(struct power_params *base_pwr,
+ struct power_params *next_pwr)
+{
+ int32_t residency = (int32_t)(next_pwr->energy_overhead -
+ base_pwr->energy_overhead) -
+ ((int32_t)(next_pwr->ss_power * next_pwr->time_overhead_us)
+ - (int32_t)(base_pwr->ss_power * base_pwr->time_overhead_us));
+
+ residency /= (int32_t)(base_pwr->ss_power - next_pwr->ss_power);
+
+ if (residency < 0) {
+ __WARN_printf("%s: Incorrect power attributes for LPM\n",
+ __func__);
+ return next_pwr->time_overhead_us;
+ }
+
+ return residency < next_pwr->time_overhead_us ?
+ next_pwr->time_overhead_us : residency;
+}
+
static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
{
struct device_node *n;
int ret = -ENOMEM;
- int i;
+ int i, j;
char *key;
c->cpu = devm_kzalloc(&lpm_pdev->dev, sizeof(*c->cpu), GFP_KERNEL);
@@ -676,6 +818,22 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
else if (ret)
goto failed;
}
+ for (i = 0; i < c->cpu->nlevels; i++) {
+ for (j = 0; j < c->cpu->nlevels; j++) {
+ if (i >= j) {
+ c->cpu->levels[i].pwr.residencies[j] = 0;
+ continue;
+ }
+
+ c->cpu->levels[i].pwr.residencies[j] =
+ calculate_residency(&c->cpu->levels[i].pwr,
+ &c->cpu->levels[j].pwr);
+
+ pr_err("%s: idx %d %u\n", __func__, j,
+ c->cpu->levels[i].pwr.residencies[j]);
+ }
+ }
+
return 0;
failed:
for (i = 0; i < c->cpu->nlevels; i++) {
@@ -732,6 +890,7 @@ struct lpm_cluster *parse_cluster(struct device_node *node,
struct device_node *n;
char *key;
int ret = 0;
+ int i, j;
c = devm_kzalloc(&lpm_pdev->dev, sizeof(*c), GFP_KERNEL);
if (!c)
@@ -789,6 +948,22 @@ struct lpm_cluster *parse_cluster(struct device_node *node,
goto failed_parse_cluster;
c->aff_level = 1;
+
+ for_each_cpu(i, &c->child_cpus) {
+ per_cpu(max_residency, i) = devm_kzalloc(
+ &lpm_pdev->dev,
+ sizeof(uint32_t) * c->cpu->nlevels,
+ GFP_KERNEL);
+ if (!per_cpu(max_residency, i))
+ return ERR_PTR(-ENOMEM);
+ per_cpu(min_residency, i) = devm_kzalloc(
+ &lpm_pdev->dev,
+ sizeof(uint32_t) * c->cpu->nlevels,
+ GFP_KERNEL);
+ if (!per_cpu(min_residency, i))
+ return ERR_PTR(-ENOMEM);
+ set_optimum_cpu_residency(c->cpu, i, true);
+ }
}
}
@@ -797,6 +972,17 @@ struct lpm_cluster *parse_cluster(struct device_node *node,
else
c->last_level = c->nlevels-1;
+ for (i = 0; i < c->nlevels; i++) {
+ for (j = 0; j < c->nlevels; j++) {
+ if (i >= j) {
+ c->levels[i].pwr.residencies[j] = 0;
+ continue;
+ }
+ c->levels[i].pwr.residencies[j] = calculate_residency(
+ &c->levels[i].pwr, &c->levels[j].pwr);
+ }
+ }
+ set_optimum_cluster_residency(c, true);
return c;
failed_parse_cluster:
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 4f880fdd1478..ced95aa2b649 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -1,4 +1,6 @@
/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2006-2007 Adam Belay <abelay@novell.com>
+ * Copyright (C) 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -83,9 +85,37 @@ struct lpm_debug {
struct lpm_cluster *lpm_root_node;
+#define MAXSAMPLES 5
+
+static bool lpm_prediction;
+module_param_named(lpm_prediction,
+ lpm_prediction, bool, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static uint32_t ref_stddev = 100;
+module_param_named(
+ ref_stddev, ref_stddev, uint, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
+static uint32_t tmr_add = 100;
+module_param_named(
+ tmr_add, tmr_add, uint, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
+struct lpm_history {
+ uint32_t resi[MAXSAMPLES];
+ int mode[MAXSAMPLES];
+ int nsamp;
+ uint32_t hptr;
+ uint32_t hinvalid;
+ uint32_t htmr_wkup;
+};
+
+static DEFINE_PER_CPU(struct lpm_history, hist);
+
static DEFINE_PER_CPU(struct lpm_cluster*, cpu_cluster);
static bool suspend_in_progress;
static struct hrtimer lpm_hrtimer;
+static struct hrtimer histtimer;
static struct lpm_debug *lpm_debug;
static phys_addr_t lpm_debug_phys;
static const int num_dbg_elements = 0x100;
@@ -327,10 +357,37 @@ static enum hrtimer_restart lpm_hrtimer_cb(struct hrtimer *h)
return HRTIMER_NORESTART;
}
+static void histtimer_cancel(void)
+{
+ if (!lpm_prediction)
+ return;
+
+ hrtimer_try_to_cancel(&histtimer);
+}
+
+static enum hrtimer_restart histtimer_fn(struct hrtimer *h)
+{
+ int cpu = raw_smp_processor_id();
+ struct lpm_history *history = &per_cpu(hist, cpu);
+
+ history->hinvalid = 1;
+ return HRTIMER_NORESTART;
+}
+
+static void histtimer_start(uint32_t time_us)
+{
+ uint64_t time_ns = time_us * NSEC_PER_USEC;
+ ktime_t hist_ktime = ns_to_ktime(time_ns);
+
+ histtimer.function = histtimer_fn;
+ hrtimer_start(&histtimer, hist_ktime, HRTIMER_MODE_REL_PINNED);
+}
+
static void msm_pm_set_timer(uint32_t modified_time_us)
{
u64 modified_time_ns = modified_time_us * NSEC_PER_USEC;
ktime_t modified_ktime = ns_to_ktime(modified_time_ns);
+
lpm_hrtimer.function = lpm_hrtimer_cb;
hrtimer_start(&lpm_hrtimer, modified_ktime, HRTIMER_MODE_REL_PINNED);
}
@@ -415,22 +472,160 @@ static int set_device_mode(struct lpm_cluster *cluster, int ndevice,
return -EINVAL;
}
+static uint64_t lpm_cpuidle_predict(struct cpuidle_device *dev,
+ struct lpm_cpu *cpu, int *idx_restrict,
+ uint32_t *idx_restrict_time)
+{
+ int i, j, divisor;
+ uint64_t max, avg, stddev;
+ int64_t thresh = LLONG_MAX;
+ struct lpm_history *history = &per_cpu(hist, dev->cpu);
+ uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu);
+
+ if (!lpm_prediction)
+ return 0;
+
+ /*
+ * Samples are marked invalid when woken-up due to timer,
+ * so donot predict.
+ */
+ if (history->hinvalid) {
+ history->hinvalid = 0;
+ history->htmr_wkup = 1;
+ return 0;
+ }
+
+ /*
+ * Predict only when all the samples are collected.
+ */
+ if (history->nsamp < MAXSAMPLES)
+ return 0;
+
+ /*
+ * Check if the samples are not much deviated, if so use the
+ * average of those as predicted sleep time. Else if any
+ * specific mode has more premature exits return the index of
+ * that mode.
+ */
+
+again:
+ max = avg = divisor = stddev = 0;
+ for (i = 0; i < MAXSAMPLES; i++) {
+ int64_t value = history->resi[i];
+
+ if (value <= thresh) {
+ avg += value;
+ divisor++;
+ if (value > max)
+ max = value;
+ }
+ }
+ do_div(avg, divisor);
+
+ for (i = 0; i < MAXSAMPLES; i++) {
+ int64_t value = history->resi[i];
+
+ if (value <= thresh) {
+ int64_t diff = value - avg;
+
+ stddev += diff * diff;
+ }
+ }
+ do_div(stddev, divisor);
+ stddev = int_sqrt(stddev);
+
+ /*
+ * If the deviation is less, return the average, else
+ * ignore one maximum sample and retry
+ */
+ if (((avg > stddev * 6) && (divisor >= (MAXSAMPLES - 1)))
+ || stddev <= ref_stddev) {
+ return avg;
+ } else if (divisor > (MAXSAMPLES - 1)) {
+ thresh = max - 1;
+ goto again;
+ }
+
+ /*
+ * Find the number of premature exits for each of the mode,
+ * excluding clockgating mode, and they are more than fifty
+ * percent restrict that and deeper modes.
+ */
+ if (history->htmr_wkup != 1) {
+ for (j = 1; j < cpu->nlevels; j++) {
+ uint32_t failed = 0;
+ uint64_t total = 0;
+
+ for (i = 0; i < MAXSAMPLES; i++) {
+ if ((history->mode[i] == j) &&
+ (history->resi[i] < min_residency[j])) {
+ failed++;
+ total += history->resi[i];
+ }
+ }
+ if (failed > (MAXSAMPLES/2)) {
+ *idx_restrict = j;
+ do_div(total, failed);
+ *idx_restrict_time = total;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static inline void invalidate_predict_history(struct cpuidle_device *dev)
+{
+ struct lpm_history *history = &per_cpu(hist, dev->cpu);
+
+ if (!lpm_prediction)
+ return;
+
+ if (history->hinvalid) {
+ history->hinvalid = 0;
+ history->htmr_wkup = 1;
+ }
+}
+
+static void clear_predict_history(void)
+{
+ struct lpm_history *history;
+ int i;
+ unsigned int cpu;
+
+ if (!lpm_prediction)
+ return;
+
+ for_each_possible_cpu(cpu) {
+ history = &per_cpu(hist, cpu);
+ for (i = 0; i < MAXSAMPLES; i++) {
+ history->resi[i] = 0;
+ history->mode[i] = -1;
+ history->hptr = 0;
+ history->nsamp = 0;
+ }
+ }
+}
+
+static void update_history(struct cpuidle_device *dev, int idx);
+
static int cpu_power_select(struct cpuidle_device *dev,
struct lpm_cpu *cpu)
{
int best_level = -1;
- uint32_t best_level_pwr = ~0U;
uint32_t latency_us = pm_qos_request_for_cpu(PM_QOS_CPU_DMA_LATENCY,
dev->cpu);
uint32_t sleep_us =
(uint32_t)(ktime_to_us(tick_nohz_get_sleep_length()));
uint32_t modified_time_us = 0;
uint32_t next_event_us = 0;
- uint32_t pwr;
- int i;
+ int i, idx_restrict;
uint32_t lvl_latency_us = 0;
- uint32_t lvl_overhead_us = 0;
- uint32_t lvl_overhead_energy = 0;
+ uint64_t predicted = 0;
+ uint32_t htime = 0, idx_restrict_time = 0;
+ uint32_t next_wakeup_us = sleep_us;
+ uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu);
+ uint32_t *max_residency = get_per_cpu_max_residency(dev->cpu);
if (!cpu)
return -EINVAL;
@@ -438,12 +633,13 @@ static int cpu_power_select(struct cpuidle_device *dev,
if (sleep_disabled)
return 0;
+ idx_restrict = cpu->nlevels + 1;
+
next_event_us = (uint32_t)(ktime_to_us(get_next_event_time(dev->cpu)));
for (i = 0; i < cpu->nlevels; i++) {
struct lpm_cpu_level *level = &cpu->levels[i];
struct power_params *pwr_params = &level->pwr;
- uint32_t next_wakeup_us = sleep_us;
enum msm_pm_sleep_mode mode = level->mode;
bool allow;
@@ -454,56 +650,76 @@ static int cpu_power_select(struct cpuidle_device *dev,
lvl_latency_us = pwr_params->latency_us;
- lvl_overhead_us = pwr_params->time_overhead_us;
-
- lvl_overhead_energy = pwr_params->energy_overhead;
-
if (latency_us < lvl_latency_us)
- continue;
+ break;
if (next_event_us) {
if (next_event_us < lvl_latency_us)
- continue;
+ break;
if (((next_event_us - lvl_latency_us) < sleep_us) ||
(next_event_us < sleep_us))
next_wakeup_us = next_event_us - lvl_latency_us;
}
- if (next_wakeup_us <= pwr_params->time_overhead_us)
- continue;
-
- /*
- * If wakeup time greater than overhead by a factor of 1000
- * assume that core steady state power dominates the power
- * equation
- */
- if ((next_wakeup_us >> 10) > lvl_overhead_us) {
- pwr = pwr_params->ss_power;
- } else {
- pwr = pwr_params->ss_power;
- pwr -= (lvl_overhead_us * pwr_params->ss_power) /
- next_wakeup_us;
- pwr += pwr_params->energy_overhead / next_wakeup_us;
+ if (!i) {
+ /*
+ * If the next_wake_us itself is not sufficient for
+ * deeper low power modes than clock gating do not
+ * call prediction.
+ */
+ if (next_wakeup_us > max_residency[i]) {
+ predicted = lpm_cpuidle_predict(dev, cpu,
+ &idx_restrict, &idx_restrict_time);
+ if (predicted < min_residency[i])
+ predicted = 0;
+ } else
+ invalidate_predict_history(dev);
}
- if (best_level_pwr >= pwr) {
- best_level = i;
- best_level_pwr = pwr;
- if (next_event_us && next_event_us < sleep_us &&
- (mode != MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT))
- modified_time_us
- = next_event_us - lvl_latency_us;
- else
- modified_time_us = 0;
- }
+ if (i >= idx_restrict)
+ break;
+
+ best_level = i;
+
+ if (next_event_us && next_event_us < sleep_us &&
+ (mode != MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT))
+ modified_time_us
+ = next_event_us - lvl_latency_us;
+ else
+ modified_time_us = 0;
+
+ if (predicted ? (predicted <= max_residency[i])
+ : (next_wakeup_us <= max_residency[i]))
+ break;
}
if (modified_time_us)
msm_pm_set_timer(modified_time_us);
+ /*
+ * Start timer to avoid staying in shallower mode forever
+ * incase of misprediciton
+ */
+ if ((predicted || (idx_restrict != (cpu->nlevels + 1)))
+ && ((best_level >= 0)
+ && (best_level < (cpu->nlevels-1)))) {
+ htime = predicted + tmr_add;
+ if (htime == tmr_add)
+ htime = idx_restrict_time;
+ else if (htime > max_residency[best_level])
+ htime = max_residency[best_level];
+
+ if ((next_wakeup_us > htime) &&
+ ((next_wakeup_us - htime) > max_residency[best_level]))
+ histtimer_start(htime);
+ }
+
trace_cpu_power_select(best_level, sleep_us, latency_us, next_event_us);
+ trace_cpu_pred_select(idx_restrict_time ? 2 : (predicted ? 1 : 0),
+ predicted, htime);
+
return best_level;
}
@@ -554,8 +770,6 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle)
{
int best_level = -1;
int i;
- uint32_t best_level_pwr = ~0U;
- uint32_t pwr;
struct cpumask mask;
uint32_t latency_us = ~0U;
uint32_t sleep_us;
@@ -596,10 +810,10 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle)
continue;
if (from_idle && latency_us < pwr_params->latency_us)
- continue;
+ break;
if (sleep_us < pwr_params->time_overhead_us)
- continue;
+ break;
if (suspend_in_progress && from_idle && level->notify_rpm)
continue;
@@ -607,19 +821,10 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle)
if (level->notify_rpm && msm_rpm_waiting_for_ack())
continue;
- if ((sleep_us >> 10) > pwr_params->time_overhead_us) {
- pwr = pwr_params->ss_power;
- } else {
- pwr = pwr_params->ss_power;
- pwr -= (pwr_params->time_overhead_us *
- pwr_params->ss_power) / sleep_us;
- pwr += pwr_params->energy_overhead / sleep_us;
- }
+ best_level = i;
- if (best_level_pwr >= pwr) {
- best_level = i;
- best_level_pwr = pwr;
- }
+ if (sleep_us <= pwr_params->max_residency)
+ break;
}
return best_level;
@@ -675,6 +880,7 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
}
us = us + 1;
+ clear_predict_history();
do_div(us, USEC_PER_SEC/SCLK_HZ);
msm_mpm_enter_sleep(us, from_idle, cpumask);
}
@@ -1009,6 +1215,39 @@ static int lpm_cpuidle_select(struct cpuidle_driver *drv,
return idx;
}
+static void update_history(struct cpuidle_device *dev, int idx)
+{
+ struct lpm_history *history = &per_cpu(hist, dev->cpu);
+ uint32_t tmr = 0;
+
+ if (!lpm_prediction)
+ return;
+
+ if (history->htmr_wkup) {
+ if (!history->hptr)
+ history->hptr = MAXSAMPLES-1;
+ else
+ history->hptr--;
+
+ history->resi[history->hptr] += dev->last_residency;
+ history->htmr_wkup = 0;
+ tmr = 1;
+ } else
+ history->resi[history->hptr] = dev->last_residency;
+
+ history->mode[history->hptr] = idx;
+
+ trace_cpu_pred_hist(history->mode[history->hptr],
+ history->resi[history->hptr], history->hptr, tmr);
+
+ if (history->nsamp < MAXSAMPLES)
+ history->nsamp++;
+
+ (history->hptr)++;
+ if (history->hptr >= MAXSAMPLES)
+ history->hptr = 0;
+}
+
static int lpm_cpuidle_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
@@ -1043,12 +1282,13 @@ exit:
cluster_unprepare(cluster, cpumask, idx, true, end_time);
cpu_unprepare(cluster, idx, true);
sched_set_cpu_cstate(smp_processor_id(), 0, 0, 0);
-
- trace_cpu_idle_exit(idx, success);
end_time = ktime_to_ns(ktime_get()) - start_time;
- dev->last_residency = do_div(end_time, 1000);
+ do_div(end_time, 1000);
+ dev->last_residency = end_time;
+ update_history(dev, idx);
+ trace_cpu_idle_exit(idx, success);
local_irq_enable();
-
+ histtimer_cancel();
return idx;
}
@@ -1320,6 +1560,7 @@ static int lpm_probe(struct platform_device *pdev)
*/
suspend_set_ops(&lpm_suspend_ops);
hrtimer_init(&lpm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_init(&histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ret = remote_spin_lock_init(&scm_handoff_lock, SCM_HANDOFF_LOCK_ID);
if (ret) {
diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h
index 8e05336be21a..63fe0a0fbc08 100644
--- a/drivers/cpuidle/lpm-levels.h
+++ b/drivers/cpuidle/lpm-levels.h
@@ -27,6 +27,9 @@ struct power_params {
uint32_t ss_power; /* Steady state power */
uint32_t energy_overhead; /* Enter + exit over head */
uint32_t time_overhead_us; /* Enter + exit overhead */
+ uint32_t residencies[NR_LPM_LEVELS];
+ uint32_t min_residency;
+ uint32_t max_residency;
};
struct lpm_cpu_level {
@@ -55,6 +58,9 @@ struct lpm_level_avail {
struct kobject *kobj;
struct kobj_attribute idle_enabled_attr;
struct kobj_attribute suspend_enabled_attr;
+ void *data;
+ int idx;
+ bool cpu_node;
};
struct lpm_cluster_level {
@@ -119,7 +125,8 @@ bool lpm_cpu_mode_allow(unsigned int cpu,
unsigned int mode, bool from_idle);
bool lpm_cluster_mode_allow(struct lpm_cluster *cluster,
unsigned int mode, bool from_idle);
-
+uint32_t *get_per_cpu_max_residency(int cpu);
+uint32_t *get_per_cpu_min_residency(int cpu);
extern struct lpm_cluster *lpm_root_node;
#ifdef CONFIG_SMP
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index a3b25b3d8dd1..3615be45b6d9 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -244,6 +244,28 @@ static const struct adreno_gpu_core adreno_gpulist[] = {
.core = 5,
.major = 4,
.minor = 0,
+ .patchid = 0,
+ .features = ADRENO_PREEMPTION | ADRENO_64BIT |
+ ADRENO_CONTENT_PROTECTION |
+ ADRENO_GPMU | ADRENO_SPTP_PC,
+ .pm4fw_name = "a530_pm4.fw",
+ .pfpfw_name = "a530_pfp.fw",
+ .zap_name = "a540_zap",
+ .gpudev = &adreno_a5xx_gpudev,
+ .gmem_size = SZ_1M,
+ .num_protected_regs = 0x20,
+ .busy_mask = 0xFFFFFFFE,
+ .gpmufw_name = "a540_gpmu.fw2",
+ .gpmu_major = 3,
+ .gpmu_minor = 0,
+ .gpmu_tsens = 0x000C000D,
+ .max_power = 5448,
+ },
+ {
+ .gpurev = ADRENO_REV_A540,
+ .core = 5,
+ .major = 4,
+ .minor = 0,
.patchid = ANY_ID,
.features = ADRENO_PREEMPTION | ADRENO_64BIT |
ADRENO_CONTENT_PROTECTION |
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 498386903936..1356835d0e93 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -2799,6 +2799,18 @@ static void adreno_regulator_disable_poll(struct kgsl_device *device)
adreno_iommu_sync(device, false);
}
+static void adreno_gpu_model(struct kgsl_device *device, char *str,
+ size_t bufsz)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ snprintf(str, bufsz, "Adreno%d%d%dv%d",
+ ADRENO_CHIPID_CORE(adreno_dev->chipid),
+ ADRENO_CHIPID_MAJOR(adreno_dev->chipid),
+ ADRENO_CHIPID_MINOR(adreno_dev->chipid),
+ ADRENO_CHIPID_PATCH(adreno_dev->chipid) + 1);
+}
+
static const struct kgsl_functable adreno_functable = {
/* Mandatory functions */
.regread = adreno_regread,
@@ -2835,7 +2847,8 @@ static const struct kgsl_functable adreno_functable = {
.regulator_disable = adreno_regulator_disable,
.pwrlevel_change_settings = adreno_pwrlevel_change_settings,
.regulator_disable_poll = adreno_regulator_disable_poll,
- .clk_set_options = adreno_clk_set_options
+ .clk_set_options = adreno_clk_set_options,
+ .gpu_model = adreno_gpu_model,
};
static struct platform_driver adreno_platform_driver = {
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 286f7d63c8fe..d4858f3f818e 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -1015,6 +1015,12 @@ static inline int adreno_is_a540v1(struct adreno_device *adreno_dev)
(ADRENO_CHIPID_PATCH(adreno_dev->chipid) == 0);
}
+static inline int adreno_is_a540v2(struct adreno_device *adreno_dev)
+{
+ return (ADRENO_GPUREV(adreno_dev) == ADRENO_REV_A540) &&
+ (ADRENO_CHIPID_PATCH(adreno_dev->chipid) == 1);
+}
+
/*
* adreno_checkreg_off() - Checks the validity of a register enum
* @adreno_dev: Pointer to adreno device
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 1782d1d54946..8ac058a7c5b0 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -2147,9 +2147,11 @@ static int _me_init_ucode_workarounds(struct adreno_device *adreno_dev)
case ADRENO_REV_A540:
/*
* WFI after every direct-render 3D mode draw and
- * WFI after every 2D Mode 3 draw.
+ * WFI after every 2D Mode 3 draw. This is needed
+ * only on a540v1.
*/
- return 0x0000000A;
+ if (adreno_is_a540v1(adreno_dev))
+ return 0x0000000A;
default:
return 0x00000000; /* No ucode workarounds enabled */
}
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 25f5de6ce645..7ac84b777051 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -579,4 +579,19 @@ static inline void __user *to_user_ptr(uint64_t address)
return (void __user *)(uintptr_t)address;
}
+static inline void kgsl_gpu_sysfs_add_link(struct kobject *dst,
+ struct kobject *src, const char *src_name,
+ const char *dst_name)
+{
+ struct kernfs_node *old;
+
+ if (dst == NULL || src == NULL)
+ return;
+
+ old = sysfs_get_dirent(src->sd, src_name);
+ if (IS_ERR_OR_NULL(old))
+ return;
+
+ kernfs_create_link(dst->sd, dst_name, old);
+}
#endif /* __KGSL_H */
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 0df6dd8628a5..24511a4de6f1 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -167,6 +167,8 @@ struct kgsl_functable {
void (*regulator_disable_poll)(struct kgsl_device *device);
void (*clk_set_options)(struct kgsl_device *device,
const char *name, struct clk *clk);
+ void (*gpu_model)(struct kgsl_device *device, char *str,
+ size_t bufsz);
};
struct kgsl_ioctl {
@@ -261,6 +263,7 @@ struct kgsl_device {
struct kgsl_snapshot *snapshot;
u32 snapshot_faultcount; /* Total number of faults since boot */
+ bool force_panic; /* Force panic after snapshot dump */
struct kobject snapshot_kobj;
struct kobject ppd_kobj;
@@ -281,6 +284,7 @@ struct kgsl_device {
/* Number of active contexts seen globally for this device */
int active_context_count;
+ struct kobject *gpu_sysfs_kobj;
};
#define KGSL_MMU_DEVICE(_mmu) \
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 1f2178848664..d71c6a63f2d3 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -21,6 +21,7 @@
#include <linux/delay.h>
#include <linux/msm_adreno_devfreq.h>
#include <linux/of_device.h>
+#include <linux/thermal.h>
#include "kgsl.h"
#include "kgsl_pwrscale.h"
@@ -590,22 +591,10 @@ static ssize_t kgsl_pwrctrl_max_pwrlevel_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%u\n", pwr->max_pwrlevel);
}
-static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{ struct kgsl_device *device = kgsl_device_from_dev(dev);
- struct kgsl_pwrctrl *pwr;
- int ret;
- unsigned int level = 0;
-
- if (device == NULL)
- return 0;
-
- pwr = &device->pwrctrl;
-
- ret = kgsl_sysfs_store(buf, &level);
- if (ret)
- return ret;
+static void kgsl_pwrctrl_min_pwrlevel_set(struct kgsl_device *device,
+ int level)
+{
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
mutex_lock(&device->mutex);
if (level > pwr->num_pwrlevels - 2)
@@ -621,6 +610,24 @@ static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev,
kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel);
mutex_unlock(&device->mutex);
+}
+
+static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ int ret;
+ unsigned int level = 0;
+
+ if (device == NULL)
+ return 0;
+
+ ret = kgsl_sysfs_store(buf, &level);
+ if (ret)
+ return ret;
+
+ kgsl_pwrctrl_min_pwrlevel_set(device, level);
return count;
}
@@ -664,24 +671,13 @@ static int _get_nearest_pwrlevel(struct kgsl_pwrctrl *pwr, unsigned int clock)
return -ERANGE;
}
-static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static void kgsl_pwrctrl_max_clock_set(struct kgsl_device *device, int val)
{
- struct kgsl_device *device = kgsl_device_from_dev(dev);
struct kgsl_pwrctrl *pwr;
- unsigned int val = 0;
- int level, ret;
-
- if (device == NULL)
- return 0;
+ int level;
pwr = &device->pwrctrl;
- ret = kgsl_sysfs_store(buf, &val);
- if (ret)
- return ret;
-
mutex_lock(&device->mutex);
level = _get_nearest_pwrlevel(pwr, val);
/* If the requested power level is not supported by hw, try cycling */
@@ -715,21 +711,37 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
if (pwr->sysfs_pwr_limit)
kgsl_pwr_limits_set_freq(pwr->sysfs_pwr_limit,
pwr->pwrlevels[level].gpu_freq);
- return count;
+ return;
err:
mutex_unlock(&device->mutex);
- return count;
}
-static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
-
struct kgsl_device *device = kgsl_device_from_dev(dev);
+ unsigned int val = 0;
+ int ret;
+
+ if (device == NULL)
+ return 0;
+
+ ret = kgsl_sysfs_store(buf, &val);
+ if (ret)
+ return ret;
+
+ kgsl_pwrctrl_max_clock_set(device, val);
+
+ return count;
+}
+
+static unsigned int kgsl_pwrctrl_max_clock_get(struct kgsl_device *device)
+{
struct kgsl_pwrctrl *pwr;
unsigned int freq;
+
if (device == NULL)
return 0;
pwr = &device->pwrctrl;
@@ -743,7 +755,17 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev,
(TH_HZ - pwr->thermal_timeout) * (hfreq / TH_HZ);
}
- return snprintf(buf, PAGE_SIZE, "%d\n", freq);
+ return freq;
+}
+
+static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ kgsl_pwrctrl_max_clock_get(device));
}
static ssize_t kgsl_pwrctrl_gpuclk_store(struct device *dev,
@@ -903,9 +925,14 @@ static ssize_t kgsl_pwrctrl_gpu_available_frequencies_show(
if (device == NULL)
return 0;
pwr = &device->pwrctrl;
- for (index = 0; index < pwr->num_pwrlevels - 1; index++)
- num_chars += snprintf(buf + num_chars, PAGE_SIZE, "%d ",
- pwr->pwrlevels[index].gpu_freq);
+ for (index = 0; index < pwr->num_pwrlevels - 1; index++) {
+ num_chars += scnprintf(buf + num_chars,
+ PAGE_SIZE - num_chars - 1,
+ "%d ", pwr->pwrlevels[index].gpu_freq);
+ /* One space for trailing null and another for the newline */
+ if (num_chars >= PAGE_SIZE - 2)
+ break;
+ }
buf[num_chars++] = '\n';
return num_chars;
}
@@ -1171,6 +1198,195 @@ static ssize_t kgsl_popp_show(struct device *dev,
test_bit(POPP_ON, &device->pwrscale.popp_state));
}
+static ssize_t kgsl_pwrctrl_gpu_model_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ char model_str[32] = {0};
+
+ if (device == NULL)
+ return 0;
+
+ device->ftbl->gpu_model(device, model_str, sizeof(model_str));
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", model_str);
+}
+
+static ssize_t kgsl_pwrctrl_gpu_busy_percentage_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_clk_stats *stats;
+ unsigned int busy_percent = 0;
+
+ if (device == NULL)
+ return 0;
+ stats = &device->pwrctrl.clk_stats;
+
+ if (stats->total_old != 0)
+ busy_percent = (stats->busy_old * 100) / stats->total_old;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d %%\n", busy_percent);
+
+ /* Reset the stats if GPU is OFF */
+ if (!test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) {
+ stats->busy_old = 0;
+ stats->total_old = 0;
+ }
+ return ret;
+}
+
+static ssize_t kgsl_pwrctrl_min_clock_mhz_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_pwrctrl *pwr;
+
+ if (device == NULL)
+ return 0;
+ pwr = &device->pwrctrl;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ pwr->pwrlevels[pwr->min_pwrlevel].gpu_freq / 1000000);
+}
+
+static ssize_t kgsl_pwrctrl_min_clock_mhz_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ int level, ret;
+ unsigned int freq;
+ struct kgsl_pwrctrl *pwr;
+
+ if (device == NULL)
+ return 0;
+
+ pwr = &device->pwrctrl;
+
+ ret = kgsl_sysfs_store(buf, &freq);
+ if (ret)
+ return ret;
+
+ freq *= 1000000;
+ level = _get_nearest_pwrlevel(pwr, freq);
+
+ if (level >= 0)
+ kgsl_pwrctrl_min_pwrlevel_set(device, level);
+
+ return count;
+}
+
+static ssize_t kgsl_pwrctrl_max_clock_mhz_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ unsigned int freq;
+
+ if (device == NULL)
+ return 0;
+
+ freq = kgsl_pwrctrl_max_clock_get(device);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", freq / 1000000);
+}
+
+static ssize_t kgsl_pwrctrl_max_clock_mhz_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ unsigned int val = 0;
+ int ret;
+
+ if (device == NULL)
+ return 0;
+
+ ret = kgsl_sysfs_store(buf, &val);
+ if (ret)
+ return ret;
+
+ val *= 1000000;
+ kgsl_pwrctrl_max_clock_set(device, val);
+
+ return count;
+}
+
+static ssize_t kgsl_pwrctrl_clock_mhz_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+
+ if (device == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n",
+ kgsl_pwrctrl_active_freq(&device->pwrctrl) / 1000000);
+}
+
+static ssize_t kgsl_pwrctrl_freq_table_mhz_show(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_pwrctrl *pwr;
+ int index, num_chars = 0;
+
+ if (device == NULL)
+ return 0;
+
+ pwr = &device->pwrctrl;
+ for (index = 0; index < pwr->num_pwrlevels - 1; index++) {
+ num_chars += scnprintf(buf + num_chars,
+ PAGE_SIZE - num_chars - 1,
+ "%d ", pwr->pwrlevels[index].gpu_freq / 1000000);
+ /* One space for trailing null and another for the newline */
+ if (num_chars >= PAGE_SIZE - 2)
+ break;
+ }
+
+ buf[num_chars++] = '\n';
+
+ return num_chars;
+}
+
+static ssize_t kgsl_pwrctrl_temp_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_pwrctrl *pwr;
+ int ret, id = 0, temperature = 0;
+
+ if (device == NULL)
+ goto done;
+
+ pwr = &device->pwrctrl;
+
+ if (!pwr->tsens_name)
+ goto done;
+
+ id = sensor_get_id((char *)pwr->tsens_name);
+ if (id < 0)
+ goto done;
+
+ ret = sensor_get_temp(id, &temperature);
+ if (ret)
+ goto done;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ temperature);
+done:
+ return 0;
+}
+
static DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show,
kgsl_pwrctrl_gpuclk_store);
static DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show,
@@ -1222,6 +1438,17 @@ static DEVICE_ATTR(popp, 0644, kgsl_popp_show, kgsl_popp_store);
static DEVICE_ATTR(force_no_nap, 0644,
kgsl_pwrctrl_force_no_nap_show,
kgsl_pwrctrl_force_no_nap_store);
+static DEVICE_ATTR(gpu_model, 0444, kgsl_pwrctrl_gpu_model_show, NULL);
+static DEVICE_ATTR(gpu_busy_percentage, 0444,
+ kgsl_pwrctrl_gpu_busy_percentage_show, NULL);
+static DEVICE_ATTR(min_clock_mhz, 0644, kgsl_pwrctrl_min_clock_mhz_show,
+ kgsl_pwrctrl_min_clock_mhz_store);
+static DEVICE_ATTR(max_clock_mhz, 0644, kgsl_pwrctrl_max_clock_mhz_show,
+ kgsl_pwrctrl_max_clock_mhz_store);
+static DEVICE_ATTR(clock_mhz, 0444, kgsl_pwrctrl_clock_mhz_show, NULL);
+static DEVICE_ATTR(freq_table_mhz, 0444,
+ kgsl_pwrctrl_freq_table_mhz_show, NULL);
+static DEVICE_ATTR(temp, 0444, kgsl_pwrctrl_temp_show, NULL);
static const struct device_attribute *pwrctrl_attr_list[] = {
&dev_attr_gpuclk,
@@ -1243,12 +1470,50 @@ static const struct device_attribute *pwrctrl_attr_list[] = {
&dev_attr_bus_split,
&dev_attr_default_pwrlevel,
&dev_attr_popp,
+ &dev_attr_gpu_model,
+ &dev_attr_gpu_busy_percentage,
+ &dev_attr_min_clock_mhz,
+ &dev_attr_max_clock_mhz,
+ &dev_attr_clock_mhz,
+ &dev_attr_freq_table_mhz,
+ &dev_attr_temp,
NULL
};
+struct sysfs_link {
+ const char *src;
+ const char *dst;
+};
+
+static struct sysfs_link link_names[] = {
+ { "gpu_model", "gpu_model",},
+ { "gpu_busy_percentage", "gpu_busy",},
+ { "min_clock_mhz", "gpu_min_clock",},
+ { "max_clock_mhz", "gpu_max_clock",},
+ { "clock_mhz", "gpu_clock",},
+ { "freq_table_mhz", "gpu_freq_table",},
+ { "temp", "gpu_tmu",},
+};
+
int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device)
{
- return kgsl_create_device_sysfs_files(device->dev, pwrctrl_attr_list);
+ int i, ret;
+
+ ret = kgsl_create_device_sysfs_files(device->dev, pwrctrl_attr_list);
+ if (ret)
+ return ret;
+
+ device->gpu_sysfs_kobj = kobject_create_and_add("gpu", kernel_kobj);
+ if (IS_ERR_OR_NULL(device->gpu_sysfs_kobj))
+ return (device->gpu_sysfs_kobj == NULL) ?
+ -ENOMEM : PTR_ERR(device->gpu_sysfs_kobj);
+
+ for (i = 0; i < ARRAY_SIZE(link_names); i++)
+ kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj,
+ &device->dev->kobj, link_names[i].src,
+ link_names[i].dst);
+
+ return 0;
}
void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device)
@@ -1860,6 +2125,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
kgsl_pwrctrl_vbif_init();
+ /* temperature sensor name */
+ of_property_read_string(pdev->dev.of_node, "qcom,tsens-name",
+ &pwr->tsens_name);
+
return result;
}
@@ -1913,20 +2182,38 @@ void kgsl_idle_check(struct work_struct *work)
{
struct kgsl_device *device = container_of(work, struct kgsl_device,
idle_check_ws);
+ int ret = 0;
+ unsigned int requested_state;
+
WARN_ON(device == NULL);
if (device == NULL)
return;
mutex_lock(&device->mutex);
+ requested_state = device->requested_state;
+
if (device->state == KGSL_STATE_ACTIVE
|| device->state == KGSL_STATE_NAP) {
- if (!atomic_read(&device->active_cnt))
- kgsl_pwrctrl_change_state(device,
+ if (!atomic_read(&device->active_cnt)) {
+ ret = kgsl_pwrctrl_change_state(device,
device->requested_state);
+ if (ret == -EBUSY) {
+ /*
+ * If the GPU is currently busy, restore
+ * the requested state and reschedule
+ * idle work.
+ */
+ kgsl_pwrctrl_request_state(device,
+ requested_state);
+ kgsl_schedule_work(&device->idle_check_ws);
+ }
+ }
+
+ if (!ret)
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
- kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
if (device->state == KGSL_STATE_ACTIVE)
mod_timer(&device->idle_timer,
jiffies +
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index ae21a274fada..2de42d87bcbe 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -152,6 +152,7 @@ struct kgsl_regulator {
* @sysfs_pwr_limit - pointer to the sysfs limits node
* isense_clk_indx - index of isense clock, 0 if no isense
* isense_clk_on_level - isense clock rate is XO rate below this level.
+ * tsens_name - pointer to temperature sensor name of GPU temperature sensor
*/
struct kgsl_pwrctrl {
@@ -204,6 +205,7 @@ struct kgsl_pwrctrl {
struct kgsl_pwr_limit *sysfs_pwr_limit;
unsigned int gpu_bimc_int_clk_freq;
bool gpu_bimc_interface_enabled;
+ const char *tsens_name;
};
int kgsl_pwrctrl_init(struct kgsl_device *device);
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index d90aec42f30a..01d3b74c16fd 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -910,6 +910,14 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor)
pwrscale->history[i].type = i;
}
+ /* Add links to the devfreq sysfs nodes */
+ kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj,
+ &pwrscale->devfreqptr->dev.kobj, "governor",
+ "gpu_governor");
+ kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj,
+ &pwrscale->devfreqptr->dev.kobj,
+ "available_governors", "gpu_available_governor");
+
return 0;
}
EXPORT_SYMBOL(kgsl_pwrscale_init);
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 2c9f17f9e7a4..a2e4a909062f 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -810,6 +810,29 @@ static ssize_t faultcount_store(struct kgsl_device *device, const char *buf,
return count;
}
+/* Show the force_panic request status */
+static ssize_t force_panic_show(struct kgsl_device *device, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", device->force_panic);
+}
+
+/* Store the panic request value to force_panic */
+static ssize_t force_panic_store(struct kgsl_device *device, const char *buf,
+ size_t count)
+{
+ unsigned int val = 0;
+ int ret;
+
+ if (device && count > 0)
+ device->force_panic = 0;
+
+ ret = kgsl_sysfs_store(buf, &val);
+
+ if (!ret && device)
+ device->force_panic = (bool)val;
+
+ return (ssize_t) ret < 0 ? ret : count;
+}
/* Show the timestamp of the last collected snapshot */
static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
{
@@ -835,6 +858,7 @@ struct kgsl_snapshot_attribute attr_##_name = { \
static SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL);
static SNAPSHOT_ATTR(faultcount, 0644, faultcount_show, faultcount_store);
+static SNAPSHOT_ATTR(force_panic, 0644, force_panic_show, force_panic_store);
static ssize_t snapshot_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
@@ -914,6 +938,7 @@ int kgsl_device_snapshot_init(struct kgsl_device *device)
device->snapshot = NULL;
device->snapshot_faultcount = 0;
+ device->force_panic = 0;
ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot,
&device->dev->kobj, "snapshot");
@@ -929,7 +954,11 @@ int kgsl_device_snapshot_init(struct kgsl_device *device)
goto done;
ret = sysfs_create_file(&device->snapshot_kobj, &attr_faultcount.attr);
+ if (ret)
+ goto done;
+ ret = sysfs_create_file(&device->snapshot_kobj,
+ &attr_force_panic.attr);
done:
return ret;
}
@@ -954,6 +983,7 @@ void kgsl_device_snapshot_close(struct kgsl_device *device)
device->snapshot_memory.ptr = NULL;
device->snapshot_memory.size = 0;
device->snapshot_faultcount = 0;
+ device->force_panic = 0;
}
EXPORT_SYMBOL(kgsl_device_snapshot_close);
@@ -1032,6 +1062,7 @@ void kgsl_snapshot_save_frozen_objs(struct work_struct *work)
{
struct kgsl_snapshot *snapshot = container_of(work,
struct kgsl_snapshot, work);
+ struct kgsl_device *device = kgsl_get_device(KGSL_DEVICE_3D0);
struct kgsl_snapshot_object *obj, *tmp;
size_t size = 0;
void *ptr;
@@ -1073,12 +1104,15 @@ done:
snapshot->process = NULL;
if (snapshot->ib1base && !snapshot->ib1dumped)
- pr_warn("kgsl: snapshot: Active IB1:%016llx not dumped\n",
+ KGSL_DRV_ERR(device,
+ "snapshot: Active IB1:%016llx not dumped\n",
snapshot->ib1base);
else if (snapshot->ib2base && !snapshot->ib2dumped)
- pr_warn("kgsl: snapshot: Active IB2:%016llx not dumped\n",
+ KGSL_DRV_ERR(device,
+ "snapshot: Active IB2:%016llx not dumped\n",
snapshot->ib2base);
complete_all(&snapshot->dump_gate);
+ BUG_ON(device->force_panic);
return;
}
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 787a9e5a15c8..d8ef5f59edcb 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -39,6 +39,8 @@
#define PMI_CHG_SCALE_2 391750000000
#define QPNP_VADC_HC_VREF_CODE 0x4000
#define QPNP_VADC_HC_VDD_REFERENCE_MV 1875
+/* Clamp negative ADC code to 0 */
+#define QPNP_VADC_HC_MAX_CODE 0x7FFF
/* Units for temperature below (on x axis) is in 0.1DegC as
required by the battery driver. Note the resolution used
@@ -752,6 +754,8 @@ int32_t qpnp_adc_scale_pmic_therm(struct qpnp_vadc_chip *vadc,
if (adc_properties->adc_hc) {
/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+ if (adc_code > QPNP_VADC_HC_MAX_CODE)
+ adc_code = 0;
pmic_voltage = (int64_t) adc_code;
pmic_voltage *= (int64_t) (adc_properties->adc_vdd_reference
* 1000);
@@ -862,6 +866,8 @@ int32_t qpnp_adc_tdkntcg_therm(struct qpnp_vadc_chip *chip,
if (adc_properties->adc_hc) {
/* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */
+ if (adc_code > QPNP_VADC_HC_MAX_CODE)
+ adc_code = 0;
xo_thm_voltage = (int64_t) adc_code;
xo_thm_voltage *= (int64_t) (adc_properties->adc_vdd_reference
* 1000);
@@ -1059,6 +1065,8 @@ int32_t qpnp_adc_scale_therm_pu2(struct qpnp_vadc_chip *chip,
if (adc_properties->adc_hc) {
/* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */
+ if (adc_code > QPNP_VADC_HC_MAX_CODE)
+ adc_code = 0;
therm_voltage = (int64_t) adc_code;
therm_voltage *= (int64_t) (adc_properties->adc_vdd_reference
* 1000);
@@ -1094,6 +1102,8 @@ int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *chip,
if (adc_properties->adc_hc) {
/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+ if (reg > QPNP_VADC_HC_MAX_CODE)
+ reg = 0;
adc_voltage = (int64_t) reg;
adc_voltage *= QPNP_VADC_HC_VDD_REFERENCE_MV;
adc_voltage = div64_s64(adc_voltage,
@@ -1228,6 +1238,8 @@ int32_t qpnp_adc_scale_default(struct qpnp_vadc_chip *vadc,
if (adc_properties->adc_hc) {
/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+ if (adc_code > QPNP_VADC_HC_MAX_CODE)
+ adc_code = 0;
scale_voltage = (int64_t) adc_code;
scale_voltage *= (adc_properties->adc_vdd_reference * 1000);
scale_voltage = div64_s64(scale_voltage,
diff --git a/drivers/hwtracing/coresight/coresight-csr.c b/drivers/hwtracing/coresight/coresight-csr.c
index dfb2922b6f33..3c18d686091a 100644
--- a/drivers/hwtracing/coresight/coresight-csr.c
+++ b/drivers/hwtracing/coresight/coresight-csr.c
@@ -191,8 +191,6 @@ static int csr_probe(struct platform_device *pdev)
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
- /* Store the driver data pointer for use in exported functions */
- csrdrvdata = drvdata;
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
@@ -220,6 +218,8 @@ static int csr_probe(struct platform_device *pdev)
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
+ /* Store the driver data pointer for use in exported functions */
+ csrdrvdata = drvdata;
dev_info(dev, "CSR initialized\n");
return 0;
}
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index a234d61802ce..fb2f27299417 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -774,8 +774,6 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
- /* Store the driver data pointer for use in exported functions */
- stmdrvdata = drvdata;
drvdata->dev = &adev->dev;
dev_set_drvdata(dev, drvdata);
@@ -846,6 +844,8 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
if (boot_enable)
coresight_enable(drvdata->csdev);
+ /* Store the driver data pointer for use in exported functions */
+ stmdrvdata = drvdata;
return 0;
err:
coresight_unregister(drvdata->csdev);
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
index a0fcad198f62..bb44b6d82ccd 100644
--- a/drivers/iio/adc/qcom-rradc.c
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -418,9 +418,9 @@ static int rradc_post_process_gpio(struct rradc_chip *chip,
.sts = _sts, \
}, \
-#define RR_ADC_CHAN_TEMP(_dname, _scale, _lsb, _msb, _sts) \
+#define RR_ADC_CHAN_TEMP(_dname, _scale, mask, _lsb, _msb, _sts) \
RR_ADC_CHAN(_dname, IIO_TEMP, \
- BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
+ mask, \
_scale, _lsb, _msb, _sts) \
#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb, _sts) \
@@ -443,9 +443,11 @@ static const struct rradc_channels rradc_chans[] = {
FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB,
FG_ADC_RR_BATT_ID_STS)
RR_ADC_CHAN_TEMP("batt_therm", &rradc_post_process_therm,
+ BIT(IIO_CHAN_INFO_RAW),
FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB,
FG_ADC_RR_BATT_THERM_STS)
RR_ADC_CHAN_TEMP("skin_temp", &rradc_post_process_therm,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB,
FG_ADC_RR_AUX_THERM_STS)
RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr,
@@ -461,24 +463,30 @@ static const struct rradc_channels rradc_chans[] = {
FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB,
FG_ADC_RR_DC_IN_V_STS)
RR_ADC_CHAN_TEMP("die_temp", &rradc_post_process_die_temp,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB,
FG_ADC_RR_PMI_DIE_TEMP_STS)
RR_ADC_CHAN_TEMP("chg_temp", &rradc_post_process_chg_temp,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB,
FG_ADC_RR_CHARGER_TEMP_STS)
RR_ADC_CHAN_VOLT("gpio", &rradc_post_process_gpio,
FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB,
FG_ADC_RR_GPIO_STS)
RR_ADC_CHAN_TEMP("chg_temp_hot", &rradc_post_process_chg_temp_hot,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_CHARGER_HOT, FG_ADC_RR_CHARGER_HOT,
FG_ADC_RR_CHARGER_TEMP_STS)
RR_ADC_CHAN_TEMP("chg_temp_too_hot", &rradc_post_process_chg_temp_hot,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_CHARGER_TOO_HOT, FG_ADC_RR_CHARGER_TOO_HOT,
FG_ADC_RR_CHARGER_TEMP_STS)
RR_ADC_CHAN_TEMP("skin_temp_hot", &rradc_post_process_skin_temp_hot,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_SKIN_HOT, FG_ADC_RR_SKIN_HOT,
FG_ADC_RR_AUX_THERM_STS)
RR_ADC_CHAN_TEMP("skin_temp_too_hot", &rradc_post_process_skin_temp_hot,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_SKIN_TOO_HOT, FG_ADC_RR_SKIN_TOO_HOT,
FG_ADC_RR_AUX_THERM_STS)
};
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
index 4c341ffb6094..9d61eb110e2f 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
@@ -347,7 +347,7 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
ssize_t retval;
- unsigned char tmpbuf[count + 1];
+ unsigned char *tmpbuf;
struct rmidev_data *dev_data = filp->private_data;
if (IS_ERR(dev_data)) {
@@ -361,6 +361,10 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf,
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
+ tmpbuf = kzalloc(count + 1, GFP_KERNEL);
+ if (!tmpbuf)
+ return -ENOMEM;
+
mutex_lock(&(dev_data->file_mutex));
retval = synaptics_rmi4_reg_read(rmidev->rmi4_data,
@@ -377,7 +381,7 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf,
clean_up:
mutex_unlock(&(dev_data->file_mutex));
-
+ kfree(tmpbuf);
return retval;
}
@@ -393,7 +397,7 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
ssize_t retval;
- unsigned char tmpbuf[count + 1];
+ unsigned char *tmpbuf;
struct rmidev_data *dev_data = filp->private_data;
if (IS_ERR(dev_data)) {
@@ -407,9 +411,14 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf,
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
- if (copy_from_user(tmpbuf, buf, count))
- return -EFAULT;
+ tmpbuf = kzalloc(count + 1, GFP_KERNEL);
+ if (!tmpbuf)
+ return -ENOMEM;
+ if (copy_from_user(tmpbuf, buf, count)) {
+ kfree(tmpbuf);
+ return -EFAULT;
+ }
mutex_lock(&(dev_data->file_mutex));
retval = synaptics_rmi4_reg_write(rmidev->rmi4_data,
@@ -420,7 +429,7 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf,
*f_pos += retval;
mutex_unlock(&(dev_data->file_mutex));
-
+ kfree(tmpbuf);
return retval;
}
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index a892b73a4288..4036997f49c7 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -935,9 +935,14 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
return NULL;
/* TCR */
- reg = (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) |
- (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) |
- (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_ORGN0_SHIFT);
+ if (cfg->iommu_dev && cfg->iommu_dev->archdata.dma_coherent)
+ reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) |
+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) |
+ (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT);
+ else
+ reg = (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) |
+ (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) |
+ (ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_ORGN0_SHIFT);
switch (1 << data->pg_shift) {
case SZ_4K:
diff --git a/drivers/iommu/msm_dma_iommu_mapping.c b/drivers/iommu/msm_dma_iommu_mapping.c
index 334f4e95c068..0a8728ce36dc 100644
--- a/drivers/iommu/msm_dma_iommu_mapping.c
+++ b/drivers/iommu/msm_dma_iommu_mapping.c
@@ -376,6 +376,7 @@ int msm_dma_unmap_all_for_dev(struct device *dev)
return ret;
}
+EXPORT_SYMBOL(msm_dma_unmap_all_for_dev);
/*
* Only to be called by ION code when a buffer is freed
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
index b434161f5599..9a469abc56ca 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -248,13 +248,31 @@ static enum cam_ahb_clk_vote msm_isp47_get_cam_clk_vote(
return 0;
}
-static int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev,
+int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev,
struct msm_isp_ahb_clk_cfg *ahb_cfg)
{
int rc = 0;
enum cam_ahb_clk_vote vote;
+ enum cam_ahb_clk_vote src_clk_vote;
+ struct msm_isp_clk_rates clk_rates;
- vote = msm_isp47_get_cam_clk_vote(ahb_cfg->vote);
+ if (ahb_cfg)
+ vote = msm_isp47_get_cam_clk_vote(ahb_cfg->vote);
+ else
+ vote = CAM_AHB_SVS_VOTE;
+
+ vfe_dev->hw_info->vfe_ops.platform_ops.get_clk_rates(vfe_dev,
+ &clk_rates);
+ if (vfe_dev->msm_isp_vfe_clk_rate <= clk_rates.svs_rate)
+ src_clk_vote = CAM_AHB_SVS_VOTE;
+ else if (vfe_dev->msm_isp_vfe_clk_rate <= clk_rates.nominal_rate)
+ src_clk_vote = CAM_AHB_NOMINAL_VOTE;
+ else
+ src_clk_vote = CAM_AHB_TURBO_VOTE;
+
+ /* vote for higher of the user requested or src clock matched vote */
+ if (vote < src_clk_vote)
+ vote = src_clk_vote;
if (vote && vfe_dev->ahb_vote != vote) {
rc = cam_config_ahb_clk(NULL, 0,
@@ -320,6 +338,7 @@ enable_regulators_failed:
void msm_vfe47_release_hardware(struct vfe_device *vfe_dev)
{
enum cam_ahb_clk_client id;
+ unsigned long rate = 0;
/* when closing node, disable all irq */
vfe_dev->irq0_mask = 0;
@@ -345,6 +364,8 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev)
vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE;
+ vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(vfe_dev, &rate);
+
vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(
vfe_dev, 0);
vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0);
@@ -378,8 +399,8 @@ void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev)
/* BUS_CFG */
msm_camera_io_w(0x00000101, vfe_dev->vfe_base + 0x84);
/* IRQ_MASK/CLEAR */
- msm_vfe47_config_irq(vfe_dev, 0x810000E0, 0xFFFFFF7E,
- MSM_ISP_IRQ_ENABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 0x810000E0, 0xFFFFFF7E, MSM_ISP_IRQ_ENABLE);
msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64);
msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68);
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
@@ -387,8 +408,8 @@ void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev)
void msm_vfe47_clear_status_reg(struct vfe_device *vfe_dev)
{
- msm_vfe47_config_irq(vfe_dev, 0x80000000, 0x0,
- MSM_ISP_IRQ_SET);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 0x80000000, 0x0, MSM_ISP_IRQ_SET);
msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64);
msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68);
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58);
@@ -536,7 +557,8 @@ void msm_vfe47_read_irq_status(struct vfe_device *vfe_dev,
vfe_dev->error_info.camif_status =
msm_camera_io_r(vfe_dev->vfe_base + 0x4A4);
/* mask off camif error after first occurrance */
- msm_vfe47_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, 0,
+ (1 << 0), MSM_ISP_IRQ_DISABLE);
}
if (*irq_status1 & (1 << 7))
@@ -775,7 +797,8 @@ void msm_vfe47_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
stream_composite_mask << (comp_mask_index * 8));
msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74);
- msm_vfe47_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0,
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << (comp_mask_index + 25), 0,
MSM_ISP_IRQ_ENABLE);
}
@@ -790,7 +813,8 @@ void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev,
comp_mask &= ~(0x7F << (comp_mask_index * 8));
msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74);
- msm_vfe47_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0,
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ (1 << (comp_mask_index + 25)), 0,
MSM_ISP_IRQ_DISABLE);
}
@@ -799,7 +823,8 @@ void msm_vfe47_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
{
int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
- msm_vfe47_config_irq(vfe_dev, 1 << (stream_info->wm[vfe_idx][0] + 8), 0,
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << (stream_info->wm[vfe_idx][0] + 8), 0,
MSM_ISP_IRQ_ENABLE);
}
@@ -808,7 +833,8 @@ void msm_vfe47_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
{
int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
- msm_vfe47_config_irq(vfe_dev, (1 << (stream_info->wm[vfe_idx][0] + 8)),
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ (1 << (stream_info->wm[vfe_idx][0] + 8)),
0, MSM_ISP_IRQ_DISABLE);
}
@@ -1060,7 +1086,8 @@ void msm_vfe47_cfg_fetch_engine(struct vfe_device *vfe_dev,
temp |= (1 << 1);
msm_camera_io_w(temp, vfe_dev->vfe_base + 0x84);
- msm_vfe47_config_irq(vfe_dev, (1 << 24), 0,
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ (1 << 24), 0,
MSM_ISP_IRQ_ENABLE);
temp = fe_cfg->fetch_height - 1;
@@ -1390,7 +1417,8 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x64);
msm_camera_io_w(0x81, vfe_dev->vfe_base + 0x68);
msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58);
- msm_vfe47_config_irq(vfe_dev, 0x15, 0x81,
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 0x15, 0x81,
MSM_ISP_IRQ_ENABLE);
if ((vfe_dev->hvx_cmd > HVX_DISABLE) &&
@@ -1422,7 +1450,7 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
update_state = DISABLE_CAMIF;
/* turn off camif violation and error irqs */
- msm_vfe47_config_irq(vfe_dev, 0, 0x81,
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev, 0, 0x81,
MSM_ISP_IRQ_DISABLE);
val = msm_camera_io_r(vfe_dev->vfe_base + 0x464);
/* disable danger signal */
@@ -1447,7 +1475,8 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
msm_camera_io_w(0, vfe_dev->vfe_base + 0x64);
msm_camera_io_w(1 << 0, vfe_dev->vfe_base + 0x68);
msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58);
- msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask,
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ vfe_dev->irq0_mask,
vfe_dev->irq1_mask, MSM_ISP_IRQ_SET);
}
@@ -1728,7 +1757,8 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev,
msm_camera_io_w(val, vfe_dev->vfe_vbif_base + VFE47_VBIF_CLK_OFFSET);
/* Keep only halt and reset mask */
- msm_vfe47_config_irq(vfe_dev, (1 << 31), (1 << 8),
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ (1 << 31), (1 << 8),
MSM_ISP_IRQ_SET);
/*Clear IRQ Status0, only leave reset irq mask*/
@@ -1777,7 +1807,8 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev,
void msm_vfe47_axi_restart(struct vfe_device *vfe_dev,
uint32_t blocking, uint32_t enable_camif)
{
- msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask,
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ vfe_dev->irq0_mask, vfe_dev->irq1_mask,
MSM_ISP_IRQ_SET);
msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x64);
msm_camera_io_w(0xFFFFFEFF, vfe_dev->vfe_base + 0x68);
@@ -1884,7 +1915,8 @@ void msm_vfe47_stats_cfg_comp_mask(
comp_mask_reg |= stats_mask << (request_comp_index * 16);
atomic_set(stats_comp_mask, stats_mask |
atomic_read(stats_comp_mask));
- msm_vfe47_config_irq(vfe_dev, 1 << (29 + request_comp_index),
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << (29 + request_comp_index),
0, MSM_ISP_IRQ_ENABLE);
} else {
if (!(atomic_read(stats_comp_mask) & stats_mask))
@@ -1893,7 +1925,8 @@ void msm_vfe47_stats_cfg_comp_mask(
atomic_set(stats_comp_mask,
~stats_mask & atomic_read(stats_comp_mask));
comp_mask_reg &= ~(stats_mask << (request_comp_index * 16));
- msm_vfe47_config_irq(vfe_dev, 1 << (29 + request_comp_index),
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << (29 + request_comp_index),
0, MSM_ISP_IRQ_DISABLE);
}
@@ -1916,32 +1949,41 @@ void msm_vfe47_stats_cfg_wm_irq_mask(
switch (STATS_IDX(stream_info->stream_handle[vfe_idx])) {
case STATS_COMP_IDX_AEC_BG:
- msm_vfe47_config_irq(vfe_dev, 1 << 15, 0, MSM_ISP_IRQ_ENABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 15, 0, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_HDR_BE:
- msm_vfe47_config_irq(vfe_dev, 1 << 16, 0, MSM_ISP_IRQ_ENABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 16, 0, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_BG:
- msm_vfe47_config_irq(vfe_dev, 1 << 17, 0, MSM_ISP_IRQ_ENABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 17, 0, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_BF:
- msm_vfe47_config_irq(vfe_dev, 1 << 18, 1 << 26,
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 18, 1 << 26,
MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_HDR_BHIST:
- msm_vfe47_config_irq(vfe_dev, 1 << 19, 0, MSM_ISP_IRQ_ENABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 19, 0, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_RS:
- msm_vfe47_config_irq(vfe_dev, 1 << 20, 0, MSM_ISP_IRQ_ENABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 20, 0, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_CS:
- msm_vfe47_config_irq(vfe_dev, 1 << 21, 0, MSM_ISP_IRQ_ENABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 21, 0, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_IHIST:
- msm_vfe47_config_irq(vfe_dev, 1 << 22, 0, MSM_ISP_IRQ_ENABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 22, 0, MSM_ISP_IRQ_ENABLE);
break;
case STATS_COMP_IDX_BHIST:
- msm_vfe47_config_irq(vfe_dev, 1 << 23, 0, MSM_ISP_IRQ_ENABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 23, 0, MSM_ISP_IRQ_ENABLE);
break;
default:
pr_err("%s: Invalid stats idx %d\n", __func__,
@@ -1958,32 +2000,41 @@ void msm_vfe47_stats_clear_wm_irq_mask(
switch (STATS_IDX(stream_info->stream_handle[vfe_idx])) {
case STATS_COMP_IDX_AEC_BG:
- msm_vfe47_config_irq(vfe_dev, 1 << 15, 0, MSM_ISP_IRQ_DISABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 15, 0, MSM_ISP_IRQ_DISABLE);
break;
case STATS_COMP_IDX_HDR_BE:
- msm_vfe47_config_irq(vfe_dev, 1 << 16, 0, MSM_ISP_IRQ_DISABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 16, 0, MSM_ISP_IRQ_DISABLE);
break;
case STATS_COMP_IDX_BG:
- msm_vfe47_config_irq(vfe_dev, 1 << 17, 0, MSM_ISP_IRQ_DISABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 17, 0, MSM_ISP_IRQ_DISABLE);
break;
case STATS_COMP_IDX_BF:
- msm_vfe47_config_irq(vfe_dev, 1 << 18, 1 << 26,
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 18, 1 << 26,
MSM_ISP_IRQ_DISABLE);
break;
case STATS_COMP_IDX_HDR_BHIST:
- msm_vfe47_config_irq(vfe_dev, 1 << 19, 0, MSM_ISP_IRQ_DISABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 19, 0, MSM_ISP_IRQ_DISABLE);
break;
case STATS_COMP_IDX_RS:
- msm_vfe47_config_irq(vfe_dev, 1 << 20, 0, MSM_ISP_IRQ_DISABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 20, 0, MSM_ISP_IRQ_DISABLE);
break;
case STATS_COMP_IDX_CS:
- msm_vfe47_config_irq(vfe_dev, 1 << 21, 0, MSM_ISP_IRQ_DISABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 21, 0, MSM_ISP_IRQ_DISABLE);
break;
case STATS_COMP_IDX_IHIST:
- msm_vfe47_config_irq(vfe_dev, 1 << 22, 0, MSM_ISP_IRQ_DISABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 22, 0, MSM_ISP_IRQ_DISABLE);
break;
case STATS_COMP_IDX_BHIST:
- msm_vfe47_config_irq(vfe_dev, 1 << 23, 0, MSM_ISP_IRQ_DISABLE);
+ vfe_dev->hw_info->vfe_ops.irq_ops.config_irq(vfe_dev,
+ 1 << 23, 0, MSM_ISP_IRQ_DISABLE);
break;
default:
pr_err("%s: Invalid stats idx %d\n", __func__,
@@ -2336,6 +2387,9 @@ int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate)
return rc;
*rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate);
vfe_dev->msm_isp_vfe_clk_rate = *rate;
+
+ if (vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg)
+ vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg(vfe_dev, NULL);
return 0;
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
index 8581373b3b71..3955196d1deb 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
@@ -193,4 +193,6 @@ int msm_vfe47_update_bandwidth(
void msm_vfe47_config_irq(struct vfe_device *vfe_dev,
uint32_t irq0_mask, uint32_t irq1_mask,
enum msm_isp_irq_operation oper);
+int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev,
+ struct msm_isp_ahb_clk_cfg *ahb_cfg);
#endif /* __MSM_ISP47_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
index a792404c243c..49520bb44ad8 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
@@ -246,6 +246,8 @@ struct msm_vfe_hardware_info vfe48_hw_info = {
.num_iommu_secure_ctx = 0,
.vfe_clk_idx = VFE48_SRC_CLK_DTSI_IDX,
.runtime_axi_update = 1,
+ .min_ib = 100000000,
+ .min_ab = 100000000,
.vfe_ops = {
.irq_ops = {
.read_irq_status = msm_vfe47_read_irq_status,
@@ -306,6 +308,7 @@ struct msm_vfe_hardware_info vfe48_hw_info = {
.process_error_status = msm_vfe47_process_error_status,
.is_module_cfg_lock_needed =
msm_vfe47_is_module_cfg_lock_needed,
+ .ahb_clk_cfg = msm_isp47_ahb_clk_cfg,
},
.stats_ops = {
.get_stats_idx = msm_vfe47_get_stats_idx,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index 39a0845a886f..7488f371545b 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -1023,31 +1023,32 @@ static void msm_isp_calculate_bandwidth(
struct msm_vfe_axi_stream *stream_info)
{
int bpp = 0;
+ struct vfe_device *vfe_dev;
struct msm_vfe_axi_shared_data *axi_data;
int i;
- if (stream_info->stream_src < RDI_INTF_0) {
- for (i = 0; i < stream_info->num_isp; i++) {
- axi_data = &stream_info->vfe_dev[i]->axi_data;
+ for (i = 0; i < stream_info->num_isp; i++) {
+ vfe_dev = stream_info->vfe_dev[i];
+ axi_data = &vfe_dev->axi_data;
+ if (stream_info->stream_src < RDI_INTF_0) {
stream_info->bandwidth[i] =
- (axi_data->src_info[VFE_PIX_0].pixel_clock /
+ (vfe_dev->msm_isp_vfe_clk_rate /
axi_data->src_info[VFE_PIX_0].width) *
stream_info->max_width[i];
stream_info->bandwidth[i] =
(unsigned long)stream_info->bandwidth[i] *
stream_info->format_factor / ISP_Q2;
- }
- } else {
- int rdi = SRC_TO_INTF(stream_info->stream_src);
- bpp = msm_isp_get_bit_per_pixel(stream_info->output_format);
- if (rdi < VFE_SRC_MAX) {
- for (i = 0; i < stream_info->num_isp; i++) {
- axi_data = &stream_info->vfe_dev[i]->axi_data;
+ } else {
+ int rdi = SRC_TO_INTF(stream_info->stream_src);
+
+ bpp = msm_isp_get_bit_per_pixel(
+ stream_info->output_format);
+ if (rdi < VFE_SRC_MAX) {
stream_info->bandwidth[i] =
- (axi_data->src_info[rdi].pixel_clock / 8) * bpp;
+ (vfe_dev->msm_isp_vfe_clk_rate / 8) * bpp;
+ } else {
+ pr_err("%s: Invalid rdi interface\n", __func__);
}
- } else {
- pr_err("%s: Invalid rdi interface\n", __func__);
}
}
}
@@ -1103,7 +1104,6 @@ int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
uint32_t io_format = 0;
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd = arg;
struct msm_vfe_axi_stream *stream_info;
- unsigned long flags;
if (stream_cfg_cmd->stream_src >= VFE_AXI_SRC_MAX) {
pr_err("%s:%d invalid stream_src %d\n", __func__, __LINE__,
@@ -1113,12 +1113,9 @@ int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
stream_info = msm_isp_get_stream_common_data(vfe_dev,
stream_cfg_cmd->stream_src);
- spin_lock_irqsave(&stream_info->lock, flags);
-
rc = msm_isp_axi_create_stream(vfe_dev,
&vfe_dev->axi_data, stream_cfg_cmd, stream_info);
if (rc) {
- spin_unlock_irqrestore(&stream_info->lock, flags);
pr_err("%s: create stream failed\n", __func__);
return rc;
}
@@ -1127,7 +1124,6 @@ int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
vfe_dev, stream_info, stream_cfg_cmd);
if (rc) {
msm_isp_axi_destroy_stream(vfe_dev, stream_info);
- spin_unlock_irqrestore(&stream_info->lock, flags);
pr_err("%s: Request validation failed\n", __func__);
return rc;
}
@@ -1235,7 +1231,6 @@ done:
msm_isp_axi_free_wm(vfe_dev, stream_info);
msm_isp_axi_destroy_stream(vfe_dev, stream_info);
}
- spin_unlock_irqrestore(&stream_info->lock, flags);
return rc;
}
@@ -1246,7 +1241,6 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_stream_cfg_cmd stream_cfg;
int vfe_idx;
- unsigned long flags;
if (HANDLE_TO_IDX(stream_release_cmd->stream_handle) >=
VFE_AXI_SRC_MAX) {
@@ -1256,13 +1250,10 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
stream_info = msm_isp_get_stream_common_data(vfe_dev,
HANDLE_TO_IDX(stream_release_cmd->stream_handle));
- spin_lock_irqsave(&stream_info->lock, flags);
-
vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev, stream_info);
if (vfe_idx == -ENOTTY ||
stream_release_cmd->stream_handle !=
stream_info->stream_handle[vfe_idx]) {
- spin_unlock_irqrestore(&stream_info->lock, flags);
pr_err("%s: Invalid stream %p handle %x/%x vfe_idx %d vfe_dev %d num_isp %d\n",
__func__, stream_info,
stream_release_cmd->stream_handle,
@@ -1276,9 +1267,7 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
stream_cfg.cmd = STOP_STREAM;
stream_cfg.num_streams = 1;
stream_cfg.stream_handle[0] = stream_release_cmd->stream_handle;
- spin_unlock_irqrestore(&stream_info->lock, flags);
msm_isp_cfg_axi_stream(vfe_dev, (void *) &stream_cfg);
- spin_lock_irqsave(&stream_info->lock, flags);
}
for (i = 0; i < stream_info->num_planes; i++) {
@@ -1296,7 +1285,6 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
msm_isp_axi_free_wm(vfe_dev, stream_info);
msm_isp_axi_destroy_stream(vfe_dev, stream_info);
- spin_unlock_irqrestore(&stream_info->lock, flags);
return rc;
}
@@ -1411,6 +1399,7 @@ static void __msm_isp_axi_stream_update(
switch (stream_info->state) {
case UPDATING:
stream_info->state = ACTIVE;
+ complete_all(&stream_info->active_comp);
break;
case STOP_PENDING:
msm_isp_axi_stream_enable_cfg(stream_info);
@@ -2268,13 +2257,14 @@ int msm_isp_axi_halt(struct vfe_device *vfe_dev,
int msm_isp_axi_reset(struct vfe_device *vfe_dev,
struct msm_vfe_axi_reset_cmd *reset_cmd)
{
- int rc = 0, i, k;
+ int rc = 0, i, k, j;
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
uint32_t bufq_handle = 0, bufq_id = 0;
struct msm_isp_timestamp timestamp;
unsigned long flags;
struct vfe_device *update_vfes[MAX_VFE] = {0, 0};
+ int vfe_idx;
if (!reset_cmd) {
pr_err("%s: NULL pointer reset cmd %pK\n", __func__, reset_cmd);
@@ -2345,6 +2335,20 @@ int msm_isp_axi_reset(struct vfe_device *vfe_dev,
ISP_EVENT_BUF_FATAL_ERROR);
return rc;
}
+ if (stream_info->num_planes > 1) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_comp_mask(vfe_dev, stream_info);
+ } else {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_irq_mask(vfe_dev, stream_info);
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(
+ vfe_dev, stream_info);
+ for (j = 0; j < stream_info->num_planes; j++)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ enable_wm(
+ vfe_dev->vfe_base,
+ stream_info->wm[vfe_idx][j], 1);
axi_data->src_info[SRC_TO_INTF(stream_info->
stream_src)].frame_id =
@@ -2731,6 +2735,7 @@ static void __msm_isp_stop_axi_streams(struct msm_vfe_axi_stream **streams,
&timestamp);
msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG);
msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG);
+ stream_info->undelivered_request_cnt = 0;
for (k = 0; k < stream_info->num_isp; k++) {
vfe_dev = stream_info->vfe_dev[k];
if (stream_info->num_planes > 1)
@@ -2879,6 +2884,8 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl,
msm_isp_get_timestamp(&timestamp);
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (stream_cfg_cmd->stream_handle[i] == 0)
+ continue;
stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl,
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX)
@@ -3018,6 +3025,8 @@ static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev_ioctl,
return -EINVAL;
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (stream_cfg_cmd->stream_handle[i] == 0)
+ continue;
stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl,
HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
@@ -3044,12 +3053,37 @@ int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0, ret;
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg;
+ uint32_t stream_idx[MAX_NUM_STREAM];
int i;
+ int vfe_idx;
+ struct msm_vfe_axi_stream *stream_info;
+
+ memset(stream_idx, 0, sizeof(stream_idx));
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >=
VFE_AXI_SRC_MAX)
return -EINVAL;
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
+ vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev,
+ stream_info);
+ if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] !=
+ stream_cfg_cmd->stream_handle[i]) {
+ pr_err("%s: Invalid stream handle %x vfe_idx %d expected %x\n",
+ __func__, stream_cfg_cmd->stream_handle[i],
+ vfe_idx,
+ (vfe_idx != -ENOTTY) ?
+ stream_info->stream_handle[vfe_idx] : 0);
+ return -EINVAL;
+ }
+ /* check for duplicate stream handle */
+ if (stream_idx[stream_info->stream_src] ==
+ stream_cfg_cmd->stream_handle[i])
+ stream_cfg_cmd->stream_handle[i] = 0;
+ else
+ stream_idx[stream_info->stream_src] =
+ stream_cfg_cmd->stream_handle[i];
}
if (stream_cfg_cmd->cmd == START_STREAM) {
msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 1);
@@ -3387,8 +3421,10 @@ static void msm_isp_remove_buf_queue(struct vfe_device *vfe_dev,
if (stream_info->bufq_handle[bufq_id]) {
stream_info->bufq_handle[bufq_id] = 0;
- if (stream_info->state == ACTIVE)
+ if (stream_info->state == ACTIVE) {
+ init_completion(&stream_info->active_comp);
stream_info->state = UPDATING;
+ }
}
spin_unlock_irqrestore(&stream_info->lock, flags);
if (stream_info->state == UPDATING)
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index f851e8c9289e..e226f7e40a07 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -460,7 +460,6 @@ int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg)
struct msm_vfe_stats_stream_request_cmd *stream_req_cmd = arg;
struct msm_vfe_stats_stream *stream_info = NULL;
uint32_t stats_idx;
- unsigned long flags;
stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops.
get_stats_idx(stream_req_cmd->stats_type);
@@ -472,11 +471,8 @@ int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg)
stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, stats_idx);
- spin_lock_irqsave(&stream_info->lock, flags);
-
rc = msm_isp_stats_create_stream(vfe_dev, stream_req_cmd, stream_info);
if (rc < 0) {
- spin_unlock_irqrestore(&stream_info->lock, flags);
pr_err("%s: create stream failed\n", __func__);
return rc;
}
@@ -491,7 +487,6 @@ int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg)
msm_isp_stats_cfg_stream_scratch(stream_info,
VFE_PONG_FLAG);
}
- spin_unlock_irqrestore(&stream_info->lock, flags);
return rc;
}
@@ -505,7 +500,6 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
int vfe_idx;
int i;
int k;
- unsigned long flags;
if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
pr_err("%s Invalid stats index %d", __func__, stats_idx);
@@ -513,12 +507,10 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
}
stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, stats_idx);
- spin_lock_irqsave(&stream_info->lock, flags);
vfe_idx = msm_isp_get_vfe_idx_for_stats_stream_user(
vfe_dev, stream_info);
if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] !=
stream_release_cmd->stream_handle) {
- spin_unlock_irqrestore(&stream_info->lock, flags);
pr_err("%s: Invalid stream handle %x, expected %x\n",
__func__, stream_release_cmd->stream_handle,
vfe_idx != -ENOTTY ?
@@ -526,7 +518,6 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
return -EINVAL;
}
if (stream_info->state == STATS_AVAILABLE) {
- spin_unlock_irqrestore(&stream_info->lock, flags);
pr_err("%s: stream already release\n", __func__);
return rc;
}
@@ -537,9 +528,7 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
stream_cfg_cmd.num_streams = 1;
stream_cfg_cmd.stream_handle[0] =
stream_release_cmd->stream_handle;
- spin_unlock_irqrestore(&stream_info->lock, flags);
msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd);
- spin_lock_irqsave(&stream_info->lock, flags);
}
for (i = vfe_idx, k = vfe_idx + 1; k < stream_info->num_isp; k++, i++) {
@@ -556,7 +545,6 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
if (stream_info->num_isp == 0)
stream_info->state = STATS_AVAILABLE;
- spin_unlock_irqrestore(&stream_info->lock, flags);
return 0;
}
@@ -904,6 +892,10 @@ int msm_isp_stats_reset(struct vfe_device *vfe_dev)
ISP_EVENT_BUF_FATAL_ERROR);
return rc;
}
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_irq_mask(
+ vfe_dev, stream_info);
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, BIT(i), 1);
}
}
@@ -960,7 +952,9 @@ static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info;
uint32_t idx;
int vfe_idx;
+ uint32_t stats_idx[MSM_ISP_STATS_MAX];
+ memset(stats_idx, 0, sizeof(stats_idx));
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
@@ -980,6 +974,11 @@ static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev,
stream_info->stream_handle[vfe_idx]);
return -EINVAL;
}
+ /* remove duplicate handles */
+ if (stats_idx[idx] == stream_cfg_cmd->stream_handle[i])
+ stream_cfg_cmd->stream_handle[i] = 0;
+ else
+ stats_idx[idx] = stream_cfg_cmd->stream_handle[i];
}
return 0;
}
@@ -1083,6 +1082,8 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl,
num_stats_comp_mask =
vfe_dev_ioctl->hw_info->stats_hw_info->num_stats_comp_mask;
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ if (stream_cfg_cmd->stream_handle[i] == 0)
+ continue;
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
stream_info = msm_isp_get_stats_stream_common_data(
vfe_dev_ioctl, idx);
@@ -1169,7 +1170,8 @@ static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev,
vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
-
+ if (stream_cfg_cmd->stream_handle[i] == 0)
+ continue;
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
stream_info = msm_isp_get_stats_stream_common_data(
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index a4eb80f31984..71c907f2b381 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -20,6 +20,7 @@
#include "msm_isp_stats_util.h"
#include "msm_camera_io_util.h"
#include "cam_smmu_api.h"
+#include "msm_isp48.h"
#define MAX_ISP_V4l2_EVENTS 100
static DEFINE_MUTEX(bandwidth_mgr_mutex);
@@ -482,11 +483,18 @@ int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg)
}
pixel_clock = input_cfg->input_pix_clk;
- rc = vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(vfe_dev,
- &pixel_clock);
- if (rc < 0) {
- pr_err("%s: clock set rate failed\n", __func__);
- return rc;
+ /*
+ * Only set rate to higher, do not lower higher
+ * rate needed by another input
+ */
+ if (pixel_clock > vfe_dev->msm_isp_vfe_clk_rate) {
+ rc = vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(
+ vfe_dev,
+ &pixel_clock);
+ if (rc < 0) {
+ pr_err("%s: clock set rate failed\n", __func__);
+ return rc;
+ }
}
return rc;
}
@@ -1739,6 +1747,9 @@ static void msm_isp_process_overflow_irq(
if (overflow_mask) {
struct msm_isp_event_data error_event;
struct msm_vfe_axi_halt_cmd halt_cmd;
+ uint32_t val = 0;
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
if (vfe_dev->reset_pending == 1) {
pr_err("%s:%d failed: overflow %x during reset\n",
@@ -1747,6 +1758,16 @@ static void msm_isp_process_overflow_irq(
*irq_status1 &= ~overflow_mask;
return;
}
+ if (msm_vfe_is_vfe48(vfe_dev))
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0xC94);
+ pr_err("%s: vfe %d overflow mask %x, bus_error %x\n",
+ __func__, vfe_dev->pdev->id, overflow_mask, val);
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (!axi_data->free_wm[i])
+ continue;
+ pr_err("%s: wm %d assigned to stream handle %x\n",
+ __func__, i, axi_data->free_wm[i]);
+ }
halt_cmd.overflow_detected = 1;
halt_cmd.stop_camif = 1;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
index 0b3e4e1fcf04..bf3973888573 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
@@ -101,11 +101,6 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl,
i2c_tbl = a_ctrl->i2c_reg_tbl;
for (i = 0; i < size; i++) {
- /* check that the index into i2c_tbl cannot grow larger that
- the allocated size of i2c_tbl */
- if ((a_ctrl->total_steps + 1) < (a_ctrl->i2c_tbl_index))
- break;
-
if (write_arr[i].reg_write_type == MSM_ACTUATOR_WRITE_DAC) {
value = (next_lens_position <<
write_arr[i].data_shift) |
@@ -119,6 +114,11 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl,
i2c_byte2 = value & 0xFF;
CDBG("byte1:0x%x, byte2:0x%x\n",
i2c_byte1, i2c_byte2);
+ if (a_ctrl->i2c_tbl_index >
+ a_ctrl->total_steps) {
+ pr_err("failed:i2c table index out of bound\n");
+ break;
+ }
i2c_tbl[a_ctrl->i2c_tbl_index].
reg_addr = i2c_byte1;
i2c_tbl[a_ctrl->i2c_tbl_index].
@@ -139,6 +139,10 @@ static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t *a_ctrl,
i2c_byte2 = (hw_dword & write_arr[i].hw_mask) >>
write_arr[i].hw_shift;
}
+ if (a_ctrl->i2c_tbl_index > a_ctrl->total_steps) {
+ pr_err("failed: i2c table index out of bound\n");
+ break;
+ }
CDBG("i2c_byte1:0x%x, i2c_byte2:0x%x\n", i2c_byte1, i2c_byte2);
i2c_tbl[a_ctrl->i2c_tbl_index].reg_addr = i2c_byte1;
i2c_tbl[a_ctrl->i2c_tbl_index].reg_data = i2c_byte2;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
index 8f911d362477..a4ee5041bfff 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_cci_i2c.c
@@ -276,6 +276,12 @@ int32_t msm_camera_cci_i2c_write_seq_table(
client_addr_type = client->addr_type;
client->addr_type = write_setting->addr_type;
+ if (reg_setting->reg_data_size > I2C_SEQ_REG_DATA_MAX) {
+ pr_err("%s: number of bytes %u exceeding the max supported %d\n",
+ __func__, reg_setting->reg_data_size, I2C_SEQ_REG_DATA_MAX);
+ return rc;
+ }
+
for (i = 0; i < write_setting->size; i++) {
rc = msm_camera_cci_i2c_write_seq(client, reg_setting->reg_addr,
reg_setting->reg_data, reg_setting->reg_data_size);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
index 3b101798edac..7a0fb97061d5 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
@@ -290,6 +290,12 @@ int32_t msm_camera_qup_i2c_write_seq_table(struct msm_camera_i2c_client *client,
client_addr_type = client->addr_type;
client->addr_type = write_setting->addr_type;
+ if (reg_setting->reg_data_size > I2C_SEQ_REG_DATA_MAX) {
+ pr_err("%s: number of bytes %u exceeding the max supported %d\n",
+ __func__, reg_setting->reg_data_size, I2C_SEQ_REG_DATA_MAX);
+ return rc;
+ }
+
for (i = 0; i < write_setting->size; i++) {
rc = msm_camera_qup_i2c_write_seq(client, reg_setting->reg_addr,
reg_setting->reg_data, reg_setting->reg_data_size);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
index 43aadffa2983..86e7837cc02a 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
@@ -1167,6 +1167,7 @@ static int32_t msm_sensor_driver_parse(struct msm_sensor_ctrl_t *s_ctrl)
if (!s_ctrl->msm_sensor_mutex) {
pr_err("failed: no memory msm_sensor_mutex %pK",
s_ctrl->msm_sensor_mutex);
+ rc = -ENOMEM;
goto FREE_SENSOR_I2C_CLIENT;
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
index eed177ea5bab..60c4c81eddf2 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
@@ -355,13 +355,6 @@ int sde_mdp_get_plane_sizes(struct sde_mdp_format_params *fmt, u32 w, u32 h,
chroma_samp = fmt->chroma_sample;
- if (rotation) {
- if (chroma_samp == SDE_MDP_CHROMA_H2V1)
- chroma_samp = SDE_MDP_CHROMA_H1V2;
- else if (chroma_samp == SDE_MDP_CHROMA_H1V2)
- chroma_samp = SDE_MDP_CHROMA_H2V1;
- }
-
sde_mdp_get_v_h_subsample_rate(chroma_samp,
&v_subsample, &h_subsample);
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index fdf6e1b1c5d0..d9f47978a081 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -626,6 +626,11 @@ static u32 get_frame_size_compressed(int plane,
return (max_mbs_per_frame * size_per_mb * 3/2)/2;
}
+static u32 get_frame_size_nv12_ubwc_10bit(int plane, u32 height, u32 width)
+{
+ return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_BPP10_UBWC, width, height);
+}
+
static u32 get_frame_size(struct msm_vidc_inst *inst,
const struct msm_vidc_format *fmt,
int fmt_type, int plane)
@@ -712,6 +717,14 @@ struct msm_vidc_format vdec_formats[] = {
.type = CAPTURE_PORT,
},
{
+ .name = "UBWC YCbCr Semiplanar 4:2:0 10bit",
+ .description = "UBWC Y/CbCr 4:2:0 10bit",
+ .fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC,
+ .num_planes = 2,
+ .get_frame_size = get_frame_size_nv12_ubwc_10bit,
+ .type = CAPTURE_PORT,
+ },
+ {
.name = "Mpeg4",
.description = "Mpeg4 compressed format",
.fourcc = V4L2_PIX_FMT_MPEG4,
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index ce115c98a72f..d3e420f1b26b 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -1583,6 +1583,32 @@ static const struct file_operations fops_fw_capabilities = {
.llseek = seq_lseek,
};
+/*---------FW version------------*/
+static int wil_fw_version_debugfs_show(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+
+ if (wil->fw_version[0])
+ seq_printf(s, "%s\n", wil->fw_version);
+ else
+ seq_puts(s, "N/A\n");
+
+ return 0;
+}
+
+static int wil_fw_version_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_fw_version_debugfs_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_fw_version = {
+ .open = wil_fw_version_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
/*----------------*/
static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
struct dentry *dbg)
@@ -1634,6 +1660,7 @@ static const struct {
{"led_cfg", S_IRUGO | S_IWUSR, &fops_led_cfg},
{"led_blink_time", S_IRUGO | S_IWUSR, &fops_led_blink_time},
{"fw_capabilities", S_IRUGO, &fops_fw_capabilities},
+ {"fw_version", S_IRUGO, &fops_fw_version},
};
static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
@@ -1674,7 +1701,6 @@ static void wil6210_debugfs_init_isr(struct wil6210_priv *wil,
static const struct dbg_off dbg_wil_off[] = {
WIL_FIELD(privacy, S_IRUGO, doff_u32),
WIL_FIELD(status[0], S_IRUGO | S_IWUSR, doff_ulong),
- WIL_FIELD(fw_version, S_IRUGO, doff_u32),
WIL_FIELD(hw_version, S_IRUGO, doff_x32),
WIL_FIELD(recovery_count, S_IRUGO, doff_u32),
WIL_FIELD(ap_isolate, S_IRUGO, doff_u32),
diff --git a/drivers/net/wireless/ath/wil6210/fw.h b/drivers/net/wireless/ath/wil6210/fw.h
index c3191c61832c..2f2b910501ba 100644
--- a/drivers/net/wireless/ath/wil6210/fw.h
+++ b/drivers/net/wireless/ath/wil6210/fw.h
@@ -102,6 +102,9 @@ struct wil_fw_record_verify { /* type == wil_fw_verify */
/* file header
* First record of every file
*/
+/* the FW version prefix in the comment */
+#define WIL_FW_VERSION_PREFIX "FW version: "
+#define WIL_FW_VERSION_PREFIX_LEN (sizeof(WIL_FW_VERSION_PREFIX) - 1)
struct wil_fw_record_file_header {
__le32 signature ; /* Wilocity signature */
__le32 reserved;
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
index 3860238840ba..8f40eb301924 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -223,6 +223,13 @@ static int fw_handle_file_header(struct wil6210_priv *wil, const void *data,
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, d->comment,
sizeof(d->comment), true);
+ if (!memcmp(d->comment, WIL_FW_VERSION_PREFIX,
+ WIL_FW_VERSION_PREFIX_LEN))
+ memcpy(wil->fw_version,
+ d->comment + WIL_FW_VERSION_PREFIX_LEN,
+ min(sizeof(d->comment) - WIL_FW_VERSION_PREFIX_LEN,
+ sizeof(wil->fw_version) - 1));
+
return 0;
}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 88dfb93d0f3d..a509841c3187 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -892,6 +892,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
WIL_FW2_NAME);
wil_halt_cpu(wil);
+ memset(wil->fw_version, 0, sizeof(wil->fw_version));
/* Loading f/w from the file */
rc = wil_request_firmware(wil, WIL_FW_NAME, true);
if (rc)
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 81a4c6a684f5..f4fca9d4eedf 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -222,6 +222,8 @@ int wil_if_add(struct wil6210_priv *wil)
wil_dbg_misc(wil, "entered");
+ strlcpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version));
+
rc = wiphy_register(wiphy);
if (rc < 0) {
wil_err(wil, "failed to register wiphy, err %d\n", rc);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 1e0536adb6e7..ce33e919d321 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -17,6 +17,7 @@
#ifndef __WIL6210_H__
#define __WIL6210_H__
+#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <net/cfg80211.h>
@@ -579,7 +580,7 @@ struct wil6210_priv {
struct wireless_dev *wdev;
void __iomem *csr;
DECLARE_BITMAP(status, wil_status_last);
- u32 fw_version;
+ u8 fw_version[ETHTOOL_FWVERS_LEN];
u32 hw_version;
const char *hw_name;
DECLARE_BITMAP(hw_capabilities, hw_capability_last);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 0c5db9584159..6ec3ddc5b6f1 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -312,14 +312,14 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
struct wireless_dev *wdev = wil->wdev;
struct wmi_ready_event *evt = d;
- wil->fw_version = le32_to_cpu(evt->sw_version);
wil->n_mids = evt->numof_additional_mids;
- wil_info(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version,
+ wil_info(wil, "FW ver. %s(SW %d); MAC %pM; %d MID's\n",
+ wil->fw_version, le32_to_cpu(evt->sw_version),
evt->mac, wil->n_mids);
/* ignore MAC address, we already have it from the boot loader */
- snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version),
- "%d", wil->fw_version);
+ strlcpy(wdev->wiphy->fw_version, wil->fw_version,
+ sizeof(wdev->wiphy->fw_version));
wil_set_recovery_state(wil, fw_recovery_idle);
set_bit(wil_status_fwready, wil->status);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
index 08ed47f3cacf..d14f8da15595 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
@@ -150,10 +150,16 @@ int ipa2_register_ipa_ready_cb(void (*ipa_ready_cb)(void *), void *user_data)
{
int ret;
+ if (!ipa_ctx) {
+ IPAERR("IPA ctx is null\n");
+ return -ENXIO;
+ }
+
ret = ipa2_uc_state_check();
if (ret) {
ipa_ctx->uc_ntn_ctx.uc_ready_cb = ipa_ready_cb;
ipa_ctx->uc_ntn_ctx.priv = user_data;
+ return 0;
}
return -EEXIST;
diff --git a/drivers/platform/msm/sps/bam.c b/drivers/platform/msm/sps/bam.c
index 3aef2060ab52..c94536398dac 100644
--- a/drivers/platform/msm/sps/bam.c
+++ b/drivers/platform/msm/sps/bam.c
@@ -1162,7 +1162,7 @@ void bam_output_register_content(void *base, u32 ee)
print_bam_test_bus_reg(base, 0);
- print_bam_selected_reg(dev->base, BAM_MAX_EES);
+ print_bam_selected_reg(base, BAM_MAX_EES);
num_pipes = bam_read_reg_field(base, NUM_PIPES, 0,
BAM_NUM_PIPES);
@@ -1174,11 +1174,11 @@ void bam_output_register_content(void *base, u32 ee)
if (!enhd_pipe || !pipe_attr)
for (i = 0; i < num_pipes; i++)
- print_bam_pipe_selected_reg(dev->base, i);
+ print_bam_pipe_selected_reg(base, i);
else {
for (i = 0; i < num_pipes; i++) {
if (pipe_attr & (1UL << i))
- print_bam_pipe_selected_reg(dev->base, i);
+ print_bam_pipe_selected_reg(base, i);
}
}
}
diff --git a/drivers/power/qcom-charger/Makefile b/drivers/power/qcom-charger/Makefile
index aae6084c3c10..0126d2d0a18e 100644
--- a/drivers/power/qcom-charger/Makefile
+++ b/drivers/power/qcom-charger/Makefile
@@ -6,6 +6,6 @@ obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o
obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o
obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o
obj-$(CONFIG_BATTERY_BCL) += battery_current_limit.o
-obj-$(CONFIG_QPNP_SMB2) += qpnp-smb2.o smb-lib.o pmic-voter.o
-obj-$(CONFIG_SMB138X_CHARGER) += smb138x-charger.o smb-lib.o pmic-voter.o
+obj-$(CONFIG_QPNP_SMB2) += qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
+obj-$(CONFIG_SMB138X_CHARGER) += smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o
obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o
diff --git a/drivers/power/qcom-charger/battery_current_limit.c b/drivers/power/qcom-charger/battery_current_limit.c
index 2bda5ce1a8c4..951d0544efa0 100644
--- a/drivers/power/qcom-charger/battery_current_limit.c
+++ b/drivers/power/qcom-charger/battery_current_limit.c
@@ -174,6 +174,9 @@ struct bcl_context {
struct qpnp_adc_tm_btm_param btm_vph_adc_param;
/* Low temp min freq limit requested by thermal */
uint32_t thermal_freq_limit;
+ /* state of charge notifier */
+ struct notifier_block psy_nb;
+ struct work_struct soc_mitig_work;
/* BCL Peripheral monitor parameters */
struct bcl_threshold ibat_high_thresh;
@@ -204,8 +207,6 @@ static DEFINE_MUTEX(bcl_hotplug_mutex);
static bool bcl_hotplug_enabled;
static uint32_t battery_soc_val = 100;
static uint32_t soc_low_threshold;
-static struct power_supply *bcl_psy;
-static struct power_supply_desc bcl_psy_des;
static const char bcl_psy_name[] = "bcl";
static void bcl_handle_hotplug(struct work_struct *work)
@@ -277,22 +278,34 @@ static void update_cpu_freq(void)
trace_bcl_sw_mitigation_event("End Frequency Mitigation");
}
-static void power_supply_callback(struct power_supply *psy)
+static void soc_mitigate(struct work_struct *work)
{
- static struct power_supply *bms_psy;
+ if (bcl_hotplug_enabled)
+ queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work);
+ update_cpu_freq();
+}
+
+static int power_supply_callback(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct power_supply *psy = data;
+ static struct power_supply *batt_psy;
union power_supply_propval ret = {0,};
int battery_percentage;
enum bcl_threshold_state prev_soc_state;
if (gbcl->bcl_mode != BCL_DEVICE_ENABLED) {
pr_debug("BCL is not enabled\n");
- return;
+ return NOTIFY_OK;
}
- if (!bms_psy)
- bms_psy = power_supply_get_by_name("bms");
- if (bms_psy) {
- battery_percentage = power_supply_get_property(bms_psy,
+ if (strcmp(psy->desc->name, "battery"))
+ return NOTIFY_OK;
+
+ if (!batt_psy)
+ batt_psy = power_supply_get_by_name("battery");
+ if (batt_psy) {
+ battery_percentage = power_supply_get_property(batt_psy,
POWER_SUPPLY_PROP_CAPACITY, &ret);
battery_percentage = ret.intval;
battery_soc_val = battery_percentage;
@@ -302,15 +315,14 @@ static void power_supply_callback(struct power_supply *psy)
bcl_soc_state = (battery_soc_val <= soc_low_threshold) ?
BCL_LOW_THRESHOLD : BCL_HIGH_THRESHOLD;
if (bcl_soc_state == prev_soc_state)
- return;
+ return NOTIFY_OK;
trace_bcl_sw_mitigation_event(
(bcl_soc_state == BCL_LOW_THRESHOLD)
? "trigger SoC mitigation"
: "clear SoC mitigation");
- if (bcl_hotplug_enabled)
- queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work);
- update_cpu_freq();
+ schedule_work(&gbcl->soc_mitig_work);
}
+ return NOTIFY_OK;
}
static int bcl_get_battery_voltage(int *vbatt_mv)
@@ -624,7 +636,6 @@ static void bcl_periph_vbat_notify(enum bcl_trip_type type, int trip_temp,
static void bcl_periph_mode_set(enum bcl_device_mode mode)
{
int ret = 0;
- struct power_supply_config bcl_psy_cfg = {};
if (mode == BCL_DEVICE_ENABLED) {
/*
@@ -632,15 +643,11 @@ static void bcl_periph_mode_set(enum bcl_device_mode mode)
* power state changes. Make sure we read the current SoC
* and mitigate.
*/
- power_supply_callback(bcl_psy);
- bcl_psy_cfg.num_supplicants = 0;
- bcl_psy_cfg.drv_data = gbcl;
-
- bcl_psy = power_supply_register(gbcl->dev, &bcl_psy_des,
- &bcl_psy_cfg);
- if (IS_ERR(bcl_psy)) {
- pr_err("Unable to register bcl_psy rc = %ld\n",
- PTR_ERR(bcl_psy));
+ power_supply_callback(&gbcl->psy_nb, 1, gbcl);
+ ret = power_supply_reg_notifier(&gbcl->psy_nb);
+ if (ret < 0) {
+ pr_err("Unable to register soc notifier rc = %d\n",
+ ret);
return;
}
ret = msm_bcl_set_threshold(BCL_PARAM_CURRENT, BCL_HIGH_TRIP,
@@ -678,7 +685,7 @@ static void bcl_periph_mode_set(enum bcl_device_mode mode)
}
gbcl->btm_mode = BCL_VPH_MONITOR_MODE;
} else {
- power_supply_unregister(bcl_psy);
+ power_supply_unreg_notifier(&gbcl->psy_nb);
ret = msm_bcl_disable();
if (ret) {
pr_err("Error disabling BCL\n");
@@ -1627,19 +1634,6 @@ btm_probe_exit:
return ret;
}
-static int bcl_battery_get_property(struct power_supply *psy,
- enum power_supply_property prop,
- union power_supply_propval *val)
-{
- return 0;
-}
-static int bcl_battery_set_property(struct power_supply *psy,
- enum power_supply_property prop,
- const union power_supply_propval *val)
-{
- return 0;
-}
-
static uint32_t get_mask_from_core_handle(struct platform_device *pdev,
const char *key)
{
@@ -1725,12 +1719,8 @@ static int bcl_probe(struct platform_device *pdev)
pr_err("Cannot create bcl sysfs\n");
return ret;
}
- bcl_psy_des.name = bcl_psy_name;
- bcl_psy_des.type = POWER_SUPPLY_TYPE_BMS;
- bcl_psy_des.get_property = bcl_battery_get_property;
- bcl_psy_des.set_property = bcl_battery_set_property;
- bcl_psy_des.num_properties = 0;
- bcl_psy_des.external_power_changed = power_supply_callback;
+ INIT_WORK(&bcl->soc_mitig_work, soc_mitigate);
+ bcl->psy_nb.notifier_call = power_supply_callback;
bcl->bcl_hotplug_wq = alloc_workqueue("bcl_hotplug_wq", WQ_HIGHPRI, 0);
if (!bcl->bcl_hotplug_wq) {
pr_err("Workqueue alloc failed\n");
@@ -1773,6 +1763,7 @@ static int bcl_remove(struct platform_device *pdev)
int cpu;
/* De-register KTM handle */
+ power_supply_unreg_notifier(&gbcl->psy_nb);
if (gbcl->hotplug_handle)
devmgr_unregister_mitigation_client(&pdev->dev,
gbcl->hotplug_handle);
diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h
index 4feaaa0e0c4e..515f31a44ce7 100644
--- a/drivers/power/qcom-charger/fg-core.h
+++ b/drivers/power/qcom-charger/fg-core.h
@@ -54,6 +54,8 @@
CHARS_PER_ITEM) + 1) \
#define FG_SRAM_ADDRESS_MAX 255
+#define BUCKET_COUNT 8
+#define BUCKET_SOC_PCT (256 / BUCKET_COUNT)
/* Debug flag definitions */
enum fg_debug_flag {
@@ -186,6 +188,15 @@ struct fg_batt_props {
int batt_id_kohm;
};
+struct fg_cyc_ctr_data {
+ bool en;
+ bool started[BUCKET_COUNT];
+ u16 count[BUCKET_COUNT];
+ u8 last_soc[BUCKET_COUNT];
+ int id;
+ struct mutex lock;
+};
+
struct fg_irq_info {
const char *name;
const irq_handler_t handler;
@@ -209,6 +220,7 @@ struct fg_chip {
char *batt_profile;
struct fg_dt_props dt;
struct fg_batt_props bp;
+ struct fg_cyc_ctr_data cyc_ctr;
struct notifier_block nb;
struct mutex bus_lock;
struct mutex sram_rw_lock;
@@ -216,6 +228,8 @@ struct fg_chip {
u32 batt_info_base;
u32 mem_if_base;
int nom_cap_uah;
+ int status;
+ int prev_status;
bool batt_id_avail;
bool profile_loaded;
bool battery_missing;
@@ -223,6 +237,7 @@ struct fg_chip {
struct completion soc_ready;
struct delayed_work profile_load_work;
struct work_struct status_change_work;
+ struct work_struct cycle_count_work;
struct fg_alg_flag *alg_flags;
};
diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c
index a163061156ba..7739952f3254 100644
--- a/drivers/power/qcom-charger/qpnp-fg-gen3.c
+++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c
@@ -57,6 +57,8 @@
#define PROFILE_LOAD_OFFSET 0
#define NOM_CAP_WORD 58
#define NOM_CAP_OFFSET 0
+#define CYCLE_COUNT_WORD 75
+#define CYCLE_COUNT_OFFSET 0
#define PROFILE_INTEGRITY_WORD 79
#define PROFILE_INTEGRITY_OFFSET 3
#define BATT_SOC_WORD 91
@@ -92,6 +94,8 @@ static int fg_decode_value_16b(struct fg_sram_param *sp,
enum fg_sram_param_id id, int val);
static int fg_decode_default(struct fg_sram_param *sp,
enum fg_sram_param_id id, int val);
+static int fg_decode_batt_soc(struct fg_sram_param *sp,
+ enum fg_sram_param_id id, int val);
static void fg_encode_voltage(struct fg_sram_param *sp,
enum fg_sram_param_id id, int val, u8 *buf);
static void fg_encode_current(struct fg_sram_param *sp,
@@ -114,7 +118,7 @@ static void fg_encode_default(struct fg_sram_param *sp,
static struct fg_sram_param pmicobalt_v1_sram_params[] = {
PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL,
- fg_decode_default),
+ fg_decode_batt_soc),
PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141,
1000, 0, NULL, fg_decode_value_16b),
PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL,
@@ -152,7 +156,7 @@ static struct fg_sram_param pmicobalt_v1_sram_params[] = {
static struct fg_sram_param pmicobalt_v2_sram_params[] = {
PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL,
- fg_decode_default),
+ fg_decode_batt_soc),
PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141,
1000, 0, NULL, fg_decode_value_16b),
PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, 0, NULL,
@@ -260,171 +264,6 @@ module_param_named(
sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR
);
-/* Other functions HERE */
-
-static int fg_awake_cb(struct votable *votable, void *data, int awake,
- const char *client)
-{
- struct fg_chip *chip = data;
-
- if (awake)
- pm_stay_awake(chip->dev);
- else
- pm_relax(chip->dev);
-
- pr_debug("client: %s awake: %d\n", client, awake);
- return 0;
-}
-
-static bool is_charger_available(struct fg_chip *chip)
-{
- if (!chip->batt_psy)
- chip->batt_psy = power_supply_get_by_name("battery");
-
- if (!chip->batt_psy)
- return false;
-
- return true;
-}
-
-static void status_change_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip, status_change_work);
- union power_supply_propval prop = {0, };
-
- if (!is_charger_available(chip)) {
- fg_dbg(chip, FG_STATUS, "Charger not available?!\n");
- return;
- }
-
- power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS,
- &prop);
- switch (prop.intval) {
- case POWER_SUPPLY_STATUS_CHARGING:
- fg_dbg(chip, FG_POWER_SUPPLY, "Charging\n");
- break;
- case POWER_SUPPLY_STATUS_DISCHARGING:
- fg_dbg(chip, FG_POWER_SUPPLY, "Discharging\n");
- break;
- case POWER_SUPPLY_STATUS_FULL:
- fg_dbg(chip, FG_POWER_SUPPLY, "Full\n");
- break;
- default:
- break;
- }
-}
-
-#define PROFILE_LEN 224
-#define PROFILE_COMP_LEN 32
-#define SOC_READY_WAIT_MS 2000
-static void profile_load_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- profile_load_work.work);
- int rc;
- u8 buf[PROFILE_COMP_LEN], val;
- bool tried_again = false, profiles_same = false;
-
- if (!chip->batt_id_avail) {
- pr_err("batt_id not available\n");
- return;
- }
-
- rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD,
- PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
- if (rc < 0) {
- pr_err("failed to read profile integrity rc=%d\n", rc);
- return;
- }
-
- vote(chip->awake_votable, PROFILE_LOAD, true, 0);
- if (val == 0x01) {
- fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n");
- rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET,
- buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT);
- if (rc < 0) {
- pr_err("Error in reading battery profile, rc:%d\n", rc);
- goto out;
- }
- profiles_same = memcmp(chip->batt_profile, buf,
- PROFILE_COMP_LEN) == 0;
- if (profiles_same) {
- fg_dbg(chip, FG_STATUS, "Battery profile is same\n");
- goto done;
- }
- fg_dbg(chip, FG_STATUS, "profiles are different?\n");
- }
-
- fg_dbg(chip, FG_STATUS, "profile loading started\n");
- rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0);
- if (rc < 0) {
- pr_err("Error in writing to %04x, rc=%d\n",
- BATT_SOC_RESTART(chip), rc);
- goto out;
- }
-
- /* load battery profile */
- rc = fg_sram_write(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET,
- chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC);
- if (rc < 0) {
- pr_err("Error in writing battery profile, rc:%d\n", rc);
- goto out;
- }
-
- rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT,
- RESTART_GO_BIT);
- if (rc < 0) {
- pr_err("Error in writing to %04x, rc=%d\n",
- BATT_SOC_RESTART(chip), rc);
- goto out;
- }
-
-wait:
- rc = wait_for_completion_interruptible_timeout(&chip->soc_ready,
- msecs_to_jiffies(SOC_READY_WAIT_MS));
-
- /* If we were interrupted wait again one more time. */
- if (rc == -ERESTARTSYS && !tried_again) {
- tried_again = true;
- goto wait;
- } else if (rc <= 0) {
- pr_err("wait for soc_ready timed out rc=%d\n", rc);
- goto out;
- }
-
- fg_dbg(chip, FG_STATUS, "SOC is ready\n");
-
- /* Set the profile integrity bit */
- val = 0x1;
- rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD,
- PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
- if (rc < 0) {
- pr_err("failed to write profile integrity rc=%d\n", rc);
- goto out;
- }
-
- fg_dbg(chip, FG_STATUS, "profile loaded successfully");
-done:
- rc = fg_sram_read(chip, NOM_CAP_WORD, NOM_CAP_OFFSET, buf, 2,
- FG_IMA_DEFAULT);
- if (rc < 0) {
- pr_err("Error in reading %04x[%d] rc=%d\n", NOM_CAP_WORD,
- NOM_CAP_OFFSET, rc);
- goto out;
- }
-
- chip->nom_cap_uah = (int)(buf[0] | buf[1] << 8) * 1000;
- chip->profile_loaded = true;
-out:
- vote(chip->awake_votable, PROFILE_LOAD, false, 0);
- rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0);
- if (rc < 0)
- pr_err("Error in writing to %04x, rc=%d\n",
- BATT_SOC_RESTART(chip), rc);
-}
-
/* All getters HERE */
static int fg_decode_value_16b(struct fg_sram_param *sp,
@@ -436,9 +275,18 @@ static int fg_decode_value_16b(struct fg_sram_param *sp,
return sp[id].value;
}
-static int fg_decode_default(struct fg_sram_param *sp,
+static int fg_decode_batt_soc(struct fg_sram_param *sp,
enum fg_sram_param_id id, int value)
{
+ sp[id].value = (u32)value >> 24;
+ pr_debug("id: %d raw value: %x decoded value: %x\n", id, value,
+ sp[id].value);
+ return sp[id].value;
+}
+
+static int fg_decode_default(struct fg_sram_param *sp, enum fg_sram_param_id id,
+ int value)
+{
return value;
}
@@ -752,6 +600,7 @@ static int fg_get_batt_id(struct fg_chip *chip, int *val)
return 0;
}
+#define PROFILE_LEN 224
static int fg_get_batt_profile(struct fg_chip *chip)
{
struct device_node *node = chip->dev->of_node;
@@ -863,6 +712,316 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging,
return 0;
}
+/* Other functions HERE */
+
+static int fg_awake_cb(struct votable *votable, void *data, int awake,
+ const char *client)
+{
+ struct fg_chip *chip = data;
+
+ if (awake)
+ pm_stay_awake(chip->dev);
+ else
+ pm_relax(chip->dev);
+
+ pr_debug("client: %s awake: %d\n", client, awake);
+ return 0;
+}
+
+static bool is_charger_available(struct fg_chip *chip)
+{
+ if (!chip->batt_psy)
+ chip->batt_psy = power_supply_get_by_name("battery");
+
+ if (!chip->batt_psy)
+ return false;
+
+ return true;
+}
+
+static void status_change_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip, status_change_work);
+ union power_supply_propval prop = {0, };
+
+ if (!is_charger_available(chip)) {
+ fg_dbg(chip, FG_STATUS, "Charger not available?!\n");
+ return;
+ }
+
+ power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS,
+ &prop);
+ chip->prev_status = chip->status;
+ chip->status = prop.intval;
+
+ if (chip->cyc_ctr.en && chip->prev_status != chip->status)
+ schedule_work(&chip->cycle_count_work);
+
+ switch (prop.intval) {
+ case POWER_SUPPLY_STATUS_CHARGING:
+ fg_dbg(chip, FG_POWER_SUPPLY, "Charging\n");
+ break;
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ fg_dbg(chip, FG_POWER_SUPPLY, "Discharging\n");
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ fg_dbg(chip, FG_POWER_SUPPLY, "Full\n");
+ break;
+ default:
+ break;
+ }
+}
+
+static void restore_cycle_counter(struct fg_chip *chip)
+{
+ int rc = 0, i;
+ u8 data[2];
+
+ mutex_lock(&chip->cyc_ctr.lock);
+ for (i = 0; i < BUCKET_COUNT; i++) {
+ rc = fg_sram_read(chip, CYCLE_COUNT_WORD + (i / 2),
+ CYCLE_COUNT_OFFSET + (i % 2) * 2, data, 2,
+ FG_IMA_DEFAULT);
+ if (rc < 0)
+ pr_err("failed to read bucket %d rc=%d\n", i, rc);
+ else
+ chip->cyc_ctr.count[i] = data[0] | data[1] << 8;
+ }
+ mutex_unlock(&chip->cyc_ctr.lock);
+}
+
+static void clear_cycle_counter(struct fg_chip *chip)
+{
+ int rc = 0, i;
+
+ if (!chip->cyc_ctr.en)
+ return;
+
+ mutex_lock(&chip->cyc_ctr.lock);
+ memset(chip->cyc_ctr.count, 0, sizeof(chip->cyc_ctr.count));
+ for (i = 0; i < BUCKET_COUNT; i++) {
+ chip->cyc_ctr.started[i] = false;
+ chip->cyc_ctr.last_soc[i] = 0;
+ }
+ rc = fg_sram_write(chip, CYCLE_COUNT_WORD, CYCLE_COUNT_OFFSET,
+ (u8 *)&chip->cyc_ctr.count,
+ sizeof(chip->cyc_ctr.count) / sizeof(u8 *),
+ FG_IMA_DEFAULT);
+ if (rc < 0)
+ pr_err("failed to clear cycle counter rc=%d\n", rc);
+
+ mutex_unlock(&chip->cyc_ctr.lock);
+}
+
+static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket)
+{
+ int rc = 0;
+ u16 cyc_count;
+ u8 data[2];
+
+ if (bucket < 0 || (bucket > BUCKET_COUNT - 1))
+ return 0;
+
+ cyc_count = chip->cyc_ctr.count[bucket];
+ cyc_count++;
+ data[0] = cyc_count & 0xFF;
+ data[1] = cyc_count >> 8;
+
+ rc = fg_sram_write(chip, CYCLE_COUNT_WORD + (bucket / 2),
+ CYCLE_COUNT_OFFSET + (bucket % 2) * 2, data, 2,
+ FG_IMA_DEFAULT);
+ if (rc < 0)
+ pr_err("failed to write BATT_CYCLE[%d] rc=%d\n",
+ bucket, rc);
+ else
+ chip->cyc_ctr.count[bucket] = cyc_count;
+ return rc;
+}
+
+static void cycle_count_work(struct work_struct *work)
+{
+ int rc = 0, bucket, i, batt_soc;
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ cycle_count_work);
+
+ mutex_lock(&chip->cyc_ctr.lock);
+ rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &batt_soc);
+ if (rc < 0) {
+ pr_err("Failed to read battery soc rc: %d\n", rc);
+ goto out;
+ }
+
+ if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+ /* Find out which bucket the SOC falls in */
+ bucket = batt_soc / BUCKET_SOC_PCT;
+ pr_debug("batt_soc: %d bucket: %d\n", batt_soc, bucket);
+
+ /*
+ * If we've started counting for the previous bucket,
+ * then store the counter for that bucket if the
+ * counter for current bucket is getting started.
+ */
+ if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] &&
+ !chip->cyc_ctr.started[bucket]) {
+ rc = fg_inc_store_cycle_ctr(chip, bucket - 1);
+ if (rc < 0) {
+ pr_err("Error in storing cycle_ctr rc: %d\n",
+ rc);
+ goto out;
+ } else {
+ chip->cyc_ctr.started[bucket - 1] = false;
+ chip->cyc_ctr.last_soc[bucket - 1] = 0;
+ }
+ }
+ if (!chip->cyc_ctr.started[bucket]) {
+ chip->cyc_ctr.started[bucket] = true;
+ chip->cyc_ctr.last_soc[bucket] = batt_soc;
+ }
+ } else {
+ for (i = 0; i < BUCKET_COUNT; i++) {
+ if (chip->cyc_ctr.started[i] &&
+ batt_soc > chip->cyc_ctr.last_soc[i]) {
+ rc = fg_inc_store_cycle_ctr(chip, i);
+ if (rc < 0)
+ pr_err("Error in storing cycle_ctr rc: %d\n",
+ rc);
+ chip->cyc_ctr.last_soc[i] = 0;
+ }
+ chip->cyc_ctr.started[i] = false;
+ }
+ }
+out:
+ mutex_unlock(&chip->cyc_ctr.lock);
+}
+
+static int fg_get_cycle_count(struct fg_chip *chip)
+{
+ int count;
+
+ if (!chip->cyc_ctr.en)
+ return 0;
+
+ if ((chip->cyc_ctr.id <= 0) || (chip->cyc_ctr.id > BUCKET_COUNT))
+ return -EINVAL;
+
+ mutex_lock(&chip->cyc_ctr.lock);
+ count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1];
+ mutex_unlock(&chip->cyc_ctr.lock);
+ return count;
+}
+
+#define PROFILE_COMP_LEN 32
+#define SOC_READY_WAIT_MS 2000
+static void profile_load_work(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ profile_load_work.work);
+ int rc;
+ u8 buf[PROFILE_COMP_LEN], val;
+ bool tried_again = false, profiles_same = false;
+
+ if (!chip->batt_id_avail) {
+ pr_err("batt_id not available\n");
+ return;
+ }
+
+ rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD,
+ PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("failed to read profile integrity rc=%d\n", rc);
+ return;
+ }
+
+ vote(chip->awake_votable, PROFILE_LOAD, true, 0);
+ if (val == 0x01) {
+ fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n");
+ rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET,
+ buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in reading battery profile, rc:%d\n", rc);
+ goto out;
+ }
+ profiles_same = memcmp(chip->batt_profile, buf,
+ PROFILE_COMP_LEN) == 0;
+ if (profiles_same) {
+ fg_dbg(chip, FG_STATUS, "Battery profile is same\n");
+ goto done;
+ }
+ fg_dbg(chip, FG_STATUS, "profiles are different?\n");
+ }
+
+ clear_cycle_counter(chip);
+ fg_dbg(chip, FG_STATUS, "profile loading started\n");
+ rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0);
+ if (rc < 0) {
+ pr_err("Error in writing to %04x, rc=%d\n",
+ BATT_SOC_RESTART(chip), rc);
+ goto out;
+ }
+
+ /* load battery profile */
+ rc = fg_sram_write(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET,
+ chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC);
+ if (rc < 0) {
+ pr_err("Error in writing battery profile, rc:%d\n", rc);
+ goto out;
+ }
+
+ rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT,
+ RESTART_GO_BIT);
+ if (rc < 0) {
+ pr_err("Error in writing to %04x, rc=%d\n",
+ BATT_SOC_RESTART(chip), rc);
+ goto out;
+ }
+
+wait:
+ rc = wait_for_completion_interruptible_timeout(&chip->soc_ready,
+ msecs_to_jiffies(SOC_READY_WAIT_MS));
+
+ /* If we were interrupted wait again one more time. */
+ if (rc == -ERESTARTSYS && !tried_again) {
+ tried_again = true;
+ goto wait;
+ } else if (rc <= 0) {
+ pr_err("wait for soc_ready timed out rc=%d\n", rc);
+ goto out;
+ }
+
+ fg_dbg(chip, FG_STATUS, "SOC is ready\n");
+
+ /* Set the profile integrity bit */
+ val = 0x1;
+ rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD,
+ PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("failed to write profile integrity rc=%d\n", rc);
+ goto out;
+ }
+
+ fg_dbg(chip, FG_STATUS, "profile loaded successfully");
+done:
+ rc = fg_sram_read(chip, NOM_CAP_WORD, NOM_CAP_OFFSET, buf, 2,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in reading %04x[%d] rc=%d\n", NOM_CAP_WORD,
+ NOM_CAP_OFFSET, rc);
+ goto out;
+ }
+
+ chip->nom_cap_uah = (int)(buf[0] | buf[1] << 8) * 1000;
+ chip->profile_loaded = true;
+out:
+ vote(chip->awake_votable, PROFILE_LOAD, false, 0);
+ rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0);
+ if (rc < 0)
+ pr_err("Error in writing to %04x, rc=%d\n",
+ BATT_SOC_RESTART(chip), rc);
+}
+
/* PSY CALLBACKS STAY HERE */
static int fg_psy_get_property(struct power_supply *psy,
@@ -902,6 +1061,11 @@ static int fg_psy_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
pval->intval = chip->bp.float_volt_uv;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ pval->intval = fg_get_cycle_count(chip);
+ break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+ pval->intval = chip->cyc_ctr.id;
break;
default:
break;
@@ -914,7 +1078,18 @@ static int fg_psy_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *pval)
{
+ struct fg_chip *chip = power_supply_get_drvdata(psy);
+
switch (psp) {
+ case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+ if ((pval->intval > 0) && (pval->intval <= BUCKET_COUNT)) {
+ chip->cyc_ctr.id = pval->intval;
+ } else {
+ pr_err("rejecting invalid cycle_count_id = %d\n",
+ pval->intval);
+ return -EINVAL;
+ }
+ break;
default:
break;
}
@@ -926,6 +1101,8 @@ static int fg_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp) {
+ case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+ return 1;
default:
break;
}
@@ -965,6 +1142,8 @@ static enum power_supply_property fg_psy_props[] = {
POWER_SUPPLY_PROP_BATTERY_TYPE,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
};
static const struct power_supply_desc fg_psy_desc = {
@@ -1121,6 +1300,9 @@ static int fg_hw_init(struct fg_chip *chip)
}
}
+ if (chip->cyc_ctr.en)
+ restore_cycle_counter(chip);
+
return 0;
}
@@ -1180,6 +1362,7 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data)
if (chip->battery_missing) {
chip->batt_id_avail = false;
chip->profile_loaded = false;
+ clear_cycle_counter(chip);
} else {
rc = fg_batt_profile_init(chip);
if (rc < 0) {
@@ -1222,6 +1405,12 @@ static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data)
{
struct fg_chip *chip = data;
+ if (chip->cyc_ctr.en)
+ schedule_work(&chip->cycle_count_work);
+
+ if (is_charger_available(chip))
+ power_supply_changed(chip->batt_psy);
+
fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
return IRQ_HANDLED;
}
@@ -1230,6 +1419,9 @@ static irqreturn_t fg_empty_soc_irq_handler(int irq, void *data)
{
struct fg_chip *chip = data;
+ if (is_charger_available(chip))
+ power_supply_changed(chip->batt_psy);
+
fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq);
return IRQ_HANDLED;
}
@@ -1528,6 +1720,9 @@ static int fg_parse_dt(struct fg_chip *chip)
else
chip->dt.esr_timer_asleep = temp;
+ chip->cyc_ctr.en = of_property_read_bool(node, "qcom,cycle-counter-en");
+ if (chip->cyc_ctr.en)
+ chip->cyc_ctr.id = 1;
return 0;
}
@@ -1580,10 +1775,12 @@ static int fg_gen3_probe(struct platform_device *pdev)
mutex_init(&chip->bus_lock);
mutex_init(&chip->sram_rw_lock);
+ mutex_init(&chip->cyc_ctr.lock);
init_completion(&chip->soc_update);
init_completion(&chip->soc_ready);
INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work);
INIT_WORK(&chip->status_change_work, status_change_work);
+ INIT_WORK(&chip->cycle_count_work, cycle_count_work);
rc = fg_memif_init(chip);
if (rc < 0) {
diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c
index d19e7827ed83..57f31d8c58e7 100644
--- a/drivers/power/qcom-charger/qpnp-smb2.c
+++ b/drivers/power/qcom-charger/qpnp-smb2.c
@@ -18,11 +18,13 @@
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
+#include <linux/qpnp/qpnp-revid.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include "smb-reg.h"
#include "smb-lib.h"
+#include "storm-watch.h"
#include "pmic-voter.h"
#define SMB2_DEFAULT_WPWR_UW 8000000
@@ -205,6 +207,7 @@ struct smb_dt_props {
int wipower_max_uw;
u32 step_soc_threshold[STEP_CHARGING_MAX_STEPS - 1];
s32 step_cc_delta[STEP_CHARGING_MAX_STEPS];
+ struct device_node *revid_dev_node;
};
struct smb2 {
@@ -600,6 +603,8 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_STEP_CHARGING_STEP,
};
static int smb2_batt_get_prop(struct power_supply *psy,
@@ -639,6 +644,11 @@ static int smb2_batt_get_prop(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
rc = smblib_get_prop_input_current_limited(chg, val);
+ case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
+ val->intval = chg->step_chg_enabled;
+ break;
+ case POWER_SUPPLY_PROP_STEP_CHARGING_STEP:
+ rc = smblib_get_prop_step_chg_step(chg, val);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
rc = smblib_get_prop_batt_voltage_now(chg, val);
@@ -1085,6 +1095,40 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+static int smb2_setup_wa_flags(struct smb2 *chip)
+{
+ struct pmic_revid_data *pmic_rev_id;
+ struct device_node *revid_dev_node;
+
+ revid_dev_node = of_parse_phandle(chip->chg.dev->of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property\n");
+ return -EINVAL;
+ }
+
+ pmic_rev_id = get_revid_data(revid_dev_node);
+ if (IS_ERR_OR_NULL(pmic_rev_id)) {
+ /*
+ * the revid peripheral must be registered, any failure
+ * here only indicates that the rev-id module has not
+ * probed yet.
+ */
+ return -EPROBE_DEFER;
+ }
+
+ switch (pmic_rev_id->pmic_subtype) {
+ case PMICOBALT_SUBTYPE:
+ break;
+ default:
+ pr_err("PMIC subtype %d not supported\n",
+ pmic_rev_id->pmic_subtype);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/****************************
* DETERMINE INITIAL STATUS *
****************************/
@@ -1109,60 +1153,181 @@ static int smb2_determine_initial_status(struct smb2 *chip)
**************************/
struct smb2_irq_info {
- const char *name;
- const irq_handler_t handler;
- const bool wake;
- int irq;
+ const char *name;
+ const irq_handler_t handler;
+ const bool wake;
+ const struct storm_watch storm_data;
+ int irq;
};
static struct smb2_irq_info smb2_irqs[] = {
/* CHARGER IRQs */
- { "chg-error", smblib_handle_debug },
- { "chg-state-change", smblib_handle_chg_state_change, true },
- { "step-chg-state-change", smblib_handle_step_chg_state_change,
- true },
- { "step-chg-soc-update-fail", smblib_handle_step_chg_soc_update_fail,
- true },
- { "step-chg-soc-update-request",
- smblib_handle_step_chg_soc_update_request, true },
+ {
+ .name = "chg-error",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "chg-state-change",
+ .handler = smblib_handle_chg_state_change,
+ .wake = true,
+ },
+ {
+ .name = "step-chg-state-change",
+ .handler = smblib_handle_step_chg_state_change,
+ .wake = true,
+ },
+ {
+ .name = "step-chg-soc-update-fail",
+ .handler = smblib_handle_step_chg_soc_update_fail,
+ .wake = true,
+ },
+ {
+ .name = "step-chg-soc-update-request",
+ .handler = smblib_handle_step_chg_soc_update_request,
+ .wake = true,
+ },
/* OTG IRQs */
- { "otg-fail", smblib_handle_debug },
- { "otg-overcurrent", smblib_handle_debug },
- { "otg-oc-dis-sw-sts", smblib_handle_debug },
- { "testmode-change-detect", smblib_handle_debug },
+ {
+ .name = "otg-fail",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "otg-overcurrent",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "otg-oc-dis-sw-sts",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "testmode-change-detect",
+ .handler = smblib_handle_debug,
+ },
/* BATTERY IRQs */
- { "bat-temp", smblib_handle_batt_temp_changed },
- { "bat-ocp", smblib_handle_batt_psy_changed },
- { "bat-ov", smblib_handle_batt_psy_changed },
- { "bat-low", smblib_handle_batt_psy_changed },
- { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed },
- { "bat-terminal-missing", smblib_handle_batt_psy_changed },
+ {
+ .name = "bat-temp",
+ .handler = smblib_handle_batt_temp_changed,
+ },
+ {
+ .name = "bat-ocp",
+ .handler = smblib_handle_batt_psy_changed,
+ },
+ {
+ .name = "bat-ov",
+ .handler = smblib_handle_batt_psy_changed,
+ },
+ {
+ .name = "bat-low",
+ .handler = smblib_handle_batt_psy_changed,
+ },
+ {
+ .name = "bat-therm-or-id-missing",
+ .handler = smblib_handle_batt_psy_changed,
+ },
+ {
+ .name = "bat-terminal-missing",
+ .handler = smblib_handle_batt_psy_changed,
+ },
/* USB INPUT IRQs */
- { "usbin-collapse", smblib_handle_debug },
- { "usbin-lt-3p6v", smblib_handle_debug },
- { "usbin-uv", smblib_handle_debug },
- { "usbin-ov", smblib_handle_debug },
- { "usbin-plugin", smblib_handle_usb_plugin, true },
- { "usbin-src-change", smblib_handle_usb_source_change, true },
- { "usbin-icl-change", smblib_handle_icl_change, true },
- { "type-c-change", smblib_handle_usb_typec_change, true },
+ {
+ .name = "usbin-collapse",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "usbin-lt-3p6v",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "usbin-uv",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "usbin-ov",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "usbin-plugin",
+ .handler = smblib_handle_usb_plugin,
+ .wake = true,
+ },
+ {
+ .name = "usbin-src-change",
+ .handler = smblib_handle_usb_source_change,
+ .wake = true,
+ },
+ {
+ .name = "usbin-icl-change",
+ .handler = smblib_handle_icl_change,
+ .wake = true,
+ },
+ {
+ .name = "type-c-change",
+ .handler = smblib_handle_usb_typec_change,
+ .wake = true,
+ },
/* DC INPUT IRQs */
- { "dcin-collapse", smblib_handle_debug },
- { "dcin-lt-3p6v", smblib_handle_debug },
- { "dcin-uv", smblib_handle_debug },
- { "dcin-ov", smblib_handle_debug },
- { "dcin-plugin", smblib_handle_debug },
- { "div2-en-dg", smblib_handle_debug },
- { "dcin-icl-change", smblib_handle_debug },
+ {
+ .name = "dcin-collapse",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "dcin-lt-3p6v",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "dcin-uv",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "dcin-ov",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "dcin-plugin",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "div2-en-dg",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "dcin-icl-change",
+ .handler = smblib_handle_debug,
+ },
/* MISCELLANEOUS IRQs */
- { "wdog-snarl", NULL },
- { "wdog-bark", NULL },
- { "aicl-fail", smblib_handle_debug },
- { "aicl-done", smblib_handle_debug },
- { "high-duty-cycle", smblib_handle_high_duty_cycle, true },
- { "input-current-limiting", smblib_handle_debug },
- { "temperature-change", smblib_handle_debug },
- { "switcher-power-ok", smblib_handle_debug },
+ {
+ .name = "wdog-snarl",
+ .handler = NULL,
+ },
+ {
+ .name = "wdog-bark",
+ .handler = NULL,
+ },
+ {
+ .name = "aicl-fail",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "aicl-done",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "high-duty-cycle",
+ .handler = smblib_handle_high_duty_cycle,
+ .wake = true,
+ },
+ {
+ .name = "input-current-limiting",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "temperature-change",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "switcher-power-ok",
+ .handler = smblib_handle_debug,
+ },
};
static int smb2_get_irq_index_byname(const char *irq_name)
@@ -1205,6 +1370,7 @@ static int smb2_request_interrupt(struct smb2 *chip,
irq_data->parent_data = chip;
irq_data->name = irq_name;
+ irq_data->storm_data = smb2_irqs[irq_index].storm_data;
rc = devm_request_threaded_irq(chg->dev, irq, NULL,
smb2_irqs[irq_index].handler,
@@ -1270,6 +1436,13 @@ static int smb2_probe(struct platform_device *pdev)
return -EINVAL;
}
+ rc = smb2_setup_wa_flags(chip);
+ if (rc < 0) {
+ if (rc != -EPROBE_DEFER)
+ pr_err("Couldn't setup wa flags rc=%d\n", rc);
+ return rc;
+ }
+
rc = smblib_init(chg);
if (rc < 0) {
pr_err("Smblib_init failed rc=%d\n", rc);
diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c
index ed24e12e313d..e93d03788f11 100644
--- a/drivers/power/qcom-charger/smb-lib.c
+++ b/drivers/power/qcom-charger/smb-lib.c
@@ -18,6 +18,7 @@
#include <linux/irq.h>
#include "smb-lib.h"
#include "smb-reg.h"
+#include "storm-watch.h"
#include "pmic-voter.h"
#define smblib_dbg(chg, reason, fmt, ...) \
@@ -102,7 +103,8 @@ static int smblib_get_step_charging_adjustment(struct smb_charger *chg,
return rc;
}
- step_state = (stat & STEP_CHARGING_STATUS_MASK) >> 3;
+ step_state = (stat & STEP_CHARGING_STATUS_MASK) >>
+ STEP_CHARGING_STATUS_SHIFT;
rc = smblib_get_charge_param(chg, &chg->param.step_cc_delta[step_state],
cc_offset);
@@ -779,13 +781,24 @@ int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev)
int smblib_vconn_regulator_enable(struct regulator_dev *rdev)
{
struct smb_charger *chg = rdev_get_drvdata(rdev);
+ u8 stat;
int rc = 0;
+ /*
+ * VCONN_EN_ORIENTATION is overloaded with overriding the CC pin used
+ * for Vconn, and it should be set with reverse polarity of CC_OUT.
+ */
+ rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
+ return rc;
+ }
+ stat = stat & CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT;
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
- VCONN_EN_VALUE_BIT, VCONN_EN_VALUE_BIT);
+ VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT,
+ VCONN_EN_VALUE_BIT | stat);
if (rc < 0)
- dev_err(chg->dev, "Couldn't enable vconn regulator rc=%d\n",
- rc);
+ dev_err(chg->dev, "Couldn't enable vconn setting rc=%d\n", rc);
return rc;
}
@@ -1042,6 +1055,30 @@ int smblib_get_prop_batt_temp(struct smb_charger *chg,
return rc;
}
+int smblib_get_prop_step_chg_step(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+ u8 stat;
+
+ if (!chg->step_chg_enabled) {
+ val->intval = -1;
+ return 0;
+ }
+
+ rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val->intval = (stat & STEP_CHARGING_STATUS_MASK) >>
+ STEP_CHARGING_STATUS_SHIFT;
+
+ return rc;
+}
+
/***********************
* BATTERY PSY SETTERS *
***********************/
@@ -1565,6 +1602,7 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
const union power_supply_propval *val)
{
int rc;
+ u8 stat;
if (!get_effective_result(chg->pd_allowed_votable)) {
dev_err(chg->dev, "PD is not allowed\n");
@@ -1582,6 +1620,40 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
vote(chg->pd_allowed_votable, PD_VOTER, val->intval, 0);
+ /*
+ * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line
+ * when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) is set
+ * or when VCONN_EN_VALUE_BIT is set.
+ */
+ if (val->intval) {
+ rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't read TYPE_C_STATUS_4 rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ stat &= CC_ORIENTATION_BIT;
+ rc = smblib_masked_write(chg,
+ TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ VCONN_EN_ORIENTATION_BIT,
+ stat ? 0 : VCONN_EN_ORIENTATION_BIT);
+ if (rc < 0)
+ dev_err(chg->dev,
+ "Couldn't enable vconn on CC line rc=%d\n", rc);
+ }
+
+ /* CC pin selection s/w override in PD session; h/w otherwise. */
+ rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
+ TYPEC_SPARE_CFG_BIT,
+ val->intval ? TYPEC_SPARE_CFG_BIT : 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't change cc_out ctrl to %s rc=%d\n",
+ val->intval ? "SW" : "HW", rc);
+ return rc;
+ }
+
chg->pd_active = (bool)val->intval;
smblib_update_usb_type(chg);
return rc;
@@ -1597,7 +1669,6 @@ irqreturn_t smblib_handle_debug(int irq, void *data)
struct smb_charger *chg = irq_data->parent_data;
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
-
return IRQ_HANDLED;
}
@@ -1633,8 +1704,10 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data)
dev_err(chg->dev, "Couldn't get batt status type rc=%d\n", rc);
return IRQ_HANDLED;
}
- if (pval.intval == POWER_SUPPLY_STATUS_FULL)
+ if (pval.intval == POWER_SUPPLY_STATUS_FULL) {
+ power_supply_changed(chg->batt_psy);
vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0);
+ }
return IRQ_HANDLED;
}
@@ -1705,7 +1778,7 @@ irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data)
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
- smblib_handle_debug(irq, data);
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
power_supply_changed(chg->batt_psy);
return IRQ_HANDLED;
}
@@ -1715,7 +1788,7 @@ irqreturn_t smblib_handle_usb_psy_changed(int irq, void *data)
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
- smblib_handle_debug(irq, data);
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
power_supply_changed(chg->usb_psy);
return IRQ_HANDLED;
}
@@ -1768,6 +1841,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data)
}
skip_dpdm_float:
+ power_supply_changed(chg->usb_psy);
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n",
irq_data->name, chg->vbus_present ? "attached" : "detached");
return IRQ_HANDLED;
diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h
index 289fb1706e97..f5d9dda8330a 100644
--- a/drivers/power/qcom-charger/smb-lib.h
+++ b/drivers/power/qcom-charger/smb-lib.h
@@ -16,6 +16,7 @@
#include <linux/irqreturn.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/consumer.h>
+#include "storm-watch.h"
enum print_reason {
PR_INTERRUPT = BIT(0),
@@ -46,8 +47,9 @@ struct smb_regulator {
};
struct smb_irq_data {
- void *parent_data;
- const char *name;
+ void *parent_data;
+ const char *name;
+ struct storm_watch storm_data;
};
struct smb_chg_param {
@@ -161,6 +163,9 @@ struct smb_charger {
bool step_chg_enabled;
bool is_hdc;
+
+ /* workaround flag */
+ u32 wa_flags;
};
int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
@@ -228,6 +233,8 @@ int smblib_get_prop_batt_current_now(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_batt_temp(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_step_chg_step(struct smb_charger *chg,
+ union power_supply_propval *val);
int smblib_set_prop_input_suspend(struct smb_charger *chg,
const union power_supply_propval *val);
diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h
index 0d5222ec08f8..c88d132fbf70 100644
--- a/drivers/power/qcom-charger/smb-reg.h
+++ b/drivers/power/qcom-charger/smb-reg.h
@@ -32,6 +32,7 @@
#define BATTERY_CHARGER_STATUS_1_REG (CHGR_BASE + 0x06)
#define BVR_INITIAL_RAMP_BIT BIT(7)
#define CC_SOFT_TERMINATE_BIT BIT(6)
+#define STEP_CHARGING_STATUS_SHIFT 3
#define STEP_CHARGING_STATUS_MASK GENMASK(5, 3)
#define BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0)
enum {
@@ -585,6 +586,7 @@ enum {
#define FORCE_FLOAT_SDP_CFG_BIT BIT(0)
#define TAPER_TIMER_SEL_CFG_REG (USBIN_BASE + 0x64)
+#define TYPEC_SPARE_CFG_BIT BIT(7)
#define TAPER_TIMER_SEL_MASK GENMASK(1, 0)
#define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65)
@@ -606,6 +608,8 @@ enum {
#define TYPEC_VBUS_ASSERT_INT_EN_BIT BIT(0)
#define TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG (USBIN_BASE + 0x68)
+#define EXIT_SNK_BASED_ON_CC BIT(7)
+#define VCONN_EN_ORIENTATION_BIT BIT(6)
#define TYPEC_VCONN_OVERCURR_INT_EN_BIT BIT(5)
#define VCONN_EN_SRC_BIT BIT(4)
#define VCONN_EN_VALUE_BIT BIT(3)
diff --git a/drivers/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c
index 5b4e7bcccdce..33d759be9aeb 100644
--- a/drivers/power/qcom-charger/smb138x-charger.c
+++ b/drivers/power/qcom-charger/smb138x-charger.c
@@ -25,6 +25,7 @@
#include <linux/qpnp/qpnp-revid.h>
#include "smb-reg.h"
#include "smb-lib.h"
+#include "storm-watch.h"
#include "pmic-voter.h"
#define SMB138X_DEFAULT_FCC_UA 1000000
@@ -748,55 +749,170 @@ static int smb138x_determine_initial_status(struct smb138x *chip)
**************************/
struct smb138x_irq_info {
- const char *name;
- const irq_handler_t handler;
+ const char *name;
+ const irq_handler_t handler;
+ const struct storm_watch storm_data;
};
static const struct smb138x_irq_info smb138x_irqs[] = {
/* CHARGER IRQs */
- { "chg-error", smblib_handle_debug },
- { "chg-state-change", smblib_handle_debug },
- { "step-chg-state-change", smblib_handle_debug },
- { "step-chg-soc-update-fail", smblib_handle_debug },
- { "step-chg-soc-update-request", smblib_handle_debug },
+ {
+ .name = "chg-error",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "chg-state-change",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "step-chg-state-change",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "step-chg-soc-update-fail",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "step-chg-soc-update-request",
+ .handler = smblib_handle_debug,
+ },
/* OTG IRQs */
- { "otg-fail", smblib_handle_debug },
- { "otg-overcurrent", smblib_handle_debug },
- { "otg-oc-dis-sw-sts", smblib_handle_debug },
- { "testmode-change-detect", smblib_handle_debug },
+ {
+ .name = "otg-fail",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "otg-overcurrent",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "otg-oc-dis-sw-sts",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "testmode-change-detect",
+ .handler = smblib_handle_debug,
+ },
/* BATTERY IRQs */
- { "bat-temp", smblib_handle_batt_psy_changed },
- { "bat-ocp", smblib_handle_batt_psy_changed },
- { "bat-ov", smblib_handle_batt_psy_changed },
- { "bat-low", smblib_handle_batt_psy_changed },
- { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed },
- { "bat-terminal-missing", smblib_handle_batt_psy_changed },
+ {
+ .name = "bat-temp",
+ .handler = smblib_handle_batt_psy_changed,
+ },
+ {
+ .name = "bat-ocp",
+ .handler = smblib_handle_batt_psy_changed,
+ },
+ {
+ .name = "bat-ov",
+ .handler = smblib_handle_batt_psy_changed,
+ },
+ {
+ .name = "bat-low",
+ .handler = smblib_handle_batt_psy_changed,
+ },
+ {
+ .name = "bat-therm-or-id-missing",
+ .handler = smblib_handle_batt_psy_changed,
+ },
+ {
+ .name = "bat-terminal-missing",
+ .handler = smblib_handle_batt_psy_changed,
+ },
/* USB INPUT IRQs */
- { "usbin-collapse", smblib_handle_debug },
- { "usbin-lt-3p6v", smblib_handle_debug },
- { "usbin-uv", smblib_handle_debug },
- { "usbin-ov", smblib_handle_debug },
- { "usbin-plugin", smblib_handle_usb_plugin },
- { "usbin-src-change", smblib_handle_usb_source_change },
- { "usbin-icl-change", smblib_handle_debug },
- { "type-c-change", smblib_handle_usb_typec_change },
+ {
+ .name = "usbin-collapse",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "usbin-lt-3p6v",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "usbin-uv",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "usbin-ov",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "usbin-plugin",
+ .handler = smblib_handle_usb_plugin,
+ },
+ {
+ .name = "usbin-src-change",
+ .handler = smblib_handle_usb_source_change,
+ },
+ {
+ .name = "usbin-icl-change",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "type-c-change",
+ .handler = smblib_handle_usb_typec_change,
+ },
/* DC INPUT IRQs */
- { "dcin-collapse", smblib_handle_debug },
- { "dcin-lt-3p6v", smblib_handle_debug },
- { "dcin-uv", smblib_handle_debug },
- { "dcin-ov", smblib_handle_debug },
- { "dcin-plugin", smblib_handle_debug },
- { "div2-en-dg", smblib_handle_debug },
- { "dcin-icl-change", smblib_handle_debug },
+ {
+ .name = "dcin-collapse",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "dcin-lt-3p6v",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "dcin-uv",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "dcin-ov",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "dcin-plugin",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "div2-en-dg",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "dcin-icl-change",
+ .handler = smblib_handle_debug,
+ },
/* MISCELLANEOUS IRQs */
- { "wdog-snarl", smblib_handle_debug },
- { "wdog-bark", smblib_handle_debug },
- { "aicl-fail", smblib_handle_debug },
- { "aicl-done", smblib_handle_debug },
- { "high-duty-cycle", smblib_handle_debug },
- { "input-current-limiting", smblib_handle_debug },
- { "temperature-change", smblib_handle_debug },
- { "switcher-power-ok", smblib_handle_debug },
+ {
+ .name = "wdog-snarl",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "wdog-bark",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "aicl-fail",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "aicl-done",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "high-duty-cycle",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "input-current-limiting",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "temperature-change",
+ .handler = smblib_handle_debug,
+ },
+ {
+ .name = "switcher-power-ok",
+ .handler = smblib_handle_debug,
+ },
};
static int smb138x_get_irq_index_byname(const char *irq_name)
@@ -837,6 +953,7 @@ static int smb138x_request_interrupt(struct smb138x *chip,
irq_data->parent_data = chip;
irq_data->name = irq_name;
+ irq_data->storm_data = smb138x_irqs[irq_index].storm_data;
rc = devm_request_threaded_irq(chg->dev, irq, NULL,
smb138x_irqs[irq_index].handler,
diff --git a/drivers/power/qcom-charger/storm-watch.c b/drivers/power/qcom-charger/storm-watch.c
new file mode 100644
index 000000000000..90fec12bd742
--- /dev/null
+++ b/drivers/power/qcom-charger/storm-watch.c
@@ -0,0 +1,57 @@
+/* Copyright (c) 2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "storm-watch.h"
+
+/**
+ * is_storming(): Check if an event is storming
+ *
+ * @data: Data for tracking an event storm
+ *
+ * The return value will be true if a storm has been detected and
+ * false if a storm was not detected.
+ */
+bool is_storming(struct storm_watch *data)
+{
+ ktime_t curr_kt, delta_kt;
+ bool is_storming = false;
+
+ if (!data)
+ return false;
+
+ if (!data->enabled)
+ return false;
+
+ /* max storm count must be greater than 0 */
+ if (data->max_storm_count <= 0)
+ return false;
+
+ /* the period threshold must be greater than 0ms */
+ if (data->storm_period_ms <= 0)
+ return false;
+
+ curr_kt = ktime_get_boottime();
+ delta_kt = ktime_sub(curr_kt, data->last_kt);
+
+ if (ktime_to_ms(delta_kt) < data->storm_period_ms)
+ data->storm_count++;
+ else
+ data->storm_count = 0;
+
+ if (data->storm_count > data->max_storm_count) {
+ is_storming = true;
+ data->storm_count = 0;
+ }
+
+ data->last_kt = curr_kt;
+ return is_storming;
+}
diff --git a/drivers/power/qcom-charger/storm-watch.h b/drivers/power/qcom-charger/storm-watch.h
new file mode 100644
index 000000000000..44b9d64d8a87
--- /dev/null
+++ b/drivers/power/qcom-charger/storm-watch.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __STORM_WATCH_H
+#define __STORM_WATCH_H
+#include <linux/ktime.h>
+
+/**
+ * Data used to track an event storm.
+ *
+ * @storm_period_ms: The maximum time interval between two events. If this limit
+ * is exceeded then the event chain will be broken and removed
+ * from consideration for a storm.
+ * @max_storm_count: The number of chained events required to trigger a storm.
+ * @storm_count: The current number of chained events.
+ * @last_kt: Kernel time of the last event seen.
+ */
+struct storm_watch {
+ bool enabled;
+ int storm_period_ms;
+ int max_storm_count;
+ int storm_count;
+ ktime_t last_kt;
+};
+
+bool is_storming(struct storm_watch *data);
+#endif
diff --git a/drivers/power/qcom/msm-core.c b/drivers/power/qcom/msm-core.c
index e990425bd63a..727a768e63eb 100644
--- a/drivers/power/qcom/msm-core.c
+++ b/drivers/power/qcom/msm-core.c
@@ -240,10 +240,10 @@ void trigger_cpu_pwr_stats_calc(void)
if (cpu_node->sensor_id < 0)
continue;
- if (cpu_node->temp == prev_temp[cpu])
+ if (cpu_node->temp == prev_temp[cpu]) {
sensor_get_temp(cpu_node->sensor_id, &temp);
-
- cpu_node->temp = temp / scaling_factor;
+ cpu_node->temp = temp / scaling_factor;
+ }
prev_temp[cpu] = cpu_node->temp;
@@ -373,7 +373,7 @@ static int update_userspace_power(struct sched_params __user *argp)
{
int i;
int ret;
- int cpu;
+ int cpu = -1;
struct cpu_activity_info *node;
struct cpu_static_info *sp, *clear_sp;
int cpumask, cluster, mpidr;
@@ -396,7 +396,7 @@ static int update_userspace_power(struct sched_params __user *argp)
}
}
- if (cpu >= num_possible_cpus())
+ if ((cpu < 0) || (cpu >= num_possible_cpus()))
return -EINVAL;
node = &activity[cpu];
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 1a1dd804ffb3..ad4b6ffef36e 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1498,14 +1498,10 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
/* M-PHY RMMI interface clocks can be turned off */
ufs_qcom_phy_disable_iface_clk(host->generic_phy);
- /*
- * If auto hibern8 is supported then the link will already
- * be in hibern8 state and the ref clock can be gated.
- */
- if (ufshcd_is_auto_hibern8_supported(hba) ||
- !ufs_qcom_is_link_active(hba)) {
- /* turn off UFS local PHY ref_clk */
- ufs_qcom_phy_disable_ref_clk(host->generic_phy);
+ if (!ufs_qcom_is_link_active(hba)) {
+ if (!is_gating_context)
+ /* turn off UFS local PHY ref_clk */
+ ufs_qcom_phy_disable_ref_clk(host->generic_phy);
/* disable device ref_clk */
ufs_qcom_dev_ref_clk_ctrl(host, false);
}
@@ -1960,6 +1956,13 @@ static int ufs_qcom_init(struct ufs_hba *hba)
host->hba = hba;
ufshcd_set_variant(hba, host);
+ /*
+ * voting/devoting device ref_clk source is time consuming hence
+ * skip devoting it during aggressive clock gating. This clock
+ * will still be gated off during runtime suspend.
+ */
+ hba->no_ref_clk_gating = true;
+
err = ufs_qcom_ice_get_dev(host);
if (err == -EPROBE_DEFER) {
/*
diff --git a/drivers/scsi/ufs/ufs_test.c b/drivers/scsi/ufs/ufs_test.c
index 8953722e8dad..e23dc3e8d9da 100644
--- a/drivers/scsi/ufs/ufs_test.c
+++ b/drivers/scsi/ufs/ufs_test.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -689,13 +689,13 @@ static void scenario_free_end_io_fn(struct request *rq, int err)
__blk_put_request(test_iosched->req_q, test_rq->rq);
spin_unlock_irqrestore(&test_iosched->lock, flags);
- test_iosched_free_test_req_data_buffer(test_rq);
- kfree(test_rq);
-
if (err)
pr_err("%s: request %d completed, err=%d", __func__,
test_rq->req_id, err);
+ test_iosched_free_test_req_data_buffer(test_rq);
+ kfree(test_rq);
+
check_test_completion(test_iosched);
}
@@ -984,14 +984,14 @@ static void long_test_free_end_io_fn(struct request *rq, int err)
return;
}
- test_iosched_free_test_req_data_buffer(test_rq);
- kfree(test_rq);
- utd->completed_req_count++;
-
if (err)
pr_err("%s: request %d completed, err=%d", __func__,
test_rq->req_id, err);
+ test_iosched_free_test_req_data_buffer(test_rq);
+ kfree(test_rq);
+ utd->completed_req_count++;
+
check_test_completion(test_iosched);
}
@@ -1007,7 +1007,7 @@ static void long_test_free_end_io_fn(struct request *rq, int err)
static int run_long_test(struct test_iosched *test_iosched)
{
int ret = 0;
- int direction, num_bios_per_request;
+ int direction, num_bios_per_request = 1;
static unsigned int inserted_requests;
u32 sector, seed, num_bios, seq_sector_delta;
struct ufs_test_data *utd = test_iosched->blk_dev_test_data;
@@ -1028,14 +1028,12 @@ static int run_long_test(struct test_iosched *test_iosched)
/* Set test parameters */
switch (test_iosched->test_info.testcase) {
case UFS_TEST_LONG_RANDOM_READ:
- num_bios_per_request = 1;
utd->long_test_num_reqs = (utd->sector_range * SECTOR_SIZE) /
(LONG_RAND_TEST_REQ_RATIO * TEST_BIO_SIZE *
num_bios_per_request);
direction = READ;
break;
case UFS_TEST_LONG_RANDOM_WRITE:
- num_bios_per_request = 1;
utd->long_test_num_reqs = (utd->sector_range * SECTOR_SIZE) /
(LONG_RAND_TEST_REQ_RATIO * TEST_BIO_SIZE *
num_bios_per_request);
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 311a4ea24973..d478767ad3dd 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1346,12 +1346,7 @@ static void ufshcd_gate_work(struct work_struct *work)
ufshcd_set_link_hibern8(hba);
}
- /*
- * If auto hibern8 is supported then the link will already
- * be in hibern8 state and the ref clock can be gated.
- */
- if ((ufshcd_is_auto_hibern8_supported(hba) ||
- !ufshcd_is_link_active(hba)) && !hba->no_ref_clk_gating)
+ if (!ufshcd_is_link_active(hba) && !hba->no_ref_clk_gating)
ufshcd_disable_clocks(hba, true);
else
/* If link is active, device ref_clk can't be switched off */
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index edaaa0b2d3c8..cdc07411b690 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -34,6 +34,7 @@
#include <linux/qmi_encdec.h>
#include <linux/ipc_logging.h>
#include <linux/msm-bus.h>
+#include <linux/thread_info.h>
#include <linux/uaccess.h>
#include <linux/qpnp/qpnp-adc.h>
#include <soc/qcom/memory_dump.h>
@@ -41,6 +42,7 @@
#include <soc/qcom/msm_qmi_interface.h>
#include <soc/qcom/secure_buffer.h>
#include <soc/qcom/subsystem_notif.h>
+#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/service-locator.h>
#include <soc/qcom/service-notifier.h>
#include <soc/qcom/socinfo.h>
@@ -270,6 +272,10 @@ enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_MAX,
};
+struct icnss_event_pd_service_down_data {
+ bool crashed;
+};
+
struct icnss_driver_event {
struct list_head list;
enum icnss_driver_event_type type;
@@ -285,13 +291,13 @@ enum icnss_driver_state {
ICNSS_FW_READY,
ICNSS_DRIVER_PROBED,
ICNSS_FW_TEST_MODE,
- ICNSS_SUSPEND,
ICNSS_PM_SUSPEND,
ICNSS_PM_SUSPEND_NOIRQ,
ICNSS_SSR_ENABLED,
ICNSS_PDR_ENABLED,
ICNSS_PD_RESTART,
ICNSS_MSA0_ASSIGNED,
+ ICNSS_WLFW_EXISTS,
};
struct ce_irq_list {
@@ -535,9 +541,9 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
int gfp = GFP_KERNEL;
int ret = 0;
- icnss_pr_dbg("Posting event: %s(%d)%s, state: 0x%lx\n",
- icnss_driver_event_to_str(type), type,
- sync ? "-sync" : "", penv->state);
+ icnss_pr_dbg("Posting event: %s: %s%s(%d), state: 0x%lx\n",
+ current->comm, icnss_driver_event_to_str(type),
+ sync ? "-sync" : "", type, penv->state);
if (type >= ICNSS_DRIVER_EVENT_MAX) {
icnss_pr_err("Invalid Event type: %d, can't post", type);
@@ -2212,6 +2218,8 @@ static int icnss_driver_event_server_arrive(void *data)
if (!penv)
return -ENODEV;
+ set_bit(ICNSS_WLFW_EXISTS, &penv->state);
+
penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
if (!penv->wlfw_clnt) {
icnss_pr_err("QMI client handle create failed\n");
@@ -2311,6 +2319,8 @@ static int icnss_call_driver_probe(struct icnss_priv *priv)
if (!priv->ops || !priv->ops->probe)
return 0;
+ icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
+
icnss_hw_power_on(priv);
ret = priv->ops->probe(&priv->pdev->dev);
@@ -2339,6 +2349,8 @@ static int icnss_call_driver_reinit(struct icnss_priv *priv)
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
goto out;
+ icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
+
icnss_hw_power_on(priv);
ret = priv->ops->reinit(&priv->pdev->dev);
@@ -2463,36 +2475,70 @@ out:
return 0;
}
-static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
- void *data)
+static int icnss_call_driver_remove(struct icnss_priv *priv)
{
- int ret = 0;
+ icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
- if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
- icnss_pr_err("PD Down while recovery inprogress, state: 0x%lx\n",
- priv->state);
- ICNSS_ASSERT(0);
- goto out;
- }
+ clear_bit(ICNSS_FW_READY, &priv->state);
+
+ if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
+ return 0;
+
+ if (!priv->ops || !priv->ops->remove)
+ return 0;
+
+ penv->ops->remove(&priv->pdev->dev);
+
+ clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
+
+ return 0;
+}
+
+static int icnss_call_driver_shutdown(struct icnss_priv *priv)
+{
+ icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state);
set_bit(ICNSS_PD_RESTART, &priv->state);
clear_bit(ICNSS_FW_READY, &priv->state);
+ if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
+ return 0;
+
if (!priv->ops || !priv->ops->shutdown)
- goto out;
+ return 0;
- if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
+ priv->ops->shutdown(&priv->pdev->dev);
+
+ return 0;
+}
+
+static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
+ void *data)
+{
+ int ret = 0;
+ struct icnss_event_pd_service_down_data *event_data = data;
+
+ if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
+ return 0;
+
+ if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
+ icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
+ event_data->crashed, priv->state);
+ ICNSS_ASSERT(0);
goto out;
+ }
- priv->ops->shutdown(&priv->pdev->dev);
+ if (event_data->crashed)
+ icnss_call_driver_shutdown(priv);
+ else
+ icnss_call_driver_remove(priv);
out:
icnss_remove_msa_permissions(priv);
ret = icnss_hw_power_off(priv);
- icnss_pr_dbg("PD down completed: %d, state: 0x%lx\n",
- ret, priv->state);
+ kfree(data);
return ret;
}
@@ -2533,7 +2579,8 @@ static void icnss_driver_event_work(struct work_struct *work)
ret = icnss_driver_event_unregister_driver(event->data);
break;
case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN:
- icnss_driver_event_pd_service_down(penv, event->data);
+ ret = icnss_driver_event_pd_service_down(penv,
+ event->data);
break;
default:
icnss_pr_err("Invalid Event type: %d", event->type);
@@ -2543,6 +2590,11 @@ static void icnss_driver_event_work(struct work_struct *work)
penv->stats.events[event->type].processed++;
+ icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n",
+ icnss_driver_event_to_str(event->type),
+ event->sync ? "-sync" : "", event->type, ret,
+ penv->state);
+
spin_lock_irqsave(&penv->event_lock, flags);
if (event->sync) {
event->ret = ret;
@@ -2590,23 +2642,31 @@ static struct notifier_block wlfw_clnt_nb = {
.notifier_call = icnss_qmi_wlfw_clnt_svc_event_notify,
};
-static int icnss_modem_notifier_nb(struct notifier_block *this,
+static int icnss_modem_notifier_nb(struct notifier_block *nb,
unsigned long code,
- void *ss_handle)
+ void *data)
{
+ struct icnss_event_pd_service_down_data *event_data;
+ struct notif_data *notif = data;
+ struct icnss_priv *priv = container_of(nb, struct icnss_priv,
+ modem_ssr_nb);
+
icnss_pr_dbg("Modem-Notify: event %lu\n", code);
- if (code == SUBSYS_AFTER_POWERUP) {
- icnss_pr_dbg("Modem-Notify: Powerup\n");
- } else if (code == SUBSYS_BEFORE_SHUTDOWN) {
- icnss_pr_info("Modem-Notify: Before shutdown\n");
- icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
- true, NULL);
- } else if (code == SUBSYS_AFTER_SHUTDOWN) {
- icnss_pr_info("Modem-Notify: After Shutdown\n");
- } else {
- return NOTIFY_DONE;
- }
+ if (code != SUBSYS_BEFORE_SHUTDOWN)
+ return NOTIFY_OK;
+
+ icnss_pr_info("Modem went down, state: %lx\n", priv->state);
+
+ event_data = kzalloc(sizeof(*data), GFP_KERNEL);
+
+ if (event_data == NULL)
+ return notifier_from_errno(-ENOMEM);
+
+ event_data->crashed = notif->crashed;
+
+ icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+ true, event_data);
return NOTIFY_OK;
}
@@ -2665,14 +2725,23 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
{
struct icnss_priv *priv = container_of(nb, struct icnss_priv,
service_notifier_nb);
+ enum pd_subsys_state *state = data;
+ struct icnss_event_pd_service_down_data *event_data;
switch (notification) {
case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
- icnss_pr_info("Service down, state: 0x%lx\n", priv->state);
+ icnss_pr_info("Service down, data: 0x%p, state: 0x%lx\n", data,
+ priv->state);
+ event_data = kzalloc(sizeof(*data), GFP_KERNEL);
+
+ if (event_data == NULL)
+ return notifier_from_errno(-ENOMEM);
+
+ if (state == NULL || *state != SHUTDOWN)
+ event_data->crashed = true;
+
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
- true, NULL);
- icnss_pr_dbg("Service down completed, state: 0x%lx\n",
- priv->state);
+ true, event_data);
break;
case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
icnss_pr_dbg("Service up, state: 0x%lx\n", priv->state);
@@ -2818,8 +2887,6 @@ enable_pdr:
if (ret)
return ret;
- icnss_modem_ssr_unregister_notifier(priv);
-
return 0;
}
@@ -3753,9 +3820,6 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
case ICNSS_FW_TEST_MODE:
seq_puts(s, "FW TEST MODE");
continue;
- case ICNSS_SUSPEND:
- seq_puts(s, "SUSPEND");
- continue;
case ICNSS_PM_SUSPEND:
seq_puts(s, "PM SUSPEND");
continue;
@@ -3774,6 +3838,8 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
case ICNSS_MSA0_ASSIGNED:
seq_puts(s, "MSA0 ASSIGNED");
continue;
+ case ICNSS_WLFW_EXISTS:
+ continue;
}
seq_printf(s, "UNKNOWN-%d", i);
@@ -4400,55 +4466,6 @@ static int icnss_remove(struct platform_device *pdev)
return 0;
}
-static int icnss_suspend(struct platform_device *pdev,
- pm_message_t state)
-{
- int ret = 0;
-
- if (!penv) {
- ret = -ENODEV;
- goto out;
- }
-
- icnss_pr_dbg("Driver suspending, state: 0x%lx\n",
- penv->state);
-
- if (!penv->ops || !penv->ops->suspend ||
- !test_bit(ICNSS_DRIVER_PROBED, &penv->state))
- goto out;
-
- ret = penv->ops->suspend(&pdev->dev, state);
-
-out:
- if (ret == 0)
- set_bit(ICNSS_SUSPEND, &penv->state);
- return ret;
-}
-
-static int icnss_resume(struct platform_device *pdev)
-{
- int ret = 0;
-
- if (!penv) {
- ret = -ENODEV;
- goto out;
- }
-
- icnss_pr_dbg("Driver resuming, state: 0x%lx\n",
- penv->state);
-
- if (!penv->ops || !penv->ops->resume ||
- !test_bit(ICNSS_DRIVER_PROBED, &penv->state))
- goto out;
-
- ret = penv->ops->resume(&pdev->dev);
-
-out:
- if (ret == 0)
- clear_bit(ICNSS_SUSPEND, &penv->state);
- return ret;
-}
-
#ifdef CONFIG_PM_SLEEP
static int icnss_pm_suspend(struct device *dev)
{
@@ -4584,8 +4601,6 @@ MODULE_DEVICE_TABLE(of, icnss_dt_match);
static struct platform_driver icnss_driver = {
.probe = icnss_probe,
.remove = icnss_remove,
- .suspend = icnss_suspend,
- .resume = icnss_resume,
.driver = {
.name = "icnss",
.pm = &icnss_pm_ops,
diff --git a/drivers/soc/qcom/jtag-fuse.c b/drivers/soc/qcom/jtag-fuse.c
index 0b05ce9a22bb..0f347723e378 100644
--- a/drivers/soc/qcom/jtag-fuse.c
+++ b/drivers/soc/qcom/jtag-fuse.c
@@ -152,8 +152,6 @@ static int jtag_fuse_probe(struct platform_device *pdev)
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
- /* Store the driver data pointer for use in exported functions */
- fusedrvdata = drvdata;
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
@@ -174,6 +172,8 @@ static int jtag_fuse_probe(struct platform_device *pdev)
if (!drvdata->base)
return -ENOMEM;
+ /* Store the driver data pointer for use in exported functions */
+ fusedrvdata = drvdata;
dev_info(dev, "JTag Fuse initialized\n");
return 0;
}
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index 28b47ca6112a..504a3263253c 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -114,6 +114,7 @@ struct qmi_client_info {
};
static LIST_HEAD(qmi_client_list);
static DEFINE_MUTEX(qmi_list_lock);
+static DEFINE_MUTEX(qmi_client_release_lock);
static DEFINE_MUTEX(notif_add_lock);
@@ -417,9 +418,11 @@ static void root_service_service_exit(struct qmi_client_info *data,
* Destroy client handle and try connecting when
* service comes up again.
*/
+ mutex_lock(&qmi_client_release_lock);
data->service_connected = false;
qmi_handle_destroy(data->clnt_handle);
data->clnt_handle = NULL;
+ mutex_unlock(&qmi_client_release_lock);
}
static void root_service_exit_work(struct work_struct *work)
diff --git a/drivers/soc/qcom/system_stats.c b/drivers/soc/qcom/system_stats.c
index 476d2f6dca27..ba35928a991b 100644
--- a/drivers/soc/qcom/system_stats.c
+++ b/drivers/soc/qcom/system_stats.c
@@ -154,7 +154,7 @@ static int rpm_stats_write_buf(struct seq_file *m)
time = get_time_in_msec(time);
seq_printf(m, "\ttime in last mode(msec):%llu\n", time);
- time = arch_counter_get_cntpct() - rs.last_exited_at;
+ time = arch_counter_get_cntvct() - rs.last_exited_at;
time = get_time_in_sec(time);
seq_printf(m, "\ttime since last mode(sec):%llu\n", time);
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index a8c8e120c348..e0af922a0329 100755
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -1201,7 +1201,7 @@ static void ion_vm_open(struct vm_area_struct *vma)
mutex_lock(&buffer->lock);
list_add(&vma_list->list, &buffer->vmas);
mutex_unlock(&buffer->lock);
- pr_debug("%s: adding %p\n", __func__, vma);
+ pr_debug("%s: adding %pK\n", __func__, vma);
}
static void ion_vm_close(struct vm_area_struct *vma)
@@ -1216,7 +1216,7 @@ static void ion_vm_close(struct vm_area_struct *vma)
continue;
list_del(&vma_list->list);
kfree(vma_list);
- pr_debug("%s: deleting %p\n", __func__, vma);
+ pr_debug("%s: deleting %pK\n", __func__, vma);
break;
}
mutex_unlock(&buffer->lock);
diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c
index aaea7bed36e1..b2e1a4c1b170 100644
--- a/drivers/staging/android/ion/ion_cma_heap.c
+++ b/drivers/staging/android/ion/ion_cma_heap.c
@@ -94,7 +94,7 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
/* keep this for memory release */
buffer->priv_virt = info;
- dev_dbg(dev, "Allocate buffer %p\n", buffer);
+ dev_dbg(dev, "Allocate buffer %pK\n", buffer);
return 0;
err:
@@ -107,7 +107,7 @@ static void ion_cma_free(struct ion_buffer *buffer)
struct device *dev = buffer->heap->priv;
struct ion_cma_buffer_info *info = buffer->priv_virt;
- dev_dbg(dev, "Release buffer %p\n", buffer);
+ dev_dbg(dev, "Release buffer %pK\n", buffer);
/* release memory */
dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle);
sg_free_table(info->table);
@@ -123,7 +123,7 @@ static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer,
struct device *dev = heap->priv;
struct ion_cma_buffer_info *info = buffer->priv_virt;
- dev_dbg(dev, "Return buffer %p physical address %pa\n", buffer,
+ dev_dbg(dev, "Return buffer %pK physical address %pa\n", buffer,
&info->handle);
*addr = info->handle;
diff --git a/drivers/staging/android/ion/ion_cma_secure_heap.c b/drivers/staging/android/ion/ion_cma_secure_heap.c
index d945b9251437..90ae7eb65b65 100644
--- a/drivers/staging/android/ion/ion_cma_secure_heap.c
+++ b/drivers/staging/android/ion/ion_cma_secure_heap.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Linaro 2012
* Author: <benjamin.gaignard@linaro.org> for ST-Ericsson.
- * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -501,7 +501,7 @@ retry:
/* keep this for memory release */
buffer->priv_virt = info;
- dev_dbg(sheap->dev, "Allocate buffer %p\n", buffer);
+ dev_dbg(sheap->dev, "Allocate buffer %pK\n", buffer);
return info;
err:
@@ -634,7 +634,7 @@ retry:
sg = sg_next(sg);
}
buffer->priv_virt = info;
- dev_dbg(sheap->dev, "Allocate buffer %p\n", buffer);
+ dev_dbg(sheap->dev, "Allocate buffer %pK\n", buffer);
return info;
err2:
@@ -721,7 +721,7 @@ static void ion_secure_cma_free(struct ion_buffer *buffer)
struct ion_secure_cma_buffer_info *info = buffer->priv_virt;
int ret = 0;
- dev_dbg(sheap->dev, "Release buffer %p\n", buffer);
+ dev_dbg(sheap->dev, "Release buffer %pK\n", buffer);
if (msm_secure_v2_is_supported())
ret = msm_unsecure_table(info->table);
atomic_sub(buffer->size, &sheap->total_allocated);
@@ -743,8 +743,8 @@ static int ion_secure_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer,
container_of(heap, struct ion_cma_secure_heap, heap);
struct ion_secure_cma_buffer_info *info = buffer->priv_virt;
- dev_dbg(sheap->dev, "Return buffer %p physical address 0x%pa\n", buffer,
- &info->phys);
+ dev_dbg(sheap->dev, "Return buffer %pK physical address 0x%pa\n",
+ buffer, &info->phys);
*addr = info->phys;
*len = buffer->size;
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index fd4d45ad8db2..03b2b8a38991 100644
--- a/drivers/staging/android/ion/ion_system_heap.c
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -204,11 +204,16 @@ static struct page *split_page_from_secure_pool(struct ion_system_heap *heap,
split_page(page, order);
break;
}
- /* Return the remaining order-0 pages to the pool */
- if (page)
- for (j = 1; j < (1 << order); j++)
+ /*
+ * Return the remaining order-0 pages to the pool.
+ * SetPagePrivate flag to mark memory as secure.
+ */
+ if (page) {
+ for (j = 1; j < (1 << order); j++) {
+ SetPagePrivate(page + j);
free_buffer_page(heap, buffer, page + j, 0);
-
+ }
+ }
got_page:
mutex_unlock(&heap->split_page_mutex);
diff --git a/drivers/staging/android/ion/msm/msm_ion.c b/drivers/staging/android/ion/msm/msm_ion.c
index 592c603b1780..176f22ba570c 100644
--- a/drivers/staging/android/ion/msm/msm_ion.c
+++ b/drivers/staging/android/ion/msm/msm_ion.c
@@ -711,7 +711,7 @@ long msm_ion_custom_ioctl(struct ion_client *client,
} else {
handle = ion_import_dma_buf(client, data.flush_data.fd);
if (IS_ERR(handle)) {
- pr_info("%s: Could not import handle: %p\n",
+ pr_info("%s: Could not import handle: %pK\n",
__func__, handle);
return -EINVAL;
}
@@ -724,8 +724,8 @@ long msm_ion_custom_ioctl(struct ion_client *client,
+ data.flush_data.length;
if (start && check_vaddr_bounds(start, end)) {
- pr_err("%s: virtual address %p is out of bounds\n",
- __func__, data.flush_data.vaddr);
+ pr_err("%s: virtual address %pK is out of bounds\n",
+ __func__, data.flush_data.vaddr);
ret = -EINVAL;
} else {
ret = ion_do_cache_op(
diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c
index b6d76402b726..df3a638510c2 100644
--- a/drivers/thermal/msm-tsens.c
+++ b/drivers/thermal/msm-tsens.c
@@ -933,6 +933,9 @@ static struct of_device_id tsens_match[] = {
{ .compatible = "qcom,msmfalcon-tsens",
.data = (void *)TSENS_CALIB_FUSE_MAP_NONE,
},
+ { .compatible = "qcom,msmtriton-tsens",
+ .data = (void *)TSENS_CALIB_FUSE_MAP_NONE,
+ },
{}
};
@@ -5435,7 +5438,8 @@ static int get_device_tree_data(struct platform_device *pdev,
tmdev->tsens_type = TSENS_TYPE3;
else if (!strcmp(id->compatible, "qcom,msmtitanium-tsens") ||
(!strcmp(id->compatible, "qcom,msmfalcon-tsens") ||
- (!strcmp(id->compatible, "qcom,msmhamster-tsens")))) {
+ (!strcmp(id->compatible, "qcom,msmtriton-tsens") ||
+ (!strcmp(id->compatible, "qcom,msmhamster-tsens"))))) {
tmdev->tsens_type = TSENS_TYPE3;
tsens_poll_check = 0;
} else if (!strcmp(id->compatible, "qcom,msm8952-tsens") ||
@@ -5457,7 +5461,8 @@ static int get_device_tree_data(struct platform_device *pdev,
(!strcmp(id->compatible, "qcom,msmtitanium-tsens")) ||
(!strcmp(id->compatible, "qcom,msmcobalt-tsens")) ||
(!strcmp(id->compatible, "qcom,msmfalcon-tsens") ||
- (!strcmp(id->compatible, "qcom,msmhamster-tsens"))))
+ (!strcmp(id->compatible, "qcom,msmtriton-tsens") ||
+ (!strcmp(id->compatible, "qcom,msmhamster-tsens")))))
tmdev->tsens_valid_status_check = true;
}
@@ -5473,7 +5478,8 @@ static int get_device_tree_data(struct platform_device *pdev,
(!strcmp(id->compatible, "qcom,msmcobalt-tsens")) ||
(!strcmp(id->compatible, "qcom,msmhamster-tsens")) ||
(!strcmp(id->compatible, "qcom,msmfalcon-tsens") ||
- (!strcmp(id->compatible, "qcom,msmtitanium-tsens")))) {
+ (!strcmp(id->compatible, "qcom,msmtriton-tsens") ||
+ (!strcmp(id->compatible, "qcom,msmtitanium-tsens"))))) {
tmdev->tsens_critical_irq =
platform_get_irq_byname(pdev,
"tsens-critical");
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index c89ae79763c6..9ef57e5d7d64 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -175,6 +175,9 @@ config USB_F_SUBSET
config USB_F_RNDIS
tristate
+config USB_F_QCRNDIS
+ tristate
+
config USB_F_MASS_STORAGE
tristate
@@ -318,6 +321,14 @@ config USB_CONFIGFS_ECM_SUBSET
On hardware that can't implement the full protocol,
a simple CDC subset is used, placing fewer demands on USB.
+config USB_CONFIGFS_QCRNDIS
+ bool "RNDIS"
+ depends on USB_CONFIGFS
+ depends on RNDIS_IPA
+ depends on NET
+ select USB_U_ETHER
+ select USB_F_QCRNDIS
+
config USB_CONFIGFS_RNDIS
bool "RNDIS"
depends on USB_CONFIGFS
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 9a27deac7978..a213cd4c8377 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -60,3 +60,5 @@ usb_f_cdev-y := f_cdev.o
obj-$(CONFIG_USB_F_CDEV) += usb_f_cdev.o
usb_f_qdss-y := f_qdss.o u_qdss.o
obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o
+usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o
+obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o
diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c
index bc319d4fe16c..316967415aa9 100644
--- a/drivers/usb/gadget/function/f_qc_rndis.c
+++ b/drivers/usb/gadget/function/f_qc_rndis.c
@@ -25,16 +25,17 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/atomic.h>
#include "u_ether.h"
-#include "u_qc_ether.h"
#include "rndis.h"
-#include "u_bam_data.h"
+#include "u_data_ipa.h"
#include <linux/rndis_ipa.h>
+#include "configfs.h"
unsigned int rndis_dl_max_xfer_size = 9216;
module_param(rndis_dl_max_xfer_size, uint, S_IRUGO | S_IWUSR);
@@ -86,7 +87,7 @@ MODULE_PARM_DESC(rndis_dl_max_xfer_size,
*/
struct f_rndis_qc {
- struct qc_gether port;
+ struct usb_function func;
u8 ctrl_id, data_id;
u8 ethaddr[ETH_ALEN];
u32 vendorID;
@@ -94,27 +95,27 @@ struct f_rndis_qc {
u8 pkt_alignment_factor;
u32 max_pkt_size;
const char *manufacturer;
- int config;
+ struct rndis_params *params;
atomic_t ioctl_excl;
atomic_t open_excl;
struct usb_ep *notify;
struct usb_request *notify_req;
atomic_t notify_count;
- struct data_port bam_port;
- enum transport_type xport;
+ struct gadget_ipa_port bam_port;
u8 port_num;
+ u16 cdc_filter;
bool net_ready_trigger;
};
static struct ipa_usb_init_params rndis_ipa_params;
static spinlock_t rndis_lock;
static bool rndis_ipa_supported;
-static void rndis_qc_open(struct qc_gether *geth);
+static void rndis_qc_open(struct f_rndis_qc *rndis);
static inline struct f_rndis_qc *func_to_rndis_qc(struct usb_function *f)
{
- return container_of(f, struct f_rndis_qc, port.func);
+ return container_of(f, struct f_rndis_qc, func);
}
/* peak (theoretical) bulk transfer rate in bits-per-second */
@@ -322,10 +323,20 @@ static struct usb_endpoint_descriptor rndis_qc_ss_notify_desc = {
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
+ .wMaxPacketSize = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT),
.bInterval = RNDIS_QC_LOG2_STATUS_INTERVAL_MSEC + 4,
};
+static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
+ .bLength = sizeof(ss_intr_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 3 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT),
+};
+
static struct usb_ss_ep_comp_descriptor rndis_qc_ss_intr_comp_desc = {
.bLength = sizeof(ss_intr_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
@@ -333,7 +344,16 @@ static struct usb_ss_ep_comp_descriptor rndis_qc_ss_intr_comp_desc = {
/* the following 3 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
- .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT),
+ .wBytesPerInterval = cpu_to_le16(RNDIS_QC_STATUS_BYTECOUNT),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
+ .bLength = sizeof(ss_bulk_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
};
static struct usb_endpoint_descriptor rndis_qc_ss_in_desc = {
@@ -407,7 +427,7 @@ struct f_rndis_qc *_rndis_qc;
static inline int rndis_qc_lock(atomic_t *excl)
{
- if (atomic_inc_return(excl) == 1) {
+ if (atomic_inc_return(excl) == 1)
return 0;
atomic_dec(excl);
@@ -421,46 +441,6 @@ static inline void rndis_qc_unlock(atomic_t *excl)
/*-------------------------------------------------------------------------*/
-static struct sk_buff *rndis_qc_add_header(struct qc_gether *port,
- struct sk_buff *skb)
-{
- struct sk_buff *skb2;
-
- skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
- if (skb2)
- rndis_add_hdr(skb2);
-
- dev_kfree_skb_any(skb);
- return skb2;
-}
-
-int rndis_qc_rm_hdr(struct qc_gether *port,
- struct sk_buff *skb,
- struct sk_buff_head *list)
-{
- /* tmp points to a struct rndis_packet_msg_type */
- __le32 *tmp = (void *)skb->data;
-
- /* MessageType, MessageLength */
- if (cpu_to_le32(RNDIS_MSG_PACKET)
- != get_unaligned(tmp++)) {
- dev_kfree_skb_any(skb);
- return -EINVAL;
- }
- tmp++;
-
- /* DataOffset, DataLength */
- if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
- dev_kfree_skb_any(skb);
- return -EOVERFLOW;
- }
- skb_trim(skb, get_unaligned_le32(tmp++));
-
- skb_queue_tail(list, skb);
- return 0;
-}
-
-
static void rndis_qc_response_available(void *_rndis)
{
struct f_rndis_qc *rndis = _rndis;
@@ -496,12 +476,12 @@ static void rndis_qc_response_complete(struct usb_ep *ep,
int status = req->status;
struct usb_composite_dev *cdev;
- if (!rndis->port.func.config || !rndis->port.func.config->cdev) {
+ if (!rndis->func.config || !rndis->func.config->cdev) {
pr_err("%s(): cdev or config is NULL.\n", __func__);
return;
}
- cdev = rndis->port.func.config->cdev;
+ cdev = rndis->func.config->cdev;
/* after TX:
* - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
* - RNDIS_RESPONSE_AVAILABLE (status/irq)
@@ -544,7 +524,7 @@ static void rndis_qc_command_complete(struct usb_ep *ep,
u32 ul_max_xfer_size, dl_max_xfer_size;
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
- status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
+ status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
if (status < 0)
pr_err("RNDIS command error %d, %d/%d\n",
status, req->actual, req->length);
@@ -552,8 +532,8 @@ static void rndis_qc_command_complete(struct usb_ep *ep,
buf = (rndis_init_msg_type *)req->buf;
if (buf->MessageType == RNDIS_MSG_INIT) {
- ul_max_xfer_size = rndis_get_ul_max_xfer_size(rndis->config);
- u_bam_data_set_ul_max_xfer_size(ul_max_xfer_size);
+ ul_max_xfer_size = rndis_get_ul_max_xfer_size(rndis->params);
+ ipa_data_set_ul_max_xfer_size(ul_max_xfer_size);
/*
* For consistent data throughput from IPA, it is required to
* fine tune aggregation byte limit as 7KB. RNDIS IPA driver
@@ -565,11 +545,11 @@ static void rndis_qc_command_complete(struct usb_ep *ep,
*/
if (rndis_dl_max_xfer_size)
dl_max_xfer_size = min_t(u32, rndis_dl_max_xfer_size,
- rndis_get_dl_max_xfer_size(rndis->config));
+ rndis_get_dl_max_xfer_size(rndis->params));
else
dl_max_xfer_size =
- rndis_get_dl_max_xfer_size(rndis->config);
- u_bam_data_set_dl_max_xfer_size(dl_max_xfer_size);
+ rndis_get_dl_max_xfer_size(rndis->params);
+ ipa_data_set_dl_max_xfer_size(dl_max_xfer_size);
}
}
@@ -612,11 +592,11 @@ rndis_qc_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
u32 n;
/* return the result */
- buf = rndis_get_next_response(rndis->config, &n);
+ buf = rndis_get_next_response(rndis->params, &n);
if (buf) {
memcpy(req->buf, buf, n);
req->complete = rndis_qc_response_complete;
- rndis_free_response(rndis->config, buf);
+ rndis_free_response(rndis->params, buf);
value = n;
}
/* else stalls ... spec says to avoid that */
@@ -647,11 +627,31 @@ invalid:
return value;
}
+struct net_device *rndis_qc_get_net(const char *netname)
+{
+ struct net_device *net_dev;
+
+ net_dev = dev_get_by_name(&init_net, netname);
+ if (!net_dev)
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * Decrement net_dev refcount as it was incremented in
+ * dev_get_by_name().
+ */
+ dev_put(net_dev);
+ return net_dev;
+}
static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_rndis_qc *rndis = func_to_rndis_qc(f);
+ struct f_rndis_qc_opts *opts;
struct usb_composite_dev *cdev = f->config->cdev;
+ u8 src_connection_idx;
+ u8 dst_connection_idx;
+ enum usb_ctrl usb_bam_type;
+ int ret;
/* we know alt == 0 */
@@ -672,35 +672,28 @@ static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
struct net_device *net;
rndis->net_ready_trigger = false;
- if (rndis->port.in_ep->driver_data) {
+ if (rndis->bam_port.in->driver_data) {
DBG(cdev, "reset rndis\n");
- /* rndis->port is needed for disconnecting the BAM data
+ /* bam_port is needed for disconnecting the BAM data
* path. Only after the BAM data path is disconnected,
* we can disconnect the port from the network layer.
*/
- bam_data_disconnect(&rndis->bam_port, USB_FUNC_RNDIS,
- rndis->port_num);
-
- if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA)
- gether_qc_disconnect_name(&rndis->port,
- "rndis0");
+ ipa_data_disconnect(&rndis->bam_port,
+ USB_IPA_FUNC_RNDIS);
}
- if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
+ if (!rndis->bam_port.in->desc || !rndis->bam_port.out->desc) {
DBG(cdev, "init rndis\n");
if (config_ep_by_speed(cdev->gadget, f,
- rndis->port.in_ep) ||
+ rndis->bam_port.in) ||
config_ep_by_speed(cdev->gadget, f,
- rndis->port.out_ep)) {
- rndis->port.in_ep->desc = NULL;
- rndis->port.out_ep->desc = NULL;
+ rndis->bam_port.out)) {
+ rndis->bam_port.in->desc = NULL;
+ rndis->bam_port.out->desc = NULL;
goto fail;
}
}
- /* Avoid ZLPs; they can be troublesome. */
- rndis->port.is_zlp_ok = false;
-
/* RNDIS should be in the "RNDIS uninitialized" state,
* either never activated or after rndis_uninit().
*
@@ -713,30 +706,37 @@ static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
* very long time. We need another call to the link layer
* code -- gether_updown(...bool) maybe -- to do it right.
*/
- rndis->port.cdc_filter = 0;
+ rndis->cdc_filter = 0;
rndis->bam_port.cdev = cdev;
- rndis->bam_port.func = &rndis->port.func;
- rndis->bam_port.in = rndis->port.in_ep;
- rndis->bam_port.out = rndis->port.out_ep;
-
- if (bam_data_connect(&rndis->bam_port, rndis->xport,
- rndis->port_num, USB_FUNC_RNDIS))
+ rndis->bam_port.func = &rndis->func;
+ ipa_data_port_select(USB_IPA_FUNC_RNDIS);
+ usb_bam_type = usb_bam_get_bam_type(cdev->gadget->name);
+
+ src_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
+ IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE,
+ rndis->port_num);
+ dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
+ IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE,
+ rndis->port_num);
+ if (src_connection_idx < 0 || dst_connection_idx < 0) {
+ pr_err("%s: usb_bam_get_connection_idx failed\n",
+ __func__);
+ return ret;
+ }
+ if (ipa_data_connect(&rndis->bam_port, USB_IPA_FUNC_RNDIS,
+ src_connection_idx, dst_connection_idx))
goto fail;
DBG(cdev, "RNDIS RX/TX early activation ...\n");
- if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA) {
- net = gether_qc_connect_name(&rndis->port, "rndis0",
- false);
- } else {
- rndis_qc_open(&rndis->port);
- net = gether_qc_get_net("rndis0");
- }
+ rndis_qc_open(rndis);
+ net = rndis_qc_get_net("rndis0");
if (IS_ERR(net))
return PTR_ERR(net);
+ opts->net = net;
- rndis_set_param_dev(rndis->config, net,
- &rndis->port.cdc_filter);
+ rndis_set_param_dev(rndis->params, net,
+ &rndis->cdc_filter);
} else
goto fail;
@@ -753,18 +753,13 @@ static void rndis_qc_disable(struct usb_function *f)
if (!rndis->notify->driver_data)
return;
- pr_info("rndis deactivated\n");
+ DBG(cdev, "rndis deactivated\n");
- rndis_uninit(rndis->config);
- bam_data_disconnect(&rndis->bam_port, USB_FUNC_RNDIS, rndis->port_num);
- if (rndis->xport != USB_GADGET_XPORT_BAM2BAM_IPA)
- gether_qc_disconnect_name(&rndis->port, "rndis0");
+ rndis_uninit(rndis->params);
+ ipa_data_disconnect(&rndis->bam_port, USB_IPA_FUNC_RNDIS);
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA &&
- gadget_is_dwc3(cdev->gadget)) {
- msm_ep_unconfig(rndis->port.out_ep);
- msm_ep_unconfig(rndis->port.in_ep);
- }
+ msm_ep_unconfig(rndis->bam_port.out);
+ msm_ep_unconfig(rndis->bam_port.in);
usb_ep_disable(rndis->notify);
rndis->notify->driver_data = NULL;
}
@@ -789,11 +784,11 @@ static void rndis_qc_suspend(struct usb_function *f)
* host case. In case of windows, this RNDIS state machine is
* already updated due to receiving of PACKET_FILTER.
*/
- rndis_flow_control(rndis->config, true);
+ rndis_flow_control(rndis->params, true);
pr_debug("%s(): Disconnecting\n", __func__);
}
- bam_data_suspend(&rndis->bam_port, rndis->port_num, USB_FUNC_RNDIS,
+ ipa_data_suspend(&rndis->bam_port, USB_IPA_FUNC_RNDIS,
remote_wakeup_allowed);
pr_debug("rndis suspended\n");
}
@@ -816,12 +811,11 @@ static void rndis_qc_resume(struct usb_function *f)
else
remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
- bam_data_resume(&rndis->bam_port, rndis->port_num, USB_FUNC_RNDIS,
- remote_wakeup_allowed);
+ ipa_data_resume(&rndis->bam_port, USB_IPA_FUNC_RNDIS,
+ remote_wakeup_allowed);
if (!remote_wakeup_allowed) {
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA)
- rndis_qc_open(&rndis->port);
+ rndis_qc_open(rndis);
/*
* Linux Host doesn't sends RNDIS_MSG_INIT or non-zero value
* set with RNDIS_MESSAGE_PACKET_FILTER after performing bus
@@ -829,7 +823,7 @@ static void rndis_qc_resume(struct usb_function *f)
* explicitly here. For Windows host case is also being
* handle with RNDIS state machine.
*/
- rndis_flow_control(rndis->config, false);
+ rndis_flow_control(rndis->params, false);
}
pr_debug("%s: RNDIS resume completed\n", __func__);
@@ -844,26 +838,23 @@ static void rndis_qc_resume(struct usb_function *f)
* not used to tell whether the link should send packets or not.
*/
-static void rndis_qc_open(struct qc_gether *geth)
+static void rndis_qc_open(struct f_rndis_qc *rndis)
{
- struct f_rndis_qc *rndis = func_to_rndis_qc(&geth->func);
- struct usb_composite_dev *cdev = geth->func.config->cdev;
+ struct usb_composite_dev *cdev = rndis->func.config->cdev;
DBG(cdev, "%s\n", __func__);
- rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3,
+ rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3,
rndis_qc_bitrate(cdev->gadget) / 100);
- rndis_signal_connect(rndis->config);
+ rndis_signal_connect(rndis->params);
}
-static void rndis_qc_close(struct qc_gether *geth)
+void ipa_data_flow_control_enable(bool enable, struct rndis_params *param)
{
- struct f_rndis_qc *rndis = func_to_rndis_qc(&geth->func);
-
- DBG(geth->func.config->cdev, "%s\n", __func__);
-
- rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
- rndis_signal_disconnect(rndis->config);
+ if (enable)
+ ipa_data_stop_rndis_ipa(USB_IPA_FUNC_RNDIS);
+ else
+ ipa_data_start_rndis_ipa(USB_IPA_FUNC_RNDIS);
}
/*-------------------------------------------------------------------------*/
@@ -875,9 +866,35 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_rndis_qc *rndis = func_to_rndis_qc(f);
+ struct rndis_params *params;
int status;
struct usb_ep *ep;
+ /* maybe allocate device-global string IDs */
+ if (rndis_qc_string_defs[0].id == 0) {
+
+ /* control interface label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ rndis_qc_string_defs[0].id = status;
+ rndis_qc_control_intf.iInterface = status;
+
+ /* data interface label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ rndis_qc_string_defs[1].id = status;
+ rndis_qc_data_intf.iInterface = status;
+
+ /* IAD iFunction label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ rndis_qc_string_defs[2].id = status;
+ rndis_qc_iad_descriptor.iFunction = status;
+ }
+
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
@@ -902,13 +919,13 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f)
ep = usb_ep_autoconfig(cdev->gadget, &rndis_qc_fs_in_desc);
if (!ep)
goto fail;
- rndis->port.in_ep = ep;
+ rndis->bam_port.in = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &rndis_qc_fs_out_desc);
if (!ep)
goto fail;
- rndis->port.out_ep = ep;
+ rndis->bam_port.out = ep;
ep->driver_data = cdev; /* claim */
/* NOTE: a status/notification endpoint is, strictly speaking,
@@ -972,33 +989,30 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
}
- rndis->port.open = rndis_qc_open;
- rndis->port.close = rndis_qc_close;
-
- status = rndis_register(rndis_qc_response_available, rndis,
- bam_data_flow_control_enable);
- if (status < 0)
+ params = rndis_register(rndis_qc_response_available, rndis,
+ ipa_data_flow_control_enable);
+ if (params < 0)
goto fail;
- rndis->config = status;
+ rndis->params = params;
- rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
- rndis_set_host_mac(rndis->config, rndis->ethaddr);
+ rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
+ rndis_set_host_mac(rndis->params, rndis->ethaddr);
if (rndis->manufacturer && rndis->vendorID &&
- rndis_set_param_vendor(rndis->config, rndis->vendorID,
+ rndis_set_param_vendor(rndis->params, rndis->vendorID,
rndis->manufacturer))
goto fail;
pr_debug("%s(): max_pkt_per_xfer:%d\n", __func__,
rndis->ul_max_pkt_per_xfer);
- rndis_set_max_pkt_xfer(rndis->config, rndis->ul_max_pkt_per_xfer);
+ rndis_set_max_pkt_xfer(rndis->params, rndis->ul_max_pkt_per_xfer);
/* In case of aggregated packets QC device will request
* aliment to 4 (2^2).
*/
pr_debug("%s(): pkt_alignment_factor:%d\n", __func__,
rndis->pkt_alignment_factor);
- rndis_set_pkt_alignment_factor(rndis->config,
+ rndis_set_pkt_alignment_factor(rndis->params,
rndis->pkt_alignment_factor);
/* NOTE: all that is done without knowing or caring about
@@ -1009,7 +1023,7 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f)
DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
- rndis->port.in_ep->name, rndis->port.out_ep->name,
+ rndis->bam_port.in->name, rndis->bam_port.out->name,
rndis->notify->name);
return 0;
@@ -1029,24 +1043,31 @@ fail:
/* we might as well release our claims on endpoints */
if (rndis->notify)
rndis->notify->driver_data = NULL;
- if (rndis->port.out_ep->desc)
- rndis->port.out_ep->driver_data = NULL;
- if (rndis->port.in_ep->desc)
- rndis->port.in_ep->driver_data = NULL;
+ if (rndis->bam_port.out->desc)
+ rndis->bam_port.out->driver_data = NULL;
+ if (rndis->bam_port.in->desc)
+ rndis->bam_port.in->driver_data = NULL;
pr_err("%s: can't bind, err %d\n", f->name, status);
return status;
}
+static void rndis_qc_free(struct usb_function *f)
+{
+ struct f_rndis_qc_opts *opts;
+
+ opts = container_of(f->fi, struct f_rndis_qc_opts, func_inst);
+ opts->refcnt--;
+}
+
static void
rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_rndis_qc *rndis = func_to_rndis_qc(f);
- unsigned long flags;
pr_debug("rndis_qc_unbind: free\n");
- rndis_deregister(rndis->config);
+ rndis_deregister(rndis->params);
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
@@ -1055,23 +1076,17 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(rndis->notify_req->buf);
usb_ep_free_request(rndis->notify, rndis->notify_req);
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) {
- /*
- * call flush_workqueue to make sure that any pending
- * disconnect_work() from u_bam_data.c file is being
- * flushed before calling this rndis_ipa_cleanup API
- * as rndis ipa disconnect API is required to be
- * called before this.
- */
- bam_data_flush_workqueue();
- rndis_ipa_cleanup(rndis_ipa_params.private);
- rndis_ipa_supported = false;
- }
+ /*
+ * call flush_workqueue to make sure that any pending
+ * disconnect_work() from u_bam_data.c file is being
+ * flushed before calling this rndis_ipa_cleanup API
+ * as rndis ipa disconnect API is required to be
+ * called before this.
+ */
+ ipa_data_flush_workqueue();
+ rndis_ipa_cleanup(rndis_ipa_params.private);
+ rndis_ipa_supported = false;
- spin_lock_irqsave(&rndis_lock, flags);
- kfree(rndis);
- _rndis_qc = NULL;
- spin_unlock_irqrestore(&rndis_lock, flags);
}
void rndis_ipa_reset_trigger(void)
@@ -1099,7 +1114,6 @@ void rndis_net_ready_notify(void)
{
struct f_rndis_qc *rndis;
unsigned long flags;
- int port_num;
spin_lock_irqsave(&rndis_lock, flags);
rndis = _rndis_qc;
@@ -1117,19 +1131,7 @@ void rndis_net_ready_notify(void)
pr_debug("%s: Set net_ready_trigger", __func__);
rndis->net_ready_trigger = true;
spin_unlock_irqrestore(&rndis_lock, flags);
- port_num = (u_bam_data_func_to_port(USB_FUNC_RNDIS,
- RNDIS_QC_ACTIVE_PORT));
- if (port_num < 0)
- return;
- bam_data_start_rx_tx(port_num);
-}
-
-
-/* Some controllers can't support RNDIS ... */
-static inline bool can_support_rndis_qc(struct usb_configuration *c)
-{
- /* everything else is *presumably* fine */
- return true;
+ ipa_data_start_rx_tx(USB_IPA_FUNC_RNDIS);
}
/**
@@ -1144,84 +1146,42 @@ static inline bool can_support_rndis_qc(struct usb_configuration *c)
* Caller must have called @gether_setup(). Caller is also responsible
* for calling @gether_cleanup() before module unload.
*/
-int
-rndis_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
-{
- return rndis_qc_bind_config_vendor(c, ethaddr, 0, NULL, 1, 0, NULL);
-}
-int
-rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
- u32 vendorID, const char *manufacturer,
- u8 max_pkt_per_xfer,
- u8 pkt_alignment_factor,
- char *xport_name)
+static struct
+usb_function *rndis_qc_bind_config_vendor(struct usb_function_instance *fi,
+ u32 vendorID, const char *manufacturer,
+ u8 max_pkt_per_xfer, u8 pkt_alignment_factor)
{
+ struct f_rndis_qc_opts *opts = container_of(fi,
+ struct f_rndis_qc_opts, func_inst);
struct f_rndis_qc *rndis;
int status;
- if (!can_support_rndis_qc(c) || !ethaddr) {
- pr_debug("%s: invalid argument\n", __func__);
- return -EINVAL;
- }
-
- /* maybe allocate device-global string IDs */
- if (rndis_qc_string_defs[0].id == 0) {
-
- /* control interface label */
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- rndis_qc_string_defs[0].id = status;
- rndis_qc_control_intf.iInterface = status;
-
- /* data interface label */
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- rndis_qc_string_defs[1].id = status;
- rndis_qc_data_intf.iInterface = status;
-
- /* IAD iFunction label */
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- rndis_qc_string_defs[2].id = status;
- rndis_qc_iad_descriptor.iFunction = status;
- }
-
/* allocate and initialize one new instance */
status = -ENOMEM;
- rndis = kzalloc(sizeof(*rndis), GFP_KERNEL);
- if (!rndis) {
- pr_err("%s: fail allocate and initialize new instance\n",
- __func__);
- goto fail;
- }
- rndis->xport = str_to_xport(xport_name);
+ opts = container_of(fi, struct f_rndis_qc_opts, func_inst);
- /* export host's Ethernet address in CDC format */
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) {
- gether_qc_get_macs(rndis_ipa_params.device_ethaddr,
- rndis_ipa_params.host_ethaddr);
- pr_debug("setting host_ethaddr=%pM, device_ethaddr=%pM\n",
- rndis_ipa_params.host_ethaddr,
- rndis_ipa_params.device_ethaddr);
- rndis_ipa_supported = true;
- ether_addr_copy(rndis->ethaddr, &rndis_ipa_params.host_ethaddr);
- rndis_ipa_params.device_ready_notify = rndis_net_ready_notify;
- } else
- ether_addr_copy(rndis->ethaddr, ethaddr);
+ opts->refcnt++;
+ rndis = opts->rndis;
- rndis->vendorID = vendorID;
- rndis->manufacturer = manufacturer;
+ rndis->vendorID = opts->vendor_id;
+ rndis->manufacturer = opts->manufacturer;
+ /* export host's Ethernet address in CDC format */
+ random_ether_addr(rndis_ipa_params.host_ethaddr);
+ random_ether_addr(rndis_ipa_params.device_ethaddr);
+ pr_debug("setting host_ethaddr=%pM, device_ethaddr=%pM\n",
+ rndis_ipa_params.host_ethaddr,
+ rndis_ipa_params.device_ethaddr);
+ rndis_ipa_supported = true;
+ ether_addr_copy(rndis->ethaddr, rndis_ipa_params.host_ethaddr);
+ rndis_ipa_params.device_ready_notify = rndis_net_ready_notify;
/* if max_pkt_per_xfer was not configured set to default value */
rndis->ul_max_pkt_per_xfer =
max_pkt_per_xfer ? max_pkt_per_xfer :
DEFAULT_MAX_PKT_PER_XFER;
- u_bam_data_set_ul_max_pkt_num(rndis->ul_max_pkt_per_xfer);
+ ipa_data_set_ul_max_pkt_num(rndis->ul_max_pkt_per_xfer);
/*
* Check no RNDIS aggregation, and alignment if not mentioned,
@@ -1241,47 +1201,35 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
DEFAULT_PKT_ALIGNMENT_FACTOR;
/* RNDIS activates when the host changes this filter */
- rndis->port.cdc_filter = 0;
+ rndis->cdc_filter = 0;
- /* RNDIS has special (and complex) framing */
- rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
- rndis->port.wrap = rndis_qc_add_header;
- rndis->port.unwrap = rndis_qc_rm_hdr;
-
- rndis->port.func.name = "rndis";
- rndis->port.func.strings = rndis_qc_strings;
+ rndis->func.name = "rndis";
+ rndis->func.strings = rndis_qc_strings;
/* descriptors are per-instance copies */
- rndis->port.func.bind = rndis_qc_bind;
- rndis->port.func.unbind = rndis_qc_unbind;
- rndis->port.func.set_alt = rndis_qc_set_alt;
- rndis->port.func.setup = rndis_qc_setup;
- rndis->port.func.disable = rndis_qc_disable;
- rndis->port.func.suspend = rndis_qc_suspend;
- rndis->port.func.resume = rndis_qc_resume;
+ rndis->func.bind = rndis_qc_bind;
+ rndis->func.unbind = rndis_qc_unbind;
+ rndis->func.set_alt = rndis_qc_set_alt;
+ rndis->func.setup = rndis_qc_setup;
+ rndis->func.disable = rndis_qc_disable;
+ rndis->func.suspend = rndis_qc_suspend;
+ rndis->func.resume = rndis_qc_resume;
+ rndis->func.free_func = rndis_qc_free;
_rndis_qc = rndis;
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA) {
- status = rndis_ipa_init(&rndis_ipa_params);
- if (status) {
- pr_err("%s: failed to init rndis_ipa\n", __func__);
- goto fail;
- }
- }
-
- status = usb_add_function(c, &rndis->port.func);
+ status = rndis_ipa_init(&rndis_ipa_params);
if (status) {
- if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA)
- rndis_ipa_cleanup(rndis_ipa_params.private);
- goto fail;
+ pr_err("%s: failed to init rndis_ipa\n", __func__);
+ kfree(rndis);
+ return ERR_PTR(status);
}
- return 0;
+ return &rndis->func;
+}
-fail:
- kfree(rndis);
- _rndis_qc = NULL;
- return status;
+static struct usb_function *qcrndis_alloc(struct usb_function_instance *fi)
+{
+ return rndis_qc_bind_config_vendor(fi, 0, NULL, 1, 0);
}
static int rndis_qc_open_dev(struct inode *ip, struct file *fp)
@@ -1370,24 +1318,100 @@ static struct miscdevice rndis_qc_device = {
.fops = &rndis_qc_fops,
};
-static int rndis_qc_init(void)
+static void qcrndis_free_inst(struct usb_function_instance *f)
{
+ struct f_rndis_qc *rndis;
+ struct f_rndis_qc_opts *opts = container_of(f,
+ struct f_rndis_qc_opts, func_inst);
+ unsigned long flags;
+
+ rndis = opts->rndis;
+ misc_deregister(&rndis_qc_device);
+
+ ipa_data_free(USB_IPA_FUNC_RNDIS);
+ spin_lock_irqsave(&rndis_lock, flags);
+ kfree(rndis);
+ _rndis_qc = NULL;
+ kfree(opts->rndis);
+ kfree(opts);
+ spin_unlock_irqrestore(&rndis_lock, flags);
+}
+
+static int qcrndis_set_inst_name(struct usb_function_instance *fi,
+ const char *name)
+{
+ struct f_rndis_qc_opts *opts = container_of(fi,
+ struct f_rndis_qc_opts, func_inst);
+ struct f_rndis_qc *rndis;
+ int name_len;
int ret;
- pr_info("initialize rndis QC instance\n");
+ name_len = strlen(name) + 1;
+ if (name_len > MAX_INST_NAME_LEN)
+ return -ENAMETOOLONG;
+ pr_debug("initialize rndis QC instance\n");
+ rndis = kzalloc(sizeof(*rndis), GFP_KERNEL);
+ if (!rndis) {
+ pr_err("%s: fail allocate and initialize new instance\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ opts->rndis = rndis;
ret = misc_register(&rndis_qc_device);
if (ret)
pr_err("rndis QC driver failed to register\n");
spin_lock_init(&rndis_lock);
- ret = bam_data_setup(USB_FUNC_RNDIS, RNDIS_QC_NO_PORTS);
+ ret = ipa_data_setup(USB_IPA_FUNC_RNDIS);
if (ret) {
pr_err("bam_data_setup failed err: %d\n", ret);
+ kfree(rndis);
return ret;
}
- return ret;
+ return 0;
+}
+
+static inline
+struct f_rndis_qc_opts *to_f_qc_rndis_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_rndis_qc_opts,
+ func_inst.group);
+}
+
+static void qcrndis_attr_release(struct config_item *item)
+{
+ struct f_rndis_qc_opts *opts = to_f_qc_rndis_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations qcrndis_item_ops = {
+ .release = qcrndis_attr_release,
+};
+
+static struct config_item_type qcrndis_func_type = {
+ .ct_item_ops = &qcrndis_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct usb_function_instance *qcrndis_alloc_inst(void)
+{
+ struct f_rndis_qc_opts *opts;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+
+ opts->func_inst.set_inst_name = qcrndis_set_inst_name;
+ opts->func_inst.free_func_inst = qcrndis_free_inst;
+
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &qcrndis_func_type);
+
+ return &opts->func_inst;
}
static void rndis_qc_cleanup(void)
@@ -1416,3 +1440,27 @@ bool rndis_qc_get_skip_ep_config(void)
{
return rndis_ipa_params.skip_ep_cfg;
}
+
+DECLARE_USB_FUNCTION_INIT(qcrndis, qcrndis_alloc_inst, qcrndis_alloc);
+
+static int __init usb_qcrndis_init(void)
+{
+ int ret;
+
+ ret = usb_function_register(&qcrndisusb_func);
+ if (ret) {
+ pr_err("%s: failed to register diag %d\n", __func__, ret);
+ return ret;
+ }
+ return ret;
+}
+
+static void __exit usb_qcrndis_exit(void)
+{
+ usb_function_unregister(&qcrndisusb_func);
+ rndis_qc_cleanup();
+}
+
+module_init(usb_qcrndis_init);
+module_exit(usb_qcrndis_exit);
+MODULE_DESCRIPTION("USB RMNET Function Driver");
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index b0e7b65b84bd..98ac1ff58323 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -596,6 +596,7 @@ static int rndis_init_response(struct rndis_params *params,
resp->AFListOffset = cpu_to_le32(0);
resp->AFListSize = cpu_to_le32(0);
+ params->ul_max_xfer_size = le32_to_cpu(resp->MaxTransferSize);
params->resp_avail(params->v);
return 0;
}
@@ -799,7 +800,7 @@ EXPORT_SYMBOL_GPL(rndis_set_host_mac);
*/
int rndis_msg_parser(struct rndis_params *params, u8 *buf)
{
- u32 MsgType, MsgLength;
+ u32 MsgType, MsgLength, major, minor, max_transfer_size;
__le32 *tmp;
if (!buf)
@@ -822,6 +823,19 @@ int rndis_msg_parser(struct rndis_params *params, u8 *buf)
case RNDIS_MSG_INIT:
pr_debug("%s: RNDIS_MSG_INIT\n",
__func__);
+ major = get_unaligned_le32(tmp++);
+ minor = get_unaligned_le32(tmp++);
+ max_transfer_size = get_unaligned_le32(tmp++);
+
+ params->host_rndis_major_ver = major;
+ params->host_rndis_minor_ver = minor;
+ params->dl_max_xfer_size = max_transfer_size;
+
+ pr_debug("%s(): RNDIS Host Major:%d Minor:%d version\n",
+ __func__, major, minor);
+ pr_debug("%s(): UL Max Transfer size:%x\n", __func__,
+ max_transfer_size);
+
params->state = RNDIS_INITIALIZED;
return rndis_init_response(params, (rndis_init_msg_type *)buf);
@@ -1013,6 +1027,18 @@ int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed)
}
EXPORT_SYMBOL_GPL(rndis_set_param_medium);
+u32 rndis_get_dl_max_xfer_size(struct rndis_params *params)
+{
+ pr_debug("%s:\n", __func__);
+ return params->dl_max_xfer_size;
+}
+
+u32 rndis_get_ul_max_xfer_size(struct rndis_params *params)
+{
+ pr_debug("%s:\n", __func__);
+ return params->ul_max_xfer_size;
+}
+
void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer)
{
pr_debug("%s:\n", __func__);
diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h
index 939c3bebe015..3d130b0576fc 100644
--- a/drivers/usb/gadget/function/rndis.h
+++ b/drivers/usb/gadget/function/rndis.h
@@ -199,6 +199,10 @@ typedef struct rndis_params
void *v;
struct list_head resp_queue;
+ u32 host_rndis_major_ver;
+ u32 host_rndis_minor_ver;
+ u32 ul_max_xfer_size;
+ u32 dl_max_xfer_size;
} rndis_params;
/* RNDIS Message parser and other useless functions */
@@ -213,6 +217,8 @@ int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
int rndis_set_param_medium(struct rndis_params *params, u32 medium,
u32 speed);
void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer);
+u32 rndis_get_ul_max_xfer_size(struct rndis_params *params);
+u32 rndis_get_dl_max_xfer_size(struct rndis_params *params);
void rndis_add_hdr(struct sk_buff *skb);
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
struct sk_buff_head *list);
diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c
index 3a5b1e2da2e6..56e7dea427ec 100644
--- a/drivers/usb/gadget/function/u_data_ipa.c
+++ b/drivers/usb/gadget/function/u_data_ipa.c
@@ -22,37 +22,49 @@
#include <linux/termios.h>
#include <linux/usb_bam.h>
-#include "usb_gadget_xport.h"
+#include "u_data_ipa.h"
-#define IPA_N_PORTS 4
struct ipa_data_ch_info {
- struct usb_request *rx_req;
- struct usb_request *tx_req;
- unsigned long flags;
- unsigned id;
- enum transport_type trans;
- enum gadget_type gtype;
- bool is_connected;
- unsigned port_num;
- spinlock_t port_lock;
-
- struct work_struct connect_w;
- struct work_struct disconnect_w;
- struct work_struct suspend_w;
- struct work_struct resume_w;
-
- u32 src_pipe_idx;
- u32 dst_pipe_idx;
- u8 src_connection_idx;
- u8 dst_connection_idx;
- enum usb_ctrl usb_bam_type;
- struct gadget_ipa_port *port_usb;
+ struct usb_request *rx_req;
+ struct usb_request *tx_req;
+ unsigned long flags;
+ unsigned id;
+ enum ipa_func_type func_type;
+ bool is_connected;
+ unsigned port_num;
+ spinlock_t port_lock;
+
+ struct work_struct connect_w;
+ struct work_struct disconnect_w;
+ struct work_struct suspend_w;
+ struct work_struct resume_w;
+
+ u32 src_pipe_idx;
+ u32 dst_pipe_idx;
+ u8 src_connection_idx;
+ u8 dst_connection_idx;
+ enum usb_ctrl usb_bam_type;
+ struct gadget_ipa_port *port_usb;
+ struct usb_gadget *gadget;
+ atomic_t pipe_connect_notified;
struct usb_bam_connect_ipa_params ipa_params;
};
-static int n_ipa_ports;
+struct rndis_data_ch_info {
+ /* this provides downlink (device->host i.e host) side configuration*/
+ u32 dl_max_transfer_size;
+ /* this provides uplink (host->device i.e device) side configuration */
+ u32 ul_max_transfer_size;
+ u32 ul_max_packets_number;
+ bool ul_aggregation_enable;
+ u32 prod_clnt_hdl;
+ u32 cons_clnt_hdl;
+ void *priv;
+};
+
static struct workqueue_struct *ipa_data_wq;
struct ipa_data_ch_info *ipa_data_ports[IPA_N_PORTS];
+static struct rndis_data_ch_info *rndis_data;
/**
* ipa_data_endless_complete() - completion callback for endless TX/RX request
* @ep: USB endpoint for which this completion happen
@@ -132,6 +144,56 @@ static void ipa_data_stop_endless_xfer(struct ipa_data_ch_info *port, bool in)
}
}
+/*
+ * Called when IPA triggers us that the network interface is up.
+ * Starts the transfers on bulk endpoints.
+ * (optimization reasons, the pipes and bam with IPA are already connected)
+ */
+void ipa_data_start_rx_tx(enum ipa_func_type func)
+{
+ struct ipa_data_ch_info *port;
+ unsigned long flags;
+
+ pr_debug("%s: Triggered: starting tx, rx", __func__);
+ /* queue in & out requests */
+ port = ipa_data_ports[func];
+ if (!port) {
+ pr_err("%s: port is NULL, can't start tx, rx", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ if (!port->port_usb || !port->port_usb->in ||
+ !port->port_usb->out) {
+ pr_err("%s: Can't start tx, rx, ep not enabled", __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ if (!port->rx_req || !port->tx_req) {
+ pr_err("%s: No request d->rx_req=%p, d->tx_req=%p", __func__,
+ port->rx_req, port->tx_req);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+ if (!port->is_connected) {
+ pr_debug("%s: pipes are disconnected", __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ /* queue in & out requests */
+ pr_debug("%s: Starting rx", __func__);
+ if (port->port_usb->out)
+ ipa_data_start_endless_xfer(port, false);
+
+ pr_debug("%s: Starting tx", __func__);
+ if (port->port_usb->in)
+ ipa_data_start_endless_xfer(port, true);
+}
/**
* ipa_data_disconnect_work() - Perform USB IPA BAM disconnect
* @w: disconnect work
@@ -166,6 +228,23 @@ static void ipa_data_disconnect_work(struct work_struct *w)
if (ret)
pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret);
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ /*
+ * NOTE: it is required to disconnect USB and IPA BAM related
+ * pipes before calling IPA tethered function related disconnect
+ * API. IPA tethered function related disconnect API delete
+ * depedency graph with IPA RM which would results into IPA not
+ * pulling data although there is pending data on USB BAM
+ * producer pipe.
+ */
+ if (atomic_xchg(&port->pipe_connect_notified, 0) == 1) {
+ void *priv;
+
+ priv = rndis_qc_get_ipa_priv();
+ rndis_ipa_pipe_disconnect_notify(priv);
+ }
+ }
+
if (port->ipa_params.prod_clnt_hdl)
usb_bam_free_fifos(port->usb_bam_type,
port->dst_connection_idx);
@@ -173,6 +252,12 @@ static void ipa_data_disconnect_work(struct work_struct *w)
usb_bam_free_fifos(port->usb_bam_type,
port->src_connection_idx);
+ /*
+ * Decrement usage count which was incremented
+ * upon cable connect or cable disconnect in suspended state.
+ */
+ usb_gadget_autopm_put_async(port->gadget);
+
pr_debug("%s(): disconnect work completed.\n", __func__);
}
@@ -186,15 +271,15 @@ static void ipa_data_disconnect_work(struct work_struct *w)
* switch is being trigger. This API performs restoring USB endpoint operation
* and disable USB endpoint used for accelerated path.
*/
-void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
+void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func)
{
struct ipa_data_ch_info *port;
unsigned long flags;
struct usb_gadget *gadget = NULL;
- pr_debug("dev:%p port number:%d\n", gp, port_num);
- if (port_num >= n_ipa_ports) {
- pr_err("invalid ipa portno#%d\n", port_num);
+ pr_debug("dev:%p port number:%d\n", gp, func);
+ if (func >= USB_IPA_NUM_FUNCS) {
+ pr_err("invalid ipa portno#%d\n", func);
return;
}
@@ -203,9 +288,9 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
return;
}
- port = ipa_data_ports[port_num];
+ port = ipa_data_ports[func];
if (!port) {
- pr_err("port %u is NULL", port_num);
+ pr_err("port %u is NULL", func);
return;
}
@@ -223,8 +308,7 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
* complete function will be called, where we try
* to obtain the spinlock as well.
*/
- if (gadget_is_dwc3(gadget))
- msm_ep_unconfig(port->port_usb->in);
+ msm_ep_unconfig(port->port_usb->in);
spin_unlock_irqrestore(&port->port_lock, flags);
usb_ep_disable(port->port_usb->in);
spin_lock_irqsave(&port->port_lock, flags);
@@ -232,8 +316,7 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
}
if (port->port_usb->out) {
- if (gadget_is_dwc3(gadget))
- msm_ep_unconfig(port->port_usb->out);
+ msm_ep_unconfig(port->port_usb->out);
spin_unlock_irqrestore(&port->port_lock, flags);
usb_ep_disable(port->port_usb->out);
spin_lock_irqsave(&port->port_lock, flags);
@@ -257,14 +340,14 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num)
*/
static void configure_fifo(enum usb_ctrl bam_type, u8 idx, struct usb_ep *ep)
{
- struct u_bam_data_connect_info bam_info;
struct sps_mem_buffer data_fifo = {0};
+ u32 usb_bam_pipe_idx;
get_bam2bam_connection_info(bam_type, idx,
- &bam_info.usb_bam_pipe_idx,
+ &usb_bam_pipe_idx,
NULL, &data_fifo, NULL);
msm_data_fifo_config(ep, data_fifo.phys_base, data_fifo.size,
- bam_info.usb_bam_pipe_idx);
+ usb_bam_pipe_idx);
}
/**
@@ -308,8 +391,21 @@ static void ipa_data_connect_work(struct work_struct *w)
return;
}
+ /*
+ * check if connect_w got called two times during RNDIS resume as
+ * explicit flow control is called to start data transfers after
+ * ipa_data_connect()
+ */
+ if (port->is_connected) {
+ pr_debug("IPA connect is already done & Transfers started\n");
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ usb_gadget_autopm_put_async(port->gadget);
+ return;
+ }
+
gport->ipa_consumer_ep = -1;
gport->ipa_producer_ep = -1;
+
if (gport->out) {
port->rx_req = usb_ep_alloc_request(gport->out, GFP_ATOMIC);
if (!port->rx_req) {
@@ -341,8 +437,7 @@ static void ipa_data_connect_work(struct work_struct *w)
/* update IPA Parameteres here. */
port->ipa_params.usb_connection_speed = gadget->speed;
- if (gadget_is_dwc3(gadget))
- port->ipa_params.reset_pipe_after_lpm =
+ port->ipa_params.reset_pipe_after_lpm =
msm_dwc3_reset_ep_after_lpm(gadget);
port->ipa_params.skip_ep_cfg = true;
port->ipa_params.keep_ipa_awake = true;
@@ -354,49 +449,35 @@ static void ipa_data_connect_work(struct work_struct *w)
usb_bam_alloc_fifos(port->usb_bam_type,
port->src_connection_idx);
- if (gadget_is_dwc3(gadget)) {
- sps_params = MSM_SPS_MODE | MSM_DISABLE_WB
- | MSM_PRODUCER | port->src_pipe_idx;
- port->rx_req->length = 32*1024;
- port->rx_req->udc_priv = sps_params;
- configure_fifo(port->usb_bam_type,
- port->src_connection_idx,
- port->port_usb->out);
- ret = msm_ep_config(gport->out, port->rx_req,
- GFP_ATOMIC);
- if (ret) {
- pr_err("msm_ep_config() failed for OUT EP\n");
- usb_bam_free_fifos(port->usb_bam_type,
- port->src_connection_idx);
- goto free_rx_tx_req;
- }
- } else {
- sps_params = (MSM_SPS_MODE | port->src_pipe_idx |
- MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
- port->rx_req->udc_priv = sps_params;
+ sps_params = MSM_SPS_MODE | MSM_DISABLE_WB
+ | MSM_PRODUCER | port->src_pipe_idx;
+ port->rx_req->length = 32*1024;
+ port->rx_req->udc_priv = sps_params;
+ configure_fifo(port->usb_bam_type,
+ port->src_connection_idx,
+ port->port_usb->out);
+ ret = msm_ep_config(gport->out);
+ if (ret) {
+ pr_err("msm_ep_config() failed for OUT EP\n");
+ usb_bam_free_fifos(port->usb_bam_type,
+ port->src_connection_idx);
+ goto free_rx_tx_req;
}
}
if (gport->in) {
usb_bam_alloc_fifos(port->usb_bam_type,
port->dst_connection_idx);
- if (gadget_is_dwc3(gadget)) {
- sps_params = MSM_SPS_MODE | MSM_DISABLE_WB |
- port->dst_pipe_idx;
- port->tx_req->length = 32*1024;
- port->tx_req->udc_priv = sps_params;
- configure_fifo(port->usb_bam_type,
- port->dst_connection_idx, gport->in);
- ret = msm_ep_config(gport->in, port->tx_req,
- GFP_ATOMIC);
- if (ret) {
- pr_err("msm_ep_config() failed for IN EP\n");
- goto unconfig_msm_ep_out;
- }
- } else {
- sps_params = (MSM_SPS_MODE | port->dst_pipe_idx |
- MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
- port->tx_req->udc_priv = sps_params;
+ sps_params = MSM_SPS_MODE | MSM_DISABLE_WB |
+ port->dst_pipe_idx;
+ port->tx_req->length = 32*1024;
+ port->tx_req->udc_priv = sps_params;
+ configure_fifo(port->usb_bam_type,
+ port->dst_connection_idx, gport->in);
+ ret = msm_ep_config(gport->in);
+ if (ret) {
+ pr_err("msm_ep_config() failed for IN EP\n");
+ goto unconfig_msm_ep_out;
}
}
@@ -410,13 +491,20 @@ static void ipa_data_connect_work(struct work_struct *w)
if (gport->out) {
pr_debug("configure bam ipa connect for USB OUT\n");
port->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
+
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ port->ipa_params.notify = rndis_qc_get_ipa_rx_cb();
+ port->ipa_params.priv = rndis_qc_get_ipa_priv();
+ port->ipa_params.skip_ep_cfg =
+ rndis_qc_get_skip_ep_config();
+ }
+
ret = usb_bam_connect_ipa(port->usb_bam_type,
&port->ipa_params);
if (ret) {
pr_err("usb_bam_connect_ipa out failed err:%d\n", ret);
goto unconfig_msm_ep_in;
}
- gadget->bam2bam_func_enabled = true;
gport->ipa_consumer_ep = port->ipa_params.ipa_cons_ep_idx;
is_ipa_disconnected = false;
@@ -425,30 +513,71 @@ static void ipa_data_connect_work(struct work_struct *w)
if (gport->in) {
pr_debug("configure bam ipa connect for USB IN\n");
port->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
- port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS;
+
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ port->ipa_params.notify = rndis_qc_get_ipa_tx_cb();
+ port->ipa_params.priv = rndis_qc_get_ipa_priv();
+ port->ipa_params.skip_ep_cfg =
+ rndis_qc_get_skip_ep_config();
+ }
+
+ if (port->func_type == USB_IPA_FUNC_DPL)
+ port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS;
ret = usb_bam_connect_ipa(port->usb_bam_type,
&port->ipa_params);
if (ret) {
pr_err("usb_bam_connect_ipa IN failed err:%d\n", ret);
goto disconnect_usb_bam_ipa_out;
}
- gadget->bam2bam_func_enabled = true;
gport->ipa_producer_ep = port->ipa_params.ipa_prod_ep_idx;
is_ipa_disconnected = false;
}
- pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n",
- gport->ipa_producer_ep,
- gport->ipa_consumer_ep);
+ /* For DPL need to update_ipa_pipes to qti */
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ rndis_data->prod_clnt_hdl =
+ port->ipa_params.prod_clnt_hdl;
+ rndis_data->cons_clnt_hdl =
+ port->ipa_params.cons_clnt_hdl;
+ rndis_data->priv = port->ipa_params.priv;
+
+ pr_debug("ul_max_transfer_size:%d\n",
+ rndis_data->ul_max_transfer_size);
+ pr_debug("ul_max_packets_number:%d\n",
+ rndis_data->ul_max_packets_number);
+ pr_debug("dl_max_transfer_size:%d\n",
+ rndis_data->dl_max_transfer_size);
+
+ ret = rndis_ipa_pipe_connect_notify(
+ rndis_data->cons_clnt_hdl,
+ rndis_data->prod_clnt_hdl,
+ rndis_data->ul_max_transfer_size,
+ rndis_data->ul_max_packets_number,
+ rndis_data->dl_max_transfer_size,
+ rndis_data->priv);
+ if (ret) {
+ pr_err("%s: failed to connect IPA: err:%d\n",
+ __func__, ret);
+ return;
+ }
+ atomic_set(&port->pipe_connect_notified, 1);
+ }
- gqti_ctrl_update_ipa_pipes(NULL, DPL_QTI_CTRL_PORT_NO,
+ pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n",
gport->ipa_producer_ep,
gport->ipa_consumer_ep);
pr_debug("src_bam_idx:%d dst_bam_idx:%d\n",
port->src_connection_idx, port->dst_connection_idx);
+ /* Don't queue the transfers yet, only after network stack is up */
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ pr_debug("%s: Not starting now, waiting for network notify",
+ __func__);
+ return;
+ }
+
if (gport->out)
ipa_data_start_endless_xfer(port, false);
if (gport->in)
@@ -496,7 +625,7 @@ free_rx_req:
* initiate USB BAM IPA connection. This API is enabling accelerated endpoints
* and schedule connect_work() which establishes USB IPA BAM communication.
*/
-int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num,
+int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func,
u8 src_connection_idx, u8 dst_connection_idx)
{
struct ipa_data_ch_info *port;
@@ -504,10 +633,10 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num,
int ret;
pr_debug("dev:%p port#%d src_connection_idx:%d dst_connection_idx:%d\n",
- gp, port_num, src_connection_idx, dst_connection_idx);
+ gp, func, src_connection_idx, dst_connection_idx);
- if (port_num >= n_ipa_ports) {
- pr_err("invalid portno#%d\n", port_num);
+ if (func >= USB_IPA_NUM_FUNCS) {
+ pr_err("invalid portno#%d\n", func);
ret = -ENODEV;
goto err;
}
@@ -518,10 +647,11 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num,
goto err;
}
- port = ipa_data_ports[port_num];
+ port = ipa_data_ports[func];
spin_lock_irqsave(&port->port_lock, flags);
port->port_usb = gp;
+ port->gadget = gp->cdev->gadget;
port->src_connection_idx = src_connection_idx;
port->dst_connection_idx = dst_connection_idx;
port->usb_bam_type = usb_bam_get_bam_type(gp->cdev->gadget->name);
@@ -565,6 +695,19 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num,
goto err_usb_in;
}
+ /* Wait for host to enable flow_control */
+ if (port->func_type == USB_IPA_FUNC_RNDIS) {
+ ret = 0;
+ goto err_usb_in;
+ }
+
+ /*
+ * Increment usage count upon cable connect. Decrement after IPA
+ * handshake is done in disconnect work (due to cable disconnect)
+ * or in suspend work.
+ */
+ usb_gadget_autopm_get_noresume(port->gadget);
+
queue_work(ipa_data_wq, &port->connect_w);
spin_unlock_irqrestore(&port->port_lock, flags);
@@ -642,6 +785,12 @@ static void ipa_data_stop(void *param, enum usb_bam_pipe_dir dir)
}
}
+void ipa_data_flush_workqueue(void)
+{
+ pr_debug("%s(): Flushing workqueue\n", __func__);
+ flush_workqueue(ipa_data_wq);
+}
+
/**
* ipa_data_suspend() - Initiate USB BAM IPA suspend functionality
* @gp: Gadget IPA port
@@ -650,15 +799,14 @@ static void ipa_data_stop(void *param, enum usb_bam_pipe_dir dir)
* It is being used to initiate USB BAM IPA suspend functionality
* for USB bus suspend functionality.
*/
-void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num)
+void ipa_data_suspend(struct gadget_ipa_port *gp, enum ipa_func_type func,
+ bool remote_wakeup_enabled)
{
struct ipa_data_ch_info *port;
- int ret;
-
- pr_debug("dev:%p port number:%d\n", gp, port_num);
+ unsigned long flags;
- if (port_num >= n_ipa_ports) {
- pr_err("invalid ipa portno#%d\n", port_num);
+ if (func >= USB_IPA_NUM_FUNCS) {
+ pr_err("invalid ipa portno#%d\n", func);
return;
}
@@ -666,14 +814,61 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num)
pr_err("data port is null\n");
return;
}
+ pr_debug("%s: suspended port %d\n", __func__, func);
- port = ipa_data_ports[port_num];
+ port = ipa_data_ports[func];
if (!port) {
- pr_err("port %u is NULL", port_num);
+ pr_err("%s(): Port is NULL.\n", __func__);
+ return;
+ }
+
+ /* suspend with remote wakeup disabled */
+ if (!remote_wakeup_enabled) {
+ /*
+ * When remote wakeup is disabled, IPA BAM is disconnected
+ * because it cannot send new data until the USB bus is resumed.
+ * Endpoint descriptors info is saved before it gets reset by
+ * the BAM disconnect API. This lets us restore this info when
+ * the USB bus is resumed.
+ */
+ gp->in_ep_desc_backup = gp->in->desc;
+ gp->out_ep_desc_backup = gp->out->desc;
+
+ pr_debug("in_ep_desc_backup = %p, out_ep_desc_backup = %p",
+ gp->in_ep_desc_backup,
+ gp->out_ep_desc_backup);
+
+ ipa_data_disconnect(gp, func);
return;
}
+ spin_lock_irqsave(&port->port_lock, flags);
+ queue_work(ipa_data_wq, &port->suspend_w);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+static void bam2bam_data_suspend_work(struct work_struct *w)
+{
+ struct ipa_data_ch_info *port = container_of(w, struct ipa_data_ch_info,
+ connect_w);
+ unsigned long flags;
+ int ret;
+
pr_debug("%s: suspend started\n", __func__);
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ /* In case of RNDIS, host enables flow_control invoking connect_w. If it
+ * is delayed then we may end up having suspend_w run before connect_w.
+ * In this scenario, connect_w may or may not at all start if cable gets
+ * disconnected or if host changes configuration e.g. RNDIS --> MBIM
+ * For these cases don't do runtime_put as there was no _get yet, and
+ * detect this condition on disconnect to not do extra pm_runtme_get
+ * for SUSPEND --> DISCONNECT scenario.
+ */
+ if (!port->is_connected) {
+ pr_err("%s: Not yet connected. SUSPEND pending.\n", __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
ret = usb_bam_register_wake_cb(port->usb_bam_type,
port->dst_connection_idx, NULL, port);
if (ret) {
@@ -685,7 +880,23 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num)
usb_bam_register_start_stop_cbs(port->usb_bam_type,
port->dst_connection_idx, ipa_data_start,
ipa_data_stop, port);
+ /*
+ * release lock here because bam_data_start() or
+ * bam_data_stop() called from usb_bam_suspend()
+ * re-acquires port lock.
+ */
+ spin_unlock_irqrestore(&port->port_lock, flags);
usb_bam_suspend(port->usb_bam_type, &port->ipa_params);
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ /*
+ * Decrement usage count after IPA handshake is done
+ * to allow gadget parent to go to lpm. This counter was
+ * incremented upon cable connect.
+ */
+ usb_gadget_autopm_put_async(port->gadget);
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
}
/**
@@ -696,17 +907,20 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num)
* It is being used to initiate USB resume functionality
* for USB bus resume case.
*/
-void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num)
+void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func,
+ bool remote_wakeup_enabled)
{
struct ipa_data_ch_info *port;
unsigned long flags;
struct usb_gadget *gadget = NULL;
- int ret;
+ u8 src_connection_idx;
+ u8 dst_connection_idx;
+ enum usb_ctrl usb_bam_type;
- pr_debug("dev:%p port number:%d\n", gp, port_num);
+ pr_debug("dev:%p port number:%d\n", gp, func);
- if (port_num >= n_ipa_ports) {
- pr_err("invalid ipa portno#%d\n", port_num);
+ if (func >= USB_IPA_NUM_FUNCS) {
+ pr_err("invalid ipa portno#%d\n", func);
return;
}
@@ -715,12 +929,66 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num)
return;
}
- port = ipa_data_ports[port_num];
+ port = ipa_data_ports[func];
if (!port) {
- pr_err("port %u is NULL", port_num);
+ pr_err("port %u is NULL", func);
+ return;
+ }
+
+ gadget = gp->cdev->gadget;
+ /* resume with remote wakeup disabled */
+ if (!remote_wakeup_enabled) {
+ /* Restore endpoint descriptors info. */
+ gp->in->desc = gp->in_ep_desc_backup;
+ gp->out->desc = gp->out_ep_desc_backup;
+
+ pr_debug("in_ep_desc_backup = %p, out_ep_desc_backup = %p",
+ gp->in_ep_desc_backup,
+ gp->out_ep_desc_backup);
+ usb_bam_type = usb_bam_get_bam_type(gadget->name);
+ src_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
+ IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE,
+ 0);
+ dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
+ IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE,
+ 0);
+ ipa_data_connect(gp, func,
+ src_connection_idx, dst_connection_idx);
return;
}
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ /*
+ * Increment usage count here to disallow gadget
+ * parent suspend. This counter will decrement
+ * after IPA handshake is done in disconnect work
+ * (due to cable disconnect) or in bam_data_disconnect
+ * in suspended state.
+ */
+ usb_gadget_autopm_get_noresume(port->gadget);
+ queue_work(ipa_data_wq, &port->resume_w);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void bam2bam_data_resume_work(struct work_struct *w)
+{
+ struct ipa_data_ch_info *port = container_of(w, struct ipa_data_ch_info,
+ connect_w);
+ struct usb_gadget *gadget;
+ unsigned long flags;
+ int ret;
+
+ if (!port->port_usb->cdev) {
+ pr_err("!port->port_usb->cdev is NULL");
+ goto exit;
+ }
+
+ if (!port->port_usb->cdev->gadget) {
+ pr_err("!port->port_usb->cdev->gadget is NULL");
+ goto exit;
+ }
+
pr_debug("%s: resume started\n", __func__);
spin_lock_irqsave(&port->port_lock, flags);
gadget = port->port_usb->cdev->gadget;
@@ -750,6 +1018,7 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num)
usb_bam_resume(port->usb_bam_type, &port->ipa_params);
}
+exit:
spin_unlock_irqrestore(&port->port_lock, flags);
}
@@ -762,12 +1031,12 @@ void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num)
*
* Retrun: 0 in case of success, otherwise errno.
*/
-static int ipa_data_port_alloc(int portno)
+static int ipa_data_port_alloc(enum ipa_func_type func)
{
struct ipa_data_ch_info *port = NULL;
- if (ipa_data_ports[portno] != NULL) {
- pr_debug("port %d already allocated.\n", portno);
+ if (ipa_data_ports[func] != NULL) {
+ pr_debug("port %d already allocated.\n", func);
return 0;
}
@@ -775,29 +1044,29 @@ static int ipa_data_port_alloc(int portno)
if (!port)
return -ENOMEM;
- ipa_data_ports[portno] = port;
+ ipa_data_ports[func] = port;
- pr_debug("port:%p with portno:%d allocated\n", port, portno);
+ pr_debug("port:%p with portno:%d allocated\n", port, func);
return 0;
}
/**
* ipa_data_port_select() - Select particular port for BAM2BAM IPA mode
* @portno: port number to be used by particular USB function
- * @gtype: USB gadget function type
+ * @func_type: USB gadget function type
*
* It is being used by USB function driver to select which BAM2BAM IPA
* port particular USB function wants to use.
*
*/
-void ipa_data_port_select(int portno, enum gadget_type gtype)
+void ipa_data_port_select(enum ipa_func_type func)
{
struct ipa_data_ch_info *port = NULL;
- pr_debug("portno:%d\n", portno);
+ pr_debug("portno:%d\n", func);
- port = ipa_data_ports[portno];
- port->port_num = portno;
+ port = ipa_data_ports[func];
+ port->port_num = func;
port->is_connected = false;
spin_lock_init(&port->port_lock);
@@ -808,14 +1077,30 @@ void ipa_data_port_select(int portno, enum gadget_type gtype)
if (!work_pending(&port->disconnect_w))
INIT_WORK(&port->disconnect_w, ipa_data_disconnect_work);
+ INIT_WORK(&port->suspend_w, bam2bam_data_suspend_work);
+ INIT_WORK(&port->resume_w, bam2bam_data_resume_work);
+
port->ipa_params.src_client = IPA_CLIENT_USB_PROD;
port->ipa_params.dst_client = IPA_CLIENT_USB_CONS;
- port->gtype = gtype;
+ port->func_type = func;
};
+void ipa_data_free(enum ipa_func_type func)
+{
+ pr_debug("freeing %d IPA BAM port", func);
+
+ kfree(ipa_data_ports[func]);
+ ipa_data_ports[func] = NULL;
+ if (func == USB_IPA_FUNC_RNDIS)
+ kfree(rndis_data);
+ if (ipa_data_wq) {
+ destroy_workqueue(ipa_data_wq);
+ ipa_data_wq = NULL;
+ }
+}
+
/**
* ipa_data_setup() - setup BAM2BAM IPA port
- * @no_ipa_port: total number of BAM2BAM IPA port to support
*
* Each USB function who wants to use BAM2BAM IPA port would
* be counting number of IPA port to use and initialize those
@@ -823,32 +1108,34 @@ void ipa_data_port_select(int portno, enum gadget_type gtype)
*
* Retrun: 0 in case of success, otherwise errno.
*/
-int ipa_data_setup(unsigned int no_ipa_port)
+int ipa_data_setup(enum ipa_func_type func)
{
- int i, ret;
+ int ret;
- pr_debug("requested %d IPA BAM ports", no_ipa_port);
+ pr_debug("requested %d IPA BAM port", func);
- if (!no_ipa_port || no_ipa_port > IPA_N_PORTS) {
- pr_err("Invalid num of ports count:%d\n", no_ipa_port);
+ if (func >= USB_IPA_NUM_FUNCS) {
+ pr_err("Invalid num of ports count:%d\n", func);
return -EINVAL;
}
- for (i = 0; i < no_ipa_port; i++) {
- n_ipa_ports++;
- ret = ipa_data_port_alloc(i);
- if (ret) {
- n_ipa_ports--;
- pr_err("Failed to alloc port:%d\n", i);
+ ret = ipa_data_port_alloc(func);
+ if (ret) {
+ pr_err("Failed to alloc port:%d\n", func);
+ return ret;
+ }
+
+ if (func == USB_IPA_FUNC_RNDIS) {
+ rndis_data = kzalloc(sizeof(*rndis_data), GFP_KERNEL);
+ if (!rndis_data) {
+ pr_err("%s: fail allocate and initialize new instance\n",
+ __func__);
goto free_ipa_ports;
}
}
-
- pr_debug("n_ipa_ports:%d\n", n_ipa_ports);
-
if (ipa_data_wq) {
pr_debug("ipa_data_wq is already setup.");
- return 0;
+ goto free_rndis_data;
}
ipa_data_wq = alloc_workqueue("k_usb_ipa_data",
@@ -856,20 +1143,111 @@ int ipa_data_setup(unsigned int no_ipa_port)
if (!ipa_data_wq) {
pr_err("Failed to create workqueue\n");
ret = -ENOMEM;
- goto free_ipa_ports;
+ goto free_rndis_data;
}
return 0;
+free_rndis_data:
+ if (func == USB_IPA_FUNC_RNDIS)
+ kfree(rndis_data);
free_ipa_ports:
- for (i = 0; i < n_ipa_ports; i++) {
- kfree(ipa_data_ports[i]);
- ipa_data_ports[i] = NULL;
- if (ipa_data_wq) {
- destroy_workqueue(ipa_data_wq);
- ipa_data_wq = NULL;
- }
- }
+ kfree(ipa_data_ports[func]);
+ ipa_data_ports[func] = NULL;
return ret;
}
+
+void ipa_data_set_ul_max_xfer_size(u32 max_transfer_size)
+{
+ if (!max_transfer_size) {
+ pr_err("%s: invalid parameters\n", __func__);
+ return;
+ }
+ rndis_data->ul_max_transfer_size = max_transfer_size;
+ pr_debug("%s(): ul_max_xfer_size:%d\n", __func__, max_transfer_size);
+}
+
+void ipa_data_set_dl_max_xfer_size(u32 max_transfer_size)
+{
+
+ if (!max_transfer_size) {
+ pr_err("%s: invalid parameters\n", __func__);
+ return;
+ }
+ rndis_data->dl_max_transfer_size = max_transfer_size;
+ pr_debug("%s(): dl_max_xfer_size:%d\n", __func__, max_transfer_size);
+}
+
+void ipa_data_set_ul_max_pkt_num(u8 max_packets_number)
+{
+ if (!max_packets_number) {
+ pr_err("%s: invalid parameters\n", __func__);
+ return;
+ }
+
+ rndis_data->ul_max_packets_number = max_packets_number;
+
+ if (max_packets_number > 1)
+ rndis_data->ul_aggregation_enable = true;
+ else
+ rndis_data->ul_aggregation_enable = false;
+
+ pr_debug("%s(): ul_aggregation enable:%d ul_max_packets_number:%d\n",
+ __func__, rndis_data->ul_aggregation_enable,
+ max_packets_number);
+}
+
+void ipa_data_start_rndis_ipa(enum ipa_func_type func)
+{
+ struct ipa_data_ch_info *port;
+
+ pr_debug("%s\n", __func__);
+
+ port = ipa_data_ports[func];
+ if (!port) {
+ pr_err("%s: port is NULL", __func__);
+ return;
+ }
+
+ if (atomic_read(&port->pipe_connect_notified)) {
+ pr_debug("%s: Transfers already started?\n", __func__);
+ return;
+ }
+ /*
+ * Increment usage count upon cable connect. Decrement after IPA
+ * handshake is done in disconnect work due to cable disconnect
+ * or in suspend work.
+ */
+ usb_gadget_autopm_get_noresume(port->gadget);
+ queue_work(ipa_data_wq, &port->connect_w);
+}
+
+void ipa_data_stop_rndis_ipa(enum ipa_func_type func)
+{
+ struct ipa_data_ch_info *port;
+ unsigned long flags;
+
+ pr_debug("%s\n", __func__);
+
+ port = ipa_data_ports[func];
+ if (!port) {
+ pr_err("%s: port is NULL", __func__);
+ return;
+ }
+
+ if (!atomic_read(&port->pipe_connect_notified))
+ return;
+
+ rndis_ipa_reset_trigger();
+ ipa_data_stop_endless_xfer(port, true);
+ ipa_data_stop_endless_xfer(port, false);
+ spin_lock_irqsave(&port->port_lock, flags);
+ /* check if USB cable is disconnected or not */
+ if (port->port_usb) {
+ msm_ep_unconfig(port->port_usb->in);
+ msm_ep_unconfig(port->port_usb->out);
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ queue_work(ipa_data_wq, &port->disconnect_w);
+}
diff --git a/drivers/usb/gadget/function/u_data_ipa.h b/drivers/usb/gadget/function/u_data_ipa.h
index b7d47ab1bb04..a1c1055bd8ef 100644
--- a/drivers/usb/gadget/function/u_data_ipa.h
+++ b/drivers/usb/gadget/function/u_data_ipa.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014,2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,23 +13,78 @@
#ifndef __U_DATA_IPA_H
#define __U_DATA_IPA_H
-#include "usb_gadget_xport.h"
+#include <linux/usb/composite.h>
+#include <linux/rndis_ipa.h>
+#include <linux/usb/msm_hsusb.h>
+#include <linux/miscdevice.h>
+#include <linux/ipa_usb.h>
+#include <linux/usb_bam.h>
+
+enum ipa_func_type {
+ USB_IPA_FUNC_ECM,
+ USB_IPA_FUNC_MBIM,
+ USB_IPA_FUNC_RMNET,
+ USB_IPA_FUNC_RNDIS,
+ USB_IPA_FUNC_DPL,
+ USB_IPA_NUM_FUNCS,
+};
+
+/* Max Number of IPA data ports supported */
+#define IPA_N_PORTS USB_IPA_NUM_FUNCS
struct gadget_ipa_port {
struct usb_composite_dev *cdev;
struct usb_function *func;
+ int rx_buffer_size;
struct usb_ep *in;
struct usb_ep *out;
int ipa_consumer_ep;
int ipa_producer_ep;
+ const struct usb_endpoint_descriptor *in_ep_desc_backup;
+ const struct usb_endpoint_descriptor *out_ep_desc_backup;
+
+};
+
+/* for configfs support */
+#define MAX_INST_NAME_LEN 40
+
+struct f_rndis_qc_opts {
+ struct usb_function_instance func_inst;
+ struct f_rndis_qc *rndis;
+ u32 vendor_id;
+ const char *manufacturer;
+ struct net_device *net;
+ int refcnt;
};
-void ipa_data_port_select(int portno, enum gadget_type gtype);
-void ipa_data_disconnect(struct gadget_ipa_port *gp, u8 port_num);
-int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num,
+void ipa_data_port_select(enum ipa_func_type func);
+void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func);
+int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func,
u8 src_connection_idx, u8 dst_connection_idx);
-int ipa_data_setup(unsigned int no_ipa_port);
-void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num);
-void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num);
+int ipa_data_setup(enum ipa_func_type func);
+void ipa_data_free(enum ipa_func_type func);
+
+void ipa_data_flush_workqueue(void);
+void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func,
+ bool remote_wakeup_enabled);
+void ipa_data_suspend(struct gadget_ipa_port *gp, enum ipa_func_type func,
+ bool remote_wakeup_enabled);
+
+void ipa_data_set_ul_max_xfer_size(u32 ul_max_xfer_size);
+
+void ipa_data_set_dl_max_xfer_size(u32 dl_max_transfer_size);
+
+void ipa_data_set_ul_max_pkt_num(u8 ul_max_packets_number);
+
+void ipa_data_start_rx_tx(enum ipa_func_type func);
+
+void ipa_data_start_rndis_ipa(enum ipa_func_type func);
+
+void ipa_data_stop_rndis_ipa(enum ipa_func_type func);
+void *rndis_qc_get_ipa_priv(void);
+void *rndis_qc_get_ipa_rx_cb(void);
+bool rndis_qc_get_skip_ep_config(void);
+void *rndis_qc_get_ipa_tx_cb(void);
+void rndis_ipa_reset_trigger(void);
#endif
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index dd7669331d00..b30831ef4014 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -116,17 +116,20 @@ int xhci_halt(struct xhci_hcd *xhci)
STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
if (!ret) {
xhci->xhc_state |= XHCI_STATE_HALTED;
- xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
-
- if (timer_pending(&xhci->cmd_timer)) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Cleanup command queue");
- del_timer(&xhci->cmd_timer);
- xhci_cleanup_command_queue(xhci);
- }
- } else
+ } else {
xhci_warn(xhci, "Host not halted after %u microseconds.\n",
XHCI_MAX_HALT_USEC);
+ }
+
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
+
+ if (timer_pending(&xhci->cmd_timer)) {
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Cleanup command queue");
+ del_timer(&xhci->cmd_timer);
+ xhci_cleanup_command_queue(xhci);
+ }
+
return ret;
}
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index a1451a2d4826..12b98017beb2 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -2267,12 +2267,15 @@ struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, const char *phandle)
struct platform_device *pdev;
struct device *pd_dev;
+ if (!usbpd_class.p) /* usbpd_init() not yet called */
+ return ERR_PTR(-EAGAIN);
+
if (!dev->of_node)
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-EINVAL);
pd_np = of_parse_phandle(dev->of_node, phandle, 0);
if (!pd_np)
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-ENXIO);
pdev = of_find_device_by_node(pd_np);
if (!pdev)
@@ -2282,7 +2285,8 @@ struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, const char *phandle)
match_usbpd_device);
if (!pd_dev) {
platform_device_put(pdev);
- return ERR_PTR(-ENODEV);
+ /* device was found but maybe hadn't probed yet, so defer */
+ return ERR_PTR(-EPROBE_DEFER);
}
ptr = devres_alloc(devm_usbpd_put, sizeof(*ptr), GFP_KERNEL);
@@ -2294,7 +2298,7 @@ struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, const char *phandle)
pd = dev_get_drvdata(pd_dev);
if (!pd)
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-EPROBE_DEFER);
*ptr = pd;
devres_add(dev, ptr);
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 584d2edc364e..d9297a7af764 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -510,8 +510,8 @@ char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt)
pr_debug("clk_rate=%llu, bpp= %d, lane_cnt=%d\n",
pinfo->clk_rate, pinfo->bpp, lane_cnt);
- min_link_rate = (pinfo->clk_rate * 10) /
- (lane_cnt * encoding_factx10);
+ min_link_rate = (u32)div_u64((pinfo->clk_rate * 10),
+ (lane_cnt * encoding_factx10));
min_link_rate = (min_link_rate * pinfo->bpp)
/ (DP_LINK_RATE_MULTIPLIER);
min_link_rate /= ln_to_link_ratio;
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index c145f72c3c70..66cd99720afa 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -357,7 +357,7 @@ static int mdss_dsi_panel_power_lp(struct mdss_panel_data *pdata, int enable)
static int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata,
int power_state)
{
- int ret;
+ int ret = 0;
struct mdss_panel_info *pinfo;
if (pdata == NULL) {
@@ -383,7 +383,11 @@ static int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata,
switch (power_state) {
case MDSS_PANEL_POWER_OFF:
- ret = mdss_dsi_panel_power_off(pdata);
+ case MDSS_PANEL_POWER_LCD_DISABLED:
+ /* if LCD has not been disabled, then disable it now */
+ if ((pinfo->panel_power_state != MDSS_PANEL_POWER_LCD_DISABLED)
+ && (pinfo->panel_power_state != MDSS_PANEL_POWER_OFF))
+ ret = mdss_dsi_panel_power_off(pdata);
break;
case MDSS_PANEL_POWER_ON:
if (mdss_dsi_is_panel_on_lp(pdata))
@@ -2469,6 +2473,7 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
int power_state;
u32 mode;
struct mdss_panel_info *pinfo;
+ int ret;
if (pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
@@ -2529,6 +2534,20 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
rc = mdss_dsi_blank(pdata, power_state);
rc = mdss_dsi_off(pdata, power_state);
break;
+ case MDSS_EVENT_DISABLE_PANEL:
+ /* disable esd thread */
+ disable_esd_thread();
+
+ /* disable backlight */
+ ctrl_pdata->panel_data.set_backlight(pdata, 0);
+
+ /* send the off commands */
+ ctrl_pdata->off(pdata);
+
+ /* disable panel power */
+ ret = mdss_dsi_panel_power_ctrl(pdata,
+ MDSS_PANEL_POWER_LCD_DISABLED);
+ break;
case MDSS_EVENT_CONT_SPLASH_FINISH:
if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE)
rc = mdss_dsi_blank(pdata, MDSS_PANEL_POWER_OFF);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index bd1854092c6a..7091dc2f38b9 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -614,6 +614,7 @@ int mdss_dsi_wait_for_lane_idle(struct mdss_dsi_ctrl_pdata *ctrl);
irqreturn_t mdss_dsi_isr(int irq, void *ptr);
irqreturn_t hw_vsync_handler(int irq, void *data);
+void disable_esd_thread(void);
void mdss_dsi_irq_handler_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata);
void mdss_dsi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index e8d68059581f..8ffba091e2b2 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -667,6 +667,11 @@ static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata,
* for the backlight brightness. If the brightness is less
* than it, the controller can malfunction.
*/
+ pr_debug("%s: bl_level:%d\n", __func__, bl_level);
+
+ /* do not allow backlight to change when panel in disable mode */
+ if (pdata->panel_disable_mode && (bl_level != 0))
+ return;
if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0))
bl_level = pdata->panel_info.bl_min;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c
index bf545ae311f2..4208c2c43efb 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_status.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_status.c
@@ -101,6 +101,16 @@ irqreturn_t hw_vsync_handler(int irq, void *data)
}
/*
+ * disable_esd_thread() - Cancels work item for the esd check.
+ */
+void disable_esd_thread(void)
+{
+ if (pstatus_data &&
+ cancel_delayed_work(&pstatus_data->check_status))
+ pr_debug("esd thread killed\n");
+}
+
+/*
* fb_event_callback() - Call back function for the fb_register_client()
* notifying events
* @self : notifier block
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 50c7015c6731..fc8d3898351e 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -1950,6 +1950,9 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info)
pdata->panel_info.is_lpm_mode = false;
}
+ if (pdata->panel_disable_mode)
+ mdss_mdp_enable_panel_disable_mode(mfd, false);
+
return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable);
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 921391dc4bde..0085163ada52 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -394,6 +394,8 @@ struct mdss_mdp_ctl_intfs_ops {
enum dynamic_switch_modes mode, bool pre);
/* called before do any register programming from commit thread */
void (*pre_programming)(struct mdss_mdp_ctl *ctl);
+ /* called to do any interface programming for the panel disable mode */
+ void (*panel_disable_cfg)(struct mdss_mdp_ctl *ctl, bool disable);
/* to update lineptr, [1..yres] - enable, 0 - disable */
int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable);
@@ -1863,6 +1865,8 @@ int mdss_mdp_cmd_set_autorefresh_mode(struct mdss_mdp_ctl *ctl, int frame_cnt);
int mdss_mdp_cmd_get_autorefresh_mode(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_cmd_set_autorefresh(struct mdss_mdp_ctl *ctl, int frame_cnt);
int mdss_mdp_ctl_cmd_get_autorefresh(struct mdss_mdp_ctl *ctl);
+int mdss_mdp_enable_panel_disable_mode(struct msm_fb_data_type *mfd,
+ bool disable_panel);
int mdss_mdp_pp_get_version(struct mdp_pp_feature_version *version);
int mdss_mdp_layer_pre_commit_cwb(struct msm_fb_data_type *mfd,
struct mdp_layer_commit_v1 *commit);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index 2c2dc6f18fd9..72d6175686b7 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -295,9 +295,9 @@ static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer,
__func__, pinfo->yres, vclks_line, te->sync_cfg_height,
te->vsync_init_val, te->rd_ptr_irq, te->start_pos,
te->wr_ptr_irq);
- pr_debug("thrd_start =%d thrd_cont=%d pp_split=%d\n",
+ pr_debug("thrd_start =%d thrd_cont=%d pp_split=%d hw_vsync_mode:%d\n",
te->sync_threshold_start, te->sync_threshold_continue,
- ctx->pingpong_split_slave);
+ ctx->pingpong_split_slave, pinfo->mipi.hw_vsync_mode);
pingpong_base = mixer->pingpong_base;
@@ -2130,6 +2130,88 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl,
}
/*
+ * This function will be called from the sysfs node to tear down or restore
+ * any dependencies of the interface to disable the panel
+ */
+void mdss_mdp_cmd_panel_disable_cfg(struct mdss_mdp_ctl *ctl,
+ bool disable)
+{
+ struct mdss_panel_info *pinfo, *spinfo = NULL;
+ struct mdss_mdp_cmd_ctx *ctx, *sctx = NULL;
+
+ pinfo = &ctl->panel_data->panel_info;
+ mutex_lock(&ctl->offlock);
+
+ if ((pinfo->sim_panel_mode == SIM_MODE) ||
+ ((!ctl->panel_data->panel_disable_mode) &&
+ (pinfo->mipi.hw_vsync_mode == 0))) {
+ pr_err("te already in simulaiton mode\n");
+ goto exit;
+ }
+
+ ctx = (struct mdss_mdp_cmd_ctx *)ctl->intf_ctx[MASTER_CTX];
+ if (is_pingpong_split(ctl->mfd)) {
+ sctx = (struct mdss_mdp_cmd_ctx *)ctl->intf_ctx[SLAVE_CTX];
+ } else if (ctl->mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY) {
+ struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl);
+
+ if (sctl) {
+ sctx = (struct mdss_mdp_cmd_ctx *)
+ sctl->intf_ctx[MASTER_CTX];
+ spinfo = &sctl->panel_data->panel_info;
+ }
+ }
+
+ if (disable) {
+ /* cache the te params */
+ memcpy(&pinfo->te_cached, &pinfo->te,
+ sizeof(struct mdss_mdp_pp_tear_check));
+ pinfo->mipi.hw_vsync_mode = 0;
+
+ if (spinfo) {
+ spinfo->mipi.hw_vsync_mode = 0;
+ memcpy(&spinfo->te_cached, &spinfo->te,
+ sizeof(struct mdss_mdp_pp_tear_check));
+ }
+
+ pr_debug("%s: update info\n", __func__);
+ /* update the te information to use sim mode */
+ mdss_panel_override_te_params(pinfo);
+ if (spinfo)
+ mdss_panel_override_te_params(spinfo);
+
+ pr_debug("%s: reconfig tear check\n", __func__);
+ /* reconfigure tear check, remove dependency to external te */
+ if (mdss_mdp_cmd_tearcheck_setup(ctx, false)) {
+ pr_warn("%s: ctx%d tearcheck setup failed\n", __func__,
+ ctx->current_pp_num);
+ } else {
+ if (sctx && mdss_mdp_cmd_tearcheck_setup(sctx, false))
+ pr_warn("%s: ctx%d tearcheck setup failed\n",
+ __func__, sctx->current_pp_num);
+ }
+ } else {
+ /*
+ * restore the information in the panel information,
+ * the actual programming will happen during restore
+ */
+ pr_debug("%s: reset tear check\n", __func__);
+ memcpy(&pinfo->te, &pinfo->te_cached,
+ sizeof(struct mdss_mdp_pp_tear_check));
+ pinfo->mipi.hw_vsync_mode = 1;
+
+ if (spinfo) {
+ spinfo->mipi.hw_vsync_mode = 1;
+ memcpy(&spinfo->te, &spinfo->te_cached,
+ sizeof(struct mdss_mdp_pp_tear_check));
+ }
+ }
+
+exit:
+ mutex_unlock(&ctl->offlock);
+}
+
+/*
* This function will be called from the sysfs node to enable and disable the
* feature with master ctl only.
*/
@@ -3481,6 +3563,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl)
ctl->ops.reconfigure = mdss_mdp_cmd_reconfigure;
ctl->ops.pre_programming = mdss_mdp_cmd_pre_programming;
ctl->ops.update_lineptr = mdss_mdp_cmd_update_lineptr;
+ ctl->ops.panel_disable_cfg = mdss_mdp_cmd_panel_disable_cfg;
pr_debug("%s:-\n", __func__);
return 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 9dda467e53cc..965d4a6cfb5e 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -3243,6 +3243,110 @@ static ssize_t mdss_mdp_dyn_pu_store(struct device *dev,
return count;
}
+
+static ssize_t mdss_mdp_panel_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
+ struct mdss_mdp_ctl *ctl;
+ struct mdss_panel_data *pdata;
+
+ if (!mfd) {
+ pr_err("Invalid mfd structure\n");
+ return -EINVAL;
+ }
+
+ ctl = mfd_to_ctl(mfd);
+ if (!ctl) {
+ pr_err("Invalid ctl structure\n");
+ return -EINVAL;
+ }
+
+ pdata = dev_get_platdata(&mfd->pdev->dev);
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n",
+ pdata->panel_disable_mode);
+
+ return ret;
+}
+
+int mdss_mdp_enable_panel_disable_mode(struct msm_fb_data_type *mfd,
+ bool disable_panel)
+{
+ struct mdss_mdp_ctl *ctl;
+ int ret = 0;
+ struct mdss_panel_data *pdata;
+
+ ctl = mfd_to_ctl(mfd);
+ if (!ctl) {
+ pr_err("Invalid ctl structure\n");
+ ret = -EINVAL;
+ return ret;
+ }
+
+ pdata = dev_get_platdata(&mfd->pdev->dev);
+
+ pr_debug("config panel %d\n", disable_panel);
+ if (disable_panel) {
+ /* first set the flag that we enter this mode */
+ pdata->panel_disable_mode = true;
+
+ /*
+ * setup any interface config that needs to change before
+ * disabling the panel
+ */
+ if (ctl->ops.panel_disable_cfg)
+ ctl->ops.panel_disable_cfg(ctl, disable_panel);
+
+ /* disable panel */
+ ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DISABLE_PANEL,
+ NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
+ if (ret)
+ pr_err("failed to disable panel! %d\n", ret);
+ } else {
+ /* restore any interface configuration */
+ if (ctl->ops.panel_disable_cfg)
+ ctl->ops.panel_disable_cfg(ctl, disable_panel);
+
+ /*
+ * no other action is needed when reconfiguring, since all the
+ * re-configuration will happen during restore
+ */
+ pdata->panel_disable_mode = false;
+ }
+
+ return ret;
+}
+
+static ssize_t mdss_mdp_panel_disable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ int disable_panel, rc;
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
+
+ if (!mfd) {
+ pr_err("Invalid mfd structure\n");
+ rc = -EINVAL;
+ return rc;
+ }
+
+ rc = kstrtoint(buf, 10, &disable_panel);
+ if (rc) {
+ pr_err("kstrtoint failed. rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("disable panel: %d ++\n", disable_panel);
+ /* we only support disabling the panel from sysfs */
+ if (disable_panel)
+ mdss_mdp_enable_panel_disable_mode(mfd, true);
+
+ return len;
+}
+
static ssize_t mdss_mdp_cmd_autorefresh_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -3433,6 +3537,8 @@ static DEVICE_ATTR(msm_misr_en, S_IRUGO | S_IWUSR,
mdss_mdp_misr_show, mdss_mdp_misr_store);
static DEVICE_ATTR(msm_cmd_autorefresh_en, S_IRUGO | S_IWUSR,
mdss_mdp_cmd_autorefresh_show, mdss_mdp_cmd_autorefresh_store);
+static DEVICE_ATTR(msm_disable_panel, S_IRUGO | S_IWUSR,
+ mdss_mdp_panel_disable_show, mdss_mdp_panel_disable_store);
static DEVICE_ATTR(vsync_event, S_IRUGO, mdss_mdp_vsync_show_event, NULL);
static DEVICE_ATTR(lineptr_event, S_IRUGO, mdss_mdp_lineptr_show_event, NULL);
static DEVICE_ATTR(lineptr_value, S_IRUGO | S_IWUSR | S_IWGRP,
@@ -3454,6 +3560,7 @@ static struct attribute *mdp_overlay_sysfs_attrs[] = {
&dev_attr_dyn_pu.attr,
&dev_attr_msm_misr_en.attr,
&dev_attr_msm_cmd_autorefresh_en.attr,
+ &dev_attr_msm_disable_panel.attr,
&dev_attr_hist_event.attr,
&dev_attr_bl_event.attr,
&dev_attr_ad_event.attr,
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c
index 48235c5b85ba..5bd627853c59 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c
@@ -1570,7 +1570,7 @@ static int pp_pa_dither_cache_params_v1_7(
res_data->strength = dither_data.strength;
res_data->matrix_sz = MDP_DITHER_DATA_V1_7_SZ;
ret = copy_from_user(res_data->matrix_data,
- (u8 *)dither_data.matrix_data,
+ (u8 *)((unsigned long)dither_data.matrix_data),
(MDP_DITHER_DATA_V1_7_SZ * sizeof(u32)));
if (ret) {
pr_err("failed to copy the dither matrix ret %d sz %zd", ret,
@@ -1700,8 +1700,8 @@ static int pp_igc_lut_cache_params_v3(struct mdp_igc_lut_data *config,
v3_cache_data->table_fmt = v3_kernel_data->table_fmt;
} else {
ret = copy_from_user(v3_cache_data->c0_c1_data,
- (u8 *)v3_usr_config.c0_c1_data,
- len * sizeof(u32));
+ (u8 *)((unsigned long)v3_usr_config.c0_c1_data),
+ len * sizeof(u32));
if (ret) {
pr_err("copy from user failed for c0_c1_data size %zd ret %d\n",
len * sizeof(u32), ret);
@@ -1709,8 +1709,8 @@ static int pp_igc_lut_cache_params_v3(struct mdp_igc_lut_data *config,
goto igc_config_exit;
}
ret = copy_from_user(v3_cache_data->c2_data,
- (u8 *)v3_usr_config.c2_data,
- len * sizeof(u32));
+ (u8 *)((unsigned long)v3_usr_config.c2_data),
+ len * sizeof(u32));
if (ret) {
pr_err("copy from user failed for c2_data size %zd ret %d\n",
len * sizeof(u32), ret);
@@ -1846,7 +1846,7 @@ static int pp_igc_lut_cache_params_pipe_v3(
}
} else {
if (copy_from_user(v3_cache_data->c0_c1_data,
- (u8 *)v3_usr_config.c0_c1_data,
+ (u8 *)((unsigned long)v3_usr_config.c0_c1_data),
IGC_LUT_ENTRIES * sizeof(u32))) {
pr_err("error in copying the c0_c1_data of size %zd\n",
IGC_LUT_ENTRIES * sizeof(u32));
@@ -1854,7 +1854,7 @@ static int pp_igc_lut_cache_params_pipe_v3(
goto igc_config_exit;
}
if (copy_from_user(v3_cache_data->c2_data,
- (u8 *)v3_usr_config.c2_data,
+ (u8 *)((unsigned long)v3_usr_config.c2_data),
IGC_LUT_ENTRIES * sizeof(u32))) {
pr_err("error in copying the c2_data of size %zd\n",
IGC_LUT_ENTRIES * sizeof(u32));
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index b2b647dcc017..a633528b5373 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -104,6 +104,7 @@ enum {
MDSS_PANEL_POWER_ON,
MDSS_PANEL_POWER_LP1,
MDSS_PANEL_POWER_LP2,
+ MDSS_PANEL_POWER_LCD_DISABLED,
};
enum {
@@ -265,6 +266,7 @@ enum mdss_intf_events {
MDSS_EVENT_DSI_RESET_WRITE_PTR,
MDSS_EVENT_PANEL_TIMING_SWITCH,
MDSS_EVENT_DEEP_COLOR,
+ MDSS_EVENT_DISABLE_PANEL,
MDSS_EVENT_MAX,
};
@@ -692,6 +694,7 @@ struct mdss_panel_info {
char panel_name[MDSS_MAX_PANEL_LEN];
struct mdss_mdp_pp_tear_check te;
+ struct mdss_mdp_pp_tear_check te_cached;
/*
* Value of 2 only when single DSI is configured with 2 DSC
@@ -789,6 +792,12 @@ struct mdss_panel_data {
/* To store dsc cfg name passed by bootloader */
char dsc_cfg_np_name[MDSS_MAX_PANEL_LEN];
struct mdss_panel_data *next;
+
+ /*
+ * Set when the power of the panel is disabled while dsi/mdp
+ * are still on; panel will recover after unblank
+ */
+ bool panel_disable_mode;
};
struct mdss_panel_debugfs_info {
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index 9a00eff9ade9..b5da4ad1a86b 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -36,6 +36,8 @@
#include "mdss_smmu.h"
#include "mdss_debug.h"
+#define SZ_4G 0xF0000000
+
static DEFINE_MUTEX(mdp_iommu_lock);
void mdss_iommu_lock(void)
@@ -536,13 +538,13 @@ int mdss_smmu_init(struct mdss_data_type *mdata, struct device *dev)
}
static struct mdss_smmu_domain mdss_mdp_unsec = {
- "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128K, (SZ_1G - SZ_128K)};
+ "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128K, (SZ_4G - SZ_128K)};
static struct mdss_smmu_domain mdss_rot_unsec = {
- NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_1G - SZ_128K)};
+ NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_4G - SZ_128K)};
static struct mdss_smmu_domain mdss_mdp_sec = {
- "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_1G, SZ_2G};
+ "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_128K, (SZ_4G - SZ_128K)};
static struct mdss_smmu_domain mdss_rot_sec = {
- NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_1G, SZ_2G};
+ NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_128K, (SZ_4G - SZ_128K)};
static const struct of_device_id mdss_smmu_dt_match[] = {
{ .compatible = "qcom,smmu_mdp_unsec", .data = &mdss_mdp_unsec},
diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
index e8671942c2a0..785af63acabd 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -71,10 +71,12 @@ static ssize_t fuse_passthrough_read_write_iter(struct kiocb *iocb,
struct fuse_file *ff;
struct file *fuse_file, *passthrough_filp;
struct inode *fuse_inode, *passthrough_inode;
+ struct fuse_conn *fc;
ff = iocb->ki_filp->private_data;
fuse_file = iocb->ki_filp;
passthrough_filp = ff->passthrough_filp;
+ fc = ff->fc;
/* lock passthrough file to prevent it from being released */
get_file(passthrough_filp);
@@ -88,7 +90,9 @@ static ssize_t fuse_passthrough_read_write_iter(struct kiocb *iocb,
ret_val = passthrough_filp->f_op->write_iter(iocb, iter);
if (ret_val >= 0 || ret_val == -EIOCBQUEUED) {
+ spin_lock(&fc->lock);
fsstack_copy_inode_size(fuse_inode, passthrough_inode);
+ spin_unlock(&fc->lock);
fsstack_copy_attr_times(fuse_inode, passthrough_inode);
}
} else {
diff --git a/include/linux/dma-mapping-fast.h b/include/linux/dma-mapping-fast.h
index aa9fcfe73162..ddd126c0fd85 100644
--- a/include/linux/dma-mapping-fast.h
+++ b/include/linux/dma-mapping-fast.h
@@ -16,6 +16,8 @@
#include <linux/iommu.h>
#include <linux/io-pgtable-fast.h>
+struct dma_iommu_mapping;
+
struct dma_fast_smmu_mapping {
struct device *dev;
struct iommu_domain *domain;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 56855724271c..f4f5af978c7c 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -650,8 +650,8 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link)
{
}
-static int iommu_dma_supported(struct iommu_domain *domain, struct device *dev,
- u64 mask)
+static inline int iommu_dma_supported(struct iommu_domain *domain,
+ struct device *dev, u64 mask)
{
return -EINVAL;
}
diff --git a/include/linux/msm_dma_iommu_mapping.h b/include/linux/msm_dma_iommu_mapping.h
index 76451faa2073..73e69383b9b6 100644
--- a/include/linux/msm_dma_iommu_mapping.h
+++ b/include/linux/msm_dma_iommu_mapping.h
@@ -90,7 +90,7 @@ static inline void msm_dma_unmap_sg(struct device *dev,
{
}
-int msm_dma_unmap_all_for_dev(struct device *dev)
+static inline int msm_dma_unmap_all_for_dev(struct device *dev)
{
return 0;
}
diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h
index c2fa3ecb0dce..146efefde2a1 100644
--- a/include/linux/percpu-rwsem.h
+++ b/include/linux/percpu-rwsem.h
@@ -10,30 +10,96 @@
struct percpu_rw_semaphore {
struct rcu_sync rss;
- unsigned int __percpu *fast_read_ctr;
+ unsigned int __percpu *read_count;
struct rw_semaphore rw_sem;
- atomic_t slow_read_ctr;
- wait_queue_head_t write_waitq;
+ wait_queue_head_t writer;
+ int readers_block;
};
-extern void percpu_down_read(struct percpu_rw_semaphore *);
-extern int percpu_down_read_trylock(struct percpu_rw_semaphore *);
-extern void percpu_up_read(struct percpu_rw_semaphore *);
+extern int __percpu_down_read(struct percpu_rw_semaphore *, int);
+extern void __percpu_up_read(struct percpu_rw_semaphore *);
+
+static inline void percpu_down_read(struct percpu_rw_semaphore *sem)
+{
+ might_sleep();
+
+ rwsem_acquire_read(&sem->rw_sem.dep_map, 0, 0, _RET_IP_);
+
+ preempt_disable();
+ /*
+ * We are in an RCU-sched read-side critical section, so the writer
+ * cannot both change sem->state from readers_fast and start checking
+ * counters while we are here. So if we see !sem->state, we know that
+ * the writer won't be checking until we're past the preempt_enable()
+ * and that one the synchronize_sched() is done, the writer will see
+ * anything we did within this RCU-sched read-size critical section.
+ */
+ __this_cpu_inc(*sem->read_count);
+ if (unlikely(!rcu_sync_is_idle(&sem->rss)))
+ __percpu_down_read(sem, false); /* Unconditional memory barrier */
+ preempt_enable();
+ /*
+ * The barrier() from preempt_enable() prevents the compiler from
+ * bleeding the critical section out.
+ */
+}
+
+static inline int percpu_down_read_trylock(struct percpu_rw_semaphore *sem)
+{
+ int ret = 1;
+
+ preempt_disable();
+ /*
+ * Same as in percpu_down_read().
+ */
+ __this_cpu_inc(*sem->read_count);
+ if (unlikely(!rcu_sync_is_idle(&sem->rss)))
+ ret = __percpu_down_read(sem, true); /* Unconditional memory barrier */
+ preempt_enable();
+ /*
+ * The barrier() from preempt_enable() prevents the compiler from
+ * bleeding the critical section out.
+ */
+
+ if (ret)
+ rwsem_acquire_read(&sem->rw_sem.dep_map, 0, 1, _RET_IP_);
+
+ return ret;
+}
+
+static inline void percpu_up_read(struct percpu_rw_semaphore *sem)
+{
+ /*
+ * The barrier() in preempt_disable() prevents the compiler from
+ * bleeding the critical section out.
+ */
+ preempt_disable();
+ /*
+ * Same as in percpu_down_read().
+ */
+ if (likely(rcu_sync_is_idle(&sem->rss)))
+ __this_cpu_dec(*sem->read_count);
+ else
+ __percpu_up_read(sem); /* Unconditional memory barrier */
+ preempt_enable();
+
+ rwsem_release(&sem->rw_sem.dep_map, 1, _RET_IP_);
+}
extern void percpu_down_write(struct percpu_rw_semaphore *);
extern void percpu_up_write(struct percpu_rw_semaphore *);
extern int __percpu_init_rwsem(struct percpu_rw_semaphore *,
const char *, struct lock_class_key *);
+
extern void percpu_free_rwsem(struct percpu_rw_semaphore *);
-#define percpu_init_rwsem(brw) \
+#define percpu_init_rwsem(sem) \
({ \
static struct lock_class_key rwsem_key; \
- __percpu_init_rwsem(brw, #brw, &rwsem_key); \
+ __percpu_init_rwsem(sem, #sem, &rwsem_key); \
})
-
#define percpu_rwsem_is_held(sem) lockdep_is_held(&(sem)->rw_sem)
static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem,
diff --git a/include/linux/rcu_sync.h b/include/linux/rcu_sync.h
index a63a33e6196e..ece7ed9a4a70 100644
--- a/include/linux/rcu_sync.h
+++ b/include/linux/rcu_sync.h
@@ -59,6 +59,7 @@ static inline bool rcu_sync_is_idle(struct rcu_sync *rsp)
}
extern void rcu_sync_init(struct rcu_sync *, enum rcu_sync_type);
+extern void rcu_sync_enter_start(struct rcu_sync *);
extern void rcu_sync_enter(struct rcu_sync *);
extern void rcu_sync_exit(struct rcu_sync *);
extern void rcu_sync_dtor(struct rcu_sync *);
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index 8704b2e7cfbc..473cb5fb375e 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -24,8 +24,6 @@ struct icnss_driver_ops {
void (*shutdown)(struct device *dev);
int (*reinit)(struct device *dev);
void (*crash_shutdown)(void *pdev);
- int (*suspend)(struct device *dev, pm_message_t state);
- int (*resume)(struct device *dev);
int (*pm_suspend)(struct device *dev);
int (*pm_resume)(struct device *dev);
int (*suspend_noirq)(struct device *dev);
diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h
index 5adcbcf660ba..aa3b363e95e1 100644
--- a/include/sound/wcd-dsp-mgr.h
+++ b/include/sound/wcd-dsp-mgr.h
@@ -36,6 +36,9 @@ enum wdsp_cmpnt_type {
};
enum wdsp_event_type {
+ /* Initialization related */
+ WDSP_EVENT_POST_INIT,
+
/* Image download related */
WDSP_EVENT_PRE_DLOAD_CODE,
WDSP_EVENT_DLOAD_SECTION,
@@ -44,6 +47,8 @@ enum wdsp_event_type {
WDSP_EVENT_POST_DLOAD_DATA,
WDSP_EVENT_DLOAD_FAILED,
+ WDSP_EVENT_READ_SECTION,
+
/* DSP boot related */
WDSP_EVENT_PRE_BOOTUP,
WDSP_EVENT_DO_BOOT,
@@ -62,6 +67,7 @@ enum wdsp_event_type {
enum wdsp_intr {
WDSP_IPC1_INTR,
+ WDSP_ERR_INTR,
};
/*
@@ -86,6 +92,12 @@ struct wdsp_img_section {
u8 *data;
};
+struct wdsp_err_intr_arg {
+ bool mem_dumps_enabled;
+ u32 remote_start_addr;
+ size_t dump_size;
+};
+
/*
* wdsp_ops: ops/function callbacks for manager driver
* @register_cmpnt_ops: components will use this to register
@@ -109,7 +121,7 @@ struct wdsp_mgr_ops {
struct device *(*get_dev_for_cmpnt)(struct device *wdsp_dev,
enum wdsp_cmpnt_type type);
int (*intr_handler)(struct device *wdsp_dev,
- enum wdsp_intr intr);
+ enum wdsp_intr intr, void *arg);
int (*vote_for_dsp)(struct device *wdsp_dev, bool vote);
int (*suspend)(struct device *wdsp_dev);
int (*resume)(struct device *wdsp_dev);
diff --git a/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h
index 691df1b2689b..e14cab59e90a 100644
--- a/include/trace/events/trace_msm_low_power.h
+++ b/include/trace/events/trace_msm_low_power.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -43,6 +43,54 @@ TRACE_EVENT(cpu_power_select,
__entry->next_event_us)
);
+TRACE_EVENT(cpu_pred_select,
+
+ TP_PROTO(u32 predtype, u64 predicted, u32 tmr_time),
+
+ TP_ARGS(predtype, predicted, tmr_time),
+
+ TP_STRUCT__entry(
+ __field(u32, predtype)
+ __field(u64, predicted)
+ __field(u32, tmr_time)
+ ),
+
+ TP_fast_assign(
+ __entry->predtype = predtype;
+ __entry->predicted = predicted;
+ __entry->tmr_time = tmr_time;
+ ),
+
+ TP_printk("pred:%u time:%lu tmr_time:%u",
+ __entry->predtype, (unsigned long)__entry->predicted,
+ __entry->tmr_time)
+);
+
+TRACE_EVENT(cpu_pred_hist,
+
+ TP_PROTO(int idx, u32 resi, u32 sample, u32 tmr),
+
+ TP_ARGS(idx, resi, sample, tmr),
+
+ TP_STRUCT__entry(
+ __field(int, idx)
+ __field(u32, resi)
+ __field(u32, sample)
+ __field(u32, tmr)
+ ),
+
+ TP_fast_assign(
+ __entry->idx = idx;
+ __entry->resi = resi;
+ __entry->sample = sample;
+ __entry->tmr = tmr;
+ ),
+
+ TP_printk("idx:%d resi:%u sample:%u tmr:%u",
+ __entry->idx, __entry->resi,
+ __entry->sample, __entry->tmr)
+);
+
TRACE_EVENT(cpu_idle_enter,
TP_PROTO(int index),
diff --git a/include/uapi/linux/msm_vidc_dec.h b/include/uapi/linux/msm_vidc_dec.h
index f502c81665a4..48ce8e929fbf 100644
--- a/include/uapi/linux/msm_vidc_dec.h
+++ b/include/uapi/linux/msm_vidc_dec.h
@@ -486,10 +486,14 @@ enum vdec_interlaced_format {
VDEC_InterlaceInterleaveFrameBottomFieldFirst = 0x4
};
+#define VDEC_YUV_FORMAT_NV12_TP10_UBWC \
+ VDEC_YUV_FORMAT_NV12_TP10_UBWC
+
enum vdec_output_fromat {
VDEC_YUV_FORMAT_NV12 = 0x1,
VDEC_YUV_FORMAT_TILE_4x2 = 0x2,
- VDEC_YUV_FORMAT_NV12_UBWC = 0x3
+ VDEC_YUV_FORMAT_NV12_UBWC = 0x3,
+ VDEC_YUV_FORMAT_NV12_TP10_UBWC = 0x4
};
enum vdec_output_order {
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index ad4a12371069..cc6c7d0a6758 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -5376,6 +5376,12 @@ int __init cgroup_init(void)
BUG_ON(cgroup_init_cftypes(NULL, cgroup_dfl_base_files));
BUG_ON(cgroup_init_cftypes(NULL, cgroup_legacy_base_files));
+ /*
+ * The latency of the synchronize_sched() is too high for cgroups,
+ * avoid it at the cost of forcing all readers into the slow path.
+ */
+ rcu_sync_enter_start(&cgroup_threadgroup_rwsem.rss);
+
mutex_lock(&cgroup_mutex);
/* Add init_css_set to the hash table */
diff --git a/kernel/fork.c b/kernel/fork.c
index c9eb86b646ab..e89d0bae6f20 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1375,7 +1375,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
p->real_start_time = ktime_get_boot_ns();
p->io_context = NULL;
p->audit_context = NULL;
- threadgroup_change_begin(current);
cgroup_fork(p);
#ifdef CONFIG_NUMA
p->mempolicy = mpol_dup(p->mempolicy);
@@ -1527,6 +1526,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
INIT_LIST_HEAD(&p->thread_group);
p->task_works = NULL;
+ threadgroup_change_begin(current);
/*
* Ensure that the cgroup subsystem policies allow the new process to be
* forked. It should be noted the the new process's css_set can be changed
@@ -1627,6 +1627,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
bad_fork_cancel_cgroup:
cgroup_cancel_fork(p, cgrp_ss_priv);
bad_fork_free_pid:
+ threadgroup_change_end(current);
if (pid != &init_struct_pid)
free_pid(pid);
bad_fork_cleanup_io:
@@ -1657,7 +1658,6 @@ bad_fork_cleanup_policy:
mpol_put(p->mempolicy);
bad_fork_cleanup_threadgroup_lock:
#endif
- threadgroup_change_end(current);
delayacct_tsk_free(p);
bad_fork_cleanup_count:
atomic_dec(&p->cred->user->processes);
diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c
index f231e0bb311c..ce182599cf2e 100644
--- a/kernel/locking/percpu-rwsem.c
+++ b/kernel/locking/percpu-rwsem.c
@@ -8,151 +8,186 @@
#include <linux/sched.h>
#include <linux/errno.h>
-int __percpu_init_rwsem(struct percpu_rw_semaphore *brw,
+int __percpu_init_rwsem(struct percpu_rw_semaphore *sem,
const char *name, struct lock_class_key *rwsem_key)
{
- brw->fast_read_ctr = alloc_percpu(int);
- if (unlikely(!brw->fast_read_ctr))
+ sem->read_count = alloc_percpu(int);
+ if (unlikely(!sem->read_count))
return -ENOMEM;
/* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */
- __init_rwsem(&brw->rw_sem, name, rwsem_key);
- rcu_sync_init(&brw->rss, RCU_SCHED_SYNC);
- atomic_set(&brw->slow_read_ctr, 0);
- init_waitqueue_head(&brw->write_waitq);
+ rcu_sync_init(&sem->rss, RCU_SCHED_SYNC);
+ __init_rwsem(&sem->rw_sem, name, rwsem_key);
+ init_waitqueue_head(&sem->writer);
+ sem->readers_block = 0;
return 0;
}
EXPORT_SYMBOL_GPL(__percpu_init_rwsem);
-void percpu_free_rwsem(struct percpu_rw_semaphore *brw)
+void percpu_free_rwsem(struct percpu_rw_semaphore *sem)
{
/*
* XXX: temporary kludge. The error path in alloc_super()
* assumes that percpu_free_rwsem() is safe after kzalloc().
*/
- if (!brw->fast_read_ctr)
+ if (!sem->read_count)
return;
- rcu_sync_dtor(&brw->rss);
- free_percpu(brw->fast_read_ctr);
- brw->fast_read_ctr = NULL; /* catch use after free bugs */
+ rcu_sync_dtor(&sem->rss);
+ free_percpu(sem->read_count);
+ sem->read_count = NULL; /* catch use after free bugs */
}
+EXPORT_SYMBOL_GPL(percpu_free_rwsem);
-/*
- * This is the fast-path for down_read/up_read. If it succeeds we rely
- * on the barriers provided by rcu_sync_enter/exit; see the comments in
- * percpu_down_write() and percpu_up_write().
- *
- * If this helper fails the callers rely on the normal rw_semaphore and
- * atomic_dec_and_test(), so in this case we have the necessary barriers.
- */
-static bool update_fast_ctr(struct percpu_rw_semaphore *brw, unsigned int val)
+int __percpu_down_read(struct percpu_rw_semaphore *sem, int try)
{
- bool success;
+ /*
+ * Due to having preemption disabled the decrement happens on
+ * the same CPU as the increment, avoiding the
+ * increment-on-one-CPU-and-decrement-on-another problem.
+ *
+ * If the reader misses the writer's assignment of readers_block, then
+ * the writer is guaranteed to see the reader's increment.
+ *
+ * Conversely, any readers that increment their sem->read_count after
+ * the writer looks are guaranteed to see the readers_block value,
+ * which in turn means that they are guaranteed to immediately
+ * decrement their sem->read_count, so that it doesn't matter that the
+ * writer missed them.
+ */
- preempt_disable();
- success = rcu_sync_is_idle(&brw->rss);
- if (likely(success))
- __this_cpu_add(*brw->fast_read_ctr, val);
- preempt_enable();
+ smp_mb(); /* A matches D */
- return success;
-}
+ /*
+ * If !readers_block the critical section starts here, matched by the
+ * release in percpu_up_write().
+ */
+ if (likely(!smp_load_acquire(&sem->readers_block)))
+ return 1;
-/*
- * Like the normal down_read() this is not recursive, the writer can
- * come after the first percpu_down_read() and create the deadlock.
- *
- * Note: returns with lock_is_held(brw->rw_sem) == T for lockdep,
- * percpu_up_read() does rwsem_release(). This pairs with the usage
- * of ->rw_sem in percpu_down/up_write().
- */
-void percpu_down_read(struct percpu_rw_semaphore *brw)
-{
- might_sleep();
- rwsem_acquire_read(&brw->rw_sem.dep_map, 0, 0, _RET_IP_);
+ /*
+ * Per the above comment; we still have preemption disabled and
+ * will thus decrement on the same CPU as we incremented.
+ */
+ __percpu_up_read(sem);
- if (likely(update_fast_ctr(brw, +1)))
- return;
+ if (try)
+ return 0;
- /* Avoid rwsem_acquire_read() and rwsem_release() */
- __down_read(&brw->rw_sem);
- atomic_inc(&brw->slow_read_ctr);
- __up_read(&brw->rw_sem);
-}
-EXPORT_SYMBOL_GPL(percpu_down_read);
+ /*
+ * We either call schedule() in the wait, or we'll fall through
+ * and reschedule on the preempt_enable() in percpu_down_read().
+ */
+ preempt_enable_no_resched();
-int percpu_down_read_trylock(struct percpu_rw_semaphore *brw)
-{
- if (unlikely(!update_fast_ctr(brw, +1))) {
- if (!__down_read_trylock(&brw->rw_sem))
- return 0;
- atomic_inc(&brw->slow_read_ctr);
- __up_read(&brw->rw_sem);
- }
-
- rwsem_acquire_read(&brw->rw_sem.dep_map, 0, 1, _RET_IP_);
+ /*
+ * Avoid lockdep for the down/up_read() we already have them.
+ */
+ __down_read(&sem->rw_sem);
+ this_cpu_inc(*sem->read_count);
+ __up_read(&sem->rw_sem);
+
+ preempt_disable();
return 1;
}
+EXPORT_SYMBOL_GPL(__percpu_down_read);
-void percpu_up_read(struct percpu_rw_semaphore *brw)
+void __percpu_up_read(struct percpu_rw_semaphore *sem)
{
- rwsem_release(&brw->rw_sem.dep_map, 1, _RET_IP_);
-
- if (likely(update_fast_ctr(brw, -1)))
- return;
+ smp_mb(); /* B matches C */
+ /*
+ * In other words, if they see our decrement (presumably to aggregate
+ * zero, as that is the only time it matters) they will also see our
+ * critical section.
+ */
+ __this_cpu_dec(*sem->read_count);
- /* false-positive is possible but harmless */
- if (atomic_dec_and_test(&brw->slow_read_ctr))
- wake_up_all(&brw->write_waitq);
+ /* Prod writer to recheck readers_active */
+ wake_up(&sem->writer);
}
-EXPORT_SYMBOL_GPL(percpu_up_read);
+EXPORT_SYMBOL_GPL(__percpu_up_read);
+
+#define per_cpu_sum(var) \
+({ \
+ typeof(var) __sum = 0; \
+ int cpu; \
+ compiletime_assert_atomic_type(__sum); \
+ for_each_possible_cpu(cpu) \
+ __sum += per_cpu(var, cpu); \
+ __sum; \
+})
-static int clear_fast_ctr(struct percpu_rw_semaphore *brw)
+/*
+ * Return true if the modular sum of the sem->read_count per-CPU variable is
+ * zero. If this sum is zero, then it is stable due to the fact that if any
+ * newly arriving readers increment a given counter, they will immediately
+ * decrement that same counter.
+ */
+static bool readers_active_check(struct percpu_rw_semaphore *sem)
{
- unsigned int sum = 0;
- int cpu;
+ if (per_cpu_sum(*sem->read_count) != 0)
+ return false;
+
+ /*
+ * If we observed the decrement; ensure we see the entire critical
+ * section.
+ */
- for_each_possible_cpu(cpu) {
- sum += per_cpu(*brw->fast_read_ctr, cpu);
- per_cpu(*brw->fast_read_ctr, cpu) = 0;
- }
+ smp_mb(); /* C matches B */
- return sum;
+ return true;
}
-void percpu_down_write(struct percpu_rw_semaphore *brw)
+void percpu_down_write(struct percpu_rw_semaphore *sem)
{
+ /* Notify readers to take the slow path. */
+ rcu_sync_enter(&sem->rss);
+
+ down_write(&sem->rw_sem);
+
/*
- * Make rcu_sync_is_idle() == F and thus disable the fast-path in
- * percpu_down_read() and percpu_up_read(), and wait for gp pass.
- *
- * The latter synchronises us with the preceding readers which used
- * the fast-past, so we can not miss the result of __this_cpu_add()
- * or anything else inside their criticial sections.
+ * Notify new readers to block; up until now, and thus throughout the
+ * longish rcu_sync_enter() above, new readers could still come in.
*/
- rcu_sync_enter(&brw->rss);
+ WRITE_ONCE(sem->readers_block, 1);
- /* exclude other writers, and block the new readers completely */
- down_write(&brw->rw_sem);
+ smp_mb(); /* D matches A */
- /* nobody can use fast_read_ctr, move its sum into slow_read_ctr */
- atomic_add(clear_fast_ctr(brw), &brw->slow_read_ctr);
+ /*
+ * If they don't see our writer of readers_block, then we are
+ * guaranteed to see their sem->read_count increment, and therefore
+ * will wait for them.
+ */
- /* wait for all readers to complete their percpu_up_read() */
- wait_event(brw->write_waitq, !atomic_read(&brw->slow_read_ctr));
+ /* Wait for all now active readers to complete. */
+ wait_event(sem->writer, readers_active_check(sem));
}
EXPORT_SYMBOL_GPL(percpu_down_write);
-void percpu_up_write(struct percpu_rw_semaphore *brw)
+void percpu_up_write(struct percpu_rw_semaphore *sem)
{
- /* release the lock, but the readers can't use the fast-path */
- up_write(&brw->rw_sem);
/*
- * Enable the fast-path in percpu_down_read() and percpu_up_read()
- * but only after another gp pass; this adds the necessary barrier
- * to ensure the reader can't miss the changes done by us.
+ * Signal the writer is done, no fast path yet.
+ *
+ * One reason that we cannot just immediately flip to readers_fast is
+ * that new readers might fail to see the results of this writer's
+ * critical section.
+ *
+ * Therefore we force it through the slow path which guarantees an
+ * acquire and thereby guarantees the critical section's consistency.
+ */
+ smp_store_release(&sem->readers_block, 0);
+
+ /*
+ * Release the write lock, this will allow readers back in the game.
+ */
+ up_write(&sem->rw_sem);
+
+ /*
+ * Once this completes (at least one RCU-sched grace period hence) the
+ * reader fast path will be available again. Safe to use outside the
+ * exclusive write lock because its counting.
*/
- rcu_sync_exit(&brw->rss);
+ rcu_sync_exit(&sem->rss);
}
EXPORT_SYMBOL_GPL(percpu_up_write);
diff --git a/kernel/rcu/sync.c b/kernel/rcu/sync.c
index be922c9f3d37..e358313a0d6c 100644
--- a/kernel/rcu/sync.c
+++ b/kernel/rcu/sync.c
@@ -83,6 +83,18 @@ void rcu_sync_init(struct rcu_sync *rsp, enum rcu_sync_type type)
}
/**
+ * Must be called after rcu_sync_init() and before first use.
+ *
+ * Ensures rcu_sync_is_idle() returns false and rcu_sync_{enter,exit}()
+ * pairs turn into NO-OPs.
+ */
+void rcu_sync_enter_start(struct rcu_sync *rsp)
+{
+ rsp->gp_count++;
+ rsp->gp_state = GP_PASSED;
+}
+
+/**
* rcu_sync_enter() - Force readers onto slowpath
* @rsp: Pointer to rcu_sync structure to use for synchronization
*
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 4b5b0c3cef7a..ff7f6f35fc8f 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3390,16 +3390,17 @@ static void __sched notrace __schedule(bool preempt)
update_rq_clock(rq);
next = pick_next_task(rq, prev);
- wallclock = sched_ktime_clock();
- update_task_ravg(prev, rq, PUT_PREV_TASK, wallclock, 0);
- update_task_ravg(next, rq, PICK_NEXT_TASK, wallclock, 0);
clear_tsk_need_resched(prev);
clear_preempt_need_resched();
rq->clock_skip_update = 0;
BUG_ON(task_cpu(next) != cpu_of(rq));
+ wallclock = sched_ktime_clock();
if (likely(prev != next)) {
+ update_task_ravg(prev, rq, PUT_PREV_TASK, wallclock, 0);
+ update_task_ravg(next, rq, PICK_NEXT_TASK, wallclock, 0);
+
rq->nr_switches++;
rq->curr = next;
++*switch_count;
@@ -3410,6 +3411,7 @@ static void __sched notrace __schedule(bool preempt)
rq = context_switch(rq, prev, next); /* unlocks the rq */
cpu = cpu_of(rq);
} else {
+ update_task_ravg(prev, rq, TASK_UPDATE, wallclock, 0);
lockdep_unpin_lock(&rq->lock);
raw_spin_unlock_irq(&rq->lock);
}
@@ -7812,6 +7814,7 @@ void __init sched_init_smp(void)
hotcpu_notifier(cpuset_cpu_inactive, CPU_PRI_CPUSET_INACTIVE);
update_cluster_topology();
+ init_sched_hmp_boost_policy();
init_hrtick();
@@ -7860,6 +7863,7 @@ void __init sched_init(void)
BUG_ON(num_possible_cpus() > BITS_PER_LONG);
+ sched_hmp_parse_dt();
init_clusters();
#ifdef CONFIG_FAIR_GROUP_SCHED
diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c
index 61e352aeec00..a0686ea29243 100644
--- a/kernel/sched/hmp.c
+++ b/kernel/sched/hmp.c
@@ -17,6 +17,7 @@
#include <linux/cpufreq.h>
#include <linux/list_sort.h>
#include <linux/syscore_ops.h>
+#include <linux/of.h>
#include "sched.h"
#include "core_ctl.h"
@@ -225,6 +226,52 @@ fail:
return ret;
}
+/*
+ * It is possible that CPUs of the same micro architecture can have slight
+ * difference in the efficiency due to other factors like cache size. The
+ * BOOST_ON_BIG policy may not be optimial for such systems. The required
+ * boost policy can be specified via device tree to handle this.
+ */
+static int __read_mostly sched_boost_policy = SCHED_BOOST_NONE;
+
+/*
+ * This should be called after clusters are populated and
+ * the respective efficiency values are initialized.
+ */
+void init_sched_hmp_boost_policy(void)
+{
+ /*
+ * Initialize the boost type here if it is not passed from
+ * device tree.
+ */
+ if (sched_boost_policy == SCHED_BOOST_NONE) {
+ if (max_possible_efficiency != min_possible_efficiency)
+ sched_boost_policy = SCHED_BOOST_ON_BIG;
+ else
+ sched_boost_policy = SCHED_BOOST_ON_ALL;
+ }
+}
+
+void sched_hmp_parse_dt(void)
+{
+ struct device_node *sn;
+ const char *boost_policy;
+
+ if (!sched_enable_hmp)
+ return;
+
+ sn = of_find_node_by_path("/sched-hmp");
+ if (!sn)
+ return;
+
+ if (!of_property_read_string(sn, "boost-policy", &boost_policy)) {
+ if (!strcmp(boost_policy, "boost-on-big"))
+ sched_boost_policy = SCHED_BOOST_ON_BIG;
+ else if (!strcmp(boost_policy, "boost-on-all"))
+ sched_boost_policy = SCHED_BOOST_ON_ALL;
+ }
+}
+
unsigned int max_possible_efficiency = 1;
unsigned int min_possible_efficiency = UINT_MAX;
@@ -1169,12 +1216,9 @@ int task_load_will_fit(struct task_struct *p, u64 task_load, int cpu,
enum sched_boost_type sched_boost_type(void)
{
- if (sched_boost()) {
- if (min_possible_efficiency != max_possible_efficiency)
- return SCHED_BOOST_ON_BIG;
- else
- return SCHED_BOOST_ON_ALL;
- }
+ if (sched_boost())
+ return sched_boost_policy;
+
return SCHED_BOOST_NONE;
}
@@ -2697,7 +2741,8 @@ static void update_task_demand(struct task_struct *p, struct rq *rq,
void update_task_ravg(struct task_struct *p, struct rq *rq, int event,
u64 wallclock, u64 irqtime)
{
- if (!rq->window_start || sched_disable_window_stats)
+ if (!rq->window_start || sched_disable_window_stats ||
+ p->ravg.mark_start == wallclock)
return;
lockdep_assert_held(&rq->lock);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 1b641e60233e..ada5e580e968 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1399,6 +1399,8 @@ extern u64 cpu_upmigrate_discourage_read_u64(struct cgroup_subsys_state *css,
struct cftype *cft);
extern int cpu_upmigrate_discourage_write_u64(struct cgroup_subsys_state *css,
struct cftype *cft, u64 upmigrate_discourage);
+extern void sched_hmp_parse_dt(void);
+extern void init_sched_hmp_boost_policy(void);
#else /* CONFIG_SCHED_HMP */
@@ -1589,6 +1591,8 @@ static inline void post_big_task_count_change(void) { }
static inline void set_hmp_defaults(void) { }
static inline void clear_reserved(int cpu) { }
+static inline void sched_hmp_parse_dt(void) {}
+static inline void init_sched_hmp_boost_policy(void) {}
#define trace_sched_cpu_load(...)
#define trace_sched_cpu_load_lb(...)
diff --git a/kernel/sched/sched_avg.c b/kernel/sched/sched_avg.c
index c70e0466c36c..29d8a26a78ed 100644
--- a/kernel/sched/sched_avg.c
+++ b/kernel/sched/sched_avg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2015-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -60,17 +60,17 @@ void sched_get_nr_running_avg(int *avg, int *iowait_avg, int *big_avg)
spin_lock_irqsave(&per_cpu(nr_lock, cpu), flags);
curr_time = sched_clock();
+ diff = curr_time - per_cpu(last_time, cpu);
+ BUG_ON((s64)diff < 0);
+
tmp_avg += per_cpu(nr_prod_sum, cpu);
- tmp_avg += per_cpu(nr, cpu) *
- (curr_time - per_cpu(last_time, cpu));
+ tmp_avg += per_cpu(nr, cpu) * diff;
tmp_big_avg += per_cpu(nr_big_prod_sum, cpu);
- tmp_big_avg += nr_eligible_big_tasks(cpu) *
- (curr_time - per_cpu(last_time, cpu));
+ tmp_big_avg += nr_eligible_big_tasks(cpu) * diff;
tmp_iowait += per_cpu(iowait_prod_sum, cpu);
- tmp_iowait += nr_iowait_cpu(cpu) *
- (curr_time - per_cpu(last_time, cpu));
+ tmp_iowait += nr_iowait_cpu(cpu) * diff;
per_cpu(last_time, cpu) = curr_time;
@@ -107,14 +107,15 @@ EXPORT_SYMBOL(sched_get_nr_running_avg);
*/
void sched_update_nr_prod(int cpu, long delta, bool inc)
{
- int diff;
- s64 curr_time;
+ u64 diff;
+ u64 curr_time;
unsigned long flags, nr_running;
spin_lock_irqsave(&per_cpu(nr_lock, cpu), flags);
nr_running = per_cpu(nr, cpu);
curr_time = sched_clock();
diff = curr_time - per_cpu(last_time, cpu);
+ BUG_ON((s64)diff < 0);
per_cpu(last_time, cpu) = curr_time;
per_cpu(nr, cpu) = nr_running + (inc ? delta : -delta);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 36ea0d54e05b..902657d4cac5 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1020,7 +1020,7 @@ choice
config DEBUG_SPINLOCK_BITE_ON_BUG
bool "Cause a Watchdog Bite on Spinlock bug"
- depends on MSM_WATCHDOG_V2
+ depends on QCOM_WATCHDOG_V2
help
On a spinlock bug, cause a watchdog bite so that we can get the precise
state of the system captured at the time of spin dump. This is mutually
diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c
index 6d6893c7d99d..4e3a205551e0 100644
--- a/net/rmnet_data/rmnet_data_vnd.c
+++ b/net/rmnet_data/rmnet_data_vnd.c
@@ -928,7 +928,7 @@ int rmnet_vnd_add_tc_flow(uint32_t id, uint32_t map_flow, uint32_t tc_flow)
list_add(&(itm->list), &(dev_conf->flow_head));
write_unlock_irqrestore(&dev_conf->flow_map_lock, flags);
- LOGD("Created flow mapping [%s][0x%08X][0x%08X]@%p",
+ LOGD("Created flow mapping [%s][0x%08X][0x%08X]@%pK",
dev->name, itm->map_flow_id, itm->tc_flow_id[0], itm);
return RMNET_CONFIG_OK;
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f237a2188fe1..f580a1048d65 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -136,7 +136,9 @@ snd-soc-wcd9335-objs := wcd9335.o
snd-soc-wcd934x-objs := wcd934x.o
snd-soc-wcd9xxx-objs := wcd9xxx-resmgr.o wcd9xxx-mbhc.o wcd9xxx-common.o wcdcal-hwdep.o
snd-soc-wcd9xxx-v2-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o
-audio-ext-clock-objs := audio-ext-clk.o
+ifeq ($(CONFIG_COMMON_CLK_MSM), y)
+ audio-ext-clock-objs := audio-ext-clk.o
+endif
snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o
snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o wsa881x-temp-sensor.o
snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o
@@ -344,7 +346,9 @@ obj-$(CONFIG_SND_SOC_WCD9320) += snd-soc-wcd9320.o
obj-$(CONFIG_SND_SOC_WCD9330) += snd-soc-wcd9330.o
obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o
obj-$(CONFIG_SND_SOC_WCD934X) += wcd934x/
-obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clock.o
+ifeq ($(CONFIG_COMMON_CLK_MSM), y)
+ obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clock.o
+endif
obj-$(CONFIG_SND_SOC_WCD9XXX) += snd-soc-wcd9xxx.o
obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += snd-soc-wcd9xxx-v2.o
obj-$(CONFIG_SND_SOC_WCD_CPE) += snd-soc-wcd-cpe.o
diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c
index 69246ac9cc87..ee8b27dbec64 100644
--- a/sound/soc/codecs/wcd-dsp-mgr.c
+++ b/sound/soc/codecs/wcd-dsp-mgr.c
@@ -16,6 +16,8 @@
#include <linux/stringify.h>
#include <linux/of.h>
#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <soc/qcom/ramdump.h>
#include <sound/wcd-dsp-mgr.h>
#include "wcd-dsp-utils.h"
@@ -75,6 +77,32 @@ static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type);
#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state)
+/* SSR relate status macros */
+#define WDSP_SSR_STATUS_WDSP_READY BIT(0)
+#define WDSP_SSR_STATUS_CDC_READY BIT(1)
+#define WDSP_SSR_STATUS_READY \
+ (WDSP_SSR_STATUS_WDSP_READY | WDSP_SSR_STATUS_CDC_READY)
+#define WDSP_SSR_READY_WAIT_TIMEOUT (10 * HZ)
+
+enum wdsp_ssr_type {
+
+ /* Init value, indicates there is no SSR in progress */
+ WDSP_SSR_TYPE_NO_SSR = 0,
+
+ /*
+ * Indicates WDSP crashed. The manager driver internally
+ * decides when to perform WDSP restart based on the
+ * users of wdsp. Hence there is no explicit WDSP_UP.
+ */
+ WDSP_SSR_TYPE_WDSP_DOWN,
+
+ /* Indicates codec hardware is down */
+ WDSP_SSR_TYPE_CDC_DOWN,
+
+ /* Indicates codec hardware is up, trigger to restart WDSP */
+ WDSP_SSR_TYPE_CDC_UP,
+};
+
struct wdsp_cmpnt {
/* OF node of the phandle */
@@ -96,6 +124,21 @@ struct wdsp_cmpnt {
struct wdsp_cmpnt_ops *ops;
};
+struct wdsp_ramdump_data {
+
+ /* Ramdump device */
+ void *rd_dev;
+
+ /* DMA address of the dump */
+ dma_addr_t rd_addr;
+
+ /* Virtual address of the dump */
+ void *rd_v_addr;
+
+ /* Data provided through error interrupt */
+ struct wdsp_err_intr_arg err_data;
+};
+
struct wdsp_mgr_priv {
/* Manager driver's struct device pointer */
@@ -130,8 +173,35 @@ struct wdsp_mgr_priv {
/* Lock for serializing ops called by components */
struct mutex api_mutex;
+
+ struct wdsp_ramdump_data dump_data;
+
+ /* SSR related */
+ enum wdsp_ssr_type ssr_type;
+ struct mutex ssr_mutex;
+ struct work_struct ssr_work;
+ u16 ready_status;
+ struct completion ready_compl;
};
+static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type)
+{
+ switch (type) {
+ case WDSP_SSR_TYPE_NO_SSR:
+ return "NO_SSR";
+ case WDSP_SSR_TYPE_WDSP_DOWN:
+ return "WDSP_DOWN";
+ case WDSP_SSR_TYPE_CDC_DOWN:
+ return "CDC_DOWN";
+ case WDSP_SSR_TYPE_CDC_UP:
+ return "CDC_UP";
+ default:
+ pr_err("%s: Invalid ssr_type %d\n",
+ __func__, type);
+ return "Invalid";
+ }
+}
+
static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type)
{
switch (type) {
@@ -148,6 +218,26 @@ static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type)
}
}
+static void __wdsp_clr_ready_locked(struct wdsp_mgr_priv *wdsp,
+ u16 value)
+{
+ wdsp->ready_status &= ~(value);
+ WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status);
+}
+
+static void __wdsp_set_ready_locked(struct wdsp_mgr_priv *wdsp,
+ u16 value, bool mark_complete)
+{
+ wdsp->ready_status |= value;
+ WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status);
+
+ if (mark_complete &&
+ wdsp->ready_status == WDSP_SSR_STATUS_READY) {
+ WDSP_DBG(wdsp, "marking ready completion");
+ complete(&wdsp->ready_compl);
+ }
+}
+
static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp,
enum wdsp_event_type event,
void *data)
@@ -199,6 +289,18 @@ static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp,
return ret;
}
+static void wdsp_deinit_components(struct wdsp_mgr_priv *wdsp)
+{
+ struct wdsp_cmpnt *cmpnt;
+ int i;
+
+ for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
+ cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+ if (cmpnt && cmpnt->ops && cmpnt->ops->deinit)
+ cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data);
+ }
+}
+
static int wdsp_init_components(struct wdsp_mgr_priv *wdsp)
{
struct wdsp_cmpnt *cmpnt;
@@ -230,6 +332,8 @@ static int wdsp_init_components(struct wdsp_mgr_priv *wdsp)
cmpnt->ops->deinit(cmpnt->cdev,
cmpnt->priv_data);
}
+ } else {
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_INIT, NULL);
}
return ret;
@@ -272,6 +376,7 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
struct wdsp_cmpnt *ctl;
struct wdsp_img_segment *seg = NULL;
enum wdsp_event_type pre, post;
+ long status;
int ret;
ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL);
@@ -279,9 +384,11 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
if (type == WDSP_ELF_FLAG_RE) {
pre = WDSP_EVENT_PRE_DLOAD_CODE;
post = WDSP_EVENT_POST_DLOAD_CODE;
+ status = WDSP_STATUS_CODE_DLOADED;
} else if (type == WDSP_ELF_FLAG_WRITE) {
pre = WDSP_EVENT_PRE_DLOAD_DATA;
post = WDSP_EVENT_POST_DLOAD_DATA;
+ status = WDSP_STATUS_DATA_DLOADED;
} else {
WDSP_ERR(wdsp, "Invalid type %u", type);
return -EINVAL;
@@ -312,6 +419,8 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
}
}
+ WDSP_SET_STATUS(wdsp, status);
+
/* Notify all components that image is downloaded */
wdsp_broadcast_event_downseq(wdsp, post, NULL);
@@ -321,42 +430,47 @@ done:
return ret;
}
-static void wdsp_load_fw_image(struct work_struct *work)
+static int wdsp_init_and_dload_code_sections(struct wdsp_mgr_priv *wdsp)
{
- struct wdsp_mgr_priv *wdsp;
- struct wdsp_cmpnt *cmpnt;
- int ret, idx;
-
- wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work);
- if (!wdsp) {
- pr_err("%s: Invalid private_data\n", __func__);
- goto done;
- }
+ int ret;
+ bool is_initialized;
- /* Initialize the components first */
- ret = wdsp_init_components(wdsp);
- if (IS_ERR_VALUE(ret))
- goto done;
+ is_initialized = WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_INITIALIZED);
- /* Set init done status */
- WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
+ if (!is_initialized) {
+ /* Components are not initialized yet, initialize them */
+ ret = wdsp_init_components(wdsp);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "INIT failed, err = %d", ret);
+ goto done;
+ }
+ WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
+ }
/* Download the read-execute sections of image */
ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE);
if (IS_ERR_VALUE(ret)) {
WDSP_ERR(wdsp, "Error %d to download code sections", ret);
- for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
- cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
- if (cmpnt->ops && cmpnt->ops->deinit)
- cmpnt->ops->deinit(cmpnt->cdev,
- cmpnt->priv_data);
- }
- WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
+ goto done;
}
-
- WDSP_SET_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED);
done:
- return;
+ return ret;
+}
+
+static void wdsp_load_fw_image(struct work_struct *work)
+{
+ struct wdsp_mgr_priv *wdsp;
+ int ret;
+
+ wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work);
+ if (!wdsp) {
+ pr_err("%s: Invalid private_data\n", __func__);
+ return;
+ }
+
+ ret = wdsp_init_and_dload_code_sections(wdsp);
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp, "dload code sections failed, err = %d", ret);
}
static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
@@ -377,8 +491,6 @@ static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
goto done;
}
- WDSP_SET_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
-
wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL);
ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
@@ -399,6 +511,21 @@ static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp)
{
int ret;
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+
+ /*
+ * If Disable happened while SSR is in progress, then set the SSR
+ * ready status indicating WDSP is now ready. Ignore the disable
+ * event here and let the SSR handler go through shutdown.
+ */
+ if (wdsp->ssr_type != WDSP_SSR_TYPE_NO_SSR) {
+ __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY, true);
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+ return 0;
+ }
+
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+
/* Make sure wdsp is in good state */
if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status);
@@ -478,8 +605,190 @@ static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev,
return cmpnt->cdev;
}
+static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp)
+{
+ struct wdsp_img_section img_section;
+ struct wdsp_err_intr_arg *data = &wdsp->dump_data.err_data;
+ struct ramdump_segment rd_seg;
+ int ret = 0;
+
+ if (wdsp->ssr_type != WDSP_SSR_TYPE_WDSP_DOWN ||
+ !data->mem_dumps_enabled) {
+ WDSP_DBG(wdsp, "cannot dump memory, ssr_type %s, dumps %s",
+ wdsp_get_ssr_type_string(wdsp->ssr_type),
+ !(data->mem_dumps_enabled) ? "disabled" : "enabled");
+ goto done;
+ }
+
+ if (data->dump_size == 0 ||
+ data->remote_start_addr < wdsp->base_addr) {
+ WDSP_ERR(wdsp, "Invalid start addr 0x%x or dump_size 0x%zx",
+ data->remote_start_addr, data->dump_size);
+ goto done;
+ }
+
+ if (!wdsp->dump_data.rd_dev) {
+ WDSP_ERR(wdsp, "Ramdump device is not setup");
+ goto done;
+ }
+
+ WDSP_DBG(wdsp, "base_addr 0x%x, dump_start_addr 0x%x, dump_size 0x%zx",
+ wdsp->base_addr, data->remote_start_addr, data->dump_size);
+
+ /* Allocate memory for dumps */
+ wdsp->dump_data.rd_v_addr = dma_alloc_coherent(wdsp->mdev,
+ data->dump_size,
+ &wdsp->dump_data.rd_addr,
+ GFP_KERNEL);
+ if (!wdsp->dump_data.rd_v_addr)
+ goto done;
+
+ img_section.addr = data->remote_start_addr - wdsp->base_addr;
+ img_section.size = data->dump_size;
+ img_section.data = wdsp->dump_data.rd_v_addr;
+
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT,
+ WDSP_EVENT_READ_SECTION,
+ &img_section);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Failed to read dumps, size 0x%zx at addr 0x%x",
+ img_section.size, img_section.addr);
+ goto err_read_dumps;
+ }
+
+ rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr;
+ rd_seg.size = img_section.size;
+ rd_seg.v_address = wdsp->dump_data.rd_v_addr;
+
+ ret = do_ramdump(wdsp->dump_data.rd_dev, &rd_seg, 1);
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp, "do_ramdump failed with error %d", ret);
+
+err_read_dumps:
+ dma_free_coherent(wdsp->mdev, data->dump_size,
+ wdsp->dump_data.rd_v_addr, wdsp->dump_data.rd_addr);
+done:
+ return;
+}
+
+static void wdsp_ssr_work_fn(struct work_struct *work)
+{
+ struct wdsp_mgr_priv *wdsp;
+ int ret;
+
+ wdsp = container_of(work, struct wdsp_mgr_priv, ssr_work);
+ if (!wdsp) {
+ pr_err("%s: Invalid private_data\n", __func__);
+ return;
+ }
+
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+
+ wdsp_collect_ramdumps(wdsp);
+
+ /* In case of CDC_DOWN event, the DSP is already shutdown */
+ if (wdsp->ssr_type != WDSP_SSR_TYPE_CDC_DOWN) {
+ ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
+ WDSP_EVENT_DO_SHUTDOWN, NULL);
+ if (IS_ERR_VALUE(ret))
+ WDSP_ERR(wdsp, "Failed WDSP shutdown, err = %d", ret);
+ }
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL);
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
+
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+ ret = wait_for_completion_timeout(&wdsp->ready_compl,
+ WDSP_SSR_READY_WAIT_TIMEOUT);
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+ if (ret == 0) {
+ WDSP_ERR(wdsp, "wait_for_ready timed out, status = 0x%x",
+ wdsp->ready_status);
+ goto done;
+ }
+
+ /* Data sections are to downloaded per WDSP boot */
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
+
+ /*
+ * Even though code section could possible be retained on DSP
+ * crash, go ahead and still re-download just to avoid any
+ * memory corruption from previous crash.
+ */
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED);
+
+ /* If codec went down, then all components must be re-initialized */
+ if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_DOWN) {
+ wdsp_deinit_components(wdsp);
+ WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
+ }
+
+ ret = wdsp_init_and_dload_code_sections(wdsp);
+ if (IS_ERR_VALUE(ret)) {
+ WDSP_ERR(wdsp, "Failed to dload code sections err = %d",
+ ret);
+ goto done;
+ }
+
+ /* SSR handling is finished, mark SSR type as NO_SSR */
+ wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
+done:
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+}
+
+static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg,
+ enum wdsp_ssr_type ssr_type)
+{
+ enum wdsp_ssr_type current_ssr_type;
+ struct wdsp_err_intr_arg *err_data;
+
+ WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+
+ current_ssr_type = wdsp->ssr_type;
+ WDSP_DBG(wdsp, "Current ssr_type %s, handling ssr_type %s",
+ wdsp_get_ssr_type_string(current_ssr_type),
+ wdsp_get_ssr_type_string(ssr_type));
+ wdsp->ssr_type = ssr_type;
+
+ if (arg) {
+ err_data = (struct wdsp_err_intr_arg *) arg;
+ memcpy(&wdsp->dump_data.err_data, err_data,
+ sizeof(*err_data));
+ } else {
+ memset(&wdsp->dump_data.err_data, 0,
+ sizeof(wdsp->dump_data.err_data));
+ }
+
+ switch (ssr_type) {
+
+ case WDSP_SSR_TYPE_WDSP_DOWN:
+ case WDSP_SSR_TYPE_CDC_DOWN:
+ __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY);
+ if (ssr_type == WDSP_SSR_TYPE_CDC_DOWN)
+ __wdsp_clr_ready_locked(wdsp,
+ WDSP_SSR_STATUS_CDC_READY);
+ wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN,
+ NULL);
+ schedule_work(&wdsp->ssr_work);
+ break;
+
+ case WDSP_SSR_TYPE_CDC_UP:
+ __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true);
+ break;
+
+ default:
+ WDSP_ERR(wdsp, "undefined ssr_type %d\n", ssr_type);
+ /* Revert back the ssr_type for undefined events */
+ wdsp->ssr_type = current_ssr_type;
+ break;
+ }
+
+ WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+
+ return 0;
+}
+
static int wdsp_intr_handler(struct device *wdsp_dev,
- enum wdsp_intr intr)
+ enum wdsp_intr intr, void *arg)
{
struct wdsp_mgr_priv *wdsp;
int ret;
@@ -495,6 +804,9 @@ static int wdsp_intr_handler(struct device *wdsp_dev,
ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC,
WDSP_EVENT_IPC1_INTR, NULL);
break;
+ case WDSP_ERR_INTR:
+ ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN);
+ break;
default:
ret = -EINVAL;
break;
@@ -585,6 +897,11 @@ static int wdsp_mgr_bind(struct device *dev)
wdsp->ops = &wdsp_ops;
+ /* Setup ramdump device */
+ wdsp->dump_data.rd_dev = create_ramdump_device("wdsp", dev);
+ if (!wdsp->dump_data.rd_dev)
+ dev_info(dev, "%s: create_ramdump_device failed\n", __func__);
+
ret = component_bind_all(dev, wdsp->ops);
if (IS_ERR_VALUE(ret))
WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret);
@@ -616,6 +933,11 @@ static void wdsp_mgr_unbind(struct device *dev)
component_unbind_all(dev, wdsp->ops);
+ if (wdsp->dump_data.rd_dev) {
+ destroy_ramdump_device(wdsp->dump_data.rd_dev);
+ wdsp->dump_data.rd_dev = NULL;
+ }
+
/* Clear all status bits */
wdsp->status = 0x00;
@@ -746,6 +1068,12 @@ static int wdsp_mgr_probe(struct platform_device *pdev)
INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image);
INIT_LIST_HEAD(wdsp->seg_list);
mutex_init(&wdsp->api_mutex);
+ mutex_init(&wdsp->ssr_mutex);
+ wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
+ wdsp->ready_status = WDSP_SSR_STATUS_READY;
+ INIT_WORK(&wdsp->ssr_work, wdsp_ssr_work_fn);
+ init_completion(&wdsp->ready_compl);
+ arch_setup_dma_ops(wdsp->mdev, 0, 0, NULL, 0);
dev_set_drvdata(mdev, wdsp);
ret = component_master_add_with_match(mdev, &wdsp_master_ops,
@@ -759,6 +1087,7 @@ static int wdsp_mgr_probe(struct platform_device *pdev)
err_master_add:
mutex_destroy(&wdsp->api_mutex);
+ mutex_destroy(&wdsp->ssr_mutex);
err_dt_parse:
devm_kfree(mdev, wdsp->seg_list);
devm_kfree(mdev, wdsp);
@@ -775,6 +1104,7 @@ static int wdsp_mgr_remove(struct platform_device *pdev)
component_master_del(mdev, &wdsp_master_ops);
mutex_destroy(&wdsp->api_mutex);
+ mutex_destroy(&wdsp->ssr_mutex);
devm_kfree(mdev, wdsp->seg_list);
devm_kfree(mdev, wdsp);
dev_set_drvdata(mdev, NULL);
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index d207d9ccda34..3cbc1e7821cf 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -2318,7 +2318,7 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc,
schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
usecs_to_jiffies(FW_READ_TIMEOUT));
else
- pr_err("%s: Skipping to read mbhc fw, 0x%p %p\n",
+ pr_err("%s: Skipping to read mbhc fw, 0x%pK %pK\n",
__func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
}
pr_debug("%s: leave %d\n", __func__, rc);
diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c
index 3049d87c6c05..60efcb174740 100644
--- a/sound/soc/codecs/wcd-spi.c
+++ b/sound/soc/codecs/wcd-spi.c
@@ -639,12 +639,10 @@ static int wcd_spi_init(struct spi_device *spi)
WCD_SPI_SLAVE_TRNS_LEN,
0xFFFF0000,
(WCD_SPI_RW_MULTI_MAX_LEN / 4) << 16);
-done:
- return ret;
-
err_wr_en:
wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
WCD_SPI_CLK_FLAG_IMMEDIATE);
+done:
return ret;
}
@@ -813,6 +811,27 @@ static int wdsp_spi_dload_section(struct spi_device *spi,
return ret;
}
+static int wdsp_spi_read_section(struct spi_device *spi, void *data)
+{
+ struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+ struct wdsp_img_section *sec = data;
+ struct wcd_spi_msg msg;
+ int ret;
+
+ msg.remote_addr = sec->addr + wcd_spi->mem_base_addr;
+ msg.data = sec->data;
+ msg.len = sec->size;
+
+ dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n",
+ __func__, msg.remote_addr, msg.len);
+
+ ret = wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_READ);
+ if (IS_ERR_VALUE(ret))
+ dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n",
+ __func__, msg.remote_addr, msg.len);
+ return ret;
+}
+
static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
enum wdsp_event_type event,
void *data)
@@ -824,6 +843,7 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
__func__, event);
switch (event) {
+ case WDSP_EVENT_PRE_DLOAD_CODE:
case WDSP_EVENT_PRE_DLOAD_DATA:
ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
WCD_SPI_CLK_FLAG_IMMEDIATE);
@@ -846,6 +866,11 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
case WDSP_EVENT_DLOAD_SECTION:
ret = wdsp_spi_dload_section(spi, data);
break;
+
+ case WDSP_EVENT_READ_SECTION:
+ ret = wdsp_spi_read_section(spi, data);
+ break;
+
default:
dev_dbg(&spi->dev, "%s: Unhandled event %d\n",
__func__, event);
diff --git a/sound/soc/codecs/wcd9330.c b/sound/soc/codecs/wcd9330.c
index a8d6e0fa4732..fa396aa55ac9 100644
--- a/sound/soc/codecs/wcd9330.c
+++ b/sound/soc/codecs/wcd9330.c
@@ -5474,7 +5474,7 @@ static int tomtom_set_channel_map(struct snd_soc_dai *dai,
struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec);
struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
if (!tx_slot || !rx_slot) {
- pr_err("%s: Invalid tx_slot=%p, rx_slot=%p\n",
+ pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n",
__func__, tx_slot, rx_slot);
return -EINVAL;
}
@@ -5519,7 +5519,7 @@ static int tomtom_get_channel_map(struct snd_soc_dai *dai,
case AIF2_PB:
case AIF3_PB:
if (!rx_slot || !rx_num) {
- pr_err("%s: Invalid rx_slot %p or rx_num %p\n",
+ pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n",
__func__, rx_slot, rx_num);
return -EINVAL;
}
@@ -5538,7 +5538,7 @@ static int tomtom_get_channel_map(struct snd_soc_dai *dai,
case AIF4_VIFEED:
case AIF4_MAD_TX:
if (!tx_slot || !tx_num) {
- pr_err("%s: Invalid tx_slot %p or tx_num %p\n",
+ pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n",
__func__, tx_slot, tx_num);
return -EINVAL;
}
@@ -8228,7 +8228,7 @@ static void tomtom_compute_impedance(struct wcd9xxx_mbhc *mbhc, s16 *l, s16 *r,
struct tomtom_priv *tomtom;
if (!mbhc) {
- pr_err("%s: Invalid parameters mbhc = %p\n",
+ pr_err("%s: Invalid parameters mbhc = %pK\n",
__func__, mbhc);
return;
}
@@ -8287,7 +8287,7 @@ static void tomtom_zdet_error_approx(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
const int shift = TOMTOM_ZDET_ERROR_APPROX_SHIFT;
if (!zl || !zr || !mbhc) {
- pr_err("%s: Invalid parameters zl = %p zr = %p, mbhc = %p\n",
+ pr_err("%s: Invalid parameters zl = %pK zr = %pK, mbhc = %pK\n",
__func__, zl, zr, mbhc);
return;
}
@@ -8602,7 +8602,7 @@ static int tomtom_codec_fll_enable(struct snd_soc_codec *codec,
struct wcd9xxx *wcd9xxx;
if (!codec || !codec->control_data) {
- pr_err("%s: Invalid codec handle, %p\n",
+ pr_err("%s: Invalid codec handle, %pK\n",
__func__, codec);
return -EINVAL;
}
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 2cc895cf1d32..46b8e7f72eb8 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -11080,7 +11080,7 @@ static int tasha_get_channel_map(struct snd_soc_dai *dai,
case AIF4_PB:
case AIF_MIX1_PB:
if (!rx_slot || !rx_num) {
- pr_err("%s: Invalid rx_slot %p or rx_num %p\n",
+ pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n",
__func__, rx_slot, rx_num);
return -EINVAL;
}
@@ -11099,7 +11099,7 @@ static int tasha_get_channel_map(struct snd_soc_dai *dai,
case AIF4_MAD_TX:
case AIF4_VIFEED:
if (!tx_slot || !tx_num) {
- pr_err("%s: Invalid tx_slot %p or tx_num %p\n",
+ pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n",
__func__, tx_slot, tx_num);
return -EINVAL;
}
@@ -11137,7 +11137,7 @@ static int tasha_set_channel_map(struct snd_soc_dai *dai,
core = dev_get_drvdata(dai->codec->dev->parent);
if (!tx_slot || !rx_slot) {
- pr_err("%s: Invalid tx_slot=%p, rx_slot=%p\n",
+ pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n",
__func__, tx_slot, rx_slot);
return -EINVAL;
}
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
index 225b3a755f66..e649770297f1 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
@@ -28,19 +28,22 @@
#define WCD_MEM_ENABLE_MAX_RETRIES 20
#define WCD_DSP_BOOT_TIMEOUT_MS 3000
#define WCD_SYSFS_ENTRY_MAX_LEN 8
-
-#define WCD_CNTL_MUTEX_LOCK(codec, lock) \
-{ \
- dev_dbg(codec->dev, "mutex_lock(%s)\n", \
- __func__); \
- mutex_lock(&lock); \
+#define WCD_PROCFS_ENTRY_MAX_LEN 16
+#define WCD_934X_RAMDUMP_START_ADDR 0x20100000
+#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128)
+
+#define WCD_CNTL_MUTEX_LOCK(codec, lock) \
+{ \
+ dev_dbg(codec->dev, "%s: mutex_lock(%s)\n", \
+ __func__, __stringify_1(lock)); \
+ mutex_lock(&lock); \
}
-#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \
-{ \
- dev_dbg(codec->dev, "mutex_unlock(%s)\n",\
- __func__); \
- mutex_unlock(&lock); \
+#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \
+{ \
+ dev_dbg(codec->dev, "%s: mutex_unlock(%s)\n", \
+ __func__, __stringify_1(lock)); \
+ mutex_unlock(&lock); \
}
struct wcd_cntl_attribute {
@@ -147,6 +150,97 @@ static struct kobj_type wcd_cntl_ktype = {
.sysfs_ops = &wcd_cntl_sysfs_ops,
};
+static void wcd_cntl_change_online_state(struct wcd_dsp_cntl *cntl,
+ u8 online)
+{
+ struct wdsp_ssr_entry *ssr_entry = &cntl->ssr_entry;
+ unsigned long ret;
+
+ WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
+ ssr_entry->offline = !online;
+ /* Make sure the write is complete */
+ wmb();
+ ret = xchg(&ssr_entry->offline_change, 1);
+ wake_up_interruptible(&ssr_entry->offline_poll_wait);
+ dev_dbg(cntl->codec->dev,
+ "%s: requested %u, offline %u offline_change %u, ret = %ldn",
+ __func__, online, ssr_entry->offline,
+ ssr_entry->offline_change, ret);
+ WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
+}
+
+static ssize_t wdsp_ssr_entry_read(struct snd_info_entry *entry,
+ void *file_priv_data, struct file *file,
+ char __user *buf, size_t count, loff_t pos)
+{
+ int len = 0;
+ char buffer[WCD_PROCFS_ENTRY_MAX_LEN];
+ struct wcd_dsp_cntl *cntl;
+ struct wdsp_ssr_entry *ssr_entry;
+ ssize_t ret;
+ u8 offline;
+
+ cntl = (struct wcd_dsp_cntl *) entry->private_data;
+ if (!cntl) {
+ pr_err("%s: Invalid private data for SSR procfs entry\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ssr_entry = &cntl->ssr_entry;
+
+ WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
+ offline = ssr_entry->offline;
+ /* Make sure the read is complete */
+ rmb();
+ dev_dbg(cntl->codec->dev, "%s: offline = %s\n", __func__,
+ offline ? "true" : "false");
+ len = snprintf(buffer, sizeof(buffer), "%s\n",
+ offline ? "OFFLINE" : "ONLINE");
+ ret = simple_read_from_buffer(buf, count, &pos, buffer, len);
+ WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
+
+ return ret;
+}
+
+static unsigned int wdsp_ssr_entry_poll(struct snd_info_entry *entry,
+ void *private_data, struct file *file,
+ poll_table *wait)
+{
+ struct wcd_dsp_cntl *cntl;
+ struct wdsp_ssr_entry *ssr_entry;
+ unsigned int ret = 0;
+
+ if (!entry || !entry->private_data) {
+ pr_err("%s: %s is NULL\n", __func__,
+ (!entry) ? "entry" : "private_data");
+ return -EINVAL;
+ }
+
+ cntl = (struct wcd_dsp_cntl *) entry->private_data;
+ ssr_entry = &cntl->ssr_entry;
+
+ dev_dbg(cntl->codec->dev, "%s: Poll wait, offline = %u\n",
+ __func__, ssr_entry->offline);
+ poll_wait(file, &ssr_entry->offline_poll_wait, wait);
+ dev_dbg(cntl->codec->dev, "%s: Woken up Poll wait, offline = %u\n",
+ __func__, ssr_entry->offline);
+
+ WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
+ if (xchg(&ssr_entry->offline_change, 0))
+ ret = POLLIN | POLLPRI | POLLRDNORM;
+ dev_dbg(cntl->codec->dev, "%s: ret (%d) from poll_wait\n",
+ __func__, ret);
+ WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
+
+ return ret;
+}
+
+static struct snd_info_entry_ops wdsp_ssr_entry_ops = {
+ .read = wdsp_ssr_entry_read,
+ .poll = wdsp_ssr_entry_poll,
+};
+
static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl)
{
struct snd_soc_codec *codec = cntl->codec;
@@ -553,7 +647,8 @@ static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data)
if (cntl->m_dev && cntl->m_ops &&
cntl->m_ops->intr_handler)
- ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_IPC1_INTR);
+ ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_IPC1_INTR,
+ NULL);
else
ret = -EINVAL;
@@ -568,8 +663,10 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data)
{
struct wcd_dsp_cntl *cntl = data;
struct snd_soc_codec *codec = cntl->codec;
+ struct wdsp_err_intr_arg arg;
u16 status = 0;
u8 reg_val;
+ int ret = 0;
reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A);
status = status | reg_val;
@@ -580,6 +677,23 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data)
dev_info(codec->dev, "%s: error interrupt status = 0x%x\n",
__func__, status);
+ if ((status & cntl->irqs.fatal_irqs) &&
+ (cntl->m_dev && cntl->m_ops && cntl->m_ops->intr_handler)) {
+ arg.mem_dumps_enabled = cntl->ramdump_enable;
+ arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR;
+ arg.dump_size = WCD_934X_RAMDUMP_SIZE;
+ ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_ERR_INTR,
+ &arg);
+ if (IS_ERR_VALUE(ret))
+ dev_err(cntl->codec->dev,
+ "%s: Failed to handle fatal irq 0x%x\n",
+ __func__, status & cntl->irqs.fatal_irqs);
+ wcd_cntl_change_online_state(cntl, 0);
+ } else {
+ dev_err(cntl->codec->dev, "%s: Invalid intr_handler\n",
+ __func__);
+ }
+
return IRQ_HANDLED;
}
@@ -591,10 +705,15 @@ static int wcd_control_handler(struct device *dev, void *priv_data,
int ret = 0;
switch (event) {
+ case WDSP_EVENT_POST_INIT:
case WDSP_EVENT_POST_DLOAD_CODE:
case WDSP_EVENT_DLOAD_FAILED:
case WDSP_EVENT_POST_SHUTDOWN:
+ if (event == WDSP_EVENT_POST_DLOAD_CODE)
+ /* Mark DSP online since code download is complete */
+ wcd_cntl_change_online_state(cntl, 1);
+
/* Disable CPAR */
wcd_cntl_cpar_ctrl(cntl, false);
/* Disable all the clocks */
@@ -605,12 +724,8 @@ static int wcd_control_handler(struct device *dev, void *priv_data,
__func__, ret);
break;
- case WDSP_EVENT_PRE_DLOAD_CODE:
-
- wcd_cntl_enable_memory(cntl);
- break;
-
case WDSP_EVENT_PRE_DLOAD_DATA:
+ case WDSP_EVENT_PRE_DLOAD_CODE:
/* Enable all the clocks */
ret = wcd_cntl_clocks_enable(cntl);
@@ -623,6 +738,9 @@ static int wcd_control_handler(struct device *dev, void *priv_data,
/* Enable CPAR */
wcd_cntl_cpar_ctrl(cntl, true);
+
+ if (event == WDSP_EVENT_PRE_DLOAD_CODE)
+ wcd_cntl_enable_memory(cntl);
break;
case WDSP_EVENT_DO_BOOT:
@@ -697,6 +815,8 @@ static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl)
debugfs_create_u32("debug_mode", S_IRUGO | S_IWUSR,
cntl->entry, &cntl->debug_mode);
+ debugfs_create_bool("ramdump_enable", S_IRUGO | S_IWUSR,
+ cntl->entry, &cntl->ramdump_enable);
done:
return;
}
@@ -827,6 +947,10 @@ static int wcd_ctrl_component_bind(struct device *dev,
void *data)
{
struct wcd_dsp_cntl *cntl;
+ struct snd_soc_codec *codec;
+ struct snd_card *card;
+ struct snd_info_entry *entry;
+ char proc_name[WCD_PROCFS_ENTRY_MAX_LEN];
int ret = 0;
if (!dev || !master || !data) {
@@ -844,13 +968,47 @@ static int wcd_ctrl_component_bind(struct device *dev,
cntl->m_dev = master;
cntl->m_ops = data;
- if (cntl->m_ops->register_cmpnt_ops)
- ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl,
- &control_ops);
+ if (!cntl->m_ops->register_cmpnt_ops) {
+ dev_err(dev, "%s: invalid master callback register_cmpnt_ops\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
- if (ret)
+ ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, &control_ops);
+ if (ret) {
dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n",
__func__, ret);
+ goto done;
+ }
+
+ codec = cntl->codec;
+ card = codec->component.card->snd_card;
+ snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe",
+ cntl->dsp_instance, "_state");
+ entry = snd_info_create_card_entry(card, proc_name, card->proc_root);
+ if (!entry) {
+ /* Do not treat this as Fatal error */
+ dev_err(dev, "%s: Failed to create procfs entry %s\n",
+ __func__, proc_name);
+ goto done;
+ }
+
+ cntl->ssr_entry.entry = entry;
+ cntl->ssr_entry.offline = 1;
+ entry->size = WCD_PROCFS_ENTRY_MAX_LEN;
+ entry->content = SNDRV_INFO_CONTENT_DATA;
+ entry->c.ops = &wdsp_ssr_entry_ops;
+ entry->private_data = cntl;
+ ret = snd_info_register(entry);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(dev, "%s: Failed to register entry %s, err = %d\n",
+ __func__, proc_name, ret);
+ snd_info_free_entry(entry);
+ /* Let bind still happen even if creating the entry failed */
+ ret = 0;
+ }
+done:
return ret;
}
@@ -929,6 +1087,8 @@ void wcd_dsp_cntl_init(struct snd_soc_codec *codec,
memcpy(&control->irqs, &params->irqs, sizeof(control->irqs));
init_completion(&control->boot_complete);
mutex_init(&control->clk_mutex);
+ mutex_init(&control->ssr_mutex);
+ init_waitqueue_head(&control->ssr_entry.offline_poll_wait);
/*
* The default state of WDSP is in SVS mode.
@@ -981,6 +1141,7 @@ void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl)
component_del(codec->dev, &wcd_ctrl_component_ops);
mutex_destroy(&control->clk_mutex);
+ mutex_destroy(&control->ssr_mutex);
kfree(*cntl);
*cntl = NULL;
}
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h
index 3d6db776a0b5..cd6697b3d641 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h
@@ -54,6 +54,13 @@ struct wcd_dsp_params {
u32 dsp_instance;
};
+struct wdsp_ssr_entry {
+ u8 offline;
+ u8 offline_change;
+ wait_queue_head_t offline_poll_wait;
+ struct snd_info_entry *entry;
+};
+
struct wcd_dsp_cntl {
/* Handle to codec */
struct snd_soc_codec *codec;
@@ -77,6 +84,7 @@ struct wcd_dsp_cntl {
/* Debugfs related */
struct dentry *entry;
u32 debug_mode;
+ bool ramdump_enable;
/* WDSP manager drivers data */
struct device *m_dev;
@@ -88,6 +96,10 @@ struct wcd_dsp_cntl {
/* Keep track of WDSP boot status */
bool is_wdsp_booted;
+
+ /* SSR related */
+ struct wdsp_ssr_entry ssr_entry;
+ struct mutex ssr_mutex;
};
void wcd_dsp_cntl_init(struct snd_soc_codec *codec,
diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h
index ac3031ffe615..940fdf89d361 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-routing.h
+++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h
@@ -21,6 +21,7 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = {
{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
{"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
{"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
+ {"AIF4 MAD", NULL, "AIF4_MAD Mixer"},
/* Virtual input widget Mixer */
{"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"},
@@ -65,6 +66,8 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = {
{"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11"},
{"AIF3_CAP Mixer", "SLIM TX13", "SLIM TX13"},
+ {"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"},
+
{"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"},
{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
{"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
@@ -121,6 +124,7 @@ const struct snd_soc_dapm_route tavil_audio_map[] = {
{"MAD_INP MUX", "MAD", "MAD_SEL MUX"},
{"MAD_INP MUX", "DEC1", "ADC MUX1"},
+ {"MAD_BROADCAST", "Switch", "MAD_INP MUX"},
{"MAD_CPE1", "Switch", "MAD_INP MUX"},
{"MAD_CPE2", "Switch", "MAD_INP MUX"},
diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c
index b4a73d17c322..9e18c17d6f1c 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.c
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -186,6 +186,7 @@ enum {
AIF3_CAP,
AIF4_PB,
AIF4_VIFEED,
+ AIF4_MAD_TX,
NUM_CODEC_DAIS,
};
@@ -301,13 +302,13 @@ static const struct wcd9xxx_ch tavil_tx_chs[WCD934X_TX_MAX] = {
};
static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = {
- 0, /* AIF1_PB */
- BIT(AIF2_CAP) | BIT(AIF3_CAP), /* AIF1_CAP */
- 0, /* AIF2_PB */
- BIT(AIF1_CAP) | BIT(AIF3_CAP), /* AIF2_CAP */
- 0, /* AIF3_PB */
- BIT(AIF1_CAP) | BIT(AIF2_CAP), /* AIF3_CAP */
- 0, /* AIF4_PB */
+ 0, /* AIF1_PB */
+ BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */
+ 0, /* AIF2_PB */
+ BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */
+ 0, /* AIF3_PB */
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */
+ 0, /* AIF4_PB */
};
/* Codec supports 2 IIR filters */
@@ -1271,6 +1272,8 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
return 0;
}
break;
+ case AIF4_MAD_TX:
+ break;
default:
dev_err(codec->dev, "Unknown AIF %d\n", dai_id);
mutex_unlock(&tavil_p->codec_mutex);
@@ -2474,7 +2477,7 @@ done:
return ret;
}
-static int tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable)
+static int __tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable)
{
int rc = 0;
@@ -2519,6 +2522,29 @@ done:
return rc;
}
+static int tavil_codec_ape_enable_mad(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int rc = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x40);
+ rc = __tavil_codec_enable_mad(codec, true);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x00);
+ __tavil_codec_enable_mad(codec, false);
+ break;
+ }
+
+ dev_dbg(tavil->dev, "%s: event = %d\n", __func__, event);
+ return rc;
+}
+
static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -2533,7 +2559,7 @@ static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w,
goto done;
snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x20);
- rc = tavil_codec_enable_mad(codec, true);
+ rc = __tavil_codec_enable_mad(codec, true);
if (IS_ERR_VALUE(rc)) {
tavil->mad_switch_cnt--;
goto done;
@@ -2546,7 +2572,7 @@ static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w,
goto done;
snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x00);
- tavil_codec_enable_mad(codec, false);
+ __tavil_codec_enable_mad(codec, false);
break;
}
done:
@@ -5789,6 +5815,11 @@ static const struct snd_kcontrol_new aif3_cap_mixer[] = {
slim_tx_mixer_get, slim_tx_mixer_put),
};
+static const struct snd_kcontrol_new aif4_mad_mixer[] = {
+ SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+ slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text,
slim_rx_mux_get, slim_rx_mux_put);
WCD_DAPM_ENUM_EXT(slim_rx1, SND_SOC_NOPM, 0, slim_rx_mux_text,
@@ -6110,6 +6141,9 @@ static const struct snd_kcontrol_new mad_cpe1_switch =
static const struct snd_kcontrol_new mad_cpe2_switch =
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+static const struct snd_kcontrol_new mad_brdcst_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
static const struct snd_kcontrol_new adc_us_mux0_switch =
SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
@@ -6532,10 +6566,16 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)),
SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)),
+ SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0,
+ aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)),
SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM,
AIF4_VIFEED, 0, tavil_codec_enable_slimvi_feedback,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0,
+ SND_SOC_NOPM, 0, 0),
+
SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0,
aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)),
SND_SOC_DAPM_INPUT("VIINPUT"),
@@ -6670,6 +6710,10 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
WCD_DAPM_MUX("MAD_SEL MUX", 0, mad_sel),
WCD_DAPM_MUX("MAD_INP MUX", 0, mad_inp_mux),
+ SND_SOC_DAPM_SWITCH_E("MAD_BROADCAST", SND_SOC_NOPM, 0, 0,
+ &mad_brdcst_switch, tavil_codec_ape_enable_mad,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
SND_SOC_DAPM_SWITCH_E("MAD_CPE1", SND_SOC_NOPM, 0, 0,
&mad_cpe1_switch, tavil_codec_cpe_mad_ctl,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -6826,6 +6870,7 @@ static int tavil_get_channel_map(struct snd_soc_dai *dai,
case AIF1_CAP:
case AIF2_CAP:
case AIF3_CAP:
+ case AIF4_MAD_TX:
case AIF4_VIFEED:
if (!tx_slot || !tx_num) {
dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK or tx_num 0x%pK\n",
@@ -6864,6 +6909,7 @@ static int tavil_set_channel_map(struct snd_soc_dai *dai,
{
struct tavil_priv *tavil;
struct wcd9xxx *core;
+ struct wcd9xxx_codec_dai_data *dai_data = NULL;
tavil = snd_soc_codec_get_drvdata(dai->codec);
core = dev_get_drvdata(dai->codec->dev->parent);
@@ -6878,6 +6924,12 @@ static int tavil_set_channel_map(struct snd_soc_dai *dai,
wcd9xxx_init_slimslave(core, core->slim->laddr,
tx_num, tx_slot, rx_num, rx_slot);
+ /* Reserve TX13 for MAD data channel */
+ dai_data = &tavil->dai[AIF4_MAD_TX];
+ if (dai_data)
+ list_add_tail(&core->tx_chs[WCD934X_TX13].list,
+ &dai_data->wcd9xxx_ch_list);
+
return 0;
}
@@ -7208,7 +7260,7 @@ static int tavil_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec);
- int ret;
+ int ret = 0;
dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n",
__func__, dai->name, dai->id, params_rate(params),
@@ -7238,7 +7290,9 @@ static int tavil_hw_params(struct snd_pcm_substream *substream,
tavil->dai[dai->id].rate = params_rate(params);
break;
case SNDRV_PCM_STREAM_CAPTURE:
- ret = tavil_set_decimator_rate(dai, params_rate(params));
+ if (dai->id != AIF4_MAD_TX)
+ ret = tavil_set_decimator_rate(dai,
+ params_rate(params));
if (ret) {
dev_err(tavil->dev, "%s: cannot set TX Decimator rate: %d\n",
__func__, ret);
@@ -7395,6 +7449,20 @@ static struct snd_soc_dai_driver tavil_dai[] = {
},
.ops = &tavil_vi_dai_ops,
},
+ {
+ .name = "tavil_mad1",
+ .id = AIF4_MAD_TX,
+ .capture = {
+ .stream_name = "AIF4 MAD TX",
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = WCD934X_FORMATS_S16_LE,
+ .rate_min = 16000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ .ops = &tavil_dai_ops,
+ },
};
static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil)
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 52ca82fba8e9..2012e4617ee1 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -4675,7 +4675,7 @@ int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
usecs_to_jiffies(FW_READ_TIMEOUT));
else
- pr_debug("%s: Skipping to read mbhc fw, 0x%p %p\n",
+ pr_debug("%s: Skipping to read mbhc fw, 0x%pK %pK\n",
__func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
}
@@ -5073,7 +5073,7 @@ static int wcd9xxx_remeasure_z_values(struct wcd9xxx_mbhc *mbhc,
right = !!(r);
dev_dbg(codec->dev, "%s: Remeasuring impedance values\n", __func__);
- dev_dbg(codec->dev, "%s: l: %p, r: %p, left=%d, right=%d\n", __func__,
+ dev_dbg(codec->dev, "%s: l: %pK, r: %pK, left=%d, right=%d\n", __func__,
l, r, left, right);
/* Remeasure V2 values */
diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c
index e9f167fa643b..3aa9ac8d40b6 100644
--- a/sound/soc/codecs/wcd_cpe_core.c
+++ b/sound/soc/codecs/wcd_cpe_core.c
@@ -473,7 +473,7 @@ static int wcd_cpe_load_fw(struct wcd_cpe_core *core,
bool load_segment;
if (!core || !core->cpe_handle) {
- pr_err("%s: Error CPE core %p\n", __func__,
+ pr_err("%s: Error CPE core %pK\n", __func__,
core);
return -EINVAL;
}
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 28fd8930adb2..d7f4044b71ee 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -917,7 +917,7 @@ int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, u8 num_port,
if (!port || !ch_mask || !ch_rate ||
(num_port > WSA881X_MAX_SWR_PORTS)) {
dev_err(codec->dev,
- "%s: Invalid port=%p, ch_mask=%p, ch_rate=%p\n",
+ "%s: Invalid port=%pK, ch_mask=%pK, ch_rate=%pK\n",
__func__, port, ch_mask, ch_rate);
return -EINVAL;
}
diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c
index f0bf7a0db76f..5c8d91bfe400 100644
--- a/sound/soc/msm/msmcobalt.c
+++ b/sound/soc/msm/msmcobalt.c
@@ -71,6 +71,9 @@
#define WCN_CDC_SLIM_RX_CH_MAX 2
#define WCN_CDC_SLIM_TX_CH_MAX 3
+#define TDM_CHANNEL_MAX 8
+#define TDM_SLOT_OFFSET_MAX 8
+
enum {
SLIM_RX_0 = 0,
SLIM_RX_1,
@@ -170,6 +173,131 @@ struct msm_asoc_wcd93xx_codec {
void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec);
};
+enum {
+ TDM_0 = 0,
+ TDM_1,
+ TDM_2,
+ TDM_3,
+ TDM_4,
+ TDM_5,
+ TDM_6,
+ TDM_7,
+ TDM_PORT_MAX,
+};
+
+enum {
+ TDM_PRI = 0,
+ TDM_SEC,
+ TDM_TERT,
+ TDM_QUAT,
+ TDM_INTERFACE_MAX,
+};
+
+struct tdm_port {
+ u32 mode;
+ u32 channel;
+};
+
+/* TDM default config */
+static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+ { /* PRI TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ },
+ { /* SEC TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ },
+ { /* TERT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ },
+ { /* QUAT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+ }
+};
+
+/* TDM default config */
+static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+ { /* PRI TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ },
+ { /* SEC TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ },
+ { /* TERT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ },
+ { /* QUAT TDM */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+ }
+};
+
+/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */
+static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = {
+ {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */
+ {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */
+};
+
/* Default configuration of slimbus channels */
static struct dev_config slim_rx_cfg[] = {
[SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
@@ -273,6 +401,12 @@ static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025",
"KHZ_96", "KHZ_192", "KHZ_384"};
static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96",
"KHZ_192"};
+static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight"};
+static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"};
+static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32",
+ "KHZ_44P1", "KHZ_48", "KHZ_96",
+ "KHZ_192", "KHZ_352P8", "KHZ_384"};
static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"};
static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16",
"KHZ_32", "KHZ_44P1", "KHZ_48",
@@ -309,6 +443,12 @@ static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate,
ext_disp_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text);
@@ -1359,6 +1499,45 @@ static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
return 1;
}
+static int tdm_get_sample_rate(int value)
+{
+ int sample_rate = 0;
+
+ switch (value) {
+ case 0:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 2:
+ sample_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 3:
+ sample_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 4:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 5:
+ sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 6:
+ sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 7:
+ sample_rate = SAMPLING_RATE_352P8KHZ;
+ break;
+ case 8:
+ sample_rate = SAMPLING_RATE_384KHZ;
+ break;
+ default:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
static int aux_pcm_get_sample_rate(int value)
{
int sample_rate;
@@ -1375,6 +1554,45 @@ static int aux_pcm_get_sample_rate(int value)
return sample_rate;
}
+static int tdm_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val = 0;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_8KHZ:
+ sample_rate_val = 0;
+ break;
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_32KHZ:
+ sample_rate_val = 2;
+ break;
+ case SAMPLING_RATE_44P1KHZ:
+ sample_rate_val = 3;
+ break;
+ case SAMPLING_RATE_48KHZ:
+ sample_rate_val = 4;
+ break;
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 5;
+ break;
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 6;
+ break;
+ case SAMPLING_RATE_352P8KHZ:
+ sample_rate_val = 7;
+ break;
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 8;
+ break;
+ default:
+ sample_rate_val = 4;
+ break;
+ }
+ return sample_rate_val;
+}
+
static int aux_pcm_get_sample_rate_val(int sample_rate)
{
int sample_rate_val;
@@ -1391,6 +1609,361 @@ static int aux_pcm_get_sample_rate_val(int sample_rate)
return sample_rate_val;
}
+static int tdm_get_port_idx(struct snd_kcontrol *kcontrol,
+ struct tdm_port *port)
+{
+ if (port) {
+ if (strnstr(kcontrol->id.name, "PRI",
+ sizeof(kcontrol->id.name))) {
+ port->mode = TDM_PRI;
+ } else if (strnstr(kcontrol->id.name, "SEC",
+ sizeof(kcontrol->id.name))) {
+ port->mode = TDM_SEC;
+ } else if (strnstr(kcontrol->id.name, "TERT",
+ sizeof(kcontrol->id.name))) {
+ port->mode = TDM_TERT;
+ } else if (strnstr(kcontrol->id.name, "QUAT",
+ sizeof(kcontrol->id.name))) {
+ port->mode = TDM_QUAT;
+ } else {
+ pr_err("%s: unsupported mode in: %s",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ if (strnstr(kcontrol->id.name, "RX_0",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_0",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_0;
+ } else if (strnstr(kcontrol->id.name, "RX_1",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_1",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_1;
+ } else if (strnstr(kcontrol->id.name, "RX_2",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_2",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_2;
+ } else if (strnstr(kcontrol->id.name, "RX_3",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_3",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_3;
+ } else if (strnstr(kcontrol->id.name, "RX_4",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_4",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_4;
+ } else if (strnstr(kcontrol->id.name, "RX_5",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_5",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_5;
+ } else if (strnstr(kcontrol->id.name, "RX_6",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_6",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_6;
+ } else if (strnstr(kcontrol->id.name, "RX_7",
+ sizeof(kcontrol->id.name)) ||
+ strnstr(kcontrol->id.name, "TX_7",
+ sizeof(kcontrol->id.name))) {
+ port->channel = TDM_7;
+ } else {
+ pr_err("%s: unsupported channel in: %s",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
+ return 0;
+}
+
+static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+ tdm_rx_cfg[port.mode][port.channel].sample_rate);
+
+ pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_rx_cfg[port.mode][port.channel].sample_rate =
+ tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+ tdm_tx_cfg[port.mode][port.channel].sample_rate);
+
+ pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_tx_cfg[port.mode][port.channel].sample_rate =
+ tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_get_format(int value)
+{
+ int format = 0;
+
+ switch (value) {
+ case 0:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 1:
+ format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 2:
+ format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return format;
+}
+
+static int tdm_get_format_val(int format)
+{
+ int value = 0;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ value = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ value = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ value = 2;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ return value;
+}
+
+static int tdm_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+ tdm_rx_cfg[port.mode][port.channel].bit_format);
+
+ pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_rx_cfg[port.mode][port.channel].bit_format =
+ tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+ tdm_tx_cfg[port.mode][port.channel].bit_format);
+
+ pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_tx_cfg[port.mode][port.channel].bit_format =
+ tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].bit_format,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+
+ ucontrol->value.enumerated.item[0] =
+ tdm_rx_cfg[port.mode][port.channel].channels - 1;
+
+ pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].channels - 1,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_rx_cfg[port.mode][port.channel].channels =
+ ucontrol->value.enumerated.item[0] + 1;
+
+ pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+ tdm_rx_cfg[port.mode][port.channel].channels,
+ ucontrol->value.enumerated.item[0] + 1);
+ }
+ return ret;
+}
+
+static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ ucontrol->value.enumerated.item[0] =
+ tdm_tx_cfg[port.mode][port.channel].channels - 1;
+
+ pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].channels - 1,
+ ucontrol->value.enumerated.item[0]);
+ }
+ return ret;
+}
+
+static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tdm_port port;
+ int ret = tdm_get_port_idx(kcontrol, &port);
+
+ if (ret) {
+ pr_err("%s: unsupported control: %s",
+ __func__, kcontrol->id.name);
+ } else {
+ tdm_tx_cfg[port.mode][port.channel].channels =
+ ucontrol->value.enumerated.item[0] + 1;
+
+ pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+ tdm_tx_cfg[port.mode][port.channel].channels,
+ ucontrol->value.enumerated.item[0] + 1);
+ }
+ return ret;
+}
+
static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol)
{
int idx;
@@ -1789,6 +2362,24 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate,
ext_disp_rx_sample_rate_get,
ext_disp_rx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+ tdm_rx_sample_rate_get,
+ tdm_rx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+ tdm_tx_sample_rate_get,
+ tdm_tx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format,
+ tdm_rx_format_get,
+ tdm_rx_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format,
+ tdm_tx_format_get,
+ tdm_tx_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs,
+ tdm_rx_ch_get,
+ tdm_rx_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs,
+ tdm_tx_ch_get,
+ tdm_tx_ch_put),
SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate,
aux_pcm_rx_sample_rate_get,
aux_pcm_rx_sample_rate_put),
@@ -2126,6 +2717,22 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
rate->min = rate->max = SAMPLING_RATE_48KHZ;
break;
+ case MSM_BACKEND_DAI_TERT_TDM_RX_0:
+ channels->min = channels->max =
+ tdm_rx_cfg[TDM_TERT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_rx_cfg[TDM_TERT][TDM_0].bit_format);
+ rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_TDM_TX_0:
+ channels->min = channels->max =
+ tdm_tx_cfg[TDM_TERT][TDM_0].channels;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ tdm_tx_cfg[TDM_TERT][TDM_0].bit_format);
+ rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate;
+ break;
+
case MSM_BACKEND_DAI_AUXPCM_RX:
rate->min = rate->max =
aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate;
@@ -2643,7 +3250,7 @@ static void *def_tasha_mbhc_cal(void)
return NULL;
#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y))
- S(v_hs_max, 1500);
+ S(v_hs_max, 1600);
#undef S
#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y))
S(num_btn, WCD_MBHC_DEF_BUTTONS);
@@ -3207,6 +3814,157 @@ static struct snd_soc_ops msm_aux_pcm_be_ops = {
.shutdown = msm_aux_pcm_snd_shutdown,
};
+static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width,
+ int slots)
+{
+ unsigned int slot_mask = 0;
+ int i, j;
+ unsigned int *slot_offset;
+
+ for (i = TDM_0; i < TDM_PORT_MAX; i++) {
+ slot_offset = tdm_slot_offset[i];
+
+ for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) {
+ if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ slot_mask |=
+ (1 << ((slot_offset[j] * 8) / slot_width));
+ else
+ break;
+ }
+ }
+
+ return slot_mask;
+}
+
+static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ int channels, slot_width, slots;
+ unsigned int slot_mask;
+ unsigned int *slot_offset;
+ int offset_channels = 0;
+ int i;
+
+ pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+ channels = params_channels(params);
+ switch (channels) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /*
+ * up to 8 channels HW config should
+ * use 32 bit slot width for max support of
+ * stream bit width. (slot_width > bit_width)
+ */
+ slot_width = 32;
+ break;
+ default:
+ pr_err("%s: invalid param format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+ slots = 8;
+ slot_mask = tdm_param_set_slot_mask(cpu_dai->id,
+ slot_width,
+ slots);
+ if (!slot_mask) {
+ pr_err("%s: invalid slot_mask 0x%x\n",
+ __func__, slot_mask);
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("%s: invalid param channels %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+ /* currently only supporting TDM_RX_0 and TDM_TX_0 */
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_PRIMARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_PRIMARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ slot_offset = tdm_slot_offset[TDM_0];
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ offset_channels++;
+ else
+ break;
+ }
+
+ if (offset_channels == 0) {
+ pr_err("%s: slot offset not supported, offset_channels %d\n",
+ __func__, offset_channels);
+ return -EINVAL;
+ }
+
+ if (channels > offset_channels) {
+ pr_err("%s: channels %d exceed offset_channels %d\n",
+ __func__, channels, offset_channels);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ channels, slot_offset);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai, channels,
+ slot_offset, 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+end:
+ return ret;
+}
+
static struct snd_soc_ops msm_be_ops = {
.hw_params = msm_snd_hw_params,
};
@@ -3223,6 +3981,10 @@ static struct snd_soc_ops msm_wcn_ops = {
.hw_params = msm_wcn_hw_params,
};
+static struct snd_soc_ops msm_tdm_be_ops = {
+ .hw_params = msm_tdm_snd_hw_params
+};
+
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link msm_common_dai_links[] = {
/* FrontEnd DAI Links */
@@ -4023,6 +4785,34 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = {
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = 1,
},
+ {
+ .name = LPASS_BE_TERT_TDM_RX_0,
+ .stream_name = "Tertiary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36896",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_0,
+ .stream_name = "Tertiary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36897",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
};
static struct snd_soc_dai_link msm_tasha_be_dai_links[] = {
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index f0ad7daa8275..e661415a08ca 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -5832,11 +5832,6 @@ static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream,
pr_debug("%s: dev_name: %s\n",
__func__, dev_name(dai->dev));
- if (params_rate(params) != 48000) {
- dev_err(dai->dev, "%s: invalid param rate %d\n",
- __func__, params_rate(params));
- return -EINVAL;
- }
if ((params_channels(params) == 0) ||
(params_channels(params) > 8)) {
dev_err(dai->dev, "%s: invalid param channels %d\n",
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index ffa78af72544..e30a4efa6e60 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -2173,69 +2173,74 @@ int adm_arrange_mch_map(struct adm_cmd_device_open_v5 *open, int path,
int channel_mode)
{
int rc = 0, idx;
-
- memset(open->dev_channel_mapping, 0,
- PCM_FORMAT_MAX_NUM_CHANNEL);
-
- if (channel_mode == 1) {
- open->dev_channel_mapping[0] = PCM_CHANNEL_FC;
- } else if (channel_mode == 2) {
- open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
- open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
- } else if (channel_mode == 3) {
- open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
- open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
- open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
- } else if (channel_mode == 4) {
- open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
- open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
- open->dev_channel_mapping[2] = PCM_CHANNEL_LS;
- open->dev_channel_mapping[3] = PCM_CHANNEL_RS;
- } else if (channel_mode == 5) {
- open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
- open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
- open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
- open->dev_channel_mapping[3] = PCM_CHANNEL_LS;
- open->dev_channel_mapping[4] = PCM_CHANNEL_RS;
- } else if (channel_mode == 6) {
- open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
- open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
- open->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
- open->dev_channel_mapping[3] = PCM_CHANNEL_FC;
- open->dev_channel_mapping[4] = PCM_CHANNEL_LS;
- open->dev_channel_mapping[5] = PCM_CHANNEL_RS;
- } else if (channel_mode == 8) {
- open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
- open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
- open->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
- open->dev_channel_mapping[3] = PCM_CHANNEL_FC;
- open->dev_channel_mapping[4] = PCM_CHANNEL_LS;
- open->dev_channel_mapping[5] = PCM_CHANNEL_RS;
- open->dev_channel_mapping[6] = PCM_CHANNEL_LB;
- open->dev_channel_mapping[7] = PCM_CHANNEL_RB;
- } else {
- pr_err("%s: invalid num_chan %d\n", __func__,
- channel_mode);
- rc = -EINVAL;
- goto inval_ch_mod;
- }
-
+ memset(open->dev_channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
switch (path) {
case ADM_PATH_PLAYBACK:
idx = ADM_MCH_MAP_IDX_PLAYBACK;
break;
case ADM_PATH_LIVE_REC:
+ case ADM_PATH_NONLIVE_REC:
idx = ADM_MCH_MAP_IDX_REC;
break;
default:
goto non_mch_path;
- break;
};
-
- if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map)
+ if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map) {
memcpy(open->dev_channel_mapping,
- multi_ch_maps[idx].channel_mapping,
- PCM_FORMAT_MAX_NUM_CHANNEL);
+ multi_ch_maps[idx].channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ } else {
+ if (channel_mode == 1) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 2) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channel_mode == 3) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 4) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_LS;
+ open->dev_channel_mapping[3] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 5) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ open->dev_channel_mapping[3] = PCM_CHANNEL_LS;
+ open->dev_channel_mapping[4] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 6) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ open->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ open->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ open->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 7) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+ open->dev_channel_mapping[3] = PCM_CHANNEL_LFE;
+ open->dev_channel_mapping[4] = PCM_CHANNEL_LB;
+ open->dev_channel_mapping[5] = PCM_CHANNEL_RB;
+ open->dev_channel_mapping[6] = PCM_CHANNEL_CS;
+ } else if (channel_mode == 8) {
+ open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ open->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ open->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+ open->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+ open->dev_channel_mapping[6] = PCM_CHANNEL_LB;
+ open->dev_channel_mapping[7] = PCM_CHANNEL_RB;
+ } else {
+ pr_err("%s: invalid num_chan %d\n", __func__,
+ channel_mode);
+ rc = -EINVAL;
+ goto inval_ch_mod;
+ }
+ }
non_mch_path:
inval_ch_mod: