summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/msm/imem.txt6
-rw-r--r--Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt1
-rw-r--r--Documentation/devicetree/bindings/arm/msm/qcom,osm.txt104
-rw-r--r--Documentation/devicetree/bindings/batterydata/batterydata.txt221
-rw-r--r--Documentation/devicetree/bindings/clock/qcom,gcc.txt1
-rw-r--r--Documentation/devicetree/bindings/clock/qcom,gpucc.txt23
-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/input/pixart-pat9125-switch.txt10
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt3
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-cci.txt4
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-cpp.txt7
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-ir-cut.txt26
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-ir-led.txt26
-rw-r--r--Documentation/devicetree/bindings/pci/msm_pcie.txt20
-rw-r--r--Documentation/devicetree/bindings/platform/msm/ipa.txt2
-rw-r--r--Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt4
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt9
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt14
-rw-r--r--Documentation/devicetree/bindings/scheduler/sched_hmp.txt35
-rw-r--r--[-rwxr-xr-x]Documentation/devicetree/bindings/sound/qcom-audio-dev.txt23
-rw-r--r--Documentation/devicetree/bindings/thermal/tsens.txt1
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt1
-rw-r--r--Documentation/scheduler/sched-hmp.txt25
-rw-r--r--Documentation/sysctl/kernel.txt40
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi80
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35597-dsc-wqxga-cmd.dtsi64
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35597-dsc-wqxga-video.dtsi60
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-sharp-1080p-cmd.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-sharp-dualmipi-1080p-120hz.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi81
-rw-r--r--arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi377
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmfalcon.dtsi169
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi32
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-camera.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi67
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi59
-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-sensor-qrd.dtsi356
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi155
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi23
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi26
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss-pll.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi11
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi19
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi60
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi93
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi524
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi27
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi339
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi10
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi199
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-bus.dtsi1217
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi322
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi16
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-regulator.dtsi183
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon.dtsi150
-rw-r--r--arch/arm/boot/dts/qcom/msmtriton-ion.dtsi52
-rw-r--r--arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi136
-rw-r--r--arch/arm/boot/dts/qcom/msmtriton.dtsi283
-rw-r--r--arch/arm/configs/msmcortex_defconfig3
-rw-r--r--arch/arm/configs/msmfalcon_defconfig15
-rw-r--r--arch/arm/kernel/irq.c11
-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_defconfig5
-rw-r--r--arch/arm64/configs/msm_defconfig5
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig11
-rw-r--r--arch/arm64/configs/msmcortex_defconfig10
-rw-r--r--arch/arm64/configs/msmfalcon-perf_defconfig5
-rw-r--r--arch/arm64/configs/msmfalcon_defconfig6
-rw-r--r--arch/arm64/kernel/cpuinfo.c3
-rw-r--r--arch/arm64/kernel/perf_event.c2
-rw-r--r--arch/arm64/kernel/process.c2
-rw-r--r--arch/arm64/mm/dma-mapping.c265
-rw-r--r--block/blk-mq-cpumap.c16
-rw-r--r--block/blk-mq.c106
-rw-r--r--block/blk-mq.h2
-rw-r--r--drivers/base/core.c5
-rw-r--r--drivers/base/cpu.c58
-rw-r--r--drivers/base/regmap/regmap-swr.c14
-rw-r--r--drivers/char/diag/diag_dci.c4
-rw-r--r--drivers/char/diag/diagfwd_cntl.c4
-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-gpu-cobalt.c3
-rw-r--r--drivers/clk/msm/clock-mmss-cobalt.c46
-rw-r--r--drivers/clk/msm/clock-osm.c205
-rw-r--r--drivers/clk/msm/clock.h4
-rw-r--r--drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c34
-rw-r--r--drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c4
-rw-r--r--drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h10
-rw-r--r--drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c4
-rw-r--r--drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c4
-rw-r--r--drivers/clk/qcom/Kconfig22
-rw-r--r--drivers/clk/qcom/Makefile4
-rw-r--r--drivers/clk/qcom/clk-rcg.h9
-rw-r--r--drivers/clk/qcom/clk-rcg2.c136
-rw-r--r--drivers/clk/qcom/clk-smd-rpm.c397
-rw-r--r--drivers/clk/qcom/gcc-msmfalcon.c2800
-rw-r--r--drivers/clk/qcom/gdsc-regulator.c745
-rw-r--r--drivers/clk/qcom/gpucc-msmfalcon.c482
-rw-r--r--drivers/clk/qcom/vdd-level-falcon.h140
-rw-r--r--drivers/cpuidle/cpuidle.c9
-rw-r--r--drivers/cpuidle/lpm-levels-of.c210
-rw-r--r--drivers/cpuidle/lpm-levels.c661
-rw-r--r--drivers/cpuidle/lpm-levels.h26
-rw-r--r--drivers/crypto/msm/qce50.c14
-rw-r--r--drivers/devfreq/bimc-bwmon.c2
-rw-r--r--drivers/edac/cortex_arm64_edac.c1
-rw-r--r--drivers/gpu/msm/a4xx_reg.h1
-rw-r--r--drivers/gpu/msm/a5xx_reg.h4
-rw-r--r--drivers/gpu/msm/adreno-gpulist.h22
-rw-r--r--drivers/gpu/msm/adreno.c22
-rw-r--r--drivers/gpu/msm/adreno.h8
-rw-r--r--drivers/gpu/msm/adreno_a4xx.c266
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c32
-rw-r--r--drivers/gpu/msm/adreno_dispatch.c2
-rw-r--r--drivers/gpu/msm/adreno_drawctxt.c19
-rw-r--r--drivers/gpu/msm/adreno_perfcounter.c5
-rw-r--r--drivers/gpu/msm/adreno_snapshot.c154
-rw-r--r--drivers/gpu/msm/kgsl.c12
-rw-r--r--drivers/gpu/msm/kgsl.h15
-rw-r--r--drivers/gpu/msm/kgsl_device.h18
-rw-r--r--drivers/gpu/msm/kgsl_mmu.c7
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c377
-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_sharedmem.c12
-rw-r--r--drivers/gpu/msm/kgsl_snapshot.c77
-rw-r--r--drivers/gpu/msm/kgsl_snapshot.h30
-rw-r--r--drivers/hid/hid-apple.c3
-rw-r--r--drivers/hid/hid-core.c2
-rw-r--r--drivers/hwmon/qpnp-adc-common.c42
-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.c14
-rw-r--r--drivers/input/misc/ots_pat9125/pat9125_linux_driver.c470
-rw-r--r--drivers/input/misc/ots_pat9125/pixart_ots.c71
-rw-r--r--drivers/input/misc/ots_pat9125/pixart_ots.h16
-rw-r--r--drivers/input/misc/ots_pat9125/pixart_platform.h25
-rw-r--r--drivers/input/touchscreen/gt9xx/goodix_tool.c437
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.c425
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.h128
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_firmware.h6
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_update.c1007
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c20
-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.c5
-rw-r--r--drivers/irqchip/irq-gic-v3.c2
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c2
-rw-r--r--drivers/media/dvb-core/demux.h216
-rw-r--r--drivers/media/dvb-core/dmxdev.c3941
-rw-r--r--drivers/media/dvb-core/dmxdev.h137
-rw-r--r--drivers/media/dvb-core/dvb_demux.c2272
-rw-r--r--drivers/media/dvb-core/dvb_demux.h272
-rw-r--r--drivers/media/dvb-core/dvb_net.c6
-rw-r--r--drivers/media/dvb-core/dvb_ringbuffer.c71
-rw-r--r--drivers/media/dvb-core/dvb_ringbuffer.h30
-rw-r--r--drivers/media/platform/msm/Kconfig2
-rw-r--r--drivers/media/platform/msm/Makefile2
-rw-r--r--drivers/media/platform/msm/broadcast/Kconfig14
-rw-r--r--drivers/media/platform/msm/broadcast/Makefile4
-rw-r--r--drivers/media/platform/msm/broadcast/tspp.c3094
-rw-r--r--drivers/media/platform/msm/camera_v2/common/cam_soc_api.c22
-rw-r--r--drivers/media/platform/msm/camera_v2/common/cam_soc_api.h16
-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/pproc/cpp/msm_cpp.c16
-rw-r--r--drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h1
-rw-r--r--drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c18
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/Makefile2
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c14
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c75
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h4
-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_dt_util.c80
-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/ir_cut/Makefile4
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c665
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h72
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/ir_led/Makefile4
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c462
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h71
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c1
-rw-r--r--drivers/media/platform/msm/dvb/Kconfig10
-rw-r--r--drivers/media/platform/msm/dvb/Makefile2
-rw-r--r--drivers/media/platform/msm/dvb/adapter/Makefile6
-rw-r--r--drivers/media/platform/msm/dvb/adapter/mpq_adapter.c211
-rw-r--r--drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c827
-rw-r--r--drivers/media/platform/msm/dvb/demux/Kconfig46
-rw-r--r--drivers/media/platform/msm/dvb/demux/Makefile14
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c5195
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h1027
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_sw.c280
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c1968
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_sdmx.c1023
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_sdmx.h368
-rw-r--r--drivers/media/platform/msm/dvb/include/mpq_adapter.h199
-rw-r--r--drivers/media/platform/msm/dvb/include/mpq_dvb_debug.h41
-rw-r--r--drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h462
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_core.c123
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_core.h6
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c34
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c47
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h7
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c104
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h3
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h2
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_util.c7
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c72
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c58
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc.c4
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c55
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_dcvs.c8
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.c8
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_internal.h2
-rw-r--r--drivers/mfd/wcd934x-regmap.c4
-rw-r--r--drivers/mfd/wcd9xxx-core.c5
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c9
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.h3
-rw-r--r--drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c5
-rw-r--r--drivers/misc/qseecom.c8
-rw-r--r--drivers/net/usb/cdc_ncm.c20
-rw-r--r--drivers/net/wireless/ath/wil6210/Makefile1
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c237
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c53
-rw-r--r--drivers/net/wireless/ath/wil6210/ftm.c903
-rw-r--r--drivers/net/wireless/ath/wil6210/ftm.h512
-rw-r--r--drivers/net/wireless/ath/wil6210/fw.h14
-rw-r--r--drivers/net/wireless/ath/wil6210/fw_inc.c92
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c60
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c34
-rw-r--r--drivers/net/wireless/ath/wil6210/p2p.c43
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c9
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c9
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h29
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c44
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.h932
-rw-r--r--drivers/net/wireless/cnss/cnss_common.c71
-rw-r--r--drivers/net/wireless/cnss/cnss_common.h1
-rw-r--r--drivers/net/wireless/cnss/cnss_pci.c60
-rw-r--r--drivers/pci/host/pci-msm.c219
-rw-r--r--drivers/phy/phy-qcom-ufs.c15
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c2
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_i.h4
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c153
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa.c145
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c2
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_flt.c10
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_i.h13
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c53
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h14
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c181
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_rt.c20
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c62
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c171
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c25
-rw-r--r--drivers/platform/msm/qpnp-revid.c9
-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.h39
-rw-r--r--drivers/power/qcom-charger/fg-memif.c4
-rw-r--r--drivers/power/qcom-charger/fg-util.c132
-rw-r--r--drivers/power/qcom-charger/qpnp-fg-gen3.c627
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c336
-rw-r--r--drivers/power/qcom-charger/smb-lib.c378
-rw-r--r--drivers/power/qcom-charger/smb-lib.h46
-rw-r--r--drivers/power/qcom-charger/smb-reg.h9
-rw-r--r--drivers/power/qcom-charger/smb138x-charger.c221
-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/power/reset/msm-poweroff.c197
-rw-r--r--drivers/regulator/core.c2
-rw-r--r--drivers/regulator/cpr3-mmss-regulator.c13
-rw-r--r--drivers/regulator/cpr3-regulator.c159
-rw-r--r--drivers/regulator/cpr3-regulator.h5
-rw-r--r--drivers/regulator/cpr3-util.c101
-rw-r--r--drivers/regulator/cprh-kbss-regulator.c194
-rw-r--r--drivers/scsi/ufs/ufs-debugfs.c4
-rw-r--r--drivers/scsi/ufs/ufs_test.c20
-rw-r--r--drivers/soc/qcom/Kconfig41
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/common_log.c36
-rw-r--r--drivers/soc/qcom/core_ctl_helper.c99
-rw-r--r--drivers/soc/qcom/glink.c2
-rw-r--r--drivers/soc/qcom/glink_ssr.c4
-rw-r--r--drivers/soc/qcom/icnss.c356
-rw-r--r--drivers/soc/qcom/ipc_router_glink_xprt.c49
-rw-r--r--drivers/soc/qcom/jtag-fuse.c4
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.c159
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.h3
-rw-r--r--drivers/soc/qcom/pil-msa.c10
-rw-r--r--drivers/soc/qcom/qdsp6v2/Makefile3
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr.c202
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr_v2.c20
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr_v3.c18
-rw-r--r--drivers/soc/qcom/qdsp6v2/audio_notifier.c635
-rw-r--r--drivers/soc/qcom/qdsp6v2/audio_pdr.c148
-rw-r--r--drivers/soc/qcom/qdsp6v2/audio_ssr.c66
-rw-r--r--drivers/soc/qcom/rpm-smd-debug.c2
-rw-r--r--drivers/soc/qcom/secure_buffer.c2
-rw-r--r--drivers/soc/qcom/service-notifier.c21
-rw-r--r--drivers/soc/qcom/smcinvoke.c24
-rw-r--r--drivers/soc/qcom/spss_utils.c6
-rw-r--r--drivers/soc/qcom/subsys-pil-tz.c21
-rw-r--r--drivers/soc/qcom/subsystem_restart.c46
-rw-r--r--drivers/soc/qcom/system_stats.c2
-rw-r--r--drivers/soc/qcom/watchdog_v2.c43
-rw-r--r--drivers/soc/qcom/wcd-dsp-glink.c227
-rw-r--r--drivers/soc/qcom/wlan_firmware_service_v01.c38
-rw-r--r--drivers/soc/qcom/wlan_firmware_service_v01.h11
-rw-r--r--drivers/soundwire/swr-wcd-ctrl.c31
-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.c11
-rw-r--r--drivers/staging/android/uapi/msm_ion.h32
-rw-r--r--drivers/thermal/lmh_lite.c15
-rw-r--r--drivers/thermal/msm-tsens.c109
-rw-r--r--drivers/thermal/msm_thermal.c18
-rw-r--r--drivers/thermal/qpnp-adc-tm.c2
-rw-r--r--drivers/tty/serial/msm_serial_hs.c4
-rw-r--r--drivers/tty/sysrq.c6
-rw-r--r--drivers/usb/core/hcd.c3
-rw-r--r--drivers/usb/core/hub.c6
-rw-r--r--drivers/usb/dwc3/core.c14
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c58
-rw-r--r--drivers/usb/dwc3/gadget.c39
-rw-r--r--drivers/usb/gadget/Kconfig11
-rw-r--r--drivers/usb/gadget/composite.c16
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_cdev.c27
-rw-r--r--drivers/usb/gadget/function/f_fs.c2
-rw-r--r--drivers/usb/gadget/function/f_gsi.h19
-rw-r--r--drivers/usb/gadget/function/f_mtp.c3
-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.c57
-rw-r--r--drivers/usb/pd/qpnp-pdphy.c8
-rw-r--r--drivers/usb/phy/phy-msm-qusb-v2.c24
-rw-r--r--drivers/video/fbdev/msm/mdss.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_debug_xlog.c2
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c26
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c12
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c419
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h11
-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_phy_v3.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_status.c10
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_hdcp_1x.c91
-rw-r--r--drivers/video/fbdev/msm/mdss_hdcp_1x.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_panel.c69
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_panel.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c11
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_util.c68
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_util.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c51
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c115
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_debug.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_hwio.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c87
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c13
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c37
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c107
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pipe.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c14
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_trace.h40
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_wfd.c2
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h10
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.c10
-rw-r--r--drivers/video/fbdev/msm/msm_mdss_io_8974.c17
-rw-r--r--fs/ecryptfs/kthread.c13
-rw-r--r--fs/fuse/passthrough.c4
-rw-r--r--fs/proc/root.c7
-rw-r--r--include/dt-bindings/clock/qcom,gcc-msmfalcon.h394
-rw-r--r--include/dt-bindings/clock/qcom,gpu-msmfalcon.h47
-rw-r--r--include/dt-bindings/clock/qcom,rpmcc.h105
-rw-r--r--include/dt-bindings/msm/msm-bus-ids.h26
-rw-r--r--include/linux/cpumask.h8
-rw-r--r--include/linux/device.h1
-rw-r--r--include/linux/dma-mapping-fast.h2
-rw-r--r--include/linux/hrtimer.h6
-rw-r--r--include/linux/iommu.h4
-rw-r--r--include/linux/ipa.h1
-rw-r--r--include/linux/msm_dma_iommu_mapping.h2
-rw-r--r--include/linux/percpu-rwsem.h84
-rw-r--r--include/linux/perf_event.h5
-rw-r--r--include/linux/qcom_tspp.h99
-rw-r--r--include/linux/qdsp6v2/apr.h4
-rw-r--r--include/linux/qdsp6v2/audio_notifier.h105
-rw-r--r--include/linux/qdsp6v2/audio_pdr.h101
-rw-r--r--include/linux/qdsp6v2/audio_ssr.h78
-rw-r--r--include/linux/qpnp/qpnp-revid.h1
-rw-r--r--include/linux/rcu_sync.h1
-rw-r--r--include/linux/sched.h43
-rw-r--r--include/linux/sched/sysctl.h17
-rw-r--r--include/linux/tick.h12
-rw-r--r--include/linux/timer.h3
-rw-r--r--include/linux/usb/gadget.h1
-rw-r--r--include/linux/usb/hcd.h2
-rw-r--r--include/media/msm_cam_sensor.h17
-rw-r--r--include/net/cnss.h3
-rw-r--r--include/soc/qcom/core_ctl.h35
-rw-r--r--include/soc/qcom/icnss.h4
-rw-r--r--include/soc/qcom/secure_buffer.h1
-rw-r--r--include/soc/qcom/service-notifier.h5
-rw-r--r--include/sound/apr_audio-v2.h153
-rw-r--r--include/sound/q6afe-v2.h6
-rw-r--r--include/sound/q6asm-v2.h64
-rw-r--r--include/sound/wcd-dsp-mgr.h14
-rw-r--r--include/trace/events/power.h43
-rw-r--r--include/trace/events/sched.h78
-rw-r--r--include/trace/events/trace_msm_low_power.h108
-rw-r--r--include/uapi/linux/dvb/dmx.h725
-rw-r--r--include/uapi/linux/ipa_qmi_service_v01.h86
-rw-r--r--include/uapi/linux/msm_vidc_dec.h6
-rw-r--r--include/uapi/linux/rmnet_ipa_fd_ioctl.h7
-rw-r--r--include/uapi/media/msm_cam_sensor.h18
-rw-r--r--include/uapi/media/msm_camsensor_sdk.h31
-rw-r--r--include/uapi/media/msmb_camera.h4
-rw-r--r--include/uapi/sound/wcd-dsp-glink.h2
-rw-r--r--init/Kconfig36
-rw-r--r--kernel/cgroup.c13
-rw-r--r--kernel/cpu.c17
-rw-r--r--kernel/cpuset.c17
-rw-r--r--kernel/events/core.c9
-rw-r--r--kernel/fork.c4
-rw-r--r--kernel/irq/cpuhotplug.c14
-rw-r--r--kernel/locking/percpu-rwsem.c229
-rw-r--r--kernel/power/qos.c7
-rw-r--r--kernel/rcu/sync.c12
-rw-r--r--kernel/sched/Makefile2
-rw-r--r--kernel/sched/core.c317
-rw-r--r--kernel/sched/core_ctl.c1055
-rw-r--r--kernel/sched/core_ctl.h24
-rw-r--r--kernel/sched/fair.c121
-rw-r--r--kernel/sched/hmp.c106
-rw-r--r--kernel/sched/rt.c13
-rw-r--r--kernel/sched/sched.h11
-rw-r--r--kernel/sched/sched_avg.c19
-rw-r--r--kernel/sched/tune.c17
-rw-r--r--kernel/smp.c4
-rw-r--r--kernel/sysctl.c43
-rw-r--r--kernel/time/hrtimer.c104
-rw-r--r--kernel/time/timer.c68
-rw-r--r--kernel/trace/power-traces.c2
-rw-r--r--kernel/watchdog.c22
-rw-r--r--lib/Kconfig.debug2
-rw-r--r--mm/vmstat.c7
-rw-r--r--net/core/rtnetlink.c18
-rw-r--r--net/ipc_router/ipc_router_socket.c10
-rw-r--r--net/ipv4/netfilter/arp_tables.c41
-rw-r--r--net/ipv4/netfilter/ip_tables.c46
-rw-r--r--net/ipv6/netfilter/ip6_tables.c46
-rw-r--r--net/rmnet_data/rmnet_data_vnd.c2
-rw-r--r--security/Kconfig9
-rw-r--r--security/keys/key.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.c63
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.h12
-rw-r--r--sound/soc/codecs/wcd-spi.c31
-rw-r--r--sound/soc/codecs/wcd9330.c12
-rw-r--r--sound/soc/codecs/wcd9335.c137
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-dsd.c36
-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-mbhc.c29
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-mbhc.h1
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-routing.h4
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c671
-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.c7
-rw-r--r--sound/soc/msm/Kconfig13
-rw-r--r--sound/soc/msm/msm-cpe-lsm.c14
-rw-r--r--sound/soc/msm/msm-dai-fe.c68
-rw-r--r--sound/soc/msm/msmcobalt.c2273
-rw-r--r--sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c2
-rw-r--r--sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c21
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c252
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c26
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c62
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h4
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c474
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h8
-rw-r--r--sound/soc/msm/qdsp6v2/msm-qti-pp-config.c6
-rw-r--r--sound/soc/msm/qdsp6v2/msm-qti-pp-config.h37
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c109
-rw-r--r--sound/soc/msm/qdsp6v2/q6afe.c75
-rw-r--r--sound/soc/msm/qdsp6v2/q6asm.c505
-rw-r--r--sound/soc/msm/qdsp6v2/q6audio-v2.c24
-rw-r--r--sound/soc/soc-dapm.c3
-rw-r--r--tools/perf/util/evsel.c15
520 files changed, 56440 insertions, 7067 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/imem.txt b/Documentation/devicetree/bindings/arm/msm/imem.txt
index d1f8ce1e5ac8..a9d2a2456cfd 100644
--- a/Documentation/devicetree/bindings/arm/msm/imem.txt
+++ b/Documentation/devicetree/bindings/arm/msm/imem.txt
@@ -46,6 +46,12 @@ Required properties:
-compatible: "qcom,msm-imem-restart_reason
-reg: start address and size of restart_reason region in imem
+Download Mode Type:
+-------------------
+Required properties:
+-compatible: "qcom,msm-imem-dload-type"
+-reg: start address and size of dload type region in imem
+
Download Mode:
--------------
Required properties:
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt b/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt
index 56559a69eb46..296e5dd0e383 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt
@@ -34,6 +34,7 @@ Optional properties:
- qcom,ipi-ping : (boolean) send keep alive ping to other cpus if present
- qcom,wakeup-enable : (boolean) enable non secure watchdog to freeze / unfreeze
automatically across suspend / resume path.
+- qcom,scandump-size : size of scan dump memory region
Example:
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
index 518cc6f85f95..c4d651e36d02 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
@@ -56,15 +56,17 @@ Properties:
Usage: required
Value type: <prop-encoded-array>
Definition: Array which defines the frequency in Hertz, frequency,
- PLL override data, and ACC level used by the OSM hardware
- for each supported DCVS setpoint of the Power cluster.
+ PLL override data, ACC level, and virtual corner used
+ by the OSM hardware for each supported DCVS setpoint
+ of the Power cluster.
- qcom,perfcl-speedbinX-v0
Usage: required
Value type: <prop-encoded-array>
Definition: Array which defines the frequency in Hertz, frequency,
- PLL override data, and ACC level used by the OSM hardware
- for each supported DCVS setpoint of the Performance cluster.
+ PLL override data, ACC level and virtual corner used
+ by the OSM hardware for each supported DCVS setpoint
+ of the Performance cluster.
- qcom,osm-no-tz
Usage: optional
@@ -317,55 +319,55 @@ Example:
interrupt-names = "pwrcl-irq", "perfcl-irq";
qcom,pwrcl-speedbin0-v0 =
- < 300000000 0x0004000f 0x031e001e 0x1>,
- < 345600000 0x05040012 0x04200020 0x1>,
- < 422400000 0x05040016 0x04200020 0x1>,
- < 499200000 0x0504001a 0x05200020 0x1>,
- < 576000000 0x0504001e 0x06200020 0x1>,
- < 633600000 0x04040021 0x07200020 0x1>,
- < 710400000 0x04040025 0x07200020 0x1>,
- < 806400000 0x0404002a 0x08220022 0x2>,
- < 883200000 0x0404002e 0x09250025 0x2>,
- < 960000000 0x04040032 0x0a280028 0x2>,
- < 1036800000 0x04040036 0x0b2b002b 0x3>,
- < 1113600000 0x0404003a 0x0c2e002e 0x3>,
- < 1190400000 0x0404003e 0x0c320032 0x3>,
- < 1248000000 0x04040041 0x0d340034 0x3>,
- < 1324800000 0x04040045 0x0e370037 0x3>,
- < 1401600000 0x04040049 0x0f3a003a 0x3>,
- < 1478400000 0x0404004d 0x103e003e 0x3>,
- < 1574400000 0x04040052 0x10420042 0x4>,
- < 1651200000 0x04040056 0x11450045 0x4>,
- < 1728000000 0x0404005a 0x12480048 0x4>,
- < 1804800000 0x0404005e 0x134b004b 0x4>,
- < 1881600000 0x04040062 0x144e004e 0x4>;
+ < 300000000 0x0004000f 0x031e001e 0x1 1 >,
+ < 345600000 0x05040012 0x04200020 0x1 2 >,
+ < 422400000 0x05040016 0x04200020 0x1 3 >,
+ < 499200000 0x0504001a 0x05200020 0x1 4 >,
+ < 576000000 0x0504001e 0x06200020 0x1 5 >,
+ < 633600000 0x04040021 0x07200020 0x1 6 >,
+ < 710400000 0x04040025 0x07200020 0x1 7 >,
+ < 806400000 0x0404002a 0x08220022 0x2 8 >,
+ < 883200000 0x0404002e 0x09250025 0x2 9 >,
+ < 960000000 0x04040032 0x0a280028 0x2 10 >,
+ < 1036800000 0x04040036 0x0b2b002b 0x3 11 >,
+ < 1113600000 0x0404003a 0x0c2e002e 0x3 12 >,
+ < 1190400000 0x0404003e 0x0c320032 0x3 13 >,
+ < 1248000000 0x04040041 0x0d340034 0x3 14 >,
+ < 1324800000 0x04040045 0x0e370037 0x3 15 >,
+ < 1401600000 0x04040049 0x0f3a003a 0x3 16 >,
+ < 1478400000 0x0404004d 0x103e003e 0x3 17 >,
+ < 1574400000 0x04040052 0x10420042 0x4 18 >,
+ < 1651200000 0x04040056 0x11450045 0x4 19 >,
+ < 1728000000 0x0404005a 0x12480048 0x4 20>,
+ < 1804800000 0x0404005e 0x134b004b 0x4 21 >,
+ < 1881600000 0x04040062 0x144e004e 0x4 22 >;
qcom,perfcl-speedbin0-v0 =
- < 300000000 0x0004000f 0x03200020 0x1>,
- < 345600000 0x05040012 0x04200020 0x1>,
- < 422400000 0x05040016 0x04200020 0x1>,
- < 480000000 0x05040019 0x05200020 0x1>,
- < 556800000 0x0504001d 0x06200020 0x1>,
- < 633600000 0x04040021 0x07200020 0x1>,
- < 710400000 0x04040025 0x07200020 0x1>,
- < 787200000 0x04040029 0x08210021 0x1>,
- < 844800000 0x0404002c 0x09240024 0x2>,
- < 902400000 0x0404002f 0x09260026 0x2>,
- < 979200000 0x04040033 0x0a290029 0x2>,
- < 1056000000 0x04040037 0x0b2c002c 0x2>,
- < 1171200000 0x0404003d 0x0c300030 0x3>,
- < 1248000000 0x04040041 0x0d340034 0x3>,
- < 1324800000 0x04040045 0x0e370037 0x3>,
- < 1401600000 0x04040049 0x0f3b003b 0x3>,
- < 1478400000 0x0404004d 0x0f3e003e 0x3>,
- < 1536000000 0x04040050 0x10400040 0x3>,
- < 1632000000 0x04040055 0x11440044 0x4>,
- < 1708800000 0x04040059 0x12480048 0x4>,
- < 1785600000 0x0404005d 0x134a004a 0x4>,
- < 1862400000 0x04040061 0x134e004e 0x4>,
- < 1939200000 0x04040065 0x14510051 0x4>,
- < 2016000000 0x04040069 0x15540054 0x4>,
- < 2092800000 0x0404006d 0x16570057 0x4>;
+ < 300000000 0x0004000f 0x03200020 0x1 1 >,
+ < 345600000 0x05040012 0x04200020 0x1 2 >,
+ < 422400000 0x05040016 0x04200020 0x1 3 >,
+ < 480000000 0x05040019 0x05200020 0x1 4 >,
+ < 556800000 0x0504001d 0x06200020 0x1 5 >,
+ < 633600000 0x04040021 0x07200020 0x1 6 >,
+ < 710400000 0x04040025 0x07200020 0x1 7 >,
+ < 787200000 0x04040029 0x08210021 0x1 8 >,
+ < 844800000 0x0404002c 0x09240024 0x2 9 >,
+ < 902400000 0x0404002f 0x09260026 0x2 10 >,
+ < 979200000 0x04040033 0x0a290029 0x2 11 >,
+ < 1056000000 0x04040037 0x0b2c002c 0x2 12 >,
+ < 1171200000 0x0404003d 0x0c300030 0x3 13 >,
+ < 1248000000 0x04040041 0x0d340034 0x3 14 >,
+ < 1324800000 0x04040045 0x0e370037 0x3 15 >,
+ < 1401600000 0x04040049 0x0f3b003b 0x3 16 >,
+ < 1478400000 0x0404004d 0x0f3e003e 0x3 17 >,
+ < 1536000000 0x04040050 0x10400040 0x3 18 >,
+ < 1632000000 0x04040055 0x11440044 0x4 19 >,
+ < 1708800000 0x04040059 0x12480048 0x4 20 >,
+ < 1785600000 0x0404005d 0x134a004a 0x4 21 >,
+ < 1862400000 0x04040061 0x134e004e 0x4 22 >,
+ < 1939200000 0x04040065 0x14510051 0x4 23 >,
+ < 2016000000 0x04040069 0x15540054 0x4 24 >,
+ < 2092800000 0x0404006d 0x16570057 0x4 25 >;
qcom,osm-no-tz;
qcom,osm-pll-setup;
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,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
index 72f82f444091..1330d2bdc18d 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
@@ -14,6 +14,7 @@ Required properties :
"qcom,gcc-msm8974pro"
"qcom,gcc-msm8974pro-ac"
"qcom,gcc-msm8996"
+ "qcom,gcc-msmfalcon"
- reg : shall contain base register location and length
- #clock-cells : shall contain 1
diff --git a/Documentation/devicetree/bindings/clock/qcom,gpucc.txt b/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
new file mode 100644
index 000000000000..9f8ea0d6ef8f
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
@@ -0,0 +1,23 @@
+Qualcomm Technologies, Inc Graphics Clock & Reset Controller Binding
+--------------------------------------------------------------------
+
+Required properties :
+- compatible : shall contain only one of the following:
+
+ "qcom,gpucc-msmfalcon"
+
+- reg : shall contain base register location and length
+- #clock-cells : shall contain 1
+- #reset-cells : shall contain 1
+
+Optional properties :
+- #power-domain-cells : shall contain 1
+
+Example:
+ clock-controller@4000000 {
+ compatible = "qcom,gpucc-msmfalcon";
+ reg = <<0x5065000 0x10000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ #power-domain-cells = <1>;
+ };
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/input/pixart-pat9125-switch.txt b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt
new file mode 100644
index 000000000000..02f21835f870
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt
@@ -0,0 +1,10 @@
+PixArt pat9125 rotating switch
+
+The Pixart's PAT9125 controller is connected to the host processor via I2C.
+It detects the rotation when user rotates the switch and generates interrupt
+to the Host processor. The host processor reads the direction and number of
+steps over I2C and passes the data to the rest of the system.
+
+Required properties:
+
+ - compatible : should be "pixart,pat9125".
diff --git a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
index 4de22947b333..4c676fa66e62 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
@@ -35,6 +35,7 @@ Optional properties:
min y, max x and max y values.
- goodix,i2c-pull-up : To specify pull up is required.
- goodix,no-force-update : To specify force update is allowed.
+ - goodix,enable-power-off : Power off touchscreen during suspend.
- goodix,button-map : Button map of key codes. The number of key codes
depend on panel.
- goodix,cfg-data0 : Touch screen controller config data group 0. Ask vendor
@@ -55,6 +56,7 @@ Optional properties:
to provide that.
- goodix,cfg-data5 : Touch screen controller config data group 5. Ask vendor
to provide that.
+ - goodix,fw-name : Touch screen controller firmware file name.
Example:
i2c@f9927000 {
goodix@5d {
@@ -89,5 +91,6 @@ i2c@f9927000 {
20 21 22 24 26 28 29 2A FF FF
FF FF FF FF FF FF FF 22 22 22
22 22 22 FF 07 01];
+ goodix,fw_name = "gtp_fw.bin";
};
};
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
index fb1ca0261f9c..991c6d4ec255 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -168,6 +168,10 @@ Optional properties:
property should contain phandle of respective eeprom nodes
- qcom,ois-src : if optical image stabilization is supported by this sensor,
this property should contain phandle of respective ois node
+- qcom,ir-led-src : if ir led is supported by this sensor, this property
+ should contain phandle of respective ir-led node
+- qcom,ir-cut-src : if ir cut is supported by this sensor, this property
+ should contain phandle of respective ir-cut node
* Qualcomm Technologies, Inc. MSM ACTUATOR
diff --git a/Documentation/devicetree/bindings/media/video/msm-cpp.txt b/Documentation/devicetree/bindings/media/video/msm-cpp.txt
index b39c20ecbf22..52abf409cb65 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cpp.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cpp.txt
@@ -22,6 +22,10 @@ Required properties:
case dynamic clock scaling based on prevalent streams need lower clock rate.
- qcom,cpp-fw-payload-info: Child node for cpp node having infomration on
cpp firmware payload offsets. This is mandatory node.
+- resets: reset specifier pair consists of phandle for the reset controller
+ and reset lines used by this controller.
+- reset-names: reset signal name strings sorted in the same order as the resets
+ property.
Required properties of the child node:
- qcom,stripe-base = Base offset of stripes in cpp payload.
@@ -120,6 +124,7 @@ Example:
qcom,ref-we-mmu-pf-ptr-off = <22>;
qcom,set-group-buffer-len = <135>;
qcom,dup-frame-indicator-off = <70>;
-
+ resets = <&clock_mmss MMSS_CAMSS_MICRO_BCR>;
+ reset-names = "micro_iface_reset";
};
};
diff --git a/Documentation/devicetree/bindings/media/video/msm-ir-cut.txt b/Documentation/devicetree/bindings/media/video/msm-ir-cut.txt
new file mode 100644
index 000000000000..96cb28d692c1
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-ir-cut.txt
@@ -0,0 +1,26 @@
+* QTI MSM IR CUT
+
+Required properties:
+- cell-index : ir cut filter hardware core index
+- compatible :
+ - "qcom,ir-cut"
+
+Optional properties:
+- gpios : should specify the gpios to be used for the ir cut filter.
+- qcom,gpio-req-tbl-num : should contain index to gpios specific to ir cut filter
+- qcom,gpio-req-tbl-flags : should contain direction of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
+- qcom,gpio-req-tbl-label : should contain name of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
+- label : should contain unique ir cut filter name
+Example:
+
+qcom,ir-cut@60 {
+ cell-index = <0>;
+ compatible = "qcom,ir-cut";
+ label = "led-ir-label";
+ gpios = <&tlmm 60 0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "LED_IR_EN";
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-ir-led.txt b/Documentation/devicetree/bindings/media/video/msm-ir-led.txt
new file mode 100644
index 000000000000..7e66fa0cef5b
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-ir-led.txt
@@ -0,0 +1,26 @@
+* QTI MSM IR LED
+
+Required properties:
+- cell-index : ir led hardware core index
+- compatible :
+ - "qcom,ir-led"
+
+Optional properties:
+- gpios : should specify the gpios to be used for the ir led.
+- qcom,gpio-req-tbl-num : should contain index to gpios specific to ir led
+- qcom,gpio-req-tbl-flags : should contain direction of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
+- qcom,gpio-req-tbl-label : should contain name of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
+- label : should contain unique ir led name
+Example:
+
+qcom,ir-led {
+ cell-index = <0>;
+ compatible = "qcom,ir-led";
+ label = "led-ir-label";
+ gpios = <&tlmm 60 0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "LED_IR_EN";
+ };
diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt
index 4b5a6b4af789..21b6d99424c0 100644
--- a/Documentation/devicetree/bindings/pci/msm_pcie.txt
+++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt
@@ -98,6 +98,10 @@ Optional Properties:
from the endpoint.
- linux,pci-domain: For details of pci-domains properties, please refer to:
"Documentation/devicetree/bindings/pci/pci.txt"
+ - qcom,perst-delay-us-min: The minimum allowed time (unit: us) to sleep after
+ asserting or de-asserting PERST GPIO.
+ - qcom,perst-delay-us-max: The maximum allowed time (unit: us) to sleep after
+ asserting or de-asserting PERST GPIO.
- qcom,tlp-rd-size: The max TLP read size (Calculation: 128 times 2 to the
tlp-rd-size power).
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
@@ -110,6 +114,10 @@ Optional Properties:
manager(scm) driver. scm driver uses this device id to restore PCIe
controller related security configuration after coming out of the controller
power collapse.
+ - resets: reset specifier pair consists of phandle for the reset controller
+ and reset lines used by this controller.
+ - reset-names: reset signal name strings sorted in the same order as the resets
+ property.
Example:
@@ -230,13 +238,21 @@ Example:
<&clock_gcc clk_gcc_pcie_0_cfg_ahb_clk>,
<&clock_gcc clk_gcc_pcie_0_mstr_axi_clk>,
<&clock_gcc clk_gcc_pcie_0_slv_axi_clk>,
- <&clock_gcc clk_pcie_0_phy_ldo>,
- <&clock_gcc clk_gcc_pcie_phy_0_reset>;
+ <&clock_gcc clk_pcie_0_phy_ldo>;
clock-names = "pcie_0_pipe_clk", "pcie_0_ref_clk_src",
"pcie_0_aux_clk", "pcie_0_cfg_ahb_clk",
"pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk",
"pcie_0_ldo";
+
+ resets = <&clock_gcc GCC_PCIE_PHY_BCR>,
+ <&clock_gcc GCC_PCIE_PHY_COM_BCR>,
+ <&clock_gcc GCC_PCIE_PHY_NOCSR_COM_PHY_BCR>,
+ <&clock_gcc GCC_PCIE_0_PHY_BCR>;
+
+ reset-names = "pcie_phy_reset", "pcie_phy_com_reset",
+ "pcie_phy_nocsr_com_phy_reset","pcie_0_phy_reset";
+
max-clock-frequency-hz = <125000000>, <0>, <1000000>,
<0>, <0>, <0>, <0>;
qcom,l0s-supported;
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index f3166d33f9e4..a8db893f6709 100644
--- a/Documentation/devicetree/bindings/platform/msm/ipa.txt
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -80,6 +80,8 @@ memory allocation over a PCIe bridge
- qcom,rx-polling-sleep-ms: Receive Polling Timeout in millisecond,
default is 1 millisecond.
- qcom,ipa-polling-iteration: IPA Polling Iteration Count,default is 40.
+- qcom,ipa-tz-unlock-reg: Register start addresses and ranges which
+ need to be unlocked by TZ.
IPA pipe sub nodes (A2 static pipes configurations):
diff --git a/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt b/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt
index 93312df2a43b..babc4523a29a 100644
--- a/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt
+++ b/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt
@@ -6,6 +6,10 @@ Required properties:
- compatible : should be "qcom,qpnp-revid"
- reg : offset and length of the PMIC peripheral register map.
+Optional property:
+- qcom,fab-id-valid: Use this property when support to read Fab
+ identification from REV ID peripheral is available.
+
Example:
qcom,revid@100 {
compatible = "qcom,qpnp-revid";
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 bd236df6c056..bd358593fcb3 100644
--- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt
@@ -78,7 +78,9 @@ First Level Node - FG Gen3 device
Definition: Battery current (in mA) at which the fuel gauge will try to
scale towards 100%. When the charge current goes above this
the SOC should be at 100%. If this property is not
- specified, then the default value used will be 125mA.
+ specified, then the default value used will be -125mA.
+ This value has to be specified in negative values for
+ the charging current.
- qcom,fg-delta-soc-thr
Usage: optional
@@ -138,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 510a824fda79..21404dfc4b7b 100644
--- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
@@ -21,11 +21,19 @@ Charger specific properties:
Value type: <string>
Definition: "qcom,qpnp-smb2".
-- qcom,suspend-input
+- 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>
- Definition: Boolean flag which indicates that the charger should not draw
- current from any of its input sources (USB, DC).
+ Definition: Boolean flag which indicates that the platform does not have a
+ battery, and therefore charging should be disabled. In
+ addition battery properties will be faked such that the device
+ assumes normal operation.
- qcom,fcc-max-ua
Usage: optional
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/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index a01bd451c577..82befcbd24a3 100755..100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -1646,6 +1646,18 @@ Optional Properties:
secondary -> Secondary MI2S interface
tertiary -> Tertiary MI2S interface
quaternary -> Quaternary MI2S interface
+- qcom,mi2s-audio-intf: This property is used to inform machine driver
+ if mi2s backend dailink has to be added as part of the sound card dai-links.
+- qcom,auxpcm-audio-intf: This property is used to inform machine driver
+ if auxpcm backend dailink has to be added as part of the sound card dai-links.
+- qcom,msm-mi2s-master: This property is used to inform machine driver
+ if MSM is the clock master of mi2s. 1 means master and 0 means slave. The
+ first entry is primary mi2s; the second entry is secondary mi2s, and so on.
+- reg: This property provides the AUX PCM/MI2S mux select register addresses
+ and size.
+- reg_names: This property provides the name of the AUX PCM/MI2S mux select
+ registers so the machine driver can retrieve the addresses. The order of the
+ names has to match the order of the registers in "reg" property.
- asoc-platform: This is phandle list containing the references to platform device
nodes that are used as part of the sound card dai-links.
- asoc-platform-names: This property contains list of platform names. The order of
@@ -1678,6 +1690,17 @@ Example:
"csr_gp_io_mux_spkr_ctl",
"csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel";
qcom,msm-ext-pa = "primary";
+ qcom,mi2s-audio-intf;
+ qcom,auxpcm-audio-intf;
+ qcom,msm-mi2s-master = <1>, <0>, <1>, <1>;
+ reg = <0x1711a000 0x4>,
+ <0x1711b000 0x4>,
+ <0x1711c000 0x4>,
+ <0x1711d000 0x4>;
+ reg-names = "lpaif_pri_mode_muxsel",
+ "lpaif_sec_mode_muxsel",
+ "lpaif_tert_mode_muxsel",
+ "lpaif_quat_mode_muxsel";
qcom,msm-mclk-freq = <9600000>;
qcom,msm-mbhc-hphl-swh = <0>;
qcom,msm-mbhc-gnd-swh = <0>;
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/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 1d7e54f68ee4..91412a10bf65 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -175,6 +175,7 @@ parade Parade Technologies Inc.
pericom Pericom Technology Inc.
phytec PHYTEC Messtechnik GmbH
picochip Picochip Ltd
+pixart PixArt Imaging Inc
plathome Plat'Home Co., Ltd.
plda PLDA
pixcir PIXCIR MICROELECTRONICS Co., Ltd
diff --git a/Documentation/scheduler/sched-hmp.txt b/Documentation/scheduler/sched-hmp.txt
index 22449aec5558..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
@@ -1220,6 +1221,23 @@ This tunable is a percentage. Configure the minimum demand of big sync waker
task. Scheduler places small wakee tasks woken up by big sync waker on the
waker's cluster.
+*** 7.19 sched_prefer_sync_wakee_to_waker
+
+Appears at: /proc/sys/kernel/sched_prefer_sync_wakee_to_waker
+
+Default value: 0
+
+The default sync wakee policy has a preference to select an idle CPU in the
+waker cluster compared to the waker CPU running only 1 task. By selecting
+an idle CPU, it eliminates the chance of waker migrating to a different CPU
+after the wakee preempts it. This policy is also not susceptible to the
+incorrect "sync" usage i.e the waker does not goto sleep after waking up
+the wakee.
+
+However LPM exit latency associated with an idle CPU outweigh the above
+benefits on some targets. When this knob is turned on, the waker CPU is
+selected if it has only 1 runnable task.
+
=========================
8. HMP SCHEDULER TRACE POINTS
=========================
@@ -1430,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/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index 9c48b84660f5..03e6aafd5b94 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -60,6 +60,7 @@ show up in /proc/sys/kernel:
- panic_on_stackoverflow
- panic_on_unrecovered_nmi
- panic_on_warn
+- perf_event_paranoid
- pid_max
- powersave-nap [ PPC only ]
- printk
@@ -599,19 +600,6 @@ This file shows up if CONFIG_DEBUG_STACKOVERFLOW is enabled.
==============================================================
-panic_on_unrecovered_nmi:
-
-The default Linux behaviour on an NMI of either memory or unknown is
-to continue operation. For many environments such as scientific
-computing it is preferable that the box is taken out and the error
-dealt with than an uncorrected parity/ECC error get propagated.
-
-A small number of systems do generate NMI's for bizarre random reasons
-such as power management so the default is off. That sysctl works like
-the existing panic controls already in that directory.
-
-==============================================================
-
panic_on_warn:
Calls panic() in the WARN() path when set to 1. This is useful to avoid
@@ -649,6 +637,32 @@ allowed to execute.
==============================================================
+panic_on_unrecovered_nmi:
+
+The default Linux behaviour on an NMI of either memory or unknown is
+to continue operation. For many environments such as scientific
+computing it is preferable that the box is taken out and the error
+dealt with than an uncorrected parity/ECC error get propagated.
+
+A small number of systems do generate NMI's for bizarre random reasons
+such as power management so the default is off. That sysctl works like
+the existing panic controls already in that directory.
+
+==============================================================
+
+perf_event_paranoid:
+
+Controls use of the performance events system by unprivileged
+users (without CAP_SYS_ADMIN). The default value is 3 if
+CONFIG_SECURITY_PERF_EVENTS_RESTRICT is set, or 1 otherwise.
+
+ -1: Allow use of (almost) all events by all users
+>=0: Disallow raw tracepoint access by users without CAP_IOC_LOCK
+>=1: Disallow CPU event access by users without CAP_SYS_ADMIN
+>=2: Disallow kernel profiling by users without CAP_SYS_ADMIN
+>=3: Disallow all event access by users without CAP_SYS_ADMIN
+
+==============================================================
pid_max:
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/dsi-panel-nt35597-dsc-wqxga-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dsc-wqxga-cmd.dtsi
index 57c6301f2074..95a8e80ccdbd 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dsc-wqxga-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dsc-wqxga-cmd.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 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
@@ -53,41 +53,41 @@
qcom,ulps-enabled;
qcom,adjust-timer-wakeup-ms = <1>;
- qcom,mdss-dsi-on-command = [15 01 00 00 0a 00 02 ff 10
- 15 01 00 00 0a 00 02 fb 01
- 15 01 00 00 0a 00 02 ba 03
- 15 01 00 00 0a 00 02 e5 01
- 15 01 00 00 0a 00 02 b0 03
- 15 01 00 00 0a 00 02 ff 28
- 15 01 00 00 0a 00 02 7a 02
- 15 01 00 00 0a 00 02 fb 01
- 15 01 00 00 0a 00 02 ff 10
- 15 01 00 00 0a 00 02 fb 01
- 15 01 00 00 0a 00 02 c0 03
- 15 01 00 00 0a 00 02 bb 10
- 15 01 00 00 0a 00 02 ff e0
- 15 01 00 00 0a 00 02 fb 01
- 15 01 00 00 0a 00 02 6b 3d
- 15 01 00 00 0a 00 02 6c 3d
- 15 01 00 00 0a 00 02 6d 3d
- 15 01 00 00 0a 00 02 6e 3d
- 15 01 00 00 0a 00 02 6f 3d
- 15 01 00 00 0a 00 02 35 02
- 15 01 00 00 0a 00 02 36 72
- 15 01 00 00 0a 00 02 37 10
- 15 01 00 00 0a 00 02 08 c0
- 15 01 00 00 0a 00 02 ff 24
- 15 01 00 00 0a 00 02 fb 01
- 15 01 00 00 0a 00 02 c6 06
- 15 01 00 00 0a 00 02 ff 10
- 05 01 00 00 f0 00 01 11
- 05 01 00 00 f0 00 01 29
+ qcom,mdss-dsi-on-command = [15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 ba 03
+ 15 01 00 00 00 00 02 e5 01
+ 15 01 00 00 00 00 02 b0 03
+ 15 01 00 00 00 00 02 ff 28
+ 15 01 00 00 00 00 02 7a 02
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 c0 03
+ 15 01 00 00 00 00 02 bb 10
+ 15 01 00 00 00 00 02 ff e0
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 6b 3d
+ 15 01 00 00 00 00 02 6c 3d
+ 15 01 00 00 00 00 02 6d 3d
+ 15 01 00 00 00 00 02 6e 3d
+ 15 01 00 00 00 00 02 6f 3d
+ 15 01 00 00 00 00 02 35 02
+ 15 01 00 00 00 00 02 36 72
+ 15 01 00 00 00 00 02 37 10
+ 15 01 00 00 00 00 02 08 c0
+ 15 01 00 00 00 00 02 ff 24
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 c6 06
+ 15 01 00 00 00 00 02 ff 10
+ 05 01 00 00 a0 00 01 11
+ 05 01 00 00 00 00 01 29
07 01 00 00 0a 00 02 01 00];
qcom,mdss-dsi-post-panel-on-command = [05 01 00 00 a0 00 01 29];
- qcom,mdss-dsi-off-command = [05 01 00 00 78 00 02 28 00
- 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 0a 00 02 28 00
+ 05 01 00 00 0a 00 02 10 00];
qcom,compression-mode = "dsc";
qcom,config-select = <&dsi_nt35597_dsc_cmd_config2>;
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dsc-wqxga-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dsc-wqxga-video.dtsi
index 1c515f506e9d..fd11be721dbb 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dsc-wqxga-video.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dsc-wqxga-video.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 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
@@ -30,37 +30,37 @@
qcom,mdss-dsi-underflow-color = <0xff>;
qcom,mdss-dsi-border-color = <0>;
- qcom,mdss-dsi-on-command = [15 01 00 00 0a 00 02 ff 10
- 15 01 00 00 0a 00 02 fb 01
- 15 01 00 00 0a 00 02 ba 03
- 15 01 00 00 0a 00 02 e5 01
- 15 01 00 00 0a 00 02 b0 03
- 39 01 00 00 0a 00 06 3B 03 08 08 2e 64
- 15 01 00 00 0a 00 02 ff 28
- 15 01 00 00 0a 00 02 7a 02
- 15 01 00 00 0a 00 02 fb 01
- 15 01 00 00 0a 00 02 ff 10
- 15 01 00 00 0a 00 02 fb 01
- 15 01 00 00 0a 00 02 c0 03
- 15 01 00 00 0a 00 02 bb 03
- 15 01 00 00 0a 00 02 ff e0
- 15 01 00 00 0a 00 02 fb 01
- 15 01 00 00 0a 00 02 6b 3d
- 15 01 00 00 0a 00 02 6c 3d
- 15 01 00 00 0a 00 02 6d 3d
- 15 01 00 00 0a 00 02 6e 3d
- 15 01 00 00 0a 00 02 6f 3d
- 15 01 00 00 0a 00 02 35 02
- 15 01 00 00 0a 00 02 36 72
- 15 01 00 00 0a 00 02 37 10
- 15 01 00 00 0a 00 02 08 c0
- 15 01 00 00 0a 00 02 ff 10
+ qcom,mdss-dsi-on-command = [15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 ba 03
+ 15 01 00 00 00 00 02 e5 01
+ 15 01 00 00 00 00 02 b0 03
+ 39 01 00 00 00 00 06 3B 03 08 08 2e 64
+ 15 01 00 00 00 00 02 ff 28
+ 15 01 00 00 00 00 02 7a 02
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 ff 10
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 c0 03
+ 15 01 00 00 00 00 02 bb 03
+ 15 01 00 00 00 00 02 ff e0
+ 15 01 00 00 00 00 02 fb 01
+ 15 01 00 00 00 00 02 6b 3d
+ 15 01 00 00 00 00 02 6c 3d
+ 15 01 00 00 00 00 02 6d 3d
+ 15 01 00 00 00 00 02 6e 3d
+ 15 01 00 00 00 00 02 6f 3d
+ 15 01 00 00 00 00 02 35 02
+ 15 01 00 00 00 00 02 36 72
+ 15 01 00 00 00 00 02 37 10
+ 15 01 00 00 00 00 02 08 c0
+ 15 01 00 00 00 00 02 ff 10
05 01 00 00 a0 00 01 11
- 05 01 00 00 a0 00 01 29
- 07 01 00 00 a0 00 01 01];
+ 05 01 00 00 00 00 01 29
+ 07 01 00 00 0a 00 01 01];
- qcom,mdss-dsi-off-command = [05 01 00 00 78 00 02 28 00
- 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 0a 00 02 28 00
+ 05 01 00 00 0a 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-h-sync-pulse = <0>;
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-sharp-1080p-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-sharp-1080p-cmd.dtsi
index 8b01f6031211..68dabd2fe41c 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-sharp-1080p-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-sharp-1080p-cmd.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 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
@@ -15,6 +15,7 @@
qcom,mdss-dsi-panel-name = "sharp 1080p cmd mode dsi panel";
qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-panel-clockrate = <850000000>;
qcom,mdss-dsi-virtual-channel-id = <0>;
qcom,mdss-dsi-stream = <0>;
qcom,mdss-dsi-panel-width = <1080>;
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-sharp-dualmipi-1080p-120hz.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-sharp-dualmipi-1080p-120hz.dtsi
index 5aa4974ded2f..a7861e96d3e2 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-sharp-dualmipi-1080p-120hz.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-sharp-dualmipi-1080p-120hz.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, 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
@@ -16,7 +16,6 @@
"sharp 1080p 120hz dual dsi cmd mode panel";
qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
qcom,mdss-dsi-panel-framerate = <120>;
- qcom,mdss-dsi-panel-clockrate = <975000000>;
qcom,mdss-dsi-virtual-channel-id = <0>;
qcom,mdss-dsi-stream = <0>;
qcom,mdss-dsi-panel-width = <540>;
diff --git a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi
index 41b6f50c520b..ba27e3912ee6 100644
--- a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi
@@ -308,6 +308,49 @@
qcom,msm-cpudai-auxpcm-data = <0>, <0>;
qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
qcom,msm-auxpcm-interface = "primary";
+ qcom,msm-cpudai-afe-clk-ver = <2>;
+ };
+
+ dai_sec_auxpcm: qcom,msm-sec-auxpcm {
+ compatible = "qcom,msm-auxpcm-dev";
+ qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+ qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+ qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+ qcom,msm-auxpcm-interface = "secondary";
+ qcom,msm-cpudai-afe-clk-ver = <2>;
+ };
+
+ dai_tert_auxpcm: qcom,msm-tert-auxpcm {
+ compatible = "qcom,msm-auxpcm-dev";
+ qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+ qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+ qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+ qcom,msm-auxpcm-interface = "tertiary";
+ qcom,msm-cpudai-afe-clk-ver = <2>;
+ };
+
+ dai_quat_auxpcm: qcom,msm-quat-auxpcm {
+ compatible = "qcom,msm-auxpcm-dev";
+ qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+ qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+ qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+ qcom,msm-auxpcm-interface = "quaternary";
+ qcom,msm-cpudai-afe-clk-ver = <2>;
};
hdmi_dba: qcom,msm-hdmi-dba-codec-rx {
@@ -327,4 +370,42 @@
compatible = "qcom,adsp-loader";
qcom,adsp-state = <0>;
};
+
+ qcom,msm-dai-tdm-tert-rx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37152>;
+ qcom,msm-cpudai-tdm-group-num-ports = <1>;
+ qcom,msm-cpudai-tdm-group-port-id = <36896>;
+ qcom,msm-cpudai-tdm-clk-rate = <1536000>;
+ pinctrl-names = "default", "sleep";
+ dai_tert_tdm_rx_0: qcom,msm-dai-q6-tdm-tert-rx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36896>;
+ qcom,msm-cpudai-tdm-sync-mode = <1>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <1>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
+ qcom,msm-dai-tdm-tert-tx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37153>;
+ qcom,msm-cpudai-tdm-group-num-ports = <1>;
+ qcom,msm-cpudai-tdm-group-port-id = <36897 >;
+ qcom,msm-cpudai-tdm-clk-rate = <1536000>;
+ pinctrl-names = "default", "sleep";
+ dai_tert_tdm_tx_0: qcom,msm-dai-q6-tdm-tert-tx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36897 >;
+ qcom,msm-cpudai-tdm-sync-mode = <1>;
+ qcom,msm-cpudai-tdm-sync-src = <1>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <1>;
+ qcom,msm-cpudai-tdm-data-delay = <1>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
};
diff --git a/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi b/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi
new file mode 100644
index 000000000000..399892f52b6f
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi
@@ -0,0 +1,377 @@
+/* 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 <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/spmi/spmi.h>
+#include <dt-bindings/msm/power-on.h>
+
+&spmi_bus {
+ qcom,pm2falcon@2 {
+ compatible = "qcom,spmi-pmic";
+ reg = <0x2 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pm2falcon_revid: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ qcom,secondary-pon-reset;
+ qcom,hard-reset-poweroff-type =
+ <PON_POWER_OFF_SHUTDOWN>;
+ };
+
+ pm2falcon_gpios: gpios {
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm2falcon-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ status = "disabled";
+ };
+
+ gpio@c200 {
+ reg = <0xc200 0x100>;
+ qcom,pin-num = <3>;
+ status = "disabled";
+ };
+
+ gpio@c300 {
+ reg = <0xc300 0x100>;
+ qcom,pin-num = <4>;
+ status = "disabled";
+ };
+
+ gpio@c400 {
+ reg = <0xc400 0x100>;
+ qcom,pin-num = <5>;
+ status = "disabled";
+ };
+
+ gpio@c500 {
+ reg = <0xc500 0x100>;
+ qcom,pin-num = <6>;
+ status = "disabled";
+ };
+
+ gpio@c600 {
+ reg = <0xc600 0x100>;
+ qcom,pin-num = <7>;
+ status = "disabled";
+ };
+
+ gpio@c700 {
+ reg = <0xc700 0x100>;
+ qcom,pin-num = <8>;
+ status = "disabled";
+ };
+
+ gpio@c800 {
+ reg = <0xc800 0x100>;
+ qcom,pin-num = <9>;
+ status = "disabled";
+ };
+
+ gpio@c900 {
+ reg = <0xc900 0x100>;
+ qcom,pin-num = <10>;
+ status = "disabled";
+ };
+
+ gpio@ca00 {
+ reg = <0xca00 0x100>;
+ qcom,pin-num = <11>;
+ status = "disabled";
+ };
+
+ gpio@cb00 {
+ reg = <0xcb00 0x100>;
+ qcom,pin-num = <12>;
+ status = "disabled";
+ };
+
+ };
+ };
+
+ qcom,pm2falcon@3 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x3 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pm2falcon_pwm_1: pwm@b100 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb100 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base",
+ "qpnp-lpg-lut-base";
+ qcom,channel-id = <1>;
+ qcom,supported-sizes = <6>, <9>;
+ qcom,ramp-index = <0>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+
+ pm2falcon_pwm_2: pwm@b200 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb200 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base",
+ "qpnp-lpg-lut-base";
+ qcom,channel-id = <2>;
+ qcom,supported-sizes = <6>, <9>;
+ qcom,ramp-index = <1>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+
+ pm2falcon_pwm_3: pwm@b300 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb300 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base",
+ "qpnp-lpg-lut-base";
+ qcom,channel-id = <3>;
+ qcom,supported-sizes = <6>, <9>;
+ qcom,ramp-index = <2>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+
+ pm2falcon_pwm_4: pwm@b400 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb400 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base",
+ "qpnp-lpg-lut-base";
+ qcom,channel-id = <4>;
+ qcom,supported-sizes = <6>, <9>;
+ qcom,ramp-index = <3>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+
+ qcom,leds@d000 {
+ compatible = "qcom,leds-qpnp";
+ reg = <0xd000 0x100>;
+ label = "rgb";
+ status = "disabled";
+
+ red_led: qcom,rgb_0 {
+ label = "rgb";
+ qcom,id = <3>;
+ qcom,mode = "pwm";
+ pwms = <&pm2falcon_pwm_3 0 0>;
+ qcom,pwm-us = <1000>;
+ qcom,max-current = <12>;
+ qcom,default-state = "off";
+ linux,name = "red";
+ linux,default-trigger =
+ "battery-charging";
+ };
+
+ green_led: qcom,rgb_1 {
+ label = "rgb";
+ qcom,id = <4>;
+ qcom,mode = "pwm";
+ pwms = <&pm2falcon_pwm_2 0 0>;
+ qcom,pwm-us = <1000>;
+ qcom,max-current = <12>;
+ qcom,default-state = "off";
+ linux,name = "green";
+ linux,default-trigger = "battery-full";
+ };
+
+ blue_led: qcom,rgb_2 {
+ label = "rgb";
+ qcom,id = <5>;
+ qcom,mode = "pwm";
+ pwms = <&pm2falcon_pwm_1 0 0>;
+ qcom,pwm-us = <1000>;
+ qcom,max-current = <12>;
+ qcom,default-state = "off";
+ linux,name = "blue";
+ linux,default-trigger = "boot-indication";
+ };
+ };
+
+ pm2falcon_wled: qcom,leds@d800 {
+ compatible = "qcom,qpnp-wled";
+ reg = <0xd800 0x100>,
+ <0xd900 0x100>,
+ <0xdc00 0x100>,
+ <0xde00 0x100>;
+ reg-names = "qpnp-wled-ctrl-base",
+ "qpnp-wled-sink-base",
+ "qpnp-wled-ibb-base",
+ "qpnp-wled-lab-base";
+ interrupts = <0x3 0xd8 0x2>;
+ interrupt-names = "sc-irq";
+ linux,name = "wled";
+ linux,default-trigger = "bkl-trigger";
+ qcom,fdbk-output = "auto";
+ qcom,vref-mv = <350>;
+ qcom,switch-freq-khz = <800>;
+ qcom,ovp-mv = <29500>;
+ qcom,ilim-ma = <980>;
+ qcom,boost-duty-ns = <26>;
+ qcom,mod-freq-khz = <9600>;
+ qcom,dim-mode = "hybrid";
+ qcom,hyb-thres = <625>;
+ qcom,sync-dly-us = <800>;
+ qcom,fs-curr-ua = <25000>;
+ qcom,cons-sync-write-delay-us = <1000>;
+ qcom,en-phase-stag;
+ qcom,led-strings-list = [00 01 02];
+ qcom,en-ext-pfet-sc-pro;
+ status = "ok";
+ };
+
+ flash_led: qcom,leds@d300 {
+ compatible = "qcom,qpnp-flash-led-v2";
+ reg = <0xd300 0x100>;
+ label = "flash";
+ interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd3 0x3 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd3 0x4 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "led-fault-irq",
+ "all-ramp-down-done-irq",
+ "all-ramp-up-done-irq";
+ qcom,hdrm-auto-mode;
+ qcom,short-circuit-det;
+ qcom,open-circuit-det;
+ qcom,vph-droop-det;
+ qcom,thermal-derate-en;
+ qcom,thermal-derate-current = <200 500 1000>;
+ qcom,isc-delay = <192>;
+ status = "disabled";
+
+ pm2falcon_flash0: qcom,flash_0 {
+ label = "flash";
+ qcom,led-name = "led:flash_0";
+ qcom,max-current = <1500>;
+ qcom,default-led-trigger = "flash0_trigger";
+ qcom,id = <0>;
+ qcom,current-ma = <1000>;
+ qcom,duration-ms = <1280>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pm2falcon_flash1: qcom,flash_1 {
+ label = "flash";
+ qcom,led-name = "led:flash_1";
+ qcom,max-current = <1500>;
+ qcom,default-led-trigger = "flash1_trigger";
+ qcom,id = <1>;
+ qcom,current-ma = <1000>;
+ qcom,duration-ms = <1280>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pm2falcon_flash2: qcom,flash_2 {
+ label = "flash";
+ qcom,led-name = "led:flash_2";
+ qcom,max-current = <750>;
+ qcom,default-led-trigger = "flash2_trigger";
+ qcom,id = <2>;
+ qcom,current-ma = <500>;
+ qcom,duration-ms = <1280>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ pinctrl-names = "led_enable","led_disable";
+ pinctrl-0 = <&led_enable>;
+ pinctrl-1 = <&led_disable>;
+ };
+
+ pm2falcon_torch0: qcom,torch_0 {
+ label = "torch";
+ qcom,led-name = "led:torch_0";
+ qcom,max-current = <500>;
+ qcom,default-led-trigger = "torch0_trigger";
+ qcom,id = <0>;
+ qcom,current-ma = <300>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pm2falcon_torch1: qcom,torch_1 {
+ label = "torch";
+ qcom,led-name = "led:torch_1";
+ qcom,max-current = <500>;
+ qcom,default-led-trigger = "torch1_trigger";
+ qcom,id = <1>;
+ qcom,current-ma = <300>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ };
+
+ pm2falcon_torch2: qcom,torch_2 {
+ label = "torch";
+ qcom,led-name = "led:torch_2";
+ qcom,max-current = <500>;
+ qcom,default-led-trigger = "torch2_trigger";
+ qcom,id = <2>;
+ qcom,current-ma = <300>;
+ qcom,ires-ua = <12500>;
+ qcom,hdrm-voltage-mv = <325>;
+ qcom,hdrm-vol-hi-lo-win-mv = <100>;
+ pinctrl-names = "led_enable","led_disable";
+ pinctrl-0 = <&led_enable>;
+ pinctrl-1 = <&led_disable>;
+ };
+
+ pm2falcon_switch0: qcom,led_switch_0 {
+ label = "switch";
+ qcom,led-name = "led:switch_0";
+ qcom,led-mask = <3>;
+ qcom,default-led-trigger = "switch0_trigger";
+ reg0 {
+ regulator-name = "pmfalcon_bob";
+ max-voltage-uv = <3600000>;
+ };
+ };
+
+ pm2falcon_switch1: qcom,led_switch_1 {
+ label = "switch";
+ qcom,led-name = "led:switch_1";
+ qcom,led-mask = <4>;
+ qcom,default-led-trigger = "switch1_trigger";
+ reg0 {
+ regulator-name = "pmfalcon_bob";
+ max-voltage-uv = <3600000>;
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msm-pmfalcon.dtsi b/arch/arm/boot/dts/qcom/msm-pmfalcon.dtsi
new file mode 100644
index 000000000000..dec37881249c
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm-pmfalcon.dtsi
@@ -0,0 +1,169 @@
+/* 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 <dt-bindings/spmi/spmi.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+&spmi_bus {
+ qcom,pmfalcon@0 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x0 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pmfalcon_revid: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x0 IRQ_TYPE_NONE>,
+ <0x0 0x8 0x1 IRQ_TYPE_NONE>,
+ <0x0 0x8 0x4 IRQ_TYPE_NONE>,
+ <0x0 0x8 0x5 IRQ_TYPE_NONE>;
+ interrupt-names = "kpdpwr", "resin",
+ "resin-bark", "kpdpwr-resin-bark";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,system-reset;
+ qcom,store-hard-reset-reason;
+
+ qcom,pon_1 {
+ qcom,pon-type = <0>;
+ qcom,pull-up = <1>;
+ linux,code = <116>;
+ };
+
+ qcom,pon_2 {
+ qcom,pon-type = <1>;
+ qcom,pull-up = <1>;
+ linux,code = <114>;
+ };
+ };
+
+ pmfalcon_gpios: gpios {
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pmfalcon-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ status = "disabled";
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ status = "disabled";
+ };
+
+ gpio@c200 {
+ reg = <0xc200 0x100>;
+ qcom,pin-num = <3>;
+ status = "disabled";
+ };
+
+ gpio@c300 {
+ reg = <0xc300 0x100>;
+ qcom,pin-num = <4>;
+ status = "disabled";
+ };
+
+ gpio@c400 {
+ reg = <0xc400 0x100>;
+ qcom,pin-num = <5>;
+ status = "disabled";
+ };
+
+ gpio@c500 {
+ reg = <0xc500 0x100>;
+ qcom,pin-num = <6>;
+ status = "disabled";
+ };
+
+ gpio@c600 {
+ reg = <0xc600 0x100>;
+ qcom,pin-num = <7>;
+ status = "disabled";
+ };
+
+ gpio@c700 {
+ reg = <0xc700 0x100>;
+ qcom,pin-num = <8>;
+ status = "disabled";
+ };
+
+ gpio@c800 {
+ reg = <0xc800 0x100>;
+ qcom,pin-num = <9>;
+ status = "disabled";
+ };
+
+ gpio@c900 {
+ reg = <0xc900 0x100>;
+ qcom,pin-num = <10>;
+ status = "disabled";
+ };
+
+ gpio@ca00 {
+ reg = <0xca00 0x100>;
+ qcom,pin-num = <11>;
+ status = "disabled";
+ };
+
+ gpio@cb00 {
+ reg = <0xcb00 0x100>;
+ qcom,pin-num = <12>;
+ status = "disabled";
+ };
+
+ gpio@cc00 {
+ reg = <0xcc00 0x100>;
+ qcom,pin-num = <13>;
+ status = "disabled";
+ };
+ };
+
+ pmfalcon_coincell: qcom,coincell@2800 {
+ compatible = "qcom,qpnp-coincell";
+ reg = <0x2800 0x100>;
+ };
+
+ pmfalcon_rtc: qcom,pmfalcon_rtc {
+ compatible = "qcom,qpnp-rtc";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ qcom,qpnp-rtc-write = <0>;
+ qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+ qcom,pmfalcon_rtc_rw@6000 {
+ reg = <0x6000 0x100>;
+ };
+ qcom,pmfalcon_rtc_alarm@6100 {
+ reg = <0x6100 0x100>;
+ interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>;
+ };
+ };
+ };
+
+ qcom,pmfalcon@1 {
+ compatible ="qcom,spmi-pmic";
+ reg = <0x1 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
index 3f1ffd497f2c..4f76276b2790 100644
--- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
@@ -23,6 +23,7 @@
pmicobalt_revid: qcom,revid@100 {
compatible = "qcom,qpnp-revid";
reg = <0x100 0x100>;
+ qcom,fab-id-valid;
};
qcom,power-on@800 {
@@ -321,6 +322,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 {
@@ -329,23 +331,45 @@
interrupts = <0x2 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
<0x2 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
<0x2 0x40 0x2 IRQ_TYPE_EDGE_BOTH>,
- <0x2 0x40 0x3 IRQ_TYPE_EDGE_BOTH>;
+ <0x2 0x40 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x40 0x4 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x40 0x5 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x40 0x6 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x40 0x7 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "soc-update",
"soc-ready",
"bsoc-delta",
- "msoc-delta";
+ "msoc-delta",
+ "msoc-low",
+ "msoc-empty",
+ "msoc-high",
+ "msoc-full";
};
qcom,fg-batt-info@4100 {
status = "okay";
reg = <0x4100 0x100>;
- interrupts = <0x2 0x41 0x3 IRQ_TYPE_EDGE_BOTH>;
- interrupt-names = "batt-missing";
+ interrupts = <0x2 0x41 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x41 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x41 0x2 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x41 0x3 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x41 0x6 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "vbatt-pred-delta",
+ "vbatt-low",
+ "esr-delta",
+ "batt-missing",
+ "batt-temp-delta";
};
qcom,fg-memif@4400 {
status = "okay";
reg = <0x4400 0x100>;
+ interrupts = <0x2 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
+ <0x2 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "ima-rdy",
+ "mem-xcp",
+ "dma-grant";
};
};
};
diff --git a/arch/arm/boot/dts/qcom/msm8996-camera.dtsi b/arch/arm/boot/dts/qcom/msm8996-camera.dtsi
index 3422e5e7f500..282e6bcb713b 100644
--- a/arch/arm/boot/dts/qcom/msm8996-camera.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-camera.dtsi
@@ -623,6 +623,10 @@
"camss_cpp_axi_clk", "camss_cpp_clk",
"micro_iface_clk", "camss_ahb_clk",
"smmu_cpp_axi_clk", "cpp_vbif_ahb_clk";
+
+ resets = <&clock_mmss CAMSS_MICRO_BCR>;
+ reset-names = "micro_iface_reset";
+
qcom,clock-rates = <0 0 0 480000000 0 0 480000000 0 0 0 0>;
qcom,min-clock-rate = <200000000>;
qcom,bus-master = <1>;
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index 4b1b9796ebe6..7e88f524367f 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>;
@@ -1394,18 +1394,20 @@
<&clock_gcc clk_gcc_pcie_clkref_clk>,
<&clock_gcc clk_gcc_smmu_aggre0_axi_clk>,
<&clock_gcc clk_gcc_pcie_phy_cfg_ahb_clk>,
- <&clock_gcc clk_gcc_pcie_phy_aux_clk>,
- <&clock_gcc clk_gcc_pcie_phy_reset>,
- <&clock_gcc clk_gcc_pcie_phy_com_reset>,
- <&clock_gcc clk_gcc_pcie_phy_nocsr_com_phy_reset>,
- <&clock_gcc clk_gcc_pcie_0_phy_reset>;
+ <&clock_gcc clk_gcc_pcie_phy_aux_clk>;
clock-names = "pcie_0_pipe_clk", "pcie_0_ref_clk_src", "pcie_0_aux_clk",
"pcie_0_cfg_ahb_clk", "pcie_0_mstr_axi_clk",
"pcie_0_slv_axi_clk", "pcie_0_ldo", "pcie_0_smmu_clk",
- "pcie_phy_cfg_ahb_clk", "pcie_phy_aux_clk", "pcie_phy_reset",
- "pcie_phy_com_reset", "pcie_phy_nocsr_com_phy_reset",
- "pcie_0_phy_reset";
+ "pcie_phy_cfg_ahb_clk", "pcie_phy_aux_clk";
+
+ resets = <&clock_gcc PCIE_PHY_BCR>,
+ <&clock_gcc PCIE_PHY_COM_BCR>,
+ <&clock_gcc PCIE_PHY_NOCSR_COM_PHY_BCR>,
+ <&clock_gcc PCIE_0_PHY_BCR>;
+
+ reset-names = "pcie_phy_reset", "pcie_phy_com_reset",
+ "pcie_phy_nocsr_com_phy_reset","pcie_0_phy_reset";
max-clock-frequency-hz = <0>, <0>, <1010526>, <0>, <0>, <0>, <0>,
<0>, <0>, <0>, <0>, <0>, <0>, <0>;
@@ -1508,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>;
@@ -1544,18 +1546,20 @@
<&clock_gcc clk_gcc_pcie_clkref_clk>,
<&clock_gcc clk_gcc_smmu_aggre0_axi_clk>,
<&clock_gcc clk_gcc_pcie_phy_cfg_ahb_clk>,
- <&clock_gcc clk_gcc_pcie_phy_aux_clk>,
- <&clock_gcc clk_gcc_pcie_phy_reset>,
- <&clock_gcc clk_gcc_pcie_phy_com_reset>,
- <&clock_gcc clk_gcc_pcie_phy_nocsr_com_phy_reset>,
- <&clock_gcc clk_gcc_pcie_1_phy_reset>;
+ <&clock_gcc clk_gcc_pcie_phy_aux_clk>;
clock-names = "pcie_1_pipe_clk", "pcie_1_ref_clk_src", "pcie_1_aux_clk",
"pcie_1_cfg_ahb_clk", "pcie_1_mstr_axi_clk",
"pcie_1_slv_axi_clk", "pcie_1_ldo", "pcie_1_smmu_clk",
- "pcie_phy_cfg_ahb_clk", "pcie_phy_aux_clk", "pcie_phy_reset",
- "pcie_phy_com_reset", "pcie_phy_nocsr_com_phy_reset",
- "pcie_1_phy_reset";
+ "pcie_phy_cfg_ahb_clk", "pcie_phy_aux_clk";
+
+ resets = <&clock_gcc PCIE_PHY_BCR>,
+ <&clock_gcc PCIE_PHY_COM_BCR>,
+ <&clock_gcc PCIE_PHY_NOCSR_COM_PHY_BCR>,
+ <&clock_gcc PCIE_1_PHY_BCR>;
+
+ reset-names = "pcie_phy_reset", "pcie_phy_com_reset",
+ "pcie_phy_nocsr_com_phy_reset","pcie_1_phy_reset";
max-clock-frequency-hz = <0>, <0>, <1010526>, <0>, <0>, <0>, <0>,
<0>, <0>, <0>, <0>, <0>, <0>, <0>;
@@ -1659,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;
@@ -1698,18 +1702,20 @@
<&clock_gcc clk_gcc_pcie_clkref_clk>,
<&clock_gcc clk_gcc_smmu_aggre0_axi_clk>,
<&clock_gcc clk_gcc_pcie_phy_cfg_ahb_clk>,
- <&clock_gcc clk_gcc_pcie_phy_aux_clk>,
- <&clock_gcc clk_gcc_pcie_phy_reset>,
- <&clock_gcc clk_gcc_pcie_phy_com_reset>,
- <&clock_gcc clk_gcc_pcie_phy_nocsr_com_phy_reset>,
- <&clock_gcc clk_gcc_pcie_2_phy_reset>;
+ <&clock_gcc clk_gcc_pcie_phy_aux_clk>;
clock-names = "pcie_2_pipe_clk", "pcie_2_ref_clk_src", "pcie_2_aux_clk",
"pcie_2_cfg_ahb_clk", "pcie_2_mstr_axi_clk",
"pcie_2_slv_axi_clk", "pcie_2_ldo", "pcie_2_smmu_clk",
- "pcie_phy_cfg_ahb_clk", "pcie_phy_aux_clk", "pcie_phy_reset",
- "pcie_phy_com_reset", "pcie_phy_nocsr_com_phy_reset",
- "pcie_2_phy_reset";
+ "pcie_phy_cfg_ahb_clk", "pcie_phy_aux_clk";
+
+ resets = <&clock_gcc PCIE_PHY_BCR>,
+ <&clock_gcc PCIE_PHY_COM_BCR>,
+ <&clock_gcc PCIE_PHY_NOCSR_COM_PHY_BCR>,
+ <&clock_gcc PCIE_2_PHY_BCR>;
+
+ reset-names = "pcie_phy_reset", "pcie_phy_com_reset",
+ "pcie_phy_nocsr_com_phy_reset","pcie_2_phy_reset";
max-clock-frequency-hz = <0>, <0>, <1010526>, <0>, <0>, <0>, <0>,
<0>, <0>, <0>, <0>, <0>, <0>, <0>;
@@ -1809,6 +1815,11 @@
reg = <0x10 8>;
};
+ dload_type@18 {
+ compatible = "qcom,msm-imem-dload-type";
+ reg = <0x18 4>;
+ };
+
restart_reason@65c {
compatible = "qcom,msm-imem-restart_reason";
reg = <0x65c 4>;
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
index 9b50bb96750a..a1d80075abe0 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
@@ -35,6 +35,19 @@
compatible = "qcom,msmcobalt-asoc-snd-tasha";
qcom,model = "msmcobalt-tasha-snd-card";
qcom,ext-disp-audio-rx;
+ qcom,wcn-btfm;
+ qcom,mi2s-audio-intf;
+ qcom,auxpcm-audio-intf;
+ qcom,msm-mi2s-master = <1>, <1>, <1>, <1>;
+
+ reg = <0x1711a000 0x4>,
+ <0x1711b000 0x4>,
+ <0x1711c000 0x4>,
+ <0x1711d000 0x4>;
+ reg-names = "lpaif_pri_mode_muxsel",
+ "lpaif_sec_mode_muxsel",
+ "lpaif_tert_mode_muxsel",
+ "lpaif_quat_mode_muxsel";
qcom,audio-routing =
"AIF4 VI", "MCLK",
@@ -81,6 +94,10 @@
"msm-pcm-routing", "msm-cpe-lsm",
"msm-compr-dsp", "msm-pcm-dsp-noirq";
asoc-cpu = <&dai_hdmi>, <&dai_dp>,
+ <&dai_mi2s0>, <&dai_mi2s1>,
+ <&dai_mi2s2>, <&dai_mi2s3>,
+ <&dai_pri_auxpcm>, <&dai_sec_auxpcm>,
+ <&dai_tert_auxpcm>, <&dai_quat_auxpcm>,
<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
<&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>,
@@ -89,8 +106,13 @@
<&incall_record_tx>, <&incall_music_rx>,
<&incall_music_2_rx>, <&sb_5_rx>, <&sb_6_rx>,
<&sb_7_rx>, <&sb_7_tx>, <&sb_8_tx>,
- <&usb_audio_rx>, <&usb_audio_tx>;
+ <&usb_audio_rx>, <&usb_audio_tx>,
+ <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>;
asoc-cpu-names = "msm-dai-q6-hdmi.8", "msm-dai-q6-dp.24608",
+ "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+ "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+ "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
+ "msm-dai-q6-auxpcm.3", "msm-dai-q6-auxpcm.4",
"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
"msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
@@ -103,7 +125,8 @@
"msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394",
"msm-dai-q6-dev.16396", "msm-dai-q6-dev.16398",
"msm-dai-q6-dev.16399", "msm-dai-q6-dev.16401",
- "msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673";
+ "msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673",
+ "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897";
asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>;
asoc-codec-names = "msm-stub-codec.1",
"msm-ext-disp-audio-codec-rx";
@@ -118,9 +141,23 @@
compatible = "qcom,msmcobalt-asoc-snd-tavil";
qcom,model = "msmcobalt-tavil-snd-card";
qcom,ext-disp-audio-rx;
+ qcom,wcn-btfm;
+ qcom,mi2s-audio-intf;
+ qcom,auxpcm-audio-intf;
+ qcom,msm-mi2s-master = <1>, <1>, <1>, <1>;
+
+ reg = <0x1711a000 0x4>,
+ <0x1711b000 0x4>,
+ <0x1711c000 0x4>,
+ <0x1711d000 0x4>;
+ reg-names = "lpaif_pri_mode_muxsel",
+ "lpaif_sec_mode_muxsel",
+ "lpaif_tert_mode_muxsel",
+ "lpaif_quat_mode_muxsel";
qcom,audio-routing =
"RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
"AMIC2", "MIC BIAS2",
"MIC BIAS2", "Headset Mic",
"AMIC3", "MIC BIAS2",
@@ -162,15 +199,25 @@
"msm-pcm-routing", "msm-cpe-lsm",
"msm-compr-dsp", "msm-pcm-dsp-noirq";
asoc-cpu = <&dai_hdmi>, <&dai_dp>,
+ <&dai_mi2s0>, <&dai_mi2s1>,
+ <&dai_mi2s2>, <&dai_mi2s3>,
+ <&dai_pri_auxpcm>, <&dai_sec_auxpcm>,
+ <&dai_tert_auxpcm>, <&dai_quat_auxpcm>,
<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
<&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>,
<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
<&afe_proxy_tx>, <&incall_record_rx>,
<&incall_record_tx>, <&incall_music_rx>,
- <&incall_music_2_rx>, <&sb_5_rx>,
- <&usb_audio_rx>, <&usb_audio_tx>, <&sb_6_rx>;
+ <&incall_music_2_rx>, <&sb_5_rx>, <&sb_6_rx>,
+ <&sb_7_rx>, <&sb_7_tx>, <&sb_8_tx>,
+ <&usb_audio_rx>, <&usb_audio_tx>,
+ <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>;
asoc-cpu-names = "msm-dai-q6-hdmi.8", "msm-dai-q6-dp.24608",
+ "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+ "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+ "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
+ "msm-dai-q6-auxpcm.3", "msm-dai-q6-auxpcm.4",
"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
"msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
@@ -181,8 +228,10 @@
"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773",
"msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394",
+ "msm-dai-q6-dev.16396", "msm-dai-q6-dev.16398",
+ "msm-dai-q6-dev.16399", "msm-dai-q6-dev.16401",
"msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673",
- "msm-dai-q6-dev.16396";
+ "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897";
asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>;
asoc-codec-names = "msm-stub-codec.1",
"msm-ext-disp-audio-codec-rx";
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-sensor-qrd.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-qrd.dtsi
new file mode 100644
index 000000000000..4b435aee73b0
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-qrd.dtsi
@@ -0,0 +1,356 @@
+
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ led_flash0: qcom,camera-flash@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-source = <&pmicobalt_flash0 &pmicobalt_flash1>;
+ qcom,torch-source = <&pmicobalt_torch0 &pmicobalt_torch1>;
+ qcom,switch-source = <&pmicobalt_switch0>;
+ status = "ok";
+ };
+
+ led_flash1: qcom,camera-flash@1 {
+ cell-index = <1>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-source = <&pmicobalt_flash2>;
+ qcom,torch-source = <&pmicobalt_torch2>;
+ qcom,switch-source = <&pmicobalt_switch1>;
+ status = "ok";
+ };
+};
+
+&cci {
+ actuator0: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ gpios = <&tlmm 27 0>;
+ qcom,gpio-vaf = <0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_VAF";
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_actuator_vaf_active>;
+ pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ };
+
+ actuator1: qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ gpios = <&tlmm 27 0>;
+ qcom,gpio-vaf = <0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_VAF";
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_actuator_vaf_active>;
+ pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ };
+
+ ois0: qcom,ois@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,ois";
+ qcom,cci-master = <0>;
+ gpios = <&tlmm 27 0>;
+ qcom,gpio-vaf = <0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_VAF";
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_actuator_vaf_active>;
+ pinctrl-1 = <&cam_actuator_vaf_suspend>;
+ status = "disabled";
+ };
+
+ eeprom0: qcom,eeprom@0 {
+ cell-index = <0>;
+ reg = <0>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&pmcobalt_lvs1>;
+ cam_vana-supply = <&pmicobalt_bob>;
+ 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-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active
+ &cam_actuator_vaf_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend
+ &cam_actuator_vaf_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 30 0>,
+ <&pmcobalt_gpios 20 0>,
+ <&tlmm 29 0>,
+ <&tlmm 27 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-vaf = <4>;
+ qcom,gpio-req-tbl-num = <0 1 2 3 4>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VDIG",
+ "CAM_VANA",
+ "CAM_VAF";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk0_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ eeprom1: qcom,eeprom@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,eeprom";
+ cam_vdig-supply = <&pmcobalt_lvs1>;
+ cam_vio-supply = <&pmcobalt_lvs1>;
+ 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-op-mode = <0 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk2_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 15 0>,
+ <&tlmm 9 0>,
+ <&tlmm 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_VANA1";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk2_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ eeprom2: qcom,eeprom@2 {
+ cell-index = <2>;
+ reg = <0x2>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&pmcobalt_lvs1>;
+ cam_vana-supply = <&pmcobalt_l22>;
+ cam_vdig-supply = <&pmcobalt_s3>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage =
+ <0 2864000 1352000>;
+ qcom,cam-vreg-max-voltage =
+ <0 2864000 1352000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_front_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_front_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>,
+ <&pmcobalt_gpios 9 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_VDIG";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk1_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@0 {
+ cell-index = <0>;
+ compatible = "qcom,camera";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
+ qcom,led-flash-src = <&led_flash0>;
+ qcom,actuator-src = <&actuator0>;
+ qcom,ois-src = <&ois0>;
+ qcom,eeprom-src = <&eeprom0>;
+ cam_vio-supply = <&pmcobalt_lvs1>;
+ cam_vana-supply = <&pmicobalt_bob>;
+ 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-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 30 0>,
+ <&pmcobalt_gpios 20 0>,
+ <&tlmm 29 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-vana = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VDIG",
+ "CAM_VANA";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk0_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@1 {
+ cell-index = <1>;
+ compatible = "qcom,camera";
+ reg = <0x1>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <90>;
+ qcom,eeprom-src = <&eeprom1>;
+ cam_vdig-supply = <&pmcobalt_lvs1>;
+ cam_vio-supply = <&pmcobalt_lvs1>;
+ 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-op-mode = <0 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk2_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 15 0>,
+ <&tlmm 9 0>,
+ <&tlmm 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_VANA1";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk2_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk2_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+ qcom,camera@2 {
+ cell-index = <2>;
+ compatible = "qcom,camera";
+ reg = <0x02>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <270>;
+ qcom,eeprom-src = <&eeprom2>;
+ qcom,led-flash-src = <&led_flash1>;
+ qcom,actuator-src = <&actuator1>;
+ cam_vio-supply = <&pmcobalt_lvs1>;
+ cam_vana-supply = <&pmcobalt_l22>;
+ cam_vdig-supply = <&pmcobalt_s3>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage =
+ <0 2864000 1352000>;
+ qcom,cam-vreg-max-voltage =
+ <0 2864000 1352000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_front_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_front_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>,
+ <&pmcobalt_gpios 9 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_VDIG";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_mmss clk_mclk1_clk_src>,
+ <&clock_mmss clk_mmss_camss_mclk1_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+};
+&pmcobalt_gpios {
+ gpio@c800 { /* GPIO 9 - CAMERA SENSOR 2 VDIG */
+ qcom,mode = <1>; /* Output */
+ qcom,pull = <5>; /* No Pull */
+ qcom,vin-sel = <0>; /* VIN1 GPIO_LV */
+ qcom,src-sel = <0>; /* GPIO */
+ qcom,invert = <0>; /* Invert */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "ok";
+ };
+
+ gpio@d300 { /* GPIO 20 - CAMERA SENSOR 0 VDIG */
+ qcom,mode = <1>; /* Output */
+ qcom,pull = <5>; /* No Pull */
+ qcom,vin-sel = <1>; /* VIN1 GPIO_MV */
+ qcom,src-sel = <0>; /* GPIO */
+ qcom,invert = <0>; /* Invert */
+ qcom,master-en = <1>; /* Enable GPIO */
+ status = "ok";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi
index b4516f381c0c..a37fa26b1055 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi
@@ -28,7 +28,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>,
@@ -42,7 +45,7 @@
<&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",
@@ -60,7 +63,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>,
@@ -74,7 +80,7 @@
<&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",
@@ -92,7 +98,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>,
@@ -106,7 +115,7 @@
<&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",
@@ -128,8 +137,9 @@
qcom,mipi-csi-vdd-supply = <&pmcobalt_l2>;
gdscr-supply = <&gdsc_camss_top>;
vdd_sec-supply = <&pmcobalt_l1>;
- qcom,cam-vreg-name = "vdd_sec", "gdscr";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ bimc_smmu-supply = <&gdsc_bimc_smmu>;
+ qcom,cam-vreg-name = "vdd_sec", "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>,
@@ -137,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-names = "mnoc_maxi", "mnoc_ahb",
+ <&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";
};
@@ -166,8 +175,9 @@
qcom,mipi-csi-vdd-supply = <&pmcobalt_l2>;
gdscr-supply = <&gdsc_camss_top>;
vdd_sec-supply = <&pmcobalt_l1>;
- qcom,cam-vreg-name = "vdd_sec", "gdscr";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ bimc_smmu-supply = <&gdsc_bimc_smmu>;
+ qcom,cam-vreg-name = "vdd_sec", "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>,
@@ -175,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-names = "mnoc_maxi", "mnoc_ahb",
+ <&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";
};
@@ -204,8 +213,9 @@
qcom,mipi-csi-vdd-supply = <&pmcobalt_l2>;
gdscr-supply = <&gdsc_camss_top>;
vdd_sec-supply = <&pmcobalt_l1>;
- qcom,cam-vreg-name = "vdd_sec", "gdscr";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ bimc_smmu-supply = <&gdsc_bimc_smmu>;
+ qcom,cam-vreg-name = "vdd_sec", "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>,
@@ -213,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-names = "mnoc_maxi", "mnoc_ahb",
+ <&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";
};
@@ -242,8 +251,9 @@
qcom,mipi-csi-vdd-supply = <&pmcobalt_l2>;
gdscr-supply = <&gdsc_camss_top>;
vdd_sec-supply = <&pmcobalt_l1>;
- qcom,cam-vreg-name = "vdd_sec", "gdscr";
- clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ bimc_smmu-supply = <&gdsc_bimc_smmu>;
+ qcom,cam-vreg-name = "vdd_sec", "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>,
@@ -251,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-names = "mnoc_maxi", "mnoc_ahb",
+ <&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";
};
@@ -320,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>,
@@ -331,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",
@@ -342,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>,
@@ -374,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>,
@@ -385,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>,
@@ -405,6 +418,8 @@
<106 512 0 0>,
<106 512 0 0>;
qcom,msm-bus-vector-dyn-vote;
+ resets = <&clock_mmss CAMSS_MICRO_BCR>;
+ reset-names = "micro_iface_reset";
qcom,cpp-fw-payload-info {
qcom,stripe-base = <790>;
qcom,plane-base = <715>;
@@ -440,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>,
@@ -467,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",
@@ -519,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>,
@@ -532,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",
@@ -599,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>,
@@ -612,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",
@@ -685,7 +700,7 @@
mmagic-supply = <&gdsc_bimc_smmu>;
gdscr-supply = <&gdsc_camss_top>;
qcom,cam-vreg-name = "mmagic", "gdscr";
- 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>,
@@ -694,7 +709,7 @@
<&clock_mmss clk_cci_clk_src>,
<&clock_mmss clk_mmss_camss_cci_ahb_clk>,
<&clock_mmss clk_mmss_camss_cci_clk>;
- clock-names = "mnoc_axi", "mnoc_ahb", "smmu_ahb", "smmu_axi",
+ clock-names = "mmssnoc_axi", "mnoc_ahb", "smmu_ahb", "smmu_axi",
"camss_ahb_clk", "camss_top_ahb_clk",
"cci_src_clk", "cci_ahb_clk", "camss_cci_clk";
qcom,clock-rates = <0 0 0 0 0 0 19200000 0 0>,
@@ -737,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",
@@ -746,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>,
@@ -781,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",
@@ -790,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 d44002efea11..fcceac6e2469 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
@@ -21,6 +21,8 @@
qca,bt-vdd-pa-supply = <&pmcobalt_l17_pin_ctrl>;
qca,bt-vdd-ldo-supply = <&pmcobalt_l25_pin_ctrl>;
qca,bt-chip-pwd-supply = <&pmicobalt_bob_pin1>;
+ clocks = <&clock_gcc clk_rf_clk2>;
+ clock-names = "rf_clk2";
qca,bt-vdd-io-voltage-level = <1352000 1352000>;
qca,bt-vdd-xtal-voltage-level = <2040000 2040000>;
@@ -31,9 +33,9 @@
qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */
qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */
- qca,bt-vdd-core-current-level = <0>; /* LPM/PFM */
- qca,bt-vdd-pa-current-level = <0>; /* LPM/PFM */
- qca,bt-vdd-ldo-current-level = <0>; /* LPM/PFM */
+ qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */
};
};
@@ -376,6 +378,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 {
@@ -393,6 +397,13 @@
qcom,5v-boost-gpio = <&tlmm 51 0>;
};
+&dsi_dual_sharp_1080_120hz_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>;
+};
+
&mdss_hdmi_tx {
pinctrl-names = "hdmi_hpd_active", "hdmi_ddc_active", "hdmi_cec_active",
"hdmi_active", "hdmi_sleep";
@@ -418,7 +429,7 @@
};
&pmicobalt_charger {
- qcom,suspend-input;
+ qcom,batteryless-platform;
};
&pmicobalt_haptics {
@@ -532,10 +543,6 @@
};
&soc {
- sound-9335 {
- qcom,wcn-btfm;
- };
-
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
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 3a15fbf6f15c..d973bc5ed84f 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss-panels.dtsi
@@ -24,6 +24,8 @@
#include "dsi-panel-jdi-dualmipi-cmd.dtsi"
#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 {
@@ -77,17 +79,17 @@
};
&dsi_dual_nt35597_video {
- qcom,mdss-dsi-panel-timings = [00 19 05 06 0a 0f 05 06 05 03 04 00];
- qcom,mdss-dsi-t-clk-post = <0x07>;
- qcom,mdss-dsi-t-clk-pre = <0x26>;
+ qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07 05 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
qcom,esd-check-enabled;
qcom,mdss-dsi-panel-status-check-mode = "bta_check";
};
&dsi_dual_nt35597_cmd {
- qcom,mdss-dsi-panel-timings = [00 19 05 06 0a 0f 05 06 05 03 04 00];
- qcom,mdss-dsi-t-clk-post = <0x07>;
- qcom,mdss-dsi-t-clk-pre = <0x26>;
+ qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07 05 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
};
&dsi_dual_nt35597_truly_video {
@@ -149,3 +151,15 @@
qcom,mdss-dsi-t-clk-post = <0x07>;
qcom,mdss-dsi-t-clk-pre = <0x28>;
};
+
+&dsi_dual_sharp_1080_120hz_cmd {
+ qcom,mdss-dsi-panel-timings = [00 19 05 06 0a 0f 05 06 05 03 04 00];
+ 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 fd930d3d1644..ec38e46b1d89 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
@@ -21,9 +21,6 @@
interrupt-controller;
#interrupt-cells = <1>;
vdd-supply = <&gdsc_mdss>;
- vdd-cx-supply = <&pmcobalt_s1_level>;
- vdd-cx-min-uV = <RPM_SMD_REGULATOR_LEVEL_LOW_SVS>;
- vdd-cx-max-uV = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
/* Bus Scale Settings */
qcom,msm-bus,name = "mdss_mdp";
@@ -480,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-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
index 70755ec1b8f5..f9bb6e512d33 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
@@ -22,6 +22,8 @@
qca,bt-vdd-pa-supply = <&pmcobalt_l17_pin_ctrl>;
qca,bt-vdd-ldo-supply = <&pmcobalt_l25_pin_ctrl>;
qca,bt-chip-pwd-supply = <&pmicobalt_bob_pin1>;
+ clocks = <&clock_gcc clk_rf_clk2>;
+ clock-names = "rf_clk2";
qca,bt-vdd-io-voltage-level = <1352000 1352000>;
qca,bt-vdd-xtal-voltage-level = <2040000 2040000>;
@@ -32,9 +34,9 @@
qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */
qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */
- qca,bt-vdd-core-current-level = <0>; /* LPM/PFM */
- qca,bt-vdd-pa-current-level = <0>; /* LPM/PFM */
- qca,bt-vdd-ldo-current-level = <0>; /* LPM/PFM */
+ qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */
};
};
@@ -444,6 +446,13 @@
qcom,5v-boost-gpio = <&tlmm 51 0>;
};
+&dsi_dual_sharp_1080_120hz_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>;
+};
+
&pmicobalt_haptics {
status = "okay";
};
@@ -555,10 +564,6 @@
};
&soc {
- sound-9335 {
- qcom,wcn-btfm;
- };
-
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
index f4f47bc461fc..3975bc5d16f5 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
@@ -1779,6 +1779,66 @@
};
};
+ tsif0_signals_active: tsif0_signals_active {
+ tsif1_clk {
+ pins = "gpio89"; /* TSIF0 CLK */
+ function = "tsif1_clk";
+ };
+ tsif1_en {
+ pins = "gpio90"; /* TSIF0 Enable */
+ function = "tsif1_en";
+ };
+ tsif1_data {
+ pins = "gpio91"; /* TSIF0 DATA */
+ function = "tsif1_data";
+ };
+ signals_cfg {
+ pins = "gpio89", "gpio90", "gpio91";
+ drive_strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+
+ /* sync signal is only used if configured to mode-2 */
+ tsif0_sync_active: tsif0_sync_active {
+ tsif1_sync {
+ pins = "gpio9"; /* TSIF0 SYNC */
+ function = "tsif1_sync";
+ drive_strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+
+ tsif1_signals_active: tsif1_signals_active {
+ tsif2_clk {
+ pins = "gpio93"; /* TSIF1 CLK */
+ function = "tsif2_clk";
+ };
+ tsif2_en {
+ pins = "gpio94"; /* TSIF1 Enable */
+ function = "tsif2_en";
+ };
+ tsif2_data {
+ pins = "gpio95"; /* TSIF1 DATA */
+ function = "tsif2_data";
+ };
+ signals_cfg {
+ pins = "gpio93", "gpio94", "gpio95";
+ drive_strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+
+ /* sync signal is only used if configured to mode-2 */
+ tsif1_sync_active: tsif1_sync_active {
+ tsif2_sync {
+ pins = "gpio96"; /* TSIF1 SYNC */
+ function = "tsif2_sync";
+ drive_strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+
pri_aux_pcm_clk {
pri_aux_pcm_clk_sleep: pri_aux_pcm_clk_sleep {
mux {
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi
index c028ea0eeab3..5f89985db0a3 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-skuk.dtsi
@@ -12,6 +12,7 @@
#include <dt-bindings/interrupt-controller/irq.h>
#include "msmcobalt-pinctrl.dtsi"
+#include "msmcobalt-audio.dtsi"
&blsp1_uart3_hs {
status = "ok";
@@ -99,4 +100,96 @@
debounce-interval = <15>;
};
};
+
+ sound-tavil {
+ qcom,model = "msmcobalt-skuk-tavil-snd-card";
+
+ qcom,audio-routing =
+ "AIF4 VI", "MCLK",
+ "RX_BIAS", "MCLK",
+ "AMIC2", "MIC BIAS2",
+ "MIC BIAS2", "Headset Mic",
+ "DMIC0", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic0",
+ "DMIC1", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic1",
+ "DMIC2", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic2",
+ "DMIC4", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic4",
+ "SpkrLeft IN", "SPK1 OUT";
+
+ qcom,msm-mbhc-hphl-swh = <1>;
+
+ qcom,wsa-max-devs = <1>;
+ qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0213>;
+ 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-qrd.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi
index e0ae9a8873a7..51e1154beaa9 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi
@@ -10,7 +10,529 @@
* GNU General Public License for more details.
*/
-#include "msmcobalt-mtp.dtsi"
+#include <dt-bindings/interrupt-controller/irq.h>
+#include "msmcobalt-pinctrl.dtsi"
+#include "msmcobalt-camera-sensor-qrd.dtsi"
+/ {
+ bluetooth: bt_wcn3990 {
+ compatible = "qca,wcn3990";
+ qca,bt-vdd-io-supply = <&pmcobalt_s3>;
+ qca,bt-vdd-xtal-supply = <&pmcobalt_s5>;
+ qca,bt-vdd-core-supply = <&pmcobalt_l7_pin_ctrl>;
+ qca,bt-vdd-pa-supply = <&pmcobalt_l17_pin_ctrl>;
+ qca,bt-vdd-ldo-supply = <&pmcobalt_l25_pin_ctrl>;
+ qca,bt-chip-pwd-supply = <&pmicobalt_bob_pin1>;
+
+ qca,bt-vdd-io-voltage-level = <1352000 1352000>;
+ qca,bt-vdd-xtal-voltage-level = <2040000 2040000>;
+ qca,bt-vdd-core-voltage-level = <1800000 1800000>;
+ qca,bt-vdd-pa-voltage-level = <1304000 1304000>;
+ qca,bt-vdd-ldo-voltage-level = <3312000 3312000>;
+ qca,bt-chip-pwd-voltage-level = <3600000 3600000>;
+
+ qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-core-current-level = <0>; /* LPM/PFM */
+ qca,bt-vdd-pa-current-level = <0>; /* LPM/PFM */
+ qca,bt-vdd-ldo-current-level = <0>; /* LPM/PFM */
+ };
+};
+
+&blsp1_uart3_hs {
+ status = "ok";
+};
+
+&ufsphy1 {
+ vdda-phy-supply = <&pmcobalt_l1>;
+ vdda-pll-supply = <&pmcobalt_l2>;
+ vddp-ref-clk-supply = <&pmcobalt_l26>;
+ vdda-phy-max-microamp = <51400>;
+ vdda-pll-max-microamp = <14600>;
+ vddp-ref-clk-max-microamp = <100>;
+ vddp-ref-clk-always-on;
+ status = "ok";
+};
+
+&ufs1 {
+ vdd-hba-supply = <&gdsc_ufs>;
+ vdd-hba-fixed-regulator;
+ vcc-supply = <&pmcobalt_l20>;
+ vccq-supply = <&pmcobalt_l26>;
+ vccq2-supply = <&pmcobalt_s4>;
+ vcc-max-microamp = <750000>;
+ vccq-max-microamp = <560000>;
+ vccq2-max-microamp = <750000>;
+ status = "ok";
+};
+
+&ufs_ice {
+ status = "ok";
+};
+
+&sdhc_2 {
+ vdd-supply = <&pmcobalt_l21>;
+ qcom,vdd-voltage-level = <2950000 2960000>;
+ qcom,vdd-current-level = <200 800000>;
+
+ vdd-io-supply = <&pmcobalt_l13>;
+ qcom,vdd-io-voltage-level = <1808000 2960000>;
+ qcom,vdd-io-current-level = <200 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>;
+
+ qcom,clk-rates = <400000 20000000 25000000
+ 50000000 100000000 200000000>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+ cd-gpios = <&tlmm 95 0x1>;
+
+ status = "ok";
+};
+
+&uartblsp2dm1 {
+ status = "ok";
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart_console_active>;
+};
+
+&pmcobalt_gpios {
+ /* GPIO 6 for Vol+ Key */
+ gpio@c500 {
+ status = "okay";
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,out-strength = <1>;
+ };
+
+ /* GPIO 7 for Snapshot Key */
+ gpio@c600 {
+ status = "okay";
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,out-strength = <1>;
+ };
+
+ /* GPIO 8 for Focus Key */
+ gpio@c700 {
+ status = "okay";
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,out-strength = <1>;
+ };
+
+ gpio@cc00 { /* GPIO 13 */
+ qcom,mode = <1>;
+ qcom,output-type = <0>;
+ qcom,pull = <5>;
+ qcom,vin-sel = <0>;
+ qcom,out-strength = <1>;
+ qcom,src-sel = <3>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+
+ /* GPIO 21 (NFC_CLK_REQ) */
+ gpio@d400 {
+ qcom,mode = <0>;
+ qcom,vin-sel = <1>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+
+ /* GPIO 18 SMB138X */
+ gpio@d100 {
+ qcom,mode = <0>;
+ qcom,pull = <0>;
+ qcom,vin-sel = <0>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+};
+
+&i2c_5 {
+ status = "okay";
+ synaptics@20 {
+ compatible = "synaptics,dsx";
+ reg = <0x20>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <125 0x2008>;
+ vdd-supply = <&pmcobalt_l6>;
+ avdd-supply = <&pmcobalt_l28>;
+ synaptics,vdd-voltage = <1808000 1808000>;
+ synaptics,avdd-voltage = <3008000 3008000>;
+ synaptics,vdd-current = <40000>;
+ synaptics,avdd-current = <20000>;
+ pinctrl-names = "pmx_ts_active", "pmx_ts_suspend";
+ pinctrl-0 = <&ts_active>;
+ pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+ synaptics,display-coords = <0 0 1439 2559>;
+ synaptics,panel-coords = <0 0 1439 2559>;
+ synaptics,reset-gpio = <&tlmm 89 0x00>;
+ synaptics,irq-gpio = <&tlmm 125 0x2008>;
+ synaptics,disable-gpios;
+ synaptics,fw-name = "PR1702898-s3528t_60QHD_00400001.img";
+ };
+};
+
+&i2c_6 { /* BLSP1 QUP6 (NFC) */
+ status = "okay";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 92 0x00>;
+ qcom,nq-ven = <&tlmm 12 0x00>;
+ qcom,nq-firm = <&tlmm 93 0x00>;
+ qcom,nq-clkreq = <&pmcobalt_gpios 21 0x00>;
+ qcom,nq-esepwr = <&tlmm 116 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <92 0>;
+ interrupt-names = "nfc_irq";
+ pinctrl-names = "nfc_active", "nfc_suspend";
+ pinctrl-0 = <&nfc_int_active &nfc_enable_active>;
+ pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+ clocks = <&clock_gcc clk_ln_bb_clk3_pin>;
+ clock-names = "ref_clk";
+ };
+};
+
+&mdss_hdmi_tx {
+ pinctrl-names = "hdmi_hpd_active", "hdmi_ddc_active", "hdmi_cec_active",
+ "hdmi_active", "hdmi_sleep";
+ pinctrl-0 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active
+ &mdss_hdmi_ddc_suspend &mdss_hdmi_cec_suspend>;
+ pinctrl-1 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active
+ &mdss_hdmi_ddc_active &mdss_hdmi_cec_suspend>;
+ pinctrl-2 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active
+ &mdss_hdmi_cec_active &mdss_hdmi_ddc_suspend>;
+ pinctrl-3 = <&mdss_hdmi_5v_active &mdss_hdmi_hpd_active
+ &mdss_hdmi_ddc_active &mdss_hdmi_cec_active>;
+ pinctrl-4 = <&mdss_hdmi_5v_suspend &mdss_hdmi_hpd_suspend
+ &mdss_hdmi_ddc_suspend &mdss_hdmi_cec_suspend>;
+};
+
+&mdss_dp_ctrl {
+ pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
+ pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>;
+ pinctrl-1 = <&mdss_dp_aux_suspend &mdss_dp_usbplug_cc_suspend>;
+ qcom,aux-en-gpio = <&tlmm 77 0>;
+ qcom,aux-sel-gpio = <&tlmm 78 0>;
+ qcom,usbplug-cc-gpio = <&tlmm 38 0>;
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "split_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_dual_nt35597_video>;
+ 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>;
+ qcom,panel-mode-gpio = <&tlmm 91 0>;
+};
+
+&mdss_dsi1 {
+ qcom,dsi-pref-prim-pan = <&dsi_dual_nt35597_video>;
+ 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>;
+ qcom,panel-mode-gpio = <&tlmm 91 0>;
+};
+
+&labibb {
+ status = "ok";
+ qpnp,qpnp-labibb-mode = "lcd";
+};
+
+&dsi_dual_nt35597_video {
+ 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-mode-sel-gpio-state = "dual_port";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_dual_nt35597_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,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_dual_nt35597_truly_video {
+ 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-mode-sel-gpio-state = "dual_port";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_dual_nt35597_truly_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,mdss-dsi-mode-sel-gpio-state = "dual_port";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_nt35597_dsc_video {
+ 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-mode-sel-gpio-state = "single_port";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_nt35597_dsc_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,mdss-dsi-mode-sel-gpio-state = "single_port";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_sharp_4k_dsc_video {
+ 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>;
+};
+
+&dsi_sharp_4k_dsc_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>;
+};
+
+&dsi_dual_jdi_video {
+ 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,5v-boost-gpio = <&tlmm 51 0>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_dual_jdi_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,5v-boost-gpio = <&tlmm 51 0>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_sharp_1080_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>;
+};
+
+&dsi_jdi_1080_vid {
+ 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_no_labibb>;
+ qcom,5v-boost-gpio = <&tlmm 51 0>;
+};
+
+&i2c_7 {
+ status = "okay";
+ qcom,smb138x@8 {
+ compatible = "qcom,i2c-pmic";
+ reg = <0x8>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ interrupt-parent = <&spmi_bus>;
+ interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>;
+ interrupt_names = "smb138x";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ qcom,periph-map = <0x10 0x11 0x12 0x13 0x14 0x16 0x36>;
+
+ smb138x_parallel_slave: qcom,smb138x-parallel-slave@1000 {
+ compatible = "qcom,smb138x-parallel-slave";
+ reg = <0x1000 0x700>;
+ };
+ };
+};
+
+&pmicobalt_haptics {
+ status = "okay";
+};
+
+&pmcobalt_vadc {
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@85 {
+ label = "vcoin";
+ reg = <0x85>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4d {
+ label = "msm_therm";
+ reg = <0x4d>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@51 {
+ label = "quiet_therm";
+ reg = <0x51>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+};
+
+&pmcobalt_adc_tm {
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,btm-channel-number = <0x60>;
+ };
+
+ chan@4d {
+ label = "msm_therm";
+ reg = <0x4d>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x68>;
+ qcom,thermal-node;
+ };
+
+ chan@51 {
+ label = "quiet_therm";
+ reg = <0x51>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x70>;
+ qcom,thermal-node;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x78>;
+ qcom,thermal-node;
+ };
+};
+
+&wil6210 {
+ status = "ok";
+};
+
+&soc {
+ sound-9335 {
+ qcom,wcn-btfm;
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+ status = "okay";
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&pmcobalt_gpios 6 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ cam_snapshot {
+ label = "cam_snapshot";
+ gpios = <&pmcobalt_gpios 7 0x1>;
+ linux,input-type = <1>;
+ linux,code = <766>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ cam_focus {
+ label = "cam_focus";
+ gpios = <&pmcobalt_gpios 8 0x1>;
+ linux,input-type = <1>;
+ linux,code = <528>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
+};
+
+/{
+ mtp_batterydata: qcom,battery-data {
+ qcom,batt-id-range-pct = <15>;
+ #include "fg-gen3-batterydata-itech-3000mah.dtsi"
+ #include "fg-gen3-batterydata-ascent-3450mah.dtsi"
+ };
+};
&mdss_mdp {
qcom,mdss-pref-prim-intf = "dsi";
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi
index 86bc048adeb5..c2d45ec3ef07 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi
@@ -533,7 +533,7 @@
regulator-name = "pm8005_s1";
status = "okay";
regulator-min-microvolt = <524000>;
- regulator-max-microvolt = <1032000>;
+ regulator-max-microvolt = <1100000>;
qcom,enable-time = <500>;
};
};
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 db50038b297e..74aae4051ad6 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
@@ -28,88 +28,89 @@
compatible = "qcom,cpu-clock-osm-msmcobalt-v2";
/delete-property/ qcom,llm-sw-overr;
qcom,pwrcl-speedbin0-v0 =
- < 300000000 0x0004000f 0x01200020 0x1 >,
- < 364800000 0x05040013 0x01200020 0x1 >,
- < 441600000 0x05040017 0x02200020 0x1 >,
- < 518400000 0x0504001b 0x02200020 0x1 >,
- < 595200000 0x0504001f 0x02200020 0x1 >,
- < 672000000 0x05040023 0x03200020 0x1 >,
- < 748800000 0x05040027 0x03200020 0x1 >,
- < 825600000 0x0404002b 0x03220022 0x1 >,
- < 883200000 0x0404002e 0x04250025 0x1 >,
- < 960000000 0x04040032 0x04280028 0x1 >,
- < 1036800000 0x04040036 0x042b002b 0x1 >,
- < 1094400000 0x04040039 0x052e002e 0x2 >,
- < 1171200000 0x0404003d 0x05310031 0x2 >,
- < 1248000000 0x04040041 0x05340034 0x2 >,
- < 1324800000 0x04040045 0x06370037 0x2 >,
- < 1401600000 0x04040049 0x063a003a 0x2 >,
- < 1478400000 0x0404004d 0x073e003e 0x2 >,
- < 1555200000 0x04040051 0x07410041 0x2 >,
- < 1670400000 0x04040057 0x08460046 0x2 >,
- < 1747200000 0x0404005b 0x08490049 0x2 >,
- < 1824000000 0x0404005f 0x084c004c 0x3 >,
- < 1900800000 0x04040063 0x094f004f 0x3 >;
+ < 300000000 0x0004000f 0x01200020 0x1 1 >,
+ < 364800000 0x05040013 0x01200020 0x1 2 >,
+ < 441600000 0x05040017 0x02200020 0x1 3 >,
+ < 518400000 0x0504001b 0x02200020 0x1 4 >,
+ < 595200000 0x0504001f 0x02200020 0x1 5 >,
+ < 672000000 0x05040023 0x03200020 0x1 6 >,
+ < 748800000 0x05040027 0x03200020 0x1 7 >,
+ < 825600000 0x0404002b 0x03220022 0x1 8 >,
+ < 883200000 0x0404002e 0x04250025 0x1 9 >,
+ < 960000000 0x04040032 0x04280028 0x1 10 >,
+ < 1036800000 0x04040036 0x042b002b 0x1 11 >,
+ < 1094400000 0x04040039 0x052e002e 0x2 12 >,
+ < 1171200000 0x0404003d 0x05310031 0x2 13 >,
+ < 1248000000 0x04040041 0x05340034 0x2 14 >,
+ < 1324800000 0x04040045 0x06370037 0x2 15 >,
+ < 1401600000 0x04040049 0x063a003a 0x2 16 >,
+ < 1478400000 0x0404004d 0x073e003e 0x2 17 >,
+ < 1555200000 0x04040051 0x07410041 0x2 18 >,
+ < 1670400000 0x04040057 0x08460046 0x2 19 >,
+ < 1747200000 0x0404005b 0x08490049 0x2 20 >,
+ < 1824000000 0x0404005f 0x084c004c 0x3 21 >,
+ < 1900800000 0x04040063 0x094f004f 0x3 22 >;
qcom,perfcl-speedbin0-v0 =
- < 300000000 0x0004000f 0x01200020 0x1 >,
- < 345600000 0x05040012 0x01200020 0x1 >,
- < 422400000 0x05040016 0x02200020 0x1 >,
- < 499200000 0x0504001a 0x02200020 0x1 >,
- < 576000000 0x0504001e 0x02200020 0x1 >,
- < 652800000 0x05040022 0x03200020 0x1 >,
- < 729600000 0x05040026 0x03200020 0x1 >,
- < 806400000 0x0504002a 0x03220022 0x1 >,
- < 902400000 0x0404002f 0x04260026 0x1 >,
- < 979200000 0x04040033 0x04290029 0x1 >,
- < 1056000000 0x04040037 0x052c002c 0x1 >,
- < 1132800000 0x0404003b 0x052f002f 0x1 >,
- < 1190400000 0x0404003e 0x05320032 0x2 >,
- < 1267200000 0x04040042 0x06350035 0x2 >,
- < 1344000000 0x04040046 0x06380038 0x2 >,
- < 1420800000 0x0404004a 0x063b003b 0x2 >,
- < 1497600000 0x0404004e 0x073e003e 0x2 >,
- < 1574400000 0x04040052 0x07420042 0x2 >,
- < 1651200000 0x04040056 0x07450045 0x2 >,
- < 1728000000 0x0404005a 0x08480048 0x2 >,
- < 1804800000 0x0404005e 0x084b004b 0x2 >,
- < 1881600000 0x04040062 0x094e004e 0x2 >,
- < 1958400000 0x04040066 0x09520052 0x2 >,
- < 2035200000 0x0404006a 0x09550055 0x3 >,
- < 2112000000 0x0404006e 0x0a580058 0x3 >,
- < 2188800000 0x04040072 0x0a5b005b 0x3 >,
- < 2265600000 0x04040076 0x0a5e005e 0x3 >,
- < 2342400000 0x0404007a 0x0a620062 0x3 >,
- < 2419200000 0x0404007e 0x0a650065 0x3 >,
- < 2496000000 0x04040082 0x0a680068 0x3 >;
+ < 300000000 0x0004000f 0x01200020 0x1 1 >,
+ < 345600000 0x05040012 0x01200020 0x1 2 >,
+ < 422400000 0x05040016 0x02200020 0x1 3 >,
+ < 499200000 0x0504001a 0x02200020 0x1 4 >,
+ < 576000000 0x0504001e 0x02200020 0x1 5 >,
+ < 652800000 0x05040022 0x03200020 0x1 6 >,
+ < 729600000 0x05040026 0x03200020 0x1 7 >,
+ < 806400000 0x0504002a 0x03220022 0x1 8 >,
+ < 902400000 0x0404002f 0x04260026 0x1 9 >,
+ < 979200000 0x04040033 0x04290029 0x1 10 >,
+ < 1056000000 0x04040037 0x052c002c 0x1 11 >,
+ < 1132800000 0x0404003b 0x052f002f 0x1 12 >,
+ < 1190400000 0x0404003e 0x05320032 0x2 13 >,
+ < 1267200000 0x04040042 0x06350035 0x2 14 >,
+ < 1344000000 0x04040046 0x06380038 0x2 15 >,
+ < 1420800000 0x0404004a 0x063b003b 0x2 16 >,
+ < 1497600000 0x0404004e 0x073e003e 0x2 17 >,
+ < 1574400000 0x04040052 0x07420042 0x2 18 >,
+ < 1651200000 0x04040056 0x07450045 0x2 19 >,
+ < 1728000000 0x0404005a 0x08480048 0x2 20 >,
+ < 1804800000 0x0404005e 0x084b004b 0x2 21 >,
+ < 1881600000 0x04040062 0x094e004e 0x2 22 >,
+ < 1958400000 0x04040066 0x09520052 0x2 23 >,
+ < 2035200000 0x0404006a 0x09550055 0x3 24 >,
+ < 2112000000 0x0404006e 0x0a580058 0x3 25 >,
+ < 2188800000 0x04040072 0x0a5b005b 0x3 26 >,
+ < 2265600000 0x04040076 0x0a5e005e 0x3 27 >,
+ < 2342400000 0x0404007a 0x0a620062 0x3 28 >,
+ < 2419200000 0x0404007e 0x0a650065 0x3 29 >,
+ < 2496000000 0x04040082 0x0a680068 0x3 30 >;
qcom,perfcl-speedbin1-v0 =
- < 300000000 0x0004000f 0x01200020 0x1 >,
- < 345600000 0x05040012 0x01200020 0x1 >,
- < 422400000 0x05040016 0x02200020 0x1 >,
- < 499200000 0x0504001a 0x02200020 0x1 >,
- < 576000000 0x0504001e 0x02200020 0x1 >,
- < 652800000 0x05040022 0x03200020 0x1 >,
- < 729600000 0x05040026 0x03200020 0x1 >,
- < 806400000 0x0504002a 0x03220022 0x1 >,
- < 902400000 0x0404002f 0x04260026 0x1 >,
- < 979200000 0x04040033 0x04290029 0x1 >,
- < 1056000000 0x04040037 0x052c002c 0x1 >,
- < 1132800000 0x0404003b 0x052f002f 0x1 >,
- < 1190400000 0x0404003e 0x05320032 0x2 >,
- < 1267200000 0x04040042 0x06350035 0x2 >,
- < 1344000000 0x04040046 0x06380038 0x2 >,
- < 1420800000 0x0404004a 0x063b003b 0x2 >,
- < 1497600000 0x0404004e 0x073e003e 0x2 >,
- < 1574400000 0x04040052 0x07420042 0x2 >,
- < 1651200000 0x04040056 0x07450045 0x2 >,
- < 1728000000 0x0404005a 0x08480048 0x2 >,
- < 1804800000 0x0404005e 0x084b004b 0x2 >,
- < 1881600000 0x04040062 0x094e004e 0x2 >,
- < 1958400000 0x04040066 0x09520052 0x2 >,
- < 2035200000 0x0404006a 0x09550055 0x3 >,
- < 2112000000 0x0404006e 0x0a580058 0x3 >,
- < 2208000000 0x04040073 0x0a5c005c 0x3 >;
+ < 300000000 0x0004000f 0x01200020 0x1 1 >,
+ < 345600000 0x05040012 0x01200020 0x1 2 >,
+ < 422400000 0x05040016 0x02200020 0x1 3 >,
+ < 499200000 0x0504001a 0x02200020 0x1 4 >,
+ < 576000000 0x0504001e 0x02200020 0x1 5 >,
+ < 652800000 0x05040022 0x03200020 0x1 6 >,
+ < 729600000 0x05040026 0x03200020 0x1 7 >,
+ < 806400000 0x0504002a 0x03220022 0x1 8 >,
+ < 902400000 0x0404002f 0x04260026 0x1 9 >,
+ < 979200000 0x04040033 0x04290029 0x1 10 >,
+ < 1056000000 0x04040037 0x052c002c 0x1 11 >,
+ < 1132800000 0x0404003b 0x052f002f 0x1 12 >,
+ < 1190400000 0x0404003e 0x05320032 0x2 13 >,
+ < 1267200000 0x04040042 0x06350035 0x2 14 >,
+ < 1344000000 0x04040046 0x06380038 0x2 15 >,
+ < 1420800000 0x0404004a 0x063b003b 0x2 16 >,
+ < 1497600000 0x0404004e 0x073e003e 0x2 17 >,
+ < 1574400000 0x04040052 0x07420042 0x2 18 >,
+ < 1651200000 0x04040056 0x07450045 0x2 19 >,
+ < 1728000000 0x0404005a 0x08480048 0x2 20 >,
+ < 1804800000 0x0404005e 0x084b004b 0x2 21 >,
+ < 1881600000 0x04040062 0x094e004e 0x2 22 >,
+ < 1958400000 0x04040066 0x09520052 0x2 23 >,
+ < 2035200000 0x0404006a 0x09550055 0x3 24 >,
+ < 2112000000 0x0404006e 0x0a580058 0x3 25 >,
+ < 2208000000 0x04040073 0x0a5c005c 0x3 26 >,
+ < 2304000000 0x04010078 0x0a5c005c 0x3 26 >;
};
&msm_cpufreq {
@@ -170,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 >;
};
};
@@ -202,8 +208,7 @@
< 414000000 4 RPM_SMD_REGULATOR_LEVEL_SVS >,
< 515000000 5 RPM_SMD_REGULATOR_LEVEL_NOM >,
< 596000000 6 RPM_SMD_REGULATOR_LEVEL_NOM >,
- < 670000000 7 RPM_SMD_REGULATOR_LEVEL_TURBO >,
- < 710000000 8 RPM_SMD_REGULATOR_LEVEL_TURBO >;
+ < 670000000 7 RPM_SMD_REGULATOR_LEVEL_TURBO >;
qcom,gfxfreq-mx-speedbin0 =
< 0 0 >,
< 180000000 RPM_SMD_REGULATOR_LEVEL_SVS >,
@@ -212,8 +217,7 @@
< 414000000 RPM_SMD_REGULATOR_LEVEL_SVS >,
< 515000000 RPM_SMD_REGULATOR_LEVEL_NOM >,
< 596000000 RPM_SMD_REGULATOR_LEVEL_NOM >,
- < 670000000 RPM_SMD_REGULATOR_LEVEL_TURBO >,
- < 710000000 RPM_SMD_REGULATOR_LEVEL_TURBO >;
+ < 670000000 RPM_SMD_REGULATOR_LEVEL_TURBO >;
};
&mdss_mdp {
@@ -250,14 +254,14 @@
/* Speed bin 0 */
<828000 828000 828000 828000 828000
828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 952000 952000
+ 828000 900000 900000 900000 900000
+ 900000 900000 900000 952000 952000
1056000 1056000>,
/* Speed bin 1 */
<828000 828000 828000 828000 828000
828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 952000 952000
+ 828000 900000 900000 900000 900000
+ 900000 900000 900000 952000 952000
1056000 1056000>;
qcom,cpr-voltage-floor =
@@ -326,43 +330,43 @@
qcom,cpr-open-loop-voltage-fuse-adjustment =
/* Speed bin 0 */
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
/* Speed bin 1 */
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>,
- <40000 24000 0 0>;
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>,
+ <40000 24000 0 30000>;
qcom,cpr-closed-loop-voltage-fuse-adjustment =
/* Speed bin 0 */
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
/* Speed bin 1 */
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>,
- <20000 26000 0 0>;
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>,
+ <20000 26000 0 30000>;
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
@@ -396,15 +400,15 @@
/* Speed bin 0 */
<828000 828000 828000 828000 828000
828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
+ 828000 828000 900000 900000 900000
+ 900000 900000 900000 900000 900000
952000 952000 952000 1056000 1056000
1056000 1056000 1056000 1056000 1056000>,
/* Speed bin 1 */
<828000 828000 828000 828000 828000
828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
- 828000 828000 828000 828000 828000
+ 828000 828000 900000 900000 900000
+ 900000 900000 900000 900000 900000
952000 952000 952000 1056000 1056000
1056000>;
@@ -525,6 +529,11 @@
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
};
+&pm8005_s1 {
+ regulator-min-microvolt = <516000>;
+ regulator-max-microvolt = <1088000>;
+};
+
&gfx_cpr {
compatible = "qcom,cpr4-msmcobalt-v2-mmss-regulator";
};
@@ -540,12 +549,17 @@
qcom,cpr-corner-fmax-map = <1 3 5 8>;
qcom,cpr-voltage-ceiling =
- <656000 716000 772000 880000 908000
- 948000 1016000 1088000>;
+ <656000 716000 772000 880000 908000 948000 1016000 1088000>,
+ <660000 724000 772000 832000 916000 968000 1024000 1024000>,
+ <660000 724000 772000 832000 916000 968000 1024000 1024000>,
+ <660000 724000 772000 832000 916000 968000 1024000 1024000>,
+ <660000 724000 772000 832000 916000 968000 1024000 1024000>,
+ <660000 724000 772000 832000 916000 968000 1024000 1024000>,
+ <660000 724000 772000 832000 916000 968000 1024000 1024000>,
+ <660000 724000 772000 832000 916000 968000 1024000 1024000>;
qcom,cpr-voltage-floor =
- <516000 516000 532000 584000 632000
- 672000 712000 756000>;
+ <516000 516000 532000 584000 632000 672000 712000 756000>;
qcom,mem-acc-voltage = <1 1 1 2 2 2 2 2>;
@@ -590,11 +604,11 @@
0 0 3487 0 3280 1896 1874 0>;
qcom,cpr-open-loop-voltage-fuse-adjustment =
- < 72000 0 0 0>;
+ < 100000 0 0 0>;
qcom,cpr-closed-loop-voltage-adjustment =
- < 65000 26000 8000 0
- 0 0 0 0>;
+ < 96000 18000 4000 0
+ 0 13000 9000 0>;
qcom,cpr-floor-to-ceiling-max-range =
<50000 50000 50000 50000 50000 50000 70000 70000>;
@@ -655,18 +669,35 @@
/* 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 */
+
+ qcom,dcvs-tbl = /* minLoad LoadLow LoadHigh CodecCheck */
+ /* Decode */
+ /* Load > Nominal, Nominal <-> Turbo Eg.3840x2160@60 */
+ <1728000 1728000 2211840 0x3f00000c>,
+ /* Encoder */
+ /* Load > Nominal, Nominal <-> Turbo Eg. 4kx2304@30 */
+ <972000 972000 1105920 0x04000004>,
+ /* Load > SVSL1, SVSL1<-> Nominal Eg. 3840x2160@30 */
+ <939700 939700 972000 0x04000004>,
+ /* Load > SVS , SVS <-> SVSL1 Eg. 4kx2304@24 */
+ <489600 489600 939700 0x04000004>;
+
+ qcom,dcvs-limit = /* Min Frame size, Min MBs/sec */
+ <32400 30>, /* Encoder 3840x2160@30 */
+ <32400 60>; /* Decoder 3840x2160@60 */
+
};
/* GPU overrides */
&msm_gpu {
/* Updated chip ID */
qcom,chipid = <0x05040001>;
- qcom,initial-pwrlevel = <6>;
+ qcom,initial-pwrlevel = <5>;
qcom,gpu-pwrlevels {
#address-cells = <1>;
@@ -676,69 +707,61 @@
qcom,gpu-pwrlevel@0 {
reg = <0>;
- qcom,gpu-freq = <710000000>;
- qcom,bus-freq = <12>;
- qcom,bus-min = <12>;
- qcom,bus-max = <12>;
- };
-
- qcom,gpu-pwrlevel@1 {
- reg = <1>;
qcom,gpu-freq = <670000000>;
qcom,bus-freq = <12>;
qcom,bus-min = <11>;
qcom,bus-max = <12>;
};
- qcom,gpu-pwrlevel@2 {
- reg = <2>;
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
qcom,gpu-freq = <596000000>;
qcom,bus-freq = <11>;
qcom,bus-min = <9>;
qcom,bus-max = <12>;
};
- qcom,gpu-pwrlevel@3 {
- reg = <3>;
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
qcom,gpu-freq = <515000000>;
qcom,bus-freq = <11>;
qcom,bus-min = <9>;
qcom,bus-max = <12>;
};
- qcom,gpu-pwrlevel@4 {
- reg = <4>;
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
qcom,gpu-freq = <414000000>;
qcom,bus-freq = <9>;
qcom,bus-min = <8>;
qcom,bus-max = <11>;
};
- qcom,gpu-pwrlevel@5 {
- reg = <5>;
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
qcom,gpu-freq = <342000000>;
qcom,bus-freq = <8>;
qcom,bus-min = <5>;
qcom,bus-max = <9>;
};
- qcom,gpu-pwrlevel@6 {
- reg = <6>;
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
qcom,gpu-freq = <257000000>;
qcom,bus-freq = <5>;
qcom,bus-min = <3>;
qcom,bus-max = <8>;
};
- qcom,gpu-pwrlevel@7 {
- reg = <7>;
+ qcom,gpu-pwrlevel@6 {
+ reg = <6>;
qcom,gpu-freq = <180000000>;
qcom,bus-freq = <3>;
qcom,bus-min = <1>;
qcom,bus-max = <5>;
};
- qcom,gpu-pwrlevel@8 {
- reg = <8>;
+ qcom,gpu-pwrlevel@7 {
+ reg = <7>;
qcom,gpu-freq = <27000000>;
qcom,bus-freq = <0>;
qcom,bus-min = <0>;
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 789a322f73bf..96518dca0ec1 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 {
@@ -823,55 +822,55 @@
interrupt-names = "pwrcl-irq", "perfcl-irq";
qcom,pwrcl-speedbin0-v0 =
- < 300000000 0x0004000f 0x01200020 0x1 >,
- < 345600000 0x05040012 0x02200020 0x1 >,
- < 422400000 0x05040016 0x02200020 0x1 >,
- < 499200000 0x0504001a 0x02200020 0x1 >,
- < 576000000 0x0504001e 0x03200020 0x1 >,
- < 633600000 0x05040021 0x03200020 0x1 >,
- < 710400000 0x05040025 0x03200020 0x1 >,
- < 806400000 0x0504002a 0x04200020 0x1 >,
- < 883200000 0x0404002e 0x04250025 0x1 >,
- < 960000000 0x04040032 0x05280028 0x1 >,
- < 1036800000 0x04040036 0x052b002b 0x2 >,
- < 1113600000 0x0404003a 0x052e002e 0x2 >,
- < 1190400000 0x0404003e 0x06320032 0x2 >,
- < 1248000000 0x04040041 0x06340034 0x2 >,
- < 1324800000 0x04040045 0x06370037 0x2 >,
- < 1401600000 0x04040049 0x073a003a 0x2 >,
- < 1478400000 0x0404004d 0x073e003e 0x2 >,
- < 1574400000 0x04040052 0x08420042 0x2 >,
- < 1651200000 0x04040056 0x08450045 0x2 >,
- < 1728000000 0x0404005a 0x08480048 0x2 >,
- < 1804800000 0x0404005e 0x094b004b 0x3 >,
- < 1881600000 0x04040062 0x094e004e 0x3 >;
+ < 300000000 0x0004000f 0x01200020 0x1 1 >,
+ < 345600000 0x05040012 0x02200020 0x1 2 >,
+ < 422400000 0x05040016 0x02200020 0x1 3 >,
+ < 499200000 0x0504001a 0x02200020 0x1 4 >,
+ < 576000000 0x0504001e 0x03200020 0x1 5 >,
+ < 633600000 0x05040021 0x03200020 0x1 6 >,
+ < 710400000 0x05040025 0x03200020 0x1 7 >,
+ < 806400000 0x0504002a 0x04200020 0x1 8 >,
+ < 883200000 0x0404002e 0x04250025 0x1 9 >,
+ < 960000000 0x04040032 0x05280028 0x1 10 >,
+ < 1036800000 0x04040036 0x052b002b 0x2 11 >,
+ < 1113600000 0x0404003a 0x052e002e 0x2 12 >,
+ < 1190400000 0x0404003e 0x06320032 0x2 13 >,
+ < 1248000000 0x04040041 0x06340034 0x2 14 >,
+ < 1324800000 0x04040045 0x06370037 0x2 15 >,
+ < 1401600000 0x04040049 0x073a003a 0x2 16 >,
+ < 1478400000 0x0404004d 0x073e003e 0x2 17 >,
+ < 1574400000 0x04040052 0x08420042 0x2 18 >,
+ < 1651200000 0x04040056 0x08450045 0x2 19 >,
+ < 1728000000 0x0404005a 0x08480048 0x2 20 >,
+ < 1804800000 0x0404005e 0x094b004b 0x3 21 >,
+ < 1881600000 0x04040062 0x094e004e 0x3 22 >;
qcom,perfcl-speedbin0-v0 =
- < 300000000 0x0004000f 0x01200020 0x1 >,
- < 345600000 0x05040012 0x02200020 0x1 >,
- < 422400000 0x05040016 0x02200020 0x1 >,
- < 480000000 0x05040019 0x02200020 0x1 >,
- < 556800000 0x0504001d 0x03200020 0x1 >,
- < 633600000 0x05040021 0x03200020 0x1 >,
- < 710400000 0x05040025 0x03200020 0x1 >,
- < 787200000 0x05040029 0x04200020 0x1 >,
- < 844800000 0x0404002c 0x04230023 0x1 >,
- < 902400000 0x0404002f 0x04260026 0x1 >,
- < 979200000 0x04040033 0x05290029 0x1 >,
- < 1056000000 0x04040037 0x052c002c 0x1 >,
- < 1171200000 0x0404003d 0x06310031 0x2 >,
- < 1248000000 0x04040041 0x06340034 0x2 >,
- < 1324800000 0x04040045 0x06370037 0x2 >,
- < 1401600000 0x04040049 0x073a003a 0x2 >,
- < 1478400000 0x0404004d 0x073e003e 0x2 >,
- < 1536000000 0x04040050 0x07400040 0x2 >,
- < 1632000000 0x04040055 0x08440044 0x2 >,
- < 1708800000 0x04040059 0x08470047 0x2 >,
- < 1785600000 0x0404005d 0x094a004a 0x2 >,
- < 1862400000 0x04040061 0x094e004e 0x2 >,
- < 1939200000 0x04040065 0x09510051 0x3 >,
- < 2016000000 0x04040069 0x0a540054 0x3 >,
- < 2092800000 0x0404006d 0x0a570057 0x3 >;
+ < 300000000 0x0004000f 0x01200020 0x1 1 >,
+ < 345600000 0x05040012 0x02200020 0x1 2 >,
+ < 422400000 0x05040016 0x02200020 0x1 3 >,
+ < 480000000 0x05040019 0x02200020 0x1 4 >,
+ < 556800000 0x0504001d 0x03200020 0x1 5 >,
+ < 633600000 0x05040021 0x03200020 0x1 6 >,
+ < 710400000 0x05040025 0x03200020 0x1 7 >,
+ < 787200000 0x05040029 0x04200020 0x1 8 >,
+ < 844800000 0x0404002c 0x04230023 0x1 9 >,
+ < 902400000 0x0404002f 0x04260026 0x1 10 >,
+ < 979200000 0x04040033 0x05290029 0x1 11 >,
+ < 1056000000 0x04040037 0x052c002c 0x1 12 >,
+ < 1171200000 0x0404003d 0x06310031 0x2 13 >,
+ < 1248000000 0x04040041 0x06340034 0x2 14 >,
+ < 1324800000 0x04040045 0x06370037 0x2 15 >,
+ < 1401600000 0x04040049 0x073a003a 0x2 16 >,
+ < 1478400000 0x0404004d 0x073e003e 0x2 17 >,
+ < 1536000000 0x04040050 0x07400040 0x2 18 >,
+ < 1632000000 0x04040055 0x08440044 0x2 19 >,
+ < 1708800000 0x04040059 0x08470047 0x2 20 >,
+ < 1785600000 0x0404005d 0x094a004a 0x2 21 >,
+ < 1862400000 0x04040061 0x094e004e 0x2 22 >,
+ < 1939200000 0x04040065 0x09510051 0x3 23 >,
+ < 2016000000 0x04040069 0x0a540054 0x3 24 >,
+ < 2092800000 0x0404006d 0x0a570057 0x3 25 >;
qcom,up-timer =
<1000 1000>;
@@ -1579,18 +1578,25 @@
<&clock_gcc clk_gcc_pcie_0_cfg_ahb_clk>,
<&clock_gcc clk_gcc_pcie_0_mstr_axi_clk>,
<&clock_gcc clk_gcc_pcie_0_slv_axi_clk>,
- <&clock_gcc clk_gcc_pcie_clkref_clk>,
- <&clock_gcc clk_gcc_pcie_phy_reset>;
+ <&clock_gcc clk_gcc_pcie_clkref_clk>;
clock-names = "pcie_0_pipe_clk", "pcie_0_ref_clk_src",
"pcie_0_aux_clk", "pcie_0_cfg_ahb_clk",
"pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk",
- "pcie_0_ldo", "pcie_0_phy_reset";
+ "pcie_0_ldo";
max-clock-frequency-hz = <0>, <0>, <19200000>,
<0>, <0>, <0>, <0>, <0>, <0>,
<0>, <0>, <0>, <0>, <0>, <0>,
<0>, <0>;
+
+ resets = <&clock_gcc PCIE_PHY_BCR>,
+ <&clock_gcc PCIE_0_PHY_BCR>,
+ <&clock_gcc PCIE_0_PHY_BCR>;
+
+ reset-names = "pcie_phy_reset",
+ "pcie_0_phy_reset",
+ "pcie_0_phy_pipe_reset";
};
qcom,ipc_router {
@@ -2184,7 +2190,9 @@
reg-names = "tsens_physical";
interrupts = <0 458 0>, <0 445 0>;
interrupt-names = "tsens-upper-lower", "tsens-critical";
- qcom,sensors = <14>;
+ qcom,client-id = <0 1 2 3 4 7 8 9 10 11 12 13>;
+ qcom,sensor-id = <0 1 2 3 4 7 8 9 10 11 12 13>;
+ qcom,sensors = <12>;
};
tsens1: tsens@10ad000 {
@@ -2225,16 +2233,6 @@
qcom,sensor-name = "tsens_tz_sensor4";
qcom,scaling-factor = <10>;
};
- sensor_information5: qcom,sensor-information-5 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor5";
- qcom,scaling-factor = <10>;
- };
- sensor_information6: qcom,sensor-information-6 {
- qcom,sensor-type = "tsens";
- qcom,sensor-name = "tsens_tz_sensor6";
- qcom,scaling-factor = <10>;
- };
sensor_information7: qcom,sensor-information-7 {
qcom,sensor-type = "tsens";
qcom,sensor-name = "tsens_tz_sensor7";
@@ -2621,6 +2619,7 @@
"iface_clk", "noc_axi_clk", "bus_clk", "maxi_clk";
qcom,pas-id = <9>;
+ qcom,msm-bus,name = "pil-venus";
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps =
@@ -2641,6 +2640,7 @@
qcom,pet-time = <10000>;
qcom,ipi-ping;
qcom,wakeup-enable;
+ qcom,scandump-size = <0x40000>;
};
qcom,spss@1d00000 {
@@ -2694,6 +2694,11 @@
reg = <0x10 8>;
};
+ dload_type@18 {
+ compatible = "qcom,msm-imem-dload-type";
+ reg = <0x18 4>;
+ };
+
restart_reason@65c {
compatible = "qcom,msm-imem-restart_reason";
reg = <0x65c 4>;
@@ -2885,6 +2890,57 @@
qcom,icnss-adc_tm = <&pmcobalt_adc_tm>;
};
+ tspp: msm_tspp@0c1e7000 {
+ compatible = "qcom,msm_tspp";
+ reg = <0x0c1e7000 0x200>, /* MSM_TSIF0_PHYS */
+ <0x0c1e8000 0x200>, /* MSM_TSIF1_PHYS */
+ <0x0c1e9000 0x1000>, /* MSM_TSPP_PHYS */
+ <0x0c1c4000 0x23000>; /* MSM_TSPP_BAM_PHYS */
+ reg-names = "MSM_TSIF0_PHYS",
+ "MSM_TSIF1_PHYS",
+ "MSM_TSPP_PHYS",
+ "MSM_TSPP_BAM_PHYS";
+ interrupts = <0 121 0>, /* TSIF_TSPP_IRQ */
+ <0 119 0>, /* TSIF0_IRQ */
+ <0 120 0>, /* TSIF1_IRQ */
+ <0 122 0>; /* TSIF_BAM_IRQ */
+ interrupt-names = "TSIF_TSPP_IRQ",
+ "TSIF0_IRQ",
+ "TSIF1_IRQ",
+ "TSIF_BAM_IRQ";
+
+ clock-names = "iface_clk", "ref_clk";
+ clocks = <&clock_gcc clk_gcc_tsif_ahb_clk>,
+ <&clock_gcc clk_gcc_tsif_ref_clk>;
+
+ qcom,msm-bus,name = "tsif";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <82 512 0 0>, /* No vote */
+ <82 512 12288 24576>;
+ /* Max. bandwidth, 2xTSIF, each max of 96Mbps */
+
+ pinctrl-names = "disabled",
+ "tsif0-mode1", "tsif0-mode2",
+ "tsif1-mode1", "tsif1-mode2",
+ "dual-tsif-mode1", "dual-tsif-mode2";
+
+ pinctrl-0 = <>; /* disabled */
+ pinctrl-1 = <&tsif0_signals_active>; /* tsif0-mode1 */
+ pinctrl-2 = <&tsif0_signals_active
+ &tsif0_sync_active>; /* tsif0-mode2 */
+ pinctrl-3 = <&tsif1_signals_active>; /* tsif1-mode1 */
+ pinctrl-4 = <&tsif1_signals_active
+ &tsif1_sync_active>; /* tsif1-mode2 */
+ pinctrl-5 = <&tsif0_signals_active
+ &tsif1_signals_active>; /* dual-tsif-mode1 */
+ pinctrl-6 = <&tsif0_signals_active
+ &tsif0_sync_active
+ &tsif1_signals_active
+ &tsif1_sync_active>; /* dual-tsif-mode2 */
+ };
+
wil6210: qcom,wil6210 {
compatible = "qcom,wil6210";
qcom,pcie-parent = <&pcie0>;
@@ -2932,16 +2988,10 @@
};
&gdsc_usb30 {
- clock-names = "core_clk";
- clocks = <&clock_gcc clk_gcc_usb30_master_clk>;
status = "ok";
};
&gdsc_pcie_0 {
- clock-names = "master_bus_clk", "slave_bus_clk", "core_clk";
- clocks = <&clock_gcc clk_gcc_pcie_0_mstr_axi_clk>,
- <&clock_gcc clk_gcc_pcie_0_slv_axi_clk>,
- <&clock_gcc clk_gcc_pcie_0_pipe_clk>;
status = "ok";
};
@@ -2999,19 +3049,14 @@
};
&gdsc_mdss {
- clock-names = "bus_clk", "rot_clk";
- clocks = <&clock_mmss clk_mmss_mdss_axi_clk>,
- <&clock_mmss clk_mmss_mdss_rot_clk>;
proxy-supply = <&gdsc_mdss>;
qcom,proxy-consumer-enable;
status = "ok";
};
&gdsc_gpu_gx {
- clock-names = "bimc_core_clk", "core_clk", "core_root_clk";
- clocks = <&clock_gcc clk_gcc_gpu_bimc_gfx_clk>,
- <&clock_gfx clk_gpucc_gfx3d_clk>,
- <&clock_gfx clk_gfx3d_clk_src>;
+ clock-names = "core_root_clk";
+ clocks = <&clock_gfx clk_gfx3d_clk_src>;
qcom,force-enable-root-clk;
parent-supply = <&gfx_vreg>;
status = "ok";
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-bus.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-bus.dtsi
new file mode 100644
index 000000000000..cb5fce378b6c
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmfalcon-bus.dtsi
@@ -0,0 +1,1217 @@
+/* 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 <dt-bindings/msm/msm-bus-ids.h>
+
+&soc {
+ ad_hoc_bus: ad-hoc-bus {
+ /*Version = 14 */
+ compatible = "qcom,msm-bus-device";
+ reg = <0x1620000 0x20000>,
+ <0x1000000 0x80000>,
+ <0x1500000 0x10000>,
+ <0x1700000 0x20000>,
+ <0x17900000 0xE000>,
+ <0x1740000 0x10000>,
+ <0x1740000 0x10000>;
+
+ reg-names = "snoc-base", "bimc-base", "cnoc-base",
+ "a2noc-base", "gnoc-base", "mmnoc-ahb-base", "mnoc-base";
+
+ /*Buses*/
+
+ fab_a2noc: fab-a2noc {
+ cell-id = <MSM_BUS_FAB_A2_NOC>;
+ label = "fab-a2noc";
+ qcom,fab-dev;
+ qcom,base-name = "a2noc-base";
+ qcom,bypass-qos-prg;
+ qcom,bus-type = <1>;
+ qcom,qos-off = <4096>;
+ qcom,base-offset = <16384>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&clock_rpmcc RPM_AGGR2_NOC_CLK>,
+ <&clock_rpmcc RPM_AGGR2_NOC_A_CLK>;
+ };
+
+ fab_bimc: fab-bimc {
+ cell-id = <MSM_BUS_FAB_BIMC>;
+ label = "fab-bimc";
+ qcom,fab-dev;
+ qcom,base-name = "bimc-base";
+ qcom,bus-type = <2>;
+ qcom,bypass-qos-prg;
+ qcom,util-fact = <153>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&clock_rpmcc BIMC_MSMBUS_CLK>,
+ <&clock_rpmcc BIMC_MSMBUS_A_CLK>;
+ };
+
+ fab_cnoc: fab-cnoc {
+ cell-id = <MSM_BUS_FAB_CONFIG_NOC>;
+ label = "fab-cnoc";
+ qcom,fab-dev;
+ qcom,base-name = "cnoc-base";
+ qcom,bypass-qos-prg;
+ qcom,bus-type = <1>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&clock_rpmcc CNOC_MSMBUS_CLK>,
+ <&clock_rpmcc CNOC_MSMBUS_A_CLK>;
+ };
+
+ fab_gnoc: fab-gnoc {
+ cell-id = <MSM_BUS_FAB_GNOC>;
+ label = "fab-gnoc";
+ qcom,virt-dev;
+ qcom,base-name = "gnoc-base";
+ qcom,bypass-qos-prg;
+ };
+
+ fab_mnoc: fab-mnoc {
+ cell-id = <MSM_BUS_FAB_MMSS_NOC>;
+ label = "fab-mnoc";
+ qcom,fab-dev;
+ qcom,base-name = "mnoc-base";
+ qcom,bypass-qos-prg;
+ qcom,bus-type = <1>;
+ qcom,qos-off = <4096>;
+ qcom,base-offset = <20480>;
+ qcom,util-fact = <154>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&clock_rpmcc MMSSNOC_AXI_CLK>,
+ <&clock_rpmcc MMSSNOC_AXI_A_CLK>;
+ };
+
+ fab_snoc: fab-snoc {
+ cell-id = <MSM_BUS_FAB_SYS_NOC>;
+ label = "fab-snoc";
+ qcom,fab-dev;
+ qcom,base-name = "snoc-base";
+ qcom,bypass-qos-prg;
+ qcom,bus-type = <1>;
+ qcom,qos-off = <4096>;
+ qcom,base-offset = <24576>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&clock_rpmcc SNOC_MSMBUS_CLK>,
+ <&clock_rpmcc SNOC_MSMBUS_A_CLK>;
+ };
+
+ fab_mnoc_ahb: fab-mnoc-ahb {
+ cell-id = <MSM_BUS_FAB_MMSS_AHB>;
+ label = "fab-mnoc-ahb";
+ qcom,fab-dev;
+ qcom,base-name = "mmnoc-ahb-base";
+ qcom,bypass-qos-prg;
+ qcom,setrate-only-clk;
+ qcom,bus-type = <1>;
+ clock-names = "bus_clk", "bus_a_clk";
+ clocks = <&clock_mmss AHB_CLK_SRC >,
+ <&clock_mmss AHB_CLK_SRC>;
+ };
+
+ /*Masters*/
+
+ mas_ipa: mas-ipa {
+ cell-id = <MSM_BUS_MASTER_IPA>;
+ label = "mas-ipa";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <3>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_a2noc_snoc>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_IPA>;
+ };
+
+ mas_cnoc_a2noc: mas-cnoc-a2noc {
+ cell-id = <MSM_BUS_MASTER_CNOC_A2NOC>;
+ label = "mas-cnoc-a2noc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&slv_a2noc_snoc>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_CNOC_A2NOC>;
+ qcom,blacklist = <&slv_snoc_cnoc>;
+ };
+
+ mas_sdcc_1: mas-sdcc-1 {
+ cell-id = <MSM_BUS_MASTER_SDCC_1>;
+ label = "mas-sdcc-1";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_a2noc_snoc>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SDCC_1>;
+ };
+
+ mas_sdcc_2: mas-sdcc-2 {
+ cell-id = <MSM_BUS_MASTER_SDCC_2>;
+ label = "mas-sdcc-2";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_a2noc_snoc>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SDCC_2>;
+ };
+
+ mas_blsp_1: mas-blsp-1 {
+ cell-id = <MSM_BUS_MASTER_BLSP_1>;
+ label = "mas-blsp-1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_a2noc_snoc>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_BLSP_1>;
+ };
+
+ mas_blsp_2: mas-blsp-2 {
+ cell-id = <MSM_BUS_MASTER_BLSP_2>;
+ label = "mas-blsp-2";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_a2noc_snoc>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_BLSP_2>;
+ };
+
+ mas_ufs: mas-ufs {
+ cell-id = <MSM_BUS_MASTER_UFS>;
+ label = "mas-ufs";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <4>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_a2noc_snoc>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_UFS>;
+ };
+
+ mas_usb_hs: mas-usb-hs {
+ cell-id = <MSM_BUS_MASTER_USB_HS>;
+ label = "mas-usb-hs";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <1>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_a2noc_snoc>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_USB_HS>;
+ };
+
+ mas_usb3: mas-usb3 {
+ cell-id = <MSM_BUS_MASTER_USB3>;
+ label = "mas-usb3";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <2>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_a2noc_snoc>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_USB3>;
+ };
+
+ mas_crypto_c0: mas-crypto-c0 {
+ cell-id = <MSM_BUS_MASTER_CRYPTO_CORE0>;
+ label = "mas-crypto-c0";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,qport = <11>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_a2noc_snoc>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_CRYPTO_CORE0>;
+ };
+
+ mas_gnoc_bimc: mas-gnoc-bimc {
+ cell-id = <MSM_BUS_MASTER_GNOC_BIMC>;
+ label = "mas-gnoc-bimc";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <2>;
+ qcom,ap-owned;
+ qcom,qport = <0>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_ebi>;
+ qcom,prio-lvl = <0>;
+ qcom,prio-rd = <0>;
+ qcom,prio-wr = <0>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_GNOC_BIMC>;
+ };
+
+ mas_oxili: mas-oxili {
+ cell-id = <MSM_BUS_MASTER_GRAPHICS_3D>;
+ label = "mas-oxili";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <2>;
+ qcom,ap-owned;
+ qcom,qport = <1>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_hmss_l3
+ &slv_ebi &slv_bimc_snoc>;
+ qcom,prio-lvl = <0>;
+ qcom,prio-rd = <0>;
+ qcom,prio-wr = <0>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_GFX3D>;
+ };
+
+ mas_mnoc_bimc: mas-mnoc-bimc {
+ cell-id = <MSM_BUS_MNOC_BIMC_MAS>;
+ label = "mas-mnoc-bimc";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <2>;
+ qcom,ap-owned;
+ qcom,qport = <2>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_hmss_l3
+ &slv_ebi &slv_bimc_snoc>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_MNOC_BIMC>;
+ };
+
+ mas_snoc_bimc: mas-snoc-bimc {
+ cell-id = <MSM_BUS_SNOC_BIMC_MAS>;
+ label = "mas-snoc-bimc";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <2>;
+ qcom,connections = <&slv_hmss_l3 &slv_ebi>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SNOC_BIMC>;
+ };
+
+ mas_snoc_cnoc: mas-snoc-cnoc {
+ cell-id = <MSM_BUS_SNOC_CNOC_MAS>;
+ label = "mas-snoc-cnoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&slv_clk_ctl
+ &slv_qdss_cfg &slv_qm_cfg
+ &slv_srvc_cnoc &slv_ufs_cfg
+ &slv_tcsr &slv_a2noc_smmu_cfg
+ &slv_snoc_cfg &slv_tlmm_south
+ &slv_mpm &slv_cnoc_mnoc_mmss_cfg
+ &slv_sdcc_2 &slv_sdcc_1
+ &slv_spdm &slv_pmic_arb
+ &slv_prng &slv_mss_cfg
+ &slv_gpuss_cfg &slv_imem_cfg
+ &slv_usb3_0 &slv_a2noc_cfg
+ &slv_tlmm_north &slv_usb_hs
+ &slv_pdm &slv_tlmm_center
+ &slv_ahb2phy &slv_blsp_2
+ &slv_blsp_1 &slv_pimem_cfg
+ &slv_glm &slv_message_ram
+ &slv_bimc_cfg &slv_cnoc_mnoc_cfg>;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SNOC_CNOC>;
+ };
+
+ mas_qdss_dap: mas-qdss-dap {
+ cell-id = <MSM_BUS_MASTER_QDSS_DAP>;
+ label = "mas-qdss-dap";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&slv_clk_ctl
+ &slv_qdss_cfg &slv_qm_cfg
+ &slv_srvc_cnoc &slv_ufs_cfg
+ &slv_tcsr &slv_a2noc_smmu_cfg
+ &slv_snoc_cfg &slv_tlmm_south
+ &slv_mpm &slv_cnoc_mnoc_mmss_cfg
+ &slv_sdcc_2 &slv_sdcc_1
+ &slv_spdm &slv_pmic_arb
+ &slv_prng &slv_mss_cfg
+ &slv_gpuss_cfg &slv_imem_cfg
+ &slv_usb3_0 &slv_a2noc_cfg
+ &slv_tlmm_north &slv_usb_hs
+ &slv_pdm &slv_tlmm_center
+ &slv_ahb2phy &slv_blsp_2
+ &slv_blsp_1 &slv_pimem_cfg
+ &slv_glm &slv_message_ram
+ &slv_cnoc_a2noc &slv_bimc_cfg
+ &slv_cnoc_mnoc_cfg>;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_QDSS_DAP>;
+ };
+
+ mas_apps_proc: mas-apps-proc {
+ cell-id = <MSM_BUS_MASTER_AMPSS_M0>;
+ label = "mas-apps-proc";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&slv_gnoc_snoc &slv_gnoc_bimc>;
+ qcom,bus-dev = <&fab_gnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_APPSS_PROC>;
+ };
+
+ mas_cnoc_mnoc_mmss_cfg: mas-cnoc-mnoc-mmss-cfg {
+ cell-id = <MSM_BUS_MASTER_CNOC_MNOC_MMSS_CFG>;
+ label = "mas-cnoc-mnoc-mmss-cfg";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&slv_venus_throttle_cfg
+ &slv_venus_cfg &slv_camera_throttle_cfg
+ &slv_smmu_cfg &slv_camera_cfg &slv_csi_phy_cfg
+ &slv_display_throttle_cfg &slv_display_cfg
+ &slv_mmss_clk_cfg &slv_mnoc_mpu_cfg
+ &slv_misc_cfg &slv_mmss_clk_xpu_cfg>;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,mas-rpm-id = <ICBID_MASTER_CNOC_MNOC_MMSS_CFG>;
+ };
+
+ mas_cnoc_mnoc_cfg: mas-cnoc-mnoc-cfg {
+ cell-id = <MSM_BUS_MASTER_CNOC_MNOC_CFG>;
+ label = "mas-cnoc-mnoc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,connections = <&slv_srvc_mnoc>;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,mas-rpm-id = <ICBID_MASTER_CNOC_MNOC_CFG>;
+ };
+
+ mas_cpp: mas-cpp {
+ cell-id = <MSM_BUS_MASTER_CPP>;
+ label = "mas-cpp";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <4>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_mnoc_bimc>;
+ qcom,bus-dev = <&fab_mnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_CPP>;
+ };
+
+ mas_jpeg: mas-jpeg {
+ cell-id = <MSM_BUS_MASTER_JPEG>;
+ label = "mas-jpeg";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <6>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_mnoc_bimc>;
+ qcom,bus-dev = <&fab_mnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_JPEG>;
+ };
+
+ mas_mdp_p0: mas-mdp-p0 {
+ cell-id = <MSM_BUS_MASTER_MDP_PORT0>;
+ label = "mas-mdp-p0";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <0>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_mnoc_bimc>;
+ qcom,bus-dev = <&fab_mnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_MDP0>;
+ };
+
+ mas_mdp_p1: mas-mdp-p1 {
+ cell-id = <MSM_BUS_MASTER_MDP_PORT1>;
+ label = "mas-mdp-p1";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <1>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_mnoc_bimc>;
+ qcom,bus-dev = <&fab_mnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_MDP1>;
+ };
+
+ mas_venus: mas-venus {
+ cell-id = <MSM_BUS_MASTER_VIDEO_P0>;
+ label = "mas-venus";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <2>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_mnoc_bimc>;
+ qcom,bus-dev = <&fab_mnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_VIDEO>;
+ };
+
+ mas_vfe: mas-vfe {
+ cell-id = <MSM_BUS_MASTER_VFE>;
+ label = "mas-vfe";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <5>;
+ qcom,qos-mode = "bypass";
+ qcom,connections = <&slv_mnoc_bimc>;
+ qcom,bus-dev = <&fab_mnoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_VFE>;
+ };
+
+ mas_qdss_etr: mas-qdss-etr {
+ cell-id = <MSM_BUS_MASTER_QDSS_ETR>;
+ label = "mas-qdss-etr";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <1>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_pimem
+ &slv_imem &slv_snoc_cnoc
+ &slv_snoc_bimc>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_QDSS_ETR>;
+ };
+
+ mas_qdss_bam: mas-qdss-bam {
+ cell-id = <MSM_BUS_MASTER_QDSS_BAM>;
+ label = "mas-qdss-bam";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,qport = <0>;
+ qcom,qos-mode = "fixed";
+ qcom,connections = <&slv_pimem
+ &slv_imem &slv_snoc_cnoc
+ &slv_snoc_bimc>;
+ qcom,prio1 = <1>;
+ qcom,prio0 = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_QDSS_BAM>;
+ };
+
+ mas_snoc_cfg: mas-snoc-cfg {
+ cell-id = <MSM_BUS_MASTER_SNOC_CFG>;
+ label = "mas-snoc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_srvc_snoc>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_SNOC_CFG>;
+ };
+
+ mas_bimc_snoc: mas-bimc-snoc {
+ cell-id = <MSM_BUS_BIMC_SNOC_MAS>;
+ label = "mas-bimc-snoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_pimem
+ &slv_ipa &slv_qdss_stm
+ &slv_lpass &slv_hmss
+ &slv_cdsp &slv_snoc_cnoc
+ &slv_wlan &slv_imem>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_BIMC_SNOC>;
+ };
+
+ mas_a2noc_snoc: mas-a2noc-snoc {
+ cell-id = <MSM_BUS_A2NOC_SNOC_MAS>;
+ label = "mas-a2noc-snoc";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_pimem
+ &slv_ipa &slv_qdss_stm
+ &slv_lpass &slv_hmss
+ &slv_snoc_bimc &slv_cdsp
+ &slv_snoc_cnoc &slv_wlan
+ &slv_imem>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_A2NOC_SNOC>;
+ };
+ /*Internal nodes*/
+
+ /*Slaves*/
+
+ slv_a2noc_snoc:slv-a2noc-snoc {
+ cell-id = <MSM_BUS_A2NOC_SNOC_SLV>;
+ label = "slv-a2noc-snoc";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_a2noc>;
+ qcom,connections = <&mas_a2noc_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_A2NOC_SNOC>;
+ };
+
+ slv_ebi:slv-ebi {
+ cell-id = <MSM_BUS_SLAVE_EBI_CH0>;
+ label = "slv-ebi";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <2>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_EBI1>;
+ };
+
+ slv_hmss_l3:slv-hmss-l3 {
+ cell-id = <MSM_BUS_SLAVE_HMSS_L3>;
+ label = "slv-hmss-l3";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_HMSS_L3>;
+ };
+
+ slv_bimc_snoc:slv-bimc-snoc {
+ cell-id = <MSM_BUS_BIMC_SNOC_SLV>;
+ label = "slv-bimc-snoc";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_bimc>;
+ qcom,connections = <&mas_bimc_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_BIMC_SNOC>;
+ };
+
+ slv_cnoc_a2noc:slv-cnoc-a2noc {
+ cell-id = <MSM_BUS_SLAVE_CNOC_A2NOC>;
+ label = "slv-cnoc-a2noc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,connections = <&mas_cnoc_a2noc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CNOC_A2NOC>;
+ };
+
+ slv_mpm:slv-mpm {
+ cell-id = <MSM_BUS_SLAVE_MPM>;
+ label = "slv-mpm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_MPM>;
+ };
+
+ slv_pmic_arb:slv-pmic-arb {
+ cell-id = <MSM_BUS_SLAVE_PMIC_ARB>;
+ label = "slv-pmic-arb";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PMIC_ARB>;
+ };
+
+ slv_tlmm_north:slv-tlmm-north {
+ cell-id = <MSM_BUS_SLAVE_TLMM_NORTH>;
+ label = "slv-tlmm-north";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_TLMM_NORTH>;
+ };
+
+ slv_tcsr:slv-tcsr {
+ cell-id = <MSM_BUS_SLAVE_TCSR>;
+ label = "slv-tcsr";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_TCSR>;
+ };
+
+ slv_pimem_cfg:slv-pimem-cfg {
+ cell-id = <MSM_BUS_SLAVE_PIMEM_CFG>;
+ label = "slv-pimem-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PIMEM_CFG>;
+ };
+
+ slv_imem_cfg:slv-imem-cfg {
+ cell-id = <MSM_BUS_SLAVE_IMEM_CFG>;
+ label = "slv-imem-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_IMEM_CFG>;
+ };
+
+ slv_message_ram:slv-message-ram {
+ cell-id = <MSM_BUS_SLAVE_MESSAGE_RAM>;
+ label = "slv-message-ram";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_MESSAGE_RAM>;
+ };
+
+ slv_glm:slv-glm {
+ cell-id = <MSM_BUS_SLAVE_GLM>;
+ label = "slv-glm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_GLM>;
+ };
+
+ slv_bimc_cfg:slv-bimc-cfg {
+ cell-id = <MSM_BUS_SLAVE_BIMC_CFG>;
+ label = "slv-bimc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_BIMC_CFG>;
+ };
+
+ slv_prng:slv-prng {
+ cell-id = <MSM_BUS_SLAVE_PRNG>;
+ label = "slv-prng";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PRNG>;
+ };
+
+ slv_spdm:slv-spdm {
+ cell-id = <MSM_BUS_SLAVE_SPDM_WRAPPER>;
+ label = "slv-spdm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SPDM_WRAPPER>;
+ };
+
+ slv_qdss_cfg:slv-qdss-cfg {
+ cell-id = <MSM_BUS_SLAVE_QDSS_CFG>;
+ label = "slv-qdss-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_QDSS_CFG>;
+ };
+
+ slv_cnoc_mnoc_cfg:slv-cnoc-mnoc-cfg {
+ cell-id = <MSM_BUS_SLAVE_CNOC_MNOC_CFG>;
+ label = "slv-cnoc-mnoc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,connections = <&mas_cnoc_mnoc_cfg>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CNOC_MNOC_CFG>;
+ };
+
+ slv_snoc_cfg:slv-snoc-cfg {
+ cell-id = <MSM_BUS_SLAVE_SNOC_CFG>;
+ label = "slv-snoc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_CFG>;
+ };
+
+ slv_qm_cfg:slv-qm-cfg {
+ cell-id = <MSM_BUS_SLAVE_QM_CFG>;
+ label = "slv-qm-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_QM_CFG>;
+ };
+
+ slv_clk_ctl:slv-clk-ctl {
+ cell-id = <MSM_BUS_SLAVE_CLK_CTL>;
+ label = "slv-clk-ctl";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CLK_CTL>;
+ };
+
+ slv_mss_cfg:slv-mss-cfg {
+ cell-id = <MSM_BUS_SLAVE_CNOC_MSS>;
+ label = "slv-mss-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CNOC_MSS>;
+ };
+
+ slv_tlmm_south:slv-tlmm-south {
+ cell-id = <MSM_BUS_SLAVE_TLMM_SOUTH>;
+ label = "slv-tlmm-south";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_TLMM_SOUTH>;
+ };
+
+ slv_ufs_cfg:slv-ufs-cfg {
+ cell-id = <MSM_BUS_SLAVE_UFS_CFG>;
+ label = "slv-ufs-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_UFS_CFG>;
+ };
+
+ slv_a2noc_cfg:slv-a2noc-cfg {
+ cell-id = <MSM_BUS_SLAVE_A2NOC_CFG>;
+ label = "slv-a2noc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_A2NOC_CFG>;
+ };
+
+ slv_a2noc_smmu_cfg:slv-a2noc-smmu-cfg {
+ cell-id = <MSM_BUS_SLAVE_A2NOC_SMMU_CFG>;
+ label = "slv-a2noc-smmu-cfg";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_A2NOC_SMMU_CFG>;
+ };
+
+ slv_gpuss_cfg:slv-gpuss-cfg {
+ cell-id = <MSM_BUS_SLAVE_GRAPHICS_3D_CFG>;
+ label = "slv-gpuss-cfg";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_GFX3D_CFG>;
+ };
+
+ slv_ahb2phy:slv-ahb2phy {
+ cell-id = <MSM_BUS_SLAVE_PCIE20_AHB2PHY>;
+ label = "slv-ahb2phy";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PCIE20_AHB2PHY>;
+ };
+
+ slv_blsp_1:slv-blsp-1 {
+ cell-id = <MSM_BUS_SLAVE_BLSP_1>;
+ label = "slv-blsp-1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_BLSP_1>;
+ };
+
+ slv_sdcc_1:slv-sdcc-1 {
+ cell-id = <MSM_BUS_SLAVE_SDCC_1>;
+ label = "slv-sdcc-1";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SDCC_1>;
+ };
+
+ slv_sdcc_2:slv-sdcc-2 {
+ cell-id = <MSM_BUS_SLAVE_SDCC_2>;
+ label = "slv-sdcc-2";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SDCC_2>;
+ };
+
+ slv_tlmm_center:slv-tlmm-center {
+ cell-id = <MSM_BUS_SLAVE_TLMM_CENTER>;
+ label = "slv-tlmm-center";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_TLMM_CENTER>;
+ };
+
+ slv_blsp_2:slv-blsp-2 {
+ cell-id = <MSM_BUS_SLAVE_BLSP_2>;
+ label = "slv-blsp-2";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_BLSP_2>;
+ };
+
+ slv_pdm:slv-pdm {
+ cell-id = <MSM_BUS_SLAVE_PDM>;
+ label = "slv-pdm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PDM>;
+ };
+
+ slv_cnoc_mnoc_mmss_cfg:slv-cnoc-mnoc-mmss-cfg {
+ cell-id = <MSM_BUS_SLAVE_CNOC_MNOC_MMSS_CFG>;
+ label = "slv-cnoc-mnoc-mmss-cfg";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,connections = <&mas_cnoc_mnoc_mmss_cfg>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CNOC_MNOC_MMSS_CFG>;
+ };
+
+ slv_usb_hs:slv-usb-hs {
+ cell-id = <MSM_BUS_SLAVE_USB_HS>;
+ label = "slv-usb-hs";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_USB_HS>;
+ };
+
+ slv_usb3_0:slv-usb3-0 {
+ cell-id = <MSM_BUS_SLAVE_USB3>;
+ label = "slv-usb3-0";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_USB3_0>;
+ };
+
+ slv_srvc_cnoc:slv-srvc-cnoc {
+ cell-id = <MSM_BUS_SLAVE_SERVICE_CNOC>;
+ label = "slv-srvc-cnoc";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SERVICE_CNOC>;
+ };
+
+
+ slv_gnoc_bimc:slv-gnoc-bimc {
+ cell-id = <MSM_BUS_SLAVE_GNOC_BIMC>;
+ label = "slv-gnoc-bimc";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_gnoc>;
+ qcom,connections = <&mas_gnoc_bimc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_GNOC_BIMC>;
+ };
+
+ slv_gnoc_snoc:slv-gnoc-snoc {
+ cell-id = <MSM_BUS_SLAVE_GNOC_SNOC>;
+ label = "slv-gnoc-snoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_gnoc>;
+ qcom,connections = <&mas_gnoc_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_GNOC_SNOC>;
+ };
+
+ mas_gnoc_snoc: mas-gnoc-snoc {
+ cell-id = <MSM_BUS_MASTER_GNOC_SNOC>;
+ label = "mas-gnoc-snoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,connections = <&slv_pimem
+ &slv_ipa &slv_qdss_stm
+ &slv_lpass &slv_hmss
+ &slv_cdsp &slv_snoc_cnoc
+ &slv_wlan &slv_imem>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,mas-rpm-id = <ICBID_MASTER_GNOC_SNOC>;
+ };
+
+ slv_camera_cfg:slv-camera-cfg {
+ cell-id = <MSM_BUS_SLAVE_CAMERA_CFG>;
+ label = "slv-camera-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CAMERA_CFG>;
+ };
+
+ slv_camera_throttle_cfg:slv-camera-throttle-cfg {
+ cell-id = <MSM_BUS_SLAVE_CAMERA_THROTTLE_CFG>;
+ label = "slv-camera-throttle-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CAMERA_THROTTLE_CFG>;
+ };
+
+ slv_misc_cfg:slv-misc-cfg {
+ cell-id = <MSM_BUS_SLAVE_MISC_CFG>;
+ label = "slv-misc-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_MISC_CFG>;
+ };
+
+ slv_venus_throttle_cfg:slv-venus-throttle-cfg {
+ cell-id = <MSM_BUS_SLAVE_VENUS_THROTTLE_CFG>;
+ label = "slv-venus-throttle-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_VENUS_THROTTLE_CFG>;
+ };
+
+ slv_venus_cfg:slv-venus-cfg {
+ cell-id = <MSM_BUS_SLAVE_VENUS_CFG>;
+ label = "slv-venus-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_VENUS_CFG>;
+ };
+
+ slv_mmss_clk_xpu_cfg:slv-mmss-clk-xpu-cfg {
+ cell-id = <MSM_BUS_SLAVE_MMSS_CLK_XPU_CFG>;
+ label = "slv-mmss-clk-xpu-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_MMSS_CLK_XPU_CFG>;
+ };
+
+ slv_mmss_clk_cfg:slv-mmss-clk-cfg {
+ cell-id = <MSM_BUS_SLAVE_MMSS_CLK_CFG>;
+ label = "slv-mmss-clk-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_MMSS_CLK_CFG>;
+ };
+
+ slv_mnoc_mpu_cfg:slv-mnoc-mpu-cfg {
+ cell-id = <MSM_BUS_SLAVE_MNOC_MPU_CFG>;
+ label = "slv-mnoc-mpu-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_MNOC_MPU_CFG>;
+ };
+
+ slv_display_cfg:slv-display-cfg {
+ cell-id = <MSM_BUS_SLAVE_DISPLAY_CFG>;
+ label = "slv-display-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_DISPLAY_CFG>;
+ };
+
+ slv_csi_phy_cfg:slv-csi-phy-cfg {
+ cell-id = <MSM_BUS_SLAVE_CSI_PHY_CFG>;
+ label = "slv-csi-phy-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CSI_PHY_CFG>;
+ };
+
+ slv_display_throttle_cfg:slv-display-throttle-cfg {
+ cell-id = <MSM_BUS_SLAVE_DISPLAY_THROTTLE_CFG>;
+ label = "slv-display-throttle-cfg";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_DISPLAY_THROTTLE_CFG>;
+ };
+
+ slv_smmu_cfg:slv-smmu-cfg {
+ cell-id = <MSM_BUS_SLAVE_MMSS_SMMU_CFG>;
+ label = "slv-smmu-cfg";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_MMSS_SMMU_CFG>;
+ };
+
+ slv_mnoc_bimc:slv-mnoc-bimc {
+ cell-id = <MSM_BUS_MNOC_BIMC_SLV>;
+ label = "slv-mnoc-bimc";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <2>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc>;
+ qcom,connections = <&mas_mnoc_bimc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_MNOC_BIMC>;
+ };
+
+ slv_srvc_mnoc:slv-srvc-mnoc {
+ cell-id = <MSM_BUS_SLAVE_SERVICE_MNOC>;
+ label = "slv-srvc-mnoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_mnoc_ahb>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SERVICE_MNOC>;
+ };
+
+ slv_hmss:slv-hmss {
+ cell-id = <MSM_BUS_SLAVE_APPSS>;
+ label = "slv-hmss";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_APPSS>;
+ };
+
+ slv_lpass:slv-lpass {
+ cell-id = <MSM_BUS_SLAVE_LPASS>;
+ label = "slv-lpass";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_LPASS>;
+ };
+
+ slv_wlan:slv-wlan {
+ cell-id = <MSM_BUS_SLAVE_WLAN>;
+ label = "slv-wlan";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_WLAN>;
+ };
+
+ slv_cdsp:slv-cdsp {
+ cell-id = <MSM_BUS_SLAVE_CDSP>;
+ label = "slv-cdsp";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_CDSP>;
+ };
+
+ slv_ipa:slv-ipa {
+ cell-id = <MSM_BUS_SLAVE_IPA_CFG>;
+ label = "slv-ipa";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,ap-owned;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_IPA_CFG>;
+ };
+
+ slv_snoc_bimc:slv-snoc-bimc {
+ cell-id = <MSM_BUS_SNOC_BIMC_SLV>;
+ label = "slv-snoc-bimc";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,connections = <&mas_snoc_bimc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_BIMC>;
+ };
+
+ slv_snoc_cnoc:slv-snoc-cnoc {
+ cell-id = <MSM_BUS_SNOC_CNOC_SLV>;
+ label = "slv-snoc-cnoc";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,connections = <&mas_snoc_cnoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SNOC_CNOC>;
+ };
+
+ slv_imem:slv-imem {
+ cell-id = <MSM_BUS_SLAVE_OCIMEM>;
+ label = "slv-imem";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_IMEM>;
+ };
+
+ slv_pimem:slv-pimem {
+ cell-id = <MSM_BUS_SLAVE_PIMEM>;
+ label = "slv-pimem";
+ qcom,buswidth = <8>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_PIMEM>;
+ };
+
+ slv_qdss_stm:slv-qdss-stm {
+ cell-id = <MSM_BUS_SLAVE_QDSS_STM>;
+ label = "slv-qdss-stm";
+ qcom,buswidth = <4>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_QDSS_STM>;
+ };
+
+ slv_srvc_snoc:slv-srvc-snoc {
+ cell-id = <MSM_BUS_SLAVE_SERVICE_SNOC>;
+ label = "slv-srvc-snoc";
+ qcom,buswidth = <16>;
+ qcom,agg-ports = <1>;
+ qcom,bus-dev = <&fab_snoc>;
+ qcom,slv-rpm-id = <ICBID_SLAVE_SERVICE_SNOC>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi
index 352856965373..3826b00bf09e 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi
@@ -26,10 +26,12 @@
arm,buffer-size = <0x400000>;
arm,sg-enable;
+ coresight-ctis = <&cti0 &cti8>;
+
coresight-name = "coresight-tmc-etr";
- clocks = <&clock_gcc RPM_QDSS_CLK>,
- <&clock_gcc RPM_QDSS_A_CLK>;
+ clocks = <&clock_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc RPM_QDSS_A_CLK>;
clock-names = "apb_pclk", "core_a_clk";
port{
@@ -76,8 +78,10 @@
coresight-name = "coresight-tmc-etf";
- clocks = <&clock_gcc RPM_QDSS_CLK>,
- <&clock_gcc RPM_QDSS_A_CLK>;
+ coresight-ctis = <&cti0 &cti8>;
+
+ clocks = <&clock_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc RPM_QDSS_A_CLK>;
clock-names = "apb_pclk", "core_a_clk";
ports{
@@ -111,8 +115,8 @@
coresight-name = "coresight-funnel-merg";
- clocks = <&clock_gcc RPM_QDSS_CLK>,
- <&clock_gcc RPM_QDSS_A_CLK>;
+ clocks = <&clock_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc RPM_QDSS_A_CLK>;
clock-names = "apb_pclk", "core_a_clk";
ports {
@@ -146,8 +150,8 @@
coresight-name = "coresight-funnel-in0";
- clocks = <&clock_gcc RPM_QDSS_CLK>,
- <&clock_gcc RPM_QDSS_A_CLK>;
+ clocks = <&clock_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc RPM_QDSS_A_CLK>;
clock-names = "apb_pclk", "core_a_clk";
ports {
@@ -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 {
@@ -181,8 +193,8 @@
coresight-name = "coresight-stm";
- clocks = <&clock_gcc RPM_QDSS_CLK>,
- <&clock_gcc RPM_QDSS_A_CLK>;
+ clocks = <&clock_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc RPM_QDSS_A_CLK>;
clock-names = "apb_pclk", "core_a_clk";
port{
@@ -191,4 +203,294 @@
};
};
};
+
+ cti0: cti@6010000 {
+ compatible = "arm,coresight-cti";
+ reg = <0x6010000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti0";
+
+ clocks = <&clock_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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_rpmcc RPM_QDSS_CLK>,
+ <&clock_rpmcc 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-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi
index 6a000e4d4fd0..d28d09c2a527 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon-pinctrl.dtsi
@@ -32,5 +32,21 @@
bias-disable;
};
};
+
+ led_enable: led_enable {
+ mux {
+ pins = "gpio40";
+ drive_strength = <16>;
+ output-high;
+ };
+ };
+
+ led_disable: led_disable {
+ mux {
+ pins = "gpio40";
+ drive_strength = <2>;
+ output-low;
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-regulator.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-regulator.dtsi
index 2c09774c1391..124ab341ba6b 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon-regulator.dtsi
@@ -13,341 +13,348 @@
/* Stub regulators */
/ {
- /* PM660A S1 - VDD_APC0 supply */
- pm660_s1a: regulator-pm660-s1a {
+ /* PMFALCON S1 - VDD_APC0 supply */
+ pmfalcon_s1a: regulator-pmfalcon-s1a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s1a";
+ regulator-name = "pmfalcon_s1a";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <565000>;
regulator-max-microvolt = <1170000>;
};
- /* PM660A S2 + S3 = VDD_APC1 supply */
- pm660_s2a: regulator-pm660-s2a {
+ /* PMFALCON S2 + S3 = VDD_APC1 supply */
+ pmfalcon_s2a: regulator-pmfalcon-s2a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s2a";
+ regulator-name = "pmfalcon_s2a";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <565000>;
regulator-max-microvolt = <1170000>;
};
- pm660_s4a: regulator-pm660-s4a {
+ pmfalcon_s4a: regulator-pmfalcon-s4a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s4a";
+ regulator-name = "pmfalcon_s4a";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <1805000>;
regulator-max-microvolt = <2040000>;
};
- pm660_s5a: regulator-pm660-s5a {
+ pmfalcon_s5a: regulator-pmfalcon-s5a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s5a";
+ regulator-name = "pmfalcon_s5a";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
};
- pm660_s6a: regulator-pm660-s6a {
+ pmfalcon_s6a: regulator-pmfalcon-s6a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s6a";
+ regulator-name = "pmfalcon_s6a";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <504000>;
regulator-max-microvolt = <992000>;
};
- pm660_s1b: regulator-pm660-s1b {
+ pmfalcon_s1b: regulator-pmfalcon-s1b {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s1b";
+ regulator-name = "pmfalcon_s1b";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <1125000>;
regulator-max-microvolt = <1125000>;
};
- pm660_s2b: regulator-pm660-s2b {
+ pmfalcon_s2b: regulator-pmfalcon-s2b {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s2b";
+ regulator-name = "pmfalcon_s2b";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <1050000>;
regulator-max-microvolt = <1050000>;
};
- /* PM660B S3 + S4 - VDD_CX supply */
- pm660_s3b_level: regulator-pm660-s3b-level {
+ /* PMFALCON S3 + S4 - VDD_CX supply */
+ pmfalcon_s3b_level: regulator-pmfalcon-s3b-level {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s3b_level";
+ regulator-name = "pmfalcon_s3b_level";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
};
- pm660_s3b_floor_level: regulator-pm660-s3b-floor-level {
+ pmfalcon_s3b_floor_level: regulator-pmfalcon-s3b-floor-level {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s3b_floor_level";
+ regulator-name = "pmfalcon_s3b_floor_level";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
};
- pm660_s3b_level_ao: regulator-pm660-s3b-level-ao {
+ pmfalcon_s3b_level_ao: regulator-pmfalcon-s3b-level-ao {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s3b_level_ao";
+ regulator-name = "pmfalcon_s3b_level_ao";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
};
- /* PM660B S5 - VDD_MX supply */
- pm660_s5b_level: regulator-pm660-s5b-level {
+ /* PMFALCON S5 - VDD_MX supply */
+ pmfalcon_s5b_level: regulator-pmfalcon-s5b-level {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s5b_level";
+ regulator-name = "pmfalcon_s5b_level";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
};
- pm660_s5b_floor_level: regulator-pm660-s5b-floor-level {
+ pmfalcon_s5b_floor_level: regulator-pmfalcon-s5b-floor-level {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s5b_floor_level";
+ regulator-name = "pmfalcon_s5b_floor_level";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
};
- pm660_s5b_level_ao: regulator-pm660-s5b-level-ao {
+ pmfalcon_s5b_level_ao: regulator-pmfalcon-s5b-level-ao {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_s5b_level_ao";
+ regulator-name = "pmfalcon_s5b_level_ao";
qcom,hpm-min-load = <100000>;
regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
};
- pm660_l1a: regulator-pm660-l1a {
+ pmfalcon_l1a: regulator-pmfalcon-l1a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l1a";
+ regulator-name = "pmfalcon_l1a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1150000>;
regulator-max-microvolt = <1250000>;
};
- pm660_l2a: regulator-pm660-l2a {
+ pmfalcon_l2a: regulator-pmfalcon-l2a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l2a";
+ regulator-name = "pmfalcon_l2a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <950000>;
regulator-max-microvolt = <1010000>;
};
- pm660_l3a: regulator-pm660-l3a {
+ pmfalcon_l3a: regulator-pmfalcon-l3a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l3a";
+ regulator-name = "pmfalcon_l3a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <950000>;
regulator-max-microvolt = <1010000>;
};
/* TODO: remove if ADRASTEA CX/MX not voted from APPS */
- pm660_l5a: regulator-pm660-l5a {
+ pmfalcon_l5a: regulator-pmfalcon-l5a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l5a";
+ regulator-name = "pmfalcon_l5a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <525000>;
regulator-max-microvolt = <950000>;
};
- pm660_l6a: regulator-pm660-l6a {
+ pmfalcon_l6a: regulator-pmfalcon-l6a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l6a";
+ regulator-name = "pmfalcon_l6a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1370000>;
};
- pm660_l7a: regulator-pm660-l7a {
+ pmfalcon_l7a: regulator-pmfalcon-l7a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l7a";
+ regulator-name = "pmfalcon_l7a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
};
- pm660_l8a: regulator-pm660-l8a {
+ pmfalcon_l8a: regulator-pmfalcon-l8a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l8a";
+ regulator-name = "pmfalcon_l8a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1750000>;
regulator-max-microvolt = <1900000>;
};
- pm660_l9a: regulator-pm660-l9a {
+ pmfalcon_l9a: regulator-pmfalcon-l9a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l9a";
+ regulator-name = "pmfalcon_l9a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1750000>;
regulator-max-microvolt = <1900000>;
};
- pm660_l10a: regulator-pm660-l10a {
+ pmfalcon_l10a: regulator-pmfalcon-l10a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l10a";
+ regulator-name = "pmfalcon_l10a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1780000>;
regulator-max-microvolt = <1950000>;
};
- pm660_l11a: regulator-pm660-l11a {
+ pmfalcon_l11a: regulator-pmfalcon-l11a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l11a";
+ regulator-name = "pmfalcon_l11a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1780000>;
regulator-max-microvolt = <1950000>;
};
- pm660_l12a: regulator-pm660-l12a {
+ pmfalcon_l12a: regulator-pmfalcon-l12a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l12a";
+ regulator-name = "pmfalcon_l12a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1780000>;
regulator-max-microvolt = <1950000>;
};
- pm660_l13a: regulator-pm660-l13a {
+ pmfalcon_l13a: regulator-pmfalcon-l13a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l13a";
+ regulator-name = "pmfalcon_l13a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1750000>;
regulator-max-microvolt = <1950000>;
};
- pm660_l14a: regulator-pm660-l14a {
+ pmfalcon_l14a: regulator-pmfalcon-l14a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l14a";
+ regulator-name = "pmfalcon_l14a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1710000>;
regulator-max-microvolt = <1900000>;
};
- pm660_l15a: regulator-pm660-l15a {
+ pmfalcon_l15a: regulator-pmfalcon-l15a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l15a";
+ regulator-name = "pmfalcon_l15a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1650000>;
regulator-max-microvolt = <2950000>;
};
- pm660_l17a: regulator-pm660-l17a {
+ pmfalcon_l17a: regulator-pmfalcon-l17a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l17a";
+ regulator-name = "pmfalcon_l17a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1650000>;
regulator-max-microvolt = <2950000>;
};
- pm660_l19a: regulator-pm660-l19a {
+ pmfalcon_l19a: regulator-pmfalcon-l19a {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l19a";
+ regulator-name = "pmfalcon_l19a";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <3200000>;
regulator-max-microvolt = <3400000>;
};
- pm660_l1b: regulator-pm660-l1b {
+ pmfalcon_l1b: regulator-pmfalcon-l1b {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l1b";
+ regulator-name = "pmfalcon_l1b";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <925000>;
};
- pm660_l2b: regulator-pm660-l2b {
+ pmfalcon_l2b: regulator-pmfalcon-l2b {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l2b";
+ regulator-name = "pmfalcon_l2b";
qcom,hpm-min-load = <5000>;
regulator-min-microvolt = <350000>;
regulator-max-microvolt = <3100000>;
};
- pm660_l3b: regulator-pm660-l3b {
+ pmfalcon_l3b: regulator-pmfalcon-l3b {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l3b";
+ regulator-name = "pmfalcon_l3b";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1710000>;
regulator-max-microvolt = <3600000>;
};
- pm660_l4b: regulator-pm660-l4b {
+ pmfalcon_l4b: regulator-pmfalcon-l4b {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l4b";
+ regulator-name = "pmfalcon_l4b";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1700000>;
regulator-max-microvolt = <2950000>;
};
- pm660_l5b: regulator-pm660-l5b {
+ pmfalcon_l5b: regulator-pmfalcon-l5b {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l5b";
+ regulator-name = "pmfalcon_l5b";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <1721000>;
regulator-max-microvolt = <3600000>;
};
- pm660_l6b: regulator-pm660-l6b {
+ pmfalcon_l6b: regulator-pmfalcon-l6b {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l6b";
+ regulator-name = "pmfalcon_l6b";
qcom,hpm-min-load = <5000>;
regulator-min-microvolt = <1700000>;
regulator-max-microvolt = <3300000>;
};
- pm660_l7b: regulator-pm660-l7b {
+ pmfalcon_l7b: regulator-pmfalcon-l7b {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l7b";
+ regulator-name = "pmfalcon_l7b";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <2700000>;
regulator-max-microvolt = <3125000>;
};
- pm660_l8b: regulator-pm660-l8b {
+ pmfalcon_l8b: regulator-pmfalcon-l8b {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l8b";
+ regulator-name = "pmfalcon_l8b";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <3200000>;
regulator-max-microvolt = <3400000>;
};
- /* PM660B L9 = VDD_SSC_CX supply */
- pm660_l9b_level: regulator-pm660-l9b-level {
+ /* PMFALCON L9 = VDD_SSC_CX supply */
+ pmfalcon_l9b_level: regulator-pmfalcon-l9b-level {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l9b_level";
+ regulator-name = "pmfalcon_l9b_level";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
};
- pm660_l9b_floor_level: regulator-pm660-l9b-floor-level {
+ pmfalcon_l9b_floor_level: regulator-pmfalcon-l9b-floor-level {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l9b_floor_level";
+ regulator-name = "pmfalcon_l9b_floor_level";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
};
- /* PM660B L10 = VDD_SSC_MX supply */
- pm660_l10b_level: regulator-pm660-l10b-level {
+ /* PMFALCON L10 = VDD_SSC_MX supply */
+ pmfalcon_l10b_level: regulator-pmfalcon-l10b-level {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l10b_level";
+ regulator-name = "pmfalcon_l10b_level";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
};
- pm660_l10b_floor_level: regulator-pm660-l10b-floor-level {
+ pmfalcon_l10b_floor_level: regulator-pmfalcon-l10b-floor-level {
compatible = "qcom,stub-regulator";
- regulator-name = "pm660_l10b_floor_level";
+ regulator-name = "pmfalcon_l10b_floor_level";
qcom,hpm-min-load = <10000>;
regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
};
+ pmfalcon_bob: regulator-pmfalcon-bob {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pmfalcon_bob";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
/* GFX Supply */
gfx_vreg_corner: regulator-gfx-corner {
compatible = "qcom,stub-regulator";
diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
index 246a6cf5371e..df04d1a57683 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
@@ -14,6 +14,7 @@
#include <dt-bindings/clock/qcom,gcc-msmfalcon.h>
#include <dt-bindings/clock/qcom,gpu-msmfalcon.h>
#include <dt-bindings/clock/qcom,mmcc-msmfalcon.h>
+#include <dt-bindings/clock/qcom,rpmcc.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
@@ -135,6 +136,22 @@
};
};
+ clocks {
+ xo_board {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <19200000>;
+ clock-output-names = "xo_board";
+ };
+
+ sleep_clk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <32764>;
+ clock-output-names = "sleep_clk";
+ };
+ };
+
soc: soc { };
reserved-memory {
@@ -151,19 +168,19 @@
modem_fw_mem: modem_fw_region@8ac00000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0x0 0x8ac00000 0x0 0x7800000>;
+ reg = <0x0 0x8ac00000 0x0 0x7e00000>;
};
- adsp_fw_mem: adsp_fw_region@92400000 {
+ adsp_fw_mem: adsp_fw_region@92a00000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0x0 0x92400000 0x0 0x1e00000>;
+ reg = <0x0 0x92a00000 0x0 0x1e00000>;
};
- cdsp_fw_mem: cdsp_fw_region@94200000 {
+ cdsp_fw_mem: cdsp_fw_region@94800000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0x0 0x94200000 0x0 0x600000>;
+ reg = <0x0 0x94800000 0x0 0x600000>;
};
venus_fw_mem: venus_fw_region {
@@ -231,6 +248,39 @@
clock-frequency = <19200000>;
};
+ spmi_bus: qcom,spmi@800f000 {
+ compatible = "qcom,spmi-pmic-arb";
+ reg = <0x800f000 0x1000>,
+ <0x8400000 0x1000000>,
+ <0x9400000 0x1000000>,
+ <0xa400000 0x220000>,
+ <0x800a000 0x3000>;
+ reg-names = "core", "chnls", "obsrvr", "intr", "cnfg";
+ interrupt-names = "periph_irq";
+ interrupts = <GIC_SPI 326 IRQ_TYPE_NONE>;
+ qcom,ee = <0>;
+ qcom,channel = <0>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ interrupt-controller;
+ #interrupt-cells = <4>;
+ cell-index = <0>;
+ qcom,not-wakeup; /* Needed until Full-boot-chain enabled */
+ 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;
@@ -327,19 +377,31 @@
};
};
- clock_gcc: qcom,dummycc {
+ clock_rpmcc: qcom,dummycc {
compatible = "qcom,dummycc";
+ clock-output-names = "rpmcc_clocks";
#clock-cells = <1>;
};
- clock_mmss: qcom,dummycc {
+ clock_gcc: clock-controller@100000 {
compatible = "qcom,dummycc";
+ clock-output-names = "gcc_clocks";
#clock-cells = <1>;
+ #reset-cells = <1>;
};
- clock_gfx: qcom,dummycc {
+ clock_mmss: clock-controller@c8c0000 {
compatible = "qcom,dummycc";
+ clock-output-names = "mmss_clocks";
#clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ clock_gfx: clock-controller@5065000 {
+ compatible = "qcom,dummycc";
+ clock-output-names = "gfx_clocks";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
};
qcom,ipc-spinlock@1f40000 {
@@ -359,6 +421,16 @@
qcom,mpu-enabled;
};
+ dcc: dcc@10b3000 {
+ compatible = "qcom,dcc";
+ reg = <0x10b3000 0x1000>,
+ <0x10b4000 0x800>;
+ reg-names = "dcc-base", "dcc-ram-base";
+
+ clocks = <&clock_rpmcc RPM_QDSS_CLK>;
+ clock-names = "dcc_clk";
+ };
+
qcom,glink-smem-native-xprt-modem@86000000 {
compatible = "qcom,glink-smem-native-xprt";
reg = <0x86000000 0x200000>,
@@ -401,7 +473,7 @@
label = "cdsp";
};
- qcom,glink-smem-native-xprt-rpm@68000 {
+ qcom,glink-smem-native-xprt-rpm@778000 {
compatible = "qcom,glink-rpm-native-xprt";
reg = <0x778000 0x7000>,
<0x17911008 0x4>;
@@ -507,6 +579,13 @@
};
};
+ rpm_bus: qcom,rpm-smd {
+ compatible = "qcom,rpm-glink";
+ qcom,glink-edge = "rpm";
+ rpm-channel-name = "rpm_requests";
+ rpm-standalone; /* TODO: remove this after bring up */
+ };
+
qcom,ipc_router {
compatible = "qcom,ipc_router";
qcom,node-id = <1>;
@@ -541,9 +620,60 @@
qcom,xprt-version = <1>;
qcom,fragmented-data;
};
+
+ qcom,venus@cce0000 {
+ compatible = "qcom,pil-tz-generic";
+ reg = <0xcce0000 0x4000>;
+
+ vdd-supply = <&gdsc_venus>;
+ qcom,proxy-reg-names = "vdd";
+
+ clocks = <&clock_mmss MMSS_VIDEO_CORE_CLK>,
+ <&clock_mmss MMSS_VIDEO_AHB_CLK>,
+ <&clock_mmss MMSS_VIDEO_AXI_CLK>;
+ clock-names = "core_clk","iface_clk",
+ "bus_clk";
+ qcom,proxy-clock-names = "core_clk",
+ "iface_clk","bus_clk";
+
+ qcom,msm-bus,name = "pil-venus";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <63 512 0 0>,
+ <63 512 0 304000>;
+
+ qcom,pas-id = <9>;
+ qcom,proxy-timeout-ms = <100>;
+ qcom,firmware-name = "venus";
+ memory-region = <&venus_fw_mem>;
+ status = "ok";
+ };
+
+ qcom,icnss@18800000 {
+ status = "disabled";
+ compatible = "qcom,icnss";
+ reg = <0x18800000 0x800000>,
+ <0x10ac000 0x20>;
+ reg-names = "membase", "mpm_config";
+ interrupts = <0 413 0>, /* CE0 */
+ <0 414 0>, /* CE1 */
+ <0 415 0>, /* CE2 */
+ <0 416 0>, /* CE3 */
+ <0 417 0>, /* CE4 */
+ <0 418 0>, /* CE5 */
+ <0 420 0>, /* CE6 */
+ <0 421 0>, /* CE7 */
+ <0 422 0>, /* CE8 */
+ <0 423 0>, /* CE9 */
+ <0 424 0>, /* CE10 */
+ <0 425 0>; /* CE11 */
+ qcom,wlan-msa-memory = <0x100000>;
+ };
};
#include "msmfalcon-ion.dtsi"
+#include "msmfalcon-bus.dtsi"
#include "msmfalcon-regulator.dtsi"
#include "msm-gdsc-cobalt.dtsi"
@@ -619,3 +749,5 @@
&gdsc_gpu_cx {
status = "ok";
};
+#include "msm-pmfalcon.dtsi"
+#include "msm-pm2falcon.dtsi"
diff --git a/arch/arm/boot/dts/qcom/msmtriton-ion.dtsi b/arch/arm/boot/dts/qcom/msmtriton-ion.dtsi
new file mode 100644
index 000000000000..f6deef335844
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmtriton-ion.dtsi
@@ -0,0 +1,52 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ qcom,ion {
+ compatible = "qcom,msm-ion";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ system_heap: qcom,ion-heap@25 {
+ reg = <25>;
+ qcom,ion-heap-type = "SYSTEM";
+ };
+
+ system_contig_heap: qcom,ion-heap@21 {
+ reg = <21>;
+ qcom,ion-heap-type = "SYSTEM_CONTIG";
+ };
+
+ qcom,ion-heap@22 { /* ADSP HEAP */
+ reg = <22>;
+ memory-region = <&adsp_mem>;
+ qcom,ion-heap-type = "DMA";
+ };
+
+ qcom,ion-heap@27 { /* QSEECOM HEAP */
+ reg = <27>;
+ memory-region = <&qseecom_mem>;
+ qcom,ion-heap-type = "DMA";
+ };
+
+ qcom,ion-heap@10 { /* SECURE DISPLAY HEAP */
+ reg = <10>;
+ memory-region = <&secure_display_memory>;
+ qcom,ion-heap-type = "HYP_CMA";
+ };
+
+ qcom,ion-heap@9 {
+ reg = <9>;
+ qcom,ion-heap-type = "SYSTEM_SECURE";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi b/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi
new file mode 100644
index 000000000000..695a4f3b63c7
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi
@@ -0,0 +1,136 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+&soc {
+ qcom,smp2p-modem@17911008 {
+ compatible = "qcom,smp2p";
+ reg = <0x17911008 0x4>;
+ qcom,remote-pid = <1>;
+ qcom,irq-bitmask = <0x4000>;
+ interrupts = <0 451 1>;
+ };
+
+ qcom,smp2p-adsp@17911008 {
+ compatible = "qcom,smp2p";
+ reg = <0x17911008 0x4>;
+ qcom,remote-pid = <2>;
+ qcom,irq-bitmask = <0x400>;
+ interrupts = <0 158 1>;
+ };
+
+ smp2pgpio_smp2p_15_in: qcom,smp2pgpio-smp2p-15-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <15>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_15_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_15_in";
+ gpios = <&smp2pgpio_smp2p_15_in 0 0>;
+ };
+
+ smp2pgpio_smp2p_15_out: qcom,smp2pgpio-smp2p-15-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <15>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_15_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_15_out";
+ gpios = <&smp2pgpio_smp2p_15_out 0 0>;
+ };
+
+ smp2pgpio_smp2p_1_in: qcom,smp2pgpio-smp2p-1-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <1>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_1_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_1_in";
+ gpios = <&smp2pgpio_smp2p_1_in 0 0>;
+ };
+
+ smp2pgpio_smp2p_1_out: qcom,smp2pgpio-smp2p-1-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <1>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_1_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_1_out";
+ gpios = <&smp2pgpio_smp2p_1_out 0 0>;
+ };
+
+ smp2pgpio_smp2p_2_in: qcom,smp2pgpio-smp2p-2-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <2>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_2_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_2_in";
+ gpios = <&smp2pgpio_smp2p_2_in 0 0>;
+ };
+
+ smp2pgpio_smp2p_2_out: qcom,smp2pgpio-smp2p-2-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_2_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_2_out";
+ gpios = <&smp2pgpio_smp2p_2_out 0 0>;
+ };
+
+ smp2pgpio_sleepstate_2_out: qcom,smp2pgpio-sleepstate-gpio-2-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "sleepstate";
+ qcom,remote-pid = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio-sleepstate-2-out {
+ compatible = "qcom,smp2pgpio-sleepstate-out";
+ gpios = <&smp2pgpio_sleepstate_2_out 0 0>;
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi
index 17198e462024..71b4da9bb2d0 100644
--- a/arch/arm/boot/dts/qcom/msmtriton.dtsi
+++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi
@@ -14,6 +14,7 @@
#include <dt-bindings/clock/qcom,gcc-msmfalcon.h>
#include <dt-bindings/clock/qcom,gpu-msmfalcon.h>
#include <dt-bindings/clock/qcom,mmcc-msmfalcon.h>
+#include <dt-bindings/clock/qcom,rpmcc.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
/ {
@@ -134,10 +135,74 @@
};
};
+ clocks {
+ xo_board {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <19200000>;
+ clock-output-names = "xo_board";
+ };
+
+ sleep_clk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <32764>;
+ clock-output-names = "sleep_clk";
+ };
+ };
+
soc: soc { };
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ removed_region: removed_region0@85800000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0x0 0x85800000 0x0 0x3700000>;
+ };
+
+ modem_fw_mem: modem_fw_region@8ac00000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0x0 0x8ac00000 0x0 0x7e00000>;
+ };
+
+ adsp_fw_mem: adsp_fw_region@92a00000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0x0 0x92a00000 0x0 0x1e00000>;
+ };
+
+ adsp_mem: adsp_region {
+ compatible = "shared-dma-pool";
+ alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>;
+ reusable;
+ alignment = <0x0 0x400000>;
+ size = <0x0 0x400000>;
+ };
+
+ qseecom_mem: qseecom_region {
+ compatible = "shared-dma-pool";
+ alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>;
+ reusable;
+ alignment = <0x0 0x400000>;
+ size = <0x0 0x1400000>;
+ };
+
+ secure_display_memory: secure_region {
+ compatible = "shared-dma-pool";
+ alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>;
+ reusable;
+ alignment = <0x0 0x400000>;
+ size = <0x0 0x5c00000>;
+ };
+ };
};
+#include "msmtriton-smp2p.dtsi"
&soc {
#address-cells = <1>;
#size-cells = <1>;
@@ -172,6 +237,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>;
@@ -251,18 +325,221 @@
};
};
- clock_gcc: qcom,dummycc {
+ clock_rpmcc: qcom,dummycc {
compatible = "qcom,dummycc";
+ clock-output-names = "rpmcc_clocks";
#clock-cells = <1>;
};
- clock_mmss: qcom,dummycc {
+ clock_gcc: clock-controller@100000 {
compatible = "qcom,dummycc";
+ clock-output-names = "gcc_clocks";
#clock-cells = <1>;
+ #reset-cells = <1>;
};
- clock_gfx: qcom,dummycc {
+ clock_mmss: clock-controller@c8c0000 {
compatible = "qcom,dummycc";
+ clock-output-names = "mmss_clocks";
#clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ clock_gfx: clock-controller@5065000 {
+ compatible = "qcom,dummycc";
+ clock-output-names = "gfx_clocks";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ qcom,ipc-spinlock@1f40000 {
+ compatible = "qcom,ipc-spinlock-sfpb";
+ reg = <0x1f40000 0x8000>;
+ qcom,num-locks = <8>;
+ };
+
+ qcom,smem@86000000 {
+ compatible = "qcom,smem";
+ reg = <0x86000000 0x200000>,
+ <0x17911008 0x4>,
+ <0x778000 0x7000>,
+ <0x1fd4000 0x8>;
+ reg-names = "smem", "irq-reg-base", "aux-mem1",
+ "smem_targ_info_reg";
+ qcom,mpu-enabled;
+ };
+
+ qcom,glink-smem-native-xprt-modem@86000000 {
+ compatible = "qcom,glink-smem-native-xprt";
+ reg = <0x86000000 0x200000>,
+ <0x17911008 0x4>;
+ reg-names = "smem", "irq-reg-base";
+ qcom,irq-mask = <0x8000>;
+ interrupts = <0 452 1>;
+ label = "mpss";
+ };
+
+ qcom,glink-smem-native-xprt-adsp@86000000 {
+ compatible = "qcom,glink-smem-native-xprt";
+ reg = <0x86000000 0x200000>,
+ <0x17911008 0x4>;
+ reg-names = "smem", "irq-reg-base";
+ qcom,irq-mask = <0x200>;
+ interrupts = <0 157 1>;
+ label = "lpass";
+ qcom,qos-config = <&glink_qos_adsp>;
+ qcom,ramp-time = <0xaf>;
+ };
+
+ glink_qos_adsp: qcom,glink-qos-config-adsp {
+ compatible = "qcom,glink-qos-config";
+ qcom,flow-info = <0x3c 0x0>,
+ <0x3c 0x0>,
+ <0x3c 0x0>,
+ <0x3c 0x0>;
+ qcom,mtu-size = <0x800>;
+ qcom,tput-stats-cycle = <0xa>;
+ };
+
+ qcom,glink-smem-native-xprt-rpm@778000 {
+ compatible = "qcom,glink-rpm-native-xprt";
+ reg = <0x778000 0x7000>,
+ <0x17911008 0x4>;
+ reg-names = "msgram", "irq-reg-base";
+ qcom,irq-mask = <0x1>;
+ interrupts = <0 168 1>;
+ label = "rpm";
+ };
+
+ qcom,glink_pkt {
+ compatible = "qcom,glinkpkt";
+
+ qcom,glinkpkt-at-mdm0 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DS";
+ qcom,glinkpkt-dev-name = "at_mdm0";
+ };
+
+ qcom,glinkpkt-loopback_cntl {
+ qcom,glinkpkt-transport = "lloop";
+ qcom,glinkpkt-edge = "local";
+ qcom,glinkpkt-ch-name = "LOCAL_LOOPBACK_CLNT";
+ qcom,glinkpkt-dev-name = "glink_pkt_loopback_ctrl";
+ };
+
+ qcom,glinkpkt-loopback_data {
+ qcom,glinkpkt-transport = "lloop";
+ qcom,glinkpkt-edge = "local";
+ qcom,glinkpkt-ch-name = "glink_pkt_lloop_CLNT";
+ qcom,glinkpkt-dev-name = "glink_pkt_loopback";
+ };
+
+ qcom,glinkpkt-apr-apps2 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "adsp";
+ qcom,glinkpkt-ch-name = "apr_apps2";
+ qcom,glinkpkt-dev-name = "apr_apps2";
+ };
+
+ qcom,glinkpkt-data40-cntl {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA40_CNTL";
+ qcom,glinkpkt-dev-name = "smdcntl8";
+ };
+
+ qcom,glinkpkt-data1 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA1";
+ qcom,glinkpkt-dev-name = "smd7";
+ };
+
+ qcom,glinkpkt-data4 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA4";
+ qcom,glinkpkt-dev-name = "smd8";
+ };
+
+ qcom,glinkpkt-data11 {
+ qcom,glinkpkt-transport = "smem";
+ qcom,glinkpkt-edge = "mpss";
+ qcom,glinkpkt-ch-name = "DATA11";
+ qcom,glinkpkt-dev-name = "smd11";
+ };
+ };
+
+ glink_mpss: qcom,glink-ssr-modem {
+ compatible = "qcom,glink_ssr";
+ label = "modem";
+ qcom,edge = "mpss";
+ qcom,notify-edges = <&glink_lpass>, <&glink_rpm>;
+ qcom,xprt = "smem";
+ };
+
+ glink_lpass: qcom,glink-ssr-adsp {
+ compatible = "qcom,glink_ssr";
+ label = "adsp";
+ qcom,edge = "lpass";
+ qcom,notify-edges = <&glink_mpss>, <&glink_rpm>;
+ qcom,xprt = "smem";
+ };
+
+ glink_rpm: qcom,glink-ssr-rpm {
+ compatible = "qcom,glink_ssr";
+ label = "rpm";
+ qcom,edge = "rpm";
+ qcom,notify-edges = <&glink_lpass>, <&glink_mpss>;
+ qcom,xprt = "smem";
+ };
+
+ qcom,ipc_router {
+ compatible = "qcom,ipc_router";
+ qcom,node-id = <1>;
+ };
+
+ qcom,ipc_router_modem_xprt {
+ compatible = "qcom,ipc_router_glink_xprt";
+ qcom,ch-name = "IPCRTR";
+ qcom,xprt-remote = "mpss";
+ qcom,glink-xprt = "smem";
+ qcom,xprt-linkid = <1>;
+ qcom,xprt-version = <1>;
+ qcom,fragmented-data;
+ };
+
+ qcom,ipc_router_q6_xprt {
+ compatible = "qcom,ipc_router_glink_xprt";
+ qcom,ch-name = "IPCRTR";
+ qcom,xprt-remote = "lpass";
+ qcom,glink-xprt = "smem";
+ qcom,xprt-linkid = <1>;
+ qcom,xprt-version = <1>;
+ qcom,fragmented-data;
+ };
+
+ qcom,icnss@18800000 {
+ status = "disabled";
+ compatible = "qcom,icnss";
+ reg = <0x18800000 0x800000>,
+ <0x10ac000 0x20>;
+ reg-names = "membase", "mpm_config";
+ interrupts = <0 413 0>, /* CE0 */
+ <0 414 0>, /* CE1 */
+ <0 415 0>, /* CE2 */
+ <0 416 0>, /* CE3 */
+ <0 417 0>, /* CE4 */
+ <0 418 0>, /* CE5 */
+ <0 420 0>, /* CE6 */
+ <0 421 0>, /* CE7 */
+ <0 422 0>, /* CE8 */
+ <0 423 0>, /* CE9 */
+ <0 424 0>, /* CE10 */
+ <0 425 0>; /* CE11 */
+ qcom,wlan-msa-memory = <0x100000>;
};
};
+
+#include "msmtriton-ion.dtsi"
diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig
index 0a20c52bd3b2..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
@@ -459,7 +459,6 @@ CONFIG_TRACER_PKT=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_EVENT_TIMER=y
-CONFIG_MSM_CORE_CTL_HELPER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
diff --git a/arch/arm/configs/msmfalcon_defconfig b/arch/arm/configs/msmfalcon_defconfig
index 0a20c52bd3b2..64da50bb55b2 100644
--- a/arch/arm/configs/msmfalcon_defconfig
+++ b/arch/arm/configs/msmfalcon_defconfig
@@ -222,6 +222,8 @@ CONFIG_ZRAM=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_QSEECOM=y
+CONFIG_HDCP_QSEECOM=y
CONFIG_UID_CPUTIME=y
CONFIG_MSM_ULTRASOUND=y
CONFIG_SCSI=y
@@ -261,7 +263,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
@@ -331,6 +332,12 @@ CONFIG_MSM_SDE_ROTATOR=y
CONFIG_QCOM_KGSL=y
CONFIG_FB=y
CONFIG_FB_VIRTUAL=y
+CONFIG_FB_MSM=y
+CONFIG_FB_MSM_MDSS=y
+CONFIG_FB_MSM_MDSS_WRITEBACK=y
+CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
+CONFIG_FB_MSM_MDSS_DP_PANEL=y
+CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
@@ -400,7 +407,6 @@ CONFIG_STAGING=y
CONFIG_ASHMEM=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
-CONFIG_SYNC=y
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_QPNP_REVID=y
@@ -414,8 +420,12 @@ CONFIG_IPA3=y
CONFIG_RMNET_IPA3=y
CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
+CONFIG_MSM_MDSS_PLL=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
@@ -459,7 +469,6 @@ CONFIG_TRACER_PKT=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_EVENT_TIMER=y
-CONFIG_MSM_CORE_CTL_HELPER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index 1d45320ee125..f56a831de043 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -37,6 +37,7 @@
#include <linux/kallsyms.h>
#include <linux/proc_fs.h>
#include <linux/export.h>
+#include <linux/cpumask.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/hardware/cache-uniphier.h>
@@ -127,6 +128,7 @@ static bool migrate_one_irq(struct irq_desc *desc)
const struct cpumask *affinity = irq_data_get_affinity_mask(d);
struct irq_chip *c;
bool ret = false;
+ struct cpumask available_cpus;
/*
* If this is a per-CPU interrupt, or the affinity does not
@@ -135,8 +137,15 @@ static bool migrate_one_irq(struct irq_desc *desc)
if (irqd_is_per_cpu(d) || !cpumask_test_cpu(smp_processor_id(), affinity))
return false;
+ cpumask_copy(&available_cpus, affinity);
+ cpumask_andnot(&available_cpus, &available_cpus, cpu_isolated_mask);
+ affinity = &available_cpus;
+
if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
- affinity = cpu_online_mask;
+ cpumask_andnot(&available_cpus, cpu_online_mask,
+ cpu_isolated_mask);
+ if (cpumask_empty(affinity))
+ affinity = cpu_online_mask;
ret = true;
}
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 84bb603c3142..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
@@ -525,7 +524,6 @@ CONFIG_MSM_PIL_MSS_QDSP6V5=y
CONFIG_TRACER_PKT=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_AVTIMER=y
-CONFIG_MSM_CORE_CTL_HELPER=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
@@ -549,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 6119ff12d46d..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
@@ -527,7 +526,6 @@ CONFIG_TRACER_PKT=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_AVTIMER=y
-CONFIG_MSM_CORE_CTL_HELPER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
@@ -556,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 c425514ddec5..036d6aa5c062 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -16,6 +16,7 @@ CONFIG_CGROUP_CPUACCT=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_SCHED_HMP=y
CONFIG_SCHED_HMP_CSTATE_AWARE=y
+CONFIG_SCHED_CORE_CTL=y
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_PID_NS is not set
@@ -43,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
@@ -272,8 +272,8 @@ 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_SECURE_TOUCH=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_UINPUT=y
@@ -317,6 +317,7 @@ CONFIG_MSM_BCL_PERIPHERAL_CTL=y
CONFIG_BATTERY_BCL=y
CONFIG_QPNP_SMB2=y
CONFIG_SMB138X_CHARGER=y
+CONFIG_QPNP_QNOVO=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_LIMITS_MONITOR=y
CONFIG_LIMITS_LITE_HW=y
@@ -484,7 +485,6 @@ 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
CONFIG_MSM_SMD=y
@@ -525,7 +525,6 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
-CONFIG_MSM_CORE_CTL_HELPER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
@@ -550,11 +549,15 @@ CONFIG_PWM_QPNP=y
CONFIG_ARM_GIC_V3_ACL=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_MSM_TZ_LOG=y
CONFIG_SENSORS_SSC=y
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 27f7ac6663d6..77f0129776a3 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -17,6 +17,7 @@ CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_SCHED_HMP=y
CONFIG_SCHED_HMP_CSTATE_AWARE=y
+CONFIG_SCHED_CORE_CTL=y
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_PID_NS is not set
@@ -44,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
@@ -273,8 +273,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
@@ -320,6 +320,7 @@ CONFIG_MSM_BCL_PERIPHERAL_CTL=y
CONFIG_BATTERY_BCL=y
CONFIG_QPNP_SMB2=y
CONFIG_SMB138X_CHARGER=y
+CONFIG_QPNP_QNOVO=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_LIMITS_MONITOR=y
CONFIG_LIMITS_LITE_HW=y
@@ -461,7 +462,6 @@ CONFIG_SWITCH=y
CONFIG_EDAC=y
CONFIG_EDAC_MM_EDAC=y
CONFIG_EDAC_CORTEX_ARM64=y
-CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_CE=y
CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_UE=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_QPNP=y
@@ -544,7 +544,6 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
-CONFIG_MSM_CORE_CTL_HELPER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
@@ -576,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 39c2d3f71c5a..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
@@ -522,7 +518,6 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
-CONFIG_MSM_CORE_CTL_HELPER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm64/configs/msmfalcon_defconfig b/arch/arm64/configs/msmfalcon_defconfig
index a277038b3fc3..348c34a94119 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
@@ -523,6 +519,7 @@ CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_QCOM_IRQ_HELPER=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_ICNSS=y
+CONFIG_ICNSS_DEBUG=y
CONFIG_MSM_GLADIATOR_ERP_V2=y
CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y
CONFIG_MSM_GLADIATOR_HANG_DETECT=y
@@ -541,7 +538,6 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
CONFIG_MSM_MPM_OF=y
CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_AVTIMER=y
-CONFIG_MSM_CORE_CTL_HELPER=y
CONFIG_QCOM_REMOTEQDSS=y
CONFIG_MSM_SERVICE_NOTIFIER=y
CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index 3691553f218e..c3c6557eb083 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -19,6 +19,7 @@
#include <asm/cpu.h>
#include <asm/cputype.h>
#include <asm/cpufeature.h>
+#include <asm/elf.h>
#include <linux/bitops.h>
#include <linux/bug.h>
@@ -107,6 +108,8 @@ static int c_show(struct seq_file *m, void *v)
{
int i, j;
+ seq_printf(m, "Processor\t: AArch64 Processor rev %d (%s)\n",
+ read_cpuid_id() & 15, ELF_PLATFORM);
for_each_present_cpu(i) {
struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
u32 midr = cpuinfo->reg_midr;
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 10db9cbaf49e..c9df86d68b44 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -683,7 +683,7 @@ int armv8pmu_probe_num_events(struct arm_pmu *arm_pmu)
ret = smp_call_function_any(&arm_pmu->supported_cpus,
armv8pmu_read_num_pmnc_events,
&arm_pmu->num_events, 1);
- if (!ret)
+ if (ret)
idle_notifier_unregister(&pmu_idle_nb->perf_cpu_idle_nb);
return ret;
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 129fb3f8c322..7c4563cbccf3 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -259,7 +259,7 @@ void __show_regs(struct pt_regs *regs)
printk("\n");
}
if (!user_mode(regs))
- show_extra_register_data(regs, 256);
+ show_extra_register_data(regs, 64);
printk("\n");
}
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/block/blk-mq-cpumap.c b/block/blk-mq-cpumap.c
index 8764c241e5bb..8cf06af3036e 100644
--- a/block/blk-mq-cpumap.c
+++ b/block/blk-mq-cpumap.c
@@ -31,8 +31,8 @@ static int get_first_sibling(unsigned int cpu)
return cpu;
}
-int blk_mq_update_queue_map(unsigned int *map, unsigned int nr_queues,
- const struct cpumask *online_mask)
+static int blk_mq_update_queue_map(unsigned int *map,
+ unsigned int nr_queues, const struct cpumask *online_mask)
{
unsigned int i, nr_cpus, nr_uniq_cpus, queue, first_sibling;
cpumask_var_t cpus;
@@ -52,18 +52,14 @@ int blk_mq_update_queue_map(unsigned int *map, unsigned int nr_queues,
queue = 0;
for_each_possible_cpu(i) {
- if (!cpumask_test_cpu(i, online_mask)) {
- map[i] = 0;
- continue;
- }
-
/*
* Easy case - we have equal or more hardware queues. Or
* there are no thread siblings to take into account. Do
* 1:1 if enough, or sequential mapping if less.
*/
- if (nr_queues >= nr_cpus || nr_cpus == nr_uniq_cpus) {
- map[i] = cpu_to_queue_index(nr_cpus, nr_queues, queue);
+ if (nr_queues >= nr_cpu_ids) {
+ map[i] = cpu_to_queue_index(nr_cpu_ids, nr_queues,
+ queue);
queue++;
continue;
}
@@ -75,7 +71,7 @@ int blk_mq_update_queue_map(unsigned int *map, unsigned int nr_queues,
*/
first_sibling = get_first_sibling(i);
if (first_sibling == i) {
- map[i] = cpu_to_queue_index(nr_uniq_cpus, nr_queues,
+ map[i] = cpu_to_queue_index(nr_cpu_ids, nr_queues,
queue);
queue++;
} else
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 6d6f8feb48c0..8398e18d4139 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1783,10 +1783,6 @@ static void blk_mq_init_cpu_queues(struct request_queue *q,
INIT_LIST_HEAD(&__ctx->rq_list);
__ctx->queue = q;
- /* If the cpu isn't online, the cpu is mapped to first hctx */
- if (!cpu_online(i))
- continue;
-
hctx = q->mq_ops->map_queue(q, i);
/*
@@ -1820,12 +1816,9 @@ static void blk_mq_map_swqueue(struct request_queue *q,
* Map software to hardware queues
*/
queue_for_each_ctx(q, ctx, i) {
- /* If the cpu isn't online, the cpu is mapped to first hctx */
- if (!cpumask_test_cpu(i, online_mask))
- continue;
-
hctx = q->mq_ops->map_queue(q, i);
- cpumask_set_cpu(i, hctx->cpumask);
+ if (cpumask_test_cpu(i, online_mask))
+ cpumask_set_cpu(i, hctx->cpumask);
ctx->index_hw = hctx->nr_ctx;
hctx->ctxs[hctx->nr_ctx++] = ctx;
}
@@ -1863,17 +1856,22 @@ static void blk_mq_map_swqueue(struct request_queue *q,
/*
* Initialize batch roundrobin counts
+ * Set next_cpu for only those hctxs that have an online CPU
+ * in their cpumask field. For hctxs that belong to few online
+ * and few offline CPUs, this will always provide one CPU from
+ * online ones. For hctxs belonging to all offline CPUs, their
+ * cpumask will be updated in reinit_notify.
*/
- hctx->next_cpu = cpumask_first(hctx->cpumask);
- hctx->next_cpu_batch = BLK_MQ_CPU_WORK_BATCH;
+ if (cpumask_first(hctx->cpumask) < nr_cpu_ids) {
+ hctx->next_cpu = cpumask_first(hctx->cpumask);
+ hctx->next_cpu_batch = BLK_MQ_CPU_WORK_BATCH;
+ }
}
queue_for_each_ctx(q, ctx, i) {
- if (!cpumask_test_cpu(i, online_mask))
- continue;
-
hctx = q->mq_ops->map_queue(q, i);
- cpumask_set_cpu(i, hctx->tags->cpumask);
+ if (cpumask_test_cpu(i, online_mask))
+ cpumask_set_cpu(i, hctx->tags->cpumask);
}
}
@@ -2101,38 +2099,13 @@ void blk_mq_free_queue(struct request_queue *q)
blk_mq_free_hw_queues(q, set);
}
-/* Basically redo blk_mq_init_queue with queue frozen */
-static void blk_mq_queue_reinit(struct request_queue *q,
- const struct cpumask *online_mask)
-{
- WARN_ON_ONCE(!atomic_read(&q->mq_freeze_depth));
-
- blk_mq_sysfs_unregister(q);
-
- blk_mq_update_queue_map(q->mq_map, q->nr_hw_queues, online_mask);
-
- /*
- * redo blk_mq_init_cpu_queues and blk_mq_init_hw_queues. FIXME: maybe
- * we should change hctx numa_node according to new topology (this
- * involves free and re-allocate memory, worthy doing?)
- */
-
- blk_mq_map_swqueue(q, online_mask);
-
- blk_mq_sysfs_register(q);
-}
-
static int blk_mq_queue_reinit_notify(struct notifier_block *nb,
unsigned long action, void *hcpu)
{
struct request_queue *q;
+ struct blk_mq_hw_ctx *hctx;
+ int i;
int cpu = (unsigned long)hcpu;
- /*
- * New online cpumask which is going to be set in this hotplug event.
- * Declare this cpumasks as global as cpu-hotplug operation is invoked
- * one-by-one and dynamically allocating this could result in a failure.
- */
- static struct cpumask online_new;
/*
* Before hotadded cpu starts handling requests, new mappings must
@@ -2154,44 +2127,31 @@ static int blk_mq_queue_reinit_notify(struct notifier_block *nb,
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_DEAD:
case CPU_UP_CANCELED:
- cpumask_copy(&online_new, cpu_online_mask);
+ mutex_lock(&all_q_mutex);
+ list_for_each_entry(q, &all_q_list, all_q_node) {
+ queue_for_each_hw_ctx(q, hctx, i) {
+ cpumask_clear_cpu(cpu, hctx->cpumask);
+ cpumask_clear_cpu(cpu, hctx->tags->cpumask);
+ }
+ }
+ mutex_unlock(&all_q_mutex);
break;
case CPU_UP_PREPARE:
- cpumask_copy(&online_new, cpu_online_mask);
- cpumask_set_cpu(cpu, &online_new);
+ /* Update hctx->cpumask for newly onlined CPUs */
+ mutex_lock(&all_q_mutex);
+ list_for_each_entry(q, &all_q_list, all_q_node) {
+ queue_for_each_hw_ctx(q, hctx, i) {
+ cpumask_set_cpu(cpu, hctx->cpumask);
+ hctx->next_cpu_batch = BLK_MQ_CPU_WORK_BATCH;
+ cpumask_set_cpu(cpu, hctx->tags->cpumask);
+ }
+ }
+ mutex_unlock(&all_q_mutex);
break;
default:
return NOTIFY_OK;
}
- mutex_lock(&all_q_mutex);
-
- /*
- * We need to freeze and reinit all existing queues. Freezing
- * involves synchronous wait for an RCU grace period and doing it
- * one by one may take a long time. Start freezing all queues in
- * one swoop and then wait for the completions so that freezing can
- * take place in parallel.
- */
- list_for_each_entry(q, &all_q_list, all_q_node)
- blk_mq_freeze_queue_start(q);
- list_for_each_entry(q, &all_q_list, all_q_node) {
- blk_mq_freeze_queue_wait(q);
-
- /*
- * timeout handler can't touch hw queue during the
- * reinitialization
- */
- del_timer_sync(&q->timeout);
- }
-
- list_for_each_entry(q, &all_q_list, all_q_node)
- blk_mq_queue_reinit(q, &online_new);
-
- list_for_each_entry(q, &all_q_list, all_q_node)
- blk_mq_unfreeze_queue(q);
-
- mutex_unlock(&all_q_mutex);
return NOTIFY_OK;
}
diff --git a/block/blk-mq.h b/block/blk-mq.h
index 713820b47b31..7fd11bcaa409 100644
--- a/block/blk-mq.h
+++ b/block/blk-mq.h
@@ -48,8 +48,6 @@ void blk_mq_disable_hotplug(void);
* CPU -> queue mappings
*/
extern unsigned int *blk_mq_make_queue_map(struct blk_mq_tag_set *set);
-extern int blk_mq_update_queue_map(unsigned int *map, unsigned int nr_queues,
- const struct cpumask *online_mask);
extern int blk_mq_hw_queue_to_node(unsigned int *map, unsigned int);
/*
diff --git a/drivers/base/core.c b/drivers/base/core.c
index b7d56c5ea3c6..3ac683dff7de 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -72,6 +72,11 @@ int lock_device_hotplug_sysfs(void)
return restart_syscall();
}
+void lock_device_hotplug_assert(void)
+{
+ lockdep_assert_held(&device_hotplug_lock);
+}
+
#ifdef CONFIG_BLOCK
static inline int device_is_not_partition(struct device *dev)
{
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index dee022638fe6..c8bfb6077224 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -180,6 +180,58 @@ static struct attribute_group crash_note_cpu_attr_group = {
};
#endif
+#ifdef CONFIG_HOTPLUG_CPU
+
+static ssize_t show_cpu_isolated(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, dev);
+ ssize_t rc;
+ int cpuid = cpu->dev.id;
+ unsigned int isolated = cpu_isolated(cpuid);
+
+ rc = snprintf(buf, PAGE_SIZE-2, "%d\n", isolated);
+
+ return rc;
+}
+
+static ssize_t __ref store_cpu_isolated(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, dev);
+ int err;
+ int cpuid = cpu->dev.id;
+ unsigned int isolated;
+
+ err = kstrtouint(strstrip((char *)buf), 0, &isolated);
+ if (err)
+ return err;
+
+ if (isolated > 1)
+ return -EINVAL;
+
+ if (isolated)
+ sched_isolate_cpu(cpuid);
+ else
+ sched_unisolate_cpu(cpuid);
+
+ return count;
+}
+
+static DEVICE_ATTR(isolate, 0644, show_cpu_isolated, store_cpu_isolated);
+
+static struct attribute *cpu_isolated_attrs[] = {
+ &dev_attr_isolate.attr,
+ NULL
+};
+
+static struct attribute_group cpu_isolated_attr_group = {
+ .attrs = cpu_isolated_attrs,
+};
+
+#endif
+
#ifdef CONFIG_SCHED_HMP
static ssize_t show_sched_static_cpu_pwr_cost(struct device *dev,
@@ -280,6 +332,9 @@ static const struct attribute_group *common_cpu_attr_groups[] = {
#ifdef CONFIG_SCHED_HMP
&sched_hmp_cpu_attr_group,
#endif
+#ifdef CONFIG_HOTPLUG_CPU
+ &cpu_isolated_attr_group,
+#endif
NULL
};
@@ -290,6 +345,9 @@ static const struct attribute_group *hotplugable_cpu_attr_groups[] = {
#ifdef CONFIG_SCHED_HMP
&sched_hmp_cpu_attr_group,
#endif
+#ifdef CONFIG_HOTPLUG_CPU
+ &cpu_isolated_attr_group,
+#endif
NULL
};
diff --git a/drivers/base/regmap/regmap-swr.c b/drivers/base/regmap/regmap-swr.c
index 027cbfc505ab..1641c374b189 100644
--- a/drivers/base/regmap/regmap-swr.c
+++ b/drivers/base/regmap/regmap-swr.c
@@ -28,11 +28,16 @@ static int regmap_swr_gather_write(void *context,
struct device *dev = context;
struct swr_device *swr = to_swr_device(dev);
struct regmap *map = dev_get_regmap(dev, NULL);
- size_t addr_bytes = map->format.reg_bytes;
+ size_t addr_bytes;
size_t val_bytes;
int i, ret = 0;
u16 reg_addr = 0;
+ if (map == NULL) {
+ dev_err(dev, "%s: regmap is NULL\n", __func__);
+ return -EINVAL;
+ }
+ addr_bytes = map->format.reg_bytes;
if (swr == NULL) {
dev_err(dev, "%s: swr device is NULL\n", __func__);
return -EINVAL;
@@ -154,10 +159,15 @@ static int regmap_swr_read(void *context,
struct device *dev = context;
struct swr_device *swr = to_swr_device(dev);
struct regmap *map = dev_get_regmap(dev, NULL);
- size_t addr_bytes = map->format.reg_bytes;
+ size_t addr_bytes;
int ret = 0;
u16 reg_addr = 0;
+ if (map == NULL) {
+ dev_err(dev, "%s: regmap is NULL\n", __func__);
+ return -EINVAL;
+ }
+ addr_bytes = map->format.reg_bytes;
if (swr == NULL) {
dev_err(dev, "%s: swr is NULL\n", __func__);
return -EINVAL;
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index b830334dc701..f0cd6cf3967d 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -3066,8 +3066,8 @@ int diag_dci_write_proc(uint8_t peripheral, int pkt_type, char *buf, int len)
!(driver->feature[PERIPHERAL_MODEM].rcvd_feature_mask)) {
DIAG_LOG(DIAG_DEBUG_DCI,
"buf: 0x%pK, p: %d, len: %d, f_mask: %d\n",
- buf, peripheral, len,
- driver->feature[peripheral].rcvd_feature_mask);
+ buf, peripheral, len,
+ driver->feature[PERIPHERAL_MODEM].rcvd_feature_mask);
return -EINVAL;
}
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 594d3b1bf3b5..9f43cb5427f0 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -651,8 +651,8 @@ static void process_build_mask_report(uint8_t *buf, uint32_t len,
void diag_cntl_process_read_data(struct diagfwd_info *p_info, void *buf,
int len)
{
- int read_len = 0;
- int header_len = sizeof(struct diag_ctrl_pkt_header_t);
+ uint32_t read_len = 0;
+ uint32_t header_len = sizeof(struct diag_ctrl_pkt_header_t);
uint8_t *ptr = buf;
struct diag_ctrl_pkt_header_t *ctrl_pkt = NULL;
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-gpu-cobalt.c b/drivers/clk/msm/clock-gpu-cobalt.c
index 7cec9be1f42c..9d93351a083e 100644
--- a/drivers/clk/msm/clock-gpu-cobalt.c
+++ b/drivers/clk/msm/clock-gpu-cobalt.c
@@ -173,7 +173,6 @@ static struct clk_freq_tbl ftbl_gfx3d_clk_src_v2[] = {
F_SLEW( 515000000, 1030000000, gpu_pll0_pll_out_even, 1, 0, 0),
F_SLEW( 596000000, 1192000000, gpu_pll0_pll_out_even, 1, 0, 0),
F_SLEW( 670000000, 1340000000, gpu_pll0_pll_out_even, 1, 0, 0),
- F_SLEW( 710000000, 1420000000, gpu_pll0_pll_out_even, 1, 0, 0),
F_END
};
@@ -612,7 +611,7 @@ static void msm_gfxcc_hamster_fixup(void)
static void msm_gfxcc_cobalt_v2_fixup(void)
{
- gpu_pll0_pll.c.fmax[VDD_DIG_MIN] = 1420000500;
+ gpu_pll0_pll.c.fmax[VDD_DIG_MIN] = 1340000500;
gfx3d_clk_src.freq_tbl = ftbl_gfx3d_clk_src_v2;
}
diff --git a/drivers/clk/msm/clock-mmss-cobalt.c b/drivers/clk/msm/clock-mmss-cobalt.c
index 220a8ba377ba..873dd40d3a44 100644
--- a/drivers/clk/msm/clock-mmss-cobalt.c
+++ b/drivers/clk/msm/clock-mmss-cobalt.c
@@ -240,6 +240,7 @@ static struct rcg_clk ahb_clk_src = {
.set_rate = set_rate_hid,
.freq_tbl = ftbl_ahb_clk_src,
.current_freq = &rcg_dummy_freq,
+ .non_local_control_timeout = 1000,
.base = &virt_base,
.c = {
.dbg_name = "ahb_clk_src",
@@ -481,10 +482,9 @@ static struct clk_freq_tbl ftbl_video_core_clk_src[] = {
};
static struct clk_freq_tbl ftbl_video_core_clk_src_vq[] = {
- F_MM( 100000000, mmsscc_gpll0, 6, 0, 0),
F_MM( 200000000, mmsscc_gpll0, 3, 0, 0),
F_MM( 269330000, mmpll0_pll_out, 3, 0, 0),
- F_MM( 404000000, mmpll0_pll_out, 2, 0, 0),
+ F_MM( 355200000, mmpll6_pll_out, 2.5, 0, 0),
F_MM( 444000000, mmpll6_pll_out, 2, 0, 0),
F_MM( 533000000, mmpll3_pll_out, 2, 0, 0),
F_END
@@ -735,10 +735,9 @@ static struct clk_freq_tbl ftbl_video_subcore_clk_src[] = {
};
static struct clk_freq_tbl ftbl_video_subcore_clk_src_vq[] = {
- F_MM( 100000000, mmsscc_gpll0, 6, 0, 0),
F_MM( 200000000, mmsscc_gpll0, 3, 0, 0),
F_MM( 269330000, mmpll0_pll_out, 3, 0, 0),
- F_MM( 404000000, mmpll0_pll_out, 2, 0, 0),
+ F_MM( 355200000, mmpll6_pll_out, 2.5, 0, 0),
F_MM( 444000000, mmpll6_pll_out, 2, 0, 0),
F_MM( 533000000, mmpll3_pll_out, 2, 0, 0),
F_END
@@ -1113,16 +1112,16 @@ static struct rcg_clk dp_pixel_clk_src = {
.parent = &ext_dp_phy_pll_vco.c,
.ops = &clk_ops_rcg_dp,
.flags = CLKFLAG_NO_RATE_CACHE,
- VDD_DIG_FMAX_MAP3(LOWER, 148380000, LOW, 296740000,
- NOMINAL, 593470000),
+ VDD_DIG_FMAX_MAP3(LOWER, 148380, LOW, 296740,
+ NOMINAL, 593470),
CLK_INIT(dp_pixel_clk_src.c),
},
};
static struct clk_freq_tbl ftbl_dp_link_clk_src[] = {
- F_SLEW( 162000000, 324000000, ext_dp_phy_pll_link, 2, 0, 0),
- F_SLEW( 270000000, 540000000, ext_dp_phy_pll_link, 2, 0, 0),
- F_SLEW( 540000000, 1080000000, ext_dp_phy_pll_link, 2, 0, 0),
+ F_SLEW( 162000, 324000, ext_dp_phy_pll_link, 2, 0, 0),
+ F_SLEW( 270000, 540000, ext_dp_phy_pll_link, 2, 0, 0),
+ F_SLEW( 540000, 1080000, ext_dp_phy_pll_link, 2, 0, 0),
F_END
};
@@ -1136,8 +1135,8 @@ static struct rcg_clk dp_link_clk_src = {
.dbg_name = "dp_link_clk_src",
.ops = &clk_ops_rcg,
.flags = CLKFLAG_NO_RATE_CACHE,
- VDD_DIG_FMAX_MAP3(LOWER, 162000000, LOW, 270000000,
- NOMINAL, 540000000),
+ VDD_DIG_FMAX_MAP3(LOWER, 162000, LOW, 270000,
+ NOMINAL, 540000),
CLK_INIT(dp_link_clk_src.c),
},
};
@@ -1149,9 +1148,9 @@ static struct rcg_clk dp_link_clk_src = {
* clocks.
*/
static struct clk_freq_tbl ftbl_dp_crypto_clk_src[] = {
- F_MM( 101250000, ext_dp_phy_pll_link, 1, 5, 16),
- F_MM( 168750000, ext_dp_phy_pll_link, 1, 5, 16),
- F_MM( 337500000, ext_dp_phy_pll_link, 1, 5, 16),
+ F_MM( 101250, ext_dp_phy_pll_link, 1, 5, 16),
+ F_MM( 168750, ext_dp_phy_pll_link, 1, 5, 16),
+ F_MM( 337500, ext_dp_phy_pll_link, 1, 5, 16),
F_END
};
@@ -1164,8 +1163,8 @@ static struct rcg_clk dp_crypto_clk_src = {
.c = {
.dbg_name = "dp_crypto_clk_src",
.ops = &clk_ops_rcg_mnd,
- VDD_DIG_FMAX_MAP3(LOWER, 101250000, LOW, 168750000,
- NOMINAL, 337500000),
+ VDD_DIG_FMAX_MAP3(LOWER, 101250, LOW, 168750,
+ NOMINAL, 337500),
CLK_INIT(dp_crypto_clk_src.c),
},
};
@@ -2733,17 +2732,10 @@ static void msm_mmsscc_hamster_fixup(void)
static void msm_mmsscc_v2_fixup(void)
{
- cpp_clk_src.c.fmax[VDD_DIG_LOW] = 200000000;
- cpp_clk_src.c.fmax[VDD_DIG_LOW_L1] = 480000000;
- csi0_clk_src.c.fmax[VDD_DIG_LOW] = 256000000;
- csi0_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000;
- csi1_clk_src.c.fmax[VDD_DIG_LOW] = 256000000;
- csi1_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000;
- csi2_clk_src.c.fmax[VDD_DIG_LOW] = 256000000;
- csi2_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000;
- csi3_clk_src.c.fmax[VDD_DIG_LOW] = 256000000;
- csi3_clk_src.c.fmax[VDD_DIG_LOW_L1] = 300000000;
- csiphy_clk_src.c.fmax[VDD_DIG_LOW] = 256000000;
+ csi0_clk_src.c.fmax[VDD_DIG_NOMINAL] = 480000000;
+ csi1_clk_src.c.fmax[VDD_DIG_NOMINAL] = 480000000;
+ csi2_clk_src.c.fmax[VDD_DIG_NOMINAL] = 480000000;
+ csi3_clk_src.c.fmax[VDD_DIG_NOMINAL] = 480000000;
dp_pixel_clk_src.c.fmax[VDD_DIG_LOWER] = 148380000;
}
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index 8ae6a4e994f0..d6cdbbc78827 100644
--- a/drivers/clk/msm/clock-osm.c
+++ b/drivers/clk/msm/clock-osm.c
@@ -57,6 +57,7 @@ enum clk_osm_lut_data {
FREQ_DATA,
PLL_OVERRIDES,
SPARE_DATA,
+ VIRTUAL_CORNER,
NUM_FIELDS,
};
@@ -78,6 +79,7 @@ enum clk_osm_trace_packet_id {
#define MEM_ACC_INSTR_COMP(n) (0x67 + ((n) * 0x40))
#define MEM_ACC_SEQ_REG_VAL_START(n) (SEQ_REG(60 + (n)))
#define SEQ_REG1_MSMCOBALT_V2 0x1048
+#define VERSION_REG 0x0
#define OSM_TABLE_SIZE 40
#define MAX_CLUSTER_CNT 2
@@ -181,7 +183,9 @@ enum clk_osm_trace_packet_id {
#define DROOP_UNSTALL_TIMER_CTRL_REG 0x10AC
#define DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG 0x10B0
#define DROOP_WAIT_TO_RELEASE_TIMER_CTRL1_REG 0x10B4
+#define OSM_PLL_SW_OVERRIDE_EN 0x10C0
+#define PLL_SW_OVERRIDE_DROOP_EN BIT(0)
#define DCVS_DROOP_TIMER_CTRL 0x10B8
#define SEQ_MEM_ADDR 0x500
#define SEQ_CFG_BR_ADDR 0x170
@@ -376,6 +380,11 @@ static inline int clk_osm_read_reg_no_log(struct clk_osm *c, u32 offset)
return readl_relaxed_no_log((char *)c->vbases[OSM_BASE] + offset);
}
+static inline int clk_osm_mb(struct clk_osm *c, int base)
+{
+ return readl_relaxed_no_log((char *)c->vbases[base] + VERSION_REG);
+}
+
static inline int clk_osm_count_ns(struct clk_osm *c, u64 nsec)
{
u64 temp;
@@ -406,12 +415,24 @@ static long clk_osm_list_rate(struct clk *c, unsigned n)
static long clk_osm_round_rate(struct clk *c, unsigned long rate)
{
int i;
+ unsigned long rrate = 0;
- for (i = 0; i < c->num_fmax; i++)
- if (rate <= c->fmax[i])
- return c->fmax[i];
+ /*
+ * If the rate passed in is 0, return the first frequency in
+ * the FMAX table.
+ */
+ if (!rate)
+ return c->fmax[0];
+
+ for (i = 0; i < c->num_fmax; i++) {
+ if (is_better_rate(rate, rrate, c->fmax[i])) {
+ rrate = c->fmax[i];
+ if (rrate == rate)
+ break;
+ }
+ }
- return c->fmax[i-1];
+ return rrate;
}
static int clk_osm_search_table(struct osm_entry *table, int entries, long rate)
@@ -447,7 +468,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],
@@ -458,14 +479,14 @@ 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);
}
/* Make sure the write goes through before proceeding */
- mb();
+ clk_osm_mb(cpuclk, OSM_BASE);
return 0;
}
@@ -477,7 +498,7 @@ static int clk_osm_enable(struct clk *c)
clk_osm_write_reg(cpuclk, 1, ENABLE_REG);
/* Make sure the write goes through before proceeding */
- mb();
+ clk_osm_mb(cpuclk, OSM_BASE);
/* Wait for 5us for OSM hardware to enable */
udelay(5);
@@ -581,19 +602,21 @@ static void clk_osm_print_osm_table(struct clk_osm *c)
{
int i;
struct osm_entry *table = c->osm_table;
- u32 pll_src, pll_div, lval;
+ u32 pll_src, pll_div, lval, core_count;
- pr_debug("Index, Frequency, VC, OLV (mv), PLL Src, PLL Div, L-Val, ACC Level\n");
+ pr_debug("Index, Frequency, VC, OLV (mv), Core Count, PLL Src, PLL Div, L-Val, ACC Level\n");
for (i = 0; i < c->num_entries; i++) {
pll_src = (table[i].freq_data & GENMASK(27, 26)) >> 26;
pll_div = (table[i].freq_data & GENMASK(25, 24)) >> 24;
lval = table[i].freq_data & GENMASK(7, 0);
+ core_count = (table[i].freq_data & GENMASK(18, 16)) >> 16;
- pr_debug("%3d, %11lu, %2u, %5u, %6u, %8u, %7u, %5u\n",
+ pr_debug("%3d, %11lu, %2u, %5u, %2u, %6u, %8u, %7u, %5u\n",
i,
table[i].frequency,
table[i].virtual_corner,
table[i].open_loop_volt,
+ core_count,
pll_src,
pll_div,
lval,
@@ -655,14 +678,17 @@ static int clk_osm_get_lut(struct platform_device *pdev,
c->osm_table[j].freq_data = array[i + FREQ_DATA];
c->osm_table[j].override_data = array[i + PLL_OVERRIDES];
c->osm_table[j].spare_data = array[i + SPARE_DATA];
- pr_debug("index=%d freq=%ld freq_data=0x%x override_data=0x%x spare_data=0x%x\n",
+ /* Voltage corners are 0 based in the OSM LUT */
+ c->osm_table[j].virtual_corner = array[i + VIRTUAL_CORNER] - 1;
+ pr_debug("index=%d freq=%ld virtual_corner=%d freq_data=0x%x override_data=0x%x spare_data=0x%x\n",
j, c->osm_table[j].frequency,
+ c->osm_table[j].virtual_corner,
c->osm_table[j].freq_data,
c->osm_table[j].override_data,
c->osm_table[j].spare_data);
data = (array[i + FREQ_DATA] & GENMASK(18, 16)) >> 16;
- if (!last_entry && data == MAX_CONFIG) {
+ if (!last_entry) {
clk->fmax[k] = array[i];
k++;
}
@@ -1083,14 +1109,14 @@ static void clk_osm_setup_cluster_pll(struct clk_osm *c)
PLL_MODE);
/* Ensure writes complete before delaying */
- mb();
+ clk_osm_mb(c, PLL_BASE);
udelay(PLL_WAIT_LOCK_TIME_US);
writel_relaxed(0x6, c->vbases[PLL_BASE] + PLL_MODE);
/* Ensure write completes before delaying */
- mb();
+ clk_osm_mb(c, PLL_BASE);
usleep_range(50, 75);
@@ -1135,7 +1161,7 @@ static int clk_osm_setup_hw_table(struct clk_osm *c)
}
/* Make sure all writes go through */
- mb();
+ clk_osm_mb(c, OSM_BASE);
return 0;
}
@@ -1143,77 +1169,25 @@ static int clk_osm_setup_hw_table(struct clk_osm *c)
static int clk_osm_resolve_open_loop_voltages(struct clk_osm *c)
{
struct regulator *regulator = c->vdd_reg;
- struct dev_pm_opp *opp;
- unsigned long freq;
- u32 vc, mv, data;
- int i, rc = 0;
+ u32 vc, mv;
+ int i;
- /*
- * Determine frequency -> virtual corner -> open-loop voltage
- * mapping from the OPP table.
- */
for (i = 0; i < OSM_TABLE_SIZE; i++) {
- freq = c->osm_table[i].frequency;
- /*
- * Only frequencies that are supported across all configurations
- * are present in the OPP table associated with the regulator
- * device.
- */
- data = (c->osm_table[i].freq_data & GENMASK(18, 16)) >> 16;
- if (data != MAX_CONFIG) {
- if (i < 1) {
- pr_err("Invalid LUT entry at index 0\n");
- return -EINVAL;
- }
- c->osm_table[i].open_loop_volt =
- c->osm_table[i-1].open_loop_volt;
- c->osm_table[i].virtual_corner =
- c->osm_table[i-1].virtual_corner;
- continue;
- }
-
- rcu_read_lock();
- opp = dev_pm_opp_find_freq_exact(&c->vdd_dev->dev, freq, true);
- if (IS_ERR(opp)) {
- rc = PTR_ERR(opp);
- if (rc == -ERANGE)
- pr_err("Frequency %lu not found\n", freq);
- goto exit;
- }
-
- vc = dev_pm_opp_get_voltage(opp);
- if (!vc) {
- pr_err("No virtual corner found for frequency %lu\n",
- freq);
- rc = -ERANGE;
- goto exit;
- }
-
- rcu_read_unlock();
-
+ vc = c->osm_table[i].virtual_corner + 1;
/* Voltage is in uv. Convert to mv */
mv = regulator_list_corner_voltage(regulator, vc) / 1000;
-
- /* CPR virtual corners are zero-based numbered */
- vc--;
c->osm_table[i].open_loop_volt = mv;
- c->osm_table[i].virtual_corner = vc;
}
return 0;
-exit:
- rcu_read_unlock();
- return rc;
}
static int clk_osm_resolve_crossover_corners(struct clk_osm *c,
struct platform_device *pdev)
{
struct regulator *regulator = c->vdd_reg;
- struct dev_pm_opp *opp;
- unsigned long freq = 0;
- int vc, i, threshold, rc = 0;
- u32 corner_volt, data;
+ int count, vc, i, threshold, rc = 0;
+ u32 corner_volt;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,apm-threshold-voltage",
@@ -1224,70 +1198,26 @@ static int clk_osm_resolve_crossover_corners(struct clk_osm *c,
}
/* Determine crossover virtual corner */
- rcu_read_lock();
- opp = dev_pm_opp_find_freq_exact(&c->vdd_dev->dev, freq, true);
- if (IS_ERR(opp)) {
- rc = PTR_ERR(opp);
- if (rc == -ERANGE)
- pr_debug("APM placeholder frequency entry not found\n");
- goto exit;
- }
- vc = dev_pm_opp_get_voltage(opp);
- if (!vc) {
- pr_debug("APM crossover corner not found\n");
- rc = -ERANGE;
- goto exit;
+ count = regulator_count_voltages(regulator);
+ if (count < 0) {
+ pr_err("Failed to get the number of virtual corners supported\n");
+ return count;
}
- rcu_read_unlock();
- vc--;
- c->apm_crossover_vc = vc;
+
+ c->apm_crossover_vc = count - 1;
/* Determine threshold virtual corner */
for (i = 0; i < OSM_TABLE_SIZE; i++) {
- freq = c->osm_table[i].frequency;
- /*
- * Only frequencies that are supported across all configurations
- * are present in the OPP table associated with the regulator
- * device.
- */
- data = (c->osm_table[i].freq_data & GENMASK(18, 16)) >> 16;
- if (data != MAX_CONFIG)
- continue;
-
- rcu_read_lock();
- opp = dev_pm_opp_find_freq_exact(&c->vdd_dev->dev, freq, true);
- if (IS_ERR(opp)) {
- rc = PTR_ERR(opp);
- if (rc == -ERANGE)
- pr_err("Frequency %lu not found\n", freq);
- goto exit;
- }
-
- vc = dev_pm_opp_get_voltage(opp);
- if (!vc) {
- pr_err("No virtual corner found for frequency %lu\n",
- freq);
- rc = -ERANGE;
- goto exit;
- }
-
- rcu_read_unlock();
-
+ vc = c->osm_table[i].virtual_corner + 1;
corner_volt = regulator_list_corner_voltage(regulator, vc);
- /* CPR virtual corners are zero-based numbered */
- vc--;
-
if (corner_volt >= threshold) {
- c->apm_threshold_vc = vc;
+ c->apm_threshold_vc = c->osm_table[i].virtual_corner;
break;
}
}
return 0;
-exit:
- rcu_read_unlock();
- return rc;
}
static int clk_osm_set_cc_policy(struct platform_device *pdev)
@@ -1350,7 +1280,7 @@ static int clk_osm_set_cc_policy(struct platform_device *pdev)
}
/* Wait for the writes to complete */
- mb();
+ clk_osm_mb(&perfcl_clk, OSM_BASE);
rc = of_property_read_bool(pdev->dev.of_node, "qcom,set-ret-inactive");
if (rc) {
@@ -1375,7 +1305,7 @@ static int clk_osm_set_cc_policy(struct platform_device *pdev)
clk_osm_write_reg(&perfcl_clk, val, SPM_CC_DCVS_DISABLE);
/* Wait for the writes to complete */
- mb();
+ clk_osm_mb(&perfcl_clk, OSM_BASE);
devm_kfree(&pdev->dev, array);
return 0;
@@ -1470,7 +1400,7 @@ static int clk_osm_set_llm_freq_policy(struct platform_device *pdev)
clk_osm_write_reg(&perfcl_clk, regval, LLM_INTF_DCVS_DISABLE);
/* Wait for the write to complete */
- mb();
+ clk_osm_mb(&perfcl_clk, OSM_BASE);
devm_kfree(&pdev->dev, array);
return 0;
@@ -1545,7 +1475,7 @@ static int clk_osm_set_llm_volt_policy(struct platform_device *pdev)
clk_osm_write_reg(&perfcl_clk, val, LLM_INTF_DCVS_DISABLE);
/* Wait for the writes to complete */
- mb();
+ clk_osm_mb(&perfcl_clk, OSM_BASE);
devm_kfree(&pdev->dev, array);
return 0;
@@ -1746,7 +1676,7 @@ static void clk_osm_setup_osm_was(struct clk_osm *c)
}
/* Ensure writes complete before returning */
- mb();
+ clk_osm_mb(c, OSM_BASE);
}
static void clk_osm_setup_fsms(struct clk_osm *c)
@@ -1856,7 +1786,7 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
val = clk_osm_read_reg(c,
DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
- val |= BVAL(15, 0, clk_osm_count_ns(c, 500));
+ val |= BVAL(15, 0, clk_osm_count_ns(c, 15000));
clk_osm_write_reg(c, val,
DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
}
@@ -1870,7 +1800,7 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
if (c->wfx_fsm_en || c->ps_fsm_en || c->droop_fsm_en) {
clk_osm_write_reg(c, 0x1, DROOP_PROG_SYNC_DELAY_REG);
- clk_osm_write_reg(c, clk_osm_count_ns(c, 250),
+ clk_osm_write_reg(c, clk_osm_count_ns(c, 500),
DROOP_RELEASE_TIMER_CTRL);
clk_osm_write_reg(c, clk_osm_count_ns(c, 500),
DCVS_DROOP_TIMER_CTRL);
@@ -1879,6 +1809,11 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
BVAL(6, 0, 0x8);
clk_osm_write_reg(c, val, DROOP_CTRL_REG);
}
+
+ /* Enable the PLL Droop Override */
+ val = clk_osm_read_reg(c, OSM_PLL_SW_OVERRIDE_EN);
+ val |= PLL_SW_OVERRIDE_DROOP_EN;
+ clk_osm_write_reg(c, val, OSM_PLL_SW_OVERRIDE_EN);
}
static void clk_osm_do_additional_setup(struct clk_osm *c,
@@ -1947,7 +1882,7 @@ static void clk_osm_apm_vc_setup(struct clk_osm *c)
SEQ_REG(76));
/* Ensure writes complete before returning */
- mb();
+ clk_osm_mb(c, OSM_BASE);
} else {
if (msmcobalt_v1) {
scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(1),
@@ -1960,8 +1895,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/clock.h b/drivers/clk/msm/clock.h
index bee769921ff7..b7aa946f1931 100644
--- a/drivers/clk/msm/clock.h
+++ b/drivers/clk/msm/clock.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -38,7 +38,7 @@ int msm_clock_init(struct clock_init_data *data);
int find_vdd_level(struct clk *clk, unsigned long rate);
extern struct list_head orphan_clk_list;
-#ifdef CONFIG_DEBUG_FS
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_COMMON_CLK_MSM)
int clock_debug_register(struct clk *clk);
void clock_debug_print_enabled(void);
#else
diff --git a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c
index c88a5089bd60..9a080e4ee39b 100644
--- a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c
+++ b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt-util.c
@@ -190,9 +190,9 @@ int dp_config_vco_rate(struct dp_pll_vco_clk *vco, unsigned long rate)
QSERDES_COM_CLK_SEL, 0x30);
/* Different for each clock rates */
- if (rate == DP_VCO_HSCLK_RATE_1620MHz) {
- pr_debug("%s: VCO rate: %lld\n", __func__,
- DP_VCO_RATE_8100MHz);
+ if (rate == DP_VCO_HSCLK_RATE_1620MHZDIV1000) {
+ pr_debug("%s: VCO rate: %ld\n", __func__,
+ DP_VCO_RATE_8100MHZDIV1000);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_SYS_CLK_CTRL, 0x02);
MDSS_PLL_REG_W(dp_res->pll_base,
@@ -215,9 +215,9 @@ int dp_config_vco_rate(struct dp_pll_vco_clk *vco, unsigned long rate)
QSERDES_COM_LOCK_CMP2_MODE0, 0x21);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP3_MODE0, 0x00);
- } else if (rate == DP_VCO_HSCLK_RATE_2700MHz) {
- pr_debug("%s: VCO rate: %lld\n", __func__,
- DP_VCO_RATE_8100MHz);
+ } else if (rate == DP_VCO_HSCLK_RATE_2700MHZDIV1000) {
+ pr_debug("%s: VCO rate: %ld\n", __func__,
+ DP_VCO_RATE_8100MHZDIV1000);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_SYS_CLK_CTRL, 0x06);
MDSS_PLL_REG_W(dp_res->pll_base,
@@ -240,9 +240,9 @@ int dp_config_vco_rate(struct dp_pll_vco_clk *vco, unsigned long rate)
QSERDES_COM_LOCK_CMP2_MODE0, 0x38);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_LOCK_CMP3_MODE0, 0x00);
- } else if (rate == DP_VCO_HSCLK_RATE_5400MHz) {
- pr_debug("%s: VCO rate: %lld\n", __func__,
- DP_VCO_RATE_10800MHz);
+ } else if (rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000) {
+ pr_debug("%s: VCO rate: %ld\n", __func__,
+ DP_VCO_RATE_10800MHZDIV1000);
MDSS_PLL_REG_W(dp_res->pll_base,
QSERDES_COM_SYS_CLK_CTRL, 0x06);
MDSS_PLL_REG_W(dp_res->pll_base,
@@ -272,8 +272,8 @@ int dp_config_vco_rate(struct dp_pll_vco_clk *vco, unsigned long rate)
/* Make sure the PLL register writes are done */
wmb();
- if ((rate == DP_VCO_HSCLK_RATE_1620MHz)
- || (rate == DP_VCO_HSCLK_RATE_2700MHz)) {
+ if ((rate == DP_VCO_HSCLK_RATE_1620MHZDIV1000)
+ || (rate == DP_VCO_HSCLK_RATE_2700MHZDIV1000)) {
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_VCO_DIV, 0x1);
} else {
@@ -713,14 +713,14 @@ unsigned long dp_vco_get_rate(struct clk *c)
pr_err("%s: unsupported div. Phy_mode: %d\n", __func__, div);
if (link2xclk_div == 10) {
- vco_rate = DP_VCO_HSCLK_RATE_2700MHz;
+ vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
} else {
if (hsclk_div == 5)
- vco_rate = DP_VCO_HSCLK_RATE_1620MHz;
+ vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
else if (hsclk_div == 3)
- vco_rate = DP_VCO_HSCLK_RATE_2700MHz;
+ vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
else
- vco_rate = DP_VCO_HSCLK_RATE_5400MHz;
+ vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
}
pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
@@ -737,8 +737,8 @@ long dp_vco_round_rate(struct clk *c, unsigned long rate)
if (rate <= vco->min_rate)
rrate = vco->min_rate;
- else if (rate <= DP_VCO_HSCLK_RATE_2700MHz)
- rrate = DP_VCO_HSCLK_RATE_2700MHz;
+ else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
+ rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
else
rrate = vco->max_rate;
diff --git a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c
index 47b5bd7d7579..598a2e8d25de 100644
--- a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c
+++ b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.c
@@ -93,8 +93,8 @@ static struct clk_mux_ops mdss_mux_ops = {
};
static struct dp_pll_vco_clk dp_vco_clk = {
- .min_rate = DP_VCO_HSCLK_RATE_1620MHz,
- .max_rate = DP_VCO_HSCLK_RATE_5400MHz,
+ .min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000,
+ .max_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000,
.c = {
.dbg_name = "dp_vco_clk",
.ops = &dp_cobalt_vco_clk_ops,
diff --git a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h
index d2b5d98a2d41..d89545b38e64 100644
--- a/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h
+++ b/drivers/clk/msm/mdss/mdss-dp-pll-cobalt.h
@@ -155,12 +155,12 @@
#define DP_PLL_POLL_SLEEP_US 500
#define DP_PLL_POLL_TIMEOUT_US 10000
-#define DP_VCO_RATE_8100MHz 8100000000ULL
-#define DP_VCO_RATE_10800MHz 10800000000ULL
+#define DP_VCO_RATE_8100MHZDIV1000 8100000UL
+#define DP_VCO_RATE_10800MHZDIV1000 10800000UL
-#define DP_VCO_HSCLK_RATE_1620MHz 1620000000ULL
-#define DP_VCO_HSCLK_RATE_2700MHz 2700000000ULL
-#define DP_VCO_HSCLK_RATE_5400MHz 5400000000ULL
+#define DP_VCO_HSCLK_RATE_1620MHZDIV1000 1620000UL
+#define DP_VCO_HSCLK_RATE_2700MHZDIV1000 2700000UL
+#define DP_VCO_HSCLK_RATE_5400MHZDIV1000 5400000UL
int dp_vco_set_rate(struct clk *c, unsigned long rate);
unsigned long dp_vco_get_rate(struct clk *c);
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c b/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c
index f6c85cf8d9a4..5f779ec9bcc3 100644
--- a/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c
@@ -685,6 +685,10 @@ static void pll_db_commit_8996(struct mdss_pll_resources *pll,
MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, 0);
wmb(); /* make sure register committed */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_VCO_TUNE, 0);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_CODE, 0);
+ wmb(); /* make sure register committed */
+
data = pdb->in.dsiclk_sel; /* set dsiclk_sel = 1 */
MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CLK_CFG1, data);
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/Kconfig b/drivers/clk/qcom/Kconfig
index ea6e4a1423af..e39686ca4feb 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -27,7 +27,7 @@ config QCOM_CLK_RPM
config QCOM_CLK_SMD_RPM
tristate "RPM over SMD based Clock Controller"
- depends on COMMON_CLK_QCOM && QCOM_SMD_RPM
+ depends on COMMON_CLK_QCOM
select QCOM_RPMCC
help
The RPM (Resource Power Manager) is a dedicated hardware engine for
@@ -153,6 +153,26 @@ config MSM_MMCC_8996
Say Y if you want to support multimedia devices such as display,
graphics, video encode/decode, camera, etc.
+config MSM_GCC_FALCON
+ tristate "MSMFALCON Global Clock Controller"
+ select QCOM_GDSC
+ depends on COMMON_CLK_QCOM
+ ---help---
+ Support for the global clock controller on Qualcomm Technologies, Inc
+ MSMfalcon devices.
+ Say Y if you want to use peripheral devices such as UART, SPI, I2C,
+ USB, UFS, SD/eMMC, PCIe, etc.
+
+config MSM_GPUCC_FALCON
+ tristate "MSMFALCON Graphics Clock Controller"
+ select MSM_GCC_FALCON
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the graphics clock controller on Qualcomm Technologies, Inc
+ MSMfalcon devices.
+ Say Y if you want to support graphics controller devices which will
+ be required to enable those device.
+
config QCOM_HFPLL
tristate "High-Frequency PLL (HFPLL) Clock Controller"
depends on COMMON_CLK_QCOM
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index dc1b66f84af2..7ee0294e9dc7 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -13,7 +13,7 @@ clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
clk-qcom-y += clk-hfpll.o
clk-qcom-y += reset.o
clk-qcom-y += clk-dummy.o
-clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
+clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o gdsc-regulator.o
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o
@@ -25,9 +25,11 @@ obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o
obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
obj-$(CONFIG_MSM_GCC_8996) += gcc-msm8996.o
+obj-$(CONFIG_MSM_GCC_FALCON) += gcc-msmfalcon.o
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
+obj-$(CONFIG_MSM_GPUCC_FALCON) += gpucc-msmfalcon.o
obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
obj-$(CONFIG_QCOM_HFPLL) += hfpll.o
obj-$(CONFIG_KRAITCC) += krait-cc.o
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index b904c335cda4..da02ab499bff 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 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
@@ -23,6 +23,7 @@ struct freq_tbl {
u8 pre_div;
u16 m;
u16 n;
+ unsigned long src_freq;
};
/**
@@ -158,6 +159,8 @@ extern const struct clk_ops clk_dyn_rcg_ops;
* @freq_tbl: frequency table
* @current_freq: last cached frequency when using branches with shared RCGs
* @clkr: regmap clock handle
+ * @flags: set if RCG needs to be force enabled/disabled during
+ * power sequence.
*
*/
struct clk_rcg2 {
@@ -168,6 +171,9 @@ struct clk_rcg2 {
const struct freq_tbl *freq_tbl;
unsigned long current_freq;
struct clk_regmap clkr;
+
+#define FORCE_ENABLE_RCGR BIT(0)
+ u8 flags;
};
#define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
@@ -179,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 a071bba8018c..933a208392bd 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 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
@@ -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>
@@ -60,6 +61,57 @@ static int clk_rcg2_is_enabled(struct clk_hw *hw)
return (cmd & CMD_ROOT_OFF) == 0;
}
+static int clk_rcg_set_force_enable(struct clk_hw *hw)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ const char *name = clk_hw_get_name(hw);
+ int ret = 0, count;
+
+ /* force enable RCG */
+ ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
+ CMD_ROOT_EN, CMD_ROOT_EN);
+ if (ret)
+ return ret;
+
+ /* wait for RCG to turn ON */
+ for (count = 500; count > 0; count--) {
+ ret = clk_rcg2_is_enabled(hw);
+ if (ret) {
+ ret = 0;
+ break;
+ }
+ udelay(1);
+ }
+ if (!count)
+ pr_err("%s: RCG did not turn on after force enable\n", name);
+
+ return ret;
+}
+
+static int clk_rcg2_enable(struct clk_hw *hw)
+{
+ int ret = 0;
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+ if (rcg->flags & FORCE_ENABLE_RCGR)
+ ret = clk_rcg_set_force_enable(hw);
+
+ return ret;
+}
+
+static void clk_rcg2_disable(struct clk_hw *hw)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+ if (rcg->flags & FORCE_ENABLE_RCGR) {
+ /* force disable RCG - clear CMD_ROOT_EN bit */
+ regmap_update_bits(rcg->clkr.regmap,
+ rcg->cmd_rcgr + CMD_REG, CMD_ROOT_EN, 0);
+ /* Add a delay to disable the RCG */
+ udelay(100);
+ }
+}
+
static u8 clk_rcg2_get_parent(struct clk_hw *hw)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@@ -290,6 +342,8 @@ static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
}
const struct clk_ops clk_rcg2_ops = {
+ .enable = clk_rcg2_enable,
+ .disable = clk_rcg2_disable,
.is_enabled = clk_rcg2_is_enabled,
.get_parent = clk_rcg2_get_parent,
.set_parent = clk_rcg2_set_parent,
@@ -801,6 +855,8 @@ static int clk_gfx3d_set_rate(struct clk_hw *hw, unsigned long rate,
}
const struct clk_ops clk_gfx3d_ops = {
+ .enable = clk_rcg2_enable,
+ .disable = clk_rcg2_disable,
.is_enabled = clk_rcg2_is_enabled,
.get_parent = clk_rcg2_get_parent,
.set_parent = clk_rcg2_set_parent,
@@ -810,3 +866,81 @@ 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_rcg2 *rcg = to_clk_rcg2(hw);
+ 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 06eb06009268..ac007ec667bb 100644
--- a/drivers/clk/qcom/clk-smd-rpm.c
+++ b/drivers/clk/qcom/clk-smd-rpm.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2016, Linaro Limited
- * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, 2016, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -23,6 +23,8 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/soc/qcom/smd-rpm.h>
+#include <soc/qcom/rpm-smd.h>
+#include <linux/clk.h>
#include <dt-bindings/clock/qcom,rpmcc.h>
#include <dt-bindings/mfd/qcom-rpm.h>
@@ -37,6 +39,8 @@
#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \
key) \
static struct clk_smd_rpm _platform##_##_active; \
+ static unsigned long _name##_##last_active_set_vote; \
+ static unsigned long _name##_##last_sleep_set_vote; \
static struct clk_smd_rpm _platform##_##_name = { \
.rpm_res_type = (type), \
.rpm_clk_id = (r_id), \
@@ -44,6 +48,8 @@
.rpm_key = (key), \
.peer = &_platform##_##_active, \
.rate = INT_MAX, \
+ .last_active_set_vote = &_name##_##last_active_set_vote, \
+ .last_sleep_set_vote = &_name##_##last_sleep_set_vote, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_smd_rpm_ops, \
.name = #_name, \
@@ -59,6 +65,8 @@
.rpm_key = (key), \
.peer = &_platform##_##_name, \
.rate = INT_MAX, \
+ .last_active_set_vote = &_name##_##last_active_set_vote, \
+ .last_sleep_set_vote = &_name##_##last_sleep_set_vote, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_smd_rpm_ops, \
.name = #_active, \
@@ -70,6 +78,8 @@
#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \
stat_id, r, key) \
static struct clk_smd_rpm _platform##_##_active; \
+ static unsigned long _name##_##last_active_set_vote; \
+ static unsigned long _name##_##last_sleep_set_vote; \
static struct clk_smd_rpm _platform##_##_name = { \
.rpm_res_type = (type), \
.rpm_clk_id = (r_id), \
@@ -78,6 +88,8 @@
.branch = true, \
.peer = &_platform##_##_active, \
.rate = (r), \
+ .last_active_set_vote = &_name##_##last_active_set_vote, \
+ .last_sleep_set_vote = &_name##_##last_sleep_set_vote, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_smd_rpm_branch_ops, \
.name = #_name, \
@@ -94,6 +106,8 @@
.branch = true, \
.peer = &_platform##_##_name, \
.rate = (r), \
+ .last_active_set_vote = &_name##_##last_active_set_vote, \
+ .last_sleep_set_vote = &_name##_##last_sleep_set_vote, \
.hw.init = &(struct clk_init_data){ \
.ops = &clk_smd_rpm_branch_ops, \
.name = #_active, \
@@ -137,7 +151,8 @@ struct clk_smd_rpm {
struct clk_smd_rpm *peer;
struct clk_hw hw;
unsigned long rate;
- struct qcom_smd_rpm *rpm;
+ unsigned long *last_active_set_vote;
+ unsigned long *last_sleep_set_vote;
};
struct clk_smd_rpm_req {
@@ -153,61 +168,81 @@ struct rpm_cc {
};
struct rpm_smd_clk_desc {
- struct clk_smd_rpm **clks;
+ struct clk_hw **clks;
+ size_t num_rpm_clks;
size_t num_clks;
};
static DEFINE_MUTEX(rpm_smd_clk_lock);
-static int clk_smd_rpm_handoff(struct clk_smd_rpm *r)
+static int clk_smd_rpm_handoff(struct clk_hw *hw)
{
- int ret;
- struct clk_smd_rpm_req req = {
+ int ret = 0;
+ uint32_t value = cpu_to_le32(INT_MAX);
+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
+ struct msm_rpm_kvp req = {
.key = cpu_to_le32(r->rpm_key),
- .nbytes = cpu_to_le32(sizeof(u32)),
- .value = cpu_to_le32(INT_MAX),
+ .data = (void *)&value,
+ .length = sizeof(value),
};
- ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
- r->rpm_res_type, r->rpm_clk_id, &req,
- sizeof(req));
+ ret = msm_rpm_send_message(QCOM_SMD_RPM_ACTIVE_STATE, r->rpm_res_type,
+ r->rpm_clk_id, &req, 1);
if (ret)
return ret;
- ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
- r->rpm_res_type, r->rpm_clk_id, &req,
- sizeof(req));
+
+ ret = msm_rpm_send_message(QCOM_SMD_RPM_SLEEP_STATE, r->rpm_res_type,
+ r->rpm_clk_id, &req, 1);
if (ret)
return ret;
- return 0;
+ return ret;
}
static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r,
- unsigned long rate)
+ uint32_t rate)
{
- struct clk_smd_rpm_req req = {
+ int ret = 0;
+ struct msm_rpm_kvp req = {
.key = cpu_to_le32(r->rpm_key),
- .nbytes = cpu_to_le32(sizeof(u32)),
- .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */
+ .data = (void *)&rate,
+ .length = sizeof(rate),
};
- return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE,
- r->rpm_res_type, r->rpm_clk_id, &req,
- sizeof(req));
+ if (*r->last_active_set_vote == rate)
+ return ret;
+
+ ret = msm_rpm_send_message(QCOM_SMD_RPM_ACTIVE_STATE, r->rpm_res_type,
+ r->rpm_clk_id, &req, 1);
+ if (ret)
+ return ret;
+
+ *r->last_active_set_vote = rate;
+
+ return ret;
}
static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r,
- unsigned long rate)
+ uint32_t rate)
{
- struct clk_smd_rpm_req req = {
+ int ret = 0;
+ struct msm_rpm_kvp req = {
.key = cpu_to_le32(r->rpm_key),
- .nbytes = cpu_to_le32(sizeof(u32)),
- .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */
+ .data = (void *)&rate,
+ .length = sizeof(rate),
};
- return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE,
- r->rpm_res_type, r->rpm_clk_id, &req,
- sizeof(req));
+ if (*r->last_sleep_set_vote == rate)
+ return ret;
+
+ ret = msm_rpm_send_message(QCOM_SMD_RPM_SLEEP_STATE, r->rpm_res_type,
+ r->rpm_clk_id, &req, 1);
+ if (ret)
+ return ret;
+
+ *r->last_sleep_set_vote = rate;
+
+ return ret;
}
static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate,
@@ -231,7 +266,7 @@ static int clk_smd_rpm_prepare(struct clk_hw *hw)
struct clk_smd_rpm *peer = r->peer;
unsigned long this_rate = 0, this_sleep_rate = 0;
unsigned long peer_rate = 0, peer_sleep_rate = 0;
- unsigned long active_rate, sleep_rate;
+ uint32_t active_rate, sleep_rate;
int ret = 0;
mutex_lock(&rpm_smd_clk_lock);
@@ -279,7 +314,7 @@ static void clk_smd_rpm_unprepare(struct clk_hw *hw)
struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
struct clk_smd_rpm *peer = r->peer;
unsigned long peer_rate = 0, peer_sleep_rate = 0;
- unsigned long active_rate, sleep_rate;
+ uint32_t active_rate, sleep_rate;
int ret;
mutex_lock(&rpm_smd_clk_lock);
@@ -313,7 +348,7 @@ static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
struct clk_smd_rpm *peer = r->peer;
- unsigned long active_rate, sleep_rate;
+ uint32_t active_rate, sleep_rate;
unsigned long this_rate = 0, this_sleep_rate = 0;
unsigned long peer_rate = 0, peer_sleep_rate = 0;
int ret = 0;
@@ -372,33 +407,55 @@ static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw,
return r->rate;
}
-static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm)
+static int clk_smd_rpm_enable_scaling(void)
{
- int ret;
- struct clk_smd_rpm_req req = {
+ int ret = 0;
+ uint32_t value = cpu_to_le32(1);
+ struct msm_rpm_kvp req = {
.key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE),
- .nbytes = cpu_to_le32(sizeof(u32)),
- .value = cpu_to_le32(1),
+ .data = (void *)&value,
+ .length = sizeof(value),
};
- ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE,
- QCOM_SMD_RPM_MISC_CLK,
- QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req));
+ ret = msm_rpm_send_message_noirq(QCOM_SMD_RPM_SLEEP_STATE,
+ QCOM_SMD_RPM_MISC_CLK,
+ QCOM_RPM_SCALING_ENABLE_ID, &req, 1);
if (ret) {
pr_err("RPM clock scaling (sleep set) not enabled!\n");
return ret;
}
- ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE,
- QCOM_SMD_RPM_MISC_CLK,
- QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req));
+ ret = msm_rpm_send_message_noirq(QCOM_SMD_RPM_ACTIVE_STATE,
+ QCOM_SMD_RPM_MISC_CLK,
+ QCOM_RPM_SCALING_ENABLE_ID, &req, 1);
if (ret) {
pr_err("RPM clock scaling (active set) not enabled!\n");
return ret;
}
pr_debug("%s: RPM clock scaling is enabled\n", __func__);
- return 0;
+ return ret;
+}
+
+static int clk_vote_bimc(struct clk_hw *hw, uint32_t rate)
+{
+ int ret = 0;
+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw);
+ struct msm_rpm_kvp req = {
+ .key = r->rpm_key,
+ .data = (void *)&rate,
+ .length = sizeof(rate),
+ };
+
+ ret = msm_rpm_send_message_noirq(QCOM_SMD_RPM_ACTIVE_STATE,
+ r->rpm_res_type, r->rpm_clk_id, &req, 1);
+ if (ret < 0) {
+ if (ret != -EPROBE_DEFER)
+ WARN(1, "BIMC vote not sent!\n");
+ return ret;
+ }
+
+ return ret;
}
static const struct clk_ops clk_smd_rpm_ops = {
@@ -430,40 +487,41 @@ DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2);
DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4);
DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5);
-static struct clk_smd_rpm *msm8916_clks[] = {
- [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk,
- [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk,
- [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk,
- [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk,
- [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk,
- [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk,
- [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk,
- [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk,
- [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1,
- [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a,
- [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2,
- [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a,
- [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1,
- [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a,
- [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2,
- [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a,
- [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin,
- [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin,
- [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin,
- [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin,
- [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin,
- [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin,
- [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin,
- [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin,
+static struct clk_hw *msm8916_clks[] = {
+ [RPM_PCNOC_CLK] = &msm8916_pcnoc_clk.hw,
+ [RPM_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk.hw,
+ [RPM_SNOC_CLK] = &msm8916_snoc_clk.hw,
+ [RPM_SNOC_A_CLK] = &msm8916_snoc_a_clk.hw,
+ [RPM_BIMC_CLK] = &msm8916_bimc_clk.hw,
+ [RPM_BIMC_A_CLK] = &msm8916_bimc_a_clk.hw,
+ [RPM_QDSS_CLK] = &msm8916_qdss_clk.hw,
+ [RPM_QDSS_A_CLK] = &msm8916_qdss_a_clk.hw,
+ [RPM_BB_CLK1] = &msm8916_bb_clk1.hw,
+ [RPM_BB_CLK1_A] = &msm8916_bb_clk1_a.hw,
+ [RPM_BB_CLK2] = &msm8916_bb_clk2.hw,
+ [RPM_BB_CLK2_A] = &msm8916_bb_clk2_a.hw,
+ [RPM_RF_CLK1] = &msm8916_rf_clk1.hw,
+ [RPM_RF_CLK1_A] = &msm8916_rf_clk1_a.hw,
+ [RPM_RF_CLK2] = &msm8916_rf_clk2.hw,
+ [RPM_RF_CLK2_A] = &msm8916_rf_clk2_a.hw,
+ [RPM_BB_CLK1_PIN] = &msm8916_bb_clk1_pin.hw,
+ [RPM_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin.hw,
+ [RPM_BB_CLK2_PIN] = &msm8916_bb_clk2_pin.hw,
+ [RPM_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin.hw,
+ [RPM_RF_CLK1_PIN] = &msm8916_rf_clk1_pin.hw,
+ [RPM_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin.hw,
+ [RPM_RF_CLK2_PIN] = &msm8916_rf_clk2_pin.hw,
+ [RPM_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin.hw,
};
static const struct rpm_smd_clk_desc rpm_clk_msm8916 = {
.clks = msm8916_clks,
+ .num_rpm_clks = RPM_RF_CLK2_A_PIN,
.num_clks = ARRAY_SIZE(msm8916_clks),
};
/* msm8996 */
-DEFINE_CLK_SMD_RPM(msm8996, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0);
+DEFINE_CLK_SMD_RPM(msm8996, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0);
DEFINE_CLK_SMD_RPM(msm8996, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1);
DEFINE_CLK_SMD_RPM(msm8996, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2);
DEFINE_CLK_SMD_RPM(msm8996, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0);
@@ -490,55 +548,131 @@ DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, bb_clk2_pin, bb_clk2_a_pin, 2);
DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, rf_clk1_pin, rf_clk1_a_pin, 4);
DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, rf_clk2_pin, rf_clk2_a_pin, 5);
-static struct clk_smd_rpm *msm8996_clks[] = {
- [RPM_SMD_XO_CLK_SRC] = &msm8996_cxo,
- [RPM_SMD_XO_A_CLK_SRC] = &msm8996_cxo_a,
- [RPM_AGGR1_NOC_CLK] = &msm8996_aggre1_noc_clk,
- [RPM_AGGR1_NOC_A_CLK] = &msm8996_aggre1_noc_a_clk,
- [RPM_AGGR2_NOC_CLK] = &msm8996_aggre2_noc_clk,
- [RPM_AGGR2_NOC_A_CLK] = &msm8996_aggre2_noc_a_clk,
- [RPM_SMD_PCNOC_CLK] = &msm8996_pcnoc_clk,
- [RPM_SMD_PCNOC_A_CLK] = &msm8996_pcnoc_a_clk,
- [RPM_SMD_SNOC_CLK] = &msm8996_snoc_clk,
- [RPM_SMD_SNOC_A_CLK] = &msm8996_snoc_a_clk,
- [RPM_CNOC_CLK] = &msm8996_cnoc_clk,
- [RPM_CNOC_A_CLK] = &msm8996_cnoc_a_clk,
- [RPM_SMD_BIMC_CLK] = &msm8996_bimc_clk,
- [RPM_SMD_BIMC_A_CLK] = &msm8996_bimc_a_clk,
- [RPM_MMAXI_CLK] = &msm8996_mmssnoc_axi_rpm_clk,
- [RPM_MMAXI_A_CLK] = &msm8996_mmssnoc_axi_rpm_a_clk,
- [RPM_IPA_CLK] = &msm8996_ipa_clk,
- [RPM_IPA_A_CLK] = &msm8996_ipa_a_clk,
- [RPM_CE1_CLK] = &msm8996_ce1_clk,
- [RPM_CE1_A_CLK] = &msm8996_ce1_a_clk,
- [RPM_SMD_QDSS_CLK] = &msm8996_qdss_clk,
- [RPM_SMD_QDSS_A_CLK] = &msm8996_qdss_a_clk,
- [RPM_LN_BB_CLK] = &msm8996_ln_bb_clk,
- [RPM_LN_BB_A_CLK] = &msm8996_ln_bb_a_clk,
- [RPM_DIV_CLK1] = &msm8996_div_clk1,
- [RPM_DIV_CLK1_AO] = &msm8996_div_clk1_ao,
- [RPM_DIV_CLK2] = &msm8996_div_clk2,
- [RPM_DIV_CLK2_AO] = &msm8996_div_clk2_ao,
- [RPM_DIV_CLK3] = &msm8996_div_clk3,
- [RPM_DIV_CLK3_AO] = &msm8996_div_clk3_ao,
- [RPM_BB_CLK1_PIN] = &msm8996_bb_clk1_pin,
- [RPM_BB_CLK1_A_PIN] = &msm8996_bb_clk1_a_pin,
- [RPM_BB_CLK2_PIN] = &msm8996_bb_clk2_pin,
- [RPM_BB_CLK2_A_PIN] = &msm8996_bb_clk2_a_pin,
- [RPM_RF_CLK1_PIN] = &msm8996_rf_clk1_pin,
- [RPM_RF_CLK1_A_PIN] = &msm8996_rf_clk1_a_pin,
- [RPM_RF_CLK2_PIN] = &msm8996_rf_clk2_pin,
- [RPM_RF_CLK2_A_PIN] = &msm8996_rf_clk2_a_pin,
+static struct clk_hw *msm8996_clks[] = {
+ [RPM_XO_CLK_SRC] = &msm8996_cxo.hw,
+ [RPM_XO_A_CLK_SRC] = &msm8996_cxo_a.hw,
+ [RPM_PCNOC_CLK] = &msm8996_pnoc_clk.hw,
+ [RPM_PCNOC_A_CLK] = &msm8996_pnoc_a_clk.hw,
+ [RPM_SNOC_CLK] = &msm8996_snoc_clk.hw,
+ [RPM_SNOC_A_CLK] = &msm8996_snoc_a_clk.hw,
+ [RPM_BIMC_CLK] = &msm8996_bimc_clk.hw,
+ [RPM_BIMC_A_CLK] = &msm8996_bimc_a_clk.hw,
+ [RPM_QDSS_CLK] = &msm8996_qdss_clk.hw,
+ [RPM_QDSS_A_CLK] = &msm8996_qdss_a_clk.hw,
+ [RPM_BB_CLK1_PIN] = &msm8996_bb_clk1_pin.hw,
+ [RPM_BB_CLK1_A_PIN] = &msm8996_bb_clk1_a_pin.hw,
+ [RPM_BB_CLK2_PIN] = &msm8996_bb_clk2_pin.hw,
+ [RPM_BB_CLK2_A_PIN] = &msm8996_bb_clk2_a_pin.hw,
+ [RPM_RF_CLK1_PIN] = &msm8996_rf_clk1_pin.hw,
+ [RPM_RF_CLK1_A_PIN] = &msm8996_rf_clk1_a_pin.hw,
+ [RPM_RF_CLK2_PIN] = &msm8996_rf_clk2_pin.hw,
+ [RPM_RF_CLK2_A_PIN] = &msm8996_rf_clk2_a_pin.hw,
+ [RPM_AGGR1_NOC_CLK] = &msm8996_aggre1_noc_clk.hw,
+ [RPM_AGGR1_NOC_A_CLK] = &msm8996_aggre1_noc_a_clk.hw,
+ [RPM_AGGR2_NOC_CLK] = &msm8996_aggre2_noc_clk.hw,
+ [RPM_AGGR2_NOC_A_CLK] = &msm8996_aggre2_noc_a_clk.hw,
+ [RPM_CNOC_CLK] = &msm8996_cnoc_clk.hw,
+ [RPM_CNOC_A_CLK] = &msm8996_cnoc_a_clk.hw,
+ [RPM_MMAXI_CLK] = &msm8996_mmssnoc_axi_rpm_clk.hw,
+ [RPM_MMAXI_A_CLK] = &msm8996_mmssnoc_axi_rpm_a_clk.hw,
+ [RPM_IPA_CLK] = &msm8996_ipa_clk.hw,
+ [RPM_IPA_A_CLK] = &msm8996_ipa_a_clk.hw,
+ [RPM_CE1_CLK] = &msm8996_ce1_clk.hw,
+ [RPM_CE1_A_CLK] = &msm8996_ce1_a_clk.hw,
+ [RPM_DIV_CLK1] = &msm8996_div_clk1.hw,
+ [RPM_DIV_CLK1_AO] = &msm8996_div_clk1_ao.hw,
+ [RPM_DIV_CLK2] = &msm8996_div_clk2.hw,
+ [RPM_DIV_CLK2_AO] = &msm8996_div_clk2_ao.hw,
+ [RPM_DIV_CLK3] = &msm8996_div_clk3.hw,
+ [RPM_DIV_CLK3_AO] = &msm8996_div_clk3_ao.hw,
+ [RPM_LN_BB_CLK] = &msm8996_ln_bb_clk.hw,
+ [RPM_LN_BB_A_CLK] = &msm8996_ln_bb_a_clk.hw,
};
static const struct rpm_smd_clk_desc rpm_clk_msm8996 = {
.clks = msm8996_clks,
+ .num_rpm_clks = RPM_LN_BB_A_CLK,
.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);
@@ -549,23 +683,30 @@ static int rpm_smd_clk_probe(struct platform_device *pdev)
struct clk *clk;
struct rpm_cc *rcc;
struct clk_onecell_data *data;
- int ret;
+ int ret, is_8996 = 0, is_falcon = 0;
size_t num_clks, i;
- struct qcom_smd_rpm *rpm;
- struct clk_smd_rpm **rpm_smd_clks;
+ struct clk_hw **hw_clks;
const struct rpm_smd_clk_desc *desc;
- rpm = dev_get_drvdata(pdev->dev.parent);
- if (!rpm) {
- dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
- return -ENODEV;
+ 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);
if (!desc)
return -EINVAL;
- rpm_smd_clks = desc->clks;
+ hw_clks = desc->clks;
num_clks = desc->num_clks;
rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*clks) * num_clks,
@@ -578,30 +719,28 @@ static int rpm_smd_clk_probe(struct platform_device *pdev)
data->clks = clks;
data->clk_num = num_clks;
- for (i = 0; i < num_clks; i++) {
- if (!rpm_smd_clks[i]) {
+ for (i = 0; i <= desc->num_rpm_clks; i++) {
+ if (!hw_clks[i]) {
clks[i] = ERR_PTR(-ENOENT);
continue;
}
- rpm_smd_clks[i]->rpm = rpm;
-
- ret = clk_smd_rpm_handoff(rpm_smd_clks[i]);
+ ret = clk_smd_rpm_handoff(hw_clks[i]);
if (ret)
goto err;
}
- ret = clk_smd_rpm_enable_scaling(rpm);
+ ret = clk_smd_rpm_enable_scaling();
if (ret)
goto err;
for (i = 0; i < num_clks; i++) {
- if (!rpm_smd_clks[i]) {
+ if (!hw_clks[i]) {
clks[i] = ERR_PTR(-ENOENT);
continue;
}
- clk = devm_clk_register(&pdev->dev, &rpm_smd_clks[i]->hw);
+ clk = devm_clk_register(&pdev->dev, hw_clks[i]);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto err;
@@ -615,6 +754,14 @@ static int rpm_smd_clk_probe(struct platform_device *pdev)
if (ret)
goto err;
+ /* 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");
+
return 0;
err:
dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret);
diff --git a/drivers/clk/qcom/gcc-msmfalcon.c b/drivers/clk/qcom/gcc-msmfalcon.c
new file mode 100644
index 000000000000..d353cc9ade73
--- /dev/null
+++ b/drivers/clk/qcom/gcc-msmfalcon.c
@@ -0,0 +1,2800 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <dt-bindings/clock/qcom,gcc-msmfalcon.h>
+
+#include "clk-alpha-pll.h"
+#include "clk-branch.h"
+#include "common.h"
+#include "clk-pll.h"
+#include "clk-regmap.h"
+#include "clk-rcg.h"
+#include "reset.h"
+#include "vdd-level-falcon.h"
+
+#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
+
+static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
+static DEFINE_VDD_REGULATORS(vdd_dig_ao, VDD_DIG_NUM, 1, vdd_corner, NULL);
+
+enum {
+ P_CORE_BI_PLL_TEST_SE,
+ P_GPLL0_OUT_MAIN,
+ P_GPLL1_OUT_MAIN,
+ P_GPLL4_OUT_MAIN,
+ P_PLL0_EARLY_DIV_CLK_SRC,
+ P_PLL1_EARLY_DIV_CLK_SRC,
+ P_SLEEP_CLK,
+ P_XO,
+};
+
+static const struct parent_map gcc_parent_map_0[] = {
+ { P_XO, 0 },
+ { P_GPLL0_OUT_MAIN, 1 },
+ { P_PLL0_EARLY_DIV_CLK_SRC, 6 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_0[] = {
+ "xo",
+ "gpll0_out_main",
+ "gpll0_out_early_div",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_1[] = {
+ { P_XO, 0 },
+ { P_GPLL0_OUT_MAIN, 1 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_1[] = {
+ "xo",
+ "gpll0_out_main",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_2[] = {
+ { P_XO, 0 },
+ { P_GPLL0_OUT_MAIN, 1 },
+ { P_SLEEP_CLK, 5 },
+ { P_PLL0_EARLY_DIV_CLK_SRC, 6 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_2[] = {
+ "xo",
+ "gpll0_out_main",
+ "core_pi_sleep_clk",
+ "gpll0_out_early_div",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_3[] = {
+ { P_XO, 0 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_3[] = {
+ "xo",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_4[] = {
+ { P_XO, 0 },
+ { P_SLEEP_CLK, 5 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_4[] = {
+ "xo",
+ "core_pi_sleep_clk",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_5[] = {
+ { P_XO, 0 },
+ { P_GPLL4_OUT_MAIN, 5 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_5[] = {
+ "xo",
+ "gpll4_out_main",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_6[] = {
+ { P_XO, 0 },
+ { P_GPLL0_OUT_MAIN, 1 },
+ { P_PLL0_EARLY_DIV_CLK_SRC, 3 },
+ { P_GPLL1_OUT_MAIN, 4 },
+ { P_GPLL4_OUT_MAIN, 5 },
+ { P_PLL1_EARLY_DIV_CLK_SRC, 6 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_6[] = {
+ "xo",
+ "gpll0_out_main",
+ "gpll0_out_early_div",
+ "gpll1_out_main",
+ "gpll4_out_main",
+ "gpll1_out_early_div",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_7[] = {
+ { P_XO, 0 },
+ { P_GPLL0_OUT_MAIN, 1 },
+ { P_GPLL4_OUT_MAIN, 5 },
+ { P_PLL0_EARLY_DIV_CLK_SRC, 6 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_7[] = {
+ "xo",
+ "gpll0_out_main",
+ "gpll4_out_main",
+ "gpll0_out_early_div",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_8[] = {
+ { P_XO, 0 },
+ { P_GPLL0_OUT_MAIN, 1 },
+ { P_PLL0_EARLY_DIV_CLK_SRC, 2 },
+ { P_GPLL4_OUT_MAIN, 5 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_8[] = {
+ "xo",
+ "gpll0_out_main",
+ "gpll0_out_early_div",
+ "gpll4_out_main",
+ "core_bi_pll_test_se",
+};
+
+static struct clk_fixed_factor xo = {
+ .mult = 1,
+ .div = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "xo",
+ .parent_names = (const char *[]){ "cxo" },
+ .num_parents = 1,
+ .ops = &clk_fixed_factor_ops,
+ },
+};
+
+static struct clk_alpha_pll gpll0_out_main = {
+ .offset = 0x0,
+ .clkr = {
+ .enable_reg = 0x52000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpll0_out_main",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_ops,
+ },
+ },
+};
+
+static struct clk_fixed_factor gpll0_out_early_div = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "gpll0_out_early_div",
+ .parent_names = (const char *[]){ "gpll0_out_main" },
+ .num_parents = 1,
+ .ops = &clk_fixed_factor_ops,
+ },
+};
+
+static struct clk_alpha_pll gpll1_out_main = {
+ .offset = 0x1000,
+ .clkr = {
+ .enable_reg = 0x52000,
+ .enable_mask = BIT(1),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpll1_out_main",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_ops,
+ },
+ },
+};
+
+static struct clk_fixed_factor gpll1_out_early_div = {
+ .mult = 1,
+ .div = 2,
+ .hw.init = &(struct clk_init_data){
+ .name = "gpll1_out_early_div",
+ .parent_names = (const char *[]){ "gpll1_out_main" },
+ .num_parents = 1,
+ .ops = &clk_fixed_factor_ops,
+ },
+};
+
+static struct clk_alpha_pll gpll4_out_main = {
+ .offset = 0x77000,
+ .clkr = {
+ .enable_reg = 0x52000,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpll4_out_main",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_ops,
+ },
+ },
+};
+
+static const struct freq_tbl ftbl_blsp1_qup1_i2c_apps_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 blsp1_qup1_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x19020,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_blsp1_qup1_i2c_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup1_i2c_apps_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ LOW, 50000000),
+ },
+};
+
+static const struct freq_tbl ftbl_blsp1_qup1_spi_apps_clk_src[] = {
+ F(960000, P_XO, 10, 1, 2),
+ F(4800000, P_XO, 4, 0, 0),
+ F(9600000, P_XO, 2, 0, 0),
+ F(15000000, P_GPLL0_OUT_MAIN, 10, 1, 4),
+ F(19200000, P_XO, 1, 0, 0),
+ F(25000000, P_GPLL0_OUT_MAIN, 12, 1, 2),
+ F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 blsp1_qup1_spi_apps_clk_src = {
+ .cmd_rcgr = 0x1900c,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_qup1_spi_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup1_spi_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 25000000,
+ NOMINAL, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp1_qup2_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x1b020,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_blsp1_qup1_i2c_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup2_i2c_apps_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ LOW, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp1_qup2_spi_apps_clk_src = {
+ .cmd_rcgr = 0x1b00c,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_qup1_spi_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup2_spi_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 25000000,
+ NOMINAL, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp1_qup3_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x1d020,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_blsp1_qup1_i2c_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup3_i2c_apps_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ LOW, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp1_qup3_spi_apps_clk_src = {
+ .cmd_rcgr = 0x1d00c,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_qup1_spi_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup3_spi_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 25000000,
+ NOMINAL, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp1_qup4_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x1f020,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_blsp1_qup1_i2c_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup4_i2c_apps_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ LOW, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp1_qup4_spi_apps_clk_src = {
+ .cmd_rcgr = 0x1f00c,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_qup1_spi_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup4_spi_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 25000000,
+ NOMINAL, 50000000),
+ },
+};
+
+static const struct freq_tbl ftbl_blsp1_uart1_apps_clk_src[] = {
+ F(3686400, P_GPLL0_OUT_MAIN, 1, 96, 15625),
+ F(7372800, P_GPLL0_OUT_MAIN, 1, 192, 15625),
+ F(14745600, P_GPLL0_OUT_MAIN, 1, 384, 15625),
+ F(16000000, P_GPLL0_OUT_MAIN, 5, 2, 15),
+ F(19200000, P_XO, 1, 0, 0),
+ F(24000000, P_GPLL0_OUT_MAIN, 5, 1, 5),
+ F(32000000, P_GPLL0_OUT_MAIN, 1, 4, 75),
+ F(40000000, P_GPLL0_OUT_MAIN, 15, 0, 0),
+ F(46400000, P_GPLL0_OUT_MAIN, 1, 29, 375),
+ F(48000000, P_GPLL0_OUT_MAIN, 12.5, 0, 0),
+ F(51200000, P_GPLL0_OUT_MAIN, 1, 32, 375),
+ F(56000000, P_GPLL0_OUT_MAIN, 1, 7, 75),
+ F(58982400, P_GPLL0_OUT_MAIN, 1, 1536, 15625),
+ F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0),
+ F(63157895, P_GPLL0_OUT_MAIN, 9.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 blsp1_uart1_apps_clk_src = {
+ .cmd_rcgr = 0x1a00c,
+ .mnd_width = 16,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_uart1_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_uart1_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 31578947,
+ NOMINAL, 63157895),
+ },
+};
+
+static struct clk_rcg2 blsp1_uart2_apps_clk_src = {
+ .cmd_rcgr = 0x1c00c,
+ .mnd_width = 16,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_uart1_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_uart2_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 31578947,
+ NOMINAL, 63157895),
+ },
+};
+
+static struct clk_rcg2 blsp2_qup1_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x26020,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_blsp1_qup1_i2c_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp2_qup1_i2c_apps_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ LOW, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp2_qup1_spi_apps_clk_src = {
+ .cmd_rcgr = 0x2600c,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_qup1_spi_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp2_qup1_spi_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 25000000,
+ NOMINAL, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp2_qup2_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x28020,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_blsp1_qup1_i2c_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp2_qup2_i2c_apps_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ LOW, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp2_qup2_spi_apps_clk_src = {
+ .cmd_rcgr = 0x2800c,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_qup1_spi_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp2_qup2_spi_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 25000000,
+ NOMINAL, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp2_qup3_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x2a020,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_blsp1_qup1_i2c_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp2_qup3_i2c_apps_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ LOW, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp2_qup3_spi_apps_clk_src = {
+ .cmd_rcgr = 0x2a00c,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_qup1_spi_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp2_qup3_spi_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 25000000,
+ NOMINAL, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp2_qup4_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x2c020,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_blsp1_qup1_i2c_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp2_qup4_i2c_apps_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ LOW, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp2_qup4_spi_apps_clk_src = {
+ .cmd_rcgr = 0x2c00c,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_qup1_spi_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp2_qup4_spi_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 25000000,
+ NOMINAL, 50000000),
+ },
+};
+
+static struct clk_rcg2 blsp2_uart1_apps_clk_src = {
+ .cmd_rcgr = 0x2700c,
+ .mnd_width = 16,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_uart1_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp2_uart1_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 31578947,
+ NOMINAL, 63157895),
+ },
+};
+
+static struct clk_rcg2 blsp2_uart2_apps_clk_src = {
+ .cmd_rcgr = 0x2900c,
+ .mnd_width = 16,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_blsp1_uart1_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp2_uart2_apps_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 31578947,
+ NOMINAL, 63157895),
+ },
+};
+
+static const struct freq_tbl ftbl_gp1_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+ F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 gp1_clk_src = {
+ .cmd_rcgr = 0x64004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_2,
+ .freq_tbl = ftbl_gp1_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gp1_clk_src",
+ .parent_names = gcc_parent_names_2,
+ .num_parents = 5,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 50000000,
+ LOW, 100000000,
+ NOMINAL, 200000000),
+ },
+};
+
+static struct clk_rcg2 gp2_clk_src = {
+ .cmd_rcgr = 0x65004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_2,
+ .freq_tbl = ftbl_gp1_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gp2_clk_src",
+ .parent_names = gcc_parent_names_2,
+ .num_parents = 5,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 50000000,
+ LOW, 100000000,
+ NOMINAL, 200000000),
+ },
+};
+
+static struct clk_rcg2 gp3_clk_src = {
+ .cmd_rcgr = 0x66004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_2,
+ .freq_tbl = ftbl_gp1_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gp3_clk_src",
+ .parent_names = gcc_parent_names_2,
+ .num_parents = 5,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 50000000,
+ LOW, 100000000,
+ NOMINAL, 200000000),
+ },
+};
+
+static const struct freq_tbl ftbl_hmss_ahb_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(37500000, P_GPLL0_OUT_MAIN, 16, 0, 0),
+ F(75000000, P_GPLL0_OUT_MAIN, 8, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 hmss_ahb_clk_src = {
+ .cmd_rcgr = 0x48014,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_hmss_ahb_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "hmss_ahb_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3_AO(
+ LOWER, 19200000,
+ LOW, 50000000,
+ NOMINAL, 100000000),
+ },
+};
+
+static const struct freq_tbl ftbl_hmss_gpll0_clk_src[] = {
+ F(600000000, P_GPLL0_OUT_MAIN, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 hmss_gpll0_clk_src = {
+ .cmd_rcgr = 0x4805c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_hmss_gpll0_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "hmss_gpll0_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP1_AO(
+ LOWER, 600000000),
+ },
+};
+
+static const struct freq_tbl ftbl_hmss_gpll4_clk_src[] = {
+ F(384000000, P_GPLL4_OUT_MAIN, 4, 0, 0),
+ F(768000000, P_GPLL4_OUT_MAIN, 2, 0, 0),
+ F(1536000000, P_GPLL4_OUT_MAIN, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 hmss_gpll4_clk_src = {
+ .cmd_rcgr = 0x48074,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_5,
+ .freq_tbl = ftbl_hmss_gpll4_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "hmss_gpll4_clk_src",
+ .parent_names = gcc_parent_names_5,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3_AO(
+ LOWER, 400000000,
+ LOW, 800000000,
+ NOMINAL, 1600000000),
+ },
+};
+
+static const struct freq_tbl ftbl_hmss_rbcpr_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 hmss_rbcpr_clk_src = {
+ .cmd_rcgr = 0x48044,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_hmss_rbcpr_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "hmss_rbcpr_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ NOMINAL, 50000000),
+ },
+};
+
+static const struct freq_tbl ftbl_pdm2_clk_src[] = {
+ F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 pdm2_clk_src = {
+ .cmd_rcgr = 0x33010,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_1,
+ .freq_tbl = ftbl_pdm2_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pdm2_clk_src",
+ .parent_names = gcc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ LOW, 60000000),
+ },
+};
+
+static const struct freq_tbl ftbl_qspi_ser_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(80200000, P_PLL1_EARLY_DIV_CLK_SRC, 5, 0, 0),
+ F(160400000, P_GPLL1_OUT_MAIN, 5, 0, 0),
+ F(320800000, P_GPLL1_OUT_MAIN, 2.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 qspi_ser_clk_src = {
+ .cmd_rcgr = 0x4d00c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_6,
+ .freq_tbl = ftbl_qspi_ser_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "qspi_ser_clk_src",
+ .parent_names = gcc_parent_names_6,
+ .num_parents = 7,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 80200000,
+ LOW, 160400000,
+ NOMINAL, 320800000),
+ },
+};
+
+static const struct freq_tbl ftbl_sdcc1_apps_clk_src[] = {
+ F(144000, P_XO, 16, 3, 25),
+ F(400000, P_XO, 12, 1, 4),
+ F(20000000, P_PLL0_EARLY_DIV_CLK_SRC, 5, 1, 3),
+ F(25000000, P_PLL0_EARLY_DIV_CLK_SRC, 6, 1, 2),
+ F(50000000, P_PLL0_EARLY_DIV_CLK_SRC, 6, 0, 0),
+ F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+ F(192000000, P_GPLL4_OUT_MAIN, 8, 0, 0),
+ F(384000000, P_GPLL4_OUT_MAIN, 4, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 sdcc1_apps_clk_src = {
+ .cmd_rcgr = 0x1602c,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_7,
+ .freq_tbl = ftbl_sdcc1_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "sdcc1_apps_clk_src",
+ .parent_names = gcc_parent_names_7,
+ .num_parents = 5,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 50000000,
+ LOW, 100000000,
+ NOMINAL, 400000000),
+ },
+};
+
+static const struct freq_tbl ftbl_sdcc1_ice_core_clk_src[] = {
+ F(75000000, P_PLL0_EARLY_DIV_CLK_SRC, 4, 0, 0),
+ F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0),
+ F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 sdcc1_ice_core_clk_src = {
+ .cmd_rcgr = 0x16010,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_sdcc1_ice_core_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "sdcc1_ice_core_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 75000000,
+ LOW, 150000000,
+ NOMINAL, 300000000),
+ },
+};
+
+static const struct freq_tbl ftbl_sdcc2_apps_clk_src[] = {
+ F(144000, P_XO, 16, 3, 25),
+ F(400000, P_XO, 12, 1, 4),
+ F(20000000, P_PLL0_EARLY_DIV_CLK_SRC, 5, 1, 3),
+ F(25000000, P_PLL0_EARLY_DIV_CLK_SRC, 6, 1, 2),
+ F(50000000, P_PLL0_EARLY_DIV_CLK_SRC, 6, 0, 0),
+ F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+ F(192000000, P_GPLL4_OUT_MAIN, 8, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 sdcc2_apps_clk_src = {
+ .cmd_rcgr = 0x14010,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_8,
+ .freq_tbl = ftbl_sdcc2_apps_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "sdcc2_apps_clk_src",
+ .parent_names = gcc_parent_names_8,
+ .num_parents = 5,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 50000000,
+ LOW, 100000000,
+ NOMINAL, 200000000),
+ },
+};
+
+static const struct freq_tbl ftbl_ufs_axi_clk_src[] = {
+ F(50000000, P_PLL0_EARLY_DIV_CLK_SRC, 6, 0, 0),
+ F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+ F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+ F(240000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 ufs_axi_clk_src = {
+ .cmd_rcgr = 0x75018,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_ufs_axi_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "ufs_axi_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP5(
+ LOWER, 50000000,
+ LOW, 100000000,
+ LOW_L1, 150000000,
+ NOMINAL, 200000000,
+ HIGH, 240000000),
+ },
+};
+
+static const struct freq_tbl ftbl_ufs_ice_core_clk_src[] = {
+ F(75000000, P_PLL0_EARLY_DIV_CLK_SRC, 4, 0, 0),
+ F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0),
+ F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 ufs_ice_core_clk_src = {
+ .cmd_rcgr = 0x76010,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_ufs_ice_core_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "ufs_ice_core_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 75000000,
+ LOW, 150000000,
+ NOMINAL, 300000000),
+ },
+};
+
+static struct clk_rcg2 ufs_phy_aux_clk_src = {
+ .cmd_rcgr = 0x76044,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_3,
+ .freq_tbl = ftbl_hmss_rbcpr_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "ufs_phy_aux_clk_src",
+ .parent_names = gcc_parent_names_3,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP1(
+ LOWER, 19200000),
+ },
+};
+
+static const struct freq_tbl ftbl_ufs_unipro_core_clk_src[] = {
+ F(37500000, P_PLL0_EARLY_DIV_CLK_SRC, 8, 0, 0),
+ F(75000000, P_GPLL0_OUT_MAIN, 8, 0, 0),
+ F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 ufs_unipro_core_clk_src = {
+ .cmd_rcgr = 0x76028,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_ufs_unipro_core_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "ufs_unipro_core_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 37500000,
+ LOW, 75000000,
+ NOMINAL, 150000000),
+ },
+};
+
+static const struct freq_tbl ftbl_usb20_master_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0),
+ F(120000000, P_GPLL0_OUT_MAIN, 5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 usb20_master_clk_src = {
+ .cmd_rcgr = 0x2f010,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_usb20_master_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "usb20_master_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP3(
+ LOWER, 19200000,
+ LOW, 60000000,
+ NOMINAL, 120000000),
+ },
+};
+
+static struct clk_rcg2 usb20_mock_utmi_clk_src = {
+ .cmd_rcgr = 0x2f024,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_hmss_rbcpr_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "usb20_mock_utmi_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 19200000,
+ LOW, 60000000),
+ },
+};
+
+static const struct freq_tbl ftbl_usb30_master_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(66666667, P_PLL0_EARLY_DIV_CLK_SRC, 4.5, 0, 0),
+ F(120000000, P_GPLL0_OUT_MAIN, 5, 0, 0),
+ F(133333333, P_GPLL0_OUT_MAIN, 4.5, 0, 0),
+ F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0),
+ F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+ F(240000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 usb30_master_clk_src = {
+ .cmd_rcgr = 0xf014,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_usb30_master_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "usb30_master_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP4(
+ LOWER, 66666667,
+ LOW, 133333333,
+ NOMINAL, 200000000,
+ HIGH, 240000000),
+ },
+};
+
+static const struct freq_tbl ftbl_usb30_mock_utmi_clk_src[] = {
+ F(40000000, P_PLL0_EARLY_DIV_CLK_SRC, 7.5, 0, 0),
+ F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 usb30_mock_utmi_clk_src = {
+ .cmd_rcgr = 0xf028,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_0,
+ .freq_tbl = ftbl_usb30_mock_utmi_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "usb30_mock_utmi_clk_src",
+ .parent_names = gcc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ LOWER, 40000000,
+ LOW, 60000000),
+ },
+};
+
+static const struct freq_tbl ftbl_usb3_phy_aux_clk_src[] = {
+ F(1200000, P_XO, 16, 0, 0),
+ F(19200000, P_XO, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 usb3_phy_aux_clk_src = {
+ .cmd_rcgr = 0x5000c,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gcc_parent_map_4,
+ .freq_tbl = ftbl_usb3_phy_aux_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "usb3_phy_aux_clk_src",
+ .parent_names = gcc_parent_names_4,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP1(
+ LOWER, 19200000),
+ },
+};
+
+static struct clk_branch gcc_aggre2_ufs_axi_clk = {
+ .halt_reg = 0x75034,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x75034,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_aggre2_ufs_axi_clk",
+ .parent_names = (const char *[]){
+ "ufs_axi_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_aggre2_usb3_axi_clk = {
+ .halt_reg = 0xf03c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xf03c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_aggre2_usb3_axi_clk",
+ .parent_names = (const char *[]){
+ "usb30_master_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_bimc_gfx_clk = {
+ .halt_reg = 0x7106c,
+ .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE,
+ .clkr = {
+ .enable_reg = 0x7106c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_bimc_gfx_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_bimc_hmss_axi_clk = {
+ .halt_reg = 0x48004,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x52004,
+ .enable_mask = BIT(22),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_bimc_hmss_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_bimc_mss_q6_axi_clk = {
+ .halt_reg = 0x4401c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x4401c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_bimc_mss_q6_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_ahb_clk = {
+ .halt_reg = 0x17004,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x52004,
+ .enable_mask = BIT(17),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup1_i2c_apps_clk = {
+ .halt_reg = 0x19008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x19008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup1_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup1_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup1_spi_apps_clk = {
+ .halt_reg = 0x19004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x19004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup1_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup1_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup2_i2c_apps_clk = {
+ .halt_reg = 0x1b008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1b008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup2_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup2_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup2_spi_apps_clk = {
+ .halt_reg = 0x1b004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1b004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup2_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup2_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup3_i2c_apps_clk = {
+ .halt_reg = 0x1d008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1d008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup3_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup3_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup3_spi_apps_clk = {
+ .halt_reg = 0x1d004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1d004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup3_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup3_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup4_i2c_apps_clk = {
+ .halt_reg = 0x1f008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1f008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup4_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup4_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup4_spi_apps_clk = {
+ .halt_reg = 0x1f004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1f004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup4_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup4_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_uart1_apps_clk = {
+ .halt_reg = 0x1a004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1a004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_uart1_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_uart1_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_uart2_apps_clk = {
+ .halt_reg = 0x1c004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1c004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_uart2_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_uart2_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_ahb_clk = {
+ .halt_reg = 0x25004,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x52004,
+ .enable_mask = BIT(15),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_qup1_i2c_apps_clk = {
+ .halt_reg = 0x26008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x26008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_qup1_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp2_qup1_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_qup1_spi_apps_clk = {
+ .halt_reg = 0x26004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x26004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_qup1_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp2_qup1_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_qup2_i2c_apps_clk = {
+ .halt_reg = 0x28008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x28008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_qup2_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp2_qup2_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_qup2_spi_apps_clk = {
+ .halt_reg = 0x28004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x28004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_qup2_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp2_qup2_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_qup3_i2c_apps_clk = {
+ .halt_reg = 0x2a008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2a008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_qup3_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp2_qup3_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_qup3_spi_apps_clk = {
+ .halt_reg = 0x2a004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2a004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_qup3_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp2_qup3_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_qup4_i2c_apps_clk = {
+ .halt_reg = 0x2c008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2c008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_qup4_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp2_qup4_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_qup4_spi_apps_clk = {
+ .halt_reg = 0x2c004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2c004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_qup4_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp2_qup4_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_uart1_apps_clk = {
+ .halt_reg = 0x27004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x27004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_uart1_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp2_uart1_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp2_uart2_apps_clk = {
+ .halt_reg = 0x29004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x29004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp2_uart2_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp2_uart2_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_boot_rom_ahb_clk = {
+ .halt_reg = 0x38004,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x52004,
+ .enable_mask = BIT(10),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_boot_rom_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_cfg_noc_usb2_axi_clk = {
+ .halt_reg = 0x5058,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x5058,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_cfg_noc_usb2_axi_clk",
+ .parent_names = (const char *[]){
+ "usb20_master_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_cfg_noc_usb3_axi_clk = {
+ .halt_reg = 0x5018,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x5018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_cfg_noc_usb3_axi_clk",
+ .parent_names = (const char *[]){
+ "usb30_master_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_dcc_ahb_clk = {
+ .halt_reg = 0x84004,
+ .clkr = {
+ .enable_reg = 0x84004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_dcc_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gp1_clk = {
+ .halt_reg = 0x64000,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x64000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gp1_clk",
+ .parent_names = (const char *[]){
+ "gp1_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gp2_clk = {
+ .halt_reg = 0x65000,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x65000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gp2_clk",
+ .parent_names = (const char *[]){
+ "gp2_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gp3_clk = {
+ .halt_reg = 0x66000,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x66000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gp3_clk",
+ .parent_names = (const char *[]){
+ "gp3_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gpu_bimc_gfx_clk = {
+ .halt_reg = 0x71010,
+ .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE,
+ .clkr = {
+ .enable_reg = 0x71010,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gpu_bimc_gfx_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gpu_bimc_gfx_src_clk = {
+ .halt_reg = 0x7100c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x7100c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gpu_bimc_gfx_src_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gpu_cfg_ahb_clk = {
+ .halt_reg = 0x71004,
+ .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE,
+ .clkr = {
+ .enable_reg = 0x71004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gpu_cfg_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_gate2 gpll0_out_msscc = {
+ .udelay = 1,
+ .clkr = {
+ .enable_reg = 0x5200c,
+ .enable_mask = BIT(2),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpll0_out_msscc",
+ .ops = &clk_gate2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gpu_gpll0_clk = {
+ .halt_reg = 0x5200c,
+ .halt_check = BRANCH_HALT_DELAY,
+ .clkr = {
+ .enable_reg = 0x5200c,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gpu_gpll0_clk",
+ .parent_names = (const char *[]){
+ "gpll0_out_main",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gpu_gpll0_div_clk = {
+ .halt_reg = 0x5200c,
+ .halt_check = BRANCH_HALT_DELAY,
+ .clkr = {
+ .enable_reg = 0x5200c,
+ .enable_mask = BIT(3),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gpu_gpll0_div_clk",
+ .parent_names = (const char *[]){
+ "gpll0_out_early_div",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gpu_snoc_dvm_gfx_clk = {
+ .halt_reg = 0x71018,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x71018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gpu_snoc_dvm_gfx_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_hmss_ahb_clk = {
+ .halt_reg = 0x48000,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x52004,
+ .enable_mask = BIT(21),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_hmss_ahb_clk",
+ .parent_names = (const char *[]){
+ "hmss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_hmss_dvm_bus_clk = {
+ .halt_reg = 0x4808c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x4808c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_hmss_dvm_bus_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_hmss_rbcpr_clk = {
+ .halt_reg = 0x48008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x48008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_hmss_rbcpr_clk",
+ .parent_names = (const char *[]){
+ "hmss_rbcpr_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mmss_gpll0_clk = {
+ .halt_reg = 0x5200c,
+ .halt_check = BRANCH_HALT_DELAY,
+ .clkr = {
+ .enable_reg = 0x5200c,
+ .enable_mask = BIT(1),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mmss_gpll0_clk",
+ .parent_names = (const char *[]){
+ "gpll0_out_main",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mmss_gpll0_div_clk = {
+ .halt_reg = 0x5200c,
+ .halt_check = BRANCH_HALT_DELAY,
+ .clkr = {
+ .enable_reg = 0x5200c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mmss_gpll0_div_clk",
+ .parent_names = (const char *[]){
+ "gpll0_out_early_div",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mmss_noc_cfg_ahb_clk = {
+ .halt_reg = 0x9004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x9004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mmss_noc_cfg_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mmss_sys_noc_axi_clk = {
+ .halt_reg = 0x9000,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x9000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mmss_sys_noc_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mss_cfg_ahb_clk = {
+ .halt_reg = 0x8a000,
+ .clkr = {
+ .enable_reg = 0x8a000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mss_cfg_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mss_mnoc_bimc_axi_clk = {
+ .halt_reg = 0x8a004,
+ .clkr = {
+ .enable_reg = 0x8a004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mss_mnoc_bimc_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mss_q6_bimc_axi_clk = {
+ .halt_reg = 0x8a040,
+ .clkr = {
+ .enable_reg = 0x8a040,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mss_q6_bimc_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mss_snoc_axi_clk = {
+ .halt_reg = 0x8a03c,
+ .clkr = {
+ .enable_reg = 0x8a03c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mss_snoc_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_pdm2_clk = {
+ .halt_reg = 0x3300c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x3300c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_pdm2_clk",
+ .parent_names = (const char *[]){
+ "pdm2_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_pdm_ahb_clk = {
+ .halt_reg = 0x33004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x33004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_pdm_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_prng_ahb_clk = {
+ .halt_reg = 0x34004,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x52004,
+ .enable_mask = BIT(13),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_prng_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_qspi_ahb_clk = {
+ .halt_reg = 0x4d004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x4d004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_qspi_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_qspi_ser_clk = {
+ .halt_reg = 0x4d008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x4d008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_qspi_ser_clk",
+ .parent_names = (const char *[]){
+ "qspi_ser_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_rx0_usb2_clkref_clk = {
+ .halt_reg = 0x88018,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x88018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_rx0_usb2_clkref_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_rx1_usb2_clkref_clk = {
+ .halt_reg = 0x88014,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x88014,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_rx1_usb2_clkref_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_rx2_qlink_clkref_clk = {
+ .halt_reg = 0x88034,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x88034,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_rx2_qlink_clkref_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc1_ahb_clk = {
+ .halt_reg = 0x16008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x16008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc1_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc1_apps_clk = {
+ .halt_reg = 0x16004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x16004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc1_apps_clk",
+ .parent_names = (const char *[]){
+ "sdcc1_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc1_ice_core_clk = {
+ .halt_reg = 0x1600c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1600c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc1_ice_core_clk",
+ .parent_names = (const char *[]){
+ "sdcc1_ice_core_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc2_ahb_clk = {
+ .halt_reg = 0x14008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x14008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc2_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc2_apps_clk = {
+ .halt_reg = 0x14004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x14004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc2_apps_clk",
+ .parent_names = (const char *[]){
+ "sdcc2_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_ufs_ahb_clk = {
+ .halt_reg = 0x7500c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x7500c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_ufs_axi_clk = {
+ .halt_reg = 0x75008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x75008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_axi_clk",
+ .parent_names = (const char *[]){
+ "ufs_axi_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_ufs_axi_hw_ctl_clk = {
+ .halt_reg = 0x75008,
+ .clkr = {
+ .enable_reg = 0x75008,
+ .enable_mask = BIT(1),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_axi_hw_ctl_clk",
+ .parent_names = (const char *[]){
+ "gcc_ufs_axi_clk",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_hw_ctl_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_ufs_clkref_clk = {
+ .halt_reg = 0x88008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x88008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_clkref_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_ufs_ice_core_clk = {
+ .halt_reg = 0x7600c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x7600c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_ice_core_clk",
+ .parent_names = (const char *[]){
+ "ufs_ice_core_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_ufs_ice_core_hw_ctl_clk = {
+ .halt_reg = 0x7600c,
+ .clkr = {
+ .enable_reg = 0x7600c,
+ .enable_mask = BIT(1),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_ice_core_hw_ctl_clk",
+ .parent_names = (const char *[]){
+ "gcc_ufs_ice_core_clk",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_hw_ctl_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_ufs_phy_aux_clk = {
+ .halt_reg = 0x76040,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x76040,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_phy_aux_clk",
+ .parent_names = (const char *[]){
+ "ufs_phy_aux_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_ufs_phy_aux_hw_ctl_clk = {
+ .halt_reg = 0x76040,
+ .clkr = {
+ .enable_reg = 0x76040,
+ .enable_mask = BIT(1),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_phy_aux_hw_ctl_clk",
+ .parent_names = (const char *[]){
+ "gcc_ufs_phy_aux_clk",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_hw_ctl_ops,
+ },
+ },
+};
+
+static struct clk_gate2 gcc_ufs_rx_symbol_0_clk = {
+ .udelay = 500,
+ .clkr = {
+ .enable_reg = 0x75014,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_rx_symbol_0_clk",
+ .ops = &clk_gate2_ops,
+ },
+ },
+};
+
+static struct clk_gate2 gcc_ufs_rx_symbol_1_clk = {
+ .udelay = 500,
+ .clkr = {
+ .enable_reg = 0x7605c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_rx_symbol_1_clk",
+ .ops = &clk_gate2_ops,
+ },
+ },
+};
+
+static struct clk_gate2 gcc_ufs_tx_symbol_0_clk = {
+ .udelay = 500,
+ .clkr = {
+ .enable_reg = 0x75010,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_tx_symbol_0_clk",
+ .ops = &clk_gate2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_ufs_unipro_core_clk = {
+ .halt_reg = 0x76008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x76008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_unipro_core_clk",
+ .parent_names = (const char *[]){
+ "ufs_unipro_core_clk_src",
+ },
+ .flags = CLK_SET_RATE_PARENT,
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_ufs_unipro_core_hw_ctl_clk = {
+ .halt_reg = 0x76008,
+ .clkr = {
+ .enable_reg = 0x76008,
+ .enable_mask = BIT(1),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ufs_unipro_core_hw_ctl_clk",
+ .parent_names = (const char *[]){
+ "gcc_ufs_unipro_core_clk",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_hw_ctl_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb20_master_clk = {
+ .halt_reg = 0x2f004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2f004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb20_master_clk",
+ .parent_names = (const char *[]){
+ "usb20_master_clk_src",
+ },
+ .flags = CLK_SET_RATE_PARENT,
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb20_mock_utmi_clk = {
+ .halt_reg = 0x2f00c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2f00c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb20_mock_utmi_clk",
+ .parent_names = (const char *[]){
+ "usb20_mock_utmi_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb20_sleep_clk = {
+ .halt_reg = 0x2f008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x2f008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb20_sleep_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb30_master_clk = {
+ .halt_reg = 0xf008,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xf008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb30_master_clk",
+ .parent_names = (const char *[]){
+ "usb30_master_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb30_mock_utmi_clk = {
+ .halt_reg = 0xf010,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xf010,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb30_mock_utmi_clk",
+ .parent_names = (const char *[]){
+ "usb30_mock_utmi_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb30_sleep_clk = {
+ .halt_reg = 0xf00c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0xf00c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb30_sleep_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb3_clkref_clk = {
+ .halt_reg = 0x8800c,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x8800c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb3_clkref_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb3_phy_aux_clk = {
+ .halt_reg = 0x50000,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x50000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb3_phy_aux_clk",
+ .parent_names = (const char *[]){
+ "usb3_phy_aux_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_gate2 gcc_usb3_phy_pipe_clk = {
+ .udelay = 50,
+ .clkr = {
+ .enable_reg = 0x50004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb3_phy_pipe_clk",
+ .ops = &clk_gate2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb_phy_cfg_ahb2phy_clk = {
+ .halt_reg = 0x6a004,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x6a004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb_phy_cfg_ahb2phy_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch hlos1_vote_lpass_adsp_smmu_clk = {
+ .halt_reg = 0x7d014,
+ .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE,
+ .clkr = {
+ .enable_reg = 0x7d014,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "hlos1_vote_lpass_adsp_smmu_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_fixed_factor gcc_ce1_ahb_m_clk = {
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ce1_ahb_m_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+static struct clk_fixed_factor gcc_ce1_axi_m_clk = {
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_ce1_axi_m_clk",
+ .ops = &clk_dummy_ops,
+ },
+};
+
+struct clk_hw *gcc_msmfalcon_hws[] = {
+ [GCC_XO] = &xo.hw,
+ [GCC_GPLL0_EARLY_DIV] = &gpll0_out_early_div.hw,
+ [GCC_GPLL1_EARLY_DIV] = &gpll1_out_early_div.hw,
+ [GCC_CE1_AHB_M_CLK] = &gcc_ce1_ahb_m_clk.hw,
+ [GCC_CE1_AXI_M_CLK] = &gcc_ce1_axi_m_clk.hw,
+};
+
+static struct clk_regmap *gcc_falcon_clocks[] = {
+ [BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr,
+ [BLSP1_QUP1_SPI_APPS_CLK_SRC] = &blsp1_qup1_spi_apps_clk_src.clkr,
+ [BLSP1_QUP2_I2C_APPS_CLK_SRC] = &blsp1_qup2_i2c_apps_clk_src.clkr,
+ [BLSP1_QUP2_SPI_APPS_CLK_SRC] = &blsp1_qup2_spi_apps_clk_src.clkr,
+ [BLSP1_QUP3_I2C_APPS_CLK_SRC] = &blsp1_qup3_i2c_apps_clk_src.clkr,
+ [BLSP1_QUP3_SPI_APPS_CLK_SRC] = &blsp1_qup3_spi_apps_clk_src.clkr,
+ [BLSP1_QUP4_I2C_APPS_CLK_SRC] = &blsp1_qup4_i2c_apps_clk_src.clkr,
+ [BLSP1_QUP4_SPI_APPS_CLK_SRC] = &blsp1_qup4_spi_apps_clk_src.clkr,
+ [BLSP1_UART1_APPS_CLK_SRC] = &blsp1_uart1_apps_clk_src.clkr,
+ [BLSP1_UART2_APPS_CLK_SRC] = &blsp1_uart2_apps_clk_src.clkr,
+ [BLSP2_QUP1_I2C_APPS_CLK_SRC] = &blsp2_qup1_i2c_apps_clk_src.clkr,
+ [BLSP2_QUP1_SPI_APPS_CLK_SRC] = &blsp2_qup1_spi_apps_clk_src.clkr,
+ [BLSP2_QUP2_I2C_APPS_CLK_SRC] = &blsp2_qup2_i2c_apps_clk_src.clkr,
+ [BLSP2_QUP2_SPI_APPS_CLK_SRC] = &blsp2_qup2_spi_apps_clk_src.clkr,
+ [BLSP2_QUP3_I2C_APPS_CLK_SRC] = &blsp2_qup3_i2c_apps_clk_src.clkr,
+ [BLSP2_QUP3_SPI_APPS_CLK_SRC] = &blsp2_qup3_spi_apps_clk_src.clkr,
+ [BLSP2_QUP4_I2C_APPS_CLK_SRC] = &blsp2_qup4_i2c_apps_clk_src.clkr,
+ [BLSP2_QUP4_SPI_APPS_CLK_SRC] = &blsp2_qup4_spi_apps_clk_src.clkr,
+ [BLSP2_UART1_APPS_CLK_SRC] = &blsp2_uart1_apps_clk_src.clkr,
+ [BLSP2_UART2_APPS_CLK_SRC] = &blsp2_uart2_apps_clk_src.clkr,
+ [GCC_AGGRE2_UFS_AXI_CLK] = &gcc_aggre2_ufs_axi_clk.clkr,
+ [GCC_AGGRE2_USB3_AXI_CLK] = &gcc_aggre2_usb3_axi_clk.clkr,
+ [GCC_BIMC_GFX_CLK] = &gcc_bimc_gfx_clk.clkr,
+ [GCC_BIMC_HMSS_AXI_CLK] = &gcc_bimc_hmss_axi_clk.clkr,
+ [GCC_BIMC_MSS_Q6_AXI_CLK] = &gcc_bimc_mss_q6_axi_clk.clkr,
+ [GCC_BLSP1_AHB_CLK] = &gcc_blsp1_ahb_clk.clkr,
+ [GCC_BLSP1_QUP1_I2C_APPS_CLK] = &gcc_blsp1_qup1_i2c_apps_clk.clkr,
+ [GCC_BLSP1_QUP1_SPI_APPS_CLK] = &gcc_blsp1_qup1_spi_apps_clk.clkr,
+ [GCC_BLSP1_QUP2_I2C_APPS_CLK] = &gcc_blsp1_qup2_i2c_apps_clk.clkr,
+ [GCC_BLSP1_QUP2_SPI_APPS_CLK] = &gcc_blsp1_qup2_spi_apps_clk.clkr,
+ [GCC_BLSP1_QUP3_I2C_APPS_CLK] = &gcc_blsp1_qup3_i2c_apps_clk.clkr,
+ [GCC_BLSP1_QUP3_SPI_APPS_CLK] = &gcc_blsp1_qup3_spi_apps_clk.clkr,
+ [GCC_BLSP1_QUP4_I2C_APPS_CLK] = &gcc_blsp1_qup4_i2c_apps_clk.clkr,
+ [GCC_BLSP1_QUP4_SPI_APPS_CLK] = &gcc_blsp1_qup4_spi_apps_clk.clkr,
+ [GCC_BLSP1_UART1_APPS_CLK] = &gcc_blsp1_uart1_apps_clk.clkr,
+ [GCC_BLSP1_UART2_APPS_CLK] = &gcc_blsp1_uart2_apps_clk.clkr,
+ [GCC_BLSP2_AHB_CLK] = &gcc_blsp2_ahb_clk.clkr,
+ [GCC_BLSP2_QUP1_I2C_APPS_CLK] = &gcc_blsp2_qup1_i2c_apps_clk.clkr,
+ [GCC_BLSP2_QUP1_SPI_APPS_CLK] = &gcc_blsp2_qup1_spi_apps_clk.clkr,
+ [GCC_BLSP2_QUP2_I2C_APPS_CLK] = &gcc_blsp2_qup2_i2c_apps_clk.clkr,
+ [GCC_BLSP2_QUP2_SPI_APPS_CLK] = &gcc_blsp2_qup2_spi_apps_clk.clkr,
+ [GCC_BLSP2_QUP3_I2C_APPS_CLK] = &gcc_blsp2_qup3_i2c_apps_clk.clkr,
+ [GCC_BLSP2_QUP3_SPI_APPS_CLK] = &gcc_blsp2_qup3_spi_apps_clk.clkr,
+ [GCC_BLSP2_QUP4_I2C_APPS_CLK] = &gcc_blsp2_qup4_i2c_apps_clk.clkr,
+ [GCC_BLSP2_QUP4_SPI_APPS_CLK] = &gcc_blsp2_qup4_spi_apps_clk.clkr,
+ [GCC_BLSP2_UART1_APPS_CLK] = &gcc_blsp2_uart1_apps_clk.clkr,
+ [GCC_BLSP2_UART2_APPS_CLK] = &gcc_blsp2_uart2_apps_clk.clkr,
+ [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr,
+ [GCC_CFG_NOC_USB2_AXI_CLK] = &gcc_cfg_noc_usb2_axi_clk.clkr,
+ [GCC_CFG_NOC_USB3_AXI_CLK] = &gcc_cfg_noc_usb3_axi_clk.clkr,
+ [GCC_DCC_AHB_CLK] = &gcc_dcc_ahb_clk.clkr,
+ [GCC_GP1_CLK] = &gcc_gp1_clk.clkr,
+ [GCC_GP2_CLK] = &gcc_gp2_clk.clkr,
+ [GCC_GP3_CLK] = &gcc_gp3_clk.clkr,
+ [GCC_GPU_BIMC_GFX_CLK] = &gcc_gpu_bimc_gfx_clk.clkr,
+ [GCC_GPU_BIMC_GFX_SRC_CLK] = &gcc_gpu_bimc_gfx_src_clk.clkr,
+ [GCC_GPU_CFG_AHB_CLK] = &gcc_gpu_cfg_ahb_clk.clkr,
+ [GCC_GPU_GPLL0_CLK] = &gcc_gpu_gpll0_clk.clkr,
+ [GCC_GPU_GPLL0_DIV_CLK] = &gcc_gpu_gpll0_div_clk.clkr,
+ [GCC_GPU_SNOC_DVM_GFX_CLK] = &gcc_gpu_snoc_dvm_gfx_clk.clkr,
+ [GCC_HMSS_AHB_CLK] = &gcc_hmss_ahb_clk.clkr,
+ [GCC_HMSS_DVM_BUS_CLK] = &gcc_hmss_dvm_bus_clk.clkr,
+ [GCC_HMSS_RBCPR_CLK] = &gcc_hmss_rbcpr_clk.clkr,
+ [GCC_MMSS_GPLL0_CLK] = &gcc_mmss_gpll0_clk.clkr,
+ [GCC_MMSS_GPLL0_DIV_CLK] = &gcc_mmss_gpll0_div_clk.clkr,
+ [GCC_MMSS_NOC_CFG_AHB_CLK] = &gcc_mmss_noc_cfg_ahb_clk.clkr,
+ [GCC_MMSS_SYS_NOC_AXI_CLK] = &gcc_mmss_sys_noc_axi_clk.clkr,
+ [GCC_MSS_CFG_AHB_CLK] = &gcc_mss_cfg_ahb_clk.clkr,
+ [GCC_MSS_MNOC_BIMC_AXI_CLK] = &gcc_mss_mnoc_bimc_axi_clk.clkr,
+ [GCC_MSS_Q6_BIMC_AXI_CLK] = &gcc_mss_q6_bimc_axi_clk.clkr,
+ [GCC_MSS_SNOC_AXI_CLK] = &gcc_mss_snoc_axi_clk.clkr,
+ [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr,
+ [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr,
+ [GCC_PRNG_AHB_CLK] = &gcc_prng_ahb_clk.clkr,
+ [GCC_QSPI_AHB_CLK] = &gcc_qspi_ahb_clk.clkr,
+ [GCC_QSPI_SER_CLK] = &gcc_qspi_ser_clk.clkr,
+ [GCC_RX0_USB2_CLKREF_CLK] = &gcc_rx0_usb2_clkref_clk.clkr,
+ [GCC_RX1_USB2_CLKREF_CLK] = &gcc_rx1_usb2_clkref_clk.clkr,
+ [GCC_RX2_QLINK_CLKREF_CLK] = &gcc_rx2_qlink_clkref_clk.clkr,
+ [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr,
+ [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr,
+ [GCC_SDCC1_ICE_CORE_CLK] = &gcc_sdcc1_ice_core_clk.clkr,
+ [GCC_SDCC2_AHB_CLK] = &gcc_sdcc2_ahb_clk.clkr,
+ [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr,
+ [GCC_UFS_AHB_CLK] = &gcc_ufs_ahb_clk.clkr,
+ [GCC_UFS_AXI_CLK] = &gcc_ufs_axi_clk.clkr,
+ [GCC_UFS_CLKREF_CLK] = &gcc_ufs_clkref_clk.clkr,
+ [GCC_UFS_ICE_CORE_CLK] = &gcc_ufs_ice_core_clk.clkr,
+ [GCC_UFS_PHY_AUX_CLK] = &gcc_ufs_phy_aux_clk.clkr,
+ [GCC_UFS_RX_SYMBOL_0_CLK] = &gcc_ufs_rx_symbol_0_clk.clkr,
+ [GCC_UFS_RX_SYMBOL_1_CLK] = &gcc_ufs_rx_symbol_1_clk.clkr,
+ [GCC_UFS_TX_SYMBOL_0_CLK] = &gcc_ufs_tx_symbol_0_clk.clkr,
+ [GCC_UFS_UNIPRO_CORE_CLK] = &gcc_ufs_unipro_core_clk.clkr,
+ [GCC_USB20_MASTER_CLK] = &gcc_usb20_master_clk.clkr,
+ [GCC_USB20_MOCK_UTMI_CLK] = &gcc_usb20_mock_utmi_clk.clkr,
+ [GCC_USB20_SLEEP_CLK] = &gcc_usb20_sleep_clk.clkr,
+ [GCC_USB30_MASTER_CLK] = &gcc_usb30_master_clk.clkr,
+ [GCC_USB30_MOCK_UTMI_CLK] = &gcc_usb30_mock_utmi_clk.clkr,
+ [GCC_USB30_SLEEP_CLK] = &gcc_usb30_sleep_clk.clkr,
+ [GCC_USB3_CLKREF_CLK] = &gcc_usb3_clkref_clk.clkr,
+ [GCC_USB3_PHY_AUX_CLK] = &gcc_usb3_phy_aux_clk.clkr,
+ [GCC_USB3_PHY_PIPE_CLK] = &gcc_usb3_phy_pipe_clk.clkr,
+ [GCC_USB_PHY_CFG_AHB2PHY_CLK] = &gcc_usb_phy_cfg_ahb2phy_clk.clkr,
+ [GP1_CLK_SRC] = &gp1_clk_src.clkr,
+ [GP2_CLK_SRC] = &gp2_clk_src.clkr,
+ [GP3_CLK_SRC] = &gp3_clk_src.clkr,
+ [GPLL0] = &gpll0_out_main.clkr,
+ [GPLL1] = &gpll1_out_main.clkr,
+ [GPLL4] = &gpll4_out_main.clkr,
+ [HLOS1_VOTE_LPASS_ADSP_SMMU_CLK] = &hlos1_vote_lpass_adsp_smmu_clk.clkr,
+ [HMSS_AHB_CLK_SRC] = &hmss_ahb_clk_src.clkr,
+ [HMSS_GPLL0_CLK_SRC] = &hmss_gpll0_clk_src.clkr,
+ [HMSS_GPLL4_CLK_SRC] = &hmss_gpll4_clk_src.clkr,
+ [HMSS_RBCPR_CLK_SRC] = &hmss_rbcpr_clk_src.clkr,
+ [PDM2_CLK_SRC] = &pdm2_clk_src.clkr,
+ [QSPI_SER_CLK_SRC] = &qspi_ser_clk_src.clkr,
+ [SDCC1_APPS_CLK_SRC] = &sdcc1_apps_clk_src.clkr,
+ [SDCC1_ICE_CORE_CLK_SRC] = &sdcc1_ice_core_clk_src.clkr,
+ [SDCC2_APPS_CLK_SRC] = &sdcc2_apps_clk_src.clkr,
+ [UFS_AXI_CLK_SRC] = &ufs_axi_clk_src.clkr,
+ [UFS_ICE_CORE_CLK_SRC] = &ufs_ice_core_clk_src.clkr,
+ [UFS_PHY_AUX_CLK_SRC] = &ufs_phy_aux_clk_src.clkr,
+ [UFS_UNIPRO_CORE_CLK_SRC] = &ufs_unipro_core_clk_src.clkr,
+ [USB20_MASTER_CLK_SRC] = &usb20_master_clk_src.clkr,
+ [USB20_MOCK_UTMI_CLK_SRC] = &usb20_mock_utmi_clk_src.clkr,
+ [USB30_MASTER_CLK_SRC] = &usb30_master_clk_src.clkr,
+ [USB30_MOCK_UTMI_CLK_SRC] = &usb30_mock_utmi_clk_src.clkr,
+ [USB3_PHY_AUX_CLK_SRC] = &usb3_phy_aux_clk_src.clkr,
+ [GPLL0_OUT_MSSCC] = &gpll0_out_msscc.clkr,
+ [GCC_UFS_AXI_HW_CTL_CLK] = &gcc_ufs_axi_hw_ctl_clk.clkr,
+ [GCC_UFS_ICE_CORE_HW_CTL_CLK] = &gcc_ufs_ice_core_hw_ctl_clk.clkr,
+ [GCC_UFS_PHY_AUX_HW_CTL_CLK] = &gcc_ufs_phy_aux_hw_ctl_clk.clkr,
+ [GCC_UFS_UNIPRO_CORE_HW_CTL_CLK] = &gcc_ufs_unipro_core_hw_ctl_clk.clkr,
+};
+
+static const struct qcom_reset_map gcc_falcon_resets[] = {
+ [GCC_QUSB2PHY_PRIM_BCR] = { 0x12000 },
+ [GCC_QUSB2PHY_SEC_BCR] = { 0x12004 },
+ [GCC_UFS_BCR] = { 0x75000 },
+ [GCC_USB3_DP_PHY_BCR] = { 0x50028 },
+ [GCC_USB3_PHY_BCR] = { 0x50020 },
+ [GCC_USB3PHY_PHY_BCR] = { 0x50024 },
+ [GCC_USB_20_BCR] = { 0x2f000 },
+ [GCC_USB_30_BCR] = { 0xf000 },
+ [GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x6a000 },
+};
+
+static const struct regmap_config gcc_falcon_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x94000,
+ .fast_io = true,
+};
+
+static const struct qcom_cc_desc gcc_falcon_desc = {
+ .config = &gcc_falcon_regmap_config,
+ .clks = gcc_falcon_clocks,
+ .num_clks = ARRAY_SIZE(gcc_falcon_clocks),
+ .resets = gcc_falcon_resets,
+ .num_resets = ARRAY_SIZE(gcc_falcon_resets),
+};
+
+static const struct of_device_id gcc_falcon_match_table[] = {
+ { .compatible = "qcom,gcc-msmfalcon" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gcc_falcon_match_table);
+
+static int gcc_falcon_probe(struct platform_device *pdev)
+{
+ int ret = 0, i;
+ struct regmap *regmap;
+ struct clk *clk;
+
+ regmap = qcom_cc_map(pdev, &gcc_falcon_desc);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /*
+ * Set the HMSS_AHB_CLK_SLEEP_ENA bit to allow the hmss_ahb_clk to be
+ * turned off by hardware during certain apps low power modes.
+ */
+ regmap_update_bits(regmap, 0x52008, BIT(21), BIT(21));
+
+ /* register hardware clocks */
+ for (i = 0; i < ARRAY_SIZE(gcc_msmfalcon_hws); i++) {
+ clk = devm_clk_register(&pdev->dev, gcc_msmfalcon_hws[i]);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ }
+
+ vdd_dig.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig");
+ if (IS_ERR(vdd_dig.regulator[0])) {
+ if (!(PTR_ERR(vdd_dig.regulator[0]) == -EPROBE_DEFER))
+ dev_err(&pdev->dev,
+ "Unable to get vdd_dig regulator\n");
+ return PTR_ERR(vdd_dig.regulator[0]);
+ }
+
+ vdd_dig_ao.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig_ao");
+ if (IS_ERR(vdd_dig_ao.regulator[0])) {
+ if (!(PTR_ERR(vdd_dig_ao.regulator[0]) == -EPROBE_DEFER))
+ dev_err(&pdev->dev,
+ "Unable to get vdd_dig_ao regulator\n");
+ return PTR_ERR(vdd_dig_ao.regulator[0]);
+ }
+
+ ret = qcom_cc_really_probe(pdev, &gcc_falcon_desc, regmap);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register GCC clocks\n");
+ return ret;
+ }
+
+ /* Disable the GPLL0 active input to MMSS and GPU via MISC registers */
+ regmap_update_bits(regmap, 0x0902c, 0x3, 0x3);
+ regmap_update_bits(regmap, 0x71028, 0x3, 0x3);
+
+ /* This clock is used for all MMSSCC register access */
+ clk_prepare_enable(gcc_mmss_noc_cfg_ahb_clk.clkr.hw.clk);
+
+ /* This clock is used for all GPUCC register access */
+ clk_prepare_enable(gcc_gpu_cfg_ahb_clk.clkr.hw.clk);
+
+ dev_info(&pdev->dev, "Registered GCC clocks\n");
+
+ return ret;
+}
+
+static struct platform_driver gcc_falcon_driver = {
+ .probe = gcc_falcon_probe,
+ .driver = {
+ .name = "gcc-msmfalcon",
+ .of_match_table = gcc_falcon_match_table,
+ },
+};
+
+static int __init gcc_falcon_init(void)
+{
+ return platform_driver_register(&gcc_falcon_driver);
+}
+core_initcall_sync(gcc_falcon_init);
+
+static void __exit gcc_falcon_exit(void)
+{
+ platform_driver_unregister(&gcc_falcon_driver);
+}
+module_exit(gcc_falcon_exit);
diff --git a/drivers/clk/qcom/gdsc-regulator.c b/drivers/clk/qcom/gdsc-regulator.c
new file mode 100644
index 000000000000..e645354445cb
--- /dev/null
+++ b/drivers/clk/qcom/gdsc-regulator.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+
+#include "clk-branch.h"
+
+/* GDSCR */
+#define PWR_ON_MASK BIT(31)
+#define CLK_DIS_WAIT_MASK (0xF << 12)
+#define CLK_DIS_WAIT_SHIFT (12)
+#define SW_OVERRIDE_MASK BIT(2)
+#define HW_CONTROL_MASK BIT(1)
+#define SW_COLLAPSE_MASK BIT(0)
+
+/* Domain Address */
+#define GMEM_CLAMP_IO_MASK BIT(0)
+#define GMEM_RESET_MASK BIT(4)
+
+/* SW Reset */
+#define BCR_BLK_ARES_BIT BIT(0)
+
+/* Register Offset */
+#define REG_OFFSET 0x0
+
+/* Timeout Delay */
+#define TIMEOUT_US 100
+
+struct gdsc {
+ struct regulator_dev *rdev;
+ struct regulator_desc rdesc;
+ void __iomem *gdscr;
+ struct regmap *regmap;
+ struct regmap *domain_addr;
+ struct regmap *hw_ctrl;
+ struct regmap *sw_reset;
+ struct clk **clocks;
+ struct reset_control **reset_clocks;
+ bool toggle_mem;
+ bool toggle_periph;
+ bool toggle_logic;
+ bool resets_asserted;
+ bool root_en;
+ bool force_root_en;
+ bool no_status_check_on_disable;
+ bool is_gdsc_enabled;
+ bool allow_clear;
+ bool reset_aon;
+ int clock_count;
+ int reset_count;
+ int root_clk_idx;
+ u32 gds_timeout;
+};
+
+enum gdscr_status {
+ ENABLED,
+ DISABLED,
+};
+
+static DEFINE_MUTEX(gdsc_seq_lock);
+
+void gdsc_allow_clear_retention(struct regulator *regulator)
+{
+ struct gdsc *sc = regulator_get_drvdata(regulator);
+
+ if (sc)
+ sc->allow_clear = true;
+}
+
+static int poll_gdsc_status(struct gdsc *sc, enum gdscr_status status)
+{
+ struct regmap *regmap;
+ int count = sc->gds_timeout;
+ u32 val;
+
+ if (sc->hw_ctrl)
+ regmap = sc->hw_ctrl;
+ else
+ regmap = sc->regmap;
+
+ for (; count > 0; count--) {
+ regmap_read(regmap, REG_OFFSET, &val);
+ val &= PWR_ON_MASK;
+
+ switch (status) {
+ case ENABLED:
+ if (val)
+ return 0;
+ break;
+ case DISABLED:
+ if (!val)
+ return 0;
+ break;
+ }
+ /*
+ * There is no guarantee about the delay needed for the enable
+ * bit in the GDSCR to be set or reset after the GDSC state
+ * changes. Hence, keep on checking for a reasonable number
+ * of times until the bit is set with the least possible delay
+ * between succeessive tries.
+ */
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int gdsc_is_enabled(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval;
+
+ if (!sc->toggle_logic)
+ return !sc->resets_asserted;
+
+ regmap_read(sc->regmap, REG_OFFSET, &regval);
+
+ if (regval & PWR_ON_MASK) {
+ /*
+ * The GDSC might be turned on due to TZ/HYP vote on the
+ * votable GDS registers. Check the SW_COLLAPSE_MASK to
+ * determine if HLOS has voted for it.
+ */
+ if (!(regval & SW_COLLAPSE_MASK))
+ return true;
+ }
+
+ return false;
+}
+
+static int gdsc_enable(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval, hw_ctrl_regval = 0x0;
+ int i, ret = 0;
+
+ mutex_lock(&gdsc_seq_lock);
+
+ if (sc->root_en || sc->force_root_en)
+ clk_prepare_enable(sc->clocks[sc->root_clk_idx]);
+
+ regmap_read(sc->regmap, REG_OFFSET, &regval);
+ if (regval & HW_CONTROL_MASK) {
+ dev_warn(&rdev->dev, "Invalid enable while %s is under HW control\n",
+ sc->rdesc.name);
+ mutex_unlock(&gdsc_seq_lock);
+ return -EBUSY;
+ }
+
+ if (sc->toggle_logic) {
+ if (sc->sw_reset) {
+ regmap_read(sc->sw_reset, REG_OFFSET, &regval);
+ regval |= BCR_BLK_ARES_BIT;
+ regmap_write(sc->sw_reset, REG_OFFSET, regval);
+ /*
+ * BLK_ARES should be kept asserted for 1us before
+ * being de-asserted.
+ */
+ wmb();
+ udelay(1);
+
+ regval &= ~BCR_BLK_ARES_BIT;
+ regmap_write(sc->sw_reset, REG_OFFSET, regval);
+ /* Make sure de-assert goes through before continuing */
+ wmb();
+ }
+
+ if (sc->domain_addr) {
+ if (sc->reset_aon) {
+ regmap_read(sc->domain_addr, REG_OFFSET,
+ &regval);
+ regval |= GMEM_RESET_MASK;
+ regmap_write(sc->domain_addr, REG_OFFSET,
+ regval);
+ /*
+ * Keep reset asserted for at-least 1us before
+ * continuing.
+ */
+ wmb();
+ udelay(1);
+
+ regval &= ~GMEM_RESET_MASK;
+ regmap_write(sc->domain_addr, REG_OFFSET,
+ regval);
+ /*
+ * Make sure GMEM_RESET is de-asserted before
+ * continuing.
+ */
+ wmb();
+ }
+
+ regmap_read(sc->domain_addr, REG_OFFSET, &regval);
+ regval &= ~GMEM_CLAMP_IO_MASK;
+ regmap_write(sc->domain_addr, REG_OFFSET, regval);
+
+ /*
+ * Make sure CLAMP_IO is de-asserted before continuing.
+ */
+ wmb();
+ }
+
+ regmap_read(sc->regmap, REG_OFFSET, &regval);
+ regval &= ~SW_COLLAPSE_MASK;
+ regmap_write(sc->regmap, REG_OFFSET, regval);
+
+ /* Wait for 8 XO cycles before polling the status bit. */
+ mb();
+ udelay(1);
+
+ ret = poll_gdsc_status(sc, ENABLED);
+ if (ret) {
+ regmap_read(sc->regmap, REG_OFFSET, &regval);
+
+ if (sc->hw_ctrl) {
+ regmap_read(sc->hw_ctrl, REG_OFFSET,
+ &hw_ctrl_regval);
+ dev_warn(&rdev->dev, "%s state (after %d us timeout): 0x%x, GDS_HW_CTRL: 0x%x. Re-polling.\n",
+ sc->rdesc.name, sc->gds_timeout,
+ regval, hw_ctrl_regval);
+
+ ret = poll_gdsc_status(sc, ENABLED);
+ if (ret) {
+ regmap_read(sc->regmap, REG_OFFSET,
+ &regval);
+ regmap_read(sc->hw_ctrl, REG_OFFSET,
+ &hw_ctrl_regval);
+ dev_err(&rdev->dev, "%s final state (after additional %d us timeout): 0x%x, GDS_HW_CTRL: 0x%x\n",
+ sc->rdesc.name, sc->gds_timeout,
+ regval, hw_ctrl_regval);
+
+ mutex_unlock(&gdsc_seq_lock);
+ return ret;
+ }
+ } else {
+ dev_err(&rdev->dev, "%s enable timed out: 0x%x\n",
+ sc->rdesc.name,
+ regval);
+ udelay(sc->gds_timeout);
+
+ regmap_read(sc->regmap, REG_OFFSET, &regval);
+ dev_err(&rdev->dev, "%s final state: 0x%x (%d us after timeout)\n",
+ sc->rdesc.name, regval,
+ sc->gds_timeout);
+
+ mutex_unlock(&gdsc_seq_lock);
+
+ return ret;
+ }
+ }
+ } else {
+ for (i = 0; i < sc->reset_count; i++)
+ reset_control_deassert(sc->reset_clocks[i]);
+ sc->resets_asserted = false;
+ }
+
+ for (i = 0; i < sc->clock_count; i++) {
+ if (unlikely(i == sc->root_clk_idx))
+ continue;
+ if (sc->toggle_mem)
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_MEM);
+ if (sc->toggle_periph)
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_PERIPH);
+ }
+
+ /*
+ * If clocks to this power domain were already on, they will take an
+ * additional 4 clock cycles to re-enable after the rail is enabled.
+ * Delay to account for this. A delay is also needed to ensure clocks
+ * are not enabled within 400ns of enabling power to the memories.
+ */
+ udelay(1);
+
+ /* Delay to account for staggered memory powerup. */
+ udelay(1);
+
+ if (sc->force_root_en)
+ clk_disable_unprepare(sc->clocks[sc->root_clk_idx]);
+
+ sc->is_gdsc_enabled = true;
+
+ mutex_unlock(&gdsc_seq_lock);
+
+ return ret;
+}
+
+static int gdsc_disable(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval;
+ int i, ret = 0;
+
+ mutex_lock(&gdsc_seq_lock);
+
+ if (sc->force_root_en)
+ clk_prepare_enable(sc->clocks[sc->root_clk_idx]);
+
+ for (i = sc->clock_count - 1; i >= 0; i--) {
+ if (unlikely(i == sc->root_clk_idx))
+ continue;
+ if (sc->toggle_mem && sc->allow_clear)
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_MEM);
+ if (sc->toggle_periph && sc->allow_clear)
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH);
+ }
+
+ /* Delay to account for staggered memory powerdown. */
+ udelay(1);
+
+ if (sc->toggle_logic) {
+ regmap_read(sc->regmap, REG_OFFSET, &regval);
+ regval |= SW_COLLAPSE_MASK;
+ regmap_write(sc->regmap, REG_OFFSET, regval);
+
+ /* Wait for 8 XO cycles before polling the status bit. */
+ mb();
+ udelay(1);
+
+ if (sc->no_status_check_on_disable) {
+ /*
+ * Add a short delay here to ensure that gdsc_enable
+ * right after it was disabled does not put it in a
+ * weird state.
+ */
+ udelay(TIMEOUT_US);
+ } else {
+ ret = poll_gdsc_status(sc, DISABLED);
+ if (ret)
+ dev_err(&rdev->dev, "%s disable timed out: 0x%x\n",
+ sc->rdesc.name, regval);
+ }
+
+ if (sc->domain_addr) {
+ regmap_read(sc->domain_addr, REG_OFFSET, &regval);
+ regval |= GMEM_CLAMP_IO_MASK;
+ regmap_write(sc->domain_addr, REG_OFFSET, regval);
+ }
+
+ } else {
+ for (i = sc->reset_count - 1; i >= 0; i--)
+ reset_control_assert(sc->reset_clocks[i]);
+ sc->resets_asserted = true;
+ }
+
+ /*
+ * Check if gdsc_enable was called for this GDSC. If not, the root
+ * clock will not have been enabled prior to this.
+ */
+ if ((sc->is_gdsc_enabled && sc->root_en) || sc->force_root_en)
+ clk_disable_unprepare(sc->clocks[sc->root_clk_idx]);
+
+ sc->is_gdsc_enabled = false;
+
+ mutex_unlock(&gdsc_seq_lock);
+
+ return ret;
+}
+
+static unsigned int gdsc_get_mode(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval;
+
+ mutex_lock(&gdsc_seq_lock);
+ regmap_read(sc->regmap, REG_OFFSET, &regval);
+ mutex_unlock(&gdsc_seq_lock);
+
+ if (regval & HW_CONTROL_MASK)
+ return REGULATOR_MODE_FAST;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int gdsc_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval;
+ int ret = 0;
+
+ mutex_lock(&gdsc_seq_lock);
+
+ regmap_read(sc->regmap, REG_OFFSET, &regval);
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ /* Turn on HW trigger mode */
+ regval |= HW_CONTROL_MASK;
+ regmap_write(sc->regmap, REG_OFFSET, regval);
+ /*
+ * There may be a race with internal HW trigger signal,
+ * that will result in GDSC going through a power down and
+ * up cycle. In case HW trigger signal is controlled by
+ * firmware that also poll same status bits as we do, FW
+ * might read an 'on' status before the GDSC can finish
+ * power cycle. We wait 1us before returning to ensure
+ * FW can't immediately poll the status bit.
+ */
+ mb();
+ udelay(1);
+ break;
+ case REGULATOR_MODE_NORMAL:
+ /* Turn off HW trigger mode */
+ regval &= ~HW_CONTROL_MASK;
+ regmap_write(sc->regmap, REG_OFFSET, regval);
+ /*
+ * There may be a race with internal HW trigger signal,
+ * that will result in GDSC going through a power down and
+ * up cycle. If we poll too early, status bit will
+ * indicate 'on' before the GDSC can finish the power cycle.
+ * Account for this case by waiting 1us before polling.
+ */
+ mb();
+ udelay(1);
+
+ ret = poll_gdsc_status(sc, ENABLED);
+ if (ret)
+ dev_err(&rdev->dev, "%s set_mode timed out: 0x%x\n",
+ sc->rdesc.name, regval);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&gdsc_seq_lock);
+
+ return ret;
+}
+
+static struct regulator_ops gdsc_ops = {
+ .is_enabled = gdsc_is_enabled,
+ .enable = gdsc_enable,
+ .disable = gdsc_disable,
+ .set_mode = gdsc_set_mode,
+ .get_mode = gdsc_get_mode,
+};
+
+static const struct regmap_config gdsc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
+};
+
+static int gdsc_probe(struct platform_device *pdev)
+{
+ static atomic_t gdsc_count = ATOMIC_INIT(-1);
+ struct regulator_config reg_config = {};
+ struct regulator_init_data *init_data;
+ struct resource *res;
+ struct gdsc *sc;
+ uint32_t regval, clk_dis_wait_val = 0;
+ bool retain_mem, retain_periph, support_hw_trigger;
+ int i, ret;
+ u32 timeout;
+
+ sc = devm_kzalloc(&pdev->dev, sizeof(struct gdsc), GFP_KERNEL);
+ if (sc == NULL)
+ return -ENOMEM;
+
+ init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node,
+ &sc->rdesc);
+ if (init_data == NULL)
+ return -ENOMEM;
+
+ if (of_get_property(pdev->dev.of_node, "parent-supply", NULL))
+ init_data->supply_regulator = "parent";
+
+ ret = of_property_read_string(pdev->dev.of_node, "regulator-name",
+ &sc->rdesc.name);
+ if (ret)
+ return ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Failed to get resources\n");
+ return -EINVAL;
+ }
+
+ sc->gdscr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (sc->gdscr == NULL)
+ return -ENOMEM;
+
+ sc->regmap = devm_regmap_init_mmio(&pdev->dev, sc->gdscr,
+ &gdsc_regmap_config);
+ if (!sc->regmap) {
+ dev_err(&pdev->dev, "Couldn't get regmap\n");
+ return -EINVAL;
+ }
+
+ if (of_find_property(pdev->dev.of_node, "domain-addr", NULL)) {
+ sc->domain_addr = syscon_regmap_lookup_by_phandle
+ (pdev->dev.of_node, "domain-addr");
+ if (IS_ERR(sc->domain_addr))
+ return -ENODEV;
+ }
+
+ if (of_find_property(pdev->dev.of_node, "sw-reset", NULL)) {
+ sc->sw_reset = syscon_regmap_lookup_by_phandle
+ (pdev->dev.of_node, "sw-reset");
+ if (IS_ERR(sc->sw_reset))
+ return -ENODEV;
+ }
+
+ if (of_find_property(pdev->dev.of_node, "hw-ctrl-addr", NULL)) {
+ sc->hw_ctrl = syscon_regmap_lookup_by_phandle(
+ pdev->dev.of_node, "hw-ctrl-addr");
+ if (IS_ERR(sc->hw_ctrl))
+ return -ENODEV;
+ }
+
+ sc->gds_timeout = TIMEOUT_US;
+
+ ret = of_property_read_u32(pdev->dev.of_node, "qcom,gds-timeout",
+ &timeout);
+ if (!ret)
+ sc->gds_timeout = timeout;
+
+ sc->clock_count = of_property_count_strings(pdev->dev.of_node,
+ "clock-names");
+ if (sc->clock_count == -EINVAL) {
+ sc->clock_count = 0;
+ } else if (IS_ERR_VALUE(sc->clock_count)) {
+ dev_err(&pdev->dev, "Failed to get clock names\n");
+ return -EINVAL;
+ }
+
+ sc->clocks = devm_kzalloc(&pdev->dev,
+ sizeof(struct clk *) * sc->clock_count, GFP_KERNEL);
+ if (!sc->clocks)
+ return -ENOMEM;
+
+ sc->root_clk_idx = -1;
+
+ sc->root_en = of_property_read_bool(pdev->dev.of_node,
+ "qcom,enable-root-clk");
+
+ sc->force_root_en = of_property_read_bool(pdev->dev.of_node,
+ "qcom,force-enable-root-clk");
+
+ for (i = 0; i < sc->clock_count; i++) {
+ const char *clock_name;
+
+ of_property_read_string_index(pdev->dev.of_node, "clock-names",
+ i, &clock_name);
+
+ sc->clocks[i] = devm_clk_get(&pdev->dev, clock_name);
+ if (IS_ERR(sc->clocks[i])) {
+ int rc = PTR_ERR(sc->clocks[i]);
+
+ if (rc != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get %s\n",
+ clock_name);
+ return rc;
+ }
+
+ if (!strcmp(clock_name, "core_root_clk"))
+ sc->root_clk_idx = i;
+ }
+
+ if ((sc->root_en || sc->force_root_en) && (sc->root_clk_idx == -1)) {
+ dev_err(&pdev->dev, "Failed to get root clock name\n");
+ return -EINVAL;
+ }
+
+ sc->reset_aon = of_property_read_bool(pdev->dev.of_node,
+ "qcom,reset-aon-logic");
+
+ sc->rdesc.id = atomic_inc_return(&gdsc_count);
+ sc->rdesc.ops = &gdsc_ops;
+ sc->rdesc.type = REGULATOR_VOLTAGE;
+ sc->rdesc.owner = THIS_MODULE;
+ platform_set_drvdata(pdev, sc);
+
+ /*
+ * Disable HW trigger: collapse/restore occur based on registers writes.
+ * Disable SW override: Use hardware state-machine for sequencing.
+ */
+ regmap_read(sc->regmap, REG_OFFSET, &regval);
+ regval &= ~(HW_CONTROL_MASK | SW_OVERRIDE_MASK);
+
+ if (!of_property_read_u32(pdev->dev.of_node, "qcom,clk-dis-wait-val",
+ &clk_dis_wait_val)) {
+ clk_dis_wait_val = clk_dis_wait_val << CLK_DIS_WAIT_SHIFT;
+
+ /* Configure wait time between states. */
+ regval &= ~(CLK_DIS_WAIT_MASK);
+ regval |= clk_dis_wait_val;
+ }
+
+ regmap_write(sc->regmap, REG_OFFSET, regval);
+
+ sc->no_status_check_on_disable =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,no-status-check-on-disable");
+ retain_mem = of_property_read_bool(pdev->dev.of_node,
+ "qcom,retain-mem");
+ sc->toggle_mem = !retain_mem;
+ retain_periph = of_property_read_bool(pdev->dev.of_node,
+ "qcom,retain-periph");
+ sc->toggle_periph = !retain_periph;
+ sc->toggle_logic = !of_property_read_bool(pdev->dev.of_node,
+ "qcom,skip-logic-collapse");
+ support_hw_trigger = of_property_read_bool(pdev->dev.of_node,
+ "qcom,support-hw-trigger");
+ if (support_hw_trigger) {
+ init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE;
+ init_data->constraints.valid_modes_mask |=
+ REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+ }
+
+ if (!sc->toggle_logic) {
+ sc->reset_count = of_property_count_strings(pdev->dev.of_node,
+ "reset-names");
+ if (sc->reset_count == -EINVAL) {
+ sc->reset_count = 0;
+ } else if (IS_ERR_VALUE(sc->reset_count)) {
+ dev_err(&pdev->dev, "Failed to get reset clock names\n");
+ return -EINVAL;
+ }
+
+ sc->reset_clocks = devm_kzalloc(&pdev->dev,
+ sizeof(struct reset_control *) * sc->reset_count,
+ GFP_KERNEL);
+ if (!sc->reset_clocks)
+ return -ENOMEM;
+
+ for (i = 0; i < sc->reset_count; i++) {
+ const char *reset_name;
+
+ of_property_read_string_index(pdev->dev.of_node,
+ "reset-names", i, &reset_name);
+ sc->reset_clocks[i] = devm_reset_control_get(&pdev->dev,
+ reset_name);
+ if (IS_ERR(sc->reset_clocks[i])) {
+ int rc = PTR_ERR(sc->reset_clocks[i]);
+
+ if (rc != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get %s\n",
+ reset_name);
+ return rc;
+ }
+ }
+
+ regval &= ~SW_COLLAPSE_MASK;
+ regmap_write(sc->regmap, REG_OFFSET, regval);
+
+ ret = poll_gdsc_status(sc, ENABLED);
+ if (ret) {
+ dev_err(&pdev->dev, "%s enable timed out: 0x%x\n",
+ sc->rdesc.name, regval);
+ return ret;
+ }
+ }
+
+ sc->allow_clear = of_property_read_bool(pdev->dev.of_node,
+ "qcom,disallow-clear");
+ sc->allow_clear = !sc->allow_clear;
+
+ for (i = 0; i < sc->clock_count; i++) {
+ if (retain_mem || (regval & PWR_ON_MASK) || !sc->allow_clear)
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_MEM);
+ else
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_MEM);
+
+ if (retain_periph || (regval & PWR_ON_MASK) || !sc->allow_clear)
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_PERIPH);
+ else
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH);
+ }
+
+ reg_config.dev = &pdev->dev;
+ reg_config.init_data = init_data;
+ reg_config.driver_data = sc;
+ reg_config.of_node = pdev->dev.of_node;
+ reg_config.regmap = sc->regmap;
+
+ sc->rdev = regulator_register(&sc->rdesc, &reg_config);
+ if (IS_ERR(sc->rdev)) {
+ dev_err(&pdev->dev, "regulator_register(\"%s\") failed.\n",
+ sc->rdesc.name);
+ return PTR_ERR(sc->rdev);
+ }
+
+ return 0;
+}
+
+static int gdsc_remove(struct platform_device *pdev)
+{
+ struct gdsc *sc = platform_get_drvdata(pdev);
+
+ regulator_unregister(sc->rdev);
+
+ return 0;
+}
+
+static const struct of_device_id gdsc_match_table[] = {
+ { .compatible = "qcom,gdsc" },
+ {}
+};
+
+static struct platform_driver gdsc_driver = {
+ .probe = gdsc_probe,
+ .remove = gdsc_remove,
+ .driver = {
+ .name = "gdsc",
+ .of_match_table = gdsc_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init gdsc_init(void)
+{
+ return platform_driver_register(&gdsc_driver);
+}
+subsys_initcall(gdsc_init);
+
+static void __exit gdsc_exit(void)
+{
+ platform_driver_unregister(&gdsc_driver);
+}
+module_exit(gdsc_exit);
diff --git a/drivers/clk/qcom/gpucc-msmfalcon.c b/drivers/clk/qcom/gpucc-msmfalcon.c
new file mode 100644
index 000000000000..a2127e2629c7
--- /dev/null
+++ b/drivers/clk/qcom/gpucc-msmfalcon.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <dt-bindings/clock/qcom,gpu-msmfalcon.h>
+
+#include "clk-alpha-pll.h"
+#include "common.h"
+#include "clk-regmap.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-branch.h"
+#include "vdd-level-falcon.h"
+
+#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
+#define F_GFX(f, s, h, m, n, sf) { (f), (s), (2 * (h) - 1), (m), (n), (sf) }
+
+static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
+static DEFINE_VDD_REGULATORS(vdd_mx, VDD_DIG_NUM, 1, vdd_corner, NULL);
+static DEFINE_VDD_REGS_INIT(vdd_gfx, 1);
+
+enum {
+ P_CORE_BI_PLL_TEST_SE,
+ P_GPLL0_OUT_MAIN,
+ P_GPLL0_OUT_MAIN_DIV,
+ P_GPU_PLL0_PLL_OUT_MAIN,
+ P_GPU_PLL1_PLL_OUT_MAIN,
+ P_XO,
+};
+
+static const struct parent_map gpucc_parent_map_0[] = {
+ { P_XO, 0 },
+ { P_GPLL0_OUT_MAIN, 5 },
+ { P_GPLL0_OUT_MAIN_DIV, 6 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gpucc_parent_names_0[] = {
+ "cxo_a",
+ "gcc_gpu_gpll0_clk",
+ "gcc_gpu_gpll0_div_clk",
+ "core_bi_pll_test_se",
+};
+
+static const struct parent_map gpucc_parent_map_1[] = {
+ { P_XO, 0 },
+ { P_GPU_PLL0_PLL_OUT_MAIN, 1 },
+ { P_GPU_PLL1_PLL_OUT_MAIN, 3 },
+ { P_GPLL0_OUT_MAIN, 5 },
+ { P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gpucc_parent_names_1[] = {
+ "xo",
+ "gpu_pll0_pll_out_main",
+ "gpu_pll1_pll_out_main",
+ "gcc_gpu_gpll0_clk",
+ "core_bi_pll_test_se",
+};
+
+static struct pll_vco gpu_vco[] = {
+ { 1000000000, 2000000000, 0 },
+ { 500000000, 1000000000, 2 },
+ { 250000000, 500000000, 3 },
+};
+
+/* 640MHz configuration */
+static const struct pll_config gpu_pll0_config = {
+ .l = 0x21,
+ .config_ctl_val = 0x4001055b,
+ .alpha = 0x55555600,
+ .alpha_u = 0x55,
+ .alpha_en_mask = BIT(24),
+ .vco_val = 0x2 << 20,
+ .vco_mask = 0x3 << 20,
+ .main_output_mask = 0x1,
+};
+
+static struct pll_vco_data pll_data[] = {
+ /* Frequency post-div */
+ { 640000000, 0x1 },
+};
+
+static struct clk_alpha_pll gpu_pll0_pll_out_main = {
+ .offset = 0x0,
+ .vco_table = gpu_vco,
+ .num_vco = ARRAY_SIZE(gpu_vco),
+ .vco_data = pll_data,
+ .num_vco_data = ARRAY_SIZE(pll_data),
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "gpu_pll0_pll_out_main",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_ops,
+ VDD_GPU_PLL_FMAX_MAP6(
+ MIN, 266000000,
+ LOWER, 432000000,
+ LOW, 640000000,
+ LOW_L1, 800000000,
+ NOMINAL, 1020000000,
+ HIGH, 1500000000),
+ },
+ },
+};
+
+static struct clk_alpha_pll gpu_pll1_pll_out_main = {
+ .offset = 0x40,
+ .vco_table = gpu_vco,
+ .num_vco = ARRAY_SIZE(gpu_vco),
+ .vco_data = pll_data,
+ .num_vco_data = ARRAY_SIZE(pll_data),
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "gpu_pll1_pll_out_main",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_ops,
+ VDD_GPU_PLL_FMAX_MAP6(
+ MIN, 266000000,
+ LOWER, 432000000,
+ LOW, 640000000,
+ LOW_L1, 800000000,
+ NOMINAL, 1020000000,
+ HIGH, 1500000000),
+ },
+ },
+};
+
+/* GFX clock init data */
+static struct clk_init_data gpu_clks_init[] = {
+ [0] = {
+ .name = "gfx3d_clk_src",
+ .parent_names = gpucc_parent_names_1,
+ .num_parents = 3,
+ .ops = &clk_gfx3d_src_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ [1] = {
+ .name = "gpucc_gfx3d_clk",
+ .parent_names = (const char *[]){
+ "gfx3d_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ .vdd_class = &vdd_gfx,
+ },
+};
+
+/*
+ * Frequencies and PLL configuration
+ * The PLL source would be to ping-pong between GPU-PLL0
+ * and GPU-PLL1.
+ * ====================================================
+ * | F | PLL SRC Freq | PLL postdiv | RCG Div |
+ * ====================================================
+ * | 160000000 | 640000000 | 2 | 2 |
+ * | 266000000 | 532000000 | 1 | 2 |
+ * | 370000000 | 740000000 | 1 | 2 |
+ * | 465000000 | 930000000 | 1 | 2 |
+ * | 588000000 | 1176000000 | 1 | 2 |
+ * | 647000000 | 1294000000 | 1 | 2 |
+ * | 750000000 | 1500000000 | 1 | 2 |
+ * ====================================================
+*/
+
+static const struct freq_tbl ftbl_gfx3d_clk_src[] = {
+ F_GFX( 19200000, 0, 1, 0, 0, 0),
+ F_GFX(160000000, 0, 2, 0, 0, 640000000),
+ F_GFX(266000000, 0, 2, 0, 0, 532000000),
+ F_GFX(370000000, 0, 2, 0, 0, 740000000),
+ F_GFX(465000000, 0, 2, 0, 0, 930000000),
+ F_GFX(588000000, 0, 2, 0, 0, 1176000000),
+ F_GFX(647000000, 0, 2, 0, 0, 1294000000),
+ F_GFX(750000000, 0, 2, 0, 0, 1500000000),
+ { }
+};
+
+static struct clk_rcg2 gfx3d_clk_src = {
+ .cmd_rcgr = 0x1070,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .freq_tbl = ftbl_gfx3d_clk_src,
+ .parent_map = gpucc_parent_map_1,
+ .flags = FORCE_ENABLE_RCGR,
+ .clkr.hw.init = &gpu_clks_init[0],
+};
+
+static const struct freq_tbl ftbl_rbbmtimer_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 rbbmtimer_clk_src = {
+ .cmd_rcgr = 0x10b0,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gpucc_parent_map_0,
+ .freq_tbl = ftbl_rbbmtimer_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "rbbmtimer_clk_src",
+ .parent_names = gpucc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP1(MIN, 19200000),
+ },
+};
+
+static const struct freq_tbl ftbl_rbcpr_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(50000000, P_GPLL0_OUT_MAIN_DIV, 6, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 rbcpr_clk_src = {
+ .cmd_rcgr = 0x1030,
+ .mnd_width = 0,
+ .hid_width = 5,
+ .parent_map = gpucc_parent_map_0,
+ .freq_tbl = ftbl_rbcpr_clk_src,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "rbcpr_clk_src",
+ .parent_names = gpucc_parent_names_0,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ VDD_DIG_FMAX_MAP2(
+ MIN, 19200000,
+ NOMINAL, 50000000),
+ },
+};
+
+static struct clk_branch gpucc_cxo_clk = {
+ .halt_reg = 0x1020,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1020,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpucc_cxo_clk",
+ .parent_names = (const char *[]) {
+ "cxo_a",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gpucc_gfx3d_clk = {
+ .halt_reg = 0x1098,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1098,
+ .enable_mask = BIT(0),
+ .hw.init = &gpu_clks_init[1],
+ },
+};
+
+static struct clk_branch gpucc_rbbmtimer_clk = {
+ .halt_reg = 0x10d0,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x10d0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpucc_rbbmtimer_clk",
+ .parent_names = (const char *[]){
+ "rbbmtimer_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gpucc_rbcpr_clk = {
+ .halt_reg = 0x1054,
+ .halt_check = BRANCH_HALT,
+ .clkr = {
+ .enable_reg = 0x1054,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpucc_rbcpr_clk",
+ .parent_names = (const char *[]){
+ "rbcpr_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_regmap *gpucc_falcon_clocks[] = {
+ [GFX3D_CLK_SRC] = &gfx3d_clk_src.clkr,
+ [GPU_PLL0_PLL] = &gpu_pll0_pll_out_main.clkr,
+ [GPU_PLL1_PLL] = &gpu_pll1_pll_out_main.clkr,
+ [GPUCC_CXO_CLK] = &gpucc_cxo_clk.clkr,
+ [GPUCC_GFX3D_CLK] = &gpucc_gfx3d_clk.clkr,
+ [GPUCC_RBBMTIMER_CLK] = &gpucc_rbbmtimer_clk.clkr,
+ [GPUCC_RBCPR_CLK] = &gpucc_rbcpr_clk.clkr,
+ [RBBMTIMER_CLK_SRC] = &rbbmtimer_clk_src.clkr,
+ [RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr,
+};
+
+static const struct regmap_config gpucc_falcon_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x9034,
+ .fast_io = true,
+};
+
+static const struct qcom_cc_desc gpucc_falcon_desc = {
+ .config = &gpucc_falcon_regmap_config,
+ .clks = gpucc_falcon_clocks,
+ .num_clks = ARRAY_SIZE(gpucc_falcon_clocks),
+};
+
+static const struct of_device_id gpucc_falcon_match_table[] = {
+ { .compatible = "qcom,gpucc-msmfalcon" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gpucc_falcon_match_table);
+
+static int of_get_fmax_vdd_class(struct platform_device *pdev,
+ struct clk_hw *hw, char *prop_name, u32 index)
+{
+ struct device_node *of = pdev->dev.of_node;
+ int prop_len, i, j;
+ struct clk_vdd_class *vdd = hw->init->vdd_class;
+ int num = vdd->num_regulators + 1;
+ u32 *array;
+
+ if (!of_find_property(of, prop_name, &prop_len)) {
+ dev_err(&pdev->dev, "missing %s\n", prop_name);
+ return -EINVAL;
+ }
+
+ prop_len /= sizeof(u32);
+ if (prop_len % num) {
+ dev_err(&pdev->dev, "bad length %d\n", prop_len);
+ return -EINVAL;
+ }
+
+ prop_len /= num;
+ vdd->level_votes = devm_kzalloc(&pdev->dev, prop_len * sizeof(int),
+ GFP_KERNEL);
+ if (!vdd->level_votes)
+ return -ENOMEM;
+
+ vdd->vdd_uv = devm_kzalloc(&pdev->dev,
+ prop_len * sizeof(int) * (num - 1), GFP_KERNEL);
+ if (!vdd->vdd_uv)
+ return -ENOMEM;
+
+ gpu_clks_init[index].fmax = devm_kzalloc(&pdev->dev, prop_len *
+ sizeof(unsigned long), GFP_KERNEL);
+ if (!gpu_clks_init[index].fmax)
+ return -ENOMEM;
+
+ array = devm_kzalloc(&pdev->dev, prop_len * sizeof(u32) * num,
+ GFP_KERNEL);
+ if (!array)
+ return -ENOMEM;
+
+ of_property_read_u32_array(of, prop_name, array, prop_len * num);
+ for (i = 0; i < prop_len; i++) {
+ gpu_clks_init[index].fmax[i] = array[num * i];
+ for (j = 1; j < num; j++) {
+ vdd->vdd_uv[(num - 1) * i + (j - 1)] =
+ array[num * i + j];
+ }
+ }
+
+ devm_kfree(&pdev->dev, array);
+ vdd->num_levels = prop_len;
+ vdd->cur_level = prop_len;
+ gpu_clks_init[index].num_fmax = prop_len;
+
+ return 0;
+}
+
+static int gpucc_falcon_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct regmap *regmap;
+
+ regmap = qcom_cc_map(pdev, &gpucc_falcon_desc);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* CX Regulator for RBBMTimer and RBCPR clock */
+ vdd_dig.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig_gfx");
+ if (IS_ERR(vdd_dig.regulator[0])) {
+ if (!(PTR_ERR(vdd_dig.regulator[0]) == -EPROBE_DEFER))
+ dev_err(&pdev->dev,
+ "Unable to get vdd_dig regulator\n");
+ return PTR_ERR(vdd_dig.regulator[0]);
+ }
+
+ /* Mx Regulator for GPU-PLLs */
+ vdd_mx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_mx_gfx");
+ if (IS_ERR(vdd_mx.regulator[0])) {
+ if (!(PTR_ERR(vdd_mx.regulator[0]) == -EPROBE_DEFER))
+ dev_err(&pdev->dev,
+ "Unable to get vdd_mx regulator\n");
+ return PTR_ERR(vdd_mx.regulator[0]);
+ }
+
+ /* GFX Rail Regulator for GFX3D clock */
+ vdd_gfx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_gfx");
+ if (IS_ERR(vdd_gfx.regulator[0])) {
+ if (!(PTR_ERR(vdd_gfx.regulator[0]) == -EPROBE_DEFER))
+ dev_err(&pdev->dev,
+ "Unable to get vdd_gfx regulator\n");
+ return PTR_ERR(vdd_gfx.regulator[0]);
+ }
+
+ /* GFX rail fmax data linked to branch clock */
+ of_get_fmax_vdd_class(pdev, &gpucc_gfx3d_clk.clkr.hw,
+ "qcom,gfxfreq-corner", 1);
+
+ clk_alpha_pll_configure(&gpu_pll0_pll_out_main, regmap,
+ &gpu_pll0_config);
+ clk_alpha_pll_configure(&gpu_pll1_pll_out_main, regmap,
+ &gpu_pll0_config);
+
+ ret = qcom_cc_really_probe(pdev, &gpucc_falcon_desc, regmap);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register GPUCC clocks\n");
+ return ret;
+ }
+
+ clk_prepare_enable(gpucc_cxo_clk.clkr.hw.clk);
+
+ dev_info(&pdev->dev, "Registered GPUCC clocks\n");
+
+ return ret;
+}
+
+static struct platform_driver gpucc_falcon_driver = {
+ .probe = gpucc_falcon_probe,
+ .driver = {
+ .name = "gpucc-msmfalcon",
+ .of_match_table = gpucc_falcon_match_table,
+ },
+};
+
+static int __init gpucc_falcon_init(void)
+{
+ return platform_driver_register(&gpucc_falcon_driver);
+}
+core_initcall_sync(gpucc_falcon_init);
+
+static void __exit gpucc_falcon_exit(void)
+{
+ platform_driver_unregister(&gpucc_falcon_driver);
+}
+module_exit(gpucc_falcon_exit);
diff --git a/drivers/clk/qcom/vdd-level-falcon.h b/drivers/clk/qcom/vdd-level-falcon.h
new file mode 100644
index 000000000000..e8699358cf91
--- /dev/null
+++ b/drivers/clk/qcom/vdd-level-falcon.h
@@ -0,0 +1,140 @@
+/*
+ * 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 __DRIVERS_CLK_QCOM_VDD_LEVEL_FALCON_H
+#define __DRIVERS_CLK_QCOM_VDD_LEVEL_FALCON_H
+
+#include <linux/regulator/rpm-smd-regulator.h>
+#include <linux/regulator/consumer.h>
+
+#define VDD_DIG_FMAX_MAP1(l1, f1) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+#define VDD_DIG_FMAX_MAP4(l1, f1, l2, f2, l3, f3, l4, f4) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ [VDD_DIG_##l4] = (f4), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+#define VDD_DIG_FMAX_MAP5(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ [VDD_DIG_##l4] = (f4), \
+ [VDD_DIG_##l5] = (f5), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+#define VDD_DIG_FMAX_MAP6(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5, l6, f6) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ [VDD_DIG_##l4] = (f4), \
+ [VDD_DIG_##l5] = (f5), \
+ [VDD_DIG_##l6] = (f6), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+#define VDD_DIG_FMAX_MAP7(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5, l6, f6, \
+ l7, f7) \
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ [VDD_DIG_##l4] = (f4), \
+ [VDD_DIG_##l5] = (f5), \
+ [VDD_DIG_##l6] = (f6), \
+ [VDD_DIG_##l7] = (f7), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+#define VDD_DIG_FMAX_MAP1_AO(l1, f1) \
+ .vdd_class = &vdd_dig_ao, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+#define VDD_DIG_FMAX_MAP3_AO(l1, f1, l2, f2, l3, f3) \
+ .vdd_class = &vdd_dig_ao, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+#define VDD_GPU_PLL_FMAX_MAP6(l1, f1, l2, f2, l3, f3, l4, f4, l5, f5, l6, f6) \
+ .vdd_class = &vdd_mx, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ [VDD_DIG_##l4] = (f4), \
+ [VDD_DIG_##l5] = (f5), \
+ [VDD_DIG_##l6] = (f6), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+enum vdd_dig_levels {
+ VDD_DIG_NONE,
+ VDD_DIG_MIN, /* MIN SVS */
+ VDD_DIG_LOWER, /* SVS2 */
+ VDD_DIG_LOW, /* SVS */
+ VDD_DIG_LOW_L1, /* SVSL1 */
+ VDD_DIG_NOMINAL, /* NOM */
+ VDD_DIG_NOMINAL_L1, /* NOM */
+ VDD_DIG_HIGH, /* TURBO */
+ VDD_DIG_NUM
+};
+
+static int vdd_corner[] = {
+ RPM_REGULATOR_LEVEL_NONE, /* VDD_DIG_NONE */
+ RPM_REGULATOR_LEVEL_MIN_SVS, /* VDD_DIG_MIN */
+ RPM_REGULATOR_LEVEL_LOW_SVS, /* VDD_DIG_LOWER */
+ RPM_REGULATOR_LEVEL_SVS, /* VDD_DIG_LOW */
+ RPM_REGULATOR_LEVEL_SVS_PLUS, /* VDD_DIG_LOW_L1 */
+ RPM_REGULATOR_LEVEL_NOM, /* VDD_DIG_NOMINAL */
+ RPM_REGULATOR_LEVEL_NOM_PLUS, /* VDD_DIG_NOMINAL */
+ RPM_REGULATOR_LEVEL_TURBO, /* VDD_DIG_HIGH */
+};
+
+#endif
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index b622b9541279..a045b9a940e8 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -625,12 +625,15 @@ static void smp_callback(void *v)
static int cpuidle_latency_notify(struct notifier_block *b,
unsigned long l, void *v)
{
- const struct cpumask *cpus;
+ struct cpumask cpus;
- cpus = v ?: cpu_online_mask;
+ if (v)
+ cpumask_andnot(&cpus, v, cpu_isolated_mask);
+ else
+ cpumask_andnot(&cpus, cpu_online_mask, cpu_isolated_mask);
preempt_disable();
- smp_call_function_many(cpus, smp_callback, NULL, 1);
+ smp_call_function_many(&cpus, smp_callback, NULL, 1);
preempt_enable();
return NOTIFY_OK;
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 2b0df3f0e04e..37e504381313 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,36 @@ struct lpm_debug {
struct lpm_cluster *lpm_root_node;
+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;
+ int64_t stime;
+};
+
+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 +356,79 @@ static enum hrtimer_restart lpm_hrtimer_cb(struct hrtimer *h)
return HRTIMER_NORESTART;
}
+static void histtimer_cancel(void)
+{
+ 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 cluster_timer_init(struct lpm_cluster *cluster)
+{
+ struct list_head *list;
+
+ if (!cluster)
+ return;
+
+ hrtimer_init(&cluster->histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+ list_for_each(list, &cluster->child) {
+ struct lpm_cluster *n;
+
+ n = list_entry(list, typeof(*n), list);
+ cluster_timer_init(n);
+ }
+}
+
+static void clusttimer_cancel(void)
+{
+ int cpu = raw_smp_processor_id();
+ struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+
+ hrtimer_try_to_cancel(&cluster->histtimer);
+ hrtimer_try_to_cancel(&cluster->parent->histtimer);
+}
+
+static enum hrtimer_restart clusttimer_fn(struct hrtimer *h)
+{
+ struct lpm_cluster *cluster = container_of(h,
+ struct lpm_cluster, histtimer);
+
+ cluster->history.hinvalid = 1;
+ return HRTIMER_NORESTART;
+}
+
+static void clusttimer_start(struct lpm_cluster *cluster, uint32_t time_us)
+{
+ uint64_t time_ns = time_us * NSEC_PER_USEC;
+ ktime_t clust_ktime = ns_to_ktime(time_ns);
+
+ cluster->histtimer.function = clusttimer_fn;
+ hrtimer_start(&cluster->histtimer, clust_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 +513,168 @@ 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;
+ history->stime = 0;
+ return 0;
+ }
+
+ /*
+ * Predict only when all the samples are collected.
+ */
+ if (history->nsamp < MAXSAMPLES) {
+ history->stime = 0;
+ 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) {
+ history->stime = ktime_to_us(ktime_get()) + avg;
+ 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;
+ history->stime = ktime_to_us(ktime_get())
+ + *idx_restrict_time;
+ 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;
+ history->stime = 0;
+ }
+}
+
+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;
+ history->stime = 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 +682,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,66 +699,88 @@ 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;
}
static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
- struct cpumask *mask, bool from_idle)
+ struct cpumask *mask, bool from_idle, uint32_t *pred_time)
{
int cpu;
int next_cpu = raw_smp_processor_id();
ktime_t next_event;
struct cpumask online_cpus_in_cluster;
+ struct lpm_history *history;
+ int64_t prediction = LONG_MAX;
next_event.tv64 = KTIME_MAX;
if (!suspend_wake_time)
@@ -538,11 +805,21 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
next_event.tv64 = next_event_c->tv64;
next_cpu = cpu;
}
+
+ if (from_idle && lpm_prediction) {
+ history = &per_cpu(hist, cpu);
+ if (history->stime && (history->stime < prediction))
+ prediction = history->stime;
+ }
}
if (mask)
cpumask_copy(mask, cpumask_of(next_cpu));
+ if (from_idle && lpm_prediction) {
+ if (prediction > ktime_to_us(ktime_get()))
+ *pred_time = prediction - ktime_to_us(ktime_get());
+ }
if (ktime_to_us(next_event) > ktime_to_us(ktime_get()))
return ktime_to_us(ktime_sub(next_event, ktime_get()));
@@ -550,20 +827,193 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
return 0;
}
-static int cluster_select(struct lpm_cluster *cluster, bool from_idle)
+static int cluster_predict(struct lpm_cluster *cluster,
+ uint32_t *pred_us)
+{
+ int i, j;
+ int ret = 0;
+ struct cluster_history *history = &cluster->history;
+ int64_t cur_time = ktime_to_us(ktime_get());
+
+ if (!lpm_prediction)
+ return 0;
+
+ if (history->hinvalid) {
+ history->hinvalid = 0;
+ history->htmr_wkup = 1;
+ history->flag = 0;
+ return ret;
+ }
+
+ if (history->nsamp == MAXSAMPLES) {
+ for (i = 0; i < MAXSAMPLES; i++) {
+ if ((cur_time - history->stime[i])
+ > CLUST_SMPL_INVLD_TIME)
+ history->nsamp--;
+ }
+ }
+
+ if (history->nsamp < MAXSAMPLES) {
+ history->flag = 0;
+ return ret;
+ }
+
+ if (history->flag == 2)
+ history->flag = 0;
+
+ if (history->htmr_wkup != 1) {
+ uint64_t total = 0;
+
+ if (history->flag == 1) {
+ for (i = 0; i < MAXSAMPLES; i++)
+ total += history->resi[i];
+ do_div(total, MAXSAMPLES);
+ *pred_us = total;
+ return 2;
+ }
+
+ for (j = 1; j < cluster->nlevels; j++) {
+ uint32_t failed = 0;
+
+ total = 0;
+ for (i = 0; i < MAXSAMPLES; i++) {
+ if ((history->mode[i] == j) && (history->resi[i]
+ < cluster->levels[j].pwr.min_residency)) {
+ failed++;
+ total += history->resi[i];
+ }
+ }
+
+ if (failed > (MAXSAMPLES-2)) {
+ do_div(total, failed);
+ *pred_us = total;
+ history->flag = 1;
+ return 1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void update_cluster_history_time(struct cluster_history *history,
+ int idx, uint64_t start)
+{
+ history->entry_idx = idx;
+ history->entry_time = start;
+}
+
+static void update_cluster_history(struct cluster_history *history, int idx)
+{
+ uint32_t tmr = 0;
+ uint32_t residency = 0;
+ struct lpm_cluster *cluster =
+ container_of(history, struct lpm_cluster, history);
+
+ if (!lpm_prediction)
+ return;
+
+ if ((history->entry_idx == -1) || (history->entry_idx == idx)) {
+ residency = ktime_to_us(ktime_get()) - history->entry_time;
+ history->stime[history->hptr] = history->entry_time;
+ } else
+ return;
+
+ if (history->htmr_wkup) {
+ if (!history->hptr)
+ history->hptr = MAXSAMPLES-1;
+ else
+ history->hptr--;
+
+ history->resi[history->hptr] += residency;
+
+ history->htmr_wkup = 0;
+ tmr = 1;
+ } else {
+ history->resi[history->hptr] = residency;
+ }
+
+ history->mode[history->hptr] = idx;
+
+ history->entry_idx = INT_MIN;
+ history->entry_time = 0;
+
+ if (history->nsamp < MAXSAMPLES)
+ history->nsamp++;
+
+ trace_cluster_pred_hist(cluster->cluster_name,
+ history->mode[history->hptr], history->resi[history->hptr],
+ history->hptr, tmr);
+
+ (history->hptr)++;
+
+ if (history->hptr >= MAXSAMPLES)
+ history->hptr = 0;
+}
+
+static void clear_cl_history_each(struct cluster_history *history)
+{
+ int i;
+
+ for (i = 0; i < MAXSAMPLES; i++) {
+ history->resi[i] = 0;
+ history->mode[i] = -1;
+ history->stime[i] = 0;
+ }
+ history->hptr = 0;
+ history->nsamp = 0;
+ history->flag = 0;
+ history->hinvalid = 0;
+ history->htmr_wkup = 0;
+}
+
+static void clear_cl_predict_history(void)
+{
+ struct lpm_cluster *cluster = lpm_root_node;
+ struct list_head *list;
+
+ if (!lpm_prediction)
+ return;
+
+ clear_cl_history_each(&cluster->history);
+
+ list_for_each(list, &cluster->child) {
+ struct lpm_cluster *n;
+
+ n = list_entry(list, typeof(*n), list);
+ clear_cl_history_each(&n->history);
+ }
+}
+
+static int cluster_select(struct lpm_cluster *cluster, bool from_idle,
+ int *ispred)
{
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;
+ uint32_t cpupred_us = 0, pred_us = 0;
+ int pred_mode = 0, predicted = 0;
if (!cluster)
return -EINVAL;
- sleep_us = (uint32_t)get_cluster_sleep_time(cluster, NULL, from_idle);
+ sleep_us = (uint32_t)get_cluster_sleep_time(cluster, NULL,
+ from_idle, &cpupred_us);
+
+ if (from_idle) {
+ pred_mode = cluster_predict(cluster, &pred_us);
+
+ if (cpupred_us && pred_mode && (cpupred_us < pred_us))
+ pred_us = cpupred_us;
+
+ if (pred_us && pred_mode && (pred_us < sleep_us))
+ predicted = 1;
+
+ if (predicted && (pred_us == cpupred_us))
+ predicted = 2;
+ }
if (cpumask_and(&mask, cpu_online_mask, &cluster->child_cpus))
latency_us = pm_qos_request_for_cpumask(PM_QOS_CPU_DMA_LATENCY,
@@ -596,10 +1046,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,21 +1057,21 @@ 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 (predicted ? (pred_us <= pwr_params->max_residency)
+ : (sleep_us <= pwr_params->max_residency))
+ break;
}
+ if ((best_level == (cluster->nlevels - 1)) && (pred_mode == 2))
+ cluster->history.flag = 2;
+
+ *ispred = predicted;
+
+ trace_cluster_pred_select(cluster->cluster_name, best_level, sleep_us,
+ latency_us, predicted, pred_us);
+
return best_level;
}
@@ -635,7 +1085,7 @@ static void cluster_notify(struct lpm_cluster *cluster,
}
static int cluster_configure(struct lpm_cluster *cluster, int idx,
- bool from_idle)
+ bool from_idle, int predicted)
{
struct lpm_cluster_level *level = &cluster->levels[idx];
int ret, i;
@@ -653,6 +1103,10 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
cluster->num_children_in_sync.bits[0],
cluster->child_cpus.bits[0], from_idle);
lpm_stats_cluster_enter(cluster->stats, idx);
+
+ if (from_idle && lpm_prediction)
+ update_cluster_history_time(&cluster->history, idx,
+ ktime_to_us(ktime_get()));
}
for (i = 0; i < cluster->ndevices; i++) {
@@ -664,8 +1118,10 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
if (level->notify_rpm) {
struct cpumask nextcpu, *cpumask;
uint64_t us;
+ uint32_t pred_us;
- us = get_cluster_sleep_time(cluster, &nextcpu, from_idle);
+ us = get_cluster_sleep_time(cluster, &nextcpu,
+ from_idle, &pred_us);
cpumask = level->disable_dynamic_routing ? NULL : &nextcpu;
ret = msm_rpm_enter_sleep(0, cpumask);
@@ -674,6 +1130,10 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
goto failed_set_mode;
}
+ us = us + 1;
+ clear_predict_history();
+ clear_cl_predict_history();
+
do_div(us, USEC_PER_SEC/SCLK_HZ);
msm_mpm_enter_sleep(us, from_idle, cpumask);
}
@@ -684,6 +1144,15 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
sched_set_cluster_dstate(&cluster->child_cpus, idx, 0, 0);
cluster->last_level = idx;
+
+ if (predicted && (idx < (cluster->nlevels - 1))) {
+ struct power_params *pwr_params = &cluster->levels[idx].pwr;
+
+ tick_broadcast_exit();
+ clusttimer_start(cluster, pwr_params->max_residency + tmr_add);
+ tick_broadcast_enter();
+ }
+
return 0;
failed_set_mode:
@@ -702,6 +1171,7 @@ static void cluster_prepare(struct lpm_cluster *cluster,
int64_t start_time)
{
int i;
+ int predicted = 0;
if (!cluster)
return;
@@ -732,12 +1202,28 @@ static void cluster_prepare(struct lpm_cluster *cluster,
&cluster->child_cpus))
goto failed;
- i = cluster_select(cluster, from_idle);
+ i = cluster_select(cluster, from_idle, &predicted);
+
+ if (((i < 0) || (i == cluster->default_level))
+ && predicted && from_idle) {
+ update_cluster_history_time(&cluster->history,
+ -1, ktime_to_us(ktime_get()));
+
+ if (i < 0) {
+ struct power_params *pwr_params =
+ &cluster->levels[0].pwr;
+
+ tick_broadcast_exit();
+ clusttimer_start(cluster,
+ pwr_params->max_residency + tmr_add);
+ tick_broadcast_enter();
+ }
+ }
if (i < 0)
goto failed;
- if (cluster_configure(cluster, i, from_idle))
+ if (cluster_configure(cluster, i, from_idle, predicted))
goto failed;
cluster->stats->sleep_time = start_time;
@@ -781,6 +1267,10 @@ static void cluster_unprepare(struct lpm_cluster *cluster,
&lvl->num_cpu_votes, cpu);
}
+ if (from_idle && first_cpu &&
+ (cluster->last_level == cluster->default_level))
+ update_cluster_history(&cluster->history, cluster->last_level);
+
if (!first_cpu || cluster->last_level == cluster->default_level)
goto unlock_return;
@@ -822,6 +1312,10 @@ static void cluster_unprepare(struct lpm_cluster *cluster,
sched_set_cluster_dstate(&cluster->child_cpus, 0, 0, 0);
cluster_notify(cluster, &cluster->levels[last_level], false);
+
+ if (from_idle)
+ update_cluster_history(&cluster->history, last_level);
+
cluster_unprepare(cluster->parent, &cluster->child_cpus,
last_level, from_idle, end_time);
unlock_return:
@@ -1008,6 +1502,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)
{
@@ -1042,12 +1569,16 @@ 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();
-
+ if (lpm_prediction) {
+ histtimer_cancel();
+ clusttimer_cancel();
+ }
return idx;
}
@@ -1319,6 +1850,8 @@ 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);
+ cluster_timer_init(lpm_root_node);
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..3c9665ea8981 100644
--- a/drivers/cpuidle/lpm-levels.h
+++ b/drivers/cpuidle/lpm-levels.h
@@ -14,6 +14,8 @@
#include <soc/qcom/spm.h>
#define NR_LPM_LEVELS 8
+#define MAXSAMPLES 5
+#define CLUST_SMPL_INVLD_TIME 40000
extern bool use_psci;
@@ -27,6 +29,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 +60,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 {
@@ -79,6 +87,19 @@ struct low_power_ops {
enum msm_pm_l2_scm_flag tz_flag;
};
+struct cluster_history {
+ uint32_t resi[MAXSAMPLES];
+ int mode[MAXSAMPLES];
+ int64_t stime[MAXSAMPLES];
+ uint32_t hptr;
+ uint32_t hinvalid;
+ uint32_t htmr_wkup;
+ uint64_t entry_time;
+ int entry_idx;
+ int nsamp;
+ int flag;
+};
+
struct lpm_cluster {
struct list_head list;
struct list_head child;
@@ -103,6 +124,8 @@ struct lpm_cluster {
unsigned int psci_mode_shift;
unsigned int psci_mode_mask;
bool no_saw_devices;
+ struct cluster_history history;
+ struct hrtimer histtimer;
};
int set_l2_mode(struct low_power_ops *ops, int mode, bool notify_rpm);
@@ -119,7 +142,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/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 9b42e5ae129a..3562de7fc967 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -94,7 +94,6 @@ enum qce_owner {
struct dummy_request {
struct qce_sha_req sreq;
- uint8_t *in_buf;
struct scatterlist sg;
struct ahash_request areq;
};
@@ -154,6 +153,7 @@ struct qce_device {
atomic_t bunch_cmd_seq;
atomic_t last_intr_seq;
bool cadence_flag;
+ uint8_t *dummyreq_in_buf;
};
static void print_notify_debug(struct sps_event_notify *notify);
@@ -4351,8 +4351,6 @@ static int qce_setup_ce_sps_data(struct qce_device *pce_dev)
(uintptr_t)vaddr;
vaddr += pce_dev->ce_bam_info.ce_burst_size * 2;
}
- pce_dev->dummyreq.in_buf = (uint8_t *)vaddr;
- vaddr += DUMMY_REQ_DATA_LEN;
if ((vaddr - pce_dev->coh_vmem) > pce_dev->memsize ||
iovec_memsize < 0)
panic("qce50: Not enough coherent memory. Allocate %x , need %lx\n",
@@ -5887,8 +5885,8 @@ static int setup_dummy_req(struct qce_device *pce_dev)
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopqopqrpqrs";
int len = DUMMY_REQ_DATA_LEN;
- memcpy(pce_dev->dummyreq.in_buf, input, len);
- sg_set_buf(&pce_dev->dummyreq.sg, pce_dev->dummyreq.in_buf, len);
+ memcpy(pce_dev->dummyreq_in_buf, input, len);
+ sg_set_buf(&pce_dev->dummyreq.sg, pce_dev->dummyreq_in_buf, len);
sg_mark_end(&pce_dev->dummyreq.sg);
pce_dev->dummyreq.sreq.alg = QCE_HASH_SHA1;
@@ -5957,6 +5955,10 @@ void *qce_open(struct platform_device *pdev, int *rc)
if (pce_dev->iovec_vmem == NULL)
goto err_mem;
+ pce_dev->dummyreq_in_buf = kzalloc(DUMMY_REQ_DATA_LEN, GFP_KERNEL);
+ if (pce_dev->dummyreq_in_buf == NULL)
+ goto err_mem;
+
*rc = __qce_init_clk(pce_dev);
if (*rc)
goto err_mem;
@@ -5996,6 +5998,7 @@ err_enable_clk:
__qce_deinit_clk(pce_dev);
err_mem:
+ kfree(pce_dev->dummyreq_in_buf);
kfree(pce_dev->iovec_vmem);
if (pce_dev->coh_vmem)
dma_free_coherent(pce_dev->pdev, pce_dev->memsize,
@@ -6027,6 +6030,7 @@ int qce_close(void *handle)
if (pce_dev->coh_vmem)
dma_free_coherent(pce_dev->pdev, pce_dev->memsize,
pce_dev->coh_vmem, pce_dev->coh_pmem);
+ kfree(pce_dev->dummyreq_in_buf);
kfree(pce_dev->iovec_vmem);
qce_disable_clk(pce_dev);
diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c
index 707a244e62e9..315d3a67e43e 100644
--- a/drivers/devfreq/bimc-bwmon.c
+++ b/drivers/devfreq/bimc-bwmon.c
@@ -377,7 +377,7 @@ static unsigned long mon_get_zone_stats(struct bwmon *m)
zone = get_zone(m);
- count = readl_relaxed(MON2_ZONE_MAX(m, zone));
+ count = readl_relaxed(MON2_ZONE_MAX(m, zone)) + 1;
count *= SZ_1M;
dev_dbg(m->dev, "Zone%d Max byte count: %08lx\n", zone, count);
diff --git a/drivers/edac/cortex_arm64_edac.c b/drivers/edac/cortex_arm64_edac.c
index 223941a038fd..28397c191583 100644
--- a/drivers/edac/cortex_arm64_edac.c
+++ b/drivers/edac/cortex_arm64_edac.c
@@ -714,7 +714,6 @@ static void arm64_sbe_handler(struct perf_event *event,
errdata.err = SBE;
edac_printk(KERN_CRIT, EDAC_CPU, "ARM64 CPU ERP: Single-bit error interrupt received on CPU %d!\n",
cpu);
- WARN_ON(!panic_on_ce);
arm64_erp_local_handler(&errdata);
}
#endif
diff --git a/drivers/gpu/msm/a4xx_reg.h b/drivers/gpu/msm/a4xx_reg.h
index 4b69583a6ce1..8e658c1d54d2 100644
--- a/drivers/gpu/msm/a4xx_reg.h
+++ b/drivers/gpu/msm/a4xx_reg.h
@@ -197,6 +197,7 @@ enum a4xx_rb_perfctr_rb_sel {
#define A4XX_RBBM_CFG_DEBBUS_CLRC 0x94
#define A4XX_RBBM_CFG_DEBBUS_LOADIVT 0x95
+#define A4XX_RBBM_CLOCK_CTL_IP 0x97
#define A4XX_RBBM_POWER_CNTL_IP 0x98
#define A4XX_RBBM_SP_REGFILE_SLEEP_CNTL_0 0x99
#define A4XX_RBBM_SP_REGFILE_SLEEP_CNTL_1 0x9a
diff --git a/drivers/gpu/msm/a5xx_reg.h b/drivers/gpu/msm/a5xx_reg.h
index cedd02987002..f3b4e6622043 100644
--- a/drivers/gpu/msm/a5xx_reg.h
+++ b/drivers/gpu/msm/a5xx_reg.h
@@ -640,6 +640,7 @@
/* UCHE registers */
#define A5XX_UCHE_ADDR_MODE_CNTL 0xE80
+#define A5XX_UCHE_MODE_CNTL 0xE81
#define A5XX_UCHE_WRITE_THRU_BASE_LO 0xE87
#define A5XX_UCHE_WRITE_THRU_BASE_HI 0xE88
#define A5XX_UCHE_TRAP_BASE_LO 0xE89
@@ -788,6 +789,9 @@
/* COUNTABLE FOR TSE PERFCOUNTER */
#define A5XX_TSE_INPUT_PRIM_NUM 0x6
+/* COUNTABLE FOR RBBM PERFCOUNTER */
+#define A5XX_RBBM_ALWAYS_COUNT 0x0
+
/* GPMU POWER COUNTERS */
#define A5XX_SP_POWER_COUNTER_0_LO 0xA840
#define A5XX_SP_POWER_COUNTER_0_HI 0xA841
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 362493118670..1356835d0e93 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -2728,6 +2728,14 @@ static void adreno_pwrlevel_change_settings(struct kgsl_device *device,
postlevel, post);
}
+static void adreno_clk_set_options(struct kgsl_device *device, const char *name,
+ struct clk *clk)
+{
+ if (ADRENO_GPU_DEVICE(ADRENO_DEVICE(device))->clk_set_options)
+ ADRENO_GPU_DEVICE(ADRENO_DEVICE(device))->clk_set_options(
+ ADRENO_DEVICE(device), name, clk);
+}
+
static void adreno_iommu_sync(struct kgsl_device *device, bool sync)
{
struct scm_desc desc = {0};
@@ -2791,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,
@@ -2827,6 +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,
+ .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 cbbfc57e27f4..d4858f3f818e 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -766,6 +766,8 @@ struct adreno_gpudev {
void (*preemption_schedule)(struct adreno_device *);
void (*enable_64bit)(struct adreno_device *);
void (*pre_reset)(struct adreno_device *);
+ void (*clk_set_options)(struct adreno_device *,
+ const char *, struct clk *);
};
/**
@@ -1013,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_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c
index 7a691667e59f..bfbdb0e7ac1f 100644
--- a/drivers/gpu/msm/adreno_a4xx.c
+++ b/drivers/gpu/msm/adreno_a4xx.c
@@ -26,6 +26,8 @@
#include "adreno_perfcounter.h"
#define SP_TP_PWR_ON BIT(20)
+/* A4XX_RBBM_CLOCK_CTL_IP */
+#define CNTL_IP_SW_COLLAPSE BIT(0)
/*
* Define registers for a4xx that contain addresses used by the
@@ -201,6 +203,131 @@ static bool a4xx_is_sptp_idle(struct adreno_device *adreno_dev)
}
/*
+ * a4xx_enable_hwcg() - Program the clock control registers
+ * @device: The adreno device pointer
+ */
+static void a4xx_enable_hwcg(struct kgsl_device *device)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_TP0, 0x02222202);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_TP1, 0x02222202);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_TP2, 0x02222202);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_TP3, 0x02222202);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_TP0, 0x00002222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_TP1, 0x00002222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_TP2, 0x00002222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_TP3, 0x00002222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_TP0, 0x0E739CE7);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_TP1, 0x0E739CE7);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_TP2, 0x0E739CE7);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_TP3, 0x0E739CE7);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_TP0, 0x00111111);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_TP1, 0x00111111);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_TP2, 0x00111111);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_TP3, 0x00111111);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_SP0, 0x22222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_SP1, 0x22222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_SP2, 0x22222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_SP3, 0x22222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_SP0, 0x00222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_SP1, 0x00222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_SP2, 0x00222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_SP3, 0x00222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_SP0, 0x00000104);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_SP1, 0x00000104);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_SP2, 0x00000104);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_SP3, 0x00000104);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_SP0, 0x00000081);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_SP1, 0x00000081);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_SP2, 0x00000081);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_SP3, 0x00000081);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_UCHE, 0x22222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_UCHE, 0x02222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL3_UCHE, 0x00000000);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL4_UCHE, 0x00000000);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_UCHE, 0x00004444);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_UCHE, 0x00001112);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_RB0, 0x22222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_RB1, 0x22222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_RB2, 0x22222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_RB3, 0x22222222);
+ /* Disable L1 clocking in A420 due to CCU issues with it */
+ if (adreno_is_a420(adreno_dev)) {
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB0, 0x00002020);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB1, 0x00002020);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB2, 0x00002020);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB3, 0x00002020);
+ } else {
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB0, 0x00022020);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB1, 0x00022020);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB2, 0x00022020);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB3, 0x00022020);
+ }
+ /* No CCU for A405 */
+ if (!adreno_is_a405(adreno_dev)) {
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_CTL_MARB_CCU0, 0x00000922);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_CTL_MARB_CCU1, 0x00000922);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_CTL_MARB_CCU2, 0x00000922);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_CTL_MARB_CCU3, 0x00000922);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_HYST_RB_MARB_CCU0, 0x00000000);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_HYST_RB_MARB_CCU1, 0x00000000);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_HYST_RB_MARB_CCU2, 0x00000000);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_HYST_RB_MARB_CCU3, 0x00000000);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_0,
+ 0x00000001);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_1,
+ 0x00000001);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_2,
+ 0x00000001);
+ kgsl_regwrite(device,
+ A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_3,
+ 0x00000001);
+ }
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_MODE_GPC, 0x02222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_GPC, 0x04100104);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_GPC, 0x00022222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_COM_DCOM, 0x00000022);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_COM_DCOM, 0x0000010F);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_COM_DCOM, 0x00000022);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_TSE_RAS_RBBM, 0x00222222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_TSE_RAS_RBBM, 0x00004104);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_TSE_RAS_RBBM, 0x00000222);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_HLSQ, 0x00000000);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_HLSQ, 0x00000000);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_HLSQ, 0x00220000);
+ /*
+ * Due to a HW timing issue, top level HW clock gating is causing
+ * register read/writes to be dropped in adreno a430.
+ * This timing issue started happening because of SP/TP power collapse.
+ * On targets that do not have SP/TP PC there is no timing issue.
+ * The HW timing issue could be fixed by
+ * a) disabling SP/TP power collapse
+ * b) or disabling HW clock gating.
+ * Disabling HW clock gating + NAP enabled combination has
+ * minimal power impact. So this option is chosen over disabling
+ * SP/TP power collapse.
+ * Revisions of A430 which chipid 2 and above do not have the issue.
+ */
+ if (adreno_is_a430(adreno_dev) &&
+ (ADRENO_CHIPID_PATCH(adreno_dev->chipid) < 2))
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL, 0);
+ else
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL, 0xAAAAAAAA);
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2, 0);
+}
+/*
* a4xx_regulator_enable() - Enable any necessary HW regulators
* @adreno_dev: The adreno device pointer
*
@@ -212,8 +339,12 @@ static int a4xx_regulator_enable(struct adreno_device *adreno_dev)
unsigned int reg;
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
- if (!(adreno_is_a430(adreno_dev) || adreno_is_a418(adreno_dev)))
+ if (!(adreno_is_a430(adreno_dev) || adreno_is_a418(adreno_dev))) {
+ /* Halt the sp_input_clk at HM level */
+ kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL, 0x00000055);
+ a4xx_enable_hwcg(device);
return 0;
+ }
/* Set the default register values; set SW_COLLAPSE to 0 */
kgsl_regwrite(device, A4XX_RBBM_POWER_CNTL_IP, 0x778000);
@@ -221,6 +352,13 @@ static int a4xx_regulator_enable(struct adreno_device *adreno_dev)
udelay(5);
kgsl_regread(device, A4XX_RBBM_POWER_STATUS, &reg);
} while (!(reg & SP_TP_PWR_ON));
+
+ /* Disable SP clock */
+ kgsl_regrmw(device, A4XX_RBBM_CLOCK_CTL_IP, CNTL_IP_SW_COLLAPSE, 0);
+ /* Enable hardware clockgating */
+ a4xx_enable_hwcg(device);
+ /* Enable SP clock */
+ kgsl_regrmw(device, A4XX_RBBM_CLOCK_CTL_IP, CNTL_IP_SW_COLLAPSE, 1);
return 0;
}
@@ -328,131 +466,6 @@ static void a4xx_pwrlevel_change_settings(struct adreno_device *adreno_dev,
pre = 0;
}
-/*
- * a4xx_enable_hwcg() - Program the clock control registers
- * @device: The adreno device pointer
- */
-static void a4xx_enable_hwcg(struct kgsl_device *device)
-{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_TP0, 0x02222202);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_TP1, 0x02222202);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_TP2, 0x02222202);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_TP3, 0x02222202);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_TP0, 0x00002222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_TP1, 0x00002222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_TP2, 0x00002222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_TP3, 0x00002222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_TP0, 0x0E739CE7);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_TP1, 0x0E739CE7);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_TP2, 0x0E739CE7);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_TP3, 0x0E739CE7);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_TP0, 0x00111111);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_TP1, 0x00111111);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_TP2, 0x00111111);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_TP3, 0x00111111);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_SP0, 0x22222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_SP1, 0x22222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_SP2, 0x22222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_SP3, 0x22222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_SP0, 0x00222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_SP1, 0x00222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_SP2, 0x00222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_SP3, 0x00222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_SP0, 0x00000104);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_SP1, 0x00000104);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_SP2, 0x00000104);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_SP3, 0x00000104);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_SP0, 0x00000081);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_SP1, 0x00000081);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_SP2, 0x00000081);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_SP3, 0x00000081);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_UCHE, 0x22222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_UCHE, 0x02222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL3_UCHE, 0x00000000);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL4_UCHE, 0x00000000);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_UCHE, 0x00004444);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_UCHE, 0x00001112);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_RB0, 0x22222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_RB1, 0x22222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_RB2, 0x22222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_RB3, 0x22222222);
- /* Disable L1 clocking in A420 due to CCU issues with it */
- if (adreno_is_a420(adreno_dev)) {
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB0, 0x00002020);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB1, 0x00002020);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB2, 0x00002020);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB3, 0x00002020);
- } else {
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB0, 0x00022020);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB1, 0x00022020);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB2, 0x00022020);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2_RB3, 0x00022020);
- }
- /* No CCU for A405 */
- if (!adreno_is_a405(adreno_dev)) {
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_CTL_MARB_CCU0, 0x00000922);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_CTL_MARB_CCU1, 0x00000922);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_CTL_MARB_CCU2, 0x00000922);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_CTL_MARB_CCU3, 0x00000922);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_HYST_RB_MARB_CCU0, 0x00000000);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_HYST_RB_MARB_CCU1, 0x00000000);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_HYST_RB_MARB_CCU2, 0x00000000);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_HYST_RB_MARB_CCU3, 0x00000000);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_0,
- 0x00000001);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_1,
- 0x00000001);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_2,
- 0x00000001);
- kgsl_regwrite(device,
- A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_3,
- 0x00000001);
- }
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_MODE_GPC, 0x02222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_GPC, 0x04100104);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_GPC, 0x00022222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_COM_DCOM, 0x00000022);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_COM_DCOM, 0x0000010F);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_COM_DCOM, 0x00000022);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_TSE_RAS_RBBM, 0x00222222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_TSE_RAS_RBBM, 0x00004104);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_TSE_RAS_RBBM, 0x00000222);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL_HLSQ , 0x00000000);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_HYST_HLSQ, 0x00000000);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_DELAY_HLSQ, 0x00220000);
- /*
- * Due to a HW timing issue, top level HW clock gating is causing
- * register read/writes to be dropped in adreno a430.
- * This timing issue started happening because of SP/TP power collapse.
- * On targets that do not have SP/TP PC there is no timing issue.
- * The HW timing issue could be fixed by
- * a) disabling SP/TP power collapse
- * b) or disabling HW clock gating.
- * Disabling HW clock gating + NAP enabled combination has
- * minimal power impact. So this option is chosen over disabling
- * SP/TP power collapse.
- * Revisions of A430 which chipid 2 and above do not have the issue.
- */
- if (adreno_is_a430(adreno_dev) &&
- (ADRENO_CHIPID_PATCH(adreno_dev->chipid) < 2))
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL, 0);
- else
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL, 0xAAAAAAAA);
- kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2, 0);
-}
-
/**
* a4xx_protect_init() - Initializes register protection on a4xx
* @adreno_dev: Pointer to the device structure
@@ -597,7 +610,6 @@ static void a4xx_start(struct adreno_device *adreno_dev)
0x00000441);
}
- a4xx_enable_hwcg(device);
/*
* For A420 set RBBM_CLOCK_DELAY_HLSQ.CGC_HLSQ_TP_EARLY_CYC >= 2
* due to timing issue with HLSQ_TP_CLK_EN
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 583de85678fc..d52981d10ff5 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -14,6 +14,7 @@
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/scm.h>
#include <linux/pm_opp.h>
+#include <linux/clk/msm-clk.h>
#include "adreno.h"
#include "a5xx_reg.h"
@@ -439,7 +440,11 @@ static int a5xx_regulator_enable(struct adreno_device *adreno_dev)
unsigned int ret;
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
if (!(adreno_is_a530(adreno_dev) || adreno_is_a540(adreno_dev))) {
+ /* Halt the sp_input_clk at HM level */
+ kgsl_regwrite(device, A5XX_RBBM_CLOCK_CNTL, 0x00000055);
a5xx_hwcg_set(adreno_dev, true);
+ /* Turn on sp_input_clk at HM level */
+ kgsl_regrmw(device, A5XX_RBBM_CLOCK_CNTL, 3, 0);
return 0;
}
@@ -1627,6 +1632,21 @@ static void a5xx_pwrlevel_change_settings(struct adreno_device *adreno_dev,
}
}
+static void a5xx_clk_set_options(struct adreno_device *adreno_dev,
+ const char *name, struct clk *clk)
+{
+ if (adreno_is_a540(adreno_dev)) {
+ if (!strcmp(name, "mem_iface_clk"))
+ clk_set_flags(clk, CLKFLAG_NORETAIN_PERIPH);
+ clk_set_flags(clk, CLKFLAG_NORETAIN_MEM);
+ if (!strcmp(name, "core_clk")) {
+ clk_set_flags(clk, CLKFLAG_NORETAIN_PERIPH);
+ clk_set_flags(clk, CLKFLAG_NORETAIN_MEM);
+ }
+ }
+}
+
+
static void a5xx_enable_64bit(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
@@ -1855,6 +1875,11 @@ static void a5xx_start(struct adreno_device *adreno_dev)
*/
kgsl_regrmw(device, A5XX_RB_DBG_ECO_CNT, 0, (1 << 9));
}
+ /*
+ * Disable UCHE global filter as SP can invalidate/flush
+ * independently
+ */
+ kgsl_regwrite(device, A5XX_UCHE_MODE_CNTL, BIT(29));
/* Set the USE_RETENTION_FLOPS chicken bit */
kgsl_regwrite(device, A5XX_CP_CHICKEN_DBG, 0x02000000);
@@ -2127,9 +2152,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 */
}
@@ -3516,4 +3543,5 @@ struct adreno_gpudev adreno_a5xx_gpudev = {
.preemption_schedule = a5xx_preemption_schedule,
.enable_64bit = a5xx_enable_64bit,
.pre_reset = a5xx_pre_reset,
+ .clk_set_options = a5xx_clk_set_options,
};
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 5d3b2b8a7266..522c32743d3d 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -1814,7 +1814,7 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev)
}
}
- if (!adreno_cmdqueue_is_empty(dispatch_q)) {
+ if (dispatch_q && !adreno_cmdqueue_is_empty(dispatch_q)) {
cmdbatch = dispatch_q->cmd_q[dispatch_q->head];
trace_adreno_cmdbatch_fault(cmdbatch, fault);
}
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index fb95f6108fb8..d9ebe37d0cf0 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -499,13 +499,20 @@ void adreno_drawctxt_detach(struct kgsl_context *context)
/*
* If the wait for global fails due to timeout then nothing after this
- * point is likely to work very well - BUG_ON() so we can take advantage
- * of the debug tools to figure out what the h - e - double hockey
- * sticks happened. If EAGAIN error is returned then recovery will kick
- * in and there will be no more commands in the RB pipe from this
- * context which is waht we are waiting for, so ignore -EAGAIN error
+ * point is likely to work very well - Get GPU snapshot and BUG_ON()
+ * so we can take advantage of the debug tools to figure out what the
+ * h - e - double hockey sticks happened. If EAGAIN error is returned
+ * then recovery will kick in and there will be no more commands in the
+ * RB pipe from this context which is waht we are waiting for, so ignore
+ * -EAGAIN error
*/
- BUG_ON(ret && ret != -EAGAIN);
+ if (ret && ret != -EAGAIN) {
+ KGSL_DRV_ERR(device, "Wait for global ts=%d type=%d error=%d\n",
+ drawctxt->internal_timestamp,
+ drawctxt->type, ret);
+ device->force_panic = 1;
+ kgsl_device_snapshot(device, context);
+ }
kgsl_sharedmem_writel(device, &device->memstore,
KGSL_MEMSTORE_OFFSET(context->id, soptimestamp),
diff --git a/drivers/gpu/msm/adreno_perfcounter.c b/drivers/gpu/msm/adreno_perfcounter.c
index 10f4577c1103..8e354d71a291 100644
--- a/drivers/gpu/msm/adreno_perfcounter.c
+++ b/drivers/gpu/msm/adreno_perfcounter.c
@@ -812,6 +812,11 @@ static int adreno_perfcounter_enable(struct adreno_device *adreno_dev,
case KGSL_PERFCOUNTER_GROUP_ALWAYSON_PWR:
_power_counter_enable_alwayson(adreno_dev, counters);
break;
+ case KGSL_PERFCOUNTER_GROUP_RBBM:
+ /* The following rbbm countable is not reliable on a540 */
+ if (adreno_is_a540(adreno_dev))
+ if (countable == A5XX_RBBM_ALWAYS_COUNT)
+ return -EINVAL;
default:
return _perfcounter_enable_default(adreno_dev, counters, group,
counter, countable);
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 0eff3da0e494..08d7d3c1b935 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -108,7 +108,7 @@ static void push_object(int type,
}
/*
- * Return a 1 if the specified object is already on the list of buffers
+ * Returns index of the specified object is already on the list of buffers
* to be dumped
*/
@@ -120,10 +120,9 @@ static int find_object(int type, uint64_t gpuaddr,
for (index = 0; index < objbufptr; index++) {
if (objbuf[index].gpuaddr == gpuaddr &&
objbuf[index].entry->priv == process)
- return 1;
+ return index;
}
-
- return 0;
+ return -ENOENT;
}
/*
@@ -196,8 +195,6 @@ static inline void parse_ib(struct kgsl_device *device,
struct kgsl_process_private *process,
uint64_t gpuaddr, uint64_t dwords)
{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- uint64_t ib1base;
struct adreno_ib_object_list *ib_obj_list;
/*
@@ -205,11 +202,7 @@ static inline void parse_ib(struct kgsl_device *device,
* then push it into the static blob otherwise put it in the dynamic
* list
*/
-
- adreno_readreg64(adreno_dev, ADRENO_REG_CP_IB1_BASE,
- ADRENO_REG_CP_IB1_BASE_HI, &ib1base);
-
- if (gpuaddr == ib1base) {
+ if (gpuaddr == snapshot->ib1base) {
push_object(SNAPSHOT_OBJ_TYPE_IB, process,
gpuaddr, dwords);
return;
@@ -295,17 +288,12 @@ static void snapshot_rb_ibs(struct kgsl_device *device,
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
unsigned int rptr, *rbptr;
- uint64_t ibbase;
int index, i;
int parse_ibs = 0, ib_parse_start;
/* Get the current read pointers for the RB */
adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, &rptr);
- /* Address of the last processed IB */
- adreno_readreg64(adreno_dev, ADRENO_REG_CP_IB1_BASE,
- ADRENO_REG_CP_IB1_BASE_HI, &ibbase);
-
/*
* Figure out the window of ringbuffer data to dump. First we need to
* find where the last processed IB ws submitted. Start walking back
@@ -333,14 +321,14 @@ static void snapshot_rb_ibs(struct kgsl_device *device,
if (adreno_cmd_is_ib(adreno_dev, rbptr[index])) {
if (ADRENO_LEGACY_PM4(adreno_dev)) {
- if (rbptr[index + 1] == ibbase)
+ if (rbptr[index + 1] == snapshot->ib1base)
break;
} else {
uint64_t ibaddr;
ibaddr = rbptr[index + 2];
ibaddr = ibaddr << 32 | rbptr[index + 1];
- if (ibaddr == ibbase)
+ if (ibaddr == snapshot->ib1base)
break;
}
}
@@ -564,6 +552,67 @@ struct snapshot_ib_meta {
uint64_t ib2size;
};
+void kgsl_snapshot_add_active_ib_obj_list(struct kgsl_device *device,
+ struct kgsl_snapshot *snapshot)
+{
+ struct adreno_ib_object_list *ib_obj_list;
+ int index = -ENOENT;
+
+ if (!snapshot->ib1dumped)
+ index = find_object(SNAPSHOT_OBJ_TYPE_IB, snapshot->ib1base,
+ snapshot->process);
+
+ /* only do this for IB1 because the IB2's are part of IB1 objects */
+ if ((index != -ENOENT) &&
+ (snapshot->ib1base == objbuf[index].gpuaddr)) {
+ if (-E2BIG == adreno_ib_create_object_list(device,
+ objbuf[index].entry->priv,
+ objbuf[index].gpuaddr,
+ objbuf[index].size >> 2,
+ &ib_obj_list))
+ ib_max_objs = 1;
+ if (ib_obj_list) {
+ /* freeze the IB objects in the IB */
+ snapshot_freeze_obj_list(snapshot,
+ objbuf[index].entry->priv,
+ ib_obj_list, snapshot->ib2base);
+ adreno_ib_destroy_obj_list(ib_obj_list);
+ }
+ } else {
+ /* Get the IB2 index from parsed object */
+ index = find_object(SNAPSHOT_OBJ_TYPE_IB, snapshot->ib2base,
+ snapshot->process);
+
+ if (index != -ENOENT)
+ parse_ib(device, snapshot, snapshot->process,
+ snapshot->ib2base, objbuf[index].size >> 2);
+ }
+}
+
+/*
+ * active_ib_is_parsed() - Checks if active ib is already parsed
+ * @gpuaddr: Active IB base address at the time of fault
+ * @size: Active IB size
+ * @process: The process to which the IB belongs
+ *
+ * Function returns true if the active is already is parsed
+ * else false
+ */
+static bool active_ib_is_parsed(uint64_t gpuaddr, uint64_t size,
+ struct kgsl_process_private *process)
+{
+ int index;
+ /* go through the static list for gpuaddr is in list or not */
+ for (index = 0; index < objbufptr; index++) {
+ if ((objbuf[index].gpuaddr <= gpuaddr) &&
+ ((objbuf[index].gpuaddr +
+ (objbuf[index].size)) >=
+ (gpuaddr + size)) &&
+ (objbuf[index].entry->priv == process))
+ return true;
+ }
+ return false;
+}
/* Snapshot the memory for an indirect buffer */
static size_t snapshot_ib(struct kgsl_device *device, u8 *buf,
size_t remain, void *priv)
@@ -596,13 +645,11 @@ static size_t snapshot_ib(struct kgsl_device *device, u8 *buf,
return 0;
}
- if (remain < (obj->size + sizeof(*header))) {
- KGSL_CORE_ERR("snapshot: Not enough memory for the ib\n");
- return 0;
- }
-
/* only do this for IB1 because the IB2's are part of IB1 objects */
if (meta->ib1base == obj->gpuaddr) {
+
+ snapshot->ib1dumped = active_ib_is_parsed(obj->gpuaddr,
+ obj->size, obj->entry->priv);
if (-E2BIG == adreno_ib_create_object_list(device,
obj->entry->priv,
obj->gpuaddr, obj->size >> 2,
@@ -617,6 +664,11 @@ static size_t snapshot_ib(struct kgsl_device *device, u8 *buf,
}
}
+
+ if (meta->ib2base == obj->gpuaddr)
+ snapshot->ib2dumped = active_ib_is_parsed(obj->gpuaddr,
+ obj->size, obj->entry->priv);
+
/* Write the sub-header for the section */
header->gpuaddr = obj->gpuaddr;
header->ptbase =
@@ -632,9 +684,7 @@ static size_t snapshot_ib(struct kgsl_device *device, u8 *buf,
/* Dump another item on the current pending list */
static void dump_object(struct kgsl_device *device, int obj,
- struct kgsl_snapshot *snapshot,
- uint64_t ib1base, uint64_t ib1size,
- uint64_t ib2base, uint64_t ib2size)
+ struct kgsl_snapshot *snapshot)
{
struct snapshot_ib_meta meta;
@@ -642,10 +692,10 @@ static void dump_object(struct kgsl_device *device, int obj,
case SNAPSHOT_OBJ_TYPE_IB:
meta.snapshot = snapshot;
meta.obj = &objbuf[obj];
- meta.ib1base = ib1base;
- meta.ib1size = ib1size;
- meta.ib2base = ib2base;
- meta.ib2size = ib2size;
+ meta.ib1base = snapshot->ib1base;
+ meta.ib1size = snapshot->ib1size;
+ meta.ib2base = snapshot->ib2base;
+ meta.ib2size = snapshot->ib2size;
kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_IB_V2,
snapshot, snapshot_ib, &meta);
@@ -792,8 +842,6 @@ void adreno_snapshot(struct kgsl_device *device, struct kgsl_snapshot *snapshot,
struct kgsl_context *context)
{
unsigned int i;
- uint64_t ib1base, ib2base;
- unsigned int ib1size, ib2size;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
@@ -806,6 +854,16 @@ void adreno_snapshot(struct kgsl_device *device, struct kgsl_snapshot *snapshot,
setup_fault_process(device, snapshot,
context ? context->proc_priv : NULL);
+ adreno_readreg64(adreno_dev, ADRENO_REG_CP_IB1_BASE,
+ ADRENO_REG_CP_IB1_BASE_HI, &snapshot->ib1base);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_IB1_BUFSZ, &snapshot->ib1size);
+ adreno_readreg64(adreno_dev, ADRENO_REG_CP_IB2_BASE,
+ ADRENO_REG_CP_IB2_BASE_HI, &snapshot->ib2base);
+ adreno_readreg(adreno_dev, ADRENO_REG_CP_IB2_BUFSZ, &snapshot->ib2size);
+
+ snapshot->ib1dumped = false;
+ snapshot->ib2dumped = false;
+
adreno_snapshot_ringbuffer(device, snapshot, adreno_dev->cur_rb);
/* Dump the prev ringbuffer */
@@ -818,13 +876,6 @@ void adreno_snapshot(struct kgsl_device *device, struct kgsl_snapshot *snapshot,
adreno_snapshot_ringbuffer(device, snapshot,
adreno_dev->next_rb);
- adreno_readreg64(adreno_dev, ADRENO_REG_CP_IB1_BASE,
- ADRENO_REG_CP_IB1_BASE_HI, &ib1base);
- adreno_readreg(adreno_dev, ADRENO_REG_CP_IB1_BUFSZ, &ib1size);
- adreno_readreg64(adreno_dev, ADRENO_REG_CP_IB2_BASE,
- ADRENO_REG_CP_IB2_BASE_HI, &ib2base);
- adreno_readreg(adreno_dev, ADRENO_REG_CP_IB2_BUFSZ, &ib2size);
-
/* Add GPU specific sections - registers mainly, but other stuff too */
if (gpudev->snapshot)
gpudev->snapshot(adreno_dev, snapshot);
@@ -858,13 +909,13 @@ void adreno_snapshot(struct kgsl_device *device, struct kgsl_snapshot *snapshot,
* figure how often this really happens.
*/
- if (!find_object(SNAPSHOT_OBJ_TYPE_IB, ib1base,
- snapshot->process) && ib1size) {
+ if (-ENOENT == find_object(SNAPSHOT_OBJ_TYPE_IB, snapshot->ib1base,
+ snapshot->process) && snapshot->ib1size) {
push_object(SNAPSHOT_OBJ_TYPE_IB, snapshot->process,
- ib1base, ib1size);
+ snapshot->ib1base, snapshot->ib1size);
KGSL_CORE_ERR(
"CP_IB1_BASE not found in the ringbuffer.Dumping %x dwords of the buffer.\n",
- ib1size);
+ snapshot->ib1size);
}
/*
@@ -875,10 +926,10 @@ void adreno_snapshot(struct kgsl_device *device, struct kgsl_snapshot *snapshot,
* correct size.
*/
- if (!find_object(SNAPSHOT_OBJ_TYPE_IB, ib2base,
- snapshot->process) && ib2size) {
+ if (-ENOENT == find_object(SNAPSHOT_OBJ_TYPE_IB, snapshot->ib2base,
+ snapshot->process)) {
push_object(SNAPSHOT_OBJ_TYPE_IB, snapshot->process,
- ib2base, ib2size);
+ snapshot->ib2base, snapshot->ib2size);
}
/*
@@ -886,8 +937,15 @@ void adreno_snapshot(struct kgsl_device *device, struct kgsl_snapshot *snapshot,
* are parsed, more objects might be found, and objbufptr will increase
*/
for (i = 0; i < objbufptr; i++)
- dump_object(device, i, snapshot, ib1base, ib1size,
- ib2base, ib2size);
+ dump_object(device, i, snapshot);
+
+ /*
+ * Incase snapshot static blob is running out of memory, Add Active IB1
+ * and IB2 entries to obj_list so that active ib's can be dumped to
+ * snapshot dynamic blob.
+ */
+ if (!snapshot->ib1dumped || !snapshot->ib2dumped)
+ kgsl_snapshot_add_active_ib_obj_list(device, snapshot);
if (ib_max_objs)
KGSL_CORE_ERR("Max objects found in IB\n");
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 6e9abc99bcc4..88581b079246 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -485,6 +485,7 @@ err_put_proc_priv:
static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry)
{
unsigned int type;
+ int ret;
if (entry == NULL)
return;
@@ -501,9 +502,14 @@ static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry)
entry->priv->stats[type].cur -= entry->memdesc.size;
spin_unlock(&entry->priv->mem_lock);
- kgsl_mmu_unmap(entry->memdesc.pagetable, &entry->memdesc);
-
- kgsl_mem_entry_untrack_gpuaddr(entry->priv, entry);
+ ret = kgsl_mmu_unmap(entry->memdesc.pagetable, &entry->memdesc);
+ /*
+ * Do not free the gpuaddr/size if unmap fails. Because if we try
+ * to map this range in future, the iommu driver will throw
+ * a BUG_ON() because it feels we are overwriting a mapping.
+ */
+ if (ret == 0)
+ kgsl_mem_entry_untrack_gpuaddr(entry->priv, entry);
kgsl_process_private_put(entry->priv);
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 f42d822b451b..24511a4de6f1 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -165,6 +165,10 @@ struct kgsl_functable {
void (*pwrlevel_change_settings)(struct kgsl_device *device,
unsigned int prelevel, unsigned int postlevel, bool post);
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 {
@@ -259,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;
@@ -279,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) \
@@ -422,6 +428,12 @@ struct kgsl_device_private {
/**
* struct kgsl_snapshot - details for a specific snapshot instance
+ * @ib1base: Active IB1 base address at the time of fault
+ * @ib2base: Active IB2 base address at the time of fault
+ * @ib1size: Number of DWORDS pending in IB1 at the time of fault
+ * @ib2size: Number of DWORDS pending in IB2 at the time of fault
+ * @ib1dumped: Active IB1 dump status to sansphot binary
+ * @ib2dumped: Active IB2 dump status to sansphot binary
* @start: Pointer to the start of the static snapshot region
* @size: Size of the current snapshot instance
* @ptr: Pointer to the next block of memory to write to during snapshotting
@@ -437,6 +449,12 @@ struct kgsl_device_private {
* @sysfs_read: An atomic for concurrent snapshot reads via syfs.
*/
struct kgsl_snapshot {
+ uint64_t ib1base;
+ uint64_t ib2base;
+ unsigned int ib1size;
+ unsigned int ib2size;
+ bool ib1dumped;
+ bool ib2dumped;
u8 *start;
size_t size;
u8 *ptr;
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 4371c9a1b87e..ba564b2851f9 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -450,24 +450,23 @@ int
kgsl_mmu_unmap(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *memdesc)
{
+ int ret = 0;
+
if (memdesc->size == 0)
return -EINVAL;
if (PT_OP_VALID(pagetable, mmu_unmap)) {
- int ret;
uint64_t size;
size = kgsl_memdesc_footprint(memdesc);
ret = pagetable->pt_ops->mmu_unmap(pagetable, memdesc);
- if (ret)
- return ret;
atomic_dec(&pagetable->stats.entries);
atomic_long_sub(size, &pagetable->stats.mapped);
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL(kgsl_mmu_unmap);
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index ea760d9198ee..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)
@@ -1655,6 +1920,10 @@ static int _get_clocks(struct kgsl_device *device)
if (!strcmp(name, "isense_clk"))
pwr->isense_clk_indx = i;
+
+ if (device->ftbl->clk_set_options)
+ device->ftbl->clk_set_options(device, name,
+ pwr->grp_clks[i]);
break;
}
}
@@ -1856,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;
}
@@ -1909,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_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 73edc3f7e146..72895c18119f 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -807,8 +807,16 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc)
return;
if (memdesc->gpuaddr) {
- kgsl_mmu_unmap(memdesc->pagetable, memdesc);
- kgsl_mmu_put_gpuaddr(memdesc->pagetable, memdesc);
+ int ret = 0;
+
+ ret = kgsl_mmu_unmap(memdesc->pagetable, memdesc);
+ /*
+ * Do not free the gpuaddr/size if unmap fails. Because if we
+ * try to map this range in future, the iommu driver will throw
+ * a BUG_ON() because it feels we are overwriting a mapping.
+ */
+ if (ret == 0)
+ kgsl_mmu_put_gpuaddr(memdesc->pagetable, memdesc);
}
if (memdesc->ops && memdesc->ops->free)
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index dd004f9588e9..a2e4a909062f 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -100,8 +100,8 @@ static u8 *_ctxtptr;
static int snapshot_context_info(int id, void *ptr, void *data)
{
- struct kgsl_snapshot_linux_context *header =
- (struct kgsl_snapshot_linux_context *)_ctxtptr;
+ struct kgsl_snapshot_linux_context_v2 *header =
+ (struct kgsl_snapshot_linux_context_v2 *)_ctxtptr;
struct kgsl_context *context = ptr;
struct kgsl_device *device;
@@ -115,10 +115,12 @@ static int snapshot_context_info(int id, void *ptr, void *data)
kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_QUEUED,
&header->timestamp_queued);
+ kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_CONSUMED,
+ &header->timestamp_consumed);
kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED,
&header->timestamp_retired);
- _ctxtptr += sizeof(struct kgsl_snapshot_linux_context);
+ _ctxtptr += sizeof(struct kgsl_snapshot_linux_context_v2);
return 0;
}
@@ -127,11 +129,11 @@ static int snapshot_context_info(int id, void *ptr, void *data)
static size_t snapshot_os(struct kgsl_device *device,
u8 *buf, size_t remain, void *priv)
{
- struct kgsl_snapshot_linux *header = (struct kgsl_snapshot_linux *)buf;
+ struct kgsl_snapshot_linux_v2 *header =
+ (struct kgsl_snapshot_linux_v2 *)buf;
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
int ctxtcount = 0;
size_t size = sizeof(*header);
- u64 temp_ptbase;
struct kgsl_context *context;
/* Figure out how many active contexts there are - these will
@@ -141,7 +143,7 @@ static size_t snapshot_os(struct kgsl_device *device,
idr_for_each(&device->context_idr, snapshot_context_count, &ctxtcount);
read_unlock(&device->context_lock);
- size += ctxtcount * sizeof(struct kgsl_snapshot_linux_context);
+ size += ctxtcount * sizeof(struct kgsl_snapshot_linux_context_v2);
/* Make sure there is enough room for the data */
if (remain < size) {
@@ -151,9 +153,7 @@ static size_t snapshot_os(struct kgsl_device *device,
memset(header, 0, sizeof(*header));
- header->osid = KGSL_SNAPSHOT_OS_LINUX;
-
- header->state = SNAPSHOT_STATE_HUNG;
+ header->osid = KGSL_SNAPSHOT_OS_LINUX_V3;
/* Get the kernel build information */
strlcpy(header->release, utsname()->release, sizeof(header->release));
@@ -178,9 +178,8 @@ static size_t snapshot_os(struct kgsl_device *device,
context = kgsl_context_get(device, header->current_context);
/* Get the current PT base */
- temp_ptbase = kgsl_mmu_get_current_ttbr0(&device->mmu);
- /* Truncate to 32 bits in case LPAE is used */
- header->ptbase = (__u32)temp_ptbase;
+ header->ptbase = kgsl_mmu_get_current_ttbr0(&device->mmu);
+
/* And the PID for the task leader */
if (context) {
header->pid = context->tid;
@@ -811,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)
{
@@ -836,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)
@@ -915,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");
@@ -930,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;
}
@@ -955,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);
@@ -982,7 +1011,8 @@ int kgsl_snapshot_add_ib_obj_list(struct kgsl_snapshot *snapshot,
return 0;
}
-static size_t _mempool_add_object(u8 *data, struct kgsl_snapshot_object *obj)
+static size_t _mempool_add_object(struct kgsl_snapshot *snapshot, u8 *data,
+ struct kgsl_snapshot_object *obj)
{
struct kgsl_snapshot_section_header *section =
(struct kgsl_snapshot_section_header *)data;
@@ -1008,6 +1038,14 @@ static size_t _mempool_add_object(u8 *data, struct kgsl_snapshot_object *obj)
kgsl_mmu_pagetable_get_ttbr0(obj->entry->priv->pagetable);
header->type = obj->type;
+ if (kgsl_addr_range_overlap(obj->gpuaddr, obj->size,
+ snapshot->ib1base, snapshot->ib1size))
+ snapshot->ib1dumped = true;
+
+ if (kgsl_addr_range_overlap(obj->gpuaddr, obj->size,
+ snapshot->ib2base, snapshot->ib2size))
+ snapshot->ib2dumped = true;
+
memcpy(dest, obj->entry->memdesc.hostptr + obj->offset, size);
kgsl_memdesc_unmap(&obj->entry->memdesc);
@@ -1024,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;
@@ -1049,7 +1088,7 @@ void kgsl_snapshot_save_frozen_objs(struct work_struct *work)
/* even if vmalloc fails, make sure we clean up the obj_list */
list_for_each_entry_safe(obj, tmp, &snapshot->obj_list, node) {
if (snapshot->mempool) {
- size_t ret = _mempool_add_object(ptr, obj);
+ size_t ret = _mempool_add_object(snapshot, ptr, obj);
ptr += ret;
snapshot->mempool_size += ret;
}
@@ -1064,6 +1103,16 @@ done:
kgsl_process_private_put(snapshot->process);
snapshot->process = NULL;
+ if (snapshot->ib1base && !snapshot->ib1dumped)
+ KGSL_DRV_ERR(device,
+ "snapshot: Active IB1:%016llx not dumped\n",
+ snapshot->ib1base);
+ else if (snapshot->ib2base && !snapshot->ib2dumped)
+ 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/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h
index 8167ff83a18b..e2ded87b7431 100644
--- a/drivers/gpu/msm/kgsl_snapshot.h
+++ b/drivers/gpu/msm/kgsl_snapshot.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -63,12 +63,9 @@ struct kgsl_snapshot_section_header {
/* OS sub-section header */
#define KGSL_SNAPSHOT_OS_LINUX 0x0001
+#define KGSL_SNAPSHOT_OS_LINUX_V3 0x00000202
/* Linux OS specific information */
-
-#define SNAPSHOT_STATE_HUNG 0
-#define SNAPSHOT_STATE_RUNNING 1
-
struct kgsl_snapshot_linux {
int osid; /* subsection OS identifier */
int state; /* 1 if the thread is running, 0 for hung */
@@ -87,6 +84,23 @@ struct kgsl_snapshot_linux {
unsigned char comm[16]; /* Name of the process that owns the PT */
} __packed;
+struct kgsl_snapshot_linux_v2 {
+ int osid; /* subsection OS identifier */
+ __u32 seconds; /* Unix timestamp for the snapshot */
+ __u32 power_flags; /* Current power flags */
+ __u32 power_level; /* Current power level */
+ __u32 power_interval_timeout; /* Power interval timeout */
+ __u32 grpclk; /* Current GP clock value */
+ __u32 busclk; /* Current busclk value */
+ __u64 ptbase; /* Current ptbase */
+ __u32 pid; /* PID of the process that owns the PT */
+ __u32 current_context; /* ID of the current context */
+ __u32 ctxtcount; /* Number of contexts appended to section */
+ unsigned char release[32]; /* kernel release */
+ unsigned char version[32]; /* kernel version */
+ unsigned char comm[16]; /* Name of the process that owns the PT */
+} __packed;
+
/*
* This structure contains a record of an active context.
* These are appended one after another in the OS section below
@@ -99,6 +113,12 @@ struct kgsl_snapshot_linux_context {
__u32 timestamp_retired; /* The last timestamp retired by HW */
};
+struct kgsl_snapshot_linux_context_v2 {
+ __u32 id; /* The context ID */
+ __u32 timestamp_queued; /* The last queued timestamp */
+ __u32 timestamp_consumed; /* The last timestamp consumed by HW */
+ __u32 timestamp_retired; /* The last timestamp retired by HW */
+};
/* Ringbuffer sub-section header */
struct kgsl_snapshot_rb {
int start; /* dword at the start of the dump */
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 884d82f9190e..37e8b61b5c98 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -440,6 +440,9 @@ static const struct hid_device_id apple_devices[] = {
.driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI),
.driver_data = APPLE_HAS_FN },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+ USB_DEVICE_ID_APPLE_ALU_ANSI),
+ .driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO),
.driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS),
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index ec791e169f8f..9dc9f93f4e36 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1754,6 +1754,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+ USB_DEVICE_ID_APPLE_ALU_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) },
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 3776e748647e..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,
@@ -1820,7 +1832,7 @@ int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
struct qpnp_adc_amux *adc_channel_list;
struct qpnp_adc_properties *adc_prop;
struct qpnp_adc_amux_properties *amux_prop;
- int count_adc_channel_list = 0, decimation, rc = 0, i = 0;
+ int count_adc_channel_list = 0, decimation = 0, rc = 0, i = 0;
int decimation_tm_hc = 0, fast_avg_setup_tm_hc = 0, cal_val_hc = 0;
bool adc_hc;
@@ -1927,22 +1939,6 @@ int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
return -EINVAL;
}
- /*
- * ADC_TM_HC decimation setting is common across
- * channels.
- */
- if (!of_device_is_compatible(node,
- "qcom,qpnp-adc-tm-hc")) {
- rc = of_property_read_u32(child,
- "qcom,decimation", &decimation);
- if (rc) {
- pr_err("Invalid decimation\n");
- return -EINVAL;
- }
- } else {
- decimation = decimation_tm_hc;
- }
-
if (!strcmp(calibration_param, "absolute")) {
if (adc_hc)
calib_type = ADC_HC_ABS_CAL;
@@ -1980,6 +1976,18 @@ int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
fast_avg_setup = fast_avg_setup_tm_hc;
}
+ /* ADC_TM_HC decimation setting is common across channels */
+ if (!of_device_is_compatible(node, "qcom,qpnp-adc-tm-hc")) {
+ rc = of_property_read_u32(child,
+ "qcom,decimation", &decimation);
+ if (rc) {
+ pr_err("Invalid decimation\n");
+ return -EINVAL;
+ }
+ } else {
+ decimation = decimation_tm_hc;
+ }
+
if (of_device_is_compatible(node, "qcom,qpnp-vadc-hc")) {
rc = of_property_read_u32(child, "qcom,cal-val",
&cal_val_hc);
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 ae2df4f7ff0d..bb44b6d82ccd 100644
--- a/drivers/iio/adc/qcom-rradc.c
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -11,6 +11,8 @@
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) "RRADC: %s: " fmt, __func__
+
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/math64.h>
@@ -416,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) \
@@ -441,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,
@@ -459,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/misc/ots_pat9125/pat9125_linux_driver.c b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
new file mode 100644
index 000000000000..0a93f11e2b7e
--- /dev/null
+++ b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
@@ -0,0 +1,470 @@
+/* drivers/input/misc/ots_pat9125/pat9125_linux_driver.c
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+
+#include "pixart_ots.h"
+#include "pixart_platform.h"
+
+static int pat9125_init_input_data(void);
+
+#define pat9125_name "pixart_pat9125"
+
+#define pat9125_DEV_NAME pat9125_name
+
+static struct pat9125_linux_data_t pat9125data;
+
+static int pat9125_i2c_write(u8 reg, u8 *data, int len);
+static int pat9125_i2c_read(u8 reg, u8 *data);
+
+extern unsigned char ReadData(unsigned char addr)
+{
+ u8 data = 0xff;
+
+ pat9125_i2c_read(addr, &data);
+ return data;
+}
+extern void WriteData(unsigned char addr, unsigned char data)
+{
+ pat9125_i2c_write(addr, &data, 1);
+}
+extern void delay_ms(int ms)
+{
+ msleep(ms);
+}
+static int pat9125_i2c_write(u8 reg, u8 *data, int len)
+{
+ u8 buf[20];
+ int rc;
+ int ret = 0;
+ int i;
+
+ buf[0] = reg;
+ if (len >= 20) {
+ pr_debug(
+ "%s (%d) : FAILED: buffer size is limitted(20) %d\n",
+ __func__, __LINE__, len);
+ dev_err(&pat9125data.client->dev, "pat9125_i2c_write FAILED: buffer size is limitted(20)\n");
+ return -ENODEV;
+ }
+
+ for (i = 0 ; i < len; i++)
+ buf[i+1] = data[i];
+
+ /* Returns negative errno, or else the number of bytes written. */
+ rc = i2c_master_send(pat9125data.client, buf, len+1);
+
+ if (rc != len+1) {
+ pr_debug(
+ "%s (%d) : FAILED: writing to reg 0x%x\n",
+ __func__, __LINE__, reg);
+
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+static int pat9125_i2c_read(u8 reg, u8 *data)
+{
+
+ u8 buf[20];
+ int rc;
+
+ buf[0] = reg;
+
+ /*
+ * If everything went ok (i.e. 1 msg transmitted),
+ *return #bytes transmitted, else error code.
+ * thus if transmit is ok return value 1
+ */
+ rc = i2c_master_send(pat9125data.client, buf, 1);
+ if (rc != 1) {
+ pr_debug(
+ "%s (%d) : FAILED: writing to address 0x%x\n",
+ __func__, __LINE__, reg);
+ return -ENODEV;
+ }
+
+ /* returns negative errno, or else the number of bytes read */
+ rc = i2c_master_recv(pat9125data.client, buf, 1);
+ if (rc != 1) {
+ pr_debug(
+ "%s (%d) : FAILED: reading data\n",
+ __func__, __LINE__);
+ return -ENODEV;
+ }
+
+ *data = buf[0];
+ return 0;
+}
+
+void pixart_pat9125_ist(void)
+{
+
+}
+
+static irqreturn_t pixart_pat9125_irq(int irq, void *handle)
+{
+ pixart_pat9125_ist();
+ return IRQ_HANDLED;
+}
+
+static int pat9125_start(void)
+{
+ int err = (-1);
+ pr_debug(">>> %s (%d)\n", __func__, __LINE__);
+
+ err = request_threaded_irq(pat9125data.irq, NULL, pixart_pat9125_irq,
+ pat9125data.irq_flags,
+ "pixart_pat9125_irq",
+ &pat9125data);
+ if (err)
+ pr_debug("irq %d busy?\n", pat9125data.irq);
+
+ pat9125data.last_jiffies = jiffies_64;
+
+ return err;
+}
+
+static void pat9125_stop(void)
+{
+ free_irq(pat9125data.irq, &pat9125data);
+}
+
+static ssize_t pat9125_fops_read(struct file *filp,
+ char *buf, size_t count, loff_t *l)
+{
+ return 0;
+}
+
+static ssize_t pat9125_fops_write(struct file *filp,
+ const char *buf, size_t count, loff_t *f_ops)
+{
+ return 0;
+}
+
+static long pat9125_fops_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+
+static int pat9125_fops_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static int pat9125_fops_release(struct inode *inode, struct file *filp)
+{
+ pr_debug(">>> %s (%d)\n", __func__, __LINE__);
+ return 0;
+}
+static const struct file_operations pat9125_fops = {
+owner: THIS_MODULE,
+ read : pat9125_fops_read,
+ write : pat9125_fops_write,
+ /* ioctl : pat9125_fops_ioctl, */
+ unlocked_ioctl : pat9125_fops_ioctl,
+ open : pat9125_fops_open,
+ release : pat9125_fops_release,
+};
+
+/*----------------------------------------------------------------------------*/
+struct miscdevice pat9125_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = pat9125_name,
+ .fops = &pat9125_fops,
+};
+static ssize_t pat9125_test_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char s[256];
+ char *p = s;
+
+ pr_debug("%s (%d) : write_reg_store\n", __func__, __LINE__);
+
+ memcpy(s, buf, sizeof(s));
+
+ *(s+1) = '\0';
+ *(s+4) = '\0';
+ *(s+7) = '\0';
+ /* example(in console): echo w 12 34 > rw_reg */
+ if (*p == 'w') {
+ long write_addr, write_data;
+
+ p += 2;
+ if (!kstrtol(p, 16, &write_addr)) {
+ p += 3;
+ if (!kstrtol(p, 16, &write_data)) {
+ pr_debug(
+ "w 0x%x 0x%x\n",
+ (u8)write_addr, (u8)write_data);
+ WriteData((u8)write_addr, (u8)write_data);
+ }
+ }
+ /* example(in console): echo r 12 > rw_reg */
+ } else if (*p == 'r') {
+ long read_addr;
+
+ p += 2;
+
+ if (!kstrtol(p, 16, &read_addr)) {
+ int data = 0;
+
+ data = ReadData((u8)read_addr);
+ pr_debug(
+ "r 0x%x 0x%x\n",
+ (unsigned int)read_addr, data);
+ }
+ }
+ return count;
+}
+
+static ssize_t pat9125_test_show(
+ struct device *dev, struct device_attribute *attr, char *buf)
+{
+
+ /* cat */
+ pr_debug("%s (%d) :\n", __func__, __LINE__);
+
+ return 0;
+}
+static DEVICE_ATTR(
+ test,
+ S_IRUGO | S_IWUSR | S_IWGRP, pat9125_test_show, pat9125_test_store);
+static struct device_attribute *pat9125_attr_list[] = {
+ &dev_attr_test,
+};
+
+static int pat9125_create_attr(struct device *dev)
+{
+ int idx, err = 0;
+ int num = ARRAY_SIZE(pat9125_attr_list);
+
+ if (!dev)
+ return -EINVAL;
+ for (idx = 0; idx < num; idx++) {
+ err = device_create_file(dev, pat9125_attr_list[idx]);
+ if (err) {
+ pr_debug(
+ "device_create_file (%s) = %d\n",
+ pat9125_attr_list[idx]->attr.name, err);
+ break;
+ }
+ }
+ return err;
+}
+
+static int pat9125_i2c_probe(
+ struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = 0;
+ struct device_node *np;
+
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+
+ pr_debug("%s (%d) : probe module....\n", __func__, __LINE__);
+
+ memset(&pat9125data, 0, sizeof(pat9125data));
+ err = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE);
+ if (err < 0)
+ goto error_return;
+
+ pat9125data.client = client;
+ err = misc_register(&pat9125_device);
+ if (err) {
+ pr_debug("pat9125_device register failed\n");
+ goto error_return;
+ }
+
+ pat9125data.pat9125_device = pat9125_device.this_device;
+ err = pat9125_create_attr(pat9125data.pat9125_device);
+ if (err) {
+ pr_debug("create attribute err = %d\n", err);
+ goto error_return;
+ }
+
+ if (pat9125_init_input_data() < 0)
+ goto error_return;
+
+ /* interrupt initialization */
+ pat9125data.i2c_dev = &client->dev;
+
+ np = pat9125data.i2c_dev->of_node;
+ pat9125data.irq_gpio = of_get_named_gpio_flags(np,
+ "pixart_pat9125,irq-gpio", 0, &pat9125data.irq_flags);
+
+ pr_debug(
+ "irq_gpio: %d, irq_flags: 0x%x\n",
+ pat9125data.irq_gpio, pat9125data.irq_flags);
+
+ if (!gpio_is_valid(pat9125data.irq_gpio)) {
+ err = (-1);
+ pr_debug(
+ "invalid irq_gpio: %d\n",
+ pat9125data.irq_gpio);
+ goto error_return;
+ }
+
+ err = gpio_request(pat9125data.irq_gpio, "pixart_pat9125_irq_gpio");
+ if (err) {
+ pr_debug(
+ "unable to request gpio [%d], [%d]\n",
+ pat9125data.irq_gpio, err);
+ goto error_return;
+ }
+
+ err = gpio_direction_input(pat9125data.irq_gpio);
+ if (err) {
+ pr_debug("unable to set dir for gpio[%d], [%d]\n",
+ pat9125data.irq_gpio, err);
+ goto error_return;
+ }
+
+ pat9125data.irq = gpio_to_irq(pat9125data.irq_gpio);
+
+ if (!OTS_Sensor_Init())
+ goto error_return;
+
+ if (!pat9125_start())
+ goto error_return;
+
+ return 0;
+
+error_return:
+
+ return err;
+
+}
+
+
+static int pat9125_i2c_remove(struct i2c_client *client)
+{
+
+ return 0;
+}
+
+static int pat9125_suspend(struct device *dev)
+{ pr_debug("%s (%d) : pat9125 suspend\n", __func__, __LINE__);
+ return 0;
+}
+
+static int pat9125_resume(struct device *dev)
+{
+ pr_debug("%s (%d) : pat9125 resume\n", __func__, __LINE__);
+ return 0;
+}
+
+static const struct i2c_device_id pat9125_device_id[] = {
+ {pat9125_DEV_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, pat9125_device_id);
+
+static const struct dev_pm_ops pat9125_pm_ops = {
+ .suspend = pat9125_suspend,
+ .resume = pat9125_resume
+};
+
+static const struct of_device_id pixart_pat9125_match_table[] = {
+ { .compatible = "pixart,pat9125",},
+ { },
+};
+
+static struct i2c_driver pat9125_i2c_driver = {
+ .driver = {
+ .name = pat9125_DEV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &pat9125_pm_ops,
+ .of_match_table = pixart_pat9125_match_table,
+ },
+ .probe = pat9125_i2c_probe,
+ .remove = pat9125_i2c_remove,
+ .id_table = pat9125_device_id,
+};
+static int pat9125_open(struct input_dev *dev)
+{
+ pr_debug(">>> %s (%d)\n", __func__, __LINE__);
+ return 0;
+}
+
+static void pat9125_close(struct input_dev *dev)
+{
+ pr_debug(">>> %s (%d)\n", __func__, __LINE__);
+}
+
+static int pat9125_init_input_data(void)
+{
+ int ret = 0;
+
+ pr_debug("%s (%d) : initialize data\n", __func__, __LINE__);
+
+ pat9125data.pat9125_input_dev = input_allocate_device();
+
+ if (!pat9125data.pat9125_input_dev) {
+ pr_debug(
+ "%s (%d) : could not allocate mouse input device\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ input_set_drvdata(pat9125data.pat9125_input_dev, &pat9125data);
+ pat9125data.pat9125_input_dev->name = "Pixart pat9125";
+
+ pat9125data.pat9125_input_dev->open = pat9125_open;
+ pat9125data.pat9125_input_dev->close = pat9125_close;
+
+ ret = input_register_device(pat9125data.pat9125_input_dev);
+ if (ret < 0) {
+ input_free_device(pat9125data.pat9125_input_dev);
+ pr_debug(
+ "%s (%d) : could not register input device\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init pat9125_linux_init(void)
+{
+ return i2c_add_driver(&pat9125_i2c_driver);
+}
+
+
+
+
+static void __exit pat9125_linux_exit(void)
+{
+ pr_debug("%s (%d) : exit module\n", __func__, __LINE__);
+ pat9125_stop();
+ misc_register(&pat9125_device);
+ i2c_del_driver(&pat9125_i2c_driver);
+}
+
+
+module_init(pat9125_linux_init);
+module_exit(pat9125_linux_exit);
+MODULE_AUTHOR("pixart");
+MODULE_DESCRIPTION("pixart pat9125 driver");
+MODULE_LICENSE("GPL");
+
+
+
diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.c b/drivers/input/misc/ots_pat9125/pixart_ots.c
new file mode 100644
index 000000000000..70736197de3c
--- /dev/null
+++ b/drivers/input/misc/ots_pat9125/pixart_ots.c
@@ -0,0 +1,71 @@
+/* drivers/input/misc/ots_pat9125/pixart_ots.c
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include "pixart_ots.h"
+
+static void OTS_WriteRead(uint8_t address, uint8_t wdata);
+
+bool OTS_Sensor_Init(void)
+{
+ unsigned char sensor_pid = 0, read_id_ok = 0;
+
+ /*
+ * Read sensor_pid in address 0x00 to check if the
+ * serial link is valid, read value should be 0x31.
+ */
+ sensor_pid = ReadData(0x00);
+
+ if (sensor_pid == 0x31) {
+ read_id_ok = 1;
+
+ /*
+ *PAT9125 sensor recommended settings:
+ * switch to bank0, not allowed to perform OTS_RegWriteRead
+ */
+ WriteData(0x7F, 0x00);
+ /*
+ * software reset (i.e. set bit7 to 1).
+ * It will reset to 0 automatically
+ * so perform OTS_RegWriteRead is not allowed.
+ */
+ WriteData(0x06, 0x97);
+
+ /* delay 1ms */
+ delay_ms(1);
+
+ /* disable write protect */
+ OTS_WriteRead(0x09, 0x5A);
+ /* set X-axis resolution (depends on application) */
+ OTS_WriteRead(0x0D, 0x65);
+ /* set Y-axis resolution (depends on application) */
+ OTS_WriteRead(0x0E, 0xFF);
+ /* set 12-bit X/Y data format (depends on application) */
+ OTS_WriteRead(0x19, 0x04);
+ /* ONLY for VDD=VDDA=1.7~1.9V: for power saving */
+ OTS_WriteRead(0x4B, 0x04);
+
+ if (ReadData(0x5E) == 0x04) {
+ OTS_WriteRead(0x5E, 0x08);
+ if (ReadData(0x5D) == 0x10)
+ OTS_WriteRead(0x5D, 0x19);
+ }
+ OTS_WriteRead(0x09, 0x00);/* enable write protect */
+ }
+ return read_id_ok;
+}
+
+static void OTS_WriteRead(uint8_t address, uint8_t wdata)
+{
+ uint8_t read_value;
+
+ do {
+ /* Write data to specified address */
+ WriteData(address, wdata);
+ /* Read back previous written data */
+ read_value = ReadData(address);
+ /* Check if the data is correctly written */
+ } while (read_value != wdata);
+}
diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.h b/drivers/input/misc/ots_pat9125/pixart_ots.h
new file mode 100644
index 000000000000..ba1da1396ad1
--- /dev/null
+++ b/drivers/input/misc/ots_pat9125/pixart_ots.h
@@ -0,0 +1,16 @@
+/* drivers/input/misc/ots_pat9125/pixart_ots.h
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#ifndef _PIXART_OTS_H_
+#define _PIXART_OTS_H_
+
+#include "pixart_platform.h"
+
+/* export funtions */
+bool OTS_Sensor_Init(void);
+void OTS_Sensor_ReadMotion(int16_t *dx, int16_t *dy);
+
+#endif
diff --git a/drivers/input/misc/ots_pat9125/pixart_platform.h b/drivers/input/misc/ots_pat9125/pixart_platform.h
new file mode 100644
index 000000000000..a025fd06343e
--- /dev/null
+++ b/drivers/input/misc/ots_pat9125/pixart_platform.h
@@ -0,0 +1,25 @@
+/* drivers/input/misc/ots_pat9125/pixart_platform.h
+ *
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#ifndef _PIXART_PLATFORM_
+#define _PIXART_PLATFORM_
+
+#include <linux/input.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+
+/* extern functions */
+extern unsigned char ReadData(unsigned char addr);
+extern void WriteData(unsigned char addr, unsigned char data);
+
+#endif
diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c
index c67c4c8f1207..99a29401b36f 100644
--- a/drivers/input/touchscreen/gt9xx/goodix_tool.c
+++ b/drivers/input/touchscreen/gt9xx/goodix_tool.c
@@ -23,16 +23,15 @@
#include "gt9xx.h"
#include <linux/mutex.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
#define DATA_LENGTH_UINT 512
-#define CMD_HEAD_LENGTH (sizeof(st_cmd_head) - sizeof(u8 *))
+#define CMD_HEAD_LENGTH (sizeof(struct st_cmd_head) - sizeof(u8 *))
static char procname[20] = {0};
-#define UPDATE_FUNCTIONS
-
-#pragma pack(1)
-struct {
- u8 wr; /* write read flag£¬0:R 1:W 2:PID 3: */
+struct st_cmd_head {
+ u8 wr; /* write read flag 0:R 1:W 2:PID 3: */
u8 flag; /* 0:no need flag/int 1: need flag 2:need int */
u8 flag_addr[2];/* flag address */
u8 flag_val; /* flag val */
@@ -46,9 +45,9 @@ struct {
u8 addr[2]; /* address */
u8 res[3]; /* reserved */
u8 *data; /* data pointer */
-} st_cmd_head;
-#pragma pack()
-st_cmd_head cmd_head;
+} __packed;
+
+static struct st_cmd_head cmd_head;
static struct i2c_client *gt_client;
@@ -56,15 +55,11 @@ static struct proc_dir_entry *goodix_proc_entry;
static struct mutex lock;
-static s32 goodix_tool_write(struct file *filp, const char __user *buff,
- unsigned long len, void *data);
-static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
- int *eof, void *data);
static s32 (*tool_i2c_read)(u8 *, u16);
static s32 (*tool_i2c_write)(u8 *, u16);
-s32 DATA_LENGTH;
-s8 IC_TYPE[16] = {0};
+s32 data_length;
+s8 ic_type[16] = {0};
static void tool_set_proc_name(char *procname)
{
@@ -85,7 +80,6 @@ static void tool_set_proc_name(char *procname)
}
snprintf(procname, 20, "gmnode%04d%02d%02d", n_year, n_month, n_day);
- /* GTP_DEBUG("procname = %s", procname); */
}
static s32 tool_i2c_read_no_extra(u8 *buf, u16 len)
@@ -114,7 +108,7 @@ static s32 tool_i2c_read_no_extra(u8 *buf, u16 len)
}
if (i == cmd_head.retry) {
- dev_err(&client->dev, "I2C read retry limit over.\n");
+ dev_err(&gt_client->dev, "I2C read retry limit over\n");
ret = -EIO;
}
@@ -139,7 +133,7 @@ static s32 tool_i2c_write_no_extra(u8 *buf, u16 len)
}
if (i == cmd_head.retry) {
- dev_err(&client->dev, "I2C write retry limit over.\n");
+ dev_err(&gt_client->dev, "I2C write retry limit over\n");
ret = -EIO;
}
@@ -174,17 +168,17 @@ static s32 tool_i2c_write_with_extra(u8 *buf, u16 len)
static void register_i2c_func(void)
{
- if (strcmp(IC_TYPE, "GT8110") && strcmp(IC_TYPE, "GT8105")
- && strcmp(IC_TYPE, "GT801") && strcmp(IC_TYPE, "GT800")
- && strcmp(IC_TYPE, "GT801PLUS") && strcmp(IC_TYPE, "GT811")
- && strcmp(IC_TYPE, "GTxxx")) {
+ if (strcmp(ic_type, "GT8110") && strcmp(ic_type, "GT8105")
+ && strcmp(ic_type, "GT801") && strcmp(ic_type, "GT800")
+ && strcmp(ic_type, "GT801PLUS") && strcmp(ic_type, "GT811")
+ && strcmp(ic_type, "GTxxx")) {
tool_i2c_read = tool_i2c_read_with_extra;
tool_i2c_write = tool_i2c_write_with_extra;
- GTP_DEBUG("I2C function: with pre and end cmd!");
+ pr_debug("I2C function: with pre and end cmd\n");
} else {
tool_i2c_read = tool_i2c_read_no_extra;
tool_i2c_write = tool_i2c_write_no_extra;
- GTP_INFO("I2C function: without pre and end cmd!");
+ pr_info("I2C function: without pre and end cmd\n");
}
}
@@ -192,57 +186,14 @@ static void unregister_i2c_func(void)
{
tool_i2c_read = NULL;
tool_i2c_write = NULL;
- GTP_INFO("I2C function: unregister i2c transfer function!");
-}
-
-s32 init_wr_node(struct i2c_client *client)
-{
- u8 i;
-
- gt_client = client;
- memset(&cmd_head, 0, sizeof(cmd_head));
- cmd_head.data = NULL;
-
- i = 5;
- while ((!cmd_head.data) && i) {
- cmd_head.data = devm_kzalloc(&client->dev,
- i * DATA_LENGTH_UINT, GFP_KERNEL);
- if (cmd_head.data)
- break;
- i--;
- }
- if (i) {
- DATA_LENGTH = i * DATA_LENGTH_UINT;
- dev_dbg(&client->dev, "Applied memory size:%d.", DATA_LENGTH);
- } else {
- GTP_ERROR("Apply for memory failed.");
- return FAIL;
- }
-
- cmd_head.addr_len = 2;
- cmd_head.retry = 5;
-
- register_i2c_func();
-
- mutex_init(&lock);
- tool_set_proc_name(procname);
- goodix_proc_entry = create_proc_entry(procname, 0660, NULL);
- if (goodix_proc_entry == NULL) {
- GTP_ERROR("Couldn't create proc entry!");
- return FAIL;
- }
- GTP_INFO("Create proc entry success!");
- goodix_proc_entry->write_proc = goodix_tool_write;
- dix_proc_entry->read_proc = goodix_tool_read;
-
- return SUCCESS;
+ pr_info("I2C function: unregister i2c transfer function\n");
}
void uninit_wr_node(void)
{
cmd_head.data = NULL;
unregister_i2c_func();
- remove_proc_entry(procname, NULL);
+ proc_remove(goodix_proc_entry);
}
static u8 relation(u8 src, u8 dst, u8 rlt)
@@ -257,7 +208,7 @@ static u8 relation(u8 src, u8 dst, u8 rlt)
case 1:
ret = (src == dst) ? true : false;
- GTP_DEBUG("equal:src:0x%02x dst:0x%02x ret:%d.",
+ pr_debug("equal:src:0x%02x dst:0x%02x ret:%d\n",
src, dst, (s32)ret);
break;
@@ -299,23 +250,18 @@ static u8 comfirm(void)
s32 i = 0;
u8 buf[32];
-/* memcpy(&buf[GTP_ADDR_LENGTH - cmd_head.addr_len],
- * &cmd_head.flag_addr, cmd_head.addr_len);
- * memcpy(buf, &cmd_head.flag_addr, cmd_head.addr_len);
- * //Modified by Scott, 2012-02-17
- */
memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len);
for (i = 0; i < cmd_head.times; i++) {
if (tool_i2c_read(buf, 1) <= 0) {
- GTP_ERROR("Read flag data failed!");
+ dev_err(&gt_client->dev, "Read flag data failed");
return FAIL;
}
if (true == relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val,
cmd_head.flag_relation)) {
- GTP_DEBUG("value at flag addr:0x%02x.",
+ pr_debug("value at flag addr:0x%02x\n",
buf[GTP_ADDR_LENGTH]);
- GTP_DEBUG("flag value:0x%02x.", cmd_head.flag_val);
+ pr_debug("flag value:0x%02x\n", cmd_head.flag_val);
break;
}
@@ -323,135 +269,135 @@ static u8 comfirm(void)
}
if (i >= cmd_head.times) {
- GTP_ERROR("Didn't get the flag to continue!");
+ dev_err(&gt_client->dev, "Didn't get the flag to continue");
return FAIL;
}
return SUCCESS;
}
-/********************************************************
+#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
+static s32 fill_update_info(char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ u8 buf[4];
+
+ buf[0] = show_len >> 8;
+ buf[1] = show_len & 0xff;
+ buf[2] = total_len >> 8;
+ buf[3] = total_len & 0xff;
+ return simple_read_from_buffer(user_buf, count, ppos,
+ buf, sizeof(buf));
+}
+#else
+static s32 fill_update_info(char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return -ENODEV;
+}
+#endif
+
+/*
* Function:
* Goodix tool write function.
* Input:
* standard proc write function param.
* Output:
* Return write length.
- *******************************************************
*/
-static s32 goodix_tool_write(struct file *filp, const char __user *buff,
- unsigned long len, void *data)
+static s32 goodix_tool_write(struct file *filp, const char __user *userbuf,
+ size_t count, loff_t *ppos)
{
s32 ret = 0;
- GTP_DEBUG_FUNC();
- GTP_DEBUG_ARRAY((u8 *)buff, len);
-
mutex_lock(&lock);
- ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
+ ret = copy_from_user(&cmd_head, userbuf, CMD_HEAD_LENGTH);
if (ret) {
- GTP_ERROR("copy_from_user failed.");
- ret = -EACCES;
+ dev_err(&gt_client->dev, "copy_from_user failed");
+ ret = -EFAULT;
goto exit;
}
- GTP_DEBUG("wr :0x%02x.", cmd_head.wr);
- GTP_DEBUG("flag:0x%02x.", cmd_head.flag);
- GTP_DEBUG("flag addr:0x%02x%02x.", cmd_head.flag_addr[0],
- cmd_head.flag_addr[1]);
- GTP_DEBUG("flag val:0x%02x.", cmd_head.flag_val);
- GTP_DEBUG("flag rel:0x%02x.", cmd_head.flag_relation);
- GTP_DEBUG("circle :%d.", (s32)cmd_head.circle);
- GTP_DEBUG("times :%d.", (s32)cmd_head.times);
- GTP_DEBUG("retry :%d.", (s32)cmd_head.retry);
- GTP_DEBUG("delay :%d.", (s32)cmd_head.delay);
- GTP_DEBUG("data len:%d.", (s32)cmd_head.data_len);
- GTP_DEBUG("addr len:%d.", (s32)cmd_head.addr_len);
- GTP_DEBUG("addr:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]);
- GTP_DEBUG("len:%d.", (s32)len);
- GTP_DEBUG("buf[20]:0x%02x.", buff[CMD_HEAD_LENGTH]);
-
- if (cmd_head.data_len > (DATA_LENGTH - GTP_ADDR_LENGTH)) {
- pr_err("data len %d > data buff %d, rejected!\n",
- cmd_head.data_len, (DATA_LENGTH - GTP_ADDR_LENGTH));
- ret = -EINVAL;
- goto exit;
- }
- if (cmd_head.addr_len > GTP_ADDR_LENGTH) {
- pr_err(" addr len %d > data buff %d, rejected!\n",
- cmd_head.addr_len, GTP_ADDR_LENGTH);
+ dev_dbg(&gt_client->dev, "wr:0x%02x, flag:0x%02x, flag addr:0x%02x%02x,
+ flag val:0x%02x, flag rel:0x%02x,", cmd_headd.wr,
+ cmd_head.flag, cmd_head.flag_addr[0],
+ cmd_head.flag_addr[1], cmd_head.flag_val,
+ cmd_head.flag_relation);
+ dev_dbg(&gt_client->dev, "circle:%d, times:%d, retry:%d, delay:%d,
+ data len:%d, addr len:%d, addr:0x%02x%02x, write len: %d",
+ (s32)cmd_head.circle, (s32)cmd_head.times, (s32)cmd_head.retry,
+ (s32)cmd_head.delay, (s32)cmd_head.data_len,
+ (s32)cmd_head.addr_len, cmd_head.addr[0], cmd_head.addr[1],
+ (s32)count);
+
+ if (cmd_head.data_len > (data_length - GTP_ADDR_LENGTH)) {
+ dev_err(&gt_client->dev, "data len %d > data buff %d, rejected\n",
+ cmd_head.data_len, (data_length - GTP_ADDR_LENGTH));
ret = -EINVAL;
goto exit;
}
- if (cmd_head.wr == 1) {
- /* copy_from_user(&cmd_head.data[cmd_head.addr_len],
- * &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
- */
+ if (cmd_head.wr == GTP_RW_WRITE) {
ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
- &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
- if (ret)
- GTP_ERROR("copy_from_user failed.");
+ &userbuf[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if (ret) {
+ dev_err(&gt_client->dev, "copy_from_user failed");
+ ret = -EFAULT;
+ goto exit;
+ }
memcpy(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
cmd_head.addr, cmd_head.addr_len);
- GTP_DEBUG_ARRAY(cmd_head.data,
- cmd_head.data_len + cmd_head.addr_len);
- GTP_DEBUG_ARRAY((u8 *)&buff[CMD_HEAD_LENGTH],
- cmd_head.data_len);
-
- if (cmd_head.flag == 1) {
- if (comfirm() == FAIL) {
- GTP_ERROR("[WRITE]Comfirm fail!");
+ if (cmd_head.flag == GTP_NEED_FLAG) {
+ if (comfirm() == FAIL) {
+ dev_err(&gt_client->dev, "Confirm fail");
ret = -EINVAL;
goto exit;
}
- } else if (cmd_head.flag == 2) {
+ } else if (cmd_head.flag == GTP_NEED_INTERRUPT) {
/* Need interrupt! */
}
if (tool_i2c_write(
&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
cmd_head.data_len + cmd_head.addr_len) <= 0) {
- GTP_ERROR("[WRITE]Write data failed!");
+ dev_err(&gt_client->dev, "Write data failed");
ret = -EIO;
goto exit;
}
- GTP_DEBUG_ARRAY(
- &cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
- cmd_head.data_len + cmd_head.addr_len);
if (cmd_head.delay)
msleep(cmd_head.delay);
ret = cmd_head.data_len + CMD_HEAD_LENGTH;
goto exit;
- } else if (cmd_head.wr == 3) { /* Write ic type */
-
- ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH],
+ } else if (cmd_head.wr == GTP_RW_WRITE_IC_TYPE) { /* Write ic type */
+ ret = copy_from_user(&cmd_head.data[0],
+ &userbuf[CMD_HEAD_LENGTH],
cmd_head.data_len);
- if (ret)
- GTP_ERROR("copy_from_user failed.");
+ if (ret) {
+ dev_err(&gt_client->dev, "copy_from_user failed");
+ ret = -EFAULT;
+ goto exit;
+ }
- if (cmd_head.data_len > sizeof(IC_TYPE)) {
- pr_err("<<-GTP->> data len %d > data buff %d, rejected!\n",
- cmd_head.data_len, sizeof(IC_TYPE));
+ if (cmd_head.data_len > sizeof(ic_type)) {
+ dev_err(&gt_client->dev,
+ "data len %d > data buff %d, rejected\n",
+ cmd_head.data_len, sizeof(ic_type));
ret = -EINVAL;
goto exit;
}
- memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
+ memcpy(ic_type, cmd_head.data, cmd_head.data_len);
register_i2c_func();
ret = cmd_head.data_len + CMD_HEAD_LENGTH;
goto exit;
- } else if (cmd_head.wr == 5) {
-
- /* memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len); */
-
+ } else if (cmd_head.wr == GTP_RW_NO_WRITE) {
ret = cmd_head.data_len + CMD_HEAD_LENGTH;
goto exit;
- } else if (cmd_head.wr == 7) { /* disable irq! */
+ } else if (cmd_head.wr == GTP_RW_DISABLE_IRQ) { /* disable irq! */
gtp_irq_disable(i2c_get_clientdata(gt_client));
#if GTP_ESD_PROTECT
@@ -459,7 +405,7 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
#endif
ret = CMD_HEAD_LENGTH;
goto exit;
- } else if (cmd_head.wr == 9) { /* enable irq! */
+ } else if (cmd_head.wr == GTP_RW_ENABLE_IRQ) { /* enable irq! */
gtp_irq_enable(i2c_get_clientdata(gt_client));
#if GTP_ESD_PROTECT
@@ -467,41 +413,45 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
#endif
ret = CMD_HEAD_LENGTH;
goto exit;
- } else if (cmd_head.wr == 17) {
+ } else if (cmd_head.wr == GTP_RW_CHECK_RAWDIFF_MODE) {
struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
- &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
- if (ret)
- GTP_DEBUG("copy_from_user failed.");
+ &userbuf[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if (ret) {
+ dev_err(&gt_client->dev, "copy_from_user failed");
+ goto exit;
+ }
if (cmd_head.data[GTP_ADDR_LENGTH]) {
- GTP_DEBUG("gtp enter rawdiff.");
+ pr_debug("gtp enter rawdiff\n");
ts->gtp_rawdiff_mode = true;
} else {
ts->gtp_rawdiff_mode = false;
- GTP_DEBUG("gtp leave rawdiff.");
+ pr_debug("gtp leave rawdiff\n");
}
ret = CMD_HEAD_LENGTH;
goto exit;
- }
-#ifdef UPDATE_FUNCTIONS
- else if (cmd_head.wr == 11) { /* Enter update mode! */
- if (gup_enter_update_mode(gt_client) == FAIL)
+ } else if (cmd_head.wr == GTP_RW_ENTER_UPDATE_MODE) {
+ /* Enter update mode! */
+ if (gup_enter_update_mode(gt_client) == FAIL) {
ret = -EBUSY;
goto exit;
- } else if (cmd_head.wr == 13) { /* Leave update mode! */
- gup_leave_update_mode();
- } else if (cmd_head.wr == 15) { /* Update firmware! */
+ }
+ } else if (cmd_head.wr == GTP_RW_LEAVE_UPDATE_MODE) {
+ /* Leave update mode! */
+ gup_leave_update_mode(gt_client);
+ } else if (cmd_head.wr == GTP_RW_UPDATE_FW) {
+ /* Update firmware! */
show_len = 0;
total_len = 0;
- if (cmd_head.data_len + 1 > DATA_LENGTH) {
- pr_err("<<-GTP->> data len %d > data buff %d, rejected!\n",
- cmd_head.data_len + 1, DATA_LENGTH);
+ if (cmd_head.data_len + 1 > data_length) {
+ dev_err(&gt_client->dev, "data len %d > data buff %d, rejected\n",
+ cmd_head.data_len + 1, data_length);
ret = -EINVAL;
goto exit;
}
memset(cmd_head.data, 0, cmd_head.data_len + 1);
- memcpy(cmd_head.data, &buff[CMD_HEAD_LENGTH],
+ memcpy(cmd_head.data, &userbuf[CMD_HEAD_LENGTH],
cmd_head.data_len);
if (gup_update_proc((void *)cmd_head.data) == FAIL) {
@@ -509,7 +459,6 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
goto exit;
}
}
-#endif
ret = CMD_HEAD_LENGTH;
exit:
@@ -517,103 +466,131 @@ exit:
return ret;
}
-/*******************************************************
+/*
* Function:
* Goodix tool read function.
* Input:
* standard proc read function param.
* Output:
* Return read length.
- *******************************************************
-*/
-static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
- int *eof, void *data)
+ */
+static s32 goodix_tool_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
{
+ u16 data_len = 0;
s32 ret;
- GTP_DEBUG_FUNC();
+ u8 buf[32];
mutex_lock(&lock);
- if (cmd_head.wr % 2) {
- pr_err("<< [READ]command head wrong\n");
+ if (cmd_head.wr & 0x1) {
+ dev_err(&gt_client->dev, "command head wrong\n");
ret = -EINVAL;
goto exit;
- } else if (!cmd_head.wr) {
- u16 len = 0;
- s16 data_len = 0;
- u16 loc = 0;
+ }
- if (cmd_head.flag == 1) {
+ switch (cmd_head.wr) {
+ case GTP_RW_READ:
+ if (cmd_head.flag == GTP_NEED_FLAG) {
if (comfirm() == FAIL) {
- GTP_ERROR("[READ]Comfirm fail!");
+ dev_err(&gt_client->dev, "Confirm fail");
ret = -EINVAL;
goto exit;
}
- } else if (cmd_head.flag == 2) {
+ } else if (cmd_head.flag == GTP_NEED_INTERRUPT) {
/* Need interrupt! */
}
memcpy(cmd_head.data, cmd_head.addr, cmd_head.addr_len);
- GTP_DEBUG("[CMD HEAD DATA] ADDR:0x%02x%02x.", cmd_head.data[0],
+ pr_debug("[CMD HEAD DATA] ADDR:0x%02x%02x.\n", cmd_head.data[0],
cmd_head.data[1]);
- GTP_DEBUG("[CMD HEAD ADDR] ADDR:0x%02x%02x.", cmd_head.addr[0],
+ pr_debug("[CMD HEAD ADDR] ADDR:0x%02x%02x.\n", cmd_head.addr[0],
cmd_head.addr[1]);
if (cmd_head.delay)
msleep(cmd_head.delay);
data_len = cmd_head.data_len;
- while (data_len > 0) {
- if (data_len > DATA_LENGTH)
- len = DATA_LENGTH;
- else
- len = data_len;
-
- data_len -= len;
-
- if (tool_i2c_read(cmd_head.data, len) <= 0) {
- GTP_ERROR("[READ]Read data failed!");
- ret = -EINVAL;
- goto exit;
- }
- memcpy(&page[loc], &cmd_head.data[GTP_ADDR_LENGTH],
- len);
- loc += len;
+ if (data_len <= 0 || (data_len > data_length)) {
+ dev_err(&gt_client->dev, "Invalid data length %d\n",
+ data_len);
+ ret = -EINVAL;
+ goto exit;
+ }
+ if (data_len > count)
+ data_len = count;
- GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH], len);
- GTP_DEBUG_ARRAY(page, len);
+ if (tool_i2c_read(cmd_head.data, data_len) <= 0) {
+ dev_err(&gt_client->dev, "Read data failed\n");
+ ret = -EIO;
+ goto exit;
}
- } else if (cmd_head.wr == 2) {
- /* memcpy(page, "gt8", cmd_head.data_len);
- * memcpy(page, "GT818", 5);
- * page[5] = 0;
- */
-
- GTP_DEBUG("Return ic type:%s len:%d.", page,
- (s32)cmd_head.data_len);
- ret = cmd_head.data_len;
- goto exit;
- /* return sizeof(IC_TYPE_NAME); */
- } else if (cmd_head.wr == 4) {
- page[0] = show_len >> 8;
- page[1] = show_len & 0xff;
- page[2] = total_len >> 8;
- page[3] = total_len & 0xff;
- } else if (cmd_head.wr == 6) {
- /* Read error code! */
- } else if (cmd_head.wr == 8) { /*Read driver version */
- /* memcpy(page, GTP_DRIVER_VERSION,
- * strlen(GTP_DRIVER_VERSION));
- */
- s32 tmp_len;
-
- tmp_len = strlen(GTP_DRIVER_VERSION);
- memcpy(page, GTP_DRIVER_VERSION, tmp_len);
- page[tmp_len] = 0;
+ ret = simple_read_from_buffer(user_buf, count, ppos,
+ &cmd_head.data[GTP_ADDR_LENGTH], data_len);
+ break;
+ case GTP_RW_FILL_INFO:
+ ret = fill_update_info(user_buf, count, ppos);
+ break;
+ case GTP_RW_READ_VERSION:
+ /* Read driver version */
+ data_len = scnprintf(buf, sizeof(buf), "%s\n",
+ GTP_DRIVER_VERSION);
+ ret = simple_read_from_buffer(user_buf, count, ppos,
+ buf, data_len);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
- ret = cmd_head.data_len;
exit:
mutex_unlock(&lock);
return ret;
}
+
+static const struct file_operations goodix_proc_fops = {
+ .write = goodix_tool_write,
+ .read = goodix_tool_read,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+};
+
+s32 init_wr_node(struct i2c_client *client)
+{
+ u8 i;
+
+ gt_client = client;
+ memset(&cmd_head, 0, sizeof(cmd_head));
+ cmd_head.data = NULL;
+
+ i = GTP_I2C_RETRY_5;
+ while ((!cmd_head.data) && i) {
+ cmd_head.data = devm_kzalloc(&client->dev,
+ i * DATA_LENGTH_UINT, GFP_KERNEL);
+ if (cmd_head.data)
+ break;
+ i--;
+ }
+ if (i) {
+ data_length = i * DATA_LENGTH_UINT;
+ dev_dbg(&client->dev, "Applied memory size:%d", data_length);
+ }
+
+ cmd_head.addr_len = 2;
+ cmd_head.retry = GTP_I2C_RETRY_5;
+
+ register_i2c_func();
+
+ mutex_init(&lock);
+ tool_set_proc_name(procname);
+ goodix_proc_entry = proc_create(procname,
+ S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
+ goodix_proc_entry,
+ &goodix_proc_fops);
+ if (goodix_proc_entry == NULL) {
+ dev_err(&client->dev, "Couldn't create proc entry");
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c
index 6c3747108d75..a9d7666a6d6f 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.c
@@ -44,18 +44,16 @@
#include "gt9xx.h"
#include <linux/of_gpio.h>
-
+#include <linux/irq.h>
+#include <linux/module.h>
#include <linux/input/mt.h>
+#include <linux/debugfs.h>
#define GOODIX_DEV_NAME "Goodix-CTP"
#define CFG_MAX_TOUCH_POINTS 5
#define GOODIX_COORDS_ARR_SIZE 4
#define MAX_BUTTONS 4
-/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
-#define GTP_I2C_ADDRESS_HIGH 0x14
-#define GTP_I2C_ADDRESS_LOW 0x5D
-
#define GOODIX_VTG_MIN_UV 2600000
#define GOODIX_VTG_MAX_UV 3300000
#define GOODIX_I2C_VTG_MIN_UV 1800000
@@ -77,23 +75,18 @@
#if GTP_HAVE_TOUCH_KEY
static const u16 touch_key_array[] = {KEY_MENU, KEY_HOMEPAGE, KEY_BACK};
-#if GTP_DEBUG_ON
-static const int key_codes[] = {
- KEY_HOME, KEY_BACK, KEY_MENU, KEY_SEARCH
-};
-static const char *const key_names[] = {
- "Key_Home", "Key_Back", "Key_Menu", "Key_Search"
-};
-#endif
#endif
-static void gtp_reset_guitar(struct goodix_ts_data *ts, int ms);
static void gtp_int_sync(struct goodix_ts_data *ts, int ms);
static int gtp_i2c_test(struct i2c_client *client);
+static int goodix_power_off(struct goodix_ts_data *ts);
+static int goodix_power_on(struct goodix_ts_data *ts);
#if defined(CONFIG_FB)
static int fb_notifier_callback(struct notifier_block *self,
- unsigned long event, void *data);
+ unsigned long event, void *data);
+static int goodix_ts_suspend(struct device *dev);
+static int goodix_ts_resume(struct device *dev);
#elif defined(CONFIG_HAS_EARLYSUSPEND)
static void goodix_ts_early_suspend(struct early_suspend *h);
static void goodix_ts_late_resume(struct early_suspend *h);
@@ -104,7 +97,6 @@ static struct delayed_work gtp_esd_check_work;
static struct workqueue_struct *gtp_esd_check_workqueue;
static void gtp_esd_check_func(struct work_struct *work);
static int gtp_init_ext_watchdog(struct i2c_client *client);
-struct i2c_client *i2c_connect_client;
#endif
#if GTP_SLIDE_WAKEUP
@@ -119,6 +111,10 @@ static s8 gtp_enter_doze(struct goodix_ts_data *ts);
bool init_done;
static u8 chip_gt9xxs; /* true if ic is gt9xxs, like gt915s */
u8 grp_cfg_version;
+struct i2c_client *i2c_connect_client;
+
+#define GTP_DEBUGFS_DIR "ts_debug"
+#define GTP_DEBUGFS_FILE_SUSPEND "suspend"
/*******************************************************
Function:
@@ -270,7 +266,7 @@ Output:
result of i2c write operation.
> 0: succeed, otherwise: failed
*********************************************************/
-static int gtp_send_cfg(struct goodix_ts_data *ts)
+int gtp_send_cfg(struct goodix_ts_data *ts)
{
int ret;
#if GTP_DRIVER_SEND_CFG
@@ -493,30 +489,19 @@ static void goodix_ts_work_func(struct work_struct *work)
memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
}
-#if GTP_HAVE_TOUCH_KEY
+
key_value = point_data[3 + 8 * touch_num];
if (key_value || pre_key) {
- for (i = 0; i < ARRAY_SIZE(touch_key_array); i++) {
-#if GTP_DEBUG_ON
- for (ret = 0; ret < 4; ++ret) {
- if (key_codes[ret] == touch_key_array[i]) {
- GTP_DEBUG("Key: %s %s",
- key_names[ret],
- (key_value & (0x01 << i))
- ? "Down" : "Up");
- break;
- }
- }
-#endif
-
+ for (i = 0; i < ts->pdata->num_button; i++) {
input_report_key(ts->input_dev,
- touch_key_array[i], key_value & (0x01<<i));
+ ts->pdata->button_map[i],
+ key_value & (0x01<<i));
}
touch_num = 0;
pre_touch = 0;
}
-#endif
+
pre_key = key_value;
#if GTP_WITH_PEN
@@ -612,26 +597,6 @@ exit_work_func:
/*******************************************************
Function:
- Timer interrupt service routine for polling mode.
-Input:
- timer: timer struct pointer
-Output:
- Timer work mode.
- HRTIMER_NORESTART: no restart mode
-*********************************************************/
-static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer)
-{
- struct goodix_ts_data
- *ts = container_of(timer, struct goodix_ts_data, timer);
-
- queue_work(ts->goodix_wq, &ts->work);
- hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME + 6) * 1000000),
- HRTIMER_MODE_REL);
- return HRTIMER_NORESTART;
-}
-
-/*******************************************************
-Function:
External interrupt service routine for interrupt mode.
Input:
irq: interrupt number.
@@ -673,7 +638,7 @@ Input:
Output:
None.
*******************************************************/
-static void gtp_reset_guitar(struct goodix_ts_data *ts, int ms)
+void gtp_reset_guitar(struct goodix_ts_data *ts, int ms)
{
/* This reset sequence will selcet I2C slave address */
gpio_direction_output(ts->pdata->reset_gpio, 0);
@@ -747,16 +712,13 @@ static s8 gtp_enter_doze(struct goodix_ts_data *ts)
return ret;
}
#else
-/*******************************************************
-Function:
- Enter sleep mode.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- 1: succeed, otherwise failed.
-*******************************************************/
-static s8 gtp_enter_sleep(struct goodix_ts_data *ts)
+/**
+ * gtp_enter_sleep - Enter sleep mode
+ * @ts: driver private data
+ *
+ * Returns zero on success, else an error.
+ */
+static u8 gtp_enter_sleep(struct goodix_ts_data *ts)
{
int ret = -1;
s8 retry = 0;
@@ -765,20 +727,36 @@ static s8 gtp_enter_sleep(struct goodix_ts_data *ts)
(u8)GTP_REG_SLEEP, 5};
ret = gpio_direction_output(ts->pdata->irq_gpio, 0);
+ if (ret)
+ dev_err(&ts->client->dev,
+ "GTP sleep: Cannot reconfig gpio %d.\n",
+ ts->pdata->irq_gpio);
+ if (ts->pdata->enable_power_off) {
+ ret = gpio_direction_output(ts->pdata->reset_gpio, 0);
+ if (ret)
+ dev_err(&ts->client->dev,
+ "GTP sleep: Cannot reconfig gpio %d.\n",
+ ts->pdata->reset_gpio);
+ ret = goodix_power_off(ts);
+ if (ret) {
+ dev_err(&ts->client->dev, "GTP power off failed.\n");
+ return ret;
+ }
+ return 0;
+ }
usleep(5000);
while (retry++ < GTP_I2C_RETRY_5) {
ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
- if (ret > 0) {
- dev_dbg(&ts->client->dev,
- "GTP enter sleep!");
- return ret;
+ if (ret == 1) {
+ dev_dbg(&ts->client->dev, "GTP enter sleep!");
+ return 0;
}
msleep(20);
}
dev_err(&ts->client->dev, "GTP send sleep cmd failed.\n");
return ret;
}
-#endif
+#endif /* !GTP_SLIDE_WAKEUP */
/*******************************************************
Function:
@@ -794,17 +772,36 @@ static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts)
u8 retry = 0;
s8 ret = -1;
-#if GTP_POWER_CTRL_SLEEP
- gtp_reset_guitar(ts, 20);
+ if (ts->pdata->enable_power_off) {
+ ret = gpio_direction_output(ts->pdata->irq_gpio, 0);
+ if (ret)
+ dev_err(&ts->client->dev,
+ "GTP wakeup: Cannot reconfig gpio %d.\n",
+ ts->pdata->irq_gpio);
+ ret = gpio_direction_output(ts->pdata->reset_gpio, 0);
+ if (ret)
+ dev_err(&ts->client->dev,
+ "GTP wakeup: Cannot reconfig gpio %d.\n",
+ ts->pdata->reset_gpio);
+ ret = goodix_power_on(ts);
+ if (ret) {
+ dev_err(&ts->client->dev, "GTP power on failed.\n");
+ return 0;
+ }
+
+ gtp_reset_guitar(ts, 20);
+
+ ret = gtp_send_cfg(ts);
+ if (ret <= 0) {
+ dev_err(&ts->client->dev,
+ "GTP wakeup sleep failed.\n");
+ return ret;
+ }
- ret = gtp_send_cfg(ts);
- if (ret > 0) {
dev_dbg(&ts->client->dev,
- "Wakeup sleep send config success.");
- return 1;
- }
-#else
- while (retry++ < GTP_I2C_RETRY_10) {
+ "Wakeup sleep send config success.");
+ } else {
+err_retry:
#if GTP_SLIDE_WAKEUP
/* wakeup not by slide */
if (doze_status != DOZE_WAKEUP)
@@ -821,7 +818,7 @@ static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts)
}
#endif
ret = gtp_i2c_test(ts->client);
- if (ret > 0) {
+ if (ret == 2) {
dev_dbg(&ts->client->dev, "GTP wakeup sleep.");
#if (!GTP_SLIDE_WAKEUP)
if (chip_gt9xxs == 0) {
@@ -835,10 +832,10 @@ static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts)
return ret;
}
gtp_reset_guitar(ts, 20);
+ if (retry++ < GTP_I2C_RETRY_10)
+ goto err_retry;
+ dev_err(&ts->client->dev, "GTP wakeup sleep failed.\n");
}
-#endif
-
- dev_err(&ts->client->dev, "GTP wakeup sleep failed.\n");
return ret;
}
#endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/
@@ -1051,9 +1048,7 @@ static int gtp_check_product_id(struct i2c_client *client)
dev_info(&client->dev, "Goodix Product ID = %s\n", product_id);
- if (!IS_ERR(ts->pdata->product_id))
- ret = strcmp(product_id, ts->pdata->product_id);
-
+ ret = strcmp(product_id, ts->pdata->product_id);
if (ret != 0)
return -EINVAL;
@@ -1180,19 +1175,14 @@ static int gtp_request_irq(struct goodix_ts_data *ts)
int ret;
const u8 irq_table[] = GTP_IRQ_TAB;
- ret = request_irq(ts->client->irq, goodix_ts_irq_handler,
+ GTP_DEBUG("INT trigger type:%x, irq=%d", ts->int_trigger_type,
+ ts->client->irq);
+
+ ret = request_threaded_irq(ts->client->irq, NULL,
+ goodix_ts_irq_handler,
irq_table[ts->int_trigger_type],
ts->client->name, ts);
if (ret) {
- dev_err(&ts->client->dev, "Request IRQ failed!ERRNO:%d.\n",
- ret);
- gpio_direction_input(ts->pdata->irq_gpio);
-
- hrtimer_init(&ts->timer, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- ts->timer.function = goodix_ts_timer_handler;
- hrtimer_start(&ts->timer, ktime_set(1, 0),
- HRTIMER_MODE_REL);
ts->use_irq = false;
return ret;
}
@@ -1232,12 +1222,11 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts)
/* in case of "out of memory" */
input_mt_init_slots(ts->input_dev, 10, 0);
-#if GTP_HAVE_TOUCH_KEY
- for (index = 0; index < ARRAY_SIZE(touch_key_array); index++) {
+ for (index = 0; index < ts->pdata->num_button; index++) {
input_set_capability(ts->input_dev,
- EV_KEY, touch_key_array[index]);
+ EV_KEY, ts->pdata->button_map[index]);
}
-#endif
+
#if GTP_SLIDE_WAKEUP
input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
@@ -1305,6 +1294,12 @@ static int goodix_power_on(struct goodix_ts_data *ts)
{
int ret;
+ if (ts->power_on) {
+ dev_info(&ts->client->dev,
+ "Device already power on\n");
+ return 0;
+ }
+
if (!IS_ERR(ts->avdd)) {
ret = reg_set_optimum_mode_check(ts->avdd,
GOODIX_VDD_LOAD_MAX_UA);
@@ -1371,6 +1366,7 @@ static int goodix_power_on(struct goodix_ts_data *ts)
}
}
+ ts->power_on = true;
return 0;
err_enable_vcc_i2c:
@@ -1389,6 +1385,7 @@ err_set_vtg_vdd:
regulator_disable(ts->avdd);
err_enable_avdd:
err_set_opt_avdd:
+ ts->power_on = false;
return ret;
}
@@ -1402,6 +1399,12 @@ static int goodix_power_off(struct goodix_ts_data *ts)
{
int ret;
+ if (!ts->power_on) {
+ dev_info(&ts->client->dev,
+ "Device already power off\n");
+ return 0;
+ }
+
if (!IS_ERR(ts->vcc_i2c)) {
ret = regulator_set_voltage(ts->vcc_i2c, 0,
GOODIX_I2C_VTG_MAX_UV);
@@ -1434,6 +1437,7 @@ static int goodix_power_off(struct goodix_ts_data *ts)
"Regulator avdd disable failed ret=%d\n", ret);
}
+ ts->power_on = false;
return 0;
}
@@ -1486,6 +1490,100 @@ static int goodix_power_deinit(struct goodix_ts_data *ts)
return 0;
}
+static ssize_t gtp_fw_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct goodix_ts_data *ts = dev_get_drvdata(dev);
+
+ if (!strlen(ts->fw_name))
+ return snprintf(buf, GTP_FW_NAME_MAXSIZE - 1,
+ "No fw name has been given.");
+ else
+ return snprintf(buf, GTP_FW_NAME_MAXSIZE - 1,
+ "%s\n", ts->fw_name);
+}
+
+static ssize_t gtp_fw_name_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct goodix_ts_data *ts = dev_get_drvdata(dev);
+
+ if (size > GTP_FW_NAME_MAXSIZE - 1) {
+ dev_err(dev, "FW name size exceeds the limit.");
+ return -EINVAL;
+ }
+
+ strlcpy(ts->fw_name, buf, size);
+ if (ts->fw_name[size-1] == '\n')
+ ts->fw_name[size-1] = '\0';
+
+ return size;
+}
+
+static DEVICE_ATTR(fw_name, (S_IRUGO | S_IWUSR | S_IWGRP),
+ gtp_fw_name_show,
+ gtp_fw_name_store);
+
+static struct attribute *gtp_attrs[] = {
+ &dev_attr_fw_name.attr,
+ NULL
+};
+
+static const struct attribute_group gtp_attr_grp = {
+ .attrs = gtp_attrs,
+};
+
+static int gtp_debug_suspend_set(void *_data, u64 val)
+{
+ struct goodix_ts_data *ts = _data;
+
+ mutex_lock(&ts->input_dev->mutex);
+ if (val)
+ goodix_ts_suspend(&ts->client->dev);
+ else
+ goodix_ts_resume(&ts->client->dev);
+ mutex_unlock(&ts->input_dev->mutex);
+
+ return 0;
+}
+
+static int gtp_debug_suspend_get(void *_data, u64 *val)
+{
+ struct goodix_ts_data *ts = _data;
+
+ mutex_lock(&ts->input_dev->mutex);
+ *val = ts->gtp_is_suspend;
+ mutex_unlock(&ts->input_dev->mutex);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, gtp_debug_suspend_get,
+ gtp_debug_suspend_set, "%lld\n");
+
+static int gtp_debugfs_init(struct goodix_ts_data *data)
+{
+ data->debug_base = debugfs_create_dir(GTP_DEBUGFS_DIR, NULL);
+
+ if (IS_ERR_OR_NULL(data->debug_base)) {
+ pr_err("Failed to create debugfs dir\n");
+ return -EINVAL;
+ }
+
+ if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_SUSPEND,
+ S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
+ data->debug_base,
+ data,
+ &debug_suspend_fops)))) {
+ pr_err("Failed to create suspend file\n");
+ debugfs_remove_recursive(data->debug_base);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int goodix_ts_get_dt_coords(struct device *dev, char *name,
struct goodix_ts_platform_data *pdata)
{
@@ -1549,6 +1647,9 @@ static int goodix_parse_dt(struct device *dev,
pdata->no_force_update = of_property_read_bool(np,
"goodix,no-force-update");
+
+ pdata->enable_power_off = of_property_read_bool(np,
+ "goodix,enable-power-off");
/* reset, irq gpio info */
pdata->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios",
0, &pdata->reset_gpio_flags);
@@ -1562,8 +1663,17 @@ static int goodix_parse_dt(struct device *dev,
rc = of_property_read_string(np, "goodix,product-id",
&pdata->product_id);
- if (rc < 0 || strlen(pdata->product_id) > GTP_PRODUCT_ID_MAXSIZE)
- return rc;
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Failed to parse product_id.");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_string(np, "goodix,fw_name",
+ &pdata->fw_name);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Failed to parse firmware name.\n");
+ return -EINVAL;
+ }
prop = of_find_property(np, "goodix,button-map", NULL);
if (prop) {
@@ -1578,6 +1688,9 @@ static int goodix_parse_dt(struct device *dev,
dev_err(dev, "Unable to read key codes\n");
return rc;
}
+ pdata->num_button = num_buttons;
+ memcpy(pdata->button_map, button_map,
+ pdata->num_button * sizeof(u32));
}
read_cfg_num = 0;
@@ -1605,7 +1718,7 @@ static int goodix_parse_dt(struct device *dev,
prop->value, pdata->config_data_len[i]);
read_cfg_num++;
}
- dev_dbg(dev, "%d config data read from device tree.\n", read_cfg_num);
+ dev_dbg(dev, "%d config data read from device tree\n", read_cfg_num);
return 0;
}
@@ -1670,6 +1783,7 @@ static int goodix_ts_probe(struct i2c_client *client,
spin_lock_init(&ts->irq_lock);
i2c_set_clientdata(client, ts);
ts->gtp_rawdiff_mode = 0;
+ ts->power_on = false;
ret = gtp_request_io_port(ts);
if (ret) {
@@ -1693,16 +1807,20 @@ static int goodix_ts_probe(struct i2c_client *client,
ret = gtp_i2c_test(client);
if (ret != 2) {
- dev_err(&client->dev, "I2C communication ERROR!\n");
+ dev_err(&client->dev, "I2C communication ERROR\n");
goto exit_power_off;
}
-#if GTP_AUTO_UPDATE
+ if (pdata->fw_name)
+ strlcpy(ts->fw_name, pdata->fw_name,
+ strlen(pdata->fw_name) + 1);
+
+#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
ret = gup_init_update_proc(ts);
if (ret < 0) {
dev_err(&client->dev,
"GTP Create firmware update thread error.\n");
- goto exit_free_io_port;
+ goto exit_power_off;
}
#endif
@@ -1719,7 +1837,9 @@ static int goodix_ts_probe(struct i2c_client *client,
dev_err(&client->dev, "GTP request input dev failed.\n");
goto exit_free_inputdev;
}
+ input_set_drvdata(ts->input_dev, ts);
+ mutex_init(&ts->lock);
#if defined(CONFIG_FB)
ts->fb_notif.notifier_call = fb_notifier_callback;
ret = fb_register_client(&ts->fb_notif);
@@ -1738,37 +1858,48 @@ static int goodix_ts_probe(struct i2c_client *client,
INIT_WORK(&ts->work, goodix_ts_work_func);
ret = gtp_request_irq(ts);
- if (ret < 0)
- dev_info(&client->dev, "GTP works in polling mode.\n");
+ if (ret)
+ dev_info(&client->dev, "GTP request irq failed %d\n", ret);
else
- dev_info(&client->dev, "GTP works in interrupt mode.\n");
+ dev_info(&client->dev, "GTP works in interrupt mode\n");
ret = gtp_read_fw_version(client, &version_info);
if (ret != 2)
- dev_err(&client->dev, "GTP firmware version read failed.\n");
+ dev_err(&client->dev, "GTP firmware version read failed\n");
ret = gtp_check_product_id(client);
if (ret != 0) {
- dev_err(&client->dev, "GTP Product id doesn't match.\n");
+ dev_err(&client->dev, "GTP Product id doesn't match\n");
goto exit_free_irq;
}
if (ts->use_irq)
gtp_irq_enable(ts);
-#if GTP_CREATE_WR_NODE
+#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG
init_wr_node(client);
#endif
#if GTP_ESD_PROTECT
gtp_esd_switch(client, SWITCH_ON);
#endif
+ ret = sysfs_create_group(&client->dev.kobj, &gtp_attr_grp);
+ if (ret < 0) {
+ dev_err(&client->dev, "sys file creation failed\n");
+ goto exit_free_irq;
+ }
+
+ ret = gtp_debugfs_init(ts);
+ if (ret != 0)
+ goto exit_remove_sysfs;
+
init_done = true;
return 0;
exit_free_irq:
+ mutex_destroy(&ts->lock);
#if defined(CONFIG_FB)
if (fb_unregister_client(&ts->fb_notif))
dev_err(&client->dev,
- "Error occurred while unregistering fb_notifier.\n");
+ "Error occurred while unregistering fb_notifier\n");
#elif defined(CONFIG_HAS_EARLYSUSPEND)
unregister_early_suspend(&ts->early_suspend);
#endif
@@ -1785,6 +1916,8 @@ exit_free_irq:
input_free_device(ts->input_dev);
ts->input_dev = NULL;
}
+exit_remove_sysfs:
+ sysfs_remove_group(&ts->input_dev->dev.kobj, &gtp_attr_grp);
exit_free_inputdev:
kfree(ts->config_data);
exit_power_off:
@@ -1813,15 +1946,18 @@ static int goodix_ts_remove(struct i2c_client *client)
{
struct goodix_ts_data *ts = i2c_get_clientdata(client);
+ sysfs_remove_group(&ts->input_dev->dev.kobj, &gtp_attr_grp);
+
#if defined(CONFIG_FB)
if (fb_unregister_client(&ts->fb_notif))
dev_err(&client->dev,
- "Error occurred while unregistering fb_notifier.\n");
+ "Error occurred while unregistering fb_notifier\n");
#elif defined(CONFIG_HAS_EARLYSUSPEND)
unregister_early_suspend(&ts->early_suspend);
#endif
+ mutex_destroy(&ts->lock);
-#if GTP_CREATE_WR_NODE
+#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG
uninit_wr_node();
#endif
@@ -1856,6 +1992,7 @@ static int goodix_ts_remove(struct i2c_client *client)
goodix_power_deinit(ts);
i2c_set_clientdata(client, NULL);
}
+ debugfs_remove_recursive(ts->debug_base);
return 0;
}
@@ -1869,12 +2006,18 @@ Input:
Output:
None.
*******************************************************/
-static void goodix_ts_suspend(struct goodix_ts_data *ts)
+static int goodix_ts_suspend(struct device *dev)
{
- int ret = -1, i;
+ struct goodix_ts_data *ts = dev_get_drvdata(dev);
+ int ret = 0, i;
+
+ if (ts->gtp_is_suspend) {
+ dev_dbg(&ts->client->dev, "Already in suspend state\n");
+ return 0;
+ }
+ mutex_lock(&ts->lock);
#if GTP_ESD_PROTECT
- ts->gtp_is_suspend = 1;
gtp_esd_switch(ts->client, SWITCH_OFF);
#endif
@@ -1894,11 +2037,15 @@ static void goodix_ts_suspend(struct goodix_ts_data *ts)
ret = gtp_enter_sleep(ts);
#endif
if (ret < 0)
- dev_err(&ts->client->dev, "GTP early suspend failed.\n");
+ dev_err(&ts->client->dev, "GTP early suspend failed\n");
/* to avoid waking up while not sleeping,
* delay 48 + 10ms to ensure reliability
*/
msleep(58);
+ mutex_unlock(&ts->lock);
+ ts->gtp_is_suspend = 1;
+
+ return ret;
}
/*******************************************************
@@ -1909,18 +2056,25 @@ Input:
Output:
None.
*******************************************************/
-static void goodix_ts_resume(struct goodix_ts_data *ts)
+static int goodix_ts_resume(struct device *dev)
{
- int ret = -1;
+ struct goodix_ts_data *ts = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (!ts->gtp_is_suspend) {
+ dev_dbg(&ts->client->dev, "Already in awake state\n");
+ return 0;
+ }
+ mutex_lock(&ts->lock);
ret = gtp_wakeup_sleep(ts);
#if GTP_SLIDE_WAKEUP
doze_status = DOZE_DISABLED;
#endif
- if (ret < 0)
- dev_err(&ts->client->dev, "GTP resume failed.\n");
+ if (ret <= 0)
+ dev_err(&ts->client->dev, "GTP resume failed\n");
if (ts->use_irq)
gtp_irq_enable(ts);
@@ -1929,9 +2083,12 @@ static void goodix_ts_resume(struct goodix_ts_data *ts)
ktime_set(1, 0), HRTIMER_MODE_REL);
#if GTP_ESD_PROTECT
- ts->gtp_is_suspend = 0;
gtp_esd_switch(ts->client, SWITCH_ON);
#endif
+ mutex_unlock(&ts->lock);
+ ts->gtp_is_suspend = 0;
+
+ return ret;
}
#if defined(CONFIG_FB)
@@ -1947,9 +2104,9 @@ static int fb_notifier_callback(struct notifier_block *self,
ts && ts->client) {
blank = evdata->data;
if (*blank == FB_BLANK_UNBLANK)
- goodix_ts_resume(ts);
+ goodix_ts_resume(&ts->client->dev);
else if (*blank == FB_BLANK_POWERDOWN)
- goodix_ts_suspend(ts);
+ goodix_ts_suspend(&ts->client->dev);
}
return 0;
@@ -1968,7 +2125,8 @@ static void goodix_ts_early_suspend(struct early_suspend *h)
struct goodix_ts_data *ts;
ts = container_of(h, struct goodix_ts_data, early_suspend);
- goodix_ts_suspend(ts);
+ goodix_ts_suspend(&ts->client->dev);
+ return;
}
/*
@@ -2119,6 +2277,16 @@ static void gtp_esd_check_func(struct work_struct *work)
}
#endif
+#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND))
+static const struct dev_pm_ops goodix_ts_dev_pm_ops = {
+ .suspend = goodix_ts_suspend,
+ .resume = goodix_ts_resume,
+};
+#else
+static const struct dev_pm_ops goodix_ts_dev_pm_ops = {
+};
+#endif
+
static const struct i2c_device_id goodix_ts_id[] = {
{ GTP_I2C_NAME, 0 },
{ }
@@ -2141,6 +2309,9 @@ static struct i2c_driver goodix_ts_driver = {
.name = GTP_I2C_NAME,
.owner = THIS_MODULE,
.of_match_table = goodix_match_table,
+#if CONFIG_PM
+ .pm = &goodix_ts_dev_pm_ops,
+#endif
},
};
@@ -2177,7 +2348,7 @@ static void __exit goodix_ts_exit(void)
i2c_del_driver(&goodix_ts_driver);
}
-late_initcall(goodix_ts_init);
+module_init(goodix_ts_init);
module_exit(goodix_ts_exit);
MODULE_DESCRIPTION("GTP Series Driver");
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h
index 843e3d6c05b2..38487eea7b10 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.h
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.h
@@ -22,17 +22,9 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
-#include <linux/irq.h>
-#include <linux/input.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/gpio.h>
-#include <linux/regulator/consumer.h>
-#include <linux/firmware.h>
-#include <linux/debugfs.h>
+#include <linux/uaccess.h>
#if defined(CONFIG_FB)
#include <linux/notifier.h>
@@ -42,13 +34,17 @@
#define GOODIX_SUSPEND_LEVEL 1
#endif
+#define MAX_BUTTONS 4
#define GOODIX_MAX_CFG_GROUP 6
+#define GTP_FW_NAME_MAXSIZE 50
+
struct goodix_ts_platform_data {
int irq_gpio;
u32 irq_gpio_flags;
int reset_gpio;
u32 reset_gpio_flags;
const char *product_id;
+ const char *fw_name;
u32 x_max;
u32 y_max;
u32 x_min;
@@ -59,8 +55,11 @@ struct goodix_ts_platform_data {
u32 panel_maxy;
bool no_force_update;
bool i2c_pull_up;
+ bool enable_power_off;
size_t config_data_len[GOODIX_MAX_CFG_GROUP];
u8 *config_data[GOODIX_MAX_CFG_GROUP];
+ u32 button_map[MAX_BUTTONS];
+ u8 num_button;
};
struct goodix_ts_data {
spinlock_t irq_lock;
@@ -70,6 +69,7 @@ struct goodix_ts_data {
struct hrtimer timer;
struct workqueue_struct *goodix_wq;
struct work_struct work;
+ struct delayed_work goodix_update_work;
s32 irq_is_disabled;
s32 use_irq;
u16 abs_x_max;
@@ -86,6 +86,8 @@ struct goodix_ts_data {
u8 fixed_cfg;
u8 esd_running;
u8 fw_error;
+ bool power_on;
+ struct mutex lock;
struct regulator *avdd;
struct regulator *vdd;
struct regulator *vcc_i2c;
@@ -94,6 +96,7 @@ struct goodix_ts_data {
#elif defined(CONFIG_HAS_EARLYSUSPEND)
struct early_suspend early_suspend;
#endif
+ struct dentry *debug_base;
};
extern u16 show_len;
@@ -104,47 +107,23 @@ extern u16 total_len;
#define GTP_CHANGE_X2Y 0
#define GTP_DRIVER_SEND_CFG 1
#define GTP_HAVE_TOUCH_KEY 1
-#define GTP_POWER_CTRL_SLEEP 0
-/* auto updated by .bin file as default */
-#define GTP_AUTO_UPDATE 0
-/* auto updated by head_fw_array in gt9xx_firmware.h,
- * function together with GTP_AUTO_UPDATE
- */
-#define GTP_HEADER_FW_UPDATE 0
-
-#define GTP_CREATE_WR_NODE 0
#define GTP_ESD_PROTECT 0
#define GTP_WITH_PEN 0
+/* This cannot work when enable-power-off is on */
#define GTP_SLIDE_WAKEUP 0
/* double-click wakeup, function together with GTP_SLIDE_WAKEUP */
#define GTP_DBL_CLK_WAKEUP 0
-#define GTP_DEBUG_ON 0
-#define GTP_DEBUG_ARRAY_ON 0
-#define GTP_DEBUG_FUNC_ON 0
-
-/*************************** PART2:TODO define *******************************/
-/* STEP_1(REQUIRED): Define Configuration Information Group(s) */
-/* Sensor_ID Map: */
-/* sensor_opt1 sensor_opt2 Sensor_ID
- * GND GND 0
- * VDDIO GND 1
- * NC GND 2
- * GND NC/300K 3
- * VDDIO NC/300K 4
- * NC NC/300K 5
- */
-
-#define GTP_IRQ_TAB {\
+#define GTP_IRQ_TAB {\
IRQ_TYPE_EDGE_RISING,\
IRQ_TYPE_EDGE_FALLING,\
IRQ_TYPE_LEVEL_LOW,\
IRQ_TYPE_LEVEL_HIGH\
}
-/* STEP_3(optional): Specify your special config info if needed */
+
#define GTP_IRQ_TAB_RISING 0
#define GTP_IRQ_TAB_FALLING 1
#if GTP_CUSTOM_CFG
@@ -190,49 +169,52 @@ extern u16 total_len;
#define RESOLUTION_LOC 3
#define TRIGGER_LOC 8
-/* Log define */
-#define GTP_DEBUG(fmt, arg...) do {\
- if (GTP_DEBUG_ON) {\
- pr_debug("<<-GTP-DEBUG->> [%d]"fmt"\n",\
- __LINE__, ##arg); } \
- } while (0)
-
-#define GTP_DEBUG_ARRAY(array, num) do {\
- s32 i; \
- u8 *a = array; \
- if (GTP_DEBUG_ARRAY_ON) {\
- pr_debug("<<-GTP-DEBUG-ARRAY->>\n");\
- for (i = 0; i < (num); i++) { \
- pr_debug("%02x ", (a)[i]);\
- if ((i + 1) % 10 == 0) { \
- pr_debug("\n");\
- } \
- } \
- pr_debug("\n");\
- } \
- } while (0)
-
-#define GTP_DEBUG_FUNC() do {\
- if (GTP_DEBUG_FUNC_ON)\
- pr_debug("<<-GTP-FUNC->> Func:%s@Line:%d\n",\
- __func__, __LINE__);\
- } while (0)
-
-#define GTP_SWAP(x, y) do {\
- typeof(x) z = x;\
- x = y;\
- y = z;\
- } while (0)
+/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
+#define GTP_I2C_ADDRESS_HIGH 0x14
+#define GTP_I2C_ADDRESS_LOW 0x5D
+
+/* GTP CM_HEAD RW flags */
+#define GTP_RW_READ 0
+#define GTP_RW_WRITE 1
+#define GTP_RW_READ_IC_TYPE 2
+#define GTP_RW_WRITE_IC_TYPE 3
+#define GTP_RW_FILL_INFO 4
+#define GTP_RW_NO_WRITE 5
+#define GTP_RW_READ_ERROR 6
+#define GTP_RW_DISABLE_IRQ 7
+#define GTP_RW_READ_VERSION 8
+#define GTP_RW_ENABLE_IRQ 9
+#define GTP_RW_ENTER_UPDATE_MODE 11
+#define GTP_RW_LEAVE_UPDATE_MODE 13
+#define GTP_RW_UPDATE_FW 15
+#define GTP_RW_CHECK_RAWDIFF_MODE 17
+
+/* GTP need flag or interrupt */
+#define GTP_NO_NEED 0
+#define GTP_NEED_FLAG 1
+#define GTP_NEED_INTERRUPT 2
+
/*****************************End of Part III********************************/
void gtp_esd_switch(struct i2c_client *client, int on);
-#if GTP_CREATE_WR_NODE
-extern s32 init_wr_node(struct i2c_client *client);
-extern void uninit_wr_node(void);
+int gtp_i2c_read_dbl_check(struct i2c_client *client, u16 addr,
+ u8 *rxbuf, int len);
+int gtp_send_cfg(struct goodix_ts_data *ts);
+void gtp_reset_guitar(struct goodix_ts_data *ts, int ms);
+void gtp_irq_disable(struct goodix_ts_data *ts);
+void gtp_irq_enable(struct goodix_ts_data *ts);
+
+#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG
+s32 init_wr_node(struct i2c_client *client);
+void uninit_wr_node(void);
#endif
-#if GTP_AUTO_UPDATE
+#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
extern u8 gup_init_update_proc(struct goodix_ts_data *ts);
+s32 gup_enter_update_mode(struct i2c_client *client);
+void gup_leave_update_mode(struct i2c_client *client);
+s32 gup_update_proc(void *dir);
+extern struct i2c_client *i2c_connect_client;
#endif
#endif /* _GOODIX_GT9XX_H_ */
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h b/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h
deleted file mode 100644
index 81e3affe62e9..000000000000
--- a/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * make sense only when GTP_HEADER_FW_UPDATE & GTP_AUTO_UPDATE are enabled
- * define your own firmware array here
-*/
-const unsigned char header_fw_array[] = {
-};
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
index 9fcf7f0bef86..4660b27d156c 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
@@ -31,25 +31,19 @@
* 2. support firmware header array update.
* By Meta, 2013/03/11
*/
-#include <linux/kthread.h>
#include "gt9xx.h"
+#include <linux/firmware.h>
+#include <linux/workqueue.h>
+#include <linux/kernel.h>
-#if GTP_HEADER_FW_UPDATE
-#include <linux/namei.h>
-#include <linux/mount.h>
-#include "gt9xx_firmware.h"
-#endif
+#define FIRMWARE_NAME_LEN_MAX 256
#define GUP_REG_HW_INFO 0x4220
#define GUP_REG_FW_MSG 0x41E4
#define GUP_REG_PID_VID 0x8140
-#define GUP_SEARCH_FILE_TIMES 50
-#define UPDATE_FILE_PATH_2 "/data/_goodix_update_.bin"
-#define UPDATE_FILE_PATH_1 "/sdcard/_goodix_update_.bin"
-
-#define CONFIG_FILE_PATH_1 "/data/_goodix_config_.cfg"
-#define CONFIG_FILE_PATH_2 "/sdcard/_goodix_config_.cfg"
+#define GOODIX_FIRMWARE_FILE_NAME "_goodix_update_.bin"
+#define GOODIX_CONFIG_FILE_NAME "_goodix_config_.cfg"
#define FW_HEAD_LENGTH 14
#define FW_SECTION_LENGTH 0x2000
@@ -73,24 +67,22 @@
#define FAIL 0
#define SUCCESS 1
-#pragma pack(1)
-struct {
+struct st_fw_head {
u8 hw_info[4]; /* hardware info */
u8 pid[8]; /* product id */
u16 vid; /* version id */
-} st_fw_head;
-#pragma pack()
+} __packed;
-struct {
+struct st_update_msg {
u8 force_update;
u8 fw_flag;
- struct file *file;
- struct file *cfg_file;
- st_fw_head ic_fw_msg;
- mm_segment_t old_fs;
-} st_update_msg;
+ bool need_free;
+ u8 *fw_data;
+ u32 fw_len;
+ struct st_fw_head ic_fw_msg;
+};
-st_update_msg update_msg;
+static struct st_update_msg update_msg;
u16 show_len;
u16 total_len;
u8 got_file_flag;
@@ -107,7 +99,7 @@ Output:
numbers of i2c_msgs to transfer:
2: succeed, otherwise: failed
*********************************************************/
-s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
+static s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
{
s32 ret = -1;
u8 retries = 0;
@@ -126,8 +118,6 @@ s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
},
};
- GTP_DEBUG_FUNC();
-
while (retries < 5) {
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret == 2)
@@ -166,8 +156,6 @@ s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len)
.buf = buf,
};
- GTP_DEBUG_FUNC();
-
while (retries < 5) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret == 1)
@@ -176,7 +164,7 @@ s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len)
}
if (retries == 5) {
- dev_err(&client->dev, "I2C write retry limit over.\n");
+ dev_err(&client->dev, "I2C write retry limit over\n");
ret = -EIO;
}
@@ -185,117 +173,92 @@ s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len)
static s32 gup_init_panel(struct goodix_ts_data *ts)
{
+ struct i2c_client *client = ts->client;
+ u8 *config_data;
s32 ret = 0;
s32 i = 0;
u8 check_sum = 0;
u8 opr_buf[16];
u8 sensor_id = 0;
- u8 cfg_info_group1[] = CTP_CFG_GROUP1;
- u8 cfg_info_group2[] = CTP_CFG_GROUP2;
- u8 cfg_info_group3[] = CTP_CFG_GROUP3;
- u8 cfg_info_group4[] = CTP_CFG_GROUP4;
- u8 cfg_info_group5[] = CTP_CFG_GROUP5;
- u8 cfg_info_group6[] = CTP_CFG_GROUP6;
- u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3,
- cfg_info_group4, cfg_info_group5, cfg_info_group6};
- u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group1),
- CFG_GROUP_LEN(cfg_info_group2),
- CFG_GROUP_LEN(cfg_info_group3),
- CFG_GROUP_LEN(cfg_info_group4),
- CFG_GROUP_LEN(cfg_info_group5),
- CFG_GROUP_LEN(cfg_info_group6)};
-
- if ((!cfg_info_len[1]) && (!cfg_info_len[2]) &&
- (!cfg_info_len[3]) && (!cfg_info_len[4]) &&
- (!cfg_info_len[5])) {
+ for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++)
+ if (ts->pdata->config_data_len[i])
+ break;
+
+ if (i == GOODIX_MAX_CFG_GROUP) {
sensor_id = 0;
} else {
- ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID,
+ ret = gtp_i2c_read_dbl_check(client, GTP_REG_SENSOR_ID,
&sensor_id, 1);
if (ret == SUCCESS) {
- if (sensor_id >= 0x06) {
- GTP_ERROR(
- "Invalid sensor_id(0x%02X), No Config Sent!",
- sensor_id);
+ if (sensor_id >= GOODIX_MAX_CFG_GROUP) {
+ pr_err("Invalid sensor_id(0x%02X), No Config Sent",
+ sensor_id);
return -EINVAL;
}
} else {
- GTP_ERROR("Failed to get sensor_id, No config sent!");
+ pr_err("Failed to get sensor_id, No config sent\n");
return -EINVAL;
}
}
- GTP_DEBUG("Sensor_ID: %d", sensor_id);
+ pr_debug("Sensor ID selected: %d", sensor_id);
- ts->gtp_cfg_len = cfg_info_len[sensor_id];
-
- if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) {
- GTP_ERROR("Sensor_ID(%d) matches with NULL or INVALID CONFIG",
- " GROUP! NO Config Sent! You need to check you header",
- " file CFG_GROUP section!", sensor_id);
+ if (ts->pdata->config_data_len[sensor_id] < GTP_CONFIG_MIN_LENGTH ||
+ !ts->pdata->config_data_len[sensor_id]) {
+ pr_err("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP",
+ sensor_id);
return -EINVAL;
}
- ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA,
+ ret = gtp_i2c_read_dbl_check(client, GTP_REG_CONFIG_DATA,
&opr_buf[0], 1);
-
if (ret == SUCCESS) {
- GTP_DEBUG("CFG_GROUP%d Config Version: %d, IC Config Version:",
- " %d", sensor_id+1, send_cfg_buf[sensor_id][0], opr_buf[0]);
+ pr_debug("CFG_GROUP%d Config Version: %d, IC Config Version: %d",
+ sensor_id + 1,
+ ts->pdata->config_data[sensor_id][0],
+ opr_buf[0]);
- send_cfg_buf[sensor_id][0] = opr_buf[0];
+ ts->pdata->config_data[sensor_id][0] = opr_buf[0];
ts->fixed_cfg = 0;
- /*
- * if (opr_buf[0] < 90) {
- * grp_cfg_version = send_cfg_buf[sensor_id][0];
- * *** backup group config version ***
- * send_cfg_buf[sensor_id][0] = 0x00;
- * ts->fixed_cfg = 0;
- * } else { *** treated as fixed config, not send config ***
- * GTP_INFO("Ic fixed config with config version(%d)",
- * opr_buf[0]);
- * ts->fixed_cfg = 1;
- * }
- */
} else {
- GTP_ERROR("Failed to get ic config version!No config sent!");
+ pr_err("Failed to get ic config version. No config sent");
return -EINVAL;
}
- memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
- memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id],
- ts->gtp_cfg_len);
+ config_data = ts->pdata->config_data[sensor_id];
+ ts->config_data = ts->pdata->config_data[sensor_id];
+ ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id];
- GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
+ pr_debug("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x\n",
ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type);
- config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH;
- config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8);
- config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT;
- config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8);
+ config_data[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH;
+ config_data[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8);
+ config_data[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT;
+ config_data[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8);
if (GTP_INT_TRIGGER == 0) /* RISING */
- config[TRIGGER_LOC] &= 0xfe;
+ config_data[TRIGGER_LOC] &= 0xfe;
else if (GTP_INT_TRIGGER == 1) /* FALLING */
- config[TRIGGER_LOC] |= 0x01;
+ config_data[TRIGGER_LOC] |= 0x01;
check_sum = 0;
for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
- check_sum += config[i];
+ check_sum += config_data[i];
- config[ts->gtp_cfg_len] = (~check_sum) + 1;
+ config_data[ts->gtp_cfg_len] = (~check_sum) + 1;
- GTP_DEBUG_FUNC();
- ret = gtp_send_cfg(ts->client);
+ ret = gtp_send_cfg(ts);
if (ret < 0)
- GTP_ERROR("Send config error.");
+ pr_err("Send config error\n");
+ ts->config_data = NULL;
+ ts->gtp_cfg_len = 0;
msleep(20);
return 0;
}
-
static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len)
{
u8 i = 0;
@@ -308,7 +271,7 @@ static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len)
break;
if (i >= 5) {
- GTP_ERROR("Read data from 0x%02x%02x failed!", msg[0], msg[1]);
+ pr_err("Read data from 0x%02x%02x failed\n", msg[0], msg[1]);
return FAIL;
}
@@ -329,7 +292,7 @@ static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val)
break;
if (i >= 5) {
- GTP_ERROR("Set data to 0x%02x%02x failed!", msg[0], msg[1]);
+ pr_err("Set data to 0x%02x%02x failed\n", msg[0], msg[1]);
return FAIL;
}
@@ -347,7 +310,7 @@ static u8 gup_get_ic_fw_msg(struct i2c_client *client)
ret = gtp_i2c_read_dbl_check(client, GUP_REG_HW_INFO,
&buf[GTP_ADDR_LENGTH], 4);
if (ret == FAIL) {
- GTP_ERROR("[get_ic_fw_msg]get hw_info failed,exit");
+ pr_err("get hw_info failed,exit");
return FAIL;
}
@@ -356,7 +319,7 @@ static u8 gup_get_ic_fw_msg(struct i2c_client *client)
for (i = 0; i < 4; i++)
update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i];
- GTP_DEBUG("IC Hardware info:%02x%02x%02x%02x",
+ pr_debug("IC Hardware info:%02x%02x%02x%02x\n",
update_msg.ic_fw_msg.hw_info[0],
update_msg.ic_fw_msg.hw_info[1],
update_msg.ic_fw_msg.hw_info[2],
@@ -366,31 +329,31 @@ static u8 gup_get_ic_fw_msg(struct i2c_client *client)
for (retry = 0; retry < 2; retry++) {
ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1);
if (ret == FAIL) {
- GTP_ERROR("Read firmware message fail.");
+ pr_err("Read firmware message fail\n");
return ret;
}
update_msg.force_update = buf[GTP_ADDR_LENGTH];
if ((update_msg.force_update != 0xBE) && (!retry)) {
- GTP_INFO("The check sum in ic is error.");
- GTP_INFO("The IC will be updated by force.");
+ pr_info("The check sum in ic is error\n");
+ pr_info("The IC will be updated by force\n");
continue;
}
break;
}
- GTP_DEBUG("IC force update flag:0x%x", update_msg.force_update);
+ pr_debug("IC force update flag:0x%x\n", update_msg.force_update);
/* step3:get pid & vid */
ret = gtp_i2c_read_dbl_check(client, GUP_REG_PID_VID,
&buf[GTP_ADDR_LENGTH], 6);
if (ret == FAIL) {
- GTP_ERROR("[get_ic_fw_msg]get pid & vid failed,exit");
+ pr_err("get pid & vid failed,exit");
return FAIL;
}
memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid));
memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4);
- GTP_DEBUG("IC Product id:%s", update_msg.ic_fw_msg.pid);
+ pr_debug("IC Product id:%s\n", update_msg.ic_fw_msg.pid);
/* GT9XX PID MAPPING
* |-----FLASH-----RAM-----|
@@ -405,7 +368,7 @@ static u8 gup_get_ic_fw_msg(struct i2c_client *client)
*/
if (update_msg.ic_fw_msg.pid[0] != 0) {
if (!memcmp(update_msg.ic_fw_msg.pid, "9111", 4)) {
- GTP_DEBUG("IC Mapping Product id:%s",
+ pr_debug("IC Mapping Product id:%s\n",
update_msg.ic_fw_msg.pid);
memcpy(update_msg.ic_fw_msg.pid, "9110P", 5);
}
@@ -413,7 +376,7 @@ static u8 gup_get_ic_fw_msg(struct i2c_client *client)
update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH + 4] +
(buf[GTP_ADDR_LENGTH + 5] << 8);
- GTP_DEBUG("IC version id:%04x", update_msg.ic_fw_msg.vid);
+ pr_debug("IC version id:%04x\n", update_msg.ic_fw_msg.vid);
return SUCCESS;
}
@@ -423,17 +386,19 @@ s32 gup_enter_update_mode(struct i2c_client *client)
s32 ret = -1;
u8 retry = 0;
u8 rd_buf[3];
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
/* step1:RST output low last at least 2ms */
- GTP_GPIO_OUTPUT(GTP_RST_PORT, 0);
- msleep(20);
+ gpio_direction_output(ts->pdata->reset_gpio, 0);
+ usleep(20000);
/* step2:select I2C slave addr,INT:0--0xBA;1--0x28. */
- GTP_GPIO_OUTPUT(GTP_INT_PORT, (client->addr == 0x14));
+ gpio_direction_output(ts->pdata->irq_gpio,
+ (client->addr == GTP_I2C_ADDRESS_HIGH));
msleep(20);
/* step3:RST output high reset guitar */
- GTP_GPIO_OUTPUT(GTP_RST_PORT, 1);
+ gpio_direction_output(ts->pdata->reset_gpio, 1);
/* 20121211 modify start */
msleep(20);
@@ -441,25 +406,25 @@ s32 gup_enter_update_mode(struct i2c_client *client)
/* step4:Hold ss51 & dsp */
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
if (ret <= 0) {
- GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry);
+ pr_debug("Hold ss51 & dsp I2C error,retry:%d\n", retry);
continue;
}
/* step5:Confirm hold */
ret = gup_get_ic_msg(client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1);
if (ret <= 0) {
- GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry);
+ pr_debug("Hold ss51 & dsp I2C error,retry:%d\n", retry);
continue;
}
if (rd_buf[GTP_ADDR_LENGTH] == 0x0C) {
- GTP_DEBUG("Hold ss51 & dsp confirm SUCCESS");
+ pr_debug("Hold ss51 & dsp confirm SUCCESS\n");
break;
}
- GTP_DEBUG("Hold ss51 & dsp confirm 0x4180 failed,value:%d",
+ pr_debug("Hold ss51 & dsp confirm 0x4180 failed,value:%d\n",
rd_buf[GTP_ADDR_LENGTH]);
}
if (retry >= 200) {
- GTP_ERROR("Enter update Hold ss51 failed.");
+ pr_err("Enter update Hold ss51 failed\n");
return FAIL;
}
@@ -470,12 +435,13 @@ s32 gup_enter_update_mode(struct i2c_client *client)
return ret;
}
-void gup_leave_update_mode(void)
+void gup_leave_update_mode(struct i2c_client *client)
{
- GTP_GPIO_AS_INT(GTP_INT_PORT);
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
- GTP_DEBUG("[leave_update_mode]reset chip.");
- gtp_reset_guitar(i2c_connect_client, 20);
+ gpio_direction_input(ts->pdata->irq_gpio);
+ pr_debug("reset chip");
+ gtp_reset_guitar(ts, 20);
}
/* Get the correct nvram data
@@ -496,7 +462,8 @@ void gup_leave_update_mode(void)
* 3. IC PID == 91XX || File PID == 91XX
*/
-static u8 gup_enter_update_judge(st_fw_head *fw_head)
+static u8 gup_enter_update_judge(struct i2c_client *client,
+ struct st_fw_head *fw_head)
{
u16 u16_tmp;
s32 i = 0;
@@ -504,38 +471,37 @@ static u8 gup_enter_update_judge(st_fw_head *fw_head)
u16_tmp = fw_head->vid;
fw_head->vid = (u16)(u16_tmp>>8) + (u16)(u16_tmp<<8);
- GTP_DEBUG("FILE HARDWARE INFO:%02x%02x%02x%02x", fw_head->hw_info[0],
+ pr_debug("FILE HARDWARE INFO:%02x%02x%02x%02x\n", fw_head->hw_info[0],
fw_head->hw_info[1], fw_head->hw_info[2], fw_head->hw_info[3]);
- TP_DEBUG("FILE PID:%s", fw_head->pid);
- TP_DEBUG("FILE VID:%04x", fw_head->vid);
+ pr_debug("FILE PID:%s\n", fw_head->pid);
+ pr_debug("FILE VID:%04x\n", fw_head->vid);
- TP_DEBUG("IC HARDWARE INFO:%02x%02x%02x%02x",
+ pr_debug("IC HARDWARE INFO:%02x%02x%02x%02x\n",
update_msg.ic_fw_msg.hw_info[0],
update_msg.ic_fw_msg.hw_info[1],
update_msg.ic_fw_msg.hw_info[2],
update_msg.ic_fw_msg.hw_info[3]);
- TP_DEBUG("IC PID:%s", update_msg.ic_fw_msg.pid);
- TP_DEBUG("IC VID:%04x", update_msg.ic_fw_msg.vid);
+ pr_debug("IC PID:%s\n", update_msg.ic_fw_msg.pid);
+ pr_debug("IC VID:%04x\n", update_msg.ic_fw_msg.vid);
/* First two conditions */
if (!memcmp(fw_head->hw_info, update_msg.ic_fw_msg.hw_info,
sizeof(update_msg.ic_fw_msg.hw_info))) {
- GTP_DEBUG("Get the same hardware info.");
+ pr_debug("Get the same hardware info\n");
if (update_msg.force_update != 0xBE) {
- GTP_INFO("FW chksum error,need enter update.");
+ pr_info("FW chksum error,need enter update\n");
return SUCCESS;
}
/* 20130523 start */
if (strlen(update_msg.ic_fw_msg.pid) < 3) {
- GTP_INFO("Illegal IC pid, need enter update");
+ pr_info("Illegal IC pid, need enter update\n");
return SUCCESS;
}
for (i = 0; i < 3; i++) {
if ((update_msg.ic_fw_msg.pid[i] < 0x30) ||
(update_msg.ic_fw_msg.pid[i] > 0x39)) {
- GTP_INFO("Illegal IC pid, out of ",
- "bound, need enter update");
+ pr_info("Illegal IC pid, out of bound, need enter update\n");
return SUCCESS;
}
}
@@ -546,412 +512,234 @@ static u8 gup_enter_update_judge(st_fw_head *fw_head)
(!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4)) ||
(!memcmp(fw_head->pid, "91XX", 4))) {
if (!memcmp(fw_head->pid, "91XX", 4))
- GTP_DEBUG("Force none same pid update mode.");
+ pr_debug("Force none same pid update mode\n");
else
- GTP_DEBUG("Get the same pid.");
+ pr_debug("Get the same pid\n");
/* The third condition */
if (fw_head->vid > update_msg.ic_fw_msg.vid) {
- GTP_INFO("Need enter update.");
+ pr_info("Need enter update");
return SUCCESS;
}
- GTP_ERROR("Don't meet the third condition.");
- GTP_ERROR("File VID <= Ic VID, update aborted!");
+ pr_err("Don't meet the third condition\n");
+ pr_err("File VID <= Ic VID, update aborted\n");
} else {
- GTP_ERROR("File PID != Ic PID, update aborted!");
+ pr_err("File PID != Ic PID, update aborted\n");
}
} else {
- GTP_ERROR("Different Hardware, update aborted!");
+ pr_err("Different Hardware, update aborted\n");
}
return FAIL;
}
-static u8 ascii2hex(u8 a)
-{
- s8 value = 0;
-
- if (a >= '0' && a <= '9')
- value = a - '0';
- else if (a >= 'A' && a <= 'F')
- value = a - 'A' + 0x0A;
- else if (a >= 'a' && a <= 'f')
- value = a - 'a' + 0x0A;
- else
- value = 0xff;
-
- return value;
-}
-
-static s8 gup_update_config(struct i2c_client *client)
+static s8 gup_update_config(struct i2c_client *client,
+ const struct firmware *cfg)
{
- u32 file_len = 0;
s32 ret = 0;
s32 i = 0;
s32 file_cfg_len = 0;
u32 chip_cfg_len = 0;
s32 count = 0;
u8 *buf;
- u8 *pre_buf;
u8 *file_config;
- /* u8 checksum = 0; */
u8 pid[8];
+ u8 high, low;
- if (update_msg.cfg_file == NULL) {
- GTP_ERROR("[update_cfg]No need to upgrade config!");
+ if (!cfg || !cfg->data) {
+ pr_err("No need to upgrade config");
return FAIL;
}
- file_len = update_msg.cfg_file->f_op->llseek(update_msg.cfg_file,
- 0, SEEK_END);
ret = gup_get_ic_msg(client, GUP_REG_PID_VID, pid, 6);
if (ret == FAIL) {
- GTP_ERROR("[update_cfg]Read product id & version id fail.");
+ pr_err("Read product id & version id fail");
return FAIL;
}
pid[5] = '\0';
- GTP_DEBUG("update cfg get pid:%s", &pid[GTP_ADDR_LENGTH]);
+ pr_debug("update cfg get pid:%s\n", &pid[GTP_ADDR_LENGTH]);
chip_cfg_len = 186;
if (!memcmp(&pid[GTP_ADDR_LENGTH], "968", 3) ||
- !memcmp(&pid[GTP_ADDR_LENGTH], "910", 3) ||
- !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3)) {
+ !memcmp(&pid[GTP_ADDR_LENGTH], "910", 3) ||
+ !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3)) {
chip_cfg_len = 228;
}
- GTP_DEBUG("[update_cfg]config file len:%d", file_len);
- GTP_DEBUG("[update_cfg]need config len:%d", chip_cfg_len);
- if ((file_len+5) < chip_cfg_len*5) {
- GTP_ERROR("Config length error");
+ pr_debug("config file ASCII len:%d", cfg->size);
+ pr_debug("need config binary len:%d", chip_cfg_len);
+ if ((cfg->size + 5) < chip_cfg_len * 5) {
+ pr_err("Config length error");
return -EINVAL;
}
- buf = devm_kzalloc(&client->dev, file_len, GFP_KERNEL);
+ buf = devm_kzalloc(&client->dev, cfg->size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- pre_buf = devm_kzalloc(&client->dev, file_len, GFP_KERNEL);
- if (!pre_buf)
- return -ENOMEM;
-
file_config = devm_kzalloc(&client->dev, chip_cfg_len + GTP_ADDR_LENGTH,
GFP_KERNEL);
if (!file_config)
return -ENOMEM;
- update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_SET);
-
- GTP_DEBUG("[update_cfg]Read config from file.");
- ret = update_msg.cfg_file->f_op->read(update_msg.cfg_file,
- (char *)pre_buf, file_len, &update_msg.cfg_file->f_pos);
- if (ret < 0) {
- GTP_ERROR("[update_cfg]Read config file failed.");
- return ret;
- }
-
- GTP_DEBUG("[update_cfg]Delete illegal character.");
- for (i = 0, count = 0; i < file_len; i++) {
- if (pre_buf[i] == ' ' || pre_buf[i] == '\r'
- || pre_buf[i] == '\n')
+ pr_debug("Delete illegal character");
+ for (i = 0, count = 0; i < cfg->size; i++) {
+ if (cfg->data[i] == ' ' || cfg->data[i] == '\r'
+ || cfg->data[i] == '\n')
continue;
- buf[count++] = pre_buf[i];
+ buf[count++] = cfg->data[i];
}
- GTP_DEBUG("[update_cfg]Ascii to hex.");
+ pr_debug("Ascii to hex");
file_config[0] = GTP_REG_CONFIG_DATA >> 8;
file_config[1] = GTP_REG_CONFIG_DATA & 0xff;
- for (i = 0, file_cfg_len = GTP_ADDR_LENGTH; i < count; i + = 5) {
- if ((buf[i] == '0') && ((buf[i+1] == 'x') ||
- (buf[i+1] == 'X'))) {
- u8 high, low;
-
- high = ascii2hex(buf[i+2]);
- low = ascii2hex(buf[i+3]);
+ for (i = 0, file_cfg_len = GTP_ADDR_LENGTH; i < count; i = i + 5) {
+ if ((buf[i] == '0') && ((buf[i + 1] == 'x') ||
+ (buf[i + 1] == 'X'))) {
+ ret = hex2bin(&high, &buf[i + 2], 1);
+ if (ret) {
+ pr_err("Failed to convert high address from hex2bin");
+ return ret;
+ }
+ ret = hex2bin(&low, &buf[i + 3], 1);
+ if (ret) {
+ pr_err("Failed to convert low address from hex2bin");
+ return ret;
+ }
if ((high == 0xFF) || (low == 0xFF)) {
ret = 0;
- GTP_ERROR("[update_cfg]Illegal config file.");
+ pr_err("Illegal config file");
return ret;
}
file_config[file_cfg_len++] = (high<<4) + low;
} else {
ret = 0;
- GTP_ERROR("[update_cfg]Illegal config file.");
+ pr_err("Illegal config file");
return ret;
}
}
- /* cal checksum */
- /* for (i=GTP_ADDR_LENGTH; i<chip_cfg_len; i++)
- * checksum += file_config[i];
- * file_config[chip_cfg_len] = (~checksum) + 1;
- * file_config[chip_cfg_len+1] = 0x01;
- */
-
- GTP_DEBUG("config:");
- GTP_DEBUG_ARRAY(file_config+2, file_cfg_len);
-
i = 0;
while (i++ < 5) {
ret = gup_i2c_write(client, file_config, file_cfg_len);
- if (ret > 0) {
- GTP_INFO("[update_cfg]Send config SUCCESS.");
+ if (ret > 0)
break;
- }
- GTP_ERROR("[update_cfg]Send config i2c error.");
+ pr_err("Send config i2c error");
}
return ret;
}
-#if GTP_HEADER_FW_UPDATE
-static u8 gup_check_fs_mounted(char *path_name)
+static s32 gup_get_firmware_file(struct i2c_client *client,
+ struct st_update_msg *msg, u8 *path)
{
- struct path root_path;
- struct path path;
- int err;
+ s32 ret;
+ const struct firmware *fw = NULL;
- err = kern_path("/", LOOKUP_FOLLOW, &root_path);
+ ret = request_firmware(&fw, path, &client->dev);
+ if (ret < 0) {
+ dev_info(&client->dev, "Cannot get firmware - %s (%d)\n",
+ path, ret);
+ return -EEXIST;
+ }
- if (err) {
- GTP_DEBUG("\"/\" NOT Mounted: %d", err);
- return FAIL;
+ dev_dbg(&client->dev, "Config File: %s size=%d", path, fw->size);
+ msg->fw_data =
+ devm_kzalloc(&client->dev, fw->size, GFP_KERNEL);
+ if (!msg->fw_data) {
+ release_firmware(fw);
+ return -ENOMEM;
}
- err = kern_path(path_name, LOOKUP_FOLLOW, &path);
- if (err) {
- GTP_DEBUG("/data/ NOT Mounted: %d", err);
- return FAIL;
+ memcpy(msg->fw_data, fw->data, fw->size);
+ msg->fw_len = fw->size;
+ msg->need_free = true;
+ release_firmware(fw);
+ return 0;
+}
+
+static u8 gup_check_firmware_name(struct i2c_client *client,
+ u8 **path_p)
+{
+ u8 len;
+ u8 *fname;
+
+ if (!(*path_p)) {
+ *path_p = GOODIX_FIRMWARE_FILE_NAME;
+ return 0;
}
- return SUCCESS;
+ len = strnlen(*path_p, FIRMWARE_NAME_LEN_MAX);
+ if (len >= FIRMWARE_NAME_LEN_MAX) {
+ dev_err(&client->dev, "firmware name too long");
+ return -EINVAL;
+ }
- /* if (path.mnt->mnt_sb == root_path.mnt->mnt_sb)
- * return FAIL;
- * else
- * return SUCCESS;
- */
+ fname = strrchr(*path_p, '/');
+ if (fname) {
+ fname = fname + 1;
+ *path_p = fname;
+ }
+ return 0;
}
-#endif
-static u8 gup_check_update_file(struct i2c_client *client, st_fw_head *fw_head,
- u8 *path)
+static u8 gup_check_update_file(struct i2c_client *client,
+ struct st_fw_head *fw_head, u8 *path)
{
s32 ret = 0;
s32 i = 0;
s32 fw_checksum = 0;
- u8 buf[FW_HEAD_LENGTH];
-
- if (path) {
- GTP_DEBUG("Update File path:%s, %d", path, strlen(path));
- update_msg.file = file_open(path, O_RDONLY, 0);
+ u16 temp;
+ const struct firmware *fw = NULL;
- if (IS_ERR(update_msg.file)) {
- GTP_ERROR("Open update file(%s) error!", path);
- return FAIL;
- }
+ ret = request_firmware(&fw, GOODIX_CONFIG_FILE_NAME, &client->dev);
+ if (ret < 0) {
+ dev_info(&client->dev, "Cannot get config file - %s (%d)\n",
+ GOODIX_CONFIG_FILE_NAME, ret);
} else {
-#if GTP_HEADER_FW_UPDATE
- for (i = 0; i < (GUP_SEARCH_FILE_TIMES); i++) {
- GTP_DEBUG("Waiting for /data mounted [%d]", i);
-
- if (gup_check_fs_mounted("/data") == SUCCESS) {
- GTP_DEBUG("/data Mounted!");
- break;
- }
- msleep(3000);
- }
- if (i >= (GUP_SEARCH_FILE_TIMES)) {
- GTP_ERROR("Wait for /data mounted timeout!");
- return FAIL;
- }
-
- /* update config */
- update_msg.cfg_file = file_open(CONFIG_FILE_PATH_1,
- O_RDONLY, 0);
-
- if (IS_ERR(update_msg.cfg_file)) {
- GTP_DEBUG("%s is unavailable", CONFIG_FILE_PATH_1);
- } else {
- GTP_INFO("Update Config File: %s", CONFIG_FILE_PATH_1);
- ret = gup_update_config(client);
- if (ret <= 0)
- GTP_ERROR("Update config failed.");
- filp_close(update_msg.cfg_file, NULL);
- }
-
- if (sizeof(header_fw_array) < (FW_HEAD_LENGTH+FW_SECTION_LENGTH
- *4 + FW_DSP_ISP_LENGTH+FW_DSP_LENGTH + FW_BOOT_LENGTH)) {
- GTP_ERROR("INVALID header_fw_array, check your ",
- "gt9xx_firmware.h file!");
- return FAIL;
- }
- update_msg.file = file_open(UPDATE_FILE_PATH_2, O_CREAT |
- O_RDWR, 0666);
- if ((IS_ERR(update_msg.file))) {
- GTP_ERROR("Failed to Create file: %s for fw_header!",
- UPDATE_FILE_PATH_2);
- return FAIL;
- }
- update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET);
- update_msg.file->f_op->write(update_msg.file,
- (char *)header_fw_array, sizeof(header_fw_array),
- &update_msg.file->f_pos);
- file_close(update_msg.file, NULL);
- update_msg.file = file_open(UPDATE_FILE_PATH_2, O_RDONLY, 0);
-#else
- u8 fp_len = max(sizeof(UPDATE_FILE_PATH_1),
- sizeof(UPDATE_FILE_PATH_2));
- u8 cfp_len = max(sizeof(CONFIG_FILE_PATH_1),
- sizeof(CONFIG_FILE_PATH_2));
-
- u8 *search_update_path = devm_kzalloc(&client->dev, fp_len,
- GFP_KERNEL);
- if (!search_update_path)
- goto load_failed;
-
- u8 *search_cfg_path = devm_kzalloc(&client->dev, cfp_len,
- GFP_KERNEL);
- if (!search_cfg_path)
- goto load_failed;
- /* Begin to search update file,the config file & firmware
- * file must be in the same path,single or double.
- */
- searching_file = 1;
- for (i = 0; i < GUP_SEARCH_FILE_TIMES; i++) {
- if (searching_file == 0) {
- GTP_INFO(".bin/.cfg update file search ",
- "forcely terminated!");
- return FAIL;
- }
- if (i % 2) {
- memcpy(search_update_path, UPDATE_FILE_PATH_1,
- sizeof(UPDATE_FILE_PATH_1));
- memcpy(search_cfg_path, CONFIG_FILE_PATH_1,
- sizeof(CONFIG_FILE_PATH_1));
- } else {
- memcpy(search_update_path, UPDATE_FILE_PATH_2,
- sizeof(UPDATE_FILE_PATH_2));
- memcpy(search_cfg_path, CONFIG_FILE_PATH_2,
- sizeof(CONFIG_FILE_PATH_2));
- }
-
- if (!(got_file_flag&0x0F)) {
- update_msg.file = file_open(search_update_path,
- O_RDONLY, 0);
- if (!IS_ERR(update_msg.file)) {
- GTP_DEBUG("Find the bin file");
- got_file_flag |= 0x0F;
- }
- }
- if (!(got_file_flag & 0xF0)) {
- update_msg.cfg_file = file_open(search_cfg_path,
- O_RDONLY, 0);
- if (!IS_ERR(update_msg.cfg_file)) {
- GTP_DEBUG("Find the cfg file");
- got_file_flag |= 0xF0;
- }
- }
-
- if (got_file_flag) {
- if (got_file_flag == 0xFF)
- break;
- i += 4;
- }
- GTP_DEBUG("%3d:Searching %s %s file...", i,
- (got_file_flag & 0x0F) ? "" : "bin",
- (got_file_flag & 0xF0) ? "" : "cfg");
-
- msleep(3000);
- }
-
- searching_file = 0;
-
- if (!got_file_flag) {
- GTP_ERROR("Can't find update file.");
- goto load_failed;
- }
-
- if (got_file_flag & 0xF0) {
- GTP_DEBUG("Got the update config file.");
- ret = gup_update_config(client);
- if (ret <= 0)
- GTP_ERROR("Update config failed.");
- filp_close(update_msg.cfg_file, NULL);
- msleep(500); /* waiting config to be stored in FLASH. */
- }
- if (got_file_flag & 0x0F) {
- GTP_DEBUG("Got the update firmware file.");
- } else {
- GTP_ERROR("No need to upgrade firmware.");
- goto load_failed;
- }
-#endif
+ dev_dbg(&client->dev,
+ "Update config File: %s", GOODIX_CONFIG_FILE_NAME);
+ ret = gup_update_config(client, fw);
+ if (ret <= 0)
+ dev_err(&client->dev, "Update config failed");
+ release_firmware(fw);
}
- update_msg.old_fs = get_fs();
- set_fs(KERNEL_DS);
+ update_msg.need_free = false;
+ update_msg.fw_len = 0;
- update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET);
- /* update_msg.file->f_pos = 0; */
+ if (gup_check_firmware_name(client, &path))
+ goto load_failed;
- ret = update_msg.file->f_op->read(update_msg.file, (char *)buf,
- FW_HEAD_LENGTH, &update_msg.file->f_pos);
- if (ret < 0) {
- GTP_ERROR("Read firmware head in update file error.");
+ if (gup_get_firmware_file(client, &update_msg, path))
goto load_failed;
- }
- memcpy(fw_head, buf, FW_HEAD_LENGTH);
+
+ memcpy(fw_head, update_msg.fw_data, FW_HEAD_LENGTH);
/* check firmware legality */
fw_checksum = 0;
for (i = 0; i < FW_SECTION_LENGTH * 4 + FW_DSP_ISP_LENGTH +
- FW_DSP_LENGTH + FW_BOOT_LENGTH; i + = 2) {
- u16 temp;
-
- ret = update_msg.file->f_op->read(update_msg.file, (char *)buf,
- 2, &update_msg.file->f_pos);
- if (ret < 0) {
- GTP_ERROR("Read firmware file error.");
- goto load_failed;
- }
- /* GTP_DEBUG("BUF[0]:%x", buf[0]); */
- temp = (buf[0]<<8) + buf[1];
+ FW_DSP_LENGTH + FW_BOOT_LENGTH; i += 2) {
+ temp = (update_msg.fw_data[FW_HEAD_LENGTH + i] << 8) +
+ update_msg.fw_data[FW_HEAD_LENGTH + i + 1];
fw_checksum += temp;
}
- GTP_DEBUG("firmware checksum:%x", fw_checksum&0xFFFF);
+ pr_debug("firmware checksum:%x", fw_checksum & 0xFFFF);
if (fw_checksum & 0xFFFF) {
- GTP_ERROR("Illegal firmware file.");
+ dev_err(&client->dev, "Illegal firmware file");
goto load_failed;
}
return SUCCESS;
load_failed:
- set_fs(update_msg.old_fs);
- return FAIL;
-}
-
-#if 0
-static u8 gup_check_update_header(struct i2c_client *client,
- st_fw_head *fw_head)
-{
- const u8 *pos;
- int i = 0;
- u8 mask_num = 0;
- s32 ret = 0;
-
- pos = HEADER_UPDATE_DATA;
-
- memcpy(fw_head, pos, FW_HEAD_LENGTH);
- pos += FW_HEAD_LENGTH;
-
- ret = gup_enter_update_judge(fw_head);
- if (ret == SUCCESS)
- return SUCCESS;
+ if (update_msg.need_free) {
+ devm_kfree(&client->dev, update_msg.fw_data);
+ update_msg.need_free = false;
+ }
return FAIL;
}
-#endif
static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr,
u16 total_length)
@@ -964,10 +752,10 @@ static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr,
u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
u8 retry = 0;
- GTP_DEBUG("Begin burn %dk data to addr 0x%x", (total_length/1024),
+ pr_debug("Begin burn %dk data to addr 0x%x", (total_length / 1024),
start_addr);
while (burn_length < total_length) {
- GTP_DEBUG("B/T:%04d/%04d", burn_length, total_length);
+ pr_debug("B/T:%04d/%04d", burn_length, total_length);
frame_length = ((total_length - burn_length) > PACK_SIZE)
? PACK_SIZE : (total_length - burn_length);
wr_buf[0] = (u8)(burn_addr>>8);
@@ -981,33 +769,26 @@ static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr,
ret = gup_i2c_write(client, wr_buf,
GTP_ADDR_LENGTH + frame_length);
if (ret <= 0) {
- GTP_ERROR("Write frame data i2c error.");
+ pr_err("Write frame data i2c error\n");
continue;
}
ret = gup_i2c_read(client, rd_buf, GTP_ADDR_LENGTH +
frame_length);
if (ret <= 0) {
- GTP_ERROR("Read back frame data i2c error.");
+ pr_err("Read back frame data i2c error\n");
continue;
}
if (memcmp(&wr_buf[GTP_ADDR_LENGTH],
&rd_buf[GTP_ADDR_LENGTH], frame_length)) {
- GTP_ERROR("Check frame data fail,not equal.");
- GTP_DEBUG("write array:");
- GTP_DEBUG_ARRAY(&wr_buf[GTP_ADDR_LENGTH],
- frame_length);
- GTP_DEBUG("read array:");
- GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH],
- frame_length);
+ pr_err("Check frame data fail,not equal\n");
continue;
} else {
- /* GTP_DEBUG("Check frame data success."); */
break;
}
}
if (retry >= MAX_FRAME_CHECK_TIME) {
- GTP_ERROR("Burn frame data time out,exit.");
+ pr_err("Burn frame data time out,exit\n");
return FAIL;
}
burn_length += frame_length;
@@ -1018,20 +799,15 @@ static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr,
static u8 gup_load_section_file(u8 *buf, u16 offset, u16 length)
{
- s32 ret = 0;
-
- if (update_msg.file == NULL) {
- GTP_ERROR("cannot find update file,load section file fail.");
- return FAIL;
- }
- update_msg.file->f_pos = FW_HEAD_LENGTH + offset;
-
- ret = update_msg.file->f_op->read(update_msg.file, (char *)buf, length,
- &update_msg.file->f_pos);
- if (ret < 0) {
- GTP_ERROR("Read update file fail.");
+ if (!update_msg.fw_data ||
+ update_msg.fw_len < FW_HEAD_LENGTH + offset + length) {
+ pr_err(
+ "<<-GTP->> cannot load section data. fw_len=%d read end=%d\n",
+ update_msg.fw_len,
+ FW_HEAD_LENGTH + offset + length);
return FAIL;
}
+ memcpy(buf, &update_msg.fw_data[FW_HEAD_LENGTH + offset], length);
return SUCCESS;
}
@@ -1050,30 +826,26 @@ static u8 gup_recall_check(struct i2c_client *client, u8 *chk_src,
? PACK_SIZE : (chk_length - recall_length);
ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length);
if (ret <= 0) {
- GTP_ERROR("recall i2c error,exit");
+ pr_err("recall i2c error,exit\n");
return FAIL;
}
if (memcmp(&rd_buf[GTP_ADDR_LENGTH], &chk_src[recall_length],
frame_length)) {
- GTP_ERROR("Recall frame data fail,not equal.");
- GTP_DEBUG("chk_src array:");
- GTP_DEBUG_ARRAY(&chk_src[recall_length], frame_length);
- GTP_DEBUG("recall array:");
- GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length);
+ pr_err("Recall frame data fail,not equal\n");
return FAIL;
}
recall_length += frame_length;
recall_addr += frame_length;
}
- GTP_DEBUG("Recall check %dk firmware success.", (chk_length/1024));
+ pr_debug("Recall check %dk firmware success\n", (chk_length/1024));
return SUCCESS;
}
static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section,
- u16 start_addr, u8 bank_cmdi)
+ u16 start_addr, u8 bank_cmd)
{
s32 ret = 0;
u8 rd_buf[5];
@@ -1081,14 +853,14 @@ static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section,
/* step1:hold ss51 & dsp */
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_section]hold ss51 & dsp fail.");
+ pr_err("hold ss51 & dsp fail");
return FAIL;
}
/* step2:set scramble */
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_section]set scramble fail.");
+ pr_err("set scramble fail");
return FAIL;
}
@@ -1096,7 +868,7 @@ static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section,
ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK,
(bank_cmd >> 4)&0x0F);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_section]select bank %d fail.",
+ pr_err("select bank %d fail",
(bank_cmd >> 4)&0x0F);
return FAIL;
}
@@ -1104,21 +876,21 @@ static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section,
/* step4:enable accessing code */
ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_section]enable accessing code fail.");
+ pr_err("enable accessing code fail");
return FAIL;
}
/* step5:burn 8k fw section */
ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_section]burn fw_section fail.");
+ pr_err("burn fw_section fail");
return FAIL;
}
/* step6:hold ss51 & release dsp */
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_section]hold ss51 & release dsp fail.");
+ pr_err("hold ss51 & release dsp fail");
return FAIL;
}
/* must delay */
@@ -1127,27 +899,24 @@ static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section,
/* step7:send burn cmd to move data to flash from sram */
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0f);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_section]send burn cmd fail.");
+ pr_err("send burn cmd fail");
return FAIL;
}
- GTP_DEBUG("[burn_fw_section]Wait for the burn is complete......");
+ pr_debug("Wait for the burn is complete");
do {
ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_section]Get burn state fail");
+ pr_err("Get burn state fail");
return FAIL;
}
msleep(20);
- /* GTP_DEBUG("[burn_fw_section]Get burn state:%d.",
- * rd_buf[GTP_ADDR_LENGTH]);
- */
} while (rd_buf[GTP_ADDR_LENGTH]);
/* step8:select bank */
ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK,
(bank_cmd >> 4)&0x0F);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_section]select bank %d fail.",
+ pr_err("select bank %d fail",
(bank_cmd >> 4)&0x0F);
return FAIL;
}
@@ -1155,7 +924,7 @@ static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section,
/* step9:enable accessing code */
ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_section]enable accessing code fail.");
+ pr_err("enable accessing code fail");
return FAIL;
}
@@ -1163,14 +932,14 @@ static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section,
ret = gup_recall_check(client, fw_section, start_addr,
FW_SECTION_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_section]recall check 8k firmware fail.");
+ pr_err("recall check 8k firmware fail");
return FAIL;
}
/* step11:disable accessing code */
ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_section]disable accessing code fail.");
+ pr_err("disable accessing code fail");
return FAIL;
}
@@ -1183,101 +952,97 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
u8 *fw_dsp_isp = NULL;
u8 retry = 0;
- GTP_DEBUG("[burn_dsp_isp]Begin burn dsp isp---->>");
+ pr_debug("Begin burn dsp isp");
/* step1:alloc memory */
- GTP_DEBUG("[burn_dsp_isp]step1:alloc memory");
+ pr_debug("step1:alloc memory");
while (retry++ < 5) {
fw_dsp_isp = devm_kzalloc(&client->dev, FW_DSP_ISP_LENGTH,
GFP_KERNEL);
if (fw_dsp_isp == NULL) {
continue;
} else {
- GTP_INFO("[burn_dsp_isp]Alloc %dk byte memory success.",
- (FW_DSP_ISP_LENGTH/1024));
break;
}
}
- if (retry == 5) {
- GTP_ERROR("[burn_dsp_isp]Alloc memory fail,exit.");
+ if (retry == 5)
return FAIL;
- }
/* step2:load dsp isp file data */
- GTP_DEBUG("[burn_dsp_isp]step2:load dsp isp file data");
+ pr_debug("step2:load dsp isp file data");
ret = gup_load_section_file(fw_dsp_isp, (4 * FW_SECTION_LENGTH +
FW_DSP_LENGTH + FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_dsp_isp]load firmware dsp_isp fail.");
+ pr_err("load firmware dsp_isp fail");
return FAIL;
}
/* step3:disable wdt,clear cache enable */
- GTP_DEBUG("[burn_dsp_isp]step3:disable wdt,clear cache enable");
+ pr_debug("step3:disable wdt,clear cache enable");
ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00);
if (ret <= 0) {
- GTP_ERROR("[burn_dsp_isp]disable wdt fail.");
+ pr_err("disable wdt fail");
return FAIL;
}
ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00);
if (ret <= 0) {
- GTP_ERROR("[burn_dsp_isp]clear cache enable fail.");
+ pr_err("clear cache enable fail");
return FAIL;
}
/* step4:hold ss51 & dsp */
- GTP_DEBUG("[burn_dsp_isp]step4:hold ss51 & dsp");
+ pr_debug("step4:hold ss51 & dsp");
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
if (ret <= 0) {
- GTP_ERROR("[burn_dsp_isp]hold ss51 & dsp fail.");
+ pr_err("hold ss51 & dsp fail");
return FAIL;
}
/* step5:set boot from sram */
- GTP_DEBUG("[burn_dsp_isp]step5:set boot from sram");
+ pr_debug("step5:set boot from sram");
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02);
if (ret <= 0) {
- GTP_ERROR("[burn_dsp_isp]set boot from sram fail.");
+ pr_err("set boot from sram fail");
return FAIL;
}
/* step6:software reboot */
- GTP_DEBUG("[burn_dsp_isp]step6:software reboot");
+ pr_debug("step6:software reboot");
ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01);
if (ret <= 0) {
- GTP_ERROR("[burn_dsp_isp]software reboot fail.");
+ pr_err("software reboot fail");
return FAIL;
}
/* step7:select bank2 */
- GTP_DEBUG("[burn_dsp_isp]step7:select bank2");
+ pr_debug("step7:select bank2");
ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02);
if (ret <= 0) {
- GTP_ERROR("[burn_dsp_isp]select bank2 fail.");
+ pr_err("select bank2 fail");
return FAIL;
}
/* step8:enable accessing code */
- GTP_DEBUG("[burn_dsp_isp]step8:enable accessing code");
+ pr_debug("step8:enable accessing code");
ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
if (ret <= 0) {
- GTP_ERROR("[burn_dsp_isp]enable accessing code fail.");
+ pr_err("enable accessing code fail");
return FAIL;
}
/* step9:burn 4k dsp_isp */
- GTP_DEBUG("[burn_dsp_isp]step9:burn 4k dsp_isp");
+ pr_debug("step9:burn 4k dsp_isp");
ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_dsp_isp]burn dsp_isp fail.");
+ pr_err("burn dsp_isp fail");
return FAIL;
}
/* step10:set scramble */
- GTP_DEBUG("[burn_dsp_isp]step10:set scramble");
+ pr_debug("step10:set scramble");
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
if (ret <= 0) {
- GTP_ERROR("[burn_dsp_isp]set scramble fail.");
+ pr_err("set scramble fail");
return FAIL;
}
@@ -1290,98 +1055,94 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
u8 retry = 0;
s32 ret = 0;
- GTP_DEBUG("[burn_fw_ss51]Begin burn ss51 firmware---->>");
+ pr_debug("Begin burn ss51 firmware");
/* step1:alloc memory */
- GTP_DEBUG("[burn_fw_ss51]step1:alloc memory");
+ pr_debug("step1:alloc memory");
while (retry++ < 5) {
fw_ss51 = devm_kzalloc(&client->dev, FW_SECTION_LENGTH,
GFP_KERNEL);
if (fw_ss51 == NULL) {
continue;
} else {
- GTP_INFO("[burn_fw_ss51]Alloc %dk byte memory success.",
- (FW_SECTION_LENGTH/1024));
break;
}
}
- if (retry == 5) {
- GTP_ERROR("[burn_fw_ss51]Alloc memory fail,exit.");
+ if (retry == 5)
return FAIL;
- }
/* step2:load ss51 firmware section 1 file data */
- GTP_DEBUG("[burn_fw_ss51]step2:load ss51 firmware section 1 file data");
+ pr_debug("step2:load ss51 firmware section 1 file data");
ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 1 fail.");
+ pr_err("load ss51 firmware section 1 fail");
return FAIL;
}
/* step3:clear control flag */
- GTP_DEBUG("[burn_fw_ss51]step3:clear control flag");
+ pr_debug("step3:clear control flag");
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_ss51]clear control flag fail.");
+ pr_err("clear control flag fail");
return FAIL;
}
/* step4:burn ss51 firmware section 1 */
- GTP_DEBUG("[burn_fw_ss51]step4:burn ss51 firmware section 1");
+ pr_debug("step4:burn ss51 firmware section 1");
ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 1 fail.");
+ pr_err("burn ss51 firmware section 1 fail");
return FAIL;
}
/* step5:load ss51 firmware section 2 file data */
- GTP_DEBUG("[burn_fw_ss51]step5:load ss51 firmware section 2 file data");
+ pr_debug("step5:load ss51 firmware section 2 file data");
ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH,
FW_SECTION_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 2 fail.");
+ pr_err("[burn_fw_ss51]load ss51 firmware section 2 fail\n");
return FAIL;
}
/* step6:burn ss51 firmware section 2 */
- GTP_DEBUG("[burn_fw_ss51]step6:burn ss51 firmware section 2");
+ pr_debug("step6:burn ss51 firmware section 2");
ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 2 fail.");
+ pr_err("burn ss51 firmware section 2 fail");
return FAIL;
}
/* step7:load ss51 firmware section 3 file data */
- GTP_DEBUG("[burn_fw_ss51]step7:load ss51 firmware section 3 file data");
+ pr_debug("step7:load ss51 firmware section 3 file data");
ret = gup_load_section_file(fw_ss51, 2*FW_SECTION_LENGTH,
FW_SECTION_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 3 fail.");
+ pr_err("load ss51 firmware section 3 fail");
return FAIL;
}
/* step8:burn ss51 firmware section 3 */
- GTP_DEBUG("[burn_fw_ss51]step8:burn ss51 firmware section 3");
+ pr_debug("step8:burn ss51 firmware section 3");
ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 3 fail.");
+ pr_err("burn ss51 firmware section 3 fail");
return FAIL;
}
/* step9:load ss51 firmware section 4 file data */
- GTP_DEBUG("[burn_fw_ss51]step9:load ss51 firmware section 4 file data");
+ pr_debug("step9:load ss51 firmware section 4 file data");
ret = gup_load_section_file(fw_ss51, 3*FW_SECTION_LENGTH,
FW_SECTION_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 4 fail.");
+ pr_err("load ss51 firmware section 4 fail");
return FAIL;
}
/* step10:burn ss51 firmware section 4 */
- GTP_DEBUG("[burn_fw_ss51]step10:burn ss51 firmware section 4");
+ pr_debug("step10:burn ss51 firmware section 4");
ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 4 fail.");
+ pr_err("burn ss51 firmware section 4 fail");
return FAIL;
}
@@ -1395,105 +1156,97 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
u8 retry = 0;
u8 rd_buf[5];
- GTP_DEBUG("[burn_fw_dsp]Begin burn dsp firmware---->>");
+ pr_debug("Begin burn dsp firmware");
/* step1:alloc memory */
- GTP_DEBUG("[burn_fw_dsp]step1:alloc memory");
+ pr_debug("step1:alloc memory");
while (retry++ < 5) {
fw_dsp = devm_kzalloc(&client->dev, FW_DSP_LENGTH,
GFP_KERNEL);
if (fw_dsp == NULL) {
continue;
} else {
- GTP_INFO("[burn_fw_dsp]Alloc %dk byte memory success.",
- (FW_SECTION_LENGTH/1024));
break;
}
}
- if (retry == 5) {
- GTP_ERROR("[burn_fw_dsp]Alloc memory fail,exit.");
+ if (retry == 5)
return FAIL;
- }
/* step2:load firmware dsp */
- GTP_DEBUG("[burn_fw_dsp]step2:load firmware dsp");
+ pr_debug("step2:load firmware dsp");
ret = gup_load_section_file(fw_dsp, 4*FW_SECTION_LENGTH, FW_DSP_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_dsp]load firmware dsp fail.");
+ pr_err("load firmware dsp fail");
return ret;
}
/* step3:select bank3 */
- GTP_DEBUG("[burn_fw_dsp]step3:select bank3");
+ pr_debug("step3:select bank3");
ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_dsp]select bank3 fail.");
+ pr_err("select bank3 fail");
return FAIL;
}
/* Step4:hold ss51 & dsp */
- GTP_DEBUG("[burn_fw_dsp]step4:hold ss51 & dsp");
+ pr_debug("step4:hold ss51 & dsp");
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_dsp]hold ss51 & dsp fail.");
+ pr_err("hold ss51 & dsp fail");
return FAIL;
}
/* step5:set scramble */
- GTP_DEBUG("[burn_fw_dsp]step5:set scramble");
+ pr_debug("step5:set scramble");
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_dsp]set scramble fail.");
+ pr_err("set scramble fail");
return FAIL;
}
/* step6:release ss51 & dsp */
- GTP_DEBUG("[burn_fw_dsp]step6:release ss51 & dsp");
+ pr_debug("step6:release ss51 & dsp");
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_dsp]release ss51 & dsp fail.");
+ pr_err("release ss51 & dsp fail");
return FAIL;
}
/* must delay */
msleep(20);
/* step7:burn 4k dsp firmware */
- GTP_DEBUG("[burn_fw_dsp]step7:burn 4k dsp firmware");
+ pr_debug("step7:burn 4k dsp firmware");
ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_dsp]burn fw_section fail.");
+ pr_err("[burn_fw_dsp]burn fw_section fail\n");
return ret;
}
/* step8:send burn cmd to move data to flash from sram */
- GTP_DEBUG("[burn_fw_dsp]step8:send burn cmd to move data to flash",
- "from sram");
+ pr_debug("step8:send burn cmd to move data to flash from sram");
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_dsp]send burn cmd fail.");
+ pr_err("send burn cmd fail");
return ret;
}
- GTP_DEBUG("[burn_fw_dsp]Wait for the burn is complete......");
+ pr_debug("Wait for the burn is complete");
do {
ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_dsp]Get burn state fail");
+ pr_err("Get burn state fail");
return ret;
}
msleep(20);
- /* GTP_DEBUG("[burn_fw_dsp]Get burn state:%d.",
- * rd_buf[GTP_ADDR_LENGTH]);
- */
} while (rd_buf[GTP_ADDR_LENGTH]);
/* step9:recall check 4k dsp firmware */
- GTP_DEBUG("[burn_fw_dsp]step9:recall check 4k dsp firmware");
+ pr_debug("step9:recall check 4k dsp firmware");
ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_dsp]recall check 4k dsp firmware fail.");
+ pr_err("recall check 4k dsp firmware fail");
return ret;
}
- ret = SUCCESS;
+ return SUCCESS;
}
static u8 gup_burn_fw_boot(struct i2c_client *client)
@@ -1503,119 +1256,111 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
u8 retry = 0;
u8 rd_buf[5];
- GTP_DEBUG("[burn_fw_boot]Begin burn bootloader firmware---->>");
+ pr_debug("Begin burn bootloader firmware");
/* step1:Alloc memory */
- GTP_DEBUG("[burn_fw_boot]step1:Alloc memory");
+ pr_debug("step1:Alloc memory");
while (retry++ < 5) {
fw_boot = devm_kzalloc(&client->dev, FW_BOOT_LENGTH,
GFP_KERNEL);
if (fw_boot == NULL) {
continue;
} else {
- GTP_INFO("[burn_fw_boot]Alloc %dk byte memory success.",
- (FW_BOOT_LENGTH/1024));
break;
}
}
- if (retry == 5) {
- GTP_ERROR("[burn_fw_boot]Alloc memory fail,exit.");
+ if (retry == 5)
return FAIL;
- }
/* step2:load firmware bootloader */
- GTP_DEBUG("[burn_fw_boot]step2:load firmware bootloader");
+ pr_debug("step2:load firmware bootloader");
ret = gup_load_section_file(fw_boot, (4 * FW_SECTION_LENGTH +
FW_DSP_LENGTH), FW_BOOT_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_boot]load firmware dsp fail.");
+ pr_err("load firmware dsp fail");
return ret;
}
/* step3:hold ss51 & dsp */
- GTP_DEBUG("[burn_fw_boot]step3:hold ss51 & dsp");
+ pr_debug("step3:hold ss51 & dsp");
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_boot]hold ss51 & dsp fail.");
+ pr_err("hold ss51 & dsp fail");
return FAIL;
}
/* step4:set scramble */
- GTP_DEBUG("[burn_fw_boot]step4:set scramble");
+ pr_debug("step4:set scramble");
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_boot]set scramble fail.");
+ pr_err("set scramble fail");
return FAIL;
}
/* step5:release ss51 & dsp */
- GTP_DEBUG("[burn_fw_boot]step5:release ss51 & dsp");
+ pr_debug("step5:release ss51 & dsp");
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_boot]release ss51 & dsp fail.");
+ pr_err("release ss51 & dsp fail");
return FAIL;
}
/* must delay */
msleep(20);
/* step6:select bank3 */
- GTP_DEBUG("[burn_fw_boot]step6:select bank3");
+ pr_debug("step6:select bank3");
ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_boot]select bank3 fail.");
+ pr_err("select bank3 fail");
return FAIL;
}
/* step7:burn 2k bootloader firmware */
- GTP_DEBUG("[burn_fw_boot]step7:burn 2k bootloader firmware");
+ pr_debug("step7:burn 2k bootloader firmware");
ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_boot]burn fw_section fail.");
+ pr_err("burn fw_section fail");
return ret;
}
/* step7:send burn cmd to move data to flash from sram */
- GTP_DEBUG("[burn_fw_boot]step7:send burn cmd to move data to",
- "flash from sram");
+ pr_debug("step7:send burn cmd to flash data from sram");
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_boot]send burn cmd fail.");
+ pr_err("send burn cmd fail");
return ret;
}
- GTP_DEBUG("[burn_fw_boot]Wait for the burn is complete......");
+ pr_debug("Wait for the burn is complete");
do {
ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_boot]Get burn state fail");
+ pr_err("Get burn state fail");
return ret;
}
msleep(20);
- /* GTP_DEBUG("[burn_fw_boot]Get burn state:%d.",
- * rd_buf[GTP_ADDR_LENGTH]);
- */
} while (rd_buf[GTP_ADDR_LENGTH]);
/* step8:recall check 2k bootloader firmware */
- GTP_DEBUG("[burn_fw_boot]step8:recall check 2k bootloader firmware");
+ pr_debug("step8:recall check 2k bootloader firmware");
ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
if (ret == FAIL) {
- GTP_ERROR("[burn_fw_boot]recall check 4k dsp firmware fail.");
+ pr_err("recall check 4k dsp firmware fail");
return ret;
}
/* step9:enable download DSP code */
- GTP_DEBUG("[burn_fw_boot]step9:enable download DSP code ");
+ pr_debug("step9:enable download DSP code ");
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_boot]enable download DSP code fail.");
+ pr_err("enable download DSP code fail");
return FAIL;
}
/* step10:release ss51 & hold dsp */
- GTP_DEBUG("[burn_fw_boot]step10:release ss51 & hold dsp");
+ pr_debug("step10:release ss51 & hold dsp");
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08);
if (ret <= 0) {
- GTP_ERROR("[burn_fw_boot]release ss51 & hold dsp fail.");
+ pr_err("release ss51 & hold dsp fail");
return FAIL;
}
@@ -1625,46 +1370,47 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
s32 gup_update_proc(void *dir)
{
s32 ret = 0;
- u8 retry = 0;
- st_fw_head fw_head;
+ u8 retry = 0;
+ struct st_fw_head fw_head;
struct goodix_ts_data *ts = NULL;
- GTP_DEBUG("[update_proc]Begin update ......");
+ pr_debug("Begin update.");
+
+ if (!i2c_connect_client) {
+ pr_err("No i2c connect client for %s\n", __func__);
+ return -EIO;
+ }
show_len = 1;
total_len = 100;
- if (dir == NULL)
- /* wait main thread to be completed */
- msleep(3000);
ts = i2c_get_clientdata(i2c_connect_client);
if (searching_file) {
/* exit .bin update file searching */
searching_file = 0;
- GTP_INFO("Exiting searching .bin update file...");
+ pr_info("Exiting searching .bin update file.");
/* wait for auto update quitted completely */
while ((show_len != 200) && (show_len != 100))
msleep(100);
}
- update_msg.file = NULL;
ret = gup_check_update_file(i2c_connect_client, &fw_head, (u8 *)dir);
if (ret == FAIL) {
- GTP_ERROR("[update_proc]check update file fail.");
+ pr_err("check update file fail");
goto file_fail;
}
/* gtp_reset_guitar(i2c_connect_client, 20); */
ret = gup_get_ic_fw_msg(i2c_connect_client);
if (ret == FAIL) {
- GTP_ERROR("[update_proc]get ic message fail.");
+ pr_err("get ic message fail");
goto file_fail;
}
- ret = gup_enter_update_judge(&fw_head);
+ ret = gup_enter_update_judge(ts->client, &fw_head);
if (ret == FAIL) {
- GTP_ERROR("[update_proc]Check *.bin file fail.");
+ pr_err("Check *.bin file fail");
goto file_fail;
}
@@ -1675,7 +1421,7 @@ s32 gup_update_proc(void *dir)
#endif
ret = gup_enter_update_mode(i2c_connect_client);
if (ret == FAIL) {
- GTP_ERROR("[update_proc]enter update mode fail.");
+ pr_err("enter update mode fail");
goto update_fail;
}
@@ -1684,53 +1430,46 @@ s32 gup_update_proc(void *dir)
total_len = 100;
ret = gup_burn_dsp_isp(i2c_connect_client);
if (ret == FAIL) {
- GTP_ERROR("[update_proc]burn dsp isp fail.");
+ pr_err("burn dsp isp fail");
continue;
}
show_len += 10;
ret = gup_burn_fw_ss51(i2c_connect_client);
if (ret == FAIL) {
- GTP_ERROR("[update_proc]burn ss51 firmware fail.");
+ pr_err("burn ss51 firmware fail");
continue;
}
show_len += 40;
ret = gup_burn_fw_dsp(i2c_connect_client);
if (ret == FAIL) {
- GTP_ERROR("[update_proc]burn dsp firmware fail.");
+ pr_err("burn dsp firmware fail");
continue;
}
show_len += 20;
ret = gup_burn_fw_boot(i2c_connect_client);
if (ret == FAIL) {
- GTP_ERROR("[update_proc]burn bootloader fw fail.");
+ pr_err("burn bootloader fw fail");
continue;
}
show_len += 10;
- GTP_INFO("[update_proc]UPDATE SUCCESS.");
+ pr_info("UPDATE SUCCESS");
break;
}
if (retry >= 5) {
- GTP_ERROR("[update_proc]retry timeout,UPDATE FAIL.");
+ pr_err("retry timeout,UPDATE FAIL");
goto update_fail;
}
- GTP_DEBUG("[update_proc]leave update mode.");
- gup_leave_update_mode();
+ pr_debug("leave update mode");
+ gup_leave_update_mode(i2c_connect_client);
msleep(100);
- /* GTP_DEBUG("[update_proc]send config.");
- * ret = gtp_send_cfg(i2c_connect_client);
- * if(ret < 0) {
- * GTP_ERROR("[update_proc]send config fail.");
- * }
- */
-
if (ts->fw_error) {
- GTP_INFO("firmware error auto update, resent config!");
+ pr_info("firmware error auto update, resent config\n");
gup_init_panel(ts);
}
show_len = 100;
@@ -1741,7 +1480,11 @@ s32 gup_update_proc(void *dir)
#if GTP_ESD_PROTECT
gtp_esd_switch(ts->client, SWITCH_ON);
#endif
- filp_close(update_msg.file, NULL);
+ if (update_msg.need_free) {
+ devm_kfree(&ts->client->dev, update_msg.fw_data);
+ update_msg.need_free = false;
+ }
+
return SUCCESS;
update_fail:
@@ -1753,26 +1496,28 @@ update_fail:
#endif
file_fail:
- if (update_msg.file && !IS_ERR(update_msg.file))
- filp_close(update_msg.file, NULL);
-
show_len = 200;
total_len = 100;
+ if (update_msg.need_free) {
+ devm_kfree(&ts->client->dev, update_msg.fw_data);
+ update_msg.need_free = false;
+ }
return FAIL;
}
-#if GTP_AUTO_UPDATE
+static void gup_update_work(struct work_struct *work)
+{
+ if (gup_update_proc(NULL) == FAIL)
+ pr_err("Goodix update work fail\n");
+}
+
u8 gup_init_update_proc(struct goodix_ts_data *ts)
{
- struct task_struct *thread = NULL;
+ dev_dbg(&ts->client->dev, "Ready to run update work\n");
- GTP_INFO("Ready to run update thread.");
- thread = kthread_run(gup_update_proc, (void *)NULL, "guitar_update");
- if (IS_ERR(thread)) {
- GTP_ERROR("Failed to create update thread.\n");
- return -EINVAL;
- }
+ INIT_DELAYED_WORK(&ts->goodix_update_work, gup_update_work);
+ schedule_delayed_work(&ts->goodix_update_work,
+ msecs_to_jiffies(3000));
return 0;
}
-#endif
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
index e8229216fcd3..206941708141 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
@@ -492,33 +492,25 @@ static int synaptics_i2c_change_pipe_owner(
static void synaptics_secure_touch_init(struct synaptics_rmi4_data *data)
{
- int ret = 0;
data->st_initialized = 0;
init_completion(&data->st_powerdown);
init_completion(&data->st_irq_processed);
/* Get clocks */
data->core_clk = clk_get(data->pdev->dev.parent, "core_clk");
if (IS_ERR(data->core_clk)) {
- ret = PTR_ERR(data->core_clk);
- dev_err(data->pdev->dev.parent,
- "%s: error on clk_get(core_clk):%d\n", __func__, ret);
- return;
+ data->core_clk = NULL;
+ dev_warn(data->pdev->dev.parent,
+ "%s: core_clk is not defined\n", __func__);
}
data->iface_clk = clk_get(data->pdev->dev.parent, "iface_clk");
if (IS_ERR(data->iface_clk)) {
- ret = PTR_ERR(data->iface_clk);
- dev_err(data->pdev->dev.parent,
- "%s: error on clk_get(iface_clk):%d\n", __func__, ret);
- goto err_iface_clk;
+ data->iface_clk = NULL;
+ dev_warn(data->pdev->dev.parent,
+ "%s: iface_clk is not defined\n", __func__);
}
data->st_initialized = 1;
- return;
-
-err_iface_clk:
- clk_put(data->core_clk);
- data->core_clk = NULL;
}
static void synaptics_secure_touch_notify(struct synaptics_rmi4_data *rmi4_data)
{
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 a035ebd5af7e..0a8728ce36dc 100644
--- a/drivers/iommu/msm_dma_iommu_mapping.c
+++ b/drivers/iommu/msm_dma_iommu_mapping.c
@@ -358,10 +358,12 @@ int msm_dma_unmap_all_for_dev(struct device *dev)
meta_node = rb_first(root);
while (meta_node) {
struct msm_iommu_map *iommu_map;
+ struct msm_iommu_map *iommu_map_next;
meta = rb_entry(meta_node, struct msm_iommu_meta, node);
mutex_lock(&meta->lock);
- list_for_each_entry(iommu_map, &meta->iommu_maps, lnode)
+ list_for_each_entry_safe(iommu_map, iommu_map_next,
+ &meta->iommu_maps, lnode)
if (iommu_map->dev == dev)
if (!kref_put(&iommu_map->ref,
msm_iommu_map_release))
@@ -374,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/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 0608e08b4427..50c8c92d575d 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -199,7 +199,7 @@ static void gic_poke_irq(struct irq_data *d, u32 offset)
rwp_wait = gic_dist_wait_for_rwp;
}
- writel_relaxed(mask, base + offset + (gic_irq(d) / 32) * 4);
+ writel_relaxed_no_log(mask, base + offset + (gic_irq(d) / 32) * 4);
rwp_wait();
}
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 2f7529814cd9..0cae5d2e5263 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -594,7 +594,7 @@ static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led)
avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm,
led->trigger_lmh);
return min(FLASH_LED_MAX_TOTAL_CURRENT_MA,
- (int)(avail_flash_ua / MCONV));
+ (int)(div64_s64(avail_flash_ua, MCONV)));
}
static int qpnp_flash_led_calc_thermal_current_lim(struct qpnp_flash_led *led)
diff --git a/drivers/media/dvb-core/demux.h b/drivers/media/dvb-core/demux.h
index ccc1f43cb9a9..cd02396abbe7 100644
--- a/drivers/media/dvb-core/demux.h
+++ b/drivers/media/dvb-core/demux.h
@@ -7,6 +7,8 @@
* Copyright (c) 2000 Nokia Research Center
* Tampere, FINLAND
*
+ * Copyright (c) 2012-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 Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
@@ -36,6 +38,8 @@
* Common definitions
*/
+#define DMX_EVENT_QUEUE_SIZE 500 /* number of events */
+
/*
* DMX_MAX_FILTER_SIZE: Maximum length (in bytes) of a section/PES filter.
*/
@@ -57,6 +61,104 @@
#endif
/*
+ * enum dmx_success: Success codes for the Demux Callback API.
+ */
+enum dmx_success {
+ DMX_OK = 0, /* Received Ok */
+ DMX_OK_PES_END, /* Received OK, data reached end of PES packet */
+ DMX_OK_PCR, /* Received OK, data with new PCR/STC pair */
+ DMX_OK_EOS, /* Received OK, reached End-of-Stream (EOS) */
+ DMX_OK_MARKER, /* Received OK, reached a data Marker */
+ DMX_LENGTH_ERROR, /* Incorrect length */
+ DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */
+ DMX_CRC_ERROR, /* Incorrect CRC */
+ DMX_FRAME_ERROR, /* Frame alignment error */
+ DMX_FIFO_ERROR, /* Receiver FIFO overrun */
+ DMX_MISSED_ERROR, /* Receiver missed packet */
+ DMX_OK_DECODER_BUF, /* Received OK, new ES data in decoder buffer */
+ DMX_OK_IDX, /* Received OK, new index event */
+ DMX_OK_SCRAMBLING_STATUS, /* Received OK, new scrambling status */
+};
+
+
+/*
+ * struct dmx_data_ready: Parameters for event notification callback.
+ * Event notification notifies demux device that data is written
+ * and available in the device's output buffer or provides
+ * notification on errors and other events. In the latter case
+ * data_length is zero.
+ */
+struct dmx_data_ready {
+ enum dmx_success status;
+
+ /*
+ * data_length may be 0 in case of DMX_OK_PES_END or DMX_OK_EOS
+ * and in non-DMX_OK_XXX events. In DMX_OK_PES_END,
+ * data_length is for data coming after the end of PES.
+ */
+ int data_length;
+
+ union {
+ struct {
+ int start_gap;
+ int actual_length;
+ int disc_indicator_set;
+ int pes_length_mismatch;
+ u64 stc;
+ u32 tei_counter;
+ u32 cont_err_counter;
+ u32 ts_packets_num;
+ } pes_end;
+
+ struct {
+ u64 pcr;
+ u64 stc;
+ int disc_indicator_set;
+ } pcr;
+
+ struct {
+ int handle;
+ int cookie;
+ u32 offset;
+ u32 len;
+ int pts_exists;
+ u64 pts;
+ int dts_exists;
+ u64 dts;
+ u32 tei_counter;
+ u32 cont_err_counter;
+ u32 ts_packets_num;
+ u32 ts_dropped_bytes;
+ u64 stc;
+ } buf;
+
+ struct {
+ u64 id;
+ } marker;
+
+ struct dmx_index_event_info idx_event;
+ struct dmx_scrambling_status_event_info scrambling_bits;
+ };
+};
+
+/*
+ * struct data_buffer: Parameters of buffer allocated by
+ * demux device for input/output. Can be used to directly map the
+ * demux-device buffer to HW output if HW supports it.
+ */
+struct data_buffer {
+ /* dvb_ringbuffer managed by demux-device */
+ const struct dvb_ringbuffer *ringbuff;
+
+
+ /*
+ * Private handle returned by kernel demux when
+ * map_buffer is called in case external buffer
+ * is used. NULL if buffer is allocated internally.
+ */
+ void *priv_handle;
+};
+/*
* TS packet reception
*/
@@ -91,10 +193,18 @@ enum ts_filter_type {
* Using this API, the client can set the filtering properties to start/stop
* filtering TS packets on a particular TS feed.
*/
+struct dmx_ts_feed;
+
+typedef int (*dmx_ts_data_ready_cb)(
+ struct dmx_ts_feed *source,
+ struct dmx_data_ready *dmx_data_ready);
+
struct dmx_ts_feed {
int is_filtering;
struct dmx_demux *parent;
+ struct data_buffer buffer;
void *priv;
+ struct dmx_decoder_buffers *decoder_buffers;
int (*set)(struct dmx_ts_feed *feed,
u16 pid,
int type,
@@ -103,6 +213,34 @@ struct dmx_ts_feed {
struct timespec timeout);
int (*start_filtering)(struct dmx_ts_feed *feed);
int (*stop_filtering)(struct dmx_ts_feed *feed);
+ int (*set_video_codec)(struct dmx_ts_feed *feed,
+ enum dmx_video_codec video_codec);
+ int (*set_idx_params)(struct dmx_ts_feed *feed,
+ struct dmx_indexing_params *idx_params);
+ int (*get_decoder_buff_status)(
+ struct dmx_ts_feed *feed,
+ struct dmx_buffer_status *dmx_buffer_status);
+ int (*reuse_decoder_buffer)(
+ struct dmx_ts_feed *feed,
+ int cookie);
+ int (*data_ready_cb)(struct dmx_ts_feed *feed,
+ dmx_ts_data_ready_cb callback);
+ int (*notify_data_read)(struct dmx_ts_feed *feed,
+ u32 bytes_num);
+ int (*set_tsp_out_format)(struct dmx_ts_feed *feed,
+ enum dmx_tsp_format_t tsp_format);
+ int (*set_secure_mode)(struct dmx_ts_feed *feed,
+ struct dmx_secure_mode *sec_mode);
+ int (*set_cipher_ops)(struct dmx_ts_feed *feed,
+ struct dmx_cipher_operations *cipher_ops);
+ int (*oob_command)(struct dmx_ts_feed *feed,
+ struct dmx_oob_command *cmd);
+ int (*ts_insertion_init)(struct dmx_ts_feed *feed);
+ int (*ts_insertion_terminate)(struct dmx_ts_feed *feed);
+ int (*ts_insertion_insert_buffer)(struct dmx_ts_feed *feed,
+ char *data, size_t size);
+ int (*get_scrambling_bits)(struct dmx_ts_feed *feed, u8 *value);
+ int (*flush_buffer)(struct dmx_ts_feed *feed, size_t length);
};
/*
@@ -127,14 +265,21 @@ struct dmx_ts_feed {
* corresponding bits are compared. The filter only accepts sections that are
* equal to filter_value in all the tested bit positions.
*/
+
+struct dmx_section_feed;
struct dmx_section_filter {
u8 filter_value[DMX_MAX_FILTER_SIZE];
u8 filter_mask[DMX_MAX_FILTER_SIZE];
u8 filter_mode[DMX_MAX_FILTER_SIZE];
struct dmx_section_feed *parent; /* Back-pointer */
+ struct data_buffer buffer;
void *priv; /* Pointer to private data of the API client */
};
+typedef int (*dmx_section_data_ready_cb)(
+ struct dmx_section_filter *source,
+ struct dmx_data_ready *dmx_data_ready);
+
/**
* struct dmx_section_feed - Structure that contains a section feed filter
*
@@ -185,6 +330,18 @@ struct dmx_section_feed {
struct dmx_section_filter *filter);
int (*start_filtering)(struct dmx_section_feed *feed);
int (*stop_filtering)(struct dmx_section_feed *feed);
+ int (*data_ready_cb)(struct dmx_section_feed *feed,
+ dmx_section_data_ready_cb callback);
+ int (*notify_data_read)(struct dmx_section_filter *filter,
+ u32 bytes_num);
+ int (*set_secure_mode)(struct dmx_section_feed *feed,
+ struct dmx_secure_mode *sec_mode);
+ int (*set_cipher_ops)(struct dmx_section_feed *feed,
+ struct dmx_cipher_operations *cipher_ops);
+ int (*oob_command)(struct dmx_section_feed *feed,
+ struct dmx_oob_command *cmd);
+ int (*get_scrambling_bits)(struct dmx_section_feed *feed, u8 *value);
+ int (*flush_buffer)(struct dmx_section_feed *feed, size_t length);
};
/*
@@ -250,7 +407,8 @@ typedef int (*dmx_ts_cb)(const u8 *buffer1,
size_t buffer1_length,
const u8 *buffer2,
size_t buffer2_length,
- struct dmx_ts_feed *source);
+ struct dmx_ts_feed *source,
+ enum dmx_success success);
/**
* typedef dmx_section_cb - DVB demux TS filter callback function prototype
@@ -291,7 +449,18 @@ typedef int (*dmx_section_cb)(const u8 *buffer1,
size_t buffer1_len,
const u8 *buffer2,
size_t buffer2_len,
- struct dmx_section_filter *source);
+ struct dmx_section_filter *source,
+ enum dmx_success success);
+
+typedef int (*dmx_ts_fullness) (
+ struct dmx_ts_feed *source,
+ int required_space,
+ int wait);
+
+typedef int (*dmx_section_fullness) (
+ struct dmx_section_filter *source,
+ int required_space,
+ int wait);
/*--------------------------------------------------------------------------*/
/* DVB Front-End */
@@ -310,6 +479,13 @@ typedef int (*dmx_section_cb)(const u8 *buffer1,
enum dmx_frontend_source {
DMX_MEMORY_FE,
DMX_FRONTEND_0,
+ DMX_FRONTEND_1,
+ DMX_FRONTEND_2,
+ DMX_FRONTEND_3,
+ DMX_STREAM_0, /* external stream input, e.g. LVDS */
+ DMX_STREAM_1,
+ DMX_STREAM_2,
+ DMX_STREAM_3
};
/**
@@ -343,8 +519,11 @@ struct dmx_frontend {
*/
enum dmx_demux_caps {
DMX_TS_FILTERING = 1,
+ DMX_PES_FILTERING = 2,
DMX_SECTION_FILTERING = 4,
DMX_MEMORY_BASED_FILTERING = 8,
+ DMX_CRC_CHECKING = 16,
+ DMX_TS_DESCRAMBLING = 32
};
/*
@@ -556,6 +735,10 @@ struct dmx_demux {
enum dmx_demux_caps capabilities;
struct dmx_frontend *frontend;
void *priv;
+ struct data_buffer dvr_input; /* DVR input buffer */
+ int dvr_input_protected;
+ struct dentry *debugfs_demux_dir; /* debugfs dir */
+
int (*open)(struct dmx_demux *demux);
int (*close)(struct dmx_demux *demux);
int (*write)(struct dmx_demux *demux, const char __user *buf,
@@ -581,18 +764,31 @@ struct dmx_demux {
int (*get_pes_pids)(struct dmx_demux *demux, u16 *pids);
- /* private: Not used upstream and never documented */
-#if 0
int (*get_caps)(struct dmx_demux *demux, struct dmx_caps *caps);
+
int (*set_source)(struct dmx_demux *demux, const dmx_source_t *src);
-#endif
- /*
- * private: Only used at av7110, to read some data from firmware.
- * As this was never documented, we have no clue about what's
- * there, and its usage on other drivers aren't encouraged.
- */
+
+ int (*set_tsp_format)(struct dmx_demux *demux,
+ enum dmx_tsp_format_t tsp_format);
+
+ int (*set_playback_mode)(struct dmx_demux *demux,
+ enum dmx_playback_mode_t mode,
+ dmx_ts_fullness ts_fullness_callback,
+ dmx_section_fullness sec_fullness_callback);
+
+ int (*write_cancel)(struct dmx_demux *demux);
+
int (*get_stc)(struct dmx_demux *demux, unsigned int num,
u64 *stc, unsigned int *base);
+
+ int (*map_buffer)(struct dmx_demux *demux,
+ struct dmx_buffer *dmx_buffer,
+ void **priv_handle, void **mem);
+
+ int (*unmap_buffer)(struct dmx_demux *demux,
+ void *priv_handle);
+
+ int (*get_tsp_size)(struct dmx_demux *demux);
};
#endif /* #ifndef __DEMUX_H */
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index ea9abde902e9..63becfd57eaa 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -28,15 +28,74 @@
#include <linux/poll.h>
#include <linux/ioctl.h>
#include <linux/wait.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/compat.h>
+#include <linux/mm.h>
#include "dmxdev.h"
-static int debug;
+static int overflow_auto_flush = 1;
+module_param(overflow_auto_flush, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(overflow_auto_flush,
+ "Automatically flush buffer on overflow (default: on)");
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+#define DMX_DEFAULT_DECODER_BUFFER_SIZE (32768)
-#define dprintk if (debug) printk
+static inline int dvb_dmxdev_verify_buffer_size(u32 size, u32 max_size,
+ u32 size_align)
+{
+ if (size_align)
+ return size <= max_size && !(size % size_align);
+ else
+ return size <= max_size;
+}
+
+static int dvb_filter_verify_buffer_size(struct dmxdev_filter *filter)
+{
+ struct dmx_caps caps;
+ size_t size = filter->buffer.size;
+
+ /*
+ * For backward compatibility, if no demux capabilities can
+ * be retrieved assume size is ok.
+ * Decoder filter buffer size is verified when decoder buffer is set.
+ */
+ if (filter->dev->demux->get_caps) {
+ filter->dev->demux->get_caps(filter->dev->demux, &caps);
+
+ if (filter->type == DMXDEV_TYPE_SEC)
+ return dvb_dmxdev_verify_buffer_size(
+ size,
+ caps.section.max_size,
+ caps.section.size_alignment);
+
+ if (filter->params.pes.output == DMX_OUT_TAP)
+ return dvb_dmxdev_verify_buffer_size(
+ size,
+ caps.pes.max_size,
+ caps.pes.size_alignment);
+
+ size = (filter->params.pes.output == DMX_OUT_TS_TAP) ?
+ filter->dev->dvr_buffer.size : size;
+
+ if (filter->params.pes.output == DMX_OUT_TSDEMUX_TAP ||
+ filter->params.pes.output == DMX_OUT_TS_TAP) {
+ if (filter->dmx_tsp_format == DMX_TSP_FORMAT_188)
+ return dvb_dmxdev_verify_buffer_size(
+ size,
+ caps.recording_188_tsp.max_size,
+ caps.recording_188_tsp.size_alignment);
+
+ return dvb_dmxdev_verify_buffer_size(
+ size,
+ caps.recording_192_tsp.max_size,
+ caps.recording_192_tsp.size_alignment);
+ }
+ }
+
+ return 1;
+}
static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
const u8 *src, size_t len)
@@ -50,16 +109,400 @@ static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
free = dvb_ringbuffer_free(buf);
if (len > free) {
- dprintk("dmxdev: buffer overflow\n");
+ pr_debug("dmxdev: buffer overflow\n");
return -EOVERFLOW;
}
return dvb_ringbuffer_write(buf, src, len);
}
-static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
- int non_blocking, char __user *buf,
- size_t count, loff_t *ppos)
+static inline void dvb_dmxdev_notify_data_read(struct dmxdev_filter *filter,
+ int bytes_read)
+{
+ if (!filter)
+ return;
+
+ if (filter->type == DMXDEV_TYPE_SEC) {
+ if (filter->feed.sec.feed->notify_data_read)
+ filter->feed.sec.feed->notify_data_read(
+ filter->filter.sec,
+ bytes_read);
+ } else {
+ struct dmxdev_feed *feed;
+
+ /*
+ * All feeds of same demux-handle share the same output
+ * buffer, it is enough to notify on the buffer status
+ * on one of the feeds
+ */
+ feed = list_first_entry(&filter->feed.ts,
+ struct dmxdev_feed, next);
+
+ if (feed->ts->notify_data_read)
+ feed->ts->notify_data_read(
+ feed->ts,
+ bytes_read);
+ }
+}
+
+static inline u32 dvb_dmxdev_advance_event_idx(u32 index)
+{
+ index++;
+ if (index >= DMX_EVENT_QUEUE_SIZE)
+ index = 0;
+
+ return index;
+}
+
+static inline int dvb_dmxdev_events_is_full(struct dmxdev_events_queue *events)
+{
+ int new_write_index;
+
+ new_write_index = dvb_dmxdev_advance_event_idx(events->write_index);
+ if (new_write_index == events->read_index)
+ return 1;
+
+ return 0;
+
+}
+
+static inline void dvb_dmxdev_flush_events(struct dmxdev_events_queue *events)
+{
+ events->read_index = 0;
+ events->write_index = 0;
+ events->notified_index = 0;
+ events->bytes_read_no_event = 0;
+ events->current_event_data_size = 0;
+ events->wakeup_events_counter = 0;
+}
+
+static inline void dvb_dmxdev_flush_output(struct dvb_ringbuffer *buffer,
+ struct dmxdev_events_queue *events)
+{
+ dvb_dmxdev_flush_events(events);
+ dvb_ringbuffer_flush(buffer);
+}
+
+static int dvb_dmxdev_update_pes_event(struct dmx_filter_event *event,
+ int bytes_read)
+{
+ int start_delta;
+
+ if (event->params.pes.total_length <= bytes_read)
+ return event->params.pes.total_length;
+
+ /*
+ * only part of the data relevant to this event was read.
+ * Update the event's information to reflect the new state.
+ */
+ event->params.pes.total_length -= bytes_read;
+
+ start_delta = event->params.pes.start_offset -
+ event->params.pes.base_offset;
+
+ if (bytes_read <= start_delta) {
+ event->params.pes.base_offset +=
+ bytes_read;
+ } else {
+ start_delta =
+ bytes_read - start_delta;
+
+ event->params.pes.start_offset += start_delta;
+ event->params.pes.actual_length -= start_delta;
+
+ event->params.pes.base_offset =
+ event->params.pes.start_offset;
+ }
+
+ return 0;
+}
+
+static int dvb_dmxdev_update_section_event(struct dmx_filter_event *event,
+ int bytes_read)
+{
+ int start_delta;
+
+ if (event->params.section.total_length <= bytes_read)
+ return event->params.section.total_length;
+
+ /*
+ * only part of the data relevant to this event was read.
+ * Update the event's information to reflect the new state.
+ */
+
+ event->params.section.total_length -= bytes_read;
+
+ start_delta = event->params.section.start_offset -
+ event->params.section.base_offset;
+
+ if (bytes_read <= start_delta) {
+ event->params.section.base_offset +=
+ bytes_read;
+ } else {
+ start_delta =
+ bytes_read - start_delta;
+
+ event->params.section.start_offset += start_delta;
+ event->params.section.actual_length -= start_delta;
+
+ event->params.section.base_offset =
+ event->params.section.start_offset;
+ }
+
+ return 0;
+}
+
+static int dvb_dmxdev_update_rec_event(struct dmx_filter_event *event,
+ int bytes_read)
+{
+ if (event->params.recording_chunk.size <= bytes_read)
+ return event->params.recording_chunk.size;
+
+ /*
+ * only part of the data relevant to this event was read.
+ * Update the event's information to reflect the new state.
+ */
+ event->params.recording_chunk.size -= bytes_read;
+ event->params.recording_chunk.offset += bytes_read;
+
+ return 0;
+}
+
+static int dvb_dmxdev_add_event(struct dmxdev_events_queue *events,
+ struct dmx_filter_event *event)
+{
+ int res;
+ int new_write_index;
+ int data_event;
+
+ /* Check if the event is disabled */
+ if (events->event_mask.disable_mask & event->type)
+ return 0;
+
+ /* Check if we are adding an event that user already read its data */
+ if (events->bytes_read_no_event) {
+ data_event = 1;
+
+ if (event->type == DMX_EVENT_NEW_PES)
+ res = dvb_dmxdev_update_pes_event(event,
+ events->bytes_read_no_event);
+ else if (event->type == DMX_EVENT_NEW_SECTION)
+ res = dvb_dmxdev_update_section_event(event,
+ events->bytes_read_no_event);
+ else if (event->type == DMX_EVENT_NEW_REC_CHUNK)
+ res = dvb_dmxdev_update_rec_event(event,
+ events->bytes_read_no_event);
+ else
+ data_event = 0;
+
+ if (data_event) {
+ if (res) {
+ /*
+ * Data relevant to this event was fully
+ * consumed already, discard event.
+ */
+ events->bytes_read_no_event -= res;
+ return 0;
+ }
+ events->bytes_read_no_event = 0;
+ } else {
+ /*
+ * data was read beyond the non-data event,
+ * making it not relevant anymore
+ */
+ return 0;
+ }
+ }
+
+ new_write_index = dvb_dmxdev_advance_event_idx(events->write_index);
+ if (new_write_index == events->read_index) {
+ pr_err("dmxdev: events overflow\n");
+ return -EOVERFLOW;
+ }
+
+ events->queue[events->write_index] = *event;
+ events->write_index = new_write_index;
+
+ if (!(events->event_mask.no_wakeup_mask & event->type))
+ events->wakeup_events_counter++;
+
+ return 0;
+}
+
+static int dvb_dmxdev_remove_event(struct dmxdev_events_queue *events,
+ struct dmx_filter_event *event)
+{
+ if (events->notified_index == events->write_index)
+ return -ENODATA;
+
+ *event = events->queue[events->notified_index];
+
+ events->notified_index =
+ dvb_dmxdev_advance_event_idx(events->notified_index);
+
+ if (!(events->event_mask.no_wakeup_mask & event->type))
+ events->wakeup_events_counter--;
+
+ return 0;
+}
+
+static int dvb_dmxdev_update_events(struct dmxdev_events_queue *events,
+ int bytes_read)
+{
+ struct dmx_filter_event *event;
+ int res;
+ int data_event;
+
+ /*
+ * If data events are not enabled on this filter,
+ * there's nothing to update.
+ */
+ if (events->data_read_event_masked)
+ return 0;
+
+ /*
+ * Go through all events that were notified and
+ * remove them from the events queue if their respective
+ * data was read.
+ */
+ while ((events->read_index != events->notified_index) &&
+ (bytes_read)) {
+ event = events->queue + events->read_index;
+
+ data_event = 1;
+
+ if (event->type == DMX_EVENT_NEW_PES)
+ res = dvb_dmxdev_update_pes_event(event, bytes_read);
+ else if (event->type == DMX_EVENT_NEW_SECTION)
+ res = dvb_dmxdev_update_section_event(event,
+ bytes_read);
+ else if (event->type == DMX_EVENT_NEW_REC_CHUNK)
+ res = dvb_dmxdev_update_rec_event(event, bytes_read);
+ else
+ data_event = 0;
+
+ if (data_event) {
+ if (res) {
+ /*
+ * Data relevant to this event was
+ * fully consumed, remove it from the queue.
+ */
+ bytes_read -= res;
+ events->read_index =
+ dvb_dmxdev_advance_event_idx(
+ events->read_index);
+ } else {
+ bytes_read = 0;
+ }
+ } else {
+ /*
+ * non-data event was already notified,
+ * no need to keep it
+ */
+ events->read_index = dvb_dmxdev_advance_event_idx(
+ events->read_index);
+ }
+ }
+
+ if (!bytes_read)
+ return 0;
+
+ /*
+ * If we reached here it means:
+ * bytes_read != 0
+ * events->read_index == events->notified_index
+ * Check if there are pending events in the queue
+ * which the user didn't read while their relevant data
+ * was read.
+ */
+ while ((events->notified_index != events->write_index) &&
+ (bytes_read)) {
+ event = events->queue + events->notified_index;
+
+ data_event = 1;
+
+ if (event->type == DMX_EVENT_NEW_PES)
+ res = dvb_dmxdev_update_pes_event(event, bytes_read);
+ else if (event->type == DMX_EVENT_NEW_SECTION)
+ res = dvb_dmxdev_update_section_event(event,
+ bytes_read);
+ else if (event->type == DMX_EVENT_NEW_REC_CHUNK)
+ res = dvb_dmxdev_update_rec_event(event, bytes_read);
+ else
+ data_event = 0;
+
+ if (data_event) {
+ if (res) {
+ /*
+ * Data relevant to this event was
+ * fully consumed, remove it from the queue.
+ */
+ bytes_read -= res;
+ events->notified_index =
+ dvb_dmxdev_advance_event_idx(
+ events->notified_index);
+ if (!(events->event_mask.no_wakeup_mask &
+ event->type))
+ events->wakeup_events_counter--;
+ } else {
+ bytes_read = 0;
+ }
+ } else {
+ if (bytes_read)
+ /*
+ * data was read beyond the non-data event,
+ * making it not relevant anymore
+ */
+ events->notified_index =
+ dvb_dmxdev_advance_event_idx(
+ events->notified_index);
+ if (!(events->event_mask.no_wakeup_mask &
+ event->type))
+ events->wakeup_events_counter--;
+ }
+
+ events->read_index = events->notified_index;
+ }
+
+ /*
+ * Check if data was read without having a respective
+ * event in the events-queue
+ */
+ if (bytes_read)
+ events->bytes_read_no_event += bytes_read;
+
+ return 0;
+}
+
+static inline int dvb_dmxdev_check_data(struct dmxdev_filter *filter,
+ struct dvb_ringbuffer *src)
+{
+ int data_status_change;
+
+ if (filter)
+ if (mutex_lock_interruptible(&filter->mutex))
+ return -ERESTARTSYS;
+
+ if (!src->data ||
+ !dvb_ringbuffer_empty(src) ||
+ src->error ||
+ (filter &&
+ (filter->state != DMXDEV_STATE_GO) &&
+ (filter->state != DMXDEV_STATE_DONE)))
+ data_status_change = 1;
+ else
+ data_status_change = 0;
+
+ if (filter)
+ mutex_unlock(&filter->mutex);
+
+ return data_status_change;
+}
+
+static ssize_t dvb_dmxdev_buffer_read(struct dmxdev_filter *filter,
+ struct dvb_ringbuffer *src,
+ int non_blocking, char __user *buf,
+ size_t count, loff_t *ppos)
{
size_t todo;
ssize_t avail;
@@ -70,7 +513,7 @@ static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
if (src->error) {
ret = src->error;
- dvb_ringbuffer_flush(src);
+ src->error = 0;
return ret;
}
@@ -80,15 +523,35 @@ static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
break;
}
+ if (filter) {
+ if ((filter->state == DMXDEV_STATE_DONE) &&
+ dvb_ringbuffer_empty(src))
+ break;
+
+ mutex_unlock(&filter->mutex);
+ }
+
ret = wait_event_interruptible(src->queue,
- !dvb_ringbuffer_empty(src) ||
- (src->error != 0));
+ dvb_dmxdev_check_data(filter, src));
+
+ if (filter) {
+ if (mutex_lock_interruptible(&filter->mutex))
+ return -ERESTARTSYS;
+
+ if ((filter->state != DMXDEV_STATE_GO) &&
+ (filter->state != DMXDEV_STATE_DONE))
+ return -ENODEV;
+ }
+
if (ret < 0)
break;
+ if (!src->data)
+ return 0;
+
if (src->error) {
ret = src->error;
- dvb_ringbuffer_flush(src);
+ src->error = 0;
break;
}
@@ -103,6 +566,9 @@ static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
buf += ret;
}
+ if (count - todo) /* some data was read? */
+ wake_up_all(&src->queue);
+
return (count - todo) ? (count - todo) : ret;
}
@@ -120,13 +586,238 @@ static struct dmx_frontend *get_fe(struct dmx_demux *demux, int type)
return NULL;
}
+static void dvb_dvr_oob_cmd(struct dmxdev *dmxdev, struct dmx_oob_command *cmd)
+{
+ int i;
+ struct dmxdev_filter *filter;
+ struct dmxdev_feed *feed;
+
+ for (i = 0; i < dmxdev->filternum; i++) {
+ filter = &dmxdev->filter[i];
+ if (!filter || filter->state != DMXDEV_STATE_GO)
+ continue;
+
+ switch (filter->type) {
+ case DMXDEV_TYPE_SEC:
+ filter->feed.sec.feed->oob_command(
+ filter->feed.sec.feed, cmd);
+ break;
+ case DMXDEV_TYPE_PES:
+ feed = list_first_entry(&filter->feed.ts,
+ struct dmxdev_feed, next);
+ feed->ts->oob_command(feed->ts, cmd);
+ break;
+ case DMXDEV_TYPE_NONE:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int dvb_dvr_feed_cmd(struct dmxdev *dmxdev, struct dvr_command *dvr_cmd)
+{
+ int ret = 0;
+ size_t todo;
+ int bytes_written = 0;
+ size_t split;
+ size_t tsp_size;
+ u8 *data_start;
+ struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+
+ todo = dvr_cmd->cmd.data_feed_count;
+
+ if (dmxdev->demux->get_tsp_size)
+ tsp_size = dmxdev->demux->get_tsp_size(dmxdev->demux);
+ else
+ tsp_size = 188;
+
+ while (todo >= tsp_size) {
+ /* wait for input */
+ ret = wait_event_interruptible(
+ src->queue,
+ (dvb_ringbuffer_avail(src) >= tsp_size) ||
+ dmxdev->dvr_in_exit || src->error);
+
+ if (ret < 0)
+ break;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ if (dmxdev->exit || dmxdev->dvr_in_exit) {
+ spin_unlock(&dmxdev->dvr_in_lock);
+ ret = -ENODEV;
+ break;
+ }
+
+ if (src->error) {
+ spin_unlock(&dmxdev->dvr_in_lock);
+ wake_up_all(&src->queue);
+ ret = -EINVAL;
+ break;
+ }
+
+ dmxdev->dvr_processing_input = 1;
+
+ split = (src->pread + todo > src->size) ?
+ src->size - src->pread : 0;
+
+ /*
+ * In DVR PULL mode, write might block.
+ * Lock on DVR buffer is released before calling to
+ * write, if DVR was released meanwhile, dvr_in_exit is
+ * prompted. Lock is acquired when updating the read pointer
+ * again to preserve read/write pointers consistency.
+ *
+ * In protected input mode, DVR input buffer is not mapped
+ * to kernel memory. Underlying demux implementation
+ * should trigger HW to read from DVR input buffer
+ * based on current read offset.
+ */
+ if (split > 0) {
+ data_start = (dmxdev->demux->dvr_input_protected) ?
+ NULL : (src->data + src->pread);
+
+ spin_unlock(&dmxdev->dvr_in_lock);
+ ret = dmxdev->demux->write(dmxdev->demux,
+ data_start,
+ split);
+
+ if (ret < 0) {
+ pr_err("dmxdev: dvr write error %d\n", ret);
+ continue;
+ }
+
+ if (dmxdev->dvr_in_exit) {
+ ret = -ENODEV;
+ break;
+ }
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ todo -= ret;
+ bytes_written += ret;
+ DVB_RINGBUFFER_SKIP(src, ret);
+ if (ret < split) {
+ dmxdev->dvr_processing_input = 0;
+ spin_unlock(&dmxdev->dvr_in_lock);
+ wake_up_all(&src->queue);
+ continue;
+ }
+ }
+
+ data_start = (dmxdev->demux->dvr_input_protected) ?
+ NULL : (src->data + src->pread);
+
+ spin_unlock(&dmxdev->dvr_in_lock);
+ ret = dmxdev->demux->write(dmxdev->demux,
+ data_start, todo);
+
+ if (ret < 0) {
+ pr_err("dmxdev: dvr write error %d\n", ret);
+ continue;
+ }
+
+ if (dmxdev->dvr_in_exit) {
+ ret = -ENODEV;
+ break;
+ }
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ todo -= ret;
+ bytes_written += ret;
+ DVB_RINGBUFFER_SKIP(src, ret);
+ dmxdev->dvr_processing_input = 0;
+ spin_unlock(&dmxdev->dvr_in_lock);
+
+ wake_up_all(&src->queue);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return bytes_written;
+}
+
+static int dvr_input_thread_entry(void *arg)
+{
+ struct dmxdev *dmxdev = arg;
+ struct dvb_ringbuffer *cmdbuf = &dmxdev->dvr_cmd_buffer;
+ struct dvr_command dvr_cmd;
+ int leftover = 0;
+ int ret;
+
+ while (1) {
+ /* wait for input */
+ ret = wait_event_interruptible(
+ cmdbuf->queue,
+ (!cmdbuf->data) ||
+ (dvb_ringbuffer_avail(cmdbuf) >= sizeof(dvr_cmd)) ||
+ (dmxdev->dvr_in_exit));
+
+ if (ret < 0)
+ break;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ if (!cmdbuf->data || dmxdev->exit || dmxdev->dvr_in_exit) {
+ spin_unlock(&dmxdev->dvr_in_lock);
+ break;
+ }
+
+ dvb_ringbuffer_read(cmdbuf, (u8 *)&dvr_cmd, sizeof(dvr_cmd));
+
+ spin_unlock(&dmxdev->dvr_in_lock);
+
+ if (dvr_cmd.type == DVR_DATA_FEED_CMD) {
+ dvr_cmd.cmd.data_feed_count += leftover;
+
+ ret = dvb_dvr_feed_cmd(dmxdev, &dvr_cmd);
+ if (ret < 0) {
+ pr_debug("%s: DVR data feed failed, ret=%d\n",
+ __func__, ret);
+ continue;
+ }
+
+ leftover = dvr_cmd.cmd.data_feed_count - ret;
+ } else {
+ /*
+ * For EOS, try to process leftover data in the input
+ * buffer.
+ */
+ if (dvr_cmd.cmd.oobcmd.type == DMX_OOB_CMD_EOS) {
+ struct dvr_command feed_cmd;
+
+ feed_cmd.type = DVR_DATA_FEED_CMD;
+ feed_cmd.cmd.data_feed_count =
+ dvb_ringbuffer_avail(
+ &dmxdev->dvr_input_buffer);
+ dvb_dvr_feed_cmd(dmxdev, &feed_cmd);
+ }
+
+ dvb_dvr_oob_cmd(dmxdev, &dvr_cmd.cmd.oobcmd);
+ }
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!kthread_should_stop()) {
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+
+ return 0;
+}
+
static int dvb_dvr_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
struct dmxdev *dmxdev = dvbdev->priv;
struct dmx_frontend *front;
+ void *mem;
- dprintk("function : %s\n", __func__);
+ pr_debug("function : %s(%X)\n", __func__, (file->f_flags & O_ACCMODE));
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
@@ -144,21 +835,28 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
}
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
- void *mem;
if (!dvbdev->readers) {
mutex_unlock(&dmxdev->mutex);
return -EBUSY;
}
- mem = vmalloc(DVR_BUFFER_SIZE);
+ mem = vmalloc_user(DVR_BUFFER_SIZE);
if (!mem) {
mutex_unlock(&dmxdev->mutex);
return -ENOMEM;
}
dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
- dvbdev->readers--;
- }
+ dvb_dmxdev_flush_events(&dmxdev->dvr_output_events);
+ dmxdev->dvr_output_events.event_mask.disable_mask = 0;
+ dmxdev->dvr_output_events.event_mask.no_wakeup_mask = 0;
+ dmxdev->dvr_output_events.event_mask.wakeup_threshold = 1;
+ dmxdev->dvr_feeds_count = 0;
+ dmxdev->dvr_buffer_mode = DMX_BUFFER_MODE_INTERNAL;
+ dmxdev->dvr_priv_buff_handle = NULL;
- if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+ dvbdev->readers--;
+ } else if (!dvbdev->writers) {
+ dmxdev->dvr_in_exit = 0;
+ dmxdev->dvr_processing_input = 0;
dmxdev->dvr_orig_fe = dmxdev->demux->frontend;
if (!dmxdev->demux->write) {
@@ -172,9 +870,51 @@ static int dvb_dvr_open(struct inode *inode, struct file *file)
mutex_unlock(&dmxdev->mutex);
return -EINVAL;
}
+
+ mem = vmalloc_user(DVR_BUFFER_SIZE);
+ if (!mem) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENOMEM;
+ }
+
dmxdev->demux->disconnect_frontend(dmxdev->demux);
dmxdev->demux->connect_frontend(dmxdev->demux, front);
+ dmxdev->dvr_input_buffer_mode = DMX_BUFFER_MODE_INTERNAL;
+
+ dvb_ringbuffer_init(&dmxdev->dvr_input_buffer,
+ mem,
+ DVR_BUFFER_SIZE);
+
+ dmxdev->demux->dvr_input.priv_handle = NULL;
+ dmxdev->demux->dvr_input.ringbuff = &dmxdev->dvr_input_buffer;
+ dmxdev->demux->dvr_input_protected = 0;
+ mem = vmalloc(DVR_CMDS_BUFFER_SIZE);
+ if (!mem) {
+ vfree(dmxdev->dvr_input_buffer.data);
+ dmxdev->dvr_input_buffer.data = NULL;
+ mutex_unlock(&dmxdev->mutex);
+ return -ENOMEM;
+ }
+ dvb_ringbuffer_init(&dmxdev->dvr_cmd_buffer, mem,
+ DVR_CMDS_BUFFER_SIZE);
+ dvbdev->writers--;
+
+ dmxdev->dvr_input_thread =
+ kthread_run(
+ dvr_input_thread_entry,
+ (void *)dmxdev,
+ "dvr_input");
+
+ if (IS_ERR(dmxdev->dvr_input_thread)) {
+ vfree(dmxdev->dvr_input_buffer.data);
+ vfree(dmxdev->dvr_cmd_buffer.data);
+ dmxdev->dvr_input_buffer.data = NULL;
+ dmxdev->dvr_cmd_buffer.data = NULL;
+ mutex_unlock(&dmxdev->mutex);
+ return -ENOMEM;
+ }
}
+
dvbdev->users++;
mutex_unlock(&dmxdev->mutex);
return 0;
@@ -187,11 +927,6 @@ static int dvb_dvr_release(struct inode *inode, struct file *file)
mutex_lock(&dmxdev->mutex);
- if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
- dmxdev->demux->disconnect_frontend(dmxdev->demux);
- dmxdev->demux->connect_frontend(dmxdev->demux,
- dmxdev->dvr_orig_fe);
- }
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
dvbdev->readers++;
if (dmxdev->dvr_buffer.data) {
@@ -200,12 +935,100 @@ static int dvb_dvr_release(struct inode *inode, struct file *file)
spin_lock_irq(&dmxdev->lock);
dmxdev->dvr_buffer.data = NULL;
spin_unlock_irq(&dmxdev->lock);
+ wake_up_all(&dmxdev->dvr_buffer.queue);
+
+ if (dmxdev->dvr_buffer_mode == DMX_BUFFER_MODE_INTERNAL)
+ vfree(mem);
+ }
+
+ if ((dmxdev->dvr_buffer_mode == DMX_BUFFER_MODE_EXTERNAL) &&
+ dmxdev->dvr_priv_buff_handle) {
+ dmxdev->demux->unmap_buffer(dmxdev->demux,
+ dmxdev->dvr_priv_buff_handle);
+ dmxdev->dvr_priv_buff_handle = NULL;
+ }
+ } else {
+ int i;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+ dmxdev->dvr_in_exit = 1;
+ spin_unlock(&dmxdev->dvr_in_lock);
+
+ wake_up_all(&dmxdev->dvr_cmd_buffer.queue);
+
+ /*
+ * There might be dmx filters reading now from DVR
+ * device, in PULL mode, they might be also stalled
+ * on output, signal to them that DVR is exiting.
+ */
+ if (dmxdev->playback_mode == DMX_PB_MODE_PULL) {
+ wake_up_all(&dmxdev->dvr_buffer.queue);
+
+ for (i = 0; i < dmxdev->filternum; i++)
+ if (dmxdev->filter[i].state == DMXDEV_STATE_GO)
+ wake_up_all(
+ &dmxdev->filter[i].buffer.queue);
+ }
+
+ /* notify kernel demux that we are canceling */
+ if (dmxdev->demux->write_cancel)
+ dmxdev->demux->write_cancel(dmxdev->demux);
+
+ /*
+ * Now stop dvr-input thread so that no one
+ * would process data from dvr input buffer any more
+ * before it gets freed.
+ */
+ kthread_stop(dmxdev->dvr_input_thread);
+
+ dvbdev->writers++;
+ dmxdev->demux->disconnect_frontend(dmxdev->demux);
+ dmxdev->demux->connect_frontend(dmxdev->demux,
+ dmxdev->dvr_orig_fe);
+
+ if (dmxdev->dvr_input_buffer.data) {
+ void *mem = dmxdev->dvr_input_buffer.data;
+ /*
+ * Ensure all the operations on the DVR input buffer
+ * are completed before it gets freed.
+ */
+ mb();
+ spin_lock_irq(&dmxdev->dvr_in_lock);
+ dmxdev->dvr_input_buffer.data = NULL;
+ spin_unlock_irq(&dmxdev->dvr_in_lock);
+
+ if (dmxdev->dvr_input_buffer_mode ==
+ DMX_BUFFER_MODE_INTERNAL)
+ vfree(mem);
+ }
+
+ if ((dmxdev->dvr_input_buffer_mode ==
+ DMX_BUFFER_MODE_EXTERNAL) &&
+ (dmxdev->demux->dvr_input.priv_handle)) {
+ if (!dmxdev->demux->dvr_input_protected)
+ dmxdev->demux->unmap_buffer(dmxdev->demux,
+ dmxdev->demux->dvr_input.priv_handle);
+ dmxdev->demux->dvr_input.priv_handle = NULL;
+ }
+
+ if (dmxdev->dvr_cmd_buffer.data) {
+ void *mem = dmxdev->dvr_cmd_buffer.data;
+ /*
+ * Ensure all the operations on the DVR command buffer
+ * are completed before it gets freed.
+ */
+ mb();
+ spin_lock_irq(&dmxdev->dvr_in_lock);
+ dmxdev->dvr_cmd_buffer.data = NULL;
+ spin_unlock_irq(&dmxdev->dvr_in_lock);
vfree(mem);
}
}
/* TODO */
dvbdev->users--;
if (dvbdev->users == 1 && dmxdev->exit == 1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
mutex_unlock(&dmxdev->mutex);
wake_up(&dvbdev->wait_queue);
} else
@@ -214,17 +1037,21 @@ static int dvb_dvr_release(struct inode *inode, struct file *file)
return 0;
}
-static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+
+static int dvb_dvr_mmap(struct file *filp, struct vm_area_struct *vma)
{
- struct dvb_device *dvbdev = file->private_data;
+ struct dvb_device *dvbdev = filp->private_data;
struct dmxdev *dmxdev = dvbdev->priv;
+ struct dvb_ringbuffer *buffer;
+ enum dmx_buffer_mode buffer_mode;
+ int vma_size;
+ int buffer_size;
int ret;
- if (!dmxdev->demux->write)
- return -EOPNOTSUPP;
- if ((file->f_flags & O_ACCMODE) != O_WRONLY)
+ if (((filp->f_flags & O_ACCMODE) == O_RDONLY) &&
+ (vma->vm_flags & VM_WRITE))
return -EINVAL;
+
if (mutex_lock_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
@@ -232,58 +1059,693 @@ static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
mutex_unlock(&dmxdev->mutex);
return -ENODEV;
}
- ret = dmxdev->demux->write(dmxdev->demux, buf, count);
+
+ if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
+ buffer = &dmxdev->dvr_buffer;
+ buffer_mode = dmxdev->dvr_buffer_mode;
+ } else {
+ buffer = &dmxdev->dvr_input_buffer;
+ buffer_mode = dmxdev->dvr_input_buffer_mode;
+ }
+
+ if (buffer_mode == DMX_BUFFER_MODE_EXTERNAL) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EINVAL;
+ }
+
+ vma_size = vma->vm_end - vma->vm_start;
+
+ /* Make sure requested mapping is not larger than buffer size */
+ buffer_size = buffer->size + (PAGE_SIZE-1);
+ buffer_size = buffer_size & ~(PAGE_SIZE-1);
+
+ if (vma_size != buffer_size) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EINVAL;
+ }
+
+ ret = remap_vmalloc_range(vma, buffer->data, 0);
+ if (ret) {
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+ }
+
+ vma->vm_flags |= VM_DONTDUMP;
+ vma->vm_flags |= VM_DONTEXPAND;
+
mutex_unlock(&dmxdev->mutex);
return ret;
}
+static void dvb_dvr_queue_data_feed(struct dmxdev *dmxdev, size_t count)
+{
+ struct dvb_ringbuffer *cmdbuf = &dmxdev->dvr_cmd_buffer;
+ struct dvr_command *dvr_cmd;
+ int last_dvr_cmd;
+
+ spin_lock(&dmxdev->dvr_in_lock);
+
+ /* Peek at the last DVR command queued, try to coalesce FEED commands */
+ if (dvb_ringbuffer_avail(cmdbuf) >= sizeof(*dvr_cmd)) {
+ last_dvr_cmd = cmdbuf->pwrite - sizeof(*dvr_cmd);
+ if (last_dvr_cmd < 0)
+ last_dvr_cmd += cmdbuf->size;
+
+ dvr_cmd = (struct dvr_command *)&cmdbuf->data[last_dvr_cmd];
+ if (dvr_cmd->type == DVR_DATA_FEED_CMD) {
+ dvr_cmd->cmd.data_feed_count += count;
+ spin_unlock(&dmxdev->dvr_in_lock);
+ return;
+ }
+ }
+
+ /*
+ * We assume command buffer is large enough so that overflow should not
+ * happen. Overflow to the command buffer means data previously written
+ * to the input buffer is 'orphan' - does not have a matching FEED
+ * command. Issue a warning if this ever happens.
+ * Orphan data might still be processed if EOS is issued.
+ */
+ if (dvb_ringbuffer_free(cmdbuf) < sizeof(*dvr_cmd)) {
+ pr_err("%s: DVR command buffer overflow\n", __func__);
+ spin_unlock(&dmxdev->dvr_in_lock);
+ return;
+ }
+
+ dvr_cmd = (struct dvr_command *)&cmdbuf->data[cmdbuf->pwrite];
+ dvr_cmd->type = DVR_DATA_FEED_CMD;
+ dvr_cmd->cmd.data_feed_count = count;
+ DVB_RINGBUFFER_PUSH(cmdbuf, sizeof(*dvr_cmd));
+ spin_unlock(&dmxdev->dvr_in_lock);
+
+ wake_up_all(&cmdbuf->queue);
+}
+
+static int dvb_dvr_external_input_only(struct dmxdev *dmxdev)
+{
+ struct dmx_caps caps;
+ int is_external_only;
+ int flags;
+ size_t tsp_size;
+
+ if (dmxdev->demux->get_tsp_size)
+ tsp_size = dmxdev->demux->get_tsp_size(dmxdev->demux);
+ else
+ tsp_size = 188;
+
+ /*
+ * For backward compatibility, default assumes that
+ * external only buffers are not supported.
+ */
+ flags = 0;
+ if (dmxdev->demux->get_caps) {
+ dmxdev->demux->get_caps(dmxdev->demux, &caps);
+
+ if (tsp_size == 188)
+ flags = caps.playback_188_tsp.flags;
+ else
+ flags = caps.playback_192_tsp.flags;
+ }
+
+ if (!(flags & DMX_BUFFER_INTERNAL_SUPPORT) &&
+ (flags & DMX_BUFFER_EXTERNAL_SUPPORT))
+ is_external_only = 1;
+ else
+ is_external_only = 0;
+
+ return is_external_only;
+}
+
+static int dvb_dvr_verify_buffer_size(struct dmxdev *dmxdev,
+ unsigned int f_flags,
+ unsigned long size)
+{
+ struct dmx_caps caps;
+ int tsp_size;
+
+ if (!dmxdev->demux->get_caps)
+ return 1;
+
+ if (dmxdev->demux->get_tsp_size)
+ tsp_size = dmxdev->demux->get_tsp_size(dmxdev->demux);
+ else
+ tsp_size = 188;
+
+ dmxdev->demux->get_caps(dmxdev->demux, &caps);
+ if ((f_flags & O_ACCMODE) == O_RDONLY)
+ return (tsp_size == 188 && dvb_dmxdev_verify_buffer_size(size,
+ caps.recording_188_tsp.max_size,
+ caps.recording_188_tsp.size_alignment)) ||
+ (tsp_size == 192 && dvb_dmxdev_verify_buffer_size(size,
+ caps.recording_192_tsp.max_size,
+ caps.recording_192_tsp.size_alignment));
+
+ return (tsp_size == 188 && dvb_dmxdev_verify_buffer_size(size,
+ caps.playback_188_tsp.max_size,
+ caps.playback_188_tsp.size_alignment)) ||
+ (tsp_size == 192 && dvb_dmxdev_verify_buffer_size(size,
+ caps.playback_192_tsp.max_size,
+ caps.playback_192_tsp.size_alignment));
+}
+
+static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
+ struct dvb_ringbuffer *cmdbuf = &dmxdev->dvr_cmd_buffer;
+ int ret;
+ size_t todo;
+ ssize_t free_space;
+
+ if (!dmxdev->demux->write)
+ return -EOPNOTSUPP;
+
+ if (!dvb_dvr_verify_buffer_size(dmxdev, file->f_flags, src->size) ||
+ ((file->f_flags & O_ACCMODE) == O_RDONLY) ||
+ !src->data || !cmdbuf->data ||
+ (dvb_dvr_external_input_only(dmxdev) &&
+ (dmxdev->dvr_input_buffer_mode == DMX_BUFFER_MODE_INTERNAL)))
+ return -EINVAL;
+
+ if ((file->f_flags & O_NONBLOCK) &&
+ (dvb_ringbuffer_free(src) == 0))
+ return -EWOULDBLOCK;
+
+ ret = 0;
+ for (todo = count; todo > 0; todo -= ret) {
+ ret = wait_event_interruptible(src->queue,
+ (dvb_ringbuffer_free(src)) ||
+ !src->data || !cmdbuf->data ||
+ (src->error != 0) || dmxdev->dvr_in_exit);
+
+ if (ret < 0)
+ return ret;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if ((!src->data) || (!cmdbuf->data)) {
+ mutex_unlock(&dmxdev->mutex);
+ return 0;
+ }
+
+ if (dmxdev->exit || dmxdev->dvr_in_exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+
+ if (src->error) {
+ ret = src->error;
+ dvb_ringbuffer_flush(src);
+ mutex_unlock(&dmxdev->mutex);
+ wake_up_all(&src->queue);
+ return ret;
+ }
+
+ free_space = dvb_ringbuffer_free(src);
+
+ if (free_space > todo)
+ free_space = todo;
+
+ ret = dvb_ringbuffer_write_user(src, buf, free_space);
+
+ if (ret < 0) {
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+ }
+
+ buf += ret;
+
+ dvb_dvr_queue_data_feed(dmxdev, ret);
+
+ mutex_unlock(&dmxdev->mutex);
+ }
+
+ return (count - todo) ? (count - todo) : ret;
+}
+
+static int dvb_dmxdev_flush_data(struct dmxdev_filter *filter, size_t length)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ struct dvb_ringbuffer *buffer = &filter->buffer;
+ struct dmxdev_events_queue *events = &filter->events;
+
+ if (filter->type == DMXDEV_TYPE_PES &&
+ filter->params.pes.output == DMX_OUT_TS_TAP) {
+ buffer = &filter->dev->dvr_buffer;
+ events = &filter->dev->dvr_output_events;
+ }
+
+ /*
+ * Drop 'length' pending data bytes from the ringbuffer and update
+ * event queue accordingly, similarly to dvb_dmxdev_release_data().
+ */
+ spin_lock_irqsave(&filter->dev->lock, flags);
+ DVB_RINGBUFFER_SKIP(buffer, length);
+ buffer->error = 0;
+ dvb_dmxdev_flush_events(events);
+ events->current_event_start_offset = buffer->pwrite;
+ spin_unlock_irqrestore(&filter->dev->lock, flags);
+
+ if (filter->type == DMXDEV_TYPE_PES) {
+ struct dmxdev_feed *feed;
+
+ feed = list_first_entry(&filter->feed.ts,
+ struct dmxdev_feed, next);
+
+ if (feed->ts->flush_buffer)
+ return feed->ts->flush_buffer(feed->ts, length);
+ } else if (filter->type == DMXDEV_TYPE_SEC &&
+ filter->feed.sec.feed->flush_buffer) {
+ return filter->feed.sec.feed->flush_buffer(
+ filter->feed.sec.feed, length);
+ }
+
+ return ret;
+}
+
+static inline void dvb_dmxdev_auto_flush_buffer(struct dmxdev_filter *filter,
+ struct dvb_ringbuffer *buf)
+{
+ size_t flush_len;
+
+ /*
+ * When buffer overflowed, demux-dev marked the buffer in
+ * error state. If auto-flush is enabled discard current
+ * pending data in buffer.
+ */
+ if (overflow_auto_flush) {
+ flush_len = dvb_ringbuffer_avail(buf);
+ dvb_dmxdev_flush_data(filter, flush_len);
+ }
+}
+
static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
+ ssize_t res;
struct dvb_device *dvbdev = file->private_data;
struct dmxdev *dmxdev = dvbdev->priv;
+ unsigned long flags;
if (dmxdev->exit)
return -ENODEV;
- return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
- file->f_flags & O_NONBLOCK,
- buf, count, ppos);
+ if (!dvb_dvr_verify_buffer_size(dmxdev, file->f_flags,
+ dmxdev->dvr_buffer.size))
+ return -EINVAL;
+
+ res = dvb_dmxdev_buffer_read(NULL, &dmxdev->dvr_buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
+
+ if (res > 0) {
+ dvb_dmxdev_notify_data_read(dmxdev->dvr_feed, res);
+ spin_lock_irqsave(&dmxdev->lock, flags);
+ dvb_dmxdev_update_events(&dmxdev->dvr_output_events, res);
+ spin_unlock_irqrestore(&dmxdev->lock, flags);
+
+ /*
+ * in PULL mode, we might be stalling on
+ * event queue, so need to wake-up waiters
+ */
+ if (dmxdev->playback_mode == DMX_PB_MODE_PULL)
+ wake_up_all(&dmxdev->dvr_buffer.queue);
+ } else if (res == -EOVERFLOW) {
+ dvb_dmxdev_auto_flush_buffer(dmxdev->dvr_feed,
+ &dmxdev->dvr_buffer);
+ }
+
+ return res;
+}
+
+/*
+ * dvb_dvr_push_oob_cmd
+ *
+ * Note: this function assume dmxdev->mutex was taken, so command buffer cannot
+ * be released during its operation.
+ */
+static int dvb_dvr_push_oob_cmd(struct dmxdev *dmxdev, unsigned int f_flags,
+ struct dmx_oob_command *cmd)
+{
+ struct dvb_ringbuffer *cmdbuf = &dmxdev->dvr_cmd_buffer;
+ struct dvr_command *dvr_cmd;
+
+ if ((f_flags & O_ACCMODE) == O_RDONLY ||
+ dmxdev->source < DMX_SOURCE_DVR0)
+ return -EPERM;
+
+ if (dvb_ringbuffer_free(cmdbuf) < sizeof(*dvr_cmd))
+ return -ENOMEM;
+
+ dvr_cmd = (struct dvr_command *)&cmdbuf->data[cmdbuf->pwrite];
+ dvr_cmd->type = DVR_OOB_CMD;
+ dvr_cmd->cmd.oobcmd = *cmd;
+ DVB_RINGBUFFER_PUSH(cmdbuf, sizeof(*dvr_cmd));
+ wake_up_all(&cmdbuf->queue);
+
+ return 0;
+}
+
+static int dvb_dvr_flush_buffer(struct dmxdev *dmxdev, unsigned int f_flags)
+{
+ size_t flush_len;
+ int ret;
+
+ if ((f_flags & O_ACCMODE) != O_RDONLY)
+ return -EINVAL;
+
+ flush_len = dvb_ringbuffer_avail(&dmxdev->dvr_buffer);
+ ret = dvb_dmxdev_flush_data(dmxdev->dvr_feed, flush_len);
+
+ return ret;
}
static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
- unsigned long size)
+ unsigned int f_flags,
+ unsigned long size)
{
- struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer;
+ struct dvb_ringbuffer *buf;
void *newmem;
void *oldmem;
-
- dprintk("function : %s\n", __func__);
+ spinlock_t *lock;
+ enum dmx_buffer_mode buffer_mode;
+
+ pr_debug("function : %s\n", __func__);
+
+ if ((f_flags & O_ACCMODE) == O_RDONLY) {
+ buf = &dmxdev->dvr_buffer;
+ lock = &dmxdev->lock;
+ buffer_mode = dmxdev->dvr_buffer_mode;
+ } else {
+ buf = &dmxdev->dvr_input_buffer;
+ lock = &dmxdev->dvr_in_lock;
+ buffer_mode = dmxdev->dvr_input_buffer_mode;
+ }
if (buf->size == size)
return 0;
- if (!size)
+ if (!size || (buffer_mode == DMX_BUFFER_MODE_EXTERNAL))
return -EINVAL;
- newmem = vmalloc(size);
+ newmem = vmalloc_user(size);
if (!newmem)
return -ENOMEM;
oldmem = buf->data;
- spin_lock_irq(&dmxdev->lock);
+ spin_lock_irq(lock);
+
+ if (((f_flags & O_ACCMODE) != O_RDONLY) &&
+ (dmxdev->dvr_processing_input)) {
+ spin_unlock_irq(lock);
+ vfree(oldmem);
+ return -EBUSY;
+ }
+
buf->data = newmem;
buf->size = size;
/* reset and not flush in case the buffer shrinks */
dvb_ringbuffer_reset(buf);
- spin_unlock_irq(&dmxdev->lock);
+
+ spin_unlock_irq(lock);
vfree(oldmem);
return 0;
}
+static int dvb_dvr_set_buffer_mode(struct dmxdev *dmxdev,
+ unsigned int f_flags, enum dmx_buffer_mode mode)
+{
+ struct dvb_ringbuffer *buf;
+ spinlock_t *lock;
+ enum dmx_buffer_mode *buffer_mode;
+ void **buff_handle;
+ void *oldmem;
+ int *is_protected;
+
+ if ((mode != DMX_BUFFER_MODE_INTERNAL) &&
+ (mode != DMX_BUFFER_MODE_EXTERNAL))
+ return -EINVAL;
+
+ if ((mode == DMX_BUFFER_MODE_EXTERNAL) &&
+ (!dmxdev->demux->map_buffer || !dmxdev->demux->unmap_buffer))
+ return -EINVAL;
+
+ if ((f_flags & O_ACCMODE) == O_RDONLY) {
+ buf = &dmxdev->dvr_buffer;
+ lock = &dmxdev->lock;
+ buffer_mode = &dmxdev->dvr_buffer_mode;
+ buff_handle = &dmxdev->dvr_priv_buff_handle;
+ is_protected = NULL;
+ } else {
+ buf = &dmxdev->dvr_input_buffer;
+ lock = &dmxdev->dvr_in_lock;
+ buffer_mode = &dmxdev->dvr_input_buffer_mode;
+ buff_handle = &dmxdev->demux->dvr_input.priv_handle;
+ is_protected = &dmxdev->demux->dvr_input_protected;
+ }
+
+ if (mode == *buffer_mode)
+ return 0;
+
+ oldmem = buf->data;
+ spin_lock_irq(lock);
+ buf->data = NULL;
+ spin_unlock_irq(lock);
+
+ *buffer_mode = mode;
+
+ if (mode == DMX_BUFFER_MODE_INTERNAL) {
+ /* switched from external to internal */
+ if (*buff_handle) {
+ dmxdev->demux->unmap_buffer(dmxdev->demux,
+ *buff_handle);
+ *buff_handle = NULL;
+ }
+
+ if (is_protected)
+ *is_protected = 0;
+
+ /* set default internal buffer */
+ dvb_dvr_set_buffer_size(dmxdev, f_flags, DVR_BUFFER_SIZE);
+ } else if (oldmem) {
+ /* switched from internal to external */
+ vfree(oldmem);
+ }
+
+ return 0;
+}
+
+static int dvb_dvr_set_buffer(struct dmxdev *dmxdev,
+ unsigned int f_flags, struct dmx_buffer *dmx_buffer)
+{
+ struct dvb_ringbuffer *buf;
+ spinlock_t *lock;
+ enum dmx_buffer_mode buffer_mode;
+ void **buff_handle;
+ void *newmem;
+ void *oldmem;
+ int *is_protected;
+ struct dmx_caps caps;
+
+ if (dmxdev->demux->get_caps)
+ dmxdev->demux->get_caps(dmxdev->demux, &caps);
+ else
+ caps.caps = 0;
+
+ if ((f_flags & O_ACCMODE) == O_RDONLY) {
+ buf = &dmxdev->dvr_buffer;
+ lock = &dmxdev->lock;
+ buffer_mode = dmxdev->dvr_buffer_mode;
+ buff_handle = &dmxdev->dvr_priv_buff_handle;
+ is_protected = NULL;
+ } else {
+ buf = &dmxdev->dvr_input_buffer;
+ lock = &dmxdev->dvr_in_lock;
+ buffer_mode = dmxdev->dvr_input_buffer_mode;
+ buff_handle = &dmxdev->demux->dvr_input.priv_handle;
+ is_protected = &dmxdev->demux->dvr_input_protected;
+ if (!(caps.caps & DMX_CAP_SECURED_INPUT_PLAYBACK) &&
+ dmx_buffer->is_protected)
+ return -EINVAL;
+ }
+
+ if (!dmx_buffer->size ||
+ (buffer_mode == DMX_BUFFER_MODE_INTERNAL))
+ return -EINVAL;
+
+ oldmem = *buff_handle;
+
+ /*
+ * Protected buffer is relevant only for DVR input buffer
+ * when DVR device is opened for write. In such case,
+ * buffer is mapped only if the buffer is not protected one.
+ */
+ if (!is_protected || !dmx_buffer->is_protected) {
+ if (dmxdev->demux->map_buffer(dmxdev->demux, dmx_buffer,
+ buff_handle, &newmem))
+ return -ENOMEM;
+ } else {
+ newmem = NULL;
+ *buff_handle = NULL;
+ }
+
+ spin_lock_irq(lock);
+ buf->data = newmem;
+ buf->size = dmx_buffer->size;
+ if (is_protected)
+ *is_protected = dmx_buffer->is_protected;
+ dvb_ringbuffer_reset(buf);
+ spin_unlock_irq(lock);
+
+ if (oldmem)
+ dmxdev->demux->unmap_buffer(dmxdev->demux, oldmem);
+
+ return 0;
+}
+
+static int dvb_dvr_get_event(struct dmxdev *dmxdev,
+ unsigned int f_flags,
+ struct dmx_filter_event *event)
+{
+ int res;
+
+ if (!((f_flags & O_ACCMODE) == O_RDONLY))
+ return -EINVAL;
+
+ spin_lock_irq(&dmxdev->lock);
+
+ if (dmxdev->dvr_buffer.error == -EOVERFLOW) {
+ event->type = DMX_EVENT_BUFFER_OVERFLOW;
+ dmxdev->dvr_buffer.error = 0;
+ } else {
+ res = dvb_dmxdev_remove_event(&dmxdev->dvr_output_events,
+ event);
+ if (res) {
+ spin_unlock_irq(&dmxdev->lock);
+ return res;
+ }
+ }
+
+ spin_unlock_irq(&dmxdev->lock);
+
+ if (event->type == DMX_EVENT_BUFFER_OVERFLOW)
+ dvb_dmxdev_auto_flush_buffer(dmxdev->dvr_feed,
+ &dmxdev->dvr_buffer);
+
+ /*
+ * in PULL mode, we might be stalling on
+ * event queue, so need to wake-up waiters
+ */
+ if (dmxdev->playback_mode == DMX_PB_MODE_PULL)
+ wake_up_all(&dmxdev->dvr_buffer.queue);
+
+ return res;
+}
+
+static int dvb_dvr_get_buffer_status(struct dmxdev *dmxdev,
+ unsigned int f_flags,
+ struct dmx_buffer_status *dmx_buffer_status)
+{
+ struct dvb_ringbuffer *buf;
+ spinlock_t *lock;
+
+ if ((f_flags & O_ACCMODE) == O_RDONLY) {
+ buf = &dmxdev->dvr_buffer;
+ lock = &dmxdev->lock;
+ } else {
+ buf = &dmxdev->dvr_input_buffer;
+ lock = &dmxdev->dvr_in_lock;
+ }
+
+ spin_lock_irq(lock);
+
+ dmx_buffer_status->error = buf->error;
+ dmx_buffer_status->fullness = dvb_ringbuffer_avail(buf);
+ dmx_buffer_status->free_bytes = dvb_ringbuffer_free(buf);
+ dmx_buffer_status->read_offset = buf->pread;
+ dmx_buffer_status->write_offset = buf->pwrite;
+ dmx_buffer_status->size = buf->size;
+ buf->error = 0;
+
+ spin_unlock_irq(lock);
+
+ if (dmx_buffer_status->error == -EOVERFLOW)
+ dvb_dmxdev_auto_flush_buffer(dmxdev->dvr_feed, buf);
+
+ return 0;
+}
+
+static int dvb_dvr_release_data(struct dmxdev *dmxdev,
+ unsigned int f_flags,
+ u32 bytes_count)
+{
+ ssize_t buff_fullness;
+
+ if (!((f_flags & O_ACCMODE) == O_RDONLY))
+ return -EINVAL;
+
+ if (!bytes_count)
+ return 0;
+
+ buff_fullness = dvb_ringbuffer_avail(&dmxdev->dvr_buffer);
+
+ if (bytes_count > buff_fullness)
+ return -EINVAL;
+
+ DVB_RINGBUFFER_SKIP(&dmxdev->dvr_buffer, bytes_count);
+
+ dvb_dmxdev_notify_data_read(dmxdev->dvr_feed, bytes_count);
+ spin_lock_irq(&dmxdev->lock);
+ dvb_dmxdev_update_events(&dmxdev->dvr_output_events, bytes_count);
+ spin_unlock_irq(&dmxdev->lock);
+
+ wake_up_all(&dmxdev->dvr_buffer.queue);
+ return 0;
+}
+
+/*
+ * dvb_dvr_feed_data - Notify new data in DVR input buffer
+ *
+ * @dmxdev - demux device instance
+ * @f_flags - demux device file flag (access mode)
+ * @bytes_count - how many bytes were written to the input buffer
+ *
+ * Note: this function assume dmxdev->mutex was taken, so buffer cannot
+ * be released during its operation.
+ */
+static int dvb_dvr_feed_data(struct dmxdev *dmxdev,
+ unsigned int f_flags,
+ u32 bytes_count)
+{
+ ssize_t free_space;
+ struct dvb_ringbuffer *buffer = &dmxdev->dvr_input_buffer;
+
+ if ((f_flags & O_ACCMODE) == O_RDONLY)
+ return -EINVAL;
+
+ if (!bytes_count)
+ return 0;
+
+ free_space = dvb_ringbuffer_free(buffer);
+
+ if (bytes_count > free_space)
+ return -EINVAL;
+
+ DVB_RINGBUFFER_PUSH(buffer, bytes_count);
+
+ dvb_dvr_queue_data_feed(dmxdev, bytes_count);
+
+ return 0;
+}
+
static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter
*dmxdevfilter, int state)
{
@@ -301,12 +1763,13 @@ static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter,
if (buf->size == size)
return 0;
- if (!size)
+ if (!size ||
+ (dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL))
return -EINVAL;
if (dmxdevfilter->state >= DMXDEV_STATE_GO)
return -EBUSY;
- newmem = vmalloc(size);
+ newmem = vmalloc_user(size);
if (!newmem)
return -ENOMEM;
@@ -325,15 +1788,803 @@ static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter,
return 0;
}
+static int dvb_dmxdev_set_buffer_mode(struct dmxdev_filter *dmxdevfilter,
+ enum dmx_buffer_mode mode)
+{
+ struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+ void *oldmem;
+
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ if ((mode != DMX_BUFFER_MODE_INTERNAL) &&
+ (mode != DMX_BUFFER_MODE_EXTERNAL))
+ return -EINVAL;
+
+ if ((mode == DMX_BUFFER_MODE_EXTERNAL) &&
+ (!dmxdev->demux->map_buffer || !dmxdev->demux->unmap_buffer))
+ return -EINVAL;
+
+ if (mode == dmxdevfilter->buffer_mode)
+ return 0;
+
+ oldmem = buf->data;
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ buf->data = NULL;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ dmxdevfilter->buffer_mode = mode;
+
+ if (mode == DMX_BUFFER_MODE_INTERNAL) {
+ /* switched from external to internal */
+ if (dmxdevfilter->priv_buff_handle) {
+ dmxdev->demux->unmap_buffer(dmxdev->demux,
+ dmxdevfilter->priv_buff_handle);
+ dmxdevfilter->priv_buff_handle = NULL;
+ }
+ } else if (oldmem) {
+ /* switched from internal to external */
+ vfree(oldmem);
+ }
+
+ return 0;
+}
+
+static int dvb_dmxdev_set_buffer(struct dmxdev_filter *dmxdevfilter,
+ struct dmx_buffer *buffer)
+{
+ struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+ void *newmem;
+ void *oldmem;
+
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ if ((!buffer->size) ||
+ (dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_INTERNAL))
+ return -EINVAL;
+
+ oldmem = dmxdevfilter->priv_buff_handle;
+ if (dmxdev->demux->map_buffer(dmxdev->demux, buffer,
+ &dmxdevfilter->priv_buff_handle, &newmem))
+ return -ENOMEM;
+
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ buf->data = newmem;
+ buf->size = buffer->size;
+ dvb_ringbuffer_reset(buf);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ if (oldmem)
+ dmxdev->demux->unmap_buffer(dmxdev->demux, oldmem);
+
+ return 0;
+}
+
+static int dvb_dmxdev_set_tsp_out_format(struct dmxdev_filter *dmxdevfilter,
+ enum dmx_tsp_format_t dmx_tsp_format)
+{
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ if ((dmx_tsp_format > DMX_TSP_FORMAT_192_HEAD) ||
+ (dmx_tsp_format < DMX_TSP_FORMAT_188))
+ return -EINVAL;
+
+ dmxdevfilter->dmx_tsp_format = dmx_tsp_format;
+
+ return 0;
+}
+
+static int dvb_dmxdev_set_decoder_buffer_size(
+ struct dmxdev_filter *dmxdevfilter,
+ unsigned long size)
+{
+ struct dmx_caps caps;
+ struct dmx_demux *demux = dmxdevfilter->dev->demux;
+
+ if (demux->get_caps) {
+ demux->get_caps(demux, &caps);
+ if (!dvb_dmxdev_verify_buffer_size(size, caps.decoder.max_size,
+ caps.decoder.size_alignment))
+ return -EINVAL;
+ }
+
+ if (size == 0)
+ return -EINVAL;
+
+ if (dmxdevfilter->decoder_buffers.buffers_size == size)
+ return 0;
+
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ /*
+ * In case decoder buffers were already set before to some external
+ * buffers, setting the decoder buffer size alone implies transition
+ * to internal buffer mode.
+ */
+ dmxdevfilter->decoder_buffers.buffers_size = size;
+ dmxdevfilter->decoder_buffers.buffers_num = 0;
+ dmxdevfilter->decoder_buffers.is_linear = 0;
+ return 0;
+}
+
+static int dvb_dmxdev_set_source(struct dmxdev_filter *dmxdevfilter,
+ dmx_source_t *source)
+{
+ int ret = 0;
+ struct dmxdev *dev;
+
+ if (dmxdevfilter->state == DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ dev = dmxdevfilter->dev;
+ if (dev->demux->set_source)
+ ret = dev->demux->set_source(dev->demux, source);
+
+ if (!ret)
+ dev->source = *source;
+
+ return ret;
+}
+
+static int dvb_dmxdev_reuse_decoder_buf(struct dmxdev_filter *dmxdevfilter,
+ int cookie)
+{
+ struct dmxdev_feed *feed;
+
+ if (dmxdevfilter->state != DMXDEV_STATE_GO ||
+ (dmxdevfilter->type != DMXDEV_TYPE_PES) ||
+ (dmxdevfilter->params.pes.output != DMX_OUT_DECODER) ||
+ (dmxdevfilter->events.event_mask.disable_mask &
+ DMX_EVENT_NEW_ES_DATA))
+ return -EPERM;
+
+ /* Only one feed should be in the list in case of decoder */
+ feed = list_first_entry(&dmxdevfilter->feed.ts,
+ struct dmxdev_feed, next);
+ if (feed && feed->ts && feed->ts->reuse_decoder_buffer)
+ return feed->ts->reuse_decoder_buffer(feed->ts, cookie);
+
+ return -ENODEV;
+}
+
+static int dvb_dmxdev_set_event_mask(struct dmxdev_filter *dmxdevfilter,
+ struct dmx_events_mask *event_mask)
+{
+ if (!event_mask ||
+ (event_mask->wakeup_threshold >= DMX_EVENT_QUEUE_SIZE))
+ return -EINVAL;
+
+ if (dmxdevfilter->state == DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ /*
+ * Overflow event is not allowed to be masked.
+ * This is because if overflow occurs, demux stops outputting data
+ * until user is notified. If user is using events to read the data,
+ * the overflow event must be always enabled or otherwise we would
+ * never recover from overflow state.
+ */
+ event_mask->disable_mask &= ~(u32)DMX_EVENT_BUFFER_OVERFLOW;
+ event_mask->no_wakeup_mask &= ~(u32)DMX_EVENT_BUFFER_OVERFLOW;
+
+ dmxdevfilter->events.event_mask = *event_mask;
+
+ return 0;
+}
+
+static int dvb_dmxdev_get_event_mask(struct dmxdev_filter *dmxdevfilter,
+ struct dmx_events_mask *event_mask)
+{
+ if (!event_mask)
+ return -EINVAL;
+
+ *event_mask = dmxdevfilter->events.event_mask;
+
+ return 0;
+}
+
+static int dvb_dmxdev_set_indexing_params(struct dmxdev_filter *dmxdevfilter,
+ struct dmx_indexing_params *idx_params)
+{
+ int found_pid;
+ struct dmxdev_feed *feed;
+ struct dmxdev_feed *ts_feed = NULL;
+ struct dmx_caps caps;
+ int ret = 0;
+
+ if (!dmxdevfilter->dev->demux->get_caps)
+ return -EINVAL;
+
+ dmxdevfilter->dev->demux->get_caps(dmxdevfilter->dev->demux, &caps);
+
+ if (!idx_params ||
+ !(caps.caps & DMX_CAP_VIDEO_INDEXING) ||
+ (dmxdevfilter->state < DMXDEV_STATE_SET) ||
+ (dmxdevfilter->type != DMXDEV_TYPE_PES) ||
+ ((dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) &&
+ (dmxdevfilter->params.pes.output != DMX_OUT_TSDEMUX_TAP)))
+ return -EINVAL;
+
+ if (idx_params->enable && !idx_params->types)
+ return -EINVAL;
+
+ found_pid = 0;
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
+ if (feed->pid == idx_params->pid) {
+ found_pid = 1;
+ ts_feed = feed;
+ ts_feed->idx_params = *idx_params;
+ if ((dmxdevfilter->state == DMXDEV_STATE_GO) &&
+ ts_feed->ts->set_idx_params)
+ ret = ts_feed->ts->set_idx_params(
+ ts_feed->ts, idx_params);
+ break;
+ }
+ }
+
+ if (!found_pid)
+ return -EINVAL;
+
+ return ret;
+}
+
+static int dvb_dmxdev_get_scrambling_bits(struct dmxdev_filter *filter,
+ struct dmx_scrambling_bits *scrambling_bits)
+{
+ struct dmxdev_feed *feed;
+
+ if (!scrambling_bits ||
+ (filter->state != DMXDEV_STATE_GO))
+ return -EINVAL;
+
+ if (filter->type == DMXDEV_TYPE_SEC) {
+ if (filter->feed.sec.feed->get_scrambling_bits)
+ return filter->feed.sec.feed->get_scrambling_bits(
+ filter->feed.sec.feed,
+ &scrambling_bits->value);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ if (feed->pid == scrambling_bits->pid) {
+ if (feed->ts->get_scrambling_bits)
+ return feed->ts->get_scrambling_bits(feed->ts,
+ &scrambling_bits->value);
+ return -EINVAL;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void dvb_dmxdev_ts_insertion_work(struct work_struct *worker)
+{
+ struct ts_insertion_buffer *ts_buffer =
+ container_of(to_delayed_work(worker),
+ struct ts_insertion_buffer, dwork);
+ struct dmxdev_feed *feed;
+ size_t free_bytes;
+ struct dmx_ts_feed *ts;
+
+ mutex_lock(&ts_buffer->dmxdevfilter->mutex);
+
+ if (ts_buffer->abort ||
+ (ts_buffer->dmxdevfilter->state != DMXDEV_STATE_GO)) {
+ mutex_unlock(&ts_buffer->dmxdevfilter->mutex);
+ return;
+ }
+
+ feed = list_first_entry(&ts_buffer->dmxdevfilter->feed.ts,
+ struct dmxdev_feed, next);
+ ts = feed->ts;
+ free_bytes = dvb_ringbuffer_free(&ts_buffer->dmxdevfilter->buffer);
+
+ mutex_unlock(&ts_buffer->dmxdevfilter->mutex);
+
+ if (ts_buffer->size < free_bytes)
+ ts->ts_insertion_insert_buffer(ts,
+ ts_buffer->buffer, ts_buffer->size);
+
+ if (ts_buffer->repetition_time && !ts_buffer->abort)
+ schedule_delayed_work(&ts_buffer->dwork,
+ msecs_to_jiffies(ts_buffer->repetition_time));
+}
+
+static void dvb_dmxdev_queue_ts_insertion(
+ struct ts_insertion_buffer *ts_buffer)
+{
+ size_t tsp_size;
+
+ if (ts_buffer->dmxdevfilter->dmx_tsp_format == DMX_TSP_FORMAT_188)
+ tsp_size = 188;
+ else
+ tsp_size = 192;
+
+ if (ts_buffer->size % tsp_size) {
+ pr_err("%s: Wrong buffer alignment, size=%zu, tsp_size=%zu\n",
+ __func__, ts_buffer->size, tsp_size);
+ return;
+ }
+
+ ts_buffer->abort = 0;
+ schedule_delayed_work(&ts_buffer->dwork, 0);
+}
+
+static void dvb_dmxdev_cancel_ts_insertion(
+ struct ts_insertion_buffer *ts_buffer)
+{
+ /*
+ * This function assumes it is called while mutex
+ * of demux filter is taken. Since work in workqueue
+ * captures the filter's mutex to protect against the DB,
+ * mutex needs to be released before waiting for the work
+ * to get finished otherwise work in workqueue will
+ * never be finished.
+ */
+ if (!mutex_is_locked(&ts_buffer->dmxdevfilter->mutex)) {
+ pr_err("%s: mutex is not locked!\n", __func__);
+ return;
+ }
+
+ ts_buffer->abort = 1;
+
+ mutex_unlock(&ts_buffer->dmxdevfilter->mutex);
+ cancel_delayed_work_sync(&ts_buffer->dwork);
+ mutex_lock(&ts_buffer->dmxdevfilter->mutex);
+}
+
+static int dvb_dmxdev_set_ts_insertion(struct dmxdev_filter *dmxdevfilter,
+ struct dmx_set_ts_insertion *params)
+{
+ int ret = 0;
+ int first_buffer;
+ struct dmxdev_feed *feed;
+ struct ts_insertion_buffer *ts_buffer;
+ struct dmx_caps caps;
+
+ if (!dmxdevfilter->dev->demux->get_caps)
+ return -EINVAL;
+
+ dmxdevfilter->dev->demux->get_caps(dmxdevfilter->dev->demux, &caps);
+
+ if (!params ||
+ !params->size ||
+ !(caps.caps & DMX_CAP_TS_INSERTION) ||
+ (dmxdevfilter->state < DMXDEV_STATE_SET) ||
+ (dmxdevfilter->type != DMXDEV_TYPE_PES) ||
+ ((dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) &&
+ (dmxdevfilter->params.pes.output != DMX_OUT_TSDEMUX_TAP)))
+ return -EINVAL;
+
+ ts_buffer = vmalloc(sizeof(struct ts_insertion_buffer));
+ if (!ts_buffer)
+ return -ENOMEM;
+
+ ts_buffer->buffer = vmalloc(params->size);
+ if (!ts_buffer->buffer) {
+ vfree(ts_buffer);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(ts_buffer->buffer,
+ params->ts_packets, params->size)) {
+ vfree(ts_buffer->buffer);
+ vfree(ts_buffer);
+ return -EFAULT;
+ }
+
+ if (params->repetition_time &&
+ params->repetition_time < DMX_MIN_INSERTION_REPETITION_TIME)
+ params->repetition_time = DMX_MIN_INSERTION_REPETITION_TIME;
+
+ ts_buffer->size = params->size;
+ ts_buffer->identifier = params->identifier;
+ ts_buffer->repetition_time = params->repetition_time;
+ ts_buffer->dmxdevfilter = dmxdevfilter;
+ INIT_DELAYED_WORK(&ts_buffer->dwork, dvb_dmxdev_ts_insertion_work);
+
+ first_buffer = list_empty(&dmxdevfilter->insertion_buffers);
+ list_add_tail(&ts_buffer->next, &dmxdevfilter->insertion_buffers);
+
+ if (dmxdevfilter->state != DMXDEV_STATE_GO)
+ return 0;
+
+ feed = list_first_entry(&dmxdevfilter->feed.ts,
+ struct dmxdev_feed, next);
+
+ if (first_buffer && feed->ts->ts_insertion_init)
+ ret = feed->ts->ts_insertion_init(feed->ts);
+
+ if (!ret) {
+ dvb_dmxdev_queue_ts_insertion(ts_buffer);
+ } else {
+ list_del(&ts_buffer->next);
+ vfree(ts_buffer->buffer);
+ vfree(ts_buffer);
+ }
+
+ return ret;
+}
+
+static int dvb_dmxdev_abort_ts_insertion(struct dmxdev_filter *dmxdevfilter,
+ struct dmx_abort_ts_insertion *params)
+{
+ int ret = 0;
+ int found_buffer;
+ struct dmxdev_feed *feed;
+ struct ts_insertion_buffer *ts_buffer, *tmp;
+ struct dmx_caps caps;
+
+ if (!dmxdevfilter->dev->demux->get_caps)
+ return -EINVAL;
+
+ dmxdevfilter->dev->demux->get_caps(dmxdevfilter->dev->demux, &caps);
+
+ if (!params ||
+ !(caps.caps & DMX_CAP_TS_INSERTION) ||
+ (dmxdevfilter->state < DMXDEV_STATE_SET) ||
+ (dmxdevfilter->type != DMXDEV_TYPE_PES) ||
+ ((dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) &&
+ (dmxdevfilter->params.pes.output != DMX_OUT_TSDEMUX_TAP)))
+ return -EINVAL;
+
+ found_buffer = 0;
+ list_for_each_entry_safe(ts_buffer, tmp,
+ &dmxdevfilter->insertion_buffers, next) {
+ if (ts_buffer->identifier == params->identifier) {
+ list_del(&ts_buffer->next);
+ found_buffer = 1;
+ break;
+ }
+ }
+
+ if (!found_buffer)
+ return -EINVAL;
+
+ if (dmxdevfilter->state == DMXDEV_STATE_GO) {
+ dvb_dmxdev_cancel_ts_insertion(ts_buffer);
+ if (list_empty(&dmxdevfilter->insertion_buffers)) {
+ feed = list_first_entry(&dmxdevfilter->feed.ts,
+ struct dmxdev_feed, next);
+ if (feed->ts->ts_insertion_terminate)
+ ret = feed->ts->ts_insertion_terminate(
+ feed->ts);
+ }
+ }
+
+ vfree(ts_buffer->buffer);
+ vfree(ts_buffer);
+
+ return ret;
+}
+
+static int dvb_dmxdev_ts_fullness_callback(struct dmx_ts_feed *filter,
+ int required_space, int wait)
+{
+ struct dmxdev_filter *dmxdevfilter = filter->priv;
+ struct dvb_ringbuffer *src;
+ struct dmxdev_events_queue *events;
+ int ret;
+
+ if (!dmxdevfilter) {
+ pr_err("%s: NULL demux filter object!\n", __func__);
+ return -ENODEV;
+ }
+
+ if (dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) {
+ src = &dmxdevfilter->buffer;
+ events = &dmxdevfilter->events;
+ } else {
+ src = &dmxdevfilter->dev->dvr_buffer;
+ events = &dmxdevfilter->dev->dvr_output_events;
+ }
+
+ do {
+ ret = 0;
+
+ if (dmxdevfilter->dev->dvr_in_exit)
+ return -ENODEV;
+
+ spin_lock(&dmxdevfilter->dev->lock);
+
+ if ((!src->data) ||
+ (dmxdevfilter->state != DMXDEV_STATE_GO))
+ ret = -EINVAL;
+ else if (src->error)
+ ret = src->error;
+
+ if (ret) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return ret;
+ }
+
+ if ((required_space <= dvb_ringbuffer_free(src)) &&
+ (!dvb_dmxdev_events_is_full(events))) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+
+ spin_unlock(&dmxdevfilter->dev->lock);
+
+ if (!wait)
+ return -ENOSPC;
+
+ ret = wait_event_interruptible(src->queue,
+ (!src->data) ||
+ ((dvb_ringbuffer_free(src) >= required_space) &&
+ (!dvb_dmxdev_events_is_full(events))) ||
+ (src->error != 0) ||
+ (dmxdevfilter->state != DMXDEV_STATE_GO) ||
+ dmxdevfilter->dev->dvr_in_exit);
+
+ if (ret < 0)
+ return ret;
+ } while (1);
+}
+
+static int dvb_dmxdev_sec_fullness_callback(
+ struct dmx_section_filter *filter,
+ int required_space, int wait)
+{
+ struct dmxdev_filter *dmxdevfilter = filter->priv;
+ struct dvb_ringbuffer *src;
+ struct dmxdev_events_queue *events;
+ int ret;
+
+ if (!dmxdevfilter) {
+ pr_err("%s: NULL demux filter object!\n", __func__);
+ return -ENODEV;
+ }
+
+ src = &dmxdevfilter->buffer;
+ events = &dmxdevfilter->events;
+
+ do {
+ ret = 0;
+
+ if (dmxdevfilter->dev->dvr_in_exit)
+ return -ENODEV;
+
+ spin_lock(&dmxdevfilter->dev->lock);
+
+ if ((!src->data) ||
+ (dmxdevfilter->state != DMXDEV_STATE_GO))
+ ret = -EINVAL;
+ else if (src->error)
+ ret = src->error;
+
+ if (ret) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return ret;
+ }
+
+ if ((required_space <= dvb_ringbuffer_free(src)) &&
+ (!dvb_dmxdev_events_is_full(events))) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+
+ spin_unlock(&dmxdevfilter->dev->lock);
+
+ if (!wait)
+ return -ENOSPC;
+
+ ret = wait_event_interruptible(src->queue,
+ (!src->data) ||
+ ((dvb_ringbuffer_free(src) >= required_space) &&
+ (!dvb_dmxdev_events_is_full(events))) ||
+ (src->error != 0) ||
+ (dmxdevfilter->state != DMXDEV_STATE_GO) ||
+ dmxdevfilter->dev->dvr_in_exit);
+
+ if (ret < 0)
+ return ret;
+ } while (1);
+}
+
+static int dvb_dmxdev_set_playback_mode(struct dmxdev_filter *dmxdevfilter,
+ enum dmx_playback_mode_t playback_mode)
+{
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+ struct dmx_caps caps;
+
+ if (dmxdev->demux->get_caps)
+ dmxdev->demux->get_caps(dmxdev->demux, &caps);
+ else
+ caps.caps = 0;
+
+ if ((playback_mode != DMX_PB_MODE_PUSH) &&
+ (playback_mode != DMX_PB_MODE_PULL))
+ return -EINVAL;
+
+ if (((dmxdev->source < DMX_SOURCE_DVR0) ||
+ !dmxdev->demux->set_playback_mode ||
+ !(caps.caps & DMX_CAP_PULL_MODE)) &&
+ (playback_mode == DMX_PB_MODE_PULL))
+ return -EPERM;
+
+ if (dmxdevfilter->state == DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ dmxdev->playback_mode = playback_mode;
+
+ return dmxdev->demux->set_playback_mode(
+ dmxdev->demux,
+ dmxdev->playback_mode,
+ dvb_dmxdev_ts_fullness_callback,
+ dvb_dmxdev_sec_fullness_callback);
+}
+
+static int dvb_dmxdev_flush_buffer(struct dmxdev_filter *filter)
+{
+ size_t flush_len;
+ int ret;
+
+ if (filter->state != DMXDEV_STATE_GO)
+ return -EINVAL;
+
+ flush_len = dvb_ringbuffer_avail(&filter->buffer);
+ ret = dvb_dmxdev_flush_data(filter, flush_len);
+
+ return ret;
+}
+
+static int dvb_dmxdev_get_buffer_status(
+ struct dmxdev_filter *dmxdevfilter,
+ struct dmx_buffer_status *dmx_buffer_status)
+{
+ struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+
+ /*
+ * Note: Taking the dmxdevfilter->dev->lock spinlock is required only
+ * when getting the status of the Demux-userspace data ringbuffer .
+ * In case we are getting the status of a decoder buffer, taking this
+ * spinlock is not required and in fact might lead to a deadlock.
+ */
+ if ((dmxdevfilter->type == DMXDEV_TYPE_PES) &&
+ (dmxdevfilter->params.pes.output == DMX_OUT_DECODER)) {
+ struct dmxdev_feed *feed;
+ int ret;
+
+ /* Only one feed should be in the list in case of decoder */
+ feed = list_first_entry(&dmxdevfilter->feed.ts,
+ struct dmxdev_feed, next);
+
+ /* Ask for status of decoder's buffer from underlying HW */
+ if (feed->ts->get_decoder_buff_status)
+ ret = feed->ts->get_decoder_buff_status(
+ feed->ts,
+ dmx_buffer_status);
+ else
+ ret = -ENODEV;
+
+ return ret;
+ }
+
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+
+ if (!buf->data) {
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+ return -EINVAL;
+ }
+
+ dmx_buffer_status->error = buf->error;
+ dmx_buffer_status->fullness = dvb_ringbuffer_avail(buf);
+ dmx_buffer_status->free_bytes = dvb_ringbuffer_free(buf);
+ dmx_buffer_status->read_offset = buf->pread;
+ dmx_buffer_status->write_offset = buf->pwrite;
+ dmx_buffer_status->size = buf->size;
+ buf->error = 0;
+
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ if (dmx_buffer_status->error == -EOVERFLOW)
+ dvb_dmxdev_auto_flush_buffer(dmxdevfilter, buf);
+
+ return 0;
+}
+
+static int dvb_dmxdev_release_data(struct dmxdev_filter *dmxdevfilter,
+ u32 bytes_count)
+{
+ ssize_t buff_fullness;
+
+ if (!dmxdevfilter->buffer.data)
+ return -EINVAL;
+
+ if (!bytes_count)
+ return 0;
+
+ buff_fullness = dvb_ringbuffer_avail(&dmxdevfilter->buffer);
+
+ if (bytes_count > buff_fullness)
+ return -EINVAL;
+
+ DVB_RINGBUFFER_SKIP(&dmxdevfilter->buffer, bytes_count);
+
+ dvb_dmxdev_notify_data_read(dmxdevfilter, bytes_count);
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dvb_dmxdev_update_events(&dmxdevfilter->events, bytes_count);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ wake_up_all(&dmxdevfilter->buffer.queue);
+
+ return 0;
+}
+
+static int dvb_dmxdev_get_event(struct dmxdev_filter *dmxdevfilter,
+ struct dmx_filter_event *event)
+{
+ int res;
+
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+
+ /* Check first for filter overflow */
+ if (dmxdevfilter->buffer.error == -EOVERFLOW) {
+ event->type = DMX_EVENT_BUFFER_OVERFLOW;
+ } else {
+ res = dvb_dmxdev_remove_event(&dmxdevfilter->events, event);
+ if (res) {
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+ return res;
+ }
+ }
+
+ /* clear buffer error now that user was notified */
+ if (event->type == DMX_EVENT_BUFFER_OVERFLOW ||
+ event->type == DMX_EVENT_SECTION_TIMEOUT)
+ dmxdevfilter->buffer.error = 0;
+
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ if (event->type == DMX_EVENT_BUFFER_OVERFLOW)
+ dvb_dmxdev_auto_flush_buffer(dmxdevfilter,
+ &dmxdevfilter->buffer);
+
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+
+ /*
+ * If no-data events are enabled on this filter,
+ * the events can be removed from the queue when
+ * user gets them.
+ * For filters with data events enabled, the event is removed
+ * from the queue only when the respective data is read.
+ */
+ if (event->type != DMX_EVENT_BUFFER_OVERFLOW &&
+ dmxdevfilter->events.data_read_event_masked)
+ dmxdevfilter->events.read_index =
+ dvb_dmxdev_advance_event_idx(
+ dmxdevfilter->events.read_index);
+
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ /*
+ * in PULL mode, we might be stalling on
+ * event queue, so need to wake-up waiters
+ */
+ if (dmxdevfilter->dev->playback_mode == DMX_PB_MODE_PULL)
+ wake_up_all(&dmxdevfilter->buffer.queue);
+
+ return res;
+}
+
static void dvb_dmxdev_filter_timeout(unsigned long data)
{
struct dmxdev_filter *dmxdevfilter = (struct dmxdev_filter *)data;
+ struct dmx_filter_event event;
dmxdevfilter->buffer.error = -ETIMEDOUT;
spin_lock_irq(&dmxdevfilter->dev->lock);
dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT;
+ event.type = DMX_EVENT_SECTION_TIMEOUT;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
spin_unlock_irq(&dmxdevfilter->dev->lock);
- wake_up(&dmxdevfilter->buffer.queue);
+ wake_up_all(&dmxdevfilter->buffer.queue);
}
static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
@@ -352,68 +2603,519 @@ static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
- struct dmx_section_filter *filter)
+ struct dmx_section_filter *filter,
+ enum dmx_success success)
{
struct dmxdev_filter *dmxdevfilter = filter->priv;
- int ret;
+ struct dmx_filter_event event;
+ ssize_t free;
- if (dmxdevfilter->buffer.error) {
- wake_up(&dmxdevfilter->buffer.queue);
- return 0;
+ if (!dmxdevfilter) {
+ pr_err("%s: null filter. status=%d\n", __func__, success);
+ return -EINVAL;
}
+
spin_lock(&dmxdevfilter->dev->lock);
- if (dmxdevfilter->state != DMXDEV_STATE_GO) {
+
+ if (dmxdevfilter->buffer.error ||
+ dmxdevfilter->state != DMXDEV_STATE_GO ||
+ dmxdevfilter->eos_state) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+
+ /* Discard section data if event cannot be notified */
+ if (!(dmxdevfilter->events.event_mask.disable_mask &
+ DMX_EVENT_NEW_SECTION) &&
+ dvb_dmxdev_events_is_full(&dmxdevfilter->events)) {
spin_unlock(&dmxdevfilter->dev->lock);
return 0;
}
+
+ if ((buffer1_len + buffer2_len) == 0) {
+ if (success == DMX_CRC_ERROR) {
+ /* Section was dropped due to CRC error */
+ event.type = DMX_EVENT_SECTION_CRC_ERROR;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ } else {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ }
+
+ return 0;
+ }
+
+ event.params.section.base_offset = dmxdevfilter->buffer.pwrite;
+ event.params.section.start_offset = dmxdevfilter->buffer.pwrite;
+
del_timer(&dmxdevfilter->timer);
- dprintk("dmxdev: section callback %*ph\n", 6, buffer1);
- ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1,
- buffer1_len);
- if (ret == buffer1_len) {
- ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2,
- buffer2_len);
+
+ /* Verify output buffer has sufficient space, or report overflow */
+ free = dvb_ringbuffer_free(&dmxdevfilter->buffer);
+ if (free < (buffer1_len + buffer2_len)) {
+ pr_debug("%s: section filter overflow (pid=%u)\n",
+ __func__, dmxdevfilter->params.sec.pid);
+ dmxdevfilter->buffer.error = -EOVERFLOW;
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ return 0;
}
- if (ret < 0)
- dmxdevfilter->buffer.error = ret;
+
+ dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1, buffer1_len);
+ dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2, buffer2_len);
+
+ event.type = DMX_EVENT_NEW_SECTION;
+ event.params.section.total_length = buffer1_len + buffer2_len;
+ event.params.section.actual_length =
+ event.params.section.total_length;
+
+ if (success == DMX_MISSED_ERROR)
+ event.params.section.flags = DMX_FILTER_CC_ERROR;
+ else
+ event.params.section.flags = 0;
+
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+
if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
dmxdevfilter->state = DMXDEV_STATE_DONE;
spin_unlock(&dmxdevfilter->dev->lock);
- wake_up(&dmxdevfilter->buffer.queue);
+ wake_up_all(&dmxdevfilter->buffer.queue);
return 0;
}
static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
- struct dmx_ts_feed *feed)
+ struct dmx_ts_feed *feed,
+ enum dmx_success success)
{
struct dmxdev_filter *dmxdevfilter = feed->priv;
struct dvb_ringbuffer *buffer;
- int ret;
+ struct dmxdev_events_queue *events;
+ struct dmx_filter_event event;
+ ssize_t free;
+
+ if (!dmxdevfilter) {
+ pr_err("%s: null filter (feed->is_filtering=%d) status=%d\n",
+ __func__, feed->is_filtering, success);
+ return -EINVAL;
+ }
spin_lock(&dmxdevfilter->dev->lock);
- if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
+
+ if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER ||
+ dmxdevfilter->state != DMXDEV_STATE_GO ||
+ dmxdevfilter->eos_state) {
spin_unlock(&dmxdevfilter->dev->lock);
return 0;
}
- if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
- || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+ if (dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) {
buffer = &dmxdevfilter->buffer;
- else
+ events = &dmxdevfilter->events;
+ } else {
buffer = &dmxdevfilter->dev->dvr_buffer;
+ events = &dmxdevfilter->dev->dvr_output_events;
+ }
+
if (buffer->error) {
spin_unlock(&dmxdevfilter->dev->lock);
- wake_up(&buffer->queue);
+ wake_up_all(&buffer->queue);
+ return buffer->error;
+ }
+
+ if (dmxdevfilter->params.pes.output == DMX_OUT_TAP) {
+ if (success == DMX_OK && !events->current_event_data_size) {
+ events->current_event_start_offset = buffer->pwrite;
+ } else if (success == DMX_OK_PES_END) {
+ event.type = DMX_EVENT_NEW_PES;
+
+ event.params.pes.actual_length =
+ events->current_event_data_size;
+ event.params.pes.total_length =
+ events->current_event_data_size;
+
+ event.params.pes.base_offset =
+ events->current_event_start_offset;
+ event.params.pes.start_offset =
+ events->current_event_start_offset;
+
+ event.params.pes.flags = 0;
+ event.params.pes.stc = 0;
+ event.params.pes.transport_error_indicator_counter = 0;
+ event.params.pes.continuity_error_counter = 0;
+ event.params.pes.ts_packets_num = 0;
+
+ /* Do not report zero length PES */
+ if (event.params.pes.total_length)
+ dvb_dmxdev_add_event(events, &event);
+ events->current_event_data_size = 0;
+ }
+ } else if (!events->current_event_data_size) {
+ events->current_event_start_offset = buffer->pwrite;
+ }
+
+ /* Verify output buffer has sufficient space, or report overflow */
+ free = dvb_ringbuffer_free(buffer);
+ if (free < (buffer1_len + buffer2_len)) {
+ pr_debug("%s: buffer overflow error, pid=%u\n",
+ __func__, dmxdevfilter->params.pes.pid);
+ buffer->error = -EOVERFLOW;
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+
+ return -EOVERFLOW;
+ }
+
+ if (buffer1_len + buffer2_len) {
+ dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
+ dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
+
+ events->current_event_data_size += (buffer1_len + buffer2_len);
+
+ if ((dmxdevfilter->params.pes.output == DMX_OUT_TS_TAP ||
+ dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+ && events->current_event_data_size >=
+ dmxdevfilter->params.pes.rec_chunk_size) {
+ event.type = DMX_EVENT_NEW_REC_CHUNK;
+ event.params.recording_chunk.offset =
+ events->current_event_start_offset;
+ event.params.recording_chunk.size =
+ events->current_event_data_size;
+
+ dvb_dmxdev_add_event(events, &event);
+ events->current_event_data_size = 0;
+ }
+ }
+
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+}
+
+static int dvb_dmxdev_section_event_cb(struct dmx_section_filter *filter,
+ struct dmx_data_ready *dmx_data_ready)
+{
+ int res = 0;
+ struct dmxdev_filter *dmxdevfilter = filter->priv;
+ struct dmx_filter_event event;
+ ssize_t free;
+
+ if (!dmxdevfilter) {
+ pr_err("%s: null filter. event type=%d (length=%d) will be discarded\n",
+ __func__, dmx_data_ready->status,
+ dmx_data_ready->data_length);
+ return -EINVAL;
+ }
+
+ spin_lock(&dmxdevfilter->dev->lock);
+
+ if (dmxdevfilter->buffer.error == -ETIMEDOUT ||
+ dmxdevfilter->state != DMXDEV_STATE_GO ||
+ dmxdevfilter->eos_state) {
+ spin_unlock(&dmxdevfilter->dev->lock);
return 0;
}
- ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
- if (ret == buffer1_len)
- ret = dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
- if (ret < 0)
- buffer->error = ret;
+
+ if (dmx_data_ready->data_length == 0) {
+ if (dmx_data_ready->status == DMX_CRC_ERROR) {
+ /* Section was dropped due to CRC error */
+ event.type = DMX_EVENT_SECTION_CRC_ERROR;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ } else if (dmx_data_ready->status == DMX_OK_EOS) {
+ event.type = DMX_EVENT_EOS;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ } else if (dmx_data_ready->status == DMX_OK_MARKER) {
+ event.type = DMX_EVENT_MARKER;
+ event.params.marker.id = dmx_data_ready->marker.id;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ } else if (dmx_data_ready->status == DMX_OK_SCRAMBLING_STATUS) {
+ event.type = DMX_EVENT_SCRAMBLING_STATUS_CHANGE;
+ event.params.scrambling_status =
+ dmx_data_ready->scrambling_bits;
+ dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ } else if (dmx_data_ready->status == DMX_OVERRUN_ERROR) {
+ pr_debug("dmxdev: section filter overflow (pid=%u)\n",
+ dmxdevfilter->params.sec.pid);
+ /* Set buffer error to notify user overflow occurred */
+ dmxdevfilter->buffer.error = -EOVERFLOW;
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ } else {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ }
+ return 0;
+ }
+
+ event.type = DMX_EVENT_NEW_SECTION;
+ event.params.section.base_offset = dmxdevfilter->buffer.pwrite;
+ event.params.section.start_offset = dmxdevfilter->buffer.pwrite;
+ event.params.section.total_length = dmx_data_ready->data_length;
+ event.params.section.actual_length = dmx_data_ready->data_length;
+
+ if (dmx_data_ready->status == DMX_MISSED_ERROR)
+ event.params.section.flags = DMX_FILTER_CC_ERROR;
+ else
+ event.params.section.flags = 0;
+
+ free = dvb_ringbuffer_free(&dmxdevfilter->buffer);
+ if (free < dmx_data_ready->data_length) {
+ pr_err("%s: invalid data length: data_length=%d > free=%zd\n",
+ __func__, dmx_data_ready->data_length, free);
+ } else {
+ res = dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+ DVB_RINGBUFFER_PUSH(&dmxdevfilter->buffer,
+ dmx_data_ready->data_length);
+ }
+
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&dmxdevfilter->buffer.queue);
+
+ return res;
+}
+
+static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed,
+ struct dmx_data_ready *dmx_data_ready)
+{
+ struct dmxdev_filter *dmxdevfilter = feed->priv;
+ struct dvb_ringbuffer *buffer;
+ struct dmxdev_events_queue *events;
+ struct dmx_filter_event event;
+ ssize_t free;
+
+ if (!dmxdevfilter) {
+ pr_err("%s: null filter (feed->is_filtering=%d) event type=%d (length=%d) will be discarded\n",
+ __func__, feed->is_filtering,
+ dmx_data_ready->status,
+ dmx_data_ready->data_length);
+ return -EINVAL;
+ }
+
+ spin_lock(&dmxdevfilter->dev->lock);
+
+ if (dmxdevfilter->state != DMXDEV_STATE_GO ||
+ dmxdevfilter->eos_state) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+
+ if (dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) {
+ buffer = &dmxdevfilter->buffer;
+ events = &dmxdevfilter->events;
+ } else {
+ buffer = &dmxdevfilter->dev->dvr_buffer;
+ events = &dmxdevfilter->dev->dvr_output_events;
+ }
+
+ if (!buffer->error && dmx_data_ready->status == DMX_OVERRUN_ERROR) {
+ pr_debug("dmxdev: %s filter buffer overflow (pid=%u)\n",
+ dmxdevfilter->params.pes.output == DMX_OUT_DECODER ?
+ "decoder" : "",
+ dmxdevfilter->params.pes.pid);
+ /* Set buffer error to notify user overflow occurred */
+ buffer->error = -EOVERFLOW;
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
+ if (dmx_data_ready->status == DMX_OK_EOS) {
+ /* Report partial recording chunk */
+ if ((dmxdevfilter->params.pes.output == DMX_OUT_TS_TAP ||
+ dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+ && events->current_event_data_size) {
+ event.type = DMX_EVENT_NEW_REC_CHUNK;
+ event.params.recording_chunk.offset =
+ events->current_event_start_offset;
+ event.params.recording_chunk.size =
+ events->current_event_data_size;
+ events->current_event_start_offset =
+ (events->current_event_start_offset +
+ events->current_event_data_size) %
+ buffer->size;
+ events->current_event_data_size = 0;
+ dvb_dmxdev_add_event(events, &event);
+ }
+
+ dmxdevfilter->eos_state = 1;
+ pr_debug("dmxdev: DMX_OK_EOS - entering EOS state\n");
+ event.type = DMX_EVENT_EOS;
+ dvb_dmxdev_add_event(events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
+ if (dmx_data_ready->status == DMX_OK_MARKER) {
+ pr_debug("dmxdev: DMX_OK_MARKER - id=%llu\n",
+ dmx_data_ready->marker.id);
+ event.type = DMX_EVENT_MARKER;
+ event.params.marker.id = dmx_data_ready->marker.id;
+ dvb_dmxdev_add_event(events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
+ if (dmx_data_ready->status == DMX_OK_PCR) {
+ pr_debug("dmxdev: event callback DMX_OK_PCR\n");
+ event.type = DMX_EVENT_NEW_PCR;
+ event.params.pcr.pcr = dmx_data_ready->pcr.pcr;
+ event.params.pcr.stc = dmx_data_ready->pcr.stc;
+ if (dmx_data_ready->pcr.disc_indicator_set)
+ event.params.pcr.flags =
+ DMX_FILTER_DISCONTINUITY_INDICATOR;
+ else
+ event.params.pcr.flags = 0;
+
+ dvb_dmxdev_add_event(events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
+ if (dmx_data_ready->status == DMX_OK_IDX) {
+ pr_debug("dmxdev: event callback DMX_OK_IDX\n");
+ event.type = DMX_EVENT_NEW_INDEX_ENTRY;
+ event.params.index = dmx_data_ready->idx_event;
+
+ dvb_dmxdev_add_event(events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
+ if (dmx_data_ready->status == DMX_OK_SCRAMBLING_STATUS) {
+ event.type = DMX_EVENT_SCRAMBLING_STATUS_CHANGE;
+ event.params.scrambling_status =
+ dmx_data_ready->scrambling_bits;
+ dvb_dmxdev_add_event(events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
+ if (dmx_data_ready->status == DMX_OK_DECODER_BUF) {
+ event.type = DMX_EVENT_NEW_ES_DATA;
+ event.params.es_data.buf_handle = dmx_data_ready->buf.handle;
+ event.params.es_data.cookie = dmx_data_ready->buf.cookie;
+ event.params.es_data.offset = dmx_data_ready->buf.offset;
+ event.params.es_data.data_len = dmx_data_ready->buf.len;
+ event.params.es_data.pts_valid = dmx_data_ready->buf.pts_exists;
+ event.params.es_data.pts = dmx_data_ready->buf.pts;
+ event.params.es_data.dts_valid = dmx_data_ready->buf.dts_exists;
+ event.params.es_data.dts = dmx_data_ready->buf.dts;
+ event.params.es_data.stc = dmx_data_ready->buf.stc;
+ event.params.es_data.transport_error_indicator_counter =
+ dmx_data_ready->buf.tei_counter;
+ event.params.es_data.continuity_error_counter =
+ dmx_data_ready->buf.cont_err_counter;
+ event.params.es_data.ts_packets_num =
+ dmx_data_ready->buf.ts_packets_num;
+ event.params.es_data.ts_dropped_bytes =
+ dmx_data_ready->buf.ts_dropped_bytes;
+ dvb_dmxdev_add_event(events, &event);
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
+ if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
+ free = dvb_ringbuffer_free(buffer);
+ if (free < dmx_data_ready->data_length) {
+ pr_err("%s: invalid data length: data_length=%d > free=%zd\n",
+ __func__, dmx_data_ready->data_length, free);
+
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up_all(&buffer->queue);
+ return 0;
+ }
+
+ if (dmxdevfilter->params.pes.output == DMX_OUT_TAP) {
+ if (dmx_data_ready->status == DMX_OK &&
+ !events->current_event_data_size) {
+ events->current_event_start_offset = buffer->pwrite;
+ } else if (dmx_data_ready->status == DMX_OK_PES_END) {
+ event.type = DMX_EVENT_NEW_PES;
+
+ event.params.pes.base_offset =
+ events->current_event_start_offset;
+ event.params.pes.start_offset =
+ (events->current_event_start_offset +
+ dmx_data_ready->pes_end.start_gap) %
+ buffer->size;
+
+ event.params.pes.actual_length =
+ dmx_data_ready->pes_end.actual_length;
+ event.params.pes.total_length =
+ events->current_event_data_size;
+
+ event.params.pes.flags = 0;
+ if (dmx_data_ready->pes_end.disc_indicator_set)
+ event.params.pes.flags |=
+ DMX_FILTER_DISCONTINUITY_INDICATOR;
+ if (dmx_data_ready->pes_end.pes_length_mismatch)
+ event.params.pes.flags |=
+ DMX_FILTER_PES_LENGTH_ERROR;
+
+ event.params.pes.stc = dmx_data_ready->pes_end.stc;
+ event.params.pes.transport_error_indicator_counter =
+ dmx_data_ready->pes_end.tei_counter;
+ event.params.pes.continuity_error_counter =
+ dmx_data_ready->pes_end.cont_err_counter;
+ event.params.pes.ts_packets_num =
+ dmx_data_ready->pes_end.ts_packets_num;
+
+ /* Do not report zero length PES */
+ if (event.params.pes.total_length)
+ dvb_dmxdev_add_event(events, &event);
+
+ events->current_event_data_size = 0;
+ }
+ } else if (!events->current_event_data_size) {
+ events->current_event_start_offset = buffer->pwrite;
+ }
+
+ events->current_event_data_size += dmx_data_ready->data_length;
+ DVB_RINGBUFFER_PUSH(buffer, dmx_data_ready->data_length);
+
+ if ((dmxdevfilter->params.pes.output == DMX_OUT_TS_TAP) ||
+ (dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)) {
+ while (events->current_event_data_size >=
+ dmxdevfilter->params.pes.rec_chunk_size) {
+ event.type = DMX_EVENT_NEW_REC_CHUNK;
+ event.params.recording_chunk.offset =
+ events->current_event_start_offset;
+ event.params.recording_chunk.size =
+ dmxdevfilter->params.pes.rec_chunk_size;
+ events->current_event_data_size =
+ events->current_event_data_size -
+ dmxdevfilter->params.pes.rec_chunk_size;
+ events->current_event_start_offset =
+ (events->current_event_start_offset +
+ dmxdevfilter->params.pes.rec_chunk_size) %
+ buffer->size;
+
+ dvb_dmxdev_add_event(events, &event);
+ }
+ }
spin_unlock(&dmxdevfilter->dev->lock);
- wake_up(&buffer->queue);
+ wake_up_all(&buffer->queue);
return 0;
}
@@ -427,11 +3129,18 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
switch (dmxdevfilter->type) {
case DMXDEV_TYPE_SEC:
del_timer(&dmxdevfilter->timer);
- dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
+ dmxdevfilter->feed.sec.feed->stop_filtering(
+ dmxdevfilter->feed.sec.feed);
break;
case DMXDEV_TYPE_PES:
- list_for_each_entry(feed, &dmxdevfilter->feed.ts, next)
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
+ if (dmxdevfilter->params.pes.output == DMX_OUT_TS_TAP) {
+ dmxdevfilter->dev->dvr_feeds_count--;
+ if (!dmxdevfilter->dev->dvr_feeds_count)
+ dmxdevfilter->dev->dvr_feed = NULL;
+ }
feed->ts->stop_filtering(feed->ts);
+ }
break;
default:
return -EINVAL;
@@ -449,7 +3158,8 @@ static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter)
switch (filter->type) {
case DMXDEV_TYPE_SEC:
- return filter->feed.sec->start_filtering(filter->feed.sec);
+ return filter->feed.sec.feed->start_filtering(
+ filter->feed.sec.feed);
case DMXDEV_TYPE_PES:
list_for_each_entry(feed, &filter->feed.ts, next) {
ret = feed->ts->start_filtering(feed->ts);
@@ -483,7 +3193,7 @@ static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
}
filter->dev->demux->release_section_feed(dmxdev->demux,
- filter->feed.sec);
+ filter->feed.sec.feed);
return 0;
}
@@ -492,25 +3202,38 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
{
struct dmxdev_feed *feed;
struct dmx_demux *demux;
+ struct ts_insertion_buffer *ts_buffer;
if (dmxdevfilter->state < DMXDEV_STATE_GO)
return 0;
switch (dmxdevfilter->type) {
case DMXDEV_TYPE_SEC:
- if (!dmxdevfilter->feed.sec)
+ if (!dmxdevfilter->feed.sec.feed)
break;
dvb_dmxdev_feed_stop(dmxdevfilter);
if (dmxdevfilter->filter.sec)
- dmxdevfilter->feed.sec->
- release_filter(dmxdevfilter->feed.sec,
+ dmxdevfilter->feed.sec.feed->
+ release_filter(dmxdevfilter->feed.sec.feed,
dmxdevfilter->filter.sec);
dvb_dmxdev_feed_restart(dmxdevfilter);
- dmxdevfilter->feed.sec = NULL;
+ dmxdevfilter->feed.sec.feed = NULL;
break;
case DMXDEV_TYPE_PES:
dvb_dmxdev_feed_stop(dmxdevfilter);
demux = dmxdevfilter->dev->demux;
+
+ if (!list_empty(&dmxdevfilter->insertion_buffers)) {
+ feed = list_first_entry(&dmxdevfilter->feed.ts,
+ struct dmxdev_feed, next);
+
+ list_for_each_entry(ts_buffer,
+ &dmxdevfilter->insertion_buffers, next)
+ dvb_dmxdev_cancel_ts_insertion(ts_buffer);
+ if (feed->ts->ts_insertion_terminate)
+ feed->ts->ts_insertion_terminate(feed->ts);
+ }
+
list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
demux->release_ts_feed(demux, feed->ts);
feed->ts = NULL;
@@ -522,7 +3245,13 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
return -EINVAL;
}
- dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dvb_dmxdev_flush_output(&dmxdevfilter->buffer, &dmxdevfilter->events);
+ dvb_ringbuffer_reset(&dmxdevfilter->buffer);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ wake_up_all(&dmxdevfilter->buffer.queue);
+
return 0;
}
@@ -589,12 +3318,76 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
tsfeed = feed->ts;
tsfeed->priv = filter;
- ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
+ if (filter->params.pes.output == DMX_OUT_TS_TAP) {
+ tsfeed->buffer.ringbuff = &dmxdev->dvr_buffer;
+ tsfeed->buffer.priv_handle = dmxdev->dvr_priv_buff_handle;
+ if (!dmxdev->dvr_feeds_count)
+ dmxdev->dvr_feed = filter;
+ dmxdev->dvr_feeds_count++;
+ } else if (filter->params.pes.output == DMX_OUT_DECODER) {
+ tsfeed->buffer.ringbuff = &filter->buffer;
+ tsfeed->decoder_buffers = &filter->decoder_buffers;
+ tsfeed->buffer.priv_handle = filter->priv_buff_handle;
+ } else {
+ tsfeed->buffer.ringbuff = &filter->buffer;
+ tsfeed->buffer.priv_handle = filter->priv_buff_handle;
+ }
+
+ if (tsfeed->data_ready_cb) {
+ ret = tsfeed->data_ready_cb(tsfeed, dvb_dmxdev_ts_event_cb);
+
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ return ret;
+ }
+ }
+
+ ret = tsfeed->set(tsfeed, feed->pid,
+ ts_type, ts_pes,
+ filter->decoder_buffers.buffers_size,
+ timeout);
if (ret < 0) {
dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
return ret;
}
+ if (tsfeed->set_tsp_out_format)
+ tsfeed->set_tsp_out_format(tsfeed, filter->dmx_tsp_format);
+
+ if (tsfeed->set_secure_mode)
+ tsfeed->set_secure_mode(tsfeed, &filter->sec_mode);
+
+ if (tsfeed->set_cipher_ops)
+ tsfeed->set_cipher_ops(tsfeed, &feed->cipher_ops);
+
+ if ((para->pes_type == DMX_PES_VIDEO0) ||
+ (para->pes_type == DMX_PES_VIDEO1) ||
+ (para->pes_type == DMX_PES_VIDEO2) ||
+ (para->pes_type == DMX_PES_VIDEO3)) {
+ if (tsfeed->set_video_codec) {
+ ret = tsfeed->set_video_codec(tsfeed,
+ para->video_codec);
+
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux,
+ tsfeed);
+ return ret;
+ }
+ }
+ }
+
+ if ((filter->params.pes.output == DMX_OUT_TS_TAP) ||
+ (filter->params.pes.output == DMX_OUT_TSDEMUX_TAP))
+ if (tsfeed->set_idx_params) {
+ ret = tsfeed->set_idx_params(
+ tsfeed, &feed->idx_params);
+ if (ret) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux,
+ tsfeed);
+ return ret;
+ }
+ }
+
ret = tsfeed->start_filtering(tsfeed);
if (ret < 0) {
dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
@@ -604,12 +3397,50 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
return 0;
}
+static int dvb_filter_external_buffer_only(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter)
+{
+ struct dmx_caps caps;
+ int is_external_only;
+ int flags;
+
+ /*
+ * For backward compatibility, default assumes that
+ * external only buffers are not supported.
+ */
+ flags = 0;
+ if (dmxdev->demux->get_caps) {
+ dmxdev->demux->get_caps(dmxdev->demux, &caps);
+
+ if (filter->type == DMXDEV_TYPE_SEC)
+ flags = caps.section.flags;
+ else if (filter->params.pes.output == DMX_OUT_DECODER)
+ /* For decoder filters dmxdev buffer is not required */
+ flags = 0;
+ else if (filter->params.pes.output == DMX_OUT_TAP)
+ flags = caps.pes.flags;
+ else if (filter->dmx_tsp_format == DMX_TSP_FORMAT_188)
+ flags = caps.recording_188_tsp.flags;
+ else
+ flags = caps.recording_192_tsp.flags;
+ }
+
+ if (!(flags & DMX_BUFFER_INTERNAL_SUPPORT) &&
+ (flags & DMX_BUFFER_EXTERNAL_SUPPORT))
+ is_external_only = 1;
+ else
+ is_external_only = 0;
+
+ return is_external_only;
+}
+
static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
{
struct dmxdev *dmxdev = filter->dev;
struct dmxdev_feed *feed;
void *mem;
int ret, i;
+ size_t tsp_size;
if (filter->state < DMXDEV_STATE_SET)
return -EINVAL;
@@ -617,34 +3448,64 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
if (filter->state >= DMXDEV_STATE_GO)
dvb_dmxdev_filter_stop(filter);
+ if (!dvb_filter_verify_buffer_size(filter))
+ return -EINVAL;
+
if (!filter->buffer.data) {
- mem = vmalloc(filter->buffer.size);
+ /*
+ * dmxdev buffer in decoder filters is not really used
+ * to exchange data with applications. Decoder buffers
+ * can be set using DMX_SET_DECODER_BUFFER, which
+ * would not update the filter->buffer.data at all.
+ * Therefore we should not treat this filter as
+ * other regular filters and should not fail here
+ * even if user sets the buffer in deocder
+ * filter as external buffer.
+ */
+ if (filter->type == DMXDEV_TYPE_PES &&
+ (filter->params.pes.output == DMX_OUT_DECODER ||
+ filter->params.pes.output == DMX_OUT_TS_TAP))
+ filter->buffer_mode = DMX_BUFFER_MODE_INTERNAL;
+
+ if (!(filter->type == DMXDEV_TYPE_PES &&
+ filter->params.pes.output == DMX_OUT_TS_TAP) &&
+ (filter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL ||
+ dvb_filter_external_buffer_only(dmxdev, filter)))
+ return -ENOMEM;
+
+ mem = vmalloc_user(filter->buffer.size);
if (!mem)
return -ENOMEM;
spin_lock_irq(&filter->dev->lock);
filter->buffer.data = mem;
spin_unlock_irq(&filter->dev->lock);
+ } else if ((filter->buffer_mode == DMX_BUFFER_MODE_INTERNAL) &&
+ dvb_filter_external_buffer_only(dmxdev, filter)) {
+ return -ENOMEM;
}
- dvb_ringbuffer_flush(&filter->buffer);
+ filter->eos_state = 0;
+
+ spin_lock_irq(&filter->dev->lock);
+ dvb_dmxdev_flush_output(&filter->buffer, &filter->events);
+ spin_unlock_irq(&filter->dev->lock);
switch (filter->type) {
case DMXDEV_TYPE_SEC:
{
struct dmx_sct_filter_params *para = &filter->params.sec;
struct dmx_section_filter **secfilter = &filter->filter.sec;
- struct dmx_section_feed **secfeed = &filter->feed.sec;
+ struct dmx_section_feed **secfeed = &filter->feed.sec.feed;
*secfilter = NULL;
*secfeed = NULL;
-
/* find active filter/feed with same PID */
for (i = 0; i < dmxdev->filternum; i++) {
if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
dmxdev->filter[i].params.sec.pid == para->pid) {
- *secfeed = dmxdev->filter[i].feed.sec;
+ *secfeed = dmxdev->filter[i].feed.sec.feed;
break;
}
}
@@ -652,22 +3513,44 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
/* if no feed found, try to allocate new one */
if (!*secfeed) {
ret = dmxdev->demux->allocate_section_feed(dmxdev->demux,
- secfeed,
- dvb_dmxdev_section_callback);
+ secfeed,
+ dvb_dmxdev_section_callback);
if (ret < 0) {
- printk("DVB (%s): could not alloc feed\n",
+ pr_err("DVB (%s): could not alloc feed\n",
__func__);
return ret;
}
+ if ((*secfeed)->data_ready_cb) {
+ ret = (*secfeed)->data_ready_cb(
+ *secfeed,
+ dvb_dmxdev_section_event_cb);
+
+ if (ret < 0) {
+ pr_err(
+ "DVB (%s): could not set event cb\n",
+ __func__);
+ dvb_dmxdev_feed_restart(filter);
+ return ret;
+ }
+ }
+
ret = (*secfeed)->set(*secfeed, para->pid, 32768,
(para->flags & DMX_CHECK_CRC) ? 1 : 0);
if (ret < 0) {
- printk("DVB (%s): could not set feed\n",
- __func__);
+ pr_err("DVB (%s): could not set feed\n",
+ __func__);
dvb_dmxdev_feed_restart(filter);
return ret;
}
+
+ if ((*secfeed)->set_secure_mode)
+ (*secfeed)->set_secure_mode(*secfeed,
+ &filter->sec_mode);
+
+ if ((*secfeed)->set_cipher_ops)
+ (*secfeed)->set_cipher_ops(*secfeed,
+ &filter->feed.sec.cipher_ops);
} else {
dvb_dmxdev_feed_stop(filter);
}
@@ -675,12 +3558,14 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
ret = (*secfeed)->allocate_filter(*secfeed, secfilter);
if (ret < 0) {
dvb_dmxdev_feed_restart(filter);
- filter->feed.sec->start_filtering(*secfeed);
- dprintk("could not get filter\n");
+ filter->feed.sec.feed->start_filtering(*secfeed);
+ pr_debug("could not get filter\n");
return ret;
}
(*secfilter)->priv = filter;
+ (*secfilter)->buffer.ringbuff = &filter->buffer;
+ (*secfilter)->buffer.priv_handle = filter->priv_buff_handle;
memcpy(&((*secfilter)->filter_value[3]),
&(para->filter.filter[1]), DMX_FILTER_SIZE - 1);
@@ -696,8 +3581,12 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
(*secfilter)->filter_mask[2] = 0;
filter->todo = 0;
+ filter->events.data_read_event_masked =
+ filter->events.event_mask.disable_mask &
+ DMX_EVENT_NEW_SECTION;
- ret = filter->feed.sec->start_filtering(filter->feed.sec);
+ ret = filter->feed.sec.feed->start_filtering(
+ filter->feed.sec.feed);
if (ret < 0)
return ret;
@@ -705,19 +3594,93 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
break;
}
case DMXDEV_TYPE_PES:
+ if (filter->params.pes.rec_chunk_size <
+ DMX_REC_BUFF_CHUNK_MIN_SIZE)
+ filter->params.pes.rec_chunk_size =
+ DMX_REC_BUFF_CHUNK_MIN_SIZE;
+
+ if (filter->params.pes.rec_chunk_size >=
+ filter->buffer.size)
+ filter->params.pes.rec_chunk_size =
+ filter->buffer.size >> 2;
+
+ /* Align rec-chunk based on output format */
+ if (filter->dmx_tsp_format == DMX_TSP_FORMAT_188)
+ tsp_size = 188;
+ else
+ tsp_size = 192;
+
+ filter->params.pes.rec_chunk_size /= tsp_size;
+ filter->params.pes.rec_chunk_size *= tsp_size;
+
+ if (filter->params.pes.output == DMX_OUT_TS_TAP)
+ dmxdev->dvr_output_events.data_read_event_masked =
+ dmxdev->dvr_output_events.event_mask.disable_mask &
+ DMX_EVENT_NEW_REC_CHUNK;
+ else if (filter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+ filter->events.data_read_event_masked =
+ filter->events.event_mask.disable_mask &
+ DMX_EVENT_NEW_REC_CHUNK;
+ else if (filter->params.pes.output == DMX_OUT_TAP)
+ filter->events.data_read_event_masked =
+ filter->events.event_mask.disable_mask &
+ DMX_EVENT_NEW_PES;
+ else
+ filter->events.data_read_event_masked = 1;
+
+ ret = 0;
list_for_each_entry(feed, &filter->feed.ts, next) {
ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
- if (ret < 0) {
- dvb_dmxdev_filter_stop(filter);
- return ret;
+ if (ret)
+ break;
+ }
+
+ if (!ret)
+ break;
+
+ /* cleanup feeds that were started before the failure */
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ if (!feed->ts)
+ continue;
+ feed->ts->stop_filtering(feed->ts);
+ dmxdev->demux->release_ts_feed(dmxdev->demux, feed->ts);
+ feed->ts = NULL;
+
+ if (filter->params.pes.output == DMX_OUT_TS_TAP) {
+ filter->dev->dvr_feeds_count--;
+ if (!filter->dev->dvr_feeds_count)
+ filter->dev->dvr_feed = NULL;
}
}
- break;
+ return ret;
+
default:
return -EINVAL;
}
dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+
+ if ((filter->type == DMXDEV_TYPE_PES) &&
+ !list_empty(&filter->insertion_buffers)) {
+ struct ts_insertion_buffer *ts_buffer;
+
+ feed = list_first_entry(&filter->feed.ts,
+ struct dmxdev_feed, next);
+
+ ret = 0;
+ if (feed->ts->ts_insertion_init)
+ ret = feed->ts->ts_insertion_init(feed->ts);
+ if (!ret) {
+ list_for_each_entry(ts_buffer,
+ &filter->insertion_buffers, next)
+ dvb_dmxdev_queue_ts_insertion(
+ ts_buffer);
+ } else {
+ pr_err("%s: ts_insertion_init failed, err %d\n",
+ __func__, ret);
+ }
+ }
+
return 0;
}
@@ -747,11 +3710,28 @@ static int dvb_demux_open(struct inode *inode, struct file *file)
mutex_init(&dmxdevfilter->mutex);
file->private_data = dmxdevfilter;
+ memset(&dmxdevfilter->decoder_buffers,
+ 0,
+ sizeof(dmxdevfilter->decoder_buffers));
+ dmxdevfilter->decoder_buffers.buffers_size =
+ DMX_DEFAULT_DECODER_BUFFER_SIZE;
+ dmxdevfilter->buffer_mode = DMX_BUFFER_MODE_INTERNAL;
+ dmxdevfilter->priv_buff_handle = NULL;
dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
+ dvb_dmxdev_flush_events(&dmxdevfilter->events);
+ dmxdevfilter->events.event_mask.disable_mask = DMX_EVENT_NEW_ES_DATA;
+ dmxdevfilter->events.event_mask.no_wakeup_mask = 0;
+ dmxdevfilter->events.event_mask.wakeup_threshold = 1;
+
dmxdevfilter->type = DMXDEV_TYPE_NONE;
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
init_timer(&dmxdevfilter->timer);
+ dmxdevfilter->sec_mode.is_secured = 0;
+
+ INIT_LIST_HEAD(&dmxdevfilter->insertion_buffers);
+
+ dmxdevfilter->dmx_tsp_format = DMX_TSP_FORMAT_188;
dvbdev->users++;
mutex_unlock(&dmxdev->mutex);
@@ -761,23 +3741,40 @@ static int dvb_demux_open(struct inode *inode, struct file *file)
static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
struct dmxdev_filter *dmxdevfilter)
{
+ struct ts_insertion_buffer *ts_buffer, *tmp;
+
mutex_lock(&dmxdev->mutex);
mutex_lock(&dmxdevfilter->mutex);
dvb_dmxdev_filter_stop(dmxdevfilter);
dvb_dmxdev_filter_reset(dmxdevfilter);
+ list_for_each_entry_safe(ts_buffer, tmp,
+ &dmxdevfilter->insertion_buffers, next) {
+ list_del(&ts_buffer->next);
+ vfree(ts_buffer->buffer);
+ vfree(ts_buffer);
+ }
+
if (dmxdevfilter->buffer.data) {
void *mem = dmxdevfilter->buffer.data;
spin_lock_irq(&dmxdev->lock);
dmxdevfilter->buffer.data = NULL;
spin_unlock_irq(&dmxdev->lock);
- vfree(mem);
+ if (dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_INTERNAL)
+ vfree(mem);
+ }
+
+ if ((dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL) &&
+ dmxdevfilter->priv_buff_handle) {
+ dmxdev->demux->unmap_buffer(dmxdev->demux,
+ dmxdevfilter->priv_buff_handle);
+ dmxdevfilter->priv_buff_handle = NULL;
}
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
- wake_up(&dmxdevfilter->buffer.queue);
+ wake_up_all(&dmxdevfilter->buffer.queue);
mutex_unlock(&dmxdevfilter->mutex);
mutex_unlock(&dmxdev->mutex);
return 0;
@@ -795,6 +3792,7 @@ static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
struct dmxdev_filter *filter, u16 pid)
{
struct dmxdev_feed *feed;
+ int ret = 0;
if ((filter->type != DMXDEV_TYPE_PES) ||
(filter->state < DMXDEV_STATE_SET))
@@ -810,28 +3808,45 @@ static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
return -ENOMEM;
feed->pid = pid;
- list_add(&feed->next, &filter->feed.ts);
+ feed->cipher_ops.operations_count = 0;
+ feed->idx_params.enable = 0;
if (filter->state >= DMXDEV_STATE_GO)
- return dvb_dmxdev_start_feed(dmxdev, filter, feed);
+ ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
- return 0;
+ if (!ret)
+ list_add(&feed->next, &filter->feed.ts);
+ else
+ kfree(feed);
+
+ return ret;
}
static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
struct dmxdev_filter *filter, u16 pid)
{
+ int feed_count;
struct dmxdev_feed *feed, *tmp;
if ((filter->type != DMXDEV_TYPE_PES) ||
(filter->state < DMXDEV_STATE_SET))
return -EINVAL;
+ feed_count = 0;
+ list_for_each_entry(tmp, &filter->feed.ts, next)
+ feed_count++;
+
+ if (feed_count <= 1)
+ return -EINVAL;
+
list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
- if ((feed->pid == pid) && (feed->ts != NULL)) {
- feed->ts->stop_filtering(feed->ts);
- filter->dev->demux->release_ts_feed(filter->dev->demux,
- feed->ts);
+ if (feed->pid == pid) {
+ if (feed->ts != NULL) {
+ feed->ts->stop_filtering(feed->ts);
+ filter->dev->demux->release_ts_feed(
+ filter->dev->demux,
+ feed->ts);
+ }
list_del(&feed->next);
kfree(feed);
}
@@ -844,7 +3859,7 @@ static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
struct dmxdev_filter *dmxdevfilter,
struct dmx_sct_filter_params *params)
{
- dprintk("function : %s, PID=0x%04x, flags=%02x, timeout=%d\n",
+ pr_debug("function : %s, PID=0x%04x, flags=%02x, timeout=%d\n",
__func__, params->pid, params->flags, params->timeout);
dvb_dmxdev_filter_stop(dmxdevfilter);
@@ -853,6 +3868,7 @@ static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
memcpy(&dmxdevfilter->params.sec,
params, sizeof(struct dmx_sct_filter_params));
invert_mode(&dmxdevfilter->params.sec.filter);
+ dmxdevfilter->feed.sec.cipher_ops.operations_count = 0;
dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
if (params->flags & DMX_IMMEDIATE_START)
@@ -861,6 +3877,99 @@ static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
return 0;
}
+static int dvb_dmxdev_set_secure_mode(
+ struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter,
+ struct dmx_secure_mode *sec_mode)
+{
+ if (!dmxdev || !filter || !sec_mode)
+ return -EINVAL;
+
+ if (filter->state == DMXDEV_STATE_GO) {
+ pr_err("%s: invalid filter state\n", __func__);
+ return -EBUSY;
+ }
+
+ pr_debug("%s: secure=%d\n", __func__, sec_mode->is_secured);
+
+ filter->sec_mode = *sec_mode;
+
+ return 0;
+}
+
+static int dvb_dmxdev_set_cipher(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter,
+ struct dmx_cipher_operations *cipher_ops)
+{
+ struct dmxdev_feed *feed;
+ struct dmxdev_feed *ts_feed = NULL;
+ struct dmxdev_sec_feed *sec_feed = NULL;
+ struct dmx_caps caps;
+
+ if (!dmxdev || !dmxdev->demux->get_caps)
+ return -EINVAL;
+
+ dmxdev->demux->get_caps(dmxdev->demux, &caps);
+
+ if (!filter || !cipher_ops ||
+ (cipher_ops->operations_count > caps.num_cipher_ops) ||
+ (cipher_ops->operations_count >
+ DMX_MAX_CIPHER_OPERATIONS_COUNT))
+ return -EINVAL;
+
+ pr_debug("%s: pid=%d, operations=%d\n", __func__,
+ cipher_ops->pid, cipher_ops->operations_count);
+
+ if (filter->state < DMXDEV_STATE_SET ||
+ filter->state > DMXDEV_STATE_GO) {
+ pr_err("%s: invalid filter state\n", __func__);
+ return -EPERM;
+ }
+
+ if (!filter->sec_mode.is_secured && cipher_ops->operations_count) {
+ pr_err("%s: secure mode must be enabled to set cipher ops\n",
+ __func__);
+ return -EPERM;
+ }
+
+ switch (filter->type) {
+ case DMXDEV_TYPE_PES:
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ if (feed->pid == cipher_ops->pid) {
+ ts_feed = feed;
+ ts_feed->cipher_ops = *cipher_ops;
+ if (filter->state == DMXDEV_STATE_GO &&
+ ts_feed->ts->set_cipher_ops)
+ ts_feed->ts->set_cipher_ops(
+ ts_feed->ts, cipher_ops);
+ break;
+ }
+ }
+ break;
+ case DMXDEV_TYPE_SEC:
+ if (filter->params.sec.pid == cipher_ops->pid) {
+ sec_feed = &filter->feed.sec;
+ sec_feed->cipher_ops = *cipher_ops;
+ if (filter->state == DMXDEV_STATE_GO &&
+ sec_feed->feed->set_cipher_ops)
+ sec_feed->feed->set_cipher_ops(sec_feed->feed,
+ cipher_ops);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (!ts_feed && !sec_feed) {
+ pr_err("%s: pid %d is undefined for this filter\n",
+ __func__, cipher_ops->pid);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
struct dmxdev_filter *dmxdevfilter,
struct dmx_pes_filter_params *params)
@@ -891,6 +4000,55 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
return 0;
}
+static int dvb_dmxdev_set_decoder_buffer(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter,
+ struct dmx_decoder_buffers *buffs)
+{
+ int i;
+ struct dmx_decoder_buffers *dec_buffs;
+ struct dmx_caps caps;
+
+ if (!dmxdev || !filter || !buffs)
+ return -EINVAL;
+
+ dec_buffs = &filter->decoder_buffers;
+ if (!dmxdev->demux->get_caps)
+ return -EINVAL;
+
+ dmxdev->demux->get_caps(dmxdev->demux, &caps);
+ if (!dvb_dmxdev_verify_buffer_size(buffs->buffers_size,
+ caps.decoder.max_size, caps.decoder.size_alignment))
+ return -EINVAL;
+
+ if ((buffs->buffers_size == 0) ||
+ (buffs->is_linear &&
+ ((buffs->buffers_num <= 1) ||
+ (buffs->buffers_num > DMX_MAX_DECODER_BUFFER_NUM))))
+ return -EINVAL;
+
+ if (buffs->buffers_num == 0) {
+ /* Internal mode - linear buffers not supported in this mode */
+ if (!(caps.decoder.flags & DMX_BUFFER_INTERNAL_SUPPORT) ||
+ buffs->is_linear)
+ return -EINVAL;
+ } else {
+ /* External buffer(s) mode */
+ if ((!(caps.decoder.flags & DMX_BUFFER_LINEAR_GROUP_SUPPORT) &&
+ buffs->buffers_num > 1) ||
+ !(caps.decoder.flags & DMX_BUFFER_EXTERNAL_SUPPORT) ||
+ buffs->buffers_num > caps.decoder.max_buffer_num)
+ return -EINVAL;
+
+ dec_buffs->is_linear = buffs->is_linear;
+ dec_buffs->buffers_num = buffs->buffers_num;
+ dec_buffs->buffers_size = buffs->buffers_size;
+ for (i = 0; i < dec_buffs->buffers_num; i++)
+ dec_buffs->handles[i] = buffs->handles[i];
+ }
+
+ return 0;
+}
+
static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
struct file *file, char __user *buf,
size_t count, loff_t *ppos)
@@ -902,7 +4060,7 @@ static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
hcount = 3 + dfil->todo;
if (hcount > count)
hcount = count;
- result = dvb_dmxdev_buffer_read(&dfil->buffer,
+ result = dvb_dmxdev_buffer_read(dfil, &dfil->buffer,
file->f_flags & O_NONBLOCK,
buf, hcount, ppos);
if (result < 0) {
@@ -923,7 +4081,7 @@ static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
}
if (count > dfil->todo)
count = dfil->todo;
- result = dvb_dmxdev_buffer_read(&dfil->buffer,
+ result = dvb_dmxdev_buffer_read(dfil, &dfil->buffer,
file->f_flags & O_NONBLOCK,
buf, count, ppos);
if (result < 0)
@@ -942,12 +4100,36 @@ dvb_demux_read(struct file *file, char __user *buf, size_t count,
if (mutex_lock_interruptible(&dmxdevfilter->mutex))
return -ERESTARTSYS;
+ if (dmxdevfilter->eos_state &&
+ dvb_ringbuffer_empty(&dmxdevfilter->buffer)) {
+ mutex_unlock(&dmxdevfilter->mutex);
+ return 0;
+ }
+
if (dmxdevfilter->type == DMXDEV_TYPE_SEC)
ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
else
- ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
- file->f_flags & O_NONBLOCK,
- buf, count, ppos);
+ ret = dvb_dmxdev_buffer_read(dmxdevfilter,
+ &dmxdevfilter->buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
+
+ if (ret > 0) {
+ dvb_dmxdev_notify_data_read(dmxdevfilter, ret);
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dvb_dmxdev_update_events(&dmxdevfilter->events, ret);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ /*
+ * in PULL mode, we might be stalling on
+ * event queue, so need to wake-up waiters
+ */
+ if (dmxdevfilter->dev->playback_mode == DMX_PB_MODE_PULL)
+ wake_up_all(&dmxdevfilter->buffer.queue);
+ } else if (ret == -EOVERFLOW) {
+ dvb_dmxdev_auto_flush_buffer(dmxdevfilter,
+ &dmxdevfilter->buffer);
+ }
mutex_unlock(&dmxdevfilter->mutex);
return ret;
@@ -1013,6 +4195,43 @@ static int dvb_demux_do_ioctl(struct file *file,
mutex_unlock(&dmxdevfilter->mutex);
break;
+ case DMX_SET_BUFFER_MODE:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_buffer_mode(dmxdevfilter,
+ *(enum dmx_buffer_mode *)parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_BUFFER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_buffer(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_GET_BUFFER_STATUS:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_get_buffer_status(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_RELEASE_DATA:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_release_data(dmxdevfilter, arg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
case DMX_GET_PES_PIDS:
if (!dmxdev->demux->get_pes_pids) {
ret = -EINVAL;
@@ -1021,9 +4240,6 @@ static int dvb_demux_do_ioctl(struct file *file,
dmxdev->demux->get_pes_pids(dmxdev->demux, parg);
break;
-#if 0
- /* Not used upstream and never documented */
-
case DMX_GET_CAPS:
if (!dmxdev->demux->get_caps) {
ret = -EINVAL;
@@ -1033,13 +4249,65 @@ static int dvb_demux_do_ioctl(struct file *file,
break;
case DMX_SET_SOURCE:
- if (!dmxdev->demux->set_source) {
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_source(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_TS_PACKET_FORMAT:
+ if (!dmxdev->demux->set_tsp_format) {
ret = -EINVAL;
break;
}
- ret = dmxdev->demux->set_source(dmxdev->demux, parg);
+
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO) {
+ ret = -EBUSY;
+ break;
+ }
+ ret = dmxdev->demux->set_tsp_format(
+ dmxdev->demux,
+ *(enum dmx_tsp_format_t *)parg);
+ break;
+
+ case DMX_SET_TS_OUT_FORMAT:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+
+ ret = dvb_dmxdev_set_tsp_out_format(dmxdevfilter,
+ *(enum dmx_tsp_format_t *)parg);
+
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_DECODER_BUFFER_SIZE:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+
+ ret = dvb_dmxdev_set_decoder_buffer_size(dmxdevfilter, arg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_PLAYBACK_MODE:
+ ret = dvb_dmxdev_set_playback_mode(
+ dmxdevfilter,
+ *(enum dmx_playback_mode_t *)parg);
+ break;
+
+ case DMX_GET_EVENT:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_get_event(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
break;
-#endif
case DMX_GET_STC:
if (!dmxdev->demux->get_stc) {
@@ -1070,8 +4338,109 @@ static int dvb_demux_do_ioctl(struct file *file,
mutex_unlock(&dmxdevfilter->mutex);
break;
+ case DMX_SET_DECODER_BUFFER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_set_decoder_buffer(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_SECURE_MODE:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_set_secure_mode(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_CIPHER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_set_cipher(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_REUSE_DECODER_BUFFER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_reuse_decoder_buf(dmxdevfilter, arg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_EVENTS_MASK:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_event_mask(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_GET_EVENTS_MASK:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_get_event_mask(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_INDEXING_PARAMS:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_indexing_params(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_TS_INSERTION:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_ts_insertion(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_ABORT_TS_INSERTION:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_abort_ts_insertion(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_GET_SCRAMBLING_BITS:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_get_scrambling_bits(dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_FLUSH_BUFFER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_flush_buffer(dmxdevfilter);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
default:
- ret = -EINVAL;
+ pr_err("%s: unknown ioctl code (0x%x)\n",
+ __func__, cmd);
+ ret = -ENOIOCTLCMD;
break;
}
mutex_unlock(&dmxdev->mutex);
@@ -1084,13 +4453,78 @@ static long dvb_demux_ioctl(struct file *file, unsigned int cmd,
return dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl);
}
+#ifdef CONFIG_COMPAT
+
+struct dmx_set_ts_insertion32 {
+ __u32 identifier;
+ __u32 repetition_time;
+ compat_uptr_t ts_packets;
+ compat_size_t size;
+};
+
+static long dmx_set_ts_insertion32_wrapper(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+ struct dmx_set_ts_insertion32 dmx_ts_insert32;
+ struct dmx_set_ts_insertion dmx_ts_insert;
+
+ ret = copy_from_user(&dmx_ts_insert32, (void __user *)arg,
+ sizeof(dmx_ts_insert32));
+ if (ret) {
+ pr_err(
+ "%s: copy dmx_set_ts_insertion32 from user failed, ret=%d\n",
+ __func__, ret);
+ return -EFAULT;
+ }
+
+ memset(&dmx_ts_insert, 0, sizeof(dmx_ts_insert));
+ dmx_ts_insert.identifier = dmx_ts_insert32.identifier;
+ dmx_ts_insert.repetition_time = dmx_ts_insert32.repetition_time;
+ dmx_ts_insert.ts_packets = compat_ptr(dmx_ts_insert32.ts_packets);
+ dmx_ts_insert.size = dmx_ts_insert32.size;
+
+ ret = dvb_demux_do_ioctl(file, DMX_SET_TS_INSERTION, &dmx_ts_insert);
+
+ return ret;
+}
+
+#define DMX_SET_TS_INSERTION32 _IOW('o', 70, struct dmx_set_ts_insertion32)
+
+/*
+ * compat ioctl is called whenever compatibility is required, i.e when a 32bit
+ * process calls an ioctl for a 64bit kernel.
+ */
+static long dvb_demux_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ long ret = 0;
+
+ switch (cmd) {
+ case DMX_SET_TS_INSERTION32:
+ ret = dmx_set_ts_insertion32_wrapper(file, cmd, arg);
+ break;
+ case DMX_SET_TS_INSERTION:
+ pr_err("%s: 64bit ioctl code (0x%lx) used by 32bit userspace\n",
+ __func__, DMX_SET_TS_INSERTION);
+ ret = -ENOIOCTLCMD;
+ break;
+ default:
+ /* use regular ioctl */
+ ret = dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl);
+ }
+
+ return ret;
+}
+#endif
+
static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
{
struct dmxdev_filter *dmxdevfilter = file->private_data;
unsigned int mask = 0;
- if ((!dmxdevfilter) || dmxdevfilter->dev->exit)
- return POLLERR;
+ if (!dmxdevfilter)
+ return -EINVAL;
poll_wait(file, &dmxdevfilter->buffer.queue, wait);
@@ -1099,20 +4533,80 @@ static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT)
return 0;
- if (dmxdevfilter->buffer.error)
- mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+ if (dmxdevfilter->buffer.error) {
+ mask |= (POLLIN | POLLRDNORM | POLLERR);
+ if (dmxdevfilter->buffer.error == -EOVERFLOW)
+ mask |= POLLPRI;
+ }
if (!dvb_ringbuffer_empty(&dmxdevfilter->buffer))
- mask |= (POLLIN | POLLRDNORM | POLLPRI);
+ mask |= (POLLIN | POLLRDNORM);
+
+ if (dmxdevfilter->events.wakeup_events_counter >=
+ dmxdevfilter->events.event_mask.wakeup_threshold)
+ mask |= POLLPRI;
return mask;
}
+static int dvb_demux_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct dmxdev_filter *dmxdevfilter = filp->private_data;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+ int ret;
+ int vma_size;
+ int buffer_size;
+
+ vma_size = vma->vm_end - vma->vm_start;
+
+ if (vma->vm_flags & VM_WRITE)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+
+ if ((!dmxdevfilter->buffer.data) ||
+ (dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL)) {
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+ return -EINVAL;
+ }
+
+ /* Make sure requested mapping is not larger than buffer size */
+ buffer_size = dmxdevfilter->buffer.size + (PAGE_SIZE-1);
+ buffer_size = buffer_size & ~(PAGE_SIZE-1);
+
+ if (vma_size != buffer_size) {
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+ return -EINVAL;
+ }
+
+ ret = remap_vmalloc_range(vma, dmxdevfilter->buffer.data, 0);
+ if (ret) {
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+ }
+
+ vma->vm_flags |= VM_DONTDUMP;
+ vma->vm_flags |= VM_DONTEXPAND;
+
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+
+ return 0;
+}
+
static int dvb_demux_release(struct inode *inode, struct file *file)
{
struct dmxdev_filter *dmxdevfilter = file->private_data;
struct dmxdev *dmxdev = dmxdevfilter->dev;
-
int ret;
ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
@@ -1120,6 +4614,8 @@ static int dvb_demux_release(struct inode *inode, struct file *file)
mutex_lock(&dmxdev->mutex);
dmxdev->dvbdev->users--;
if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
mutex_unlock(&dmxdev->mutex);
wake_up(&dmxdev->dvbdev->wait_queue);
} else
@@ -1136,6 +4632,10 @@ static const struct file_operations dvb_demux_fops = {
.release = dvb_demux_release,
.poll = dvb_demux_poll,
.llseek = default_llseek,
+ .mmap = dvb_demux_mmap,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = dvb_demux_compat_ioctl,
+#endif
};
static const struct dvb_device dvbdev_demux = {
@@ -1161,11 +4661,44 @@ static int dvb_dvr_do_ioctl(struct file *file,
switch (cmd) {
case DMX_SET_BUFFER_SIZE:
- ret = dvb_dvr_set_buffer_size(dmxdev, arg);
+ ret = dvb_dvr_set_buffer_size(dmxdev, file->f_flags, arg);
+ break;
+
+ case DMX_SET_BUFFER_MODE:
+ ret = dvb_dvr_set_buffer_mode(dmxdev, file->f_flags,
+ *(enum dmx_buffer_mode *)parg);
+ break;
+
+ case DMX_SET_BUFFER:
+ ret = dvb_dvr_set_buffer(dmxdev, file->f_flags, parg);
+ break;
+
+ case DMX_GET_BUFFER_STATUS:
+ ret = dvb_dvr_get_buffer_status(dmxdev, file->f_flags, parg);
+ break;
+
+ case DMX_RELEASE_DATA:
+ ret = dvb_dvr_release_data(dmxdev, file->f_flags, arg);
+ break;
+
+ case DMX_FEED_DATA:
+ ret = dvb_dvr_feed_data(dmxdev, file->f_flags, arg);
+ break;
+
+ case DMX_GET_EVENT:
+ ret = dvb_dvr_get_event(dmxdev, file->f_flags, parg);
+ break;
+
+ case DMX_PUSH_OOB_COMMAND:
+ ret = dvb_dvr_push_oob_cmd(dmxdev, file->f_flags, parg);
+ break;
+
+ case DMX_FLUSH_BUFFER:
+ ret = dvb_dvr_flush_buffer(dmxdev, file->f_flags);
break;
default:
- ret = -EINVAL;
+ ret = -ENOIOCTLCMD;
break;
}
mutex_unlock(&dmxdev->mutex);
@@ -1173,10 +4706,18 @@ static int dvb_dvr_do_ioctl(struct file *file,
}
static long dvb_dvr_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+ unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl);
+}
+
+#ifdef CONFIG_COMPAT
+static long dvb_dvr_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl);
}
+#endif
static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
{
@@ -1184,21 +4725,31 @@ static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
struct dmxdev *dmxdev = dvbdev->priv;
unsigned int mask = 0;
- dprintk("function : %s\n", __func__);
-
- if (dmxdev->exit)
- return POLLERR;
-
- poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+ pr_debug("function : %s\n", __func__);
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
- if (dmxdev->dvr_buffer.error)
- mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+ poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
+ if (dmxdev->dvr_buffer.error) {
+ mask |= (POLLIN | POLLRDNORM | POLLERR);
+ if (dmxdev->dvr_buffer.error == -EOVERFLOW)
+ mask |= POLLPRI;
+ }
if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
- mask |= (POLLIN | POLLRDNORM | POLLPRI);
- } else
- mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+ mask |= (POLLIN | POLLRDNORM);
+
+ if (dmxdev->dvr_output_events.wakeup_events_counter >=
+ dmxdev->dvr_output_events.event_mask.wakeup_threshold)
+ mask |= POLLPRI;
+ } else {
+ poll_wait(file, &dmxdev->dvr_input_buffer.queue, wait);
+ if (dmxdev->dvr_input_buffer.error)
+ mask |= (POLLOUT | POLLRDNORM | POLLPRI | POLLERR);
+
+ if (dvb_ringbuffer_free(&dmxdev->dvr_input_buffer))
+ mask |= (POLLOUT | POLLRDNORM | POLLPRI);
+ }
return mask;
}
@@ -1207,7 +4758,11 @@ static const struct file_operations dvb_dvr_fops = {
.owner = THIS_MODULE,
.read = dvb_dvr_read,
.write = dvb_dvr_write,
+ .mmap = dvb_dvr_mmap,
.unlocked_ioctl = dvb_dvr_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = dvb_dvr_compat_ioctl,
+#endif
.open = dvb_dvr_open,
.release = dvb_dvr_release,
.poll = dvb_dvr_poll,
@@ -1223,9 +4778,94 @@ static const struct dvb_device dvbdev_dvr = {
#endif
.fops = &dvb_dvr_fops
};
+
+
+/**
+ * debugfs service to print active filters information.
+ */
+static int dvb_dmxdev_dbgfs_print(struct seq_file *s, void *p)
+{
+ int i;
+ struct dmxdev *dmxdev = s->private;
+ struct dmxdev_filter *filter;
+ int active_count = 0;
+ struct dmx_buffer_status buffer_status;
+ struct dmx_scrambling_bits scrambling_bits;
+ static const char * const pes_feeds[] = {"DEC", "PES", "DVR", "REC"};
+ int ret;
+
+ if (!dmxdev)
+ return 0;
+
+ for (i = 0; i < dmxdev->filternum; i++) {
+ filter = &dmxdev->filter[i];
+ if (filter->state >= DMXDEV_STATE_GO) {
+ active_count++;
+
+ seq_printf(s, "filter_%02d - ", i);
+
+ if (filter->type == DMXDEV_TYPE_SEC) {
+ seq_puts(s, "type: SEC, ");
+ seq_printf(s, "PID %04d ",
+ filter->params.sec.pid);
+ scrambling_bits.pid = filter->params.sec.pid;
+ } else {
+ seq_printf(s, "type: %s, ",
+ pes_feeds[filter->params.pes.output]);
+ seq_printf(s, "PID: %04d ",
+ filter->params.pes.pid);
+ scrambling_bits.pid = filter->params.pes.pid;
+ }
+
+ dvb_dmxdev_get_scrambling_bits(filter,
+ &scrambling_bits);
+
+ if (filter->type == DMXDEV_TYPE_PES &&
+ filter->params.pes.output == DMX_OUT_TS_TAP)
+ ret = dvb_dvr_get_buffer_status(dmxdev,
+ O_RDONLY, &buffer_status);
+ else
+ ret = dvb_dmxdev_get_buffer_status(filter,
+ &buffer_status);
+ if (!ret) {
+ seq_printf(s, "size: %08d, ",
+ buffer_status.size);
+ seq_printf(s, "fullness: %08d, ",
+ buffer_status.fullness);
+ seq_printf(s, "error: %d, ",
+ buffer_status.error);
+ }
+
+ seq_printf(s, "scramble: %d, ",
+ scrambling_bits.value);
+ seq_printf(s, "secured: %d\n",
+ filter->sec_mode.is_secured);
+ }
+ }
+
+ if (!active_count)
+ seq_puts(s, "No active filters\n");
+
+ return 0;
+}
+
+static int dvb_dmxdev_dbgfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dvb_dmxdev_dbgfs_print, inode->i_private);
+}
+
+static const struct file_operations dbgfs_filters_fops = {
+ .open = dvb_dmxdev_dbgfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
{
int i;
+ struct dmx_caps caps;
if (dmxdev->demux->open(dmxdev->demux) < 0)
return -EUSERS;
@@ -1234,8 +4874,12 @@ int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
if (!dmxdev->filter)
return -ENOMEM;
+ dmxdev->playback_mode = DMX_PB_MODE_PUSH;
+ dmxdev->demux->dvr_input_protected = 0;
+
mutex_init(&dmxdev->mutex);
spin_lock_init(&dmxdev->lock);
+ spin_lock_init(&dmxdev->dvr_in_lock);
for (i = 0; i < dmxdev->filternum; i++) {
dmxdev->filter[i].dev = dmxdev;
dmxdev->filter[i].buffer.data = NULL;
@@ -1249,6 +4893,19 @@ int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
dmxdev, DVB_DEVICE_DVR);
dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
+ dvb_ringbuffer_init(&dmxdev->dvr_input_buffer, NULL, 8192);
+
+ /* Disable auto buffer flushing if plugin does not allow it */
+ if (dmxdev->demux->get_caps) {
+ dmxdev->demux->get_caps(dmxdev->demux, &caps);
+ if (!(caps.caps & DMX_CAP_AUTO_BUFFER_FLUSH))
+ overflow_auto_flush = 0;
+ }
+
+ if (dmxdev->demux->debugfs_demux_dir)
+ debugfs_create_file("filters", S_IRUGO,
+ dmxdev->demux->debugfs_demux_dir, dmxdev,
+ &dbgfs_filters_fops);
return 0;
}
diff --git a/drivers/media/dvb-core/dmxdev.h b/drivers/media/dvb-core/dmxdev.h
index 48c6cf92ab99..ad007f4fb9ac 100644
--- a/drivers/media/dvb-core/dmxdev.h
+++ b/drivers/media/dvb-core/dmxdev.h
@@ -33,7 +33,7 @@
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-
+#include <linux/kthread.h>
#include <linux/dvb/dmx.h>
#include "dvbdev.h"
@@ -57,10 +57,87 @@ enum dmxdev_state {
struct dmxdev_feed {
u16 pid;
+ struct dmx_indexing_params idx_params;
+ struct dmx_cipher_operations cipher_ops;
struct dmx_ts_feed *ts;
struct list_head next;
};
+struct dmxdev_sec_feed {
+ struct dmx_section_feed *feed;
+ struct dmx_cipher_operations cipher_ops;
+};
+
+struct dmxdev_events_queue {
+ /*
+ * indices used to manage events queue.
+ * read_index advanced when relevant data is read
+ * from the buffer.
+ * notified_index is the index from which next events
+ * are returned.
+ * read_index <= notified_index <= write_index
+ *
+ * If user reads the data without getting the respective
+ * event first, the read/notified indices are updated
+ * automatically to reflect the actual data that exist
+ * in the buffer.
+ */
+ u32 read_index;
+ u32 write_index;
+ u32 notified_index;
+
+ /* Bytes read by user without having respective event in the queue */
+ u32 bytes_read_no_event;
+
+ /* internal tracking of PES and recording events */
+ u32 current_event_data_size;
+ u32 current_event_start_offset;
+
+ /* current setting of the events masking */
+ struct dmx_events_mask event_mask;
+
+ /*
+ * indicates if an event used for data-reading from demux
+ * filter is enabled or not. These are events on which
+ * user may wait for before calling read() on the demux filter.
+ */
+ int data_read_event_masked;
+
+ /*
+ * holds the current number of pending events in the
+ * events queue that are considered as a wake-up source
+ */
+ u32 wakeup_events_counter;
+
+ struct dmx_filter_event queue[DMX_EVENT_QUEUE_SIZE];
+};
+
+#define DMX_MIN_INSERTION_REPETITION_TIME 25 /* in msec */
+struct ts_insertion_buffer {
+ /* work scheduled for insertion of this buffer */
+ struct delayed_work dwork;
+
+ struct list_head next;
+
+ /* buffer holding TS packets for insertion */
+ char *buffer;
+
+ /* buffer size */
+ size_t size;
+
+ /* buffer ID from user */
+ u32 identifier;
+
+ /* repetition time for the buffer insertion */
+ u32 repetition_time;
+
+ /* the recording filter to which this buffer belongs */
+ struct dmxdev_filter *dmxdevfilter;
+
+ /* indication whether insertion should be aborted */
+ int abort;
+};
+
struct dmxdev_filter {
union {
struct dmx_section_filter *sec;
@@ -69,7 +146,7 @@ struct dmxdev_filter {
union {
/* list of TS and PES feeds (struct dmxdev_feed) */
struct list_head ts;
- struct dmx_section_feed *sec;
+ struct dmxdev_sec_feed sec;
} feed;
union {
@@ -77,19 +154,37 @@ struct dmxdev_filter {
struct dmx_pes_filter_params pes;
} params;
+ struct dmxdev_events_queue events;
+
enum dmxdev_type type;
enum dmxdev_state state;
struct dmxdev *dev;
struct dvb_ringbuffer buffer;
+ void *priv_buff_handle;
+ enum dmx_buffer_mode buffer_mode;
struct mutex mutex;
+ /* for recording output */
+ enum dmx_tsp_format_t dmx_tsp_format;
+ u32 rec_chunk_size;
+
+ /* list of buffers used for insertion (struct ts_insertion_buffer) */
+ struct list_head insertion_buffers;
+
+ /* End-of-stream indication has been received */
+ int eos_state;
+
/* only for sections */
struct timer_list timer;
int todo;
u8 secheader[3];
-};
+ struct dmx_secure_mode sec_mode;
+
+ /* Decoder buffer(s) related */
+ struct dmx_decoder_buffers decoder_buffers;
+};
struct dmxdev {
struct dvb_device *dvbdev;
@@ -100,18 +195,52 @@ struct dmxdev {
int filternum;
int capabilities;
+#define DMXDEV_CAP_DUPLEX 0x01
+
+ enum dmx_playback_mode_t playback_mode;
+ dmx_source_t source;
unsigned int exit:1;
-#define DMXDEV_CAP_DUPLEX 1
+ unsigned int dvr_in_exit:1;
+ unsigned int dvr_processing_input:1;
+
struct dmx_frontend *dvr_orig_fe;
struct dvb_ringbuffer dvr_buffer;
+ void *dvr_priv_buff_handle;
+ enum dmx_buffer_mode dvr_buffer_mode;
+ struct dmxdev_events_queue dvr_output_events;
+ struct dmxdev_filter *dvr_feed;
+ int dvr_feeds_count;
+
+ struct dvb_ringbuffer dvr_input_buffer;
+ enum dmx_buffer_mode dvr_input_buffer_mode;
+ struct task_struct *dvr_input_thread;
+ /* DVR commands (data feed / OOB command) queue */
+ struct dvb_ringbuffer dvr_cmd_buffer;
+
#define DVR_BUFFER_SIZE (10*188*1024)
struct mutex mutex;
spinlock_t lock;
+ spinlock_t dvr_in_lock;
+};
+
+enum dvr_cmd {
+ DVR_DATA_FEED_CMD,
+ DVR_OOB_CMD
};
+struct dvr_command {
+ enum dvr_cmd type;
+ union {
+ struct dmx_oob_command oobcmd;
+ size_t data_feed_count;
+ } cmd;
+};
+
+#define DVR_CMDS_BUFFER_SIZE (sizeof(struct dvr_command)*500)
+
int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *);
void dvb_dmxdev_release(struct dmxdev *dmxdev);
diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c
index 0cc5e935166c..d45bcc55b76a 100644
--- a/drivers/media/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb-core/dvb_demux.c
@@ -55,11 +55,118 @@ module_param(dvb_demux_feed_err_pkts, int, 0644);
MODULE_PARM_DESC(dvb_demux_feed_err_pkts,
"when set to 0, drop packets with the TEI bit set (1 by default)");
+/* counter advancing for each new dvb-demux device */
+static int dvb_demux_index;
+
+static int dvb_demux_performancecheck;
+module_param(dvb_demux_performancecheck, int, 0644);
+MODULE_PARM_DESC(dvb_demux_performancecheck,
+ "enable transport stream performance check, reported through debugfs");
+
#define dprintk_tscheck(x...) do { \
if (dvb_demux_tscheck && printk_ratelimit()) \
printk(x); \
} while (0)
+static const struct dvb_dmx_video_patterns mpeg2_seq_hdr = {
+ {0x00, 0x00, 0x01, 0xB3},
+ {0xFF, 0xFF, 0xFF, 0xFF},
+ 4,
+ DMX_IDX_MPEG_SEQ_HEADER
+};
+
+static const struct dvb_dmx_video_patterns mpeg2_gop = {
+ {0x00, 0x00, 0x01, 0xB8},
+ {0xFF, 0xFF, 0xFF, 0xFF},
+ 4,
+ DMX_IDX_MPEG_GOP
+};
+
+static const struct dvb_dmx_video_patterns mpeg2_iframe = {
+ {0x00, 0x00, 0x01, 0x00, 0x00, 0x08},
+ {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38},
+ 6,
+ DMX_IDX_MPEG_I_FRAME_START
+};
+
+static const struct dvb_dmx_video_patterns mpeg2_pframe = {
+ {0x00, 0x00, 0x01, 0x00, 0x00, 0x10},
+ {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38},
+ 6,
+ DMX_IDX_MPEG_P_FRAME_START
+};
+
+static const struct dvb_dmx_video_patterns mpeg2_bframe = {
+ {0x00, 0x00, 0x01, 0x00, 0x00, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38},
+ 6,
+ DMX_IDX_MPEG_B_FRAME_START
+};
+
+static const struct dvb_dmx_video_patterns h264_sps = {
+ {0x00, 0x00, 0x01, 0x07},
+ {0xFF, 0xFF, 0xFF, 0x1F},
+ 4,
+ DMX_IDX_H264_SPS
+};
+
+static const struct dvb_dmx_video_patterns h264_pps = {
+ {0x00, 0x00, 0x01, 0x08},
+ {0xFF, 0xFF, 0xFF, 0x1F},
+ 4,
+ DMX_IDX_H264_PPS
+};
+
+static const struct dvb_dmx_video_patterns h264_idr = {
+ {0x00, 0x00, 0x01, 0x05, 0x80},
+ {0xFF, 0xFF, 0xFF, 0x1F, 0x80},
+ 5,
+ DMX_IDX_H264_IDR_START
+};
+
+static const struct dvb_dmx_video_patterns h264_non_idr = {
+ {0x00, 0x00, 0x01, 0x01, 0x80},
+ {0xFF, 0xFF, 0xFF, 0x1F, 0x80},
+ 5,
+ DMX_IDX_H264_NON_IDR_START
+};
+
+static const struct dvb_dmx_video_patterns h264_non_access_unit_del = {
+ {0x00, 0x00, 0x01, 0x09},
+ {0xFF, 0xFF, 0xFF, 0x1F},
+ 4,
+ DMX_IDX_H264_ACCESS_UNIT_DEL
+};
+
+static const struct dvb_dmx_video_patterns h264_non_sei = {
+ {0x00, 0x00, 0x01, 0x06},
+ {0xFF, 0xFF, 0xFF, 0x1F},
+ 4,
+ DMX_IDX_H264_SEI
+};
+
+static const struct dvb_dmx_video_patterns vc1_seq_hdr = {
+ {0x00, 0x00, 0x01, 0x0F},
+ {0xFF, 0xFF, 0xFF, 0xFF},
+ 4,
+ DMX_IDX_VC1_SEQ_HEADER
+};
+
+static const struct dvb_dmx_video_patterns vc1_entry_point = {
+ {0x00, 0x00, 0x01, 0x0E},
+ {0xFF, 0xFF, 0xFF, 0xFF},
+ 4,
+ DMX_IDX_VC1_ENTRY_POINT
+};
+
+static const struct dvb_dmx_video_patterns vc1_frame = {
+ {0x00, 0x00, 0x01, 0x0D},
+ {0xFF, 0xFF, 0xFF, 0xFF},
+ 4,
+ DMX_IDX_VC1_FRAME_START
+};
+
+
/******************************************************************************
* static inlined helper functions
******************************************************************************/
@@ -69,9 +176,9 @@ static inline u16 section_length(const u8 *buf)
return 3 + ((buf[1] & 0x0f) << 8) + buf[2];
}
-static inline u16 ts_pid(const u8 *buf)
+static inline u8 ts_scrambling_ctrl(const u8 *buf)
{
- return ((buf[1] & 0x1f) << 8) + buf[2];
+ return (buf[3] >> 6) & 0x3;
}
static inline u8 payload(const u8 *tsp)
@@ -100,37 +207,360 @@ static void dvb_dmx_memcopy(struct dvb_demux_feed *f, u8 *d, const u8 *s,
memcpy(d, s, len);
}
+static u32 dvb_dmx_calc_time_delta(struct timespec past_time)
+{
+ struct timespec curr_time, delta_time;
+ u64 delta_time_us;
+
+ curr_time = current_kernel_time();
+ delta_time = timespec_sub(curr_time, past_time);
+ delta_time_us = ((s64)delta_time.tv_sec * USEC_PER_SEC) +
+ delta_time.tv_nsec / 1000;
+
+ return (u32)delta_time_us;
+}
+
/******************************************************************************
* Software filter functions
******************************************************************************/
+/*
+ * Check if two patterns are identical, taking mask into consideration.
+ * @pattern1: the first byte pattern to compare.
+ * @pattern2: the second byte pattern to compare.
+ * @mask: the bit mask to use.
+ * @pattern_size: the length of both patterns and the mask, in bytes.
+ *
+ * Return: 1 if patterns match, 0 otherwise.
+ */
+static inline int dvb_dmx_patterns_match(const u8 *pattern1, const u8 *pattern2,
+ const u8 *mask, size_t pattern_size)
+{
+ int i;
+
+ /*
+ * Assumption: it is OK to access pattern1, pattern2 and mask.
+ * This function performs no sanity checks to keep things fast.
+ */
+
+ for (i = 0; i < pattern_size; i++)
+ if ((pattern1[i] & mask[i]) != (pattern2[i] & mask[i]))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * dvb_dmx_video_pattern_search -
+ * search for framing patterns in a given buffer.
+ *
+ * Optimized version: first search for a common substring, e.g. 0x00 0x00 0x01.
+ * If this string is found, go over all the given patterns (all must start
+ * with this string) and search for their ending in the buffer.
+ *
+ * Assumption: the patterns we look for do not spread over more than two
+ * buffers.
+ *
+ * @paterns: the full patterns information to look for.
+ * @patterns_num: the number of patterns to look for.
+ * @buf: the buffer to search.
+ * @buf_size: the size of the buffer to search. we search the entire buffer.
+ * @prefix_size_masks: a bit mask (per pattern) of possible prefix sizes to use
+ * when searching for a pattern that started at the last buffer.
+ * Updated in this function for use in the next lookup.
+ * @results: lookup results (offset, type, used_prefix_size) per found pattern,
+ * up to DVB_DMX_MAX_FOUND_PATTERNS.
+ *
+ * Return:
+ * Number of patterns found (up to DVB_DMX_MAX_FOUND_PATTERNS).
+ * 0 if pattern was not found.
+ * error value on failure.
+ */
+int dvb_dmx_video_pattern_search(
+ const struct dvb_dmx_video_patterns
+ *patterns[DVB_DMX_MAX_SEARCH_PATTERN_NUM],
+ int patterns_num,
+ const u8 *buf,
+ size_t buf_size,
+ struct dvb_dmx_video_prefix_size_masks *prefix_size_masks,
+ struct dvb_dmx_video_patterns_results *results)
+{
+ int i, j;
+ unsigned int current_size;
+ u32 prefix;
+ int found = 0;
+ int start_offset = 0;
+ /* the starting common substring to look for */
+ u8 string[] = {0x00, 0x00, 0x01};
+ /* the mask for the starting string */
+ u8 string_mask[] = {0xFF, 0xFF, 0xFF};
+ /* the size of the starting string (in bytes) */
+ size_t string_size = 3;
+
+ if ((patterns == NULL) || (patterns_num <= 0) || (buf == NULL))
+ return -EINVAL;
+
+ memset(results, 0, sizeof(struct dvb_dmx_video_patterns_results));
+
+ /*
+ * handle prefix - disregard string, simply check all patterns,
+ * looking for a matching suffix at the very beginning of the buffer.
+ */
+ for (j = 0; (j < patterns_num) && !found; j++) {
+ prefix = prefix_size_masks->size_mask[j];
+ current_size = 32;
+ while (prefix) {
+ if (prefix & (0x1 << (current_size - 1))) {
+ /*
+ * check that we don't look further
+ * than buf_size boundary
+ */
+ if ((int)(patterns[j]->size - current_size) >
+ buf_size)
+ break;
+
+ if (dvb_dmx_patterns_match(
+ (patterns[j]->pattern + current_size),
+ buf, (patterns[j]->mask + current_size),
+ (patterns[j]->size - current_size))) {
+
+ /*
+ * pattern found using prefix at the
+ * very beginning of the buffer, so
+ * offset is 0, but we already zeroed
+ * everything in the beginning of the
+ * function. that's why the next line
+ * is commented.
+ */
+ /* results->info[found].offset = 0; */
+ results->info[found].type =
+ patterns[j]->type;
+ results->info[found].used_prefix_size =
+ current_size;
+ found++;
+ /*
+ * save offset to start looking from
+ * in the buffer, to avoid reusing the
+ * data of a pattern we already found.
+ */
+ start_offset = (patterns[j]->size -
+ current_size);
+
+ if (found >= DVB_DMX_MAX_FOUND_PATTERNS)
+ goto next_prefix_lookup;
+ /*
+ * we don't want to search for the same
+ * pattern with several possible prefix
+ * sizes if we have already found it,
+ * so we break from the inner loop.
+ * since we incremented 'found', we
+ * will not search for additional
+ * patterns using a prefix - that would
+ * imply ambiguous patterns where one
+ * pattern can be included in another.
+ * the for loop will exit.
+ */
+ break;
+ }
+ }
+ prefix &= ~(0x1 << (current_size - 1));
+ current_size--;
+ }
+ }
+
+ /*
+ * Search buffer for entire pattern, starting with the string.
+ * Note the external for loop does not execute if buf_size is
+ * smaller than string_size (the cast to int is required, since
+ * size_t is unsigned).
+ */
+ for (i = start_offset; i < (int)(buf_size - string_size + 1); i++) {
+ if (dvb_dmx_patterns_match(string, (buf + i), string_mask,
+ string_size)) {
+ /* now search for patterns: */
+ for (j = 0; j < patterns_num; j++) {
+ /* avoid overflow to next buffer */
+ if ((i + patterns[j]->size) > buf_size)
+ continue;
+
+ if (dvb_dmx_patterns_match(
+ (patterns[j]->pattern + string_size),
+ (buf + i + string_size),
+ (patterns[j]->mask + string_size),
+ (patterns[j]->size - string_size))) {
+
+ results->info[found].offset = i;
+ results->info[found].type =
+ patterns[j]->type;
+ /*
+ * save offset to start next prefix
+ * lookup, to avoid reusing the data
+ * of any pattern we already found.
+ */
+ if ((i + patterns[j]->size) >
+ start_offset)
+ start_offset = (i +
+ patterns[j]->size);
+ /*
+ * did not use a prefix to find this
+ * pattern, but we zeroed everything
+ * in the beginning of the function.
+ * So no need to zero used_prefix_size
+ * for results->info[found]
+ */
+
+ found++;
+ if (found >= DVB_DMX_MAX_FOUND_PATTERNS)
+ goto next_prefix_lookup;
+ /*
+ * theoretically we don't have to break
+ * here, but we don't want to search
+ * for the other matching patterns on
+ * the very same same place in the
+ * buffer. That would mean the
+ * (pattern & mask) combinations are
+ * not unique. So we break from inner
+ * loop and move on to the next place
+ * in the buffer.
+ */
+ break;
+ }
+ }
+ }
+ }
+
+next_prefix_lookup:
+ /* check for possible prefix sizes for the next buffer */
+ for (j = 0; j < patterns_num; j++) {
+ prefix_size_masks->size_mask[j] = 0;
+ for (i = 1; i < patterns[j]->size; i++) {
+ /*
+ * avoid looking outside of the buffer
+ * or reusing previously used data.
+ */
+ if (i > (buf_size - start_offset))
+ break;
+
+ if (dvb_dmx_patterns_match(patterns[j]->pattern,
+ (buf + buf_size - i),
+ patterns[j]->mask, i)) {
+ prefix_size_masks->size_mask[j] |=
+ (1 << (i - 1));
+ }
+ }
+ }
+
+ return found;
+}
+EXPORT_SYMBOL(dvb_dmx_video_pattern_search);
+
+/**
+ * dvb_dmx_notify_section_event() - Notify demux event for all filters of a
+ * specified section feed.
+ *
+ * @feed: dvb_demux_feed object
+ * @event: demux event to notify
+ * @should_lock: specifies whether the function should lock the demux
+ *
+ * Caller is responsible for locking the demux properly, either by doing the
+ * locking itself and setting 'should_lock' to 0, or have the function do it
+ * by setting 'should_lock' to 1.
+ */
+int dvb_dmx_notify_section_event(struct dvb_demux_feed *feed,
+ struct dmx_data_ready *event, int should_lock)
+{
+ struct dvb_demux_filter *f;
+
+ if (feed == NULL || event == NULL || feed->type != DMX_TYPE_SEC)
+ return -EINVAL;
+
+ if (!should_lock && !spin_is_locked(&feed->demux->lock))
+ return -EINVAL;
+
+ if (should_lock)
+ spin_lock(&feed->demux->lock);
+
+ f = feed->filter;
+ while (f && feed->feed.sec.is_filtering) {
+ feed->data_ready_cb.sec(&f->filter, event);
+ f = f->next;
+ }
+
+ if (should_lock)
+ spin_unlock(&feed->demux->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(dvb_dmx_notify_section_event);
+
+static int dvb_dmx_check_pes_end(struct dvb_demux_feed *feed)
+{
+ struct dmx_data_ready data;
+
+ if (!feed->pusi_seen)
+ return 0;
+
+ data.status = DMX_OK_PES_END;
+ data.data_length = 0;
+ data.pes_end.start_gap = 0;
+ data.pes_end.actual_length = feed->peslen;
+ data.pes_end.disc_indicator_set = 0;
+ data.pes_end.pes_length_mismatch = 0;
+ data.pes_end.stc = 0;
+ data.pes_end.tei_counter = feed->pes_tei_counter;
+ data.pes_end.cont_err_counter = feed->pes_cont_err_counter;
+ data.pes_end.ts_packets_num = feed->pes_ts_packets_num;
+
+ return feed->data_ready_cb.ts(&feed->feed.ts, &data);
+}
+
static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
const u8 *buf)
{
int count = payload(buf);
int p;
- //int ccok;
- //u8 cc;
+ int ccok;
+ u8 cc;
+ int ret;
if (count == 0)
return -1;
p = 188 - count;
- /*
cc = buf[3] & 0x0f;
- ccok = ((feed->cc + 1) & 0x0f) == cc;
+ if (feed->first_cc)
+ ccok = 1;
+ else
+ ccok = ((feed->cc + 1) & 0x0f) == cc;
+
+ feed->first_cc = 0;
feed->cc = cc;
- if (!ccok)
- printk("missed packet!\n");
- */
- if (buf[1] & 0x40) // PUSI ?
- feed->peslen = 0xfffa;
+ /* PUSI ? */
+ if (buf[1] & 0x40) {
+ dvb_dmx_check_pes_end(feed);
+ feed->pusi_seen = 1;
+ feed->peslen = 0;
+ feed->pes_tei_counter = 0;
+ feed->pes_cont_err_counter = 0;
+ feed->pes_ts_packets_num = 0;
+ }
+
+ if (feed->pusi_seen == 0)
+ return 0;
+
+ ret = feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK);
- feed->peslen += count;
+ /* Verify TS packet was copied successfully */
+ if (!ret) {
+ feed->pes_cont_err_counter += !ccok;
+ feed->pes_tei_counter += (buf[1] & 0x80) ? 1 : 0;
+ feed->pes_ts_packets_num++;
+ feed->peslen += count;
+ }
- return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts);
+ return ret;
}
static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
@@ -152,7 +582,7 @@ static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
return 0;
return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,
- NULL, 0, &f->filter);
+ NULL, 0, &f->filter, DMX_OK);
}
static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
@@ -169,10 +599,28 @@ static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
return 0;
if (sec->check_crc) {
+ struct timespec pre_crc_time;
+
+ if (dvb_demux_performancecheck)
+ pre_crc_time = current_kernel_time();
+
section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
if (section_syntax_indicator &&
- demux->check_crc32(feed, sec->secbuf, sec->seclen))
+ demux->check_crc32(feed, sec->secbuf, sec->seclen)) {
+ if (dvb_demux_performancecheck)
+ demux->total_crc_time +=
+ dvb_dmx_calc_time_delta(pre_crc_time);
+
+ /* Notify on CRC error */
+ feed->cb.sec(NULL, 0, NULL, 0,
+ &f->filter, DMX_CRC_ERROR);
+
return -1;
+ }
+
+ if (dvb_demux_performancecheck)
+ demux->total_crc_time +=
+ dvb_dmx_calc_time_delta(pre_crc_time);
}
do {
@@ -287,7 +735,7 @@ static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed,
return 0;
}
-static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
+static int dvb_dmx_swfilter_section_one_packet(struct dvb_demux_feed *feed,
const u8 *buf)
{
u8 p, count;
@@ -302,7 +750,16 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
p = 188 - count; /* payload start */
cc = buf[3] & 0x0f;
- ccok = ((feed->cc + 1) & 0x0f) == cc;
+ if (feed->first_cc)
+ ccok = 1;
+ else
+ ccok = ((feed->cc + 1) & 0x0f) == cc;
+
+ /* discard TS packets holding sections with TEI bit set */
+ if (buf[1] & 0x80)
+ return -EINVAL;
+
+ feed->first_cc = 0;
feed->cc = cc;
if (buf[3] & 0x20) {
@@ -356,28 +813,656 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
return 0;
}
+/*
+ * dvb_dmx_swfilter_section_packet - wrapper for section filtering of single
+ * TS packet.
+ *
+ * @feed: dvb demux feed
+ * @buf: buffer containing the TS packet
+ * @should_lock: specifies demux locking semantics: if not set, proper demux
+ * locking is expected to have been done by the caller.
+ *
+ * Return error status
+ */
+int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
+ const u8 *buf, int should_lock)
+{
+ int ret;
+
+ if (!should_lock && !spin_is_locked(&feed->demux->lock)) {
+ pr_err("%s: demux spinlock should have been locked\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (should_lock)
+ spin_lock(&feed->demux->lock);
+
+ ret = dvb_dmx_swfilter_section_one_packet(feed, buf);
+
+ if (should_lock)
+ spin_unlock(&feed->demux->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_section_packet);
+
+static int dvb_demux_idx_event_sort(struct dmx_index_event_info *curr,
+ struct dmx_index_event_info *new)
+{
+ if (curr->match_tsp_num > new->match_tsp_num)
+ return 0;
+
+ if (curr->match_tsp_num < new->match_tsp_num)
+ return 1;
+ /*
+ * In case TSP numbers are equal, sort according to event type giving
+ * priority to PUSI events first, then RAI and finally framing events.
+ */
+ if ((curr->type & DMX_IDX_RAI && new->type & DMX_IDX_PUSI) ||
+ (!(curr->type & DMX_IDX_PUSI) && !(curr->type & DMX_IDX_RAI) &&
+ new->type & (DMX_IDX_PUSI | DMX_IDX_RAI)))
+ return 0;
+
+ return 1;
+}
+
+static int dvb_demux_save_idx_event(struct dvb_demux_feed *feed,
+ struct dmx_index_event_info *idx_event,
+ int traverse_from_tail)
+{
+ struct dmx_index_entry *idx_entry;
+ struct dmx_index_entry *curr_entry;
+ struct list_head *pos;
+
+ /* get entry from free list */
+ if (list_empty(&feed->rec_info->idx_info.free_list)) {
+ pr_err("%s: index free list is empty\n", __func__);
+ return -ENOMEM;
+ }
+
+ idx_entry = list_first_entry(&feed->rec_info->idx_info.free_list,
+ struct dmx_index_entry, next);
+ list_del(&idx_entry->next);
+
+ idx_entry->event = *idx_event;
+
+ pos = &feed->rec_info->idx_info.ready_list;
+ if (traverse_from_tail) {
+ list_for_each_entry_reverse(curr_entry,
+ &feed->rec_info->idx_info.ready_list, next) {
+ if (dvb_demux_idx_event_sort(&curr_entry->event,
+ idx_event)) {
+ pos = &curr_entry->next;
+ break;
+ }
+ }
+ } else {
+ list_for_each_entry(curr_entry,
+ &feed->rec_info->idx_info.ready_list, next) {
+ if (!dvb_demux_idx_event_sort(&curr_entry->event,
+ idx_event)) {
+ pos = &curr_entry->next;
+ break;
+ }
+ }
+ }
+
+ if (traverse_from_tail)
+ list_add(&idx_entry->next, pos);
+ else
+ list_add_tail(&idx_entry->next, pos);
+
+ return 0;
+}
+
+int dvb_demux_push_idx_event(struct dvb_demux_feed *feed,
+ struct dmx_index_event_info *idx_event, int should_lock)
+{
+ int ret;
+
+ if (!should_lock && !spin_is_locked(&feed->demux->lock))
+ return -EINVAL;
+
+ if (should_lock)
+ spin_lock(&feed->demux->lock);
+ ret = dvb_demux_save_idx_event(feed, idx_event, 1);
+ if (should_lock)
+ spin_unlock(&feed->demux->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(dvb_demux_push_idx_event);
+
+static inline void dvb_dmx_notify_indexing(struct dvb_demux_feed *feed)
+{
+ struct dmx_data_ready dmx_data_ready;
+ struct dmx_index_entry *curr_entry;
+ struct list_head *n, *pos;
+
+ dmx_data_ready.status = DMX_OK_IDX;
+
+ list_for_each_safe(pos, n, &feed->rec_info->idx_info.ready_list) {
+ curr_entry = list_entry(pos, struct dmx_index_entry, next);
+
+ if ((feed->rec_info->idx_info.min_pattern_tsp_num == (u64)-1) ||
+ (curr_entry->event.match_tsp_num <=
+ feed->rec_info->idx_info.min_pattern_tsp_num)) {
+ dmx_data_ready.idx_event = curr_entry->event;
+ feed->data_ready_cb.ts(&feed->feed.ts, &dmx_data_ready);
+ list_del(&curr_entry->next);
+ list_add_tail(&curr_entry->next,
+ &feed->rec_info->idx_info.free_list);
+ }
+ }
+}
+
+void dvb_dmx_notify_idx_events(struct dvb_demux_feed *feed, int should_lock)
+{
+ if (!should_lock && !spin_is_locked(&feed->demux->lock))
+ return;
+
+ if (should_lock)
+ spin_lock(&feed->demux->lock);
+ dvb_dmx_notify_indexing(feed);
+ if (should_lock)
+ spin_unlock(&feed->demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_notify_idx_events);
+
+static void dvb_dmx_process_pattern_result(struct dvb_demux_feed *feed,
+ struct dvb_dmx_video_patterns_results *patterns, int pattern,
+ u64 curr_stc, u64 prev_stc,
+ u64 curr_match_tsp, u64 prev_match_tsp,
+ u64 curr_pusi_tsp, u64 prev_pusi_tsp)
+{
+ int mpeg_frame_start;
+ int h264_frame_start;
+ int vc1_frame_start;
+ int seq_start;
+ u64 frame_end_in_seq;
+ struct dmx_index_event_info idx_event;
+
+ idx_event.pid = feed->pid;
+ if (patterns->info[pattern].used_prefix_size) {
+ idx_event.match_tsp_num = prev_match_tsp;
+ idx_event.last_pusi_tsp_num = prev_pusi_tsp;
+ idx_event.stc = prev_stc;
+ } else {
+ idx_event.match_tsp_num = curr_match_tsp;
+ idx_event.last_pusi_tsp_num = curr_pusi_tsp;
+ idx_event.stc = curr_stc;
+ }
+
+ /* notify on frame-end if needed */
+ if (feed->prev_frame_valid) {
+ if (feed->prev_frame_type & DMX_IDX_MPEG_I_FRAME_START) {
+ idx_event.type = DMX_IDX_MPEG_I_FRAME_END;
+ frame_end_in_seq = DMX_IDX_MPEG_FIRST_SEQ_FRAME_END;
+ } else if (feed->prev_frame_type & DMX_IDX_MPEG_P_FRAME_START) {
+ idx_event.type = DMX_IDX_MPEG_P_FRAME_END;
+ frame_end_in_seq = DMX_IDX_MPEG_FIRST_SEQ_FRAME_END;
+ } else if (feed->prev_frame_type & DMX_IDX_MPEG_B_FRAME_START) {
+ idx_event.type = DMX_IDX_MPEG_B_FRAME_END;
+ frame_end_in_seq = DMX_IDX_MPEG_FIRST_SEQ_FRAME_END;
+ } else if (feed->prev_frame_type & DMX_IDX_H264_IDR_START) {
+ idx_event.type = DMX_IDX_H264_IDR_END;
+ frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END;
+ } else if (feed->prev_frame_type & DMX_IDX_H264_NON_IDR_START) {
+ idx_event.type = DMX_IDX_H264_NON_IDR_END;
+ frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END;
+ } else {
+ idx_event.type = DMX_IDX_VC1_FRAME_END;
+ frame_end_in_seq = DMX_IDX_VC1_FIRST_SEQ_FRAME_END;
+ }
+
+ if (feed->idx_params.types & idx_event.type)
+ dvb_demux_save_idx_event(feed, &idx_event, 1);
+
+ if (feed->first_frame_in_seq_notified &&
+ feed->idx_params.types & frame_end_in_seq) {
+ idx_event.type = frame_end_in_seq;
+ dvb_demux_save_idx_event(feed, &idx_event, 1);
+ feed->first_frame_in_seq_notified = 0;
+ }
+ }
+
+ seq_start = patterns->info[pattern].type &
+ (DMX_IDX_MPEG_SEQ_HEADER | DMX_IDX_H264_SPS |
+ DMX_IDX_VC1_SEQ_HEADER);
+
+ /* did we find start of sequence/SPS? */
+ if (seq_start) {
+ feed->first_frame_in_seq = 1;
+ feed->first_frame_in_seq_notified = 0;
+ feed->prev_frame_valid = 0;
+ idx_event.type = patterns->info[pattern].type;
+ if (feed->idx_params.types & idx_event.type)
+ dvb_demux_save_idx_event(feed, &idx_event, 1);
+ return;
+ }
+
+ mpeg_frame_start = patterns->info[pattern].type &
+ (DMX_IDX_MPEG_I_FRAME_START |
+ DMX_IDX_MPEG_P_FRAME_START |
+ DMX_IDX_MPEG_B_FRAME_START);
+
+ h264_frame_start = patterns->info[pattern].type &
+ (DMX_IDX_H264_IDR_START | DMX_IDX_H264_NON_IDR_START);
+
+ vc1_frame_start = patterns->info[pattern].type &
+ DMX_IDX_VC1_FRAME_START;
+
+ if (!mpeg_frame_start && !h264_frame_start && !vc1_frame_start) {
+ /* neither sequence nor frame, notify on the entry if needed */
+ idx_event.type = patterns->info[pattern].type;
+ if (feed->idx_params.types & idx_event.type)
+ dvb_demux_save_idx_event(feed, &idx_event, 1);
+ feed->prev_frame_valid = 0;
+ return;
+ }
+
+ /* notify on first frame in sequence/sps if needed */
+ if (feed->first_frame_in_seq) {
+ feed->first_frame_in_seq = 0;
+ feed->first_frame_in_seq_notified = 1;
+ if (mpeg_frame_start)
+ idx_event.type = DMX_IDX_MPEG_FIRST_SEQ_FRAME_START;
+ else if (h264_frame_start)
+ idx_event.type = DMX_IDX_H264_FIRST_SPS_FRAME_START;
+ else
+ idx_event.type = DMX_IDX_VC1_FIRST_SEQ_FRAME_START;
+
+ if (feed->idx_params.types & idx_event.type)
+ dvb_demux_save_idx_event(feed, &idx_event, 1);
+ }
+
+ /* notify on frame start if needed */
+ idx_event.type = patterns->info[pattern].type;
+ if (feed->idx_params.types & idx_event.type)
+ dvb_demux_save_idx_event(feed, &idx_event, 1);
+
+ feed->prev_frame_valid = 1;
+ feed->prev_frame_type = patterns->info[pattern].type;
+}
+
+void dvb_dmx_process_idx_pattern(struct dvb_demux_feed *feed,
+ struct dvb_dmx_video_patterns_results *patterns, int pattern,
+ u64 curr_stc, u64 prev_stc,
+ u64 curr_match_tsp, u64 prev_match_tsp,
+ u64 curr_pusi_tsp, u64 prev_pusi_tsp)
+{
+ spin_lock(&feed->demux->lock);
+ dvb_dmx_process_pattern_result(feed,
+ patterns, pattern,
+ curr_stc, prev_stc,
+ curr_match_tsp, prev_match_tsp,
+ curr_pusi_tsp, prev_pusi_tsp);
+ spin_unlock(&feed->demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_process_idx_pattern);
+
+static void dvb_dmx_index(struct dvb_demux_feed *feed,
+ const u8 *buf,
+ const u8 timestamp[TIMESTAMP_LEN])
+{
+ int i;
+ int p;
+ u64 stc;
+ int found_patterns;
+ int count = payload(buf);
+ u64 min_pattern_tsp_num;
+ struct dvb_demux_feed *tmp_feed;
+ struct dvb_demux *demux = feed->demux;
+ struct dmx_index_event_info idx_event;
+ struct dvb_dmx_video_patterns_results patterns;
+
+ if (feed->demux->convert_ts)
+ feed->demux->convert_ts(feed, timestamp, &stc);
+ else
+ stc = 0;
+
+ idx_event.pid = feed->pid;
+ idx_event.stc = stc;
+ idx_event.match_tsp_num = feed->rec_info->ts_output_count;
+
+ /* PUSI ? */
+ if (buf[1] & 0x40) {
+ feed->curr_pusi_tsp_num = feed->rec_info->ts_output_count;
+ if (feed->idx_params.types & DMX_IDX_PUSI) {
+ idx_event.type = DMX_IDX_PUSI;
+ idx_event.last_pusi_tsp_num =
+ feed->curr_pusi_tsp_num;
+ dvb_demux_save_idx_event(feed, &idx_event, 1);
+ }
+ }
+
+ /*
+ * if we still did not encounter a TS packet with PUSI indication,
+ * we cannot report index entries yet as we need to provide
+ * the TS packet number with PUSI indication preceding the TS
+ * packet pointed by the reported index entry.
+ */
+ if (feed->curr_pusi_tsp_num == (u64)-1) {
+ dvb_dmx_notify_indexing(feed);
+ return;
+ }
+
+ if ((feed->idx_params.types & DMX_IDX_RAI) && /* index RAI? */
+ (buf[3] & 0x20) && /* adaptation field exists? */
+ (buf[4] > 0) && /* adaptation field len > 0 ? */
+ (buf[5] & 0x40)) { /* RAI is set? */
+ idx_event.type = DMX_IDX_RAI;
+ idx_event.last_pusi_tsp_num =
+ feed->curr_pusi_tsp_num;
+ dvb_demux_save_idx_event(feed, &idx_event, 1);
+ }
+
+ /*
+ * if no pattern search is required, or the TS packet has no payload,
+ * pattern search is not executed.
+ */
+ if (!feed->pattern_num || !count) {
+ dvb_dmx_notify_indexing(feed);
+ return;
+ }
+
+ p = 188 - count; /* payload start */
+
+ found_patterns =
+ dvb_dmx_video_pattern_search(feed->patterns,
+ feed->pattern_num, &buf[p], count,
+ &feed->prefix_size, &patterns);
+
+ for (i = 0; i < found_patterns; i++)
+ dvb_dmx_process_pattern_result(feed, &patterns, i,
+ stc, feed->prev_stc,
+ feed->rec_info->ts_output_count, feed->prev_tsp_num,
+ feed->curr_pusi_tsp_num, feed->prev_pusi_tsp_num);
+
+ feed->prev_tsp_num = feed->rec_info->ts_output_count;
+ feed->prev_pusi_tsp_num = feed->curr_pusi_tsp_num;
+ feed->prev_stc = stc;
+ feed->last_pattern_tsp_num = feed->rec_info->ts_output_count;
+
+ /*
+ * it is possible to have a TS packet that has a prefix of
+ * a video pattern but the video pattern is not identified yet
+ * until we get the next TS packet of that PID. When we get
+ * the next TS packet of that PID, pattern-search would
+ * detect that we have a new index entry that starts in the
+ * previous TS packet.
+ * In order to notify the user on index entries with match_tsp_num
+ * in ascending order, index events with match_tsp_num up to
+ * the last_pattern_tsp_num are notified now to the user,
+ * the rest can't be notified now as we might hit the above
+ * scenario and cause the events not to be notified with
+ * ascending order of match_tsp_num.
+ */
+ if (feed->rec_info->idx_info.pattern_search_feeds_num == 1) {
+ /*
+ * optimization for case we have only one PID
+ * with video pattern search, in this case
+ * min_pattern_tsp_num is simply updated to the new
+ * TS packet number of the PID with pattern search.
+ */
+ feed->rec_info->idx_info.min_pattern_tsp_num =
+ feed->last_pattern_tsp_num;
+ dvb_dmx_notify_indexing(feed);
+ return;
+ }
+
+ /*
+ * if we have more than one PID with pattern search,
+ * min_pattern_tsp_num needs to be updated now based on
+ * last_pattern_tsp_num of all PIDs with pattern search.
+ */
+ min_pattern_tsp_num = (u64)-1;
+ i = feed->rec_info->idx_info.pattern_search_feeds_num;
+ list_for_each_entry(tmp_feed, &demux->feed_list, list_head) {
+ if ((tmp_feed->state != DMX_STATE_GO) ||
+ (tmp_feed->type != DMX_TYPE_TS) ||
+ (tmp_feed->feed.ts.buffer.ringbuff !=
+ feed->feed.ts.buffer.ringbuff))
+ continue;
+
+ if ((tmp_feed->last_pattern_tsp_num != (u64)-1) &&
+ ((min_pattern_tsp_num == (u64)-1) ||
+ (tmp_feed->last_pattern_tsp_num <
+ min_pattern_tsp_num)))
+ min_pattern_tsp_num = tmp_feed->last_pattern_tsp_num;
+
+ if (tmp_feed->pattern_num) {
+ i--;
+ if (i == 0)
+ break;
+ }
+ }
+
+ feed->rec_info->idx_info.min_pattern_tsp_num = min_pattern_tsp_num;
+
+ /* notify all index entries up to min_pattern_tsp_num */
+ dvb_dmx_notify_indexing(feed);
+}
+
+static inline void dvb_dmx_swfilter_output_packet(
+ struct dvb_demux_feed *feed,
+ const u8 *buf,
+ const u8 timestamp[TIMESTAMP_LEN])
+{
+ /*
+ * if we output 192 packet with timestamp at head of packet,
+ * output the timestamp now before the 188 TS packet
+ */
+ if (feed->tsp_out_format == DMX_TSP_FORMAT_192_HEAD)
+ feed->cb.ts(timestamp, TIMESTAMP_LEN, NULL,
+ 0, &feed->feed.ts, DMX_OK);
+
+ feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+
+ /*
+ * if we output 192 packet with timestamp at tail of packet,
+ * output the timestamp now after the 188 TS packet
+ */
+ if (feed->tsp_out_format == DMX_TSP_FORMAT_192_TAIL)
+ feed->cb.ts(timestamp, TIMESTAMP_LEN, NULL,
+ 0, &feed->feed.ts, DMX_OK);
+
+ if (feed->idx_params.enable)
+ dvb_dmx_index(feed, buf, timestamp);
+
+ feed->rec_info->ts_output_count++;
+}
+
+static inline void dvb_dmx_configure_decoder_fullness(
+ struct dvb_demux *demux,
+ int initialize)
+{
+ struct dvb_demux_feed *feed;
+ int j;
+
+ for (j = 0; j < demux->feednum; j++) {
+ feed = &demux->feed[j];
+
+ if ((feed->state != DMX_STATE_GO) ||
+ (feed->type != DMX_TYPE_TS) ||
+ !(feed->ts_type & TS_DECODER))
+ continue;
+
+ if (initialize) {
+ if (demux->decoder_fullness_init)
+ demux->decoder_fullness_init(feed);
+ } else {
+ if (demux->decoder_fullness_abort)
+ demux->decoder_fullness_abort(feed);
+ }
+ }
+}
+
+static inline int dvb_dmx_swfilter_buffer_check(
+ struct dvb_demux *demux,
+ u16 pid)
+{
+ int desired_space;
+ int ret;
+ struct dmx_ts_feed *ts;
+ struct dvb_demux_filter *f;
+ struct dvb_demux_feed *feed;
+ int was_locked;
+ int i, j;
+
+ if (likely(spin_is_locked(&demux->lock)))
+ was_locked = 1;
+ else
+ was_locked = 0;
+
+ /*
+ * Check that there's enough free space for data output.
+ * If there no space, wait for it (block).
+ * Since this function is called while spinlock
+ * is acquired, the lock should be released first.
+ * Once we get control back, lock is acquired back
+ * and checks that the filter is still valid.
+ */
+ for (j = 0; j < demux->feednum; j++) {
+ feed = &demux->feed[j];
+
+ if (demux->sw_filter_abort)
+ return -ENODEV;
+
+ if ((feed->state != DMX_STATE_GO) ||
+ ((feed->pid != pid) && (feed->pid != 0x2000)))
+ continue;
+
+ if (feed->secure_mode.is_secured &&
+ !dvb_dmx_is_rec_feed(feed))
+ return 0;
+
+ if (feed->type == DMX_TYPE_TS) {
+ desired_space = 192; /* upper bound */
+ ts = &feed->feed.ts;
+
+ if (feed->ts_type & TS_PACKET) {
+ if (likely(was_locked))
+ spin_unlock(&demux->lock);
+
+ ret = demux->buffer_ctrl.ts(ts,
+ desired_space, 1);
+
+ if (likely(was_locked))
+ spin_lock(&demux->lock);
+
+ if (ret < 0)
+ continue;
+ }
+
+ if (demux->sw_filter_abort)
+ return -ENODEV;
+
+ if (!ts->is_filtering)
+ continue;
+
+ if ((feed->ts_type & TS_DECODER) &&
+ (demux->decoder_fullness_wait)) {
+ if (likely(was_locked))
+ spin_unlock(&demux->lock);
+
+ ret = demux->decoder_fullness_wait(
+ feed,
+ desired_space);
+
+ if (likely(was_locked))
+ spin_lock(&demux->lock);
+
+ if (ret < 0)
+ continue;
+ }
+
+ continue;
+ }
+
+ /* else - section case */
+ desired_space = feed->feed.sec.tsfeedp + 188; /* upper bound */
+ for (i = 0; i < demux->filternum; i++) {
+ if (demux->sw_filter_abort)
+ return -EPERM;
+
+ if (!feed->feed.sec.is_filtering)
+ continue;
+
+ f = &demux->filter[i];
+ if (f->feed != feed)
+ continue;
+
+ if (likely(was_locked))
+ spin_unlock(&demux->lock);
+
+ ret = demux->buffer_ctrl.sec(&f->filter,
+ desired_space, 1);
+
+ if (likely(was_locked))
+ spin_lock(&demux->lock);
+
+ if (ret < 0)
+ break;
+ }
+ }
+
+ return 0;
+}
+
static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
- const u8 *buf)
+ const u8 *buf, const u8 timestamp[TIMESTAMP_LEN])
{
+ u16 pid = ts_pid(buf);
+ u8 scrambling_bits = ts_scrambling_ctrl(buf);
+ struct dmx_data_ready dmx_data_ready;
+
+ /*
+ * Notify on scrambling status change only when we move
+ * from clear (0) to non-clear and vise-versa
+ */
+ if ((scrambling_bits && !feed->scrambling_bits) ||
+ (!scrambling_bits && feed->scrambling_bits)) {
+ dmx_data_ready.status = DMX_OK_SCRAMBLING_STATUS;
+ dmx_data_ready.data_length = 0;
+ dmx_data_ready.scrambling_bits.pid = pid;
+ dmx_data_ready.scrambling_bits.old_value =
+ feed->scrambling_bits;
+ dmx_data_ready.scrambling_bits.new_value = scrambling_bits;
+
+ if (feed->type == DMX_TYPE_SEC)
+ dvb_dmx_notify_section_event(feed, &dmx_data_ready, 0);
+ else if (feed->feed.ts.is_filtering)
+ feed->data_ready_cb.ts(&feed->feed.ts, &dmx_data_ready);
+ }
+
+ feed->scrambling_bits = scrambling_bits;
+
switch (feed->type) {
case DMX_TYPE_TS:
if (!feed->feed.ts.is_filtering)
break;
if (feed->ts_type & TS_PACKET) {
- if (feed->ts_type & TS_PAYLOAD_ONLY)
- dvb_dmx_swfilter_payload(feed, buf);
- else
- feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts);
+ if (feed->ts_type & TS_PAYLOAD_ONLY) {
+ if (!feed->secure_mode.is_secured)
+ dvb_dmx_swfilter_payload(feed, buf);
+ } else {
+ dvb_dmx_swfilter_output_packet(feed,
+ buf, timestamp);
+ }
}
- if (feed->ts_type & TS_DECODER)
+ if ((feed->ts_type & TS_DECODER) &&
+ !feed->secure_mode.is_secured)
if (feed->demux->write_to_decoder)
feed->demux->write_to_decoder(feed, buf, 188);
break;
case DMX_TYPE_SEC:
- if (!feed->feed.sec.is_filtering)
+ if (!feed->feed.sec.is_filtering ||
+ feed->secure_mode.is_secured)
break;
- if (dvb_dmx_swfilter_section_packet(feed, buf) < 0)
+ if (dvb_dmx_swfilter_section_one_packet(feed, buf) < 0)
feed->feed.sec.seclen = feed->feed.sec.secbufp = 0;
break;
@@ -391,7 +1476,8 @@ static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
((f)->feed.ts.is_filtering) && \
(((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))
-static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
+static void dvb_dmx_swfilter_one_packet(struct dvb_demux *demux, const u8 *buf,
+ const u8 timestamp[TIMESTAMP_LEN])
{
struct dvb_demux_feed *feed;
u16 pid = ts_pid(buf);
@@ -420,9 +1506,9 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
(u64)timespec_to_ns(&delta_time);
speed_timedelta = div64_u64(speed_timedelta,
1000000); /* nsec -> usec */
- printk(KERN_INFO "TS speed %llu Kbits/sec \n",
- div64_u64(speed_bytes,
- speed_timedelta));
+ pr_info("TS speed %llu Kbits/sec\n",
+ div64_u64(speed_bytes,
+ speed_timedelta));
}
demux->speed_last_time = cur_time;
@@ -431,11 +1517,12 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
}
if (buf[1] & 0x80) {
- dprintk_tscheck("TEI detected. "
- "PID=0x%x data1=0x%x\n",
- pid, buf[1]);
- /* data in this packet can't be trusted - drop it unless
- * module option dvb_demux_feed_err_pkts is set */
+ dprintk_tscheck("TEI detected. PID=0x%x data1=0x%x\n", pid,
+ buf[1]);
+ /*
+ * data in this packet can't be trusted - drop it unless
+ * module option dvb_demux_feed_err_pkts is set
+ */
if (!dvb_demux_feed_err_pkts)
return;
} else /* if TEI bit is set, pid may be wrong- skip pkt counter */
@@ -444,10 +1531,12 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
if (pid < MAX_PID) {
if (buf[3] & 0x10)
demux->cnt_storage[pid] =
- (demux->cnt_storage[pid] + 1) & 0xf;
+ (demux->cnt_storage[pid] + 1) &
+ 0xf;
if ((buf[3] & 0xf) != demux->cnt_storage[pid]) {
- dprintk_tscheck("TS packet counter mismatch. PID=0x%x expected 0x%x got 0x%x\n",
+ dprintk_tscheck(
+ "TS packet counter mismatch. PID=0x%x expected 0x%x got 0x%x\n",
pid, demux->cnt_storage[pid],
buf[3] & 0xf);
demux->cnt_storage[pid] = buf[3] & 0xf;
@@ -456,48 +1545,76 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
/* end check */
}
+ if (demux->playback_mode == DMX_PB_MODE_PULL)
+ if (dvb_dmx_swfilter_buffer_check(demux, pid) < 0)
+ return;
+
list_for_each_entry(feed, &demux->feed_list, list_head) {
if ((feed->pid != pid) && (feed->pid != 0x2000))
continue;
- /* copy each packet only once to the dvr device, even
- * if a PID is in multiple filters (e.g. video + PCR) */
+ /*
+ * copy each packet only once to the dvr device, even
+ * if a PID is in multiple filters (e.g. video + PCR)
+ */
if ((DVR_FEED(feed)) && (dvr_done++))
continue;
if (feed->pid == pid)
- dvb_dmx_swfilter_packet_type(feed, buf);
- else if (feed->pid == 0x2000)
- feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts);
+ dvb_dmx_swfilter_packet_type(feed, buf, timestamp);
+ else if ((feed->pid == 0x2000) &&
+ (feed->feed.ts.is_filtering))
+ dvb_dmx_swfilter_output_packet(feed, buf, timestamp);
}
}
+void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf,
+ const u8 timestamp[TIMESTAMP_LEN])
+{
+ spin_lock(&demux->lock);
+ dvb_dmx_swfilter_one_packet(demux, buf, timestamp);
+ spin_unlock(&demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_packet);
+
void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
size_t count)
{
- unsigned long flags;
+ struct timespec pre_time;
+ u8 timestamp[TIMESTAMP_LEN] = {0};
+
+ if (dvb_demux_performancecheck)
+ pre_time = current_kernel_time();
+
+ spin_lock(&demux->lock);
- spin_lock_irqsave(&demux->lock, flags);
+ demux->sw_filter_abort = 0;
+ dvb_dmx_configure_decoder_fullness(demux, 1);
while (count--) {
if (buf[0] == 0x47)
- dvb_dmx_swfilter_packet(demux, buf);
+ dvb_dmx_swfilter_one_packet(demux, buf, timestamp);
buf += 188;
}
- spin_unlock_irqrestore(&demux->lock, flags);
-}
+ spin_unlock(&demux->lock);
+ if (dvb_demux_performancecheck)
+ demux->total_process_time += dvb_dmx_calc_time_delta(pre_time);
+}
EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
static inline int find_next_packet(const u8 *buf, int pos, size_t count,
- const int pktsize)
+ const int pktsize, const int leadingbytes)
{
int start = pos, lost;
while (pos < count) {
- if (buf[pos] == 0x47 ||
- (pktsize == 204 && buf[pos] == 0xB8))
+ if ((buf[pos] == 0x47 && !leadingbytes) ||
+ (pktsize == 204 && buf[pos] == 0xB8) ||
+ (pktsize == 192 && leadingbytes &&
+ (pos+leadingbytes < count) &&
+ buf[pos+leadingbytes] == 0x47))
break;
pos++;
}
@@ -506,8 +1623,11 @@ static inline int find_next_packet(const u8 *buf, int pos, size_t count,
if (lost) {
/* This garbage is part of a valid packet? */
int backtrack = pos - pktsize;
+
if (backtrack >= 0 && (buf[backtrack] == 0x47 ||
- (pktsize == 204 && buf[backtrack] == 0xB8)))
+ (pktsize == 204 && buf[backtrack] == 0xB8) ||
+ (pktsize == 192 &&
+ buf[backtrack+leadingbytes] == 0x47)))
return backtrack;
}
@@ -516,13 +1636,20 @@ static inline int find_next_packet(const u8 *buf, int pos, size_t count,
/* Filter all pktsize= 188 or 204 sized packets and skip garbage. */
static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
- size_t count, const int pktsize)
+ size_t count, const int pktsize, const int leadingbytes)
{
int p = 0, i, j;
const u8 *q;
- unsigned long flags;
+ struct timespec pre_time;
+ u8 timestamp[TIMESTAMP_LEN];
+
+ if (dvb_demux_performancecheck)
+ pre_time = current_kernel_time();
+
+ spin_lock(&demux->lock);
- spin_lock_irqsave(&demux->lock, flags);
+ demux->sw_filter_abort = 0;
+ dvb_dmx_configure_decoder_fullness(demux, 1);
if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
i = demux->tsbufp;
@@ -533,14 +1660,36 @@ static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
goto bailout;
}
memcpy(&demux->tsbuf[i], buf, j);
- if (demux->tsbuf[0] == 0x47) /* double check */
- dvb_dmx_swfilter_packet(demux, demux->tsbuf);
+
+ if (pktsize == 192) {
+ if (leadingbytes)
+ memcpy(timestamp, &demux->tsbuf[p],
+ TIMESTAMP_LEN);
+ else
+ memcpy(timestamp, &demux->tsbuf[188],
+ TIMESTAMP_LEN);
+ } else {
+ memset(timestamp, 0, TIMESTAMP_LEN);
+ }
+
+ if (pktsize == 192 &&
+ leadingbytes &&
+ demux->tsbuf[leadingbytes] == 0x47) /* double check */
+ dvb_dmx_swfilter_one_packet(demux,
+ demux->tsbuf + TIMESTAMP_LEN, timestamp);
+ else if (demux->tsbuf[0] == 0x47) /* double check */
+ dvb_dmx_swfilter_one_packet(demux,
+ demux->tsbuf, timestamp);
demux->tsbufp = 0;
p += j;
}
while (1) {
- p = find_next_packet(buf, p, count, pktsize);
+ p = find_next_packet(buf, p, count, pktsize, leadingbytes);
+
+ if (demux->sw_filter_abort)
+ goto bailout;
+
if (p >= count)
break;
if (count - p < pktsize)
@@ -553,7 +1702,19 @@ static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
demux->tsbuf[0] = 0x47;
q = demux->tsbuf;
}
- dvb_dmx_swfilter_packet(demux, q);
+
+ if (pktsize == 192) {
+ if (leadingbytes) {
+ q = &buf[p+leadingbytes];
+ memcpy(timestamp, &buf[p], TIMESTAMP_LEN);
+ } else {
+ memcpy(timestamp, &buf[p+188], TIMESTAMP_LEN);
+ }
+ } else {
+ memset(timestamp, 0, TIMESTAMP_LEN);
+ }
+
+ dvb_dmx_swfilter_one_packet(demux, q, timestamp);
p += pktsize;
}
@@ -566,33 +1727,65 @@ static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
}
bailout:
- spin_unlock_irqrestore(&demux->lock, flags);
+ spin_unlock(&demux->lock);
+
+ if (dvb_demux_performancecheck)
+ demux->total_process_time += dvb_dmx_calc_time_delta(pre_time);
}
void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
{
- _dvb_dmx_swfilter(demux, buf, count, 188);
+ _dvb_dmx_swfilter(demux, buf, count, 188, 0);
}
EXPORT_SYMBOL(dvb_dmx_swfilter);
void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
{
- _dvb_dmx_swfilter(demux, buf, count, 204);
+ _dvb_dmx_swfilter(demux, buf, count, 204, 0);
}
EXPORT_SYMBOL(dvb_dmx_swfilter_204);
void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
{
- unsigned long flags;
+ spin_lock(&demux->lock);
- spin_lock_irqsave(&demux->lock, flags);
+ demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK);
- demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts);
-
- spin_unlock_irqrestore(&demux->lock, flags);
+ spin_unlock(&demux->lock);
}
EXPORT_SYMBOL(dvb_dmx_swfilter_raw);
+void dvb_dmx_swfilter_format(
+ struct dvb_demux *demux,
+ const u8 *buf,
+ size_t count,
+ enum dmx_tsp_format_t tsp_format)
+{
+ switch (tsp_format) {
+ case DMX_TSP_FORMAT_188:
+ _dvb_dmx_swfilter(demux, buf, count, 188, 0);
+ break;
+
+ case DMX_TSP_FORMAT_192_TAIL:
+ _dvb_dmx_swfilter(demux, buf, count, 192, 0);
+ break;
+
+ case DMX_TSP_FORMAT_192_HEAD:
+ _dvb_dmx_swfilter(demux, buf, count, 192, TIMESTAMP_LEN);
+ break;
+
+ case DMX_TSP_FORMAT_204:
+ _dvb_dmx_swfilter(demux, buf, count, 204, 0);
+ break;
+
+ default:
+ pr_err("%s: invalid TS packet format (format=%d)\n", __func__,
+ tsp_format);
+ break;
+ }
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_format);
+
static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)
{
int i;
@@ -625,6 +1818,268 @@ static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux)
return &demux->feed[i];
}
+const struct dvb_dmx_video_patterns *dvb_dmx_get_pattern(u64 dmx_idx_pattern)
+{
+ switch (dmx_idx_pattern) {
+ case DMX_IDX_MPEG_SEQ_HEADER:
+ return &mpeg2_seq_hdr;
+
+ case DMX_IDX_MPEG_GOP:
+ return &mpeg2_gop;
+
+ case DMX_IDX_MPEG_I_FRAME_START:
+ return &mpeg2_iframe;
+
+ case DMX_IDX_MPEG_P_FRAME_START:
+ return &mpeg2_pframe;
+
+ case DMX_IDX_MPEG_B_FRAME_START:
+ return &mpeg2_bframe;
+
+ case DMX_IDX_H264_SPS:
+ return &h264_sps;
+
+ case DMX_IDX_H264_PPS:
+ return &h264_pps;
+
+ case DMX_IDX_H264_IDR_START:
+ return &h264_idr;
+
+ case DMX_IDX_H264_NON_IDR_START:
+ return &h264_non_idr;
+
+ case DMX_IDX_H264_ACCESS_UNIT_DEL:
+ return &h264_non_access_unit_del;
+
+ case DMX_IDX_H264_SEI:
+ return &h264_non_sei;
+
+ case DMX_IDX_VC1_SEQ_HEADER:
+ return &vc1_seq_hdr;
+
+ case DMX_IDX_VC1_ENTRY_POINT:
+ return &vc1_entry_point;
+
+ case DMX_IDX_VC1_FRAME_START:
+ return &vc1_frame;
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(dvb_dmx_get_pattern);
+
+static void dvb_dmx_init_idx_state(struct dvb_demux_feed *feed)
+{
+ feed->prev_tsp_num = (u64)-1;
+ feed->curr_pusi_tsp_num = (u64)-1;
+ feed->prev_pusi_tsp_num = (u64)-1;
+ feed->prev_frame_valid = 0;
+ feed->first_frame_in_seq = 0;
+ feed->first_frame_in_seq_notified = 0;
+ feed->last_pattern_tsp_num = (u64)-1;
+ feed->pattern_num = 0;
+ memset(&feed->prefix_size, 0,
+ sizeof(struct dvb_dmx_video_prefix_size_masks));
+
+ if (feed->idx_params.types &
+ (DMX_IDX_MPEG_SEQ_HEADER |
+ DMX_IDX_MPEG_FIRST_SEQ_FRAME_START |
+ DMX_IDX_MPEG_FIRST_SEQ_FRAME_END)) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_MPEG_SEQ_HEADER);
+ feed->pattern_num++;
+ }
+
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types & DMX_IDX_MPEG_GOP)) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_MPEG_GOP);
+ feed->pattern_num++;
+ }
+
+ /* MPEG2 I-frame */
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types &
+ (DMX_IDX_MPEG_I_FRAME_START | DMX_IDX_MPEG_I_FRAME_END |
+ DMX_IDX_MPEG_P_FRAME_END | DMX_IDX_MPEG_B_FRAME_END |
+ DMX_IDX_MPEG_FIRST_SEQ_FRAME_START |
+ DMX_IDX_MPEG_FIRST_SEQ_FRAME_END))) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_MPEG_I_FRAME_START);
+ feed->pattern_num++;
+ }
+
+ /* MPEG2 P-frame */
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types &
+ (DMX_IDX_MPEG_P_FRAME_START | DMX_IDX_MPEG_P_FRAME_END |
+ DMX_IDX_MPEG_I_FRAME_END | DMX_IDX_MPEG_B_FRAME_END |
+ DMX_IDX_MPEG_FIRST_SEQ_FRAME_START |
+ DMX_IDX_MPEG_FIRST_SEQ_FRAME_END))) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_MPEG_P_FRAME_START);
+ feed->pattern_num++;
+ }
+
+ /* MPEG2 B-frame */
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types &
+ (DMX_IDX_MPEG_B_FRAME_START | DMX_IDX_MPEG_B_FRAME_END |
+ DMX_IDX_MPEG_I_FRAME_END | DMX_IDX_MPEG_P_FRAME_END |
+ DMX_IDX_MPEG_FIRST_SEQ_FRAME_START |
+ DMX_IDX_MPEG_FIRST_SEQ_FRAME_END))) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_MPEG_B_FRAME_START);
+ feed->pattern_num++;
+ }
+
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types &
+ (DMX_IDX_H264_SPS |
+ DMX_IDX_H264_FIRST_SPS_FRAME_START |
+ DMX_IDX_H264_FIRST_SPS_FRAME_END))) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_H264_SPS);
+ feed->pattern_num++;
+ }
+
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types & DMX_IDX_H264_PPS)) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_H264_PPS);
+ feed->pattern_num++;
+ }
+
+ /* H264 IDR */
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types &
+ (DMX_IDX_H264_IDR_START | DMX_IDX_H264_IDR_END |
+ DMX_IDX_H264_NON_IDR_END |
+ DMX_IDX_H264_FIRST_SPS_FRAME_START |
+ DMX_IDX_H264_FIRST_SPS_FRAME_END))) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_H264_IDR_START);
+ feed->pattern_num++;
+ }
+
+ /* H264 non-IDR */
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types &
+ (DMX_IDX_H264_NON_IDR_START | DMX_IDX_H264_NON_IDR_END |
+ DMX_IDX_H264_IDR_END |
+ DMX_IDX_H264_FIRST_SPS_FRAME_START |
+ DMX_IDX_H264_FIRST_SPS_FRAME_END))) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_START);
+ feed->pattern_num++;
+ }
+
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types & DMX_IDX_H264_ACCESS_UNIT_DEL)) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_H264_ACCESS_UNIT_DEL);
+ feed->pattern_num++;
+ }
+
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types & DMX_IDX_H264_SEI)) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_H264_SEI);
+ feed->pattern_num++;
+ }
+
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types &
+ (DMX_IDX_VC1_SEQ_HEADER |
+ DMX_IDX_VC1_FIRST_SEQ_FRAME_START |
+ DMX_IDX_VC1_FIRST_SEQ_FRAME_END))) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_VC1_SEQ_HEADER);
+ feed->pattern_num++;
+ }
+
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types & DMX_IDX_VC1_ENTRY_POINT)) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_VC1_ENTRY_POINT);
+ feed->pattern_num++;
+ }
+
+ /* VC1 frame */
+ if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) &&
+ (feed->idx_params.types &
+ (DMX_IDX_VC1_FRAME_START | DMX_IDX_VC1_FRAME_END |
+ DMX_IDX_VC1_FIRST_SEQ_FRAME_START |
+ DMX_IDX_VC1_FIRST_SEQ_FRAME_END))) {
+ feed->patterns[feed->pattern_num] =
+ dvb_dmx_get_pattern(DMX_IDX_VC1_FRAME_START);
+ feed->pattern_num++;
+ }
+
+ if (feed->pattern_num)
+ feed->rec_info->idx_info.pattern_search_feeds_num++;
+}
+
+static struct dvb_demux_rec_info *dvb_dmx_alloc_rec_info(
+ struct dmx_ts_feed *ts_feed)
+{
+ int i;
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+ struct dvb_demux_rec_info *rec_info;
+ struct dvb_demux_feed *tmp_feed;
+
+ /* check if this feed share recording buffer with other active feeds */
+ list_for_each_entry(tmp_feed, &demux->feed_list, list_head) {
+ if ((tmp_feed->state == DMX_STATE_GO) &&
+ (tmp_feed->type == DMX_TYPE_TS) &&
+ (tmp_feed != feed) &&
+ (tmp_feed->feed.ts.buffer.ringbuff ==
+ ts_feed->buffer.ringbuff)) {
+ /* indexing information is shared between the feeds */
+ tmp_feed->rec_info->ref_count++;
+ return tmp_feed->rec_info;
+ }
+ }
+
+ /* Need to allocate a new indexing info */
+ for (i = 0; i < demux->feednum; i++)
+ if (!demux->rec_info_pool[i].ref_count)
+ break;
+
+ if (i == demux->feednum)
+ return NULL;
+
+ rec_info = &demux->rec_info_pool[i];
+ rec_info->ref_count++;
+ INIT_LIST_HEAD(&rec_info->idx_info.free_list);
+ INIT_LIST_HEAD(&rec_info->idx_info.ready_list);
+
+ for (i = 0; i < DMX_IDX_EVENT_QUEUE_SIZE; i++)
+ list_add(&rec_info->idx_info.events[i].next,
+ &rec_info->idx_info.free_list);
+
+ rec_info->ts_output_count = 0;
+ rec_info->idx_info.min_pattern_tsp_num = (u64)-1;
+ rec_info->idx_info.pattern_search_feeds_num = 0;
+ rec_info->idx_info.indexing_feeds_num = 0;
+
+ return rec_info;
+}
+
+static void dvb_dmx_free_rec_info(struct dmx_ts_feed *ts_feed)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+
+ if (!feed->rec_info || !feed->rec_info->ref_count) {
+ pr_err("%s: invalid idx info state\n", __func__);
+ return;
+ }
+
+ feed->rec_info->ref_count--;
+}
+
static int dvb_demux_feed_find(struct dvb_demux_feed *feed)
{
struct dvb_demux_feed *entry;
@@ -640,7 +2095,7 @@ static void dvb_demux_feed_add(struct dvb_demux_feed *feed)
{
spin_lock_irq(&feed->demux->lock);
if (dvb_demux_feed_find(feed)) {
- printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n",
+ pr_err("%s: feed already in list (type=%x state=%x pid=%x)\n",
__func__, feed->type, feed->state, feed->pid);
goto out;
}
@@ -654,7 +2109,7 @@ static void dvb_demux_feed_del(struct dvb_demux_feed *feed)
{
spin_lock_irq(&feed->demux->lock);
if (!(dvb_demux_feed_find(feed))) {
- printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n",
+ pr_err("%s: feed not in list (type=%x state=%x pid=%x)\n",
__func__, feed->type, feed->state, feed->pid);
goto out;
}
@@ -738,7 +2193,34 @@ static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed)
return -ENODEV;
}
- if ((ret = demux->start_feed(feed)) < 0) {
+ feed->first_cc = 1;
+ feed->scrambling_bits = 0;
+
+ if ((feed->ts_type & TS_PACKET) &&
+ !(feed->ts_type & TS_PAYLOAD_ONLY)) {
+ feed->rec_info = dvb_dmx_alloc_rec_info(ts_feed);
+ if (!feed->rec_info) {
+ mutex_unlock(&demux->mutex);
+ return -ENOMEM;
+ }
+ if (feed->idx_params.enable) {
+ dvb_dmx_init_idx_state(feed);
+ feed->rec_info->idx_info.indexing_feeds_num++;
+ if (demux->set_indexing)
+ demux->set_indexing(feed);
+ }
+ } else {
+ feed->pattern_num = 0;
+ feed->rec_info = NULL;
+ }
+
+ ret = demux->start_feed(feed);
+ if (ret < 0) {
+ if ((feed->ts_type & TS_PACKET) &&
+ !(feed->ts_type & TS_PAYLOAD_ONLY)) {
+ dvb_dmx_free_rec_info(ts_feed);
+ feed->rec_info = NULL;
+ }
mutex_unlock(&demux->mutex);
return ret;
}
@@ -776,11 +2258,337 @@ static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed)
ts_feed->is_filtering = 0;
feed->state = DMX_STATE_ALLOCATED;
spin_unlock_irq(&demux->lock);
+
+ if (feed->rec_info) {
+ if (feed->pattern_num)
+ feed->rec_info->idx_info.pattern_search_feeds_num--;
+ if (feed->idx_params.enable)
+ feed->rec_info->idx_info.indexing_feeds_num--;
+ dvb_dmx_free_rec_info(ts_feed);
+ feed->rec_info = NULL;
+ }
+
mutex_unlock(&demux->mutex);
return ret;
}
+static int dmx_ts_feed_decoder_buff_status(struct dmx_ts_feed *ts_feed,
+ struct dmx_buffer_status *dmx_buffer_status)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+ int ret;
+
+ mutex_lock(&demux->mutex);
+
+ if (feed->state < DMX_STATE_GO) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+
+ if (!demux->decoder_buffer_status) {
+ mutex_unlock(&demux->mutex);
+ return -ENODEV;
+ }
+
+ ret = demux->decoder_buffer_status(feed, dmx_buffer_status);
+
+ mutex_unlock(&demux->mutex);
+
+ return ret;
+}
+
+static int dmx_ts_feed_reuse_decoder_buffer(struct dmx_ts_feed *ts_feed,
+ int cookie)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+ int ret;
+
+ mutex_lock(&demux->mutex);
+
+ if (feed->state < DMX_STATE_GO) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+
+ if (!demux->reuse_decoder_buffer) {
+ mutex_unlock(&demux->mutex);
+ return -ENODEV;
+ }
+
+ ret = demux->reuse_decoder_buffer(feed, cookie);
+
+ mutex_unlock(&demux->mutex);
+
+ return ret;
+}
+
+static int dmx_ts_feed_data_ready_cb(struct dmx_ts_feed *feed,
+ dmx_ts_data_ready_cb callback)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (dvbdmxfeed->state == DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ dvbdmxfeed->data_ready_cb.ts = callback;
+
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+static int dmx_ts_set_secure_mode(struct dmx_ts_feed *feed,
+ struct dmx_secure_mode *secure_mode)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+ if (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (dvbdmxfeed->state == DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+
+ dvbdmxfeed->secure_mode = *secure_mode;
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+static int dmx_ts_set_cipher_ops(struct dmx_ts_feed *feed,
+ struct dmx_cipher_operations *cipher_ops)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if ((dvbdmxfeed->state == DMX_STATE_GO) &&
+ dvbdmx->set_cipher_op)
+ ret = dvbdmx->set_cipher_op(dvbdmxfeed, cipher_ops);
+
+ if (!ret)
+ dvbdmxfeed->cipher_ops = *cipher_ops;
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+}
+
+static int dmx_ts_set_video_codec(
+ struct dmx_ts_feed *ts_feed,
+ enum dmx_video_codec video_codec)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+
+ feed->video_codec = video_codec;
+
+ return 0;
+}
+
+static int dmx_ts_set_idx_params(struct dmx_ts_feed *ts_feed,
+ struct dmx_indexing_params *idx_params)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *dvbdmx = feed->demux;
+ int idx_enabled;
+ int ret = 0;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if ((feed->state == DMX_STATE_GO) &&
+ !feed->rec_info) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ idx_enabled = feed->idx_params.enable;
+ feed->idx_params = *idx_params;
+
+ if (feed->state == DMX_STATE_GO) {
+ spin_lock_irq(&dvbdmx->lock);
+ if (feed->pattern_num)
+ feed->rec_info->idx_info.pattern_search_feeds_num--;
+ if (idx_enabled && !idx_params->enable)
+ feed->rec_info->idx_info.indexing_feeds_num--;
+ if (!idx_enabled && idx_params->enable)
+ feed->rec_info->idx_info.indexing_feeds_num++;
+ dvb_dmx_init_idx_state(feed);
+ spin_unlock_irq(&dvbdmx->lock);
+
+ if (dvbdmx->set_indexing)
+ ret = dvbdmx->set_indexing(feed);
+ }
+
+ mutex_unlock(&dvbdmx->mutex);
+
+ return ret;
+}
+
+static int dvbdmx_ts_feed_oob_cmd(struct dmx_ts_feed *ts_feed,
+ struct dmx_oob_command *cmd)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dmx_data_ready data;
+ struct dvb_demux *dvbdmx = feed->demux;
+ int ret = 0;
+ int secure_non_rec = feed->secure_mode.is_secured &&
+ !dvb_dmx_is_rec_feed(feed);
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (feed->state != DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ /* Decoder & non-recording secure feeds are handled by plug-in */
+ if ((feed->ts_type & TS_DECODER) || secure_non_rec) {
+ if (feed->demux->oob_command)
+ ret = feed->demux->oob_command(feed, cmd);
+ }
+
+ if (!(feed->ts_type & (TS_PAYLOAD_ONLY | TS_PACKET)) ||
+ secure_non_rec) {
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+ }
+
+ data.data_length = 0;
+
+ switch (cmd->type) {
+ case DMX_OOB_CMD_EOS:
+ if (feed->ts_type & TS_PAYLOAD_ONLY)
+ dvb_dmx_check_pes_end(feed);
+
+ data.status = DMX_OK_EOS;
+ ret = feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ break;
+
+ case DMX_OOB_CMD_MARKER:
+ data.status = DMX_OK_MARKER;
+ data.marker.id = cmd->params.marker.id;
+ ret = feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+}
+
+static int dvbdmx_ts_get_scrambling_bits(struct dmx_ts_feed *ts_feed,
+ u8 *value)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+
+ spin_lock(&demux->lock);
+
+ if (!ts_feed->is_filtering) {
+ spin_unlock(&demux->lock);
+ return -EINVAL;
+ }
+
+ *value = feed->scrambling_bits;
+ spin_unlock(&demux->lock);
+
+ return 0;
+}
+
+static int dvbdmx_ts_insertion_insert_buffer(struct dmx_ts_feed *ts_feed,
+ char *data, size_t size)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+
+ spin_lock(&demux->lock);
+ if (!ts_feed->is_filtering) {
+ spin_unlock(&demux->lock);
+ return 0;
+ }
+
+ feed->cb.ts(data, size, NULL, 0, ts_feed, DMX_OK);
+
+ spin_unlock(&demux->lock);
+
+ return 0;
+}
+
+static int dmx_ts_set_tsp_out_format(
+ struct dmx_ts_feed *ts_feed,
+ enum dmx_tsp_format_t tsp_format)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *dvbdmx = feed->demux;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (feed->state == DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ feed->tsp_out_format = tsp_format;
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+/**
+ * dvbdmx_ts_reset_pes_state() - Reset the current PES length and PES counters
+ *
+ * @feed: dvb demux feed object
+ */
+void dvbdmx_ts_reset_pes_state(struct dvb_demux_feed *feed)
+{
+ unsigned long flags;
+
+ /*
+ * Reset PES state.
+ * PUSI seen indication is kept so we can get partial PES.
+ */
+ spin_lock_irqsave(&feed->demux->lock, flags);
+
+ feed->peslen = 0;
+ feed->pes_tei_counter = 0;
+ feed->pes_cont_err_counter = 0;
+ feed->pes_ts_packets_num = 0;
+
+ spin_unlock_irqrestore(&feed->demux->lock, flags);
+}
+EXPORT_SYMBOL(dvbdmx_ts_reset_pes_state);
+
+static int dvbdmx_ts_flush_buffer(struct dmx_ts_feed *ts_feed, size_t length)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&demux->mutex))
+ return -ERESTARTSYS;
+
+ dvbdmx_ts_reset_pes_state(feed);
+
+ if ((feed->ts_type & TS_DECODER) && demux->flush_decoder_buffer)
+ /* Call decoder specific flushing if one exists */
+ ret = demux->flush_decoder_buffer(feed, length);
+
+ mutex_unlock(&demux->mutex);
+ return ret;
+}
+
static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
struct dmx_ts_feed **ts_feed,
dmx_ts_cb callback)
@@ -800,8 +2608,21 @@ static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
feed->cb.ts = callback;
feed->demux = demux;
feed->pid = 0xffff;
- feed->peslen = 0xfffa;
+ feed->peslen = 0;
+ feed->pes_tei_counter = 0;
+ feed->pes_ts_packets_num = 0;
+ feed->pes_cont_err_counter = 0;
+ feed->secure_mode.is_secured = 0;
feed->buffer = NULL;
+ feed->tsp_out_format = DMX_TSP_FORMAT_188;
+ feed->idx_params.enable = 0;
+
+ /* default behaviour - pass first PES data even if it is
+ * partial PES data from previous PES that we didn't receive its header.
+ * Override this to 0 in your start_feed function in order to handle
+ * first PES differently.
+ */
+ feed->pusi_seen = 1;
(*ts_feed) = &feed->feed.ts;
(*ts_feed)->parent = dmx;
@@ -810,6 +2631,22 @@ static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
(*ts_feed)->start_filtering = dmx_ts_feed_start_filtering;
(*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
(*ts_feed)->set = dmx_ts_feed_set;
+ (*ts_feed)->set_video_codec = dmx_ts_set_video_codec;
+ (*ts_feed)->set_idx_params = dmx_ts_set_idx_params;
+ (*ts_feed)->set_tsp_out_format = dmx_ts_set_tsp_out_format;
+ (*ts_feed)->get_decoder_buff_status = dmx_ts_feed_decoder_buff_status;
+ (*ts_feed)->reuse_decoder_buffer = dmx_ts_feed_reuse_decoder_buffer;
+ (*ts_feed)->data_ready_cb = dmx_ts_feed_data_ready_cb;
+ (*ts_feed)->notify_data_read = NULL;
+ (*ts_feed)->set_secure_mode = dmx_ts_set_secure_mode;
+ (*ts_feed)->set_cipher_ops = dmx_ts_set_cipher_ops;
+ (*ts_feed)->oob_command = dvbdmx_ts_feed_oob_cmd;
+ (*ts_feed)->get_scrambling_bits = dvbdmx_ts_get_scrambling_bits;
+ (*ts_feed)->ts_insertion_init = NULL;
+ (*ts_feed)->ts_insertion_terminate = NULL;
+ (*ts_feed)->ts_insertion_insert_buffer =
+ dvbdmx_ts_insertion_insert_buffer;
+ (*ts_feed)->flush_buffer = dvbdmx_ts_flush_buffer;
if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
feed->state = DMX_STATE_FREE;
@@ -845,7 +2682,7 @@ static int dvbdmx_release_ts_feed(struct dmx_demux *dmx,
feed->state = DMX_STATE_FREE;
feed->filter->state = DMX_STATE_FREE;
-
+ ts_feed->priv = NULL;
dvb_demux_feed_del(feed);
feed->pid = 0xffff;
@@ -971,6 +2808,8 @@ static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed)
dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
dvbdmxfeed->feed.sec.secbufp = 0;
dvbdmxfeed->feed.sec.seclen = 0;
+ dvbdmxfeed->first_cc = 1;
+ dvbdmxfeed->scrambling_bits = 0;
if (!dvbdmx->start_feed) {
mutex_unlock(&dvbdmx->mutex);
@@ -1017,6 +2856,66 @@ static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed)
return ret;
}
+
+static int dmx_section_feed_data_ready_cb(struct dmx_section_feed *feed,
+ dmx_section_data_ready_cb callback)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (dvbdmxfeed->state == DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ dvbdmxfeed->data_ready_cb.sec = callback;
+
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+static int dmx_section_set_secure_mode(struct dmx_section_feed *feed,
+ struct dmx_secure_mode *secure_mode)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (dvbdmxfeed->state == DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+
+ dvbdmxfeed->secure_mode = *secure_mode;
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+static int dmx_section_set_cipher_ops(struct dmx_section_feed *feed,
+ struct dmx_cipher_operations *cipher_ops)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if ((dvbdmxfeed->state == DMX_STATE_GO) &&
+ dvbdmx->set_cipher_op) {
+ ret = dvbdmx->set_cipher_op(dvbdmxfeed, cipher_ops);
+ }
+
+ if (!ret)
+ dvbdmxfeed->cipher_ops = *cipher_ops;
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+}
+
static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
struct dmx_section_filter *filter)
{
@@ -1050,12 +2949,82 @@ static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
f->next = f->next->next;
}
+ filter->priv = NULL;
dvbdmxfilter->state = DMX_STATE_FREE;
spin_unlock_irq(&dvbdmx->lock);
mutex_unlock(&dvbdmx->mutex);
return 0;
}
+static int dvbdmx_section_feed_oob_cmd(struct dmx_section_feed *section_feed,
+ struct dmx_oob_command *cmd)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)section_feed;
+ struct dvb_demux *dvbdmx = feed->demux;
+ struct dmx_data_ready data;
+ int ret = 0;
+
+ data.data_length = 0;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (feed->state != DMX_STATE_GO) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ /* Secure section feeds are handled by the plug-in */
+ if (feed->secure_mode.is_secured) {
+ if (feed->demux->oob_command)
+ ret = feed->demux->oob_command(feed, cmd);
+ else
+ ret = 0;
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+ }
+
+ switch (cmd->type) {
+ case DMX_OOB_CMD_EOS:
+ data.status = DMX_OK_EOS;
+ break;
+
+ case DMX_OOB_CMD_MARKER:
+ data.status = DMX_OK_MARKER;
+ data.marker.id = cmd->params.marker.id;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!ret)
+ ret = dvb_dmx_notify_section_event(feed, &data, 1);
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+}
+
+static int dvbdmx_section_get_scrambling_bits(
+ struct dmx_section_feed *section_feed, u8 *value)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)section_feed;
+ struct dvb_demux *demux = feed->demux;
+
+ spin_lock(&demux->lock);
+
+ if (!section_feed->is_filtering) {
+ spin_unlock(&demux->lock);
+ return -EINVAL;
+ }
+
+ *value = feed->scrambling_bits;
+ spin_unlock(&demux->lock);
+
+ return 0;
+}
+
static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
struct dmx_section_feed **feed,
dmx_section_cb callback)
@@ -1075,11 +3044,14 @@ static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
dvbdmxfeed->cb.sec = callback;
dvbdmxfeed->demux = dvbdmx;
dvbdmxfeed->pid = 0xffff;
+ dvbdmxfeed->secure_mode.is_secured = 0;
+ dvbdmxfeed->tsp_out_format = DMX_TSP_FORMAT_188;
dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0;
dvbdmxfeed->feed.sec.tsfeedp = 0;
dvbdmxfeed->filter = NULL;
dvbdmxfeed->buffer = NULL;
+ dvbdmxfeed->idx_params.enable = 0;
(*feed) = &dvbdmxfeed->feed.sec;
(*feed)->is_filtering = 0;
@@ -1091,6 +3063,13 @@ static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
(*feed)->start_filtering = dmx_section_feed_start_filtering;
(*feed)->stop_filtering = dmx_section_feed_stop_filtering;
(*feed)->release_filter = dmx_section_feed_release_filter;
+ (*feed)->data_ready_cb = dmx_section_feed_data_ready_cb;
+ (*feed)->notify_data_read = NULL;
+ (*feed)->set_secure_mode = dmx_section_set_secure_mode;
+ (*feed)->set_cipher_ops = dmx_section_set_cipher_ops;
+ (*feed)->oob_command = dvbdmx_section_feed_oob_cmd;
+ (*feed)->get_scrambling_bits = dvbdmx_section_get_scrambling_bits;
+ (*feed)->flush_buffer = NULL;
mutex_unlock(&dvbdmx->mutex);
return 0;
@@ -1113,7 +3092,7 @@ static int dvbdmx_release_section_feed(struct dmx_demux *demux,
dvbdmxfeed->buffer = NULL;
#endif
dvbdmxfeed->state = DMX_STATE_FREE;
-
+ feed->priv = NULL;
dvb_demux_feed_del(dvbdmxfeed);
dvbdmxfeed->pid = 0xffff;
@@ -1149,23 +3128,18 @@ static int dvbdmx_close(struct dmx_demux *demux)
return 0;
}
-static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)
+static int dvbdmx_write(struct dmx_demux *demux, const char *buf, size_t count)
{
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
- void *p;
- if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
+ if (!demux->frontend || !buf || demux->dvr_input_protected ||
+ (demux->frontend->source != DMX_MEMORY_FE))
return -EINVAL;
-
- p = memdup_user(buf, count);
- if (IS_ERR(p))
- return PTR_ERR(p);
- if (mutex_lock_interruptible(&dvbdemux->mutex)) {
- kfree(p);
+ if (mutex_lock_interruptible(&dvbdemux->mutex))
return -ERESTARTSYS;
- }
- dvb_dmx_swfilter(dvbdemux, p, count);
- kfree(p);
+
+ dvb_dmx_swfilter_format(dvbdemux, buf, count, dvbdemux->tsp_format);
+
mutex_unlock(&dvbdemux->mutex);
if (signal_pending(current))
@@ -1173,6 +3147,40 @@ static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t
return count;
}
+static int dvbdmx_write_cancel(struct dmx_demux *demux)
+{
+ struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
+
+ spin_lock_irq(&dvbdmx->lock);
+
+ /* cancel any pending wait for decoder's buffers */
+ dvbdmx->sw_filter_abort = 1;
+ dvbdmx->tsbufp = 0;
+ dvb_dmx_configure_decoder_fullness(dvbdmx, 0);
+
+ spin_unlock_irq(&dvbdmx->lock);
+
+ return 0;
+}
+
+static int dvbdmx_set_playback_mode(struct dmx_demux *demux,
+ enum dmx_playback_mode_t mode,
+ dmx_ts_fullness ts_fullness_callback,
+ dmx_section_fullness sec_fullness_callback)
+{
+ struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ dvbdmx->playback_mode = mode;
+ dvbdmx->buffer_ctrl.ts = ts_fullness_callback;
+ dvbdmx->buffer_ctrl.sec = sec_fullness_callback;
+
+ mutex_unlock(&dvbdmx->mutex);
+
+ return 0;
+}
+
static int dvbdmx_add_frontend(struct dmx_demux *demux,
struct dmx_frontend *frontend)
{
@@ -1230,7 +3238,7 @@ static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
mutex_lock(&dvbdemux->mutex);
-
+ dvbdemux->sw_filter_abort = 0;
demux->frontend = NULL;
mutex_unlock(&dvbdemux->mutex);
return 0;
@@ -1244,6 +3252,48 @@ static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids)
return 0;
}
+static int dvbdmx_get_tsp_size(struct dmx_demux *demux)
+{
+ int tsp_size;
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ mutex_lock(&dvbdemux->mutex);
+ tsp_size = dvbdemux->ts_packet_size;
+ mutex_unlock(&dvbdemux->mutex);
+
+ return tsp_size;
+}
+
+static int dvbdmx_set_tsp_format(
+ struct dmx_demux *demux,
+ enum dmx_tsp_format_t tsp_format)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ if ((tsp_format > DMX_TSP_FORMAT_204) ||
+ (tsp_format < DMX_TSP_FORMAT_188))
+ return -EINVAL;
+
+ mutex_lock(&dvbdemux->mutex);
+
+ dvbdemux->tsp_format = tsp_format;
+ switch (tsp_format) {
+ case DMX_TSP_FORMAT_188:
+ dvbdemux->ts_packet_size = 188;
+ break;
+ case DMX_TSP_FORMAT_192_TAIL:
+ case DMX_TSP_FORMAT_192_HEAD:
+ dvbdemux->ts_packet_size = 192;
+ break;
+ case DMX_TSP_FORMAT_204:
+ dvbdemux->ts_packet_size = 204;
+ break;
+ }
+
+ mutex_unlock(&dvbdemux->mutex);
+ return 0;
+}
+
int dvb_dmx_init(struct dvb_demux *dvbdemux)
{
int i;
@@ -1262,13 +3312,52 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux)
dvbdemux->filter = NULL;
return -ENOMEM;
}
+
+ dvbdemux->rec_info_pool = vmalloc(dvbdemux->feednum *
+ sizeof(struct dvb_demux_rec_info));
+ if (!dvbdemux->rec_info_pool) {
+ vfree(dvbdemux->feed);
+ vfree(dvbdemux->filter);
+ dvbdemux->feed = NULL;
+ dvbdemux->filter = NULL;
+ return -ENOMEM;
+ }
+
+ dvbdemux->sw_filter_abort = 0;
+ dvbdemux->total_process_time = 0;
+ dvbdemux->total_crc_time = 0;
+ snprintf(dvbdemux->alias,
+ MAX_DVB_DEMUX_NAME_LEN,
+ "demux%d",
+ dvb_demux_index++);
+
+ dvbdemux->dmx.debugfs_demux_dir =
+ debugfs_create_dir(dvbdemux->alias, NULL);
+
+ if (dvbdemux->dmx.debugfs_demux_dir != NULL) {
+ debugfs_create_u32(
+ "total_processing_time",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ dvbdemux->dmx.debugfs_demux_dir,
+ &dvbdemux->total_process_time);
+
+ debugfs_create_u32(
+ "total_crc_time",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ dvbdemux->dmx.debugfs_demux_dir,
+ &dvbdemux->total_crc_time);
+ }
+
for (i = 0; i < dvbdemux->filternum; i++) {
dvbdemux->filter[i].state = DMX_STATE_FREE;
dvbdemux->filter[i].index = i;
}
+
for (i = 0; i < dvbdemux->feednum; i++) {
dvbdemux->feed[i].state = DMX_STATE_FREE;
dvbdemux->feed[i].index = i;
+
+ dvbdemux->rec_info_pool[i].ref_count = 0;
}
dvbdemux->cnt_storage = vmalloc(MAX_PID + 1);
@@ -1288,6 +3377,9 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux)
dvbdemux->recording = 0;
dvbdemux->tsbufp = 0;
+ dvbdemux->tsp_format = DMX_TSP_FORMAT_188;
+ dvbdemux->ts_packet_size = 188;
+
if (!dvbdemux->check_crc32)
dvbdemux->check_crc32 = dvb_dmx_crc32;
@@ -1299,10 +3391,14 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux)
dmx->open = dvbdmx_open;
dmx->close = dvbdmx_close;
dmx->write = dvbdmx_write;
+ dmx->write_cancel = dvbdmx_write_cancel;
+ dmx->set_playback_mode = dvbdmx_set_playback_mode;
dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed;
dmx->release_ts_feed = dvbdmx_release_ts_feed;
dmx->allocate_section_feed = dvbdmx_allocate_section_feed;
dmx->release_section_feed = dvbdmx_release_section_feed;
+ dmx->map_buffer = NULL;
+ dmx->unmap_buffer = NULL;
dmx->add_frontend = dvbdmx_add_frontend;
dmx->remove_frontend = dvbdmx_remove_frontend;
@@ -1311,6 +3407,9 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux)
dmx->disconnect_frontend = dvbdmx_disconnect_frontend;
dmx->get_pes_pids = dvbdmx_get_pes_pids;
+ dmx->set_tsp_format = dvbdmx_set_tsp_format;
+ dmx->get_tsp_size = dvbdmx_get_tsp_size;
+
mutex_init(&dvbdemux->mutex);
spin_lock_init(&dvbdemux->lock);
@@ -1321,9 +3420,14 @@ EXPORT_SYMBOL(dvb_dmx_init);
void dvb_dmx_release(struct dvb_demux *dvbdemux)
{
+ if (dvbdemux->dmx.debugfs_demux_dir != NULL)
+ debugfs_remove_recursive(dvbdemux->dmx.debugfs_demux_dir);
+
+ dvb_demux_index--;
vfree(dvbdemux->cnt_storage);
vfree(dvbdemux->filter);
vfree(dvbdemux->feed);
+ vfree(dvbdemux->rec_info_pool);
}
EXPORT_SYMBOL(dvb_dmx_release);
diff --git a/drivers/media/dvb-core/dvb_demux.h b/drivers/media/dvb-core/dvb_demux.h
index ae7fc33c3231..779de7ed078a 100644
--- a/drivers/media/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb-core/dvb_demux.h
@@ -27,6 +27,7 @@
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
+#include <linux/debugfs.h>
#include "demux.h"
@@ -44,6 +45,8 @@
#define MAX_PID 0x1fff
+#define TIMESTAMP_LEN 4
+
#define SPEED_PKTS_INTERVAL 50000
struct dvb_demux_filter {
@@ -64,6 +67,92 @@ struct dvb_demux_filter {
#define DMX_FEED_ENTRY(pos) list_entry(pos, struct dvb_demux_feed, list_head)
+
+struct dmx_index_entry {
+ struct dmx_index_event_info event;
+ struct list_head next;
+};
+
+#define DMX_IDX_EVENT_QUEUE_SIZE DMX_EVENT_QUEUE_SIZE
+
+struct dvb_demux_rec_info {
+ /* Reference counter for number of feeds using this information */
+ int ref_count;
+
+ /* Counter for number of TS packets output to recording buffer */
+ u64 ts_output_count;
+
+ /* Indexing information */
+ struct {
+ /*
+ * Minimum TS packet number encountered in recording filter
+ * among all feeds that search for video patterns
+ */
+ u64 min_pattern_tsp_num;
+
+ /* Number of indexing-enabled feeds */
+ u8 indexing_feeds_num;
+
+ /* Number of feeds with video pattern search request */
+ u8 pattern_search_feeds_num;
+
+ /* Index entries pool */
+ struct dmx_index_entry events[DMX_IDX_EVENT_QUEUE_SIZE];
+
+ /* List of free entries that can be used for new index events */
+ struct list_head free_list;
+
+ /* List holding ready index entries not notified to user yet */
+ struct list_head ready_list;
+ } idx_info;
+};
+
+#define DVB_DMX_MAX_PATTERN_LEN 6
+struct dvb_dmx_video_patterns {
+ /* the byte pattern to look for */
+ u8 pattern[DVB_DMX_MAX_PATTERN_LEN];
+
+ /* the byte mask to use (same length as pattern) */
+ u8 mask[DVB_DMX_MAX_PATTERN_LEN];
+
+ /* the length of the pattern, in bytes */
+ size_t size;
+
+ /* the type of the pattern. One of DMX_IDX_* definitions */
+ u64 type;
+};
+
+#define DVB_DMX_MAX_FOUND_PATTERNS 20
+#define DVB_DMX_MAX_SEARCH_PATTERN_NUM 20
+struct dvb_dmx_video_prefix_size_masks {
+ /*
+ * a bit mask (per pattern) of possible prefix sizes to use
+ * when searching for a pattern that started in the previous TS packet.
+ * Updated by dvb_dmx_video_pattern_search for use in the next lookup.
+ */
+ u32 size_mask[DVB_DMX_MAX_FOUND_PATTERNS];
+};
+
+struct dvb_dmx_video_patterns_results {
+ struct {
+ /*
+ * The offset in the buffer where the pattern was found.
+ * If a pattern is found using a prefix (i.e. started on the
+ * previous buffer), offset is zero.
+ */
+ u32 offset;
+
+ /*
+ * The type of the pattern found.
+ * One of DMX_IDX_* definitions.
+ */
+ u64 type;
+
+ /* The prefix size that was used to find this pattern */
+ u32 used_prefix_size;
+ } info[DVB_DMX_MAX_FOUND_PATTERNS];
+};
+
struct dvb_demux_feed {
union {
struct dmx_ts_feed ts;
@@ -75,6 +164,11 @@ struct dvb_demux_feed {
dmx_section_cb sec;
} cb;
+ union {
+ dmx_ts_data_ready_cb ts;
+ dmx_section_data_ready_cb sec;
+ } data_ready_cb;
+
struct dvb_demux *demux;
void *priv;
int type;
@@ -82,6 +176,9 @@ struct dvb_demux_feed {
u16 pid;
u8 *buffer;
int buffer_size;
+ enum dmx_tsp_format_t tsp_out_format;
+ struct dmx_secure_mode secure_mode;
+ struct dmx_cipher_operations cipher_ops;
struct timespec timeout;
struct dvb_demux_filter *filter;
@@ -90,12 +187,34 @@ struct dvb_demux_feed {
enum dmx_ts_pes pes_type;
int cc;
+ int first_cc;
int pusi_seen; /* prevents feeding of garbage from previous section */
+ u8 scrambling_bits;
+
+ struct dvb_demux_rec_info *rec_info;
+ u64 prev_tsp_num;
+ u64 prev_stc;
+ u64 curr_pusi_tsp_num;
+ u64 prev_pusi_tsp_num;
+ int prev_frame_valid;
+ u64 prev_frame_type;
+ int first_frame_in_seq;
+ int first_frame_in_seq_notified;
+ u64 last_pattern_tsp_num;
+ int pattern_num;
+const struct dvb_dmx_video_patterns *patterns[DVB_DMX_MAX_SEARCH_PATTERN_NUM];
+ struct dvb_dmx_video_prefix_size_masks prefix_size;
u16 peslen;
+ u32 pes_tei_counter;
+ u32 pes_cont_err_counter;
+ u32 pes_ts_packets_num;
struct list_head list_head;
unsigned int index; /* a unique index for each feed (can be used as hardware pid filter index) */
+
+ enum dmx_video_codec video_codec;
+ struct dmx_indexing_params idx_params;
};
struct dvb_demux {
@@ -107,10 +226,27 @@ struct dvb_demux {
int (*stop_feed)(struct dvb_demux_feed *feed);
int (*write_to_decoder)(struct dvb_demux_feed *feed,
const u8 *buf, size_t len);
+ int (*decoder_fullness_init)(struct dvb_demux_feed *feed);
+ int (*decoder_fullness_wait)(struct dvb_demux_feed *feed,
+ size_t required_space);
+ int (*decoder_fullness_abort)(struct dvb_demux_feed *feed);
+ int (*decoder_buffer_status)(struct dvb_demux_feed *feed,
+ struct dmx_buffer_status *dmx_buffer_status);
+ int (*reuse_decoder_buffer)(struct dvb_demux_feed *feed,
+ int cookie);
+ int (*set_cipher_op)(struct dvb_demux_feed *feed,
+ struct dmx_cipher_operations *cipher_ops);
u32 (*check_crc32)(struct dvb_demux_feed *feed,
const u8 *buf, size_t len);
void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst,
const u8 *src, size_t len);
+ int (*oob_command)(struct dvb_demux_feed *feed,
+ struct dmx_oob_command *cmd);
+ void (*convert_ts)(struct dvb_demux_feed *feed,
+ const u8 timestamp[TIMESTAMP_LEN],
+ u64 *timestampIn27Mhz);
+ int (*set_indexing)(struct dvb_demux_feed *feed);
+ int (*flush_decoder_buffer)(struct dvb_demux_feed *feed, size_t length);
int users;
#define MAX_DVB_DEMUX_USERS 10
@@ -136,10 +272,35 @@ struct dvb_demux {
struct timespec speed_last_time; /* for TS speed check */
uint32_t speed_pkts_cnt; /* for TS speed check */
+
+ enum dmx_tsp_format_t tsp_format;
+ size_t ts_packet_size;
+
+ enum dmx_playback_mode_t playback_mode;
+ int sw_filter_abort;
+
+ struct {
+ dmx_ts_fullness ts;
+ dmx_section_fullness sec;
+ } buffer_ctrl;
+
+ struct dvb_demux_rec_info *rec_info_pool;
+
+ /*
+ * the following is used for debugfs exposing info
+ * about dvb demux performance.
+ */
+#define MAX_DVB_DEMUX_NAME_LEN 10
+ char alias[MAX_DVB_DEMUX_NAME_LEN];
+
+ u32 total_process_time;
+ u32 total_crc_time;
};
int dvb_dmx_init(struct dvb_demux *dvbdemux);
void dvb_dmx_release(struct dvb_demux *dvbdemux);
+int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, const u8 *buf,
+ int should_lock);
void dvb_dmx_swfilter_packets(struct dvb_demux *dvbdmx, const u8 *buf,
size_t count);
void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count);
@@ -147,5 +308,116 @@ void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf,
size_t count);
void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf,
size_t count);
+void dvb_dmx_swfilter_format(
+ struct dvb_demux *demux, const u8 *buf,
+ size_t count,
+ enum dmx_tsp_format_t tsp_format);
+void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf,
+ const u8 timestamp[TIMESTAMP_LEN]);
+const struct dvb_dmx_video_patterns *dvb_dmx_get_pattern(u64 dmx_idx_pattern);
+int dvb_dmx_video_pattern_search(
+ const struct dvb_dmx_video_patterns
+ *patterns[DVB_DMX_MAX_SEARCH_PATTERN_NUM],
+ int patterns_num,
+ const u8 *buf, size_t buf_size,
+ struct dvb_dmx_video_prefix_size_masks *prefix_size_masks,
+ struct dvb_dmx_video_patterns_results *results);
+int dvb_demux_push_idx_event(struct dvb_demux_feed *feed,
+ struct dmx_index_event_info *idx_event, int should_lock);
+void dvb_dmx_process_idx_pattern(struct dvb_demux_feed *feed,
+ struct dvb_dmx_video_patterns_results *patterns, int pattern,
+ u64 curr_stc, u64 prev_stc,
+ u64 curr_match_tsp, u64 prev_match_tsp,
+ u64 curr_pusi_tsp, u64 prev_pusi_tsp);
+void dvb_dmx_notify_idx_events(struct dvb_demux_feed *feed, int should_lock);
+int dvb_dmx_notify_section_event(struct dvb_demux_feed *feed,
+ struct dmx_data_ready *event, int should_lock);
+void dvbdmx_ts_reset_pes_state(struct dvb_demux_feed *feed);
+
+/**
+ * dvb_dmx_is_video_feed - Returns whether the PES feed
+ * is video one.
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is video feed, 0 otherwise.
+ */
+static inline int dvb_dmx_is_video_feed(struct dvb_demux_feed *feed)
+{
+ if (feed->type != DMX_TYPE_TS)
+ return 0;
+
+ if (feed->ts_type & (~TS_DECODER))
+ return 0;
+
+ if ((feed->pes_type == DMX_PES_VIDEO0) ||
+ (feed->pes_type == DMX_PES_VIDEO1) ||
+ (feed->pes_type == DMX_PES_VIDEO2) ||
+ (feed->pes_type == DMX_PES_VIDEO3))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * dvb_dmx_is_pcr_feed - Returns whether the PES feed
+ * is PCR one.
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is PCR feed, 0 otherwise.
+ */
+static inline int dvb_dmx_is_pcr_feed(struct dvb_demux_feed *feed)
+{
+ if (feed->type != DMX_TYPE_TS)
+ return 0;
+
+ if (feed->ts_type & (~TS_DECODER))
+ return 0;
+
+ if ((feed->pes_type == DMX_PES_PCR0) ||
+ (feed->pes_type == DMX_PES_PCR1) ||
+ (feed->pes_type == DMX_PES_PCR2) ||
+ (feed->pes_type == DMX_PES_PCR3))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * dvb_dmx_is_sec_feed - Returns whether this is a section feed
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is a section feed, 0 otherwise.
+ */
+static inline int dvb_dmx_is_sec_feed(struct dvb_demux_feed *feed)
+{
+ return (feed->type == DMX_TYPE_SEC);
+}
+
+/**
+ * dvb_dmx_is_rec_feed - Returns whether this is a recording feed
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is recording feed, 0 otherwise.
+ */
+static inline int dvb_dmx_is_rec_feed(struct dvb_demux_feed *feed)
+{
+ if (feed->type != DMX_TYPE_TS)
+ return 0;
+
+ if (feed->ts_type & (TS_DECODER | TS_PAYLOAD_ONLY))
+ return 0;
+
+ return 1;
+}
+
+static inline u16 ts_pid(const u8 *buf)
+{
+ return ((buf[1] & 0x1f) << 8) + buf[2];
+}
+
#endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
index ce4332e80a91..454584a8bf17 100644
--- a/drivers/media/dvb-core/dvb_net.c
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -761,7 +761,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
- struct dmx_ts_feed *feed)
+ struct dmx_ts_feed *feed,
+ enum dmx_success success)
{
struct net_device *dev = feed->priv;
@@ -870,7 +871,8 @@ static void dvb_net_sec(struct net_device *dev,
static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
- struct dmx_section_filter *filter)
+ struct dmx_section_filter *filter,
+ enum dmx_success success)
{
struct net_device *dev = filter->priv;
diff --git a/drivers/media/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c
index 1100e98a7b1d..d61be58e22f0 100644
--- a/drivers/media/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb-core/dvb_ringbuffer.c
@@ -37,6 +37,8 @@
#define PKT_READY 0
#define PKT_DISPOSED 1
+#define PKT_PENDING 2
+
void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
@@ -55,7 +57,7 @@ void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
{
- return (rbuf->pread==rbuf->pwrite);
+ return (rbuf->pread == rbuf->pwrite);
}
@@ -167,25 +169,29 @@ ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t
}
ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
- const u8 __user *buf, size_t len)
+ const u8 __user *buf, size_t len)
{
- int status;
size_t todo = len;
size_t split;
+ ssize_t oldpwrite = rbuf->pwrite;
- split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
+ split = (rbuf->pwrite + len > rbuf->size) ?
+ rbuf->size - rbuf->pwrite :
+ 0;
if (split > 0) {
- status = copy_from_user(rbuf->data+rbuf->pwrite, buf, split);
- if (status)
- return len - todo;
+ if (copy_from_user(rbuf->data + rbuf->pwrite, buf, split))
+ return -EFAULT;
buf += split;
todo -= split;
rbuf->pwrite = 0;
}
- status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo);
- if (status)
- return len - todo;
+
+ if (copy_from_user(rbuf->data + rbuf->pwrite, buf, todo)) {
+ rbuf->pwrite = oldpwrite;
+ return -EFAULT;
+ }
+
rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
return len;
@@ -205,6 +211,31 @@ ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t le
return status;
}
+ssize_t dvb_ringbuffer_pkt_start(struct dvb_ringbuffer *rbuf, size_t len)
+{
+ ssize_t oldpwrite = rbuf->pwrite;
+
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_PENDING);
+
+ return oldpwrite;
+}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_start);
+
+int dvb_ringbuffer_pkt_close(struct dvb_ringbuffer *rbuf, ssize_t idx)
+{
+ idx = (idx + 2) % rbuf->size;
+
+ if (rbuf->data[idx] != PKT_PENDING)
+ return -EINVAL;
+
+ rbuf->data[idx] = PKT_READY;
+
+ return 0;
+}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_close);
+
ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
int offset, u8 __user *buf, size_t len)
{
@@ -212,6 +243,9 @@ ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
size_t split;
size_t pktlen;
+ if (DVB_RINGBUFFER_PEEK(rbuf, (idx+2)) != PKT_READY)
+ return -EINVAL;
+
pktlen = rbuf->data[idx] << 8;
pktlen |= rbuf->data[(idx + 1) % rbuf->size];
if (offset > pktlen) return -EINVAL;
@@ -232,6 +266,7 @@ ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
return len;
}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read_user);
ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
int offset, u8* buf, size_t len)
@@ -240,6 +275,9 @@ ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
size_t split;
size_t pktlen;
+ if (rbuf->data[(idx + 2) % rbuf->size] != PKT_READY)
+ return -EINVAL;
+
pktlen = rbuf->data[idx] << 8;
pktlen |= rbuf->data[(idx + 1) % rbuf->size];
if (offset > pktlen) return -EINVAL;
@@ -257,6 +295,7 @@ ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
memcpy(buf, rbuf->data+idx, todo);
return len;
}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
{
@@ -276,6 +315,7 @@ void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
}
}
}
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
{
@@ -291,7 +331,10 @@ ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t*
idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
}
- consumed = (idx - rbuf->pread) % rbuf->size;
+ if (idx >= rbuf->pread)
+ consumed = idx - rbuf->pread;
+ else
+ consumed = rbuf->size - (rbuf->pread - idx);
while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
@@ -304,6 +347,9 @@ ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t*
return idx;
}
+ if (curpktstatus == PKT_PENDING)
+ return -EFAULT;
+
consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
}
@@ -311,8 +357,7 @@ ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t*
// no packets available
return -1;
}
-
-
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);
EXPORT_SYMBOL(dvb_ringbuffer_init);
EXPORT_SYMBOL(dvb_ringbuffer_empty);
diff --git a/drivers/media/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb-core/dvb_ringbuffer.h
index 3ebc2d34b4a2..2fe589e5d7ea 100644
--- a/drivers/media/dvb-core/dvb_ringbuffer.h
+++ b/drivers/media/dvb-core/dvb_ringbuffer.h
@@ -108,6 +108,9 @@ extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf);
/* advance read ptr by @num: bytes */
#define DVB_RINGBUFFER_SKIP(rbuf,num) \
(rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size
+/* advance write ptr by <num> bytes */
+#define DVB_RINGBUFFER_PUSH(rbuf, num) \
+ ((rbuf)->pwrite = (((rbuf)->pwrite+(num))%(rbuf)->size))
/*
* read @len: bytes from ring buffer into @buf:
@@ -200,4 +203,31 @@ extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx);
extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen);
+/**
+ * Start a new packet that will be written directly by the user to the packet
+ * buffer.
+ * The function only writes the header of the packet into the packet buffer,
+ * and the packet is in pending state (can't be read by the reader) until it is
+ * closed using dvb_ringbuffer_pkt_close. You must write the data into the
+ * packet buffer using dvb_ringbuffer_write followed by
+ * dvb_ringbuffer_pkt_close.
+ *
+ * @rbuf: Ringbuffer concerned.
+ * @len: Size of the packet's data
+ * returns Index of the packet's header that was started.
+ */
+extern ssize_t dvb_ringbuffer_pkt_start(struct dvb_ringbuffer *rbuf,
+ size_t len);
+
+/**
+ * Close a packet that was started using dvb_ringbuffer_pkt_start.
+ * The packet will be marked as ready to be ready.
+ *
+ * @rbuf: Ringbuffer concerned.
+ * @idx: Packet index that was returned by dvb_ringbuffer_pkt_start
+ * returns error status, -EINVAL if the provided index is invalid
+ */
+extern int dvb_ringbuffer_pkt_close(struct dvb_ringbuffer *rbuf, ssize_t idx);
+
+
#endif /* _DVB_RINGBUFFER_H_ */
diff --git a/drivers/media/platform/msm/Kconfig b/drivers/media/platform/msm/Kconfig
index 16060773ac96..e2523e06ab76 100644
--- a/drivers/media/platform/msm/Kconfig
+++ b/drivers/media/platform/msm/Kconfig
@@ -41,3 +41,5 @@ endif # MSMB_CAMERA
source "drivers/media/platform/msm/vidc/Kconfig"
source "drivers/media/platform/msm/sde/Kconfig"
+source "drivers/media/platform/msm/dvb/Kconfig"
+source "drivers/media/platform/msm/broadcast/Kconfig"
diff --git a/drivers/media/platform/msm/Makefile b/drivers/media/platform/msm/Makefile
index ff0369bdeca5..a3f802d3ce59 100644
--- a/drivers/media/platform/msm/Makefile
+++ b/drivers/media/platform/msm/Makefile
@@ -5,3 +5,5 @@
obj-$(CONFIG_MSMB_CAMERA) += camera_v2/
obj-$(CONFIG_MSM_VIDC_V4L2) += vidc/
obj-y += sde/
+obj-y += broadcast/
+obj-$(CONFIG_DVB_MPQ) += dvb/
diff --git a/drivers/media/platform/msm/broadcast/Kconfig b/drivers/media/platform/msm/broadcast/Kconfig
new file mode 100644
index 000000000000..cdd1b2091179
--- /dev/null
+++ b/drivers/media/platform/msm/broadcast/Kconfig
@@ -0,0 +1,14 @@
+#
+# MSM Broadcast subsystem drivers
+#
+
+config TSPP
+ depends on ARCH_QCOM
+ tristate "TSPP (Transport Stream Packet Processor) Support"
+ ---help---
+ Transport Stream Packet Processor v1 is used to offload the
+ processing of MPEG transport streams from the main processor.
+ It is used to process incoming transport streams from TSIF
+ to supports use-cases such as transport stream live play
+ and recording.
+ This can also be compiled as a loadable module.
diff --git a/drivers/media/platform/msm/broadcast/Makefile b/drivers/media/platform/msm/broadcast/Makefile
new file mode 100644
index 000000000000..3735bdc212ad
--- /dev/null
+++ b/drivers/media/platform/msm/broadcast/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for MSM Broadcast subsystem drivers.
+#
+obj-$(CONFIG_TSPP) += tspp.o
diff --git a/drivers/media/platform/msm/broadcast/tspp.c b/drivers/media/platform/msm/broadcast/tspp.c
new file mode 100644
index 000000000000..275b8b90af05
--- /dev/null
+++ b/drivers/media/platform/msm/broadcast/tspp.c
@@ -0,0 +1,3094 @@
+/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h> /* Just for modules */
+#include <linux/kernel.h> /* Only for KERN_INFO */
+#include <linux/err.h> /* Error macros */
+#include <linux/list.h> /* Linked list */
+#include <linux/cdev.h>
+#include <linux/init.h> /* Needed for the macros */
+#include <linux/io.h> /* IO macros */
+#include <linux/device.h> /* Device drivers need this */
+#include <linux/sched.h> /* Externally defined globals */
+#include <linux/pm_runtime.h> /* Runtime power management */
+#include <linux/fs.h>
+#include <linux/uaccess.h> /* copy_to_user */
+#include <linux/slab.h> /* kfree, kzalloc */
+#include <linux/ioport.h> /* XXX_ mem_region */
+#include <linux/dma-mapping.h> /* dma_XXX */
+#include <linux/dmapool.h> /* DMA pools */
+#include <linux/delay.h> /* msleep */
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/poll.h> /* poll() file op */
+#include <linux/wait.h> /* wait() macros, sleeping */
+#include <linux/bitops.h> /* BIT() macro */
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/rpm-smd-regulator.h>
+#include <linux/msm-sps.h> /* BAM stuff */
+#include <linux/wakelock.h> /* Locking functions */
+#include <linux/timer.h> /* Timer services */
+#include <linux/jiffies.h> /* Jiffies counter */
+#include <linux/qcom_tspp.h>
+#include <linux/debugfs.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/string.h>
+#include <linux/msm-bus.h>
+#include <linux/interrupt.h> /* tasklet */
+
+/*
+ * General defines
+ */
+#define TSPP_TSIF_INSTANCES 2
+#define TSPP_GPIOS_PER_TSIF 4
+#define TSPP_FILTER_TABLES 3
+#define TSPP_MAX_DEVICES 1
+#define TSPP_NUM_CHANNELS 16
+#define TSPP_NUM_PRIORITIES 16
+#define TSPP_NUM_KEYS 8
+#define INVALID_CHANNEL 0xFFFFFFFF
+#define TSPP_BAM_DEFAULT_IPC_LOGLVL 2
+/*
+ * BAM descriptor FIFO size (in number of descriptors).
+ * Max number of descriptors allowed by SPS which is 8K-1.
+ */
+#define TSPP_SPS_DESCRIPTOR_COUNT (8 * 1024 - 1)
+#define TSPP_PACKET_LENGTH 188
+#define TSPP_MIN_BUFFER_SIZE (TSPP_PACKET_LENGTH)
+
+/* Max descriptor buffer size allowed by SPS */
+#define TSPP_MAX_BUFFER_SIZE (32 * 1024 - 1)
+
+/*
+ * Returns whether to use DMA pool for TSPP output buffers.
+ * For buffers smaller than page size, using DMA pool
+ * provides better memory utilization as dma_alloc_coherent
+ * allocates minimum of page size.
+ */
+#define TSPP_USE_DMA_POOL(buff_size) ((buff_size) < PAGE_SIZE)
+
+/*
+ * Max allowed TSPP buffers/descriptors.
+ * If SPS desc FIFO holds X descriptors, we can queue up to X-1 descriptors.
+ */
+#define TSPP_NUM_BUFFERS (TSPP_SPS_DESCRIPTOR_COUNT - 1)
+#define TSPP_TSIF_DEFAULT_TIME_LIMIT 60
+#define SPS_DESCRIPTOR_SIZE 8
+#define MIN_ACCEPTABLE_BUFFER_COUNT 2
+#define TSPP_DEBUG(msg...)
+
+/*
+ * TSIF register offsets
+ */
+#define TSIF_STS_CTL_OFF (0x0)
+#define TSIF_TIME_LIMIT_OFF (0x4)
+#define TSIF_CLK_REF_OFF (0x8)
+#define TSIF_LPBK_FLAGS_OFF (0xc)
+#define TSIF_LPBK_DATA_OFF (0x10)
+#define TSIF_TEST_CTL_OFF (0x14)
+#define TSIF_TEST_MODE_OFF (0x18)
+#define TSIF_TEST_RESET_OFF (0x1c)
+#define TSIF_TEST_EXPORT_OFF (0x20)
+#define TSIF_TEST_CURRENT_OFF (0x24)
+
+#define TSIF_DATA_PORT_OFF (0x100)
+
+/* bits for TSIF_STS_CTL register */
+#define TSIF_STS_CTL_EN_IRQ BIT(28)
+#define TSIF_STS_CTL_PACK_AVAIL BIT(27)
+#define TSIF_STS_CTL_1ST_PACKET BIT(26)
+#define TSIF_STS_CTL_OVERFLOW BIT(25)
+#define TSIF_STS_CTL_LOST_SYNC BIT(24)
+#define TSIF_STS_CTL_TIMEOUT BIT(23)
+#define TSIF_STS_CTL_INV_SYNC BIT(21)
+#define TSIF_STS_CTL_INV_NULL BIT(20)
+#define TSIF_STS_CTL_INV_ERROR BIT(19)
+#define TSIF_STS_CTL_INV_ENABLE BIT(18)
+#define TSIF_STS_CTL_INV_DATA BIT(17)
+#define TSIF_STS_CTL_INV_CLOCK BIT(16)
+#define TSIF_STS_CTL_SPARE BIT(15)
+#define TSIF_STS_CTL_EN_NULL BIT(11)
+#define TSIF_STS_CTL_EN_ERROR BIT(10)
+#define TSIF_STS_CTL_LAST_BIT BIT(9)
+#define TSIF_STS_CTL_EN_TIME_LIM BIT(8)
+#define TSIF_STS_CTL_EN_TCR BIT(7)
+#define TSIF_STS_CTL_TEST_MODE BIT(6)
+#define TSIF_STS_CTL_MODE_2 BIT(5)
+#define TSIF_STS_CTL_EN_DM BIT(4)
+#define TSIF_STS_CTL_STOP BIT(3)
+#define TSIF_STS_CTL_START BIT(0)
+
+/*
+ * TSPP register offsets
+ */
+#define TSPP_RST 0x00
+#define TSPP_CLK_CONTROL 0x04
+#define TSPP_CONFIG 0x08
+#define TSPP_CONTROL 0x0C
+#define TSPP_PS_DISABLE 0x10
+#define TSPP_MSG_IRQ_STATUS 0x14
+#define TSPP_MSG_IRQ_MASK 0x18
+#define TSPP_IRQ_STATUS 0x1C
+#define TSPP_IRQ_MASK 0x20
+#define TSPP_IRQ_CLEAR 0x24
+#define TSPP_PIPE_ERROR_STATUS(_n) (0x28 + (_n << 2))
+#define TSPP_STATUS 0x68
+#define TSPP_CURR_TSP_HEADER 0x6C
+#define TSPP_CURR_PID_FILTER 0x70
+#define TSPP_SYSTEM_KEY(_n) (0x74 + (_n << 2))
+#define TSPP_CBC_INIT_VAL(_n) (0x94 + (_n << 2))
+#define TSPP_DATA_KEY_RESET 0x9C
+#define TSPP_KEY_VALID 0xA0
+#define TSPP_KEY_ERROR 0xA4
+#define TSPP_TEST_CTRL 0xA8
+#define TSPP_VERSION 0xAC
+#define TSPP_GENERICS 0xB0
+#define TSPP_NOP 0xB4
+
+/*
+ * Register bit definitions
+ */
+/* TSPP_RST */
+#define TSPP_RST_RESET BIT(0)
+
+/* TSPP_CLK_CONTROL */
+#define TSPP_CLK_CONTROL_FORCE_CRYPTO BIT(9)
+#define TSPP_CLK_CONTROL_FORCE_PES_PL BIT(8)
+#define TSPP_CLK_CONTROL_FORCE_PES_AF BIT(7)
+#define TSPP_CLK_CONTROL_FORCE_RAW_CTRL BIT(6)
+#define TSPP_CLK_CONTROL_FORCE_PERF_CNT BIT(5)
+#define TSPP_CLK_CONTROL_FORCE_CTX_SEARCH BIT(4)
+#define TSPP_CLK_CONTROL_FORCE_TSP_PROC BIT(3)
+#define TSPP_CLK_CONTROL_FORCE_CONS_AHB2MEM BIT(2)
+#define TSPP_CLK_CONTROL_FORCE_TS_AHB2MEM BIT(1)
+#define TSPP_CLK_CONTROL_SET_CLKON BIT(0)
+
+/* TSPP_CONFIG */
+#define TSPP_CONFIG_SET_PACKET_LENGTH(_a, _b) (_a = (_a & 0xF0) | \
+((_b & 0xF) << 8))
+#define TSPP_CONFIG_GET_PACKET_LENGTH(_a) ((_a >> 8) & 0xF)
+#define TSPP_CONFIG_DUP_WITH_DISC_EN BIT(7)
+#define TSPP_CONFIG_PES_SYNC_ERROR_MASK BIT(6)
+#define TSPP_CONFIG_PS_LEN_ERR_MASK BIT(5)
+#define TSPP_CONFIG_PS_CONT_ERR_UNSP_MASK BIT(4)
+#define TSPP_CONFIG_PS_CONT_ERR_MASK BIT(3)
+#define TSPP_CONFIG_PS_DUP_TSP_MASK BIT(2)
+#define TSPP_CONFIG_TSP_ERR_IND_MASK BIT(1)
+#define TSPP_CONFIG_TSP_SYNC_ERR_MASK BIT(0)
+
+/* TSPP_CONTROL */
+#define TSPP_CONTROL_PID_FILTER_LOCK BIT(5)
+#define TSPP_CONTROL_FORCE_KEY_CALC BIT(4)
+#define TSPP_CONTROL_TSP_CONS_SRC_DIS BIT(3)
+#define TSPP_CONTROL_TSP_TSIF1_SRC_DIS BIT(2)
+#define TSPP_CONTROL_TSP_TSIF0_SRC_DIS BIT(1)
+#define TSPP_CONTROL_PERF_COUNT_INIT BIT(0)
+
+/* TSPP_MSG_IRQ_STATUS + TSPP_MSG_IRQ_MASK */
+#define TSPP_MSG_TSPP_IRQ BIT(2)
+#define TSPP_MSG_TSIF_1_IRQ BIT(1)
+#define TSPP_MSG_TSIF_0_IRQ BIT(0)
+
+/* TSPP_IRQ_STATUS + TSPP_IRQ_MASK + TSPP_IRQ_CLEAR */
+#define TSPP_IRQ_STATUS_TSP_RD_CMPL BIT(19)
+#define TSPP_IRQ_STATUS_KEY_ERROR BIT(18)
+#define TSPP_IRQ_STATUS_KEY_SWITCHED_BAD BIT(17)
+#define TSPP_IRQ_STATUS_KEY_SWITCHED BIT(16)
+#define TSPP_IRQ_STATUS_PS_BROKEN(_n) BIT((_n))
+
+/* TSPP_PIPE_ERROR_STATUS */
+#define TSPP_PIPE_PES_SYNC_ERROR BIT(3)
+#define TSPP_PIPE_PS_LENGTH_ERROR BIT(2)
+#define TSPP_PIPE_PS_CONTINUITY_ERROR BIT(1)
+#define TSPP_PIP_PS_LOST_START BIT(0)
+
+/* TSPP_STATUS */
+#define TSPP_STATUS_TSP_PKT_AVAIL BIT(10)
+#define TSPP_STATUS_TSIF1_DM_REQ BIT(6)
+#define TSPP_STATUS_TSIF0_DM_REQ BIT(2)
+#define TSPP_CURR_FILTER_TABLE BIT(0)
+
+/* TSPP_GENERICS */
+#define TSPP_GENERICS_CRYPTO_GEN BIT(12)
+#define TSPP_GENERICS_MAX_CONS_PIPES BIT(7)
+#define TSPP_GENERICS_MAX_PIPES BIT(2)
+#define TSPP_GENERICS_TSIF_1_GEN BIT(1)
+#define TSPP_GENERICS_TSIF_0_GEN BIT(0)
+
+/*
+ * TSPP memory regions
+ */
+#define TSPP_PID_FILTER_TABLE0 0x800
+#define TSPP_PID_FILTER_TABLE1 0x880
+#define TSPP_PID_FILTER_TABLE2 0x900
+#define TSPP_GLOBAL_PERFORMANCE 0x980 /* see tspp_global_performance */
+#define TSPP_PIPE_CONTEXT 0x990 /* see tspp_pipe_context */
+#define TSPP_PIPE_PERFORMANCE 0x998 /* see tspp_pipe_performance */
+#define TSPP_TSP_BUFF_WORD(_n) (0xC10 + (_n << 2))
+#define TSPP_DATA_KEY 0xCD0
+
+struct debugfs_entry {
+ const char *name;
+ mode_t mode;
+ int offset;
+};
+
+static const struct debugfs_entry debugfs_tsif_regs[] = {
+ {"sts_ctl", S_IRUGO | S_IWUSR, TSIF_STS_CTL_OFF},
+ {"time_limit", S_IRUGO | S_IWUSR, TSIF_TIME_LIMIT_OFF},
+ {"clk_ref", S_IRUGO | S_IWUSR, TSIF_CLK_REF_OFF},
+ {"lpbk_flags", S_IRUGO | S_IWUSR, TSIF_LPBK_FLAGS_OFF},
+ {"lpbk_data", S_IRUGO | S_IWUSR, TSIF_LPBK_DATA_OFF},
+ {"test_ctl", S_IRUGO | S_IWUSR, TSIF_TEST_CTL_OFF},
+ {"test_mode", S_IRUGO | S_IWUSR, TSIF_TEST_MODE_OFF},
+ {"test_reset", S_IWUSR, TSIF_TEST_RESET_OFF},
+ {"test_export", S_IRUGO | S_IWUSR, TSIF_TEST_EXPORT_OFF},
+ {"test_current", S_IRUGO, TSIF_TEST_CURRENT_OFF},
+ {"data_port", S_IRUSR, TSIF_DATA_PORT_OFF},
+};
+
+static const struct debugfs_entry debugfs_tspp_regs[] = {
+ {"rst", S_IRUGO | S_IWUSR, TSPP_RST},
+ {"clk_control", S_IRUGO | S_IWUSR, TSPP_CLK_CONTROL},
+ {"config", S_IRUGO | S_IWUSR, TSPP_CONFIG},
+ {"control", S_IRUGO | S_IWUSR, TSPP_CONTROL},
+ {"ps_disable", S_IRUGO | S_IWUSR, TSPP_PS_DISABLE},
+ {"msg_irq_status", S_IRUGO | S_IWUSR, TSPP_MSG_IRQ_STATUS},
+ {"msg_irq_mask", S_IRUGO | S_IWUSR, TSPP_MSG_IRQ_MASK},
+ {"irq_status", S_IRUGO | S_IWUSR, TSPP_IRQ_STATUS},
+ {"irq_mask", S_IRUGO | S_IWUSR, TSPP_IRQ_MASK},
+ {"irq_clear", S_IRUGO | S_IWUSR, TSPP_IRQ_CLEAR},
+ /* {"pipe_error_status",S_IRUGO | S_IWUSR, TSPP_PIPE_ERROR_STATUS}, */
+ {"status", S_IRUGO | S_IWUSR, TSPP_STATUS},
+ {"curr_tsp_header", S_IRUGO | S_IWUSR, TSPP_CURR_TSP_HEADER},
+ {"curr_pid_filter", S_IRUGO | S_IWUSR, TSPP_CURR_PID_FILTER},
+ /* {"system_key", S_IRUGO | S_IWUSR, TSPP_SYSTEM_KEY}, */
+ /* {"cbc_init_val", S_IRUGO | S_IWUSR, TSPP_CBC_INIT_VAL}, */
+ {"data_key_reset", S_IRUGO | S_IWUSR, TSPP_DATA_KEY_RESET},
+ {"key_valid", S_IRUGO | S_IWUSR, TSPP_KEY_VALID},
+ {"key_error", S_IRUGO | S_IWUSR, TSPP_KEY_ERROR},
+ {"test_ctrl", S_IRUGO | S_IWUSR, TSPP_TEST_CTRL},
+ {"version", S_IRUGO | S_IWUSR, TSPP_VERSION},
+ {"generics", S_IRUGO | S_IWUSR, TSPP_GENERICS},
+ {"pid_filter_table0", S_IRUGO | S_IWUSR, TSPP_PID_FILTER_TABLE0},
+ {"pid_filter_table1", S_IRUGO | S_IWUSR, TSPP_PID_FILTER_TABLE1},
+ {"pid_filter_table2", S_IRUGO | S_IWUSR, TSPP_PID_FILTER_TABLE2},
+ {"tsp_total_num", S_IRUGO | S_IWUSR, TSPP_GLOBAL_PERFORMANCE},
+ {"tsp_ignored_num", S_IRUGO | S_IWUSR, TSPP_GLOBAL_PERFORMANCE + 4},
+ {"tsp_err_ind_num", S_IRUGO | S_IWUSR, TSPP_GLOBAL_PERFORMANCE + 8},
+ {"tsp_sync_err_num", S_IRUGO | S_IWUSR, TSPP_GLOBAL_PERFORMANCE + 16},
+ {"pipe_context", S_IRUGO | S_IWUSR, TSPP_PIPE_CONTEXT},
+ {"pipe_performance", S_IRUGO | S_IWUSR, TSPP_PIPE_PERFORMANCE},
+ {"data_key", S_IRUGO | S_IWUSR, TSPP_DATA_KEY}
+};
+
+struct tspp_pid_filter {
+ u32 filter; /* see FILTER_ macros */
+ u32 config; /* see FILTER_ macros */
+};
+
+/* tsp_info */
+#define FILTER_HEADER_ERROR_MASK BIT(7)
+#define FILTER_TRANS_END_DISABLE BIT(6)
+#define FILTER_DEC_ON_ERROR_EN BIT(5)
+#define FILTER_DECRYPT BIT(4)
+#define FILTER_HAS_ENCRYPTION(_p) (_p->config & FILTER_DECRYPT)
+#define FILTER_GET_PIPE_NUMBER0(_p) (_p->config & 0xF)
+#define FILTER_SET_PIPE_NUMBER0(_p, _b) (_p->config = \
+ (_p->config & ~0xF) | (_b & 0xF))
+#define FILTER_GET_PIPE_PROCESS0(_p) ((_p->filter >> 30) & 0x3)
+#define FILTER_SET_PIPE_PROCESS0(_p, _b) (_p->filter = \
+ (_p->filter & ~(0x3<<30)) | ((_b & 0x3) << 30))
+#define FILTER_GET_PIPE_PID(_p) ((_p->filter >> 13) & 0x1FFF)
+#define FILTER_SET_PIPE_PID(_p, _b) (_p->filter = \
+ (_p->filter & ~(0x1FFF<<13)) | ((_b & 0x1FFF) << 13))
+#define FILTER_GET_PID_MASK(_p) (_p->filter & 0x1FFF)
+#define FILTER_SET_PID_MASK(_p, _b) (_p->filter = \
+ (_p->filter & ~0x1FFF) | (_b & 0x1FFF))
+#define FILTER_GET_PIPE_PROCESS1(_p) ((_p->config >> 30) & 0x3)
+#define FILTER_SET_PIPE_PROCESS1(_p, _b) (_p->config = \
+ (_p->config & ~(0x3<<30)) | ((_b & 0x3) << 30))
+#define FILTER_GET_KEY_NUMBER(_p) ((_p->config >> 8) & 0x7)
+#define FILTER_SET_KEY_NUMBER(_p, _b) (_p->config = \
+ (_p->config & ~(0x7<<8)) | ((_b & 0x7) << 8))
+
+struct tspp_global_performance_regs {
+ u32 tsp_total;
+ u32 tsp_ignored;
+ u32 tsp_error;
+ u32 tsp_sync;
+};
+
+struct tspp_pipe_context_regs {
+ u16 pes_bytes_left;
+ u16 count;
+ u32 tsif_suffix;
+} __packed;
+#define CONTEXT_GET_STATE(_a) (_a & 0x3)
+#define CONTEXT_UNSPEC_LENGTH BIT(11)
+#define CONTEXT_GET_CONT_COUNT(_a) ((_a >> 12) & 0xF)
+
+#define MSEC_TO_JIFFIES(msec) ((msec) * HZ / 1000)
+
+struct tspp_pipe_performance_regs {
+ u32 tsp_total;
+ u32 ps_duplicate_tsp;
+ u32 tsp_no_payload;
+ u32 tsp_broken_ps;
+ u32 ps_total_num;
+ u32 ps_continuity_error;
+ u32 ps_length_error;
+ u32 pes_sync_error;
+};
+
+struct tspp_tsif_device {
+ void __iomem *base;
+ u32 time_limit;
+ u32 ref_count;
+ enum tspp_tsif_mode mode;
+ int clock_inverse;
+ int data_inverse;
+ int sync_inverse;
+ int enable_inverse;
+ u32 tsif_irq;
+
+ /* debugfs */
+ struct dentry *dent_tsif;
+ struct dentry *debugfs_tsif_regs[ARRAY_SIZE(debugfs_tsif_regs)];
+ u32 stat_rx;
+ u32 stat_overflow;
+ u32 stat_lost_sync;
+ u32 stat_timeout;
+};
+
+enum tspp_buf_state {
+ TSPP_BUF_STATE_EMPTY, /* buffer has been allocated, but not waiting */
+ TSPP_BUF_STATE_WAITING, /* buffer is waiting to be filled */
+ TSPP_BUF_STATE_DATA, /* buffer is not empty and can be read */
+ TSPP_BUF_STATE_LOCKED /* buffer is being read by a client */
+};
+
+struct tspp_mem_buffer {
+ struct tspp_mem_buffer *next;
+ struct sps_mem_buffer sps;
+ struct tspp_data_descriptor desc; /* buffer descriptor for kernel api */
+ enum tspp_buf_state state;
+ size_t filled; /* how much data this buffer is holding */
+ int read_index; /* where to start reading data from */
+};
+
+/* this represents each char device 'channel' */
+struct tspp_channel {
+ struct tspp_device *pdev; /* can use container_of instead? */
+ struct sps_pipe *pipe;
+ struct sps_connect config;
+ struct sps_register_event event;
+ struct tspp_mem_buffer *data; /* list of buffers */
+ struct tspp_mem_buffer *read; /* first buffer ready to be read */
+ struct tspp_mem_buffer *waiting; /* first outstanding transfer */
+ struct tspp_mem_buffer *locked; /* buffer currently being read */
+ wait_queue_head_t in_queue; /* set when data is received */
+ u32 id; /* channel id (0-15) */
+ int used; /* is this channel in use? */
+ int key; /* which encryption key index is used */
+ u32 buffer_size; /* size of the sps transfer buffers */
+ u32 max_buffers; /* how many buffers should be allocated */
+ u32 buffer_count; /* how many buffers are actually allocated */
+ u32 filter_count; /* how many filters have been added to this channel */
+ u32 int_freq; /* generate interrupts every x descriptors */
+ enum tspp_source src;
+ enum tspp_mode mode;
+ tspp_notifier *notifier; /* used only with kernel api */
+ void *notify_data; /* data to be passed with the notifier */
+ u32 expiration_period_ms; /* notification on partially filled buffers */
+ struct timer_list expiration_timer;
+ struct dma_pool *dma_pool;
+ tspp_memfree *memfree; /* user defined memory free function */
+ void *user_info; /* user cookie passed to memory alloc/free function */
+};
+
+struct tspp_pid_filter_table {
+ struct tspp_pid_filter filter[TSPP_NUM_PRIORITIES];
+};
+
+struct tspp_key_entry {
+ u32 even_lsb;
+ u32 even_msb;
+ u32 odd_lsb;
+ u32 odd_msb;
+};
+
+struct tspp_key_table {
+ struct tspp_key_entry entry[TSPP_NUM_KEYS];
+};
+
+struct tspp_pinctrl {
+ struct pinctrl *pinctrl;
+
+ struct pinctrl_state *disabled;
+ struct pinctrl_state *tsif0_mode1;
+ struct pinctrl_state *tsif0_mode2;
+ struct pinctrl_state *tsif1_mode1;
+ struct pinctrl_state *tsif1_mode2;
+ struct pinctrl_state *dual_mode1;
+ struct pinctrl_state *dual_mode2;
+
+ bool tsif0_active;
+ bool tsif1_active;
+};
+
+/* this represents the actual hardware device */
+struct tspp_device {
+ struct list_head devlist; /* list of all devices */
+ struct platform_device *pdev;
+ void __iomem *base;
+ uint32_t tsif_bus_client;
+ unsigned int tspp_irq;
+ unsigned int bam_irq;
+ unsigned long bam_handle;
+ struct sps_bam_props bam_props;
+ struct wakeup_source ws;
+ spinlock_t spinlock;
+ struct tasklet_struct tlet;
+ struct tspp_tsif_device tsif[TSPP_TSIF_INSTANCES];
+ /* clocks */
+ struct clk *tsif_pclk;
+ struct clk *tsif_ref_clk;
+ /* regulators */
+ struct regulator *tsif_vreg;
+ /* data */
+ struct tspp_pid_filter_table *filters[TSPP_FILTER_TABLES];
+ struct tspp_channel channels[TSPP_NUM_CHANNELS];
+ struct tspp_key_table *tspp_key_table;
+ struct tspp_global_performance_regs *tspp_global_performance;
+ struct tspp_pipe_context_regs *tspp_pipe_context;
+ struct tspp_pipe_performance_regs *tspp_pipe_performance;
+ bool req_irqs;
+ /* pinctrl */
+ struct mutex mutex;
+ struct tspp_pinctrl pinctrl;
+
+ struct dentry *dent;
+ struct dentry *debugfs_regs[ARRAY_SIZE(debugfs_tspp_regs)];
+};
+
+static int tspp_key_entry;
+static u32 channel_id; /* next channel id number to assign */
+
+static LIST_HEAD(tspp_devices);
+
+/*** IRQ ***/
+static irqreturn_t tspp_isr(int irq, void *dev)
+{
+ struct tspp_device *device = dev;
+ u32 status, mask;
+ u32 data;
+
+ status = readl_relaxed(device->base + TSPP_IRQ_STATUS);
+ mask = readl_relaxed(device->base + TSPP_IRQ_MASK);
+ status &= mask;
+
+ if (!status) {
+ dev_warn(&device->pdev->dev, "Spurious interrupt");
+ return IRQ_NONE;
+ }
+
+ /* if (status & TSPP_IRQ_STATUS_TSP_RD_CMPL) */
+
+ if (status & TSPP_IRQ_STATUS_KEY_ERROR) {
+ /* read the key error info */
+ data = readl_relaxed(device->base + TSPP_KEY_ERROR);
+ dev_info(&device->pdev->dev, "key error 0x%x", data);
+ }
+ if (status & TSPP_IRQ_STATUS_KEY_SWITCHED_BAD) {
+ data = readl_relaxed(device->base + TSPP_KEY_VALID);
+ dev_info(&device->pdev->dev, "key invalidated: 0x%x", data);
+ }
+ if (status & TSPP_IRQ_STATUS_KEY_SWITCHED)
+ dev_info(&device->pdev->dev, "key switched");
+
+ if (status & 0xffff)
+ dev_info(&device->pdev->dev, "broken pipe %i", status & 0xffff);
+
+ writel_relaxed(status, device->base + TSPP_IRQ_CLEAR);
+
+ /*
+ * Before returning IRQ_HANDLED to the generic interrupt handling
+ * framework need to make sure all operations including clearing of
+ * interrupt status registers in the hardware is performed.
+ * Thus a barrier after clearing the interrupt status register
+ * is required to guarantee that the interrupt status register has
+ * really been cleared by the time we return from this handler.
+ */
+ wmb();
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tsif_isr(int irq, void *dev)
+{
+ struct tspp_tsif_device *tsif_device = dev;
+ u32 sts_ctl = ioread32(tsif_device->base + TSIF_STS_CTL_OFF);
+
+ if (!(sts_ctl & (TSIF_STS_CTL_PACK_AVAIL |
+ TSIF_STS_CTL_OVERFLOW |
+ TSIF_STS_CTL_LOST_SYNC |
+ TSIF_STS_CTL_TIMEOUT)))
+ return IRQ_NONE;
+
+ if (sts_ctl & TSIF_STS_CTL_OVERFLOW)
+ tsif_device->stat_overflow++;
+
+ if (sts_ctl & TSIF_STS_CTL_LOST_SYNC)
+ tsif_device->stat_lost_sync++;
+
+ if (sts_ctl & TSIF_STS_CTL_TIMEOUT)
+ tsif_device->stat_timeout++;
+
+ iowrite32(sts_ctl, tsif_device->base + TSIF_STS_CTL_OFF);
+
+ /*
+ * Before returning IRQ_HANDLED to the generic interrupt handling
+ * framework need to make sure all operations including clearing of
+ * interrupt status registers in the hardware is performed.
+ * Thus a barrier after clearing the interrupt status register
+ * is required to guarantee that the interrupt status register has
+ * really been cleared by the time we return from this handler.
+ */
+ wmb();
+ return IRQ_HANDLED;
+}
+
+/*** callbacks ***/
+static void tspp_sps_complete_cb(struct sps_event_notify *notify)
+{
+ struct tspp_device *pdev;
+
+ if (!notify || !notify->user)
+ return;
+
+ pdev = notify->user;
+ tasklet_schedule(&pdev->tlet);
+}
+
+static void tspp_expiration_timer(unsigned long data)
+{
+ struct tspp_device *pdev = (struct tspp_device *)data;
+
+ if (pdev)
+ tasklet_schedule(&pdev->tlet);
+}
+
+/*** tasklet ***/
+static void tspp_sps_complete_tlet(unsigned long data)
+{
+ int i;
+ int complete;
+ unsigned long flags;
+ struct sps_iovec iovec;
+ struct tspp_channel *channel;
+ struct tspp_device *device = (struct tspp_device *)data;
+
+ spin_lock_irqsave(&device->spinlock, flags);
+
+ for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
+ complete = 0;
+ channel = &device->channels[i];
+
+ if (!channel->used || !channel->waiting)
+ continue;
+
+ /* stop the expiration timer */
+ if (channel->expiration_period_ms)
+ del_timer(&channel->expiration_timer);
+
+ /* get completions */
+ while (channel->waiting->state == TSPP_BUF_STATE_WAITING) {
+ if (sps_get_iovec(channel->pipe, &iovec) != 0) {
+ pr_err("tspp: Error in iovec on channel %i",
+ channel->id);
+ break;
+ }
+ if (iovec.size == 0)
+ break;
+
+ if (DESC_FULL_ADDR(iovec.flags, iovec.addr)
+ != channel->waiting->sps.phys_base)
+ pr_err("tspp: buffer mismatch %pa",
+ &channel->waiting->sps.phys_base);
+
+ complete = 1;
+ channel->waiting->state = TSPP_BUF_STATE_DATA;
+ channel->waiting->filled = iovec.size;
+ channel->waiting->read_index = 0;
+
+ if (channel->src == TSPP_SOURCE_TSIF0)
+ device->tsif[0].stat_rx++;
+ else if (channel->src == TSPP_SOURCE_TSIF1)
+ device->tsif[1].stat_rx++;
+
+ /* update the pointers */
+ channel->waiting = channel->waiting->next;
+ }
+
+ /* wake any waiting processes */
+ if (complete) {
+ wake_up_interruptible(&channel->in_queue);
+
+ /* call notifiers */
+ if (channel->notifier)
+ channel->notifier(channel->id,
+ channel->notify_data);
+ }
+
+ /* restart expiration timer */
+ if (channel->expiration_period_ms)
+ mod_timer(&channel->expiration_timer,
+ jiffies +
+ MSEC_TO_JIFFIES(
+ channel->expiration_period_ms));
+ }
+
+ spin_unlock_irqrestore(&device->spinlock, flags);
+}
+
+static int tspp_config_gpios(struct tspp_device *device,
+ enum tspp_source source,
+ int enable)
+{
+ int ret;
+ struct pinctrl_state *s;
+ struct tspp_pinctrl *p = &device->pinctrl;
+ bool mode2;
+
+ /*
+ * TSIF devices are handled separately, however changing of the pinctrl
+ * state must be protected from race condition.
+ */
+ if (mutex_lock_interruptible(&device->mutex))
+ return -ERESTARTSYS;
+
+ switch (source) {
+ case TSPP_SOURCE_TSIF0:
+ mode2 = device->tsif[0].mode == TSPP_TSIF_MODE_2;
+ if (enable == p->tsif1_active) {
+ if (enable)
+ /* Both tsif enabled */
+ s = mode2 ? p->dual_mode2 : p->dual_mode1;
+ else
+ /* Both tsif disabled */
+ s = p->disabled;
+ } else if (enable) {
+ /* Only tsif0 is enabled */
+ s = mode2 ? p->tsif0_mode2 : p->tsif0_mode1;
+ } else {
+ /* Only tsif1 is enabled */
+ s = mode2 ? p->tsif1_mode2 : p->tsif1_mode1;
+ }
+
+ ret = pinctrl_select_state(p->pinctrl, s);
+ if (!ret)
+ p->tsif0_active = enable;
+ break;
+ case TSPP_SOURCE_TSIF1:
+ mode2 = device->tsif[1].mode == TSPP_TSIF_MODE_2;
+ if (enable == p->tsif0_active) {
+ if (enable)
+ /* Both tsif enabled */
+ s = mode2 ? p->dual_mode2 : p->dual_mode1;
+ else
+ /* Both tsif disabled */
+ s = p->disabled;
+ } else if (enable) {
+ /* Only tsif1 is enabled */
+ s = mode2 ? p->tsif1_mode2 : p->tsif1_mode1;
+ } else {
+ /* Only tsif0 is enabled */
+ s = mode2 ? p->tsif0_mode2 : p->tsif0_mode1;
+ }
+
+ ret = pinctrl_select_state(p->pinctrl, s);
+ if (!ret)
+ p->tsif1_active = enable;
+ break;
+ default:
+ pr_err("%s: invalid source %d\n", __func__, source);
+ mutex_unlock(&device->mutex);
+ return -EINVAL;
+ }
+
+ if (ret)
+ pr_err("%s: failed to change pinctrl state, ret=%d\n",
+ __func__, ret);
+
+ mutex_unlock(&device->mutex);
+ return ret;
+}
+
+static int tspp_get_pinctrl(struct tspp_device *device)
+{
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *state;
+
+ pinctrl = devm_pinctrl_get(&device->pdev->dev);
+ if (IS_ERR(pinctrl)) {
+ pr_err("%s: Unable to get pinctrl handle\n", __func__);
+ return -EINVAL;
+ }
+ device->pinctrl.pinctrl = pinctrl;
+
+ state = pinctrl_lookup_state(pinctrl, "disabled");
+ if (IS_ERR(state)) {
+ pr_err("%s: Unable to find state %s\n",
+ __func__, "disabled");
+ return -EINVAL;
+ }
+ device->pinctrl.disabled = state;
+
+ state = pinctrl_lookup_state(pinctrl, "tsif0-mode1");
+ if (IS_ERR(state)) {
+ pr_err("%s: Unable to find state %s\n",
+ __func__, "tsif0-mode1");
+ return -EINVAL;
+ }
+ device->pinctrl.tsif0_mode1 = state;
+
+ state = pinctrl_lookup_state(pinctrl, "tsif0-mode2");
+ if (IS_ERR(state)) {
+ pr_err("%s: Unable to find state %s\n",
+ __func__, "tsif0-mode2");
+ return -EINVAL;
+ }
+ device->pinctrl.tsif0_mode2 = state;
+
+ state = pinctrl_lookup_state(pinctrl, "tsif1-mode1");
+ if (IS_ERR(state)) {
+ pr_err("%s: Unable to find state %s\n",
+ __func__, "tsif1-mode1");
+ return -EINVAL;
+ }
+ device->pinctrl.tsif1_mode1 = state;
+
+ state = pinctrl_lookup_state(pinctrl, "tsif1-mode2");
+ if (IS_ERR(state)) {
+ pr_err("%s: Unable to find state %s\n",
+ __func__, "tsif1-mode2");
+ return -EINVAL;
+ }
+ device->pinctrl.tsif1_mode2 = state;
+
+ state = pinctrl_lookup_state(pinctrl, "dual-tsif-mode1");
+ if (IS_ERR(state)) {
+ pr_err("%s: Unable to find state %s\n",
+ __func__, "dual-tsif-mode1");
+ return -EINVAL;
+ }
+ device->pinctrl.dual_mode1 = state;
+
+ state = pinctrl_lookup_state(pinctrl, "dual-tsif-mode2");
+ if (IS_ERR(state)) {
+ pr_err("%s: Unable to find state %s\n",
+ __func__, "dual-tsif-mode2");
+ return -EINVAL;
+ }
+ device->pinctrl.dual_mode2 = state;
+
+ device->pinctrl.tsif0_active = false;
+ device->pinctrl.tsif1_active = false;
+
+ return 0;
+}
+
+
+/*** Clock functions ***/
+static int tspp_clock_start(struct tspp_device *device)
+{
+ int rc;
+
+ if (device == NULL) {
+ pr_err("tspp: Can't start clocks, invalid device\n");
+ return -EINVAL;
+ }
+
+ if (device->tsif_bus_client) {
+ rc = msm_bus_scale_client_update_request(
+ device->tsif_bus_client, 1);
+ if (rc) {
+ pr_err("tspp: Can't enable bus\n");
+ return -EBUSY;
+ }
+ }
+
+ if (device->tsif_vreg) {
+ rc = regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SUPER_TURBO,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc) {
+ pr_err("Unable to set CX voltage.\n");
+ if (device->tsif_bus_client)
+ msm_bus_scale_client_update_request(
+ device->tsif_bus_client, 0);
+ return rc;
+ }
+ }
+
+ if (device->tsif_pclk && clk_prepare_enable(device->tsif_pclk) != 0) {
+ pr_err("tspp: Can't start pclk");
+
+ if (device->tsif_vreg) {
+ regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_NONE,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ }
+
+ if (device->tsif_bus_client)
+ msm_bus_scale_client_update_request(
+ device->tsif_bus_client, 0);
+ return -EBUSY;
+ }
+
+ if (device->tsif_ref_clk &&
+ clk_prepare_enable(device->tsif_ref_clk) != 0) {
+ pr_err("tspp: Can't start ref clk");
+ clk_disable_unprepare(device->tsif_pclk);
+ if (device->tsif_vreg) {
+ regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_NONE,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ }
+
+ if (device->tsif_bus_client)
+ msm_bus_scale_client_update_request(
+ device->tsif_bus_client, 0);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void tspp_clock_stop(struct tspp_device *device)
+{
+ int rc;
+
+ if (device == NULL) {
+ pr_err("tspp: Can't stop clocks, invalid device\n");
+ return;
+ }
+
+ if (device->tsif_pclk)
+ clk_disable_unprepare(device->tsif_pclk);
+
+ if (device->tsif_ref_clk)
+ clk_disable_unprepare(device->tsif_ref_clk);
+
+ if (device->tsif_vreg) {
+ rc = regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_NONE,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc)
+ pr_err("Unable to set CX voltage.\n");
+ }
+
+ if (device->tsif_bus_client) {
+ rc = msm_bus_scale_client_update_request(
+ device->tsif_bus_client, 0);
+ if (rc)
+ pr_err("tspp: Can't disable bus\n");
+ }
+}
+
+/*** TSIF functions ***/
+static int tspp_start_tsif(struct tspp_tsif_device *tsif_device)
+{
+ int start_hardware = 0;
+ u32 ctl;
+
+ if (tsif_device->ref_count == 0) {
+ start_hardware = 1;
+ } else if (tsif_device->ref_count > 0) {
+ ctl = readl_relaxed(tsif_device->base + TSIF_STS_CTL_OFF);
+ if ((ctl & TSIF_STS_CTL_START) != 1) {
+ /* this hardware should already be running */
+ pr_warn("tspp: tsif hw not started but ref count > 0");
+ start_hardware = 1;
+ }
+ }
+
+ if (start_hardware) {
+ ctl = TSIF_STS_CTL_EN_IRQ |
+ TSIF_STS_CTL_EN_DM |
+ TSIF_STS_CTL_PACK_AVAIL |
+ TSIF_STS_CTL_OVERFLOW |
+ TSIF_STS_CTL_LOST_SYNC;
+
+ if (tsif_device->clock_inverse)
+ ctl |= TSIF_STS_CTL_INV_CLOCK;
+
+ if (tsif_device->data_inverse)
+ ctl |= TSIF_STS_CTL_INV_DATA;
+
+ if (tsif_device->sync_inverse)
+ ctl |= TSIF_STS_CTL_INV_SYNC;
+
+ if (tsif_device->enable_inverse)
+ ctl |= TSIF_STS_CTL_INV_ENABLE;
+
+ switch (tsif_device->mode) {
+ case TSPP_TSIF_MODE_LOOPBACK:
+ ctl |= TSIF_STS_CTL_EN_NULL |
+ TSIF_STS_CTL_EN_ERROR |
+ TSIF_STS_CTL_TEST_MODE;
+ break;
+ case TSPP_TSIF_MODE_1:
+ ctl |= TSIF_STS_CTL_EN_TIME_LIM |
+ TSIF_STS_CTL_EN_TCR;
+ break;
+ case TSPP_TSIF_MODE_2:
+ ctl |= TSIF_STS_CTL_EN_TIME_LIM |
+ TSIF_STS_CTL_EN_TCR |
+ TSIF_STS_CTL_MODE_2;
+ break;
+ default:
+ pr_warn("tspp: unknown tsif mode 0x%x",
+ tsif_device->mode);
+ }
+ writel_relaxed(ctl, tsif_device->base + TSIF_STS_CTL_OFF);
+ writel_relaxed(tsif_device->time_limit,
+ tsif_device->base + TSIF_TIME_LIMIT_OFF);
+ /* assure register configuration is done before starting TSIF */
+ wmb();
+ writel_relaxed(ctl | TSIF_STS_CTL_START,
+ tsif_device->base + TSIF_STS_CTL_OFF);
+ /* assure TSIF start configuration */
+ wmb();
+ }
+
+ ctl = readl_relaxed(tsif_device->base + TSIF_STS_CTL_OFF);
+ if (!(ctl & TSIF_STS_CTL_START))
+ return -EBUSY;
+
+ tsif_device->ref_count++;
+ return 0;
+}
+
+static void tspp_stop_tsif(struct tspp_tsif_device *tsif_device)
+{
+ if (tsif_device->ref_count == 0)
+ return;
+
+ tsif_device->ref_count--;
+
+ if (tsif_device->ref_count == 0) {
+ writel_relaxed(TSIF_STS_CTL_STOP,
+ tsif_device->base + TSIF_STS_CTL_OFF);
+ /* assure TSIF stop configuration */
+ wmb();
+ }
+}
+
+/*** local TSPP functions ***/
+static int tspp_channels_in_use(struct tspp_device *pdev)
+{
+ int i;
+ int count = 0;
+
+ for (i = 0; i < TSPP_NUM_CHANNELS; i++)
+ count += (pdev->channels[i].used ? 1 : 0);
+
+ return count;
+}
+
+static struct tspp_device *tspp_find_by_id(int id)
+{
+ struct tspp_device *dev;
+
+ list_for_each_entry(dev, &tspp_devices, devlist) {
+ if (dev->pdev->id == id)
+ return dev;
+ }
+ return NULL;
+}
+
+static int tspp_get_key_entry(void)
+{
+ int i;
+
+ for (i = 0; i < TSPP_NUM_KEYS; i++) {
+ if (!(tspp_key_entry & (1 << i))) {
+ tspp_key_entry |= (1 << i);
+ return i;
+ }
+ }
+ return 1 < TSPP_NUM_KEYS;
+}
+
+static void tspp_free_key_entry(int entry)
+{
+ if (entry > TSPP_NUM_KEYS) {
+ pr_err("tspp_free_key_entry: index out of bounds");
+ return;
+ }
+
+ tspp_key_entry &= ~(1 << entry);
+}
+
+static int tspp_alloc_buffer(u32 channel_id, struct tspp_data_descriptor *desc,
+ u32 size, struct dma_pool *dma_pool, tspp_allocator *alloc, void *user)
+{
+ if (size < TSPP_MIN_BUFFER_SIZE ||
+ size > TSPP_MAX_BUFFER_SIZE) {
+ pr_err("tspp: bad buffer size %i", size);
+ return -ENOMEM;
+ }
+
+ if (alloc) {
+ TSPP_DEBUG("tspp using alloc function");
+ desc->virt_base = alloc(channel_id, size,
+ &desc->phys_base, user);
+ } else {
+ if (!dma_pool)
+ desc->virt_base = dma_alloc_coherent(NULL, size,
+ &desc->phys_base, GFP_KERNEL);
+ else
+ desc->virt_base = dma_pool_alloc(dma_pool, GFP_KERNEL,
+ &desc->phys_base);
+
+ if (desc->virt_base == 0) {
+ pr_err("tspp: dma buffer allocation failed %i\n", size);
+ return -ENOMEM;
+ }
+ }
+
+ desc->size = size;
+ return 0;
+}
+
+static int tspp_queue_buffer(struct tspp_channel *channel,
+ struct tspp_mem_buffer *buffer)
+{
+ int rc;
+ u32 flags = 0;
+
+ /* make sure the interrupt frequency is valid */
+ if (channel->int_freq < 1)
+ channel->int_freq = 1;
+
+ /* generate interrupt according to requested frequency */
+ if (buffer->desc.id % channel->int_freq == channel->int_freq-1)
+ flags = SPS_IOVEC_FLAG_INT;
+
+ /* start the transfer */
+ rc = sps_transfer_one(channel->pipe,
+ buffer->sps.phys_base,
+ buffer->sps.size,
+ flags ? channel->pdev : NULL,
+ flags);
+ if (rc < 0)
+ return rc;
+
+ buffer->state = TSPP_BUF_STATE_WAITING;
+
+ return 0;
+}
+
+static int tspp_global_reset(struct tspp_device *pdev)
+{
+ u32 i, val;
+
+ /* stop all TSIFs */
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+ pdev->tsif[i].ref_count = 1; /* allows stopping hw */
+ tspp_stop_tsif(&pdev->tsif[i]); /* will reset ref_count to 0 */
+ pdev->tsif[i].time_limit = TSPP_TSIF_DEFAULT_TIME_LIMIT;
+ pdev->tsif[i].clock_inverse = 0;
+ pdev->tsif[i].data_inverse = 0;
+ pdev->tsif[i].sync_inverse = 0;
+ pdev->tsif[i].enable_inverse = 0;
+ }
+ writel_relaxed(TSPP_RST_RESET, pdev->base + TSPP_RST);
+ /* assure state is reset before continuing with configuration */
+ wmb();
+
+ /* TSPP tables */
+ for (i = 0; i < TSPP_FILTER_TABLES; i++)
+ memset_io(pdev->filters[i],
+ 0, sizeof(struct tspp_pid_filter_table));
+
+ /* disable all filters */
+ val = (2 << TSPP_NUM_CHANNELS) - 1;
+ writel_relaxed(val, pdev->base + TSPP_PS_DISABLE);
+
+ /* TSPP registers */
+ val = readl_relaxed(pdev->base + TSPP_CONTROL);
+ writel_relaxed(val | TSPP_CLK_CONTROL_FORCE_PERF_CNT,
+ pdev->base + TSPP_CONTROL);
+ /* assure tspp performance count clock is set to 0 */
+ wmb();
+ memset_io(pdev->tspp_global_performance, 0,
+ sizeof(struct tspp_global_performance_regs));
+ memset_io(pdev->tspp_pipe_context, 0,
+ sizeof(struct tspp_pipe_context_regs));
+ memset_io(pdev->tspp_pipe_performance, 0,
+ sizeof(struct tspp_pipe_performance_regs));
+ /* assure tspp pipe context registers are set to 0 */
+ wmb();
+ writel_relaxed(val & ~TSPP_CLK_CONTROL_FORCE_PERF_CNT,
+ pdev->base + TSPP_CONTROL);
+ /* assure tspp performance count clock is reset */
+ wmb();
+
+ val = readl_relaxed(pdev->base + TSPP_CONFIG);
+ val &= ~(TSPP_CONFIG_PS_LEN_ERR_MASK |
+ TSPP_CONFIG_PS_CONT_ERR_UNSP_MASK |
+ TSPP_CONFIG_PS_CONT_ERR_MASK);
+ TSPP_CONFIG_SET_PACKET_LENGTH(val, TSPP_PACKET_LENGTH);
+ writel_relaxed(val, pdev->base + TSPP_CONFIG);
+ writel_relaxed(0x0007ffff, pdev->base + TSPP_IRQ_MASK);
+ writel_relaxed(0x000fffff, pdev->base + TSPP_IRQ_CLEAR);
+ writel_relaxed(0, pdev->base + TSPP_RST);
+ /* assure tspp reset clear */
+ wmb();
+
+ tspp_key_entry = 0;
+
+ return 0;
+}
+
+static void tspp_channel_init(struct tspp_channel *channel,
+ struct tspp_device *pdev)
+{
+ channel->pdev = pdev;
+ channel->data = NULL;
+ channel->read = NULL;
+ channel->waiting = NULL;
+ channel->locked = NULL;
+ channel->id = channel_id++;
+ channel->used = 0;
+ channel->buffer_size = TSPP_MIN_BUFFER_SIZE;
+ channel->max_buffers = TSPP_NUM_BUFFERS;
+ channel->buffer_count = 0;
+ channel->filter_count = 0;
+ channel->int_freq = 1;
+ channel->src = TSPP_SOURCE_NONE;
+ channel->mode = TSPP_MODE_DISABLED;
+ channel->notifier = NULL;
+ channel->notify_data = NULL;
+ channel->expiration_period_ms = 0;
+ channel->memfree = NULL;
+ channel->user_info = NULL;
+ init_waitqueue_head(&channel->in_queue);
+}
+
+static void tspp_set_tsif_mode(struct tspp_channel *channel,
+ enum tspp_tsif_mode mode)
+{
+ int index;
+
+ switch (channel->src) {
+ case TSPP_SOURCE_TSIF0:
+ index = 0;
+ break;
+ case TSPP_SOURCE_TSIF1:
+ index = 1;
+ break;
+ default:
+ pr_warn("tspp: can't set mode for non-tsif source %d",
+ channel->src);
+ return;
+ }
+ channel->pdev->tsif[index].mode = mode;
+}
+
+static void tspp_set_signal_inversion(struct tspp_channel *channel,
+ int clock_inverse, int data_inverse,
+ int sync_inverse, int enable_inverse)
+{
+ int index;
+
+ switch (channel->src) {
+ case TSPP_SOURCE_TSIF0:
+ index = 0;
+ break;
+ case TSPP_SOURCE_TSIF1:
+ index = 1;
+ break;
+ default:
+ return;
+ }
+ channel->pdev->tsif[index].clock_inverse = clock_inverse;
+ channel->pdev->tsif[index].data_inverse = data_inverse;
+ channel->pdev->tsif[index].sync_inverse = sync_inverse;
+ channel->pdev->tsif[index].enable_inverse = enable_inverse;
+}
+
+static int tspp_is_buffer_size_aligned(u32 size, enum tspp_mode mode)
+{
+ u32 alignment;
+
+ switch (mode) {
+ case TSPP_MODE_RAW:
+ /* must be a multiple of 192 */
+ alignment = (TSPP_PACKET_LENGTH + 4);
+ if (size % alignment)
+ return 0;
+ return 1;
+
+ case TSPP_MODE_RAW_NO_SUFFIX:
+ /* must be a multiple of 188 */
+ alignment = TSPP_PACKET_LENGTH;
+ if (size % alignment)
+ return 0;
+ return 1;
+
+ case TSPP_MODE_DISABLED:
+ case TSPP_MODE_PES:
+ default:
+ /* no alignment requirement */
+ return 1;
+ }
+
+}
+
+static u32 tspp_align_buffer_size_by_mode(u32 size, enum tspp_mode mode)
+{
+ u32 new_size;
+ u32 alignment;
+
+ switch (mode) {
+ case TSPP_MODE_RAW:
+ /* must be a multiple of 192 */
+ alignment = (TSPP_PACKET_LENGTH + 4);
+ break;
+
+ case TSPP_MODE_RAW_NO_SUFFIX:
+ /* must be a multiple of 188 */
+ alignment = TSPP_PACKET_LENGTH;
+ break;
+
+ case TSPP_MODE_DISABLED:
+ case TSPP_MODE_PES:
+ default:
+ /* no alignment requirement - give the user what he asks for */
+ alignment = 1;
+ break;
+ }
+ /* align up */
+ new_size = (((size + alignment - 1) / alignment) * alignment);
+ return new_size;
+}
+
+static void tspp_destroy_buffers(u32 channel_id, struct tspp_channel *channel)
+{
+ int i;
+ struct tspp_mem_buffer *pbuf, *temp;
+
+ pbuf = channel->data;
+ for (i = 0; i < channel->buffer_count; i++) {
+ if (pbuf->desc.phys_base) {
+ if (channel->memfree) {
+ channel->memfree(channel_id,
+ pbuf->desc.size,
+ pbuf->desc.virt_base,
+ pbuf->desc.phys_base,
+ channel->user_info);
+ } else {
+ if (!channel->dma_pool)
+ dma_free_coherent(
+ &channel->pdev->pdev->dev,
+ pbuf->desc.size,
+ pbuf->desc.virt_base,
+ pbuf->desc.phys_base);
+ else
+ dma_pool_free(channel->dma_pool,
+ pbuf->desc.virt_base,
+ pbuf->desc.phys_base);
+ }
+ pbuf->desc.phys_base = 0;
+ }
+ pbuf->desc.virt_base = 0;
+ pbuf->state = TSPP_BUF_STATE_EMPTY;
+ temp = pbuf;
+ pbuf = pbuf->next;
+ kfree(temp);
+ }
+}
+
+static int msm_tspp_req_irqs(struct tspp_device *device)
+{
+ int rc;
+ int i;
+ int j;
+
+ rc = request_irq(device->tspp_irq, tspp_isr, IRQF_SHARED,
+ dev_name(&device->pdev->dev), device);
+ if (rc) {
+ dev_err(&device->pdev->dev,
+ "failed to request TSPP IRQ %d : %d",
+ device->tspp_irq, rc);
+ return rc;
+ }
+
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+ rc = request_irq(device->tsif[i].tsif_irq,
+ tsif_isr, IRQF_SHARED, dev_name(&device->pdev->dev),
+ &device->tsif[i]);
+ if (rc) {
+ dev_err(&device->pdev->dev,
+ "failed to request TSIF%d IRQ: %d",
+ i, rc);
+ goto failed;
+ }
+ }
+ device->req_irqs = true;
+ return 0;
+
+failed:
+ free_irq(device->tspp_irq, device);
+ for (j = 0; j < i; j++)
+ free_irq(device->tsif[j].tsif_irq, device);
+
+ return rc;
+}
+
+static inline void msm_tspp_free_irqs(struct tspp_device *device)
+{
+ int i;
+
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+ if (device->tsif[i].tsif_irq)
+ free_irq(device->tsif[i].tsif_irq, &device->tsif[i]);
+ }
+
+ if (device->tspp_irq)
+ free_irq(device->tspp_irq, device);
+ device->req_irqs = false;
+}
+
+/*** TSPP API functions ***/
+
+/**
+ * tspp_open_stream - open a TSPP stream for use.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @source: stream source parameters.
+ *
+ * Return error status
+ *
+ */
+int tspp_open_stream(u32 dev, u32 channel_id,
+ struct tspp_select_source *source)
+{
+ u32 val;
+ int rc;
+ struct tspp_device *pdev;
+ struct tspp_channel *channel;
+ bool req_irqs = false;
+
+ TSPP_DEBUG("tspp_open_stream %i %i %i %i",
+ dev, channel_id, source->source, source->mode);
+
+ if (dev >= TSPP_MAX_DEVICES) {
+ pr_err("tspp: device id out of range");
+ return -ENODEV;
+ }
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return -ECHRNG;
+ }
+
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_str: can't find device %i", dev);
+ return -ENODEV;
+ }
+ channel = &pdev->channels[channel_id];
+ channel->src = source->source;
+ tspp_set_tsif_mode(channel, source->mode);
+ tspp_set_signal_inversion(channel, source->clk_inverse,
+ source->data_inverse, source->sync_inverse,
+ source->enable_inverse);
+
+ /* Request IRQ resources on first open */
+ if (!pdev->req_irqs && (source->source == TSPP_SOURCE_TSIF0 ||
+ source->source == TSPP_SOURCE_TSIF1)) {
+ rc = msm_tspp_req_irqs(pdev);
+ if (rc) {
+ pr_err("tspp: error requesting irqs\n");
+ return rc;
+ }
+ req_irqs = true;
+ }
+
+ switch (source->source) {
+ case TSPP_SOURCE_TSIF0:
+ if (tspp_config_gpios(pdev, channel->src, 1) != 0) {
+ rc = -EBUSY;
+ pr_err("tspp: error enabling tsif0 GPIOs\n");
+ goto free_irq;
+ }
+ /* make sure TSIF0 is running & enabled */
+ if (tspp_start_tsif(&pdev->tsif[0]) != 0) {
+ rc = -EBUSY;
+ pr_err("tspp: error starting tsif0");
+ goto free_irq;
+ }
+ if (pdev->tsif[0].ref_count == 1) {
+ val = readl_relaxed(pdev->base + TSPP_CONTROL);
+ writel_relaxed(val & ~TSPP_CONTROL_TSP_TSIF0_SRC_DIS,
+ pdev->base + TSPP_CONTROL);
+ /* Assure BAM TS PKT packet processing is enabled */
+ wmb();
+ }
+ break;
+ case TSPP_SOURCE_TSIF1:
+ if (tspp_config_gpios(pdev, channel->src, 1) != 0) {
+ rc = -EBUSY;
+ pr_err("tspp: error enabling tsif1 GPIOs\n");
+ goto free_irq;
+ }
+ /* make sure TSIF1 is running & enabled */
+ if (tspp_start_tsif(&pdev->tsif[1]) != 0) {
+ rc = -EBUSY;
+ pr_err("tspp: error starting tsif1");
+ goto free_irq;
+ }
+ if (pdev->tsif[1].ref_count == 1) {
+ val = readl_relaxed(pdev->base + TSPP_CONTROL);
+ writel_relaxed(val & ~TSPP_CONTROL_TSP_TSIF1_SRC_DIS,
+ pdev->base + TSPP_CONTROL);
+ /* Assure BAM TS PKT packet processing is enabled */
+ wmb();
+ }
+ break;
+ case TSPP_SOURCE_MEM:
+ break;
+ default:
+ pr_err("tspp: channel %i invalid source %i",
+ channel->id, source->source);
+ return -EBUSY;
+ }
+
+ return 0;
+
+free_irq:
+ /* Free irqs only if were requested during opening of this stream */
+ if (req_irqs)
+ msm_tspp_free_irqs(pdev);
+ return rc;
+}
+EXPORT_SYMBOL(tspp_open_stream);
+
+/**
+ * tspp_close_stream - close a TSPP stream.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ *
+ * Return error status
+ *
+ */
+int tspp_close_stream(u32 dev, u32 channel_id)
+{
+ u32 val;
+ u32 prev_ref_count = 0;
+ struct tspp_device *pdev;
+ struct tspp_channel *channel;
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return -ECHRNG;
+ }
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_cs: can't find device %i", dev);
+ return -EBUSY;
+ }
+ channel = &pdev->channels[channel_id];
+
+ switch (channel->src) {
+ case TSPP_SOURCE_TSIF0:
+ prev_ref_count = pdev->tsif[0].ref_count;
+ tspp_stop_tsif(&pdev->tsif[0]);
+ if (tspp_config_gpios(pdev, channel->src, 0) != 0)
+ pr_err("tspp: error disabling tsif0 GPIOs\n");
+
+ if (prev_ref_count == 1) {
+ val = readl_relaxed(pdev->base + TSPP_CONTROL);
+ writel_relaxed(val | TSPP_CONTROL_TSP_TSIF0_SRC_DIS,
+ pdev->base + TSPP_CONTROL);
+ /* Assure BAM TS PKT packet processing is disabled */
+ wmb();
+ }
+ break;
+ case TSPP_SOURCE_TSIF1:
+ prev_ref_count = pdev->tsif[1].ref_count;
+ tspp_stop_tsif(&pdev->tsif[1]);
+ if (tspp_config_gpios(pdev, channel->src, 0) != 0)
+ pr_err("tspp: error disabling tsif0 GPIOs\n");
+
+ if (prev_ref_count == 1) {
+ val = readl_relaxed(pdev->base + TSPP_CONTROL);
+ writel_relaxed(val | TSPP_CONTROL_TSP_TSIF1_SRC_DIS,
+ pdev->base + TSPP_CONTROL);
+ /* Assure BAM TS PKT packet processing is disabled */
+ wmb();
+ }
+ break;
+ case TSPP_SOURCE_MEM:
+ break;
+ case TSPP_SOURCE_NONE:
+ break;
+ }
+
+ channel->src = TSPP_SOURCE_NONE;
+
+ /* Free requested interrupts to save power */
+ if ((pdev->tsif[0].ref_count + pdev->tsif[1].ref_count) == 0 &&
+ prev_ref_count)
+ msm_tspp_free_irqs(pdev);
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_close_stream);
+
+static int tspp_init_sps_device(struct tspp_device *dev)
+{
+ int ret;
+
+ ret = sps_register_bam_device(&dev->bam_props, &dev->bam_handle);
+ if (ret) {
+ pr_err("tspp: failed to register bam device, err-%d\n", ret);
+ return ret;
+ }
+
+ ret = sps_device_reset(dev->bam_handle);
+ if (ret) {
+ sps_deregister_bam_device(dev->bam_handle);
+ pr_err("tspp: error resetting bam device, err=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * tspp_open_channel - open a TSPP channel.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ *
+ * Return error status
+ *
+ */
+int tspp_open_channel(u32 dev, u32 channel_id)
+{
+ int rc = 0;
+ struct sps_connect *config;
+ struct sps_register_event *event;
+ struct tspp_channel *channel;
+ struct tspp_device *pdev;
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return -ECHRNG;
+ }
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_oc: can't find device %i", dev);
+ return -ENODEV;
+ }
+ channel = &pdev->channels[channel_id];
+
+ if (channel->used) {
+ pr_err("tspp channel already in use");
+ return -EBUSY;
+ }
+
+ config = &channel->config;
+ event = &channel->event;
+
+ /* start the clocks if needed */
+ if (tspp_channels_in_use(pdev) == 0) {
+ rc = tspp_clock_start(pdev);
+ if (rc)
+ return rc;
+
+ if (pdev->bam_handle == SPS_DEV_HANDLE_INVALID) {
+ rc = tspp_init_sps_device(pdev);
+ if (rc) {
+ pr_err("tspp: failed to init sps device, err=%d\n",
+ rc);
+ tspp_clock_stop(pdev);
+ return rc;
+ }
+ }
+
+ __pm_stay_awake(&pdev->ws);
+ }
+
+ /* mark it as used */
+ channel->used = 1;
+
+ /* start the bam */
+ channel->pipe = sps_alloc_endpoint();
+ if (channel->pipe == 0) {
+ pr_err("tspp: error allocating endpoint");
+ rc = -ENOMEM;
+ goto err_sps_alloc;
+ }
+
+ /* get default configuration */
+ sps_get_config(channel->pipe, config);
+
+ config->source = pdev->bam_handle;
+ config->destination = SPS_DEV_HANDLE_MEM;
+ config->mode = SPS_MODE_SRC;
+ config->options =
+ SPS_O_AUTO_ENABLE | /* connection is auto-enabled */
+ SPS_O_STREAMING | /* streaming mode */
+ SPS_O_DESC_DONE | /* interrupt on end of descriptor */
+ SPS_O_ACK_TRANSFERS | /* must use sps_get_iovec() */
+ SPS_O_HYBRID; /* Read actual descriptors in sps_get_iovec() */
+ config->src_pipe_index = channel->id;
+ config->desc.size =
+ TSPP_SPS_DESCRIPTOR_COUNT * SPS_DESCRIPTOR_SIZE;
+ config->desc.base = dma_alloc_coherent(&pdev->pdev->dev,
+ config->desc.size,
+ &config->desc.phys_base,
+ GFP_KERNEL);
+ if (config->desc.base == 0) {
+ pr_err("tspp: error allocating sps descriptors");
+ rc = -ENOMEM;
+ goto err_desc_alloc;
+ }
+
+ memset(config->desc.base, 0, config->desc.size);
+
+ rc = sps_connect(channel->pipe, config);
+ if (rc) {
+ pr_err("tspp: error connecting bam");
+ goto err_connect;
+ }
+
+ event->mode = SPS_TRIGGER_CALLBACK;
+ event->options = SPS_O_DESC_DONE;
+ event->callback = tspp_sps_complete_cb;
+ event->xfer_done = NULL;
+ event->user = pdev;
+
+ rc = sps_register_event(channel->pipe, event);
+ if (rc) {
+ pr_err("tspp: error registering event");
+ goto err_event;
+ }
+
+ init_timer(&channel->expiration_timer);
+ channel->expiration_timer.function = tspp_expiration_timer;
+ channel->expiration_timer.data = (unsigned long)pdev;
+ channel->expiration_timer.expires = 0xffffffffL;
+
+ rc = pm_runtime_get(&pdev->pdev->dev);
+ if (rc < 0) {
+ dev_err(&pdev->pdev->dev,
+ "Runtime PM: Unable to wake up tspp device, rc = %d",
+ rc);
+ }
+ return 0;
+
+err_event:
+ sps_disconnect(channel->pipe);
+err_connect:
+ dma_free_coherent(&pdev->pdev->dev, config->desc.size,
+ config->desc.base, config->desc.phys_base);
+err_desc_alloc:
+ sps_free_endpoint(channel->pipe);
+err_sps_alloc:
+ channel->used = 0;
+ return rc;
+}
+EXPORT_SYMBOL(tspp_open_channel);
+
+/**
+ * tspp_close_channel - close a TSPP channel.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ *
+ * Return error status
+ *
+ */
+int tspp_close_channel(u32 dev, u32 channel_id)
+{
+ int i;
+ int id;
+ int table_idx;
+ u32 val;
+ unsigned long flags;
+
+ struct sps_connect *config;
+ struct tspp_device *pdev;
+ struct tspp_channel *channel;
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return -ECHRNG;
+ }
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_close: can't find device %i", dev);
+ return -ENODEV;
+ }
+ channel = &pdev->channels[channel_id];
+
+ /* if the channel is not used, we are done */
+ if (!channel->used)
+ return 0;
+
+ /*
+ * Need to protect access to used and waiting fields, as they are
+ * used by the tasklet which is invoked from interrupt context
+ */
+ spin_lock_irqsave(&pdev->spinlock, flags);
+ channel->used = 0;
+ channel->waiting = NULL;
+ spin_unlock_irqrestore(&pdev->spinlock, flags);
+
+ if (channel->expiration_period_ms)
+ del_timer(&channel->expiration_timer);
+
+ channel->notifier = NULL;
+ channel->notify_data = NULL;
+ channel->expiration_period_ms = 0;
+
+ config = &channel->config;
+ pdev = channel->pdev;
+
+ /* disable pipe (channel) */
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+ writel_relaxed(val | channel->id, pdev->base + TSPP_PS_DISABLE);
+ /* Assure PS_DISABLE register is set */
+ wmb();
+
+ /* unregister all filters for this channel */
+ for (table_idx = 0; table_idx < TSPP_FILTER_TABLES; table_idx++) {
+ for (i = 0; i < TSPP_NUM_PRIORITIES; i++) {
+ struct tspp_pid_filter *filter =
+ &pdev->filters[table_idx]->filter[i];
+ id = FILTER_GET_PIPE_NUMBER0(filter);
+ if (id == channel->id) {
+ if (FILTER_HAS_ENCRYPTION(filter))
+ tspp_free_key_entry(
+ FILTER_GET_KEY_NUMBER(filter));
+ filter->config = 0;
+ filter->filter = 0;
+ }
+ }
+ }
+ channel->filter_count = 0;
+
+ /* disconnect the bam */
+ if (sps_disconnect(channel->pipe) != 0)
+ pr_warn("tspp: Error freeing sps endpoint (%i)", channel->id);
+
+ /* destroy the buffers */
+ dma_free_coherent(&pdev->pdev->dev, config->desc.size,
+ config->desc.base, config->desc.phys_base);
+
+ sps_free_endpoint(channel->pipe);
+
+ tspp_destroy_buffers(channel_id, channel);
+
+ dma_pool_destroy(channel->dma_pool);
+ channel->dma_pool = NULL;
+
+ channel->src = TSPP_SOURCE_NONE;
+ channel->mode = TSPP_MODE_DISABLED;
+ channel->memfree = NULL;
+ channel->user_info = NULL;
+ channel->buffer_count = 0;
+ channel->data = NULL;
+ channel->read = NULL;
+ channel->locked = NULL;
+
+ if (tspp_channels_in_use(pdev) == 0) {
+ sps_deregister_bam_device(pdev->bam_handle);
+ pdev->bam_handle = SPS_DEV_HANDLE_INVALID;
+
+ __pm_relax(&pdev->ws);
+ tspp_clock_stop(pdev);
+ }
+
+ pm_runtime_put(&pdev->pdev->dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_close_channel);
+
+/**
+ * tspp_get_ref_clk_counter - return the TSIF clock reference (TCR) counter.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @source: The TSIF source from which the counter should be read
+ * @tcr_counter: the value of TCR counter
+ *
+ * Return error status
+ *
+ * TCR increments at a rate equal to 27 MHz/256 = 105.47 kHz.
+ * If source is neither TSIF 0 or TSIF1 0 is returned.
+ */
+int tspp_get_ref_clk_counter(u32 dev, enum tspp_source source, u32 *tcr_counter)
+{
+ struct tspp_device *pdev;
+ struct tspp_tsif_device *tsif_device;
+
+ if (!tcr_counter)
+ return -EINVAL;
+
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_get_ref_clk_counter: can't find device %i\n", dev);
+ return -ENODEV;
+ }
+
+ switch (source) {
+ case TSPP_SOURCE_TSIF0:
+ tsif_device = &pdev->tsif[0];
+ break;
+
+ case TSPP_SOURCE_TSIF1:
+ tsif_device = &pdev->tsif[1];
+ break;
+
+ default:
+ tsif_device = NULL;
+ break;
+ }
+
+ if (tsif_device && tsif_device->ref_count)
+ *tcr_counter = ioread32(tsif_device->base + TSIF_CLK_REF_OFF);
+ else
+ *tcr_counter = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_get_ref_clk_counter);
+
+/**
+ * tspp_add_filter - add a TSPP filter to a channel.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @filter: TSPP filter parameters
+ *
+ * Return error status
+ *
+ */
+int tspp_add_filter(u32 dev, u32 channel_id,
+ struct tspp_filter *filter)
+{
+ int i, rc;
+ int other_channel;
+ int entry;
+ u32 val, pid, enabled;
+ struct tspp_device *pdev;
+ struct tspp_pid_filter p;
+ struct tspp_channel *channel;
+
+ TSPP_DEBUG("tspp: add filter");
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return -ECHRNG;
+ }
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_add: can't find device %i", dev);
+ return -ENODEV;
+ }
+
+ channel = &pdev->channels[channel_id];
+
+ if (filter->source > TSPP_SOURCE_MEM) {
+ pr_err("tspp invalid source");
+ return -ENOSR;
+ }
+
+ if (filter->priority >= TSPP_NUM_PRIORITIES) {
+ pr_err("tspp invalid filter priority");
+ return -ENOSR;
+ }
+
+ channel->mode = filter->mode;
+ /*
+ * if buffers are already allocated, verify they fulfil
+ * the alignment requirements.
+ */
+ if ((channel->buffer_count > 0) &&
+ (!tspp_is_buffer_size_aligned(channel->buffer_size, channel->mode)))
+ pr_warn("tspp: buffers allocated with incorrect alignment\n");
+
+ if (filter->mode == TSPP_MODE_PES) {
+ for (i = 0; i < TSPP_NUM_PRIORITIES; i++) {
+ struct tspp_pid_filter *tspp_filter =
+ &pdev->filters[channel->src]->filter[i];
+ pid = FILTER_GET_PIPE_PID((tspp_filter));
+ enabled = FILTER_GET_PIPE_PROCESS0(tspp_filter);
+ if (enabled && (pid == filter->pid)) {
+ other_channel =
+ FILTER_GET_PIPE_NUMBER0(tspp_filter);
+ pr_err("tspp: pid 0x%x already in use by channel %i",
+ filter->pid, other_channel);
+ return -EBADSLT;
+ }
+ }
+ }
+
+ /* make sure this priority is not already in use */
+ enabled = FILTER_GET_PIPE_PROCESS0(
+ (&(pdev->filters[channel->src]->filter[filter->priority])));
+ if (enabled) {
+ pr_err("tspp: filter priority %i source %i is already enabled\n",
+ filter->priority, channel->src);
+ return -ENOSR;
+ }
+
+ if (channel->mode == TSPP_MODE_PES) {
+ /*
+ * if we are already processing in PES mode, disable pipe
+ * (channel) and filter to be updated
+ */
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+ writel_relaxed(val | (1 << channel->id),
+ pdev->base + TSPP_PS_DISABLE);
+ /* Assure PS_DISABLE register is set */
+ wmb();
+ }
+
+ /* update entry */
+ p.filter = 0;
+ p.config = FILTER_TRANS_END_DISABLE;
+ FILTER_SET_PIPE_PROCESS0((&p), filter->mode);
+ FILTER_SET_PIPE_PID((&p), filter->pid);
+ FILTER_SET_PID_MASK((&p), filter->mask);
+ FILTER_SET_PIPE_NUMBER0((&p), channel->id);
+ FILTER_SET_PIPE_PROCESS1((&p), TSPP_MODE_DISABLED);
+ if (filter->decrypt) {
+ entry = tspp_get_key_entry();
+ if (entry == -1) {
+ pr_err("tspp: no more keys available!");
+ } else {
+ p.config |= FILTER_DECRYPT;
+ FILTER_SET_KEY_NUMBER((&p), entry);
+ }
+ }
+
+ pdev->filters[channel->src]->
+ filter[filter->priority].config = p.config;
+ pdev->filters[channel->src]->
+ filter[filter->priority].filter = p.filter;
+
+ /*
+ * allocate buffers if needed (i.e. if user did has not already called
+ * tspp_allocate_buffers() explicitly).
+ */
+ if (channel->buffer_count == 0) {
+ channel->buffer_size =
+ tspp_align_buffer_size_by_mode(channel->buffer_size,
+ channel->mode);
+ rc = tspp_allocate_buffers(dev, channel->id,
+ channel->max_buffers,
+ channel->buffer_size,
+ channel->int_freq, NULL, NULL, NULL);
+ if (rc != 0) {
+ pr_err("tspp: tspp_allocate_buffers failed\n");
+ return rc;
+ }
+ }
+
+ /* reenable pipe */
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+ writel_relaxed(val & ~(1 << channel->id), pdev->base + TSPP_PS_DISABLE);
+ /* Assure PS_DISABLE register is reset */
+ wmb();
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+
+ channel->filter_count++;
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_add_filter);
+
+/**
+ * tspp_remove_filter - remove a TSPP filter from a channel.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @filter: TSPP filter parameters
+ *
+ * Return error status
+ *
+ */
+int tspp_remove_filter(u32 dev, u32 channel_id,
+ struct tspp_filter *filter)
+{
+ int entry;
+ u32 val;
+ struct tspp_device *pdev;
+ int src;
+ struct tspp_pid_filter *tspp_filter;
+ struct tspp_channel *channel;
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return -ECHRNG;
+ }
+ if (!filter) {
+ pr_err("tspp: NULL filter pointer");
+ return -EINVAL;
+ }
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_remove: can't find device %i", dev);
+ return -ENODEV;
+ }
+ if (filter->priority >= TSPP_NUM_PRIORITIES) {
+ pr_err("tspp invalid filter priority");
+ return -ENOSR;
+ }
+ channel = &pdev->channels[channel_id];
+
+ src = channel->src;
+ tspp_filter = &(pdev->filters[src]->filter[filter->priority]);
+
+ /* disable pipe (channel) */
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+ writel_relaxed(val | channel->id, pdev->base + TSPP_PS_DISABLE);
+ /* Assure PS_DISABLE register is set */
+ wmb();
+
+ /* update data keys */
+ if (tspp_filter->config & FILTER_DECRYPT) {
+ entry = FILTER_GET_KEY_NUMBER(tspp_filter);
+ tspp_free_key_entry(entry);
+ }
+
+ /* update pid table */
+ tspp_filter->config = 0;
+ tspp_filter->filter = 0;
+
+ channel->filter_count--;
+
+ /* reenable pipe */
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+ writel_relaxed(val & ~(1 << channel->id),
+ pdev->base + TSPP_PS_DISABLE);
+ /* Assure PS_DISABLE register is reset */
+ wmb();
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_remove_filter);
+
+/**
+ * tspp_set_key - set TSPP key in key table.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @key: TSPP key parameters
+ *
+ * Return error status
+ *
+ */
+int tspp_set_key(u32 dev, u32 channel_id, struct tspp_key *key)
+{
+ int i;
+ int id;
+ int key_index;
+ int data;
+ struct tspp_channel *channel;
+ struct tspp_device *pdev;
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return -ECHRNG;
+ }
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_set: can't find device %i", dev);
+ return -ENODEV;
+ }
+ channel = &pdev->channels[channel_id];
+
+ /* read the key index used by this channel */
+ for (i = 0; i < TSPP_NUM_PRIORITIES; i++) {
+ struct tspp_pid_filter *tspp_filter =
+ &(pdev->filters[channel->src]->filter[i]);
+ id = FILTER_GET_PIPE_NUMBER0(tspp_filter);
+ if (id == channel->id) {
+ if (FILTER_HAS_ENCRYPTION(tspp_filter)) {
+ key_index = FILTER_GET_KEY_NUMBER(tspp_filter);
+ break;
+ }
+ }
+ }
+ if (i == TSPP_NUM_PRIORITIES) {
+ pr_err("tspp: no encryption on this channel");
+ return -ENOKEY;
+ }
+
+ if (key->parity == TSPP_KEY_PARITY_EVEN) {
+ pdev->tspp_key_table->entry[key_index].even_lsb = key->lsb;
+ pdev->tspp_key_table->entry[key_index].even_msb = key->msb;
+ } else {
+ pdev->tspp_key_table->entry[key_index].odd_lsb = key->lsb;
+ pdev->tspp_key_table->entry[key_index].odd_msb = key->msb;
+ }
+ data = readl_relaxed(channel->pdev->base + TSPP_KEY_VALID);
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_set_key);
+
+/**
+ * tspp_register_notification - register TSPP channel notification function.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @notify: notification function
+ * @userdata: user data to pass to notification function
+ * @timer_ms: notification for partially filled buffers
+ *
+ * Return error status
+ *
+ */
+int tspp_register_notification(u32 dev, u32 channel_id,
+ tspp_notifier *notify, void *userdata, u32 timer_ms)
+{
+ struct tspp_channel *channel;
+ struct tspp_device *pdev;
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return -ECHRNG;
+ }
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_reg: can't find device %i", dev);
+ return -ENODEV;
+ }
+ channel = &pdev->channels[channel_id];
+ channel->notifier = notify;
+ channel->notify_data = userdata;
+ channel->expiration_period_ms = timer_ms;
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_register_notification);
+
+/**
+ * tspp_unregister_notification - unregister TSPP channel notification function.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ *
+ * Return error status
+ *
+ */
+int tspp_unregister_notification(u32 dev, u32 channel_id)
+{
+ struct tspp_channel *channel;
+ struct tspp_device *pdev;
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return -ECHRNG;
+ }
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_unreg: can't find device %i", dev);
+ return -ENODEV;
+ }
+ channel = &pdev->channels[channel_id];
+ channel->notifier = NULL;
+ channel->notify_data = 0;
+ return 0;
+}
+EXPORT_SYMBOL(tspp_unregister_notification);
+
+/**
+ * tspp_get_buffer - get TSPP data buffer.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ *
+ * Return error status
+ *
+ */
+const struct tspp_data_descriptor *tspp_get_buffer(u32 dev, u32 channel_id)
+{
+ struct tspp_mem_buffer *buffer;
+ struct tspp_channel *channel;
+ struct tspp_device *pdev;
+ unsigned long flags;
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return NULL;
+ }
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp_get: can't find device %i", dev);
+ return NULL;
+ }
+
+ spin_lock_irqsave(&pdev->spinlock, flags);
+
+ channel = &pdev->channels[channel_id];
+
+ if (!channel->read) {
+ spin_unlock_irqrestore(&pdev->spinlock, flags);
+ pr_warn("tspp: no buffer to get on channel %i!",
+ channel->id);
+ return NULL;
+ }
+
+ buffer = channel->read;
+ /* see if we have any buffers ready to read */
+ if (buffer->state != TSPP_BUF_STATE_DATA) {
+ spin_unlock_irqrestore(&pdev->spinlock, flags);
+ return NULL;
+ }
+
+ if (buffer->state == TSPP_BUF_STATE_DATA) {
+ /* mark the buffer as busy */
+ buffer->state = TSPP_BUF_STATE_LOCKED;
+
+ /* increment the pointer along the list */
+ channel->read = channel->read->next;
+ }
+
+ spin_unlock_irqrestore(&pdev->spinlock, flags);
+
+ return &buffer->desc;
+}
+EXPORT_SYMBOL(tspp_get_buffer);
+
+/**
+ * tspp_release_buffer - release TSPP data buffer back to TSPP.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @descriptor_id: buffer descriptor ID
+ *
+ * Return error status
+ *
+ */
+int tspp_release_buffer(u32 dev, u32 channel_id, u32 descriptor_id)
+{
+ int i, found = 0;
+ struct tspp_mem_buffer *buffer;
+ struct tspp_channel *channel;
+ struct tspp_device *pdev;
+ unsigned long flags;
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("tspp: channel id out of range");
+ return -ECHRNG;
+ }
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp: can't find device %i", dev);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&pdev->spinlock, flags);
+
+ channel = &pdev->channels[channel_id];
+
+ if (descriptor_id > channel->buffer_count)
+ pr_warn("tspp: desc id looks weird 0x%08x", descriptor_id);
+
+ /* find the correct descriptor */
+ buffer = channel->locked;
+ for (i = 0; i < channel->buffer_count; i++) {
+ if (buffer->desc.id == descriptor_id) {
+ found = 1;
+ break;
+ }
+ buffer = buffer->next;
+ }
+ channel->locked = channel->locked->next;
+
+ if (!found) {
+ spin_unlock_irqrestore(&pdev->spinlock, flags);
+ pr_err("tspp: cant find desc %i", descriptor_id);
+ return -EINVAL;
+ }
+
+ /* make sure the buffer is in the expected state */
+ if (buffer->state != TSPP_BUF_STATE_LOCKED) {
+ spin_unlock_irqrestore(&pdev->spinlock, flags);
+ pr_err("tspp: buffer %i not locked", descriptor_id);
+ return -EINVAL;
+ }
+ /* unlock the buffer and requeue it */
+ buffer->state = TSPP_BUF_STATE_WAITING;
+
+ if (tspp_queue_buffer(channel, buffer))
+ pr_warn("tspp: can't requeue buffer");
+
+ spin_unlock_irqrestore(&pdev->spinlock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_release_buffer);
+
+/**
+ * tspp_allocate_buffers - allocate TSPP data buffers.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @channel_id: Channel ID number (up to TSPP_NUM_CHANNELS)
+ * @count: number of buffers to allocate
+ * @size: size of each buffer to allocate
+ * @int_freq: interrupt frequency
+ * @alloc: user defined memory allocator function. Pass NULL for default.
+ * @memfree: user defined memory free function. Pass NULL for default.
+ * @user: user data to pass to the memory allocator/free function
+ *
+ * Return error status
+ *
+ * The user can optionally call this function explicitly to allocate the TSPP
+ * data buffers. Alternatively, if the user did not call this function, it
+ * is called implicitly by tspp_add_filter().
+ */
+int tspp_allocate_buffers(u32 dev, u32 channel_id, u32 count, u32 size,
+ u32 int_freq, tspp_allocator *alloc,
+ tspp_memfree *memfree, void *user)
+{
+ struct tspp_channel *channel;
+ struct tspp_device *pdev;
+ struct tspp_mem_buffer *last = NULL;
+
+ TSPP_DEBUG("tspp_allocate_buffers");
+
+ if (channel_id >= TSPP_NUM_CHANNELS) {
+ pr_err("%s: channel id out of range", __func__);
+ return -ECHRNG;
+ }
+
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("%s: can't find device %i", __func__, dev);
+ return -ENODEV;
+ }
+
+ if (count < MIN_ACCEPTABLE_BUFFER_COUNT) {
+ pr_err("%s: tspp requires a minimum of %i buffers\n",
+ __func__, MIN_ACCEPTABLE_BUFFER_COUNT);
+ return -EINVAL;
+ }
+
+ if (count > TSPP_NUM_BUFFERS) {
+ pr_err("%s: tspp requires a maximum of %i buffers\n",
+ __func__, TSPP_NUM_BUFFERS);
+ return -EINVAL;
+ }
+
+ channel = &pdev->channels[channel_id];
+
+ /* allow buffer allocation only if there was no previous buffer
+ * allocation for this channel.
+ */
+ if (channel->buffer_count > 0) {
+ pr_err("%s: buffers already allocated for channel %u",
+ __func__, channel_id);
+ return -EINVAL;
+ }
+
+ channel->max_buffers = count;
+
+ /* set up interrupt frequency */
+ if (int_freq > channel->max_buffers) {
+ int_freq = channel->max_buffers;
+ pr_warn("%s: setting interrupt frequency to %u\n",
+ __func__, int_freq);
+ }
+ channel->int_freq = int_freq;
+ /*
+ * it is the responsibility of the caller to tspp_allocate_buffers(),
+ * whether it's the user or the driver, to make sure the size parameter
+ * is compatible to the channel mode.
+ */
+ channel->buffer_size = size;
+
+ /* save user defined memory free function for later use */
+ channel->memfree = memfree;
+ channel->user_info = user;
+
+ /*
+ * For small buffers, create a DMA pool so that memory
+ * is not wasted through dma_alloc_coherent.
+ */
+ if (TSPP_USE_DMA_POOL(channel->buffer_size)) {
+ channel->dma_pool = dma_pool_create("tspp",
+ &pdev->pdev->dev, channel->buffer_size, 0, 0);
+ if (!channel->dma_pool) {
+ pr_err("%s: Can't allocate memory pool\n", __func__);
+ return -ENOMEM;
+ }
+ } else {
+ channel->dma_pool = NULL;
+ }
+
+
+ for (channel->buffer_count = 0;
+ channel->buffer_count < channel->max_buffers;
+ channel->buffer_count++) {
+
+ /* allocate the descriptor */
+ struct tspp_mem_buffer *desc = (struct tspp_mem_buffer *)
+ kmalloc(sizeof(struct tspp_mem_buffer), GFP_KERNEL);
+ if (!desc) {
+ pr_warn("%s: Can't allocate desc %i",
+ __func__, channel->buffer_count);
+ break;
+ }
+
+ desc->desc.id = channel->buffer_count;
+ /* allocate the buffer */
+ if (tspp_alloc_buffer(channel_id, &desc->desc,
+ channel->buffer_size, channel->dma_pool,
+ alloc, user) != 0) {
+ kfree(desc);
+ pr_warn("%s: Can't allocate buffer %i",
+ __func__, channel->buffer_count);
+ break;
+ }
+
+ /* add the descriptor to the list */
+ desc->filled = 0;
+ desc->read_index = 0;
+ if (!channel->data) {
+ channel->data = desc;
+ desc->next = channel->data;
+ } else {
+ if (last != NULL)
+ last->next = desc;
+ }
+ last = desc;
+ desc->next = channel->data;
+
+ /* prepare the sps descriptor */
+ desc->sps.phys_base = desc->desc.phys_base;
+ desc->sps.base = desc->desc.virt_base;
+ desc->sps.size = desc->desc.size;
+
+ /* start the transfer */
+ if (tspp_queue_buffer(channel, desc))
+ pr_err("%s: can't queue buffer %i",
+ __func__, desc->desc.id);
+ }
+
+ if (channel->buffer_count < channel->max_buffers) {
+ /*
+ * we failed to allocate the requested number of buffers.
+ * we don't allow a partial success, so need to clean up here.
+ */
+ tspp_destroy_buffers(channel_id, channel);
+ channel->buffer_count = 0;
+
+ dma_pool_destroy(channel->dma_pool);
+ channel->dma_pool = NULL;
+ return -ENOMEM;
+ }
+
+ channel->waiting = channel->data;
+ channel->read = channel->data;
+ channel->locked = channel->data;
+
+ /* Now that buffers are scheduled to HW, kick data expiration timer */
+ if (channel->expiration_period_ms)
+ mod_timer(&channel->expiration_timer,
+ jiffies +
+ MSEC_TO_JIFFIES(
+ channel->expiration_period_ms));
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_allocate_buffers);
+
+/*** debugfs ***/
+static int debugfs_iomem_x32_set(void *data, u64 val)
+{
+ int rc;
+ int clock_started = 0;
+ struct tspp_device *pdev;
+
+ pdev = tspp_find_by_id(0);
+ if (!pdev) {
+ pr_err("%s: can't find device 0\n", __func__);
+ return 0;
+ }
+
+ if (tspp_channels_in_use(pdev) == 0) {
+ rc = tspp_clock_start(pdev);
+ if (rc) {
+ pr_err("%s: tspp_clock_start failed %d\n",
+ __func__, rc);
+ return 0;
+ }
+ clock_started = 1;
+ }
+
+ writel_relaxed(val, data);
+ /* Assure register write */
+ wmb();
+
+ if (clock_started)
+ tspp_clock_stop(pdev);
+ return 0;
+}
+
+static int debugfs_iomem_x32_get(void *data, u64 *val)
+{
+ int rc;
+ int clock_started = 0;
+ struct tspp_device *pdev;
+
+ pdev = tspp_find_by_id(0);
+ if (!pdev) {
+ pr_err("%s: can't find device 0\n", __func__);
+ *val = 0;
+ return 0;
+ }
+
+ if (tspp_channels_in_use(pdev) == 0) {
+ rc = tspp_clock_start(pdev);
+ if (rc) {
+ pr_err("%s: tspp_clock_start failed %d\n",
+ __func__, rc);
+ *val = 0;
+ return 0;
+ }
+ clock_started = 1;
+ }
+
+ *val = readl_relaxed(data);
+
+ if (clock_started)
+ tspp_clock_stop(pdev);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
+ debugfs_iomem_x32_set, "0x%08llx");
+
+static void tsif_debugfs_init(struct tspp_tsif_device *tsif_device,
+ int instance)
+{
+ char name[10];
+
+ snprintf(name, 10, "tsif%i", instance);
+ tsif_device->dent_tsif = debugfs_create_dir(
+ name, NULL);
+ if (tsif_device->dent_tsif) {
+ int i;
+ void __iomem *base = tsif_device->base;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_tsif_regs); i++) {
+ tsif_device->debugfs_tsif_regs[i] =
+ debugfs_create_file(
+ debugfs_tsif_regs[i].name,
+ debugfs_tsif_regs[i].mode,
+ tsif_device->dent_tsif,
+ base + debugfs_tsif_regs[i].offset,
+ &fops_iomem_x32);
+ }
+
+ debugfs_create_u32(
+ "stat_rx_chunks",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ tsif_device->dent_tsif,
+ &tsif_device->stat_rx);
+
+ debugfs_create_u32(
+ "stat_overflow",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ tsif_device->dent_tsif,
+ &tsif_device->stat_overflow);
+
+ debugfs_create_u32(
+ "stat_lost_sync",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ tsif_device->dent_tsif,
+ &tsif_device->stat_lost_sync);
+
+ debugfs_create_u32(
+ "stat_timeout",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ tsif_device->dent_tsif,
+ &tsif_device->stat_timeout);
+ }
+}
+
+static void tsif_debugfs_exit(struct tspp_tsif_device *tsif_device)
+{
+ int i;
+
+ debugfs_remove_recursive(tsif_device->dent_tsif);
+ tsif_device->dent_tsif = NULL;
+ for (i = 0; i < ARRAY_SIZE(debugfs_tsif_regs); i++)
+ tsif_device->debugfs_tsif_regs[i] = NULL;
+}
+
+static void tspp_debugfs_init(struct tspp_device *device, int instance)
+{
+ char name[10];
+
+ snprintf(name, 10, "tspp%i", instance);
+ device->dent = debugfs_create_dir(
+ name, NULL);
+ if (device->dent) {
+ int i;
+ void __iomem *base = device->base;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_tspp_regs); i++)
+ device->debugfs_regs[i] =
+ debugfs_create_file(
+ debugfs_tspp_regs[i].name,
+ debugfs_tspp_regs[i].mode,
+ device->dent,
+ base + debugfs_tspp_regs[i].offset,
+ &fops_iomem_x32);
+ }
+}
+
+static void tspp_debugfs_exit(struct tspp_device *device)
+{
+ int i;
+
+ debugfs_remove_recursive(device->dent);
+ for (i = 0; i < ARRAY_SIZE(debugfs_tspp_regs); i++)
+ device->debugfs_regs[i] = NULL;
+}
+
+static int msm_tspp_map_irqs(struct platform_device *pdev,
+ struct tspp_device *device)
+{
+ int rc;
+
+ /* get IRQ numbers from platform information */
+
+ /* map TSPP IRQ */
+ rc = platform_get_irq_byname(pdev, "TSIF_TSPP_IRQ");
+ if (rc > 0) {
+ device->tspp_irq = rc;
+ } else {
+ dev_err(&pdev->dev, "failed to get TSPP IRQ");
+ return -EINVAL;
+ }
+
+ /* map TSIF IRQs */
+ rc = platform_get_irq_byname(pdev, "TSIF0_IRQ");
+ if (rc > 0) {
+ device->tsif[0].tsif_irq = rc;
+ } else {
+ dev_err(&pdev->dev, "failed to get TSIF0 IRQ");
+ return -EINVAL;
+ }
+
+ rc = platform_get_irq_byname(pdev, "TSIF1_IRQ");
+ if (rc > 0) {
+ device->tsif[1].tsif_irq = rc;
+ } else {
+ dev_err(&pdev->dev, "failed to get TSIF1 IRQ");
+ return -EINVAL;
+ }
+
+ /* map BAM IRQ */
+ rc = platform_get_irq_byname(pdev, "TSIF_BAM_IRQ");
+ if (rc > 0) {
+ device->bam_irq = rc;
+ } else {
+ dev_err(&pdev->dev, "failed to get TSPP BAM IRQ");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int msm_tspp_probe(struct platform_device *pdev)
+{
+ int rc = -ENODEV;
+ u32 version;
+ u32 i;
+ struct tspp_device *device;
+ struct resource *mem_tsif0;
+ struct resource *mem_tsif1;
+ struct resource *mem_tspp;
+ struct resource *mem_bam;
+ struct msm_bus_scale_pdata *tspp_bus_pdata = NULL;
+ unsigned long rate;
+
+ if (pdev->dev.of_node) {
+ /* ID is always 0 since there is only 1 instance of TSPP */
+ pdev->id = 0;
+ tspp_bus_pdata = msm_bus_cl_get_pdata(pdev);
+ } else {
+ /* must have device tree data */
+ pr_err("tspp: Device tree data not available\n");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* OK, we will use this device */
+ device = kzalloc(sizeof(struct tspp_device), GFP_KERNEL);
+ if (!device) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* set up references */
+ device->pdev = pdev;
+ platform_set_drvdata(pdev, device);
+
+ /* setup pin control */
+ rc = tspp_get_pinctrl(device);
+ if (rc) {
+ pr_err("tspp: failed to get pin control data, rc=%d\n", rc);
+ goto err_pinctrl;
+ }
+
+ /* register bus client */
+ if (tspp_bus_pdata) {
+ device->tsif_bus_client =
+ msm_bus_scale_register_client(tspp_bus_pdata);
+ if (!device->tsif_bus_client)
+ pr_err("tspp: Unable to register bus client\n");
+ } else {
+ device->tsif_bus_client = 0;
+ }
+
+ /* map regulators */
+ device->tsif_vreg = devm_regulator_get_optional(&pdev->dev, "vdd_cx");
+ if (IS_ERR(device->tsif_vreg)) {
+ rc = PTR_ERR(device->tsif_vreg);
+ device->tsif_vreg = NULL;
+ if (rc == -ENODEV) {
+ pr_notice("%s: vdd_cx regulator will not be used\n",
+ __func__);
+ } else {
+ dev_err(&pdev->dev,
+ "failed to get CX regulator, err=%d\n", rc);
+ goto err_regulator;
+ }
+ } else {
+ /* Set an initial voltage and enable the regulator */
+ rc = regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_NONE,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to set CX voltage.\n");
+ goto err_regulator;
+ }
+
+ rc = regulator_enable(device->tsif_vreg);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to enable CX regulator.\n");
+ goto err_regulator;
+ }
+ }
+
+ /* map clocks */
+ device->tsif_pclk = clk_get(&pdev->dev, "iface_clk");
+ if (IS_ERR(device->tsif_pclk)) {
+ rc = PTR_ERR(device->tsif_pclk);
+ device->tsif_pclk = NULL;
+ goto err_pclock;
+ }
+
+ device->tsif_ref_clk = clk_get(&pdev->dev, "ref_clk");
+ if (IS_ERR(device->tsif_ref_clk)) {
+ rc = PTR_ERR(device->tsif_ref_clk);
+ device->tsif_ref_clk = NULL;
+ goto err_refclock;
+ }
+ rate = clk_round_rate(device->tsif_ref_clk, 1);
+ rc = clk_set_rate(device->tsif_ref_clk, rate);
+ if (rc)
+ goto err_res_tsif0;
+
+ /* map I/O memory */
+ mem_tsif0 = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "MSM_TSIF0_PHYS");
+ if (!mem_tsif0) {
+ pr_err("tspp: Missing tsif0 MEM resource\n");
+ rc = -ENXIO;
+ goto err_res_tsif0;
+ }
+ device->tsif[0].base = ioremap(mem_tsif0->start,
+ resource_size(mem_tsif0));
+ if (!device->tsif[0].base) {
+ pr_err("tspp: ioremap failed\n");
+ goto err_map_tsif0;
+ }
+
+ mem_tsif1 = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "MSM_TSIF1_PHYS");
+ if (!mem_tsif1) {
+ dev_err(&pdev->dev, "Missing tsif1 MEM resource\n");
+ rc = -ENXIO;
+ goto err_res_tsif1;
+ }
+ device->tsif[1].base = ioremap(mem_tsif1->start,
+ resource_size(mem_tsif1));
+ if (!device->tsif[1].base) {
+ dev_err(&pdev->dev, "ioremap failed");
+ goto err_map_tsif1;
+ }
+
+ mem_tspp = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "MSM_TSPP_PHYS");
+ if (!mem_tspp) {
+ dev_err(&pdev->dev, "Missing MEM resource");
+ rc = -ENXIO;
+ goto err_res_dev;
+ }
+ device->base = ioremap(mem_tspp->start, resource_size(mem_tspp));
+ if (!device->base) {
+ dev_err(&pdev->dev, "ioremap failed");
+ goto err_map_dev;
+ }
+
+ mem_bam = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "MSM_TSPP_BAM_PHYS");
+ if (!mem_bam) {
+ pr_err("tspp: Missing bam MEM resource");
+ rc = -ENXIO;
+ goto err_res_bam;
+ }
+ memset(&device->bam_props, 0, sizeof(device->bam_props));
+ device->bam_props.phys_addr = mem_bam->start;
+ device->bam_props.virt_addr = ioremap(mem_bam->start,
+ resource_size(mem_bam));
+ if (!device->bam_props.virt_addr) {
+ dev_err(&pdev->dev, "ioremap failed");
+ goto err_map_bam;
+ }
+
+ if (msm_tspp_map_irqs(pdev, device))
+ goto err_irq;
+ device->req_irqs = false;
+
+ /* power management */
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ tspp_debugfs_init(device, 0);
+
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+ tsif_debugfs_init(&device->tsif[i], i);
+
+ wakeup_source_init(&device->ws, dev_name(&pdev->dev));
+
+ /* set up pointers to ram-based 'registers' */
+ device->filters[0] = device->base + TSPP_PID_FILTER_TABLE0;
+ device->filters[1] = device->base + TSPP_PID_FILTER_TABLE1;
+ device->filters[2] = device->base + TSPP_PID_FILTER_TABLE2;
+ device->tspp_key_table = device->base + TSPP_DATA_KEY;
+ device->tspp_global_performance =
+ device->base + TSPP_GLOBAL_PERFORMANCE;
+ device->tspp_pipe_context =
+ device->base + TSPP_PIPE_CONTEXT;
+ device->tspp_pipe_performance =
+ device->base + TSPP_PIPE_PERFORMANCE;
+
+ device->bam_props.summing_threshold = 0x10;
+ device->bam_props.irq = device->bam_irq;
+ device->bam_props.manage = SPS_BAM_MGR_LOCAL;
+ /*add SPS BAM log level*/
+ device->bam_props.ipc_loglevel = TSPP_BAM_DEFAULT_IPC_LOGLVL;
+
+ if (tspp_clock_start(device) != 0) {
+ dev_err(&pdev->dev, "Can't start clocks");
+ goto err_clock;
+ }
+
+ device->bam_handle = SPS_DEV_HANDLE_INVALID;
+
+ spin_lock_init(&device->spinlock);
+ mutex_init(&device->mutex);
+ tasklet_init(&device->tlet, tspp_sps_complete_tlet,
+ (unsigned long)device);
+
+ /* initialize everything to a known state */
+ tspp_global_reset(device);
+
+ version = readl_relaxed(device->base + TSPP_VERSION);
+ /*
+ * TSPP version can be bits [7:0] or alternatively,
+ * TSPP major version is bits [31:28].
+ */
+ if ((version != 0x1) && (((version >> 28) & 0xF) != 0x1))
+ pr_warn("tspp: unrecognized hw version=%i", version);
+
+ /* initialize the channels */
+ for (i = 0; i < TSPP_NUM_CHANNELS; i++)
+ tspp_channel_init(&(device->channels[i]), device);
+
+ /* stop the clocks for power savings */
+ tspp_clock_stop(device);
+
+ /* everything is ok, so add the device to the list */
+ list_add_tail(&(device->devlist), &tspp_devices);
+ return 0;
+
+err_clock:
+ tspp_debugfs_exit(device);
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+ tsif_debugfs_exit(&device->tsif[i]);
+err_irq:
+ iounmap(device->bam_props.virt_addr);
+err_map_bam:
+err_res_bam:
+ iounmap(device->base);
+err_map_dev:
+err_res_dev:
+ iounmap(device->tsif[1].base);
+err_map_tsif1:
+err_res_tsif1:
+ iounmap(device->tsif[0].base);
+err_map_tsif0:
+err_res_tsif0:
+ if (device->tsif_ref_clk)
+ clk_put(device->tsif_ref_clk);
+err_refclock:
+ if (device->tsif_pclk)
+ clk_put(device->tsif_pclk);
+err_pclock:
+ if (device->tsif_vreg)
+ regulator_disable(device->tsif_vreg);
+err_regulator:
+ if (device->tsif_bus_client)
+ msm_bus_scale_unregister_client(device->tsif_bus_client);
+err_pinctrl:
+ kfree(device);
+
+out:
+ return rc;
+}
+
+static int msm_tspp_remove(struct platform_device *pdev)
+{
+ struct tspp_channel *channel;
+ u32 i;
+
+ struct tspp_device *device = platform_get_drvdata(pdev);
+
+ /* free the buffers, and delete the channels */
+ for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
+ channel = &device->channels[i];
+ tspp_close_channel(device->pdev->id, i);
+ }
+
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+ tsif_debugfs_exit(&device->tsif[i]);
+
+ mutex_destroy(&device->mutex);
+
+ if (device->tsif_bus_client)
+ msm_bus_scale_unregister_client(device->tsif_bus_client);
+
+ wakeup_source_trash(&device->ws);
+ if (device->req_irqs)
+ msm_tspp_free_irqs(device);
+
+ iounmap(device->bam_props.virt_addr);
+ iounmap(device->base);
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+ iounmap(device->tsif[i].base);
+
+ if (device->tsif_ref_clk)
+ clk_put(device->tsif_ref_clk);
+
+ if (device->tsif_pclk)
+ clk_put(device->tsif_pclk);
+
+ if (device->tsif_vreg)
+ regulator_disable(device->tsif_vreg);
+
+ pm_runtime_disable(&pdev->dev);
+
+ kfree(device);
+
+ return 0;
+}
+
+/*** power management ***/
+
+static int tspp_runtime_suspend(struct device *dev)
+{
+ dev_dbg(dev, "pm_runtime: suspending...");
+ return 0;
+}
+
+static int tspp_runtime_resume(struct device *dev)
+{
+ dev_dbg(dev, "pm_runtime: resuming...");
+ return 0;
+}
+
+static const struct dev_pm_ops tspp_dev_pm_ops = {
+ .runtime_suspend = tspp_runtime_suspend,
+ .runtime_resume = tspp_runtime_resume,
+};
+
+static const struct of_device_id msm_match_table[] = {
+ {.compatible = "qcom,msm_tspp"},
+ {}
+};
+
+static struct platform_driver msm_tspp_driver = {
+ .probe = msm_tspp_probe,
+ .remove = msm_tspp_remove,
+ .driver = {
+ .name = "msm_tspp",
+ .pm = &tspp_dev_pm_ops,
+ .of_match_table = msm_match_table,
+ },
+};
+
+
+static int __init mod_init(void)
+{
+ int rc;
+
+ /* register the driver, and check hardware */
+ rc = platform_driver_register(&msm_tspp_driver);
+ if (rc)
+ pr_err("tspp: platform_driver_register failed: %d", rc);
+
+ return rc;
+}
+
+static void __exit mod_exit(void)
+{
+ /* delete low level driver */
+ platform_driver_unregister(&msm_tspp_driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_DESCRIPTION("TSPP platform device");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
index 8da079d9d0c8..d6bb18522e0c 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
@@ -542,6 +542,28 @@ int msm_camera_put_clk_info_and_rates(struct platform_device *pdev,
}
EXPORT_SYMBOL(msm_camera_put_clk_info_and_rates);
+/* Get reset info from DT */
+int msm_camera_get_reset_info(struct platform_device *pdev,
+ struct reset_control **micro_iface_reset)
+{
+ if (!pdev || !micro_iface_reset)
+ return -EINVAL;
+
+ if (of_property_match_string(pdev->dev.of_node, "reset-names",
+ "micro_iface_reset")) {
+ pr_err("err: Reset property not found\n");
+ return -EINVAL;
+ }
+
+ *micro_iface_reset = devm_reset_control_get
+ (&pdev->dev, "micro_iface_reset");
+ if (IS_ERR(*micro_iface_reset))
+ return PTR_ERR(*micro_iface_reset);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_camera_get_reset_info);
+
/* Get regulators from DT */
int msm_camera_get_regulator_info(struct platform_device *pdev,
struct msm_cam_regulator **vdd_info,
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.h b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.h
index b4494d4d6bab..0e9d26bebe30 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.h
+++ b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.h
@@ -21,6 +21,7 @@
#include <linux/regulator/consumer.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/reset.h>
#include <soc/qcom/camera2.h>
enum cam_bus_client {
@@ -187,6 +188,21 @@ int msm_camera_clk_enable(struct device *dev,
long msm_camera_clk_set_rate(struct device *dev,
struct clk *clk,
long clk_rate);
+
+/**
+ * @brief : Gets reset info
+ *
+ * This function extracts the reset information for a specific
+ * platform device
+ *
+ * @param pdev : platform device to get reset information
+ * @param micro_iface_reset : Pointer to populate the reset names
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+
+int msm_camera_get_reset_info(struct platform_device *pdev,
+ struct reset_control **micro_iface_reset);
/**
* @brief : Sets flags of a clock
*
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/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index f4add41c85c9..3ac4c3af3208 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -4017,11 +4017,20 @@ static int cpp_probe(struct platform_device *pdev)
}
}
+ rc = msm_camera_get_reset_info(pdev,
+ &cpp_dev->micro_iface_reset);
+ if (rc < 0) {
+ cpp_dev->micro_iface_reset = NULL;
+ pr_err("%s: failed to get micro_iface_reset\n",
+ __func__);
+ goto get_reg_err;
+ }
+
rc = msm_camera_get_regulator_info(pdev, &cpp_dev->cpp_vdd,
&cpp_dev->num_reg);
if (rc < 0) {
pr_err("%s: failed to get the regulators\n", __func__);
- goto get_reg_err;
+ goto get_reset_err;
}
msm_cpp_fetch_dt_params(cpp_dev);
@@ -4104,6 +4113,8 @@ static int cpp_probe(struct platform_device *pdev)
cpp_probe_init_error:
media_entity_cleanup(&cpp_dev->msm_sd.sd.entity);
msm_sd_unregister(&cpp_dev->msm_sd);
+get_reset_err:
+ reset_control_put(cpp_dev->micro_iface_reset);
get_reg_err:
msm_camera_put_clk_info(pdev, &cpp_dev->clk_info, &cpp_dev->cpp_clk,
cpp_dev->num_clks);
@@ -4161,6 +4172,9 @@ static int cpp_device_remove(struct platform_device *dev)
msm_camera_unregister_bus_client(CAM_BUS_CLIENT_CPP);
mutex_destroy(&cpp_dev->mutex);
kfree(cpp_dev->work);
+
+ reset_control_put(cpp_dev->micro_iface_reset);
+
destroy_workqueue(cpp_dev->timer_wq);
kfree(cpp_dev->cpp_clk);
kfree(cpp_dev);
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
index 1784e27b1e37..d5abe0202717 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
@@ -211,6 +211,7 @@ struct cpp_device {
struct clk **cpp_clk;
struct msm_cam_clk_info *clk_info;
size_t num_clks;
+ struct reset_control *micro_iface_reset;
struct msm_cam_regulator *cpp_vdd;
int num_reg;
struct mutex mutex;
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c
index ee52284e3ae3..2c313016bc90 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp_soc.c
@@ -103,20 +103,11 @@ static int cpp_get_clk_freq_tbl(struct clk *clk, struct cpp_hw_info *hw_info,
int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev)
{
- uint32_t msm_micro_iface_idx;
int rc;
- msm_micro_iface_idx = msm_cpp_get_clock_index(cpp_dev,
- "micro_iface_clk");
- if (msm_micro_iface_idx < 0) {
- pr_err("Fail to get clock index\n");
- return -EINVAL;
- }
-
- rc = clk_reset(cpp_dev->cpp_clk[msm_micro_iface_idx],
- CLK_RESET_ASSERT);
+ rc = reset_control_assert(cpp_dev->micro_iface_reset);
if (rc) {
- pr_err("%s:micro_iface_clk assert failed\n",
+ pr_err("%s:micro_iface_reset assert failed\n",
__func__);
return -EINVAL;
}
@@ -129,10 +120,9 @@ int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev)
*/
usleep_range(1000, 1200);
- rc = clk_reset(cpp_dev->cpp_clk[msm_micro_iface_idx],
- CLK_RESET_DEASSERT);
+ rc = reset_control_deassert(cpp_dev->micro_iface_reset);
if (rc) {
- pr_err("%s:micro_iface_clk de-assert failed\n", __func__);
+ pr_err("%s:micro_iface_reset de-assert failed\n", __func__);
return -EINVAL;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile
index 539ba24e109b..872dc59d218e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile
@@ -4,5 +4,5 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_vb2
ccflags-y += -Idrivers/media/platform/msm/camera_v2/camera
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
-obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ eeprom/ ois/ flash/
+obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ eeprom/ ois/ flash/ ir_led/ ir_cut/
obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor_init.o msm_sensor_driver.o msm_sensor.o
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/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
index 6a1b385c3d8b..e4cee1fa4ffc 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
@@ -1052,6 +1052,25 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d csiphy config_vreg failed\n",
+ __func__, __LINE__);
+ goto csiphy_resource_fail;
+ }
+ rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d csiphy enable_vreg failed\n",
+ __func__, __LINE__);
+ goto top_vreg_enable_failed;
+ }
+
rc = msm_camera_clk_enable(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk,
csiphy_dev->num_clk, true);
@@ -1088,6 +1107,11 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
return 0;
+top_vreg_enable_failed:
+ msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
csiphy_resource_fail:
if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
CAM_AHB_SUSPEND_VOTE) < 0)
@@ -1128,6 +1152,24 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
pr_err("%s: failed to vote for AHB\n", __func__);
return rc;
}
+ rc = msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d csiphy config_vreg failed\n",
+ __func__, __LINE__);
+ goto csiphy_resource_fail;
+ }
+ rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 1);
+ if (rc < 0) {
+ pr_err("%s:%d csiphy enable_vreg failed\n",
+ __func__, __LINE__);
+ goto top_vreg_enable_failed;
+ }
rc = msm_camera_clk_enable(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_clk_info, csiphy_dev->csiphy_clk,
@@ -1139,7 +1181,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
csiphy_dev->ref_count--;
goto csiphy_resource_fail;
}
- CDBG("%s:%d called\n", __func__, __LINE__);
+ CDBG("%s:%d clk enable success\n", __func__, __LINE__);
if (csiphy_dev->csiphy_3phase == CSI_3PHASE_HW)
msm_csiphy_3ph_reset(csiphy_dev);
@@ -1161,7 +1203,11 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
csiphy_dev->hw_version);
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
return 0;
-
+top_vreg_enable_failed:
+ msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
csiphy_resource_fail:
if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
CAM_AHB_SUSPEND_VOTE) < 0)
@@ -1275,6 +1321,14 @@ static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
csiphy_dev->csiphy_3p_clk, 2, false);
}
+ msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
+ msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg, csiphy_dev->regulator_count,
+ NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0);
+
csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
@@ -1386,6 +1440,13 @@ static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
csiphy_dev->csiphy_3p_clk, 2, false);
}
+ msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg, csiphy_dev->regulator_count,
+ NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0);
+ msm_camera_config_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg, csiphy_dev->regulator_count,
+ NULL, 0, &csiphy_dev->csiphy_reg_ptr[0], 0);
+
csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
@@ -1711,6 +1772,14 @@ static int csiphy_probe(struct platform_device *pdev)
goto csiphy_no_resource;
}
+ rc = msm_camera_get_dt_vreg_data(pdev->dev.of_node,
+ &(new_csiphy_dev->csiphy_vreg),
+ &(new_csiphy_dev->regulator_count));
+ if (rc < 0) {
+ pr_err("%s: get vreg data from dtsi fail\n", __func__);
+ rc = -EFAULT;
+ goto csiphy_no_resource;
+ }
/* ToDo: Enable 3phase clock for dynamic clock enable/disable */
rc = msm_csiphy_get_clk_info(new_csiphy_dev, pdev);
if (rc < 0) {
@@ -1781,7 +1850,7 @@ static int msm_csiphy_exit(struct platform_device *pdev)
&csiphy_dev->csiphy_all_clk,
csiphy_dev->num_all_clk);
- msm_camera_put_reg_base(pdev, csiphy_dev->base, "csid", true);
+ msm_camera_put_reg_base(pdev, csiphy_dev->base, "csiphy", true);
if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V30) {
msm_camera_put_reg_base(pdev, csiphy_dev->clk_mux_base,
"csiphy_clk_mux", true);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
index aba88da1157e..70462dcd3b12 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
@@ -20,6 +20,7 @@
#include <media/msm_cam_sensor.h>
#include "msm_sd.h"
#include "msm_camera_io_util.h"
+#include "msm_camera_dt_util.h"
#include "cam_soc_api.h"
#define MAX_CSIPHY 3
@@ -183,6 +184,9 @@ struct csiphy_device {
uint8_t num_irq_registers;
uint32_t csiphy_sof_debug;
uint32_t csiphy_sof_debug_count;
+ struct camera_vreg_t *csiphy_vreg;
+ struct regulator *csiphy_reg_ptr[MAX_REGULATOR];
+ int32_t regulator_count;
};
#define VIDIOC_MSM_CSIPHY_RELEASE \
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_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
index 6b867bfb5c4a..3f079fe2c173 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
@@ -270,6 +270,38 @@ int msm_sensor_get_sub_module_index(struct device_node *of_node,
src_node = NULL;
}
+ src_node = of_parse_phandle(of_node, "qcom,ir-led-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,ir led cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed %d\n", __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ sensor_info->subdev_id[SUB_MODULE_IR_LED] = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+
+ src_node = of_parse_phandle(of_node, "qcom,ir-cut-src", 0);
+ if (!src_node) {
+ CDBG("%s:%d src_node NULL\n", __func__, __LINE__);
+ } else {
+ rc = of_property_read_u32(src_node, "cell-index", &val);
+ CDBG("%s qcom,ir cut cell index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed %d\n", __func__, __LINE__, rc);
+ goto ERROR;
+ }
+ sensor_info->subdev_id[SUB_MODULE_IR_CUT] = val;
+ of_node_put(src_node);
+ src_node = NULL;
+ }
+
rc = of_property_read_u32(of_node, "qcom,strobe-flash-sd-index", &val);
if (rc != -EINVAL) {
CDBG("%s qcom,strobe-flash-sd-index %d, rc %d\n", __func__,
@@ -748,6 +780,54 @@ int msm_camera_init_gpio_pin_tbl(struct device_node *of_node,
rc = -ENOMEM;
return rc;
}
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-ir-p", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-ir-p failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-ir-p invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+
+ gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_P] =
+ gpio_array[val];
+ gconf->gpio_num_info->valid[IR_CUT_FILTER_GPIO_P] = 1;
+
+ CDBG("%s qcom,gpio-ir-p %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_P]);
+ } else {
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,gpio-ir-m", &val);
+ if (rc != -EINVAL) {
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-ir-m failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-ir-m invalid %d\n",
+ __func__, __LINE__, val);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+
+ gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_M] =
+ gpio_array[val];
+
+ gconf->gpio_num_info->valid[IR_CUT_FILTER_GPIO_M] = 1;
+
+ CDBG("%s qcom,gpio-ir-m %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_M]);
+ } else {
+ rc = 0;
+ }
+
rc = of_property_read_u32(of_node, "qcom,gpio-vana", &val);
if (rc != -EINVAL) {
if (rc < 0) {
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/ir_cut/Makefile b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/Makefile
new file mode 100644
index 000000000000..8950c1c83763
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSMB_CAMERA) += msm_ir_cut.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c
new file mode 100644
index 000000000000..a0e35ba99a25
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.c
@@ -0,0 +1,665 @@
+/* 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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include "msm_ir_cut.h"
+#include "msm_camera_dt_util.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+DEFINE_MSM_MUTEX(msm_ir_cut_mutex);
+
+static struct v4l2_file_operations msm_ir_cut_v4l2_subdev_fops;
+
+static const struct of_device_id msm_ir_cut_dt_match[] = {
+ {.compatible = "qcom,ir-cut", .data = NULL},
+ {}
+};
+
+static struct msm_ir_cut_table msm_gpio_ir_cut_table;
+
+static struct msm_ir_cut_table *ir_cut_table[] = {
+ &msm_gpio_ir_cut_table,
+};
+
+static int32_t msm_ir_cut_get_subdev_id(
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl, void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+
+ CDBG("Enter\n");
+ if (!subdev_id) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+ if (ir_cut_ctrl->ir_cut_device_type != MSM_CAMERA_PLATFORM_DEVICE) {
+ pr_err("failed\n");
+ return -EINVAL;
+ }
+
+ *subdev_id = ir_cut_ctrl->pdev->id;
+
+ CDBG("subdev_id %d\n", *subdev_id);
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_cut_init(
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
+ struct msm_ir_cut_cfg_data_t *ir_cut_data)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter");
+
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_on(ir_cut_ctrl, ir_cut_data);
+
+ CDBG("Exit");
+ return rc;
+}
+
+static int32_t msm_ir_cut_release(
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl)
+{
+ int32_t rc = 0;
+
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_RELEASE) {
+ pr_err("%s:%d Invalid ir_cut state = %d",
+ __func__, __LINE__, ir_cut_ctrl->ir_cut_state);
+ return 0;
+ }
+
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_on(ir_cut_ctrl, NULL);
+ if (rc < 0) {
+ pr_err("%s:%d camera_ir_cut_on failed rc = %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+ ir_cut_ctrl->ir_cut_state = MSM_CAMERA_IR_CUT_RELEASE;
+ return 0;
+}
+
+static int32_t msm_ir_cut_off(struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
+ struct msm_ir_cut_cfg_data_t *ir_cut_data)
+{
+ int rc = 0;
+
+ CDBG("Enter cut off\n");
+
+ if (ir_cut_ctrl->gconf) {
+ rc = msm_camera_request_gpio_table(
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 1);
+
+ if (rc < 0) {
+ pr_err("ERR:%s:Failed in selecting state: %d\n",
+ __func__, rc);
+
+ return rc;
+ }
+ } else {
+ pr_err("%s: No IR CUT GPIOs\n", __func__);
+ return 0;
+ }
+
+ if (ir_cut_ctrl->cam_pinctrl_status) {
+ rc = pinctrl_select_state(
+ ir_cut_ctrl->pinctrl_info.pinctrl,
+ ir_cut_ctrl->pinctrl_info.gpio_state_active);
+
+ if (rc < 0)
+ pr_err("ERR:%s:%d cannot set pin to active state: %d",
+ __func__, __LINE__, rc);
+ }
+
+ CDBG("ERR:%s:gpio_conf->gpio_num_info->gpio_num[0] = %d",
+ __func__,
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_P]);
+
+ CDBG("ERR:%s:gpio_conf->gpio_num_info->gpio_num[1] = %d",
+ __func__,
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_M]);
+
+ gpio_set_value_cansleep(
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_P],
+ 0);
+
+ gpio_set_value_cansleep(
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_M],
+ 1);
+
+ if (ir_cut_ctrl->gconf) {
+ rc = msm_camera_request_gpio_table(
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 0);
+
+ if (rc < 0) {
+ pr_err("ERR:%s:Failed in selecting state: %d\n",
+ __func__, rc);
+
+ return rc;
+ }
+ } else {
+ pr_err("%s: No IR CUT GPIOs\n", __func__);
+ return 0;
+ }
+
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_cut_on(
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
+ struct msm_ir_cut_cfg_data_t *ir_cut_data)
+{
+ int rc = 0;
+
+ CDBG("Enter ir cut on\n");
+
+ if (ir_cut_ctrl->gconf) {
+ rc = msm_camera_request_gpio_table(
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 1);
+
+ if (rc < 0) {
+ pr_err("ERR:%s:Failed in selecting state: %d\n",
+ __func__, rc);
+
+ return rc;
+ }
+ } else {
+ pr_err("%s: No IR CUT GPIOs\n", __func__);
+ return 0;
+ }
+
+ if (ir_cut_ctrl->cam_pinctrl_status) {
+ rc = pinctrl_select_state(
+ ir_cut_ctrl->pinctrl_info.pinctrl,
+ ir_cut_ctrl->pinctrl_info.gpio_state_active);
+
+ if (rc < 0)
+ pr_err("ERR:%s:%d cannot set pin to active state: %d",
+ __func__, __LINE__, rc);
+ }
+
+ CDBG("ERR:%s: gpio_conf->gpio_num_info->gpio_num[0] = %d",
+ __func__,
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_P]);
+
+ CDBG("ERR:%s: gpio_conf->gpio_num_info->gpio_num[1] = %d",
+ __func__,
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_M]);
+
+ gpio_set_value_cansleep(
+ ir_cut_ctrl->gconf->gpio_num_info->
+ gpio_num[IR_CUT_FILTER_GPIO_P],
+ 1);
+
+ gpio_set_value_cansleep(
+ ir_cut_ctrl->gconf->
+ gpio_num_info->gpio_num[IR_CUT_FILTER_GPIO_M],
+ 1);
+
+ if (ir_cut_ctrl->gconf) {
+ rc = msm_camera_request_gpio_table(
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl_size, 0);
+
+ if (rc < 0) {
+ pr_err("ERR:%s:Failed in selecting state: %d\n",
+ __func__, rc);
+
+ return rc;
+ }
+ } else {
+ pr_err("%s: No IR CUT GPIOs\n", __func__);
+ return 0;
+ }
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_cut_handle_init(
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
+ struct msm_ir_cut_cfg_data_t *ir_cut_data)
+{
+ uint32_t i = 0;
+ int32_t rc = -EFAULT;
+ enum msm_ir_cut_driver_type ir_cut_driver_type =
+ ir_cut_ctrl->ir_cut_driver_type;
+
+ CDBG("Enter");
+
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT) {
+ pr_err("%s:%d Invalid ir_cut state = %d",
+ __func__, __LINE__, ir_cut_ctrl->ir_cut_state);
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ir_cut_table); i++) {
+ if (ir_cut_driver_type == ir_cut_table[i]->ir_cut_driver_type) {
+ ir_cut_ctrl->func_tbl = &ir_cut_table[i]->func_tbl;
+ rc = 0;
+ break;
+ }
+ }
+
+ if (rc < 0) {
+ pr_err("%s:%d failed invalid ir_cut_driver_type %d\n",
+ __func__, __LINE__, ir_cut_driver_type);
+ return -EINVAL;
+ }
+
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_init(
+ ir_cut_ctrl, ir_cut_data);
+ if (rc < 0) {
+ pr_err("%s:%d camera_ir_cut_init failed rc = %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ ir_cut_ctrl->ir_cut_state = MSM_CAMERA_IR_CUT_INIT;
+
+ CDBG("Exit");
+ return 0;
+}
+
+static int32_t msm_ir_cut_config(struct msm_ir_cut_ctrl_t *ir_cut_ctrl,
+ void __user *argp)
+{
+ int32_t rc = -EINVAL;
+ struct msm_ir_cut_cfg_data_t *ir_cut_data =
+ (struct msm_ir_cut_cfg_data_t *) argp;
+
+ mutex_lock(ir_cut_ctrl->ir_cut_mutex);
+
+ CDBG("Enter %s type %d\n", __func__, ir_cut_data->cfg_type);
+
+ switch (ir_cut_data->cfg_type) {
+ case CFG_IR_CUT_INIT:
+ rc = msm_ir_cut_handle_init(ir_cut_ctrl, ir_cut_data);
+ break;
+ case CFG_IR_CUT_RELEASE:
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT)
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_release(
+ ir_cut_ctrl);
+ break;
+ case CFG_IR_CUT_OFF:
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT)
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_off(
+ ir_cut_ctrl, ir_cut_data);
+ break;
+ case CFG_IR_CUT_ON:
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT)
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_on(
+ ir_cut_ctrl, ir_cut_data);
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(ir_cut_ctrl->ir_cut_mutex);
+
+ CDBG("Exit %s type %d\n", __func__, ir_cut_data->cfg_type);
+
+ return rc;
+}
+
+static long msm_ir_cut_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_ir_cut_ctrl_t *fctrl = NULL;
+ void __user *argp = (void __user *)arg;
+
+ CDBG("Enter\n");
+
+ if (!sd) {
+ pr_err("sd NULL\n");
+ return -EINVAL;
+ }
+ fctrl = v4l2_get_subdevdata(sd);
+ if (!fctrl) {
+ pr_err("fctrl NULL\n");
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_ir_cut_get_subdev_id(fctrl, argp);
+ case VIDIOC_MSM_IR_CUT_CFG:
+ return msm_ir_cut_config(fctrl, argp);
+ case MSM_SD_NOTIFY_FREEZE:
+ return 0;
+ case MSM_SD_SHUTDOWN:
+ if (!fctrl->func_tbl) {
+ pr_err("fctrl->func_tbl NULL\n");
+ return -EINVAL;
+ } else {
+ return fctrl->func_tbl->camera_ir_cut_release(fctrl);
+ }
+ default:
+ pr_err_ratelimited("invalid cmd %d\n", cmd);
+ return -ENOIOCTLCMD;
+ }
+ CDBG("Exit\n");
+}
+
+static struct v4l2_subdev_core_ops msm_ir_cut_subdev_core_ops = {
+ .ioctl = msm_ir_cut_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_ir_cut_subdev_ops = {
+ .core = &msm_ir_cut_subdev_core_ops,
+};
+static int msm_ir_cut_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+
+ int rc = 0;
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl = v4l2_get_subdevdata(sd);
+
+ CDBG("Enter\n");
+
+ if (!ir_cut_ctrl) {
+ pr_err("%s: failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ir_cut_ctrl->ir_cut_state == MSM_CAMERA_IR_CUT_INIT)
+ rc = ir_cut_ctrl->func_tbl->camera_ir_cut_release(
+ ir_cut_ctrl);
+
+ CDBG("Exit\n");
+
+ return rc;
+};
+
+static const struct v4l2_subdev_internal_ops msm_ir_cut_internal_ops = {
+ .close = msm_ir_cut_close,
+};
+
+static int32_t msm_ir_cut_get_gpio_dt_data(struct device_node *of_node,
+ struct msm_ir_cut_ctrl_t *fctrl)
+{
+ int32_t rc = 0, i = 0;
+ uint16_t *gpio_array = NULL;
+ int16_t gpio_array_size = 0;
+ struct msm_camera_gpio_conf *gconf = NULL;
+
+ gpio_array_size = of_gpio_count(of_node);
+ CDBG("%s gpio count %d\n", __func__, gpio_array_size);
+
+ if (gpio_array_size > 0) {
+ fctrl->power_info.gpio_conf =
+ kzalloc(sizeof(struct msm_camera_gpio_conf),
+ GFP_KERNEL);
+ if (!fctrl->power_info.gpio_conf) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ gconf = fctrl->power_info.gpio_conf;
+
+ gpio_array = kcalloc(gpio_array_size, sizeof(uint16_t),
+ GFP_KERNEL);
+ if (!gpio_array)
+ return -ENOMEM;
+ for (i = 0; i < gpio_array_size; i++) {
+ gpio_array[i] = of_get_gpio(of_node, i);
+ if (((int16_t)gpio_array[i]) < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -EINVAL;
+ goto free_gpio_array;
+ }
+ CDBG("%s gpio_array[%d] = %d\n", __func__, i,
+ gpio_array[i]);
+ }
+
+ rc = msm_camera_get_dt_gpio_req_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto free_gpio_array;
+ }
+ kfree(gpio_array);
+
+ if (fctrl->ir_cut_driver_type == IR_CUT_DRIVER_DEFAULT)
+ fctrl->ir_cut_driver_type = IR_CUT_DRIVER_GPIO;
+ CDBG("%s:%d fctrl->ir_cut_driver_type = %d", __func__, __LINE__,
+ fctrl->ir_cut_driver_type);
+ }
+
+ return rc;
+
+free_gpio_array:
+ kfree(gpio_array);
+ return rc;
+}
+
+static int32_t msm_ir_cut_get_dt_data(struct device_node *of_node,
+ struct msm_ir_cut_ctrl_t *fctrl)
+{
+ int32_t rc = 0;
+
+ CDBG("called\n");
+
+ if (!of_node) {
+ pr_err("of_node NULL\n");
+ return -EINVAL;
+ }
+
+ /* Read the sub device */
+ rc = of_property_read_u32(of_node, "cell-index", &fctrl->pdev->id);
+ if (rc < 0) {
+ pr_err("failed rc %d\n", rc);
+ return rc;
+ }
+
+ fctrl->ir_cut_driver_type = IR_CUT_DRIVER_DEFAULT;
+
+ /* Read the gpio information from device tree */
+ rc = msm_ir_cut_get_gpio_dt_data(of_node, fctrl);
+ if (rc < 0) {
+ pr_err("%s:%d msm_ir_cut_get_gpio_dt_data failed rc %d\n",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_ir_cut_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct msm_ir_cut_cfg_data_t32 *u32 =
+ (struct msm_ir_cut_cfg_data_t32 *)arg;
+ struct msm_ir_cut_cfg_data_t ir_cut_data;
+
+ CDBG("Enter");
+ ir_cut_data.cfg_type = u32->cfg_type;
+
+ switch (cmd) {
+ case VIDIOC_MSM_IR_CUT_CFG32:
+ cmd = VIDIOC_MSM_IR_CUT_CFG;
+ break;
+ default:
+ return msm_ir_cut_subdev_ioctl(sd, cmd, arg);
+ }
+
+ rc = msm_ir_cut_subdev_ioctl(sd, cmd, &ir_cut_data);
+
+ CDBG("Exit");
+ return rc;
+}
+
+static long msm_ir_cut_subdev_fops_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_ir_cut_subdev_do_ioctl);
+}
+#endif
+
+static int32_t msm_ir_cut_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0, i = 0;
+ struct msm_ir_cut_ctrl_t *ir_cut_ctrl = NULL;
+
+ CDBG("Enter");
+ if (!pdev->dev.of_node) {
+ pr_err("of_node NULL\n");
+ return -EINVAL;
+ }
+
+ ir_cut_ctrl = kzalloc(sizeof(struct msm_ir_cut_ctrl_t), GFP_KERNEL);
+ if (!ir_cut_ctrl)
+ return -ENOMEM;
+
+ memset(ir_cut_ctrl, 0, sizeof(struct msm_ir_cut_ctrl_t));
+
+ ir_cut_ctrl->pdev = pdev;
+
+ rc = msm_ir_cut_get_dt_data(pdev->dev.of_node, ir_cut_ctrl);
+
+ if (rc < 0) {
+ pr_err("%s:%d msm_ir_cut_get_dt_data failed\n",
+ __func__, __LINE__);
+ kfree(ir_cut_ctrl);
+ return -EINVAL;
+ }
+
+ rc = msm_sensor_driver_get_gpio_data(&(ir_cut_ctrl->gconf),
+ (&pdev->dev)->of_node);
+
+ if ((rc < 0) || (ir_cut_ctrl->gconf == NULL)) {
+ pr_err("%s: No IR CUT GPIOs\n", __func__);
+
+ kfree(ir_cut_ctrl);
+ return -EINVAL;
+ }
+
+ CDBG("%s: gpio_request_table_size = %d\n",
+ __func__,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl_size);
+
+ for (i = 0;
+ i < ir_cut_ctrl->gconf->cam_gpio_req_tbl_size; i++) {
+ CDBG("%s: gpio = %d\n", __func__,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl[i].gpio);
+ CDBG("%s: gpio-flags = %lu\n", __func__,
+ ir_cut_ctrl->gconf->cam_gpio_req_tbl[i].flags);
+ CDBG("%s: gconf->gpio_num_info->gpio_num[%d] = %d\n",
+ __func__, i,
+ ir_cut_ctrl->gconf->gpio_num_info->gpio_num[i]);
+ }
+
+ ir_cut_ctrl->cam_pinctrl_status = 1;
+
+ rc = msm_camera_pinctrl_init(
+ &(ir_cut_ctrl->pinctrl_info), &(pdev->dev));
+
+ if (rc < 0) {
+ pr_err("ERR:%s: Error in reading IR CUT pinctrl\n",
+ __func__);
+ ir_cut_ctrl->cam_pinctrl_status = 0;
+ }
+
+ ir_cut_ctrl->ir_cut_state = MSM_CAMERA_IR_CUT_RELEASE;
+ ir_cut_ctrl->power_info.dev = &ir_cut_ctrl->pdev->dev;
+ ir_cut_ctrl->ir_cut_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ ir_cut_ctrl->ir_cut_mutex = &msm_ir_cut_mutex;
+
+ /* Initialize sub device */
+ v4l2_subdev_init(&ir_cut_ctrl->msm_sd.sd, &msm_ir_cut_subdev_ops);
+ v4l2_set_subdevdata(&ir_cut_ctrl->msm_sd.sd, ir_cut_ctrl);
+
+ ir_cut_ctrl->msm_sd.sd.internal_ops = &msm_ir_cut_internal_ops;
+ ir_cut_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(ir_cut_ctrl->msm_sd.sd.name,
+ ARRAY_SIZE(ir_cut_ctrl->msm_sd.sd.name),
+ "msm_camera_ir_cut");
+ media_entity_init(&ir_cut_ctrl->msm_sd.sd.entity, 0, NULL, 0);
+ ir_cut_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ ir_cut_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_IR_CUT;
+ ir_cut_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
+ msm_sd_register(&ir_cut_ctrl->msm_sd);
+
+ CDBG("%s:%d ir_cut sd name = %s", __func__, __LINE__,
+ ir_cut_ctrl->msm_sd.sd.entity.name);
+ msm_ir_cut_v4l2_subdev_fops = v4l2_subdev_fops;
+#ifdef CONFIG_COMPAT
+ msm_ir_cut_v4l2_subdev_fops.compat_ioctl32 =
+ msm_ir_cut_subdev_fops_ioctl;
+#endif
+ ir_cut_ctrl->msm_sd.sd.devnode->fops = &msm_ir_cut_v4l2_subdev_fops;
+
+ CDBG("probe success\n");
+ return rc;
+}
+
+MODULE_DEVICE_TABLE(of, msm_ir_cut_dt_match);
+
+static struct platform_driver msm_ir_cut_platform_driver = {
+ .probe = msm_ir_cut_platform_probe,
+ .driver = {
+ .name = "qcom,ir-cut",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ir_cut_dt_match,
+ },
+};
+
+static int __init msm_ir_cut_init_module(void)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+ rc = platform_driver_register(&msm_ir_cut_platform_driver);
+ if (!rc)
+ return rc;
+
+ pr_err("platform probe for ir_cut failed");
+
+ return rc;
+}
+
+static void __exit msm_ir_cut_exit_module(void)
+{
+ platform_driver_unregister(&msm_ir_cut_platform_driver);
+}
+
+static struct msm_ir_cut_table msm_gpio_ir_cut_table = {
+ .ir_cut_driver_type = IR_CUT_DRIVER_GPIO,
+ .func_tbl = {
+ .camera_ir_cut_init = msm_ir_cut_init,
+ .camera_ir_cut_release = msm_ir_cut_release,
+ .camera_ir_cut_off = msm_ir_cut_off,
+ .camera_ir_cut_on = msm_ir_cut_on,
+ },
+};
+
+module_init(msm_ir_cut_init_module);
+module_exit(msm_ir_cut_exit_module);
+MODULE_DESCRIPTION("MSM IR CUT");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h
new file mode 100644
index 000000000000..23fd23952523
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_cut/msm_ir_cut.h
@@ -0,0 +1,72 @@
+/* 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 MSM_IR_CUT_H
+#define MSM_IR_CUT_H
+
+#include <soc/qcom/camera2.h>
+#include "msm_camera_dt_util.h"
+#include "msm_camera_io_util.h"
+#include "msm_sd.h"
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+enum msm_camera_ir_cut_state_t {
+ MSM_CAMERA_IR_CUT_INIT,
+ MSM_CAMERA_IR_CUT_RELEASE,
+};
+
+enum msm_ir_cut_driver_type {
+ IR_CUT_DRIVER_GPIO,
+ IR_CUT_DRIVER_DEFAULT,
+};
+
+struct msm_ir_cut_ctrl_t;
+
+struct msm_ir_cut_func_t {
+ int32_t (*camera_ir_cut_init)(struct msm_ir_cut_ctrl_t *,
+ struct msm_ir_cut_cfg_data_t *);
+ int32_t (*camera_ir_cut_release)(struct msm_ir_cut_ctrl_t *);
+ int32_t (*camera_ir_cut_off)(struct msm_ir_cut_ctrl_t *,
+ struct msm_ir_cut_cfg_data_t *);
+ int32_t (*camera_ir_cut_on)(struct msm_ir_cut_ctrl_t *,
+ struct msm_ir_cut_cfg_data_t *);
+};
+
+struct msm_ir_cut_table {
+ enum msm_ir_cut_driver_type ir_cut_driver_type;
+ struct msm_ir_cut_func_t func_tbl;
+};
+
+struct msm_ir_cut_ctrl_t {
+ struct msm_sd_subdev msm_sd;
+ struct platform_device *pdev;
+ struct msm_ir_cut_func_t *func_tbl;
+ struct msm_camera_power_ctrl_t power_info;
+
+ enum msm_camera_device_type_t ir_cut_device_type;
+ struct mutex *ir_cut_mutex;
+
+ /* ir_cut driver type */
+ enum msm_ir_cut_driver_type ir_cut_driver_type;
+
+ /* ir_cut state */
+ enum msm_camera_ir_cut_state_t ir_cut_state;
+
+ struct msm_camera_gpio_conf *gconf;
+ struct msm_pinctrl_info pinctrl_info;
+ uint8_t cam_pinctrl_status;
+};
+
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_led/Makefile b/drivers/media/platform/msm/camera_v2/sensor/ir_led/Makefile
new file mode 100644
index 000000000000..6e99ecc2c78d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_led/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/platform/msm/camera_v2
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+obj-$(CONFIG_MSMB_CAMERA) += msm_ir_led.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c
new file mode 100644
index 000000000000..9af7abc5cd04
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.c
@@ -0,0 +1,462 @@
+/* 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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include "msm_ir_led.h"
+#include "msm_camera_dt_util.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+DEFINE_MSM_MUTEX(msm_ir_led_mutex);
+
+static struct v4l2_file_operations msm_ir_led_v4l2_subdev_fops;
+
+static const struct of_device_id msm_ir_led_dt_match[] = {
+ {.compatible = "qcom,ir-led", .data = NULL},
+ {}
+};
+
+static struct msm_ir_led_table msm_default_ir_led_table;
+
+static struct msm_ir_led_table *ir_led_table[] = {
+ &msm_default_ir_led_table,
+};
+
+static int32_t msm_ir_led_get_subdev_id(
+ struct msm_ir_led_ctrl_t *ir_led_ctrl, void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+
+ CDBG("Enter\n");
+ if (!subdev_id) {
+ pr_err("subdevice ID is not valid\n");
+ return -EINVAL;
+ }
+ if (ir_led_ctrl->ir_led_device_type != MSM_CAMERA_PLATFORM_DEVICE) {
+ pr_err("device type is not matching\n");
+ return -EINVAL;
+ }
+
+ *subdev_id = ir_led_ctrl->pdev->id;
+
+ CDBG("subdev_id %d\n", *subdev_id);
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_led_init(
+ struct msm_ir_led_ctrl_t *ir_led_ctrl,
+ struct msm_ir_led_cfg_data_t *ir_led_data)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_off(ir_led_ctrl, ir_led_data);
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+static int32_t msm_ir_led_release(
+ struct msm_ir_led_ctrl_t *ir_led_ctrl)
+{
+ int32_t rc = 0;
+
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_RELEASE) {
+ pr_err("Invalid ir_led state = %d\n",
+ ir_led_ctrl->ir_led_state);
+ return 0;
+ }
+
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_off(ir_led_ctrl, NULL);
+ if (rc < 0) {
+ pr_err("camera_ir_led_off failed (%d)\n", rc);
+ return rc;
+ }
+ ir_led_ctrl->ir_led_state = MSM_CAMERA_IR_LED_RELEASE;
+ return 0;
+}
+
+static int32_t msm_ir_led_off(struct msm_ir_led_ctrl_t *ir_led_ctrl,
+ struct msm_ir_led_cfg_data_t *ir_led_data)
+{
+ CDBG("Enter\n");
+
+ if (ir_led_ctrl->pwm_dev)
+ pwm_disable(ir_led_ctrl->pwm_dev);
+ else
+ pr_err("pwm device is null\n");
+
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_led_on(
+ struct msm_ir_led_ctrl_t *ir_led_ctrl,
+ struct msm_ir_led_cfg_data_t *ir_led_data)
+{
+ int rc;
+
+ CDBG("pwm duty on(ns) %d, pwm period(ns) %d\n",
+ ir_led_data->pwm_duty_on_ns, ir_led_data->pwm_period_ns);
+
+ if (ir_led_ctrl->pwm_dev) {
+ rc = pwm_config(ir_led_ctrl->pwm_dev,
+ ir_led_data->pwm_duty_on_ns,
+ ir_led_data->pwm_period_ns);
+ if (rc) {
+ pr_err("PWM config failed (%d)\n", rc);
+ return rc;
+ }
+
+ rc = pwm_enable(ir_led_ctrl->pwm_dev);
+ if (rc) {
+ pr_err("PWM enable failed(%d)\n", rc);
+ return rc;
+ }
+ } else
+ pr_err("pwm device is null\n");
+
+ return 0;
+}
+
+static int32_t msm_ir_led_handle_init(
+ struct msm_ir_led_ctrl_t *ir_led_ctrl,
+ struct msm_ir_led_cfg_data_t *ir_led_data)
+{
+ uint32_t i = 0;
+ int32_t rc = -EFAULT;
+ enum msm_ir_led_driver_type ir_led_driver_type =
+ ir_led_ctrl->ir_led_driver_type;
+
+ CDBG("Enter\n");
+
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT) {
+ pr_err("Invalid ir_led state = %d\n",
+ ir_led_ctrl->ir_led_state);
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ir_led_table); i++) {
+ if (ir_led_driver_type == ir_led_table[i]->ir_led_driver_type) {
+ ir_led_ctrl->func_tbl = &ir_led_table[i]->func_tbl;
+ rc = 0;
+ break;
+ }
+ }
+
+ if (rc < 0) {
+ pr_err("failed invalid ir_led_driver_type %d\n",
+ ir_led_driver_type);
+ return -EINVAL;
+ }
+
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_init(
+ ir_led_ctrl, ir_led_data);
+ if (rc < 0) {
+ pr_err("camera_ir_led_init failed (%d)\n", rc);
+ return rc;
+ }
+
+ ir_led_ctrl->ir_led_state = MSM_CAMERA_IR_LED_INIT;
+
+ CDBG("Exit\n");
+ return 0;
+}
+
+static int32_t msm_ir_led_config(struct msm_ir_led_ctrl_t *ir_led_ctrl,
+ void __user *argp)
+{
+ int32_t rc = -EINVAL;
+ struct msm_ir_led_cfg_data_t *ir_led_data =
+ (struct msm_ir_led_cfg_data_t *) argp;
+
+ mutex_lock(ir_led_ctrl->ir_led_mutex);
+
+ CDBG("type %d\n", ir_led_data->cfg_type);
+
+ switch (ir_led_data->cfg_type) {
+ case CFG_IR_LED_INIT:
+ rc = msm_ir_led_handle_init(ir_led_ctrl, ir_led_data);
+ break;
+ case CFG_IR_LED_RELEASE:
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT)
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_release(
+ ir_led_ctrl);
+ break;
+ case CFG_IR_LED_OFF:
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT)
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_off(
+ ir_led_ctrl, ir_led_data);
+ break;
+ case CFG_IR_LED_ON:
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT)
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_on(
+ ir_led_ctrl, ir_led_data);
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(ir_led_ctrl->ir_led_mutex);
+
+ CDBG("Exit: type %d\n", ir_led_data->cfg_type);
+
+ return rc;
+}
+
+static long msm_ir_led_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_ir_led_ctrl_t *fctrl = NULL;
+ void __user *argp = (void __user *)arg;
+
+ CDBG("Enter\n");
+
+ if (!sd) {
+ pr_err(" v4l2 ir led subdevice is NULL\n");
+ return -EINVAL;
+ }
+ fctrl = v4l2_get_subdevdata(sd);
+ if (!fctrl) {
+ pr_err("fctrl NULL\n");
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ return msm_ir_led_get_subdev_id(fctrl, argp);
+ case VIDIOC_MSM_IR_LED_CFG:
+ return msm_ir_led_config(fctrl, argp);
+ case MSM_SD_NOTIFY_FREEZE:
+ return 0;
+ case MSM_SD_SHUTDOWN:
+ if (!fctrl->func_tbl) {
+ pr_err("No call back funcions\n");
+ return -EINVAL;
+ } else {
+ return fctrl->func_tbl->camera_ir_led_release(fctrl);
+ }
+ default:
+ pr_err_ratelimited("invalid cmd %d\n", cmd);
+ return -ENOIOCTLCMD;
+ }
+ CDBG("Exit\n");
+}
+
+static struct v4l2_subdev_core_ops msm_ir_led_subdev_core_ops = {
+ .ioctl = msm_ir_led_subdev_ioctl,
+};
+
+static struct v4l2_subdev_ops msm_ir_led_subdev_ops = {
+ .core = &msm_ir_led_subdev_core_ops,
+};
+
+static int msm_ir_led_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh) {
+
+ int rc = 0;
+ struct msm_ir_led_ctrl_t *ir_led_ctrl = v4l2_get_subdevdata(sd);
+
+ if (!ir_led_ctrl) {
+ pr_err("v4l2 subdevice data read failed\n");
+ return -EINVAL;
+ }
+
+ CDBG("Enter\n");
+
+ if (ir_led_ctrl->ir_led_state == MSM_CAMERA_IR_LED_INIT)
+ rc = ir_led_ctrl->func_tbl->camera_ir_led_release(
+ ir_led_ctrl);
+
+ CDBG("Exit (%d)\n", rc);
+
+ return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_ir_led_internal_ops = {
+ .close = msm_ir_led_close,
+};
+
+static int32_t msm_ir_led_get_dt_data(struct device_node *of_node,
+ struct msm_ir_led_ctrl_t *fctrl)
+{
+ int32_t rc = 0;
+
+ CDBG("called\n");
+
+ /* Read the sub device */
+ rc = of_property_read_u32(of_node, "cell-index", &fctrl->pdev->id);
+ if (rc < 0) {
+ pr_err("reading cell-index for ir-led node is failed(rc) %d\n",
+ rc);
+ return rc;
+ }
+
+ fctrl->ir_led_driver_type = IR_LED_DRIVER_DEFAULT;
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long msm_ir_led_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct msm_ir_led_cfg_data_t32 *u32 =
+ (struct msm_ir_led_cfg_data_t32 *)arg;
+ struct msm_ir_led_cfg_data_t ir_led_data;
+
+ CDBG("Enter\n");
+ ir_led_data.cfg_type = u32->cfg_type;
+ ir_led_data.pwm_duty_on_ns = u32->pwm_duty_on_ns;
+ ir_led_data.pwm_period_ns = u32->pwm_period_ns;
+
+ switch (cmd) {
+ case VIDIOC_MSM_IR_LED_CFG32:
+ cmd = VIDIOC_MSM_IR_LED_CFG;
+ break;
+ default:
+ return msm_ir_led_subdev_ioctl(sd, cmd, arg);
+ }
+
+ rc = msm_ir_led_subdev_ioctl(sd, cmd, &ir_led_data);
+
+ CDBG("Exit\n");
+ return rc;
+}
+
+static long msm_ir_led_subdev_fops_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_ir_led_subdev_do_ioctl);
+}
+#endif
+
+static int32_t msm_ir_led_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ struct msm_ir_led_ctrl_t *ir_led_ctrl = NULL;
+
+ CDBG("Enter\n");
+ if (!pdev->dev.of_node) {
+ pr_err("IR LED device node is not present in device tree\n");
+ return -EINVAL;
+ }
+
+ ir_led_ctrl = devm_kzalloc(&pdev->dev, sizeof(struct msm_ir_led_ctrl_t),
+ GFP_KERNEL);
+ if (!ir_led_ctrl)
+ return -ENOMEM;
+
+ ir_led_ctrl->pdev = pdev;
+
+ /* Reading PWM device node */
+ ir_led_ctrl->pwm_dev = of_pwm_get(pdev->dev.of_node, NULL);
+
+ if (IS_ERR(ir_led_ctrl->pwm_dev)) {
+ rc = PTR_ERR(ir_led_ctrl->pwm_dev);
+ pr_err("Cannot get PWM device (%d)\n", rc);
+ ir_led_ctrl->pwm_dev = NULL;
+ }
+
+ rc = msm_ir_led_get_dt_data(pdev->dev.of_node, ir_led_ctrl);
+ if (rc < 0) {
+ pr_err("msm_ir_led_get_dt_data failed\n");
+ devm_kfree(&pdev->dev, ir_led_ctrl);
+ return -EINVAL;
+ }
+
+ ir_led_ctrl->ir_led_state = MSM_CAMERA_IR_LED_RELEASE;
+ ir_led_ctrl->power_info.dev = &ir_led_ctrl->pdev->dev;
+ ir_led_ctrl->ir_led_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+ ir_led_ctrl->ir_led_mutex = &msm_ir_led_mutex;
+
+ /* Initialize sub device */
+ v4l2_subdev_init(&ir_led_ctrl->msm_sd.sd, &msm_ir_led_subdev_ops);
+ v4l2_set_subdevdata(&ir_led_ctrl->msm_sd.sd, ir_led_ctrl);
+
+ ir_led_ctrl->msm_sd.sd.internal_ops = &msm_ir_led_internal_ops;
+ ir_led_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(ir_led_ctrl->msm_sd.sd.name,
+ ARRAY_SIZE(ir_led_ctrl->msm_sd.sd.name),
+ "msm_camera_ir_led");
+ media_entity_init(&ir_led_ctrl->msm_sd.sd.entity, 0, NULL, 0);
+ ir_led_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ ir_led_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_IR_LED;
+ ir_led_ctrl->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x1;
+ msm_sd_register(&ir_led_ctrl->msm_sd);
+
+ CDBG("ir_led sd name = %s\n",
+ ir_led_ctrl->msm_sd.sd.entity.name);
+ msm_ir_led_v4l2_subdev_fops = v4l2_subdev_fops;
+#ifdef CONFIG_COMPAT
+ msm_ir_led_v4l2_subdev_fops.compat_ioctl32 =
+ msm_ir_led_subdev_fops_ioctl;
+#endif
+ ir_led_ctrl->msm_sd.sd.devnode->fops = &msm_ir_led_v4l2_subdev_fops;
+
+ CDBG("probe success\n");
+ return rc;
+}
+
+MODULE_DEVICE_TABLE(of, msm_ir_led_dt_match);
+
+static struct platform_driver msm_ir_led_platform_driver = {
+ .probe = msm_ir_led_platform_probe,
+ .driver = {
+ .name = "qcom,ir-led",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ir_led_dt_match,
+ },
+};
+
+static int __init msm_ir_led_init_module(void)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+ rc = platform_driver_register(&msm_ir_led_platform_driver);
+ if (!rc)
+ return rc;
+
+ pr_err("ir-led driver register failed (%d)\n", rc);
+
+ return rc;
+}
+
+static void __exit msm_ir_led_exit_module(void)
+{
+ platform_driver_unregister(&msm_ir_led_platform_driver);
+}
+
+static struct msm_ir_led_table msm_default_ir_led_table = {
+ .ir_led_driver_type = IR_LED_DRIVER_DEFAULT,
+ .func_tbl = {
+ .camera_ir_led_init = msm_ir_led_init,
+ .camera_ir_led_release = msm_ir_led_release,
+ .camera_ir_led_off = msm_ir_led_off,
+ .camera_ir_led_on = msm_ir_led_on,
+ },
+};
+
+module_init(msm_ir_led_init_module);
+module_exit(msm_ir_led_exit_module);
+MODULE_DESCRIPTION("MSM IR LED");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h
new file mode 100644
index 000000000000..a0923ffc81da
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/ir_led/msm_ir_led.h
@@ -0,0 +1,71 @@
+/* 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 MSM_IR_LED_H
+#define MSM_IR_LED_H
+
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/msm_cam_sensor.h>
+#include <soc/qcom/camera2.h>
+#include "msm_sd.h"
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+enum msm_camera_ir_led_state_t {
+ MSM_CAMERA_IR_LED_INIT,
+ MSM_CAMERA_IR_LED_RELEASE,
+};
+
+enum msm_ir_led_driver_type {
+ IR_LED_DRIVER_GPIO,
+ IR_LED_DRIVER_DEFAULT,
+};
+
+struct msm_ir_led_ctrl_t;
+
+struct msm_ir_led_func_t {
+ int32_t (*camera_ir_led_init)(struct msm_ir_led_ctrl_t *,
+ struct msm_ir_led_cfg_data_t *);
+ int32_t (*camera_ir_led_release)(struct msm_ir_led_ctrl_t *);
+ int32_t (*camera_ir_led_off)(struct msm_ir_led_ctrl_t *,
+ struct msm_ir_led_cfg_data_t *);
+ int32_t (*camera_ir_led_on)(struct msm_ir_led_ctrl_t *,
+ struct msm_ir_led_cfg_data_t *);
+};
+
+struct msm_ir_led_table {
+ enum msm_ir_led_driver_type ir_led_driver_type;
+ struct msm_ir_led_func_t func_tbl;
+};
+
+struct msm_ir_led_ctrl_t {
+ struct msm_sd_subdev msm_sd;
+ struct platform_device *pdev;
+ struct pwm_device *pwm_dev;
+ struct msm_ir_led_func_t *func_tbl;
+ struct msm_camera_power_ctrl_t power_info;
+
+ enum msm_camera_device_type_t ir_led_device_type;
+ struct mutex *ir_led_mutex;
+
+ /* ir_led driver type */
+ enum msm_ir_led_driver_type ir_led_driver_type;
+
+ /* ir_led state */
+ enum msm_camera_ir_led_state_t ir_led_state;
+};
+
+#endif
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/dvb/Kconfig b/drivers/media/platform/msm/dvb/Kconfig
new file mode 100644
index 000000000000..e205c8172075
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/Kconfig
@@ -0,0 +1,10 @@
+config DVB_MPQ
+ tristate "Qualcomm Technologies Inc Multimedia Processor DVB Adapter"
+ depends on ARCH_QCOM && DVB_CORE
+ default n
+
+ help
+ Support for Qualcomm Technologies Inc MPQ based DVB adapter.
+ Say Y or M if you own such a device and want to use it.
+
+source "drivers/media/platform/msm/dvb/demux/Kconfig"
diff --git a/drivers/media/platform/msm/dvb/Makefile b/drivers/media/platform/msm/dvb/Makefile
new file mode 100644
index 000000000000..862ebca24db9
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_DVB_MPQ) += adapter/
+obj-$(CONFIG_DVB_MPQ_DEMUX) += demux/
diff --git a/drivers/media/platform/msm/dvb/adapter/Makefile b/drivers/media/platform/msm/dvb/adapter/Makefile
new file mode 100644
index 000000000000..f7da6b5b2f06
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/adapter/Makefile
@@ -0,0 +1,6 @@
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/platform/msm/dvb/include/
+
+obj-$(CONFIG_DVB_MPQ) += mpq-adapter.o
+
+mpq-adapter-y := mpq_adapter.o mpq_stream_buffer.o
diff --git a/drivers/media/platform/msm/dvb/adapter/mpq_adapter.c b/drivers/media/platform/msm/dvb/adapter/mpq_adapter.c
new file mode 100644
index 000000000000..a3d2533485b5
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/adapter/mpq_adapter.c
@@ -0,0 +1,211 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "mpq_adapter.h"
+#include "mpq_dvb_debug.h"
+
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* data-structure holding MPQ adapter information */
+static struct
+{
+ /* MPQ adapter registered to dvb-core */
+ struct dvb_adapter adapter;
+
+ /* mutex protect against the data-structure */
+ struct mutex mutex;
+
+ /* List of stream interfaces registered to the MPQ adapter */
+ struct {
+ /* pointer to the stream buffer using for data tunneling */
+ struct mpq_streambuffer *stream_buffer;
+
+ /* callback triggered when the stream interface is registered */
+ mpq_adapter_stream_if_callback callback;
+
+ /* parameter passed to the callback function */
+ void *user_param;
+ } interfaces[MPQ_ADAPTER_MAX_NUM_OF_INTERFACES];
+} mpq_info;
+
+
+/**
+ * Initialize MPQ DVB adapter module.
+ *
+ * Return error status
+ */
+static int __init mpq_adapter_init(void)
+{
+ int i;
+ int result;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ mutex_init(&mpq_info.mutex);
+
+ /* reset stream interfaces list */
+ for (i = 0; i < MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; i++) {
+ mpq_info.interfaces[i].stream_buffer = NULL;
+ mpq_info.interfaces[i].callback = NULL;
+ }
+
+ /* regsiter a new dvb-adapter to dvb-core */
+ result = dvb_register_adapter(&mpq_info.adapter,
+ "Qualcomm DVB adapter",
+ THIS_MODULE,
+ NULL,
+ adapter_nr);
+
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: dvb_register_adapter failed, errno %d\n",
+ __func__,
+ result);
+ }
+
+ return result;
+}
+
+
+/**
+ * Cleanup MPQ DVB adapter module.
+ */
+static void __exit mpq_adapter_exit(void)
+{
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ /* un-regsiter adapter from dvb-core */
+ dvb_unregister_adapter(&mpq_info.adapter);
+ mutex_destroy(&mpq_info.mutex);
+}
+
+struct dvb_adapter *mpq_adapter_get(void)
+{
+ return &mpq_info.adapter;
+}
+EXPORT_SYMBOL(mpq_adapter_get);
+
+
+int mpq_adapter_register_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ struct mpq_streambuffer *stream_buffer)
+{
+ int ret;
+
+ if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES) {
+ ret = -EINVAL;
+ goto register_failed;
+ }
+
+ if (mutex_lock_interruptible(&mpq_info.mutex)) {
+ ret = -ERESTARTSYS;
+ goto register_failed;
+ }
+
+ if (mpq_info.interfaces[interface_id].stream_buffer != NULL) {
+ /* already registered interface */
+ ret = -EINVAL;
+ goto register_failed_unlock_mutex;
+ }
+
+ mpq_info.interfaces[interface_id].stream_buffer = stream_buffer;
+ mutex_unlock(&mpq_info.mutex);
+
+ /*
+ * If callback is installed, trigger it to notify that
+ * stream interface was registered.
+ */
+ if (mpq_info.interfaces[interface_id].callback != NULL) {
+ mpq_info.interfaces[interface_id].callback(
+ interface_id,
+ mpq_info.interfaces[interface_id].user_param);
+ }
+
+ return 0;
+
+register_failed_unlock_mutex:
+ mutex_unlock(&mpq_info.mutex);
+register_failed:
+ return ret;
+}
+EXPORT_SYMBOL(mpq_adapter_register_stream_if);
+
+
+int mpq_adapter_unregister_stream_if(
+ enum mpq_adapter_stream_if interface_id)
+{
+ if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&mpq_info.mutex))
+ return -ERESTARTSYS;
+
+ /* clear the registered interface */
+ mpq_info.interfaces[interface_id].stream_buffer = NULL;
+
+ mutex_unlock(&mpq_info.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_unregister_stream_if);
+
+
+int mpq_adapter_get_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ struct mpq_streambuffer **stream_buffer)
+{
+ if ((interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES) ||
+ (stream_buffer == NULL))
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&mpq_info.mutex))
+ return -ERESTARTSYS;
+
+ *stream_buffer = mpq_info.interfaces[interface_id].stream_buffer;
+
+ mutex_unlock(&mpq_info.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_get_stream_if);
+
+
+int mpq_adapter_notify_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ mpq_adapter_stream_if_callback callback,
+ void *user_param)
+{
+ if (interface_id >= MPQ_ADAPTER_MAX_NUM_OF_INTERFACES)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&mpq_info.mutex))
+ return -ERESTARTSYS;
+
+ mpq_info.interfaces[interface_id].callback = callback;
+ mpq_info.interfaces[interface_id].user_param = user_param;
+
+ mutex_unlock(&mpq_info.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_adapter_notify_stream_if);
+
+
+module_init(mpq_adapter_init);
+module_exit(mpq_adapter_exit);
+
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. MPQ adapter");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c b/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c
new file mode 100644
index 000000000000..97533081766a
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c
@@ -0,0 +1,827 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_stream_buffer.h"
+
+
+int mpq_streambuffer_init(
+ struct mpq_streambuffer *sbuff,
+ enum mpq_streambuffer_mode mode,
+ struct mpq_streambuffer_buffer_desc *data_buffers,
+ u32 data_buff_num,
+ void *packet_buff,
+ size_t packet_buff_size)
+{
+ if ((sbuff == NULL) || (data_buffers == NULL) ||
+ (packet_buff == NULL) || (data_buff_num == 0))
+ return -EINVAL;
+
+ if (data_buff_num > 1) {
+ if (mode != MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR)
+ return -EINVAL;
+ /* Linear buffer group */
+ dvb_ringbuffer_init(
+ &sbuff->raw_data,
+ data_buffers,
+ data_buff_num *
+ sizeof(struct mpq_streambuffer_buffer_desc));
+ } else {
+ if (mode != MPQ_STREAMBUFFER_BUFFER_MODE_RING)
+ return -EINVAL;
+ /* Single ring-buffer */
+ dvb_ringbuffer_init(&sbuff->raw_data,
+ data_buffers[0].base, data_buffers[0].size);
+ }
+ sbuff->mode = mode;
+ sbuff->buffers = data_buffers;
+ sbuff->pending_buffers_count = 0;
+ sbuff->buffers_num = data_buff_num;
+ sbuff->cb = NULL;
+ dvb_ringbuffer_init(&sbuff->packet_data, packet_buff, packet_buff_size);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_init);
+
+void mpq_streambuffer_terminate(struct mpq_streambuffer *sbuff)
+{
+ spin_lock(&sbuff->packet_data.lock);
+ spin_lock(&sbuff->raw_data.lock);
+ sbuff->packet_data.error = -ENODEV;
+ sbuff->raw_data.error = -ENODEV;
+ spin_unlock(&sbuff->raw_data.lock);
+ spin_unlock(&sbuff->packet_data.lock);
+
+ wake_up_all(&sbuff->raw_data.queue);
+ wake_up_all(&sbuff->packet_data.queue);
+}
+EXPORT_SYMBOL(mpq_streambuffer_terminate);
+
+ssize_t mpq_streambuffer_pkt_next(
+ struct mpq_streambuffer *sbuff,
+ ssize_t idx, size_t *pktlen)
+{
+ ssize_t packet_idx;
+
+ spin_lock(&sbuff->packet_data.lock);
+
+ /* buffer was released, return no packet available */
+ if (sbuff->packet_data.error == -ENODEV) {
+ spin_unlock(&sbuff->packet_data.lock);
+ return -ENODEV;
+ }
+
+ packet_idx = dvb_ringbuffer_pkt_next(&sbuff->packet_data, idx, pktlen);
+ spin_unlock(&sbuff->packet_data.lock);
+
+ return packet_idx;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_next);
+
+
+ssize_t mpq_streambuffer_pkt_read(
+ struct mpq_streambuffer *sbuff,
+ size_t idx,
+ struct mpq_streambuffer_packet_header *packet,
+ u8 *user_data)
+{
+ size_t ret;
+ size_t read_len;
+
+ spin_lock(&sbuff->packet_data.lock);
+
+ /* buffer was released, return no packet available */
+ if (sbuff->packet_data.error == -ENODEV) {
+ spin_unlock(&sbuff->packet_data.lock);
+ return -ENODEV;
+ }
+
+ /* read-out the packet header first */
+ ret = dvb_ringbuffer_pkt_read(
+ &sbuff->packet_data, idx, 0,
+ (u8 *)packet,
+ sizeof(struct mpq_streambuffer_packet_header));
+
+ /* verify length, at least packet header should exist */
+ if (ret != sizeof(struct mpq_streambuffer_packet_header)) {
+ spin_unlock(&sbuff->packet_data.lock);
+ return -EINVAL;
+ }
+
+ read_len = ret;
+
+ /* read-out private user-data if there are such */
+ if ((packet->user_data_len) && (user_data != NULL)) {
+ ret = dvb_ringbuffer_pkt_read(
+ &sbuff->packet_data,
+ idx,
+ sizeof(struct mpq_streambuffer_packet_header),
+ user_data,
+ packet->user_data_len);
+
+ if (ret < 0) {
+ spin_unlock(&sbuff->packet_data.lock);
+ return ret;
+ }
+
+ read_len += ret;
+ }
+
+ spin_unlock(&sbuff->packet_data.lock);
+
+ return read_len;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_read);
+
+
+int mpq_streambuffer_pkt_dispose(
+ struct mpq_streambuffer *sbuff,
+ size_t idx,
+ int dispose_data)
+{
+ int ret;
+ struct mpq_streambuffer_packet_header packet;
+
+ if (sbuff == NULL)
+ return -EINVAL;
+
+ spin_lock(&sbuff->packet_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->packet_data.error == -ENODEV) {
+ spin_unlock(&sbuff->packet_data.lock);
+ return -ENODEV;
+ }
+
+ /* read-out the packet header first */
+ ret = dvb_ringbuffer_pkt_read(&sbuff->packet_data, idx,
+ 0,
+ (u8 *)&packet,
+ sizeof(struct mpq_streambuffer_packet_header));
+
+ spin_unlock(&sbuff->packet_data.lock);
+
+ if (ret != sizeof(struct mpq_streambuffer_packet_header))
+ return -EINVAL;
+
+ if ((sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) ||
+ (dispose_data)) {
+ /* Advance the read pointer in the raw-data buffer first */
+ ret = mpq_streambuffer_data_read_dispose(sbuff,
+ packet.raw_data_len);
+ if (ret != 0)
+ return ret;
+ }
+
+ spin_lock(&sbuff->packet_data.lock);
+ spin_lock(&sbuff->raw_data.lock);
+
+ /* check if buffer was released */
+ if ((sbuff->packet_data.error == -ENODEV) ||
+ (sbuff->raw_data.error == -ENODEV)) {
+ spin_unlock(&sbuff->raw_data.lock);
+ spin_unlock(&sbuff->packet_data.lock);
+ return -ENODEV;
+ }
+
+ /* Move read pointer to the next linear buffer for subsequent reads */
+ if ((sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) &&
+ (packet.raw_data_len > 0)) {
+ struct mpq_streambuffer_buffer_desc *desc;
+
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pread];
+
+ desc->write_ptr = 0;
+ desc->read_ptr = 0;
+
+ DVB_RINGBUFFER_SKIP(&sbuff->raw_data,
+ sizeof(struct mpq_streambuffer_buffer_desc));
+ sbuff->pending_buffers_count--;
+
+ wake_up_all(&sbuff->raw_data.queue);
+ }
+
+ /* Now clear the packet from the packet header */
+ dvb_ringbuffer_pkt_dispose(&sbuff->packet_data, idx);
+
+ spin_unlock(&sbuff->raw_data.lock);
+ spin_unlock(&sbuff->packet_data.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_dispose);
+
+int mpq_streambuffer_pkt_write(
+ struct mpq_streambuffer *sbuff,
+ struct mpq_streambuffer_packet_header *packet,
+ u8 *user_data)
+{
+ ssize_t idx;
+ size_t len;
+
+ if ((sbuff == NULL) || (packet == NULL))
+ return -EINVAL;
+
+ spin_lock(&sbuff->packet_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->packet_data.error == -ENODEV) {
+ spin_unlock(&sbuff->packet_data.lock);
+ return -ENODEV;
+ }
+
+ /* Make sure we can go to the next linear buffer */
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR &&
+ sbuff->pending_buffers_count == sbuff->buffers_num &&
+ packet->raw_data_len) {
+ spin_unlock(&sbuff->packet_data.lock);
+ return -ENOSPC;
+ }
+
+ len = sizeof(struct mpq_streambuffer_packet_header) +
+ packet->user_data_len;
+
+ /* Make sure enough space available for packet header */
+ if (dvb_ringbuffer_free(&sbuff->packet_data) <
+ (len + DVB_RINGBUFFER_PKTHDRSIZE)) {
+ spin_unlock(&sbuff->packet_data.lock);
+ return -ENOSPC;
+ }
+
+ /* Starting writing packet header */
+ idx = dvb_ringbuffer_pkt_start(&sbuff->packet_data, len);
+
+ /* Write non-user private data header */
+ dvb_ringbuffer_write(&sbuff->packet_data,
+ (u8 *)packet,
+ sizeof(struct mpq_streambuffer_packet_header));
+
+ /* Write user's own private data header */
+ dvb_ringbuffer_write(&sbuff->packet_data,
+ user_data,
+ packet->user_data_len);
+
+ dvb_ringbuffer_pkt_close(&sbuff->packet_data, idx);
+
+ /* Move write pointer to next linear buffer for subsequent writes */
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR &&
+ packet->raw_data_len) {
+ DVB_RINGBUFFER_PUSH(&sbuff->raw_data,
+ sizeof(struct mpq_streambuffer_buffer_desc));
+ sbuff->pending_buffers_count++;
+ }
+
+ spin_unlock(&sbuff->packet_data.lock);
+ wake_up_all(&sbuff->packet_data.queue);
+
+ return idx;
+}
+EXPORT_SYMBOL(mpq_streambuffer_pkt_write);
+
+ssize_t mpq_streambuffer_data_write(
+ struct mpq_streambuffer *sbuff,
+ const u8 *buf, size_t len)
+{
+ int res;
+
+ if ((sbuff == NULL) || (buf == NULL))
+ return -EINVAL;
+
+ spin_lock(&sbuff->raw_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->raw_data.error == -ENODEV) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENODEV;
+ }
+
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_RING) {
+ if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len)) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENOSPC;
+ }
+ /*
+ * Secure buffers are not permitted to be mapped into kernel
+ * memory, and so buffer base address may be NULL
+ */
+ if (sbuff->raw_data.data == NULL) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -EPERM;
+ }
+ res = dvb_ringbuffer_write(&sbuff->raw_data, buf, len);
+ wake_up_all(&sbuff->raw_data.queue);
+ } else {
+ /* Linear buffer group */
+ struct mpq_streambuffer_buffer_desc *desc;
+
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pwrite];
+
+ /*
+ * Secure buffers are not permitted to be mapped into kernel
+ * memory, and so buffer base address may be NULL
+ */
+ if (desc->base == NULL) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -EPERM;
+ }
+
+ if ((sbuff->pending_buffers_count == sbuff->buffers_num) ||
+ ((desc->size - desc->write_ptr) < len)) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: No space available! %d pending buffers out of %d total buffers. write_ptr=%d, size=%d\n",
+ __func__,
+ sbuff->pending_buffers_count,
+ sbuff->buffers_num,
+ desc->write_ptr,
+ desc->size);
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENOSPC;
+ }
+ memcpy(desc->base + desc->write_ptr, buf, len);
+ desc->write_ptr += len;
+ res = len;
+ }
+
+ spin_unlock(&sbuff->raw_data.lock);
+ return res;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_write);
+
+
+int mpq_streambuffer_data_write_deposit(
+ struct mpq_streambuffer *sbuff,
+ size_t len)
+{
+ if (sbuff == NULL)
+ return -EINVAL;
+
+ spin_lock(&sbuff->raw_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->raw_data.error == -ENODEV) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENODEV;
+ }
+
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_RING) {
+ if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len)) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENOSPC;
+ }
+
+ DVB_RINGBUFFER_PUSH(&sbuff->raw_data, len);
+ wake_up_all(&sbuff->raw_data.queue);
+ } else {
+ /* Linear buffer group */
+ struct mpq_streambuffer_buffer_desc *desc =
+ (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pwrite];
+
+ if ((sbuff->pending_buffers_count == sbuff->buffers_num) ||
+ ((desc->size - desc->write_ptr) < len)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: No space available!\n",
+ __func__);
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENOSPC;
+ }
+ desc->write_ptr += len;
+ }
+
+ spin_unlock(&sbuff->raw_data.lock);
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_write_deposit);
+
+
+ssize_t mpq_streambuffer_data_read(
+ struct mpq_streambuffer *sbuff,
+ u8 *buf, size_t len)
+{
+ ssize_t actual_len = 0;
+ u32 offset;
+
+ if ((sbuff == NULL) || (buf == NULL))
+ return -EINVAL;
+
+ spin_lock(&sbuff->raw_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->raw_data.error == -ENODEV) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENODEV;
+ }
+
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_RING) {
+ /*
+ * Secure buffers are not permitted to be mapped into kernel
+ * memory, and so buffer base address may be NULL
+ */
+ if (sbuff->raw_data.data == NULL) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -EPERM;
+ }
+
+ offset = sbuff->raw_data.pread;
+ actual_len = dvb_ringbuffer_avail(&sbuff->raw_data);
+ if (actual_len < len)
+ len = actual_len;
+ if (len)
+ dvb_ringbuffer_read(&sbuff->raw_data, buf, len);
+
+ wake_up_all(&sbuff->raw_data.queue);
+ } else {
+ /* Linear buffer group */
+ struct mpq_streambuffer_buffer_desc *desc;
+
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pread];
+
+ /*
+ * Secure buffers are not permitted to be mapped into kernel
+ * memory, and so buffer base address may be NULL
+ */
+ if (desc->base == NULL) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -EPERM;
+ }
+
+ actual_len = (desc->write_ptr - desc->read_ptr);
+ if (actual_len < len)
+ len = actual_len;
+ memcpy(buf, desc->base + desc->read_ptr, len);
+ offset = desc->read_ptr;
+ desc->read_ptr += len;
+ }
+
+ spin_unlock(&sbuff->raw_data.lock);
+
+ if (sbuff->cb)
+ sbuff->cb(sbuff, offset, len, sbuff->cb_user_data);
+
+ return len;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_read);
+
+
+ssize_t mpq_streambuffer_data_read_user(
+ struct mpq_streambuffer *sbuff,
+ u8 __user *buf, size_t len)
+{
+ ssize_t actual_len = 0;
+ u32 offset;
+
+ if ((sbuff == NULL) || (buf == NULL))
+ return -EINVAL;
+
+ /* check if buffer was released */
+ if (sbuff->raw_data.error == -ENODEV)
+ return -ENODEV;
+
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_RING) {
+ /*
+ * Secure buffers are not permitted to be mapped into kernel
+ * memory, and so buffer base address may be NULL
+ */
+ if (sbuff->raw_data.data == NULL)
+ return -EPERM;
+
+ offset = sbuff->raw_data.pread;
+ actual_len = dvb_ringbuffer_avail(&sbuff->raw_data);
+ if (actual_len < len)
+ len = actual_len;
+ if (len)
+ dvb_ringbuffer_read_user(&sbuff->raw_data, buf, len);
+ wake_up_all(&sbuff->raw_data.queue);
+ } else {
+ /* Linear buffer group */
+ struct mpq_streambuffer_buffer_desc *desc;
+
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pread];
+
+ /*
+ * Secure buffers are not permitted to be mapped into kernel
+ * memory, and so buffer base address may be NULL
+ */
+ if (desc->base == NULL)
+ return -EPERM;
+
+ actual_len = (desc->write_ptr - desc->read_ptr);
+ if (actual_len < len)
+ len = actual_len;
+ if (copy_to_user(buf, desc->base + desc->read_ptr, len))
+ return -EFAULT;
+
+ offset = desc->read_ptr;
+ desc->read_ptr += len;
+ }
+
+ if (sbuff->cb)
+ sbuff->cb(sbuff, offset, len, sbuff->cb_user_data);
+
+ return len;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_read_user);
+
+int mpq_streambuffer_data_read_dispose(
+ struct mpq_streambuffer *sbuff,
+ size_t len)
+{
+ u32 offset;
+
+ if (sbuff == NULL)
+ return -EINVAL;
+
+ spin_lock(&sbuff->raw_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->raw_data.error == -ENODEV) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENODEV;
+ }
+
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_RING) {
+ if (unlikely(dvb_ringbuffer_avail(&sbuff->raw_data) < len)) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -EINVAL;
+ }
+
+ offset = sbuff->raw_data.pread;
+ DVB_RINGBUFFER_SKIP(&sbuff->raw_data, len);
+ wake_up_all(&sbuff->raw_data.queue);
+ } else {
+ struct mpq_streambuffer_buffer_desc *desc;
+
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pread];
+ offset = desc->read_ptr;
+
+ if ((desc->read_ptr + len) > desc->size)
+ desc->read_ptr = desc->size;
+ else
+ desc->read_ptr += len;
+ }
+
+ spin_unlock(&sbuff->raw_data.lock);
+
+ if (sbuff->cb)
+ sbuff->cb(sbuff, offset, len, sbuff->cb_user_data);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_read_dispose);
+
+
+int mpq_streambuffer_get_buffer_handle(
+ struct mpq_streambuffer *sbuff,
+ int read_buffer,
+ int *handle)
+{
+ struct mpq_streambuffer_buffer_desc *desc = NULL;
+
+ if ((sbuff == NULL) || (handle == NULL))
+ return -EINVAL;
+
+ spin_lock(&sbuff->raw_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->raw_data.error == -ENODEV) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENODEV;
+ }
+
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_RING) {
+ *handle = sbuff->buffers[0].handle;
+ } else {
+ if (read_buffer)
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pread];
+ else
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pwrite];
+ *handle = desc->handle;
+ }
+
+ spin_unlock(&sbuff->raw_data.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_get_buffer_handle);
+
+
+int mpq_streambuffer_register_data_dispose(
+ struct mpq_streambuffer *sbuff,
+ mpq_streambuffer_dispose_cb cb_func,
+ void *user_data)
+{
+ if ((sbuff == NULL) || (cb_func == NULL))
+ return -EINVAL;
+
+ sbuff->cb = cb_func;
+ sbuff->cb_user_data = user_data;
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_register_data_dispose);
+
+
+ssize_t mpq_streambuffer_data_free(
+ struct mpq_streambuffer *sbuff)
+{
+ struct mpq_streambuffer_buffer_desc *desc;
+
+ if (sbuff == NULL)
+ return -EINVAL;
+
+ spin_lock(&sbuff->raw_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->raw_data.error == -ENODEV) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENODEV;
+ }
+
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_RING) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return dvb_ringbuffer_free(&sbuff->raw_data);
+ }
+
+ if (sbuff->pending_buffers_count == sbuff->buffers_num) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return 0;
+ }
+
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pwrite];
+
+ spin_unlock(&sbuff->raw_data.lock);
+
+ return desc->size - desc->write_ptr;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_free);
+
+
+ssize_t mpq_streambuffer_data_avail(
+ struct mpq_streambuffer *sbuff)
+{
+ struct mpq_streambuffer_buffer_desc *desc;
+
+ if (sbuff == NULL)
+ return -EINVAL;
+
+ spin_lock(&sbuff->raw_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->raw_data.error == -ENODEV) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENODEV;
+ }
+
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_RING) {
+ ssize_t avail = dvb_ringbuffer_avail(&sbuff->raw_data);
+
+ spin_unlock(&sbuff->raw_data.lock);
+ return avail;
+ }
+
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pread];
+
+ spin_unlock(&sbuff->raw_data.lock);
+
+ return desc->write_ptr - desc->read_ptr;
+}
+EXPORT_SYMBOL(mpq_streambuffer_data_avail);
+
+int mpq_streambuffer_get_data_rw_offset(
+ struct mpq_streambuffer *sbuff,
+ u32 *read_offset,
+ u32 *write_offset)
+{
+ if (sbuff == NULL)
+ return -EINVAL;
+
+ spin_lock(&sbuff->raw_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->raw_data.error == -ENODEV) {
+ spin_unlock(&sbuff->raw_data.lock);
+ return -ENODEV;
+ }
+
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_RING) {
+ if (read_offset)
+ *read_offset = sbuff->raw_data.pread;
+ if (write_offset)
+ *write_offset = sbuff->raw_data.pwrite;
+ } else {
+ struct mpq_streambuffer_buffer_desc *desc;
+
+ if (read_offset) {
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pread];
+ *read_offset = desc->read_ptr;
+ }
+ if (write_offset) {
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pwrite];
+ *write_offset = desc->write_ptr;
+ }
+ }
+
+ spin_unlock(&sbuff->raw_data.lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_get_data_rw_offset);
+
+ssize_t mpq_streambuffer_metadata_free(struct mpq_streambuffer *sbuff)
+{
+ ssize_t free;
+
+ if (sbuff == NULL)
+ return -EINVAL;
+
+ spin_lock(&sbuff->packet_data.lock);
+
+ /* check if buffer was released */
+ if (sbuff->packet_data.error == -ENODEV) {
+ spin_unlock(&sbuff->packet_data.lock);
+ return -ENODEV;
+ }
+
+ free = dvb_ringbuffer_free(&sbuff->packet_data);
+
+ spin_unlock(&sbuff->packet_data.lock);
+
+ return free;
+}
+EXPORT_SYMBOL(mpq_streambuffer_metadata_free);
+
+int mpq_streambuffer_flush(struct mpq_streambuffer *sbuff)
+{
+ struct mpq_streambuffer_buffer_desc *desc;
+ size_t len;
+ int idx;
+ int ret = 0;
+
+ if (sbuff == NULL)
+ return -EINVAL;
+
+ spin_lock(&sbuff->packet_data.lock);
+ spin_lock(&sbuff->raw_data.lock);
+
+ /* Check if buffer was released */
+ if (sbuff->packet_data.error == -ENODEV ||
+ sbuff->raw_data.error == -ENODEV) {
+ ret = -ENODEV;
+ goto end;
+ }
+
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR)
+ while (sbuff->pending_buffers_count) {
+ desc = (struct mpq_streambuffer_buffer_desc *)
+ &sbuff->raw_data.data[sbuff->raw_data.pread];
+ desc->write_ptr = 0;
+ desc->read_ptr = 0;
+ DVB_RINGBUFFER_SKIP(&sbuff->raw_data,
+ sizeof(struct mpq_streambuffer_buffer_desc));
+ sbuff->pending_buffers_count--;
+ }
+ else
+ dvb_ringbuffer_flush(&sbuff->raw_data);
+
+ /*
+ * Dispose all packets (simply flushing is not enough since we want
+ * the packets' status to move to disposed).
+ */
+ do {
+ idx = dvb_ringbuffer_pkt_next(&sbuff->packet_data, -1, &len);
+ if (idx >= 0)
+ dvb_ringbuffer_pkt_dispose(&sbuff->packet_data, idx);
+ } while (idx >= 0);
+
+end:
+ spin_unlock(&sbuff->raw_data.lock);
+ spin_unlock(&sbuff->packet_data.lock);
+ return ret;
+}
+EXPORT_SYMBOL(mpq_streambuffer_flush);
diff --git a/drivers/media/platform/msm/dvb/demux/Kconfig b/drivers/media/platform/msm/dvb/demux/Kconfig
new file mode 100644
index 000000000000..319e2ab2eb96
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/demux/Kconfig
@@ -0,0 +1,46 @@
+config DVB_MPQ_DEMUX
+ tristate "DVB Demux Device"
+ depends on DVB_MPQ && ION && ION_MSM
+ default n
+
+ help
+ Support for Qualcomm Technologies Inc based dvb demux device.
+ Say Y or M if you own such a device and want to use it.
+ The Demux device is used to stream playback either
+ from TSIF interface or from DVR interface.
+
+config DVB_MPQ_NUM_DMX_DEVICES
+ int "Number of demux devices"
+ depends on DVB_MPQ_DEMUX
+ default 4
+ range 1 255
+
+ help
+ Configure number of demux devices.
+ Depends on your use-cases for maximum concurrent stream playback.
+
+choice
+ prompt "Demux Hardware Plugin"
+ depends on DVB_MPQ_DEMUX
+ default DVB_MPQ_TSPP1
+ help
+ Enable support of specific demux HW plugin depending on the existing HW support.
+ Depending on the enabled HW, demux may take advantage of HW capbailities
+ to perform some tasks in HW instead of SW.
+
+ config DVB_MPQ_TSPP1
+ bool "TSPPv1 plugin"
+ depends on TSPP
+ help
+ Use this option if your HW has
+ Transport Stream Packet Processor(TSPP) version1 support.
+ Demux may take adavantage of HW capabilities to perform
+ some tasks in HW instead of SW.
+
+ config DVB_MPQ_SW
+ bool "Software plugin"
+ help
+ Use this option if your HW does not have any
+ TSPP hardware support. All demux tasks will be
+ performed in SW.
+endchoice
diff --git a/drivers/media/platform/msm/dvb/demux/Makefile b/drivers/media/platform/msm/dvb/demux/Makefile
new file mode 100644
index 000000000000..c08fa85a8d5d
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/demux/Makefile
@@ -0,0 +1,14 @@
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/platform/msm/dvb/include/
+ccflags-y += -Idrivers/misc/
+
+obj-$(CONFIG_DVB_MPQ_DEMUX) += mpq-dmx-hw-plugin.o
+
+mpq-dmx-hw-plugin-y := mpq_dmx_plugin_common.o
+
+mpq-dmx-hw-plugin-$(CONFIG_QSEECOM) += mpq_sdmx.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSPP1) += mpq_dmx_plugin_tspp_v1.o
+
+mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_SW) += mpq_dmx_plugin_sw.o
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
new file mode 100644
index 000000000000..d1e3c090c972
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -0,0 +1,5195 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/file.h>
+#include <linux/scatterlist.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+#include "mpq_sdmx.h"
+
+#define SDMX_MAJOR_VERSION_MATCH (8)
+
+/* Length of mandatory fields that must exist in header of video PES */
+#define PES_MANDATORY_FIELDS_LEN 9
+
+/* Index of first byte in TS packet holding STC */
+#define STC_LOCATION_IDX 188
+
+#define MAX_PES_LENGTH (SZ_64K)
+
+#define MAX_TS_PACKETS_FOR_SDMX_PROCESS (500)
+
+/*
+ * PES header length field is 8 bits so PES header length after this field
+ * can be up to 256 bytes.
+ * Preceding fields of the PES header total to 9 bytes
+ * (including the PES header length field).
+ */
+#define MAX_PES_HEADER_LENGTH (256 + PES_MANDATORY_FIELDS_LEN)
+
+/* TS packet with adaptation field only can take up the entire TSP */
+#define MAX_TSP_ADAPTATION_LENGTH (184)
+
+#define MAX_SDMX_METADATA_LENGTH \
+ (TS_PACKET_HEADER_LENGTH + \
+ MAX_TSP_ADAPTATION_LENGTH + \
+ MAX_PES_HEADER_LENGTH)
+
+#define SDMX_METADATA_BUFFER_SIZE (64*1024)
+#define SDMX_SECTION_BUFFER_SIZE (64*1024)
+#define SDMX_PCR_BUFFER_SIZE (64*1024)
+
+/* Number of demux devices, has default of linux configuration */
+static int mpq_demux_device_num = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+module_param(mpq_demux_device_num, int, S_IRUGO);
+
+/* ION heap IDs used for allocating video output buffer */
+static int video_secure_ion_heap = ION_CP_MM_HEAP_ID;
+module_param(video_secure_ion_heap, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(video_secure_ion_heap, "ION heap for secure video buffer allocation");
+
+static int video_nonsecure_ion_heap = ION_IOMMU_HEAP_ID;
+module_param(video_nonsecure_ion_heap, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(video_nonsecure_ion_heap, "ION heap for non-secure video buffer allocation");
+
+/* Value of TS packet scramble bits field for even key */
+static int mpq_sdmx_scramble_even = 0x2;
+module_param(mpq_sdmx_scramble_even, int, S_IRUGO | S_IWUSR);
+
+/* Value of TS packet scramble bits field for odd key */
+static int mpq_sdmx_scramble_odd = 0x3;
+module_param(mpq_sdmx_scramble_odd, int, S_IRUGO | S_IWUSR);
+
+/*
+ * Default action (discard or pass) taken when scramble bit is not one of the
+ * pass-through / odd / even values.
+ * When set packets will be discarded, otherwise passed through.
+ */
+static int mpq_sdmx_scramble_default_discard = 1;
+module_param(mpq_sdmx_scramble_default_discard, int, S_IRUGO | S_IWUSR);
+
+/* Max number of TS packets allowed as input for a single sdmx process */
+static int mpq_sdmx_proc_limit = MAX_TS_PACKETS_FOR_SDMX_PROCESS;
+module_param(mpq_sdmx_proc_limit, int, S_IRUGO | S_IWUSR);
+
+/* Debug flag for secure demux process */
+static int mpq_sdmx_debug;
+module_param(mpq_sdmx_debug, int, S_IRUGO | S_IWUSR);
+
+/*
+ * Indicates whether the demux should search for frame boundaries
+ * and notify on video packets on frame-basis or whether to provide
+ * only video PES packet payloads as-is.
+ */
+static int video_framing = 1;
+module_param(video_framing, int, S_IRUGO | S_IWUSR);
+
+/* TSIF operation mode: 1 = TSIF_MODE_1, 2 = TSIF_MODE_2, 3 = TSIF_LOOPBACK */
+static int tsif_mode = 2;
+module_param(tsif_mode, int, S_IRUGO | S_IWUSR);
+
+/* Inverse TSIF clock signal */
+static int clock_inv;
+module_param(clock_inv, int, S_IRUGO | S_IWUSR);
+
+/* Global data-structure for managing demux devices */
+static struct
+{
+ /* ION demux client used for memory allocation */
+ struct ion_client *ion_client;
+
+ /* demux devices array */
+ struct mpq_demux *devices;
+
+ /* Stream buffers objects used for tunneling to decoders */
+ struct mpq_streambuffer
+ decoder_buffers[MPQ_ADAPTER_MAX_NUM_OF_INTERFACES];
+
+ /* Indicates whether secure demux TZ application is available */
+ int secure_demux_app_loaded;
+} mpq_dmx_info;
+
+
+int mpq_dmx_get_param_scramble_odd(void)
+{
+ return mpq_sdmx_scramble_odd;
+}
+
+int mpq_dmx_get_param_scramble_even(void)
+{
+ return mpq_sdmx_scramble_even;
+}
+
+int mpq_dmx_get_param_scramble_default_discard(void)
+{
+ return mpq_sdmx_scramble_default_discard;
+}
+
+int mpq_dmx_get_param_tsif_mode(void)
+{
+ return tsif_mode;
+}
+
+int mpq_dmx_get_param_clock_inv(void)
+{
+ return clock_inv;
+}
+
+/* Check that PES header is valid and that it is a video PES */
+static int mpq_dmx_is_valid_video_pes(struct pes_packet_header *pes_header)
+{
+ /* start-code valid? */
+ if ((pes_header->packet_start_code_prefix_1 != 0) ||
+ (pes_header->packet_start_code_prefix_2 != 0) ||
+ (pes_header->packet_start_code_prefix_3 != 1))
+ return -EINVAL;
+
+ /* stream_id is video? */
+ if ((pes_header->stream_id & 0xF0) != 0xE0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Check if a framing pattern is a video frame pattern or a header pattern */
+static inline int mpq_dmx_is_video_frame(
+ enum dmx_video_codec codec,
+ u64 pattern_type)
+{
+ switch (codec) {
+ case DMX_VIDEO_CODEC_MPEG2:
+ if ((pattern_type == DMX_IDX_MPEG_I_FRAME_START) ||
+ (pattern_type == DMX_IDX_MPEG_P_FRAME_START) ||
+ (pattern_type == DMX_IDX_MPEG_B_FRAME_START))
+ return 1;
+ return 0;
+
+ case DMX_VIDEO_CODEC_H264:
+ if ((pattern_type == DMX_IDX_H264_IDR_START) ||
+ (pattern_type == DMX_IDX_H264_NON_IDR_START))
+ return 1;
+ return 0;
+
+ case DMX_VIDEO_CODEC_VC1:
+ if (pattern_type == DMX_IDX_VC1_FRAME_START)
+ return 1;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * mpq_dmx_get_pattern_params - Returns the required video
+ * patterns for framing operation based on video codec.
+ *
+ * @video_codec: the video codec.
+ * @patterns: a pointer to the pattern parameters, updated by this function.
+ * @patterns_num: number of patterns, updated by this function.
+ */
+static inline int mpq_dmx_get_pattern_params(
+ enum dmx_video_codec video_codec,
+ const struct dvb_dmx_video_patterns
+ *patterns[DVB_DMX_MAX_SEARCH_PATTERN_NUM],
+ int *patterns_num)
+{
+ switch (video_codec) {
+ case DMX_VIDEO_CODEC_MPEG2:
+ patterns[0] = dvb_dmx_get_pattern(DMX_IDX_MPEG_SEQ_HEADER);
+ patterns[1] = dvb_dmx_get_pattern(DMX_IDX_MPEG_GOP);
+ patterns[2] = dvb_dmx_get_pattern(DMX_IDX_MPEG_I_FRAME_START);
+ patterns[3] = dvb_dmx_get_pattern(DMX_IDX_MPEG_P_FRAME_START);
+ patterns[4] = dvb_dmx_get_pattern(DMX_IDX_MPEG_B_FRAME_START);
+ *patterns_num = 5;
+ break;
+
+ case DMX_VIDEO_CODEC_H264:
+ patterns[0] = dvb_dmx_get_pattern(DMX_IDX_H264_SPS);
+ patterns[1] = dvb_dmx_get_pattern(DMX_IDX_H264_PPS);
+ patterns[2] = dvb_dmx_get_pattern(DMX_IDX_H264_IDR_START);
+ patterns[3] = dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_START);
+ patterns[4] = dvb_dmx_get_pattern(DMX_IDX_H264_SEI);
+ *patterns_num = 5;
+ break;
+
+ case DMX_VIDEO_CODEC_VC1:
+ patterns[0] = dvb_dmx_get_pattern(DMX_IDX_VC1_SEQ_HEADER);
+ patterns[1] = dvb_dmx_get_pattern(DMX_IDX_VC1_ENTRY_POINT);
+ patterns[2] = dvb_dmx_get_pattern(DMX_IDX_VC1_FRAME_START);
+ *patterns_num = 3;
+ break;
+
+ default:
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ *patterns_num = 0;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * mpq_dmx_update_decoder_stat -
+ * Update decoder output statistics in debug-fs.
+ *
+ * @mpq_feed: decoder feed object
+ */
+void mpq_dmx_update_decoder_stat(struct mpq_feed *mpq_feed)
+{
+ struct timespec curr_time;
+ u64 delta_time_ms;
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+ enum mpq_adapter_stream_if idx;
+
+ if (!dvb_dmx_is_video_feed(mpq_feed->dvb_demux_feed) ||
+ (mpq_feed->video_info.stream_interface >
+ MPQ_ADAPTER_VIDEO3_STREAM_IF))
+ return;
+
+ idx = mpq_feed->video_info.stream_interface;
+
+ curr_time = current_kernel_time();
+ if (unlikely(!mpq_demux->decoder_stat[idx].out_count)) {
+ mpq_demux->decoder_stat[idx].out_last_time = curr_time;
+ mpq_demux->decoder_stat[idx].out_count++;
+ return;
+ }
+
+ /* calculate time-delta between frame */
+ delta_time_ms = mpq_dmx_calc_time_delta(&curr_time,
+ &mpq_demux->decoder_stat[idx].out_last_time);
+
+ mpq_demux->decoder_stat[idx].out_interval_sum += (u32)delta_time_ms;
+
+ mpq_demux->decoder_stat[idx].out_interval_average =
+ mpq_demux->decoder_stat[idx].out_interval_sum /
+ mpq_demux->decoder_stat[idx].out_count;
+
+ if (delta_time_ms > mpq_demux->decoder_stat[idx].out_interval_max)
+ mpq_demux->decoder_stat[idx].out_interval_max = delta_time_ms;
+
+ mpq_demux->decoder_stat[idx].out_last_time = curr_time;
+ mpq_demux->decoder_stat[idx].out_count++;
+}
+
+/*
+ * mpq_dmx_update_sdmx_stat -
+ * Update SDMX statistics in debug-fs.
+ *
+ * @mpq_demux: mpq_demux object
+ * @bytes_processed: number of bytes processed by sdmx
+ * @process_start_time: time before sdmx process was triggered
+ * @process_end_time: time after sdmx process finished
+ */
+static inline void mpq_dmx_update_sdmx_stat(struct mpq_demux *mpq_demux,
+ u32 bytes_processed, struct timespec *process_start_time,
+ struct timespec *process_end_time)
+{
+ u32 packets_num;
+ u64 process_time;
+
+ mpq_demux->sdmx_process_count++;
+ packets_num = bytes_processed / mpq_demux->demux.ts_packet_size;
+ mpq_demux->sdmx_process_packets_sum += packets_num;
+ mpq_demux->sdmx_process_packets_average =
+ mpq_demux->sdmx_process_packets_sum /
+ mpq_demux->sdmx_process_count;
+
+ process_time =
+ mpq_dmx_calc_time_delta(process_end_time, process_start_time);
+
+ mpq_demux->sdmx_process_time_sum += process_time;
+ mpq_demux->sdmx_process_time_average =
+ mpq_demux->sdmx_process_time_sum /
+ mpq_demux->sdmx_process_count;
+
+ if ((mpq_demux->sdmx_process_count == 1) ||
+ (packets_num < mpq_demux->sdmx_process_packets_min))
+ mpq_demux->sdmx_process_packets_min = packets_num;
+
+ if ((mpq_demux->sdmx_process_count == 1) ||
+ (process_time > mpq_demux->sdmx_process_time_max))
+ mpq_demux->sdmx_process_time_max = process_time;
+}
+
+static int mpq_sdmx_log_level_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t mpq_sdmx_log_level_read(struct file *fp,
+ char __user *user_buffer, size_t count, loff_t *position)
+{
+ char user_str[16];
+ struct mpq_demux *mpq_demux = fp->private_data;
+ int ret;
+
+ ret = scnprintf(user_str, 16, "%d", mpq_demux->sdmx_log_level);
+ ret = simple_read_from_buffer(user_buffer, count, position,
+ user_str, ret+1);
+
+ return ret;
+}
+
+static ssize_t mpq_sdmx_log_level_write(struct file *fp,
+ const char __user *user_buffer, size_t count, loff_t *position)
+{
+ char user_str[16];
+ int ret;
+ int ret_count;
+ int level;
+ struct mpq_demux *mpq_demux = fp->private_data;
+
+ if (count >= 16)
+ return -EINVAL;
+
+ ret_count = simple_write_to_buffer(user_str, 16, position, user_buffer,
+ count);
+ if (ret_count < 0)
+ return ret_count;
+
+ ret = kstrtoint(user_str, 0, &level);
+ if (ret)
+ return ret;
+
+ if (level < SDMX_LOG_NO_PRINT || level > SDMX_LOG_VERBOSE)
+ return -EINVAL;
+
+ mutex_lock(&mpq_demux->mutex);
+ mpq_demux->sdmx_log_level = level;
+ if (mpq_demux->sdmx_session_handle != SDMX_INVALID_SESSION_HANDLE) {
+ ret = sdmx_set_log_level(mpq_demux->sdmx_session_handle,
+ mpq_demux->sdmx_log_level);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Could not set sdmx log level. ret = %d\n",
+ __func__, ret);
+ mutex_unlock(&mpq_demux->mutex);
+ return -EINVAL;
+ }
+ }
+
+ mutex_unlock(&mpq_demux->mutex);
+ return ret_count;
+}
+
+static const struct file_operations sdmx_debug_fops = {
+ .open = mpq_sdmx_log_level_open,
+ .read = mpq_sdmx_log_level_read,
+ .write = mpq_sdmx_log_level_write,
+ .owner = THIS_MODULE,
+};
+
+/* Extend dvb-demux debugfs with common plug-in entries */
+void mpq_dmx_init_debugfs_entries(struct mpq_demux *mpq_demux)
+{
+ int i;
+ char file_name[50];
+ struct dentry *debugfs_decoder_dir;
+
+ /*
+ * Extend dvb-demux debugfs with HW statistics.
+ * Note that destruction of debugfs directory is done
+ * when dvb-demux is terminated.
+ */
+ mpq_demux->hw_notification_count = 0;
+ mpq_demux->hw_notification_interval = 0;
+ mpq_demux->hw_notification_size = 0;
+ mpq_demux->hw_notification_min_size = 0xFFFFFFFF;
+
+ if (mpq_demux->demux.dmx.debugfs_demux_dir == NULL)
+ return;
+
+ debugfs_create_u32(
+ "hw_notification_interval",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_interval);
+
+ debugfs_create_u32(
+ "hw_notification_min_interval",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_min_interval);
+
+ debugfs_create_u32(
+ "hw_notification_count",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_count);
+
+ debugfs_create_u32(
+ "hw_notification_size",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_size);
+
+ debugfs_create_u32(
+ "hw_notification_min_size",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_min_size);
+
+ debugfs_decoder_dir = debugfs_create_dir("decoder",
+ mpq_demux->demux.dmx.debugfs_demux_dir);
+
+ for (i = 0;
+ debugfs_decoder_dir &&
+ (i < MPQ_ADAPTER_MAX_NUM_OF_INTERFACES);
+ i++) {
+ snprintf(file_name, 50, "decoder%d_drop_count", i);
+ debugfs_create_u32(
+ file_name,
+ S_IRUGO,
+ debugfs_decoder_dir,
+ &mpq_demux->decoder_stat[i].drop_count);
+
+ snprintf(file_name, 50, "decoder%d_out_count", i);
+ debugfs_create_u32(
+ file_name,
+ S_IRUGO,
+ debugfs_decoder_dir,
+ &mpq_demux->decoder_stat[i].out_count);
+
+ snprintf(file_name, 50, "decoder%d_out_interval_sum", i);
+ debugfs_create_u32(
+ file_name,
+ S_IRUGO,
+ debugfs_decoder_dir,
+ &mpq_demux->decoder_stat[i].out_interval_sum);
+
+ snprintf(file_name, 50, "decoder%d_out_interval_average", i);
+ debugfs_create_u32(
+ file_name,
+ S_IRUGO,
+ debugfs_decoder_dir,
+ &mpq_demux->decoder_stat[i].out_interval_average);
+
+ snprintf(file_name, 50, "decoder%d_out_interval_max", i);
+ debugfs_create_u32(
+ file_name,
+ S_IRUGO,
+ debugfs_decoder_dir,
+ &mpq_demux->decoder_stat[i].out_interval_max);
+
+ snprintf(file_name, 50, "decoder%d_ts_errors", i);
+ debugfs_create_u32(
+ file_name,
+ S_IRUGO,
+ debugfs_decoder_dir,
+ &mpq_demux->decoder_stat[i].ts_errors);
+
+ snprintf(file_name, 50, "decoder%d_cc_errors", i);
+ debugfs_create_u32(
+ file_name,
+ S_IRUGO,
+ debugfs_decoder_dir,
+ &mpq_demux->decoder_stat[i].cc_errors);
+ }
+
+ debugfs_create_u32(
+ "sdmx_process_count",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_count);
+
+ debugfs_create_u32(
+ "sdmx_process_time_sum",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_time_sum);
+
+ debugfs_create_u32(
+ "sdmx_process_time_average",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_time_average);
+
+ debugfs_create_u32(
+ "sdmx_process_time_max",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_time_max);
+
+ debugfs_create_u32(
+ "sdmx_process_packets_sum",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_packets_sum);
+
+ debugfs_create_u32(
+ "sdmx_process_packets_average",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_packets_average);
+
+ debugfs_create_u32(
+ "sdmx_process_packets_min",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_packets_min);
+
+ debugfs_create_file("sdmx_log_level",
+ S_IRUGO | S_IWUSR | S_IWGRP,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ mpq_demux,
+ &sdmx_debug_fops);
+}
+
+/* Update dvb-demux debugfs with HW notification statistics */
+void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux)
+{
+ struct timespec curr_time;
+ u64 delta_time_ms;
+
+ curr_time = current_kernel_time();
+ if (likely(mpq_demux->hw_notification_count)) {
+ /* calculate time-delta between notifications */
+ delta_time_ms = mpq_dmx_calc_time_delta(&curr_time,
+ &mpq_demux->last_notification_time);
+
+ mpq_demux->hw_notification_interval = delta_time_ms;
+
+ if ((mpq_demux->hw_notification_count == 1) ||
+ (mpq_demux->hw_notification_interval &&
+ mpq_demux->hw_notification_interval <
+ mpq_demux->hw_notification_min_interval))
+ mpq_demux->hw_notification_min_interval =
+ mpq_demux->hw_notification_interval;
+ }
+
+ mpq_demux->hw_notification_count++;
+ mpq_demux->last_notification_time = curr_time;
+}
+
+static void mpq_sdmx_check_app_loaded(void)
+{
+ int session;
+ u32 version;
+ int ret;
+
+ ret = sdmx_open_session(&session);
+ if (ret != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Could not initialize session with SDMX. ret = %d\n",
+ __func__, ret);
+ mpq_dmx_info.secure_demux_app_loaded = 0;
+ return;
+ }
+
+ /* Check proper sdmx major version */
+ ret = sdmx_get_version(session, &version);
+ if (ret != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Could not get sdmx version. ret = %d\n",
+ __func__, ret);
+ } else {
+ if ((version >> 8) != SDMX_MAJOR_VERSION_MATCH)
+ MPQ_DVB_ERR_PRINT(
+ "%s: sdmx major version does not match. expected=%d, actual=%d\n",
+ __func__, SDMX_MAJOR_VERSION_MATCH,
+ (version >> 8));
+ else
+ MPQ_DVB_DBG_PRINT(
+ "%s: sdmx major version is ok = %d\n",
+ __func__, SDMX_MAJOR_VERSION_MATCH);
+ }
+
+ mpq_dmx_info.secure_demux_app_loaded = 1;
+ sdmx_close_session(session);
+}
+
+int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func)
+{
+ int i;
+ int j;
+ int result;
+ struct mpq_demux *mpq_demux;
+ struct dvb_adapter *mpq_adapter;
+ struct mpq_feed *feed;
+
+ MPQ_DVB_DBG_PRINT("%s executed, device num %d\n",
+ __func__,
+ mpq_demux_device_num);
+
+ mpq_adapter = mpq_adapter_get();
+
+ if (mpq_adapter == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_adapter is not valid\n",
+ __func__);
+ result = -EPERM;
+ goto init_failed;
+ }
+
+ if (mpq_demux_device_num == 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_demux_device_num set to 0\n",
+ __func__);
+
+ result = -EPERM;
+ goto init_failed;
+ }
+
+ mpq_dmx_info.devices = NULL;
+ mpq_dmx_info.ion_client = NULL;
+
+ mpq_dmx_info.secure_demux_app_loaded = 0;
+
+ /* Allocate memory for all MPQ devices */
+ mpq_dmx_info.devices =
+ vzalloc(mpq_demux_device_num*sizeof(struct mpq_demux));
+
+ if (!mpq_dmx_info.devices) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: failed to allocate devices memory\n",
+ __func__);
+
+ result = -ENOMEM;
+ goto init_failed;
+ }
+
+ /*
+ * Create a new ION client used by demux to allocate memory
+ * for decoder's buffers.
+ */
+ mpq_dmx_info.ion_client =
+ msm_ion_client_create("demux_client");
+ if (IS_ERR_OR_NULL(mpq_dmx_info.ion_client)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: msm_ion_client_create\n",
+ __func__);
+
+ result = PTR_ERR(mpq_dmx_info.ion_client);
+ if (!result)
+ result = -ENOMEM;
+ mpq_dmx_info.ion_client = NULL;
+ goto init_failed_free_demux_devices;
+ }
+
+ /* Initialize and register all demux devices to the system */
+ for (i = 0; i < mpq_demux_device_num; i++) {
+ mpq_demux = mpq_dmx_info.devices+i;
+ mpq_demux->idx = i;
+
+ /* initialize demux source to memory by default */
+ mpq_demux->source = DMX_SOURCE_DVR0 + i;
+
+ /*
+ * Give the plugin pointer to the ion client so
+ * that it can allocate memory from ION if it requires so
+ */
+ mpq_demux->ion_client = mpq_dmx_info.ion_client;
+
+ mutex_init(&mpq_demux->mutex);
+
+ mpq_demux->num_secure_feeds = 0;
+ mpq_demux->num_active_feeds = 0;
+ mpq_demux->sdmx_filter_count = 0;
+ mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
+ mpq_demux->sdmx_eos = 0;
+ mpq_demux->sdmx_log_level = SDMX_LOG_NO_PRINT;
+
+ if (mpq_demux->demux.feednum > MPQ_MAX_DMX_FILES) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: err - actual feednum (%d) larger than max, enlarge MPQ_MAX_DMX_FILES!\n",
+ __func__,
+ mpq_demux->demux.feednum);
+ result = -EINVAL;
+ goto init_failed_free_demux_devices;
+ }
+
+ /* Initialize private feed info */
+ for (j = 0; j < MPQ_MAX_DMX_FILES; j++) {
+ feed = &mpq_demux->feeds[j];
+ memset(feed, 0, sizeof(*feed));
+ feed->sdmx_filter_handle = SDMX_INVALID_FILTER_HANDLE;
+ feed->mpq_demux = mpq_demux;
+ feed->session_id = 0;
+ }
+
+ /*
+ * mpq_demux_plugin_hw_init should be implemented
+ * by the specific plugin
+ */
+ result = dmx_init_func(mpq_adapter, mpq_demux);
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: dmx_init_func (errno=%d)\n",
+ __func__,
+ result);
+
+ goto init_failed_free_demux_devices;
+ }
+
+ mpq_demux->is_initialized = 1;
+
+ /*
+ * dvb-demux is now initialized,
+ * update back-pointers of private feeds
+ */
+ for (j = 0; j < MPQ_MAX_DMX_FILES; j++) {
+ feed = &mpq_demux->feeds[j];
+ feed->dvb_demux_feed = &mpq_demux->demux.feed[j];
+ mpq_demux->demux.feed[j].priv = feed;
+ }
+
+ /*
+ * Add capability of receiving input from memory.
+ * Every demux in our system may be connected to memory input,
+ * or any live input.
+ */
+ mpq_demux->fe_memory.source = DMX_MEMORY_FE;
+ result =
+ mpq_demux->demux.dmx.add_frontend(
+ &mpq_demux->demux.dmx,
+ &mpq_demux->fe_memory);
+
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: add_frontend (mem) failed (errno=%d)\n",
+ __func__,
+ result);
+
+ goto init_failed_free_demux_devices;
+ }
+ }
+
+ return 0;
+
+init_failed_free_demux_devices:
+ mpq_dmx_plugin_exit();
+init_failed:
+ return result;
+}
+
+void mpq_dmx_plugin_exit(void)
+{
+ int i;
+ struct mpq_demux *mpq_demux;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ if (mpq_dmx_info.ion_client != NULL) {
+ ion_client_destroy(mpq_dmx_info.ion_client);
+ mpq_dmx_info.ion_client = NULL;
+ }
+
+ if (mpq_dmx_info.devices != NULL) {
+ for (i = 0; i < mpq_demux_device_num; i++) {
+ mpq_demux = mpq_dmx_info.devices + i;
+
+ if (!mpq_demux->is_initialized)
+ continue;
+
+ if (mpq_demux->mpq_dmx_plugin_release)
+ mpq_demux->mpq_dmx_plugin_release(mpq_demux);
+
+ mpq_demux->demux.dmx.remove_frontend(
+ &mpq_demux->demux.dmx,
+ &mpq_demux->fe_memory);
+
+ if (mpq_dmx_info.secure_demux_app_loaded)
+ mpq_sdmx_close_session(mpq_demux);
+ mutex_destroy(&mpq_demux->mutex);
+ dvb_dmxdev_release(&mpq_demux->dmxdev);
+ dvb_dmx_release(&mpq_demux->demux);
+ }
+
+ vfree(mpq_dmx_info.devices);
+ mpq_dmx_info.devices = NULL;
+ }
+}
+
+int mpq_dmx_set_source(
+ struct dmx_demux *demux,
+ const dmx_source_t *src)
+{
+ int i;
+ int dvr_index;
+ int dmx_index;
+ struct dvb_demux *dvb_demux = demux->priv;
+ struct mpq_demux *mpq_demux;
+
+ if ((mpq_dmx_info.devices == NULL) || (dvb_demux == NULL)) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ mpq_demux = dvb_demux->priv;
+ if (mpq_demux == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * For dvr sources,
+ * verify that this source is connected to the respective demux
+ */
+ dmx_index = mpq_demux - mpq_dmx_info.devices;
+
+ if (*src >= DMX_SOURCE_DVR0) {
+ dvr_index = *src - DMX_SOURCE_DVR0;
+
+ if (dvr_index != dmx_index) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: can't connect demux%d to dvr%d\n",
+ __func__,
+ dmx_index,
+ dvr_index);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * For front-end sources,
+ * verify that this source is not already set to different demux
+ */
+ for (i = 0; i < mpq_demux_device_num; i++) {
+ if ((&mpq_dmx_info.devices[i] != mpq_demux) &&
+ (mpq_dmx_info.devices[i].source == *src)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: demux%d source can't be set,\n"
+ "demux%d occupies this source already\n",
+ __func__,
+ dmx_index,
+ i);
+ return -EBUSY;
+ }
+ }
+
+ mpq_demux->source = *src;
+ return 0;
+}
+
+/**
+ * Takes an ION allocated buffer's file descriptor and handles the details of
+ * mapping it into kernel memory and obtaining an ION handle for it.
+ * Internal helper function.
+ *
+ * @client: ION client
+ * @handle: ION file descriptor to map
+ * @priv_handle: returned ION handle. Must be freed when no longer needed
+ * @kernel_mem: returned kernel mapped pointer
+ *
+ * Note: mapping might not be possible in secured heaps/buffers, and so NULL
+ * might be returned in kernel_mem
+ *
+ * Return errors status
+ */
+static int mpq_map_buffer_to_kernel(
+ struct ion_client *client,
+ int handle,
+ struct ion_handle **priv_handle,
+ void **kernel_mem)
+{
+ struct ion_handle *ion_handle;
+ unsigned long ionflag = 0;
+ int ret;
+
+ if (client == NULL || priv_handle == NULL || kernel_mem == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ ion_handle = ion_import_dma_buf(client, handle);
+ if (IS_ERR_OR_NULL(ion_handle)) {
+ ret = PTR_ERR(ion_handle);
+ MPQ_DVB_ERR_PRINT("%s: ion_import_dma_buf failed %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+
+ goto map_buffer_failed;
+ }
+
+ ret = ion_handle_get_flags(client, ion_handle, &ionflag);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("%s: ion_handle_get_flags failed %d\n",
+ __func__, ret);
+ goto map_buffer_failed_free_buff;
+ }
+
+ if (ionflag & ION_FLAG_SECURE) {
+ MPQ_DVB_DBG_PRINT("%s: secured buffer\n", __func__);
+ *kernel_mem = NULL;
+ } else {
+ unsigned long tmp;
+ *kernel_mem = ion_map_kernel(client, ion_handle);
+ if (IS_ERR_OR_NULL(*kernel_mem)) {
+ ret = PTR_ERR(*kernel_mem);
+ MPQ_DVB_ERR_PRINT("%s: ion_map_kernel failed, ret=%d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto map_buffer_failed_free_buff;
+ }
+ ion_handle_get_size(client, ion_handle, &tmp);
+ MPQ_DVB_DBG_PRINT(
+ "%s: mapped to address 0x%p, size=%lu\n",
+ __func__, *kernel_mem, tmp);
+ }
+
+ *priv_handle = ion_handle;
+ return 0;
+
+map_buffer_failed_free_buff:
+ ion_free(client, ion_handle);
+map_buffer_failed:
+ return ret;
+}
+
+int mpq_dmx_map_buffer(struct dmx_demux *demux, struct dmx_buffer *dmx_buffer,
+ void **priv_handle, void **kernel_mem)
+{
+ struct dvb_demux *dvb_demux = demux->priv;
+ struct mpq_demux *mpq_demux;
+
+ if ((mpq_dmx_info.devices == NULL) || (dvb_demux == NULL) ||
+ (priv_handle == NULL) || (kernel_mem == NULL)) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ mpq_demux = dvb_demux->priv;
+ if (mpq_demux == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ return mpq_map_buffer_to_kernel(
+ mpq_demux->ion_client,
+ dmx_buffer->handle,
+ (struct ion_handle **)priv_handle, kernel_mem);
+}
+
+int mpq_dmx_unmap_buffer(struct dmx_demux *demux,
+ void *priv_handle)
+{
+ struct dvb_demux *dvb_demux = demux->priv;
+ struct ion_handle *ion_handle = priv_handle;
+ struct mpq_demux *mpq_demux;
+ unsigned long ionflag = 0;
+ int ret;
+
+ if ((mpq_dmx_info.devices == NULL) || (dvb_demux == NULL) ||
+ (priv_handle == NULL)) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ mpq_demux = dvb_demux->priv;
+ if (mpq_demux == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = ion_handle_get_flags(mpq_demux->ion_client, ion_handle, &ionflag);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("%s: ion_handle_get_flags failed %d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ if (!(ionflag & ION_FLAG_SECURE))
+ ion_unmap_kernel(mpq_demux->ion_client, ion_handle);
+
+ ion_free(mpq_demux->ion_client, ion_handle);
+
+ return 0;
+}
+
+int mpq_dmx_reuse_decoder_buffer(struct dvb_demux_feed *feed, int cookie)
+{
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+
+ MPQ_DVB_DBG_PRINT("%s: cookie=%d\n", __func__, cookie);
+
+ if (cookie < 0) {
+ MPQ_DVB_ERR_PRINT("%s: invalid cookie parameter\n", __func__);
+ return -EINVAL;
+ }
+
+ if (dvb_dmx_is_video_feed(feed)) {
+ struct mpq_video_feed_info *feed_data;
+ struct mpq_feed *mpq_feed;
+ struct mpq_streambuffer *stream_buffer;
+ int ret;
+
+ mutex_lock(&mpq_demux->mutex);
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
+
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+ if (stream_buffer == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid feed, feed_data->video_buffer is NULL\n",
+ __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ mutex_unlock(&mpq_demux->mutex);
+ return -EINVAL;
+ }
+
+ ret = mpq_streambuffer_pkt_dispose(stream_buffer, cookie, 1);
+ spin_unlock(&feed_data->video_buffer_lock);
+ mutex_unlock(&mpq_demux->mutex);
+
+ return ret;
+ }
+
+ /* else */
+ MPQ_DVB_ERR_PRINT("%s: Invalid feed type %d\n",
+ __func__, feed->pes_type);
+
+ return -EINVAL;
+}
+
+/**
+ * Handles the details of internal decoder buffer allocation via ION.
+ * Internal helper function.
+ * @feed_data: decoder feed object
+ * @dec_buffs: buffer information
+ * @client: ION client
+ *
+ * Return error status
+ */
+static int mpq_dmx_init_internal_buffers(
+ struct mpq_demux *mpq_demux,
+ struct mpq_video_feed_info *feed_data,
+ struct dmx_decoder_buffers *dec_buffs)
+{
+ struct ion_handle *temp_handle = NULL;
+ void *payload_buffer = NULL;
+ int actual_buffer_size = 0;
+ int ret = 0;
+
+ MPQ_DVB_DBG_PRINT("%s: Internal decoder buffer allocation\n", __func__);
+
+ actual_buffer_size = dec_buffs->buffers_size;
+ actual_buffer_size += (SZ_4K - 1);
+ actual_buffer_size &= ~(SZ_4K - 1);
+
+ temp_handle = ion_alloc(mpq_demux->ion_client,
+ actual_buffer_size, SZ_4K,
+ ION_HEAP(video_secure_ion_heap) |
+ ION_HEAP(video_nonsecure_ion_heap),
+ mpq_demux->decoder_alloc_flags);
+
+ if (IS_ERR_OR_NULL(temp_handle)) {
+ ret = PTR_ERR(temp_handle);
+ MPQ_DVB_ERR_PRINT("%s: FAILED to allocate payload buffer %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ payload_buffer = ion_map_kernel(mpq_demux->ion_client, temp_handle);
+
+ if (IS_ERR_OR_NULL(payload_buffer)) {
+ ret = PTR_ERR(payload_buffer);
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to map payload buffer %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto init_failed_free_payload_buffer;
+ }
+ feed_data->buffer_desc.decoder_buffers_num = 1;
+ feed_data->buffer_desc.ion_handle[0] = temp_handle;
+ feed_data->buffer_desc.desc[0].base = payload_buffer;
+ feed_data->buffer_desc.desc[0].size = actual_buffer_size;
+ feed_data->buffer_desc.desc[0].read_ptr = 0;
+ feed_data->buffer_desc.desc[0].write_ptr = 0;
+ feed_data->buffer_desc.desc[0].handle =
+ ion_share_dma_buf_fd(mpq_demux->ion_client, temp_handle);
+ if (IS_ERR_VALUE(feed_data->buffer_desc.desc[0].handle)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to share payload buffer %d\n",
+ __func__, ret);
+ ret = -ENOMEM;
+ goto init_failed_unmap_payload_buffer;
+ }
+
+ feed_data->buffer_desc.shared_file = fget(
+ feed_data->buffer_desc.desc[0].handle);
+
+ return 0;
+
+init_failed_unmap_payload_buffer:
+ ion_unmap_kernel(mpq_demux->ion_client, temp_handle);
+ feed_data->buffer_desc.desc[0].base = NULL;
+init_failed_free_payload_buffer:
+ ion_free(mpq_demux->ion_client, temp_handle);
+ feed_data->buffer_desc.ion_handle[0] = NULL;
+ feed_data->buffer_desc.desc[0].size = 0;
+ feed_data->buffer_desc.decoder_buffers_num = 0;
+ feed_data->buffer_desc.shared_file = NULL;
+end:
+ return ret;
+}
+
+/**
+ * Handles the details of external decoder buffers allocated by user.
+ * Each buffer is mapped into kernel memory and an ION handle is obtained, and
+ * decoder feed object is updated with related information.
+ * Internal helper function.
+ * @feed_data: decoder feed object
+ * @dec_buffs: buffer information
+ * @client: ION client
+ *
+ * Return error status
+ */
+static int mpq_dmx_init_external_buffers(
+ struct mpq_video_feed_info *feed_data,
+ struct dmx_decoder_buffers *dec_buffs,
+ struct ion_client *client)
+{
+ struct ion_handle *temp_handle = NULL;
+ void *payload_buffer = NULL;
+ int actual_buffer_size = 0;
+ int ret = 0;
+ int i;
+
+ /*
+ * Payload buffer was allocated externally (through ION).
+ * Map the ion handles to kernel memory
+ */
+ MPQ_DVB_DBG_PRINT("%s: External decoder buffer allocation\n", __func__);
+
+ actual_buffer_size = dec_buffs->buffers_size;
+ if (!dec_buffs->is_linear) {
+ MPQ_DVB_DBG_PRINT("%s: Ex. Ring-buffer\n", __func__);
+ feed_data->buffer_desc.decoder_buffers_num = 1;
+ } else {
+ MPQ_DVB_DBG_PRINT("%s: Ex. Linear\n", __func__);
+ feed_data->buffer_desc.decoder_buffers_num =
+ dec_buffs->buffers_num;
+ }
+
+ for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) {
+ ret = mpq_map_buffer_to_kernel(
+ client,
+ dec_buffs->handles[i],
+ &temp_handle,
+ &payload_buffer);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed mapping buffer %d\n",
+ __func__, i);
+ goto init_failed;
+ }
+ feed_data->buffer_desc.ion_handle[i] = temp_handle;
+ feed_data->buffer_desc.desc[i].base = payload_buffer;
+ feed_data->buffer_desc.desc[i].handle =
+ dec_buffs->handles[i];
+ feed_data->buffer_desc.desc[i].size =
+ dec_buffs->buffers_size;
+ feed_data->buffer_desc.desc[i].read_ptr = 0;
+ feed_data->buffer_desc.desc[i].write_ptr = 0;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Buffer #%d: base=0x%p, handle=%d, size=%d\n",
+ __func__, i,
+ feed_data->buffer_desc.desc[i].base,
+ feed_data->buffer_desc.desc[i].handle,
+ feed_data->buffer_desc.desc[i].size);
+ }
+
+ return 0;
+
+init_failed:
+ for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) {
+ if (feed_data->buffer_desc.ion_handle[i]) {
+ if (feed_data->buffer_desc.desc[i].base) {
+ ion_unmap_kernel(client,
+ feed_data->buffer_desc.ion_handle[i]);
+ feed_data->buffer_desc.desc[i].base = NULL;
+ }
+ ion_free(client, feed_data->buffer_desc.ion_handle[i]);
+ feed_data->buffer_desc.ion_handle[i] = NULL;
+ feed_data->buffer_desc.desc[i].size = 0;
+ }
+ }
+ return ret;
+}
+
+/**
+ * Handles the details of initializing the mpq_streambuffer object according
+ * to the user decoder buffer configuration: External/Internal buffers and
+ * ring/linear buffering mode.
+ * Internal helper function.
+ * @feed: dvb demux feed object, contains the buffers configuration
+ * @feed_data: decoder feed object
+ * @stream_buffer: stream buffer object to initialize
+ *
+ * Return error status
+ */
+static int mpq_dmx_init_streambuffer(
+ struct mpq_feed *feed,
+ struct mpq_video_feed_info *feed_data,
+ struct mpq_streambuffer *stream_buffer)
+{
+ int ret;
+ void *packet_buffer = NULL;
+ struct mpq_demux *mpq_demux = feed->mpq_demux;
+ struct ion_client *client = mpq_demux->ion_client;
+ struct dmx_decoder_buffers *dec_buffs = NULL;
+ enum mpq_streambuffer_mode mode;
+
+ dec_buffs = feed->dvb_demux_feed->feed.ts.decoder_buffers;
+
+ /* Allocate packet buffer holding the meta-data */
+ packet_buffer = vmalloc(VIDEO_META_DATA_BUFFER_SIZE);
+
+ if (packet_buffer == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to allocate packets buffer\n",
+ __func__);
+
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ MPQ_DVB_DBG_PRINT("%s: dec_buffs: num=%d, size=%d, linear=%d\n",
+ __func__,
+ dec_buffs->buffers_num,
+ dec_buffs->buffers_size,
+ dec_buffs->is_linear);
+
+ if (dec_buffs->buffers_num == 0)
+ ret = mpq_dmx_init_internal_buffers(
+ mpq_demux, feed_data, dec_buffs);
+ else
+ ret = mpq_dmx_init_external_buffers(
+ feed_data, dec_buffs, client);
+
+ if (ret != 0)
+ goto init_failed_free_packet_buffer;
+
+ mode = dec_buffs->is_linear ? MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR :
+ MPQ_STREAMBUFFER_BUFFER_MODE_RING;
+ ret = mpq_streambuffer_init(
+ feed_data->video_buffer,
+ mode,
+ feed_data->buffer_desc.desc,
+ feed_data->buffer_desc.decoder_buffers_num,
+ packet_buffer,
+ VIDEO_META_DATA_BUFFER_SIZE);
+
+ if (ret != 0)
+ goto init_failed_free_packet_buffer;
+
+ goto end;
+
+
+init_failed_free_packet_buffer:
+ vfree(packet_buffer);
+end:
+ return ret;
+}
+
+static void mpq_dmx_release_streambuffer(
+ struct mpq_feed *feed,
+ struct mpq_video_feed_info *feed_data,
+ struct mpq_streambuffer *video_buffer,
+ struct ion_client *client)
+{
+ int buf_num = 0;
+ int i;
+ struct dmx_decoder_buffers *dec_buffs =
+ feed->dvb_demux_feed->feed.ts.decoder_buffers;
+
+ mpq_adapter_unregister_stream_if(feed_data->stream_interface);
+
+ mpq_streambuffer_terminate(video_buffer);
+
+ vfree(video_buffer->packet_data.data);
+
+ buf_num = feed_data->buffer_desc.decoder_buffers_num;
+
+ for (i = 0; i < buf_num; i++) {
+ if (feed_data->buffer_desc.ion_handle[i]) {
+ if (feed_data->buffer_desc.desc[i].base) {
+ ion_unmap_kernel(client,
+ feed_data->buffer_desc.ion_handle[i]);
+ feed_data->buffer_desc.desc[i].base = NULL;
+ }
+
+ /*
+ * Un-share the buffer if kernel it the one that
+ * shared it.
+ */
+ if (!dec_buffs->buffers_num &&
+ feed_data->buffer_desc.shared_file) {
+ fput(feed_data->buffer_desc.shared_file);
+ feed_data->buffer_desc.shared_file = NULL;
+ }
+
+ ion_free(client, feed_data->buffer_desc.ion_handle[i]);
+ feed_data->buffer_desc.ion_handle[i] = NULL;
+ feed_data->buffer_desc.desc[i].size = 0;
+ }
+ }
+}
+
+int mpq_dmx_flush_stream_buffer(struct dvb_demux_feed *feed)
+{
+ struct mpq_feed *mpq_feed = feed->priv;
+ struct mpq_video_feed_info *feed_data = &mpq_feed->video_info;
+ struct mpq_streambuffer *sbuff;
+ int ret = 0;
+
+ if (!dvb_dmx_is_video_feed(feed)) {
+ MPQ_DVB_DBG_PRINT("%s: not a video feed, feed type=%d\n",
+ __func__, feed->pes_type);
+ return 0;
+ }
+
+ spin_lock(&feed_data->video_buffer_lock);
+
+ sbuff = feed_data->video_buffer;
+ if (sbuff == NULL) {
+ MPQ_DVB_DBG_PRINT("%s: feed_data->video_buffer is NULL\n",
+ __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return -ENODEV;
+ }
+
+ feed_data->pending_pattern_len = 0;
+
+ ret = mpq_streambuffer_flush(sbuff);
+ if (ret)
+ MPQ_DVB_ERR_PRINT("%s: mpq_streambuffer_flush failed, ret=%d\n",
+ __func__, ret);
+
+ spin_unlock(&feed_data->video_buffer_lock);
+
+ return ret;
+}
+
+static int mpq_dmx_flush_buffer(struct dmx_ts_feed *ts_feed, size_t length)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&demux->mutex))
+ return -ERESTARTSYS;
+
+ dvbdmx_ts_reset_pes_state(feed);
+
+ if (dvb_dmx_is_video_feed(feed)) {
+ MPQ_DVB_DBG_PRINT("%s: flushing video buffer\n", __func__);
+
+ ret = mpq_dmx_flush_stream_buffer(feed);
+ }
+
+ mutex_unlock(&demux->mutex);
+ return ret;
+}
+
+/**
+ * mpq_dmx_init_video_feed - Initializes of video feed information
+ * used to pass data directly to decoder.
+ *
+ * @mpq_feed: The mpq feed object
+ *
+ * Return error code.
+ */
+int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed)
+{
+ int ret;
+ struct mpq_video_feed_info *feed_data = &mpq_feed->video_info;
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+ struct mpq_streambuffer *stream_buffer;
+
+ /* get and store framing information if required */
+ if (video_framing) {
+ mpq_dmx_get_pattern_params(
+ mpq_feed->dvb_demux_feed->video_codec,
+ feed_data->patterns, &feed_data->patterns_num);
+ if (!feed_data->patterns_num) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to get framing pattern parameters\n",
+ __func__);
+
+ ret = -EINVAL;
+ goto init_failed_free_priv_data;
+ }
+ }
+
+ /* Register the new stream-buffer interface to MPQ adapter */
+ switch (mpq_feed->dvb_demux_feed->pes_type) {
+ case DMX_PES_VIDEO0:
+ feed_data->stream_interface =
+ MPQ_ADAPTER_VIDEO0_STREAM_IF;
+ break;
+
+ case DMX_PES_VIDEO1:
+ feed_data->stream_interface =
+ MPQ_ADAPTER_VIDEO1_STREAM_IF;
+ break;
+
+ case DMX_PES_VIDEO2:
+ feed_data->stream_interface =
+ MPQ_ADAPTER_VIDEO2_STREAM_IF;
+ break;
+
+ case DMX_PES_VIDEO3:
+ feed_data->stream_interface =
+ MPQ_ADAPTER_VIDEO3_STREAM_IF;
+ break;
+
+ default:
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid pes type %d\n",
+ __func__,
+ mpq_feed->dvb_demux_feed->pes_type);
+ ret = -EINVAL;
+ goto init_failed_free_priv_data;
+ }
+
+ /* make sure not occupied already */
+ stream_buffer = NULL;
+ mpq_adapter_get_stream_if(
+ feed_data->stream_interface,
+ &stream_buffer);
+ if (stream_buffer != NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Video interface %d already occupied!\n",
+ __func__,
+ feed_data->stream_interface);
+ ret = -EBUSY;
+ goto init_failed_free_priv_data;
+ }
+
+ feed_data->video_buffer =
+ &mpq_dmx_info.decoder_buffers[feed_data->stream_interface];
+
+ ret = mpq_dmx_init_streambuffer(
+ mpq_feed, feed_data, feed_data->video_buffer);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_init_streambuffer failed, err = %d\n",
+ __func__, ret);
+ goto init_failed_free_priv_data;
+ }
+
+ ret = mpq_adapter_register_stream_if(
+ feed_data->stream_interface,
+ feed_data->video_buffer);
+
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_adapter_register_stream_if failed, err = %d\n",
+ __func__, ret);
+ goto init_failed_free_stream_buffer;
+ }
+
+ spin_lock_init(&feed_data->video_buffer_lock);
+
+ feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN;
+ feed_data->pes_header_offset = 0;
+ mpq_feed->dvb_demux_feed->pusi_seen = 0;
+ mpq_feed->dvb_demux_feed->peslen = 0;
+ feed_data->fullness_wait_cancel = 0;
+ mpq_streambuffer_get_data_rw_offset(feed_data->video_buffer, NULL,
+ &feed_data->frame_offset);
+ feed_data->last_pattern_offset = 0;
+ feed_data->pending_pattern_len = 0;
+ feed_data->last_framing_match_type = 0;
+ feed_data->found_sequence_header_pattern = 0;
+ memset(&feed_data->prefix_size, 0,
+ sizeof(struct dvb_dmx_video_prefix_size_masks));
+ feed_data->first_prefix_size = 0;
+ feed_data->saved_pts_dts_info.pts_exist = 0;
+ feed_data->saved_pts_dts_info.dts_exist = 0;
+ feed_data->new_pts_dts_info.pts_exist = 0;
+ feed_data->new_pts_dts_info.dts_exist = 0;
+ feed_data->saved_info_used = 1;
+ feed_data->new_info_exists = 0;
+ feed_data->first_pts_dts_copy = 1;
+ feed_data->tei_errs = 0;
+ feed_data->last_continuity = -1;
+ feed_data->continuity_errs = 0;
+ feed_data->ts_packets_num = 0;
+ feed_data->ts_dropped_bytes = 0;
+
+ mpq_demux->decoder_stat[feed_data->stream_interface].drop_count = 0;
+ mpq_demux->decoder_stat[feed_data->stream_interface].out_count = 0;
+ mpq_demux->decoder_stat[feed_data->stream_interface].
+ out_interval_sum = 0;
+ mpq_demux->decoder_stat[feed_data->stream_interface].
+ out_interval_max = 0;
+ mpq_demux->decoder_stat[feed_data->stream_interface].ts_errors = 0;
+ mpq_demux->decoder_stat[feed_data->stream_interface].cc_errors = 0;
+
+ return 0;
+
+init_failed_free_stream_buffer:
+ mpq_dmx_release_streambuffer(mpq_feed, feed_data,
+ feed_data->video_buffer, mpq_demux->ion_client);
+ mpq_adapter_unregister_stream_if(feed_data->stream_interface);
+init_failed_free_priv_data:
+ feed_data->video_buffer = NULL;
+ return ret;
+}
+
+/**
+ * mpq_dmx_terminate_video_feed - terminate video feed information
+ * that was previously initialized in mpq_dmx_init_video_feed
+ *
+ * @mpq_feed: The mpq feed used for the video TS packets
+ *
+ * Return error code.
+ */
+int mpq_dmx_terminate_video_feed(struct mpq_feed *mpq_feed)
+{
+ struct mpq_streambuffer *video_buffer;
+ struct mpq_video_feed_info *feed_data;
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+
+ if (mpq_feed == NULL)
+ return -EINVAL;
+
+ feed_data = &mpq_feed->video_info;
+
+ spin_lock(&feed_data->video_buffer_lock);
+ video_buffer = feed_data->video_buffer;
+ feed_data->video_buffer = NULL;
+ wake_up_all(&video_buffer->raw_data.queue);
+ spin_unlock(&feed_data->video_buffer_lock);
+
+ mpq_dmx_release_streambuffer(mpq_feed, feed_data,
+ video_buffer, mpq_demux->ion_client);
+
+ return 0;
+}
+
+struct dvb_demux_feed *mpq_dmx_peer_rec_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux_feed *tmp;
+ struct dvb_demux *dvb_demux = feed->demux;
+
+ list_for_each_entry(tmp, &dvb_demux->feed_list, list_head) {
+ if (tmp != feed && tmp->state == DMX_STATE_GO &&
+ tmp->feed.ts.buffer.ringbuff ==
+ feed->feed.ts.buffer.ringbuff) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: main feed pid=%d, secondary feed pid=%d\n",
+ __func__, tmp->pid, feed->pid);
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+static int mpq_sdmx_alloc_data_buf(struct mpq_feed *mpq_feed, size_t size)
+{
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+ void *buf_base;
+ int ret;
+
+ mpq_feed->sdmx_buf_handle = ion_alloc(mpq_demux->ion_client,
+ size,
+ SZ_4K,
+ ION_HEAP(ION_QSECOM_HEAP_ID),
+ 0);
+ if (IS_ERR_OR_NULL(mpq_feed->sdmx_buf_handle)) {
+ ret = PTR_ERR(mpq_feed->sdmx_buf_handle);
+ mpq_feed->sdmx_buf_handle = NULL;
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to allocate sdmx buffer %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ buf_base = ion_map_kernel(mpq_demux->ion_client,
+ mpq_feed->sdmx_buf_handle);
+ if (IS_ERR_OR_NULL(buf_base)) {
+ ret = PTR_ERR(buf_base);
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to map sdmx buffer %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto failed_free_buf;
+ }
+
+ dvb_ringbuffer_init(&mpq_feed->sdmx_buf, buf_base, size);
+
+ return 0;
+
+failed_free_buf:
+ ion_free(mpq_demux->ion_client, mpq_feed->sdmx_buf_handle);
+ mpq_feed->sdmx_buf_handle = NULL;
+end:
+ return ret;
+}
+
+static int mpq_sdmx_free_data_buf(struct mpq_feed *mpq_feed)
+{
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+
+ if (mpq_feed->sdmx_buf_handle) {
+ ion_unmap_kernel(mpq_demux->ion_client,
+ mpq_feed->sdmx_buf_handle);
+ mpq_feed->sdmx_buf.data = NULL;
+ ion_free(mpq_demux->ion_client,
+ mpq_feed->sdmx_buf_handle);
+ mpq_feed->sdmx_buf_handle = NULL;
+ }
+
+ return 0;
+}
+
+static int mpq_sdmx_init_metadata_buffer(struct mpq_demux *mpq_demux,
+ struct mpq_feed *feed, struct sdmx_buff_descr *metadata_buff_desc)
+{
+ void *metadata_buff_base;
+ ion_phys_addr_t temp;
+ int ret;
+ size_t size;
+
+ feed->metadata_buf_handle = ion_alloc(mpq_demux->ion_client,
+ SDMX_METADATA_BUFFER_SIZE,
+ SZ_4K,
+ ION_HEAP(ION_QSECOM_HEAP_ID),
+ 0);
+ if (IS_ERR_OR_NULL(feed->metadata_buf_handle)) {
+ ret = PTR_ERR(feed->metadata_buf_handle);
+ feed->metadata_buf_handle = NULL;
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to allocate metadata buffer %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ metadata_buff_base = ion_map_kernel(mpq_demux->ion_client,
+ feed->metadata_buf_handle);
+ if (IS_ERR_OR_NULL(metadata_buff_base)) {
+ ret = PTR_ERR(metadata_buff_base);
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to map metadata buffer %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto failed_free_metadata_buf;
+ }
+
+ ret = ion_phys(mpq_demux->ion_client,
+ feed->metadata_buf_handle,
+ &temp,
+ &size);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to get physical address %d\n",
+ __func__, ret);
+ goto failed_unmap_metadata_buf;
+ }
+ metadata_buff_desc->size = size;
+ metadata_buff_desc->base_addr = (u64)temp;
+
+ dvb_ringbuffer_init(&feed->metadata_buf, metadata_buff_base,
+ SDMX_METADATA_BUFFER_SIZE);
+
+ return 0;
+
+failed_unmap_metadata_buf:
+ ion_unmap_kernel(mpq_demux->ion_client, feed->metadata_buf_handle);
+failed_free_metadata_buf:
+ ion_free(mpq_demux->ion_client, feed->metadata_buf_handle);
+ feed->metadata_buf_handle = NULL;
+end:
+ return ret;
+}
+
+static int mpq_sdmx_terminate_metadata_buffer(struct mpq_feed *mpq_feed)
+{
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+
+ if (mpq_feed->metadata_buf_handle) {
+ ion_unmap_kernel(mpq_demux->ion_client,
+ mpq_feed->metadata_buf_handle);
+ mpq_feed->metadata_buf.data = NULL;
+ ion_free(mpq_demux->ion_client,
+ mpq_feed->metadata_buf_handle);
+ mpq_feed->metadata_buf_handle = NULL;
+ }
+
+ return 0;
+}
+
+int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed)
+{
+ int ret = 0;
+ struct mpq_demux *mpq_demux;
+ struct mpq_feed *mpq_feed;
+ struct mpq_feed *main_rec_feed = NULL;
+ struct dvb_demux_feed *tmp;
+
+ if (feed == NULL)
+ return -EINVAL;
+
+ mpq_demux = feed->demux->priv;
+
+ mutex_lock(&mpq_demux->mutex);
+ mpq_feed = feed->priv;
+
+ if (mpq_feed->sdmx_filter_handle != SDMX_INVALID_FILTER_HANDLE) {
+ if (mpq_feed->filter_type == SDMX_RAW_FILTER) {
+ tmp = mpq_dmx_peer_rec_feed(feed);
+ if (tmp)
+ main_rec_feed = tmp->priv;
+ }
+
+ if (main_rec_feed) {
+ /* This feed is part of a recording filter */
+ MPQ_DVB_DBG_PRINT(
+ "%s: Removing raw pid %d from filter %d\n",
+ __func__, feed->pid,
+ mpq_feed->sdmx_filter_handle);
+ ret = sdmx_remove_raw_pid(
+ mpq_demux->sdmx_session_handle,
+ mpq_feed->sdmx_filter_handle, feed->pid);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: SDMX_remove_raw_pid failed. ret = %d\n",
+ __func__, ret);
+
+ /* If this feed that we are removing was set as primary,
+ * now other feeds should be set as primary
+ */
+ if (!mpq_feed->secondary_feed)
+ main_rec_feed->secondary_feed = 0;
+ } else {
+ MPQ_DVB_DBG_PRINT("%s: Removing filter %d, pid %d\n",
+ __func__, mpq_feed->sdmx_filter_handle,
+ feed->pid);
+ ret = sdmx_remove_filter(mpq_demux->sdmx_session_handle,
+ mpq_feed->sdmx_filter_handle);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: SDMX_remove_filter failed. ret = %d\n",
+ __func__, ret);
+ }
+
+ mpq_demux->sdmx_filter_count--;
+ mpq_feed->sdmx_filter_handle =
+ SDMX_INVALID_FILTER_HANDLE;
+ }
+
+ mpq_sdmx_close_session(mpq_demux);
+ mpq_demux->num_secure_feeds--;
+ }
+
+ if (dvb_dmx_is_video_feed(feed)) {
+ ret = mpq_dmx_terminate_video_feed(mpq_feed);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_terminate_video_feed failed. ret = %d\n",
+ __func__, ret);
+ }
+
+ if (mpq_feed->sdmx_buf_handle) {
+ wake_up_all(&mpq_feed->sdmx_buf.queue);
+ mpq_sdmx_free_data_buf(mpq_feed);
+ }
+
+ mpq_sdmx_terminate_metadata_buffer(mpq_feed);
+ mpq_demux->num_active_feeds--;
+
+ mutex_unlock(&mpq_demux->mutex);
+
+ return ret;
+}
+
+int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed)
+{
+ if (dvb_dmx_is_video_feed(feed)) {
+ struct mpq_feed *mpq_feed;
+ struct mpq_video_feed_info *feed_data;
+
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
+ feed_data->fullness_wait_cancel = 0;
+
+ return 0;
+ }
+
+ /* else */
+ MPQ_DVB_DBG_PRINT(
+ "%s: Invalid feed type %d\n",
+ __func__,
+ feed->pes_type);
+
+ return -EINVAL;
+}
+
+/**
+ * Returns whether the free space of decoder's output
+ * buffer is larger than specific number of bytes.
+ *
+ * @sbuff: MPQ stream buffer used for decoder data.
+ * @required_space: number of required free bytes in the buffer
+ *
+ * Return 1 if required free bytes are available, 0 otherwise.
+ */
+static inline int mpq_dmx_check_decoder_fullness(
+ struct mpq_streambuffer *sbuff,
+ size_t required_space)
+{
+ ssize_t free = mpq_streambuffer_data_free(sbuff);
+ ssize_t free_meta = mpq_streambuffer_metadata_free(sbuff);
+
+ /* Verify meta-data buffer can contain at least 1 packet */
+ if (free_meta < VIDEO_META_DATA_PACKET_SIZE)
+ return 0;
+
+ /*
+ * For linear buffers, verify there's enough space for this TSP
+ * and an additional buffer is free, as framing might required one
+ * more buffer to be available.
+ */
+ if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR)
+ return (free >= required_space &&
+ sbuff->pending_buffers_count < sbuff->buffers_num-1);
+ else
+ /* Ring buffer mode */
+ return (free >= required_space);
+}
+
+/**
+ * Checks whether decoder's output buffer has free space
+ * for specific number of bytes, if not, the function waits
+ * until the amount of free-space is available.
+ *
+ * @feed: decoder's feed object
+ * @required_space: number of required free bytes in the buffer
+ * @lock_feed: indicates whether mutex should be held before
+ * accessing the feed information. If the caller of this function
+ * already holds a mutex then this should be set to 0 and 1 otherwise.
+ *
+ * Return 0 if required space is available and error code
+ * in case waiting on buffer fullness was aborted.
+ */
+static int mpq_dmx_decoder_fullness_check(
+ struct dvb_demux_feed *feed,
+ size_t required_space,
+ int lock_feed)
+{
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+ struct mpq_streambuffer *sbuff = NULL;
+ struct mpq_video_feed_info *feed_data;
+ struct mpq_feed *mpq_feed;
+ int ret = 0;
+
+ if (!dvb_dmx_is_video_feed(feed)) {
+ MPQ_DVB_DBG_PRINT("%s: Invalid feed type %d\n",
+ __func__,
+ feed->pes_type);
+ return -EINVAL;
+ }
+
+ if (lock_feed) {
+ mutex_lock(&mpq_demux->mutex);
+ } else if (!mutex_is_locked(&mpq_demux->mutex)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Mutex should have been locked\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
+
+ sbuff = feed_data->video_buffer;
+ if (sbuff == NULL) {
+ if (lock_feed)
+ mutex_unlock(&mpq_demux->mutex);
+ MPQ_DVB_ERR_PRINT("%s: mpq_streambuffer object is NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if ((feed_data->video_buffer != NULL) &&
+ (!feed_data->fullness_wait_cancel) &&
+ (!mpq_dmx_check_decoder_fullness(sbuff, required_space))) {
+ DEFINE_WAIT(__wait);
+
+ for (;;) {
+ prepare_to_wait(&sbuff->raw_data.queue,
+ &__wait,
+ TASK_INTERRUPTIBLE);
+ if (!feed_data->video_buffer ||
+ feed_data->fullness_wait_cancel ||
+ mpq_dmx_check_decoder_fullness(sbuff,
+ required_space))
+ break;
+
+ if (!signal_pending(current)) {
+ mutex_unlock(&mpq_demux->mutex);
+ schedule();
+ mutex_lock(&mpq_demux->mutex);
+ continue;
+ }
+
+ ret = -ERESTARTSYS;
+ break;
+ }
+ finish_wait(&sbuff->raw_data.queue, &__wait);
+ }
+
+ if (ret < 0) {
+ if (lock_feed)
+ mutex_unlock(&mpq_demux->mutex);
+ return ret;
+ }
+
+ if ((feed_data->fullness_wait_cancel) ||
+ (feed_data->video_buffer == NULL)) {
+ if (lock_feed)
+ mutex_unlock(&mpq_demux->mutex);
+ return -EINVAL;
+ }
+
+ if (lock_feed)
+ mutex_unlock(&mpq_demux->mutex);
+ return 0;
+}
+
+int mpq_dmx_decoder_fullness_wait(
+ struct dvb_demux_feed *feed,
+ size_t required_space)
+{
+ return mpq_dmx_decoder_fullness_check(feed, required_space, 1);
+}
+
+int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed)
+{
+ if (dvb_dmx_is_video_feed(feed)) {
+ struct mpq_feed *mpq_feed;
+ struct mpq_video_feed_info *feed_data;
+ struct dvb_ringbuffer *video_buff;
+
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
+
+ feed_data->fullness_wait_cancel = 1;
+
+ spin_lock(&feed_data->video_buffer_lock);
+ if (feed_data->video_buffer == NULL) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: video_buffer released\n",
+ __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ video_buff = &feed_data->video_buffer->raw_data;
+ wake_up_all(&video_buff->queue);
+ spin_unlock(&feed_data->video_buffer_lock);
+
+ return 0;
+ }
+
+ /* else */
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid feed type %d\n",
+ __func__,
+ feed->pes_type);
+
+ return -EINVAL;
+}
+
+int mpq_dmx_parse_mandatory_pes_header(
+ struct dvb_demux_feed *feed,
+ struct mpq_video_feed_info *feed_data,
+ struct pes_packet_header *pes_header,
+ const u8 *buf,
+ u32 *ts_payload_offset,
+ int *bytes_avail)
+{
+ int left_size, copy_len;
+
+ if (feed_data->pes_header_offset < PES_MANDATORY_FIELDS_LEN) {
+ left_size =
+ PES_MANDATORY_FIELDS_LEN -
+ feed_data->pes_header_offset;
+
+ copy_len = (left_size > *bytes_avail) ?
+ *bytes_avail :
+ left_size;
+
+ memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset),
+ (buf + *ts_payload_offset),
+ copy_len);
+
+ feed_data->pes_header_offset += copy_len;
+
+ if (left_size > *bytes_avail)
+ return -EINVAL;
+
+ /* else - we have beginning of PES header */
+ *bytes_avail -= left_size;
+ *ts_payload_offset += left_size;
+
+ /* Make sure the PES packet is valid */
+ if (mpq_dmx_is_valid_video_pes(pes_header) < 0) {
+ /*
+ * Since the new PES header parsing
+ * failed, reset pusi_seen to drop all
+ * data until next PUSI
+ */
+ feed->pusi_seen = 0;
+ feed_data->pes_header_offset = 0;
+
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid packet\n",
+ __func__);
+
+ return -EINVAL;
+ }
+
+ feed_data->pes_header_left_bytes =
+ pes_header->pes_header_data_length;
+ }
+
+ return 0;
+}
+
+static inline void mpq_dmx_get_pts_dts(struct mpq_video_feed_info *feed_data,
+ struct pes_packet_header *pes_header)
+{
+ struct dmx_pts_dts_info *info = &(feed_data->new_pts_dts_info);
+
+ /* Get PTS/DTS information from PES header */
+
+ if ((pes_header->pts_dts_flag == 2) ||
+ (pes_header->pts_dts_flag == 3)) {
+ info->pts_exist = 1;
+
+ info->pts =
+ ((u64)pes_header->pts_1 << 30) |
+ ((u64)pes_header->pts_2 << 22) |
+ ((u64)pes_header->pts_3 << 15) |
+ ((u64)pes_header->pts_4 << 7) |
+ (u64)pes_header->pts_5;
+ } else {
+ info->pts_exist = 0;
+ info->pts = 0;
+ }
+
+ if (pes_header->pts_dts_flag == 3) {
+ info->dts_exist = 1;
+
+ info->dts =
+ ((u64)pes_header->dts_1 << 30) |
+ ((u64)pes_header->dts_2 << 22) |
+ ((u64)pes_header->dts_3 << 15) |
+ ((u64)pes_header->dts_4 << 7) |
+ (u64)pes_header->dts_5;
+ } else {
+ info->dts_exist = 0;
+ info->dts = 0;
+ }
+
+ feed_data->new_info_exists = 1;
+}
+
+int mpq_dmx_parse_remaining_pes_header(
+ struct dvb_demux_feed *feed,
+ struct mpq_video_feed_info *feed_data,
+ struct pes_packet_header *pes_header,
+ const u8 *buf,
+ u32 *ts_payload_offset,
+ int *bytes_avail)
+{
+ int left_size, copy_len;
+
+ /* Remaining header bytes that need to be processed? */
+ if (!feed_data->pes_header_left_bytes)
+ return 0;
+
+ /* Did we capture the PTS value (if exists)? */
+ if ((*bytes_avail != 0) &&
+ (feed_data->pes_header_offset <
+ (PES_MANDATORY_FIELDS_LEN+5)) &&
+ ((pes_header->pts_dts_flag == 2) ||
+ (pes_header->pts_dts_flag == 3))) {
+
+ /* 5 more bytes should be there */
+ left_size =
+ PES_MANDATORY_FIELDS_LEN + 5 -
+ feed_data->pes_header_offset;
+
+ copy_len = (left_size > *bytes_avail) ?
+ *bytes_avail :
+ left_size;
+
+ memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset),
+ (buf + *ts_payload_offset),
+ copy_len);
+
+ feed_data->pes_header_offset += copy_len;
+ feed_data->pes_header_left_bytes -= copy_len;
+
+ if (left_size > *bytes_avail)
+ return -EINVAL;
+
+ /* else - we have the PTS */
+ *bytes_avail -= copy_len;
+ *ts_payload_offset += copy_len;
+ }
+
+ /* Did we capture the DTS value (if exist)? */
+ if ((*bytes_avail != 0) &&
+ (feed_data->pes_header_offset <
+ (PES_MANDATORY_FIELDS_LEN+10)) &&
+ (pes_header->pts_dts_flag == 3)) {
+
+ /* 5 more bytes should be there */
+ left_size =
+ PES_MANDATORY_FIELDS_LEN + 10 -
+ feed_data->pes_header_offset;
+
+ copy_len = (left_size > *bytes_avail) ?
+ *bytes_avail :
+ left_size;
+
+ memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset),
+ (buf + *ts_payload_offset),
+ copy_len);
+
+ feed_data->pes_header_offset += copy_len;
+ feed_data->pes_header_left_bytes -= copy_len;
+
+ if (left_size > *bytes_avail)
+ return -EINVAL;
+
+ /* else - we have the DTS */
+ *bytes_avail -= copy_len;
+ *ts_payload_offset += copy_len;
+ }
+
+ /* Any more header bytes?! */
+ if (feed_data->pes_header_left_bytes >= *bytes_avail) {
+ feed_data->pes_header_left_bytes -= *bytes_avail;
+ return -EINVAL;
+ }
+
+ /* get PTS/DTS information from PES header to be written later */
+ mpq_dmx_get_pts_dts(feed_data, pes_header);
+
+ /* Got PES header, process payload */
+ *bytes_avail -= feed_data->pes_header_left_bytes;
+ *ts_payload_offset += feed_data->pes_header_left_bytes;
+ feed_data->pes_header_left_bytes = 0;
+
+ return 0;
+}
+
+static void mpq_dmx_check_continuity(struct mpq_video_feed_info *feed_data,
+ int current_continuity,
+ int discontinuity_indicator)
+{
+ const int max_continuity = 0x0F; /* 4 bits in the TS packet header */
+
+ /* sanity check */
+ if (unlikely((current_continuity < 0) ||
+ (current_continuity > max_continuity))) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: received invalid continuity counter value %d\n",
+ __func__, current_continuity);
+ return;
+ }
+
+ /* reset last continuity */
+ if ((feed_data->last_continuity == -1) ||
+ (discontinuity_indicator)) {
+ feed_data->last_continuity = current_continuity;
+ return;
+ }
+
+ /* check for continuity errors */
+ if (current_continuity !=
+ ((feed_data->last_continuity + 1) & max_continuity))
+ feed_data->continuity_errs++;
+
+ /* save for next time */
+ feed_data->last_continuity = current_continuity;
+}
+
+static inline void mpq_dmx_prepare_es_event_data(
+ struct mpq_streambuffer_packet_header *packet,
+ struct mpq_adapter_video_meta_data *meta_data,
+ struct mpq_video_feed_info *feed_data,
+ struct mpq_streambuffer *stream_buffer,
+ struct dmx_data_ready *data,
+ int cookie)
+{
+ struct dmx_pts_dts_info *pts_dts;
+
+ if (meta_data->packet_type == DMX_PES_PACKET) {
+ pts_dts = &meta_data->info.pes.pts_dts_info;
+ data->buf.stc = meta_data->info.pes.stc;
+ } else {
+ pts_dts = &meta_data->info.framing.pts_dts_info;
+ data->buf.stc = meta_data->info.framing.stc;
+ }
+
+ pts_dts = meta_data->packet_type == DMX_PES_PACKET ?
+ &meta_data->info.pes.pts_dts_info :
+ &meta_data->info.framing.pts_dts_info;
+
+ data->data_length = 0;
+ data->buf.handle = packet->raw_data_handle;
+ data->buf.cookie = cookie;
+ data->buf.offset = packet->raw_data_offset;
+ data->buf.len = packet->raw_data_len;
+ data->buf.pts_exists = pts_dts->pts_exist;
+ data->buf.pts = pts_dts->pts;
+ data->buf.dts_exists = pts_dts->dts_exist;
+ data->buf.dts = pts_dts->dts;
+ data->buf.tei_counter = feed_data->tei_errs;
+ data->buf.cont_err_counter = feed_data->continuity_errs;
+ data->buf.ts_packets_num = feed_data->ts_packets_num;
+ data->buf.ts_dropped_bytes = feed_data->ts_dropped_bytes;
+ data->status = DMX_OK_DECODER_BUF;
+
+ MPQ_DVB_DBG_PRINT("%s: cookie=%d\n", __func__, data->buf.cookie);
+
+ /* reset counters */
+ feed_data->ts_packets_num = 0;
+ feed_data->ts_dropped_bytes = 0;
+ feed_data->tei_errs = 0;
+ feed_data->continuity_errs = 0;
+}
+
+static int mpq_sdmx_dvr_buffer_desc(struct mpq_demux *mpq_demux,
+ struct sdmx_buff_descr *buf_desc)
+{
+ struct dvb_ringbuffer *rbuf = (struct dvb_ringbuffer *)
+ mpq_demux->demux.dmx.dvr_input.ringbuff;
+ struct ion_handle *ion_handle =
+ mpq_demux->demux.dmx.dvr_input.priv_handle;
+ ion_phys_addr_t phys_addr;
+ size_t len;
+ int ret;
+
+ ret = ion_phys(mpq_demux->ion_client, ion_handle, &phys_addr, &len);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to obtain physical address of input buffer. ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ buf_desc->base_addr = (u64)phys_addr;
+ buf_desc->size = rbuf->size;
+
+ return 0;
+}
+
+static inline int mpq_dmx_notify_overflow(struct dvb_demux_feed *feed)
+{
+ struct dmx_data_ready data;
+
+ data.data_length = 0;
+ data.status = DMX_OVERRUN_ERROR;
+ return feed->data_ready_cb.ts(&feed->feed.ts, &data);
+}
+
+/**
+ * mpq_dmx_decoder_frame_closure - Helper function to handle closing current
+ * pending frame upon reaching EOS.
+ *
+ * @mpq_demux - mpq demux instance
+ * @mpq_feed - mpq feed object
+ */
+static void mpq_dmx_decoder_frame_closure(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed)
+{
+ struct mpq_streambuffer_packet_header packet;
+ struct mpq_streambuffer *stream_buffer;
+ struct mpq_adapter_video_meta_data meta_data;
+ struct mpq_video_feed_info *feed_data;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct dmx_data_ready data;
+ int cookie;
+
+ feed_data = &mpq_feed->video_info;
+
+ /*
+ * spin-lock is taken to protect against manipulation of video
+ * output buffer by the API (terminate video feed, re-use of video
+ * buffers).
+ */
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+
+ if (stream_buffer == NULL) {
+ MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return;
+ }
+
+ /* Report last pattern found */
+ if ((feed_data->pending_pattern_len) &&
+ mpq_dmx_is_video_frame(feed->video_codec,
+ feed_data->last_framing_match_type)) {
+ meta_data.packet_type = DMX_FRAMING_INFO_PACKET;
+ mpq_dmx_write_pts_dts(feed_data,
+ &(meta_data.info.framing.pts_dts_info));
+ mpq_dmx_save_pts_dts(feed_data);
+ packet.user_data_len =
+ sizeof(struct mpq_adapter_video_meta_data);
+ packet.raw_data_len = feed_data->pending_pattern_len;
+ packet.raw_data_offset = feed_data->frame_offset;
+ meta_data.info.framing.pattern_type =
+ feed_data->last_framing_match_type;
+ meta_data.info.framing.stc = feed_data->last_framing_match_stc;
+ meta_data.info.framing.continuity_error_counter =
+ feed_data->continuity_errs;
+ meta_data.info.framing.transport_error_indicator_counter =
+ feed_data->tei_errs;
+ meta_data.info.framing.ts_dropped_bytes =
+ feed_data->ts_dropped_bytes;
+ meta_data.info.framing.ts_packets_num =
+ feed_data->ts_packets_num;
+
+ mpq_streambuffer_get_buffer_handle(stream_buffer,
+ 0, /* current write buffer handle */
+ &packet.raw_data_handle);
+
+ mpq_dmx_update_decoder_stat(mpq_feed);
+
+ /* Writing meta-data that includes the framing information */
+ cookie = mpq_streambuffer_pkt_write(stream_buffer, &packet,
+ (u8 *)&meta_data);
+ if (cookie >= 0) {
+ mpq_dmx_prepare_es_event_data(&packet, &meta_data,
+ feed_data, stream_buffer, &data, cookie);
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ } else {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
+ __func__, cookie);
+ }
+ }
+
+ spin_unlock(&feed_data->video_buffer_lock);
+}
+
+/**
+ * mpq_dmx_decoder_pes_closure - Helper function to handle closing current PES
+ * upon reaching EOS.
+ *
+ * @mpq_demux - mpq demux instance
+ * @mpq_feed - mpq feed object
+ */
+static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed)
+{
+ struct mpq_streambuffer_packet_header packet;
+ struct mpq_streambuffer *stream_buffer;
+ struct mpq_adapter_video_meta_data meta_data;
+ struct mpq_video_feed_info *feed_data;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct dmx_data_ready data;
+ int cookie;
+
+ feed_data = &mpq_feed->video_info;
+
+ /*
+ * spin-lock is taken to protect against manipulation of video
+ * output buffer by the API (terminate video feed, re-use of video
+ * buffers).
+ */
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+
+ if (stream_buffer == NULL) {
+ MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return;
+ }
+
+ /*
+ * Close previous PES.
+ * Push new packet to the meta-data buffer.
+ */
+ if ((feed->pusi_seen) && (feed_data->pes_header_left_bytes == 0)) {
+ packet.raw_data_len = feed->peslen;
+ mpq_streambuffer_get_buffer_handle(stream_buffer,
+ 0, /* current write buffer handle */
+ &packet.raw_data_handle);
+ packet.raw_data_offset = feed_data->frame_offset;
+ packet.user_data_len =
+ sizeof(struct mpq_adapter_video_meta_data);
+
+ mpq_dmx_write_pts_dts(feed_data,
+ &(meta_data.info.pes.pts_dts_info));
+
+ meta_data.packet_type = DMX_PES_PACKET;
+ meta_data.info.pes.stc = feed_data->prev_stc;
+
+ mpq_dmx_update_decoder_stat(mpq_feed);
+
+ cookie = mpq_streambuffer_pkt_write(stream_buffer, &packet,
+ (u8 *)&meta_data);
+ if (cookie >= 0) {
+ /* Save write offset where new PES will begin */
+ mpq_streambuffer_get_data_rw_offset(stream_buffer, NULL,
+ &feed_data->frame_offset);
+ mpq_dmx_prepare_es_event_data(&packet, &meta_data,
+ feed_data, stream_buffer, &data, cookie);
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ } else {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
+ __func__, cookie);
+ }
+ }
+ /* Reset PES info */
+ feed->peslen = 0;
+ feed_data->pes_header_offset = 0;
+ feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN;
+
+ spin_unlock(&feed_data->video_buffer_lock);
+}
+
+static int mpq_dmx_process_video_packet_framing(
+ struct dvb_demux_feed *feed,
+ const u8 *buf,
+ u64 curr_stc)
+{
+ int bytes_avail;
+ u32 ts_payload_offset;
+ struct mpq_video_feed_info *feed_data;
+ const struct ts_packet_header *ts_header;
+ struct mpq_streambuffer *stream_buffer;
+ struct pes_packet_header *pes_header;
+ struct mpq_demux *mpq_demux;
+ struct mpq_feed *mpq_feed;
+
+ struct dvb_dmx_video_patterns_results framing_res;
+ struct mpq_streambuffer_packet_header packet;
+ struct mpq_adapter_video_meta_data meta_data;
+ int bytes_written = 0;
+ int bytes_to_write = 0;
+ int found_patterns = 0;
+ int first_pattern = 0;
+ int i;
+ int is_video_frame = 0;
+ int pending_data_len = 0;
+ int ret = 0;
+ int discontinuity_indicator = 0;
+ struct dmx_data_ready data;
+
+ mpq_demux = feed->demux->priv;
+
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
+
+ /*
+ * spin-lock is taken to protect against manipulation of video
+ * output buffer by the API (terminate video feed, re-use of video
+ * buffers). Mutex on the video-feed cannot be held here
+ * since SW demux holds a spin-lock while calling write_to_decoder
+ */
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+
+ if (stream_buffer == NULL) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: video_buffer released\n",
+ __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ ts_header = (const struct ts_packet_header *)buf;
+
+ pes_header = &feed_data->pes_header;
+
+ /* Make sure this TS packet has a payload and not scrambled */
+ if ((ts_header->sync_byte != 0x47) ||
+ (ts_header->adaptation_field_control == 0) ||
+ (ts_header->adaptation_field_control == 2) ||
+ (ts_header->transport_scrambling_control)) {
+ /* continue to next packet */
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ if (ts_header->payload_unit_start_indicator) { /* PUSI? */
+ if (feed->pusi_seen) { /* Did we see PUSI before? */
+ /*
+ * Double check that we are not in middle of
+ * previous PES header parsing.
+ */
+ if (feed_data->pes_header_left_bytes != 0)
+ MPQ_DVB_ERR_PRINT(
+ "%s: received PUSI while handling PES header of previous PES\n",
+ __func__);
+
+ feed->peslen = 0;
+ feed_data->pes_header_offset = 0;
+ feed_data->pes_header_left_bytes =
+ PES_MANDATORY_FIELDS_LEN;
+ } else {
+ feed->pusi_seen = 1;
+ }
+ }
+
+ /*
+ * Parse PES data only if PUSI was encountered,
+ * otherwise the data is dropped
+ */
+ if (!feed->pusi_seen) {
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0; /* drop and wait for next packets */
+ }
+
+ ts_payload_offset = sizeof(struct ts_packet_header);
+
+ /*
+ * Skip adaptation field if exists.
+ * Save discontinuity indicator if exists.
+ */
+ if (ts_header->adaptation_field_control == 3) {
+ const struct ts_adaptation_field *adaptation_field =
+ (const struct ts_adaptation_field *)(buf +
+ ts_payload_offset);
+
+ discontinuity_indicator =
+ adaptation_field->discontinuity_indicator;
+ ts_payload_offset += buf[ts_payload_offset] + 1;
+ }
+
+ bytes_avail = TS_PACKET_SIZE - ts_payload_offset;
+
+ /* Get the mandatory fields of the video PES header */
+ if (mpq_dmx_parse_mandatory_pes_header(feed, feed_data,
+ pes_header, buf,
+ &ts_payload_offset,
+ &bytes_avail)) {
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ if (mpq_dmx_parse_remaining_pes_header(feed, feed_data,
+ pes_header, buf,
+ &ts_payload_offset,
+ &bytes_avail)) {
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ /*
+ * If we reached here,
+ * then we are now at the PES payload data
+ */
+ if (bytes_avail == 0) {
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ /*
+ * the decoder requires demux to do framing,
+ * so search for the patterns now.
+ */
+ found_patterns = dvb_dmx_video_pattern_search(
+ feed_data->patterns,
+ feed_data->patterns_num,
+ (buf + ts_payload_offset),
+ bytes_avail,
+ &feed_data->prefix_size,
+ &framing_res);
+
+ if (!feed_data->found_sequence_header_pattern) {
+ for (i = 0; i < found_patterns; i++) {
+ if ((framing_res.info[i].type ==
+ DMX_IDX_MPEG_SEQ_HEADER) ||
+ (framing_res.info[i].type ==
+ DMX_IDX_H264_SPS) ||
+ (framing_res.info[i].type ==
+ DMX_IDX_VC1_SEQ_HEADER)) {
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Found Sequence Pattern, buf %p, i = %d, offset = %d, type = %lld\n",
+ __func__, buf, i,
+ framing_res.info[i].offset,
+ framing_res.info[i].type);
+
+ first_pattern = i;
+ feed_data->found_sequence_header_pattern = 1;
+ ts_payload_offset +=
+ framing_res.info[i].offset;
+ bytes_avail -= framing_res.info[i].offset;
+
+ if (framing_res.info[i].used_prefix_size) {
+ feed_data->first_prefix_size =
+ framing_res.info[i].
+ used_prefix_size;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * If decoder requires demux to do framing,
+ * pass data to decoder only after sequence header
+ * or equivalent is found. Otherwise the data is dropped.
+ */
+ if (!feed_data->found_sequence_header_pattern) {
+ feed_data->prev_stc = curr_stc;
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ /* Update error counters based on TS header */
+ feed_data->ts_packets_num++;
+ feed_data->tei_errs += ts_header->transport_error_indicator;
+ mpq_demux->decoder_stat[feed_data->stream_interface].ts_errors +=
+ ts_header->transport_error_indicator;
+ mpq_dmx_check_continuity(feed_data,
+ ts_header->continuity_counter,
+ discontinuity_indicator);
+ mpq_demux->decoder_stat[feed_data->stream_interface].cc_errors +=
+ feed_data->continuity_errs;
+
+ /* Need to back-up the PTS information of the very first frame */
+ if (feed_data->first_pts_dts_copy) {
+ for (i = first_pattern; i < found_patterns; i++) {
+ is_video_frame = mpq_dmx_is_video_frame(
+ feed->video_codec,
+ framing_res.info[i].type);
+
+ if (is_video_frame == 1) {
+ mpq_dmx_save_pts_dts(feed_data);
+ feed_data->first_pts_dts_copy = 0;
+ break;
+ }
+ }
+ }
+
+ /*
+ * write prefix used to find first Sequence pattern, if needed.
+ * feed_data->patterns[0]->pattern always contains the sequence
+ * header pattern.
+ */
+ if (feed_data->first_prefix_size) {
+ ret = mpq_streambuffer_data_write(stream_buffer,
+ feed_data->patterns[0]->pattern,
+ feed_data->first_prefix_size);
+ if (ret < 0) {
+ mpq_demux->decoder_stat
+ [feed_data->stream_interface].drop_count +=
+ feed_data->first_prefix_size;
+ feed_data->ts_dropped_bytes +=
+ feed_data->first_prefix_size;
+ MPQ_DVB_DBG_PRINT("%s: could not write prefix\n",
+ __func__);
+ if (ret == -ENOSPC)
+ mpq_dmx_notify_overflow(feed);
+ } else {
+ MPQ_DVB_DBG_PRINT(
+ "%s: Writing pattern prefix of size %d\n",
+ __func__, feed_data->first_prefix_size);
+ /*
+ * update the length of the data we report
+ * to include the size of the prefix that was used.
+ */
+ feed_data->pending_pattern_len +=
+ feed_data->first_prefix_size;
+ }
+ }
+
+ feed->peslen += bytes_avail;
+ pending_data_len += bytes_avail;
+
+ meta_data.packet_type = DMX_FRAMING_INFO_PACKET;
+ packet.user_data_len = sizeof(struct mpq_adapter_video_meta_data);
+
+ /*
+ * Go over all the patterns that were found in this packet.
+ * For each pattern found, write the relevant data to the data
+ * buffer, then write the respective meta-data.
+ * Each pattern can only be reported when the next pattern is found
+ * (in order to know the data length).
+ * There are three possible cases for each pattern:
+ * 1. This is the very first pattern we found in any TS packet in this
+ * feed.
+ * 2. This is the first pattern found in this TS packet, but we've
+ * already found patterns in previous packets.
+ * 3. This is not the first pattern in this packet, i.e., we've
+ * already found patterns in this TS packet.
+ */
+ for (i = first_pattern; i < found_patterns; i++) {
+ if (i == first_pattern) {
+ /*
+ * The way to identify the very first pattern:
+ * 1. It's the first pattern found in this packet.
+ * 2. The pending_pattern_len, which indicates the
+ * data length of the previous pattern that has
+ * not yet been reported, is usually 0. However,
+ * it may be larger than 0 if a prefix was used
+ * to find this pattern (i.e., the pattern was
+ * split over two TS packets). In that case,
+ * pending_pattern_len equals first_prefix_size.
+ * first_prefix_size is set to 0 later in this
+ * function.
+ */
+ if (feed_data->first_prefix_size ==
+ feed_data->pending_pattern_len) {
+ /*
+ * This is the very first pattern, so no
+ * previous pending frame data exists.
+ * Update frame info and skip to the
+ * next frame.
+ */
+ feed_data->last_framing_match_type =
+ framing_res.info[i].type;
+ feed_data->last_pattern_offset =
+ framing_res.info[i].offset;
+ if (framing_res.info[i].used_prefix_size)
+ feed_data->last_framing_match_stc =
+ feed_data->prev_stc;
+ else
+ feed_data->last_framing_match_stc =
+ curr_stc;
+ continue;
+ }
+ /*
+ * This is the first pattern in this
+ * packet and previous frame from
+ * previous packet is pending for report
+ */
+ bytes_to_write = framing_res.info[i].offset;
+ } else {
+ /* Previous pending frame is in the same packet */
+ bytes_to_write =
+ framing_res.info[i].offset -
+ feed_data->last_pattern_offset;
+ }
+
+ ret = mpq_streambuffer_data_write(
+ stream_buffer,
+ (buf + ts_payload_offset + bytes_written),
+ bytes_to_write);
+ if (ret < 0) {
+ mpq_demux->decoder_stat
+ [feed_data->stream_interface].drop_count +=
+ bytes_to_write;
+ feed_data->ts_dropped_bytes += bytes_to_write;
+ MPQ_DVB_DBG_PRINT(
+ "%s: Couldn't write %d bytes to data buffer, ret=%d\n",
+ __func__, bytes_to_write, ret);
+ if (ret == -ENOSPC)
+ mpq_dmx_notify_overflow(feed);
+ } else {
+ bytes_written += bytes_to_write;
+ pending_data_len -= bytes_to_write;
+ feed_data->pending_pattern_len += bytes_to_write;
+ }
+
+ is_video_frame = mpq_dmx_is_video_frame(
+ feed->video_codec,
+ feed_data->last_framing_match_type);
+ if (is_video_frame == 1) {
+ mpq_dmx_write_pts_dts(feed_data,
+ &(meta_data.info.framing.pts_dts_info));
+ mpq_dmx_save_pts_dts(feed_data);
+
+ packet.raw_data_len = feed_data->pending_pattern_len -
+ framing_res.info[i].used_prefix_size;
+ packet.raw_data_offset = feed_data->frame_offset;
+ meta_data.info.framing.pattern_type =
+ feed_data->last_framing_match_type;
+ meta_data.info.framing.stc =
+ feed_data->last_framing_match_stc;
+ meta_data.info.framing.continuity_error_counter =
+ feed_data->continuity_errs;
+ meta_data.info.framing.
+ transport_error_indicator_counter =
+ feed_data->tei_errs;
+ meta_data.info.framing.ts_dropped_bytes =
+ feed_data->ts_dropped_bytes;
+ meta_data.info.framing.ts_packets_num =
+ feed_data->ts_packets_num;
+
+ mpq_streambuffer_get_buffer_handle(
+ stream_buffer,
+ 0, /* current write buffer handle */
+ &packet.raw_data_handle);
+
+ mpq_dmx_update_decoder_stat(mpq_feed);
+
+ /*
+ * Write meta-data that includes the framing information
+ */
+ ret = mpq_streambuffer_pkt_write(stream_buffer, &packet,
+ (u8 *)&meta_data);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
+ __func__, ret);
+ if (ret == -ENOSPC)
+ mpq_dmx_notify_overflow(feed);
+ } else {
+ mpq_dmx_prepare_es_event_data(
+ &packet, &meta_data, feed_data,
+ stream_buffer, &data, ret);
+
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+
+ if (feed_data->video_buffer->mode ==
+ MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR)
+ feed_data->frame_offset = 0;
+ else
+ mpq_streambuffer_get_data_rw_offset(
+ feed_data->video_buffer,
+ NULL,
+ &feed_data->frame_offset);
+ }
+
+ /*
+ * In linear buffers, after writing the packet
+ * we switched over to a new linear buffer for the new
+ * frame. In that case, we should re-write the prefix
+ * of the existing frame if any exists.
+ */
+ if ((MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR ==
+ feed_data->video_buffer->mode) &&
+ framing_res.info[i].used_prefix_size) {
+ ret = mpq_streambuffer_data_write(stream_buffer,
+ feed_data->prev_pattern +
+ DVB_DMX_MAX_PATTERN_LEN -
+ framing_res.info[i].used_prefix_size,
+ framing_res.info[i].used_prefix_size);
+
+ if (ret < 0) {
+ feed_data->pending_pattern_len = 0;
+ mpq_demux->decoder_stat
+ [feed_data->stream_interface].
+ drop_count += bytes_avail;
+ feed_data->ts_dropped_bytes +=
+ framing_res.info[i].used_prefix_size;
+ if (ret == -ENOSPC)
+ mpq_dmx_notify_overflow(feed);
+ } else {
+ feed_data->pending_pattern_len =
+ framing_res.info[i].used_prefix_size;
+ }
+ } else {
+ s32 offset = (s32)feed_data->frame_offset;
+ u32 buff_size =
+ feed_data->video_buffer->buffers[0].size;
+
+ offset -= framing_res.info[i].used_prefix_size;
+ offset += (offset < 0) ? buff_size : 0;
+ feed_data->pending_pattern_len =
+ framing_res.info[i].used_prefix_size;
+
+ if (MPQ_STREAMBUFFER_BUFFER_MODE_RING ==
+ feed_data->video_buffer->mode) {
+ feed_data->frame_offset = (u32)offset;
+ }
+ }
+ }
+
+ /* save the last match for next time */
+ feed_data->last_framing_match_type =
+ framing_res.info[i].type;
+ feed_data->last_pattern_offset =
+ framing_res.info[i].offset;
+ if (framing_res.info[i].used_prefix_size)
+ feed_data->last_framing_match_stc = feed_data->prev_stc;
+ else
+ feed_data->last_framing_match_stc = curr_stc;
+ }
+
+ feed_data->prev_stc = curr_stc;
+ feed_data->first_prefix_size = 0;
+
+ /*
+ * Save the trailing of the TS packet as we might have a pattern
+ * split that we need to re-use when closing the next
+ * video linear buffer.
+ */
+ if (MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR ==
+ feed_data->video_buffer->mode)
+ memcpy(feed_data->prev_pattern,
+ buf + TS_PACKET_SIZE - DVB_DMX_MAX_PATTERN_LEN,
+ DVB_DMX_MAX_PATTERN_LEN);
+
+ if (pending_data_len) {
+ ret = mpq_streambuffer_data_write(
+ stream_buffer,
+ (buf + ts_payload_offset + bytes_written),
+ pending_data_len);
+
+ if (ret < 0) {
+ mpq_demux->decoder_stat
+ [feed_data->stream_interface].drop_count +=
+ pending_data_len;
+ feed_data->ts_dropped_bytes += pending_data_len;
+ MPQ_DVB_DBG_PRINT(
+ "%s: Couldn't write %d pending bytes to data buffer, ret=%d\n",
+ __func__, pending_data_len, ret);
+ if (ret == -ENOSPC)
+ mpq_dmx_notify_overflow(feed);
+ } else {
+ feed_data->pending_pattern_len += pending_data_len;
+ }
+ }
+
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+}
+
+static int mpq_dmx_process_video_packet_no_framing(
+ struct dvb_demux_feed *feed,
+ const u8 *buf,
+ u64 curr_stc)
+{
+ int bytes_avail;
+ u32 ts_payload_offset;
+ struct mpq_video_feed_info *feed_data;
+ const struct ts_packet_header *ts_header;
+ struct mpq_streambuffer *stream_buffer;
+ struct pes_packet_header *pes_header;
+ struct mpq_demux *mpq_demux;
+ struct mpq_feed *mpq_feed;
+ int discontinuity_indicator = 0;
+ struct dmx_data_ready data;
+ int cookie;
+ int ret;
+
+ mpq_demux = feed->demux->priv;
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
+
+ /*
+ * spin-lock is taken to protect against manipulation of video
+ * output buffer by the API (terminate video feed, re-use of video
+ * buffers). Mutex on the video-feed cannot be held here
+ * since SW demux holds a spin-lock while calling write_to_decoder
+ */
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+ if (stream_buffer == NULL) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: video_buffer released\n",
+ __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ ts_header = (const struct ts_packet_header *)buf;
+
+ pes_header = &feed_data->pes_header;
+
+ /* Make sure this TS packet has a payload and not scrambled */
+ if ((ts_header->sync_byte != 0x47) ||
+ (ts_header->adaptation_field_control == 0) ||
+ (ts_header->adaptation_field_control == 2) ||
+ (ts_header->transport_scrambling_control)) {
+ /* continue to next packet */
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ if (ts_header->payload_unit_start_indicator) { /* PUSI? */
+ if (feed->pusi_seen) { /* Did we see PUSI before? */
+ struct mpq_streambuffer_packet_header packet;
+ struct mpq_adapter_video_meta_data meta_data;
+
+ /*
+ * Close previous PES.
+ * Push new packet to the meta-data buffer.
+ * Double check that we are not in middle of
+ * previous PES header parsing.
+ */
+
+ if (feed_data->pes_header_left_bytes == 0) {
+ packet.raw_data_len = feed->peslen;
+ mpq_streambuffer_get_buffer_handle(
+ stream_buffer,
+ 0, /* current write buffer handle */
+ &packet.raw_data_handle);
+ packet.raw_data_offset =
+ feed_data->frame_offset;
+ packet.user_data_len =
+ sizeof(struct
+ mpq_adapter_video_meta_data);
+
+ mpq_dmx_write_pts_dts(feed_data,
+ &(meta_data.info.pes.pts_dts_info));
+
+ /* Mark that we detected start of new PES */
+ feed_data->first_pts_dts_copy = 1;
+
+ meta_data.packet_type = DMX_PES_PACKET;
+ meta_data.info.pes.stc = feed_data->prev_stc;
+
+ mpq_dmx_update_decoder_stat(mpq_feed);
+
+ cookie = mpq_streambuffer_pkt_write(
+ stream_buffer, &packet,
+ (u8 *)&meta_data);
+ if (cookie < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
+ __func__, cookie);
+ } else {
+ /*
+ * Save write offset where new PES
+ * will begin
+ */
+ mpq_streambuffer_get_data_rw_offset(
+ stream_buffer,
+ NULL,
+ &feed_data->frame_offset);
+
+ mpq_dmx_prepare_es_event_data(
+ &packet, &meta_data,
+ feed_data,
+ stream_buffer, &data, cookie);
+
+ feed->data_ready_cb.ts(&feed->feed.ts,
+ &data);
+ }
+ } else {
+ MPQ_DVB_ERR_PRINT(
+ "%s: received PUSI while handling PES header of previous PES\n",
+ __func__);
+ }
+
+ /* Reset PES info */
+ feed->peslen = 0;
+ feed_data->pes_header_offset = 0;
+ feed_data->pes_header_left_bytes =
+ PES_MANDATORY_FIELDS_LEN;
+ } else {
+ feed->pusi_seen = 1;
+ }
+
+ feed_data->prev_stc = curr_stc;
+ }
+
+ /*
+ * Parse PES data only if PUSI was encountered,
+ * otherwise the data is dropped
+ */
+ if (!feed->pusi_seen) {
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0; /* drop and wait for next packets */
+ }
+
+ ts_payload_offset = sizeof(struct ts_packet_header);
+
+ /*
+ * Skip adaptation field if exists.
+ * Save discontinuity indicator if exists.
+ */
+ if (ts_header->adaptation_field_control == 3) {
+ const struct ts_adaptation_field *adaptation_field =
+ (const struct ts_adaptation_field *)(buf +
+ ts_payload_offset);
+
+ discontinuity_indicator =
+ adaptation_field->discontinuity_indicator;
+ ts_payload_offset += buf[ts_payload_offset] + 1;
+ }
+
+ bytes_avail = TS_PACKET_SIZE - ts_payload_offset;
+
+ /* Get the mandatory fields of the video PES header */
+ if (mpq_dmx_parse_mandatory_pes_header(feed, feed_data,
+ pes_header, buf,
+ &ts_payload_offset,
+ &bytes_avail)) {
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ if (mpq_dmx_parse_remaining_pes_header(feed, feed_data,
+ pes_header, buf,
+ &ts_payload_offset,
+ &bytes_avail)) {
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ /*
+ * If we reached here,
+ * then we are now at the PES payload data
+ */
+ if (bytes_avail == 0) {
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ /*
+ * Need to back-up the PTS information
+ * of the start of new PES
+ */
+ if (feed_data->first_pts_dts_copy) {
+ mpq_dmx_save_pts_dts(feed_data);
+ feed_data->first_pts_dts_copy = 0;
+ }
+
+ /* Update error counters based on TS header */
+ feed_data->ts_packets_num++;
+ feed_data->tei_errs += ts_header->transport_error_indicator;
+ mpq_demux->decoder_stat[feed_data->stream_interface].ts_errors +=
+ ts_header->transport_error_indicator;
+ mpq_dmx_check_continuity(feed_data,
+ ts_header->continuity_counter,
+ discontinuity_indicator);
+ mpq_demux->decoder_stat[feed_data->stream_interface].cc_errors +=
+ feed_data->continuity_errs;
+
+ ret = mpq_streambuffer_data_write(stream_buffer, buf+ts_payload_offset,
+ bytes_avail);
+ if (ret < 0) {
+ mpq_demux->decoder_stat
+ [feed_data->stream_interface].drop_count += bytes_avail;
+ feed_data->ts_dropped_bytes += bytes_avail;
+ if (ret == -ENOSPC)
+ mpq_dmx_notify_overflow(feed);
+ } else {
+ feed->peslen += bytes_avail;
+ }
+
+ spin_unlock(&feed_data->video_buffer_lock);
+
+ return 0;
+}
+
+int mpq_dmx_decoder_buffer_status(struct dvb_demux_feed *feed,
+ struct dmx_buffer_status *dmx_buffer_status)
+{
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+ struct mpq_video_feed_info *feed_data;
+ struct mpq_streambuffer *video_buff;
+ struct mpq_feed *mpq_feed;
+
+ if (!dvb_dmx_is_video_feed(feed)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid feed type %d\n",
+ __func__,
+ feed->pes_type);
+ return -EINVAL;
+ }
+
+ mutex_lock(&mpq_demux->mutex);
+
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
+ video_buff = feed_data->video_buffer;
+ if (!video_buff) {
+ mutex_unlock(&mpq_demux->mutex);
+ return -EINVAL;
+ }
+
+ dmx_buffer_status->error = video_buff->raw_data.error;
+
+ if (video_buff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) {
+ dmx_buffer_status->fullness =
+ video_buff->buffers[0].size *
+ video_buff->pending_buffers_count;
+ dmx_buffer_status->free_bytes =
+ video_buff->buffers[0].size *
+ (video_buff->buffers_num -
+ video_buff->pending_buffers_count);
+ dmx_buffer_status->size =
+ video_buff->buffers[0].size *
+ video_buff->buffers_num;
+ } else {
+ dmx_buffer_status->fullness =
+ mpq_streambuffer_data_avail(video_buff);
+ dmx_buffer_status->free_bytes =
+ mpq_streambuffer_data_free(video_buff);
+ dmx_buffer_status->size = video_buff->buffers[0].size;
+ }
+
+ mpq_streambuffer_get_data_rw_offset(
+ video_buff,
+ &dmx_buffer_status->read_offset,
+ &dmx_buffer_status->write_offset);
+
+ mutex_unlock(&mpq_demux->mutex);
+
+ return 0;
+}
+
+int mpq_dmx_process_video_packet(
+ struct dvb_demux_feed *feed,
+ const u8 *buf)
+{
+ u64 curr_stc;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+
+ if ((mpq_demux->source >= DMX_SOURCE_DVR0) &&
+ (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) {
+ curr_stc = 0;
+ } else {
+ curr_stc = buf[STC_LOCATION_IDX + 2] << 16;
+ curr_stc += buf[STC_LOCATION_IDX + 1] << 8;
+ curr_stc += buf[STC_LOCATION_IDX];
+ curr_stc *= 256; /* convert from 105.47 KHZ to 27MHz */
+ }
+
+ if (!video_framing)
+ return mpq_dmx_process_video_packet_no_framing(feed, buf,
+ curr_stc);
+ else
+ return mpq_dmx_process_video_packet_framing(feed, buf,
+ curr_stc);
+}
+
+int mpq_dmx_extract_pcr_and_dci(const u8 *buf, u64 *pcr, int *dci)
+{
+ const struct ts_packet_header *ts_header;
+ const struct ts_adaptation_field *adaptation_field;
+
+ if (buf == NULL || pcr == NULL || dci == NULL)
+ return 0;
+
+ ts_header = (const struct ts_packet_header *)buf;
+
+ /* Make sure this TS packet has a adaptation field */
+ if ((ts_header->sync_byte != 0x47) ||
+ (ts_header->adaptation_field_control == 0) ||
+ (ts_header->adaptation_field_control == 1) ||
+ ts_header->transport_error_indicator)
+ return 0;
+
+ adaptation_field = (const struct ts_adaptation_field *)
+ (buf + sizeof(struct ts_packet_header));
+
+ if ((!adaptation_field->adaptation_field_length) ||
+ (!adaptation_field->PCR_flag))
+ return 0; /* 0 adaptation field or no PCR */
+
+ *pcr = ((u64)adaptation_field->program_clock_reference_base_1) << 25;
+ *pcr += ((u64)adaptation_field->program_clock_reference_base_2) << 17;
+ *pcr += ((u64)adaptation_field->program_clock_reference_base_3) << 9;
+ *pcr += ((u64)adaptation_field->program_clock_reference_base_4) << 1;
+ *pcr += adaptation_field->program_clock_reference_base_5;
+ *pcr *= 300;
+ *pcr += (((u64)adaptation_field->program_clock_reference_ext_1) << 8) +
+ adaptation_field->program_clock_reference_ext_2;
+
+ *dci = adaptation_field->discontinuity_indicator;
+
+ return 1;
+}
+
+int mpq_dmx_process_pcr_packet(
+ struct dvb_demux_feed *feed,
+ const u8 *buf)
+{
+ u64 stc;
+ struct dmx_data_ready data;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+
+ if (mpq_dmx_extract_pcr_and_dci(buf, &data.pcr.pcr,
+ &data.pcr.disc_indicator_set) == 0)
+ return 0;
+
+ /*
+ * When we play from front-end, we configure HW
+ * to output the extra timestamp, if we are playing
+ * from DVR, we don't have a timestamp if the packet
+ * format is not 192-tail.
+ */
+ if ((mpq_demux->source >= DMX_SOURCE_DVR0) &&
+ (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) {
+ stc = 0;
+ } else {
+ stc = buf[STC_LOCATION_IDX + 2] << 16;
+ stc += buf[STC_LOCATION_IDX + 1] << 8;
+ stc += buf[STC_LOCATION_IDX];
+ stc *= 256; /* convert from 105.47 KHZ to 27MHz */
+ }
+
+ data.data_length = 0;
+ data.pcr.stc = stc;
+ data.status = DMX_OK_PCR;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+
+ return 0;
+}
+
+int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed)
+{
+ struct mpq_video_feed_info *feed_data = &mpq_feed->video_info;
+ struct mpq_streambuffer *stream_buffer;
+ struct mpq_streambuffer_packet_header oob_packet;
+ struct mpq_adapter_video_meta_data oob_meta_data;
+ int ret;
+
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+
+ if (stream_buffer == NULL) {
+ MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ memset(&oob_packet, 0, sizeof(oob_packet));
+ oob_packet.user_data_len = sizeof(oob_meta_data);
+ oob_meta_data.packet_type = DMX_EOS_PACKET;
+
+ ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet,
+ (u8 *)&oob_meta_data);
+
+ spin_unlock(&feed_data->video_buffer_lock);
+ return (ret < 0) ? ret : 0;
+}
+
+void mpq_dmx_convert_tts(struct dvb_demux_feed *feed,
+ const u8 timestamp[TIMESTAMP_LEN],
+ u64 *timestampIn27Mhz)
+{
+ if (unlikely(!timestampIn27Mhz))
+ return;
+
+ *timestampIn27Mhz = timestamp[2] << 16;
+ *timestampIn27Mhz += timestamp[1] << 8;
+ *timestampIn27Mhz += timestamp[0];
+ *timestampIn27Mhz *= 256; /* convert from 105.47 KHZ to 27MHz */
+}
+
+int mpq_sdmx_open_session(struct mpq_demux *mpq_demux)
+{
+ enum sdmx_status ret = SDMX_SUCCESS;
+ enum sdmx_proc_mode proc_mode;
+ enum sdmx_pkt_format pkt_format;
+
+ MPQ_DVB_DBG_PRINT("%s: ref_count %d\n",
+ __func__, mpq_demux->sdmx_session_ref_count);
+
+ if (mpq_demux->sdmx_session_ref_count) {
+ /* session is already open */
+ mpq_demux->sdmx_session_ref_count++;
+ return ret;
+ }
+
+ proc_mode = (mpq_demux->demux.playback_mode == DMX_PB_MODE_PUSH) ?
+ SDMX_PUSH_MODE : SDMX_PULL_MODE;
+ MPQ_DVB_DBG_PRINT(
+ "%s: Proc mode = %s\n",
+ __func__, SDMX_PUSH_MODE == proc_mode ? "Push" : "Pull");
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0) {
+ pkt_format = SDMX_192_BYTE_PKT;
+ } else if (mpq_demux->demux.tsp_format == DMX_TSP_FORMAT_188) {
+ pkt_format = SDMX_188_BYTE_PKT;
+ } else if (mpq_demux->demux.tsp_format == DMX_TSP_FORMAT_192_TAIL) {
+ pkt_format = SDMX_192_BYTE_PKT;
+ } else {
+ MPQ_DVB_ERR_PRINT("%s: invalid tsp format\n", __func__);
+ return -EINVAL;
+ }
+
+ MPQ_DVB_DBG_PRINT("%s: (%s) source, packet format: %d\n",
+ __func__,
+ (mpq_demux->source < DMX_SOURCE_DVR0) ?
+ "frontend" : "DVR", pkt_format);
+
+ /* open session and set configuration */
+ ret = sdmx_open_session(&mpq_demux->sdmx_session_handle);
+ if (ret != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT("%s: Could not open session. ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ MPQ_DVB_DBG_PRINT("%s: new session_handle = %d\n",
+ __func__, mpq_demux->sdmx_session_handle);
+
+ ret = sdmx_set_session_cfg(mpq_demux->sdmx_session_handle,
+ proc_mode,
+ SDMX_PKT_ENC_MODE,
+ pkt_format,
+ mpq_sdmx_scramble_odd,
+ mpq_sdmx_scramble_even);
+ if (ret != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT("%s: Could not set session config. ret=%d\n",
+ __func__, ret);
+ sdmx_close_session(mpq_demux->sdmx_session_handle);
+ mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
+ return -EINVAL;
+ }
+
+ ret = sdmx_set_log_level(mpq_demux->sdmx_session_handle,
+ mpq_demux->sdmx_log_level);
+ if (ret != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT("%s: Could not set log level. ret=%d\n",
+ __func__, ret);
+ /* Don't fail open session if just log level setting failed */
+ ret = 0;
+ }
+
+ mpq_demux->sdmx_process_count = 0;
+ mpq_demux->sdmx_process_time_sum = 0;
+ mpq_demux->sdmx_process_time_average = 0;
+ mpq_demux->sdmx_process_time_max = 0;
+ mpq_demux->sdmx_process_packets_sum = 0;
+ mpq_demux->sdmx_process_packets_average = 0;
+ mpq_demux->sdmx_process_packets_min = 0;
+
+ mpq_demux->sdmx_session_ref_count++;
+ return ret;
+}
+
+int mpq_sdmx_close_session(struct mpq_demux *mpq_demux)
+{
+ int ret = 0;
+ enum sdmx_status status;
+
+ MPQ_DVB_DBG_PRINT("%s: session_handle = %d, ref_count %d\n",
+ __func__,
+ mpq_demux->sdmx_session_handle,
+ mpq_demux->sdmx_session_ref_count);
+
+ if (!mpq_demux->sdmx_session_ref_count)
+ return -EINVAL;
+
+ if (mpq_demux->sdmx_session_ref_count == 1) {
+ status = sdmx_close_session(mpq_demux->sdmx_session_handle);
+ if (status != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT("%s: sdmx_close_session failed %d\n",
+ __func__, status);
+ }
+ mpq_demux->sdmx_eos = 0;
+ mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
+ }
+
+ mpq_demux->sdmx_session_ref_count--;
+
+ return ret;
+}
+
+static int mpq_sdmx_get_buffer_chunks(struct mpq_demux *mpq_demux,
+ struct ion_handle *buff_handle,
+ u32 actual_buff_size,
+ struct sdmx_buff_descr buff_chunks[SDMX_MAX_PHYSICAL_CHUNKS])
+{
+ int i;
+ struct sg_table *sg_ptr;
+ struct scatterlist *sg;
+ u32 chunk_size;
+ int ret;
+
+ memset(buff_chunks, 0,
+ sizeof(struct sdmx_buff_descr) * SDMX_MAX_PHYSICAL_CHUNKS);
+
+ sg_ptr = ion_sg_table(mpq_demux->ion_client, buff_handle);
+ if (IS_ERR_OR_NULL(sg_ptr)) {
+ ret = PTR_ERR(sg_ptr);
+ MPQ_DVB_ERR_PRINT("%s: ion_sg_table failed, ret=%d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -EINVAL;
+ return ret;
+ }
+
+ if (sg_ptr->nents == 0) {
+ MPQ_DVB_ERR_PRINT("%s: num of scattered entries is 0\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (sg_ptr->nents > SDMX_MAX_PHYSICAL_CHUNKS) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: num of scattered entries %d greater than max supported %d\n",
+ __func__, sg_ptr->nents, SDMX_MAX_PHYSICAL_CHUNKS);
+ return -EINVAL;
+ }
+
+ sg = sg_ptr->sgl;
+ for (i = 0; i < sg_ptr->nents; i++) {
+ buff_chunks[i].base_addr = (u64)sg_dma_address(sg);
+
+ if (sg->length > actual_buff_size)
+ chunk_size = actual_buff_size;
+ else
+ chunk_size = sg->length;
+
+ buff_chunks[i].size = chunk_size;
+ sg = sg_next(sg);
+ actual_buff_size -= chunk_size;
+ }
+
+ return 0;
+}
+
+static int mpq_sdmx_init_data_buffer(struct mpq_demux *mpq_demux,
+ struct mpq_feed *feed, u32 *num_buffers,
+ struct sdmx_data_buff_descr buf_desc[DMX_MAX_DECODER_BUFFER_NUM],
+ enum sdmx_buf_mode *buf_mode)
+{
+ struct dvb_demux_feed *dvbdmx_feed = feed->dvb_demux_feed;
+ struct dvb_ringbuffer *buffer;
+ struct mpq_video_feed_info *feed_data = &feed->video_info;
+ struct ion_handle *sdmx_buff;
+ int ret;
+ int i;
+
+ *buf_mode = SDMX_RING_BUF;
+
+ if (dvb_dmx_is_video_feed(feed->dvb_demux_feed)) {
+ if (feed_data->buffer_desc.decoder_buffers_num > 1)
+ *buf_mode = SDMX_LINEAR_GROUP_BUF;
+ *num_buffers = feed_data->buffer_desc.decoder_buffers_num;
+
+ for (i = 0; i < *num_buffers; i++) {
+ buf_desc[i].length =
+ feed_data->buffer_desc.desc[i].size;
+
+ ret = mpq_sdmx_get_buffer_chunks(mpq_demux,
+ feed_data->buffer_desc.ion_handle[i],
+ buf_desc[i].length,
+ buf_desc[i].buff_chunks);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_get_buffer_chunks failed\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+ }
+
+ *num_buffers = 1;
+ if (dvb_dmx_is_sec_feed(dvbdmx_feed) ||
+ dvb_dmx_is_pcr_feed(dvbdmx_feed)) {
+ buffer = &feed->sdmx_buf;
+ sdmx_buff = feed->sdmx_buf_handle;
+ } else {
+ buffer = (struct dvb_ringbuffer *)
+ dvbdmx_feed->feed.ts.buffer.ringbuff;
+ sdmx_buff = dvbdmx_feed->feed.ts.buffer.priv_handle;
+ }
+
+ if (sdmx_buff == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid buffer allocation\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ buf_desc[0].length = buffer->size;
+ ret = mpq_sdmx_get_buffer_chunks(mpq_demux, sdmx_buff,
+ buf_desc[0].length,
+ buf_desc[0].buff_chunks);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_get_buffer_chunks failed\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mpq_sdmx_filter_setup(struct mpq_demux *mpq_demux,
+ struct dvb_demux_feed *dvbdmx_feed)
+{
+ int ret = 0;
+ struct mpq_feed *feed;
+ struct mpq_feed *main_rec_feed = NULL;
+ struct dvb_demux_feed *tmp;
+ struct sdmx_buff_descr metadata_buff_desc;
+ struct sdmx_data_buff_descr *data_buff_desc = NULL;
+ u32 data_buf_num = DMX_MAX_DECODER_BUFFER_NUM;
+ enum sdmx_buf_mode buf_mode;
+ enum sdmx_raw_out_format ts_out_format = SDMX_188_OUTPUT;
+ u32 filter_flags = 0;
+
+ feed = dvbdmx_feed->priv;
+
+ if (dvb_dmx_is_sec_feed(dvbdmx_feed)) {
+ feed->filter_type = SDMX_SECTION_FILTER;
+ if (dvbdmx_feed->feed.sec.check_crc)
+ filter_flags |= SDMX_FILTER_FLAG_VERIFY_SECTION_CRC;
+ MPQ_DVB_DBG_PRINT("%s: SDMX_SECTION_FILTER\n", __func__);
+ } else if (dvb_dmx_is_pcr_feed(dvbdmx_feed)) {
+ feed->filter_type = SDMX_PCR_FILTER;
+ MPQ_DVB_DBG_PRINT("%s: SDMX_PCR_FILTER\n", __func__);
+ } else if (dvb_dmx_is_video_feed(dvbdmx_feed)) {
+ feed->filter_type = SDMX_SEPARATED_PES_FILTER;
+ MPQ_DVB_DBG_PRINT("%s: SDMX_SEPARATED_PES_FILTER\n", __func__);
+ } else if (dvb_dmx_is_rec_feed(dvbdmx_feed)) {
+ feed->filter_type = SDMX_RAW_FILTER;
+ switch (dvbdmx_feed->tsp_out_format) {
+ case (DMX_TSP_FORMAT_188):
+ ts_out_format = SDMX_188_OUTPUT;
+ break;
+ case (DMX_TSP_FORMAT_192_HEAD):
+ ts_out_format = SDMX_192_HEAD_OUTPUT;
+ break;
+ case (DMX_TSP_FORMAT_192_TAIL):
+ ts_out_format = SDMX_192_TAIL_OUTPUT;
+ break;
+ default:
+ MPQ_DVB_ERR_PRINT(
+ "%s: Unsupported TS output format %d\n",
+ __func__, dvbdmx_feed->tsp_out_format);
+ return -EINVAL;
+ }
+ MPQ_DVB_DBG_PRINT("%s: SDMX_RAW_FILTER\n", __func__);
+ } else {
+ feed->filter_type = SDMX_PES_FILTER;
+ MPQ_DVB_DBG_PRINT("%s: SDMX_PES_FILTER\n", __func__);
+ }
+
+ data_buff_desc = vmalloc(
+ sizeof(*data_buff_desc)*DMX_MAX_DECODER_BUFFER_NUM);
+ if (!data_buff_desc) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: failed to allocate memory for data buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /*
+ * Recording feed sdmx filter handle lookup:
+ * In case this is a recording filter with multiple feeds,
+ * this feed is either the first feed of a new recording filter,
+ * or it is another feed of an existing filter for which a filter was
+ * already opened with sdmx. In such case, we need to look up in the
+ * feed pool for a allocated feed with same output buffer (meaning they
+ * belong to the same filter) and to use the already allocated sdmx
+ * filter handle.
+ */
+ if (feed->filter_type == SDMX_RAW_FILTER) {
+ tmp = mpq_dmx_peer_rec_feed(dvbdmx_feed);
+ if (tmp)
+ main_rec_feed = tmp->priv;
+ }
+
+ /*
+ * If this PID is not part of existing recording filter,
+ * configure a new filter to SDMX.
+ */
+ if (!main_rec_feed) {
+ feed->secondary_feed = 0;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Adding new sdmx filter, pid %d, flags=0x%X, ts_out_format=%d\n",
+ __func__, dvbdmx_feed->pid, filter_flags,
+ ts_out_format);
+
+ /* Meta-data initialization,
+ * Recording filters do no need meta-data buffers.
+ */
+ if (dvb_dmx_is_rec_feed(dvbdmx_feed)) {
+ metadata_buff_desc.base_addr = 0;
+ metadata_buff_desc.size = 0;
+ } else {
+ ret = mpq_sdmx_init_metadata_buffer(mpq_demux, feed,
+ &metadata_buff_desc);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to initialize metadata buffer. ret=%d\n",
+ __func__, ret);
+ goto sdmx_filter_setup_failed;
+ }
+ }
+
+ ret = mpq_sdmx_init_data_buffer(mpq_demux, feed, &data_buf_num,
+ data_buff_desc, &buf_mode);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to initialize data buffer. ret=%d\n",
+ __func__, ret);
+ mpq_sdmx_terminate_metadata_buffer(feed);
+ goto sdmx_filter_setup_failed;
+ }
+ ret = sdmx_add_filter(mpq_demux->sdmx_session_handle,
+ dvbdmx_feed->pid,
+ feed->filter_type,
+ &metadata_buff_desc,
+ buf_mode,
+ data_buf_num,
+ data_buff_desc,
+ &feed->sdmx_filter_handle,
+ ts_out_format,
+ filter_flags);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: SDMX_add_filter failed. ret = %d\n",
+ __func__, ret);
+ ret = -ENODEV;
+ mpq_sdmx_terminate_metadata_buffer(feed);
+ goto sdmx_filter_setup_failed;
+ }
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: feed=0x%p, filter pid=%d, handle=%d, data buffer(s)=%d, size=%d\n",
+ __func__, feed, dvbdmx_feed->pid,
+ feed->sdmx_filter_handle,
+ data_buf_num, data_buff_desc[0].length);
+
+ mpq_demux->sdmx_filter_count++;
+ } else {
+ MPQ_DVB_DBG_PRINT(
+ "%s: Adding RAW pid to sdmx, pid %d\n",
+ __func__, dvbdmx_feed->pid);
+
+ feed->secondary_feed = 1;
+ feed->sdmx_filter_handle = main_rec_feed->sdmx_filter_handle;
+ ret = sdmx_add_raw_pid(mpq_demux->sdmx_session_handle,
+ feed->sdmx_filter_handle, dvbdmx_feed->pid);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to add raw pid, ret=%d\n",
+ __func__, ret);
+ ret = -ENODEV;
+ goto sdmx_filter_setup_failed;
+ }
+ }
+
+ /*
+ * If pid has a key ladder id associated, we need to
+ * set it to SDMX.
+ */
+ if (dvbdmx_feed->secure_mode.is_secured &&
+ dvbdmx_feed->cipher_ops.operations_count) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: set key-ladder %d to PID %d\n",
+ __func__,
+ dvbdmx_feed->cipher_ops.operations[0].key_ladder_id,
+ dvbdmx_feed->cipher_ops.pid);
+
+ ret = sdmx_set_kl_ind(mpq_demux->sdmx_session_handle,
+ dvbdmx_feed->cipher_ops.pid,
+ dvbdmx_feed->cipher_ops.operations[0].key_ladder_id);
+
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to set key ladder, ret=%d\n",
+ __func__, ret);
+ }
+ }
+
+ vfree(data_buff_desc);
+ return 0;
+
+sdmx_filter_setup_failed:
+ vfree(data_buff_desc);
+ return ret;
+}
+
+/**
+ * mpq_sdmx_init_feed - initialize secure demux related elements of mpq feed
+ *
+ * @mpq_demux: mpq_demux object
+ * @mpq_feed: mpq_feed object
+ *
+ * Note: the function assumes mpq_demux->mutex locking is done by caller.
+ */
+static int mpq_sdmx_init_feed(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed)
+{
+ int ret;
+
+ ret = mpq_sdmx_open_session(mpq_demux);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_open_session failed, ret=%d\n",
+ __func__, ret);
+
+ ret = -ENODEV;
+ goto init_sdmx_feed_failed;
+ }
+
+ /* PCR and sections have internal buffer for SDMX */
+ if (dvb_dmx_is_pcr_feed(mpq_feed->dvb_demux_feed))
+ ret = mpq_sdmx_alloc_data_buf(mpq_feed, SDMX_PCR_BUFFER_SIZE);
+ else if (dvb_dmx_is_sec_feed(mpq_feed->dvb_demux_feed))
+ ret = mpq_sdmx_alloc_data_buf(mpq_feed,
+ SDMX_SECTION_BUFFER_SIZE);
+ else
+ ret = 0;
+
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("%s: init buffer failed, ret=%d\n",
+ __func__, ret);
+ goto init_sdmx_feed_failed_free_sdmx;
+ }
+
+ ret = mpq_sdmx_filter_setup(mpq_demux, mpq_feed->dvb_demux_feed);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_filter_setup failed, ret=%d\n",
+ __func__, ret);
+ goto init_sdmx_feed_failed_free_data_buff;
+ }
+
+ mpq_demux->num_secure_feeds++;
+ return 0;
+
+init_sdmx_feed_failed_free_data_buff:
+ mpq_sdmx_free_data_buf(mpq_feed);
+init_sdmx_feed_failed_free_sdmx:
+ mpq_sdmx_close_session(mpq_demux);
+init_sdmx_feed_failed:
+ return ret;
+}
+
+int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed)
+{
+ int ret = 0;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+ struct mpq_feed *mpq_feed = feed->priv;
+
+ if (mutex_lock_interruptible(&mpq_demux->mutex))
+ return -ERESTARTSYS;
+
+ mpq_feed->sdmx_buf_handle = NULL;
+ mpq_feed->metadata_buf_handle = NULL;
+ mpq_feed->sdmx_filter_handle = SDMX_INVALID_FILTER_HANDLE;
+
+ if (feed->type != DMX_TYPE_SEC)
+ feed->feed.ts.flush_buffer = mpq_dmx_flush_buffer;
+
+ if (dvb_dmx_is_video_feed(feed)) {
+ ret = mpq_dmx_init_video_feed(mpq_feed);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_init_video_feed failed, ret=%d\n",
+ __func__, ret);
+ goto init_mpq_feed_end;
+ }
+ }
+
+ /*
+ * sdmx is not relevant for recording filters, which always use
+ * regular filters (non-sdmx)
+ */
+ if (!mpq_sdmx_is_loaded() || !feed->secure_mode.is_secured ||
+ dvb_dmx_is_rec_feed(feed)) {
+ if (!mpq_sdmx_is_loaded())
+ mpq_demux->sdmx_session_handle =
+ SDMX_INVALID_SESSION_HANDLE;
+ goto init_mpq_feed_end;
+ }
+
+ /* Initialization of secure demux filters (PES/PCR/Video/Section) */
+ ret = mpq_sdmx_init_feed(mpq_demux, mpq_feed);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_init_feed failed, ret=%d\n",
+ __func__, ret);
+ if (dvb_dmx_is_video_feed(feed))
+ mpq_dmx_terminate_video_feed(mpq_feed);
+ }
+
+init_mpq_feed_end:
+ if (!ret) {
+ mpq_demux->num_active_feeds++;
+ mpq_feed->session_id++;
+ }
+ mutex_unlock(&mpq_demux->mutex);
+ return ret;
+}
+
+/**
+ * Note: Called only when filter is in "GO" state - after feed has been started.
+ */
+int mpq_dmx_set_cipher_ops(struct dvb_demux_feed *feed,
+ struct dmx_cipher_operations *cipher_ops)
+{
+ struct mpq_feed *mpq_feed;
+ struct mpq_demux *mpq_demux;
+ int ret = 0;
+
+ if (!feed || !feed->priv || !cipher_ops) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid parameters\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ MPQ_DVB_DBG_PRINT("%s(%d, %d, %d)\n",
+ __func__, cipher_ops->pid,
+ cipher_ops->operations_count,
+ cipher_ops->operations[0].key_ladder_id);
+
+ if ((cipher_ops->operations_count > 1) ||
+ (cipher_ops->operations_count &&
+ cipher_ops->operations[0].encrypt)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid cipher operations, count=%d, encrypt=%d\n",
+ __func__, cipher_ops->operations_count,
+ cipher_ops->operations[0].encrypt);
+ return -EINVAL;
+ }
+
+ if (!feed->secure_mode.is_secured) {
+ /*
+ * Filter is not configured as secured, setting cipher
+ * operations is not allowed.
+ */
+ MPQ_DVB_ERR_PRINT(
+ "%s: Cannot set cipher operations to non-secure filter\n",
+ __func__);
+ return -EPERM;
+ }
+
+ mpq_feed = feed->priv;
+ mpq_demux = mpq_feed->mpq_demux;
+
+ mutex_lock(&mpq_demux->mutex);
+
+ /*
+ * Feed is running in secure mode, this secure mode request is to
+ * update the key ladder id
+ */
+ if ((mpq_demux->sdmx_session_handle != SDMX_INVALID_SESSION_HANDLE) &&
+ cipher_ops->operations_count) {
+ ret = sdmx_set_kl_ind(mpq_demux->sdmx_session_handle,
+ cipher_ops->pid,
+ cipher_ops->operations[0].key_ladder_id);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to set key ladder, ret=%d\n",
+ __func__, ret);
+ ret = -ENODEV;
+ }
+ }
+
+ mutex_unlock(&mpq_demux->mutex);
+
+ return ret;
+}
+
+static int mpq_sdmx_invalidate_buffer(struct mpq_feed *mpq_feed)
+{
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct mpq_video_feed_info *feed_data;
+ struct dvb_ringbuffer *buffer;
+ struct ion_handle *ion_handle;
+ int ret = 0;
+ int i;
+
+ if (!dvb_dmx_is_video_feed(feed)) {
+ if (dvb_dmx_is_sec_feed(feed) ||
+ dvb_dmx_is_pcr_feed(feed)) {
+ buffer = (struct dvb_ringbuffer *)
+ &mpq_feed->sdmx_buf;
+ ion_handle = mpq_feed->sdmx_buf_handle;
+ } else {
+ buffer = (struct dvb_ringbuffer *)
+ feed->feed.ts.buffer.ringbuff;
+ ion_handle = feed->feed.ts.buffer.priv_handle;
+ }
+
+ ret = msm_ion_do_cache_op(mpq_feed->mpq_demux->ion_client,
+ ion_handle, buffer->data,
+ buffer->size, ION_IOC_INV_CACHES);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: msm_ion_do_cache_op failed, ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Video buffers */
+ feed_data = &mpq_feed->video_info;
+ for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) {
+ if (feed_data->buffer_desc.desc[i].base) {
+ /* Non-secured buffer */
+ ret = msm_ion_do_cache_op(
+ mpq_feed->mpq_demux->ion_client,
+ feed_data->buffer_desc.ion_handle[i],
+ feed_data->buffer_desc.desc[i].base,
+ feed_data->buffer_desc.desc[i].size,
+ ION_IOC_INV_CACHES);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: msm_ion_do_cache_op failed, ret = %d\n",
+ __func__, ret);
+ }
+ }
+
+ return ret;
+}
+
+static void mpq_sdmx_prepare_filter_status(struct mpq_demux *mpq_demux,
+ struct sdmx_filter_status *filter_sts,
+ struct mpq_feed *mpq_feed)
+{
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct mpq_video_feed_info *feed_data;
+ struct mpq_streambuffer *sbuff;
+
+ filter_sts->filter_handle = mpq_feed->sdmx_filter_handle;
+ filter_sts->metadata_fill_count =
+ dvb_ringbuffer_avail(&mpq_feed->metadata_buf);
+ filter_sts->metadata_write_offset = mpq_feed->metadata_buf.pwrite;
+ filter_sts->error_indicators = 0;
+ filter_sts->status_indicators = 0;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Filter meta-data buffer status: fill count = %d, write_offset = %d\n",
+ __func__, filter_sts->metadata_fill_count,
+ filter_sts->metadata_write_offset);
+
+ if (!dvb_dmx_is_video_feed(feed)) {
+ struct dvb_ringbuffer *buffer;
+
+ if (dvb_dmx_is_sec_feed(feed) ||
+ dvb_dmx_is_pcr_feed(feed)) {
+ buffer = (struct dvb_ringbuffer *)
+ &mpq_feed->sdmx_buf;
+ } else {
+ buffer = (struct dvb_ringbuffer *)
+ feed->feed.ts.buffer.ringbuff;
+ }
+
+ filter_sts->data_fill_count = dvb_ringbuffer_avail(buffer);
+ filter_sts->data_write_offset = buffer->pwrite;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Filter buffers status: fill count = %d, write_offset = %d\n",
+ __func__, filter_sts->data_fill_count,
+ filter_sts->data_write_offset);
+
+ return;
+ }
+
+ /* Video feed - decoder buffers */
+ feed_data = &mpq_feed->video_info;
+
+ spin_lock(&mpq_feed->video_info.video_buffer_lock);
+ sbuff = feed_data->video_buffer;
+ if (sbuff == NULL) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: video_buffer released\n",
+ __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return;
+ }
+
+ if (feed_data->buffer_desc.decoder_buffers_num > 1) {
+ /* linear mode */
+ filter_sts->data_fill_count = sbuff->pending_buffers_count;
+ filter_sts->data_write_offset =
+ sbuff->raw_data.pwrite /
+ sizeof(struct mpq_streambuffer_buffer_desc);
+ } else {
+ /* ring buffer mode */
+ filter_sts->data_fill_count =
+ mpq_streambuffer_data_avail(sbuff);
+ mpq_streambuffer_get_data_rw_offset(sbuff, NULL,
+ &filter_sts->data_write_offset);
+
+ }
+
+ spin_unlock(&mpq_feed->video_info.video_buffer_lock);
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Decoder buffers filter status: fill count = %d, write_offset = %d\n",
+ __func__, filter_sts->data_fill_count,
+ filter_sts->data_write_offset);
+}
+
+static int mpq_sdmx_section_filtering(struct mpq_feed *mpq_feed,
+ struct dvb_demux_filter *f,
+ struct sdmx_metadata_header *header)
+{
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ int ret;
+ u8 neq = 0;
+ u8 xor;
+ u8 tmp;
+ int i;
+
+ if (!mutex_is_locked(&mpq_feed->mpq_demux->mutex)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Mutex should have been locked\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
+ tmp = DVB_RINGBUFFER_PEEK(&mpq_feed->sdmx_buf, i);
+ xor = f->filter.filter_value[i] ^ tmp;
+
+ if (f->maskandmode[i] & xor)
+ return 0;
+
+ neq |= f->maskandnotmode[i] & xor;
+ }
+
+ if (f->doneq && !neq)
+ return 0;
+
+ if (feed->demux->playback_mode == DMX_PB_MODE_PULL) {
+ mutex_unlock(&mpq_feed->mpq_demux->mutex);
+
+ ret = feed->demux->buffer_ctrl.sec(&f->filter,
+ header->payload_length, 1);
+
+ mutex_lock(&mpq_feed->mpq_demux->mutex);
+
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: buffer_ctrl.sec aborted\n",
+ __func__);
+ return ret;
+ }
+
+ if (mpq_feed->sdmx_filter_handle ==
+ SDMX_INVALID_FILTER_HANDLE) {
+ MPQ_DVB_DBG_PRINT("%s: filter was stopped\n",
+ __func__);
+ return -ENODEV;
+ }
+ }
+
+ if (mpq_feed->sdmx_buf.pread + header->payload_length <
+ mpq_feed->sdmx_buf.size) {
+ feed->cb.sec(&mpq_feed->sdmx_buf.data[mpq_feed->sdmx_buf.pread],
+ header->payload_length,
+ NULL, 0, &f->filter, DMX_OK);
+ } else {
+ int split = mpq_feed->sdmx_buf.size - mpq_feed->sdmx_buf.pread;
+
+ feed->cb.sec(&mpq_feed->sdmx_buf.data[mpq_feed->sdmx_buf.pread],
+ split,
+ &mpq_feed->sdmx_buf.data[0],
+ header->payload_length - split,
+ &f->filter, DMX_OK);
+ }
+
+ return 0;
+}
+
+static int mpq_sdmx_check_ts_stall(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts,
+ size_t req,
+ int events_only)
+{
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ int ret;
+
+ if (!mutex_is_locked(&mpq_feed->mpq_demux->mutex)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Mutex should have been locked\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * For PULL mode need to verify there is enough space for the dmxdev
+ * event. Also, if data buffer is full we want to stall until some
+ * data is removed from it to prevent calling the sdmx when it cannot
+ * output data to the still full buffer.
+ */
+ if (mpq_demux->demux.playback_mode == DMX_PB_MODE_PULL) {
+ MPQ_DVB_DBG_PRINT("%s: Stalling for events and %zu bytes\n",
+ __func__, req);
+
+ mutex_unlock(&mpq_demux->mutex);
+
+ ret = mpq_demux->demux.buffer_ctrl.ts(&feed->feed.ts, req, 1);
+ MPQ_DVB_DBG_PRINT("%s: stall result = %d\n",
+ __func__, ret);
+
+ mutex_lock(&mpq_demux->mutex);
+
+ if (mpq_feed->sdmx_filter_handle ==
+ SDMX_INVALID_FILTER_HANDLE) {
+ MPQ_DVB_DBG_PRINT("%s: filter was stopped\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Handle filter results for filters with no extra meta-data */
+static void mpq_sdmx_pes_filter_results(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts)
+{
+ int ret;
+ struct sdmx_metadata_header header;
+ struct sdmx_pes_counters counters;
+ struct dmx_data_ready data_event;
+ struct dmx_data_ready pes_event;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct dvb_ringbuffer *buf = (struct dvb_ringbuffer *)
+ feed->feed.ts.buffer.ringbuff;
+ ssize_t bytes_avail;
+
+ if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
+ goto pes_filter_check_overflow;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Meta: fill=%u, write=%u. Data: fill=%u, write=%u\n",
+ __func__, sts->metadata_fill_count, sts->metadata_write_offset,
+ sts->data_fill_count, sts->data_write_offset);
+
+ mpq_feed->metadata_buf.pwrite = sts->metadata_write_offset;
+
+ if ((sts->metadata_fill_count == 0) &&
+ (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)) {
+ ssize_t free = dvb_ringbuffer_free(buf);
+
+ ret = 0;
+ if ((free + SZ_2K) < MAX_PES_LENGTH)
+ ret = mpq_sdmx_check_ts_stall(mpq_demux, mpq_feed, sts,
+ free + SZ_2K, 0);
+ else
+ MPQ_DVB_ERR_PRINT(
+ "%s: Cannot stall when free space bigger than max PES size\n",
+ __func__);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_sdmx_check_ts_stall aborted\n",
+ __func__);
+ return;
+ }
+ }
+
+ while (sts->metadata_fill_count) {
+ bytes_avail = dvb_ringbuffer_avail(&mpq_feed->metadata_buf);
+ if (bytes_avail < (sizeof(header) + sizeof(counters))) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: metadata_fill_count is %d less than required %zu bytes\n",
+ __func__,
+ sts->metadata_fill_count,
+ sizeof(header) + sizeof(counters));
+
+ /* clean-up remaining bytes to try to recover */
+ DVB_RINGBUFFER_SKIP(&mpq_feed->metadata_buf,
+ bytes_avail);
+ sts->metadata_fill_count = 0;
+ break;
+ }
+
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf, (u8 *)&header,
+ sizeof(header));
+ MPQ_DVB_DBG_PRINT(
+ "%s: metadata header: start=%u, length=%u\n",
+ __func__, header.payload_start, header.payload_length);
+ sts->metadata_fill_count -= sizeof(header);
+
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf, (u8 *)&counters,
+ sizeof(counters));
+ sts->metadata_fill_count -= sizeof(counters);
+
+ /* Notify new data in buffer */
+ data_event.status = DMX_OK;
+ data_event.data_length = header.payload_length;
+ ret = mpq_sdmx_check_ts_stall(mpq_demux, mpq_feed, sts,
+ data_event.data_length, 0);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_sdmx_check_ts_stall aborted\n",
+ __func__);
+ return;
+ }
+
+ feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
+
+ /* Notify new complete PES */
+ pes_event.status = DMX_OK_PES_END;
+ pes_event.pes_end.actual_length = header.payload_length;
+ pes_event.pes_end.start_gap = 0;
+ pes_event.data_length = 0;
+
+ /* Parse error indicators */
+ if (sts->error_indicators & SDMX_FILTER_ERR_INVALID_PES_LEN)
+ pes_event.pes_end.pes_length_mismatch = 1;
+ else
+ pes_event.pes_end.pes_length_mismatch = 0;
+
+ pes_event.pes_end.disc_indicator_set = 0;
+
+ pes_event.pes_end.stc = 0;
+ pes_event.pes_end.tei_counter = counters.transport_err_count;
+ pes_event.pes_end.cont_err_counter =
+ counters.continuity_err_count;
+ pes_event.pes_end.ts_packets_num =
+ counters.pes_ts_count;
+
+ ret = mpq_sdmx_check_ts_stall(mpq_demux, mpq_feed, sts, 0, 1);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_sdmx_check_ts_stall aborted\n",
+ __func__);
+ return;
+ }
+ feed->data_ready_cb.ts(&feed->feed.ts, &pes_event);
+ }
+
+pes_filter_check_overflow:
+ if ((mpq_demux->demux.playback_mode == DMX_PB_MODE_PUSH) &&
+ (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)) {
+ MPQ_DVB_ERR_PRINT("%s: DMX_OVERRUN_ERROR\n", __func__);
+ mpq_dmx_notify_overflow(feed);
+ }
+
+ if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
+ data_event.data_length = 0;
+ data_event.status = DMX_OK_EOS;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
+ }
+}
+
+static void mpq_sdmx_section_filter_results(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts)
+{
+ struct sdmx_metadata_header header;
+ struct dmx_data_ready event;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct dvb_demux_filter *f;
+ struct dmx_section_feed *sec = &feed->feed.sec;
+ ssize_t bytes_avail;
+
+ /* Parse error indicators */
+ if (sts->error_indicators & SDMX_FILTER_ERR_SEC_VERIF_CRC32_FAIL) {
+ MPQ_DVB_DBG_PRINT("%s: Notify CRC err event\n", __func__);
+ event.status = DMX_CRC_ERROR;
+ event.data_length = 0;
+ dvb_dmx_notify_section_event(feed, &event, 1);
+ }
+
+ if (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)
+ MPQ_DVB_ERR_PRINT("%s: internal section buffer overflowed!\n",
+ __func__);
+
+ if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
+ goto section_filter_check_eos;
+
+ mpq_feed->metadata_buf.pwrite = sts->metadata_write_offset;
+ mpq_feed->sdmx_buf.pwrite = sts->data_write_offset;
+
+ while (sts->metadata_fill_count) {
+ bytes_avail = dvb_ringbuffer_avail(&mpq_feed->metadata_buf);
+ if (bytes_avail < sizeof(header)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: metadata_fill_count is %d less than required %zu bytes\n",
+ __func__,
+ sts->metadata_fill_count,
+ sizeof(header));
+
+ /* clean-up remaining bytes to try to recover */
+ DVB_RINGBUFFER_SKIP(&mpq_feed->metadata_buf,
+ bytes_avail);
+ sts->metadata_fill_count = 0;
+ break;
+ }
+
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf, (u8 *) &header,
+ sizeof(header));
+ sts->metadata_fill_count -= sizeof(header);
+ MPQ_DVB_DBG_PRINT(
+ "%s: metadata header: start=%u, length=%u\n",
+ __func__, header.payload_start, header.payload_length);
+
+ f = feed->filter;
+ do {
+ if (mpq_sdmx_section_filtering(mpq_feed, f, &header))
+ return;
+ } while ((f = f->next) && sec->is_filtering);
+
+ DVB_RINGBUFFER_SKIP(&mpq_feed->sdmx_buf, header.payload_length);
+ }
+
+section_filter_check_eos:
+ if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
+ event.data_length = 0;
+ event.status = DMX_OK_EOS;
+ dvb_dmx_notify_section_event(feed, &event, 1);
+ }
+}
+
+static void mpq_sdmx_decoder_filter_results(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts)
+{
+ struct sdmx_metadata_header header;
+ struct sdmx_pes_counters counters;
+ int pes_header_offset;
+ struct ts_packet_header *ts_header;
+ struct ts_adaptation_field *ts_adapt;
+ struct pes_packet_header *pes_header;
+ u8 metadata_buf[MAX_SDMX_METADATA_LENGTH];
+ struct mpq_streambuffer *sbuf;
+ int ret;
+ struct dmx_data_ready data_event;
+ struct dmx_data_ready data;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ ssize_t bytes_avail;
+
+ if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
+ goto decoder_filter_check_flags;
+
+ /* Update meta data buffer write pointer */
+ mpq_feed->metadata_buf.pwrite = sts->metadata_write_offset;
+
+ if ((mpq_demux->demux.playback_mode == DMX_PB_MODE_PULL) &&
+ (sts->error_indicators & SDMX_FILTER_ERR_D_LIN_BUFS_FULL)) {
+ MPQ_DVB_DBG_PRINT("%s: Decoder stall...\n", __func__);
+
+ ret = mpq_dmx_decoder_fullness_check(
+ mpq_feed->dvb_demux_feed, 0, 0);
+ if (ret) {
+ /* we reach here if demuxing was aborted */
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_dmx_decoder_fullness_check aborted\n",
+ __func__);
+ return;
+ }
+ }
+
+ while (sts->metadata_fill_count) {
+ struct mpq_streambuffer_packet_header packet;
+ struct mpq_adapter_video_meta_data meta_data;
+
+ bytes_avail = dvb_ringbuffer_avail(&mpq_feed->metadata_buf);
+ if (bytes_avail < (sizeof(header) + sizeof(counters))) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: metadata_fill_count is %d less than required %zu bytes\n",
+ __func__,
+ sts->metadata_fill_count,
+ sizeof(header) + sizeof(counters));
+
+ /* clean-up remaining bytes to try to recover */
+ DVB_RINGBUFFER_SKIP(&mpq_feed->metadata_buf,
+ bytes_avail);
+ sts->metadata_fill_count = 0;
+ break;
+ }
+
+ /* Read metadata header */
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf, (u8 *)&header,
+ sizeof(header));
+ sts->metadata_fill_count -= sizeof(header);
+ MPQ_DVB_DBG_PRINT(
+ "%s: metadata header: start=%u, length=%u, metadata=%u\n",
+ __func__, header.payload_start, header.payload_length,
+ header.metadata_length);
+
+ /* Read metadata - PES counters */
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf, (u8 *)&counters,
+ sizeof(counters));
+ sts->metadata_fill_count -= sizeof(counters);
+
+ /* Read metadata - TS & PES headers */
+ bytes_avail = dvb_ringbuffer_avail(&mpq_feed->metadata_buf);
+ if ((header.metadata_length < MAX_SDMX_METADATA_LENGTH) &&
+ (header.metadata_length >= sizeof(counters)) &&
+ (bytes_avail >=
+ (header.metadata_length - sizeof(counters)))) {
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf,
+ metadata_buf,
+ header.metadata_length - sizeof(counters));
+ } else {
+ MPQ_DVB_ERR_PRINT(
+ "%s: meta-data size %d larger than available meta-data %zd or max allowed %d\n",
+ __func__, header.metadata_length,
+ bytes_avail,
+ MAX_SDMX_METADATA_LENGTH);
+
+ /* clean-up remaining bytes to try to recover */
+ DVB_RINGBUFFER_SKIP(&mpq_feed->metadata_buf,
+ bytes_avail);
+ sts->metadata_fill_count = 0;
+ break;
+ }
+
+ sts->metadata_fill_count -=
+ (header.metadata_length - sizeof(counters));
+
+ ts_header = (struct ts_packet_header *)&metadata_buf[0];
+ if (ts_header->adaptation_field_control == 1) {
+ ts_adapt = NULL;
+ pes_header_offset = sizeof(*ts_header);
+ } else {
+ ts_adapt = (struct ts_adaptation_field *)
+ &metadata_buf[sizeof(*ts_header)];
+ pes_header_offset = sizeof(*ts_header) + 1 +
+ ts_adapt->adaptation_field_length;
+ }
+ pes_header = (struct pes_packet_header *)
+ &metadata_buf[pes_header_offset];
+ meta_data.packet_type = DMX_PES_PACKET;
+ /* TODO - set to real STC when SDMX supports it */
+ meta_data.info.pes.stc = 0;
+
+ if (pes_header->pts_dts_flag & 0x2) {
+ meta_data.info.pes.pts_dts_info.pts_exist = 1;
+ meta_data.info.pes.pts_dts_info.pts =
+ ((u64)pes_header->pts_1 << 30) |
+ ((u64)pes_header->pts_2 << 22) |
+ ((u64)pes_header->pts_3 << 15) |
+ ((u64)pes_header->pts_4 << 7) |
+ (u64)pes_header->pts_5;
+ } else {
+ meta_data.info.pes.pts_dts_info.pts_exist = 0;
+ }
+
+ if (pes_header->pts_dts_flag & 0x1) {
+ meta_data.info.pes.pts_dts_info.dts_exist = 1;
+ meta_data.info.pes.pts_dts_info.dts =
+ ((u64)pes_header->dts_1 << 30) |
+ ((u64)pes_header->dts_2 << 22) |
+ ((u64)pes_header->dts_3 << 15) |
+ ((u64)pes_header->dts_4 << 7) |
+ (u64)pes_header->dts_5;
+ } else {
+ meta_data.info.pes.pts_dts_info.dts_exist = 0;
+ }
+
+ spin_lock(&mpq_feed->video_info.video_buffer_lock);
+
+ mpq_feed->video_info.tei_errs =
+ counters.transport_err_count;
+ mpq_feed->video_info.continuity_errs =
+ counters.continuity_err_count;
+ mpq_feed->video_info.ts_packets_num =
+ counters.pes_ts_count;
+ mpq_feed->video_info.ts_dropped_bytes =
+ counters.drop_count *
+ mpq_demux->demux.ts_packet_size;
+
+ sbuf = mpq_feed->video_info.video_buffer;
+ if (sbuf == NULL) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: video_buffer released\n",
+ __func__);
+ spin_unlock(&mpq_feed->video_info.video_buffer_lock);
+ return;
+ }
+
+ if (!header.payload_length) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: warnning - video frame with 0 length, dropping\n",
+ __func__);
+ spin_unlock(&mpq_feed->video_info.video_buffer_lock);
+ continue;
+ }
+
+ packet.raw_data_len = header.payload_length;
+ packet.user_data_len = sizeof(meta_data);
+ mpq_streambuffer_get_buffer_handle(sbuf, 0,
+ &packet.raw_data_handle);
+ mpq_streambuffer_get_data_rw_offset(sbuf,
+ NULL, &packet.raw_data_offset);
+ ret = mpq_streambuffer_data_write_deposit(sbuf,
+ header.payload_length);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_streambuffer_data_write_deposit failed. ret=%d\n",
+ __func__, ret);
+ }
+ mpq_dmx_update_decoder_stat(mpq_feed);
+ ret = mpq_streambuffer_pkt_write(sbuf, &packet,
+ (u8 *)&meta_data);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
+ __func__, ret);
+ } else {
+ mpq_dmx_prepare_es_event_data(
+ &packet, &meta_data, &mpq_feed->video_info,
+ sbuf, &data, ret);
+ MPQ_DVB_DBG_PRINT("%s: Notify ES Event\n", __func__);
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ }
+
+ spin_unlock(&mpq_feed->video_info.video_buffer_lock);
+ }
+
+decoder_filter_check_flags:
+ if ((mpq_demux->demux.playback_mode == DMX_PB_MODE_PUSH) &&
+ (sts->error_indicators & SDMX_FILTER_ERR_D_LIN_BUFS_FULL)) {
+ MPQ_DVB_ERR_PRINT("%s: DMX_OVERRUN_ERROR\n", __func__);
+ mpq_dmx_notify_overflow(mpq_feed->dvb_demux_feed);
+ }
+
+ if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
+ /* Notify decoder via the stream buffer */
+ ret = mpq_dmx_decoder_eos_cmd(mpq_feed);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to notify decoder on EOS, ret=%d\n",
+ __func__, ret);
+
+ /* Notify user filter */
+ data_event.data_length = 0;
+ data_event.status = DMX_OK_EOS;
+ mpq_feed->dvb_demux_feed->data_ready_cb.ts(
+ &mpq_feed->dvb_demux_feed->feed.ts, &data_event);
+ }
+}
+
+static void mpq_sdmx_pcr_filter_results(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts)
+{
+ int ret;
+ struct sdmx_metadata_header header;
+ struct dmx_data_ready data;
+ struct dvb_ringbuffer *rbuff = &mpq_feed->sdmx_buf;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ u8 buf[TS_PACKET_HEADER_LENGTH + MAX_TSP_ADAPTATION_LENGTH +
+ TIMESTAMP_LEN];
+ size_t stc_len = 0;
+ ssize_t bytes_avail;
+
+ if (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)
+ MPQ_DVB_ERR_PRINT("%s: internal PCR buffer overflowed!\n",
+ __func__);
+
+ if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
+ goto pcr_filter_check_eos;
+
+ if (mpq_demux->demux.tsp_format == DMX_TSP_FORMAT_192_TAIL)
+ stc_len = 4;
+
+ mpq_feed->metadata_buf.pwrite = sts->metadata_write_offset;
+ rbuff->pwrite = sts->data_write_offset;
+
+ while (sts->metadata_fill_count) {
+ bytes_avail = dvb_ringbuffer_avail(&mpq_feed->metadata_buf);
+ if (bytes_avail < sizeof(header)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: metadata_fill_count is %d less than required %zu bytes\n",
+ __func__,
+ sts->metadata_fill_count,
+ sizeof(header));
+
+ /* clean-up remaining bytes to try to recover */
+ DVB_RINGBUFFER_SKIP(&mpq_feed->metadata_buf,
+ bytes_avail);
+ sts->metadata_fill_count = 0;
+ break;
+ }
+
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf, (u8 *) &header,
+ sizeof(header));
+ MPQ_DVB_DBG_PRINT(
+ "%s: metadata header: start=%u, length=%u\n",
+ __func__, header.payload_start, header.payload_length);
+ sts->metadata_fill_count -= sizeof(header);
+
+ dvb_ringbuffer_read(rbuff, buf, header.payload_length);
+
+ if (mpq_dmx_extract_pcr_and_dci(buf, &data.pcr.pcr,
+ &data.pcr.disc_indicator_set)) {
+
+ if (stc_len) {
+ data.pcr.stc =
+ buf[header.payload_length-2] << 16;
+ data.pcr.stc +=
+ buf[header.payload_length-3] << 8;
+ data.pcr.stc += buf[header.payload_length-4];
+ /* convert from 105.47 KHZ to 27MHz */
+ data.pcr.stc *= 256;
+ } else {
+ data.pcr.stc = 0;
+ }
+
+ data.data_length = 0;
+ data.status = DMX_OK_PCR;
+ ret = mpq_sdmx_check_ts_stall(
+ mpq_demux, mpq_feed, sts, 0, 1);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_sdmx_check_ts_stall aborted\n",
+ __func__);
+ return;
+ }
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ }
+ }
+
+pcr_filter_check_eos:
+ if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
+ data.data_length = 0;
+ data.status = DMX_OK_EOS;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ }
+}
+
+static void mpq_sdmx_raw_filter_results(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts)
+{
+ int ret;
+ ssize_t new_data;
+ struct dmx_data_ready data_event;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct dvb_ringbuffer *buf = (struct dvb_ringbuffer *)
+ feed->feed.ts.buffer.ringbuff;
+
+ if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
+ goto raw_filter_check_flags;
+
+ new_data = sts->data_write_offset -
+ buf->pwrite;
+ if (new_data < 0)
+ new_data += buf->size;
+
+ ret = mpq_sdmx_check_ts_stall(mpq_demux, mpq_feed, sts,
+ new_data + feed->demux->ts_packet_size, 0);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_sdmx_check_ts_stall aborted\n",
+ __func__);
+ return;
+ }
+
+ data_event.status = DMX_OK;
+ data_event.data_length = new_data;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
+ MPQ_DVB_DBG_PRINT("%s: Callback DMX_OK, size=%d\n",
+ __func__, data_event.data_length);
+
+raw_filter_check_flags:
+ if ((mpq_demux->demux.playback_mode == DMX_PB_MODE_PUSH) &&
+ (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)) {
+ MPQ_DVB_DBG_PRINT("%s: DMX_OVERRUN_ERROR\n", __func__);
+ mpq_dmx_notify_overflow(feed);
+ }
+
+ if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) {
+ data_event.data_length = 0;
+ data_event.status = DMX_OK_EOS;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
+ }
+
+}
+
+static void mpq_sdmx_process_results(struct mpq_demux *mpq_demux)
+{
+ int i;
+ int sdmx_filters;
+ struct sdmx_filter_status *sts;
+ struct mpq_feed *mpq_feed;
+ u8 mpq_feed_idx;
+
+ sdmx_filters = mpq_demux->sdmx_filter_count;
+ for (i = 0; i < sdmx_filters; i++) {
+ sts = &mpq_demux->sdmx_filters_state.status[i];
+ MPQ_DVB_DBG_PRINT(
+ "%s: Filter: handle=%d, status=0x%x, errors=0x%x\n",
+ __func__, sts->filter_handle, sts->status_indicators,
+ sts->error_indicators);
+ MPQ_DVB_DBG_PRINT("%s: Metadata fill count=%d (write=%d)\n",
+ __func__, sts->metadata_fill_count,
+ sts->metadata_write_offset);
+ MPQ_DVB_DBG_PRINT("%s: Data fill count=%d (write=%d)\n",
+ __func__, sts->data_fill_count, sts->data_write_offset);
+
+ mpq_feed_idx = mpq_demux->sdmx_filters_state.mpq_feed_idx[i];
+ mpq_feed = &mpq_demux->feeds[mpq_feed_idx];
+ if ((mpq_feed->dvb_demux_feed->state != DMX_STATE_GO) ||
+ (sts->filter_handle != mpq_feed->sdmx_filter_handle) ||
+ mpq_feed->secondary_feed ||
+ (mpq_demux->sdmx_filters_state.session_id[i] !=
+ mpq_feed->session_id))
+ continue;
+
+ /* Invalidate output buffer before processing the results */
+ mpq_sdmx_invalidate_buffer(mpq_feed);
+
+ if (sts->error_indicators & SDMX_FILTER_ERR_MD_BUF_FULL)
+ MPQ_DVB_ERR_PRINT(
+ "%s: meta-data buff for pid %d overflowed!\n",
+ __func__, mpq_feed->dvb_demux_feed->pid);
+
+ switch (mpq_feed->filter_type) {
+ case SDMX_PCR_FILTER:
+ mpq_sdmx_pcr_filter_results(mpq_demux, mpq_feed, sts);
+ break;
+ case SDMX_PES_FILTER:
+ mpq_sdmx_pes_filter_results(mpq_demux, mpq_feed,
+ sts);
+ break;
+ case SDMX_SEPARATED_PES_FILTER:
+ mpq_sdmx_decoder_filter_results(mpq_demux, mpq_feed,
+ sts);
+ break;
+ case SDMX_SECTION_FILTER:
+ mpq_sdmx_section_filter_results(mpq_demux, mpq_feed,
+ sts);
+ break;
+ case SDMX_RAW_FILTER:
+ mpq_sdmx_raw_filter_results(mpq_demux, mpq_feed, sts);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int mpq_sdmx_process_buffer(struct mpq_demux *mpq_demux,
+ struct sdmx_buff_descr *input,
+ u32 fill_count,
+ u32 read_offset)
+{
+ struct sdmx_filter_status *sts;
+ struct mpq_feed *mpq_feed;
+ u8 flags = 0;
+ u32 errors;
+ u32 status;
+ u32 prev_read_offset;
+ u32 prev_fill_count;
+ enum sdmx_status sdmx_res;
+ int i;
+ int filter_index = 0;
+ int bytes_read;
+ struct timespec process_start_time;
+ struct timespec process_end_time;
+
+ mutex_lock(&mpq_demux->mutex);
+
+ /*
+ * All active filters may get totally closed and therefore
+ * sdmx session may get terminated, in such case nothing to process
+ */
+ if (mpq_demux->sdmx_session_handle == SDMX_INVALID_SESSION_HANDLE) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: sdmx filters aborted, filter-count %d, session %d\n",
+ __func__, mpq_demux->sdmx_filter_count,
+ mpq_demux->sdmx_session_handle);
+ mutex_unlock(&mpq_demux->mutex);
+ return 0;
+ }
+
+ /* Set input flags */
+ if (mpq_demux->sdmx_eos)
+ flags |= SDMX_INPUT_FLAG_EOS;
+ if (mpq_sdmx_debug)
+ flags |= SDMX_INPUT_FLAG_DBG_ENABLE;
+
+ /* Build up to date filter status array */
+ for (i = 0; i < MPQ_MAX_DMX_FILES; i++) {
+ mpq_feed = &mpq_demux->feeds[i];
+ if ((mpq_feed->sdmx_filter_handle != SDMX_INVALID_FILTER_HANDLE)
+ && (!mpq_feed->secondary_feed)) {
+ sts = mpq_demux->sdmx_filters_state.status +
+ filter_index;
+ mpq_sdmx_prepare_filter_status(mpq_demux, sts,
+ mpq_feed);
+ mpq_demux->sdmx_filters_state.mpq_feed_idx[filter_index]
+ = i;
+ mpq_demux->sdmx_filters_state.session_id[filter_index] =
+ mpq_feed->session_id;
+ filter_index++;
+ }
+ }
+
+ /* Sanity check */
+ if (filter_index != mpq_demux->sdmx_filter_count) {
+ mutex_unlock(&mpq_demux->mutex);
+ MPQ_DVB_ERR_PRINT(
+ "%s: Updated %d SDMX filters status but should be %d\n",
+ __func__, filter_index, mpq_demux->sdmx_filter_count);
+ return -ERESTART;
+ }
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Before SDMX_process: input read_offset=%u, fill count=%u\n",
+ __func__, read_offset, fill_count);
+
+ process_start_time = current_kernel_time();
+
+ prev_read_offset = read_offset;
+ prev_fill_count = fill_count;
+ sdmx_res = sdmx_process(mpq_demux->sdmx_session_handle, flags, input,
+ &fill_count, &read_offset, &errors, &status,
+ mpq_demux->sdmx_filter_count,
+ mpq_demux->sdmx_filters_state.status);
+
+ process_end_time = current_kernel_time();
+ bytes_read = prev_fill_count - fill_count;
+
+ mpq_dmx_update_sdmx_stat(mpq_demux, bytes_read,
+ &process_start_time, &process_end_time);
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: SDMX result=%d, input_fill_count=%u, read_offset=%u, read %d bytes from input, status=0x%X, errors=0x%X\n",
+ __func__, sdmx_res, fill_count, read_offset, bytes_read,
+ status, errors);
+
+ if ((sdmx_res == SDMX_SUCCESS) ||
+ (sdmx_res == SDMX_STATUS_STALLED_IN_PULL_MODE)) {
+ if (sdmx_res == SDMX_STATUS_STALLED_IN_PULL_MODE)
+ MPQ_DVB_DBG_PRINT("%s: SDMX stalled for PULL mode\n",
+ __func__);
+
+ mpq_sdmx_process_results(mpq_demux);
+ } else {
+ MPQ_DVB_ERR_PRINT(
+ "%s: SDMX Process returned %d\n",
+ __func__, sdmx_res);
+ }
+
+ mutex_unlock(&mpq_demux->mutex);
+
+ return bytes_read;
+}
+
+int mpq_sdmx_process(struct mpq_demux *mpq_demux,
+ struct sdmx_buff_descr *input,
+ u32 fill_count,
+ u32 read_offset,
+ size_t tsp_size)
+{
+ int ret;
+ int todo;
+ int total_bytes_read = 0;
+ int limit = mpq_sdmx_proc_limit * tsp_size;
+
+ MPQ_DVB_DBG_PRINT(
+ "\n\n%s: read_offset=%u, fill_count=%u, tsp_size=%zu\n",
+ __func__, read_offset, fill_count, tsp_size);
+
+ while (fill_count >= tsp_size) {
+ todo = fill_count > limit ? limit : fill_count;
+ ret = mpq_sdmx_process_buffer(mpq_demux, input, todo,
+ read_offset);
+
+ if (mpq_demux->demux.sw_filter_abort) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Demuxing from DVR was aborted\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ if (ret > 0) {
+ total_bytes_read += ret;
+ fill_count -= ret;
+ read_offset += ret;
+ if (read_offset >= input->size)
+ read_offset -= input->size;
+ } else {
+ /*
+ * ret < 0: some error occurred
+ * ret == 0: not enough data (less than 1 TS packet)
+ */
+ if (ret < 0)
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_process_buffer failed, returned %d\n",
+ __func__, ret);
+ break;
+ }
+ }
+
+ return total_bytes_read;
+}
+
+static int mpq_sdmx_write(struct mpq_demux *mpq_demux,
+ struct ion_handle *input_handle,
+ const char *buf,
+ size_t count)
+{
+ struct ion_handle *ion_handle =
+ mpq_demux->demux.dmx.dvr_input.priv_handle;
+ struct dvb_ringbuffer *rbuf = (struct dvb_ringbuffer *)
+ mpq_demux->demux.dmx.dvr_input.ringbuff;
+ struct sdmx_buff_descr buf_desc;
+ u32 read_offset;
+ int ret;
+
+ if (mpq_demux == NULL || input_handle == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = mpq_sdmx_dvr_buffer_desc(mpq_demux, &buf_desc);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to init input buffer descriptor. ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+ read_offset = mpq_demux->demux.dmx.dvr_input.ringbuff->pread;
+
+
+ /*
+ * We must flush the buffer before SDMX starts reading from it
+ * so that it gets a valid data in memory.
+ */
+ ret = msm_ion_do_cache_op(mpq_demux->ion_client,
+ ion_handle, rbuf->data,
+ rbuf->size, ION_IOC_CLEAN_CACHES);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: msm_ion_do_cache_op failed, ret = %d\n",
+ __func__, ret);
+
+ return mpq_sdmx_process(mpq_demux, &buf_desc, count,
+ read_offset, mpq_demux->demux.ts_packet_size);
+}
+
+int mpq_dmx_write(struct dmx_demux *demux, const char *buf, size_t count)
+{
+ struct dvb_demux *dvb_demux;
+ struct mpq_demux *mpq_demux;
+ int ret = count;
+
+ if (demux == NULL)
+ return -EINVAL;
+
+ dvb_demux = demux->priv;
+ mpq_demux = dvb_demux->priv;
+
+ /* Route through secure demux - process secure feeds if any exist */
+ if (mpq_sdmx_is_loaded() && mpq_demux->sdmx_filter_count) {
+ ret = mpq_sdmx_write(mpq_demux,
+ demux->dvr_input.priv_handle,
+ buf,
+ count);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_write failed. ret = %d\n",
+ __func__, ret);
+ ret = count;
+ }
+ }
+
+ /*
+ * Route through sw filter - process non-secure feeds if any exist.
+ * For sw filter, should process the same amount of bytes the sdmx
+ * process managed to consume, unless some sdmx error occurred, for
+ * which should process the whole buffer
+ */
+ if (mpq_demux->num_active_feeds > mpq_demux->num_secure_feeds)
+ dvb_dmx_swfilter_format(dvb_demux, buf, ret,
+ dvb_demux->tsp_format);
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ return ret;
+}
+
+int mpq_sdmx_is_loaded(void)
+{
+ static int sdmx_load_checked;
+
+ if (!sdmx_load_checked) {
+ mpq_sdmx_check_app_loaded();
+ sdmx_load_checked = 1;
+ }
+
+ return mpq_dmx_info.secure_demux_app_loaded;
+}
+
+int mpq_dmx_oob_command(struct dvb_demux_feed *feed,
+ struct dmx_oob_command *cmd)
+{
+ struct mpq_feed *mpq_feed = feed->priv;
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+ struct dmx_data_ready event;
+ int ret = 0;
+
+ mutex_lock(&mpq_demux->mutex);
+ mpq_feed = feed->priv;
+
+ if (!dvb_dmx_is_video_feed(feed) && !dvb_dmx_is_pcr_feed(feed) &&
+ !feed->secure_mode.is_secured) {
+ mutex_unlock(&mpq_demux->mutex);
+ return 0;
+ }
+
+ event.data_length = 0;
+
+ switch (cmd->type) {
+ case DMX_OOB_CMD_EOS:
+ event.status = DMX_OK_EOS;
+ if (!feed->secure_mode.is_secured) {
+ if (dvb_dmx_is_video_feed(feed)) {
+ if (!video_framing)
+ mpq_dmx_decoder_pes_closure(mpq_demux,
+ mpq_feed);
+ else
+ mpq_dmx_decoder_frame_closure(mpq_demux,
+ mpq_feed);
+ ret = mpq_dmx_decoder_eos_cmd(mpq_feed);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: Couldn't write oob eos packet\n",
+ __func__);
+ }
+ ret = feed->data_ready_cb.ts(&feed->feed.ts, &event);
+ } else if (!mpq_demux->sdmx_eos) {
+ struct sdmx_buff_descr buf_desc;
+
+ mpq_demux->sdmx_eos = 1;
+ ret = mpq_sdmx_dvr_buffer_desc(mpq_demux, &buf_desc);
+ if (!ret) {
+ mutex_unlock(&mpq_demux->mutex);
+ mpq_sdmx_process_buffer(mpq_demux, &buf_desc,
+ 0, 0);
+ return 0;
+ }
+ }
+ break;
+ case DMX_OOB_CMD_MARKER:
+ event.status = DMX_OK_MARKER;
+ event.marker.id = cmd->params.marker.id;
+
+ if (feed->type == DMX_TYPE_SEC)
+ ret = dvb_dmx_notify_section_event(feed, &event, 1);
+ else
+ /* MPQ_TODO: Notify decoder via the stream buffer */
+ ret = feed->data_ready_cb.ts(&feed->feed.ts, &event);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&mpq_demux->mutex);
+ return ret;
+}
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
new file mode 100644
index 000000000000..f36e9e7e7a23
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
@@ -0,0 +1,1027 @@
+/* Copyright (c) 2012-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 _MPQ_DMX_PLUGIN_COMMON_H
+#define _MPQ_DMX_PLUGIN_COMMON_H
+
+#include <linux/msm_ion.h>
+
+#include "dvbdev.h"
+#include "dmxdev.h"
+#include "demux.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "mpq_adapter.h"
+#include "mpq_sdmx.h"
+
+#define TS_PACKET_SYNC_BYTE (0x47)
+#define TS_PACKET_SIZE (188)
+#define TS_PACKET_HEADER_LENGTH (4)
+
+/* Length of mandatory fields that must exist in header of video PES */
+#define PES_MANDATORY_FIELDS_LEN 9
+
+/*
+ * 500 PES header packets in the meta-data buffer,
+ * should be more than enough
+ */
+#define VIDEO_NUM_OF_PES_PACKETS 500
+
+#define VIDEO_META_DATA_PACKET_SIZE \
+ (DVB_RINGBUFFER_PKTHDRSIZE + \
+ sizeof(struct mpq_streambuffer_packet_header) + \
+ sizeof(struct mpq_adapter_video_meta_data))
+
+#define VIDEO_META_DATA_BUFFER_SIZE \
+ (VIDEO_NUM_OF_PES_PACKETS * VIDEO_META_DATA_PACKET_SIZE)
+
+/* Max number open() request can be done on demux device */
+#define MPQ_MAX_DMX_FILES 128
+
+/**
+ * TSIF alias name length
+ */
+#define TSIF_NAME_LENGTH 20
+
+/**
+ * struct ts_packet_header - Transport packet header
+ * as defined in MPEG2 transport stream standard.
+ */
+struct ts_packet_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned sync_byte:8;
+ unsigned transport_error_indicator:1;
+ unsigned payload_unit_start_indicator:1;
+ unsigned transport_priority:1;
+ unsigned pid_msb:5;
+ unsigned pid_lsb:8;
+ unsigned transport_scrambling_control:2;
+ unsigned adaptation_field_control:2;
+ unsigned continuity_counter:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned sync_byte:8;
+ unsigned pid_msb:5;
+ unsigned transport_priority:1;
+ unsigned payload_unit_start_indicator:1;
+ unsigned transport_error_indicator:1;
+ unsigned pid_lsb:8;
+ unsigned continuity_counter:4;
+ unsigned adaptation_field_control:2;
+ unsigned transport_scrambling_control:2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+/**
+ * struct ts_adaptation_field - Adaptation field prefix
+ * as defined in MPEG2 transport stream standard.
+ */
+struct ts_adaptation_field {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned adaptation_field_length:8;
+ unsigned discontinuity_indicator:1;
+ unsigned random_access_indicator:1;
+ unsigned elementary_stream_priority_indicator:1;
+ unsigned PCR_flag:1;
+ unsigned OPCR_flag:1;
+ unsigned splicing_point_flag:1;
+ unsigned transport_private_data_flag:1;
+ unsigned adaptation_field_extension_flag:1;
+ unsigned program_clock_reference_base_1:8;
+ unsigned program_clock_reference_base_2:8;
+ unsigned program_clock_reference_base_3:8;
+ unsigned program_clock_reference_base_4:8;
+ unsigned program_clock_reference_base_5:1;
+ unsigned reserved:6;
+ unsigned program_clock_reference_ext_1:1;
+ unsigned program_clock_reference_ext_2:8;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned adaptation_field_length:8;
+ unsigned adaptation_field_extension_flag:1;
+ unsigned transport_private_data_flag:1;
+ unsigned splicing_point_flag:1;
+ unsigned OPCR_flag:1;
+ unsigned PCR_flag:1;
+ unsigned elementary_stream_priority_indicator:1;
+ unsigned random_access_indicator:1;
+ unsigned discontinuity_indicator:1;
+ unsigned program_clock_reference_base_1:8;
+ unsigned program_clock_reference_base_2:8;
+ unsigned program_clock_reference_base_3:8;
+ unsigned program_clock_reference_base_4:8;
+ unsigned program_clock_reference_ext_1:1;
+ unsigned reserved:6;
+ unsigned program_clock_reference_base_5:1;
+ unsigned program_clock_reference_ext_2:8;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+
+/*
+ * PES packet header containing dts and/or pts values
+ * as defined in MPEG2 transport stream standard.
+ */
+struct pes_packet_header {
+#if defined(__BIG_ENDIAN_BITFIELD)
+ unsigned packet_start_code_prefix_1:8;
+ unsigned packet_start_code_prefix_2:8;
+ unsigned packet_start_code_prefix_3:8;
+ unsigned stream_id:8;
+ unsigned pes_packet_length_msb:8;
+ unsigned pes_packet_length_lsb:8;
+ unsigned reserved_bits0:2;
+ unsigned pes_scrambling_control:2;
+ unsigned pes_priority:1;
+ unsigned data_alignment_indicator:1;
+ unsigned copyright:1;
+ unsigned original_or_copy:1;
+ unsigned pts_dts_flag:2;
+ unsigned escr_flag:1;
+ unsigned es_rate_flag:1;
+ unsigned dsm_trick_mode_flag:1;
+ unsigned additional_copy_info_flag:1;
+ unsigned pes_crc_flag:1;
+ unsigned pes_extension_flag:1;
+ unsigned pes_header_data_length:8;
+ unsigned reserved_bits1:4;
+ unsigned pts_1:3;
+ unsigned marker_bit0:1;
+ unsigned pts_2:8;
+ unsigned pts_3:7;
+ unsigned marker_bit1:1;
+ unsigned pts_4:8;
+ unsigned pts_5:7;
+ unsigned marker_bit2:1;
+ unsigned reserved_bits2:4;
+ unsigned dts_1:3;
+ unsigned marker_bit3:1;
+ unsigned dts_2:8;
+ unsigned dts_3:7;
+ unsigned marker_bit4:1;
+ unsigned dts_4:8;
+ unsigned dts_5:7;
+ unsigned marker_bit5:1;
+ unsigned reserved_bits3:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned packet_start_code_prefix_1:8;
+ unsigned packet_start_code_prefix_2:8;
+ unsigned packet_start_code_prefix_3:8;
+ unsigned stream_id:8;
+ unsigned pes_packet_length_lsb:8;
+ unsigned pes_packet_length_msb:8;
+ unsigned original_or_copy:1;
+ unsigned copyright:1;
+ unsigned data_alignment_indicator:1;
+ unsigned pes_priority:1;
+ unsigned pes_scrambling_control:2;
+ unsigned reserved_bits0:2;
+ unsigned pes_extension_flag:1;
+ unsigned pes_crc_flag:1;
+ unsigned additional_copy_info_flag:1;
+ unsigned dsm_trick_mode_flag:1;
+ unsigned es_rate_flag:1;
+ unsigned escr_flag:1;
+ unsigned pts_dts_flag:2;
+ unsigned pes_header_data_length:8;
+ unsigned marker_bit0:1;
+ unsigned pts_1:3;
+ unsigned reserved_bits1:4;
+ unsigned pts_2:8;
+ unsigned marker_bit1:1;
+ unsigned pts_3:7;
+ unsigned pts_4:8;
+ unsigned marker_bit2:1;
+ unsigned pts_5:7;
+ unsigned marker_bit3:1;
+ unsigned dts_1:3;
+ unsigned reserved_bits2:4;
+ unsigned dts_2:8;
+ unsigned marker_bit4:1;
+ unsigned dts_3:7;
+ unsigned dts_4:8;
+ unsigned marker_bit5:1;
+ unsigned dts_5:7;
+ unsigned reserved_bits3:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} __packed;
+
+/**
+ * mpq_decoder_buffers_desc - decoder buffer(s) management information.
+ *
+ * @desc: Array of buffer descriptors as they are passed to mpq_streambuffer
+ * upon its initialization. These descriptors must remain valid as long as
+ * the mpq_streambuffer object is used.
+ * @ion_handle: Array of ION handles, one for each decoder buffer, used for
+ * kernel memory mapping or allocation. Handles are saved in order to release
+ * resources properly later on.
+ * @decoder_buffers_num: number of buffers that are managed, either externally
+ * or internally by the mpq_streambuffer object
+ * @shared_file: File handle of internally allocated video buffer shared
+ * with video consumer.
+ */
+struct mpq_decoder_buffers_desc {
+ struct mpq_streambuffer_buffer_desc desc[DMX_MAX_DECODER_BUFFER_NUM];
+ struct ion_handle *ion_handle[DMX_MAX_DECODER_BUFFER_NUM];
+ u32 decoder_buffers_num;
+ struct file *shared_file;
+};
+
+/*
+ * mpq_video_feed_info - private data used for video feed.
+ *
+ * @video_buffer: Holds the streamer buffer shared with
+ * the decoder for feeds having the data going to the decoder.
+ * @video_buffer_lock: Lock protecting against video output buffer.
+ * The lock protects against API calls to manipulate the output buffer
+ * (initialize, free, re-use buffers) and dvb-sw demux parsing the video
+ * data through mpq_dmx_process_video_packet().
+ * @buffer_desc: Holds decoder buffer(s) information used for stream buffer.
+ * @pes_header: Used for feeds that output data to decoder,
+ * holds PES header of current processed PES.
+ * @pes_header_left_bytes: Used for feeds that output data to decoder,
+ * holds remaining PES header bytes of current processed PES.
+ * @pes_header_offset: Holds the offset within the current processed
+ * pes header.
+ * @fullness_wait_cancel: Flag used to signal to abort waiting for
+ * decoder's fullness.
+ * @stream_interface: The ID of the video stream interface registered
+ * with this stream buffer.
+ * @patterns: pointer to the framing patterns to look for.
+ * @patterns_num: number of framing patterns.
+ * @prev_pattern: holds the trailing data of the last processed video packet.
+ * @frame_offset: Saves data buffer offset to which a new frame will be written
+ * @last_pattern_offset: Holds the previous pattern offset
+ * @pending_pattern_len: Accumulated number of data bytes that will be
+ * reported for this frame.
+ * @last_framing_match_type: Used for saving the type of
+ * the previous pattern match found in this video feed.
+ * @last_framing_match_stc: Used for saving the STC attached to TS packet
+ * of the previous pattern match found in this video feed.
+ * @found_sequence_header_pattern: Flag used to note that an MPEG-2
+ * Sequence Header, H.264 SPS or VC-1 Sequence Header pattern
+ * (whichever is relevant according to the video standard) had already
+ * been found.
+ * @prefix_size: a bit mask representing the size(s) of possible prefixes
+ * to the pattern, already found in the previous buffer. If bit 0 is set,
+ * a prefix of size 1 was found. If bit 1 is set, a prefix of size 2 was
+ * found, etc. This supports a prefix size of up to 32, which is more
+ * than we need. The search function updates prefix_size as needed
+ * for the next buffer search.
+ * @first_prefix_size: used to save the prefix size used to find the first
+ * pattern written to the stream buffer.
+ * @saved_pts_dts_info: used to save PTS/DTS information until it is written.
+ * @new_pts_dts_info: used to store PTS/DTS information from current PES header.
+ * @saved_info_used: indicates if saved PTS/DTS information was used.
+ * @new_info_exists: indicates if new PTS/DTS information exists in
+ * new_pts_dts_info that should be saved to saved_pts_dts_info.
+ * @first_pts_dts_copy: a flag used to indicate if PTS/DTS information needs
+ * to be copied from the currently parsed PES header to the saved_pts_dts_info.
+ * @tei_errs: Transport stream Transport Error Indicator (TEI) counter.
+ * @last_continuity: last continuity counter value found in TS packet header.
+ * Initialized to -1.
+ * @continuity_errs: Transport stream continuity error counter.
+ * @ts_packets_num: TS packets counter.
+ * @ts_dropped_bytes: counts the number of bytes dropped due to insufficient
+ * buffer space.
+ * @prev_stc: STC attached to the previous video TS packet
+ */
+struct mpq_video_feed_info {
+ struct mpq_streambuffer *video_buffer;
+ spinlock_t video_buffer_lock;
+ struct mpq_decoder_buffers_desc buffer_desc;
+ struct pes_packet_header pes_header;
+ u32 pes_header_left_bytes;
+ u32 pes_header_offset;
+ int fullness_wait_cancel;
+ enum mpq_adapter_stream_if stream_interface;
+const struct dvb_dmx_video_patterns *patterns[DVB_DMX_MAX_SEARCH_PATTERN_NUM];
+ int patterns_num;
+ char prev_pattern[DVB_DMX_MAX_PATTERN_LEN];
+ u32 frame_offset;
+ u32 last_pattern_offset;
+ u32 pending_pattern_len;
+ u64 last_framing_match_type;
+ u64 last_framing_match_stc;
+ int found_sequence_header_pattern;
+ struct dvb_dmx_video_prefix_size_masks prefix_size;
+ u32 first_prefix_size;
+ struct dmx_pts_dts_info saved_pts_dts_info;
+ struct dmx_pts_dts_info new_pts_dts_info;
+ int saved_info_used;
+ int new_info_exists;
+ int first_pts_dts_copy;
+ u32 tei_errs;
+ int last_continuity;
+ u32 continuity_errs;
+ u32 ts_packets_num;
+ u32 ts_dropped_bytes;
+ u64 prev_stc;
+};
+
+/**
+ * mpq feed object - mpq common plugin feed information
+ *
+ * @dvb_demux_feed: Back pointer to dvb demux level feed object
+ * @mpq_demux: Pointer to common mpq demux object
+ * @plugin_priv: Plugin specific private data
+ * @sdmx_filter_handle: Secure demux filter handle. Recording feed may share
+ * same filter handle
+ * @secondary_feed: Specifies if this feed shares filter handle with
+ * other feeds
+ * @metadata_buf: Ring buffer object for managing the metadata buffer
+ * @metadata_buf_handle: Allocation handle for the metadata buffer
+ * @session_id: Counter that is incremented every time feed is initialized
+ * through mpq_dmx_init_mpq_feed
+ * @sdmx_buf: Ring buffer object for intermediate output data from the sdmx
+ * @sdmx_buf_handle: Allocation handle for the sdmx intermediate data buffer
+ * @video_info: Video feed specific information
+ */
+struct mpq_feed {
+ struct dvb_demux_feed *dvb_demux_feed;
+ struct mpq_demux *mpq_demux;
+ void *plugin_priv;
+
+ /* Secure demux related */
+ int sdmx_filter_handle;
+ int secondary_feed;
+ enum sdmx_filter filter_type;
+ struct dvb_ringbuffer metadata_buf;
+ struct ion_handle *metadata_buf_handle;
+
+ u8 session_id;
+ struct dvb_ringbuffer sdmx_buf;
+ struct ion_handle *sdmx_buf_handle;
+
+ struct mpq_video_feed_info video_info;
+};
+
+/**
+ * struct mpq_demux - mpq demux information
+ * @idx: Instance index
+ * @demux: The dvb_demux instance used by mpq_demux
+ * @dmxdev: The dmxdev instance used by mpq_demux
+ * @fe_memory: Handle of front-end memory source to mpq_demux
+ * @source: The current source connected to the demux
+ * @is_initialized: Indicates whether this demux device was
+ * initialized or not.
+ * @ion_client: ION demux client used to allocate memory from ION.
+ * @mutex: Lock used to protect against private feed data
+ * @feeds: mpq common feed object pool
+ * @num_active_feeds: Number of active mpq feeds
+ * @num_secure_feeds: Number of secure feeds (have a sdmx filter associated)
+ * currently allocated.
+ * Used before each call to sdmx_process() to build up to date state.
+ * @sdmx_session_handle: Secure demux open session handle
+ * @sdmx_filter_count: Number of active secure demux filters
+ * @sdmx_eos: End-of-stream indication flag for current sdmx session
+ * @sdmx_filters_state: Array holding buffers status for each secure
+ * demux filter.
+ * @decoder_alloc_flags: ION flags to be used when allocating internally
+ * @plugin_priv: Underlying plugin's own private data
+ * @mpq_dmx_plugin_release: Underlying plugin's release function
+ * @hw_notification_interval: Notification interval in msec,
+ * exposed in debugfs.
+ * @hw_notification_min_interval: Minimum notification internal in msec,
+ * exposed in debugfs.
+ * @hw_notification_count: Notification count, exposed in debugfs.
+ * @hw_notification_size: Notification size in bytes, exposed in debugfs.
+ * @hw_notification_min_size: Minimum notification size in bytes,
+ * exposed in debugfs.
+ * @decoder_stat: Decoder output statistics, exposed in debug-fs.
+ * @sdmx_process_count: Total number of times sdmx_process is called.
+ * @sdmx_process_time_sum: Total time sdmx_process takes.
+ * @sdmx_process_time_average: Average time sdmx_process takes.
+ * @sdmx_process_time_max: Max time sdmx_process takes.
+ * @sdmx_process_packets_sum: Total packets number sdmx_process handled.
+ * @sdmx_process_packets_average: Average packets number sdmx_process handled.
+ * @sdmx_process_packets_min: Minimum packets number sdmx_process handled.
+ * @last_notification_time: Time of last HW notification.
+ */
+struct mpq_demux {
+ int idx;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_memory;
+ dmx_source_t source;
+ int is_initialized;
+ struct ion_client *ion_client;
+ struct mutex mutex;
+ struct mpq_feed feeds[MPQ_MAX_DMX_FILES];
+ u32 num_active_feeds;
+ u32 num_secure_feeds;
+ int sdmx_session_handle;
+ int sdmx_session_ref_count;
+ int sdmx_filter_count;
+ int sdmx_eos;
+ struct {
+ /* SDMX filters status */
+ struct sdmx_filter_status status[MPQ_MAX_DMX_FILES];
+
+ /* Index of the feed respective to SDMX filter */
+ u8 mpq_feed_idx[MPQ_MAX_DMX_FILES];
+
+ /*
+ * Snapshot of session_id of the feed
+ * when SDMX process was called. This is used
+ * to identify whether the feed has been
+ * restarted when processing SDMX results.
+ * May happen when demux is stalled in playback
+ * from memory with PULL mode.
+ */
+ u8 session_id[MPQ_MAX_DMX_FILES];
+ } sdmx_filters_state;
+
+ unsigned int decoder_alloc_flags;
+
+ /* HW plugin specific */
+ void *plugin_priv;
+ int (*mpq_dmx_plugin_release)(struct mpq_demux *mpq_demux);
+
+ /* debug-fs */
+ u32 hw_notification_interval;
+ u32 hw_notification_min_interval;
+ u32 hw_notification_count;
+ u32 hw_notification_size;
+ u32 hw_notification_min_size;
+
+ struct {
+ /*
+ * Accumulated number of bytes
+ * dropped due to decoder buffer fullness.
+ */
+ u32 drop_count;
+
+ /* Counter incremeneted for each video frame output by demux */
+ u32 out_count;
+
+ /*
+ * Sum of intervals (msec) holding the time
+ * between two successive video frames output.
+ */
+ u32 out_interval_sum;
+
+ /*
+ * Average interval (msec) between two
+ * successive video frames output.
+ */
+ u32 out_interval_average;
+
+ /*
+ * Max interval (msec) between two
+ * successive video frames output.
+ */
+ u32 out_interval_max;
+
+ /* Counter for number of decoder packets with TEI bit set */
+ u32 ts_errors;
+
+ /*
+ * Counter for number of decoder packets
+ * with continuity counter errors.
+ */
+ u32 cc_errors;
+
+ /* Time of last video frame output */
+ struct timespec out_last_time;
+ } decoder_stat[MPQ_ADAPTER_MAX_NUM_OF_INTERFACES];
+
+ u32 sdmx_process_count;
+ u32 sdmx_process_time_sum;
+ u32 sdmx_process_time_average;
+ u32 sdmx_process_time_max;
+ u32 sdmx_process_packets_sum;
+ u32 sdmx_process_packets_average;
+ u32 sdmx_process_packets_min;
+ enum sdmx_log_level sdmx_log_level;
+
+ struct timespec last_notification_time;
+};
+
+/**
+ * mpq_dmx_init - initialization and registration function of
+ * single MPQ demux device
+ *
+ * @adapter: The adapter to register mpq_demux to
+ * @mpq_demux: The mpq demux to initialize
+ *
+ * Every HW plug-in needs to provide implementation of such
+ * function that will be called for each demux device on the
+ * module initialization. The function mpq_demux_plugin_init
+ * should be called during the HW plug-in module initialization.
+ */
+typedef int (*mpq_dmx_init)(struct dvb_adapter *mpq_adapter,
+ struct mpq_demux *demux);
+
+/**
+ * mpq_demux_plugin_init - Initialize demux devices and register
+ * them to the dvb adapter.
+ *
+ * @dmx_init_func: Pointer to the function to be used
+ * to initialize demux of the underlying HW plugin.
+ *
+ * Return error code
+ *
+ * Should be called at the HW plugin module initialization.
+ */
+int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func);
+
+/**
+ * mpq_demux_plugin_exit - terminate demux devices.
+ *
+ * Should be called at the HW plugin module termination.
+ */
+void mpq_dmx_plugin_exit(void);
+
+/**
+ * mpq_dmx_set_source - implmenetation of set_source routine.
+ *
+ * @demux: The demux device to set its source.
+ * @src: The source to be set.
+ *
+ * Return error code
+ *
+ * Can be used by the underlying plugins to implement kernel
+ * demux API set_source routine.
+ */
+int mpq_dmx_set_source(struct dmx_demux *demux, const dmx_source_t *src);
+
+/**
+ * mpq_dmx_map_buffer - map user-space buffer into kernel space.
+ *
+ * @demux: The demux device.
+ * @dmx_buffer: The demux buffer from user-space, assumes that
+ * buffer handle is ION file-handle.
+ * @priv_handle: Saves ION-handle of the buffer imported by this function.
+ * @kernel_mem: Saves kernel mapped address of the buffer.
+ *
+ * Return error code
+ *
+ * The function maps the buffer into kernel memory only if the buffer
+ * was not allocated with secure flag, otherwise the returned kernel
+ * memory address is set to NULL.
+ */
+int mpq_dmx_map_buffer(struct dmx_demux *demux, struct dmx_buffer *dmx_buffer,
+ void **priv_handle, void **kernel_mem);
+
+/**
+ * mpq_dmx_unmap_buffer - unmap user-space buffer from kernel space memory.
+ *
+ * @demux: The demux device.
+ * @priv_handle: ION-handle of the buffer returned from mpq_dmx_map_buffer.
+ *
+ * Return error code
+ *
+ * The function unmaps the buffer from kernel memory only if the buffer
+ * was not allocated with secure flag.
+ */
+int mpq_dmx_unmap_buffer(struct dmx_demux *demux, void *priv_handle);
+
+/**
+ * mpq_dmx_decoder_fullness_init - Initialize waiting
+ * mechanism on decoder's buffer fullness.
+ *
+ * @feed: The decoder's feed
+ *
+ * Return error code.
+ */
+int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_decoder_fullness_wait - Checks whether decoder buffer
+ * have free space as required, if not, wait for it.
+ *
+ * @feed: The decoder's feed
+ * @required_space: the required free space to wait for
+ *
+ * Return error code.
+ */
+int mpq_dmx_decoder_fullness_wait(struct dvb_demux_feed *feed,
+ size_t required_space);
+
+/**
+ * mpq_dmx_decoder_fullness_abort - Aborts waiting
+ * on decoder's buffer fullness if any waiting is done
+ * now. After calling this, to wait again the user must
+ * call mpq_dmx_decoder_fullness_init.
+ *
+ * @feed: The decoder's feed
+ *
+ * Return error code.
+ */
+int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_decoder_buffer_status - Returns the
+ * status of the decoder's buffer.
+ *
+ * @feed: The decoder's feed
+ * @dmx_buffer_status: Status of decoder's buffer
+ *
+ * Return error code.
+ */
+int mpq_dmx_decoder_buffer_status(struct dvb_demux_feed *feed,
+ struct dmx_buffer_status *dmx_buffer_status);
+
+/**
+ * mpq_dmx_reuse_decoder_buffer - release buffer passed to decoder for reuse
+ * by the stream-buffer.
+ *
+ * @feed: The decoder's feed.
+ * @cookie: stream-buffer handle of the buffer.
+ *
+ * Return error code
+ *
+ * The function releases the buffer provided by the stream-buffer
+ * connected to the decoder back to the stream-buffer for reuse.
+ */
+int mpq_dmx_reuse_decoder_buffer(struct dvb_demux_feed *feed, int cookie);
+
+/**
+ * mpq_dmx_process_video_packet - Assemble PES data and output it
+ * to the stream-buffer connected to the decoder.
+ *
+ * @feed: The feed used for the video TS packets
+ * @buf: The buffer holding video TS packet.
+ *
+ * Return error code.
+ *
+ * The function assumes it receives buffer with single TS packet
+ * of the relevant PID.
+ * If the output buffer is full while assembly, the function drops
+ * the packet and does not write them to the output buffer.
+ * Scrambled packets are bypassed.
+ */
+int mpq_dmx_process_video_packet(struct dvb_demux_feed *feed, const u8 *buf);
+
+/**
+ * mpq_dmx_process_pcr_packet - Extract PCR/STC pairs from
+ * a 192 bytes packet.
+ *
+ * @feed: The feed used for the PCR TS packets
+ * @buf: The buffer holding pcr/stc packet.
+ *
+ * Return error code.
+ *
+ * The function assumes it receives buffer with single TS packet
+ * of the relevant PID, and that it has 4 bytes
+ * suffix as extra timestamp in the following format:
+ *
+ * Byte3: TSIF flags
+ * Byte0-2: TTS, 0..2^24-1 at 105.47 Khz (27*10^6/256).
+ *
+ * The function callbacks dmxdev after extraction of the pcr/stc
+ * pair.
+ */
+int mpq_dmx_process_pcr_packet(struct dvb_demux_feed *feed, const u8 *buf);
+
+/**
+ * mpq_dmx_extract_pcr_and_dci() - Extract the PCR field and discontinuity
+ * indicator from a TS packet buffer.
+ *
+ * @buf: TS packet buffer
+ * @pcr: returned PCR value
+ * @dci: returned discontinuity indicator
+ *
+ * Returns 1 if PCR was extracted, 0 otherwise.
+ */
+int mpq_dmx_extract_pcr_and_dci(const u8 *buf, u64 *pcr, int *dci);
+
+/**
+ * mpq_dmx_init_debugfs_entries -
+ * Extend dvb-demux debugfs with mpq related entries (HW statistics and secure
+ * demux log level).
+ *
+ * @mpq_demux: The mpq_demux device to initialize.
+ */
+void mpq_dmx_init_debugfs_entries(struct mpq_demux *mpq_demux);
+
+/**
+ * mpq_dmx_update_hw_statistics -
+ * Update dvb-demux debugfs with HW notification statistics.
+ *
+ * @mpq_demux: The mpq_demux device to update.
+ */
+void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux);
+
+/**
+ * mpq_dmx_set_cipher_ops - Handles setting of cipher operations
+ *
+ * @feed: The feed to set its cipher operations
+ * @cipher_ops: Cipher operations to be set
+ *
+ * This common function handles only the case when working with
+ * secure-demux. When working with secure demux a single decrypt cipher
+ * operation is allowed.
+ *
+ * Return error code
+ */
+int mpq_dmx_set_cipher_ops(struct dvb_demux_feed *feed,
+ struct dmx_cipher_operations *cipher_ops);
+
+/**
+ * mpq_dmx_convert_tts - Convert timestamp attached by HW to each TS
+ * packet to 27MHz.
+ *
+ * @feed: The feed with TTS attached
+ * @timestamp: Buffer holding the timestamp attached by the HW
+ * @timestampIn27Mhz: Timestamp result in 27MHz
+ *
+ * Return error code
+ */
+void mpq_dmx_convert_tts(struct dvb_demux_feed *feed,
+ const u8 timestamp[TIMESTAMP_LEN],
+ u64 *timestampIn27Mhz);
+
+/**
+ * mpq_sdmx_open_session - Handle the details of opening a new secure demux
+ * session for the specified mpq demux instance. Multiple calls to this
+ * is allowed, reference counting is managed to open it only when needed.
+ *
+ * @mpq_demux: mpq demux instance
+ *
+ * Return error code
+ */
+int mpq_sdmx_open_session(struct mpq_demux *mpq_demux);
+
+/**
+ * mpq_sdmx_close_session - Closes secure demux session. The session
+ * is closed only if reference counter of the session reaches 0.
+ *
+ * @mpq_demux: mpq demux instance
+ *
+ * Return error code
+ */
+int mpq_sdmx_close_session(struct mpq_demux *mpq_demux);
+
+/**
+ * mpq_dmx_init_mpq_feed - Initialize an mpq feed object
+ * The function allocates mpq_feed object and saves in the dvb_demux_feed
+ * priv field.
+ *
+ * @feed: A dvb demux level feed parent object
+ *
+ * Return error code
+ */
+int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_terminate_feed - Destroy an mpq feed object
+ *
+ * @feed: A dvb demux level feed parent object
+ *
+ * Return error code
+ */
+int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_init_video_feed() - Initializes video related data structures
+ *
+ * @mpq_feed: mpq_feed object to initialize
+ *
+ * Return error code
+ */
+int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed);
+
+/**
+ * mpq_dmx_terminate_video_feed() - Release video related feed resources
+ *
+ * @mpq_feed: mpq_feed object to terminate
+ *
+ * Return error code
+ */
+int mpq_dmx_terminate_video_feed(struct mpq_feed *mpq_feed);
+
+/**
+ * mpq_dmx_write - demux write() function implementation.
+ *
+ * A wrapper function used for writing new data into the demux via DVR.
+ * It checks where new data should actually go, the secure demux or the normal
+ * dvb demux software demux.
+ *
+ * @demux: demux interface
+ * @buf: input buffer
+ * @count: number of data bytes in input buffer
+ *
+ * Return number of bytes processed or error code
+ */
+int mpq_dmx_write(struct dmx_demux *demux, const char *buf, size_t count);
+
+/**
+ * mpq_sdmx_process - Perform demuxing process on the specified input buffer
+ * in the secure demux instance
+ *
+ * @mpq_demux: mpq demux instance
+ * @input: input buffer descriptor
+ * @fill_count: number of data bytes in input buffer that can be read
+ * @read_offset: offset in buffer for reading
+ * @tsp_size: size of single TS packet
+ *
+ * Return number of bytes read or error code
+ */
+int mpq_sdmx_process(struct mpq_demux *mpq_demux,
+ struct sdmx_buff_descr *input,
+ u32 fill_count,
+ u32 read_offset,
+ size_t tsp_size);
+
+/**
+ * mpq_sdmx_loaded - Returns 1 if secure demux application is loaded,
+ * 0 otherwise. This function should be used to determine whether or not
+ * processing should take place in the SDMX.
+ */
+int mpq_sdmx_is_loaded(void);
+
+/**
+ * mpq_dmx_oob_command - Handles OOB command from dvb-demux.
+ *
+ * OOB marker commands trigger callback to the dmxdev.
+ * Handling of EOS command may trigger current (last on stream) PES/Frame to
+ * be reported, in addition to callback to the dmxdev.
+ * In case secure demux is active for the feed, EOS command is passed to the
+ * secure demux for handling.
+ *
+ * @feed: dvb demux feed object
+ * @cmd: oob command data
+ *
+ * returns 0 on success or error
+ */
+int mpq_dmx_oob_command(struct dvb_demux_feed *feed,
+ struct dmx_oob_command *cmd);
+
+/**
+ * mpq_dmx_peer_rec_feed() - For a recording filter with multiple feeds objects
+ * search for a feed object that shares the same filter as the specified feed
+ * object, and return it.
+ * This can be used to test whether the specified feed object is the first feed
+ * allocate for the recording filter - return value is NULL.
+ *
+ * @feed: dvb demux feed object
+ *
+ * Return the dvb_demux_feed sharing the same filter's buffer or NULL if no
+ * such is found.
+ */
+struct dvb_demux_feed *mpq_dmx_peer_rec_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_decoder_eos_cmd() - Report EOS event to the mpq_streambuffer
+ *
+ * @mpq_feed: Video mpq_feed object for notification
+ *
+ * Return error code
+ */
+int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed);
+
+/**
+ * mpq_dmx_parse_mandatory_pes_header() - Parse non-optional PES header fields
+ * from TS packet buffer and save results in the feed object.
+ *
+ * @feed: Video dvb demux feed object
+ * @feed_data: Structure where results will be saved
+ * @pes_header: Saved PES header
+ * @buf: Input buffer containing TS packet with the PES header
+ * @ts_payload_offset: Offset in 'buf' where payload begins
+ * @bytes_avail: Length of actual payload
+ *
+ * Return error code
+ */
+int mpq_dmx_parse_mandatory_pes_header(
+ struct dvb_demux_feed *feed,
+ struct mpq_video_feed_info *feed_data,
+ struct pes_packet_header *pes_header,
+ const u8 *buf,
+ u32 *ts_payload_offset,
+ int *bytes_avail);
+
+/**
+ * mpq_dmx_parse_remaining_pes_header() - Parse optional PES header fields
+ * from TS packet buffer and save results in the feed object.
+ * This function depends on mpq_dmx_parse_mandatory_pes_header being called
+ * first for state to be valid.
+ *
+ * @feed: Video dvb demux feed object
+ * @feed_data: Structure where results will be saved
+ * @pes_header: Saved PES header
+ * @buf: Input buffer containing TS packet with the PES header
+ * @ts_payload_offset: Offset in 'buf' where payload begins
+ * @bytes_avail: Length of actual payload
+ *
+ * Return error code
+ */
+int mpq_dmx_parse_remaining_pes_header(
+ struct dvb_demux_feed *feed,
+ struct mpq_video_feed_info *feed_data,
+ struct pes_packet_header *pes_header,
+ const u8 *buf,
+ u32 *ts_payload_offset,
+ int *bytes_avail);
+
+/**
+ * mpq_dmx_flush_stream_buffer() - Flush video stream buffer object of the
+ * specific video feed, both meta-data packets and data.
+ *
+ * @feed: dvb demux video feed object
+ *
+ * Return error code
+ */
+int mpq_dmx_flush_stream_buffer(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_save_pts_dts() - Save the current PTS/DTS data
+ *
+ * @feed_data: Video feed structure where PTS/DTS is saved
+ */
+static inline void mpq_dmx_save_pts_dts(struct mpq_video_feed_info *feed_data)
+{
+ if (feed_data->new_info_exists) {
+ feed_data->saved_pts_dts_info.pts_exist =
+ feed_data->new_pts_dts_info.pts_exist;
+ feed_data->saved_pts_dts_info.pts =
+ feed_data->new_pts_dts_info.pts;
+ feed_data->saved_pts_dts_info.dts_exist =
+ feed_data->new_pts_dts_info.dts_exist;
+ feed_data->saved_pts_dts_info.dts =
+ feed_data->new_pts_dts_info.dts;
+
+ feed_data->new_info_exists = 0;
+ feed_data->saved_info_used = 0;
+ }
+}
+
+/**
+ * mpq_dmx_write_pts_dts() - Write out the saved PTS/DTS data and mark as used
+ *
+ * @feed_data: Video feed structure where PTS/DTS was saved
+ * @info: PTS/DTS structure to write to
+ */
+static inline void mpq_dmx_write_pts_dts(struct mpq_video_feed_info *feed_data,
+ struct dmx_pts_dts_info *info)
+{
+ if (!feed_data->saved_info_used) {
+ info->pts_exist = feed_data->saved_pts_dts_info.pts_exist;
+ info->pts = feed_data->saved_pts_dts_info.pts;
+ info->dts_exist = feed_data->saved_pts_dts_info.dts_exist;
+ info->dts = feed_data->saved_pts_dts_info.dts;
+
+ feed_data->saved_info_used = 1;
+ } else {
+ info->pts_exist = 0;
+ info->dts_exist = 0;
+ }
+}
+
+/*
+ * mpq_dmx_calc_time_delta -
+ * Calculate delta in msec between two time snapshots.
+ *
+ * @curr_time: value of current time
+ * @prev_time: value of previous time
+ *
+ * Return time-delta in msec
+ */
+static inline u32 mpq_dmx_calc_time_delta(struct timespec *curr_time,
+ struct timespec *prev_time)
+{
+ struct timespec delta_time;
+ u64 delta_time_ms;
+
+ delta_time = timespec_sub(*curr_time, *prev_time);
+
+ delta_time_ms = ((u64)delta_time.tv_sec * MSEC_PER_SEC) +
+ delta_time.tv_nsec / NSEC_PER_MSEC;
+
+ return (u32)delta_time_ms;
+}
+
+void mpq_dmx_update_decoder_stat(struct mpq_feed *mpq_feed);
+
+/* Return the common module parameter tsif_mode */
+int mpq_dmx_get_param_tsif_mode(void);
+
+/* Return the common module parameter clock_inv */
+int mpq_dmx_get_param_clock_inv(void);
+
+/* Return the common module parameter mpq_sdmx_scramble_odd */
+int mpq_dmx_get_param_scramble_odd(void);
+
+/* Return the common module parameter mpq_sdmx_scramble_even */
+int mpq_dmx_get_param_scramble_even(void);
+
+/* Return the common module parameter mpq_sdmx_scramble_default_discard */
+int mpq_dmx_get_param_scramble_default_discard(void);
+
+
+#endif /* _MPQ_DMX_PLUGIN_COMMON_H */
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_sw.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_sw.c
new file mode 100644
index 000000000000..b350e4f4144a
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_sw.c
@@ -0,0 +1,280 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+
+static int mpq_sw_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+ int ret = -EINVAL;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+
+ MPQ_DVB_DBG_PRINT("%s(pid=%d) executed\n", __func__, feed->pid);
+
+ if (mpq_demux == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: invalid mpq_demux handle\n", __func__);
+ goto out;
+ }
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0) {
+ MPQ_DVB_ERR_PRINT("%s: only DVR source is supported (%d)\n",
+ __func__, mpq_demux->source);
+ goto out;
+ }
+
+ /*
+ * Always feed sections/PES starting from a new one and
+ * do not partial transfer data from older one
+ */
+ feed->pusi_seen = 0;
+
+ ret = mpq_dmx_init_mpq_feed(feed);
+ if (ret)
+ MPQ_DVB_ERR_PRINT("%s: mpq_dmx_init_mpq_feed failed(%d)\n",
+ __func__, ret);
+out:
+ return ret;
+}
+
+static int mpq_sw_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+ int ret;
+
+ MPQ_DVB_DBG_PRINT("%s(%d) executed\n", __func__, feed->pid);
+
+ ret = mpq_dmx_terminate_feed(feed);
+ if (ret)
+ MPQ_DVB_ERR_PRINT("%s: mpq_dmx_terminate_feed failed(%d)\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int mpq_sw_dmx_write_to_decoder(struct dvb_demux_feed *feed,
+ const u8 *buf, size_t len)
+{
+ /*
+ * It is assumed that this function is called once for each
+ * TS packet of the relevant feed.
+ */
+ if (len > (TIMESTAMP_LEN + TS_PACKET_SIZE))
+ MPQ_DVB_DBG_PRINT(
+ "%s: warnning - len larger than one packet\n",
+ __func__);
+
+ if (dvb_dmx_is_video_feed(feed))
+ return mpq_dmx_process_video_packet(feed, buf);
+
+ if (dvb_dmx_is_pcr_feed(feed))
+ return mpq_dmx_process_pcr_packet(feed, buf);
+
+ return 0;
+}
+
+static int mpq_sw_dmx_set_source(struct dmx_demux *demux,
+ const dmx_source_t *src)
+{
+ int ret = -EINVAL;
+
+ if (demux == NULL || demux->priv == NULL || src == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ goto out;
+ }
+
+ if (*src >= DMX_SOURCE_DVR0 && *src <= DMX_SOURCE_DVR3) {
+ ret = mpq_dmx_set_source(demux, src);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_set_source(%d) failed, ret=%d\n",
+ __func__, *src, ret);
+ } else {
+ MPQ_DVB_ERR_PRINT("%s: not a DVR source\n", __func__);
+ }
+
+out:
+ return ret;
+}
+
+static int mpq_sw_dmx_get_caps(struct dmx_demux *demux, struct dmx_caps *caps)
+{
+ struct dvb_demux *dvb_demux = demux->priv;
+
+ if (dvb_demux == NULL || caps == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA |
+ DMX_CAP_TS_INSERTION | DMX_CAP_VIDEO_INDEXING |
+ DMX_CAP_AUTO_BUFFER_FLUSH;
+ caps->recording_max_video_pids_indexed = 0;
+ caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
+ caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+ caps->num_pid_filters = MPQ_MAX_DMX_FILES;
+ caps->num_section_filters = dvb_demux->filternum;
+ caps->num_section_filters_per_pid = dvb_demux->filternum;
+ caps->section_filter_length = DMX_FILTER_SIZE;
+ caps->num_demod_inputs = 0;
+ caps->num_memory_inputs = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+ caps->max_bitrate = 192;
+ caps->demod_input_max_bitrate = 96;
+ caps->memory_input_max_bitrate = 96;
+ caps->num_cipher_ops = 1;
+
+ /* No STC support */
+ caps->max_stc = 0;
+
+ /* Buffer requirements */
+ caps->section.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->section.max_buffer_num = 1;
+ caps->section.max_size = 0xFFFFFFFF;
+ caps->section.size_alignment = 0;
+ caps->pes.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->pes.max_buffer_num = 1;
+ caps->pes.max_size = 0xFFFFFFFF;
+ caps->pes.size_alignment = 0;
+ caps->recording_188_tsp.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->recording_188_tsp.max_buffer_num = 1;
+ caps->recording_188_tsp.max_size = 0xFFFFFFFF;
+ caps->recording_188_tsp.size_alignment = 0;
+ caps->recording_192_tsp.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->recording_192_tsp.max_buffer_num = 1;
+ caps->recording_192_tsp.max_size = 0xFFFFFFFF;
+ caps->recording_192_tsp.size_alignment = 0;
+ caps->playback_188_tsp.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->playback_188_tsp.max_buffer_num = 1;
+ caps->playback_188_tsp.max_size = 0xFFFFFFFF;
+ caps->playback_188_tsp.size_alignment = 188;
+ caps->playback_192_tsp.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->playback_192_tsp.max_buffer_num = 1;
+ caps->playback_192_tsp.max_size = 0xFFFFFFFF;
+ caps->playback_192_tsp.size_alignment = 192;
+ caps->decoder.flags =
+ DMX_BUFFER_SECURED_IF_DECRYPTED |
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_LINEAR_GROUP_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->decoder.max_buffer_num = DMX_MAX_DECODER_BUFFER_NUM;
+ caps->decoder.max_size = 0xFFFFFFFF;
+ caps->decoder.size_alignment = SZ_4K;
+
+ return 0;
+}
+
+static int mpq_sw_dmx_init(struct dvb_adapter *mpq_adapter,
+ struct mpq_demux *mpq_demux)
+{
+ int ret;
+ struct dvb_demux *dvb_demux = &mpq_demux->demux;
+
+ /* Set the kernel-demux object capabilities */
+ mpq_demux->demux.dmx.capabilities =
+ DMX_TS_FILTERING |
+ DMX_PES_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING |
+ DMX_CRC_CHECKING |
+ DMX_TS_DESCRAMBLING;
+
+ mpq_demux->decoder_alloc_flags = ION_FLAG_CACHED;
+
+ /* Set dvb-demux "virtual" function pointers */
+ dvb_demux->priv = (void *)mpq_demux;
+ dvb_demux->filternum = MPQ_MAX_DMX_FILES;
+ dvb_demux->feednum = MPQ_MAX_DMX_FILES;
+ dvb_demux->start_feed = mpq_sw_dmx_start_filtering;
+ dvb_demux->stop_feed = mpq_sw_dmx_stop_filtering;
+ dvb_demux->write_to_decoder = mpq_sw_dmx_write_to_decoder;
+ dvb_demux->decoder_fullness_init = mpq_dmx_decoder_fullness_init;
+ dvb_demux->decoder_fullness_wait = mpq_dmx_decoder_fullness_wait;
+ dvb_demux->decoder_fullness_abort = mpq_dmx_decoder_fullness_abort;
+ dvb_demux->decoder_buffer_status = mpq_dmx_decoder_buffer_status;
+ dvb_demux->reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer;
+ dvb_demux->set_cipher_op = mpq_dmx_set_cipher_ops;
+ dvb_demux->oob_command = mpq_dmx_oob_command;
+ dvb_demux->convert_ts = mpq_dmx_convert_tts;
+ dvb_demux->flush_decoder_buffer = NULL;
+
+ /* Initialize dvb_demux object */
+ ret = dvb_dmx_init(dvb_demux);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed, ret=%d\n",
+ __func__, ret);
+ goto init_failed;
+ }
+
+ /* Now initialize the dmx-dev object */
+ mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES;
+ mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+ mpq_demux->dmxdev.capabilities = DMXDEV_CAP_DUPLEX;
+
+ mpq_demux->dmxdev.demux->set_source = mpq_sw_dmx_set_source;
+ mpq_demux->dmxdev.demux->get_stc = NULL;
+ mpq_demux->dmxdev.demux->get_caps = mpq_sw_dmx_get_caps;
+ mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer;
+ mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer;
+ mpq_demux->dmxdev.demux->write = mpq_dmx_write;
+ ret = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed, ret=%d\n",
+ __func__, ret);
+ goto init_failed_dmx_release;
+ }
+
+ /* Extend dvb-demux debugfs with mpq demux statistics. */
+ mpq_dmx_init_debugfs_entries(mpq_demux);
+
+ return 0;
+
+init_failed_dmx_release:
+ dvb_dmx_release(dvb_demux);
+init_failed:
+ return ret;
+}
+
+static int __init mpq_dmx_sw_plugin_init(void)
+{
+ return mpq_dmx_plugin_init(mpq_sw_dmx_init);
+}
+
+static void __exit mpq_dmx_sw_plugin_exit(void)
+{
+ mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_sw_plugin_init);
+module_exit(mpq_dmx_sw_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. demux software plugin");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
new file mode 100644
index 000000000000..be88bc1bf19f
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
@@ -0,0 +1,1968 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+#include <linux/qcom_tspp.h>
+#include "mpq_dvb_debug.h"
+#include "mpq_dmx_plugin_common.h"
+
+#define TSIF_COUNT 2
+
+/* Max number of PID filters */
+#define TSPP_MAX_PID_FILTER_NUM 128
+
+/* Max number of user-defined HW PID filters */
+#define TSPP_MAX_HW_PID_FILTER_NUM 15
+
+/* HW index of the last entry in the TSPP HW filter table */
+#define TSPP_LAST_HW_FILTER_INDEX 15
+
+/* Number of filters required to accept all packets except NULL packets */
+#define TSPP_BLOCK_NULLS_FILTERS_NUM 13
+
+/* Max number of section filters */
+#define TSPP_MAX_SECTION_FILTER_NUM 128
+
+/* For each TSIF we use a single pipe holding the data after PID filtering */
+#define TSPP_CHANNEL 0
+
+/* the channel_id set to TSPP driver based on TSIF number and channel type */
+#define TSPP_CHANNEL_ID(tsif, ch) ((tsif << 1) + ch)
+#define TSPP_GET_TSIF_NUM(ch_id) (ch_id >> 1)
+
+/* mask that set to care for all bits in pid filter */
+#define TSPP_PID_MASK 0x1FFF
+
+/* dvb-demux defines pid 0x2000 as full capture pid */
+#define TSPP_PASS_THROUGH_PID 0x2000
+
+/* NULL packets pid */
+#define TSPP_NULL_PACKETS_PID 0x1FFF
+
+#define TSPP_RAW_TTS_SIZE 192
+#define TSPP_RAW_SIZE 188
+
+#define MAX_BAM_DESCRIPTOR_SIZE (32 * 1024 - 1)
+
+#define MAX_BAM_DESCRIPTOR_COUNT (8 * 1024 - 2)
+
+#define TSPP_BUFFER_SIZE (500 * 1024) /* 500KB */
+
+#define TSPP_DEFAULT_DESCRIPTOR_SIZE (TSPP_RAW_TTS_SIZE)
+
+#define TSPP_BUFFER_COUNT(buffer_size) \
+ ((buffer_size) / tspp_desc_size)
+
+/* When TSPP notifies demux that new packets are received.
+ * Using max descriptor size (170 packets).
+ * Assuming 20MBit/sec stream, with 170 packets
+ * per descriptor there would be about 82 descriptors,
+ * Meaning about 82 notifications per second.
+ */
+#define TSPP_NOTIFICATION_SIZE(desc_size) \
+ (MAX_BAM_DESCRIPTOR_SIZE / (desc_size))
+
+/* Channel timeout in msec */
+#define TSPP_CHANNEL_TIMEOUT 100
+
+enum mem_buffer_allocation_mode {
+ MPQ_DMX_TSPP_INTERNAL_ALLOC = 0,
+ MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC = 1
+};
+
+/* module parameters for load time configuration */
+static int allocation_mode = MPQ_DMX_TSPP_INTERNAL_ALLOC;
+static int tspp_out_buffer_size = TSPP_BUFFER_SIZE;
+static int tspp_desc_size = TSPP_DEFAULT_DESCRIPTOR_SIZE;
+static int tspp_notification_size =
+ TSPP_NOTIFICATION_SIZE(TSPP_DEFAULT_DESCRIPTOR_SIZE);
+static int tspp_channel_timeout = TSPP_CHANNEL_TIMEOUT;
+static int tspp_out_ion_heap = ION_QSECOM_HEAP_ID;
+
+module_param(allocation_mode, int, S_IRUGO | S_IWUSR);
+module_param(tspp_out_buffer_size, int, S_IRUGO | S_IWUSR);
+module_param(tspp_desc_size, int, S_IRUGO | S_IWUSR);
+module_param(tspp_notification_size, int, S_IRUGO | S_IWUSR);
+module_param(tspp_channel_timeout, int, S_IRUGO | S_IWUSR);
+module_param(tspp_out_ion_heap, int, S_IRUGO | S_IWUSR);
+
+/* The following structure hold singleton information
+ * required for dmx implementation on top of TSPP.
+ */
+static struct
+{
+ /* Information for each TSIF input processing */
+ struct {
+ /*
+ * TSPP pipe holding all TS packets after PID filtering.
+ * The following is reference count for number of feeds
+ * allocated on that pipe.
+ */
+ int channel_ref;
+
+ /* Counter for data notifications on the pipe */
+ atomic_t data_cnt;
+
+ /* flag to indicate control operation is in progress */
+ atomic_t control_op;
+
+ /* ION handle used for TSPP data buffer allocation */
+ struct ion_handle *ch_mem_heap_handle;
+
+ /* TSPP data buffer heap virtual base address */
+ void *ch_mem_heap_virt_base;
+
+ /* TSPP data buffer heap physical base address */
+ ion_phys_addr_t ch_mem_heap_phys_base;
+
+ /* Buffer allocation index */
+ int buff_index;
+
+ /* Number of buffers */
+ u32 buffer_count;
+
+ /*
+ * Array holding the IDs of the TSPP buffer descriptors in the
+ * current aggregate, in order to release these descriptors at
+ * the end of processing.
+ */
+ int *aggregate_ids;
+
+ /*
+ * Holds PIDs of allocated filters along with
+ * how many feeds are opened on the same PID. For
+ * TSPP HW filters, holds also the filter table index.
+ * When pid == -1, the entry is free.
+ */
+ struct {
+ int pid;
+ int ref_count;
+ int hw_index;
+ } filters[TSPP_MAX_PID_FILTER_NUM];
+
+ /* Indicates available/allocated filter table indexes */
+ int hw_indexes[TSPP_MAX_HW_PID_FILTER_NUM];
+
+ /* Number of currently allocated PID filters */
+ u16 current_filter_count;
+
+ /*
+ * Flag to indicate whether the user added a filter to accept
+ * NULL packets (PID = 0x1FFF)
+ */
+ int pass_nulls_flag;
+
+ /*
+ * Flag to indicate whether the user added a filter to accept
+ * all packets (PID = 0x2000)
+ */
+ int pass_all_flag;
+
+ /*
+ * Flag to indicate whether the filter that accepts
+ * all packets has already been added and is
+ * currently enabled
+ */
+ int accept_all_filter_exists_flag;
+
+ /* Thread processing TS packets from TSPP */
+ struct task_struct *thread;
+ wait_queue_head_t wait_queue;
+
+ /* TSIF alias */
+ char name[TSIF_NAME_LENGTH];
+
+ /* Pointer to the demux connected to this TSIF */
+ struct mpq_demux *mpq_demux;
+
+ /* Mutex protecting the data-structure */
+ struct mutex mutex;
+ } tsif[TSIF_COUNT];
+
+ /* ION client used for TSPP data buffer allocation */
+ struct ion_client *ion_client;
+} mpq_dmx_tspp_info;
+
+static void *tspp_mem_allocator(int channel_id, u32 size,
+ phys_addr_t *phys_base, void *user)
+{
+ void *virt_addr = NULL;
+ int i = TSPP_GET_TSIF_NUM(channel_id);
+
+ if (mpq_dmx_tspp_info.tsif[i].buff_index ==
+ mpq_dmx_tspp_info.tsif[i].buffer_count)
+ return NULL;
+
+ virt_addr =
+ (mpq_dmx_tspp_info.tsif[i].ch_mem_heap_virt_base +
+ (mpq_dmx_tspp_info.tsif[i].buff_index * size));
+
+ *phys_base =
+ (mpq_dmx_tspp_info.tsif[i].ch_mem_heap_phys_base +
+ (mpq_dmx_tspp_info.tsif[i].buff_index * size));
+
+ mpq_dmx_tspp_info.tsif[i].buff_index++;
+
+ return virt_addr;
+}
+
+static void tspp_mem_free(int channel_id, u32 size,
+ void *virt_base, phys_addr_t phys_base, void *user)
+{
+ int i = TSPP_GET_TSIF_NUM(channel_id);
+
+ /*
+ * actual buffer heap free is done in mpq_dmx_tspp_plugin_exit().
+ * we update index here, so if this function is called repetitively
+ * for all the buffers, then afterwards tspp_mem_allocator()
+ * can be called again.
+ * Note: it would be incorrect to call tspp_mem_allocator()
+ * a few times, then call tspp_mem_free(), then call
+ * tspp_mem_allocator() again.
+ */
+ if (mpq_dmx_tspp_info.tsif[i].buff_index > 0)
+ mpq_dmx_tspp_info.tsif[i].buff_index--;
+}
+
+/**
+ * Returns a free HW filter index that can be used.
+ *
+ * @tsif: The TSIF to allocate filter from
+ *
+ * Return HW filter index or -ENOMEM if no filters available
+ */
+static int mpq_tspp_allocate_hw_filter_index(int tsif)
+{
+ int i;
+
+ for (i = 0; i < TSPP_MAX_HW_PID_FILTER_NUM; i++) {
+ if (mpq_dmx_tspp_info.tsif[tsif].hw_indexes[i] == 0) {
+ mpq_dmx_tspp_info.tsif[tsif].hw_indexes[i] = 1;
+ return i;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/**
+ * Releases a HW filter index for future reuse.
+ *
+ * @tsif: The TSIF from which the filter should be released
+ * @hw_index: The HW index to release
+ *
+ */
+static inline void mpq_tspp_release_hw_filter_index(int tsif, int hw_index)
+{
+ if ((hw_index >= 0) && (hw_index < TSPP_MAX_HW_PID_FILTER_NUM))
+ mpq_dmx_tspp_info.tsif[tsif].hw_indexes[hw_index] = 0;
+}
+
+
+/**
+ * Returns a free filter slot that can be used.
+ *
+ * @tsif: The TSIF to allocate filter from
+ *
+ * Return filter index or -ENOMEM if no filters available
+ */
+static int mpq_tspp_get_free_filter_slot(int tsif)
+{
+ int slot;
+
+ for (slot = 0; slot < TSPP_MAX_PID_FILTER_NUM; slot++)
+ if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid == -1)
+ return slot;
+
+ return -ENOMEM;
+}
+
+/**
+ * Returns filter index of specific pid.
+ *
+ * @tsif: The TSIF to which the pid is allocated
+ * @pid: The pid to search for
+ *
+ * Return filter index or -1 if no filter available
+ */
+static int mpq_tspp_get_filter_slot(int tsif, int pid)
+{
+ int slot;
+
+ for (slot = 0; slot < TSPP_MAX_PID_FILTER_NUM; slot++)
+ if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid == pid)
+ return slot;
+
+ return -EINVAL;
+}
+
+/**
+ * mpq_dmx_tspp_swfilter_desc - helper function
+ *
+ * Takes a tspp buffer descriptor and send it to the SW filter for demuxing,
+ * one TS packet at a time.
+ *
+ * @mpq_demux - mpq demux object
+ * @tspp_data_desc - tspp buffer descriptor
+ */
+static inline void mpq_dmx_tspp_swfilter_desc(struct mpq_demux *mpq_demux,
+ const struct tspp_data_descriptor *tspp_data_desc)
+{
+ u32 notif_size;
+ int i;
+
+ notif_size = tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
+ for (i = 0; i < notif_size; i++)
+ dvb_dmx_swfilter_packet(&mpq_demux->demux,
+ ((u8 *)tspp_data_desc->virt_base) +
+ i * TSPP_RAW_TTS_SIZE,
+ ((u8 *)tspp_data_desc->virt_base) +
+ i * TSPP_RAW_TTS_SIZE + TSPP_RAW_SIZE);
+}
+
+/**
+ * Demux TS packets from TSPP by secure-demux.
+ * The function assumes the buffer is physically contiguous
+ * and that TSPP descriptors are continuous in memory.
+ *
+ * @tsif: The TSIF interface to process its packets
+ * @channel_id: the TSPP output pipe with the TS packets
+ */
+static void mpq_dmx_tspp_aggregated_process(int tsif, int channel_id)
+{
+ const struct tspp_data_descriptor *tspp_data_desc;
+ struct mpq_demux *mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+ struct sdmx_buff_descr input;
+ size_t aggregate_len = 0;
+ size_t aggregate_count = 0;
+ phys_addr_t buff_start_addr_phys;
+ phys_addr_t buff_current_addr_phys = 0;
+ u32 notif_size;
+ int i;
+
+ while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) {
+ if (aggregate_count == 0)
+ buff_current_addr_phys = tspp_data_desc->phys_base;
+ notif_size = tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
+ mpq_dmx_tspp_info.tsif[tsif].aggregate_ids[aggregate_count] =
+ tspp_data_desc->id;
+ aggregate_len += tspp_data_desc->size;
+ aggregate_count++;
+ mpq_demux->hw_notification_size += notif_size;
+
+ /* Let SW filter process only if it might be relevant */
+ if (mpq_demux->num_active_feeds > mpq_demux->num_secure_feeds)
+ mpq_dmx_tspp_swfilter_desc(mpq_demux, tspp_data_desc);
+
+ }
+
+ if (!aggregate_count)
+ return;
+
+ buff_start_addr_phys =
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base;
+
+ input.base_addr = (u64)buff_start_addr_phys;
+ input.size = mpq_dmx_tspp_info.tsif[tsif].buffer_count * tspp_desc_size;
+
+ if (mpq_sdmx_is_loaded() && mpq_demux->sdmx_filter_count) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: SDMX Processing %zu descriptors: %zu bytes at start address 0x%llx, read offset %d\n",
+ __func__, aggregate_count, aggregate_len,
+ input.base_addr,
+ (int)(buff_current_addr_phys - buff_start_addr_phys));
+
+ mpq_sdmx_process(mpq_demux, &input, aggregate_len,
+ buff_current_addr_phys - buff_start_addr_phys,
+ TSPP_RAW_TTS_SIZE);
+ }
+
+ for (i = 0; i < aggregate_count; i++)
+ tspp_release_buffer(0, channel_id,
+ mpq_dmx_tspp_info.tsif[tsif].aggregate_ids[i]);
+}
+
+
+/**
+ * Demux thread function handling data from specific TSIF.
+ *
+ * @arg: TSIF number
+ */
+static int mpq_dmx_tspp_thread(void *arg)
+{
+ int tsif = (int)(uintptr_t)arg;
+ struct mpq_demux *mpq_demux;
+ const struct tspp_data_descriptor *tspp_data_desc;
+ atomic_t *data_cnt;
+ u32 notif_size;
+ int channel_id;
+ int ref_count;
+ int ret;
+
+ do {
+ ret = wait_event_interruptible(
+ mpq_dmx_tspp_info.tsif[tsif].wait_queue,
+ (atomic_read(&mpq_dmx_tspp_info.tsif[tsif].data_cnt) &&
+ !atomic_read(&mpq_dmx_tspp_info.tsif[tsif].control_op))
+ || kthread_should_stop());
+
+ if ((ret < 0) || kthread_should_stop()) {
+ MPQ_DVB_ERR_PRINT("%s: exit\n", __func__);
+ break;
+ }
+
+ /* Lock against the TSPP filters data-structure */
+ if (mutex_lock_interruptible(
+ &mpq_dmx_tspp_info.tsif[tsif].mutex))
+ return -ERESTARTSYS;
+
+ channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);
+
+ ref_count = mpq_dmx_tspp_info.tsif[tsif].channel_ref;
+ data_cnt = &mpq_dmx_tspp_info.tsif[tsif].data_cnt;
+
+ /* Make sure channel is still active */
+ if (ref_count == 0) {
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ continue;
+ }
+
+ atomic_dec(data_cnt);
+
+ mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+ mpq_demux->hw_notification_size = 0;
+
+ if (allocation_mode != MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC &&
+ mpq_sdmx_is_loaded())
+ pr_err_once(
+ "%s: TSPP Allocation mode does not support secure demux.\n",
+ __func__);
+
+ if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC &&
+ mpq_sdmx_is_loaded()) {
+ mpq_dmx_tspp_aggregated_process(tsif, channel_id);
+ } else {
+ /*
+ * Go through all filled descriptors
+ * and perform demuxing on them
+ */
+ do {
+ if (atomic_read(&mpq_dmx_tspp_info.tsif[tsif].
+ control_op)) {
+ /* restore for next iteration */
+ atomic_inc(data_cnt);
+ break;
+ }
+ tspp_data_desc = tspp_get_buffer(0, channel_id);
+ if (!tspp_data_desc)
+ break;
+
+ notif_size = tspp_data_desc->size /
+ TSPP_RAW_TTS_SIZE;
+ mpq_demux->hw_notification_size += notif_size;
+
+ mpq_dmx_tspp_swfilter_desc(mpq_demux,
+ tspp_data_desc);
+ /*
+ * Notify TSPP that the buffer
+ * is no longer needed
+ */
+ tspp_release_buffer(0, channel_id,
+ tspp_data_desc->id);
+ } while (1);
+ }
+
+ if (mpq_demux->hw_notification_size &&
+ (mpq_demux->hw_notification_size <
+ mpq_demux->hw_notification_min_size))
+ mpq_demux->hw_notification_min_size =
+ mpq_demux->hw_notification_size;
+
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ } while (1);
+
+ return 0;
+}
+
+/**
+ * Callback function from TSPP when new data is ready.
+ *
+ * @channel_id: Channel with new TS packets
+ * @user: user-data holding TSIF number
+ */
+static void mpq_tspp_callback(int channel_id, void *user)
+{
+ int tsif = (int)(uintptr_t)user;
+ struct mpq_demux *mpq_demux;
+
+ /* Save statistics on TSPP notifications */
+ mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
+ mpq_dmx_update_hw_statistics(mpq_demux);
+
+ atomic_inc(&mpq_dmx_tspp_info.tsif[tsif].data_cnt);
+ wake_up(&mpq_dmx_tspp_info.tsif[tsif].wait_queue);
+}
+
+/**
+ * Free memory of channel output of specific TSIF.
+ *
+ * @tsif: The TSIF id to which memory should be freed.
+ */
+static void mpq_dmx_channel_mem_free(int tsif)
+{
+ MPQ_DVB_DBG_PRINT("%s(%d)\n", __func__, tsif);
+
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base = 0;
+
+ if (!IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle)) {
+ if (!IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].
+ ch_mem_heap_virt_base))
+ ion_unmap_kernel(mpq_dmx_tspp_info.ion_client,
+ mpq_dmx_tspp_info.tsif[tsif].
+ ch_mem_heap_handle);
+
+ ion_free(mpq_dmx_tspp_info.ion_client,
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle);
+ }
+
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_virt_base = NULL;
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle = NULL;
+}
+
+/**
+ * Allocate memory for channel output of specific TSIF.
+ *
+ * @tsif: The TSIF id to which memory should be allocated.
+ *
+ * Return error status
+ */
+static int mpq_dmx_channel_mem_alloc(int tsif)
+{
+ int result;
+ size_t len;
+
+ MPQ_DVB_DBG_PRINT("%s(%d)\n", __func__, tsif);
+
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle =
+ ion_alloc(mpq_dmx_tspp_info.ion_client,
+ (mpq_dmx_tspp_info.tsif[tsif].buffer_count * tspp_desc_size),
+ SZ_4K,
+ ION_HEAP(tspp_out_ion_heap),
+ 0); /* non-cached */
+
+ if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle)) {
+ MPQ_DVB_ERR_PRINT("%s: ion_alloc() failed\n", __func__);
+ mpq_dmx_channel_mem_free(tsif);
+ return -ENOMEM;
+ }
+
+ /* save virtual base address of heap */
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_virt_base =
+ ion_map_kernel(mpq_dmx_tspp_info.ion_client,
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle);
+ if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].
+ ch_mem_heap_virt_base)) {
+ MPQ_DVB_ERR_PRINT("%s: ion_map_kernel() failed\n", __func__);
+ mpq_dmx_channel_mem_free(tsif);
+ return -ENOMEM;
+ }
+
+ /* save physical base address of heap */
+ result = ion_phys(mpq_dmx_tspp_info.ion_client,
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle,
+ &(mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base), &len);
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT("%s: ion_phys() failed\n", __func__);
+ mpq_dmx_channel_mem_free(tsif);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * Add a filter to accept all packets as the last entry
+ * of the TSPP HW filter table.
+ *
+ * @channel_id: Channel ID number.
+ * @source: TSPP source.
+ *
+ * Return error status
+ */
+static int mpq_tspp_add_accept_all_filter(int channel_id,
+ enum tspp_source source)
+{
+ struct tspp_filter tspp_filter;
+ int tsif = TSPP_GET_TSIF_NUM(channel_id);
+ int ret;
+
+ MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n",
+ __func__, channel_id, source);
+
+ if (mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag) {
+ MPQ_DVB_DBG_PRINT("%s: accept all filter already exists\n",
+ __func__);
+ return 0;
+ }
+
+ /* This filter will be the last entry in the table */
+ tspp_filter.priority = TSPP_LAST_HW_FILTER_INDEX;
+ /* Pass all pids - set mask to 0 */
+ tspp_filter.pid = 0;
+ tspp_filter.mask = 0;
+ /*
+ * Include TTS in RAW packets, if you change this to
+ * TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE
+ * accordingly.
+ */
+ tspp_filter.mode = TSPP_MODE_RAW;
+ tspp_filter.source = source;
+ tspp_filter.decrypt = 0;
+
+ ret = tspp_add_filter(0, channel_id, &tspp_filter);
+ if (!ret) {
+ mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag = 1;
+ MPQ_DVB_DBG_PRINT(
+ "%s: accept all filter added successfully\n",
+ __func__);
+ }
+
+ return ret;
+}
+
+/**
+ * Remove the filter that accepts all packets from the last entry
+ * of the TSPP HW filter table.
+ *
+ * @channel_id: Channel ID number.
+ * @source: TSPP source.
+ *
+ * Return error status
+ */
+static int mpq_tspp_remove_accept_all_filter(int channel_id,
+ enum tspp_source source)
+{
+ struct tspp_filter tspp_filter;
+ int tsif = TSPP_GET_TSIF_NUM(channel_id);
+ int ret;
+
+ MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n",
+ __func__, channel_id, source);
+
+ if (mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag == 0) {
+ MPQ_DVB_DBG_PRINT("%s: accept all filter doesn't exist\n",
+ __func__);
+ return 0;
+ }
+
+ tspp_filter.priority = TSPP_LAST_HW_FILTER_INDEX;
+
+ ret = tspp_remove_filter(0, channel_id, &tspp_filter);
+ if (!ret) {
+ mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag = 0;
+ MPQ_DVB_DBG_PRINT(
+ "%s: accept all filter removed successfully\n",
+ __func__);
+ }
+
+ return ret;
+}
+
+/**
+ * Add filters designed to accept all packets except NULL packets, i.e.
+ * packets with PID = 0x1FFF.
+ * This function is called after user-defined filters were removed,
+ * so it assumes that the first 13 HW filters in the TSPP filter
+ * table are free for use.
+ *
+ * @channel_id: Channel ID number.
+ * @source: TSPP source.
+ *
+ * Return 0 on success, -1 otherwise
+ */
+static int mpq_tspp_add_null_blocking_filters(int channel_id,
+ enum tspp_source source)
+{
+ struct tspp_filter tspp_filter;
+ int ret = 0;
+ int i, j;
+ u16 full_pid_mask = 0x1FFF;
+ u8 mask_shift;
+ u8 pid_shift;
+ int tsif = TSPP_GET_TSIF_NUM(channel_id);
+
+ MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n",
+ __func__, channel_id, source);
+
+ /*
+ * Add a total of 13 filters that will accept packets with
+ * every PID other than 0x1FFF, which is the NULL PID.
+ *
+ * Filter 0: accept all PIDs with bit 12 clear, i.e.
+ * PID = 0x0000 .. 0x0FFF (4096 PIDs in total):
+ * Mask = 0x1000, PID = 0x0000.
+ *
+ * Filter 12: Accept PID 0x1FFE:
+ * Mask = 0x1FFF, PID = 0x1FFE.
+ *
+ * In general: For N = 0 .. 12,
+ * Filter <N>: accept all PIDs with <N> MSBits set and bit <N-1> clear.
+ * Filter <N> Mask = N+1 MSBits set, others clear.
+ * Filter <N> PID = <N> MSBits set, others clear.
+ */
+
+ /*
+ * Include TTS in RAW packets, if you change this to
+ * TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE
+ * accordingly.
+ */
+ tspp_filter.mode = TSPP_MODE_RAW;
+ tspp_filter.source = source;
+ tspp_filter.decrypt = 0;
+
+ for (i = 0; i < TSPP_BLOCK_NULLS_FILTERS_NUM; i++) {
+ tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif);
+ if (tspp_filter.priority != i) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: got unexpected HW index %d, expected %d\n",
+ __func__, tspp_filter.priority, i);
+ ret = -1;
+ break;
+ }
+ mask_shift = (TSPP_BLOCK_NULLS_FILTERS_NUM - 1 - i);
+ pid_shift = (TSPP_BLOCK_NULLS_FILTERS_NUM - i);
+ tspp_filter.mask =
+ ((full_pid_mask >> mask_shift) << mask_shift);
+ tspp_filter.pid = ((full_pid_mask >> pid_shift) << pid_shift);
+
+ if (tspp_add_filter(0, channel_id, &tspp_filter)) {
+ ret = -1;
+ break;
+ }
+ }
+
+ if (ret) {
+ /* cleanup on failure */
+ for (j = 0; j < i; j++) {
+ tspp_filter.priority = j;
+ mpq_tspp_release_hw_filter_index(tsif, j);
+ tspp_remove_filter(0, channel_id, &tspp_filter);
+ }
+ } else {
+ MPQ_DVB_DBG_PRINT(
+ "%s: NULL blocking filters added successfully\n",
+ __func__);
+ }
+
+ return ret;
+}
+
+/**
+ * Remove filters designed to accept all packets except NULL packets, i.e.
+ * packets with PID = 0x1FFF.
+ *
+ * @channel_id: Channel ID number.
+ *
+ * @source: TSPP source.
+ *
+ * Return 0 on success, -1 otherwise
+ */
+static int mpq_tspp_remove_null_blocking_filters(int channel_id,
+ enum tspp_source source)
+{
+ struct tspp_filter tspp_filter;
+ int tsif = TSPP_GET_TSIF_NUM(channel_id);
+ int ret = 0;
+ int i;
+
+ MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n",
+ __func__, channel_id, source);
+
+ for (i = 0; i < TSPP_BLOCK_NULLS_FILTERS_NUM; i++) {
+ tspp_filter.priority = i;
+ if (tspp_remove_filter(0, channel_id, &tspp_filter)) {
+ MPQ_DVB_ERR_PRINT("%s: failed to remove filter %d\n",
+ __func__, i);
+ ret = -1;
+ }
+
+ mpq_tspp_release_hw_filter_index(tsif, i);
+ }
+
+ return ret;
+}
+
+/**
+ * Add all current user-defined filters (up to 15) as HW filters
+ *
+ * @channel_id: Channel ID number.
+ *
+ * @source: TSPP source.
+ *
+ * Return 0 on success, -1 otherwise
+ */
+static int mpq_tspp_add_all_user_filters(int channel_id,
+ enum tspp_source source)
+{
+ struct tspp_filter tspp_filter;
+ int tsif = TSPP_GET_TSIF_NUM(channel_id);
+ int slot;
+ u16 added_count = 0;
+ u16 total_filters_count = 0;
+
+ MPQ_DVB_DBG_PRINT("%s: executed\n", __func__);
+
+ /*
+ * Include TTS in RAW packets, if you change this to
+ * TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE
+ * accordingly.
+ */
+ tspp_filter.mode = TSPP_MODE_RAW;
+ tspp_filter.source = source;
+ tspp_filter.decrypt = 0;
+
+ for (slot = 0; slot < TSPP_MAX_PID_FILTER_NUM; slot++) {
+ if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid == -1)
+ continue;
+
+ /*
+ * count total number of user filters to verify that it is
+ * exactly TSPP_MAX_HW_PID_FILTER_NUM as expected.
+ */
+ total_filters_count++;
+
+ if (added_count > TSPP_MAX_HW_PID_FILTER_NUM)
+ continue;
+
+ tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif);
+
+ if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid ==
+ TSPP_PASS_THROUGH_PID) {
+ /* pass all pids */
+ tspp_filter.pid = 0;
+ tspp_filter.mask = 0;
+ } else {
+ tspp_filter.pid =
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid;
+ tspp_filter.mask = TSPP_PID_MASK;
+ }
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: adding HW filter, PID = %d, mask = 0x%X, index = %d\n",
+ __func__, tspp_filter.pid, tspp_filter.mask,
+ tspp_filter.priority);
+
+ if (!tspp_add_filter(0, channel_id, &tspp_filter)) {
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index =
+ tspp_filter.priority;
+ added_count++;
+ } else {
+ MPQ_DVB_ERR_PRINT("%s: tspp_add_filter failed\n",
+ __func__);
+ }
+ }
+
+ if ((added_count != TSPP_MAX_HW_PID_FILTER_NUM) ||
+ (added_count != total_filters_count))
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * Remove all user-defined HW filters
+ *
+ * @channel_id: Channel ID number.
+ *
+ * @source: TSPP source.
+ *
+ * Return 0 on success, -1 otherwise
+ */
+static int mpq_tspp_remove_all_user_filters(int channel_id,
+ enum tspp_source source)
+{
+ struct tspp_filter tspp_filter;
+ int ret = 0;
+ int tsif = TSPP_GET_TSIF_NUM(channel_id);
+ int i;
+
+ MPQ_DVB_DBG_PRINT("%s: executed\n", __func__);
+
+ for (i = 0; i < TSPP_MAX_HW_PID_FILTER_NUM; i++) {
+ tspp_filter.priority = i;
+ MPQ_DVB_DBG_PRINT("%s: Removing HW filter %d\n",
+ __func__, tspp_filter.priority);
+ if (tspp_remove_filter(0, channel_id, &tspp_filter))
+ ret = -1;
+
+ mpq_tspp_release_hw_filter_index(tsif, i);
+ mpq_dmx_tspp_info.tsif[tsif].filters[i].hw_index = -1;
+ }
+
+ return ret;
+}
+
+/**
+ * Configure TSPP channel to filter the PID of new feed.
+ *
+ * @feed: The feed to configure the channel with
+ *
+ * Return error status
+ *
+ * The function checks if the new PID can be added to an already
+ * allocated channel, if not, a new channel is allocated and configured.
+ */
+static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed)
+{
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+ struct tspp_select_source tspp_source;
+ struct tspp_filter tspp_filter;
+ int tsif;
+ int tsif_mode = mpq_dmx_get_param_tsif_mode();
+ int ret = 0;
+ int slot;
+ int channel_id;
+ int *channel_ref_count;
+ u32 buffer_size;
+ int restore_user_filters = 0;
+ int remove_accept_all_filter = 0;
+ int remove_null_blocking_filters = 0;
+ size_t agg_size;
+
+ tspp_source.clk_inverse = mpq_dmx_get_param_clock_inv();
+ tspp_source.data_inverse = 0;
+ tspp_source.sync_inverse = 0;
+ tspp_source.enable_inverse = 0;
+
+ MPQ_DVB_DBG_PRINT("%s: executed, PID = %d\n", __func__, feed->pid);
+
+ switch (tsif_mode) {
+ case 1:
+ tspp_source.mode = TSPP_TSIF_MODE_1;
+ break;
+ case 2:
+ tspp_source.mode = TSPP_TSIF_MODE_2;
+ break;
+ default:
+ tspp_source.mode = TSPP_TSIF_MODE_LOOPBACK;
+ break;
+ }
+
+ /* determine the TSIF we are reading from */
+ if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+ tsif = 0;
+ tspp_source.source = TSPP_SOURCE_TSIF0;
+ } else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+ tsif = 1;
+ tspp_source.source = TSPP_SOURCE_TSIF1;
+ } else {
+ /* invalid source */
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid input source (%d)\n",
+ __func__,
+ mpq_demux->source);
+
+ return -EINVAL;
+ }
+
+ atomic_inc(&mpq_dmx_tspp_info.tsif[tsif].control_op);
+ if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex)) {
+ atomic_dec(&mpq_dmx_tspp_info.tsif[tsif].control_op);
+ return -ERESTARTSYS;
+ }
+
+ /*
+ * It is possible that this PID was already requested before.
+ * Can happen if we play and record same PES or PCR
+ * piggypacked on video packet.
+ */
+ slot = mpq_tspp_get_filter_slot(tsif, feed->pid);
+ if (slot >= 0) {
+ /* PID already configured */
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++;
+ goto out;
+ }
+
+
+ channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);
+ channel_ref_count = &mpq_dmx_tspp_info.tsif[tsif].channel_ref;
+
+ /*
+ * Recalculate 'tspp_notification_size' and buffer count in case
+ * 'tspp_desc_size' or 'tspp_out_buffer_size' parameters have changed.
+ */
+ buffer_size = tspp_desc_size;
+ tspp_notification_size = TSPP_NOTIFICATION_SIZE(tspp_desc_size);
+ mpq_dmx_tspp_info.tsif[tsif].buffer_count =
+ TSPP_BUFFER_COUNT(tspp_out_buffer_size);
+ if (mpq_dmx_tspp_info.tsif[tsif].buffer_count >
+ MAX_BAM_DESCRIPTOR_COUNT)
+ mpq_dmx_tspp_info.tsif[tsif].buffer_count =
+ MAX_BAM_DESCRIPTOR_COUNT;
+
+ /* check if required TSPP pipe is already allocated or not */
+ if (*channel_ref_count == 0) {
+ if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
+ agg_size = mpq_dmx_tspp_info.tsif[tsif].buffer_count *
+ sizeof(int);
+ mpq_dmx_tspp_info.tsif[tsif].aggregate_ids =
+ vzalloc(agg_size);
+ if (!mpq_dmx_tspp_info.tsif[tsif].aggregate_ids) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to allocate memory for buffer descriptors aggregation\n",
+ __func__);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = mpq_dmx_channel_mem_alloc(tsif);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_channel_mem_alloc(%d) failed (%d)\n",
+ __func__,
+ channel_id,
+ ret);
+
+ goto add_channel_failed;
+ }
+ }
+
+ ret = tspp_open_channel(0, channel_id);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tspp_open_channel(%d) failed (%d)\n",
+ __func__,
+ channel_id,
+ ret);
+
+ goto add_channel_failed;
+ }
+
+ /* set TSPP source */
+ ret = tspp_open_stream(0, channel_id, &tspp_source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tspp_select_source(%d,%d) failed (%d)\n",
+ __func__,
+ channel_id,
+ tspp_source.source,
+ ret);
+
+ goto add_channel_close_ch;
+ }
+
+ /* register notification on TS packets */
+ tspp_register_notification(0,
+ channel_id,
+ mpq_tspp_callback,
+ (void *)(uintptr_t)tsif,
+ tspp_channel_timeout);
+
+ /*
+ * Register allocator and provide allocation function
+ * that allocates from contiguous memory so that we can have
+ * big notification size, smallest descriptor, and still provide
+ * TZ with single big buffer based on notification size.
+ */
+ if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
+ ret = tspp_allocate_buffers(0, channel_id,
+ mpq_dmx_tspp_info.tsif[tsif].buffer_count,
+ buffer_size, tspp_notification_size,
+ tspp_mem_allocator, tspp_mem_free, NULL);
+ } else {
+ ret = tspp_allocate_buffers(0, channel_id,
+ mpq_dmx_tspp_info.tsif[tsif].buffer_count,
+ buffer_size, tspp_notification_size,
+ NULL, NULL, NULL);
+ }
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tspp_allocate_buffers(%d) failed (%d)\n",
+ __func__,
+ channel_id,
+ ret);
+
+ goto add_channel_unregister_notif;
+ }
+
+ mpq_dmx_tspp_info.tsif[tsif].mpq_demux = mpq_demux;
+ }
+
+ /* add new PID to the existing pipe */
+ slot = mpq_tspp_get_free_filter_slot(tsif);
+ if (slot < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_get_free_filter_slot(%d) failed\n",
+ __func__, tsif);
+
+ goto add_channel_unregister_notif;
+ }
+
+ if (feed->pid == TSPP_PASS_THROUGH_PID)
+ mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 1;
+ else if (feed->pid == TSPP_NULL_PACKETS_PID)
+ mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 1;
+
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = feed->pid;
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++;
+
+ tspp_filter.priority = -1;
+
+ if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count <
+ TSPP_MAX_HW_PID_FILTER_NUM) {
+ /* HW filtering mode */
+ tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif);
+ if (tspp_filter.priority < 0)
+ goto add_channel_free_filter_slot;
+
+ if (feed->pid == TSPP_PASS_THROUGH_PID) {
+ /* pass all pids */
+ tspp_filter.pid = 0;
+ tspp_filter.mask = 0;
+ } else {
+ tspp_filter.pid = feed->pid;
+ tspp_filter.mask = TSPP_PID_MASK;
+ }
+
+ /*
+ * Include TTS in RAW packets, if you change this to
+ * TSPP_MODE_RAW_NO_SUFFIX you must also change
+ * TSPP_RAW_TTS_SIZE accordingly.
+ */
+ tspp_filter.mode = TSPP_MODE_RAW;
+ tspp_filter.source = tspp_source.source;
+ tspp_filter.decrypt = 0;
+ ret = tspp_add_filter(0, channel_id, &tspp_filter);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tspp_add_filter(%d) failed (%d)\n",
+ __func__,
+ channel_id,
+ ret);
+
+ goto add_channel_free_filter_slot;
+ }
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index =
+ tspp_filter.priority;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: HW filtering mode: added TSPP HW filter, PID = %d, mask = 0x%X, index = %d\n",
+ __func__, tspp_filter.pid, tspp_filter.mask,
+ tspp_filter.priority);
+ } else if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count ==
+ TSPP_MAX_HW_PID_FILTER_NUM) {
+ /* Crossing the threshold - from HW to SW filtering mode */
+
+ /* Add a temporary filter to accept all packets */
+ ret = mpq_tspp_add_accept_all_filter(channel_id,
+ tspp_source.source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_add_accept_all_filter(%d, %d) failed\n",
+ __func__, channel_id, tspp_source.source);
+
+ goto add_channel_free_filter_slot;
+ }
+
+ /* Remove all existing user filters */
+ ret = mpq_tspp_remove_all_user_filters(channel_id,
+ tspp_source.source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_remove_all_user_filters(%d, %d) failed\n",
+ __func__, channel_id, tspp_source.source);
+
+ restore_user_filters = 1;
+ remove_accept_all_filter = 1;
+
+ goto add_channel_free_filter_slot;
+ }
+
+ /* Add HW filters to block NULL packets */
+ ret = mpq_tspp_add_null_blocking_filters(channel_id,
+ tspp_source.source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_add_null_blocking_filters(%d, %d) failed\n",
+ __func__, channel_id, tspp_source.source);
+
+ restore_user_filters = 1;
+ remove_accept_all_filter = 1;
+
+ goto add_channel_free_filter_slot;
+ }
+
+ /* Remove filters that accepts all packets, if necessary */
+ if ((mpq_dmx_tspp_info.tsif[tsif].pass_all_flag == 0) &&
+ (mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag == 0)) {
+
+ ret = mpq_tspp_remove_accept_all_filter(channel_id,
+ tspp_source.source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_remove_accept_all_filter(%d, %d) failed\n",
+ __func__, channel_id,
+ tspp_source.source);
+
+ remove_null_blocking_filters = 1;
+ restore_user_filters = 1;
+ remove_accept_all_filter = 1;
+
+ goto add_channel_free_filter_slot;
+ }
+ }
+ } else {
+ /* Already working in SW filtering mode */
+ if (mpq_dmx_tspp_info.tsif[tsif].pass_all_flag ||
+ mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag) {
+
+ ret = mpq_tspp_add_accept_all_filter(channel_id,
+ tspp_source.source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_add_accept_all_filter(%d, %d) failed\n",
+ __func__, channel_id,
+ tspp_source.source);
+
+ goto add_channel_free_filter_slot;
+ }
+ }
+ }
+
+ (*channel_ref_count)++;
+ mpq_dmx_tspp_info.tsif[tsif].current_filter_count++;
+
+ MPQ_DVB_DBG_PRINT("%s: success, current_filter_count = %d\n",
+ __func__, mpq_dmx_tspp_info.tsif[tsif].current_filter_count);
+
+ goto out;
+
+add_channel_free_filter_slot:
+ /* restore internal database state */
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = -1;
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count--;
+
+ /* release HW index if we allocated one */
+ if (tspp_filter.priority >= 0) {
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index = -1;
+ mpq_tspp_release_hw_filter_index(tsif, tspp_filter.priority);
+ }
+
+ /* restore HW filter table state if necessary */
+ if (remove_null_blocking_filters)
+ mpq_tspp_remove_null_blocking_filters(channel_id,
+ tspp_source.source);
+
+ if (restore_user_filters)
+ mpq_tspp_add_all_user_filters(channel_id, tspp_source.source);
+
+ if (remove_accept_all_filter)
+ mpq_tspp_remove_accept_all_filter(channel_id,
+ tspp_source.source);
+
+ /* restore flags. we can only get here if we changed the flags. */
+ if (feed->pid == TSPP_PASS_THROUGH_PID)
+ mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 0;
+ else if (feed->pid == TSPP_NULL_PACKETS_PID)
+ mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 0;
+
+add_channel_unregister_notif:
+ if (*channel_ref_count == 0) {
+ tspp_unregister_notification(0, channel_id);
+ tspp_close_stream(0, channel_id);
+ }
+add_channel_close_ch:
+ if (*channel_ref_count == 0)
+ tspp_close_channel(0, channel_id);
+add_channel_failed:
+ if (*channel_ref_count == 0)
+ if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
+ vfree(mpq_dmx_tspp_info.tsif[tsif].aggregate_ids);
+ mpq_dmx_tspp_info.tsif[tsif].aggregate_ids = NULL;
+ mpq_dmx_channel_mem_free(tsif);
+ }
+
+out:
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ atomic_dec(&mpq_dmx_tspp_info.tsif[tsif].control_op);
+ return ret;
+}
+
+/**
+ * Removes filter from TSPP.
+ *
+ * @feed: The feed to remove
+ *
+ * Return error status
+ *
+ * The function checks if this is the only PID allocated within
+ * the channel, if so, the channel is closed as well.
+ */
+static int mpq_tspp_dmx_remove_channel(struct dvb_demux_feed *feed)
+{
+ int tsif;
+ int ret = 0;
+ int channel_id;
+ int slot;
+ atomic_t *data_cnt;
+ int *channel_ref_count;
+ enum tspp_source tspp_source;
+ struct tspp_filter tspp_filter;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+ int restore_null_blocking_filters = 0;
+ int remove_accept_all_filter = 0;
+ int remove_user_filters = 0;
+ int accept_all_filter_existed = 0;
+
+ MPQ_DVB_DBG_PRINT("%s: executed, PID = %d\n", __func__, feed->pid);
+
+ /* determine the TSIF we are reading from */
+ if (mpq_demux->source == DMX_SOURCE_FRONT0) {
+ tsif = 0;
+ tspp_source = TSPP_SOURCE_TSIF0;
+ } else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
+ tsif = 1;
+ tspp_source = TSPP_SOURCE_TSIF1;
+ } else {
+ /* invalid source */
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid input source (%d)\n",
+ __func__,
+ mpq_demux->source);
+
+ return -EINVAL;
+ }
+
+ atomic_inc(&mpq_dmx_tspp_info.tsif[tsif].control_op);
+ if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex)) {
+ atomic_dec(&mpq_dmx_tspp_info.tsif[tsif].control_op);
+ return -ERESTARTSYS;
+ }
+
+ channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);
+ channel_ref_count = &mpq_dmx_tspp_info.tsif[tsif].channel_ref;
+ data_cnt = &mpq_dmx_tspp_info.tsif[tsif].data_cnt;
+
+ /* check if required TSPP pipe is already allocated or not */
+ if (*channel_ref_count == 0) {
+ /* invalid feed provided as the channel is not allocated */
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid feed (%d)\n",
+ __func__,
+ channel_id);
+
+ ret = -EINVAL;
+ goto out;
+ }
+
+ slot = mpq_tspp_get_filter_slot(tsif, feed->pid);
+
+ if (slot < 0) {
+ /* invalid feed provided as it has no filter allocated */
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_get_filter_slot failed (%d,%d)\n",
+ __func__,
+ feed->pid,
+ tsif);
+
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* since filter was found, ref_count > 0 so it's ok to decrement it */
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count--;
+
+ if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count) {
+ /*
+ * there are still references to this pid, do not
+ * remove the filter yet
+ */
+ goto out;
+ }
+
+ if (feed->pid == TSPP_PASS_THROUGH_PID)
+ mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 0;
+ else if (feed->pid == TSPP_NULL_PACKETS_PID)
+ mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 0;
+
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = -1;
+
+ if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count <=
+ TSPP_MAX_HW_PID_FILTER_NUM) {
+ /* staying in HW filtering mode */
+ tspp_filter.priority =
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index;
+ ret = tspp_remove_filter(0, channel_id, &tspp_filter);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: tspp_remove_filter failed (%d,%d)\n",
+ __func__,
+ channel_id,
+ tspp_filter.priority);
+
+ goto remove_channel_failed_restore_count;
+ }
+ mpq_tspp_release_hw_filter_index(tsif, tspp_filter.priority);
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index = -1;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: HW filtering mode: Removed TSPP HW filter, PID = %d, index = %d\n",
+ __func__, feed->pid, tspp_filter.priority);
+ } else if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count ==
+ (TSPP_MAX_HW_PID_FILTER_NUM + 1)) {
+ /* Crossing the threshold - from SW to HW filtering mode */
+
+ accept_all_filter_existed =
+ mpq_dmx_tspp_info.tsif[tsif].
+ accept_all_filter_exists_flag;
+
+ /* Add a temporary filter to accept all packets */
+ ret = mpq_tspp_add_accept_all_filter(channel_id,
+ tspp_source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_add_accept_all_filter(%d, %d) failed\n",
+ __func__, channel_id, tspp_source);
+
+ goto remove_channel_failed_restore_count;
+ }
+
+ ret = mpq_tspp_remove_null_blocking_filters(channel_id,
+ tspp_source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_remove_null_blocking_filters(%d, %d) failed\n",
+ __func__, channel_id, tspp_source);
+
+ restore_null_blocking_filters = 1;
+ if (!accept_all_filter_existed)
+ remove_accept_all_filter = 1;
+
+ goto remove_channel_failed_restore_count;
+ }
+
+ ret = mpq_tspp_add_all_user_filters(channel_id,
+ tspp_source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_add_all_user_filters(%d, %d) failed\n",
+ __func__, channel_id, tspp_source);
+
+ remove_user_filters = 1;
+ restore_null_blocking_filters = 1;
+ if (!accept_all_filter_existed)
+ remove_accept_all_filter = 1;
+
+ goto remove_channel_failed_restore_count;
+ }
+
+ ret = mpq_tspp_remove_accept_all_filter(channel_id,
+ tspp_source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_remove_accept_all_filter(%d, %d) failed\n",
+ __func__, channel_id, tspp_source);
+
+ remove_user_filters = 1;
+ restore_null_blocking_filters = 1;
+ if (!accept_all_filter_existed)
+ remove_accept_all_filter = 1;
+
+ goto remove_channel_failed_restore_count;
+ }
+ } else {
+ /* staying in SW filtering mode */
+ if ((mpq_dmx_tspp_info.tsif[tsif].pass_all_flag == 0) &&
+ (mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag == 0)) {
+
+ ret = mpq_tspp_remove_accept_all_filter(channel_id,
+ tspp_source);
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_tspp_remove_accept_all_filter(%d, %d) failed\n",
+ __func__, channel_id,
+ tspp_source);
+
+ goto remove_channel_failed_restore_count;
+ }
+ }
+ }
+
+ mpq_dmx_tspp_info.tsif[tsif].current_filter_count--;
+ (*channel_ref_count)--;
+
+ MPQ_DVB_DBG_PRINT("%s: success, current_filter_count = %d\n",
+ __func__, mpq_dmx_tspp_info.tsif[tsif].current_filter_count);
+
+ if (*channel_ref_count == 0) {
+ /* channel is not used any more, release it */
+ tspp_unregister_notification(0, channel_id);
+ tspp_close_stream(0, channel_id);
+ tspp_close_channel(0, channel_id);
+ atomic_set(data_cnt, 0);
+
+ if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
+ vfree(mpq_dmx_tspp_info.tsif[tsif].aggregate_ids);
+ mpq_dmx_tspp_info.tsif[tsif].aggregate_ids = NULL;
+ mpq_dmx_channel_mem_free(tsif);
+ }
+ }
+
+ goto out;
+
+remove_channel_failed_restore_count:
+ /* restore internal database state */
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = feed->pid;
+ mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++;
+
+ if (remove_user_filters)
+ mpq_tspp_remove_all_user_filters(channel_id, tspp_source);
+
+ if (restore_null_blocking_filters)
+ mpq_tspp_add_null_blocking_filters(channel_id, tspp_source);
+
+ if (remove_accept_all_filter)
+ mpq_tspp_remove_accept_all_filter(channel_id, tspp_source);
+
+ /* restore flags. we can only get here if we changed the flags. */
+ if (feed->pid == TSPP_PASS_THROUGH_PID)
+ mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 1;
+ else if (feed->pid == TSPP_NULL_PACKETS_PID)
+ mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 1;
+
+out:
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
+ atomic_dec(&mpq_dmx_tspp_info.tsif[tsif].control_op);
+ return ret;
+}
+
+static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
+{
+ int ret;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s(pid=%d) executed\n",
+ __func__,
+ feed->pid);
+
+ if (mpq_demux == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid mpq_demux handle\n",
+ __func__);
+
+ return -EINVAL;
+ }
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0) {
+ /* source from TSPP, need to configure tspp pipe */
+ ret = mpq_tspp_dmx_add_channel(feed);
+
+ if (ret < 0) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_tspp_dmx_add_channel failed(%d)\n",
+ __func__,
+ ret);
+ return ret;
+ }
+ }
+
+ /*
+ * Always feed sections/PES starting from a new one and
+ * do not partial transfer data from older one
+ */
+ feed->pusi_seen = 0;
+
+ ret = mpq_dmx_init_mpq_feed(feed);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_init_mpq_feed failed(%d)\n",
+ __func__,
+ ret);
+ if (mpq_demux->source < DMX_SOURCE_DVR0)
+ mpq_tspp_dmx_remove_channel(feed);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mpq_tspp_dmx_stop_filtering(struct dvb_demux_feed *feed)
+{
+ int ret = 0;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+
+ MPQ_DVB_DBG_PRINT("%s(%d) executed\n", __func__, feed->pid);
+
+ mpq_dmx_terminate_feed(feed);
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0) {
+ /* source from TSPP, need to configure tspp pipe */
+ ret = mpq_tspp_dmx_remove_channel(feed);
+ }
+
+ return ret;
+}
+
+static int mpq_tspp_dmx_write_to_decoder(
+ struct dvb_demux_feed *feed,
+ const u8 *buf,
+ size_t len)
+{
+ /*
+ * It is assumed that this function is called once for each
+ * TS packet of the relevant feed.
+ */
+ if (len > TSPP_RAW_TTS_SIZE)
+ MPQ_DVB_DBG_PRINT(
+ "%s: warnning - len larger than one packet\n",
+ __func__);
+
+ if (dvb_dmx_is_video_feed(feed))
+ return mpq_dmx_process_video_packet(feed, buf);
+
+ if (dvb_dmx_is_pcr_feed(feed))
+ return mpq_dmx_process_pcr_packet(feed, buf);
+
+ return 0;
+}
+
+/**
+ * Returns demux capabilities of TSPPv1 plugin
+ *
+ * @demux: demux device
+ * @caps: Returned capbabilities
+ *
+ * Return error code
+ */
+static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux,
+ struct dmx_caps *caps)
+{
+ struct dvb_demux *dvb_demux = demux->priv;
+
+ if ((dvb_demux == NULL) || (caps == NULL)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid parameters\n",
+ __func__);
+
+ return -EINVAL;
+ }
+
+ caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA |
+ DMX_CAP_TS_INSERTION | DMX_CAP_VIDEO_INDEXING |
+ DMX_CAP_AUTO_BUFFER_FLUSH;
+ caps->recording_max_video_pids_indexed = 0;
+ caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
+ caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+ caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM;
+ caps->num_section_filters = dvb_demux->filternum;
+ caps->num_section_filters_per_pid = dvb_demux->filternum;
+ caps->section_filter_length = DMX_FILTER_SIZE;
+ caps->num_demod_inputs = TSIF_COUNT;
+ caps->num_memory_inputs = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
+ caps->max_bitrate = 192;
+ caps->demod_input_max_bitrate = 96;
+ caps->memory_input_max_bitrate = 96;
+ caps->num_cipher_ops = 1;
+
+ /* TSIF reports 3 bytes STC at unit of 27MHz/256 */
+ caps->max_stc = (u64)0xFFFFFF * 256;
+
+ /* Buffer requirements */
+ caps->section.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->section.max_buffer_num = 1;
+ caps->section.max_size = 0xFFFFFFFF;
+ caps->section.size_alignment = 0;
+ caps->pes.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->pes.max_buffer_num = 1;
+ caps->pes.max_size = 0xFFFFFFFF;
+ caps->pes.size_alignment = 0;
+ caps->recording_188_tsp.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->recording_188_tsp.max_buffer_num = 1;
+ caps->recording_188_tsp.max_size = 0xFFFFFFFF;
+ caps->recording_188_tsp.size_alignment = 0;
+ caps->recording_192_tsp.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->recording_192_tsp.max_buffer_num = 1;
+ caps->recording_192_tsp.max_size = 0xFFFFFFFF;
+ caps->recording_192_tsp.size_alignment = 0;
+ caps->playback_188_tsp.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->playback_188_tsp.max_buffer_num = 1;
+ caps->playback_188_tsp.max_size = 0xFFFFFFFF;
+ caps->playback_188_tsp.size_alignment = 188;
+ caps->playback_192_tsp.flags =
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->playback_192_tsp.max_buffer_num = 1;
+ caps->playback_192_tsp.max_size = 0xFFFFFFFF;
+ caps->playback_192_tsp.size_alignment = 192;
+ caps->decoder.flags =
+ DMX_BUFFER_SECURED_IF_DECRYPTED |
+ DMX_BUFFER_EXTERNAL_SUPPORT |
+ DMX_BUFFER_INTERNAL_SUPPORT |
+ DMX_BUFFER_LINEAR_GROUP_SUPPORT |
+ DMX_BUFFER_CACHED;
+ caps->decoder.max_buffer_num = DMX_MAX_DECODER_BUFFER_NUM;
+ caps->decoder.max_size = 0xFFFFFFFF;
+ caps->decoder.size_alignment = SZ_4K;
+
+ return 0;
+}
+
+
+/**
+ * Reads TSIF STC from TSPP
+ *
+ * @demux: demux device
+ * @num: STC number. 0 for TSIF0 and 1 for TSIF1.
+ * @stc: STC value
+ * @base: divisor to get 90KHz value
+ *
+ * Return error code
+ */
+static int mpq_tspp_dmx_get_stc(struct dmx_demux *demux, unsigned int num,
+ u64 *stc, unsigned int *base)
+{
+ enum tspp_source source;
+ u32 tcr_counter;
+
+ if (!demux || !stc || !base)
+ return -EINVAL;
+
+ if (num == 0)
+ source = TSPP_SOURCE_TSIF0;
+ else if (num == 1)
+ source = TSPP_SOURCE_TSIF1;
+ else
+ return -EINVAL;
+
+ tspp_get_ref_clk_counter(0, source, &tcr_counter);
+
+ *stc = ((u64)tcr_counter) * 256; /* conversion to 27MHz */
+ *base = 300; /* divisor to get 90KHz clock from stc value */
+
+ return 0;
+}
+
+static int mpq_tspp_dmx_init(
+ struct dvb_adapter *mpq_adapter,
+ struct mpq_demux *mpq_demux)
+{
+ int result;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ mpq_dmx_tspp_info.ion_client = mpq_demux->ion_client;
+
+ /* Set the kernel-demux object capabilities */
+ mpq_demux->demux.dmx.capabilities =
+ DMX_TS_FILTERING |
+ DMX_PES_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING |
+ DMX_CRC_CHECKING |
+ DMX_TS_DESCRAMBLING;
+
+ mpq_demux->decoder_alloc_flags = ION_FLAG_CACHED;
+
+ /* Set dvb-demux "virtual" function pointers */
+ mpq_demux->demux.priv = (void *)mpq_demux;
+ mpq_demux->demux.filternum = TSPP_MAX_SECTION_FILTER_NUM;
+ mpq_demux->demux.feednum = MPQ_MAX_DMX_FILES;
+ mpq_demux->demux.start_feed = mpq_tspp_dmx_start_filtering;
+ mpq_demux->demux.stop_feed = mpq_tspp_dmx_stop_filtering;
+ mpq_demux->demux.write_to_decoder = mpq_tspp_dmx_write_to_decoder;
+ mpq_demux->demux.decoder_fullness_init = mpq_dmx_decoder_fullness_init;
+ mpq_demux->demux.decoder_fullness_wait = mpq_dmx_decoder_fullness_wait;
+ mpq_demux->demux.decoder_fullness_abort =
+ mpq_dmx_decoder_fullness_abort;
+ mpq_demux->demux.decoder_buffer_status = mpq_dmx_decoder_buffer_status;
+ mpq_demux->demux.reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer;
+ mpq_demux->demux.set_cipher_op = mpq_dmx_set_cipher_ops;
+ mpq_demux->demux.oob_command = mpq_dmx_oob_command;
+ mpq_demux->demux.convert_ts = mpq_dmx_convert_tts;
+ mpq_demux->demux.flush_decoder_buffer = NULL;
+
+ /* Initialize dvb_demux object */
+ result = dvb_dmx_init(&mpq_demux->demux);
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
+ goto init_failed;
+ }
+
+ /* Now initailize the dmx-dev object */
+ mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES;
+ mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
+ mpq_demux->dmxdev.capabilities = DMXDEV_CAP_DUPLEX;
+
+ mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
+ mpq_demux->dmxdev.demux->get_stc = mpq_tspp_dmx_get_stc;
+ mpq_demux->dmxdev.demux->get_caps = mpq_tspp_dmx_get_caps;
+ mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer;
+ mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer;
+ mpq_demux->dmxdev.demux->write = mpq_dmx_write;
+ result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
+ if (result < 0) {
+ MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
+ __func__,
+ result);
+ goto init_failed_dmx_release;
+ }
+
+ /* Extend dvb-demux debugfs with TSPP statistics. */
+ mpq_dmx_init_debugfs_entries(mpq_demux);
+
+ return 0;
+
+init_failed_dmx_release:
+ dvb_dmx_release(&mpq_demux->demux);
+init_failed:
+ return result;
+}
+
+static int __init mpq_dmx_tspp_plugin_init(void)
+{
+ int i;
+ int j;
+ int ret;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ for (i = 0; i < TSIF_COUNT; i++) {
+ mpq_dmx_tspp_info.tsif[i].aggregate_ids = NULL;
+ mpq_dmx_tspp_info.tsif[i].channel_ref = 0;
+ mpq_dmx_tspp_info.tsif[i].buff_index = 0;
+ mpq_dmx_tspp_info.tsif[i].ch_mem_heap_handle = NULL;
+ mpq_dmx_tspp_info.tsif[i].ch_mem_heap_virt_base = NULL;
+ mpq_dmx_tspp_info.tsif[i].ch_mem_heap_phys_base = 0;
+ atomic_set(&mpq_dmx_tspp_info.tsif[i].data_cnt, 0);
+ atomic_set(&mpq_dmx_tspp_info.tsif[i].control_op, 0);
+
+ for (j = 0; j < TSPP_MAX_PID_FILTER_NUM; j++) {
+ mpq_dmx_tspp_info.tsif[i].filters[j].pid = -1;
+ mpq_dmx_tspp_info.tsif[i].filters[j].ref_count = 0;
+ mpq_dmx_tspp_info.tsif[i].filters[j].hw_index = -1;
+ }
+
+ for (j = 0; j < TSPP_MAX_HW_PID_FILTER_NUM; j++)
+ mpq_dmx_tspp_info.tsif[i].hw_indexes[j] = 0;
+
+ mpq_dmx_tspp_info.tsif[i].current_filter_count = 0;
+ mpq_dmx_tspp_info.tsif[i].pass_nulls_flag = 0;
+ mpq_dmx_tspp_info.tsif[i].pass_all_flag = 0;
+ mpq_dmx_tspp_info.tsif[i].accept_all_filter_exists_flag = 0;
+
+ snprintf(mpq_dmx_tspp_info.tsif[i].name,
+ TSIF_NAME_LENGTH,
+ "dmx_tsif%d",
+ i);
+
+ init_waitqueue_head(&mpq_dmx_tspp_info.tsif[i].wait_queue);
+ mpq_dmx_tspp_info.tsif[i].thread =
+ kthread_run(
+ mpq_dmx_tspp_thread, (void *)(uintptr_t)i,
+ mpq_dmx_tspp_info.tsif[i].name);
+
+ if (IS_ERR(mpq_dmx_tspp_info.tsif[i].thread)) {
+ for (j = 0; j < i; j++) {
+ kthread_stop(mpq_dmx_tspp_info.tsif[j].thread);
+ mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
+ }
+
+ MPQ_DVB_ERR_PRINT(
+ "%s: kthread_run failed\n",
+ __func__);
+
+ return -ENOMEM;
+ }
+
+ mutex_init(&mpq_dmx_tspp_info.tsif[i].mutex);
+ }
+
+ ret = mpq_dmx_plugin_init(mpq_tspp_dmx_init);
+
+ if (ret < 0) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_plugin_init failed (errno=%d)\n",
+ __func__,
+ ret);
+
+ for (i = 0; i < TSIF_COUNT; i++) {
+ kthread_stop(mpq_dmx_tspp_info.tsif[i].thread);
+ mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
+ }
+ }
+
+ return ret;
+}
+
+static void __exit mpq_dmx_tspp_plugin_exit(void)
+{
+ int i;
+
+ MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
+
+ for (i = 0; i < TSIF_COUNT; i++) {
+ mutex_lock(&mpq_dmx_tspp_info.tsif[i].mutex);
+
+ /*
+ * Note: tspp_close_channel will also free the TSPP buffers
+ * even if we allocated them ourselves,
+ * using our free function.
+ */
+ if (mpq_dmx_tspp_info.tsif[i].channel_ref) {
+ tspp_unregister_notification(0,
+ TSPP_CHANNEL_ID(i, TSPP_CHANNEL));
+ tspp_close_channel(0,
+ TSPP_CHANNEL_ID(i, TSPP_CHANNEL));
+
+ if (allocation_mode ==
+ MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
+ vfree(mpq_dmx_tspp_info.tsif[i].aggregate_ids);
+ mpq_dmx_tspp_info.tsif[i].aggregate_ids = NULL;
+ mpq_dmx_channel_mem_free(i);
+ }
+ }
+
+ mutex_unlock(&mpq_dmx_tspp_info.tsif[i].mutex);
+ kthread_stop(mpq_dmx_tspp_info.tsif[i].thread);
+ mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
+ }
+
+ mpq_dmx_plugin_exit();
+}
+
+
+module_init(mpq_dmx_tspp_plugin_init);
+module_exit(mpq_dmx_tspp_plugin_exit);
+
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. demux TSPP version 1 HW Plugin");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c
new file mode 100644
index 000000000000..625609a07f02
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.c
@@ -0,0 +1,1023 @@
+/* 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include "qseecom_kernel.h"
+#include "mpq_sdmx.h"
+
+static struct qseecom_handle *sdmx_qseecom_handles[SDMX_MAX_SESSIONS];
+static struct mutex sdmx_lock[SDMX_MAX_SESSIONS];
+
+#define QSEECOM_SBUFF_SIZE SZ_128K
+
+enum sdmx_cmd_id {
+ SDMX_OPEN_SESSION_CMD,
+ SDMX_CLOSE_SESSION_CMD,
+ SDMX_SET_SESSION_CFG_CMD,
+ SDMX_ADD_FILTER_CMD,
+ SDMX_REMOVE_FILTER_CMD,
+ SDMX_SET_KL_IDX_CMD,
+ SDMX_ADD_RAW_PID_CMD,
+ SDMX_REMOVE_RAW_PID_CMD,
+ SDMX_PROCESS_CMD,
+ SDMX_GET_DBG_COUNTERS_CMD,
+ SDMX_RESET_DBG_COUNTERS_CMD,
+ SDMX_GET_VERSION_CMD,
+ SDMX_INVALIDATE_KL_CMD,
+ SDMX_SET_LOG_LEVEL_CMD
+};
+
+#pragma pack(push, sdmx, 1)
+
+struct sdmx_proc_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u8 flags;
+ struct sdmx_buff_descr in_buf_descr;
+ u32 inp_fill_cnt;
+ u32 in_rd_offset;
+ u32 num_filters;
+ struct sdmx_filter_status filters_status[];
+};
+
+struct sdmx_proc_rsp {
+ enum sdmx_status ret;
+ u32 inp_fill_cnt;
+ u32 in_rd_offset;
+ u32 err_indicators;
+ u32 status_indicators;
+};
+
+struct sdmx_open_ses_req {
+ enum sdmx_cmd_id cmd_id;
+};
+
+struct sdmx_open_ses_rsp {
+ enum sdmx_status ret;
+ u32 session_handle;
+};
+
+struct sdmx_close_ses_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+};
+
+struct sdmx_close_ses_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_ses_cfg_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ enum sdmx_proc_mode process_mode;
+ enum sdmx_inp_mode input_mode;
+ enum sdmx_pkt_format packet_len;
+ u8 odd_scramble_bits;
+ u8 even_scramble_bits;
+};
+
+struct sdmx_ses_cfg_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_set_kl_ind_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 pid;
+ u32 kl_index;
+};
+
+struct sdmx_set_kl_ind_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_add_filt_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 pid;
+ enum sdmx_filter filter_type;
+ struct sdmx_buff_descr meta_data_buf;
+ enum sdmx_buf_mode buffer_mode;
+ enum sdmx_raw_out_format ts_out_format;
+ u32 flags;
+ u32 num_data_bufs;
+ struct sdmx_data_buff_descr data_bufs[];
+};
+
+struct sdmx_add_filt_rsp {
+ enum sdmx_status ret;
+ u32 filter_handle;
+};
+
+struct sdmx_rem_filt_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 filter_handle;
+};
+
+struct sdmx_rem_filt_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_add_raw_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 filter_handle;
+ u32 pid;
+};
+
+struct sdmx_add_raw_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_rem_raw_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 filter_handle;
+ u32 pid;
+};
+
+struct sdmx_rem_raw_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_get_counters_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 num_filters;
+};
+
+struct sdmx_get_counters_rsp {
+ enum sdmx_status ret;
+ struct sdmx_session_dbg_counters session_counters;
+ u32 num_filters;
+ struct sdmx_filter_dbg_counters filter_counters[];
+};
+
+struct sdmx_rst_counters_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+};
+
+struct sdmx_rst_counters_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_get_version_req {
+ enum sdmx_cmd_id cmd_id;
+};
+
+struct sdmx_get_version_rsp {
+ enum sdmx_status ret;
+ int32_t version;
+};
+
+struct sdmx_set_log_level_req {
+ enum sdmx_cmd_id cmd_id;
+ enum sdmx_log_level level;
+ u32 session_handle;
+};
+
+struct sdmx_set_log_level_rsp {
+ enum sdmx_status ret;
+};
+
+#pragma pack(pop, sdmx)
+
+static int get_cmd_rsp_buffers(int handle_index,
+ void **cmd,
+ int *cmd_len,
+ void **rsp,
+ int *rsp_len)
+{
+ if (*cmd_len & QSEECOM_ALIGN_MASK)
+ *cmd_len = QSEECOM_ALIGN(*cmd_len);
+
+ if (*rsp_len & QSEECOM_ALIGN_MASK)
+ *rsp_len = QSEECOM_ALIGN(*rsp_len);
+
+ if ((*rsp_len + *cmd_len) > QSEECOM_SBUFF_SIZE) {
+ pr_err("%s: shared buffer too small to hold cmd=%d and rsp=%d\n",
+ __func__, *cmd_len, *rsp_len);
+ return SDMX_STATUS_OUT_OF_MEM;
+ }
+
+ *cmd = sdmx_qseecom_handles[handle_index]->sbuf;
+ *rsp = sdmx_qseecom_handles[handle_index]->sbuf + *cmd_len;
+ return SDMX_SUCCESS;
+}
+
+/*
+ * Returns version of secure-demux app.
+ *
+ * @session_handle: Returned instance handle. Must not be NULL.
+ * Return error code
+ */
+int sdmx_get_version(int session_handle, int32_t *version)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_get_version_req *cmd;
+ struct sdmx_get_version_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS) ||
+ (version == NULL))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_get_version_req);
+ rsp_len = sizeof(struct sdmx_get_version_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_GET_VERSION_CMD;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+ *version = rsp->version;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+
+}
+EXPORT_SYMBOL(sdmx_get_version);
+
+/*
+ * Initializes a new secure demux instance and returns a handle of the instance.
+ *
+ * @session_handle: handle of a secure demux instance to get its version.
+ * Return the version if successful or an error code.
+ */
+int sdmx_open_session(int *session_handle)
+{
+ int res, cmd_len, rsp_len;
+ enum sdmx_status ret, version_ret;
+ struct sdmx_open_ses_req *cmd;
+ struct sdmx_open_ses_rsp *rsp;
+ struct qseecom_handle *qseecom_handle = NULL;
+ int32_t version;
+
+ /* Input validation */
+ if (session_handle == NULL)
+ return SDMX_STATUS_GENERAL_FAILURE;
+
+ /* Start the TZ app */
+ res = qseecom_start_app(&qseecom_handle, "securemm",
+ QSEECOM_SBUFF_SIZE);
+
+ if (res < 0)
+ return SDMX_STATUS_GENERAL_FAILURE;
+
+ cmd_len = sizeof(struct sdmx_open_ses_req);
+ rsp_len = sizeof(struct sdmx_open_ses_rsp);
+
+ /* Get command and response buffers */
+ cmd = (struct sdmx_open_ses_req *)qseecom_handle->sbuf;
+
+ if (cmd_len & QSEECOM_ALIGN_MASK)
+ cmd_len = QSEECOM_ALIGN(cmd_len);
+
+ rsp = (struct sdmx_open_ses_rsp *)qseecom_handle->sbuf + cmd_len;
+
+ if (rsp_len & QSEECOM_ALIGN_MASK)
+ rsp_len = QSEECOM_ALIGN(rsp_len);
+
+ /* Will be later overridden by SDMX response */
+ *session_handle = SDMX_INVALID_SESSION_HANDLE;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_OPEN_SESSION_CMD;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(qseecom_handle, (void *)cmd, cmd_len,
+ (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ qseecom_shutdown_app(&qseecom_handle);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ /* Parse response struct */
+ *session_handle = rsp->session_handle;
+
+ /* Initialize handle and mutex */
+ sdmx_qseecom_handles[*session_handle] = qseecom_handle;
+ mutex_init(&sdmx_lock[*session_handle]);
+ ret = rsp->ret;
+
+ /* Get and print the app version */
+ version_ret = sdmx_get_version(*session_handle, &version);
+ if (version_ret == SDMX_SUCCESS)
+ pr_info("TZ SDMX version is %x.%x\n", version >> 8,
+ version & 0xFF);
+ else
+ pr_err("Error reading TZ SDMX version\n");
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_open_session);
+
+/*
+ * Closes a secure demux instance.
+ *
+ * @session_handle: handle of a secure demux instance to close.
+ * Return error code
+ */
+int sdmx_close_session(int session_handle)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_close_ses_req *cmd;
+ struct sdmx_close_ses_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_close_ses_req);
+ rsp_len = sizeof(struct sdmx_close_ses_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_CLOSE_SESSION_CMD;
+ cmd->session_handle = session_handle;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+
+ /* Shutdown the TZ app (or at least free the current handle) */
+ res = qseecom_shutdown_app(&sdmx_qseecom_handles[session_handle]);
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ sdmx_qseecom_handles[session_handle] = NULL;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_close_session);
+
+/*
+ * Configures an open secure demux instance.
+ *
+ * @session_handle: secure demux instance
+ * @proc_mode: Defines secure demux's behavior in case of output
+ * buffer overflow.
+ * @inp_mode: Defines the input encryption settings.
+ * @pkt_format: TS packet length in input buffer.
+ * @odd_scramble_bits: Value of the scramble bits indicating the ODD key.
+ * @even_scramble_bits: Value of the scramble bits indicating the EVEN key.
+ * Return error code
+ */
+int sdmx_set_session_cfg(int session_handle,
+ enum sdmx_proc_mode proc_mode,
+ enum sdmx_inp_mode inp_mode,
+ enum sdmx_pkt_format pkt_format,
+ u8 odd_scramble_bits,
+ u8 even_scramble_bits)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_ses_cfg_req *cmd;
+ struct sdmx_ses_cfg_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_ses_cfg_req);
+ rsp_len = sizeof(struct sdmx_ses_cfg_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_SET_SESSION_CFG_CMD;
+ cmd->session_handle = session_handle;
+ cmd->process_mode = proc_mode;
+ cmd->input_mode = inp_mode;
+ cmd->packet_len = pkt_format;
+ cmd->odd_scramble_bits = odd_scramble_bits;
+ cmd->even_scramble_bits = even_scramble_bits;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_set_session_cfg);
+
+/*
+ * Creates a new secure demux filter and returns a filter handle
+ *
+ * @session_handle: secure demux instance
+ * @pid: pid to filter
+ * @filter_type: type of filtering
+ * @meta_data_buf: meta data buffer descriptor
+ * @data_buf_mode: data buffer mode (ring/linear)
+ * @num_data_bufs: number of data buffers (use 1 for a ring buffer)
+ * @data_bufs: data buffers descriptors array
+ * @filter_handle: returned filter handle
+ * @ts_out_format: output format for raw filters
+ * @flags: optional flags for filter
+ * (currently only clear section CRC verification is supported)
+ *
+ * Return error code
+ */
+int sdmx_add_filter(int session_handle,
+ u16 pid,
+ enum sdmx_filter filterype,
+ struct sdmx_buff_descr *meta_data_buf,
+ enum sdmx_buf_mode d_buf_mode,
+ u32 num_data_bufs,
+ struct sdmx_data_buff_descr *data_bufs,
+ int *filter_handle,
+ enum sdmx_raw_out_format ts_out_format,
+ u32 flags)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_add_filt_req *cmd;
+ struct sdmx_add_filt_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS) ||
+ (filter_handle == NULL))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_add_filt_req)
+ + num_data_bufs * sizeof(struct sdmx_data_buff_descr);
+ rsp_len = sizeof(struct sdmx_add_filt_rsp);
+
+ /* Will be later overridden by SDMX response */
+ *filter_handle = SDMX_INVALID_FILTER_HANDLE;
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_ADD_FILTER_CMD;
+ cmd->session_handle = session_handle;
+ cmd->pid = (u32)pid;
+ cmd->filter_type = filterype;
+ cmd->ts_out_format = ts_out_format;
+ cmd->flags = flags;
+ if (meta_data_buf != NULL)
+ memcpy(&(cmd->meta_data_buf), meta_data_buf,
+ sizeof(struct sdmx_buff_descr));
+ else
+ memset(&(cmd->meta_data_buf), 0, sizeof(cmd->meta_data_buf));
+
+ cmd->buffer_mode = d_buf_mode;
+ cmd->num_data_bufs = num_data_bufs;
+ memcpy(cmd->data_bufs, data_bufs,
+ num_data_bufs * sizeof(struct sdmx_data_buff_descr));
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ /* Parse response struct */
+ *filter_handle = rsp->filter_handle;
+ ret = rsp->ret;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_add_filter);
+
+/*
+ * Removes a secure demux filter
+ *
+ * @session_handle: secure demux instance
+ * @filter_handle: filter handle to remove
+ *
+ * Return error code
+ */
+int sdmx_remove_filter(int session_handle, int filter_handle)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_rem_filt_req *cmd;
+ struct sdmx_rem_filt_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_rem_filt_req);
+ rsp_len = sizeof(struct sdmx_rem_filt_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_REMOVE_FILTER_CMD;
+ cmd->session_handle = session_handle;
+ cmd->filter_handle = filter_handle;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_remove_filter);
+
+/*
+ * Associates a key ladder index for the specified pid
+ *
+ * @session_handle: secure demux instance
+ * @pid: pid
+ * @key_ladder_index: key ladder index to associate to the pid
+ *
+ * Return error code
+ *
+ * Note: if pid already has some key ladder index associated, it will be
+ * overridden.
+ */
+int sdmx_set_kl_ind(int session_handle, u16 pid, u32 key_ladder_index)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_set_kl_ind_req *cmd;
+ struct sdmx_set_kl_ind_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_set_kl_ind_req);
+ rsp_len = sizeof(struct sdmx_set_kl_ind_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_SET_KL_IDX_CMD;
+ cmd->session_handle = session_handle;
+ cmd->pid = (u32)pid;
+ cmd->kl_index = key_ladder_index;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_set_kl_ind);
+
+/*
+ * Adds the specified pid to an existing raw (recording) filter
+ *
+ * @session_handle: secure demux instance
+ * @filter_handle: raw filter handle
+ * @pid: pid
+ *
+ * Return error code
+ */
+int sdmx_add_raw_pid(int session_handle, int filter_handle, u16 pid)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_add_raw_req *cmd;
+ struct sdmx_add_raw_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_add_raw_req);
+ rsp_len = sizeof(struct sdmx_add_raw_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_ADD_RAW_PID_CMD;
+ cmd->session_handle = session_handle;
+ cmd->filter_handle = filter_handle;
+ cmd->pid = (u32)pid;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_add_raw_pid);
+
+/*
+ * Removes the specified pid from a raw (recording) filter
+ *
+ * @session_handle: secure demux instance
+ * @filter_handle: raw filter handle
+ * @pid: pid
+ *
+ * Return error code
+ */
+int sdmx_remove_raw_pid(int session_handle, int filter_handle, u16 pid)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_rem_raw_req *cmd;
+ struct sdmx_rem_raw_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_rem_raw_req);
+ rsp_len = sizeof(struct sdmx_rem_raw_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_REMOVE_RAW_PID_CMD;
+ cmd->session_handle = session_handle;
+ cmd->filter_handle = filter_handle;
+ cmd->pid = (u32)pid;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_remove_raw_pid);
+
+/*
+ * Call secure demux to perform processing on the specified input buffer
+ *
+ * @session_handle: secure demux instance
+ * @flags: input flags. Currently only EOS marking is supported.
+ * @input_buf_desc: input buffer descriptor
+ * @input_fill_count: number of bytes available in input buffer
+ * @input_read_offset: offset inside input buffer where data starts
+ * @error_indicators: returned general error indicators
+ * @status_indicators: returned general status indicators
+ * @num_filters: number of filters in filter status array
+ * @filter_status: filter status descriptor array
+ *
+ * Return error code
+ */
+int sdmx_process(int session_handle, u8 flags,
+ struct sdmx_buff_descr *input_buf_desc,
+ u32 *input_fill_count,
+ u32 *input_read_offset,
+ u32 *error_indicators,
+ u32 *status_indicators,
+ u32 num_filters,
+ struct sdmx_filter_status *filter_status)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_proc_req *cmd;
+ struct sdmx_proc_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS) ||
+ (input_buf_desc == NULL) ||
+ (input_fill_count == NULL) || (input_read_offset == NULL) ||
+ (error_indicators == NULL) || (status_indicators == NULL) ||
+ (filter_status == NULL))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_proc_req)
+ + num_filters * sizeof(struct sdmx_filter_status);
+ rsp_len = sizeof(struct sdmx_proc_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_PROCESS_CMD;
+ cmd->session_handle = session_handle;
+ cmd->flags = flags;
+ cmd->in_buf_descr.base_addr = input_buf_desc->base_addr;
+ cmd->in_buf_descr.size = input_buf_desc->size;
+ cmd->inp_fill_cnt = *input_fill_count;
+ cmd->in_rd_offset = *input_read_offset;
+ cmd->num_filters = num_filters;
+ memcpy(cmd->filters_status, filter_status,
+ num_filters * sizeof(struct sdmx_filter_status));
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ /* Parse response struct */
+ *input_fill_count = rsp->inp_fill_cnt;
+ *input_read_offset = rsp->in_rd_offset;
+ *error_indicators = rsp->err_indicators;
+ *status_indicators = rsp->status_indicators;
+ memcpy(filter_status, cmd->filters_status,
+ num_filters * sizeof(struct sdmx_filter_status));
+ ret = rsp->ret;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_process);
+
+/*
+ * Returns session-level & filter-level debug counters
+ *
+ * @session_handle: secure demux instance
+ * @session_counters: returned session-level debug counters
+ * @num_filters: returned number of filters reported in filter_counters
+ * @filter_counters: returned filter-level debug counters array
+ *
+ * Return error code
+ */
+int sdmx_get_dbg_counters(int session_handle,
+ struct sdmx_session_dbg_counters *session_counters,
+ u32 *num_filters,
+ struct sdmx_filter_dbg_counters *filter_counters)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_get_counters_req *cmd;
+ struct sdmx_get_counters_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS) ||
+ (session_counters == NULL) || (num_filters == NULL) ||
+ (filter_counters == NULL))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_get_counters_req);
+ rsp_len = sizeof(struct sdmx_get_counters_rsp)
+ + *num_filters * sizeof(struct sdmx_filter_dbg_counters);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_GET_DBG_COUNTERS_CMD;
+ cmd->session_handle = session_handle;
+ cmd->num_filters = *num_filters;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ /* Parse response struct */
+ *session_counters = rsp->session_counters;
+ *num_filters = rsp->num_filters;
+ memcpy(filter_counters, rsp->filter_counters,
+ *num_filters * sizeof(struct sdmx_filter_dbg_counters));
+ ret = rsp->ret;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_get_dbg_counters);
+
+/*
+ * Reset debug counters
+ *
+ * @session_handle: secure demux instance
+ *
+ * Return error code
+ */
+int sdmx_reset_dbg_counters(int session_handle)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_rst_counters_req *cmd;
+ struct sdmx_rst_counters_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_rst_counters_req);
+ rsp_len = sizeof(struct sdmx_rst_counters_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_RESET_DBG_COUNTERS_CMD;
+ cmd->session_handle = session_handle;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+out:
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_reset_dbg_counters);
+
+/*
+ * Set debug log verbosity level
+ *
+ * @session_handle: secure demux instance
+ * @level: requested log level
+ *
+ * Return error code
+ */
+int sdmx_set_log_level(int session_handle, enum sdmx_log_level level)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_set_log_level_req *cmd;
+ struct sdmx_set_log_level_rsp *rsp;
+ enum sdmx_status ret;
+
+ cmd_len = sizeof(struct sdmx_set_log_level_req);
+ rsp_len = sizeof(struct sdmx_set_log_level_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ ret = get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+ if (ret)
+ goto out;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_SET_LOG_LEVEL_CMD;
+ cmd->session_handle = session_handle;
+ cmd->level = level;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+ ret = rsp->ret;
+out:
+ /* Unlock */
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return ret;
+}
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
new file mode 100644
index 000000000000..799f688d186a
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/demux/mpq_sdmx.h
@@ -0,0 +1,368 @@
+/* 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
+ * 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 _MPQ_SDMX_H
+#define _MPQ_SDMX_H
+
+#include <linux/types.h>
+
+/* Constant declarations */
+#define SDMX_MAX_SESSIONS (4)
+#define SDMX_LOOPBACK_PID (0x2000)
+
+#define SDMX_MAX_PHYSICAL_CHUNKS (256)
+
+/* Filter-level error indicators */
+#define SDMX_FILTER_SUCCESS (0)
+#define SDMX_FILTER_ERR_MD_BUF_FULL BIT(0)
+#define SDMX_FILTER_ERR_D_BUF_FULL BIT(1)
+#define SDMX_FILTER_ERR_D_LIN_BUFS_FULL BIT(2)
+#define SDMX_FILTER_ERR_INVALID_SCRAMBLE_BITS BIT(3)
+#define SDMX_FILTER_ERR_KL_IND_NOT_SET BIT(4)
+#define SDMX_FILTER_ERR_CAS_DECRYPT_ERROR BIT(5)
+#define SDMX_FILTER_ERR_SEC_VERIF_CRC32_FAIL BIT(6)
+#define SDMX_FILTER_ERR_SEC_INTERNAL_MALLOC_FAIL BIT(7)
+#define SDMX_FILTER_ERR_SEC_LEN_INVALID BIT(8)
+#define SDMX_FILTER_ERR_SEC_PUSI_PTR_INVALID BIT(9)
+#define SDMX_FILTER_ERR_TS_SYNC_BYTE_INVALID BIT(10)
+#define SDMX_FILTER_ERR_TS_TRANSPORT_ERR BIT(11)
+#define SDMX_FILTER_ERR_CONT_CNT_INVALID BIT(12)
+#define SDMX_FILTER_ERR_CONT_CNT_DUPLICATE BIT(13)
+#define SDMX_FILTER_ERR_INVALID_PES_HDR BIT(14)
+#define SDMX_FILTER_ERR_INVALID_PES_LEN BIT(15)
+#define SDMX_FILTER_ERR_INVALID_PES_ENCRYPTION BIT(16)
+#define SDMX_FILTER_ERR_SECURITY_FAULT BIT(17)
+#define SDMX_FILTER_ERR_IN_NS_BUFFER BIT(18)
+
+/* Filter-level status indicators */
+#define SDMX_FILTER_STATUS_EOS BIT(0)
+#define SDMX_FILTER_STATUS_WR_PTR_CHANGED BIT(1)
+
+/* Filter-level flags */
+#define SDMX_FILTER_FLAG_VERIFY_SECTION_CRC BIT(0)
+
+#define SDMX_INVALID_SESSION_HANDLE (-1)
+#define SDMX_INVALID_FILTER_HANDLE (-1)
+
+/* Input flags */
+#define SDMX_INPUT_FLAG_EOS BIT(0)
+#define SDMX_INPUT_FLAG_DBG_ENABLE BIT(1)
+
+
+enum sdmx_buf_mode {
+ SDMX_RING_BUF,
+ SDMX_LINEAR_GROUP_BUF,
+};
+
+enum sdmx_proc_mode {
+ SDMX_PUSH_MODE,
+ SDMX_PULL_MODE,
+};
+
+enum sdmx_inp_mode {
+ SDMX_PKT_ENC_MODE,
+ SDMX_BULK_ENC_MODE,
+ SDMX_CLEAR_MODE,
+};
+
+enum sdmx_pkt_format {
+ SDMX_188_BYTE_PKT = 188,
+ SDMX_192_BYTE_PKT = 192,
+ SDMX_195_BYTE_PKT = 195,
+};
+
+enum sdmx_log_level {
+ SDMX_LOG_NO_PRINT,
+ SDMX_LOG_MSG_ERROR,
+ SDMX_LOG_DEBUG,
+ SDMX_LOG_VERBOSE
+};
+
+enum sdmx_status {
+ SDMX_SUCCESS = 0,
+ SDMX_STATUS_GENERAL_FAILURE = -1,
+ SDMX_STATUS_MAX_OPEN_SESSIONS_REACHED = -2,
+ SDMX_STATUS_INVALID_SESSION_HANDLE = -3,
+ SDMX_STATUS_INVALID_INPUT_PARAMS = -4,
+ SDMX_STATUS_UNSUPPORTED_MODE = -5,
+ SDMX_STATUS_INVALID_PID = -6,
+ SDMX_STATUS_OUT_OF_MEM = -7,
+ SDMX_STATUS_FILTER_EXISTS = -8,
+ SDMX_STATUS_INVALID_FILTER_HANDLE = -9,
+ SDMX_STATUS_MAX_RAW_PIDS_REACHED = -10,
+ SDMX_STATUS_SINGLE_PID_RAW_FILTER = -11,
+ SDMX_STATUS_INP_BUF_INVALID_PARAMS = -12,
+ SDMX_STATUS_INVALID_FILTER_CFG = -13,
+ SDMX_STATUS_STALLED_IN_PULL_MODE = -14,
+ SDMX_STATUS_SECURITY_FAULT = -15,
+ SDMX_STATUS_NS_BUFFER_ERROR = -16,
+};
+
+enum sdmx_filter {
+ SDMX_PES_FILTER, /* Other PES */
+ SDMX_SEPARATED_PES_FILTER, /* Separated PES (for decoder) */
+ SDMX_SECTION_FILTER, /* Section */
+ SDMX_PCR_FILTER, /* PCR */
+ SDMX_RAW_FILTER, /* Recording */
+};
+
+enum sdmx_raw_out_format {
+ SDMX_188_OUTPUT,
+ SDMX_192_HEAD_OUTPUT,
+ SDMX_192_TAIL_OUTPUT
+};
+
+#pragma pack(push, sdmx, 1)
+
+struct sdmx_session_dbg_counters {
+ /* Total number of TS-packets input to SDMX. */
+ u32 ts_pkt_in;
+
+ /* Total number of TS-packets filtered out by SDMX. */
+ u32 ts_pkt_out;
+};
+
+struct sdmx_filter_dbg_counters {
+ int filter_handle;
+
+ /* Number of TS-packets filtered. */
+ u32 ts_pkt_count;
+
+ /* Number of TS-packets with adaptation field only (no payload). */
+ u32 ts_pkt_no_payload;
+
+ /* Number of TS-packets with the discontinuity indicator set. */
+ u32 ts_pkt_discont;
+
+ /* Number of duplicate TS-packets detected. */
+ u32 ts_pkt_dup;
+
+ /* Number of packets not decrypted because the key wasn't ready. */
+ u32 ts_pkt_key_not_ready;
+};
+
+struct sdmx_pes_counters {
+ /* Number of TS packets with the TEI flag set */
+ u32 transport_err_count;
+
+ /* Number of TS packets with continuity counter errors */
+ u32 continuity_err_count;
+
+ /* Number of TS packets composing this PES frame */
+ u32 pes_ts_count;
+
+ /* Number of TS packets dropped due to full buffer */
+ u32 drop_count;
+};
+
+struct sdmx_buff_descr {
+ /* Physical address where buffer starts */
+ u64 base_addr;
+
+ /* Size of buffer */
+ u32 size;
+};
+
+struct sdmx_data_buff_descr {
+ /* Physical chunks of the buffer */
+ struct sdmx_buff_descr buff_chunks[SDMX_MAX_PHYSICAL_CHUNKS];
+
+ /* Length of buffer */
+ u32 length;
+};
+
+/*
+ * Data payload residing in the data buffers is described using this meta-data
+ * header. The meta data header specifies where the payload is located in the
+ * data buffer and how big it is.
+ * The meta data header optionally carries additional relevant meta data
+ * immediately following the meta-data header.
+ */
+struct sdmx_metadata_header {
+ /*
+ * Payload start offset inside data buffer. In case data is managed
+ * as a linear buffer group, this specifies buffer index.
+ */
+ u32 payload_start;
+
+ /* Payload length */
+ u32 payload_length;
+
+ /* Number of meta data bytes immediately following this header */
+ u32 metadata_length;
+};
+
+
+struct sdmx_filter_status {
+ /* Secure demux filter handle */
+ int filter_handle;
+
+ /*
+ * Number of pending bytes in filter's output data buffer.
+ * For linear buffer mode, this is number of buffers pending.
+ */
+ u32 data_fill_count;
+
+ /*
+ * Offset in data buffer for next data payload to be written.
+ * For linear buffer mode, this is a buffer index.
+ */
+ u32 data_write_offset;
+
+ /* Number of pending bytes in filter's output meta data buffer */
+ u32 metadata_fill_count;
+
+ /* Offset in meta data buffer for next metadata header to be written */
+ u32 metadata_write_offset;
+
+ /* Errors (bitmap) reported by secure demux for this filter */
+ u32 error_indicators;
+
+ /* General status (bitmap) reported by secure demux for this filter */
+ u32 status_indicators;
+};
+#pragma pack(pop, sdmx)
+
+#ifdef CONFIG_QSEECOM
+
+int sdmx_open_session(int *session_handle);
+
+int sdmx_close_session(int session_handle);
+
+int sdmx_get_version(int session_handle, int32_t *version);
+
+int sdmx_set_session_cfg(int session_handle, enum sdmx_proc_mode proc_mode,
+ enum sdmx_inp_mode inp_mode, enum sdmx_pkt_format pkt_format,
+ u8 odd_scramble_bits, u8 even_scramble_bits);
+
+int sdmx_add_filter(int session_handle, u16 pid, enum sdmx_filter filter_type,
+ struct sdmx_buff_descr *meta_data_buf, enum sdmx_buf_mode data_buf_mode,
+ u32 num_data_bufs, struct sdmx_data_buff_descr *data_bufs,
+ int *filter_handle, enum sdmx_raw_out_format ts_out_format, u32 flags);
+
+int sdmx_remove_filter(int session_handle, int filter_handle);
+
+int sdmx_set_kl_ind(int session_handle, u16 pid, u32 key_ladder_index);
+
+int sdmx_add_raw_pid(int session_handle, int filter_handle, u16 pid);
+
+int sdmx_remove_raw_pid(int session_handle, int filter_handle, u16 pid);
+
+int sdmx_process(int session_handle, u8 flags,
+ struct sdmx_buff_descr *input_buf_desc,
+ u32 *input_fill_count, u32 *input_read_offset,
+ u32 *error_indicators,
+ u32 *status_indicators,
+ u32 num_filters,
+ struct sdmx_filter_status *filter_status);
+
+int sdmx_get_dbg_counters(int session_handle,
+ struct sdmx_session_dbg_counters *session_counters,
+ u32 *num_filters,
+ struct sdmx_filter_dbg_counters *filter_counters);
+
+int sdmx_reset_dbg_counters(int session_handle);
+
+int sdmx_set_log_level(int session_handle, enum sdmx_log_level level);
+
+#else
+
+static inline int sdmx_open_session(int *session_handle)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_close_session(int session_handle)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_get_version(int session_handle, int32_t *version)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_set_session_cfg(int session_handle,
+ enum sdmx_proc_mode proc_mode,
+ enum sdmx_inp_mode inp_mode, enum sdmx_pkt_format pkt_format,
+ u8 odd_scramble_bits, u8 even_scramble_bits)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_add_filter(int session_handle, u16 pid,
+ enum sdmx_filter filter_type,
+ struct sdmx_buff_descr *meta_data_buf, enum sdmx_buf_mode data_buf_mode,
+ u32 num_data_bufs, struct sdmx_data_buff_descr *data_bufs,
+ int *filter_handle, enum sdmx_raw_out_format ts_out_format, u32 flags)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_remove_filter(int session_handle, int filter_handle)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_set_kl_ind(int session_handle, u16 pid,
+ u32 key_ladder_index)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_add_raw_pid(int session_handle, int filter_handle,
+ u16 pid)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_remove_raw_pid(int session_handle, int filter_handle,
+ u16 pid)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_process(int session_handle, u8 flags,
+ struct sdmx_buff_descr *input_buf_desc,
+ u32 *input_fill_count, u32 *input_read_offset,
+ u32 *error_indicators,
+ u32 *status_indicators,
+ u32 num_filters,
+ struct sdmx_filter_status *filter_status)
+{
+ *status_indicators = 0;
+ *error_indicators = 0;
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_get_dbg_counters(int session_handle,
+ struct sdmx_session_dbg_counters *session_counters,
+ u32 *num_filters,
+ struct sdmx_filter_dbg_counters *filter_counters)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_reset_dbg_counters(int session_handle)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+static inline int sdmx_set_log_level(int session_handle,
+ enum sdmx_log_level level)
+{
+ return SDMX_STATUS_GENERAL_FAILURE;
+}
+
+#endif
+
+#endif /* _MPQ_SDMX_H */
diff --git a/drivers/media/platform/msm/dvb/include/mpq_adapter.h b/drivers/media/platform/msm/dvb/include/mpq_adapter.h
new file mode 100644
index 000000000000..54e671c3b38d
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/include/mpq_adapter.h
@@ -0,0 +1,199 @@
+/* Copyright (c) 2012-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 _MPQ_ADAPTER_H
+#define _MPQ_ADAPTER_H
+
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "mpq_stream_buffer.h"
+
+
+
+/** IDs of interfaces holding stream-buffers */
+enum mpq_adapter_stream_if {
+ /** Interface holding stream-buffer for video0 stream */
+ MPQ_ADAPTER_VIDEO0_STREAM_IF = 0,
+
+ /** Interface holding stream-buffer for video1 stream */
+ MPQ_ADAPTER_VIDEO1_STREAM_IF = 1,
+
+ /** Interface holding stream-buffer for video1 stream */
+ MPQ_ADAPTER_VIDEO2_STREAM_IF = 2,
+
+ /** Interface holding stream-buffer for video1 stream */
+ MPQ_ADAPTER_VIDEO3_STREAM_IF = 3,
+
+ /** Maximum number of interfaces holding stream-buffers */
+ MPQ_ADAPTER_MAX_NUM_OF_INTERFACES,
+};
+
+enum dmx_packet_type {
+ DMX_PES_PACKET,
+ DMX_FRAMING_INFO_PACKET,
+ DMX_EOS_PACKET,
+ DMX_MARKER_PACKET
+};
+
+struct dmx_pts_dts_info {
+ /** Indication whether PTS exist */
+ int pts_exist;
+
+ /** Indication whether DTS exist */
+ int dts_exist;
+
+ /** PTS value associated with the PES data if any */
+ u64 pts;
+
+ /** DTS value associated with the PES data if any */
+ u64 dts;
+};
+
+struct dmx_framing_packet_info {
+ /** framing pattern type, one of DMX_IDX_* definitions */
+ u64 pattern_type;
+
+ /** PTS/DTS information */
+ struct dmx_pts_dts_info pts_dts_info;
+
+ /** STC value attached to first TS packet holding the pattern */
+ u64 stc;
+
+ /*
+ * Number of TS packets with Transport Error Indicator (TEI)
+ * found while constructing the frame.
+ */
+ __u32 transport_error_indicator_counter;
+
+ /* Number of continuity errors found while constructing the frame */
+ __u32 continuity_error_counter;
+
+ /*
+ * Number of dropped bytes due to insufficient buffer space,
+ * since last reported frame.
+ */
+ __u32 ts_dropped_bytes;
+
+ /* Total number of TS packets holding the frame */
+ __u32 ts_packets_num;
+};
+
+struct dmx_pes_packet_info {
+ /** PTS/DTS information */
+ struct dmx_pts_dts_info pts_dts_info;
+
+ /** STC value attached to first TS packet holding the PES */
+ u64 stc;
+};
+
+struct dmx_marker_info {
+ /* marker id */
+ u64 id;
+};
+
+/** The meta-data used for video interface */
+struct mpq_adapter_video_meta_data {
+ /** meta-data packet type */
+ enum dmx_packet_type packet_type;
+
+ /** packet-type specific information */
+ union {
+ struct dmx_framing_packet_info framing;
+ struct dmx_pes_packet_info pes;
+ struct dmx_marker_info marker;
+ } info;
+} __packed;
+
+
+/** Callback function to notify on registrations of specific interfaces */
+typedef void (*mpq_adapter_stream_if_callback)(
+ enum mpq_adapter_stream_if interface_id,
+ void *user_param);
+
+
+/**
+ * mpq_adapter_get - Returns pointer to Qualcomm Technologies Inc. DVB adapter
+ *
+ * Return dvb adapter or NULL if not exist.
+ */
+struct dvb_adapter *mpq_adapter_get(void);
+
+
+/**
+ * mpq_adapter_register_stream_if - Register a stream interface.
+ *
+ * @interface_id: The interface id
+ * @stream_buffer: The buffer used for the interface
+ *
+ * Return error status
+ *
+ * Stream interface used to connect between two units in tunneling
+ * mode using mpq_streambuffer implementation.
+ * The producer of the interface should register the new interface,
+ * consumer may get the interface using mpq_adapter_get_stream_if.
+ *
+ * Note that the function holds a pointer to this interface,
+ * stream_buffer pointer assumed to be valid as long as interface
+ * is active.
+ */
+int mpq_adapter_register_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ struct mpq_streambuffer *stream_buffer);
+
+
+/**
+ * mpq_adapter_unregister_stream_if - Un-register a stream interface.
+ *
+ * @interface_id: The interface id
+ *
+ * Return error status
+ */
+int mpq_adapter_unregister_stream_if(
+ enum mpq_adapter_stream_if interface_id);
+
+
+/**
+ * mpq_adapter_get_stream_if - Get buffer used for a stream interface.
+ *
+ * @interface_id: The interface id
+ * @stream_buffer: The returned stream buffer
+ *
+ * Return error status
+ */
+int mpq_adapter_get_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ struct mpq_streambuffer **stream_buffer);
+
+
+/**
+ * mpq_adapter_notify_stream_if - Register notification
+ * to be triggered when a stream interface is registered.
+ *
+ * @interface_id: The interface id
+ * @callback: The callback to be triggered when the interface is registered
+ * @user_param: A parameter that is passed back to the callback function
+ * when triggered.
+ *
+ * Return error status
+ *
+ * Producer may use this to register notification when desired
+ * interface registered in the system and query its information
+ * afterwards using mpq_adapter_get_stream_if.
+ * To remove the callback, this function should be called with NULL
+ * value in callback parameter.
+ */
+int mpq_adapter_notify_stream_if(
+ enum mpq_adapter_stream_if interface_id,
+ mpq_adapter_stream_if_callback callback,
+ void *user_param);
+
+#endif /* _MPQ_ADAPTER_H */
diff --git a/drivers/media/platform/msm/dvb/include/mpq_dvb_debug.h b/drivers/media/platform/msm/dvb/include/mpq_dvb_debug.h
new file mode 100644
index 000000000000..720b1b9cb70b
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/include/mpq_dvb_debug.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2012-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 _MPQ_DVB_DEBUG_H
+#define _MPQ_DVB_DEBUG_H
+
+/* Enable this line if you want to output debug printouts */
+#define MPG_DVB_DEBUG_ENABLE
+
+#undef MPQ_DVB_DBG_PRINT /* undef it, just in case */
+
+#ifdef MPG_DVB_DEBUG_ENABLE
+#define MPQ_DVB_ERR_PRINT(fmt, args...) pr_err(fmt, ## args)
+#define MPQ_DVB_WARN_PRINT(fmt, args...) pr_warn(fmt, ## args)
+#define MPQ_DVB_NOTICE_PRINT(fmt, args...) pr_notice(fmt, ## args)
+#define MPQ_DVB_DBG_PRINT(fmt, args...) pr_debug(fmt, ## args)
+#else /* MPG_DVB_DEBUG_ENABLE */
+#define MPQ_DVB_ERR_PRINT(fmt, args...)
+#define MPQ_DVB_WARN_PRINT(fmt, args...)
+#define MPQ_DVB_NOTICE_PRINT(fmt, args...)
+#define MPQ_DVB_DBG_PRINT(fmt, args...)
+#endif /* MPG_DVB_DEBUG_ENABLE */
+
+
+/*
+ * The following can be used to disable specific printout
+ * by adding a letter to the end of MPQ_DVB_DBG_PRINT
+ */
+#undef MPQ_DVB_DBG_PRINTT
+#define MPQ_DVB_DBG_PRINTT(fmt, args...)
+
+#endif /* _MPQ_DVB_DEBUG_H */
diff --git a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h
new file mode 100644
index 000000000000..b24dc1f3b5ff
--- /dev/null
+++ b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h
@@ -0,0 +1,462 @@
+/* Copyright (c) 2012-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 _MPQ_STREAM_BUFFER_H
+#define _MPQ_STREAM_BUFFER_H
+
+#include "dvb_ringbuffer.h"
+
+
+/**
+ * DOC: MPQ Stream Buffer
+ *
+ * A stream buffer implementation is used to transfer data between two units
+ * such as demux and decoders. The implementation relies on dvb_ringbuffer
+ * implementation. Refer to dvb_ringbuffer.h for details.
+ *
+ * The implementation uses two dvb_ringbuffers, one to pass the
+ * raw-data (PES payload for example) and the other to pass
+ * meta-data (information from PES header for example).
+ *
+ * The meta-data uses dvb_ringbuffer packet interface. Each meta-data
+ * packet points to the data buffer, and includes the offset to the data in the
+ * buffer, the size of raw-data described by the meta-data packet, and also the
+ * size of user's own parameters if any required.
+ *
+ * Data can be managed in two ways: ring-buffer & linear buffers, as specified
+ * in initialization when calling the mpq_streambuffer_init function.
+ * For managing data as a ring buffer exactly 1 data buffer descriptor must be
+ * specified in initialization. For this mode, dvb_ringbuffer is used "as-is".
+ * For managing data in several linear buffers, an array of buffer descriptors
+ * must be passed.
+ * For both modes, data descriptor(s) must be remain valid throughout the life
+ * span of the mpq_streambuffer object.
+ * Apart from initialization API remains the same for both modes.
+ *
+ * Contrary to dvb_ringbuffer implementation, this API makes sure there's
+ * enough data to read/write when making read/write operations.
+ * Users interested to flush/reset specific buffer, check for bytes
+ * ready or space available for write should use the respective services
+ * in dvb_ringbuffer (dvb_ringbuffer_avail, dvb_ringbuffer_free,
+ * dvb_ringbuffer_reset, dvb_ringbuffer_flush,
+ * dvb_ringbuffer_flush_spinlock_wakeup).
+ *
+ * Concurrency protection is handled in the same manner as in
+ * dvb_ringbuffer implementation.
+ *
+ * Typical call flow from producer:
+ *
+ * - Start writing the raw-data of new packet, the following call is
+ * repeated until end of data of the specific packet
+ *
+ * mpq_streambuffer_data_write(...)
+ *
+ * - Now write a new packet describing the new available raw-data
+ * mpq_streambuffer_pkt_write(...)
+ *
+ * For linear buffer mode, writing a new packet with data size > 0, causes the
+ * current buffer to be marked as pending for reading, and triggers moving to
+ * the next available buffer, that shall now be the current write buffer.
+ *
+ * Typical call flow from consumer:
+ *
+ * - Poll for next available packet:
+ * mpq_streambuffer_pkt_next(&streambuff,-1,&len)
+ *
+ * In different approach, consumer can wait on event for new data and then
+ * call mpq_streambuffer_pkt_next, waiting for data can be done as follows:
+ *
+ * wait_event_interruptible(
+ * streambuff->packet_data->queue,
+ * !dvb_ringbuffer_empty(&streambuff->packet_data) ||
+ * (streambuff->packet_data.error != 0);
+ *
+ * - Get the new packet information:
+ * mpq_streambuffer_pkt_read(..)
+ *
+ * - Read the raw-data of the new packet. Here you can use two methods:
+ *
+ * 1. Read the data to a user supplied buffer:
+ * mpq_streambuffer_data_read()
+ *
+ * In this case memory copy is done, read pointer is updated in the raw
+ * data buffer, the amount of raw-data is provided part of the
+ * packet's information. User should then call mpq_streambuffer_pkt_dispose
+ * with dispose_data set to 0 as the raw-data was already disposed.
+ * Note that secure buffer cannot be accessed directly and an error will
+ * occur.
+ *
+ * 2. Access the data directly using the raw-data address. The address
+ * of the raw data is provided part of the packet's information. User
+ * then should call mpq_streambuffer_pkt_dispose with dispose_data set
+ * to 1 to dispose the packet along with it's raw-data.
+ *
+ * - Disposal of packets:
+ * mpq_streambuffer_pkt_dispose(...)
+ *
+ * For linear buffer mode, disposing of a packet with data size > 0,
+ * regardless of the 'dispose_data' parameter, causes the current buffer's
+ * data to be disposed and marked as free for writing, and triggers moving to
+ * the next available buffer, that shall now be the current read buffer.
+ */
+
+struct mpq_streambuffer;
+struct mpq_streambuffer_packet_header;
+
+typedef void (*mpq_streambuffer_dispose_cb) (
+ struct mpq_streambuffer *sbuff,
+ u32 offset,
+ size_t len,
+ void *user_data);
+
+enum mpq_streambuffer_mode {
+ MPQ_STREAMBUFFER_BUFFER_MODE_RING,
+ MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR
+};
+
+/**
+ * struct mpq_streambuffer - mpq stream buffer representation
+ *
+ * @raw_data: The buffer used to hold raw-data, or linear buffer descriptors
+ * @packet_data: The buffer user to hold the meta-data
+ * @buffers: array of buffer descriptor(s) holding buffer initial & dynamic
+ * buffer information
+ * @mode: mpq_streambuffer buffer management work mode - Ring-buffer or Linear
+ * buffers
+ * @buffers_num: number of data buffers to manage
+ * @pending_buffers_count: for linear buffer management, counts the number of
+ * buffer that has been
+ */
+struct mpq_streambuffer {
+ struct dvb_ringbuffer raw_data;
+ struct dvb_ringbuffer packet_data;
+ struct mpq_streambuffer_buffer_desc *buffers;
+ enum mpq_streambuffer_mode mode;
+ u32 buffers_num;
+ u32 pending_buffers_count;
+ mpq_streambuffer_dispose_cb cb;
+ void *cb_user_data;
+};
+
+/**
+ * mpq_streambuffer_linear_desc
+ * @handle: ION handle's file descriptor of buffer
+ * @base: kernel mapped address to start of buffer.
+ * Can be NULL for secured buffers
+ * @size: size of buffer
+ * @read_ptr: initial read pointer value (should normally be 0)
+ * @write_ptr: initial write pointer value (should normally be 0)
+ */
+struct mpq_streambuffer_buffer_desc {
+ int handle;
+ void *base;
+ u32 size;
+ u32 read_ptr;
+ u32 write_ptr;
+};
+
+/**
+ * struct mpq_streambuffer_packet_header - packet header saved in packet buffer
+ * @user_data_len: length of private user (meta) data
+ * @raw_data_handle: ION handle's file descriptor of raw-data buffer
+ * @raw_data_offset: offset of raw-data from start of buffer (0 for linear)
+ * @raw_data_len: size of raw-data in the raw-data buffer (can be 0)
+ *
+ * The packet structure that is saved in each packet-buffer:
+ * user_data_len
+ * raw_data_handle
+ * raw_data_offset
+ * raw_data_len
+ * private user-data bytes
+ */
+struct mpq_streambuffer_packet_header {
+ u32 user_data_len;
+ int raw_data_handle;
+ u32 raw_data_offset;
+ u32 raw_data_len;
+} __packed;
+
+/**
+ * mpq_streambuffer_init - Initialize a new stream buffer
+ *
+ * @sbuff: The buffer to initialize
+ * @data_buffers: array of data buffer descriptor(s).
+ * Data descriptor(s) must be remain valid throughout the life
+ * span of the mpq_streambuffer object
+ * @data_buff_num: number of data buffer in array
+ * @packet_buff: The buffer holding meta-data
+ * @packet_buff_size: Size of meta-data buffer
+ *
+ * Return Error status, -EINVAL if any of the arguments are invalid
+ *
+ * Note:
+ * for data_buff_num > 1, mpq_streambuffer object manages these buffers as a
+ * separated set of linear buffers. A linear buffer cannot wrap-around and one
+ * can only write as many data bytes as the buffer's size. Data will not be
+ * written to the next free buffer.
+ */
+int mpq_streambuffer_init(
+ struct mpq_streambuffer *sbuff,
+ enum mpq_streambuffer_mode mode,
+ struct mpq_streambuffer_buffer_desc *data_buffers,
+ u32 data_buff_num,
+ void *packet_buff,
+ size_t packet_buff_size);
+
+/**
+ * mpq_streambuffer_terminate - Terminate stream buffer
+ *
+ * @sbuff: The buffer to terminate
+ *
+ * The function sets the the buffers error flags to ENODEV
+ * and wakeup any waiting threads on the buffer queues.
+ * Threads waiting on the buffer queues should check if
+ * error was set.
+ */
+void mpq_streambuffer_terminate(struct mpq_streambuffer *sbuff);
+
+/**
+ * mpq_streambuffer_packet_next - Returns index of next available packet.
+ *
+ * @sbuff: The stream buffer
+ * @idx: Previous packet index or -1 to return index of the the first
+ * available packet.
+ * @pktlen: The length of the ready packet
+ *
+ * Return index to the packet-buffer, -1 if buffer is empty
+ *
+ * After getting the index, the user of this function can either
+ * access the packet buffer directly using the returned index
+ * or ask to read the data back from the buffer using mpq_ringbuffer_pkt_read
+ */
+ssize_t mpq_streambuffer_pkt_next(
+ struct mpq_streambuffer *sbuff,
+ ssize_t idx, size_t *pktlen);
+
+/**
+ * mpq_streambuffer_pkt_read - Reads out the packet from the provided index.
+ *
+ * @sbuff: The stream buffer
+ * @idx: The index of the packet to be read
+ * @packet: The read packet's header
+ * @user_data: The read private user data
+ *
+ * Return The actual number of bytes read, -EINVAL if the packet is
+ * already disposed or the packet-data is invalid.
+ *
+ * The packet is not disposed after this function is called, to dispose it
+ * along with the raw-data it points to use mpq_streambuffer_pkt_dispose.
+ * If there are no private user-data, the user-data pointer can be NULL.
+ * The caller of this function must make sure that the private user-data
+ * buffer has enough space for the private user-data length
+ */
+ssize_t mpq_streambuffer_pkt_read(
+ struct mpq_streambuffer *sbuff,
+ size_t idx,
+ struct mpq_streambuffer_packet_header *packet,
+ u8 *user_data);
+
+/**
+ * mpq_streambuffer_pkt_dispose - Disposes a packet from the packet buffer
+ *
+ * @sbuff: The stream buffer
+ * @idx: The index of the packet to be disposed
+ * @dispose_data: Indicates whether to update the read pointer inside the
+ * raw-data buffer for the respective data pointed by the packet.
+ *
+ * Return error status, -EINVAL if the packet-data is invalid
+ *
+ * The function updates the read pointer inside the raw-data buffer
+ * for the respective data pointed by the packet if dispose_data is set.
+ */
+int mpq_streambuffer_pkt_dispose(
+ struct mpq_streambuffer *sbuff,
+ size_t idx,
+ int dispose_data);
+
+/**
+ * mpq_streambuffer_pkt_write - Write a new packet to the packet buffer.
+ *
+ * @sbuff: The stream buffer
+ * @packet: The packet header to write
+ * @user_data: The private user-data to be written
+ *
+ * Return error status, -ENOSPC if there's no space to write the packet
+ */
+int mpq_streambuffer_pkt_write(
+ struct mpq_streambuffer *sbuff,
+ struct mpq_streambuffer_packet_header *packet,
+ u8 *user_data);
+
+/**
+ * mpq_streambuffer_data_write - Write data to raw-data buffer
+ *
+ * @sbuff: The stream buffer
+ * @buf: The buffer holding the data to be written
+ * @len: The length of the data buffer
+ *
+ * Return The actual number of bytes written or -ENOSPC if
+ * no space to write the data
+ */
+ssize_t mpq_streambuffer_data_write(
+ struct mpq_streambuffer *sbuff,
+ const u8 *buf, size_t len);
+
+/**
+ * mpq_streambuffer_data_write_deposit - Advances the raw-buffer write pointer.
+ * Assumes the raw-data was written by the user directly
+ *
+ * @sbuff: The stream buffer
+ * @len: The length of the raw-data that was already written
+ *
+ * Return error status
+ */
+int mpq_streambuffer_data_write_deposit(
+ struct mpq_streambuffer *sbuff,
+ size_t len);
+
+/**
+ * mpq_streambuffer_data_read - Reads out raw-data to the provided buffer.
+ *
+ * @sbuff: The stream buffer
+ * @buf: The buffer to read the raw-data data to
+ * @len: The length of the buffer that will hold the raw-data
+ *
+ * Return The actual number of bytes read or error code
+ *
+ * This function copies the data from the ring-buffer to the
+ * provided buf parameter. The user can save the extra copy by accessing
+ * the data pointer directly and reading from it, then update the
+ * read pointer by the amount of data that was read using
+ * mpq_streambuffer_data_read_dispose
+ */
+ssize_t mpq_streambuffer_data_read(
+ struct mpq_streambuffer *sbuff,
+ u8 *buf, size_t len);
+
+/**
+ * mpq_streambuffer_data_read_user
+ *
+ * Same as mpq_streambuffer_data_read except data can be copied to user-space
+ * buffer.
+ */
+ssize_t mpq_streambuffer_data_read_user(
+ struct mpq_streambuffer *sbuff,
+ u8 __user *buf, size_t len);
+
+/**
+ * mpq_streambuffer_data_read_dispose - Advances the raw-buffer read pointer.
+ * Assumes the raw-data was read by the user directly.
+ *
+ * @sbuff: The stream buffer
+ * @len: The length of the raw-data to be disposed
+ *
+ * Return error status, -EINVAL if buffer there's no enough data to
+ * be disposed
+ *
+ * The user can instead dispose a packet along with the data in the
+ * raw-data buffer using mpq_streambuffer_pkt_dispose.
+ */
+int mpq_streambuffer_data_read_dispose(
+ struct mpq_streambuffer *sbuff,
+ size_t len);
+/**
+ * mpq_streambuffer_get_buffer_handle - Returns the current linear buffer
+ * ION handle.
+ * @sbuff: The stream buffer
+ * @read_buffer: specifies if a read buffer handle is requested (when set),
+ * or a write buffer handle is requested.
+ * For linear buffer mode read & write buffers may be different
+ * buffers. For ring buffer mode, the same (single) buffer handle
+ * is returned.
+ * buffer handle
+ * @handle: returned handle
+ *
+ * Return error status
+ * -EINVAL is arguments are invalid.
+ * -EPERM if stream buffer specified was not initialized with linear support.
+ */
+int mpq_streambuffer_get_buffer_handle(
+ struct mpq_streambuffer *sbuff,
+ int read_buffer,
+ int *handle);
+
+/**
+ * mpq_streambuffer_data_free - Returns number of free bytes in data buffer.
+ * @sbuff: The stream buffer object
+ *
+ * Note: for linear buffer management this return number of free bytes in the
+ * current write buffer only.
+ */
+ssize_t mpq_streambuffer_data_free(
+ struct mpq_streambuffer *sbuff);
+
+/**
+ * mpq_streambuffer_data_avail - Returns number of bytes in data buffer that
+ * can be read.
+ * @sbuff: The stream buffer object
+ *
+ * Note: for linear buffer management this return number of data bytes in the
+ * current read buffer only.
+ */
+ssize_t mpq_streambuffer_data_avail(
+ struct mpq_streambuffer *sbuff);
+
+/**
+ * mpq_streambuffer_register_pkt_dispose - Registers a callback to notify on
+ * packet disposal events.
+ * can be read.
+ * @sbuff: The stream buffer object
+ * @cb_func: user callback function
+ * @user_data: user data to be passed to callback function.
+ *
+ * Returns error status
+ * -EINVAL if arguments are invalid
+ */
+int mpq_streambuffer_register_data_dispose(
+ struct mpq_streambuffer *sbuff,
+ mpq_streambuffer_dispose_cb cb_func,
+ void *user_data);
+
+/**
+ * mpq_streambuffer_data_rw_offset - returns read/write offsets of current data
+ * buffer.
+ * @sbuff: The stream buffer object
+ * @read_offset: returned read offset
+ * @write_offset: returned write offset
+ *
+ * Note: read offset or write offset may be NULL if not required.
+ * Returns error status
+ * -EINVAL if arguments are invalid
+ */
+int mpq_streambuffer_get_data_rw_offset(
+ struct mpq_streambuffer *sbuff,
+ u32 *read_offset,
+ u32 *write_offset);
+
+/**
+ * mpq_streambuffer_metadata_free - returns number of free bytes in the meta
+ * data buffer, or error status.
+ * @sbuff: the stream buffer object
+ */
+ssize_t mpq_streambuffer_metadata_free(struct mpq_streambuffer *sbuff);
+
+/**
+ * mpq_streambuffer_flush - flush both pending packets and data in buffer
+ *
+ * @sbuff: the stream buffer object
+ *
+ * Returns error status
+ */
+int mpq_streambuffer_flush(struct mpq_streambuffer *sbuff);
+
+#endif /* _MPQ_STREAM_BUFFER_H */
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index 3e5cbdecfba4..594bac6c5902 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -41,8 +41,12 @@
#define ROT_HW_ACQUIRE_TIMEOUT_IN_MS 100
/* default pixel per clock ratio */
-#define ROT_PIXEL_PER_CLK_NUMERATOR 4
-#define ROT_PIXEL_PER_CLK_DENOMINATOR 1
+#define ROT_PIXEL_PER_CLK_NUMERATOR 36
+#define ROT_PIXEL_PER_CLK_DENOMINATOR 10
+#define ROT_FUDGE_FACTOR_NUMERATOR 105
+#define ROT_FUDGE_FACTOR_DENOMINATOR 100
+#define ROT_OVERHEAD_NUMERATOR 27
+#define ROT_OVERHEAD_DENOMINATOR 10000
/*
* Max rotator hw blocks possible. Used for upper array limits instead of
@@ -278,6 +282,10 @@ static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
SDEROT_EVTLOG(on);
SDEROT_DBG("%s: rotator regulators", on ? "Enable" : "Disable");
+
+ if (mgr->ops_hw_pre_pmevent)
+ mgr->ops_hw_pre_pmevent(mgr, on);
+
ret = sde_rot_enable_vreg(mgr->module_power.vreg_config,
mgr->module_power.num_vreg, on);
if (ret) {
@@ -286,10 +294,13 @@ static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
return;
}
+ if (mgr->ops_hw_post_pmevent)
+ mgr->ops_hw_post_pmevent(mgr, on);
+
mgr->regulator_enable = on;
}
-static int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
+int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
{
struct clk *clk;
int ret = 0;
@@ -991,12 +1002,33 @@ static u32 sde_rotator_calc_buf_bw(struct sde_mdp_format_params *fmt,
return bw;
}
+static int sde_rotator_find_max_fps(struct sde_rot_mgr *mgr)
+{
+ struct sde_rot_file_private *priv;
+ struct sde_rot_perf *perf;
+ int max_fps = 0;
+
+ list_for_each_entry(priv, &mgr->file_list, list) {
+ list_for_each_entry(perf, &priv->perf_list, list) {
+ if (perf->config.frame_rate > max_fps)
+ max_fps = perf->config.frame_rate;
+ }
+ }
+
+ SDEROT_DBG("Max fps:%d\n", max_fps);
+ return max_fps;
+}
+
static int sde_rotator_calc_perf(struct sde_rot_mgr *mgr,
struct sde_rot_perf *perf)
{
struct sde_rotation_config *config = &perf->config;
u32 read_bw, write_bw;
struct sde_mdp_format_params *in_fmt, *out_fmt;
+ struct sde_rotator_device *rot_dev;
+ int max_fps;
+
+ rot_dev = platform_get_drvdata(mgr->pdev);
in_fmt = sde_get_format_params(config->input.format);
if (!in_fmt) {
@@ -1009,17 +1041,44 @@ static int sde_rotator_calc_perf(struct sde_rot_mgr *mgr,
return -EINVAL;
}
+ /*
+ * rotator processes 4 pixels per clock, but the actual throughtput
+ * is 3.6. We also need to take into account for overhead time. Final
+ * equation is:
+ * W x H / throughput / (1/fps - overhead) * fudge_factor
+ */
+ max_fps = sde_rotator_find_max_fps(mgr);
perf->clk_rate = config->input.width * config->input.height;
- perf->clk_rate *= config->frame_rate;
- /* rotator processes 4 pixels per clock */
perf->clk_rate = (perf->clk_rate * mgr->pixel_per_clk.denom) /
mgr->pixel_per_clk.numer;
+ perf->clk_rate *= max_fps;
+ perf->clk_rate = (perf->clk_rate * mgr->fudge_factor.numer) /
+ mgr->fudge_factor.denom;
+ perf->clk_rate *= mgr->overhead.denom;
+
+ /*
+ * check for override overhead default value
+ */
+ if (rot_dev->min_overhead_us > (mgr->overhead.numer * 100))
+ perf->clk_rate = DIV_ROUND_UP_ULL(perf->clk_rate,
+ (mgr->overhead.denom - max_fps *
+ (rot_dev->min_overhead_us / 100)));
+ else
+ perf->clk_rate = DIV_ROUND_UP_ULL(perf->clk_rate,
+ (mgr->overhead.denom - max_fps *
+ mgr->overhead.numer));
+
+ /*
+ * check for Override clock calcualtion
+ */
+ if (rot_dev->min_rot_clk > perf->clk_rate)
+ perf->clk_rate = rot_dev->min_rot_clk;
read_bw = sde_rotator_calc_buf_bw(in_fmt, config->input.width,
- config->input.height, config->frame_rate);
+ config->input.height, max_fps);
write_bw = sde_rotator_calc_buf_bw(out_fmt, config->output.width,
- config->output.height, config->frame_rate);
+ config->output.height, max_fps);
read_bw = sde_apply_comp_ratio_factor(read_bw, in_fmt,
&config->input.comp_ratio);
@@ -1028,13 +1087,22 @@ static int sde_rotator_calc_perf(struct sde_rot_mgr *mgr,
perf->bw = read_bw + write_bw;
+ /*
+ * check for override bw calculation
+ */
+ if (rot_dev->min_bw > perf->bw)
+ perf->bw = rot_dev->min_bw;
+
perf->rdot_limit = sde_mdp_get_ot_limit(
config->input.width, config->input.height,
- config->input.format, config->frame_rate, true);
+ config->input.format, max_fps, true);
perf->wrot_limit = sde_mdp_get_ot_limit(
config->input.width, config->input.height,
- config->input.format, config->frame_rate, false);
+ config->input.format, max_fps, false);
+ SDEROT_DBG("clk:%lu, rdBW:%d, wrBW:%d, rdOT:%d, wrOT:%d\n",
+ perf->clk_rate, read_bw, write_bw, perf->rdot_limit,
+ perf->wrot_limit);
return 0;
}
@@ -1740,16 +1808,9 @@ static int sde_rotator_open_session(struct sde_rot_mgr *mgr,
config.session_id = session_id;
perf->config = config;
- perf->last_wb_idx = -1;
+ perf->last_wb_idx = 0;
INIT_LIST_HEAD(&perf->list);
-
- ret = sde_rotator_calc_perf(mgr, perf);
- if (ret) {
- SDEROT_ERR("error setting the session %d\n", ret);
- goto copy_user_err;
- }
-
list_add(&perf->list, &private->perf_list);
ret = sde_rotator_resource_ctrl(mgr, true);
@@ -1770,25 +1831,17 @@ static int sde_rotator_open_session(struct sde_rot_mgr *mgr,
goto enable_clk_err;
}
- ret = sde_rotator_update_perf(mgr);
- if (ret) {
- SDEROT_ERR("fail to open session, not enough clk/bw\n");
- goto perf_err;
- }
SDEROT_DBG("open session id=%u in{%u,%u}f:%u out{%u,%u}f:%u\n",
config.session_id, config.input.width, config.input.height,
config.input.format, config.output.width, config.output.height,
config.output.format);
goto done;
-perf_err:
- sde_rotator_clk_ctrl(mgr, false);
enable_clk_err:
update_clk_err:
sde_rotator_resource_ctrl(mgr, false);
resource_err:
list_del_init(&perf->list);
-copy_user_err:
devm_kfree(&mgr->pdev->dev, perf->work_distribution);
alloc_err:
devm_kfree(&mgr->pdev->dev, perf);
@@ -1860,11 +1913,23 @@ static int sde_rotator_config_session(struct sde_rot_mgr *mgr,
}
ret = sde_rotator_update_perf(mgr);
+ if (ret) {
+ SDEROT_ERR("error in updating perf: %d\n", ret);
+ goto done;
+ }
+
+ ret = sde_rotator_update_clk(mgr);
+ if (ret) {
+ SDEROT_ERR("error in updating the rotator clk: %d\n", ret);
+ goto done;
+ }
- SDEROT_DBG("reconfig session id=%u in{%u,%u}f:%u out{%u,%u}f:%u\n",
+ SDEROT_DBG(
+ "reconfig session id=%u in{%u,%u}f:%u out{%u,%u}f:%u fps:%d clk:%lu, bw:%llu\n",
config->session_id, config->input.width, config->input.height,
config->input.format, config->output.width,
- config->output.height, config->output.format);
+ config->output.height, config->output.format,
+ config->frame_rate, perf->clk_rate, perf->bw);
done:
return ret;
}
@@ -2379,6 +2444,10 @@ int sde_rotator_core_init(struct sde_rot_mgr **pmgr,
mgr->queue_count = 1;
mgr->pixel_per_clk.numer = ROT_PIXEL_PER_CLK_NUMERATOR;
mgr->pixel_per_clk.denom = ROT_PIXEL_PER_CLK_DENOMINATOR;
+ mgr->fudge_factor.numer = ROT_FUDGE_FACTOR_NUMERATOR;
+ mgr->fudge_factor.denom = ROT_FUDGE_FACTOR_DENOMINATOR;
+ mgr->overhead.numer = ROT_OVERHEAD_NUMERATOR;
+ mgr->overhead.denom = ROT_OVERHEAD_DENOMINATOR;
mutex_init(&mgr->lock);
atomic_set(&mgr->device_suspended, 0);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index 8659d361be07..781b03e1b974 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -281,6 +281,8 @@ struct sde_rot_mgr {
u32 hwacquire_timeout;
struct sde_mult_factor pixel_per_clk;
+ struct sde_mult_factor fudge_factor;
+ struct sde_mult_factor overhead;
int (*ops_config_hw)(struct sde_rot_hw_resource *hw,
struct sde_rot_entry *entry);
@@ -293,6 +295,8 @@ struct sde_rot_mgr {
void (*ops_hw_free)(struct sde_rot_mgr *mgr,
struct sde_rot_hw_resource *hw);
int (*ops_hw_init)(struct sde_rot_mgr *mgr);
+ void (*ops_hw_pre_pmevent)(struct sde_rot_mgr *mgr, bool pmon);
+ void (*ops_hw_post_pmevent)(struct sde_rot_mgr *mgr, bool pmon);
void (*ops_hw_destroy)(struct sde_rot_mgr *mgr);
ssize_t (*ops_hw_show_caps)(struct sde_rot_mgr *mgr,
struct device_attribute *attr, char *buf, ssize_t len);
@@ -405,6 +409,8 @@ int sde_rotator_validate_request(struct sde_rot_mgr *rot_dev,
struct sde_rot_file_private *ctx,
struct sde_rot_entry_container *req);
+int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable);
+
static inline void sde_rot_mgr_lock(struct sde_rot_mgr *mgr)
{
mutex_lock(&mgr->lock);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
index c609dbd2036e..94223b557990 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -914,6 +914,34 @@ static int sde_rotator_evtlog_create_debugfs(
return 0;
}
+
+static int sde_rotator_perf_create_debugfs(
+ struct sde_rotator_device *rot_dev,
+ struct dentry *debugfs_root)
+{
+ rot_dev->perf_root = debugfs_create_dir("perf", debugfs_root);
+ if (IS_ERR_OR_NULL(rot_dev->perf_root)) {
+ pr_err("debugfs_create_dir for perf failed, error %ld\n",
+ PTR_ERR(rot_dev->perf_root));
+ rot_dev->perf_root = NULL;
+ return -ENODEV;
+ }
+
+ rot_dev->min_rot_clk = 0;
+ debugfs_create_u32("min_rot_clk", S_IRUGO | S_IWUSR,
+ rot_dev->perf_root, &rot_dev->min_rot_clk);
+
+ rot_dev->min_bw = 0;
+ debugfs_create_u32("min_bw", S_IRUGO | S_IWUSR,
+ rot_dev->perf_root, &rot_dev->min_bw);
+
+ rot_dev->min_overhead_us = 0;
+ debugfs_create_u32("min_overhead_us", S_IRUGO | S_IWUSR,
+ rot_dev->perf_root, &rot_dev->min_overhead_us);
+
+ return 0;
+}
+
/*
* struct sde_rotator_stat_ops - processed statistics file operations
*/
@@ -1006,6 +1034,12 @@ struct dentry *sde_rotator_create_debugfs(
return NULL;
}
+ if (sde_rotator_perf_create_debugfs(rot_dev, debugfs_root)) {
+ SDEROT_ERR("fail create perf debugfs\n");
+ debugfs_remove_recursive(debugfs_root);
+ return NULL;
+ }
+
return debugfs_root;
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index d34623a531ba..b88f03ce89ae 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -546,22 +546,8 @@ static struct vb2_mem_ops sde_rotator_vb2_mem_ops = {
static int sde_rotator_s_ctx_ctrl(struct sde_rotator_ctx *ctx,
s32 *ctx_ctrl, struct v4l2_ctrl *ctrl)
{
- struct sde_rotator_device *rot_dev = ctx->rot_dev;
- struct sde_rotation_config config;
- s32 prev_val;
- int ret;
-
- prev_val = *ctx_ctrl;
*ctx_ctrl = ctrl->val;
- sde_rotator_get_config_from_ctx(ctx, &config);
- ret = sde_rotator_session_config(rot_dev->mgr, ctx->private, &config);
- if (ret) {
- SDEDEV_WARN(rot_dev->dev, "fail %s:%d s:%d\n",
- ctrl->name, ctrl->val, ctx->session_id);
- *ctx_ctrl = prev_val;
- }
-
- return ret;
+ return 0;
}
/*
@@ -1137,6 +1123,13 @@ static int sde_rotator_try_fmt_vid_cap(struct file *file,
struct sde_rotation_config config;
int ret;
+ if ((f->fmt.pix.width == 0) || (f->fmt.pix.height == 0)) {
+ SDEDEV_WARN(ctx->rot_dev->dev,
+ "Not supporting 0 width/height: %dx%d\n",
+ f->fmt.pix.width, f->fmt.pix.height);
+ return -EINVAL;
+ }
+
sde_rot_mgr_lock(rot_dev->mgr);
sde_rotator_get_config_from_ctx(ctx, &config);
config.output.format = f->fmt.pix.pixelformat;
@@ -1176,6 +1169,13 @@ static int sde_rotator_try_fmt_vid_out(struct file *file,
struct sde_rotation_config config;
int ret;
+ if ((f->fmt.pix.width == 0) || (f->fmt.pix.height == 0)) {
+ SDEDEV_WARN(ctx->rot_dev->dev,
+ "Not supporting 0 width/height: %dx%d\n",
+ f->fmt.pix.width, f->fmt.pix.height);
+ return -EINVAL;
+ }
+
sde_rot_mgr_lock(rot_dev->mgr);
sde_rotator_get_config_from_ctx(ctx, &config);
config.input.format = f->fmt.pix.pixelformat;
@@ -1213,7 +1213,6 @@ static int sde_rotator_s_fmt_vid_cap(struct file *file,
{
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
struct sde_rotator_device *rot_dev = ctx->rot_dev;
- struct sde_rotation_config config;
int ret;
ret = sde_rotator_try_fmt_vid_cap(file, fh, f);
@@ -1235,12 +1234,6 @@ static int sde_rotator_s_fmt_vid_cap(struct file *file,
f->fmt.pix.field,
f->fmt.pix.width, f->fmt.pix.height);
- /* configure hal to current input/output setting */
- sde_rot_mgr_lock(rot_dev->mgr);
- sde_rotator_get_config_from_ctx(ctx, &config);
- sde_rotator_session_config(rot_dev->mgr, ctx->private, &config);
- sde_rot_mgr_unlock(rot_dev->mgr);
-
return 0;
}
@@ -1524,7 +1517,6 @@ static int sde_rotator_s_crop(struct file *file, void *fh,
{
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
struct sde_rotator_device *rot_dev = ctx->rot_dev;
- struct sde_rotation_config config;
struct sde_rotation_item item;
struct v4l2_rect rect;
@@ -1597,12 +1589,6 @@ static int sde_rotator_s_crop(struct file *file, void *fh,
return -EINVAL;
}
- /* configure hal to current input/output setting */
- sde_rot_mgr_lock(rot_dev->mgr);
- sde_rotator_get_config_from_ctx(ctx, &config);
- sde_rotator_session_config(rot_dev->mgr, ctx->private, &config);
- sde_rot_mgr_unlock(rot_dev->mgr);
-
return 0;
}
@@ -2336,6 +2322,9 @@ static int sde_rotator_probe(struct platform_device *pdev)
rot_dev->early_submit = SDE_ROTATOR_EARLY_SUBMIT;
rot_dev->fence_timeout = SDE_ROTATOR_FENCE_TIMEOUT;
rot_dev->streamoff_timeout = SDE_ROTATOR_STREAM_OFF_TIMEOUT;
+ rot_dev->min_rot_clk = 0;
+ rot_dev->min_bw = 0;
+ rot_dev->min_overhead_us = 0;
rot_dev->drvdata = sde_rotator_get_drv_data(&pdev->dev);
rot_dev->pdev = pdev;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
index fd247d10128c..f3c904817296 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
@@ -160,6 +160,9 @@ struct sde_rotator_statistics {
* @session_id: Next context session identifier
* @fence_timeout: Timeout value in msec for fence wait
* @streamoff_timeout: Timeout value in msec for stream off
+ * @min_rot_clk: Override the minimum rotator clock from perf calculation
+ * @min_bw: Override the minimum bandwidth from perf calculation
+ * @min_overhead_us: Override the minimum overhead in us from perf calculation
* @debugfs_root: Pointer to debugfs directory entry.
* @stats: placeholder for rotator statistics
*/
@@ -177,8 +180,12 @@ struct sde_rotator_device {
u32 session_id;
u32 fence_timeout;
u32 streamoff_timeout;
+ u32 min_rot_clk;
+ u32 min_bw;
+ u32 min_overhead_us;
struct sde_rotator_statistics stats;
struct dentry *debugfs_root;
+ struct dentry *perf_root;
};
static inline
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index e9d2dd5ec972..d2bc76874c48 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -271,6 +271,7 @@ static int sde_hw_rotator_pending_swts(struct sde_hw_rotator *rot,
SDEROT_DBG("ts:0x%x, queue_id:%d, swts:0x%x, pending:%d\n",
ctx->timestamp, ctx->q_id, swts, pending);
+ SDEROT_EVTLOG(ctx->timestamp, swts, ctx->q_id, ts_diff);
return pending;
}
@@ -1240,6 +1241,94 @@ static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot)
}
/*
+ * sde_hw_rotator_pre_pmevent - SDE rotator core will call this before a
+ * PM event occurs
+ * @mgr: Pointer to rotator manager
+ * @pmon: Boolean indicate an on/off power event
+ */
+void sde_hw_rotator_pre_pmevent(struct sde_rot_mgr *mgr, bool pmon)
+{
+ struct sde_hw_rotator *rot;
+ u32 l_ts, h_ts, swts, hwts;
+ u32 rotsts, regdmasts;
+
+ /*
+ * Check last HW timestamp with SW timestamp before power off event.
+ * If there is a mismatch, that will be quite possible the rotator HW
+ * is either hang or not finishing last submitted job. In that case,
+ * it is best to do a timeout eventlog to capture some good events
+ * log data for analysis.
+ */
+ if (!pmon && mgr && mgr->hw_data) {
+ rot = mgr->hw_data;
+ h_ts = atomic_read(&rot->timestamp[ROT_QUEUE_HIGH_PRIORITY]);
+ l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]);
+
+ /* contruct the combined timstamp */
+ swts = (h_ts & SDE_REGDMA_SWTS_MASK) |
+ ((l_ts & SDE_REGDMA_SWTS_MASK) <<
+ SDE_REGDMA_SWTS_SHIFT);
+
+ /* Need to turn on clock to access rotator register */
+ sde_rotator_clk_ctrl(mgr, true);
+ hwts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
+ regdmasts = SDE_ROTREG_READ(rot->mdss_base,
+ REGDMA_CSR_REGDMA_BLOCK_STATUS);
+ rotsts = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
+
+ SDEROT_DBG(
+ "swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
+ swts, hwts, regdmasts, rotsts);
+ SDEROT_EVTLOG(swts, hwts, regdmasts, rotsts);
+
+ if ((swts != hwts) && ((regdmasts & REGDMA_BUSY) ||
+ (rotsts & ROT_STATUS_MASK))) {
+ SDEROT_ERR(
+ "Mismatch SWTS with HWTS: swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
+ swts, hwts, regdmasts, rotsts);
+ SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus",
+ "panic");
+ }
+
+ /* Turn off rotator clock after checking rotator registers */
+ sde_rotator_clk_ctrl(mgr, false);
+ }
+}
+
+/*
+ * sde_hw_rotator_post_pmevent - SDE rotator core will call this after a
+ * PM event occurs
+ * @mgr: Pointer to rotator manager
+ * @pmon: Boolean indicate an on/off power event
+ */
+void sde_hw_rotator_post_pmevent(struct sde_rot_mgr *mgr, bool pmon)
+{
+ struct sde_hw_rotator *rot;
+ u32 l_ts, h_ts, swts;
+
+ /*
+ * After a power on event, the rotator HW is reset to default setting.
+ * It is necessary to synchronize the SW timestamp with the HW.
+ */
+ if (pmon && mgr && mgr->hw_data) {
+ rot = mgr->hw_data;
+ h_ts = atomic_read(&rot->timestamp[ROT_QUEUE_HIGH_PRIORITY]);
+ l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]);
+
+ /* contruct the combined timstamp */
+ swts = (h_ts & SDE_REGDMA_SWTS_MASK) |
+ ((l_ts & SDE_REGDMA_SWTS_MASK) <<
+ SDE_REGDMA_SWTS_SHIFT);
+
+ SDEROT_DBG("swts:0x%x, h_ts:0x%x, l_ts;0x%x\n",
+ swts, h_ts, l_ts);
+ SDEROT_EVTLOG(swts, h_ts, l_ts);
+ rot->reset_hw_ts = true;
+ rot->last_hw_ts = swts;
+ }
+}
+
+/*
* sde_hw_rotator_destroy - Destroy hw rotator and free allocated resources
* @mgr: Pointer to rotator manager
*/
@@ -1455,6 +1544,15 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
return -EINVAL;
}
+ if (rot->reset_hw_ts) {
+ SDEROT_EVTLOG(rot->last_hw_ts);
+ SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG,
+ rot->last_hw_ts);
+ /* ensure write is issued to the rotator HW */
+ wmb();
+ rot->reset_hw_ts = false;
+ }
+
flags = (item->flags & SDE_ROTATION_FLIP_LR) ?
SDE_ROT_FLAG_FLIP_LR : 0;
flags |= (item->flags & SDE_ROTATION_FLIP_UD) ?
@@ -1511,7 +1609,8 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
&entry->dst_buf);
}
- SDEROT_EVTLOG(flags, item->input.width, item->input.height,
+ SDEROT_EVTLOG(ctx->timestamp, flags,
+ item->input.width, item->input.height,
item->output.width, item->output.height,
entry->src_buf.p[0].addr, entry->dst_buf.p[0].addr);
@@ -1715,6 +1814,7 @@ static int sde_rotator_hw_rev_init(struct sde_hw_rotator *rot)
mdata->regdump = sde_rot_r3_regdump;
mdata->regdump_size = ARRAY_SIZE(sde_rot_r3_regdump);
+ SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG, 0);
return 0;
}
@@ -2174,6 +2274,8 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
mgr->ops_hw_create_debugfs = sde_rotator_r3_create_debugfs;
mgr->ops_hw_get_pixfmt = sde_hw_rotator_get_pixfmt;
mgr->ops_hw_is_valid_pixfmt = sde_hw_rotator_is_valid_pixfmt;
+ mgr->ops_hw_pre_pmevent = sde_hw_rotator_pre_pmevent;
+ mgr->ops_hw_post_pmevent = sde_hw_rotator_post_pmevent;
ret = sde_hw_rotator_parse_dt(mgr->hw_data, mgr->pdev);
if (ret)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
index 3267e4418b3a..a748b87a231a 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
@@ -250,9 +250,10 @@
/* General defines */
#define ROT_DONE_MASK 0x1
#define ROT_DONE_CLEAR 0x1
-#define ROT_BUSY_BIT BIT(1)
+#define ROT_BUSY_BIT BIT(0)
#define ROT_ERROR_BIT BIT(8)
#define ROT_STATUS_MASK (ROT_BUSY_BIT | ROT_ERROR_BIT)
+#define REGDMA_BUSY BIT(0)
#define REGDMA_EN 0x1
#define REGDMA_SECURE_EN BIT(8)
#define REGDMA_HALT BIT(16)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
index b95f838f463b..91ac3d0371fa 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
@@ -270,6 +270,8 @@ struct sde_hw_rotator {
spinlock_t rotisr_lock;
bool dbgmem;
+ bool reset_hw_ts;
+ u32 last_hw_ts;
};
/**
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..becea0c59521 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)
@@ -662,7 +667,7 @@ static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst,
int rc = 0;
switch (ctrl->id) {
case V4L2_CID_MPEG_VIDC_VIDEO_MVC_BUFFER_LAYOUT:
- if (inst->fmts[OUTPUT_PORT]->fourcc != V4L2_PIX_FMT_H264_MVC) {
+ if (inst->fmts[OUTPUT_PORT].fourcc != V4L2_PIX_FMT_H264_MVC) {
dprintk(VIDC_ERR, "Control %#x only valid for MVC\n",
ctrl->id);
rc = -ENOTSUPP;
@@ -670,7 +675,7 @@ static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst,
}
break;
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
- if (inst->fmts[OUTPUT_PORT]->fourcc == V4L2_PIX_FMT_H264_MVC &&
+ if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_H264_MVC &&
ctrl->val != V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) {
dprintk(VIDC_ERR,
"Profile %#x not supported for MVC\n",
@@ -680,7 +685,7 @@ static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst,
}
break;
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
- if (inst->fmts[OUTPUT_PORT]->fourcc == V4L2_PIX_FMT_H264_MVC &&
+ if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_H264_MVC &&
ctrl->val >= V4L2_MPEG_VIDEO_H264_LEVEL_5_2) {
dprintk(VIDC_ERR, "Level %#x not supported for MVC\n",
ctrl->val);
@@ -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,
@@ -883,10 +896,10 @@ int msm_vdec_prepare_buf(struct msm_vidc_inst *inst,
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (b->length != inst->fmts[CAPTURE_PORT]->num_planes) {
+ if (b->length != inst->fmts[CAPTURE_PORT].num_planes) {
dprintk(VIDC_ERR,
"Planes mismatch: needed: %d, allocated: %d\n",
- inst->fmts[CAPTURE_PORT]->num_planes,
+ inst->fmts[CAPTURE_PORT].num_planes,
b->length);
rc = -EINVAL;
break;
@@ -962,10 +975,10 @@ int msm_vdec_release_buf(struct msm_vidc_inst *inst,
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (b->length != inst->fmts[CAPTURE_PORT]->num_planes) {
+ if (b->length != inst->fmts[CAPTURE_PORT].num_planes) {
dprintk(VIDC_ERR,
"Planes mismatch: needed: %d, to release: %d\n",
- inst->fmts[CAPTURE_PORT]->num_planes, b->length);
+ inst->fmts[CAPTURE_PORT].num_planes, b->length);
rc = -EINVAL;
break;
}
@@ -1086,9 +1099,9 @@ int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
hdev = inst->core->device;
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- fmt = inst->fmts[CAPTURE_PORT];
+ fmt = &inst->fmts[CAPTURE_PORT];
else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- fmt = inst->fmts[OUTPUT_PORT];
+ fmt = &inst->fmts[OUTPUT_PORT];
else
return -ENOTSUPP;
@@ -1237,6 +1250,8 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
rc = -EINVAL;
goto err_invalid_fmt;
}
+ memcpy(&inst->fmts[fmt->type], fmt,
+ sizeof(struct msm_vidc_format));
inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
@@ -1244,7 +1259,6 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
msm_comm_get_hal_output_buffer(inst),
f->fmt.pix_mp.pixelformat);
- inst->fmts[fmt->type] = fmt;
if (msm_comm_get_stream_output_mode(inst) ==
HAL_VIDEO_DECODER_SECONDARY) {
frame_sz.buffer_type = HAL_BUFFER_OUTPUT2;
@@ -1259,10 +1273,10 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
}
f->fmt.pix_mp.plane_fmt[0].sizeimage =
- fmt->get_frame_size(0,
+ inst->fmts[fmt->type].get_frame_size(0,
f->fmt.pix_mp.height, f->fmt.pix_mp.width);
- extra_idx = EXTRADATA_IDX(fmt->num_planes);
+ extra_idx = EXTRADATA_IDX(inst->fmts[fmt->type].num_planes);
if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
VENUS_EXTRADATA_SIZE(
@@ -1270,8 +1284,8 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
inst->prop.width[CAPTURE_PORT]);
}
- f->fmt.pix_mp.num_planes = fmt->num_planes;
- for (i = 0; i < fmt->num_planes; ++i) {
+ f->fmt.pix_mp.num_planes = inst->fmts[fmt->type].num_planes;
+ for (i = 0; i < inst->fmts[fmt->type].num_planes; ++i) {
inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] =
f->fmt.pix_mp.plane_fmt[i].sizeimage;
}
@@ -1290,6 +1304,8 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
rc = -EINVAL;
goto err_invalid_fmt;
}
+ memcpy(&inst->fmts[fmt->type], fmt,
+ sizeof(struct msm_vidc_format));
rc = msm_comm_try_state(inst, MSM_VIDC_CORE_INIT_DONE);
if (rc) {
@@ -1297,17 +1313,16 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
goto err_invalid_fmt;
}
- if (!(get_hal_codec(fmt->fourcc) &
+ if (!(get_hal_codec(inst->fmts[fmt->type].fourcc) &
inst->core->dec_codec_supported)) {
dprintk(VIDC_ERR,
"Codec(%#x) is not present in the supported codecs list(%#x)\n",
- get_hal_codec(fmt->fourcc),
+ get_hal_codec(inst->fmts[fmt->type].fourcc),
inst->core->dec_codec_supported);
rc = -EINVAL;
goto err_invalid_fmt;
}
- inst->fmts[fmt->type] = fmt;
rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
if (rc) {
dprintk(VIDC_ERR, "Failed to open instance\n");
@@ -1330,14 +1345,15 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
frame_sz.height);
msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz);
- max_input_size = get_frame_size(inst, fmt, f->type, 0);
+ max_input_size = get_frame_size(
+inst, &inst->fmts[fmt->type], f->type, 0);
if (f->fmt.pix_mp.plane_fmt[0].sizeimage > max_input_size ||
!f->fmt.pix_mp.plane_fmt[0].sizeimage) {
f->fmt.pix_mp.plane_fmt[0].sizeimage = max_input_size;
}
- f->fmt.pix_mp.num_planes = fmt->num_planes;
- for (i = 0; i < fmt->num_planes; ++i) {
+ f->fmt.pix_mp.num_planes = inst->fmts[fmt->type].num_planes;
+ for (i = 0; i < inst->fmts[fmt->type].num_planes; ++i) {
inst->bufq[OUTPUT_PORT].vb2_bufq.plane_sizes[i] =
f->fmt.pix_mp.plane_fmt[i].sizeimage;
}
@@ -1451,20 +1467,20 @@ static int msm_vdec_queue_setup(struct vb2_queue *q,
switch (q->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- *num_planes = inst->fmts[OUTPUT_PORT]->num_planes;
+ *num_planes = inst->fmts[OUTPUT_PORT].num_planes;
if (*num_buffers < MIN_NUM_OUTPUT_BUFFERS ||
*num_buffers > MAX_NUM_OUTPUT_BUFFERS)
*num_buffers = MIN_NUM_OUTPUT_BUFFERS;
for (i = 0; i < *num_planes; i++) {
sizes[i] = get_frame_size(inst,
- inst->fmts[OUTPUT_PORT], q->type, i);
+ &inst->fmts[OUTPUT_PORT], q->type, i);
}
rc = set_actual_buffer_count(inst, *num_buffers,
HAL_BUFFER_INPUT);
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
dprintk(VIDC_DBG, "Getting bufreqs on capture plane\n");
- *num_planes = inst->fmts[CAPTURE_PORT]->num_planes;
+ *num_planes = inst->fmts[CAPTURE_PORT].num_planes;
rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
if (rc) {
dprintk(VIDC_ERR, "Failed to open instance\n");
@@ -1549,7 +1565,7 @@ static int msm_vdec_queue_setup(struct vb2_queue *q,
}
extra_idx =
- EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
+ EXTRADATA_IDX(inst->fmts[CAPTURE_PORT].num_planes);
if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
sizes[extra_idx] =
VENUS_EXTRADATA_SIZE(
@@ -1650,7 +1666,7 @@ static inline int start_streaming(struct msm_vidc_inst *inst)
unsigned int buffer_size;
struct msm_vidc_format *fmt = NULL;
- fmt = inst->fmts[CAPTURE_PORT];
+ fmt = &inst->fmts[CAPTURE_PORT];
buffer_size = fmt->get_frame_size(0,
inst->prop.height[CAPTURE_PORT],
inst->prop.width[CAPTURE_PORT]);
@@ -1872,8 +1888,6 @@ int msm_vdec_inst_init(struct msm_vidc_inst *inst)
dprintk(VIDC_ERR, "Invalid input = %pK\n", inst);
return -EINVAL;
}
- inst->fmts[OUTPUT_PORT] = &vdec_formats[2];
- inst->fmts[CAPTURE_PORT] = &vdec_formats[0];
inst->prop.height[CAPTURE_PORT] = DEFAULT_HEIGHT;
inst->prop.width[CAPTURE_PORT] = DEFAULT_WIDTH;
inst->prop.height[OUTPUT_PORT] = DEFAULT_HEIGHT;
@@ -1889,6 +1903,10 @@ int msm_vdec_inst_init(struct msm_vidc_inst *inst)
inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_STATIC;
inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_STATIC;
inst->prop.fps = DEFAULT_FPS;
+ memcpy(&inst->fmts[OUTPUT_PORT], &vdec_formats[2],
+ sizeof(struct msm_vidc_format));
+ memcpy(&inst->fmts[CAPTURE_PORT], &vdec_formats[0],
+ sizeof(struct msm_vidc_format));
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 99f30d9cb97b..9c855e89e3ff 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1485,7 +1485,7 @@ static int msm_venc_queue_setup(struct vb2_queue *q,
default:
break;
}
- inst->fmts[CAPTURE_PORT]->num_planes = *num_planes;
+ inst->fmts[CAPTURE_PORT].num_planes = *num_planes;
for (i = 0; i < *num_planes; i++) {
int extra_idx = EXTRADATA_IDX(*num_planes);
@@ -1560,7 +1560,7 @@ static int msm_venc_queue_setup(struct vb2_queue *q,
break;
}
- inst->fmts[OUTPUT_PORT]->num_planes = *num_planes;
+ inst->fmts[OUTPUT_PORT].num_planes = *num_planes;
rc = call_hfi_op(hdev, session_set_property, inst->session,
property_id, &new_buf_count);
if (rc)
@@ -1570,12 +1570,12 @@ static int msm_venc_queue_setup(struct vb2_queue *q,
inst->buff_req.buffer[0].buffer_size,
inst->buff_req.buffer[0].buffer_alignment,
inst->buff_req.buffer[0].buffer_count_actual);
- sizes[0] = inst->fmts[OUTPUT_PORT]->get_frame_size(
+ sizes[0] = inst->fmts[OUTPUT_PORT].get_frame_size(
0, inst->prop.height[OUTPUT_PORT],
inst->prop.width[OUTPUT_PORT]);
extra_idx =
- EXTRADATA_IDX(inst->fmts[OUTPUT_PORT]->num_planes);
+ EXTRADATA_IDX(inst->fmts[OUTPUT_PORT].num_planes);
if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
buff_req_buffer = get_buff_req_buffer(inst,
HAL_BUFFER_EXTRADATA_INPUT);
@@ -1610,7 +1610,7 @@ static int msm_venc_toggle_hier_p(struct msm_vidc_inst *inst, int layers)
return -EINVAL;
}
- if (inst->fmts[CAPTURE_PORT]->fourcc != V4L2_PIX_FMT_VP8)
+ if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_VP8)
return 0;
num_enh_layers = layers ? : 0;
@@ -2177,10 +2177,10 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD:
- if (inst->fmts[CAPTURE_PORT]->fourcc != V4L2_PIX_FMT_H264 &&
- inst->fmts[CAPTURE_PORT]->fourcc !=
+ if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_H264 &&
+ inst->fmts[CAPTURE_PORT].fourcc !=
V4L2_PIX_FMT_H264_NO_SC &&
- inst->fmts[CAPTURE_PORT]->fourcc !=
+ inst->fmts[CAPTURE_PORT].fourcc !=
V4L2_PIX_FMT_HEVC) {
dprintk(VIDC_ERR,
"Control %#x only valid for H264 and HEVC\n",
@@ -2669,8 +2669,8 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE: {
bool codec_avc =
- inst->fmts[CAPTURE_PORT]->fourcc == V4L2_PIX_FMT_H264 ||
- inst->fmts[CAPTURE_PORT]->fourcc ==
+ inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_H264 ||
+ inst->fmts[CAPTURE_PORT].fourcc ==
V4L2_PIX_FMT_H264_NO_SC;
temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE);
@@ -2696,8 +2696,8 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
cir_mbs = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS);
is_cont_intra_supported =
- (inst->fmts[CAPTURE_PORT]->fourcc == V4L2_PIX_FMT_H264) ||
- (inst->fmts[CAPTURE_PORT]->fourcc == V4L2_PIX_FMT_HEVC);
+ (inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_H264) ||
+ (inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_HEVC);
if (is_cont_intra_supported) {
if (ctrl->val != HAL_INTRA_REFRESH_NONE)
@@ -3054,7 +3054,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS:
- if (inst->fmts[CAPTURE_PORT]->fourcc != V4L2_PIX_FMT_HEVC) {
+ if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC) {
dprintk(VIDC_ERR, "Hier B supported for HEVC only\n");
rc = -ENOTSUPP;
break;
@@ -3483,8 +3483,6 @@ int msm_venc_inst_init(struct msm_vidc_inst *inst)
dprintk(VIDC_ERR, "Invalid input = %pK\n", inst);
return -EINVAL;
}
- inst->fmts[CAPTURE_PORT] = &venc_formats[4];
- inst->fmts[OUTPUT_PORT] = &venc_formats[0];
inst->prop.height[CAPTURE_PORT] = DEFAULT_HEIGHT;
inst->prop.width[CAPTURE_PORT] = DEFAULT_WIDTH;
inst->prop.height[OUTPUT_PORT] = DEFAULT_HEIGHT;
@@ -3501,6 +3499,10 @@ int msm_venc_inst_init(struct msm_vidc_inst *inst)
inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_STATIC;
inst->prop.fps = DEFAULT_FPS;
inst->capability.pixelprocess_capabilities = 0;
+ memcpy(&inst->fmts[CAPTURE_PORT], &venc_formats[4],
+ sizeof(struct msm_vidc_format));
+ memcpy(&inst->fmts[OUTPUT_PORT], &venc_formats[0],
+ sizeof(struct msm_vidc_format));
return rc;
}
@@ -3624,7 +3626,8 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
goto exit;
}
- inst->fmts[fmt->type] = fmt;
+ memcpy(&inst->fmts[fmt->type], fmt,
+ sizeof(struct msm_vidc_format));
rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
if (rc) {
@@ -3676,7 +3679,8 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
rc = -EINVAL;
goto exit;
}
- inst->fmts[fmt->type] = fmt;
+ memcpy(&inst->fmts[fmt->type], fmt,
+ sizeof(struct msm_vidc_format));
msm_comm_set_color_format(inst, HAL_BUFFER_INPUT, fmt->fourcc);
} else {
@@ -3686,7 +3690,7 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
goto exit;
}
- f->fmt.pix_mp.num_planes = fmt->num_planes;
+ f->fmt.pix_mp.num_planes = inst->fmts[fmt->type].num_planes;
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
struct hal_frame_size frame_sz = {0};
@@ -3717,12 +3721,12 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
struct hal_buffer_requirements *bufreq = NULL;
int extra_idx = 0;
- for (i = 0; i < fmt->num_planes; ++i) {
+ for (i = 0; i < inst->fmts[fmt->type].num_planes; ++i) {
f->fmt.pix_mp.plane_fmt[i].sizeimage =
- fmt->get_frame_size(i,
+ inst->fmts[fmt->type].get_frame_size(i,
f->fmt.pix_mp.height, f->fmt.pix_mp.width);
}
- extra_idx = EXTRADATA_IDX(fmt->num_planes);
+ extra_idx = EXTRADATA_IDX(inst->fmts[fmt->type].num_planes);
if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
bufreq = get_buff_req_buffer(inst,
HAL_BUFFER_EXTRADATA_INPUT);
@@ -3757,11 +3761,11 @@ int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
}
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- fmt = inst->fmts[CAPTURE_PORT];
+ fmt = &inst->fmts[CAPTURE_PORT];
height = inst->prop.height[CAPTURE_PORT];
width = inst->prop.width[CAPTURE_PORT];
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- fmt = inst->fmts[OUTPUT_PORT];
+ fmt = &inst->fmts[OUTPUT_PORT];
height = inst->prop.height[OUTPUT_PORT];
width = inst->prop.width[OUTPUT_PORT];
} else {
@@ -3864,10 +3868,10 @@ int msm_venc_prepare_buf(struct msm_vidc_inst *inst,
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (b->length != inst->fmts[CAPTURE_PORT]->num_planes) {
+ if (b->length != inst->fmts[CAPTURE_PORT].num_planes) {
dprintk(VIDC_ERR,
"Planes mismatch: needed: %d, allocated: %d\n",
- inst->fmts[CAPTURE_PORT]->num_planes,
+ inst->fmts[CAPTURE_PORT].num_planes,
b->length);
rc = -EINVAL;
break;
@@ -3935,10 +3939,10 @@ int msm_venc_release_buf(struct msm_vidc_inst *inst,
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: {
if (b->length !=
- inst->fmts[CAPTURE_PORT]->num_planes) {
+ inst->fmts[CAPTURE_PORT].num_planes) {
dprintk(VIDC_ERR,
"Planes mismatch: needed: %d, to release: %d\n",
- inst->fmts[CAPTURE_PORT]->num_planes,
+ inst->fmts[CAPTURE_PORT].num_planes,
b->length);
rc = -EINVAL;
break;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 437ad43e23e9..b12eeddc678f 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -682,7 +682,7 @@ static bool valid_v4l2_buffer(struct v4l2_buffer *b,
MAX_PORT_NUM;
return port != MAX_PORT_NUM &&
- inst->fmts[port]->num_planes == b->length;
+ inst->fmts[port].num_planes == b->length;
}
int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b)
@@ -849,7 +849,7 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b)
dprintk(VIDC_DBG, "Queueing device address = %pa\n",
&binfo->device_addr[i]);
- if (inst->fmts[OUTPUT_PORT]->fourcc ==
+ if (inst->fmts[OUTPUT_PORT].fourcc ==
V4L2_PIX_FMT_HEVC_HYBRID && binfo->handle[i] &&
b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
rc = msm_comm_smem_cache_operations(inst,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index f0a3875a8f28..d1cc08d53017 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -521,12 +521,12 @@ static int msm_comm_vote_bus(struct msm_vidc_core *core)
struct v4l2_control ctrl;
codec = inst->session_type == MSM_VIDC_DECODER ?
- inst->fmts[OUTPUT_PORT]->fourcc :
- inst->fmts[CAPTURE_PORT]->fourcc;
+ inst->fmts[OUTPUT_PORT].fourcc :
+ inst->fmts[CAPTURE_PORT].fourcc;
yuv = inst->session_type == MSM_VIDC_DECODER ?
- inst->fmts[CAPTURE_PORT]->fourcc :
- inst->fmts[OUTPUT_PORT]->fourcc;
+ inst->fmts[CAPTURE_PORT].fourcc :
+ inst->fmts[OUTPUT_PORT].fourcc;
vote_data[i].domain = get_hal_domain(inst->session_type);
vote_data[i].codec = get_hal_codec(codec);
@@ -1004,8 +1004,8 @@ static void handle_session_init_done(enum hal_command_response cmd, void *data)
core = inst->core;
hdev = inst->core->device;
codec = inst->session_type == MSM_VIDC_DECODER ?
- inst->fmts[OUTPUT_PORT]->fourcc :
- inst->fmts[CAPTURE_PORT]->fourcc;
+ inst->fmts[OUTPUT_PORT].fourcc :
+ inst->fmts[CAPTURE_PORT].fourcc;
/* check if capabilities are available for this session */
for (i = 0; i < VIDC_MAX_SESSIONS; i++) {
@@ -1737,19 +1737,6 @@ static struct vb2_buffer *get_vb_from_device_addr(struct buf_queue *bufq,
return vb;
}
-static void msm_vidc_try_suspend(struct msm_vidc_inst *inst)
-{
- bool batch_mode;
-
- batch_mode = msm_comm_g_ctrl_for_id(inst, V4L2_CID_VIDC_QBUF_MODE)
- == V4L2_VIDC_QBUF_BATCHED;
- if (batch_mode) {
- dprintk(VIDC_DBG,
- "Trying to suspend Venus after finishing Batch\n");
- msm_comm_suspend(inst->core->id);
- }
-}
-
static void handle_ebd(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_data_done *response = data;
@@ -1821,8 +1808,6 @@ static void handle_ebd(enum hal_command_response cmd, void *data)
msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD);
}
- msm_vidc_try_suspend(inst);
-
put_inst(inst);
}
@@ -2043,7 +2028,7 @@ static void handle_fbd(enum hal_command_response cmd, void *data)
ns_to_timeval(time_usec * NSEC_PER_USEC);
vbuf->flags = 0;
extra_idx =
- EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
+ EXTRADATA_IDX(inst->fmts[CAPTURE_PORT].num_planes);
if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
vb->planes[extra_idx].m.userptr =
(unsigned long)fill_buf_done->extra_data_buffer;
@@ -2121,7 +2106,6 @@ static void handle_fbd(enum hal_command_response cmd, void *data)
msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD);
}
- msm_vidc_try_suspend(inst);
err_handle_fbd:
put_inst(inst);
}
@@ -2295,8 +2279,8 @@ int msm_comm_scale_clocks_load(struct msm_vidc_core *core,
list_for_each_entry(inst, &core->instances, list) {
codec = inst->session_type == MSM_VIDC_DECODER ?
- inst->fmts[OUTPUT_PORT]->fourcc :
- inst->fmts[CAPTURE_PORT]->fourcc;
+ inst->fmts[OUTPUT_PORT].fourcc :
+ inst->fmts[CAPTURE_PORT].fourcc;
if (msm_comm_turbo_session(inst))
clk_scale_data.power_mode[num_sessions] =
@@ -2727,9 +2711,9 @@ static int msm_comm_session_init(int flipped_state,
goto exit;
}
if (inst->session_type == MSM_VIDC_DECODER) {
- fourcc = inst->fmts[OUTPUT_PORT]->fourcc;
+ fourcc = inst->fmts[OUTPUT_PORT].fourcc;
} else if (inst->session_type == MSM_VIDC_ENCODER) {
- fourcc = inst->fmts[CAPTURE_PORT]->fourcc;
+ fourcc = inst->fmts[CAPTURE_PORT].fourcc;
} else {
dprintk(VIDC_ERR, "Invalid session\n");
return -EINVAL;
@@ -3617,7 +3601,7 @@ static void populate_frame_data(struct vidc_frame_data *data,
data->buffer_type = msm_comm_get_hal_output_buffer(inst);
}
- extra_idx = EXTRADATA_IDX(inst->fmts[port]->num_planes);
+ extra_idx = EXTRADATA_IDX(inst->fmts[port].num_planes);
if (extra_idx && extra_idx < VIDEO_MAX_PLANES &&
vb->planes[extra_idx].m.userptr) {
data->extradata_addr = vb->planes[extra_idx].m.userptr;
@@ -4808,9 +4792,20 @@ int msm_vidc_trigger_ssr(struct msm_vidc_core *core,
return -EINVAL;
}
hdev = core->device;
- if (core->state == VIDC_CORE_INIT_DONE)
+ if (core->state == VIDC_CORE_INIT_DONE) {
+ /*
+ * In current implementation user-initiated SSR triggers
+ * a fatal error from hardware. However, there is no way
+ * to know if fatal error is due to SSR or not. Handle
+ * user SSR as non-fatal.
+ */
+ mutex_lock(&core->lock);
+ core->resources.debug_timeout = false;
+ mutex_unlock(&core->lock);
rc = call_hfi_op(hdev, core_trigger_ssr,
hdev->hfi_device_data, type);
+ }
+
return rc;
}
@@ -5281,7 +5276,7 @@ void msm_comm_print_inst_info(struct msm_vidc_inst *inst)
port = is_decode ? OUTPUT_PORT : CAPTURE_PORT;
dprintk(VIDC_ERR,
"%s session, Codec type: %s HxW: %d x %d fps: %d bitrate: %d bit-depth: %s\n",
- is_decode ? "Decode" : "Encode", inst->fmts[port]->name,
+ is_decode ? "Decode" : "Encode", inst->fmts[port].name,
inst->prop.height[port], inst->prop.width[port],
inst->prop.fps, inst->prop.bitrate,
!inst->bit_depth ? "8" : "10");
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
index 9e67ef096c63..0e62811bf41b 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
@@ -237,8 +237,8 @@ void msm_dcvs_init_load(struct msm_vidc_inst *inst)
}
fourcc = inst->session_type == MSM_VIDC_DECODER ?
- inst->fmts[OUTPUT_PORT]->fourcc :
- inst->fmts[CAPTURE_PORT]->fourcc;
+ inst->fmts[OUTPUT_PORT].fourcc :
+ inst->fmts[CAPTURE_PORT].fourcc;
for (i = 0; i < num_rows; i++) {
bool matches = msm_dcvs_check_codec_supported(
@@ -589,7 +589,7 @@ static bool msm_dcvs_check_supported(struct msm_vidc_inst *inst)
}
is_codec_supported =
msm_dcvs_check_codec_supported(
- inst->fmts[OUTPUT_PORT]->fourcc,
+ inst->fmts[OUTPUT_PORT].fourcc,
inst->dcvs.supported_codecs,
inst->session_type);
if (!is_codec_supported ||
@@ -607,7 +607,7 @@ static bool msm_dcvs_check_supported(struct msm_vidc_inst *inst)
inst->dcvs.extra_buffer_count = DCVS_ENC_EXTRA_OUTPUT_BUFFERS;
is_codec_supported =
msm_dcvs_check_codec_supported(
- inst->fmts[CAPTURE_PORT]->fourcc,
+ inst->fmts[CAPTURE_PORT].fourcc,
inst->dcvs.supported_codecs,
inst->session_type);
if (!is_codec_supported ||
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index d3027c08d24e..efb90c69881f 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -289,10 +289,10 @@ static ssize_t inst_info_read(struct file *file, char __user *buf,
for (i = 0; i < MAX_PORT_NUM; i++) {
write_str(&dbg_buf, "capability: %s\n", i == OUTPUT_PORT ?
"Output" : "Capture");
- write_str(&dbg_buf, "name : %s\n", inst->fmts[i]->name);
- write_str(&dbg_buf, "planes : %d\n", inst->fmts[i]->num_planes);
+ write_str(&dbg_buf, "name : %s\n", inst->fmts[i].name);
+ write_str(&dbg_buf, "planes : %d\n", inst->fmts[i].num_planes);
write_str(
- &dbg_buf, "type: %s\n", inst->fmts[i]->type == OUTPUT_PORT ?
+ &dbg_buf, "type: %s\n", inst->fmts[i].type == OUTPUT_PORT ?
"Output" : "Capture");
switch (inst->buffer_mode_set[i]) {
case HAL_BUFFER_MODE_STATIC:
@@ -311,7 +311,7 @@ static ssize_t inst_info_read(struct file *file, char __user *buf,
write_str(&dbg_buf, "count: %u\n",
inst->bufq[i].vb2_bufq.num_buffers);
- for (j = 0; j < inst->fmts[i]->num_planes; j++)
+ for (j = 0; j < inst->fmts[i].num_planes; j++)
write_str(&dbg_buf, "size for plane %d: %u\n", j,
inst->bufq[i].vb2_bufq.plane_sizes[j]);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index b6e74715ad07..161e94f99040 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -261,7 +261,7 @@ struct msm_vidc_inst {
void *session;
struct session_prop prop;
enum instance_state state;
- struct msm_vidc_format *fmts[MAX_PORT_NUM];
+ struct msm_vidc_format fmts[MAX_PORT_NUM];
struct buf_queue bufq[MAX_PORT_NUM];
struct msm_vidc_list pendingq;
struct msm_vidc_list scratchbufs;
diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c
index b102264ca8fd..02ddf3225af8 100644
--- a/drivers/mfd/wcd934x-regmap.c
+++ b/drivers/mfd/wcd934x-regmap.c
@@ -1872,6 +1872,10 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)
case WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1:
case WCD934X_CPE_SS_CPAR_CTL:
case WCD934X_CPE_SS_STATUS:
+ case WCD934X_CODEC_RPM_RST_CTL:
+ case WCD934X_SIDO_NEW_VOUT_A_STARTUP:
+ case WCD934X_SIDO_NEW_VOUT_D_STARTUP:
+ case WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL:
return true;
}
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 981d372277ee..5bcd0db929b3 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -1219,6 +1219,11 @@ static int wcd9xxx_slim_probe(struct slim_device *slim)
intf_type = wcd9xxx_get_intf_type();
+ if (!slim) {
+ ret = -EINVAL;
+ goto err;
+ }
+
if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
dev_dbg(&slim->dev, "%s:Codec is detected in I2C mode\n",
__func__);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
index f7ad8f61f2e7..0c44f79549d4 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
@@ -692,7 +692,7 @@ static int audio_aio_events_pending(struct q6audio_aio *audio)
spin_lock_irqsave(&audio->event_queue_lock, flags);
empty = !list_empty(&audio->event_queue);
spin_unlock_irqrestore(&audio->event_queue_lock, flags);
- return empty || audio->event_abort;
+ return empty || audio->event_abort || audio->reset_event;
}
static long audio_aio_process_event_req_common(struct q6audio_aio *audio,
@@ -720,6 +720,12 @@ static long audio_aio_process_event_req_common(struct q6audio_aio *audio,
if (rc < 0)
return rc;
+ if (audio->reset_event) {
+ audio->reset_event = false;
+ pr_err("In SSR, post ENETRESET err\n");
+ return -ENETRESET;
+ }
+
if (audio->event_abort) {
audio->event_abort = 0;
return -ENODEV;
@@ -1327,6 +1333,7 @@ int audio_aio_open(struct q6audio_aio *audio, struct file *file)
audio->drv_ops.out_flush(audio);
audio->opened = 1;
+ audio->reset_event = false;
file->private_data = audio;
audio->codec_ioctl = audio_aio_ioctl;
audio->codec_compat_ioctl = audio_aio_compat_ioctl;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h
index d4a7c7e5483b..9c53f58b746f 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h
@@ -1,6 +1,6 @@
/* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-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
@@ -199,6 +199,7 @@ struct q6audio_aio {
int feedback;
int rflush; /* Read flush */
int wflush; /* Write flush */
+ bool reset_event;
long (*codec_ioctl)(struct file *, unsigned int, unsigned long);
long (*codec_compat_ioctl)(struct file *, unsigned int, unsigned long);
};
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
index f9534b2bfa7f..6e82c8051886 100644
--- a/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -107,8 +107,9 @@ void audio_aio_cb(uint32_t opcode, uint32_t token,
audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
break;
case RESET_EVENTS:
- pr_debug("%s: Received opcode:0x%x\n", __func__, opcode);
+ pr_err("%s: Received opcode:0x%x\n", __func__, opcode);
audio->stopped = 1;
+ audio->reset_event = true;
wake_up(&audio->event_wait);
break;
default:
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index bd554667de96..644178a0cdfc 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -7146,8 +7146,10 @@ static int qseecom_open(struct inode *inode, struct file *file)
atomic_set(&data->ioctl_count, 0);
data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL);
- if (!(data->sglistinfo_ptr))
+ if (!(data->sglistinfo_ptr)) {
+ kzfree(data);
return -ENOMEM;
+ }
return ret;
}
@@ -8011,8 +8013,10 @@ static int qseecom_check_whitelist_feature(void)
qseecom.whitelist_support = true;
ret = 0;
} else {
- pr_err("Failed to check whitelist: ret = %d, result = 0x%x\n",
+ pr_info("Check whitelist with ret = %d, result = 0x%x\n",
ret, resp.result);
+ qseecom.whitelist_support = false;
+ ret = 0;
}
kfree(buf);
return ret;
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 8c2bb77db049..b11fe09552bf 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -945,8 +945,6 @@ EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting);
static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
{
- int ret;
-
/* MBIM backwards compatible function? */
if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)
return -ENODEV;
@@ -955,16 +953,7 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
* Additionally, generic NCM devices are assumed to accept arbitrarily
* placed NDP.
*/
- ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM, 0);
-
- /*
- * We should get an event when network connection is "connected" or
- * "disconnected". Set network connection in "disconnected" state
- * (carrier is OFF) during attach, so the IP network stack does not
- * start IPv6 negotiation and more.
- */
- usbnet_link_change(dev, 0, 0);
- return ret;
+ return cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM, 0);
}
static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remainder, size_t max)
@@ -1547,7 +1536,8 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
static const struct driver_info cdc_ncm_info = {
.description = "CDC NCM",
- .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET,
+ .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET
+ | FLAG_LINK_INTR,
.bind = cdc_ncm_bind,
.unbind = cdc_ncm_unbind,
.manage_power = usbnet_manage_power,
@@ -1560,7 +1550,7 @@ static const struct driver_info cdc_ncm_info = {
static const struct driver_info wwan_info = {
.description = "Mobile Broadband Network Device",
.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET
- | FLAG_WWAN,
+ | FLAG_LINK_INTR | FLAG_WWAN,
.bind = cdc_ncm_bind,
.unbind = cdc_ncm_unbind,
.manage_power = usbnet_manage_power,
@@ -1573,7 +1563,7 @@ static const struct driver_info wwan_info = {
static const struct driver_info wwan_noarp_info = {
.description = "Mobile Broadband Network Device (NO ARP)",
.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET
- | FLAG_WWAN | FLAG_NOARP,
+ | FLAG_LINK_INTR | FLAG_WWAN | FLAG_NOARP,
.bind = cdc_ncm_bind,
.unbind = cdc_ncm_unbind,
.manage_power = usbnet_manage_power,
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
index 81651c7dec72..a0f76581e6eb 100644
--- a/drivers/net/wireless/ath/wil6210/Makefile
+++ b/drivers/net/wireless/ath/wil6210/Makefile
@@ -19,6 +19,7 @@ wil6210-y += wil_platform.o
wil6210-y += ethtool.o
wil6210-y += wil_crash_dump.o
wil6210-y += p2p.o
+wil6210-y += ftm.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 18e95148e891..17b419d408cd 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -17,6 +17,7 @@
#include <linux/etherdevice.h>
#include "wil6210.h"
#include "wmi.h"
+#include "ftm.h"
#define WIL_MAX_ROC_DURATION_MS 5000
@@ -36,6 +37,90 @@ static struct ieee80211_channel wil_60ghz_channels[] = {
/* channel 4 not supported yet */
};
+/* Vendor id to be used in vendor specific command and events
+ * to user space.
+ * NOTE: The authoritative place for definition of QCA_NL80211_VENDOR_ID,
+ * vendor subcmd definitions prefixed with QCA_NL80211_VENDOR_SUBCMD, and
+ * qca_wlan_vendor_attr is open source file src/common/qca-vendor.h in
+ * git://w1.fi/srv/git/hostap.git; the values here are just a copy of that
+ */
+
+#define QCA_NL80211_VENDOR_ID 0x001374
+
+enum qca_nl80211_vendor_subcmds {
+ QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128,
+ QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129,
+ QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION = 130,
+ QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT = 131,
+ QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE = 132,
+ QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER = 133,
+ QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134,
+ QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135,
+ QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136,
+};
+
+/* vendor specific commands */
+static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = {
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_ftm_get_capabilities
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_ftm_start_session
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_ftm_abort_session
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_ftm_configure_responder
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_aoa_start_measurement
+ },
+ {
+ .info.vendor_id = QCA_NL80211_VENDOR_ID,
+ .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS,
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = wil_aoa_abort_measurement
+ },
+};
+
+/* vendor specific events */
+static const struct nl80211_vendor_cmd_info wil_nl80211_vendor_events[] = {
+ [QCA_NL80211_VENDOR_EVENT_FTM_MEAS_RESULT_INDEX] = {
+ .vendor_id = QCA_NL80211_VENDOR_ID,
+ .subcmd = QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT
+ },
+ [QCA_NL80211_VENDOR_EVENT_FTM_SESSION_DONE_INDEX] = {
+ .vendor_id = QCA_NL80211_VENDOR_ID,
+ .subcmd = QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE
+ },
+ [QCA_NL80211_VENDOR_EVENT_AOA_MEAS_RESULT_INDEX] = {
+ .vendor_id = QCA_NL80211_VENDOR_ID,
+ .subcmd = QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT
+ },
+};
+
static struct ieee80211_supported_band wil_band_60ghz = {
.channels = wil_60ghz_channels,
.n_channels = ARRAY_SIZE(wil_60ghz_channels),
@@ -354,10 +439,13 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
wil_dbg_misc(wil, "%s(), wdev=0x%p iftype=%d\n",
__func__, wdev, wdev->iftype);
+ mutex_lock(&wil->p2p_wdev_mutex);
if (wil->scan_request) {
wil_err(wil, "Already scanning\n");
+ mutex_unlock(&wil->p2p_wdev_mutex);
return -EAGAIN;
}
+ mutex_unlock(&wil->p2p_wdev_mutex);
/* check we are client side */
switch (wdev->iftype) {
@@ -760,14 +848,11 @@ static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil,
return rc;
}
-static struct wil_tid_crypto_rx_single *
-wil_find_crypto_ctx(struct wil6210_priv *wil, u8 key_index,
- enum wmi_key_usage key_usage, const u8 *mac_addr)
+static struct wil_sta_info *
+wil_find_sta_by_key_usage(struct wil6210_priv *wil,
+ enum wmi_key_usage key_usage, const u8 *mac_addr)
{
int cid = -EINVAL;
- int tid = 0;
- struct wil_sta_info *s;
- struct wil_tid_crypto_rx *c;
if (key_usage == WMI_KEY_USE_TX_GROUP)
return NULL; /* not needed */
@@ -778,18 +863,72 @@ wil_find_crypto_ctx(struct wil6210_priv *wil, u8 key_index,
else if (key_usage == WMI_KEY_USE_RX_GROUP)
cid = wil_find_cid_by_idx(wil, 0);
if (cid < 0) {
- wil_err(wil, "No CID for %pM %s[%d]\n", mac_addr,
- key_usage_str[key_usage], key_index);
+ wil_err(wil, "No CID for %pM %s\n", mac_addr,
+ key_usage_str[key_usage]);
return ERR_PTR(cid);
}
- s = &wil->sta[cid];
- if (key_usage == WMI_KEY_USE_PAIRWISE)
- c = &s->tid_crypto_rx[tid];
- else
- c = &s->group_crypto_rx;
+ return &wil->sta[cid];
+}
+
+static void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage,
+ struct wil_sta_info *cs,
+ struct key_params *params)
+{
+ struct wil_tid_crypto_rx_single *cc;
+ int tid;
- return &c->key_id[key_index];
+ if (!cs)
+ return;
+
+ switch (key_usage) {
+ case WMI_KEY_USE_PAIRWISE:
+ for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
+ cc = &cs->tid_crypto_rx[tid].key_id[key_index];
+ if (params->seq)
+ memcpy(cc->pn, params->seq,
+ IEEE80211_GCMP_PN_LEN);
+ else
+ memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
+ cc->key_set = true;
+ }
+ break;
+ case WMI_KEY_USE_RX_GROUP:
+ cc = &cs->group_crypto_rx.key_id[key_index];
+ if (params->seq)
+ memcpy(cc->pn, params->seq, IEEE80211_GCMP_PN_LEN);
+ else
+ memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
+ cc->key_set = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void wil_del_rx_key(u8 key_index, enum wmi_key_usage key_usage,
+ struct wil_sta_info *cs)
+{
+ struct wil_tid_crypto_rx_single *cc;
+ int tid;
+
+ if (!cs)
+ return;
+
+ switch (key_usage) {
+ case WMI_KEY_USE_PAIRWISE:
+ for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
+ cc = &cs->tid_crypto_rx[tid].key_id[key_index];
+ cc->key_set = false;
+ }
+ break;
+ case WMI_KEY_USE_RX_GROUP:
+ cc = &cs->group_crypto_rx.key_id[key_index];
+ cc->key_set = false;
+ break;
+ default:
+ break;
+ }
}
static int wil_cfg80211_add_key(struct wiphy *wiphy,
@@ -801,24 +940,26 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
int rc;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
- struct wil_tid_crypto_rx_single *cc = wil_find_crypto_ctx(wil,
- key_index,
- key_usage,
- mac_addr);
+ struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, key_usage,
+ mac_addr);
+
+ if (!params) {
+ wil_err(wil, "NULL params\n");
+ return -EINVAL;
+ }
wil_dbg_misc(wil, "%s(%pM %s[%d] PN %*phN)\n", __func__,
mac_addr, key_usage_str[key_usage], key_index,
params->seq_len, params->seq);
- if (IS_ERR(cc)) {
+ if (IS_ERR(cs)) {
wil_err(wil, "Not connected, %s(%pM %s[%d] PN %*phN)\n",
__func__, mac_addr, key_usage_str[key_usage], key_index,
params->seq_len, params->seq);
return -EINVAL;
}
- if (cc)
- cc->key_set = false;
+ wil_del_rx_key(key_index, key_usage, cs);
if (params->seq && params->seq_len != IEEE80211_GCMP_PN_LEN) {
wil_err(wil,
@@ -831,13 +972,8 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
rc = wmi_add_cipher_key(wil, key_index, mac_addr, params->key_len,
params->key, key_usage);
- if ((rc == 0) && cc) {
- if (params->seq)
- memcpy(cc->pn, params->seq, IEEE80211_GCMP_PN_LEN);
- else
- memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
- cc->key_set = true;
- }
+ if (!rc)
+ wil_set_crypto_rx(key_index, key_usage, cs, params);
return rc;
}
@@ -849,20 +985,18 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy,
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
- struct wil_tid_crypto_rx_single *cc = wil_find_crypto_ctx(wil,
- key_index,
- key_usage,
- mac_addr);
+ struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, key_usage,
+ mac_addr);
wil_dbg_misc(wil, "%s(%pM %s[%d])\n", __func__, mac_addr,
key_usage_str[key_usage], key_index);
- if (IS_ERR(cc))
+ if (IS_ERR(cs))
wil_info(wil, "Not connected, %s(%pM %s[%d])\n", __func__,
mac_addr, key_usage_str[key_usage], key_index);
- if (!IS_ERR_OR_NULL(cc))
- cc->key_set = false;
+ if (!IS_ERR_OR_NULL(cs))
+ wil_del_rx_key(key_index, key_usage, cs);
return wmi_del_cipher_key(wil, key_index, mac_addr, key_usage);
}
@@ -1363,19 +1497,16 @@ static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- u8 started;
+ struct wil_p2p_info *p2p = &wil->p2p;
+
+ if (!p2p->p2p_dev_started)
+ return;
wil_dbg_misc(wil, "%s: entered\n", __func__);
mutex_lock(&wil->mutex);
- started = wil_p2p_stop_discovery(wil);
- if (started && wil->scan_request) {
- cfg80211_scan_done(wil->scan_request, 1);
- wil->scan_request = NULL;
- wil->radio_wdev = wil->wdev;
- }
+ wil_p2p_stop_radio_operations(wil);
+ p2p->p2p_dev_started = 0;
mutex_unlock(&wil->mutex);
-
- wil->p2p.p2p_dev_started = 0;
}
static struct cfg80211_ops wil_cfg80211_ops = {
@@ -1437,6 +1568,11 @@ static void wil_wiphy_init(struct wiphy *wiphy)
wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
wiphy->mgmt_stypes = wil_mgmt_stypes;
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
+
+ wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands);
+ wiphy->vendor_commands = wil_nl80211_vendor_commands;
+ wiphy->vendor_events = wil_nl80211_vendor_events;
+ wiphy->n_vendor_events = ARRAY_SIZE(wil_nl80211_vendor_events);
}
struct wireless_dev *wil_cfg80211_init(struct device *dev)
@@ -1460,14 +1596,8 @@ struct wireless_dev *wil_cfg80211_init(struct device *dev)
set_wiphy_dev(wdev->wiphy, dev);
wil_wiphy_init(wdev->wiphy);
- rc = wiphy_register(wdev->wiphy);
- if (rc < 0)
- goto out_failed_reg;
-
return wdev;
-out_failed_reg:
- wiphy_free(wdev->wiphy);
out:
kfree(wdev);
@@ -1483,7 +1613,6 @@ void wil_wdev_free(struct wil6210_priv *wil)
if (!wdev)
return;
- wiphy_unregister(wdev->wiphy);
wiphy_free(wdev->wiphy);
kfree(wdev);
}
@@ -1494,11 +1623,11 @@ void wil_p2p_wdev_free(struct wil6210_priv *wil)
mutex_lock(&wil->p2p_wdev_mutex);
p2p_wdev = wil->p2p_wdev;
+ wil->p2p_wdev = NULL;
+ wil->radio_wdev = wil_to_wdev(wil);
+ mutex_unlock(&wil->p2p_wdev_mutex);
if (p2p_wdev) {
- wil->p2p_wdev = NULL;
- wil->radio_wdev = wil_to_wdev(wil);
cfg80211_unregister_wdev(p2p_wdev);
kfree(p2p_wdev);
}
- mutex_unlock(&wil->p2p_wdev_mutex);
}
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index f8e36ba47ac7..d3e420f1b26b 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -1559,6 +1559,56 @@ static const struct file_operations fops_led_blink_time = {
.open = simple_open,
};
+/*---------FW capabilities------------*/
+static int wil_fw_capabilities_debugfs_show(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+
+ seq_printf(s, "fw_capabilities : %*pb\n", WMI_FW_CAPABILITY_MAX,
+ wil->fw_capabilities);
+
+ return 0;
+}
+
+static int wil_fw_capabilities_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_fw_capabilities_debugfs_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_fw_capabilities = {
+ .open = wil_fw_capabilities_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .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)
@@ -1609,6 +1659,8 @@ static const struct {
{"recovery", S_IRUGO | S_IWUSR, &fops_recovery},
{"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,
@@ -1649,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/ftm.c b/drivers/net/wireless/ath/wil6210/ftm.c
new file mode 100644
index 000000000000..5cf07343a33c
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/ftm.c
@@ -0,0 +1,903 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/etherdevice.h>
+#include <net/netlink.h>
+#include "wil6210.h"
+#include "ftm.h"
+#include "wmi.h"
+
+/* FTM session ID we use with FW */
+#define WIL_FTM_FW_SESSION_ID 1
+
+/* fixed spare allocation we reserve in NL messages we allocate */
+#define WIL_FTM_NL_EXTRA_ALLOC 32
+
+/* approx maximum length for FTM_MEAS_RESULT NL80211 event */
+#define WIL_FTM_MEAS_RESULT_MAX_LENGTH 2048
+
+/* timeout for waiting for standalone AOA measurement, milliseconds */
+#define WIL_AOA_MEASUREMENT_TIMEOUT 1000
+
+/* maximum number of allowed FTM measurements per burst */
+#define WIL_FTM_MAX_MEAS_PER_BURST 31
+
+/* initial token to use on non-secure FTM measurement */
+#define WIL_TOF_FTM_DEFAULT_INITIAL_TOKEN 2
+
+#define WIL_TOF_FTM_MAX_LCI_LENGTH (240)
+#define WIL_TOF_FTM_MAX_LCR_LENGTH (240)
+
+static const struct
+nla_policy wil_nl80211_loc_policy[QCA_WLAN_VENDOR_ATTR_LOC_MAX + 1] = {
+ [QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE] = { .type = NLA_U64 },
+ [QCA_WLAN_VENDOR_ATTR_LOC_CAPA] = { .type = NLA_NESTED },
+ [QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS] = { .type = NLA_NESTED },
+ [QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS] = { .type = NLA_NESTED },
+ [QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE] = { .type = NLA_FLAG },
+ [QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS] = { .type = NLA_U32 },
+ [QCA_WLAN_VENDOR_ATTR_FTM_INITIAL_TOKEN] = { .type = NLA_U8 },
+ [QCA_WLAN_VENDOR_ATTR_AOA_TYPE] = { .type = NLA_U32 },
+ [QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK] = { .type = NLA_U32 },
+};
+
+static const struct
+nla_policy wil_nl80211_ftm_peer_policy[
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX + 1] = {
+ [QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR] = { .len = ETH_ALEN },
+ [QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS] = { .type = NLA_U32 },
+ [QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS] = { .type = NLA_NESTED },
+ [QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID] = { .type = NLA_U8 },
+};
+
+static const struct
+nla_policy wil_nl80211_ftm_meas_param_policy[
+ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MAX + 1] = {
+ [QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST] = { .type = NLA_U8 },
+ [QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP] = { .type = NLA_U8 },
+ [QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION] = { .type = NLA_U8 },
+ [QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD] = { .type = NLA_U16 },
+};
+
+static int wil_ftm_parse_meas_params(struct wil6210_priv *wil,
+ struct nlattr *attr,
+ struct wil_ftm_meas_params *params)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MAX + 1];
+ int rc;
+
+ if (!attr) {
+ /* temporary defaults for one-shot measurement */
+ params->meas_per_burst = 1;
+ params->burst_period = 5; /* 500 milliseconds */
+ return 0;
+ }
+ rc = nla_parse_nested(tb, QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MAX,
+ attr, wil_nl80211_ftm_meas_param_policy);
+ if (rc) {
+ wil_err(wil, "invalid measurement params\n");
+ return rc;
+ }
+ if (tb[QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST])
+ params->meas_per_burst = nla_get_u8(
+ tb[QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP])
+ params->num_of_bursts_exp = nla_get_u8(
+ tb[QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION])
+ params->burst_duration = nla_get_u8(
+ tb[QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD])
+ params->burst_period = nla_get_u16(
+ tb[QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD]);
+ return 0;
+}
+
+static int wil_ftm_validate_meas_params(struct wil6210_priv *wil,
+ struct wil_ftm_meas_params *params)
+{
+ /* temporary allow only single-burst */
+ if (params->meas_per_burst > WIL_FTM_MAX_MEAS_PER_BURST ||
+ params->num_of_bursts_exp != 0) {
+ wil_err(wil, "invalid measurement params\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wil_ftm_append_meas_params(struct wil6210_priv *wil,
+ struct sk_buff *msg,
+ struct wil_ftm_meas_params *params)
+{
+ struct nlattr *nl_p;
+
+ nl_p = nla_nest_start(
+ msg, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS);
+ if (!nl_p)
+ goto out_put_failure;
+ if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST,
+ params->meas_per_burst) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP,
+ params->num_of_bursts_exp) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION,
+ params->burst_duration) ||
+ nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD,
+ params->burst_period))
+ goto out_put_failure;
+ nla_nest_end(msg, nl_p);
+ return 0;
+out_put_failure:
+ return -ENOBUFS;
+}
+
+static int wil_ftm_append_peer_meas_res(struct wil6210_priv *wil,
+ struct sk_buff *msg,
+ struct wil_ftm_peer_meas_res *res)
+{
+ struct nlattr *nl_mres, *nl_f;
+ int i;
+
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR,
+ ETH_ALEN, res->mac_addr) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS,
+ res->flags) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS,
+ res->status))
+ goto out_put_failure;
+ if (res->status == QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED &&
+ nla_put_u8(msg,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS,
+ res->value_seconds))
+ goto out_put_failure;
+ if (res->has_params &&
+ wil_ftm_append_meas_params(wil, msg, &res->params))
+ goto out_put_failure;
+ nl_mres = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS);
+ if (!nl_mres)
+ goto out_put_failure;
+ for (i = 0; i < res->n_meas; i++) {
+ nl_f = nla_nest_start(msg, i);
+ if (!nl_f)
+ goto out_put_failure;
+ if (nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1,
+ res->meas[i].t1) ||
+ nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2,
+ res->meas[i].t2) ||
+ nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3,
+ res->meas[i].t3) ||
+ nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4,
+ res->meas[i].t4))
+ goto out_put_failure;
+ nla_nest_end(msg, nl_f);
+ }
+ nla_nest_end(msg, nl_mres);
+ return 0;
+out_put_failure:
+ wil_err(wil, "fail to append peer result\n");
+ return -ENOBUFS;
+}
+
+static void wil_ftm_send_meas_result(struct wil6210_priv *wil,
+ struct wil_ftm_peer_meas_res *res)
+{
+ struct sk_buff *vendor_event = NULL;
+ struct nlattr *nl_res;
+ int rc = 0;
+
+ wil_dbg_misc(wil, "sending %d results for peer %pM\n",
+ res->n_meas, res->mac_addr);
+
+ vendor_event = cfg80211_vendor_event_alloc(
+ wil_to_wiphy(wil),
+ wil->wdev,
+ WIL_FTM_MEAS_RESULT_MAX_LENGTH,
+ QCA_NL80211_VENDOR_EVENT_FTM_MEAS_RESULT_INDEX,
+ GFP_KERNEL);
+ if (!vendor_event) {
+ wil_err(wil, "fail to allocate measurement result\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (nla_put_u64(
+ vendor_event, QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE,
+ wil->ftm.session_cookie)) {
+ rc = -ENOBUFS;
+ goto out;
+ }
+
+ nl_res = nla_nest_start(vendor_event,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS);
+ if (!nl_res) {
+ rc = -ENOBUFS;
+ goto out;
+ }
+
+ rc = wil_ftm_append_peer_meas_res(wil, vendor_event, res);
+ if (rc)
+ goto out;
+
+ nla_nest_end(vendor_event, nl_res);
+ cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+ vendor_event = NULL;
+out:
+ if (vendor_event)
+ kfree_skb(vendor_event);
+ if (rc)
+ wil_err(wil, "send peer result failed, err %d\n", rc);
+}
+
+static void wil_ftm_send_peer_res(struct wil6210_priv *wil)
+{
+ if (!wil->ftm.has_ftm_res || !wil->ftm.ftm_res)
+ return;
+
+ wil_ftm_send_meas_result(wil, wil->ftm.ftm_res);
+ wil->ftm.has_ftm_res = 0;
+ wil->ftm.ftm_res->n_meas = 0;
+}
+
+static void wil_aoa_measurement_timeout(struct work_struct *work)
+{
+ struct wil_ftm_priv *ftm = container_of(work, struct wil_ftm_priv,
+ aoa_timeout_work);
+ struct wil6210_priv *wil = container_of(ftm, struct wil6210_priv, ftm);
+ struct wil_aoa_meas_result res;
+
+ wil_dbg_misc(wil, "AOA measurement timeout\n");
+
+ memset(&res, 0, sizeof(res));
+ ether_addr_copy(res.mac_addr, wil->ftm.aoa_peer_mac_addr);
+ res.type = wil->ftm.aoa_type;
+ res.status = QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED;
+ wil_aoa_cfg80211_meas_result(wil, &res);
+}
+
+static int
+wil_ftm_cfg80211_start_session(struct wil6210_priv *wil,
+ struct wil_ftm_session_request *request)
+{
+ int rc = 0;
+ bool has_lci = false, has_lcr = false;
+ u8 max_meas = 0, *ptr;
+ u32 i, cmd_len;
+ struct wmi_tof_session_start_cmd *cmd;
+
+ mutex_lock(&wil->ftm.lock);
+ if (wil->ftm.session_started) {
+ wil_err(wil, "FTM session already running\n");
+ rc = -EAGAIN;
+ goto out;
+ }
+ /* for now allow measurement to associated AP only */
+ if (!test_bit(wil_status_fwconnected, wil->status)) {
+ wil_err(wil, "must be associated\n");
+ rc = -ENOTSUPP;
+ goto out;
+ }
+
+ for (i = 0; i < request->n_peers; i++) {
+ if (request->peers[i].flags &
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI)
+ has_lci = true;
+ if (request->peers[i].flags &
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR)
+ has_lcr = true;
+ max_meas = max(max_meas,
+ request->peers[i].params.meas_per_burst);
+ }
+
+ wil->ftm.ftm_res = kzalloc(sizeof(*wil->ftm.ftm_res) +
+ max_meas * sizeof(struct wil_ftm_peer_meas) +
+ (has_lci ? WIL_TOF_FTM_MAX_LCI_LENGTH : 0) +
+ (has_lcr ? WIL_TOF_FTM_MAX_LCR_LENGTH : 0), GFP_KERNEL);
+ if (!wil->ftm.ftm_res) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ ptr = (u8 *)wil->ftm.ftm_res;
+ ptr += sizeof(struct wil_ftm_peer_meas_res) +
+ max_meas * sizeof(struct wil_ftm_peer_meas);
+ if (has_lci) {
+ wil->ftm.ftm_res->lci = ptr;
+ ptr += WIL_TOF_FTM_MAX_LCI_LENGTH;
+ }
+ if (has_lcr)
+ wil->ftm.ftm_res->lcr = ptr;
+ wil->ftm.max_ftm_meas = max_meas;
+
+ cmd_len = sizeof(struct wmi_tof_session_start_cmd) +
+ request->n_peers * sizeof(struct wmi_ftm_dest_info);
+ cmd = kzalloc(cmd_len, GFP_KERNEL);
+ if (!cmd) {
+ rc = -ENOMEM;
+ goto out_ftm_res;
+ }
+
+ cmd->session_id = cpu_to_le32(WIL_FTM_FW_SESSION_ID);
+ cmd->num_of_dest = cpu_to_le16(request->n_peers);
+ for (i = 0; i < request->n_peers; i++) {
+ ether_addr_copy(cmd->ftm_dest_info[i].dst_mac,
+ request->peers[i].mac_addr);
+ cmd->ftm_dest_info[i].channel = request->peers[i].channel;
+ if (request->peers[i].flags &
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE) {
+ cmd->ftm_dest_info[i].flags |=
+ WMI_TOF_SESSION_START_FLAG_SECURED;
+ cmd->ftm_dest_info[i].initial_token =
+ request->peers[i].secure_token_id;
+ } else {
+ cmd->ftm_dest_info[i].initial_token =
+ WIL_TOF_FTM_DEFAULT_INITIAL_TOKEN;
+ }
+ if (request->peers[i].flags &
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP)
+ cmd->ftm_dest_info[i].flags |=
+ WMI_TOF_SESSION_START_FLAG_ASAP;
+ if (request->peers[i].flags &
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI)
+ cmd->ftm_dest_info[i].flags |=
+ WMI_TOF_SESSION_START_FLAG_LCI_REQ;
+ if (request->peers[i].flags &
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR)
+ cmd->ftm_dest_info[i].flags |=
+ WMI_TOF_SESSION_START_FLAG_LCR_REQ;
+ cmd->ftm_dest_info[i].num_of_ftm_per_burst =
+ request->peers[i].params.meas_per_burst;
+ cmd->ftm_dest_info[i].num_of_bursts_exp =
+ request->peers[i].params.num_of_bursts_exp;
+ cmd->ftm_dest_info[i].burst_duration =
+ request->peers[i].params.burst_duration;
+ cmd->ftm_dest_info[i].burst_period =
+ cpu_to_le16(request->peers[i].params.burst_period);
+ }
+
+ rc = wmi_send(wil, WMI_TOF_SESSION_START_CMDID, cmd, cmd_len);
+ kfree(cmd);
+
+ if (rc)
+ goto out_ftm_res;
+
+ wil->ftm.session_cookie = request->session_cookie;
+ wil->ftm.session_started = 1;
+
+out_ftm_res:
+ if (rc) {
+ kfree(wil->ftm.ftm_res);
+ wil->ftm.ftm_res = NULL;
+ }
+out:
+ mutex_unlock(&wil->ftm.lock);
+ return rc;
+}
+
+static void
+wil_ftm_cfg80211_session_ended(struct wil6210_priv *wil, u32 status)
+{
+ struct sk_buff *vendor_event = NULL;
+
+ mutex_lock(&wil->ftm.lock);
+
+ if (!wil->ftm.session_started) {
+ wil_dbg_misc(wil, "FTM session not started, ignoring event\n");
+ goto out;
+ }
+
+ /* finish the session */
+ wil_dbg_misc(wil, "finishing FTM session\n");
+
+ /* send left-over results if any */
+ wil_ftm_send_peer_res(wil);
+
+ wil->ftm.session_started = 0;
+ kfree(wil->ftm.ftm_res);
+ wil->ftm.ftm_res = NULL;
+
+ vendor_event = cfg80211_vendor_event_alloc(
+ wil_to_wiphy(wil),
+ wil->wdev,
+ WIL_FTM_NL_EXTRA_ALLOC,
+ QCA_NL80211_VENDOR_EVENT_FTM_SESSION_DONE_INDEX,
+ GFP_KERNEL);
+ if (!vendor_event)
+ goto out;
+
+ if (nla_put_u64(vendor_event,
+ QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE,
+ wil->ftm.session_cookie) ||
+ nla_put_u32(vendor_event,
+ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS, status)) {
+ wil_err(wil, "failed to fill session done event\n");
+ goto out;
+ }
+ cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+ vendor_event = NULL;
+out:
+ kfree_skb(vendor_event);
+ mutex_unlock(&wil->ftm.lock);
+}
+
+static void wil_aoa_timer_fn(ulong x)
+{
+ struct wil6210_priv *wil = (void *)x;
+
+ wil_dbg_misc(wil, "AOA timer\n");
+ schedule_work(&wil->ftm.aoa_timeout_work);
+}
+
+static int
+wil_aoa_cfg80211_start_measurement(struct wil6210_priv *wil,
+ struct wil_aoa_meas_request *request)
+{
+ int rc = 0;
+ struct cfg80211_bss *bss;
+ struct wmi_aoa_meas_cmd cmd;
+
+ mutex_lock(&wil->ftm.lock);
+
+ if (wil->ftm.aoa_started) {
+ wil_err(wil, "AOA measurement already running\n");
+ rc = -EAGAIN;
+ goto out;
+ }
+ if (request->type >= QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX) {
+ wil_err(wil, "invalid AOA type: %d\n", request->type);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ bss = cfg80211_get_bss(wil_to_wiphy(wil), NULL, request->mac_addr,
+ NULL, 0,
+ IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
+ if (!bss) {
+ wil_err(wil, "Unable to find BSS\n");
+ rc = -ENOENT;
+ goto out;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ ether_addr_copy(cmd.mac_addr, request->mac_addr);
+ cmd.channel = bss->channel->hw_value - 1;
+ cmd.aoa_meas_type = request->type;
+
+ rc = wmi_send(wil, WMI_AOA_MEAS_CMDID, &cmd, sizeof(cmd));
+ if (rc)
+ goto out_bss;
+
+ ether_addr_copy(wil->ftm.aoa_peer_mac_addr, request->mac_addr);
+ mod_timer(&wil->ftm.aoa_timer,
+ jiffies + msecs_to_jiffies(WIL_AOA_MEASUREMENT_TIMEOUT));
+ wil->ftm.aoa_started = 1;
+out_bss:
+ cfg80211_put_bss(wil_to_wiphy(wil), bss);
+out:
+ mutex_unlock(&wil->ftm.lock);
+ return rc;
+}
+
+void wil_aoa_cfg80211_meas_result(struct wil6210_priv *wil,
+ struct wil_aoa_meas_result *result)
+{
+ struct sk_buff *vendor_event = NULL;
+
+ mutex_lock(&wil->ftm.lock);
+
+ if (!wil->ftm.aoa_started) {
+ wil_info(wil, "AOA not started, not sending result\n");
+ goto out;
+ }
+
+ wil_dbg_misc(wil, "sending AOA measurement result\n");
+
+ vendor_event = cfg80211_vendor_event_alloc(
+ wil_to_wiphy(wil),
+ wil->wdev,
+ result->length + WIL_FTM_NL_EXTRA_ALLOC,
+ QCA_NL80211_VENDOR_EVENT_AOA_MEAS_RESULT_INDEX,
+ GFP_KERNEL);
+ if (!vendor_event) {
+ wil_err(wil, "fail to allocate measurement result\n");
+ goto out;
+ }
+
+ if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_MAC_ADDR,
+ ETH_ALEN, result->mac_addr) ||
+ nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_AOA_TYPE,
+ result->type) ||
+ nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS,
+ result->status) ||
+ nla_put_u32(vendor_event,
+ QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK,
+ result->antenna_array_mask)) {
+ wil_err(wil, "failed to fill vendor event\n");
+ goto out;
+ }
+
+ if (result->length > 0 &&
+ nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT,
+ result->length, result->data)) {
+ wil_err(wil, "failed to fill vendor event with AOA data\n");
+ goto out;
+ }
+
+ cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+
+ del_timer_sync(&wil->ftm.aoa_timer);
+ wil->ftm.aoa_started = 0;
+out:
+ mutex_unlock(&wil->ftm.lock);
+}
+
+void wil_ftm_evt_session_ended(struct wil6210_priv *wil,
+ struct wmi_tof_session_end_event *evt)
+{
+ u32 status;
+
+ switch (evt->status) {
+ case WMI_TOF_SESSION_END_NO_ERROR:
+ status = QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK;
+ break;
+ case WMI_TOF_SESSION_END_PARAMS_ERROR:
+ status = QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID;
+ break;
+ case WMI_TOF_SESSION_END_FAIL:
+ status = QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED;
+ break;
+ case WMI_TOF_SESSION_END_ABORTED:
+ status = QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED;
+ break;
+ default:
+ status = QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED;
+ break;
+ }
+
+ wil_ftm_cfg80211_session_ended(wil, status);
+}
+
+void wil_ftm_evt_per_dest_res(struct wil6210_priv *wil,
+ struct wmi_tof_ftm_per_dest_res_event *evt)
+{
+ u32 i, index;
+ __le64 tmp = 0;
+ u8 n_meas;
+
+ mutex_lock(&wil->ftm.lock);
+
+ if (!wil->ftm.session_started || !wil->ftm.ftm_res) {
+ wil_dbg_misc(wil, "Session not running, ignoring res event\n");
+ goto out;
+ }
+ if (wil->ftm.has_ftm_res &&
+ !ether_addr_equal(evt->dst_mac, wil->ftm.ftm_res->mac_addr)) {
+ wil_dbg_misc(wil,
+ "Results for previous peer not properly terminated\n");
+ wil_ftm_send_peer_res(wil);
+ }
+
+ if (!wil->ftm.has_ftm_res) {
+ ether_addr_copy(wil->ftm.ftm_res->mac_addr, evt->dst_mac);
+ wil->ftm.has_ftm_res = 1;
+ }
+
+ n_meas = evt->actual_ftm_per_burst;
+ switch (evt->status) {
+ case WMI_PER_DEST_RES_NO_ERROR:
+ wil->ftm.ftm_res->status =
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK;
+ break;
+ case WMI_PER_DEST_RES_TX_RX_FAIL:
+ /* FW reports corrupted results here, discard. */
+ n_meas = 0;
+ wil->ftm.ftm_res->status =
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK;
+ break;
+ case WMI_PER_DEST_RES_PARAM_DONT_MATCH:
+ wil->ftm.ftm_res->status =
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID;
+ break;
+ default:
+ wil_err(wil, "unexpected status %d\n", evt->status);
+ wil->ftm.ftm_res->status =
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID;
+ break;
+ }
+
+ for (i = 0; i < n_meas; i++) {
+ index = wil->ftm.ftm_res->n_meas;
+ if (index >= wil->ftm.max_ftm_meas) {
+ wil_dbg_misc(wil, "Too many measurements, some lost\n");
+ break;
+ }
+ memcpy(&tmp, evt->responder_ftm_res[i].t1,
+ sizeof(evt->responder_ftm_res[i].t1));
+ wil->ftm.ftm_res->meas[index].t1 = le64_to_cpu(tmp);
+ memcpy(&tmp, evt->responder_ftm_res[i].t2,
+ sizeof(evt->responder_ftm_res[i].t2));
+ wil->ftm.ftm_res->meas[index].t2 = le64_to_cpu(tmp);
+ memcpy(&tmp, evt->responder_ftm_res[i].t3,
+ sizeof(evt->responder_ftm_res[i].t3));
+ wil->ftm.ftm_res->meas[index].t3 = le64_to_cpu(tmp);
+ memcpy(&tmp, evt->responder_ftm_res[i].t4,
+ sizeof(evt->responder_ftm_res[i].t4));
+ wil->ftm.ftm_res->meas[index].t4 = le64_to_cpu(tmp);
+ wil->ftm.ftm_res->n_meas++;
+ }
+
+ if (evt->flags & WMI_PER_DEST_RES_BURST_REPORT_END)
+ wil_ftm_send_peer_res(wil);
+out:
+ mutex_unlock(&wil->ftm.lock);
+}
+
+void wil_aoa_evt_meas(struct wil6210_priv *wil,
+ struct wmi_aoa_meas_event *evt,
+ int len)
+{
+ int data_len = len - offsetof(struct wmi_aoa_meas_event, meas_data);
+ struct wil_aoa_meas_result *res;
+
+ data_len = min_t(int, le16_to_cpu(evt->length), data_len);
+
+ res = kmalloc(sizeof(*res) + data_len, GFP_KERNEL);
+ if (!res)
+ return;
+
+ ether_addr_copy(res->mac_addr, evt->mac_addr);
+ res->type = evt->aoa_meas_type;
+ res->antenna_array_mask = le32_to_cpu(evt->meas_rf_mask);
+ res->status = evt->meas_status;
+ res->length = data_len;
+ memcpy(res->data, evt->meas_data, data_len);
+
+ wil_dbg_misc(wil, "AOA result status %d type %d mask %d length %d\n",
+ res->status, res->type,
+ res->antenna_array_mask, res->length);
+
+ wil_aoa_cfg80211_meas_result(wil, res);
+ kfree(res);
+}
+
+int wil_ftm_get_capabilities(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct sk_buff *skb;
+ struct nlattr *attr;
+
+ if (!test_bit(WMI_FW_CAPABILITY_FTM, wil->fw_capabilities))
+ return -ENOTSUPP;
+
+ /* we should get the capabilities from the FW. for now,
+ * report dummy capabilities for one shot measurement
+ */
+ skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 128);
+ if (!skb)
+ return -ENOMEM;
+ attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_LOC_CAPA);
+ if (!attr ||
+ nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS,
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER |
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR |
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP |
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA) ||
+ nla_put_u16(skb, QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS,
+ 1) ||
+ nla_put_u16(skb, QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS, 1) ||
+ nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP,
+ 0) ||
+ nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST,
+ 4) ||
+ nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES,
+ BIT(QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE))) {
+ wil_err(wil, "fail to fill get_capabilities reply\n");
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ nla_nest_end(skb, attr);
+
+ return cfg80211_vendor_cmd_reply(skb);
+}
+
+int wil_ftm_start_session(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil_ftm_session_request *request;
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_LOC_MAX + 1];
+ struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX + 1];
+ struct nlattr *peer;
+ int rc, n_peers = 0, index = 0, tmp;
+ struct cfg80211_bss *bss;
+
+ if (!test_bit(WMI_FW_CAPABILITY_FTM, wil->fw_capabilities))
+ return -ENOTSUPP;
+
+ rc = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_LOC_MAX, data, data_len,
+ wil_nl80211_loc_policy);
+ if (rc) {
+ wil_err(wil, "Invalid ATTR\n");
+ return rc;
+ }
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS]) {
+ wil_err(wil, "no peers specified\n");
+ return -EINVAL;
+ }
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE]) {
+ wil_err(wil, "session cookie not specified\n");
+ return -EINVAL;
+ }
+
+ nla_for_each_nested(peer, tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS],
+ tmp)
+ n_peers++;
+
+ if (!n_peers) {
+ wil_err(wil, "empty peer list\n");
+ return -EINVAL;
+ }
+
+ /* for now only allow measurement for a single peer */
+ if (n_peers != 1) {
+ wil_err(wil, "only single peer allowed\n");
+ return -EINVAL;
+ }
+
+ request = kzalloc(sizeof(*request) +
+ n_peers * sizeof(struct wil_ftm_meas_peer_info),
+ GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->session_cookie =
+ nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE]);
+ request->n_peers = n_peers;
+ nla_for_each_nested(peer, tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS],
+ tmp) {
+ rc = nla_parse_nested(tb2, QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX,
+ peer, wil_nl80211_ftm_peer_policy);
+ if (rc) {
+ wil_err(wil, "Invalid peer ATTR\n");
+ goto out;
+ }
+ if (!tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR] ||
+ nla_len(tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR])
+ != ETH_ALEN) {
+ wil_err(wil, "Peer MAC address missing or invalid\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ memcpy(request->peers[index].mac_addr,
+ nla_data(tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR]),
+ ETH_ALEN);
+ bss = cfg80211_get_bss(wiphy, NULL,
+ request->peers[index].mac_addr, NULL, 0,
+ IEEE80211_BSS_TYPE_ANY,
+ IEEE80211_PRIVACY_ANY);
+ if (!bss) {
+ wil_err(wil, "invalid bss at index %d\n", index);
+ rc = -ENOENT;
+ goto out;
+ }
+ request->peers[index].channel = bss->channel->hw_value - 1;
+ cfg80211_put_bss(wiphy, bss);
+ if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS])
+ request->peers[index].flags = nla_get_u32(
+ tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS]);
+ if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID])
+ request->peers[index].secure_token_id = nla_get_u8(
+ tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID]);
+ rc = wil_ftm_parse_meas_params(
+ wil,
+ tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS],
+ &request->peers[index].params);
+ if (!rc)
+ rc = wil_ftm_validate_meas_params(
+ wil, &request->peers[index].params);
+ if (rc)
+ goto out;
+ index++;
+ }
+
+ rc = wil_ftm_cfg80211_start_session(wil, request);
+out:
+ kfree(request);
+ return rc;
+}
+
+int wil_ftm_abort_session(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+ wil_dbg_misc(wil, "stub\n");
+ return -ENOTSUPP;
+}
+
+int wil_ftm_configure_responder(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+ wil_dbg_misc(wil, "stub\n");
+ return -ENOTSUPP;
+}
+
+int wil_aoa_start_measurement(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil_aoa_meas_request request;
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_LOC_MAX + 1];
+ int rc;
+
+ if (!test_bit(WMI_FW_CAPABILITY_FTM, wil->fw_capabilities))
+ return -ENOTSUPP;
+
+ wil_dbg_misc(wil, "AOA start measurement\n");
+
+ rc = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_LOC_MAX, data, data_len,
+ wil_nl80211_loc_policy);
+ if (rc) {
+ wil_err(wil, "Invalid ATTR\n");
+ return rc;
+ }
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]) {
+ wil_err(wil, "Must specify MAC address and type\n");
+ return -EINVAL;
+ }
+
+ memset(&request, 0, sizeof(request));
+ ether_addr_copy(request.mac_addr,
+ nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]));
+ request.type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]);
+
+ rc = wil_aoa_cfg80211_start_measurement(wil, &request);
+ return rc;
+}
+
+int wil_aoa_abort_measurement(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+ wil_dbg_misc(wil, "stub\n");
+ return -ENOTSUPP;
+}
+
+void wil_ftm_init(struct wil6210_priv *wil)
+{
+ mutex_init(&wil->ftm.lock);
+ setup_timer(&wil->ftm.aoa_timer, wil_aoa_timer_fn, (ulong)wil);
+ INIT_WORK(&wil->ftm.aoa_timeout_work, wil_aoa_measurement_timeout);
+}
+
+void wil_ftm_deinit(struct wil6210_priv *wil)
+{
+ del_timer_sync(&wil->ftm.aoa_timer);
+ cancel_work_sync(&wil->ftm.aoa_timeout_work);
+ kfree(wil->ftm.ftm_res);
+}
+
+void wil_ftm_stop_operations(struct wil6210_priv *wil)
+{
+ wil_ftm_cfg80211_session_ended(
+ wil, QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED);
+}
diff --git a/drivers/net/wireless/ath/wil6210/ftm.h b/drivers/net/wireless/ath/wil6210/ftm.h
new file mode 100644
index 000000000000..9721344579aa
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/ftm.h
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __WIL6210_FTM_H__
+#define __WIL6210_FTM_H__
+
+/**
+ * NOTE: The authoritative place for definition of QCA_NL80211_VENDOR_ID,
+ * vendor subcmd definitions prefixed with QCA_NL80211_VENDOR_SUBCMD, and
+ * qca_wlan_vendor_attr is open source file src/common/qca-vendor.h in
+ * git://w1.fi/srv/git/hostap.git; the values here are just a copy of that
+ */
+
+/**
+ * enum qca_vendor_attr_loc - attributes for FTM and AOA commands
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE: Session cookie, specified in
+ * %QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION. It will be provided by driver
+ * events and can be used to identify events targeted for this session.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA: Nested attribute containing extra
+ * FTM/AOA capabilities, returned by %QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA.
+ * see %enum qca_wlan_vendor_attr_loc_capa.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS: array of nested attributes
+ * containing information about each peer in measurement session
+ * request. See %enum qca_wlan_vendor_attr_peer_info for supported
+ * attributes for each peer
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RESULTS: nested attribute containing
+ * measurement results for a peer. reported by the
+ * %QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT event.
+ * See %enum qca_wlan_vendor_attr_peer_result for list of supported
+ * attributes.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE: flag attribute for
+ * enabling or disabling responder functionality.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_LCI: used in the
+ * %QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER command in order to
+ * specify the LCI report that will be sent by the responder during
+ * a measurement exchange. The format is defined in IEEE P802.11-REVmc/D5.0,
+ * 9.4.2.22.10
+ * @QCA_WLAN_VENDOR_ATTR_FTM_LCR: provided with the
+ * %QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER command in order to
+ * specify the location civic report that will be sent by the responder during
+ * a measurement exchange. The format is defined in IEEE P802.11-REVmc/D5.0,
+ * 9.4.2.22.13
+ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS: session/measurement completion
+ * status code, reported in %QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE
+ * and %QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT
+ * @QCA_WLAN_VENDOR_ATTR_FTM_INITIAL_TOKEN: initial dialog token used
+ * by responder (0 if not specified)
+ * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE: AOA measurement type. Requested in
+ * %QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS and optionally in
+ * %QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION if AOA measurements
+ * are needed as part of an FTM session.
+ * Reported by QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT.
+ * See enum qca_wlan_vendor_attr_aoa_type.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK: bit mask indicating
+ * which antenna arrays were used in location measurement.
+ * Reported in %QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT and
+ * %QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT
+ * @QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT: AOA measurement data.
+ * Its contents depends on the AOA type and antenna array mask:
+ * %QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: array of U16 values,
+ * phase of the strongest CIR path for each antenna in the measured
+ * array(s).
+ * %QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: array of 2 U16
+ * values, phase and amplitude of the strongest CIR path for each
+ * antenna in the measured array(s)
+ */
+enum qca_wlan_vendor_attr_loc {
+ /* we reuse these attributes */
+ QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
+ QCA_WLAN_VENDOR_ATTR_PAD = 13,
+ QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE = 14,
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA = 15,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS = 16,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS = 17,
+ QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE = 18,
+ QCA_WLAN_VENDOR_ATTR_FTM_LCI = 19,
+ QCA_WLAN_VENDOR_ATTR_FTM_LCR = 20,
+ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS = 21,
+ QCA_WLAN_VENDOR_ATTR_FTM_INITIAL_TOKEN = 22,
+ QCA_WLAN_VENDOR_ATTR_AOA_TYPE = 23,
+ QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK = 24,
+ QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LOC_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LOC_MAX = QCA_WLAN_VENDOR_ATTR_LOC_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_loc_capa - indoor location capabilities
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS: various flags. See
+ * %enum qca_wlan_vendor_attr_loc_capa_flags
+ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS: Maximum number
+ * of measurement sessions that can run concurrently.
+ * Default is one session (no session concurrency)
+ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS: The total number of unique
+ * peers that are supported in running sessions. For example,
+ * if the value is 8 and maximum number of sessions is 2, you can
+ * have one session with 8 unique peers, or 2 sessions with 4 unique
+ * peers each, and so on.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP: Maximum number
+ * of bursts per peer, as an exponent (2^value). Default is 0,
+ * meaning no multi-burst support.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST: Maximum number
+ * of measurement exchanges allowed in a single burst
+ * @QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES: Supported AOA measurement
+ * types. A bit mask (unsigned 32 bit value), each bit corresponds
+ * to an AOA type as defined by %enum qca_vendor_attr_aoa_type.
+ */
+enum qca_wlan_vendor_attr_loc_capa {
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_INVALID,
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS,
+ QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS,
+ QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS,
+ QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP,
+ QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST,
+ QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_MAX =
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_loc_capa_flags: Indoor location capability flags
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER: Set if driver
+ * can be configured as an FTM responder (for example, an AP that
+ * services FTM requests). %QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER
+ * will be supported if set.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver
+ * can run FTM sessions. %QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION
+ * will be supported if set.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder
+ * supports immediate (ASAP) response.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone
+ * AOA measurement using %QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM: Set if driver supports
+ * requesting AOA measurements as part of an FTM session.
+ */
+enum qca_wlan_vendor_attr_loc_capa_flags {
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER = 1 << 0,
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR = 1 << 1,
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP = 1 << 2,
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA = 1 << 3,
+ QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM = 1 << 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_peer_info: information about
+ * a single peer in a measurement session.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR: The MAC address of the peer.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS: Various flags related
+ * to measurement. See %enum qca_wlan_vendor_attr_ftm_peer_meas_flags.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS: Nested attribute of
+ * FTM measurement parameters, as specified by IEEE P802.11-REVmc/D7.0,
+ * 9.4.2.167. See %enum qca_wlan_vendor_attr_ftm_meas_param for
+ * list of supported attributes.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID: Initial token ID for
+ * secure measurement
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA
+ * measurement every _value_ bursts. If 0 or not specified,
+ * AOA measurements will be disabled for this peer.
+ */
+enum qca_wlan_vendor_attr_ftm_peer_info {
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX =
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_peer_meas_flags: Measurement request flags,
+ * per-peer
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP: If set, request
+ * immediate (ASAP) response from peer
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI: If set, request
+ * LCI report from peer. The LCI report includes the absolute
+ * location of the peer in "official" coordinates (similar to GPS).
+ * See IEEE P802.11-REVmc/D7.0, 11.24.6.7 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR: If set, request
+ * Location civic report from peer. The LCR includes the location
+ * of the peer in free-form format. See IEEE P802.11-REVmc/D7.0,
+ * 11.24.6.7 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE: If set,
+ * request a secure measurement.
+ * %QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID must also be provided.
+ */
+enum qca_wlan_vendor_attr_ftm_peer_meas_flags {
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP = 1 << 0,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI = 1 << 1,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR = 1 << 2,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE = 1 << 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_meas_param: Measurement parameters
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST: Number of measurements
+ * to perform in a single burst.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP: Number of bursts to
+ * perform, specified as an exponent (2^value)
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION: Duration of burst
+ * instance, as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD: Time between bursts,
+ * as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167. Must
+ * be larger than %QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION
+ */
+enum qca_wlan_vendor_attr_ftm_meas_param {
+ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_INVALID,
+ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST,
+ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP,
+ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION,
+ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MAX =
+ QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_peer_result: Per-peer results
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR: MAC address of the reported
+ * peer
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS: Status of measurement
+ * request for this peer.
+ * See %enum qca_wlan_vendor_attr_ftm_peer_result_status
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS: Various flags related
+ * to measurement results for this peer.
+ * See %enum qca_wlan_vendor_attr_ftm_peer_result_flags
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS: Specified when
+ * request failed and peer requested not to send an additional request
+ * for this number of seconds.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI: LCI report when received
+ * from peer. In the format specified by IEEE P802.11-REVmc/D7.0,
+ * 9.4.2.22.10
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR: Location civic report when
+ * received from peer.In the format specified by IEEE P802.11-REVmc/D7.0,
+ * 9.4.2.22.13
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS: Reported when peer
+ * overridden some measurement request parameters. See
+ * enum qca_wlan_vendor_attr_ftm_meas_param.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS: AOA measurement
+ * for this peer. Same contents as %QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS: Array of measurement
+ * results. Each entry is a nested attribute defined
+ * by enum qca_wlan_vendor_attr_ftm_meas.
+ */
+enum qca_wlan_vendor_attr_ftm_peer_result {
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_INVALID,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAX =
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_peer_result_status
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK: Request sent ok and results
+ * will be provided. Peer may have overridden some measurement parameters,
+ * in which case overridden parameters will be report by
+ * %QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS attribute
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE: Peer is incapable
+ * of performing the measurement request. No more results will be sent
+ * for this peer in this session.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED: Peer reported request
+ * failed, and requested not to send an additional request for number
+ * of seconds specified by %QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS
+ * attribute.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID: Request validation
+ * failed. Request was not sent over the air.
+ */
+enum qca_wlan_vendor_attr_ftm_peer_result_status {
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED,
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_peer_result_flags : Various flags
+ * for measurement result, per-peer
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE: If set,
+ * measurement completed for this peer. No more results will be reported
+ * for this peer in this session.
+ */
+enum qca_wlan_vendor_attr_ftm_peer_result_flags {
+ QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE = 1 << 0,
+};
+
+/**
+ * enum qca_vendor_attr_loc_session_status: Session completion status code
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK: Session completed
+ * successfully.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED: Session aborted
+ * by request
+ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID: Session request
+ * was invalid and was not started
+ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED: Session had an error
+ * and did not complete normally (for example out of resources)
+ *
+ */
+enum qca_vendor_attr_loc_session_status {
+ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK,
+ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED,
+ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID,
+ QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_meas: Single measurement data
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1: Time of departure(TOD) of FTM packet as
+ * recorded by responder, in picoseconds.
+ * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2: Time of arrival(TOA) of FTM packet at
+ * initiator, in picoseconds.
+ * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3: TOD of ACK packet as recorded by
+ * initiator, in picoseconds.
+ * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4: TOA of ACK packet at
+ * responder, in picoseconds.
+ * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI: RSSI (signal level) as recorded
+ * during this measurement exchange. Optional and will be provided if
+ * the hardware can measure it.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR: TOD error reported by
+ * responder. Not always provided.
+ * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR: TOA error reported by
+ * responder. Not always provided.
+ * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR: TOD error measured by
+ * initiator. Not always provided.
+ * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR: TOA error measured by
+ * initiator. Not always provided.
+ * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD: Dummy attribute for padding.
+ */
+enum qca_wlan_vendor_attr_ftm_meas {
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INVALID,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_MAX =
+ QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_aoa_type: AOA measurement type
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: Phase of the strongest
+ * CIR (channel impulse response) path for each antenna.
+ * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: Phase and amplitude
+ * of the strongest CIR path for each antenna.
+ */
+enum qca_wlan_vendor_attr_aoa_type {
+ QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE,
+ QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP,
+ QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX,
+};
+
+/* vendor event indices, used from both cfg80211.c and ftm.c */
+enum qca_nl80211_vendor_events_index {
+ QCA_NL80211_VENDOR_EVENT_FTM_MEAS_RESULT_INDEX,
+ QCA_NL80211_VENDOR_EVENT_FTM_SESSION_DONE_INDEX,
+ QCA_NL80211_VENDOR_EVENT_AOA_MEAS_RESULT_INDEX,
+};
+
+/* measurement parameters. Specified for each peer as part
+ * of measurement request, or provided with measurement
+ * results for peer in case peer overridden parameters
+ */
+struct wil_ftm_meas_params {
+ u8 meas_per_burst;
+ u8 num_of_bursts_exp;
+ u8 burst_duration;
+ u16 burst_period;
+};
+
+/* measurement request for a single peer */
+struct wil_ftm_meas_peer_info {
+ u8 mac_addr[ETH_ALEN];
+ u8 channel;
+ u32 flags; /* enum qca_wlan_vendor_attr_ftm_peer_meas_flags */
+ struct wil_ftm_meas_params params;
+ u8 secure_token_id;
+};
+
+/* session request, passed to wil_ftm_cfg80211_start_session */
+struct wil_ftm_session_request {
+ u64 session_cookie;
+ u32 n_peers;
+ /* keep last, variable size according to n_peers */
+ struct wil_ftm_meas_peer_info peers[0];
+};
+
+/* single measurement for a peer */
+struct wil_ftm_peer_meas {
+ u64 t1, t2, t3, t4;
+};
+
+/* measurement results for a single peer */
+struct wil_ftm_peer_meas_res {
+ u8 mac_addr[ETH_ALEN];
+ u32 flags; /* enum qca_wlan_vendor_attr_ftm_peer_result_flags */
+ u8 status; /* enum qca_wlan_vendor_attr_ftm_peer_result_status */
+ u8 value_seconds;
+ bool has_params; /* true if params is valid */
+ struct wil_ftm_meas_params params; /* peer overridden params */
+ u8 *lci;
+ u8 lci_length;
+ u8 *lcr;
+ u8 lcr_length;
+ u32 n_meas;
+ /* keep last, variable size according to n_meas */
+ struct wil_ftm_peer_meas meas[0];
+};
+
+/* standalone AOA measurement request */
+struct wil_aoa_meas_request {
+ u8 mac_addr[ETH_ALEN];
+ u32 type;
+};
+
+/* AOA measurement result */
+struct wil_aoa_meas_result {
+ u8 mac_addr[ETH_ALEN];
+ u32 type;
+ u32 antenna_array_mask;
+ u32 status;
+ u32 length;
+ /* keep last, variable size according to length */
+ u8 data[0];
+};
+
+/* private data related to FTM. Part of the wil6210_priv structure */
+struct wil_ftm_priv {
+ struct mutex lock; /* protects the FTM data */
+ u8 session_started;
+ u64 session_cookie;
+ struct wil_ftm_peer_meas_res *ftm_res;
+ u8 has_ftm_res;
+ u32 max_ftm_meas;
+
+ /* standalone AOA measurement */
+ u8 aoa_started;
+ u8 aoa_peer_mac_addr[ETH_ALEN];
+ u32 aoa_type;
+ struct timer_list aoa_timer;
+ struct work_struct aoa_timeout_work;
+};
+
+int wil_ftm_get_capabilities(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len);
+int wil_ftm_start_session(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len);
+int wil_ftm_abort_session(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len);
+int wil_ftm_configure_responder(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len);
+int wil_aoa_start_measurement(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len);
+int wil_aoa_abort_measurement(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int data_len);
+
+#endif /* __WIL6210_FTM_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/fw.h b/drivers/net/wireless/ath/wil6210/fw.h
index 7a2c6c129ad5..2f2b910501ba 100644
--- a/drivers/net/wireless/ath/wil6210/fw.h
+++ b/drivers/net/wireless/ath/wil6210/fw.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014,2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -58,6 +58,15 @@ struct wil_fw_record_comment { /* type == wil_fw_type_comment */
u8 data[0]; /* free-form data [data_size], see above */
} __packed;
+/* FW capabilities encoded inside a comment record */
+#define WIL_FW_CAPABILITIES_MAGIC (0xabcddcba)
+struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */
+ /* identifies capabilities record */
+ __le32 magic;
+ /* capabilities (variable size), see enum wmi_fw_capability */
+ u8 capabilities[0];
+};
+
/* perform action
* data_size = @head.size - offsetof(struct wil_fw_record_action, data)
*/
@@ -93,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 d30657ee7e83..8f40eb301924 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -118,6 +118,12 @@ static int wil_fw_verify(struct wil6210_priv *wil, const u8 *data, size_t size)
return (int)dlen;
}
+static int fw_ignore_section(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ return 0;
+}
+
static int fw_handle_comment(struct wil6210_priv *wil, const void *data,
size_t size)
{
@@ -126,6 +132,27 @@ static int fw_handle_comment(struct wil6210_priv *wil, const void *data,
return 0;
}
+static int
+fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_capabilities *rec = data;
+ size_t capa_size;
+
+ if (size < sizeof(*rec) ||
+ le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC)
+ return 0;
+
+ capa_size = size - offsetof(struct wil_fw_record_capabilities,
+ capabilities);
+ bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
+ memcpy(wil->fw_capabilities, rec->capabilities,
+ min(sizeof(wil->fw_capabilities), capa_size));
+ wil_hex_dump_fw("CAPA", DUMP_PREFIX_OFFSET, 16, 1,
+ rec->capabilities, capa_size, false);
+ return 0;
+}
+
static int fw_handle_data(struct wil6210_priv *wil, const void *data,
size_t size)
{
@@ -196,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;
}
@@ -383,42 +417,51 @@ static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data,
static const struct {
int type;
- int (*handler)(struct wil6210_priv *wil, const void *data, size_t size);
+ int (*load_handler)(struct wil6210_priv *wil, const void *data,
+ size_t size);
+ int (*parse_handler)(struct wil6210_priv *wil, const void *data,
+ size_t size);
} wil_fw_handlers[] = {
- {wil_fw_type_comment, fw_handle_comment},
- {wil_fw_type_data, fw_handle_data},
- {wil_fw_type_fill, fw_handle_fill},
+ {wil_fw_type_comment, fw_handle_comment, fw_handle_capabilities},
+ {wil_fw_type_data, fw_handle_data, fw_ignore_section},
+ {wil_fw_type_fill, fw_handle_fill, fw_ignore_section},
/* wil_fw_type_action */
/* wil_fw_type_verify */
- {wil_fw_type_file_header, fw_handle_file_header},
- {wil_fw_type_direct_write, fw_handle_direct_write},
- {wil_fw_type_gateway_data, fw_handle_gateway_data},
- {wil_fw_type_gateway_data4, fw_handle_gateway_data4},
+ {wil_fw_type_file_header, fw_handle_file_header,
+ fw_handle_file_header},
+ {wil_fw_type_direct_write, fw_handle_direct_write, fw_ignore_section},
+ {wil_fw_type_gateway_data, fw_handle_gateway_data, fw_ignore_section},
+ {wil_fw_type_gateway_data4, fw_handle_gateway_data4,
+ fw_ignore_section},
};
static int wil_fw_handle_record(struct wil6210_priv *wil, int type,
- const void *data, size_t size)
+ const void *data, size_t size, bool load)
{
int i;
- for (i = 0; i < ARRAY_SIZE(wil_fw_handlers); i++) {
+ for (i = 0; i < ARRAY_SIZE(wil_fw_handlers); i++)
if (wil_fw_handlers[i].type == type)
- return wil_fw_handlers[i].handler(wil, data, size);
- }
+ return load ?
+ wil_fw_handlers[i].load_handler(
+ wil, data, size) :
+ wil_fw_handlers[i].parse_handler(
+ wil, data, size);
wil_err_fw(wil, "unknown record type: %d\n", type);
return -EINVAL;
}
/**
- * wil_fw_load - load FW into device
- *
- * Load the FW and uCode code and data to the corresponding device
- * memory regions
+ * wil_fw_process - process section from FW file
+ * if load is true: Load the FW and uCode code and data to the
+ * corresponding device memory regions,
+ * otherwise only parse and look for capabilities
*
* Return error code
*/
-static int wil_fw_load(struct wil6210_priv *wil, const void *data, size_t size)
+static int wil_fw_process(struct wil6210_priv *wil, const void *data,
+ size_t size, bool load)
{
int rc = 0;
const struct wil_fw_record_head *hdr;
@@ -437,7 +480,7 @@ static int wil_fw_load(struct wil6210_priv *wil, const void *data, size_t size)
return -EINVAL;
}
rc = wil_fw_handle_record(wil, le16_to_cpu(hdr->type),
- &hdr[1], hdr_sz);
+ &hdr[1], hdr_sz, load);
if (rc)
return rc;
}
@@ -456,13 +499,16 @@ static int wil_fw_load(struct wil6210_priv *wil, const void *data, size_t size)
}
/**
- * wil_request_firmware - Request firmware and load to device
+ * wil_request_firmware - Request firmware
*
- * Request firmware image from the file and load it to device
+ * Request firmware image from the file
+ * If load is true, load firmware to device, otherwise
+ * only parse and extract capabilities
*
* Return error code
*/
-int wil_request_firmware(struct wil6210_priv *wil, const char *name)
+int wil_request_firmware(struct wil6210_priv *wil, const char *name,
+ bool load)
{
int rc, rc1;
const struct firmware *fw;
@@ -482,7 +528,7 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name)
rc = rc1;
goto out;
}
- rc = wil_fw_load(wil, d, rc1);
+ rc = wil_fw_process(wil, d, rc1, load);
if (rc < 0)
goto out;
}
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index f10c47dcbde5..64046e0bd0a2 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -599,7 +599,7 @@ void wil6210_clear_irq(struct wil6210_priv *wil)
void wil6210_set_halp(struct wil6210_priv *wil)
{
- wil_dbg_misc(wil, "%s()\n", __func__);
+ wil_dbg_irq(wil, "%s()\n", __func__);
wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICS),
BIT_DMA_EP_MISC_ICR_HALP);
@@ -607,7 +607,7 @@ void wil6210_set_halp(struct wil6210_priv *wil)
void wil6210_clear_halp(struct wil6210_priv *wil)
{
- wil_dbg_misc(wil, "%s()\n", __func__);
+ wil_dbg_irq(wil, "%s()\n", __func__);
wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICR),
BIT_DMA_EP_MISC_ICR_HALP);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 49bd9b45ce22..5285ebc8b9af 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -232,6 +232,9 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
+ if (unlikely(!ndev))
+ return;
+
might_sleep();
wil_info(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid,
reason_code, from_event ? "+" : "-");
@@ -515,6 +518,8 @@ int wil_priv_init(struct wil6210_priv *wil)
spin_lock_init(&wil->wmi_ev_lock);
init_waitqueue_head(&wil->wq);
+ wil_ftm_init(wil);
+
wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi");
if (!wil->wmi_wq)
return -EAGAIN;
@@ -562,6 +567,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
{
wil_dbg_misc(wil, "%s()\n", __func__);
+ wil_ftm_deinit(wil);
wil_set_recovery_state(wil, fw_recovery_idle);
del_timer_sync(&wil->scan_timer);
del_timer_sync(&wil->p2p.discovery_timer);
@@ -852,6 +858,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
bitmap_zero(wil->status, wil_status_last);
mutex_unlock(&wil->wmi_mutex);
+ mutex_lock(&wil->p2p_wdev_mutex);
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
wil->scan_request);
@@ -859,6 +866,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL;
}
+ mutex_unlock(&wil->p2p_wdev_mutex);
wil_mask_irq(wil);
@@ -887,11 +895,12 @@ 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);
+ rc = wil_request_firmware(wil, WIL_FW_NAME, true);
if (rc)
return rc;
- rc = wil_request_firmware(wil, WIL_FW2_NAME);
+ rc = wil_request_firmware(wil, WIL_FW2_NAME, true);
if (rc)
return rc;
@@ -1034,10 +1043,10 @@ int wil_up(struct wil6210_priv *wil)
int __wil_down(struct wil6210_priv *wil)
{
- int rc;
-
WARN_ON(!mutex_is_locked(&wil->mutex));
+ set_bit(wil_status_resetting, wil->status);
+
if (wil->platform_ops.bus_request)
wil->platform_ops.bus_request(wil->platform_handle, 0);
@@ -1049,8 +1058,10 @@ int __wil_down(struct wil6210_priv *wil)
}
wil_enable_irq(wil);
- (void)wil_p2p_stop_discovery(wil);
+ wil_p2p_stop_radio_operations(wil);
+ wil_ftm_stop_operations(wil);
+ mutex_lock(&wil->p2p_wdev_mutex);
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
wil->scan_request);
@@ -1058,18 +1069,7 @@ int __wil_down(struct wil6210_priv *wil)
cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL;
}
-
- if (test_bit(wil_status_fwconnected, wil->status) ||
- test_bit(wil_status_fwconnecting, wil->status)) {
-
- mutex_unlock(&wil->mutex);
- rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0,
- WMI_DISCONNECT_EVENTID, NULL, 0,
- WIL6210_DISCONNECT_TO_MS);
- mutex_lock(&wil->mutex);
- if (rc)
- wil_err(wil, "timeout waiting for disconnect\n");
- }
+ mutex_unlock(&wil->p2p_wdev_mutex);
wil_reset(wil, false);
@@ -1113,8 +1113,8 @@ void wil_halp_vote(struct wil6210_priv *wil)
mutex_lock(&wil->halp.lock);
- wil_dbg_misc(wil, "%s: start, HALP ref_cnt (%d)\n", __func__,
- wil->halp.ref_cnt);
+ wil_dbg_irq(wil, "%s: start, HALP ref_cnt (%d)\n", __func__,
+ wil->halp.ref_cnt);
if (++wil->halp.ref_cnt == 1) {
wil6210_set_halp(wil);
@@ -1124,15 +1124,15 @@ void wil_halp_vote(struct wil6210_priv *wil)
/* Mask HALP as done in case the interrupt is raised */
wil6210_mask_halp(wil);
} else {
- wil_dbg_misc(wil,
- "%s: HALP vote completed after %d ms\n",
- __func__,
- jiffies_to_msecs(to_jiffies - rc));
+ wil_dbg_irq(wil,
+ "%s: HALP vote completed after %d ms\n",
+ __func__,
+ jiffies_to_msecs(to_jiffies - rc));
}
}
- wil_dbg_misc(wil, "%s: end, HALP ref_cnt (%d)\n", __func__,
- wil->halp.ref_cnt);
+ wil_dbg_irq(wil, "%s: end, HALP ref_cnt (%d)\n", __func__,
+ wil->halp.ref_cnt);
mutex_unlock(&wil->halp.lock);
}
@@ -1143,16 +1143,16 @@ void wil_halp_unvote(struct wil6210_priv *wil)
mutex_lock(&wil->halp.lock);
- wil_dbg_misc(wil, "%s: start, HALP ref_cnt (%d)\n", __func__,
- wil->halp.ref_cnt);
+ wil_dbg_irq(wil, "%s: start, HALP ref_cnt (%d)\n", __func__,
+ wil->halp.ref_cnt);
if (--wil->halp.ref_cnt == 0) {
wil6210_clear_halp(wil);
- wil_dbg_misc(wil, "%s: HALP unvote\n", __func__);
+ wil_dbg_irq(wil, "%s: HALP unvote\n", __func__);
}
- wil_dbg_misc(wil, "%s: end, HALP ref_cnt (%d)\n", __func__,
- wil->halp.ref_cnt);
+ wil_dbg_irq(wil, "%s: end, HALP ref_cnt (%d)\n", __func__,
+ wil->halp.ref_cnt);
mutex_unlock(&wil->halp.lock);
}
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index bd4c17ca2484..f4fca9d4eedf 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -185,13 +185,6 @@ void *wil_if_alloc(struct device *dev)
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
- netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
- WIL6210_NAPI_BUDGET);
- netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
- WIL6210_NAPI_BUDGET);
-
- netif_tx_stop_all_queues(ndev);
-
return wil;
out_priv:
@@ -222,25 +215,48 @@ void wil_if_free(struct wil6210_priv *wil)
int wil_if_add(struct wil6210_priv *wil)
{
+ struct wireless_dev *wdev = wil_to_wdev(wil);
+ struct wiphy *wiphy = wdev->wiphy;
struct net_device *ndev = wil_to_ndev(wil);
int rc;
- wil_dbg_misc(wil, "%s()\n", __func__);
+ 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);
+ return rc;
+ }
+
+ netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
+ WIL6210_NAPI_BUDGET);
+ netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
+ WIL6210_NAPI_BUDGET);
+
+ netif_tx_stop_all_queues(ndev);
rc = register_netdev(ndev);
if (rc < 0) {
dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
- return rc;
+ goto out_wiphy;
}
return 0;
+
+out_wiphy:
+ wiphy_unregister(wdev->wiphy);
+ return rc;
}
void wil_if_remove(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
+ struct wireless_dev *wdev = wil_to_wdev(wil);
wil_dbg_misc(wil, "%s()\n", __func__);
unregister_netdev(ndev);
+ wiphy_unregister(wdev->wiphy);
}
diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c
index 213b8259638c..42148da111f1 100644
--- a/drivers/net/wireless/ath/wil6210/p2p.c
+++ b/drivers/net/wireless/ath/wil6210/p2p.c
@@ -259,3 +259,46 @@ void wil_p2p_search_expired(struct work_struct *work)
mutex_unlock(&wil->p2p_wdev_mutex);
}
}
+
+void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)
+{
+ struct wil_p2p_info *p2p = &wil->p2p;
+
+ lockdep_assert_held(&wil->mutex);
+
+ mutex_lock(&wil->p2p_wdev_mutex);
+
+ if (wil->radio_wdev != wil->p2p_wdev)
+ goto out;
+
+ if (!p2p->discovery_started) {
+ /* Regular scan on the p2p device */
+ if (wil->scan_request &&
+ wil->scan_request->wdev == wil->p2p_wdev) {
+ cfg80211_scan_done(wil->scan_request, 1);
+ wil->scan_request = NULL;
+ }
+ goto out;
+ }
+
+ /* Search or listen on p2p device */
+ mutex_unlock(&wil->p2p_wdev_mutex);
+ wil_p2p_stop_discovery(wil);
+ mutex_lock(&wil->p2p_wdev_mutex);
+
+ if (wil->scan_request) {
+ /* search */
+ cfg80211_scan_done(wil->scan_request, 1);
+ wil->scan_request = NULL;
+ } else {
+ /* listen */
+ cfg80211_remain_on_channel_expired(wil->radio_wdev,
+ p2p->cookie,
+ &p2p->listen_chan,
+ GFP_KERNEL);
+ }
+
+out:
+ wil->radio_wdev = wil->wdev;
+ mutex_unlock(&wil->p2p_wdev_mutex);
+}
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 7b5c4222bc33..44746ca0d2e6 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -20,6 +20,7 @@
#include <linux/interrupt.h>
#include <linux/suspend.h>
#include "wil6210.h"
+#include <linux/rtnetlink.h>
static bool use_msi = true;
module_param(use_msi, bool, S_IRUGO);
@@ -38,6 +39,7 @@ void wil_set_capabilities(struct wil6210_priv *wil)
u32 rev_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
bitmap_zero(wil->hw_capabilities, hw_capability_last);
+ bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
switch (rev_id) {
case JTAG_DEV_ID_SPARROW_B0:
@@ -51,6 +53,9 @@ void wil_set_capabilities(struct wil6210_priv *wil)
}
wil_info(wil, "Board hardware is %s\n", wil->hw_name);
+
+ /* extract FW capabilities from file without loading the FW */
+ wil_request_firmware(wil, WIL_FW_NAME, false);
}
void wil_disable_irq(struct wil6210_priv *wil)
@@ -293,6 +298,9 @@ static void wil_pcie_remove(struct pci_dev *pdev)
#endif /* CONFIG_PM */
wil6210_debugfs_remove(wil);
+ rtnl_lock();
+ wil_p2p_wdev_free(wil);
+ rtnl_unlock();
wil_if_remove(wil);
wil_if_pcie_disable(wil);
pci_iounmap(pdev, csr);
@@ -300,7 +308,6 @@ static void wil_pcie_remove(struct pci_dev *pdev)
pci_disable_device(pdev);
if (wil->platform_ops.uninit)
wil->platform_ops.uninit(wil->platform_handle);
- wil_p2p_wdev_free(wil);
wil_if_free(wil);
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index f2f6a404d3d1..4c38520d4dd2 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -873,9 +873,12 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
rc = -EINVAL;
goto out_free;
}
- vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+ spin_lock_bh(&txdata->lock);
+ vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
txdata->enabled = 1;
+ spin_unlock_bh(&txdata->lock);
+
if (txdata->dot1x_open && (agg_wsize >= 0))
wil_addba_tx_request(wil, id, agg_wsize);
@@ -950,9 +953,11 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size)
rc = -EINVAL;
goto out_free;
}
- vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+ spin_lock_bh(&txdata->lock);
+ vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
txdata->enabled = 1;
+ spin_unlock_bh(&txdata->lock);
return 0;
out_free:
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 16e8ba570011..a19dba5b9e5f 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>
@@ -24,6 +25,7 @@
#include <linux/types.h>
#include "wmi.h"
#include "wil_platform.h"
+#include "ftm.h"
extern bool no_fw_recovery;
extern unsigned int mtu_max;
@@ -579,10 +581,11 @@ 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);
+ DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX);
u8 n_mids; /* number of additional MIDs as reported by FW */
u32 recovery_count; /* num of FW recovery attempts in a short time */
u32 recovery_state; /* FW recovery state machine */
@@ -660,12 +663,14 @@ struct wil6210_priv {
/* P2P_DEVICE vif */
struct wireless_dev *p2p_wdev;
- struct mutex p2p_wdev_mutex; /* protect @p2p_wdev */
+ struct mutex p2p_wdev_mutex; /* protect @p2p_wdev and @scan_request */
struct wireless_dev *radio_wdev;
/* High Access Latency Policy voting */
struct wil_halp halp;
+ struct wil_ftm_priv ftm;
+
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
struct notifier_block pm_notify;
@@ -844,6 +849,7 @@ u8 wil_p2p_stop_discovery(struct wil6210_priv *wil);
int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie);
void wil_p2p_listen_expired(struct work_struct *work);
void wil_p2p_search_expired(struct work_struct *work);
+void wil_p2p_stop_radio_operations(struct wil6210_priv *wil);
/* WMI for P2P */
int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi);
@@ -869,6 +875,8 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
u8 chan, u8 hidden_ssid, u8 is_go);
int wmi_pcp_stop(struct wil6210_priv *wil);
int wmi_led_cfg(struct wil6210_priv *wil, bool enable);
+int wmi_aoa_meas(struct wil6210_priv *wil, const void *mac_addr, u8 chan,
+ u8 type);
void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
u16 reason_code, bool from_event);
void wil_probe_client_flush(struct wil6210_priv *wil);
@@ -897,7 +905,8 @@ void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
int wil_iftype_nl2wmi(enum nl80211_iftype type);
int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd);
-int wil_request_firmware(struct wil6210_priv *wil, const char *name);
+int wil_request_firmware(struct wil6210_priv *wil, const char *name,
+ bool load);
int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_suspend(struct wil6210_priv *wil, bool is_runtime);
@@ -911,4 +920,18 @@ void wil_halp_unvote(struct wil6210_priv *wil);
void wil6210_set_halp(struct wil6210_priv *wil);
void wil6210_clear_halp(struct wil6210_priv *wil);
+void wil_ftm_init(struct wil6210_priv *wil);
+void wil_ftm_deinit(struct wil6210_priv *wil);
+void wil_ftm_stop_operations(struct wil6210_priv *wil);
+void wil_aoa_cfg80211_meas_result(struct wil6210_priv *wil,
+ struct wil_aoa_meas_result *result);
+
+void wil_ftm_evt_session_ended(struct wil6210_priv *wil,
+ struct wmi_tof_session_end_event *evt);
+void wil_ftm_evt_per_dest_res(struct wil6210_priv *wil,
+ struct wmi_tof_ftm_per_dest_res_event *evt);
+void wil_aoa_evt_meas(struct wil6210_priv *wil,
+ struct wmi_aoa_meas_event *evt,
+ int len);
+
#endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index c9fef36977ca..daa7a33d12d8 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -22,6 +22,7 @@
#include "txrx.h"
#include "wmi.h"
#include "trace.h"
+#include "ftm.h"
static uint max_assoc_sta = WIL6210_MAX_CID;
module_param(max_assoc_sta, uint, S_IRUGO | S_IWUSR);
@@ -312,14 +313,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);
@@ -424,6 +425,7 @@ static void wmi_evt_tx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
void *d, int len)
{
+ mutex_lock(&wil->p2p_wdev_mutex);
if (wil->scan_request) {
struct wmi_scan_complete_event *data = d;
bool aborted = (data->status != WMI_SCAN_SUCCESS);
@@ -433,14 +435,13 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
wil->scan_request, aborted);
del_timer_sync(&wil->scan_timer);
- mutex_lock(&wil->p2p_wdev_mutex);
cfg80211_scan_done(wil->scan_request, aborted);
wil->radio_wdev = wil->wdev;
- mutex_unlock(&wil->p2p_wdev_mutex);
wil->scan_request = NULL;
} else {
wil_err(wil, "SCAN_COMPLETE while not scanning\n");
}
+ mutex_unlock(&wil->p2p_wdev_mutex);
}
static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
@@ -772,6 +773,30 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
spin_unlock_bh(&sta->tid_rx_lock);
}
+static void wmi_evt_aoa_meas(struct wil6210_priv *wil, int id,
+ void *d, int len)
+{
+ struct wmi_aoa_meas_event *evt = d;
+
+ wil_aoa_evt_meas(wil, evt, len);
+}
+
+static void wmi_evt_ftm_session_ended(struct wil6210_priv *wil, int id,
+ void *d, int len)
+{
+ struct wmi_tof_session_end_event *evt = d;
+
+ wil_ftm_evt_session_ended(wil, evt);
+}
+
+static void wmi_evt_per_dest_res(struct wil6210_priv *wil, int id,
+ void *d, int len)
+{
+ struct wmi_tof_ftm_per_dest_res_event *evt = d;
+
+ wil_ftm_evt_per_dest_res(wil, evt);
+}
+
/**
* Some events are ignored for purpose; and need not be interpreted as
* "unhandled events"
@@ -799,6 +824,13 @@ static const struct {
{WMI_DELBA_EVENTID, wmi_evt_delba},
{WMI_VRING_EN_EVENTID, wmi_evt_vring_en},
{WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_ignore},
+ {WMI_AOA_MEAS_EVENTID, wmi_evt_aoa_meas},
+ {WMI_TOF_SESSION_END_EVENTID, wmi_evt_ftm_session_ended},
+ {WMI_TOF_GET_CAPABILITIES_EVENTID, wmi_evt_ignore},
+ {WMI_TOF_SET_LCR_EVENTID, wmi_evt_ignore},
+ {WMI_TOF_SET_LCI_EVENTID, wmi_evt_ignore},
+ {WMI_TOF_FTM_PER_DEST_RES_EVENTID, wmi_evt_per_dest_res},
+ {WMI_TOF_CHANNEL_INFO_EVENTID, wmi_evt_ignore},
};
/*
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 685fe0ddea26..f430e8a80603 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -46,6 +46,16 @@ enum wmi_mid {
MID_BROADCAST = 0xFF,
};
+/* FW capability IDs
+ * Each ID maps to a bit in a 32-bit bitmask value provided by the FW to
+ * the host
+ */
+enum wmi_fw_capability {
+ WMI_FW_CAPABILITY_FTM = 0,
+ WMI_FW_CAPABILITY_PS_CONFIG = 1,
+ WMI_FW_CAPABILITY_MAX,
+};
+
/* WMI_CMD_HDR */
struct wmi_cmd_hdr {
u8 mid;
@@ -120,6 +130,8 @@ enum wmi_command_id {
WMI_BF_SM_MGMT_CMDID = 0x838,
WMI_BF_RXSS_MGMT_CMDID = 0x839,
WMI_BF_TRIG_CMDID = 0x83A,
+ WMI_LINK_MAINTAIN_CFG_WRITE_CMDID = 0x842,
+ WMI_LINK_MAINTAIN_CFG_READ_CMDID = 0x843,
WMI_SET_SECTORS_CMDID = 0x849,
WMI_MAINTAIN_PAUSE_CMDID = 0x850,
WMI_MAINTAIN_RESUME_CMDID = 0x851,
@@ -134,10 +146,15 @@ enum wmi_command_id {
WMI_BF_CTRL_CMDID = 0x862,
WMI_NOTIFY_REQ_CMDID = 0x863,
WMI_GET_STATUS_CMDID = 0x864,
+ WMI_GET_RF_STATUS_CMDID = 0x866,
+ WMI_GET_BASEBAND_TYPE_CMDID = 0x867,
WMI_UNIT_TEST_CMDID = 0x900,
WMI_HICCUP_CMDID = 0x901,
WMI_FLASH_READ_CMDID = 0x902,
WMI_FLASH_WRITE_CMDID = 0x903,
+ /* Power management */
+ WMI_TRAFFIC_DEFERRAL_CMDID = 0x904,
+ WMI_TRAFFIC_RESUME_CMDID = 0x905,
/* P2P */
WMI_P2P_CFG_CMDID = 0x910,
WMI_PORT_ALLOCATE_CMDID = 0x911,
@@ -150,6 +167,26 @@ enum wmi_command_id {
WMI_PCP_START_CMDID = 0x918,
WMI_PCP_STOP_CMDID = 0x919,
WMI_GET_PCP_FACTOR_CMDID = 0x91B,
+ /* Power Save Configuration Commands */
+ WMI_PS_DEV_PROFILE_CFG_CMDID = 0x91C,
+ /* Not supported yet */
+ WMI_PS_DEV_CFG_CMDID = 0x91D,
+ /* Not supported yet */
+ WMI_PS_DEV_CFG_READ_CMDID = 0x91E,
+ /* Per MAC Power Save Configuration commands
+ * Not supported yet
+ */
+ WMI_PS_MID_CFG_CMDID = 0x91F,
+ /* Not supported yet */
+ WMI_PS_MID_CFG_READ_CMDID = 0x920,
+ WMI_RS_CFG_CMDID = 0x921,
+ WMI_GET_DETAILED_RS_RES_CMDID = 0x922,
+ WMI_AOA_MEAS_CMDID = 0x923,
+ WMI_TOF_SESSION_START_CMDID = 0x991,
+ WMI_TOF_GET_CAPABILITIES_CMDID = 0x992,
+ WMI_TOF_SET_LCR_CMDID = 0x993,
+ WMI_TOF_SET_LCI_CMDID = 0x994,
+ WMI_TOF_CHANNEL_INFO_CMDID = 0x995,
WMI_SET_MAC_ADDRESS_CMDID = 0xF003,
WMI_ABORT_SCAN_CMDID = 0xF007,
WMI_SET_PROMISCUOUS_MODE_CMDID = 0xF041,
@@ -291,9 +328,8 @@ enum wmi_scan_type {
/* WMI_START_SCAN_CMDID */
struct wmi_start_scan_cmd {
u8 direct_scan_mac_addr[WMI_MAC_LEN];
- /* DMG Beacon frame is transmitted during active scanning */
+ /* run scan with discovery beacon. Relevant for ACTIVE scan only. */
u8 discovery_mode;
- /* reserved */
u8 reserved;
/* Max duration in the home channel(ms) */
__le32 dwell_time;
@@ -453,6 +489,12 @@ struct wmi_port_delete_cmd {
u8 reserved[3];
} __packed;
+/* WMI_TRAFFIC_DEFERRAL_CMDID */
+struct wmi_traffic_deferral_cmd {
+ /* Bit vector: bit[0] - wake on Unicast, bit[1] - wake on Broadcast */
+ u8 wakeup_trigger;
+} __packed;
+
/* WMI_P2P_CFG_CMDID */
enum wmi_discovery_mode {
WMI_DISCOVERY_MODE_NON_OFFLOAD = 0x00,
@@ -818,85 +860,193 @@ struct wmi_pmc_cmd {
__le64 mem_base;
} __packed;
+enum wmi_aoa_meas_type {
+ WMI_AOA_PHASE_MEAS = 0x00,
+ WMI_AOA_PHASE_AMP_MEAS = 0x01,
+};
+
+/* WMI_AOA_MEAS_CMDID */
+struct wmi_aoa_meas_cmd {
+ u8 mac_addr[WMI_MAC_LEN];
+ /* channels IDs:
+ * 0 - 58320 MHz
+ * 1 - 60480 MHz
+ * 2 - 62640 MHz
+ */
+ u8 channel;
+ /* enum wmi_aoa_meas_type */
+ u8 aoa_meas_type;
+ __le32 meas_rf_mask;
+} __packed;
+
+enum wmi_tof_burst_duration {
+ WMI_TOF_BURST_DURATION_250_USEC = 2,
+ WMI_TOF_BURST_DURATION_500_USEC = 3,
+ WMI_TOF_BURST_DURATION_1_MSEC = 4,
+ WMI_TOF_BURST_DURATION_2_MSEC = 5,
+ WMI_TOF_BURST_DURATION_4_MSEC = 6,
+ WMI_TOF_BURST_DURATION_8_MSEC = 7,
+ WMI_TOF_BURST_DURATION_16_MSEC = 8,
+ WMI_TOF_BURST_DURATION_32_MSEC = 9,
+ WMI_TOF_BURST_DURATION_64_MSEC = 10,
+ WMI_TOF_BURST_DURATION_128_MSEC = 11,
+ WMI_TOF_BURST_DURATION_NO_PREFERENCES = 15,
+};
+
+enum wmi_tof_session_start_flags {
+ WMI_TOF_SESSION_START_FLAG_SECURED = 0x1,
+ WMI_TOF_SESSION_START_FLAG_ASAP = 0x2,
+ WMI_TOF_SESSION_START_FLAG_LCI_REQ = 0x4,
+ WMI_TOF_SESSION_START_FLAG_LCR_REQ = 0x8,
+};
+
+/* WMI_TOF_SESSION_START_CMDID */
+struct wmi_ftm_dest_info {
+ u8 channel;
+ /* wmi_tof_session_start_flags_e */
+ u8 flags;
+ u8 initial_token;
+ u8 num_of_ftm_per_burst;
+ u8 num_of_bursts_exp;
+ /* wmi_tof_burst_duration_e */
+ u8 burst_duration;
+ /* Burst Period indicate interval between two consecutive burst
+ * instances, in units of 100 ms
+ */
+ __le16 burst_period;
+ u8 dst_mac[WMI_MAC_LEN];
+ __le16 reserved;
+} __packed;
+
+/* WMI_TOF_SESSION_START_CMDID */
+struct wmi_tof_session_start_cmd {
+ __le32 session_id;
+ u8 num_of_aoa_measures;
+ u8 aoa_type;
+ __le16 num_of_dest;
+ u8 reserved[4];
+ struct wmi_ftm_dest_info ftm_dest_info[0];
+} __packed;
+
+enum wmi_tof_channel_info_report_type {
+ WMI_TOF_CHANNEL_INFO_TYPE_CIR = 0x1,
+ WMI_TOF_CHANNEL_INFO_TYPE_RSSI = 0x2,
+ WMI_TOF_CHANNEL_INFO_TYPE_SNR = 0x4,
+ WMI_TOF_CHANNEL_INFO_TYPE_DEBUG_DATA = 0x8,
+ WMI_TOF_CHANNEL_INFO_TYPE_VENDOR_SPECIFIC = 0x10,
+};
+
+/* WMI_TOF_CHANNEL_INFO_CMDID */
+struct wmi_tof_channel_info_cmd {
+ /* wmi_tof_channel_info_report_type_e */
+ __le32 channel_info_report_request;
+} __packed;
+
/* WMI Events
* List of Events (target to host)
*/
enum wmi_event_id {
- WMI_READY_EVENTID = 0x1001,
- WMI_CONNECT_EVENTID = 0x1002,
- WMI_DISCONNECT_EVENTID = 0x1003,
- WMI_SCAN_COMPLETE_EVENTID = 0x100A,
- WMI_REPORT_STATISTICS_EVENTID = 0x100B,
- WMI_RD_MEM_RSP_EVENTID = 0x1800,
- WMI_FW_READY_EVENTID = 0x1801,
- WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x200,
- WMI_ECHO_RSP_EVENTID = 0x1803,
- WMI_FS_TUNE_DONE_EVENTID = 0x180A,
- WMI_CORR_MEASURE_EVENTID = 0x180B,
- WMI_READ_RSSI_EVENTID = 0x180C,
- WMI_TEMP_SENSE_DONE_EVENTID = 0x180E,
- WMI_DC_CALIB_DONE_EVENTID = 0x180F,
- WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811,
- WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812,
- WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815,
- WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816,
- WMI_MARLON_R_READ_DONE_EVENTID = 0x1818,
- WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819,
- WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181A,
- WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181D,
- WMI_RF_RX_TEST_DONE_EVENTID = 0x181E,
- WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820,
- WMI_VRING_CFG_DONE_EVENTID = 0x1821,
- WMI_BA_STATUS_EVENTID = 0x1823,
- WMI_RCP_ADDBA_REQ_EVENTID = 0x1824,
- WMI_RCP_ADDBA_RESP_SENT_EVENTID = 0x1825,
- WMI_DELBA_EVENTID = 0x1826,
- WMI_GET_SSID_EVENTID = 0x1828,
- WMI_GET_PCP_CHANNEL_EVENTID = 0x182A,
- WMI_SW_TX_COMPLETE_EVENTID = 0x182B,
- WMI_READ_MAC_RXQ_EVENTID = 0x1830,
- WMI_READ_MAC_TXQ_EVENTID = 0x1831,
- WMI_WRITE_MAC_RXQ_EVENTID = 0x1832,
- WMI_WRITE_MAC_TXQ_EVENTID = 0x1833,
- WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834,
- WMI_BEAMFORMING_MGMT_DONE_EVENTID = 0x1836,
- WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837,
- WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839,
- WMI_RS_MGMT_DONE_EVENTID = 0x1852,
- WMI_RF_MGMT_STATUS_EVENTID = 0x1853,
- WMI_THERMAL_THROTTLING_STATUS_EVENTID = 0x1855,
- WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838,
- WMI_RX_MGMT_PACKET_EVENTID = 0x1840,
- WMI_TX_MGMT_PACKET_EVENTID = 0x1841,
- WMI_OTP_READ_RESULT_EVENTID = 0x1856,
- WMI_LED_CFG_DONE_EVENTID = 0x1858,
+ WMI_READY_EVENTID = 0x1001,
+ WMI_CONNECT_EVENTID = 0x1002,
+ WMI_DISCONNECT_EVENTID = 0x1003,
+ WMI_SCAN_COMPLETE_EVENTID = 0x100A,
+ WMI_REPORT_STATISTICS_EVENTID = 0x100B,
+ WMI_RD_MEM_RSP_EVENTID = 0x1800,
+ WMI_FW_READY_EVENTID = 0x1801,
+ WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x200,
+ WMI_ECHO_RSP_EVENTID = 0x1803,
+ WMI_FS_TUNE_DONE_EVENTID = 0x180A,
+ WMI_CORR_MEASURE_EVENTID = 0x180B,
+ WMI_READ_RSSI_EVENTID = 0x180C,
+ WMI_TEMP_SENSE_DONE_EVENTID = 0x180E,
+ WMI_DC_CALIB_DONE_EVENTID = 0x180F,
+ WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811,
+ WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812,
+ WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815,
+ WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816,
+ WMI_MARLON_R_READ_DONE_EVENTID = 0x1818,
+ WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819,
+ WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181A,
+ WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181D,
+ WMI_RF_RX_TEST_DONE_EVENTID = 0x181E,
+ WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820,
+ WMI_VRING_CFG_DONE_EVENTID = 0x1821,
+ WMI_BA_STATUS_EVENTID = 0x1823,
+ WMI_RCP_ADDBA_REQ_EVENTID = 0x1824,
+ WMI_RCP_ADDBA_RESP_SENT_EVENTID = 0x1825,
+ WMI_DELBA_EVENTID = 0x1826,
+ WMI_GET_SSID_EVENTID = 0x1828,
+ WMI_GET_PCP_CHANNEL_EVENTID = 0x182A,
+ WMI_SW_TX_COMPLETE_EVENTID = 0x182B,
+ WMI_READ_MAC_RXQ_EVENTID = 0x1830,
+ WMI_READ_MAC_TXQ_EVENTID = 0x1831,
+ WMI_WRITE_MAC_RXQ_EVENTID = 0x1832,
+ WMI_WRITE_MAC_TXQ_EVENTID = 0x1833,
+ WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834,
+ WMI_BEAMFORMING_MGMT_DONE_EVENTID = 0x1836,
+ WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837,
+ WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839,
+ WMI_RS_MGMT_DONE_EVENTID = 0x1852,
+ WMI_RF_MGMT_STATUS_EVENTID = 0x1853,
+ WMI_THERMAL_THROTTLING_STATUS_EVENTID = 0x1855,
+ WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838,
+ WMI_RX_MGMT_PACKET_EVENTID = 0x1840,
+ WMI_TX_MGMT_PACKET_EVENTID = 0x1841,
+ WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID = 0x1842,
+ WMI_LINK_MAINTAIN_CFG_READ_DONE_EVENTID = 0x1843,
+ WMI_OTP_READ_RESULT_EVENTID = 0x1856,
+ WMI_LED_CFG_DONE_EVENTID = 0x1858,
/* Performance monitoring events */
- WMI_DATA_PORT_OPEN_EVENTID = 0x1860,
- WMI_WBE_LINK_DOWN_EVENTID = 0x1861,
- WMI_BF_CTRL_DONE_EVENTID = 0x1862,
- WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863,
- WMI_GET_STATUS_DONE_EVENTID = 0x1864,
- WMI_VRING_EN_EVENTID = 0x1865,
- WMI_UNIT_TEST_EVENTID = 0x1900,
- WMI_FLASH_READ_DONE_EVENTID = 0x1902,
- WMI_FLASH_WRITE_DONE_EVENTID = 0x1903,
+ WMI_DATA_PORT_OPEN_EVENTID = 0x1860,
+ WMI_WBE_LINK_DOWN_EVENTID = 0x1861,
+ WMI_BF_CTRL_DONE_EVENTID = 0x1862,
+ WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863,
+ WMI_GET_STATUS_DONE_EVENTID = 0x1864,
+ WMI_VRING_EN_EVENTID = 0x1865,
+ WMI_GET_RF_STATUS_EVENTID = 0x1866,
+ WMI_GET_BASEBAND_TYPE_EVENTID = 0x1867,
+ WMI_UNIT_TEST_EVENTID = 0x1900,
+ WMI_FLASH_READ_DONE_EVENTID = 0x1902,
+ WMI_FLASH_WRITE_DONE_EVENTID = 0x1903,
+ /* Power management */
+ WMI_TRAFFIC_DEFERRAL_EVENTID = 0x1904,
+ WMI_TRAFFIC_RESUME_EVENTID = 0x1905,
/* P2P */
- WMI_P2P_CFG_DONE_EVENTID = 0x1910,
- WMI_PORT_ALLOCATED_EVENTID = 0x1911,
- WMI_PORT_DELETED_EVENTID = 0x1912,
- WMI_LISTEN_STARTED_EVENTID = 0x1914,
- WMI_SEARCH_STARTED_EVENTID = 0x1915,
- WMI_DISCOVERY_STARTED_EVENTID = 0x1916,
- WMI_DISCOVERY_STOPPED_EVENTID = 0x1917,
- WMI_PCP_STARTED_EVENTID = 0x1918,
- WMI_PCP_STOPPED_EVENTID = 0x1919,
- WMI_PCP_FACTOR_EVENTID = 0x191A,
- WMI_SET_CHANNEL_EVENTID = 0x9000,
- WMI_ASSOC_REQ_EVENTID = 0x9001,
- WMI_EAPOL_RX_EVENTID = 0x9002,
- WMI_MAC_ADDR_RESP_EVENTID = 0x9003,
- WMI_FW_VER_EVENTID = 0x9004,
- WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENTID = 0x9005,
+ WMI_P2P_CFG_DONE_EVENTID = 0x1910,
+ WMI_PORT_ALLOCATED_EVENTID = 0x1911,
+ WMI_PORT_DELETED_EVENTID = 0x1912,
+ WMI_LISTEN_STARTED_EVENTID = 0x1914,
+ WMI_SEARCH_STARTED_EVENTID = 0x1915,
+ WMI_DISCOVERY_STARTED_EVENTID = 0x1916,
+ WMI_DISCOVERY_STOPPED_EVENTID = 0x1917,
+ WMI_PCP_STARTED_EVENTID = 0x1918,
+ WMI_PCP_STOPPED_EVENTID = 0x1919,
+ WMI_PCP_FACTOR_EVENTID = 0x191A,
+ /* Power Save Configuration Events */
+ WMI_PS_DEV_PROFILE_CFG_EVENTID = 0x191C,
+ /* Not supported yet */
+ WMI_PS_DEV_CFG_EVENTID = 0x191D,
+ /* Not supported yet */
+ WMI_PS_DEV_CFG_READ_EVENTID = 0x191E,
+ /* Not supported yet */
+ WMI_PS_MID_CFG_EVENTID = 0x191F,
+ /* Not supported yet */
+ WMI_PS_MID_CFG_READ_EVENTID = 0x1920,
+ WMI_RS_CFG_DONE_EVENTID = 0x1921,
+ WMI_GET_DETAILED_RS_RES_EVENTID = 0x1922,
+ WMI_AOA_MEAS_EVENTID = 0x1923,
+ WMI_TOF_SESSION_END_EVENTID = 0x1991,
+ WMI_TOF_GET_CAPABILITIES_EVENTID = 0x1992,
+ WMI_TOF_SET_LCR_EVENTID = 0x1993,
+ WMI_TOF_SET_LCI_EVENTID = 0x1994,
+ WMI_TOF_FTM_PER_DEST_RES_EVENTID = 0x1995,
+ WMI_TOF_CHANNEL_INFO_EVENTID = 0x1996,
+ WMI_SET_CHANNEL_EVENTID = 0x9000,
+ WMI_ASSOC_REQ_EVENTID = 0x9001,
+ WMI_EAPOL_RX_EVENTID = 0x9002,
+ WMI_MAC_ADDR_RESP_EVENTID = 0x9003,
+ WMI_FW_VER_EVENTID = 0x9004,
+ WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENTID = 0x9005,
};
/* Events data structures */
@@ -943,10 +1093,85 @@ struct wmi_get_status_done_event {
/* WMI_FW_VER_EVENTID */
struct wmi_fw_ver_event {
- u8 major;
- u8 minor;
- __le16 subminor;
- __le16 build;
+ /* FW image version */
+ __le32 fw_major;
+ __le32 fw_minor;
+ __le32 fw_subminor;
+ __le32 fw_build;
+ /* FW image build time stamp */
+ __le32 hour;
+ __le32 minute;
+ __le32 second;
+ __le32 day;
+ __le32 month;
+ __le32 year;
+ /* Boot Loader image version */
+ __le32 bl_major;
+ __le32 bl_minor;
+ __le32 bl_subminor;
+ __le32 bl_build;
+ /* The number of entries in the FW capabilies array */
+ u8 fw_capabilities_len;
+ u8 reserved[3];
+ /* FW capabilities info
+ * Must be the last member of the struct
+ */
+ __le32 fw_capabilities[0];
+} __packed;
+
+/* WMI_GET_RF_STATUS_EVENTID */
+enum rf_type {
+ RF_UNKNOWN = 0x00,
+ RF_MARLON = 0x01,
+ RF_SPARROW = 0x02,
+};
+
+/* WMI_GET_RF_STATUS_EVENTID */
+enum board_file_rf_type {
+ BF_RF_MARLON = 0x00,
+ BF_RF_SPARROW = 0x01,
+};
+
+/* WMI_GET_RF_STATUS_EVENTID */
+enum rf_status {
+ RF_OK = 0x00,
+ RF_NO_COMM = 0x01,
+ RF_WRONG_BOARD_FILE = 0x02,
+};
+
+/* WMI_GET_RF_STATUS_EVENTID */
+struct wmi_get_rf_status_event {
+ /* enum rf_type */
+ __le32 rf_type;
+ /* attached RFs bit vector */
+ __le32 attached_rf_vector;
+ /* enabled RFs bit vector */
+ __le32 enabled_rf_vector;
+ /* enum rf_status, refers to enabled RFs */
+ u8 rf_status[32];
+ /* enum board file RF type */
+ __le32 board_file_rf_type;
+ /* board file platform type */
+ __le32 board_file_platform_type;
+ /* board file version */
+ __le32 board_file_version;
+ __le32 reserved[2];
+} __packed;
+
+/* WMI_GET_BASEBAND_TYPE_EVENTID */
+enum baseband_type {
+ BASEBAND_UNKNOWN = 0x00,
+ BASEBAND_SPARROW_M_A0 = 0x03,
+ BASEBAND_SPARROW_M_A1 = 0x04,
+ BASEBAND_SPARROW_M_B0 = 0x05,
+ BASEBAND_SPARROW_M_C0 = 0x06,
+ BASEBAND_SPARROW_M_D0 = 0x07,
+};
+
+/* WMI_GET_BASEBAND_TYPE_EVENTID */
+struct wmi_get_baseband_type_event {
+ /* enum baseband_type */
+ __le32 baseband_type;
} __packed;
/* WMI_MAC_ADDR_RESP_EVENTID */
@@ -1410,4 +1635,553 @@ struct wmi_led_cfg_done_event {
__le32 status;
} __packed;
+#define WMI_NUM_MCS (13)
+
+/* Rate search parameters configuration per connection */
+struct wmi_rs_cfg {
+ /* The maximal allowed PER for each MCS
+ * MCS will be considered as failed if PER during RS is higher
+ */
+ u8 per_threshold[WMI_NUM_MCS];
+ /* Number of MPDUs for each MCS
+ * this is the minimal statistic required to make an educated
+ * decision
+ */
+ u8 min_frame_cnt[WMI_NUM_MCS];
+ /* stop threshold [0-100] */
+ u8 stop_th;
+ /* MCS1 stop threshold [0-100] */
+ u8 mcs1_fail_th;
+ u8 max_back_failure_th;
+ /* Debug feature for disabling internal RS trigger (which is
+ * currently triggered by BF Done)
+ */
+ u8 dbg_disable_internal_trigger;
+ __le32 back_failure_mask;
+ __le32 mcs_en_vec;
+} __packed;
+
+/* WMI_RS_CFG_CMDID */
+struct wmi_rs_cfg_cmd {
+ /* connection id */
+ u8 cid;
+ /* enable or disable rate search */
+ u8 rs_enable;
+ /* rate search configuration */
+ struct wmi_rs_cfg rs_cfg;
+} __packed;
+
+/* WMI_RS_CFG_DONE_EVENTID */
+struct wmi_rs_cfg_done_event {
+ u8 cid;
+ /* enum wmi_fw_status */
+ u8 status;
+ u8 reserved[2];
+} __packed;
+
+/* WMI_GET_DETAILED_RS_RES_CMDID */
+struct wmi_get_detailed_rs_res_cmd {
+ /* connection id */
+ u8 cid;
+ u8 reserved[3];
+} __packed;
+
+/* RS results status */
+enum wmi_rs_results_status {
+ WMI_RS_RES_VALID = 0x00,
+ WMI_RS_RES_INVALID = 0x01,
+};
+
+/* Rate search results */
+struct wmi_rs_results {
+ /* number of sent MPDUs */
+ u8 num_of_tx_pkt[WMI_NUM_MCS];
+ /* number of non-acked MPDUs */
+ u8 num_of_non_acked_pkt[WMI_NUM_MCS];
+ /* RS timestamp */
+ __le32 tsf;
+ /* RS selected MCS */
+ u8 mcs;
+} __packed;
+
+/* WMI_GET_DETAILED_RS_RES_EVENTID */
+struct wmi_get_detailed_rs_res_event {
+ u8 cid;
+ /* enum wmi_rs_results_status */
+ u8 status;
+ /* detailed rs results */
+ struct wmi_rs_results rs_results;
+ u8 reserved[3];
+} __packed;
+
+/* broadcast connection ID */
+#define WMI_LINK_MAINTAIN_CFG_CID_BROADCAST (0xFFFFFFFF)
+
+/* Types wmi_link_maintain_cfg presets for WMI_LINK_MAINTAIN_CFG_WRITE_CMD */
+enum wmi_link_maintain_cfg_type {
+ /* AP/PCP default normal (non-FST) configuration settings */
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_NORMAL_AP = 0x00,
+ /* AP/PCP default FST configuration settings */
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_FST_AP = 0x01,
+ /* STA default normal (non-FST) configuration settings */
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_NORMAL_STA = 0x02,
+ /* STA default FST configuration settings */
+ WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_FST_STA = 0x03,
+ /* custom configuration settings */
+ WMI_LINK_MAINTAIN_CFG_TYPE_CUSTOM = 0x04,
+ /* number of defined configuration types */
+ WMI_LINK_MAINTAIN_CFG_TYPES_NUM = 0x05,
+};
+
+/* Response status codes for WMI_LINK_MAINTAIN_CFG_WRITE/READ commands */
+enum wmi_link_maintain_cfg_response_status {
+ /* WMI_LINK_MAINTAIN_CFG_WRITE/READ command successfully accomplished
+ */
+ WMI_LINK_MAINTAIN_CFG_RESPONSE_STATUS_OK = 0x00,
+ /* ERROR due to bad argument in WMI_LINK_MAINTAIN_CFG_WRITE/READ
+ * command request
+ */
+ WMI_LINK_MAINTAIN_CFG_RESPONSE_STATUS_BAD_ARGUMENT = 0x01,
+};
+
+/* Link Loss and Keep Alive configuration */
+struct wmi_link_maintain_cfg {
+ /* link_loss_enable_detectors_vec */
+ __le32 link_loss_enable_detectors_vec;
+ /* detectors check period usec */
+ __le32 check_link_loss_period_usec;
+ /* max allowed tx ageing */
+ __le32 tx_ageing_threshold_usec;
+ /* keep alive period for high SNR */
+ __le32 keep_alive_period_usec_high_snr;
+ /* keep alive period for low SNR */
+ __le32 keep_alive_period_usec_low_snr;
+ /* lower snr limit for keep alive period update */
+ __le32 keep_alive_snr_threshold_low_db;
+ /* upper snr limit for keep alive period update */
+ __le32 keep_alive_snr_threshold_high_db;
+ /* num of successive bad bcons causing link-loss */
+ __le32 bad_beacons_num_threshold;
+ /* SNR limit for bad_beacons_detector */
+ __le32 bad_beacons_snr_threshold_db;
+} __packed;
+
+/* WMI_LINK_MAINTAIN_CFG_WRITE_CMDID */
+struct wmi_link_maintain_cfg_write_cmd {
+ /* enum wmi_link_maintain_cfg_type_e - type of requested default
+ * configuration to be applied
+ */
+ __le32 cfg_type;
+ /* requested connection ID or WMI_LINK_MAINTAIN_CFG_CID_BROADCAST */
+ __le32 cid;
+ /* custom configuration settings to be applied (relevant only if
+ * cfg_type==WMI_LINK_MAINTAIN_CFG_TYPE_CUSTOM)
+ */
+ struct wmi_link_maintain_cfg lm_cfg;
+} __packed;
+
+/* WMI_LINK_MAINTAIN_CFG_READ_CMDID */
+struct wmi_link_maintain_cfg_read_cmd {
+ /* connection ID which configuration settings are requested */
+ __le32 cid;
+} __packed;
+
+/* WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID */
+struct wmi_link_maintain_cfg_write_done_event {
+ /* requested connection ID */
+ __le32 cid;
+ /* wmi_link_maintain_cfg_response_status_e - write status */
+ __le32 status;
+} __packed;
+
+/* \WMI_LINK_MAINTAIN_CFG_READ_DONE_EVENT */
+struct wmi_link_maintain_cfg_read_done_event {
+ /* requested connection ID */
+ __le32 cid;
+ /* wmi_link_maintain_cfg_response_status_e - read status */
+ __le32 status;
+ /* Retrieved configuration settings */
+ struct wmi_link_maintain_cfg lm_cfg;
+} __packed;
+
+enum wmi_traffic_deferral_status {
+ WMI_TRAFFIC_DEFERRAL_APPROVED = 0x0,
+ WMI_TRAFFIC_DEFERRAL_REJECTED = 0x1,
+};
+
+/* WMI_TRAFFIC_DEFERRAL_EVENTID */
+struct wmi_traffic_deferral_event {
+ /* enum wmi_traffic_deferral_status_e */
+ u8 status;
+} __packed;
+
+enum wmi_traffic_resume_status {
+ WMI_TRAFFIC_RESUME_SUCCESS = 0x0,
+ WMI_TRAFFIC_RESUME_FAILED = 0x1,
+};
+
+/* WMI_TRAFFIC_RESUME_EVENTID */
+struct wmi_traffic_resume_event {
+ /* enum wmi_traffic_resume_status_e */
+ u8 status;
+} __packed;
+
+/* Power Save command completion status codes */
+enum wmi_ps_cfg_cmd_status {
+ WMI_PS_CFG_CMD_STATUS_SUCCESS = 0x00,
+ WMI_PS_CFG_CMD_STATUS_BAD_PARAM = 0x01,
+ /* other error */
+ WMI_PS_CFG_CMD_STATUS_ERROR = 0x02,
+};
+
+/* Device Power Save Profiles */
+enum wmi_ps_profile_type {
+ WMI_PS_PROFILE_TYPE_DEFAULT = 0x00,
+ WMI_PS_PROFILE_TYPE_PS_DISABLED = 0x01,
+ WMI_PS_PROFILE_TYPE_MAX_PS = 0x02,
+ WMI_PS_PROFILE_TYPE_LOW_LATENCY_PS = 0x03,
+};
+
+/* WMI_PS_DEV_PROFILE_CFG_CMDID
+ *
+ * Power save profile to be used by the device
+ *
+ * Returned event:
+ * - WMI_PS_DEV_PROFILE_CFG_EVENTID
+ */
+struct wmi_ps_dev_profile_cfg_cmd {
+ /* wmi_ps_profile_type_e */
+ u8 ps_profile;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_PS_DEV_PROFILE_CFG_EVENTID */
+struct wmi_ps_dev_profile_cfg_event {
+ /* wmi_ps_cfg_cmd_status_e */
+ __le32 status;
+} __packed;
+
+enum wmi_ps_level {
+ WMI_PS_LEVEL_DEEP_SLEEP = 0x00,
+ WMI_PS_LEVEL_SHALLOW_SLEEP = 0x01,
+ /* awake = all PS mechanisms are disabled */
+ WMI_PS_LEVEL_AWAKE = 0x02,
+};
+
+enum wmi_ps_deep_sleep_clk_level {
+ /* 33k */
+ WMI_PS_DEEP_SLEEP_CLK_LEVEL_RTC = 0x00,
+ /* 10k */
+ WMI_PS_DEEP_SLEEP_CLK_LEVEL_OSC = 0x01,
+ /* @RTC Low latency */
+ WMI_PS_DEEP_SLEEP_CLK_LEVEL_RTC_LT = 0x02,
+ WMI_PS_DEEP_SLEEP_CLK_LEVEL_XTAL = 0x03,
+ WMI_PS_DEEP_SLEEP_CLK_LEVEL_SYSCLK = 0x04,
+ /* Not Applicable */
+ WMI_PS_DEEP_SLEEP_CLK_LEVEL_N_A = 0xFF,
+};
+
+/* Response by the FW to a D3 entry request */
+enum wmi_ps_d3_resp_policy {
+ WMI_PS_D3_RESP_POLICY_DEFAULT = 0x00,
+ /* debug -D3 req is always denied */
+ WMI_PS_D3_RESP_POLICY_DENIED = 0x01,
+ /* debug -D3 req is always approved */
+ WMI_PS_D3_RESP_POLICY_APPROVED = 0x02,
+};
+
+/* Device common power save configurations */
+struct wmi_ps_dev_cfg {
+ /* lowest level of PS allowed while unassociated, enum wmi_ps_level_e
+ */
+ u8 ps_unassoc_min_level;
+ /* lowest deep sleep clock level while nonassoc, enum
+ * wmi_ps_deep_sleep_clk_level_e
+ */
+ u8 ps_unassoc_deep_sleep_min_level;
+ /* lowest level of PS allowed while associated, enum wmi_ps_level_e */
+ u8 ps_assoc_min_level;
+ /* lowest deep sleep clock level while assoc, enum
+ * wmi_ps_deep_sleep_clk_level_e
+ */
+ u8 ps_assoc_deep_sleep_min_level;
+ /* enum wmi_ps_deep_sleep_clk_level_e */
+ u8 ps_assoc_low_latency_ds_min_level;
+ /* enum wmi_ps_d3_resp_policy_e */
+ u8 ps_D3_response_policy;
+ /* BOOL */
+ u8 ps_D3_pm_pme_enabled;
+ /* BOOL */
+ u8 ps_halp_enable;
+ u8 ps_deep_sleep_enter_thresh_msec;
+ /* BOOL */
+ u8 ps_voltage_scaling_en;
+} __packed;
+
+/* WMI_PS_DEV_CFG_CMDID
+ *
+ * Configure common Power Save parameters of the device and all MIDs.
+ *
+ * Returned event:
+ * - WMI_PS_DEV_CFG_EVENTID
+ */
+struct wmi_ps_dev_cfg_cmd {
+ /* Device Power Save configuration to be applied */
+ struct wmi_ps_dev_cfg ps_dev_cfg;
+ /* alignment to 32b */
+ u8 reserved[2];
+} __packed;
+
+/* WMI_PS_DEV_CFG_EVENTID */
+struct wmi_ps_dev_cfg_event {
+ /* wmi_ps_cfg_cmd_status_e */
+ __le32 status;
+} __packed;
+
+/* WMI_PS_DEV_CFG_READ_CMDID
+ *
+ * request to retrieve device Power Save configuration
+ * (WMI_PS_DEV_CFG_CMD params)
+ *
+ * Returned event:
+ * - WMI_PS_DEV_CFG_READ_EVENTID
+ */
+struct wmi_ps_dev_cfg_read_cmd {
+ __le32 reserved;
+} __packed;
+
+/* WMI_PS_DEV_CFG_READ_EVENTID */
+struct wmi_ps_dev_cfg_read_event {
+ /* wmi_ps_cfg_cmd_status_e */
+ __le32 status;
+ /* Retrieved device Power Save configuration (WMI_PS_DEV_CFG_CMD
+ * params)
+ */
+ struct wmi_ps_dev_cfg dev_ps_cfg;
+ /* alignment to 32b */
+ u8 reserved[2];
+} __packed;
+
+/* Per Mac Power Save configurations */
+struct wmi_ps_mid_cfg {
+ /* Low power RX in BTI is enabled, BOOL */
+ u8 beacon_lprx_enable;
+ /* Sync to sector ID enabled, BOOL */
+ u8 beacon_sync_to_sectorId_enable;
+ /* Low power RX in DTI is enabled, BOOL */
+ u8 frame_exchange_lprx_enable;
+ /* Sleep Cycle while in scheduled PS, 1-31 */
+ u8 scheduled_sleep_cycle_pow2;
+ /* Stay Awake for k BIs every (sleep_cycle - k) BIs, 1-31 */
+ u8 scheduled_num_of_awake_bis;
+ u8 am_to_traffic_load_thresh_mbp;
+ u8 traffic_to_am_load_thresh_mbps;
+ u8 traffic_to_am_num_of_no_traffic_bis;
+ /* BOOL */
+ u8 continuous_traffic_psm;
+ __le16 no_traffic_to_min_usec;
+ __le16 no_traffic_to_max_usec;
+ __le16 snoozing_sleep_interval_milisec;
+ u8 max_no_data_awake_events;
+ /* Trigger WEB after k failed beacons */
+ u8 num_of_failed_beacons_rx_to_trigger_web;
+ /* Trigger BF after k failed beacons */
+ u8 num_of_failed_beacons_rx_to_trigger_bf;
+ /* Trigger SOB after k successful beacons */
+ u8 num_of_successful_beacons_rx_to_trigger_sob;
+} __packed;
+
+/* WMI_PS_MID_CFG_CMDID
+ *
+ * Configure Power Save parameters of a specific MID.
+ * These parameters are relevant for the specific BSS this MID belongs to.
+ *
+ * Returned event:
+ * - WMI_PS_MID_CFG_EVENTID
+ */
+struct wmi_ps_mid_cfg_cmd {
+ /* MAC ID */
+ u8 mid;
+ /* mid PS configuration to be applied */
+ struct wmi_ps_mid_cfg ps_mid_cfg;
+} __packed;
+
+/* WMI_PS_MID_CFG_EVENTID */
+struct wmi_ps_mid_cfg_event {
+ /* MAC ID */
+ u8 mid;
+ /* alignment to 32b */
+ u8 reserved[3];
+ /* wmi_ps_cfg_cmd_status_e */
+ __le32 status;
+} __packed;
+
+/* WMI_PS_MID_CFG_READ_CMDID
+ *
+ * request to retrieve Power Save configuration of mid
+ * (WMI_PS_MID_CFG_CMD params)
+ *
+ * Returned event:
+ * - WMI_PS_MID_CFG_READ_EVENTID
+ */
+struct wmi_ps_mid_cfg_read_cmd {
+ /* MAC ID */
+ u8 mid;
+ /* alignment to 32b */
+ u8 reserved[3];
+} __packed;
+
+/* WMI_PS_MID_CFG_READ_EVENTID */
+struct wmi_ps_mid_cfg_read_event {
+ /* MAC ID */
+ u8 mid;
+ /* Retrieved MID Power Save configuration(WMI_PS_MID_CFG_CMD params) */
+ struct wmi_ps_mid_cfg mid_ps_cfg;
+ /* wmi_ps_cfg_cmd_status_e */
+ __le32 status;
+} __packed;
+
+#define WMI_AOA_MAX_DATA_SIZE (128)
+
+enum wmi_aoa_meas_status {
+ WMI_AOA_MEAS_SUCCESS = 0x00,
+ WMI_AOA_MEAS_PEER_INCAPABLE = 0x01,
+ WMI_AOA_MEAS_FAILURE = 0x02,
+};
+
+/* WMI_AOA_MEAS_EVENTID */
+struct wmi_aoa_meas_event {
+ u8 mac_addr[WMI_MAC_LEN];
+ /* channels IDs:
+ * 0 - 58320 MHz
+ * 1 - 60480 MHz
+ * 2 - 62640 MHz
+ */
+ u8 channel;
+ /* enum wmi_aoa_meas_type */
+ u8 aoa_meas_type;
+ /* Measurments are from RFs, defined by the mask */
+ __le32 meas_rf_mask;
+ /* enum wmi_aoa_meas_status */
+ u8 meas_status;
+ u8 reserved;
+ /* Length of meas_data in bytes */
+ __le16 length;
+ u8 meas_data[WMI_AOA_MAX_DATA_SIZE];
+} __packed;
+
+/* WMI_TOF_GET_CAPABILITIES_EVENTID */
+struct wmi_tof_get_capabilities_event {
+ u8 ftm_capability;
+ /* maximum supported number of destination to start TOF */
+ u8 max_num_of_dest;
+ /* maximum supported number of measurements per burst */
+ u8 max_num_of_meas_per_burst;
+ u8 reserved;
+ /* maximum supported multi bursts */
+ __le16 max_multi_bursts_sessions;
+ /* maximum supported FTM burst duration , wmi_tof_burst_duration_e */
+ __le16 max_ftm_burst_duration;
+ /* AOA supported types */
+ __le32 aoa_supported_types;
+} __packed;
+
+enum wmi_tof_session_end_status {
+ WMI_TOF_SESSION_END_NO_ERROR = 0x00,
+ WMI_TOF_SESSION_END_FAIL = 0x01,
+ WMI_TOF_SESSION_END_PARAMS_ERROR = 0x02,
+ WMI_TOF_SESSION_END_ABORTED = 0x03,
+};
+
+/* WMI_TOF_SESSION_END_EVENTID */
+struct wmi_tof_session_end_event {
+ /* FTM session ID */
+ __le32 session_id;
+ /* wmi_tof_session_end_status_e */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
+/* Responder FTM Results */
+struct wmi_responder_ftm_res {
+ u8 t1[6];
+ u8 t2[6];
+ u8 t3[6];
+ u8 t4[6];
+ __le16 tod_err;
+ __le16 toa_err;
+ __le16 tod_err_initiator;
+ __le16 toa_err_initiator;
+} __packed;
+
+enum wmi_tof_ftm_per_dest_res_status {
+ WMI_PER_DEST_RES_NO_ERROR = 0x00,
+ WMI_PER_DEST_RES_TX_RX_FAIL = 0x01,
+ WMI_PER_DEST_RES_PARAM_DONT_MATCH = 0x02,
+};
+
+enum wmi_tof_ftm_per_dest_res_flags {
+ WMI_PER_DEST_RES_REQ_START = 0x01,
+ WMI_PER_DEST_RES_BURST_REPORT_END = 0x02,
+ WMI_PER_DEST_RES_REQ_END = 0x04,
+ WMI_PER_DEST_RES_PARAM_UPDATE = 0x08,
+};
+
+/* WMI_TOF_FTM_PER_DEST_RES_EVENTID */
+struct wmi_tof_ftm_per_dest_res_event {
+ /* FTM session ID */
+ __le32 session_id;
+ /* destination MAC address */
+ u8 dst_mac[WMI_MAC_LEN];
+ /* wmi_tof_ftm_per_dest_res_flags_e */
+ u8 flags;
+ /* wmi_tof_ftm_per_dest_res_status_e */
+ u8 status;
+ /* responder ASAP */
+ u8 responder_asap;
+ /* responder number of FTM per burst */
+ u8 responder_num_ftm_per_burst;
+ /* responder number of FTM burst exponent */
+ u8 responder_num_ftm_bursts_exp;
+ /* responder burst duration ,wmi_tof_burst_duration_e */
+ u8 responder_burst_duration;
+ /* responder burst period, indicate interval between two consecutive
+ * burst instances, in units of 100 ms
+ */
+ __le16 responder_burst_period;
+ /* receive burst counter */
+ __le16 bursts_cnt;
+ /* tsf of responder start burst */
+ __le32 tsf_sync;
+ /* actual received ftm per burst */
+ u8 actual_ftm_per_burst;
+ u8 reserved0[7];
+ struct wmi_responder_ftm_res responder_ftm_res[0];
+} __packed;
+
+enum wmi_tof_channel_info_type {
+ WMI_TOF_CHANNEL_INFO_AOA = 0x00,
+ WMI_TOF_CHANNEL_INFO_LCI = 0x01,
+ WMI_TOF_CHANNEL_INFO_LCR = 0x02,
+ WMI_TOF_CHANNEL_INFO_VENDOR_SPECIFIC = 0x03,
+ WMI_TOF_CHANNEL_INFO_CIR = 0x04,
+ WMI_TOF_CHANNEL_INFO_RSSI = 0x05,
+ WMI_TOF_CHANNEL_INFO_SNR = 0x06,
+ WMI_TOF_CHANNEL_INFO_DEBUG = 0x07,
+};
+
+/* WMI_TOF_CHANNEL_INFO_EVENTID */
+struct wmi_tof_channel_info_event {
+ /* FTM session ID */
+ __le32 session_id;
+ /* destination MAC address */
+ u8 dst_mac[WMI_MAC_LEN];
+ /* wmi_tof_channel_info_type_e */
+ u8 type;
+ /* data report length */
+ u8 len;
+ /* data report payload */
+ u8 report[0];
+} __packed;
+
#endif /* __WILOCITY_WMI_H__ */
diff --git a/drivers/net/wireless/cnss/cnss_common.c b/drivers/net/wireless/cnss/cnss_common.c
index f63e958b1205..7805882aa6fe 100644
--- a/drivers/net/wireless/cnss/cnss_common.c
+++ b/drivers/net/wireless/cnss/cnss_common.c
@@ -24,6 +24,31 @@
#include "cnss_common.h"
#include <net/cfg80211.h>
+#define AR6320_REV1_VERSION 0x5000000
+#define AR6320_REV1_1_VERSION 0x5000001
+#define AR6320_REV1_3_VERSION 0x5000003
+#define AR6320_REV2_1_VERSION 0x5010000
+#define AR6320_REV3_VERSION 0x5020000
+#define AR6320_REV3_2_VERSION 0x5030000
+#define AR900B_DEV_VERSION 0x1000000
+#define QCA9377_REV1_1_VERSION 0x5020001
+
+static struct cnss_fw_files FW_FILES_QCA6174_FW_1_1 = {
+ "qwlan11.bin", "bdwlan11.bin", "otp11.bin", "utf11.bin",
+ "utfbd11.bin", "epping11.bin", "evicted11.bin"};
+static struct cnss_fw_files FW_FILES_QCA6174_FW_2_0 = {
+ "qwlan20.bin", "bdwlan20.bin", "otp20.bin", "utf20.bin",
+ "utfbd20.bin", "epping20.bin", "evicted20.bin"};
+static struct cnss_fw_files FW_FILES_QCA6174_FW_1_3 = {
+ "qwlan13.bin", "bdwlan13.bin", "otp13.bin", "utf13.bin",
+ "utfbd13.bin", "epping13.bin", "evicted13.bin"};
+static struct cnss_fw_files FW_FILES_QCA6174_FW_3_0 = {
+ "qwlan30.bin", "bdwlan30.bin", "otp30.bin", "utf30.bin",
+ "utfbd30.bin", "epping30.bin", "evicted30.bin"};
+static struct cnss_fw_files FW_FILES_DEFAULT = {
+ "qwlan.bin", "bdwlan.bin", "otp.bin", "utf.bin",
+ "utfbd.bin", "epping.bin", "evicted.bin"};
+
enum cnss_dev_bus_type {
CNSS_BUS_NONE = -1,
CNSS_BUS_PCI,
@@ -424,3 +449,49 @@ int cnss_power_down(struct device *dev)
return ret;
}
EXPORT_SYMBOL(cnss_power_down);
+
+void cnss_get_qca9377_fw_files(struct cnss_fw_files *pfw_files,
+ u32 size, u32 tufello_dual_fw)
+{
+ if (tufello_dual_fw)
+ memcpy(pfw_files, &FW_FILES_DEFAULT, sizeof(*pfw_files));
+ else
+ memcpy(pfw_files, &FW_FILES_QCA6174_FW_3_0, sizeof(*pfw_files));
+}
+EXPORT_SYMBOL(cnss_get_qca9377_fw_files);
+
+int cnss_get_fw_files_for_target(struct cnss_fw_files *pfw_files,
+ u32 target_type, u32 target_version)
+{
+ if (!pfw_files)
+ return -ENODEV;
+
+ switch (target_version) {
+ case AR6320_REV1_VERSION:
+ case AR6320_REV1_1_VERSION:
+ memcpy(pfw_files, &FW_FILES_QCA6174_FW_1_1, sizeof(*pfw_files));
+ break;
+ case AR6320_REV1_3_VERSION:
+ memcpy(pfw_files, &FW_FILES_QCA6174_FW_1_3, sizeof(*pfw_files));
+ break;
+ case AR6320_REV2_1_VERSION:
+ memcpy(pfw_files, &FW_FILES_QCA6174_FW_2_0, sizeof(*pfw_files));
+ break;
+ case AR6320_REV3_VERSION:
+ case AR6320_REV3_2_VERSION:
+ memcpy(pfw_files, &FW_FILES_QCA6174_FW_3_0, sizeof(*pfw_files));
+ break;
+ default:
+ memcpy(pfw_files, &FW_FILES_DEFAULT, sizeof(*pfw_files));
+ pr_err("%s default version 0x%X 0x%X", __func__,
+ target_type, target_version);
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(cnss_get_fw_files_for_target);
+
+const char *cnss_wlan_get_evicted_data_file(void)
+{
+ return FW_FILES_QCA6174_FW_3_0.evicted_data;
+}
diff --git a/drivers/net/wireless/cnss/cnss_common.h b/drivers/net/wireless/cnss/cnss_common.h
index 0d299f3b6208..07ef9844b6db 100644
--- a/drivers/net/wireless/cnss/cnss_common.h
+++ b/drivers/net/wireless/cnss/cnss_common.h
@@ -41,4 +41,5 @@ int cnss_sdio_power_up(struct device *dev);
int cnss_sdio_power_down(struct device *dev);
int cnss_pcie_power_up(struct device *dev);
int cnss_pcie_power_down(struct device *dev);
+const char *cnss_wlan_get_evicted_data_file(void);
#endif /* _NET_CNSS_COMMON_H_ */
diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c
index 5d9329168699..ec6955452391 100644
--- a/drivers/net/wireless/cnss/cnss_pci.c
+++ b/drivers/net/wireless/cnss/cnss_pci.c
@@ -78,29 +78,6 @@
#define QCA6174_FW_3_0 (0x30)
#define QCA6174_FW_3_2 (0x32)
#define BEELINER_FW (0x00)
-#define AR6320_REV1_VERSION 0x5000000
-#define AR6320_REV1_1_VERSION 0x5000001
-#define AR6320_REV1_3_VERSION 0x5000003
-#define AR6320_REV2_1_VERSION 0x5010000
-#define AR6320_REV3_VERSION 0x5020000
-#define AR6320_REV3_2_VERSION 0x5030000
-#define AR900B_DEV_VERSION 0x1000000
-
-static struct cnss_fw_files FW_FILES_QCA6174_FW_1_1 = {
-"qwlan11.bin", "bdwlan11.bin", "otp11.bin", "utf11.bin",
-"utfbd11.bin", "epping11.bin", "evicted11.bin"};
-static struct cnss_fw_files FW_FILES_QCA6174_FW_2_0 = {
-"qwlan20.bin", "bdwlan20.bin", "otp20.bin", "utf20.bin",
-"utfbd20.bin", "epping20.bin", "evicted20.bin"};
-static struct cnss_fw_files FW_FILES_QCA6174_FW_1_3 = {
-"qwlan13.bin", "bdwlan13.bin", "otp13.bin", "utf13.bin",
-"utfbd13.bin", "epping13.bin", "evicted13.bin"};
-static struct cnss_fw_files FW_FILES_QCA6174_FW_3_0 = {
-"qwlan30.bin", "bdwlan30.bin", "otp30.bin", "utf30.bin",
-"utfbd30.bin", "epping30.bin", "evicted30.bin"};
-static struct cnss_fw_files FW_FILES_DEFAULT = {
-"qwlan.bin", "bdwlan.bin", "otp.bin", "utf.bin",
-"utfbd.bin", "epping.bin", "evicted.bin"};
#define QCA6180_VENDOR_ID (0x168C)
#define QCA6180_DEVICE_ID (0x0041)
@@ -1093,37 +1070,6 @@ int cnss_get_fw_files(struct cnss_fw_files *pfw_files)
}
EXPORT_SYMBOL(cnss_get_fw_files);
-int cnss_get_fw_files_for_target(struct cnss_fw_files *pfw_files,
- u32 target_type, u32 target_version)
-{
- if (!pfw_files)
- return -ENODEV;
-
- switch (target_version) {
- case AR6320_REV1_VERSION:
- case AR6320_REV1_1_VERSION:
- memcpy(pfw_files, &FW_FILES_QCA6174_FW_1_1, sizeof(*pfw_files));
- break;
- case AR6320_REV1_3_VERSION:
- memcpy(pfw_files, &FW_FILES_QCA6174_FW_1_3, sizeof(*pfw_files));
- break;
- case AR6320_REV2_1_VERSION:
- memcpy(pfw_files, &FW_FILES_QCA6174_FW_2_0, sizeof(*pfw_files));
- break;
- case AR6320_REV3_VERSION:
- case AR6320_REV3_2_VERSION:
- memcpy(pfw_files, &FW_FILES_QCA6174_FW_3_0, sizeof(*pfw_files));
- break;
- default:
- memcpy(pfw_files, &FW_FILES_DEFAULT, sizeof(*pfw_files));
- pr_err("%s version mismatch 0x%X 0x%X",
- __func__, target_type, target_version);
- break;
- }
- return 0;
-}
-EXPORT_SYMBOL(cnss_get_fw_files_for_target);
-
#ifdef CONFIG_CNSS_SECURE_FW
static void cnss_wlan_fw_mem_alloc(struct pci_dev *pdev)
{
@@ -2058,7 +2004,7 @@ static void cnss_wlan_memory_expansion(void)
{
struct device *dev;
const struct firmware *fw_entry;
- const char *filename = FW_FILES_QCA6174_FW_3_0.evicted_data;
+ const char *filename;
u_int32_t fw_entry_size, size_left, dma_size_left, length;
char *fw_temp;
char *fw_data;
@@ -2067,6 +2013,7 @@ static void cnss_wlan_memory_expansion(void)
u_int32_t total_length = 0;
struct pci_dev *pdev;
+ filename = cnss_wlan_get_evicted_data_file();
pdev = penv->pdev;
dev = &pdev->dev;
cnss_seg_info = penv->cnss_seg_info;
@@ -3452,6 +3399,9 @@ int cnss_pm_runtime_request(struct device *dev,
case CNSS_PM_REQUEST_RESUME:
ret = pm_request_resume(dev);
break;
+ case CNSS_PM_GET_NORESUME:
+ pm_runtime_get_noresume(dev);
+ break;
default:
ret = -EINVAL;
break;
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 3f186137e730..14af8ca66d1c 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/of_gpio.h>
#include <linux/clk/msm-clk.h>
+#include <linux/reset.h>
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
#include <linux/debugfs.h>
@@ -298,7 +299,7 @@
#define MAX_PROP_SIZE 32
#define MAX_RC_NAME_LEN 15
#define MSM_PCIE_MAX_VREG 4
-#define MSM_PCIE_MAX_CLK 13
+#define MSM_PCIE_MAX_CLK 9
#define MSM_PCIE_MAX_PIPE_CLK 1
#define MAX_RC_NUM 3
#define MAX_DEVICE_NUM 20
@@ -312,6 +313,9 @@
#define PCIE_CLEAR 0xDEADBEEF
#define PCIE_LINK_DOWN 0xFFFFFFFF
+#define MSM_PCIE_MAX_RESET 4
+#define MSM_PCIE_MAX_PIPE_RESET 1
+
#define MSM_PCIE_MSI_PHY 0xa0000000
#define PCIE20_MSI_CTRL_ADDR (0x820)
#define PCIE20_MSI_CTRL_UPPER_ADDR (0x824)
@@ -496,6 +500,13 @@ struct msm_pcie_vreg_info_t {
bool required;
};
+/* reset info structure */
+struct msm_pcie_reset_info_t {
+ struct reset_control *hdl;
+ char *name;
+ bool required;
+};
+
/* clock info structure */
struct msm_pcie_clk_info_t {
struct clk *hdl;
@@ -552,6 +563,8 @@ struct msm_pcie_dev_t {
struct msm_pcie_res_info_t res[MSM_PCIE_MAX_RES];
struct msm_pcie_irq_info_t irq[MSM_PCIE_MAX_IRQ];
struct msm_pcie_irq_info_t msi[MSM_PCIE_MAX_MSI];
+ struct msm_pcie_reset_info_t reset[MSM_PCIE_MAX_RESET];
+ struct msm_pcie_reset_info_t pipe_reset[MSM_PCIE_MAX_PIPE_RESET];
void __iomem *parf;
void __iomem *phy;
@@ -609,6 +622,8 @@ struct msm_pcie_dev_t {
uint32_t cpl_timeout;
uint32_t current_bdf;
short current_short_bdf;
+ uint32_t perst_delay_us_min;
+ uint32_t perst_delay_us_max;
uint32_t tlp_rd_size;
bool linkdown_panic;
bool ep_wakeirq;
@@ -707,6 +722,43 @@ static struct msm_pcie_gpio_info_t msm_pcie_gpio_info[MSM_PCIE_MAX_GPIO] = {
{"qcom,ep-gpio", 0, 1, 1, 0, 0}
};
+/* resets */
+static struct msm_pcie_reset_info_t
+msm_pcie_reset_info[MAX_RC_NUM][MSM_PCIE_MAX_RESET] = {
+ {
+ {NULL, "pcie_phy_reset", false},
+ {NULL, "pcie_phy_com_reset", false},
+ {NULL, "pcie_phy_nocsr_com_phy_reset", false},
+ {NULL, "pcie_0_phy_reset", false}
+ },
+ {
+ {NULL, "pcie_phy_reset", false},
+ {NULL, "pcie_phy_com_reset", false},
+ {NULL, "pcie_phy_nocsr_com_phy_reset", false},
+ {NULL, "pcie_1_phy_reset", false}
+ },
+ {
+ {NULL, "pcie_phy_reset", false},
+ {NULL, "pcie_phy_com_reset", false},
+ {NULL, "pcie_phy_nocsr_com_phy_reset", false},
+ {NULL, "pcie_2_phy_reset", false}
+ }
+};
+
+/* pipe reset */
+static struct msm_pcie_reset_info_t
+msm_pcie_pipe_reset_info[MAX_RC_NUM][MSM_PCIE_MAX_PIPE_RESET] = {
+ {
+ {NULL, "pcie_0_phy_pipe_reset", false}
+ },
+ {
+ {NULL, "pcie_1_phy_pipe_reset", false}
+ },
+ {
+ {NULL, "pcie_2_phy_pipe_reset", false}
+ }
+};
+
/* clocks */
static struct msm_pcie_clk_info_t
msm_pcie_clk_info[MAX_RC_NUM][MSM_PCIE_MAX_CLK] = {
@@ -719,11 +771,7 @@ static struct msm_pcie_clk_info_t
{NULL, "pcie_0_ldo", 0, false, true},
{NULL, "pcie_0_smmu_clk", 0, false, false},
{NULL, "pcie_phy_cfg_ahb_clk", 0, false, false},
- {NULL, "pcie_phy_aux_clk", 0, false, false},
- {NULL, "pcie_phy_reset", 0, false, false},
- {NULL, "pcie_phy_com_reset", 0, false, false},
- {NULL, "pcie_phy_nocsr_com_phy_reset", 0, false, false},
- {NULL, "pcie_0_phy_reset", 0, false, true}
+ {NULL, "pcie_phy_aux_clk", 0, false, false}
},
{
{NULL, "pcie_1_ref_clk_src", 0, false, false},
@@ -734,11 +782,7 @@ static struct msm_pcie_clk_info_t
{NULL, "pcie_1_ldo", 0, false, true},
{NULL, "pcie_1_smmu_clk", 0, false, false},
{NULL, "pcie_phy_cfg_ahb_clk", 0, false, false},
- {NULL, "pcie_phy_aux_clk", 0, false, false},
- {NULL, "pcie_phy_reset", 0, false, false},
- {NULL, "pcie_phy_com_reset", 0, false, false},
- {NULL, "pcie_phy_nocsr_com_phy_reset", 0, false, false},
- {NULL, "pcie_1_phy_reset", 0, false, true}
+ {NULL, "pcie_phy_aux_clk", 0, false, false}
},
{
{NULL, "pcie_2_ref_clk_src", 0, false, false},
@@ -749,11 +793,7 @@ static struct msm_pcie_clk_info_t
{NULL, "pcie_2_ldo", 0, false, true},
{NULL, "pcie_2_smmu_clk", 0, false, false},
{NULL, "pcie_phy_cfg_ahb_clk", 0, false, false},
- {NULL, "pcie_phy_aux_clk", 0, false, false},
- {NULL, "pcie_phy_reset", 0, false, false},
- {NULL, "pcie_phy_com_reset", 0, false, false},
- {NULL, "pcie_phy_nocsr_com_phy_reset", 0, false, false},
- {NULL, "pcie_2_phy_reset", 0, false, true}
+ {NULL, "pcie_phy_aux_clk", 0, false, false}
}
};
@@ -1720,7 +1760,8 @@ static inline int msm_pcie_check_align(struct msm_pcie_dev_t *dev,
static bool msm_pcie_confirm_linkup(struct msm_pcie_dev_t *dev,
bool check_sw_stts,
- bool check_ep)
+ bool check_ep,
+ void __iomem *ep_conf)
{
u32 val;
@@ -1747,7 +1788,7 @@ static bool msm_pcie_confirm_linkup(struct msm_pcie_dev_t *dev,
}
if (check_ep) {
- val = readl_relaxed(dev->conf);
+ val = readl_relaxed(ep_conf);
PCIE_DBG(dev,
"PCIe: device ID and vender ID of EP of RC %d are 0x%x.\n",
dev->rc_idx, val);
@@ -1776,6 +1817,10 @@ static void msm_pcie_cfg_recover(struct msm_pcie_dev_t *dev, bool rc)
cfg = dev->dm_core;
shadow = dev->rc_shadow;
} else {
+ if (!msm_pcie_confirm_linkup(dev, false, true,
+ dev->pcidev_table[i].conf_base))
+ continue;
+
shadow = dev->ep_shadow[i];
PCIE_DBG(dev,
"PCIe Device: %02x:%02x.%01x\n",
@@ -1935,6 +1980,10 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
dev->cpl_timeout);
PCIE_DBG_FS(dev, "current_bdf: 0x%x\n",
dev->current_bdf);
+ PCIE_DBG_FS(dev, "perst_delay_us_min: %dus\n",
+ dev->perst_delay_us_min);
+ PCIE_DBG_FS(dev, "perst_delay_us_max: %dus\n",
+ dev->perst_delay_us_max);
PCIE_DBG_FS(dev, "tlp_rd_size: 0x%x\n",
dev->tlp_rd_size);
PCIE_DBG_FS(dev, "rc_corr_counter: %lu\n",
@@ -3282,7 +3331,7 @@ static struct pci_ops msm_pcie_ops = {
static int msm_pcie_gpio_init(struct msm_pcie_dev_t *dev)
{
- int rc, i;
+ int rc = 0, i;
struct msm_pcie_gpio_info_t *info;
PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
@@ -3431,6 +3480,7 @@ static int msm_pcie_clk_init(struct msm_pcie_dev_t *dev)
{
int i, rc = 0;
struct msm_pcie_clk_info_t *info;
+ struct msm_pcie_reset_info_t *reset_info;
PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
@@ -3474,9 +3524,6 @@ static int msm_pcie_clk_init(struct msm_pcie_dev_t *dev)
if (!info->hdl)
continue;
- if (i >= MSM_PCIE_MAX_CLK - (dev->common_phy ? 4 : 1))
- clk_reset(info->hdl, CLK_RESET_DEASSERT);
-
if (info->config_mem)
msm_pcie_config_clock_mem(dev, info);
@@ -3519,6 +3566,21 @@ static int msm_pcie_clk_init(struct msm_pcie_dev_t *dev)
regulator_disable(dev->gdsc);
}
+ for (i = 0; i < MSM_PCIE_MAX_RESET; i++) {
+ reset_info = &dev->reset[i];
+ if (reset_info->hdl) {
+ rc = reset_control_deassert(reset_info->hdl);
+ if (rc)
+ PCIE_ERR(dev,
+ "PCIe: RC%d failed to deassert reset for %s.\n",
+ dev->rc_idx, reset_info->name);
+ else
+ PCIE_DBG2(dev,
+ "PCIe: RC%d successfully deasserted reset for %s.\n",
+ dev->rc_idx, reset_info->name);
+ }
+ }
+
PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
return rc;
@@ -3562,6 +3624,7 @@ static int msm_pcie_pipe_clk_init(struct msm_pcie_dev_t *dev)
{
int i, rc = 0;
struct msm_pcie_clk_info_t *info;
+ struct msm_pcie_reset_info_t *pipe_reset_info;
PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
@@ -3571,7 +3634,6 @@ static int msm_pcie_pipe_clk_init(struct msm_pcie_dev_t *dev)
if (!info->hdl)
continue;
- clk_reset(info->hdl, CLK_RESET_DEASSERT);
if (info->config_mem)
msm_pcie_config_clock_mem(dev, info);
@@ -3608,6 +3670,22 @@ static int msm_pcie_pipe_clk_init(struct msm_pcie_dev_t *dev)
clk_disable_unprepare(dev->pipeclk[i].hdl);
}
+ for (i = 0; i < MSM_PCIE_MAX_PIPE_RESET; i++) {
+ pipe_reset_info = &dev->pipe_reset[i];
+ if (pipe_reset_info->hdl) {
+ rc = reset_control_deassert(
+ pipe_reset_info->hdl);
+ if (rc)
+ PCIE_ERR(dev,
+ "PCIe: RC%d failed to deassert pipe reset for %s.\n",
+ dev->rc_idx, pipe_reset_info->name);
+ else
+ PCIE_DBG2(dev,
+ "PCIe: RC%d successfully deasserted pipe reset for %s.\n",
+ dev->rc_idx, pipe_reset_info->name);
+ }
+ }
+
PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
return rc;
@@ -3945,6 +4023,8 @@ static int msm_pcie_get_resources(struct msm_pcie_dev_t *dev,
struct msm_pcie_res_info_t *res_info;
struct msm_pcie_irq_info_t *irq_info;
struct msm_pcie_irq_info_t *msi_info;
+ struct msm_pcie_reset_info_t *reset_info;
+ struct msm_pcie_reset_info_t *pipe_reset_info;
char prop_name[MAX_PROP_SIZE];
const __be32 *prop;
u32 *clkfreq = NULL;
@@ -4161,6 +4241,54 @@ static int msm_pcie_get_resources(struct msm_pcie_dev_t *dev,
}
}
+ for (i = 0; i < MSM_PCIE_MAX_RESET; i++) {
+ reset_info = &dev->reset[i];
+
+ reset_info->hdl = devm_reset_control_get(&pdev->dev,
+ reset_info->name);
+
+ if (IS_ERR(reset_info->hdl)) {
+ if (reset_info->required) {
+ PCIE_DBG(dev,
+ "Reset %s isn't available:%ld\n",
+ reset_info->name,
+ PTR_ERR(reset_info->hdl));
+
+ ret = PTR_ERR(reset_info->hdl);
+ reset_info->hdl = NULL;
+ goto out;
+ } else {
+ PCIE_DBG(dev, "Ignoring Reset %s\n",
+ reset_info->name);
+ reset_info->hdl = NULL;
+ }
+ }
+ }
+
+ for (i = 0; i < MSM_PCIE_MAX_PIPE_RESET; i++) {
+ pipe_reset_info = &dev->pipe_reset[i];
+
+ pipe_reset_info->hdl = devm_reset_control_get(&pdev->dev,
+ pipe_reset_info->name);
+
+ if (IS_ERR(pipe_reset_info->hdl)) {
+ if (pipe_reset_info->required) {
+ PCIE_DBG(dev,
+ "Pipe Reset %s isn't available:%ld\n",
+ pipe_reset_info->name,
+ PTR_ERR(pipe_reset_info->hdl));
+
+ ret = PTR_ERR(pipe_reset_info->hdl);
+ pipe_reset_info->hdl = NULL;
+ goto out;
+ } else {
+ PCIE_DBG(dev, "Ignoring Pipe Reset %s\n",
+ pipe_reset_info->name);
+ pipe_reset_info->hdl = NULL;
+ }
+ }
+ }
+
dev->bus_scale_table = msm_bus_cl_get_pdata(pdev);
if (!dev->bus_scale_table) {
PCIE_DBG(dev, "PCIe: No bus scale table for RC%d (%s)\n",
@@ -4427,8 +4555,7 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options)
dev->rc_idx);
gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
1 - dev->gpio[MSM_PCIE_GPIO_PERST].on);
- usleep_range(PERST_PROPAGATION_DELAY_US_MIN,
- PERST_PROPAGATION_DELAY_US_MAX);
+ usleep_range(dev->perst_delay_us_min, dev->perst_delay_us_max);
/* set max tlp read size */
msm_pcie_write_reg_field(dev->dm_core, PCIE20_DEVICE_CONTROL_STATUS,
@@ -4444,11 +4571,11 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options)
usleep_range(LINK_UP_TIMEOUT_US_MIN, LINK_UP_TIMEOUT_US_MAX);
val = readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS);
} while ((!(val & XMLH_LINK_UP) ||
- !msm_pcie_confirm_linkup(dev, false, false))
+ !msm_pcie_confirm_linkup(dev, false, false, NULL))
&& (link_check_count++ < LINK_UP_CHECK_MAX_COUNT));
if ((val & XMLH_LINK_UP) &&
- msm_pcie_confirm_linkup(dev, false, false)) {
+ msm_pcie_confirm_linkup(dev, false, false, NULL)) {
PCIE_DBG(dev, "Link is up after %d checkings\n",
link_check_count);
PCIE_INFO(dev, "PCIe RC%d link initialized\n", dev->rc_idx);
@@ -5956,6 +6083,34 @@ static int msm_pcie_probe(struct platform_device *pdev)
PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: cpl-timeout: 0x%x.\n",
rc_idx, msm_pcie_dev[rc_idx].cpl_timeout);
+ msm_pcie_dev[rc_idx].perst_delay_us_min =
+ PERST_PROPAGATION_DELAY_US_MIN;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,perst-delay-us-min",
+ &msm_pcie_dev[rc_idx].perst_delay_us_min);
+ if (ret)
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: perst-delay-us-min does not exist. Use default value %dus.\n",
+ rc_idx, msm_pcie_dev[rc_idx].perst_delay_us_min);
+ else
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: perst-delay-us-min: %dus.\n",
+ rc_idx, msm_pcie_dev[rc_idx].perst_delay_us_min);
+
+ msm_pcie_dev[rc_idx].perst_delay_us_max =
+ PERST_PROPAGATION_DELAY_US_MAX;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,perst-delay-us-max",
+ &msm_pcie_dev[rc_idx].perst_delay_us_max);
+ if (ret)
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: perst-delay-us-max does not exist. Use default value %dus.\n",
+ rc_idx, msm_pcie_dev[rc_idx].perst_delay_us_max);
+ else
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: perst-delay-us-max: %dus.\n",
+ rc_idx, msm_pcie_dev[rc_idx].perst_delay_us_max);
+
msm_pcie_dev[rc_idx].tlp_rd_size = PCIE_TLP_RD_SIZE;
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,tlp-rd-size",
@@ -6052,6 +6207,11 @@ static int msm_pcie_probe(struct platform_device *pdev)
sizeof(msm_pcie_irq_info));
memcpy(msm_pcie_dev[rc_idx].msi, msm_pcie_msi_info,
sizeof(msm_pcie_msi_info));
+ memcpy(msm_pcie_dev[rc_idx].reset, msm_pcie_reset_info[rc_idx],
+ sizeof(msm_pcie_reset_info[rc_idx]));
+ memcpy(msm_pcie_dev[rc_idx].pipe_reset,
+ msm_pcie_pipe_reset_info[rc_idx],
+ sizeof(msm_pcie_pipe_reset_info[rc_idx]));
msm_pcie_dev[rc_idx].shadow_en = true;
for (i = 0; i < PCIE_CONF_SPACE_DW; i++)
msm_pcie_dev[rc_idx].rc_shadow[i] = PCIE_CLEAR;
@@ -6329,7 +6489,8 @@ static int msm_pcie_pm_suspend(struct pci_dev *dev,
}
if (dev && !(options & MSM_PCIE_CONFIG_NO_CFG_RESTORE)
- && msm_pcie_confirm_linkup(pcie_dev, true, true)) {
+ && msm_pcie_confirm_linkup(pcie_dev, true, true,
+ pcie_dev->conf)) {
ret = pci_save_state(dev);
pcie_dev->saved_state = pci_store_saved_state(dev);
}
@@ -6838,7 +6999,7 @@ int msm_pcie_recover_config(struct pci_dev *dev)
return -ENODEV;
}
- if (msm_pcie_confirm_linkup(pcie_dev, true, true)) {
+ if (msm_pcie_confirm_linkup(pcie_dev, true, true, pcie_dev->conf)) {
PCIE_DBG(pcie_dev,
"Recover config space of RC%d and its EP\n",
pcie_dev->rc_idx);
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
index b2c58430785a..e0cab3a683d6 100644
--- a/drivers/phy/phy-qcom-ufs.c
+++ b/drivers/phy/phy-qcom-ufs.c
@@ -191,27 +191,20 @@ ufs_qcom_phy_init_clks(struct phy *generic_phy,
struct ufs_qcom_phy *phy_common)
{
int err;
- struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
- err = ufs_qcom_phy_clk_get(generic_phy, "tx_iface_clk",
- &phy_common->tx_iface_clk);
/*
* tx_iface_clk does not exist in newer version of ufs-phy HW,
* so don't return error if it is not found
*/
- if (err)
- dev_dbg(phy->dev, "%s: failed to get tx_iface_clk\n",
- __func__);
+ __ufs_qcom_phy_clk_get(generic_phy, "tx_iface_clk",
+ &phy_common->tx_iface_clk, false);
- err = ufs_qcom_phy_clk_get(generic_phy, "rx_iface_clk",
- &phy_common->rx_iface_clk);
/*
* rx_iface_clk does not exist in newer version of ufs-phy HW,
* so don't return error if it is not found
*/
- if (err)
- dev_dbg(phy->dev, "%s: failed to get rx_iface_clk\n",
- __func__);
+ __ufs_qcom_phy_clk_get(generic_phy, "rx_iface_clk",
+ &phy_common->rx_iface_clk, false);
err = ufs_qcom_phy_clk_get(generic_phy, "ref_clk_src",
&phy_common->ref_clk_src);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 695c8bc4cbc0..16f50030b960 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -3152,7 +3152,7 @@ static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in,
} else if (in->client ==
IPA_CLIENT_APPS_WAN_CONS) {
sys->pyld_hdlr = ipa_wan_rx_pyld_hdlr;
- if (in->napi_enabled) {
+ if (in->recycle_enabled) {
sys->repl_hdlr =
ipa_replenish_rx_cache_recycle;
sys->rx_pool_sz =
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index 581a5f9d8a2e..fec4d5484d28 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -41,8 +41,6 @@
#define MTU_BYTE 1500
#define IPA_MAX_NUM_PIPES 0x14
-#define IPA_WAN_CONS_DESC_FIFO_SZ 0x5E80
-#define IPA_WAN_NAPI_CONS_RX_POOL_SZ 3000
#define IPA_SYS_DESC_FIFO_SZ 0x2000
#define IPA_SYS_TX_DATA_DESC_FIFO_SZ 0x1000
#define IPA_LAN_RX_HEADER_LENGTH (2)
@@ -53,6 +51,8 @@
#define IPA_UC_FINISH_MAX 6
#define IPA_UC_WAIT_MIN_SLEEP 1000
#define IPA_UC_WAII_MAX_SLEEP 1200
+#define IPA_WAN_NAPI_CONS_RX_POOL_SZ (IPA_GENERIC_RX_POOL_SZ*3)
+#define IPA_WAN_CONS_DESC_FIFO_SZ (IPA_SYS_DESC_FIFO_SZ*3)
#define IPA_MAX_STATUS_STAT_NUM 30
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/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 1be9a6745531..ebb93e246048 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -34,6 +34,8 @@
#include <linux/rmnet_ipa_fd_ioctl.h>
#include <linux/ipa.h>
#include <uapi/linux/net_map.h>
+#include <uapi/linux/msm_rmnet.h>
+#include <net/rmnet_config.h>
#include "ipa_trace.h"
@@ -1231,6 +1233,79 @@ static void apps_ipa_packet_receive_notify(void *priv,
}
+static int handle_ingress_format(struct net_device *dev,
+ struct rmnet_ioctl_extended_s *in)
+{
+ int ret = 0;
+ struct rmnet_phys_ep_conf_s *ep_cfg;
+
+ IPAWANDBG("Get RMNET_IOCTL_SET_INGRESS_DATA_FORMAT\n");
+ if ((in->u.data) & RMNET_IOCTL_INGRESS_FORMAT_CHECKSUM)
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.cfg.cs_offload_en =
+ IPA_ENABLE_CS_OFFLOAD_DL;
+
+ if ((in->u.data) & RMNET_IOCTL_INGRESS_FORMAT_AGG_DATA) {
+ IPAWANERR("get AGG size %d count %d\n",
+ in->u.ingress_format.agg_size,
+ in->u.ingress_format.agg_count);
+
+ ret = ipa_disable_apps_wan_cons_deaggr(
+ in->u.ingress_format.agg_size,
+ in->u.ingress_format.agg_count);
+
+ if (!ret) {
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.aggr.aggr_byte_limit =
+ in->u.ingress_format.agg_size;
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.aggr.aggr_pkt_limit =
+ in->u.ingress_format.agg_count;
+
+ ipa_to_apps_ep_cfg.recycle_enabled = true;
+ ep_cfg = (struct rmnet_phys_ep_conf_s *)
+ rcu_dereference(dev->rx_handler_data);
+ ep_cfg->recycle = ipa_recycle_wan_skb;
+ pr_info("Wan Recycle Enabled\n");
+ }
+ }
+
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.hdr_len = 4;
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1;
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.hdr_ofst_metadata = 1;
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 2;
+
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.hdr_total_len_or_pad_valid = true;
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.hdr_total_len_or_pad = 0;
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.hdr_payload_len_inc_padding =
+ true;
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.hdr_total_len_or_pad_offset = 0;
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.hdr_little_endian = 0;
+ ipa_to_apps_ep_cfg.ipa_ep_cfg.metadata_mask.metadata_mask = 0xFF000000;
+
+ ipa_to_apps_ep_cfg.client = IPA_CLIENT_APPS_WAN_CONS;
+ ipa_to_apps_ep_cfg.notify = apps_ipa_packet_receive_notify;
+ ipa_to_apps_ep_cfg.priv = dev;
+
+ ipa_to_apps_ep_cfg.napi_enabled = ipa_rmnet_res.ipa_napi_enable;
+ if (ipa_to_apps_ep_cfg.napi_enabled)
+ ipa_to_apps_ep_cfg.desc_fifo_sz = IPA_WAN_CONS_DESC_FIFO_SZ;
+ else
+ ipa_to_apps_ep_cfg.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
+
+ mutex_lock(&ipa_to_apps_pipe_handle_guard);
+ if (atomic_read(&is_ssr)) {
+ IPAWANDBG("In SSR sequence/recovery\n");
+ mutex_unlock(&ipa_to_apps_pipe_handle_guard);
+ return -EFAULT;
+ }
+ ret = ipa2_setup_sys_pipe(&ipa_to_apps_ep_cfg, &ipa_to_apps_hdl);
+ mutex_unlock(&ipa_to_apps_pipe_handle_guard);
+
+ if (ret)
+ IPAWANERR("failed to configure ingress\n");
+
+ return ret;
+}
+
/**
* ipa_wwan_ioctl() - I/O control for wwan network driver.
*
@@ -1531,83 +1606,7 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
break;
case RMNET_IOCTL_SET_INGRESS_DATA_FORMAT:/* Set IDF */
- IPAWANDBG("get RMNET_IOCTL_SET_INGRESS_DATA_FORMAT\n");
- if ((extend_ioctl_data.u.data) &
- RMNET_IOCTL_INGRESS_FORMAT_CHECKSUM)
- ipa_to_apps_ep_cfg.ipa_ep_cfg.cfg.
- cs_offload_en =
- IPA_ENABLE_CS_OFFLOAD_DL;
-
- if ((extend_ioctl_data.u.data) &
- RMNET_IOCTL_INGRESS_FORMAT_AGG_DATA) {
- IPAWANERR("get AGG size %d count %d\n",
- extend_ioctl_data.u.
- ingress_format.agg_size,
- extend_ioctl_data.u.
- ingress_format.agg_count);
- if (!ipa_disable_apps_wan_cons_deaggr(
- extend_ioctl_data.u.
- ingress_format.agg_size,
- extend_ioctl_data.
- u.ingress_format.agg_count)) {
- ipa_to_apps_ep_cfg.ipa_ep_cfg.aggr.
- aggr_byte_limit = extend_ioctl_data.
- u.ingress_format.agg_size;
- ipa_to_apps_ep_cfg.ipa_ep_cfg.aggr.
- aggr_pkt_limit = extend_ioctl_data.
- u.ingress_format.agg_count;
- }
- }
-
- ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.hdr_len = 4;
- ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.
- hdr_ofst_metadata_valid = 1;
- ipa_to_apps_ep_cfg.ipa_ep_cfg.
- hdr.hdr_ofst_metadata = 1;
- ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.
- hdr_ofst_pkt_size_valid = 1;
- ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.
- hdr_ofst_pkt_size = 2;
-
- ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.
- hdr_total_len_or_pad_valid = true;
- ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.
- hdr_total_len_or_pad = 0;
- ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.
- hdr_payload_len_inc_padding = true;
- ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.
- hdr_total_len_or_pad_offset = 0;
- ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.
- hdr_little_endian = 0;
- ipa_to_apps_ep_cfg.ipa_ep_cfg.metadata_mask.
- metadata_mask = 0xFF000000;
-
- ipa_to_apps_ep_cfg.client = IPA_CLIENT_APPS_WAN_CONS;
- ipa_to_apps_ep_cfg.notify =
- apps_ipa_packet_receive_notify;
- ipa_to_apps_ep_cfg.priv = dev;
-
- ipa_to_apps_ep_cfg.napi_enabled =
- ipa_rmnet_res.ipa_napi_enable;
- if (ipa_to_apps_ep_cfg.napi_enabled)
- ipa_to_apps_ep_cfg.desc_fifo_sz =
- IPA_WAN_CONS_DESC_FIFO_SZ;
- else
- ipa_to_apps_ep_cfg.desc_fifo_sz =
- IPA_SYS_DESC_FIFO_SZ;
-
- mutex_lock(&ipa_to_apps_pipe_handle_guard);
- if (atomic_read(&is_ssr)) {
- IPAWANDBG("In SSR sequence/recovery\n");
- mutex_unlock(&ipa_to_apps_pipe_handle_guard);
- rc = -EFAULT;
- break;
- }
- rc = ipa2_setup_sys_pipe(
- &ipa_to_apps_ep_cfg, &ipa_to_apps_hdl);
- mutex_unlock(&ipa_to_apps_pipe_handle_guard);
- if (rc)
- IPAWANERR("failed to configure ingress\n");
+ rc = handle_ingress_format(dev, &extend_ioctl_data);
break;
case RMNET_IOCTL_SET_XLAT_DEV_INFO:
wan_msg = kzalloc(sizeof(struct ipa_wan_msg),
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 1df2bc6b902c..24fbc5c738d8 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -38,6 +38,16 @@
#include <linux/hash.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/smem.h>
+#include <soc/qcom/scm.h>
+
+#ifdef CONFIG_ARM64
+
+/* Outer caches unsupported on ARM64 platforms */
+#define outer_flush_range(x, y)
+#define __cpuc_flush_dcache_area __flush_dcache_area
+
+#endif
+
#define IPA_SUBSYSTEM_NAME "ipa_fws"
#include "ipa_i.h"
#include "../ipa_rm_i.h"
@@ -62,7 +72,7 @@
#define IPA_AGGR_MAX_STR_LENGTH (10)
-#define CLEANUP_TAG_PROCESS_TIMEOUT 150
+#define CLEANUP_TAG_PROCESS_TIMEOUT 500
#define IPA_AGGR_STR_IN_BYTES(str) \
(strnlen((str), IPA_AGGR_MAX_STR_LENGTH - 1) + 1)
@@ -199,6 +209,21 @@ struct ipa3_ioc_nat_alloc_mem32 {
};
#endif
+#define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311
+#define TZ_MEM_PROTECT_REGION_ID 0x10
+
+struct tz_smmu_ipa_protect_region_iovec_s {
+ u64 input_addr;
+ u64 output_addr;
+ u64 size;
+ u32 attr;
+} __packed;
+
+struct tz_smmu_ipa_protect_region_s {
+ phys_addr_t iovec_buf;
+ u32 size_bytes;
+} __packed;
+
static void ipa3_start_tag_process(struct work_struct *work);
static DECLARE_WORK(ipa3_tag_work, ipa3_start_tag_process);
@@ -2213,7 +2238,7 @@ static int ipa3_q6_set_ex_path_to_apps(void)
}
}
- /* Will wait 150msecs for IPA tag process completion */
+ /* Will wait 500msecs for IPA tag process completion */
retval = ipa3_tag_process(desc, num_descs,
msecs_to_jiffies(CLEANUP_TAG_PROCESS_TIMEOUT));
if (retval) {
@@ -4036,6 +4061,53 @@ static ssize_t ipa3_write(struct file *file, const char __user *buf,
return count;
}
+static int ipa3_tz_unlock_reg(struct ipa3_context *ipa3_ctx)
+{
+ int i, size, ret, resp;
+ struct tz_smmu_ipa_protect_region_iovec_s *ipa_tz_unlock_vec;
+ struct tz_smmu_ipa_protect_region_s cmd_buf;
+
+ if (ipa3_ctx && ipa3_ctx->ipa_tz_unlock_reg_num > 0) {
+ size = ipa3_ctx->ipa_tz_unlock_reg_num *
+ sizeof(struct tz_smmu_ipa_protect_region_iovec_s);
+ ipa_tz_unlock_vec = kzalloc(PAGE_ALIGN(size), GFP_KERNEL);
+ if (ipa_tz_unlock_vec == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ipa3_ctx->ipa_tz_unlock_reg_num; i++) {
+ ipa_tz_unlock_vec[i].input_addr =
+ ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr ^
+ (ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr &
+ 0xFFF);
+ ipa_tz_unlock_vec[i].output_addr =
+ ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr ^
+ (ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr &
+ 0xFFF);
+ ipa_tz_unlock_vec[i].size =
+ ipa3_ctx->ipa_tz_unlock_reg[i].size;
+ ipa_tz_unlock_vec[i].attr = IPA_TZ_UNLOCK_ATTRIBUTE;
+ }
+
+ /* pass physical address of command buffer */
+ cmd_buf.iovec_buf = virt_to_phys((void *)ipa_tz_unlock_vec);
+ cmd_buf.size_bytes = size;
+
+ /* flush cache to DDR */
+ __cpuc_flush_dcache_area((void *)ipa_tz_unlock_vec, size);
+ outer_flush_range(cmd_buf.iovec_buf, cmd_buf.iovec_buf + size);
+
+ ret = scm_call(SCM_SVC_MP, TZ_MEM_PROTECT_REGION_ID, &cmd_buf,
+ sizeof(cmd_buf), &resp, sizeof(resp));
+ if (ret) {
+ IPAERR("scm call SCM_SVC_MP failed: %d\n", ret);
+ kfree(ipa_tz_unlock_vec);
+ return -EFAULT;
+ }
+ kfree(ipa_tz_unlock_vec);
+ }
+ return 0;
+}
+
/**
* ipa3_pre_init() - Initialize the IPA Driver.
* This part contains all initialization which doesn't require IPA HW, such
@@ -4120,6 +4192,27 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
ipa3_ctx->apply_rg10_wa = resource_p->apply_rg10_wa;
ipa3_ctx->gsi_ch20_wa = resource_p->gsi_ch20_wa;
ipa3_ctx->ipa3_active_clients_logging.log_rdy = false;
+ if (resource_p->ipa_tz_unlock_reg) {
+ ipa3_ctx->ipa_tz_unlock_reg_num =
+ resource_p->ipa_tz_unlock_reg_num;
+ ipa3_ctx->ipa_tz_unlock_reg = kcalloc(
+ ipa3_ctx->ipa_tz_unlock_reg_num,
+ sizeof(*ipa3_ctx->ipa_tz_unlock_reg),
+ GFP_KERNEL);
+ if (ipa3_ctx->ipa_tz_unlock_reg == NULL) {
+ result = -ENOMEM;
+ goto fail_tz_unlock_reg;
+ }
+ for (i = 0; i < ipa3_ctx->ipa_tz_unlock_reg_num; i++) {
+ ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr =
+ resource_p->ipa_tz_unlock_reg[i].reg_addr;
+ ipa3_ctx->ipa_tz_unlock_reg[i].size =
+ resource_p->ipa_tz_unlock_reg[i].size;
+ }
+ }
+
+ /* unlock registers for uc */
+ ipa3_tz_unlock_reg(ipa3_ctx);
/* default aggregation parameters */
ipa3_ctx->aggregation_type = IPA_MBIM_16;
@@ -4568,6 +4661,8 @@ fail_init_mem_partition:
fail_bind:
kfree(ipa3_ctx->ctrl);
fail_mem_ctrl:
+ kfree(ipa3_ctx->ipa_tz_unlock_reg);
+fail_tz_unlock_reg:
ipc_log_context_destroy(ipa3_ctx->logbuf);
fail_logbuf:
kfree(ipa3_ctx);
@@ -4579,8 +4674,10 @@ fail_mem_ctx:
static int get_ipa_dts_configuration(struct platform_device *pdev,
struct ipa3_plat_drv_res *ipa_drv_res)
{
- int result;
+ int i, result, pos;
struct resource *resource;
+ u32 *ipa_tz_unlock_reg;
+ int elem_num;
/* initialize ipa3_res */
ipa_drv_res->ipa_pipe_mem_start_ofst = IPA_PIPE_MEM_START_OFST;
@@ -4595,6 +4692,8 @@ static int get_ipa_dts_configuration(struct platform_device *pdev,
ipa_drv_res->lan_rx_ring_size = IPA_GENERIC_RX_POOL_SZ;
ipa_drv_res->apply_rg10_wa = false;
ipa_drv_res->gsi_ch20_wa = false;
+ ipa_drv_res->ipa_tz_unlock_reg_num = 0;
+ ipa_drv_res->ipa_tz_unlock_reg = NULL;
smmu_info.disable_htw = of_property_read_bool(pdev->dev.of_node,
"qcom,smmu-disable-htw");
@@ -4808,6 +4907,46 @@ static int get_ipa_dts_configuration(struct platform_device *pdev,
ipa_drv_res->apply_rg10_wa
? "Needed" : "Not needed");
+ elem_num = of_property_count_elems_of_size(pdev->dev.of_node,
+ "qcom,ipa-tz-unlock-reg", sizeof(u32));
+
+ if (elem_num > 0 && elem_num % 2 == 0) {
+ ipa_drv_res->ipa_tz_unlock_reg_num = elem_num / 2;
+
+ ipa_tz_unlock_reg = kcalloc(elem_num, sizeof(u32), GFP_KERNEL);
+ if (ipa_tz_unlock_reg == NULL)
+ return -ENOMEM;
+
+ ipa_drv_res->ipa_tz_unlock_reg = kcalloc(
+ ipa_drv_res->ipa_tz_unlock_reg_num,
+ sizeof(*ipa_drv_res->ipa_tz_unlock_reg),
+ GFP_KERNEL);
+ if (ipa_drv_res->ipa_tz_unlock_reg == NULL) {
+ kfree(ipa_tz_unlock_reg);
+ return -ENOMEM;
+ }
+
+ if (of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,ipa-tz-unlock-reg", ipa_tz_unlock_reg,
+ elem_num)) {
+ IPAERR("failed to read register addresses\n");
+ kfree(ipa_tz_unlock_reg);
+ kfree(ipa_drv_res->ipa_tz_unlock_reg);
+ return -EFAULT;
+ }
+
+ pos = 0;
+ for (i = 0; i < ipa_drv_res->ipa_tz_unlock_reg_num; i++) {
+ ipa_drv_res->ipa_tz_unlock_reg[i].reg_addr =
+ ipa_tz_unlock_reg[pos++];
+ ipa_drv_res->ipa_tz_unlock_reg[i].size =
+ ipa_tz_unlock_reg[pos++];
+ IPADBG("tz unlock reg %d: addr 0x%pa size %d\n", i,
+ &ipa_drv_res->ipa_tz_unlock_reg[i].reg_addr,
+ ipa_drv_res->ipa_tz_unlock_reg[i].size);
+ }
+ kfree(ipa_tz_unlock_reg);
+ }
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 9e346f12a108..643e40402499 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -3180,7 +3180,7 @@ static int ipa3_assign_policy(struct ipa_sys_connect_params *in,
IPA_CLIENT_APPS_WAN_CONS) {
sys->pyld_hdlr = ipa3_wan_rx_pyld_hdlr;
sys->free_rx_wrapper = ipa3_free_rx_wrapper;
- if (in->napi_enabled) {
+ if (in->recycle_enabled) {
sys->repl_hdlr =
ipa3_replenish_rx_cache_recycle;
sys->rx_pool_sz =
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index aff39fc18f67..df413c991a53 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -136,7 +136,7 @@ static int ipa_prep_flt_tbl_for_cmt(enum ipa_ip_type ip,
IPAERR("failed to calculate HW FLT rule size\n");
return -EPERM;
}
- IPADBG("pipe %d rule_id (handle) %u hw_len %d priority %u\n",
+ IPADBG_LOW("pipe %d rule_id(handle) %u hw_len %d priority %u\n",
pipe_idx, entry->rule_id, entry->hw_len, entry->prio);
if (entry->rule.hashable)
@@ -1372,18 +1372,18 @@ void ipa3_install_dflt_flt_rules(u32 ipa_ep_idx)
mutex_lock(&ipa3_ctx->lock);
tbl = &ipa3_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v4];
- tbl->sticky_rear = true;
rule.action = IPA_PASS_TO_EXCEPTION;
- __ipa_add_flt_rule(tbl, IPA_IP_v4, &rule, false,
+ __ipa_add_flt_rule(tbl, IPA_IP_v4, &rule, true,
&ep->dflt_flt4_rule_hdl);
ipa3_ctx->ctrl->ipa3_commit_flt(IPA_IP_v4);
+ tbl->sticky_rear = true;
tbl = &ipa3_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v6];
- tbl->sticky_rear = true;
rule.action = IPA_PASS_TO_EXCEPTION;
- __ipa_add_flt_rule(tbl, IPA_IP_v6, &rule, false,
+ __ipa_add_flt_rule(tbl, IPA_IP_v6, &rule, true,
&ep->dflt_flt6_rule_hdl);
ipa3_ctx->ctrl->ipa3_commit_flt(IPA_IP_v6);
+ tbl->sticky_rear = true;
mutex_unlock(&ipa3_ctx->lock);
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index ed21d7ff6220..8e85822d9719 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -43,8 +43,6 @@
#define MTU_BYTE 1500
#define IPA3_MAX_NUM_PIPES 31
-#define IPA_WAN_CONS_DESC_FIFO_SZ 0x5E80
-#define IPA_WAN_NAPI_CONS_RX_POOL_SZ 3000
#define IPA_SYS_DESC_FIFO_SZ 0x800
#define IPA_SYS_TX_DATA_DESC_FIFO_SZ 0x1000
#define IPA_LAN_RX_HEADER_LENGTH (2)
@@ -55,6 +53,8 @@
#define IPA_UC_FINISH_MAX 6
#define IPA_UC_WAIT_MIN_SLEEP 1000
#define IPA_UC_WAII_MAX_SLEEP 1200
+#define IPA_WAN_NAPI_CONS_RX_POOL_SZ (IPA_GENERIC_RX_POOL_SZ*3)
+#define IPA_WAN_CONS_DESC_FIFO_SZ (IPA_SYS_DESC_FIFO_SZ*3)
#define IPA_MAX_STATUS_STAT_NUM 30
@@ -1012,6 +1012,11 @@ struct ipa3_ready_cb_info {
void *user_data;
};
+struct ipa_tz_unlock_reg_info {
+ u64 reg_addr;
+ u32 size;
+};
+
/**
* struct ipa3_context - IPA context
* @class: pointer to the struct class
@@ -1228,6 +1233,8 @@ struct ipa3_context {
struct list_head ipa_ready_cb_list;
struct completion init_completion_obj;
struct ipa3_smp2p_info smp2p_info;
+ u32 ipa_tz_unlock_reg_num;
+ struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
};
/**
@@ -1266,6 +1273,8 @@ struct ipa3_plat_drv_res {
bool apply_rg10_wa;
bool gsi_ch20_wa;
bool tethered_flow_control;
+ u32 ipa_tz_unlock_reg_num;
+ struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
};
/**
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
index d68a2ce3c041..bf8a5ade04bd 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
@@ -111,6 +111,12 @@ static struct msg_desc ipa3_init_modem_driver_cmplt_resp_desc = {
.ei_array = ipa3_init_modem_driver_cmplt_resp_msg_data_v01_ei,
};
+static struct msg_desc ipa3_install_fltr_rule_req_ex_desc = {
+ .max_msg_len = QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_MAX_MSG_LEN_V01,
+ .msg_id = QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_V01,
+ .ei_array = ipa3_install_fltr_rule_req_ex_msg_data_v01_ei,
+};
+
static int ipa3_handle_indication_req(void *req_h, void *req)
{
struct ipa_indication_reg_req_msg_v01 *indication_req;
@@ -299,6 +305,10 @@ static int ipa3_a5_svc_req_desc_cb(unsigned int msg_id,
*req_desc = &ipa3_install_fltr_rule_req_desc;
rc = sizeof(struct ipa_install_fltr_rule_req_msg_v01);
break;
+ case QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_V01:
+ *req_desc = &ipa3_install_fltr_rule_req_ex_desc;
+ rc = sizeof(struct ipa_install_fltr_rule_req_ex_msg_v01);
+ break;
case QMI_IPA_FILTER_INSTALLED_NOTIF_REQ_V01:
*req_desc = &ipa3_filter_installed_notif_req_desc;
rc = sizeof(struct ipa_fltr_installed_notif_req_msg_v01);
@@ -623,6 +633,49 @@ int ipa3_qmi_filter_request_send(struct ipa_install_fltr_rule_req_msg_v01 *req)
resp.resp.error, "ipa_install_filter");
}
+/* sending filter-install-request to modem*/
+int ipa3_qmi_filter_request_ex_send(
+ struct ipa_install_fltr_rule_req_ex_msg_v01 *req)
+{
+ struct ipa_install_fltr_rule_resp_ex_msg_v01 resp;
+ struct msg_desc req_desc, resp_desc;
+ int rc;
+
+ /* check if the filter rules from IPACM is valid */
+ if (req->filter_spec_ex_list_len == 0) {
+ IPAWANDBG("IPACM pass zero rules to Q6\n");
+ } else {
+ IPAWANDBG("IPACM pass %u rules to Q6\n",
+ req->filter_spec_ex_list_len);
+ }
+
+ /* cache the qmi_filter_request */
+ memcpy(&(ipa3_qmi_ctx->ipa_install_fltr_rule_req_ex_msg_cache[
+ ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg]),
+ req, sizeof(struct ipa_install_fltr_rule_req_ex_msg_v01));
+ ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg++;
+ ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg %= 10;
+
+ req_desc.max_msg_len =
+ QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_MAX_MSG_LEN_V01;
+ req_desc.msg_id = QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_V01;
+ req_desc.ei_array = ipa3_install_fltr_rule_req_ex_msg_data_v01_ei;
+
+ memset(&resp, 0, sizeof(struct ipa_install_fltr_rule_resp_ex_msg_v01));
+ resp_desc.max_msg_len =
+ QMI_IPA_INSTALL_FILTER_RULE_EX_RESP_MAX_MSG_LEN_V01;
+ resp_desc.msg_id = QMI_IPA_INSTALL_FILTER_RULE_EX_RESP_V01;
+ resp_desc.ei_array = ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei;
+
+ rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc,
+ req,
+ sizeof(struct ipa_install_fltr_rule_req_ex_msg_v01),
+ &resp_desc, &resp, sizeof(resp),
+ QMI_SEND_REQ_TIMEOUT_MS);
+ return ipa3_check_qmi_response(rc,
+ QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_V01, resp.resp.result,
+ resp.resp.error, "ipa_install_filter");
+}
int ipa3_qmi_enable_force_clear_datapath_send(
struct ipa_enable_force_clear_datapath_req_msg_v01 *req)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
index 0f641204cc77..e0126ec392c3 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
@@ -73,6 +73,9 @@ u32 q6_ul_filter_rule_hdl[MAX_NUM_Q6_RULE];
int num_ipa_install_fltr_rule_req_msg;
struct ipa_install_fltr_rule_req_msg_v01
ipa_install_fltr_rule_req_msg_cache[MAX_NUM_QMI_RULE_CACHE];
+int num_ipa_install_fltr_rule_req_ex_msg;
+struct ipa_install_fltr_rule_req_ex_msg_v01
+ ipa_install_fltr_rule_req_ex_msg_cache[MAX_NUM_QMI_RULE_CACHE];
int num_ipa_fltr_installed_notif_req_msg;
struct ipa_fltr_installed_notif_req_msg_v01
ipa_fltr_installed_notif_req_msg_cache[MAX_NUM_QMI_RULE_CACHE];
@@ -115,6 +118,8 @@ extern struct elem_info ipa3_stop_data_usage_quota_req_msg_data_v01_ei[];
extern struct elem_info ipa3_stop_data_usage_quota_resp_msg_data_v01_ei[];
extern struct elem_info ipa3_init_modem_driver_cmplt_req_msg_data_v01_ei[];
extern struct elem_info ipa3_init_modem_driver_cmplt_resp_msg_data_v01_ei[];
+extern struct elem_info ipa3_install_fltr_rule_req_ex_msg_data_v01_ei[];
+extern struct elem_info ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei[];
/**
* struct ipa3_rmnet_context - IPA rmnet context
@@ -140,6 +145,9 @@ void ipa3_qmi_service_exit(void);
int ipa3_qmi_filter_request_send(
struct ipa_install_fltr_rule_req_msg_v01 *req);
+int ipa3_qmi_filter_request_ex_send(
+ struct ipa_install_fltr_rule_req_ex_msg_v01 *req);
+
/* sending filter-installed-notify-request to modem*/
int ipa3_qmi_filter_notify_send(struct ipa_fltr_installed_notif_req_msg_v01
*req);
@@ -209,6 +217,12 @@ static inline int ipa3_qmi_filter_request_send(
return -EPERM;
}
+static inline int ipa3_qmi_filter_request_ex_send(
+ struct ipa_install_fltr_rule_req_ex_msg_v01 *req)
+{
+ return -EPERM;
+}
+
/* sending filter-installed-notify-request to modem*/
static inline int ipa3_qmi_filter_notify_send(
struct ipa_fltr_installed_notif_req_msg_v01 *req)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
index 6907811c7ab6..6a5cb4891c02 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.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
@@ -2744,3 +2744,182 @@ struct elem_info ipa3_stop_data_usage_quota_resp_msg_data_v01_ei[] = {
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
+
+struct elem_info ipa3_install_fltr_rule_req_ex_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ filter_spec_ex_list_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ filter_spec_ex_list_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_IPA_MAX_FILTERS_EX_V01,
+ .elem_size = sizeof(struct
+ ipa_filter_spec_ex_type_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ filter_spec_ex_list),
+ .ei_array = ipa_filter_spec_ex_type_data_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ source_pipe_index_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ source_pipe_index),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ num_ipv4_filters_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ num_ipv4_filters),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ num_ipv6_filters_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ num_ipv6_filters),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ xlat_filter_indices_list_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ xlat_filter_indices_list_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = QMI_IPA_MAX_FILTERS_EX_V01,
+ .elem_size = sizeof(uint32_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_req_ex_msg_v01,
+ xlat_filter_indices_list),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_resp_ex_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_resp_ex_msg_v01,
+ rule_id_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_resp_ex_msg_v01,
+ rule_id_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = QMI_IPA_MAX_FILTERS_EX_V01,
+ .elem_size = sizeof(uint32_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct ipa_install_fltr_rule_resp_ex_msg_v01,
+ rule_id),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index b06e33a8258a..6c7bf500e760 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -289,7 +289,7 @@ static int ipa_prep_rt_tbl_for_cmt(enum ipa_ip_type ip,
return -EPERM;
}
- IPADBG("RT rule id (handle) %d hw_len %u priority %u\n",
+ IPADBG_LOW("RT rule id (handle) %d hw_len %u priority %u\n",
entry->id, entry->hw_len, entry->prio);
if (entry->rule.hashable)
@@ -642,23 +642,23 @@ int __ipa_commit_rt_v3(enum ipa_ip_type ip)
goto fail_imm_cmd_construct;
}
- IPADBG("Hashable HEAD\n");
+ IPADBG_LOW("Hashable HEAD\n");
IPA_DUMP_BUFF(alloc_params.hash_hdr.base,
alloc_params.hash_hdr.phys_base, alloc_params.hash_hdr.size);
- IPADBG("Non-Hashable HEAD\n");
+ IPADBG_LOW("Non-Hashable HEAD\n");
IPA_DUMP_BUFF(alloc_params.nhash_hdr.base,
alloc_params.nhash_hdr.phys_base, alloc_params.nhash_hdr.size);
if (alloc_params.hash_bdy.size) {
- IPADBG("Hashable BODY\n");
+ IPADBG_LOW("Hashable BODY\n");
IPA_DUMP_BUFF(alloc_params.hash_bdy.base,
alloc_params.hash_bdy.phys_base,
alloc_params.hash_bdy.size);
}
if (alloc_params.nhash_bdy.size) {
- IPADBG("Non-Hashable BODY\n");
+ IPADBG_LOW("Non-Hashable BODY\n");
IPA_DUMP_BUFF(alloc_params.nhash_bdy.base,
alloc_params.nhash_bdy.phys_base,
alloc_params.nhash_bdy.size);
@@ -1675,7 +1675,7 @@ int ipa3_rt_read_tbl_from_hw(u32 tbl_idx, enum ipa_ip_type ip_type,
u8 *rule_addr;
int rule_idx;
- IPADBG("tbl_idx=%d ip_type=%d hashable=%d entry=0x%p num_entry=0x%p\n",
+ IPADBG_LOW("tbl_idx=%d ip_t=%d hashable=%d entry=0x%p num_entry=0x%p\n",
tbl_idx, ip_type, hashable, entry, num_entry);
if (ip_type == IPA_IP_v4 && tbl_idx >= IPA_MEM_PART(v4_rt_num_index)) {
@@ -1716,7 +1716,7 @@ int ipa3_rt_read_tbl_from_hw(u32 tbl_idx, enum ipa_ip_type ip_type,
IPA_MEM_PART(v6_rt_nhash_ofst);
}
- IPADBG("hdr_base_ofst=0x%llx\n", hdr_base_ofst);
+ IPADBG_LOW("hdr_base_ofst=0x%llx\n", hdr_base_ofst);
res = ipahal_fltrt_read_addr_from_hdr(ipa_sram_mmio + hdr_base_ofst,
tbl_idx, &tbl_addr, &is_sys);
@@ -1724,7 +1724,7 @@ int ipa3_rt_read_tbl_from_hw(u32 tbl_idx, enum ipa_ip_type ip_type,
IPAERR("failed to read table address from header structure\n");
goto bail;
}
- IPADBG("rt tbl %d: tbl_addr=0x%llx is_sys=%d\n",
+ IPADBG_LOW("rt tbl %d: tbl_addr=0x%llx is_sys=%d\n",
tbl_idx, tbl_addr, is_sys);
if (!tbl_addr) {
IPAERR("invalid rt tbl addr\n");
@@ -1760,7 +1760,7 @@ int ipa3_rt_read_tbl_from_hw(u32 tbl_idx, enum ipa_ip_type ip_type,
rule_addr = ipa_sram_mmio + hdr_base_ofst + tbl_addr;
}
- IPADBG("First rule addr 0x%p\n", rule_addr);
+ IPADBG_LOW("First rule addr 0x%p\n", rule_addr);
if (!rule_addr) {
/* Modem table in system memory or empty table */
@@ -1776,7 +1776,7 @@ int ipa3_rt_read_tbl_from_hw(u32 tbl_idx, enum ipa_ip_type ip_type,
goto bail;
}
- IPADBG("rule_size=%d\n", entry[rule_idx].rule_size);
+ IPADBG_LOW("rule_size=%d\n", entry[rule_idx].rule_size);
if (!entry[rule_idx].rule_size)
break;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
index e355d9db3777..67b3cb301f1f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
@@ -115,7 +115,7 @@ static u64 ipa_fltrt_create_tbl_addr(bool is_sys, u64 addr)
static void ipa_fltrt_parse_tbl_addr(u64 hwaddr, u64 *addr, bool *is_sys)
{
- IPAHAL_DBG("Parsing hwaddr 0x%llx\n", hwaddr);
+ IPAHAL_DBG_LOW("Parsing hwaddr 0x%llx\n", hwaddr);
*is_sys = !(hwaddr & 0x1);
hwaddr &= (~0ULL - 1);
@@ -254,7 +254,7 @@ static int ipa_rt_gen_hw_rule(struct ipahal_rt_rule_gen_params *params,
}
rule_hdr->u.hdr.en_rule = en_rule;
- IPAHAL_DBG("en_rule 0x%x\n", en_rule);
+ IPAHAL_DBG_LOW("en_rule 0x%x\n", en_rule);
ipa_write_64(rule_hdr->u.word, (u8 *)rule_hdr);
if (*hw_len == 0) {
@@ -327,12 +327,12 @@ static int ipa_flt_gen_hw_rule(struct ipahal_flt_rule_gen_params *params,
}
rule_hdr->u.hdr.en_rule = en_rule;
- IPAHAL_DBG("en_rule=0x%x, action=%d, rt_idx=%d, retain_hdr=%d\n",
+ IPAHAL_DBG_LOW("en_rule=0x%x, action=%d, rt_idx=%d, retain_hdr=%d\n",
en_rule,
rule_hdr->u.hdr.action,
rule_hdr->u.hdr.rt_tbl_idx,
rule_hdr->u.hdr.retain_hdr);
- IPAHAL_DBG("priority=%d, rule_id=%d\n",
+ IPAHAL_DBG_LOW("priority=%d, rule_id=%d\n",
rule_hdr->u.hdr.priority,
rule_hdr->u.hdr.rule_id);
@@ -1152,25 +1152,25 @@ static int ipa_fltrt_generate_hw_rule_bdy(enum ipa_ip_type ipt,
* OFFSET_MEQ32_0 with mask of 0 and val of 0 and offset 0
*/
if (attrib->attrib_mask == 0) {
- IPAHAL_DBG("building default rule\n");
+ IPAHAL_DBG_LOW("building default rule\n");
*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(ipa3_0_ofst_meq32[0]);
extra_wrd_i = ipa_write_8(0, extra_wrd_i); /* offset */
rest_wrd_i = ipa_write_32(0, rest_wrd_i); /* mask */
rest_wrd_i = ipa_write_32(0, rest_wrd_i); /* val */
}
- IPAHAL_DBG("extra_word_1 0x%llx\n", *(u64 *)extra_wrd_start);
- IPAHAL_DBG("extra_word_2 0x%llx\n",
+ IPAHAL_DBG_LOW("extra_word_1 0x%llx\n", *(u64 *)extra_wrd_start);
+ IPAHAL_DBG_LOW("extra_word_2 0x%llx\n",
*(u64 *)(extra_wrd_start + IPA3_0_HW_TBL_WIDTH));
extra_wrd_i = ipa_pad_to_64(extra_wrd_i);
sz = extra_wrd_i - extra_wrd_start;
- IPAHAL_DBG("extra words params sz %d\n", sz);
+ IPAHAL_DBG_LOW("extra words params sz %d\n", sz);
*buf = ipa_fltrt_copy_mem(extra_wrd_start, *buf, sz);
rest_wrd_i = ipa_pad_to_64(rest_wrd_i);
sz = rest_wrd_i - rest_wrd_start;
- IPAHAL_DBG("non extra words params sz %d\n", sz);
+ IPAHAL_DBG_LOW("non extra words params sz %d\n", sz);
*buf = ipa_fltrt_copy_mem(rest_wrd_start, *buf, sz);
fail_err_check:
@@ -1208,7 +1208,7 @@ static int ipa_fltrt_calc_extra_wrd_bytes(
if (attrib->ihl_offset_eq_16_present)
num++;
- IPAHAL_DBG("extra bytes number %d\n", num);
+ IPAHAL_DBG_LOW("extra bytes number %d\n", num);
return num;
}
@@ -2024,7 +2024,7 @@ static int ipa_fltrt_parse_hw_rule_eq(u8 *addr, u32 hdr_sz,
eq_bitmap = atrb->rule_eq_bitmap;
- IPAHAL_DBG("eq_bitmap=0x%x\n", eq_bitmap);
+ IPAHAL_DBG_LOW("eq_bitmap=0x%x\n", eq_bitmap);
if (eq_bitmap & IPA_GET_RULE_EQ_BIT_PTRN(IPA_TOS_EQ))
atrb->tos_eq_present = true;
@@ -2080,7 +2080,7 @@ static int ipa_fltrt_parse_hw_rule_eq(u8 *addr, u32 hdr_sz,
extra = &dummy_extra_wrd;
rest = addr + hdr_sz;
}
- IPAHAL_DBG("addr=0x%p extra=0x%p rest=0x%p\n", addr, extra, rest);
+ IPAHAL_DBG_LOW("addr=0x%p extra=0x%p rest=0x%p\n", addr, extra, rest);
if (atrb->tos_eq_present)
atrb->tos_eq = *extra++;
@@ -2182,13 +2182,13 @@ static int ipa_fltrt_parse_hw_rule_eq(u8 *addr, u32 hdr_sz,
rest += 4;
}
- IPAHAL_DBG("before rule alignment rest=0x%p\n", rest);
+ IPAHAL_DBG_LOW("before rule alignment rest=0x%p\n", rest);
rest = (u8 *)(((unsigned long)rest + IPA3_0_HW_RULE_START_ALIGNMENT) &
~IPA3_0_HW_RULE_START_ALIGNMENT);
- IPAHAL_DBG("after rule alignment rest=0x%p\n", rest);
+ IPAHAL_DBG_LOW("after rule alignment rest=0x%p\n", rest);
*rule_size = rest - addr;
- IPAHAL_DBG("rule_size=0x%x\n", *rule_size);
+ IPAHAL_DBG_LOW("rule_size=0x%x\n", *rule_size);
return 0;
}
@@ -2198,12 +2198,12 @@ static int ipa_rt_parse_hw_rule(u8 *addr, struct ipahal_rt_rule_entry *rule)
struct ipa3_0_rt_rule_hw_hdr *rule_hdr;
struct ipa_ipfltri_rule_eq *atrb;
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
rule_hdr = (struct ipa3_0_rt_rule_hw_hdr *)addr;
atrb = &rule->eq_attrib;
- IPAHAL_DBG("read hdr 0x%llx\n", rule_hdr->u.word);
+ IPAHAL_DBG_LOW("read hdr 0x%llx\n", rule_hdr->u.word);
if (rule_hdr->u.word == 0) {
/* table termintator - empty table */
@@ -2235,7 +2235,7 @@ static int ipa_flt_parse_hw_rule(u8 *addr, struct ipahal_flt_rule_entry *rule)
struct ipa3_0_flt_rule_hw_hdr *rule_hdr;
struct ipa_ipfltri_rule_eq *atrb;
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
rule_hdr = (struct ipa3_0_flt_rule_hw_hdr *)addr;
atrb = &rule->rule.eq_attrib;
@@ -2775,11 +2775,11 @@ static int ipa_fltrt_alloc_lcl_bdy(
/* The HAL allocates larger sizes than the given effective ones
* for alignments and border indications
*/
- IPAHAL_DBG("lcl tbl bdy total effective sizes: hash=%u nhash=%u\n",
+ IPAHAL_DBG_LOW("lcl tbl bdy total effective sizes: hash=%u nhash=%u\n",
params->total_sz_lcl_hash_tbls,
params->total_sz_lcl_nhash_tbls);
- IPAHAL_DBG("lcl tbl bdy count: hash=%u nhash=%u\n",
+ IPAHAL_DBG_LOW("lcl tbl bdy count: hash=%u nhash=%u\n",
params->num_lcl_hash_tbls,
params->num_lcl_nhash_tbls);
@@ -2798,7 +2798,7 @@ static int ipa_fltrt_alloc_lcl_bdy(
params->nhash_bdy.size += obj->blk_sz_alignment;
params->nhash_bdy.size &= ~(obj->blk_sz_alignment);
- IPAHAL_DBG("nhash lcl tbl bdy total h/w size = %u\n",
+ IPAHAL_DBG_LOW("nhash lcl tbl bdy total h/w size = %u\n",
params->nhash_bdy.size);
params->nhash_bdy.base = dma_alloc_coherent(
@@ -2829,7 +2829,7 @@ static int ipa_fltrt_alloc_lcl_bdy(
params->hash_bdy.size += obj->blk_sz_alignment;
params->hash_bdy.size &= ~(obj->blk_sz_alignment);
- IPAHAL_DBG("hash lcl tbl bdy total h/w size = %u\n",
+ IPAHAL_DBG_LOW("hash lcl tbl bdy total h/w size = %u\n",
params->hash_bdy.size);
params->hash_bdy.base = dma_alloc_coherent(
@@ -2862,7 +2862,7 @@ hash_bdy_fail:
int ipahal_fltrt_allocate_hw_tbl_imgs(
struct ipahal_fltrt_alloc_imgs_params *params)
{
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
/* Input validation */
if (!params) {
@@ -2904,7 +2904,7 @@ int ipahal_fltrt_allocate_hw_sys_tbl(struct ipa_mem_buffer *tbl_mem)
{
struct ipahal_fltrt_obj *obj;
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
if (!tbl_mem) {
IPAHAL_ERR("Input err\n");
@@ -2958,7 +2958,7 @@ int ipahal_fltrt_write_addr_to_hdr(u64 addr, void *hdr_base, u32 hdr_idx,
u64 hwaddr;
u8 *hdr;
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
obj = &ipahal_fltrt_objs[ipahal_ctx->hw_type];
@@ -2991,7 +2991,7 @@ int ipahal_fltrt_read_addr_from_hdr(void *hdr_base, u32 hdr_idx, u64 *addr,
u64 hwaddr;
u8 *hdr;
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
obj = &ipahal_fltrt_objs[ipahal_ctx->hw_type];
@@ -3023,7 +3023,7 @@ int ipahal_rt_generate_hw_rule(struct ipahal_rt_rule_gen_params *params,
u8 *tmp = NULL;
int rc;
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
if (!params || !hw_len) {
IPAHAL_ERR("Input err: params=%p hw_len=%p\n", params, hw_len);
@@ -3081,7 +3081,7 @@ int ipahal_flt_generate_hw_rule(struct ipahal_flt_rule_gen_params *params,
u8 *tmp = NULL;
int rc;
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
if (!params || !hw_len) {
IPAHAL_ERR("Input err: params=%p hw_len=%p\n", params, hw_len);
@@ -3138,7 +3138,7 @@ int ipahal_flt_generate_equation(enum ipa_ip_type ipt,
const struct ipa_rule_attrib *attrib,
struct ipa_ipfltri_rule_eq *eq_atrb)
{
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
if (ipt >= IPA_IP_MAX) {
IPAHAL_ERR("Input err: Invalid ip type %d\n", ipt);
@@ -3165,7 +3165,7 @@ int ipahal_flt_generate_equation(enum ipa_ip_type ipt,
int ipahal_rt_parse_hw_rule(u8 *rule_addr,
struct ipahal_rt_rule_entry *rule)
{
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
if (!rule_addr || !rule) {
IPAHAL_ERR("Input err: rule_addr=%p rule=%p\n",
@@ -3186,7 +3186,7 @@ int ipahal_rt_parse_hw_rule(u8 *rule_addr,
int ipahal_flt_parse_hw_rule(u8 *rule_addr,
struct ipahal_flt_rule_entry *rule)
{
- IPAHAL_DBG("Entry\n");
+ IPAHAL_DBG_LOW("Entry\n");
if (!rule_addr || !rule) {
IPAHAL_ERR("Input err: rule_addr=%p rule=%p\n",
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 2cd08d77df6e..a2fef45cc55f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -34,6 +34,8 @@
#include <linux/rmnet_ipa_fd_ioctl.h>
#include <linux/ipa.h>
#include <uapi/linux/net_map.h>
+#include <uapi/linux/msm_rmnet.h>
+#include <net/rmnet_config.h>
#include "ipa_trace.h"
@@ -1241,6 +1243,83 @@ static void apps_ipa_packet_receive_notify(void *priv,
IPAWANERR("Invalid evt %d received in wan_ipa_receive\n", evt);
}
+static int handle3_ingress_format(struct net_device *dev,
+ struct rmnet_ioctl_extended_s *in)
+{
+ int ret = 0;
+ struct ipa_sys_connect_params *ipa_wan_ep_cfg;
+ struct rmnet_phys_ep_conf_s *ep_cfg;
+
+ IPAWANDBG("Get RMNET_IOCTL_SET_INGRESS_DATA_FORMAT\n");
+ ipa_wan_ep_cfg = &rmnet_ipa3_ctx->ipa_to_apps_ep_cfg;
+ if ((in->u.data) & RMNET_IOCTL_INGRESS_FORMAT_CHECKSUM)
+ ipa_wan_ep_cfg->ipa_ep_cfg.cfg.cs_offload_en =
+ IPA_ENABLE_CS_OFFLOAD_DL;
+
+ if ((in->u.data) & RMNET_IOCTL_INGRESS_FORMAT_AGG_DATA) {
+ IPAWANERR("get AGG size %d count %d\n",
+ in->u.ingress_format.agg_size,
+ in->u.ingress_format.agg_count);
+
+ ret = ipa_disable_apps_wan_cons_deaggr(
+ in->u.ingress_format.agg_size,
+ in->u.ingress_format.agg_count);
+
+ if (!ret) {
+ ipa_wan_ep_cfg->ipa_ep_cfg.aggr.aggr_byte_limit =
+ in->u.ingress_format.agg_size;
+ ipa_wan_ep_cfg->ipa_ep_cfg.aggr.aggr_pkt_limit =
+ in->u.ingress_format.agg_count;
+
+ ipa_wan_ep_cfg->recycle_enabled = true;
+ ep_cfg = (struct rmnet_phys_ep_conf_s *)
+ rcu_dereference(dev->rx_handler_data);
+ ep_cfg->recycle = ipa_recycle_wan_skb;
+ pr_info("Wan Recycle Enabled\n");
+ }
+ }
+
+ ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_len = 4;
+ ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_ofst_metadata_valid = 1;
+ ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_ofst_metadata = 1;
+ ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_ofst_pkt_size_valid = 1;
+ ipa_wan_ep_cfg->ipa_ep_cfg.hdr.hdr_ofst_pkt_size = 2;
+
+ ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_total_len_or_pad_valid = true;
+ ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_total_len_or_pad = 0;
+ ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_payload_len_inc_padding = true;
+ ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_total_len_or_pad_offset = 0;
+ ipa_wan_ep_cfg->ipa_ep_cfg.hdr_ext.hdr_little_endian = 0;
+ ipa_wan_ep_cfg->ipa_ep_cfg.metadata_mask.metadata_mask = 0xFF000000;
+
+ ipa_wan_ep_cfg->client = IPA_CLIENT_APPS_WAN_CONS;
+ ipa_wan_ep_cfg->notify = apps_ipa_packet_receive_notify;
+ ipa_wan_ep_cfg->priv = dev;
+
+ ipa_wan_ep_cfg->napi_enabled = ipa3_rmnet_res.ipa_napi_enable;
+ if (ipa_wan_ep_cfg->napi_enabled)
+ ipa_wan_ep_cfg->desc_fifo_sz = IPA_WAN_CONS_DESC_FIFO_SZ;
+ else
+ ipa_wan_ep_cfg->desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
+
+ mutex_lock(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard);
+
+ if (atomic_read(&rmnet_ipa3_ctx->is_ssr)) {
+ IPAWANDBG("In SSR sequence/recovery\n");
+ mutex_unlock(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard);
+ return -EFAULT;
+ }
+ ret = ipa3_setup_sys_pipe(&rmnet_ipa3_ctx->ipa_to_apps_ep_cfg,
+ &rmnet_ipa3_ctx->ipa3_to_apps_hdl);
+
+ mutex_unlock(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard);
+
+ if (ret)
+ IPAWANERR("failed to configure ingress\n");
+
+ return ret;
+}
+
/**
* ipa3_wwan_ioctl() - I/O control for wwan network driver.
*
@@ -1556,91 +1635,7 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
break;
case RMNET_IOCTL_SET_INGRESS_DATA_FORMAT:/* Set IDF */
- IPAWANDBG("get RMNET_IOCTL_SET_INGRESS_DATA_FORMAT\n");
- if ((extend_ioctl_data.u.data) &
- RMNET_IOCTL_INGRESS_FORMAT_CHECKSUM)
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.
- ipa_ep_cfg.cfg.cs_offload_en =
- IPA_ENABLE_CS_OFFLOAD_DL;
-
- if ((extend_ioctl_data.u.data) &
- RMNET_IOCTL_INGRESS_FORMAT_AGG_DATA) {
- IPAWANERR("get AGG size %d count %d\n",
- extend_ioctl_data.u.
- ingress_format.agg_size,
- extend_ioctl_data.u.
- ingress_format.agg_count);
- if (!ipa_disable_apps_wan_cons_deaggr(
- extend_ioctl_data.u.
- ingress_format.agg_size,
- extend_ioctl_data.
- u.ingress_format.agg_count)) {
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.
- ipa_ep_cfg.aggr.aggr_byte_limit =
- extend_ioctl_data.u.ingress_format.
- agg_size;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.
- ipa_ep_cfg.aggr.aggr_pkt_limit =
- extend_ioctl_data.u.ingress_format.
- agg_count;
- }
- }
-
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.
- hdr_len = 4;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.
- hdr_ofst_metadata_valid = 1;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.
- hdr.hdr_ofst_metadata = 1;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.
- hdr_ofst_pkt_size_valid = 1;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr.
- hdr_ofst_pkt_size = 2;
-
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.
- hdr_total_len_or_pad_valid = true;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.
- hdr_total_len_or_pad = 0;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.
- hdr_payload_len_inc_padding = true;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.
- hdr_total_len_or_pad_offset = 0;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.hdr_ext.
- hdr_little_endian = 0;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.ipa_ep_cfg.
- metadata_mask.metadata_mask = 0xFF000000;
-
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.client =
- IPA_CLIENT_APPS_WAN_CONS;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.notify =
- apps_ipa_packet_receive_notify;
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.priv = dev;
-
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.napi_enabled =
- ipa3_rmnet_res.ipa_napi_enable;
- if (rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.napi_enabled)
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.
- desc_fifo_sz = IPA_WAN_CONS_DESC_FIFO_SZ;
- else
- rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.
- desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
-
- mutex_lock(
- &rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard);
- if (atomic_read(&rmnet_ipa3_ctx->is_ssr)) {
- IPAWANDBG("In SSR sequence/recovery\n");
- mutex_unlock(&rmnet_ipa3_ctx->
- ipa_to_apps_pipe_handle_guard);
- rc = -EFAULT;
- break;
- }
- rc = ipa3_setup_sys_pipe(
- &rmnet_ipa3_ctx->ipa_to_apps_ep_cfg,
- &rmnet_ipa3_ctx->ipa3_to_apps_hdl);
- mutex_unlock(&rmnet_ipa3_ctx->
- ipa_to_apps_pipe_handle_guard);
- if (rc)
- IPAWANERR("failed to configure ingress\n");
+ rc = handle3_ingress_format(dev, &extend_ioctl_data);
break;
case RMNET_IOCTL_SET_XLAT_DEV_INFO:
wan_msg = kzalloc(sizeof(struct ipa_wan_msg),
@@ -1972,6 +1967,12 @@ static int get_ipa_rmnet_dts_configuration(struct platform_device *pdev,
"qcom,ipa-advertise-sg-support");
pr_info("IPA SG support = %s\n",
ipa_rmnet_drv_res->ipa_advertise_sg_support ? "True" : "False");
+
+ ipa_rmnet_drv_res->ipa_napi_enable =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,napi");
+ pr_info("IPA napi = %s\n",
+ ipa_rmnet_drv_res->ipa_napi_enable ? "True" : "False");
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c
index 80b07ab79163..92636cba0f1c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c
@@ -97,6 +97,31 @@ static long ipa3_wan_ioctl(struct file *filp,
}
break;
+ case WAN_IOC_ADD_FLT_RULE_EX:
+ IPAWANDBG("device %s got WAN_IOC_ADD_FLT_RULE_EX :>>>\n",
+ DRIVER_NAME);
+ pyld_sz = sizeof(struct ipa_install_fltr_rule_req_ex_msg_v01);
+ param = kzalloc(pyld_sz, GFP_KERNEL);
+ if (!param) {
+ retval = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+ retval = -EFAULT;
+ break;
+ }
+ if (ipa3_qmi_filter_request_ex_send(
+ (struct ipa_install_fltr_rule_req_ex_msg_v01 *)param)) {
+ IPAWANDBG("IPACM->Q6 add filter rule failed\n");
+ retval = -EFAULT;
+ break;
+ }
+ if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
case WAN_IOC_ADD_FLT_RULE_INDEX:
IPAWANDBG("device %s got WAN_IOC_ADD_FLT_RULE_INDEX :>>>\n",
DRIVER_NAME);
diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c
index 0bbda4eb4116..78e685f789cd 100644
--- a/drivers/platform/msm/qpnp-revid.c
+++ b/drivers/platform/msm/qpnp-revid.c
@@ -27,6 +27,7 @@
#define REVID_SUBTYPE 0x5
#define REVID_STATUS1 0x8
#define REVID_SPARE_0 0x60
+#define REVID_FAB_ID 0xf2
#define QPNP_REVID_DEV_NAME "qcom,qpnp-revid"
@@ -154,7 +155,7 @@ static size_t build_pmic_string(char *buf, size_t n, int sid,
static int qpnp_revid_probe(struct platform_device *pdev)
{
u8 rev1, rev2, rev3, rev4, pmic_type, pmic_subtype, pmic_status;
- u8 option1, option2, option3, option4, spare0;
+ u8 option1, option2, option3, option4, spare0, fab_id;
unsigned int base;
int rc;
char pmic_string[PMIC_STRING_MAXLENGTH] = {'\0'};
@@ -199,6 +200,11 @@ static int qpnp_revid_probe(struct platform_device *pdev)
pmic_subtype = PMI8937_PERIPHERAL_SUBTYPE;
}
+ if (of_property_read_bool(pdev->dev.of_node, "qcom,fab-id-valid"))
+ fab_id = qpnp_read_byte(regmap, base + REVID_FAB_ID);
+ else
+ fab_id = -EINVAL;
+
revid_chip = devm_kzalloc(&pdev->dev, sizeof(struct revid_chip),
GFP_KERNEL);
if (!revid_chip)
@@ -211,6 +217,7 @@ static int qpnp_revid_probe(struct platform_device *pdev)
revid_chip->data.rev4 = rev4;
revid_chip->data.pmic_subtype = pmic_subtype;
revid_chip->data.pmic_type = pmic_type;
+ revid_chip->data.fab_id = fab_id;
if (pmic_subtype < ARRAY_SIZE(pmic_names))
revid_chip->data.pmic_name = pmic_names[pmic_subtype];
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 08ec7334737e..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 {
@@ -114,6 +116,7 @@ enum fg_sram_param_id {
FG_SRAM_VOLTAGE_PRED,
FG_SRAM_OCV,
FG_SRAM_RSLOW,
+ FG_SRAM_ALG_FLAGS,
/* Entries below here are configurable during initialization */
FG_SRAM_CUTOFF_VOLT,
FG_SRAM_EMPTY_VOLT,
@@ -143,6 +146,23 @@ struct fg_sram_param {
int val);
};
+enum fg_alg_flag_id {
+ ALG_FLAG_SOC_LT_OTG_MIN = 0,
+ ALG_FLAG_SOC_LT_RECHARGE,
+ ALG_FLAG_IBATT_LT_ITERM,
+ ALG_FLAG_IBATT_GT_HPM,
+ ALG_FLAG_IBATT_GT_UPM,
+ ALG_FLAG_VBATT_LT_RECHARGE,
+ ALG_FLAG_VBATT_GT_VFLOAT,
+ ALG_FLAG_MAX,
+};
+
+struct fg_alg_flag {
+ char *name;
+ u8 bit;
+ bool invalid;
+};
+
/* DT parameters for FG device */
struct fg_dt_props {
int cutoff_volt_mv;
@@ -168,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;
@@ -179,7 +208,7 @@ struct fg_chip {
struct device *dev;
struct pmic_revid_data *pmic_rev_id;
struct regmap *regmap;
- struct dentry *dentry;
+ struct dentry *dfs_root;
struct power_supply *fg_psy;
struct power_supply *batt_psy;
struct iio_channel *batt_id_chan;
@@ -191,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;
@@ -198,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;
@@ -205,6 +237,8 @@ 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;
};
/* Debugfs data structures are below */
@@ -249,7 +283,8 @@ extern int fg_read(struct fg_chip *chip, int addr, u8 *val, int len);
extern int fg_write(struct fg_chip *chip, int addr, u8 *val, int len);
extern int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val);
extern int fg_ima_init(struct fg_chip *chip);
-extern int fg_sram_debugfs_create(struct fg_chip *chip);
+extern int fg_debugfs_create(struct fg_chip *chip);
extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len);
extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos);
+extern s64 fg_float_decode(u16 val);
#endif
diff --git a/drivers/power/qcom-charger/fg-memif.c b/drivers/power/qcom-charger/fg-memif.c
index 087223d708da..c271b24adfc4 100644
--- a/drivers/power/qcom-charger/fg-memif.c
+++ b/drivers/power/qcom-charger/fg-memif.c
@@ -499,8 +499,10 @@ out:
return rc;
}
- if (retry_once)
+ if (retry_once) {
+ retry_once = false;
goto retry;
+ }
return rc;
}
diff --git a/drivers/power/qcom-charger/fg-util.c b/drivers/power/qcom-charger/fg-util.c
index 5f133d6f39c1..bf5a446452a4 100644
--- a/drivers/power/qcom-charger/fg-util.c
+++ b/drivers/power/qcom-charger/fg-util.c
@@ -29,6 +29,26 @@ static struct fg_dbgfs dbgfs_data = {
},
};
+#define EXPONENT_SHIFT 11
+#define EXPONENT_OFFSET -9
+#define MANTISSA_SIGN_BIT 10
+#define MICRO_UNIT 1000000
+s64 fg_float_decode(u16 val)
+{
+ s8 exponent;
+ s32 mantissa;
+
+ /* mantissa bits are shifted out during sign extension */
+ exponent = ((s16)val >> EXPONENT_SHIFT) + EXPONENT_OFFSET;
+ /* exponent bits are shifted out during sign extension */
+ mantissa = sign_extend32(val, MANTISSA_SIGN_BIT) * MICRO_UNIT;
+
+ if (exponent < 0)
+ return (s64)mantissa >> -exponent;
+
+ return (s64)mantissa << exponent;
+}
+
void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
{
int pos = 0;
@@ -591,27 +611,23 @@ static const struct file_operations fg_sram_dfs_reg_fops = {
* fg_debugfs_create: adds new fg_sram debugfs entry
* @return zero on success
*/
-int fg_sram_debugfs_create(struct fg_chip *chip)
+static int fg_sram_debugfs_create(struct fg_chip *chip)
{
- struct dentry *root;
+ struct dentry *dfs_sram;
struct dentry *file;
mode_t dfs_mode = S_IRUSR | S_IWUSR;
pr_debug("Creating FG_SRAM debugfs file-system\n");
- root = debugfs_create_dir("fg_sram", NULL);
- if (IS_ERR_OR_NULL(root)) {
- pr_err("Error creating top level directory err:%ld",
- (long)root);
- if (PTR_ERR(root) == -ENODEV)
- pr_err("debugfs is not enabled in the kernel");
- return -ENODEV;
+ dfs_sram = debugfs_create_dir("sram", chip->dfs_root);
+ if (!dfs_sram) {
+ pr_err("error creating fg sram dfs rc=%ld\n",
+ (long)dfs_sram);
+ return -ENOMEM;
}
- if (!root)
- return -ENOENT;
-
dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data);
- file = debugfs_create_blob("help", S_IRUGO, root, &dbgfs_data.help_msg);
+ file = debugfs_create_blob("help", S_IRUGO, dfs_sram,
+ &dbgfs_data.help_msg);
if (!file) {
pr_err("error creating help entry\n");
goto err_remove_fs;
@@ -619,30 +635,106 @@ int fg_sram_debugfs_create(struct fg_chip *chip)
dbgfs_data.chip = chip;
- file = debugfs_create_u32("count", dfs_mode, root, &(dbgfs_data.cnt));
+ file = debugfs_create_u32("count", dfs_mode, dfs_sram,
+ &(dbgfs_data.cnt));
if (!file) {
pr_err("error creating 'count' entry\n");
goto err_remove_fs;
}
- file = debugfs_create_x32("address", dfs_mode,
- root, &(dbgfs_data.addr));
+ file = debugfs_create_x32("address", dfs_mode, dfs_sram,
+ &(dbgfs_data.addr));
if (!file) {
pr_err("error creating 'address' entry\n");
goto err_remove_fs;
}
- file = debugfs_create_file("data", dfs_mode, root, &dbgfs_data,
- &fg_sram_dfs_reg_fops);
+ file = debugfs_create_file("data", dfs_mode, dfs_sram, &dbgfs_data,
+ &fg_sram_dfs_reg_fops);
if (!file) {
pr_err("error creating 'data' entry\n");
goto err_remove_fs;
}
- chip->dentry = root;
return 0;
err_remove_fs:
- debugfs_remove_recursive(root);
+ debugfs_remove_recursive(dfs_sram);
+ return -ENOMEM;
+}
+
+static int fg_alg_flags_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t fg_alg_flags_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct fg_chip *chip = file->private_data;
+ char buf[512];
+ u8 alg_flags = 0;
+ int rc, i, len;
+
+ rc = fg_sram_read(chip, chip->sp[FG_SRAM_ALG_FLAGS].addr_word,
+ chip->sp[FG_SRAM_ALG_FLAGS].addr_byte, &alg_flags, 1,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("failed to read algorithm flags rc=%d\n", rc);
+ return -EFAULT;
+ }
+
+ len = 0;
+ for (i = 0; i < ALG_FLAG_MAX; ++i) {
+ if (len > ARRAY_SIZE(buf) - 1)
+ return -EFAULT;
+ if (chip->alg_flags[i].invalid)
+ continue;
+
+ len += snprintf(buf + len, sizeof(buf) - sizeof(*buf) * len,
+ "%s = %d\n", chip->alg_flags[i].name,
+ (bool)(alg_flags & chip->alg_flags[i].bit));
+ }
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static const struct file_operations fg_alg_flags_fops = {
+ .open = fg_alg_flags_open,
+ .read = fg_alg_flags_read,
+};
+
+int fg_debugfs_create(struct fg_chip *chip)
+{
+ int rc;
+
+ pr_debug("Creating debugfs file-system\n");
+ chip->dfs_root = debugfs_create_dir("fg", NULL);
+ if (IS_ERR_OR_NULL(chip->dfs_root)) {
+ if (PTR_ERR(chip->dfs_root) == -ENODEV)
+ pr_err("debugfs is not enabled in the kernel\n");
+ else
+ pr_err("error creating fg dfs root rc=%ld\n",
+ (long)chip->dfs_root);
+ return -ENODEV;
+ }
+
+ rc = fg_sram_debugfs_create(chip);
+ if (rc < 0) {
+ pr_err("failed to create sram dfs rc=%d\n", rc);
+ goto err_remove_fs;
+ }
+
+ if (!debugfs_create_file("alg_flags", S_IRUSR, chip->dfs_root, chip,
+ &fg_alg_flags_fops)) {
+ pr_err("failed to create alg_flags file\n");
+ goto err_remove_fs;
+ }
+
+ return 0;
+
+err_remove_fs:
+ debugfs_remove_recursive(chip->dfs_root);
return -ENOMEM;
}
diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c
index dda94bb3f932..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
@@ -73,6 +75,8 @@
#define LAST_BATT_SOC_OFFSET 0
#define LAST_MONOTONIC_SOC_WORD 119
#define LAST_MONOTONIC_SOC_OFFSET 2
+#define ALG_FLAGS_WORD 120
+#define ALG_FLAGS_OFFSET 1
/* v2 SRAM address and offset in ascending order */
#define DELTA_SOC_THR_v2_WORD 13
@@ -82,7 +86,7 @@
#define CHG_TERM_CURR_v2_WORD 15
#define CHG_TERM_CURR_v2_OFFSET 1
#define EMPTY_VOLT_v2_WORD 15
-#define EMPTY_VOLT_v2_OFFSET 2
+#define EMPTY_VOLT_v2_OFFSET 3
#define VBATT_LOW_v2_WORD 16
#define VBATT_LOW_v2_OFFSET 0
@@ -90,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,
@@ -112,13 +118,15 @@ 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,
fg_decode_value_16b),
PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 244141, 1000, 0, NULL,
fg_decode_value_16b),
+ PARAM(ALG_FLAGS, ALG_FLAGS_WORD, ALG_FLAGS_OFFSET, 1, 1, 1, 0, NULL,
+ fg_decode_default),
/* Entries below here are configurable during initialization */
PARAM(CUTOFF_VOLT, CUTOFF_VOLT_WORD, CUTOFF_VOLT_OFFSET, 2, 1000000,
244141, 0, fg_encode_voltage, NULL),
@@ -148,20 +156,22 @@ 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,
fg_decode_value_16b),
PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 244141, 1000, 0, NULL,
fg_decode_value_16b),
+ PARAM(ALG_FLAGS, ALG_FLAGS_WORD, ALG_FLAGS_OFFSET, 1, 1, 1, 0, NULL,
+ fg_decode_default),
/* Entries below here are configurable during initialization */
PARAM(CUTOFF_VOLT, CUTOFF_VOLT_WORD, CUTOFF_VOLT_OFFSET, 2, 1000000,
244141, 0, fg_encode_voltage, NULL),
- PARAM(EMPTY_VOLT, EMPTY_VOLT_v2_WORD, EMPTY_VOLT_v2_OFFSET, 1, 100000,
- 390625, -2000, fg_encode_voltage, NULL),
- PARAM(VBATT_LOW, VBATT_LOW_v2_WORD, VBATT_LOW_v2_OFFSET, 1, 100000,
- 390625, -2000, fg_encode_voltage, NULL),
+ PARAM(EMPTY_VOLT, EMPTY_VOLT_v2_WORD, EMPTY_VOLT_v2_OFFSET, 1, 1000,
+ 15625, -2000, fg_encode_voltage, NULL),
+ PARAM(VBATT_LOW, VBATT_LOW_v2_WORD, VBATT_LOW_v2_OFFSET, 1, 1000,
+ 15625, -2000, fg_encode_voltage, NULL),
PARAM(SYS_TERM_CURR, SYS_TERM_CURR_WORD, SYS_TERM_CURR_OFFSET, 3,
1000000, 122070, 0, fg_encode_current, NULL),
PARAM(CHG_TERM_CURR, CHG_TERM_CURR_v2_WORD, CHG_TERM_CURR_v2_OFFSET, 1,
@@ -183,6 +193,67 @@ static struct fg_sram_param pmicobalt_v2_sram_params[] = {
ESR_TIMER_CHG_INIT_OFFSET, 2, 1, 1, 0, fg_encode_default, NULL),
};
+static struct fg_alg_flag pmicobalt_v1_alg_flags[] = {
+ [ALG_FLAG_SOC_LT_OTG_MIN] = {
+ .name = "SOC_LT_OTG_MIN",
+ .bit = BIT(0),
+ },
+ [ALG_FLAG_SOC_LT_RECHARGE] = {
+ .name = "SOC_LT_RECHARGE",
+ .bit = BIT(1),
+ },
+ [ALG_FLAG_IBATT_LT_ITERM] = {
+ .name = "IBATT_LT_ITERM",
+ .bit = BIT(2),
+ },
+ [ALG_FLAG_IBATT_GT_HPM] = {
+ .name = "IBATT_GT_HPM",
+ .bit = BIT(3),
+ },
+ [ALG_FLAG_IBATT_GT_UPM] = {
+ .name = "IBATT_GT_UPM",
+ .bit = BIT(4),
+ },
+ [ALG_FLAG_VBATT_LT_RECHARGE] = {
+ .name = "VBATT_LT_RECHARGE",
+ .bit = BIT(5),
+ },
+ [ALG_FLAG_VBATT_GT_VFLOAT] = {
+ .invalid = true,
+ },
+};
+
+static struct fg_alg_flag pmicobalt_v2_alg_flags[] = {
+ [ALG_FLAG_SOC_LT_OTG_MIN] = {
+ .name = "SOC_LT_OTG_MIN",
+ .bit = BIT(0),
+ },
+ [ALG_FLAG_SOC_LT_RECHARGE] = {
+ .name = "SOC_LT_RECHARGE",
+ .bit = BIT(1),
+ },
+ [ALG_FLAG_IBATT_LT_ITERM] = {
+ .name = "IBATT_LT_ITERM",
+ .bit = BIT(2),
+ },
+ [ALG_FLAG_IBATT_GT_HPM] = {
+ .name = "IBATT_GT_HPM",
+ .bit = BIT(4),
+ },
+ [ALG_FLAG_IBATT_GT_UPM] = {
+ .name = "IBATT_GT_UPM",
+ .bit = BIT(5),
+ },
+ [ALG_FLAG_VBATT_LT_RECHARGE] = {
+ .name = "VBATT_LT_RECHARGE",
+ .bit = BIT(6),
+ },
+ [ALG_FLAG_VBATT_GT_VFLOAT] = {
+ .name = "VBATT_GT_VFLOAT",
+ .bit = BIT(7),
+ },
+};
+
static int fg_gen3_debug_mask;
module_param_named(
debug_mask, fg_gen3_debug_mask, int, S_IRUSR | S_IWUSR
@@ -193,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,
@@ -369,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;
}
@@ -410,7 +325,7 @@ static void fg_encode_current(struct fg_sram_param *sp,
int64_t temp;
s64 current_ma;
- current_ma = -val;
+ current_ma = val;
temp = (int64_t)div_s64(current_ma * sp[id].numrtr, sp[id].denmtr);
pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val);
for (i = 0; i < sp[id].len; i++) {
@@ -685,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;
@@ -796,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,
@@ -835,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;
@@ -847,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;
}
@@ -859,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;
}
@@ -898,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 = {
@@ -1054,6 +1300,9 @@ static int fg_hw_init(struct fg_chip *chip)
}
}
+ if (chip->cyc_ctr.en)
+ restore_cycle_counter(chip);
+
return 0;
}
@@ -1113,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) {
@@ -1155,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;
}
@@ -1163,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;
}
@@ -1276,7 +1535,7 @@ static int fg_register_interrupts(struct fg_chip *chip)
#define DEFAULT_CUTOFF_VOLT_MV 3200
#define DEFAULT_EMPTY_VOLT_MV 3100
#define DEFAULT_CHG_TERM_CURR_MA 100
-#define DEFAULT_SYS_TERM_CURR_MA 125
+#define DEFAULT_SYS_TERM_CURR_MA -125
#define DEFAULT_DELTA_SOC_THR 1
#define DEFAULT_RECHARGE_SOC_THR 95
#define DEFAULT_BATT_TEMP_COLD 0
@@ -1318,12 +1577,15 @@ static int fg_parse_dt(struct fg_chip *chip)
switch (chip->pmic_rev_id->pmic_subtype) {
case PMICOBALT_SUBTYPE:
- if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4)
+ if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4) {
chip->sp = pmicobalt_v1_sram_params;
- else if (chip->pmic_rev_id->rev4 == PMICOBALT_V2P0_REV4)
+ chip->alg_flags = pmicobalt_v1_alg_flags;
+ } else if (chip->pmic_rev_id->rev4 == PMICOBALT_V2P0_REV4) {
chip->sp = pmicobalt_v2_sram_params;
- else
+ chip->alg_flags = pmicobalt_v2_alg_flags;
+ } else {
return -EINVAL;
+ }
break;
default:
return -EINVAL;
@@ -1458,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;
}
@@ -1465,7 +1730,7 @@ static int fg_parse_dt(struct fg_chip *chip)
static void fg_cleanup(struct fg_chip *chip)
{
power_supply_unreg_notifier(&chip->nb);
- debugfs_remove_recursive(chip->dentry);
+ debugfs_remove_recursive(chip->dfs_root);
if (chip->awake_votable)
destroy_votable(chip->awake_votable);
@@ -1510,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) {
@@ -1562,7 +1829,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
if (fg_irqs[SOC_UPDATE_IRQ].irq)
disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);
- rc = fg_sram_debugfs_create(chip);
+ rc = fg_debugfs_create(chip);
if (rc < 0) {
dev_err(chip->dev, "Error in creating debugfs entries, rc:%d\n",
rc);
diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c
index a7dd0bfc66ad..1b63f51088ee 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
@@ -197,7 +199,7 @@ static struct smb_params v1_params = {
#define STEP_CHARGING_MAX_STEPS 5
struct smb_dt_props {
- bool suspend_input;
+ bool no_battery;
int fcc_ua;
int usb_icl_ua;
int dc_icl_ua;
@@ -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 {
@@ -256,8 +259,8 @@ static int smb2_parse_dt(struct smb2 *chip)
if (rc < 0)
chg->step_chg_enabled = false;
- chip->dt.suspend_input = of_property_read_bool(node,
- "qcom,suspend-input");
+ chip->dt.no_battery = of_property_read_bool(node,
+ "qcom,batteryless-platform");
rc = of_property_read_u32(node,
"qcom,fcc-max-ua", &chip->dt.fcc_ua);
@@ -392,7 +395,11 @@ static int smb2_usb_get_prop(struct power_supply *psy,
rc = -EINVAL;
break;
}
- return rc;
+ if (rc < 0) {
+ pr_debug("Couldn't get prop %d rc = %d\n", psp, rc);
+ return -ENODATA;
+ }
+ return 0;
}
static int smb2_usb_set_prop(struct power_supply *psy,
@@ -506,8 +513,11 @@ static int smb2_dc_get_prop(struct power_supply *psy,
default:
return -EINVAL;
}
-
- return rc;
+ if (rc < 0) {
+ pr_debug("Couldn't get prop %d rc = %d\n", psp, rc);
+ return -ENODATA;
+ }
+ return 0;
}
static int smb2_dc_set_prop(struct power_supply *psy,
@@ -589,14 +599,21 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_CHARGER_TEMP,
POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ 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,
+ POWER_SUPPLY_PROP_CHARGE_DONE,
};
static int smb2_batt_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
- int rc;
struct smb_charger *chg = power_supply_get_drvdata(psy);
+ int rc = 0;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
@@ -628,13 +645,38 @@ 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);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ rc = smblib_get_prop_batt_current_now(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ rc = smblib_get_prop_batt_temp(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_DONE:
+ val->intval = chg->chg_done;
break;
default:
pr_err("batt power supply prop %d not supported\n", psp);
return -EINVAL;
}
- return rc;
+ if (rc < 0) {
+ pr_debug("Couldn't get prop %d rc = %d\n", psp, rc);
+ return -ENODATA;
+ }
+
+ return 0;
}
static int smb2_batt_set_prop(struct power_supply *psy,
@@ -654,6 +696,9 @@ static int smb2_batt_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CAPACITY:
rc = smblib_set_prop_batt_capacity(chg, val);
break;
+ case POWER_SUPPLY_PROP_CHARGE_DONE:
+ chg->chg_done = val->intval;
+ break;
default:
rc = -EINVAL;
}
@@ -919,6 +964,9 @@ static int smb2_init_hw(struct smb2 *chip)
struct smb_charger *chg = &chip->chg;
int rc;
+ if (chip->dt.no_battery)
+ chg->fake_capacity = 50;
+
if (chip->dt.fcc_ua < 0)
smblib_get_charge_param(chg, &chg->param.fcc, &chip->dt.fcc_ua);
@@ -935,13 +983,15 @@ static int smb2_init_hw(struct smb2 *chip)
/* votes must be cast before configuring software control */
vote(chg->pl_disable_votable,
- USBIN_ICL_VOTER, true, 0);
+ PL_INDIRECT_VOTER, true, 0);
vote(chg->pl_disable_votable,
CHG_STATE_VOTER, true, 0);
+ vote(chg->pl_disable_votable,
+ PARALLEL_PSY_VOTER, true, 0);
vote(chg->usb_suspend_votable,
- DEFAULT_VOTER, chip->dt.suspend_input, 0);
+ DEFAULT_VOTER, chip->dt.no_battery, 0);
vote(chg->dc_suspend_votable,
- DEFAULT_VOTER, chip->dt.suspend_input, 0);
+ DEFAULT_VOTER, chip->dt.no_battery, 0);
vote(chg->fcc_max_votable,
DEFAULT_VOTER, true, chip->dt.fcc_ua);
vote(chg->fv_votable,
@@ -951,14 +1001,10 @@ static int smb2_init_hw(struct smb2 *chip)
vote(chg->dc_icl_votable,
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
- /*
- * Configure charge enable for software control; active high, and end
- * the charge cycle while the battery is OV.
- */
+ /* Configure charge enable for software control; active high */
rc = smblib_masked_write(chg, CHGR_CFG2_REG,
CHG_EN_POLARITY_BIT |
- CHG_EN_SRC_BIT |
- BAT_OV_ECC_BIT, BAT_OV_ECC_BIT);
+ CHG_EN_SRC_BIT, 0);
if (rc < 0) {
dev_err(chg->dev, "Couldn't configure charger rc=%d\n", rc);
return rc;
@@ -1054,6 +1100,43 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+static int smb2_setup_wa_flags(struct smb2 *chip)
+{
+ struct smb_charger *chg = &chip->chg;
+ 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:
+ if (pmic_rev_id->rev4 == PMICOBALT_V1P1_REV4) /* PMI rev 1.1 */
+ chg->wa_flags |= QC_CHARGER_DETECTION_WA_BIT;
+ break;
+ default:
+ pr_err("PMIC subtype %d not supported\n",
+ pmic_rev_id->pmic_subtype);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/****************************
* DETERMINE INITIAL STATUS *
****************************/
@@ -1078,60 +1161,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)
@@ -1174,6 +1378,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,
@@ -1239,6 +1444,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 21b330127369..e9c189ae17e7 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);
@@ -519,7 +521,7 @@ static int smblib_fcc_max_vote_callback(struct votable *votable, void *data,
{
struct smb_charger *chg = data;
- return vote(chg->fcc_votable, FCC_MAX_RESULT, true, fcc_ua);
+ return vote(chg->fcc_votable, FCC_MAX_RESULT_VOTER, true, fcc_ua);
}
static int smblib_fcc_vote_callback(struct votable *votable, void *data,
@@ -729,6 +731,17 @@ static int smblib_chg_disable_vote_callback(struct votable *votable, void *data,
return 0;
}
+
+static int smblib_pl_enable_indirect_vote_callback(struct votable *votable,
+ void *data, int chg_enable, const char *client)
+{
+ struct smb_charger *chg = data;
+
+ vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, !chg_enable, 0);
+
+ return 0;
+}
+
/*****************
* OTG REGULATOR *
*****************/
@@ -779,13 +792,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;
}
@@ -870,25 +894,28 @@ int smblib_get_prop_batt_capacity(struct smb_charger *chg,
int smblib_get_prop_batt_status(struct smb_charger *chg,
union power_supply_propval *val)
{
- int rc;
- u8 stat;
union power_supply_propval pval = {0, };
+ bool usb_online, dc_online;
+ u8 stat;
+ int rc;
- smblib_get_prop_input_suspend(chg, &pval);
- if (pval.intval) {
- val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ rc = smblib_get_prop_usb_online(chg, &pval);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't get usb online property rc=%d\n",
+ rc);
return rc;
}
+ usb_online = (bool)pval.intval;
- rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat);
+ rc = smblib_get_prop_dc_online(chg, &pval);
if (rc < 0) {
- dev_err(chg->dev, "Couldn't read POWER_PATH_STATUS rc=%d\n",
+ dev_err(chg->dev, "Couldn't get dc online property rc=%d\n",
rc);
return rc;
}
+ dc_online = (bool)pval.intval;
- if (!(stat & (USE_USBIN_BIT | USE_DCIN_BIT)) ||
- !(stat & VALID_INPUT_POWER_SOURCE_BIT)) {
+ if (!usb_online && !dc_online) {
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
return rc;
}
@@ -899,16 +926,29 @@ int smblib_get_prop_batt_status(struct smb_charger *chg,
rc);
return rc;
}
- smblib_dbg(chg, PR_REGISTER, "BATTERY_CHARGER_STATUS_1 = 0x%02x\n",
- stat);
stat = stat & BATTERY_CHARGER_STATUS_MASK;
- if (stat >= COMPLETED_CHARGE)
- val->intval = POWER_SUPPLY_STATUS_FULL;
- else
+ switch (stat) {
+ case TRICKLE_CHARGE:
+ case PRE_CHARGE:
+ case FAST_CHARGE:
+ case FULLON_CHARGE:
+ case TAPER_CHARGE:
val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case TERMINATE_CHARGE:
+ case INHIBIT_CHARGE:
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ break;
+ case DISABLE_CHARGE:
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+ }
- return rc;
+ return 0;
}
int smblib_get_prop_batt_charge_type(struct smb_charger *chg,
@@ -923,8 +963,6 @@ int smblib_get_prop_batt_charge_type(struct smb_charger *chg,
rc);
return rc;
}
- smblib_dbg(chg, PR_REGISTER, "BATTERY_CHARGER_STATUS_1 = 0x%02x\n",
- stat);
switch (stat & BATTERY_CHARGER_STATUS_MASK) {
case TRICKLE_CHARGE:
@@ -1003,6 +1041,69 @@ int smblib_get_prop_input_current_limited(struct smb_charger *chg,
return 0;
}
+int smblib_get_prop_batt_voltage_now(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+
+ if (!chg->bms_psy)
+ return -EINVAL;
+
+ rc = power_supply_get_property(chg->bms_psy,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW, val);
+ return rc;
+}
+
+int smblib_get_prop_batt_current_now(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+
+ if (!chg->bms_psy)
+ return -EINVAL;
+
+ rc = power_supply_get_property(chg->bms_psy,
+ POWER_SUPPLY_PROP_CURRENT_NOW, val);
+ return rc;
+}
+
+int smblib_get_prop_batt_temp(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+
+ if (!chg->bms_psy)
+ return -EINVAL;
+
+ rc = power_supply_get_property(chg->bms_psy,
+ POWER_SUPPLY_PROP_TEMP, val);
+ 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 *
***********************/
@@ -1054,13 +1155,14 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg,
chg->system_temp_level = val->intval;
if (chg->system_temp_level == chg->thermal_levels)
- return vote(chg->chg_disable_votable, THERMAL_DAEMON, true, 0);
+ return vote(chg->chg_disable_votable,
+ THERMAL_DAEMON_VOTER, true, 0);
- vote(chg->chg_disable_votable, THERMAL_DAEMON, false, 0);
+ vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, false, 0);
if (chg->system_temp_level == 0)
- return vote(chg->fcc_votable, THERMAL_DAEMON, false, 0);
+ return vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, false, 0);
- vote(chg->fcc_votable, THERMAL_DAEMON, true,
+ vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, true,
chg->thermal_mitigation[chg->system_temp_level]);
return 0;
}
@@ -1181,7 +1283,6 @@ int smblib_get_prop_usb_online(struct smb_charger *chg,
val->intval = (stat & USE_USBIN_BIT) &&
(stat & VALID_INPUT_POWER_SOURCE_BIT);
-
return rc;
}
@@ -1500,7 +1601,11 @@ int smblib_set_prop_usb_voltage_min(struct smb_charger *chg,
return rc;
}
- chg->voltage_min_uv = val->intval;
+ if (chg->mode == PARALLEL_MASTER)
+ vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER,
+ min_uv > MICRO_5V, 0);
+
+ chg->voltage_min_uv = min_uv;
return rc;
}
@@ -1518,7 +1623,7 @@ int smblib_set_prop_usb_voltage_max(struct smb_charger *chg,
return rc;
}
- chg->voltage_max_uv = val->intval;
+ chg->voltage_max_uv = max_uv;
return rc;
}
@@ -1526,6 +1631,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");
@@ -1543,6 +1649,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;
@@ -1558,45 +1698,61 @@ 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;
}
-irqreturn_t smblib_handle_chg_state_change(int irq, void *data)
+static void smblib_pl_handle_chg_state_change(struct smb_charger *chg, u8 stat)
{
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- union power_supply_propval pval = {0, };
- int rc;
-
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+ bool pl_enabled;
if (chg->mode != PARALLEL_MASTER)
- return IRQ_HANDLED;
+ return;
- rc = smblib_get_prop_batt_charge_type(chg, &pval);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't get batt charge type rc=%d\n", rc);
- return IRQ_HANDLED;
+ pl_enabled = !get_effective_result_locked(chg->pl_disable_votable);
+ switch (stat) {
+ case FAST_CHARGE:
+ case FULLON_CHARGE:
+ vote(chg->pl_disable_votable, CHG_STATE_VOTER, false, 0);
+ break;
+ case TAPER_CHARGE:
+ if (pl_enabled) {
+ cancel_delayed_work_sync(&chg->pl_taper_work);
+ schedule_delayed_work(&chg->pl_taper_work, 0);
+ }
+ break;
+ case TERMINATE_CHARGE:
+ case INHIBIT_CHARGE:
+ case DISABLE_CHARGE:
+ vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0);
+ break;
+ default:
+ break;
}
+}
- if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_FAST)
- vote(chg->pl_disable_votable, CHG_STATE_VOTER, false, 0);
+irqreturn_t smblib_handle_chg_state_change(int irq, void *data)
+{
+ union power_supply_propval pval = {0, };
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+ u8 stat;
+ int rc;
- if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER
- && !get_effective_result_locked(chg->pl_disable_votable)) {
- cancel_delayed_work_sync(&chg->pl_taper_work);
- schedule_delayed_work(&chg->pl_taper_work, 0);
- }
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- rc = smblib_get_prop_batt_status(chg, &pval);
+ rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
if (rc < 0) {
- dev_err(chg->dev, "Couldn't get batt status type rc=%d\n", rc);
+ dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n",
+ rc);
return IRQ_HANDLED;
}
- if (pval.intval == POWER_SUPPLY_STATUS_FULL)
- vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0);
+ stat = stat & BATTERY_CHARGER_STATUS_MASK;
+ smblib_pl_handle_chg_state_change(chg, stat);
+ pval.intval = (stat == TERMINATE_CHARGE);
+ power_supply_set_property(chg->batt_psy, POWER_SUPPLY_PROP_CHARGE_DONE,
+ &pval);
+ power_supply_changed(chg->batt_psy);
return IRQ_HANDLED;
}
@@ -1666,7 +1822,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;
}
@@ -1676,7 +1832,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;
}
@@ -1729,54 +1885,29 @@ 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;
}
-#define MICRO_5P5V 5500000
-#define USB_WEAK_INPUT_MA 1500000
-static bool is_icl_pl_ready(struct smb_charger *chg)
+#define USB_WEAK_INPUT_MA 1400000
+irqreturn_t smblib_handle_icl_change(int irq, void *data)
{
- union power_supply_propval pval = {0, };
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
int icl_ma;
int rc;
- rc = smblib_get_prop_usb_voltage_now(chg, &pval);
+ rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &icl_ma);
if (rc < 0) {
- dev_err(chg->dev, "Couldn't get prop usb voltage rc=%d\n", rc);
- return false;
- }
-
- if (pval.intval <= MICRO_5P5V) {
- rc = smblib_get_charge_param(chg,
- &chg->param.icl_stat, &icl_ma);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't get ICL status rc=%d\n",
- rc);
- return false;
- }
-
- if (icl_ma < USB_WEAK_INPUT_MA)
- return false;
+ dev_err(chg->dev, "Couldn't get ICL status rc=%d\n", rc);
+ return IRQ_HANDLED;
}
- /*
- * Always enable parallel charging when USB INPUT is higher than 5V
- * regardless of the AICL results. Assume chargers above 5V are strong
- */
-
- return true;
-}
-
-irqreturn_t smblib_handle_icl_change(int irq, void *data)
-{
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
-
if (chg->mode == PARALLEL_MASTER)
- vote(chg->pl_disable_votable, USBIN_ICL_VOTER,
- !is_icl_pl_ready(chg), 0);
+ vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER,
+ icl_ma >= USB_WEAK_INPUT_MA, 0);
smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
@@ -1813,12 +1944,27 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg,
if (!rising)
return;
+ if (chg->mode == PARALLEL_MASTER)
+ vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0);
+
/* the APSD done handler will set the USB supply type */
apsd_result = smblib_get_apsd_result(chg);
smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-3p0-auth-done rising; %s detected\n",
apsd_result->name);
}
+static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
+ bool rising, bool qc_charger)
+{
+ if (rising && !qc_charger) {
+ vote(chg->pd_allowed_votable, DEFAULT_VOTER, true, 0);
+ power_supply_changed(chg->usb_psy);
+ }
+
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: smblib_handle_hvdcp_check_timeout %s\n",
+ rising ? "rising" : "falling");
+}
+
/* triggers when HVDCP is detected */
static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
bool rising)
@@ -1850,8 +1996,9 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
vote(chg->pd_allowed_votable, DEFAULT_VOTER, true, 0);
break;
case DCP_CHARGER_BIT:
- schedule_delayed_work(&chg->hvdcp_detect_work,
- msecs_to_jiffies(HVDCP_DET_MS));
+ if (chg->wa_flags & QC_CHARGER_DETECTION_WA_BIT)
+ schedule_delayed_work(&chg->hvdcp_detect_work,
+ msecs_to_jiffies(HVDCP_DET_MS));
break;
default:
break;
@@ -1885,6 +2032,10 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data)
smblib_handle_hvdcp_detect_done(chg,
(bool)(stat & QC_CHARGER_BIT));
+ smblib_handle_hvdcp_check_timeout(chg,
+ (bool)(stat & HVDCP_CHECK_TIMEOUT_BIT),
+ (bool)(stat & QC_CHARGER_BIT));
+
smblib_handle_hvdcp_3p0_auth_done(chg,
(bool)(stat & QC_AUTH_DONE_STATUS_BIT));
@@ -1938,8 +2089,9 @@ static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
!rising || sink_attached, 0);
if (!rising || sink_attached) {
- /* icl votes to disable parallel charging */
- vote(chg->pl_disable_votable, USBIN_ICL_VOTER, true, 0);
+ /* reset both usbin current and voltage votes */
+ vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
+ vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
/* reset taper_end voter here */
vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0);
}
@@ -1964,11 +2116,6 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
}
smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat);
- if (stat & TYPEC_VBUS_ERROR_STATUS_BIT) {
- dev_err(chg->dev, "IRQ: vbus-error rising\n");
- return IRQ_HANDLED;
- }
-
smblib_handle_typec_cc(chg,
(bool)(stat & CC_ATTACHED_BIT));
smblib_handle_typec_debounce_done(chg,
@@ -1977,6 +2124,10 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
power_supply_changed(chg->usb_psy);
+ if (stat & TYPEC_VBUS_ERROR_STATUS_BIT)
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s vbus-error\n",
+ irq_data->name);
+
return IRQ_HANDLED;
}
@@ -2037,8 +2188,7 @@ static void smblib_pl_detect_work(struct work_struct *work)
struct smb_charger *chg = container_of(work, struct smb_charger,
pl_detect_work);
- if (!get_effective_result_locked(chg->pl_disable_votable))
- rerun_election(chg->pl_disable_votable);
+ vote(chg->pl_disable_votable, PARALLEL_PSY_VOTER, false, 0);
}
#define MINIMUM_PARALLEL_FCC_UA 500000
@@ -2063,7 +2213,7 @@ static void smblib_pl_taper_work(struct work_struct *work)
}
if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
- vote(chg->awake_votable, PL_VOTER, true, 0);
+ vote(chg->awake_votable, PL_TAPER_WORK_RUNNING_VOTER, true, 0);
/* Reduce the taper percent by 25 percent */
chg->pl.taper_percent = chg->pl.taper_percent
* TAPER_RESIDUAL_PERCENT / 100;
@@ -2077,7 +2227,7 @@ static void smblib_pl_taper_work(struct work_struct *work)
* Master back to Fast Charge, get out of this round of taper reduction
*/
done:
- vote(chg->awake_votable, PL_VOTER, false, 0);
+ vote(chg->awake_votable, PL_TAPER_WORK_RUNNING_VOTER, false, 0);
}
static void clear_hdc_work(struct work_struct *work)
@@ -2179,6 +2329,15 @@ static int smblib_create_votables(struct smb_charger *chg)
return rc;
}
+ chg->pl_enable_votable_indirect = create_votable("PL_ENABLE_INDIRECT",
+ VOTE_SET_ANY,
+ smblib_pl_enable_indirect_vote_callback,
+ chg);
+ if (IS_ERR(chg->pl_enable_votable_indirect)) {
+ rc = PTR_ERR(chg->pl_enable_votable_indirect);
+ return rc;
+ }
+
return rc;
}
@@ -2204,6 +2363,10 @@ static void smblib_destroy_votables(struct smb_charger *chg)
destroy_votable(chg->awake_votable);
if (chg->pl_disable_votable)
destroy_votable(chg->pl_disable_votable);
+ if (chg->chg_disable_votable)
+ destroy_votable(chg->chg_disable_votable);
+ if (chg->pl_enable_votable_indirect)
+ destroy_votable(chg->pl_enable_votable_indirect);
}
static void smblib_iio_deinit(struct smb_charger *chg)
@@ -2240,9 +2403,6 @@ int smblib_init(struct smb_charger *chg)
return rc;
}
- chg->bms_psy = power_supply_get_by_name("bms");
- chg->pl.psy = power_supply_get_by_name("parallel");
-
rc = smblib_register_notifier(chg);
if (rc < 0) {
dev_err(chg->dev,
@@ -2250,6 +2410,12 @@ int smblib_init(struct smb_charger *chg)
return rc;
}
+ chg->bms_psy = power_supply_get_by_name("bms");
+ chg->pl.psy = power_supply_get_by_name("parallel");
+ if (chg->pl.psy)
+ vote(chg->pl_disable_votable, PARALLEL_PSY_VOTER,
+ false, 0);
+
break;
case PARALLEL_SLAVE:
break;
diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h
index aeb1eb2c454f..c9732c25dfcd 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),
@@ -23,16 +24,19 @@ enum print_reason {
PR_MISC = BIT(2),
};
-#define DEFAULT_VOTER "DEFAULT_VOTER"
-#define USER_VOTER "USER_VOTER"
-#define PD_VOTER "PD_VOTER"
-#define PL_VOTER "PL_VOTER"
-#define USBIN_ICL_VOTER "USBIN_ICL_VOTER"
-#define CHG_STATE_VOTER "CHG_STATE_VOTER"
-#define TYPEC_SRC_VOTER "TYPEC_SRC_VOTER"
-#define TAPER_END_VOTER "TAPER_END_VOTER"
-#define FCC_MAX_RESULT "FCC_MAX_RESULT"
-#define THERMAL_DAEMON "THERMAL_DAEMON"
+#define DEFAULT_VOTER "DEFAULT_VOTER"
+#define USER_VOTER "USER_VOTER"
+#define PD_VOTER "PD_VOTER"
+#define PL_TAPER_WORK_RUNNING_VOTER "PL_TAPER_WORK_RUNNING_VOTER"
+#define PARALLEL_PSY_VOTER "PARALLEL_PSY_VOTER"
+#define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER"
+#define USBIN_I_VOTER "USBIN_I_VOTER"
+#define USBIN_V_VOTER "USBIN_V_VOTER"
+#define CHG_STATE_VOTER "CHG_STATE_VOTER"
+#define TYPEC_SRC_VOTER "TYPEC_SRC_VOTER"
+#define TAPER_END_VOTER "TAPER_END_VOTER"
+#define FCC_MAX_RESULT_VOTER "FCC_MAX_RESULT_VOTER"
+#define THERMAL_DAEMON_VOTER "THERMAL_DAEMON_VOTER"
enum smb_mode {
PARALLEL_MASTER = 0,
@@ -40,14 +44,19 @@ enum smb_mode {
NUM_MODES,
};
+enum {
+ QC_CHARGER_DETECTION_WA_BIT = BIT(0),
+};
+
struct smb_regulator {
struct regulator_dev *rdev;
struct regulator_desc rdesc;
};
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 {
@@ -137,6 +146,7 @@ struct smb_charger {
struct votable *awake_votable;
struct votable *pl_disable_votable;
struct votable *chg_disable_votable;
+ struct votable *pl_enable_votable_indirect;
/* work */
struct work_struct bms_update_work;
@@ -161,6 +171,10 @@ struct smb_charger {
bool step_chg_enabled;
bool is_hdc;
+ bool chg_done;
+
+ /* workaround flag */
+ u32 wa_flags;
};
int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
@@ -222,6 +236,14 @@ int smblib_get_prop_system_temp_level(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_input_current_limited(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_batt_voltage_now(struct smb_charger *chg,
+ union power_supply_propval *val);
+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..c4ad72e254f9 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 {
@@ -40,8 +41,9 @@ enum {
FAST_CHARGE,
FULLON_CHARGE,
TAPER_CHARGE,
- COMPLETED_CHARGE,
+ TERMINATE_CHARGE,
INHIBIT_CHARGE,
+ DISABLE_CHARGE,
};
#define BATTERY_CHARGER_STATUS_2_REG (CHGR_BASE + 0x07)
@@ -425,7 +427,7 @@ enum {
#define APSD_STATUS_REG (USBIN_BASE + 0x07)
#define APSD_STATUS_7_BIT BIT(7)
-#define APSD_STATUS_6_BIT BIT(6)
+#define HVDCP_CHECK_TIMEOUT_BIT BIT(6)
#define SLOW_PLUGIN_TIMEOUT_BIT BIT(5)
#define ENUMERATION_DONE_BIT BIT(4)
#define VADP_CHANGE_DONE_AFTER_AUTH_BIT BIT(3)
@@ -585,6 +587,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 +609,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 a77b0bbc193c..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
@@ -184,8 +185,11 @@ static int smb138x_usb_get_prop(struct power_supply *psy,
pr_err("get prop %d is not supported\n", prop);
return -EINVAL;
}
-
- return rc;
+ if (rc < 0) {
+ pr_debug("Couldn't get prop %d rc = %d\n", prop, rc);
+ return -ENODATA;
+ }
+ return 0;
}
static int smb138x_usb_set_prop(struct power_supply *psy,
@@ -305,12 +309,14 @@ static int smb138x_batt_get_prop(struct power_supply *psy,
rc = smblib_get_prop_charger_temp_max(chg, val);
break;
default:
- pr_err("batt power supply get prop %d not supported\n",
- prop);
+ pr_err("batt power supply get prop %d not supported\n", prop);
return -EINVAL;
}
-
- return rc;
+ if (rc < 0) {
+ pr_debug("Couldn't get prop %d rc = %d\n", prop, rc);
+ return -ENODATA;
+ }
+ return 0;
}
static int smb138x_batt_set_prop(struct power_supply *psy,
@@ -436,8 +442,11 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
prop);
return -EINVAL;
}
-
- return rc;
+ if (rc < 0) {
+ pr_debug("Couldn't get prop %d rc = %d\n", prop, rc);
+ return -ENODATA;
+ }
+ return 0;
}
static int smb138x_parallel_set_prop(struct power_supply *psy,
@@ -740,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)
@@ -829,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/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index 75a0de0c532b..2f109013f723 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -36,6 +36,7 @@
#define EMERGENCY_DLOAD_MAGIC1 0x322A4F99
#define EMERGENCY_DLOAD_MAGIC2 0xC67E4350
#define EMERGENCY_DLOAD_MAGIC3 0x77777777
+#define EMMC_DLOAD_TYPE 0x2
#define SCM_IO_DISABLE_PMIC_ARBITER 1
#define SCM_IO_DEASSERT_PS_HOLD 2
@@ -46,12 +47,20 @@
static int restart_mode;
-void *restart_reason;
+static void *restart_reason, *dload_type_addr;
static bool scm_pmic_arbiter_disable_supported;
static bool scm_deassert_ps_hold_supported;
/* Download mode master kill-switch */
static void __iomem *msm_ps_hold;
static phys_addr_t tcsr_boot_misc_detect;
+static void scm_disable_sdi(void);
+
+/* Runtime could be only changed value once.
+ * There is no API from TZ to re-enable the registers.
+ * So the SDI cannot be re-enabled when it already by-passed.
+*/
+static int download_mode = 1;
+static struct kobject dload_kobj;
#ifdef CONFIG_QCOM_DLOAD_MODE
#define EDL_MODE_PROP "qcom,msm-imem-emergency_download_mode"
@@ -64,9 +73,23 @@ static void *emergency_dload_mode_addr;
static bool scm_dload_supported;
static int dload_set(const char *val, struct kernel_param *kp);
-static int download_mode = 1;
+/* interface for exporting attributes */
+struct reset_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
+ char *buf);
+ size_t (*store)(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count);
+};
+#define to_reset_attr(_attr) \
+ container_of(_attr, struct reset_attribute, attr)
+#define RESET_ATTR(_name, _mode, _show, _store) \
+ static struct reset_attribute reset_attr_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
module_param_call(download_mode, dload_set, param_get_int,
&download_mode, 0644);
+
static int panic_prep_restart(struct notifier_block *this,
unsigned long event, void *ptr)
{
@@ -170,7 +193,10 @@ static int dload_set(const char *val, struct kernel_param *kp)
return 0;
}
#else
-#define set_dload_mode(x) do {} while (0)
+static void set_dload_mode(int on)
+{
+ return;
+}
static void enable_emergency_dload_mode(void)
{
@@ -183,6 +209,26 @@ static bool get_dload_mode(void)
}
#endif
+static void scm_disable_sdi(void)
+{
+ int ret;
+ struct scm_desc desc = {
+ .args[0] = 1,
+ .args[1] = 0,
+ .arginfo = SCM_ARGS(2),
+ };
+
+ /* Needed to bypass debug image on some chips */
+ if (!is_scm_armv8())
+ ret = scm_call_atomic2(SCM_SVC_BOOT,
+ SCM_WDOG_DEBUG_BOOT_PART, 1, 0);
+ else
+ ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT,
+ SCM_WDOG_DEBUG_BOOT_PART), &desc);
+ if (ret)
+ pr_err("Failed to disable secure wdog debug: %d\n", ret);
+}
+
void msm_set_restart_mode(int mode)
{
restart_mode = mode;
@@ -320,13 +366,6 @@ static void deassert_ps_hold(void)
static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd)
{
- int ret;
- struct scm_desc desc = {
- .args[0] = 1,
- .args[1] = 0,
- .arginfo = SCM_ARGS(2),
- };
-
pr_notice("Going down for restart now\n");
msm_restart_prepare(cmd);
@@ -341,16 +380,7 @@ static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd)
msm_trigger_wdog_bite();
#endif
- /* Needed to bypass debug image on some chips */
- if (!is_scm_armv8())
- ret = scm_call_atomic2(SCM_SVC_BOOT,
- SCM_WDOG_DEBUG_BOOT_PART, 1, 0);
- else
- ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT,
- SCM_WDOG_DEBUG_BOOT_PART), &desc);
- if (ret)
- pr_err("Failed to disable secure wdog debug: %d\n", ret);
-
+ scm_disable_sdi();
halt_spmi_pmic_arbiter();
deassert_ps_hold();
@@ -359,27 +389,11 @@ static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd)
static void do_msm_poweroff(void)
{
- int ret;
- struct scm_desc desc = {
- .args[0] = 1,
- .args[1] = 0,
- .arginfo = SCM_ARGS(2),
- };
-
pr_notice("Powering off the SoC\n");
-#ifdef CONFIG_QCOM_DLOAD_MODE
+
set_dload_mode(0);
-#endif
+ scm_disable_sdi();
qpnp_pon_system_pwr_off(PON_POWER_OFF_SHUTDOWN);
- /* Needed to bypass debug image on some chips */
- if (!is_scm_armv8())
- ret = scm_call_atomic2(SCM_SVC_BOOT,
- SCM_WDOG_DEBUG_BOOT_PART, 1, 0);
- else
- ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT,
- SCM_WDOG_DEBUG_BOOT_PART), &desc);
- if (ret)
- pr_err("Failed to disable wdog debug: %d\n", ret);
halt_spmi_pmic_arbiter();
deassert_ps_hold();
@@ -389,6 +403,84 @@ static void do_msm_poweroff(void)
return;
}
+static ssize_t attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct reset_attribute *reset_attr = to_reset_attr(attr);
+ ssize_t ret = -EIO;
+
+ if (reset_attr->show)
+ ret = reset_attr->show(kobj, attr, buf);
+
+ return ret;
+}
+
+static ssize_t attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct reset_attribute *reset_attr = to_reset_attr(attr);
+ ssize_t ret = -EIO;
+
+ if (reset_attr->store)
+ ret = reset_attr->store(kobj, attr, buf, count);
+
+ return ret;
+}
+
+static const struct sysfs_ops reset_sysfs_ops = {
+ .show = attr_show,
+ .store = attr_store,
+};
+
+static struct kobj_type reset_ktype = {
+ .sysfs_ops = &reset_sysfs_ops,
+};
+
+static ssize_t show_emmc_dload(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ uint32_t read_val, show_val;
+
+ read_val = __raw_readl(dload_type_addr);
+ if (read_val == EMMC_DLOAD_TYPE)
+ show_val = 1;
+ else
+ show_val = 0;
+
+ return snprintf(buf, sizeof(show_val), "%u\n", show_val);
+}
+
+static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ uint32_t enabled;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &enabled);
+ if (ret < 0)
+ return ret;
+
+ if (!((enabled == 0) || (enabled == 1)))
+ return -EINVAL;
+
+ if (enabled == 1)
+ __raw_writel(EMMC_DLOAD_TYPE, dload_type_addr);
+ else
+ __raw_writel(0, dload_type_addr);
+
+ return count;
+}
+RESET_ATTR(emmc_dload, 0644, show_emmc_dload, store_emmc_dload);
+
+static struct attribute *reset_attrs[] = {
+ &reset_attr_emmc_dload.attr,
+ NULL
+};
+
+static struct attribute_group reset_attr_group = {
+ .attrs = reset_attrs,
+};
+
static int msm_restart_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -419,6 +511,33 @@ static int msm_restart_probe(struct platform_device *pdev)
pr_err("unable to map imem EDLOAD mode offset\n");
}
+ np = of_find_compatible_node(NULL, NULL,
+ "qcom,msm-imem-dload-type");
+ if (!np) {
+ pr_err("unable to find DT imem dload-type node\n");
+ goto skip_sysfs_create;
+ } else {
+ dload_type_addr = of_iomap(np, 0);
+ if (!dload_type_addr) {
+ pr_err("unable to map imem dload-type offset\n");
+ goto skip_sysfs_create;
+ }
+ }
+
+ ret = kobject_init_and_add(&dload_kobj, &reset_ktype,
+ kernel_kobj, "%s", "dload");
+ if (ret) {
+ pr_err("%s:Error in creation kobject_add\n", __func__);
+ kobject_put(&dload_kobj);
+ goto skip_sysfs_create;
+ }
+
+ ret = sysfs_create_group(&dload_kobj, &reset_attr_group);
+ if (ret) {
+ pr_err("%s:Error in creation sysfs_create_group\n", __func__);
+ kobject_del(&dload_kobj);
+ }
+skip_sysfs_create:
#endif
np = of_find_compatible_node(NULL, NULL,
"qcom,msm-imem-restart_reason");
@@ -454,6 +573,8 @@ static int msm_restart_probe(struct platform_device *pdev)
download_mode = scm_is_secure_device();
set_dload_mode(download_mode);
+ if (!download_mode)
+ scm_disable_sdi();
return 0;
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 27a5deb1213e..80a9f0ee288b 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -4223,7 +4223,7 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
debugfs_create_file("consumers", 0444, rdev->debugfs, rdev,
&reg_consumers_fops);
- reg = regulator_get(NULL, rdev->desc->name);
+ reg = regulator_get(NULL, rdev_get_name(rdev));
if (IS_ERR(reg) || reg == NULL) {
pr_err("Error-Bad Function Input\n");
goto error;
diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c
index b0439871c41a..59cbe7460750 100644
--- a/drivers/regulator/cpr3-mmss-regulator.c
+++ b/drivers/regulator/cpr3-mmss-regulator.c
@@ -217,6 +217,14 @@ msmcobalt_v1_rev0_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
};
static const int msmcobalt_v2_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
+ 516000,
+ 628000,
+ 752000,
+ 924000,
+};
+
+static const int
+msmcobalt_v2_rev0_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
616000,
740000,
828000,
@@ -759,7 +767,10 @@ static int cpr3_msm8996_mmss_calculate_open_loop_voltages(
goto done;
}
- if (vreg->thread->ctrl->soc_revision == MSMCOBALT_V2_SOC_ID)
+ if (vreg->thread->ctrl->soc_revision == MSMCOBALT_V2_SOC_ID
+ && fuse->cpr_fusing_rev == 0)
+ ref_volt = msmcobalt_v2_rev0_mmss_fuse_ref_volt;
+ else if (vreg->thread->ctrl->soc_revision == MSMCOBALT_V2_SOC_ID)
ref_volt = msmcobalt_v2_mmss_fuse_ref_volt;
else if (vreg->thread->ctrl->soc_revision == MSMCOBALT_V1_SOC_ID
&& fuse->cpr_fusing_rev == 0)
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index 232373092746..0df2b80ceca5 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -1264,6 +1264,8 @@ static void cprh_controller_program_sdelta(
mb();
}
+static int cprh_regulator_aging_adjust(struct cpr3_controller *ctrl);
+
/**
* cpr3_regulator_init_cprh() - performs hardware initialization at the
* controller and thread level required for CPRh operation.
@@ -1290,6 +1292,16 @@ static int cpr3_regulator_init_cprh(struct cpr3_controller *ctrl)
return -EINVAL;
}
+ rc = cprh_regulator_aging_adjust(ctrl);
+ if (rc && rc != -ETIMEDOUT) {
+ /*
+ * Don't fail initialization if the CPR aging measurement
+ * timed out due to sensors not being available.
+ */
+ cpr3_err(ctrl, "CPR aging adjustment failed, rc=%d\n", rc);
+ return rc;
+ }
+
cprh_controller_program_sdelta(ctrl);
rc = cpr3_regulator_init_cprh_corners(&ctrl->thread[0].vreg[0]);
@@ -3346,7 +3358,7 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
u32 mask, reg, result, quot_min, quot_max, sel_min, sel_max;
u32 quot_min_scaled, quot_max_scaled;
u32 gcnt, gcnt_ref, gcnt0_restore, gcnt1_restore, irq_restore;
- u32 cont_dly_restore, up_down_dly_restore = 0;
+ u32 ro_mask_restore, cont_dly_restore, up_down_dly_restore = 0;
int quot_delta, quot_delta_scaled, quot_delta_scaled_sum;
int *quot_delta_results;
int rc, rc2, i, aging_measurement_count, filtered_count;
@@ -3362,7 +3374,8 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
if (rc) {
cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
rc);
- goto cleanup;
+ kfree(quot_delta_results);
+ return rc;
}
}
@@ -3378,7 +3391,8 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
/* Switch from HW to SW closed-loop if necessary */
if (ctrl->supports_hw_closed_loop) {
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ||
+ ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
@@ -3396,6 +3410,10 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt);
cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt);
+ /* Unmask all RO's */
+ ro_mask_restore = cpr3_read(ctrl, CPR3_REG_RO_MASK(0));
+ cpr3_write(ctrl, CPR3_REG_RO_MASK(0), 0);
+
/*
* Mask all sensors except for the one to measure and bypass all
* sensors in collapsible domains.
@@ -3534,6 +3552,8 @@ cleanup:
cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_restore);
+ cpr3_write(ctrl, CPR3_REG_RO_MASK(0), ro_mask_restore);
+
cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt0_restore);
cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt1_restore);
@@ -3564,7 +3584,8 @@ cleanup:
CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID);
if (ctrl->supports_hw_closed_loop) {
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ||
+ ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
ctrl->use_hw_closed_loop
@@ -3670,14 +3691,16 @@ static void cpr3_regulator_readjust_volt_and_quot(struct cpr3_regulator *vreg,
static void cpr3_regulator_set_aging_ref_adjustment(
struct cpr3_controller *ctrl, int ref_adjust_volt)
{
+ struct cpr3_regulator *vreg;
int i, j;
for (i = 0; i < ctrl->thread_count; i++) {
for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- cpr3_regulator_readjust_volt_and_quot(
- &ctrl->thread[i].vreg[j],
- ctrl->aging_ref_adjust_volt,
- ref_adjust_volt);
+ vreg = &ctrl->thread[i].vreg[j];
+ cpr3_regulator_readjust_volt_and_quot(vreg,
+ ctrl->aging_ref_adjust_volt, ref_adjust_volt);
+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH)
+ cprh_adjust_voltages_for_apm(vreg);
}
}
@@ -3866,6 +3889,126 @@ cleanup:
}
/**
+ * cprh_regulator_aging_adjust() - adjust the target quotients and open-loop
+ * voltages for CPRh regulators based on the output of CPR aging
+ * sensors
+ * @ctrl: Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_regulator_aging_adjust(struct cpr3_controller *ctrl)
+{
+ int i, j, id, rc, rc2, aging_volt, init_volt;
+ int max_aging_volt = 0;
+ u32 reg;
+
+ if (!ctrl->aging_required || !ctrl->cpr_enabled)
+ return 0;
+
+ if (!ctrl->vdd_regulator) {
+ cpr3_err(ctrl, "vdd-supply regulator missing\n");
+ return -ENODEV;
+ }
+
+ init_volt = regulator_get_voltage(ctrl->vdd_regulator);
+ if (init_volt < 0) {
+ cpr3_err(ctrl, "could not get vdd-supply voltage, rc=%d\n",
+ init_volt);
+ return init_volt;
+ }
+
+ if (init_volt > ctrl->aging_ref_volt) {
+ cpr3_info(ctrl, "unable to perform CPR aging measurement as vdd=%d uV > aging voltage=%d uV\n",
+ init_volt, ctrl->aging_ref_volt);
+ return 0;
+ }
+
+ /* Verify that none of the aging sensors are currently masked. */
+ for (i = 0; i < ctrl->aging_sensor_count; i++) {
+ id = ctrl->aging_sensor[i].sensor_id;
+ reg = cpr3_read(ctrl, CPR3_REG_SENSOR_MASK_READ(id));
+ if (reg & BIT(id % 32)) {
+ cpr3_info(ctrl, "unable to perform CPR aging measurement as CPR sensor %d is masked\n",
+ id);
+ return 0;
+ }
+ }
+
+ rc = regulator_set_voltage(ctrl->vdd_regulator, ctrl->aging_ref_volt,
+ INT_MAX);
+ if (rc) {
+ cpr3_err(ctrl, "unable to set vdd-supply to aging voltage=%d uV, rc=%d\n",
+ ctrl->aging_ref_volt, rc);
+ return rc;
+ }
+
+ if (ctrl->aging_vdd_mode) {
+ rc = regulator_set_mode(ctrl->vdd_regulator,
+ ctrl->aging_vdd_mode);
+ if (rc) {
+ cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
+ ctrl->aging_vdd_mode, rc);
+ goto cleanup;
+ }
+ }
+
+ /* Perform aging measurement on all aging sensors */
+ for (i = 0; i < ctrl->aging_sensor_count; i++) {
+ for (j = 0; j < CPR3_AGING_RETRY_COUNT; j++) {
+ rc = cpr3_regulator_measure_aging(ctrl,
+ &ctrl->aging_sensor[i]);
+ if (!rc)
+ break;
+ }
+
+ if (!rc) {
+ aging_volt =
+ cpr3_voltage_adjustment(
+ ctrl->aging_sensor[i].ro_scale,
+ ctrl->aging_sensor[i].measured_quot_diff
+ - ctrl->aging_sensor[i].init_quot_diff);
+ max_aging_volt = max(max_aging_volt, aging_volt);
+ } else {
+ cpr3_err(ctrl, "CPR aging measurement failed after %d tries, rc=%d\n",
+ j, rc);
+ ctrl->aging_failed = true;
+ ctrl->aging_required = false;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ /* Adjust the CPR target quotients according to the aging measurement */
+ if (!rc) {
+ cpr3_regulator_set_aging_ref_adjustment(ctrl, max_aging_volt);
+
+ cpr3_info(ctrl, "aging measurement successful; aging reference adjustment voltage=%d uV\n",
+ ctrl->aging_ref_adjust_volt);
+ ctrl->aging_succeeded = true;
+ ctrl->aging_required = false;
+ }
+
+ rc2 = regulator_set_voltage(ctrl->vdd_regulator, init_volt, INT_MAX);
+ if (rc2) {
+ cpr3_err(ctrl, "unable to reset vdd-supply to initial voltage=%d uV, rc=%d\n",
+ init_volt, rc2);
+ return rc2;
+ }
+
+ if (ctrl->aging_complete_vdd_mode) {
+ rc2 = regulator_set_mode(ctrl->vdd_regulator,
+ ctrl->aging_complete_vdd_mode);
+ if (rc2) {
+ cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
+ ctrl->aging_complete_vdd_mode, rc2);
+ return rc2;
+ }
+ }
+
+ return rc;
+}
+
+/**
* cpr3_regulator_update_ctrl_state() - update the state of the CPR controller
* to reflect the corners used by all CPR3 regulators as well as
* the CPR operating mode and perform aging adjustments if needed
diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h
index 8897def3ef76..ac571271b0d5 100644
--- a/drivers/regulator/cpr3-regulator.h
+++ b/drivers/regulator/cpr3-regulator.h
@@ -875,6 +875,7 @@ int cpr4_parse_core_count_temp_voltage_adj(struct cpr3_regulator *vreg,
bool use_corner_band);
int cpr3_apm_init(struct cpr3_controller *ctrl);
int cpr3_mem_acc_init(struct cpr3_regulator *vreg);
+void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg);
#else
@@ -1047,6 +1048,10 @@ static inline int cpr3_mem_acc_init(struct cpr3_regulator *vreg)
return 0;
}
+static inline void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg)
+{
+}
+
#endif /* CONFIG_REGULATOR_CPR3 */
#endif /* __REGULATOR_CPR_REGULATOR_H__ */
diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c
index 51179f28fcf5..c377a65a6393 100644
--- a/drivers/regulator/cpr3-util.c
+++ b/drivers/regulator/cpr3-util.c
@@ -1202,6 +1202,23 @@ int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl)
if (rc)
return rc;
+ ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd");
+ if (IS_ERR(ctrl->vdd_regulator)) {
+ rc = PTR_ERR(ctrl->vdd_regulator);
+ if (rc != -EPROBE_DEFER) {
+ /* vdd-supply is optional for CPRh controllers. */
+ if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
+ cpr3_debug(ctrl, "unable to request vdd regulator, rc=%d\n",
+ rc);
+ ctrl->vdd_regulator = NULL;
+ return 0;
+ }
+ cpr3_err(ctrl, "unable to request vdd regulator, rc=%d\n",
+ rc);
+ }
+ return rc;
+ }
+
/*
* Regulator device handles are not necessary for CPRh controllers
* since communication with the regulators is completely managed
@@ -1210,15 +1227,6 @@ int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl)
if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH)
return rc;
- ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd");
- if (IS_ERR(ctrl->vdd_regulator)) {
- rc = PTR_ERR(ctrl->vdd_regulator);
- if (rc != -EPROBE_DEFER)
- cpr3_err(ctrl, "unable request vdd regulator, rc=%d\n",
- rc);
- return rc;
- }
-
ctrl->system_regulator = devm_regulator_get_optional(ctrl->dev,
"system");
if (IS_ERR(ctrl->system_regulator)) {
@@ -2000,3 +2008,78 @@ done:
return rc;
}
+
+/**
+ * cprh_adjust_voltages_for_apm() - adjust per-corner floor and ceiling voltages
+ * so that they do not overlap the APM threshold voltage.
+ * @vreg: Pointer to the CPR3 regulator
+ *
+ * The memory array power mux (APM) must be configured for a specific supply
+ * based upon where the VDD voltage lies with respect to the APM threshold
+ * voltage. When using CPR hardware closed-loop, the voltage may vary anywhere
+ * between the floor and ceiling voltage without software notification.
+ * Therefore, it is required that the floor to ceiling range for every corner
+ * not intersect the APM threshold voltage. This function adjusts the floor to
+ * ceiling range for each corner which violates this requirement.
+ *
+ * The following algorithm is applied:
+ * if floor < threshold <= ceiling:
+ * if open_loop >= threshold, then floor = threshold - adj
+ * else ceiling = threshold - step
+ * where:
+ * adj = APM hysteresis voltage established to minimize the number of
+ * corners with artificially increased floor voltages
+ * step = voltage in microvolts of a single step of the VDD supply
+ *
+ * The open-loop voltage is also bounded by the new floor or ceiling value as
+ * needed.
+ *
+ * Return: none
+ */
+void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg)
+{
+ struct cpr3_controller *ctrl = vreg->thread->ctrl;
+ struct cpr3_corner *corner;
+ int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop;
+
+ if (!ctrl->apm_threshold_volt) {
+ /* APM not being used. */
+ return;
+ }
+
+ ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt,
+ ctrl->step_volt);
+ ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt);
+
+ threshold = ctrl->apm_threshold_volt;
+ adj = ctrl->apm_adj_volt;
+
+ for (i = 0; i < vreg->corner_count; i++) {
+ corner = &vreg->corner[i];
+
+ if (threshold <= corner->floor_volt
+ || threshold > corner->ceiling_volt)
+ continue;
+
+ prev_floor = corner->floor_volt;
+ prev_ceiling = corner->ceiling_volt;
+ prev_open_loop = corner->open_loop_volt;
+
+ if (corner->open_loop_volt >= threshold) {
+ corner->floor_volt = max(corner->floor_volt,
+ threshold - adj);
+ if (corner->open_loop_volt < corner->floor_volt)
+ corner->open_loop_volt = corner->floor_volt;
+ } else {
+ corner->ceiling_volt = threshold - ctrl->step_volt;
+ }
+
+ if (corner->floor_volt != prev_floor
+ || corner->ceiling_volt != prev_ceiling
+ || corner->open_loop_volt != prev_open_loop)
+ cpr3_debug(vreg, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n",
+ threshold, adj, i, prev_floor, prev_ceiling,
+ prev_open_loop, corner->floor_volt,
+ corner->ceiling_volt, corner->open_loop_volt);
+ }
+}
diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c
index 284180b0e72f..953ea5f33f40 100644
--- a/drivers/regulator/cprh-kbss-regulator.c
+++ b/drivers/regulator/cprh-kbss-regulator.c
@@ -54,6 +54,8 @@
* @force_highest_corner: Flag indicating that all corners must operate
* at the voltage of the highest corner. This is
* applicable to MSMCOBALT only.
+ * @aging_init_quot_diff: Initial quotient difference between CPR aging
+ * min and max sensors measured at time of manufacturing
*
* This struct holds the values for all of the fuses read from memory.
*/
@@ -65,6 +67,7 @@ struct cprh_msmcobalt_kbss_fuses {
u64 speed_bin;
u64 cpr_fusing_rev;
u64 force_highest_corner;
+ u64 aging_init_quot_diff;
};
/*
@@ -192,6 +195,18 @@ msmcobalt_cpr_force_highest_corner_param[] = {
{},
};
+static const struct cpr3_fuse_param
+msmcobalt_kbss_aging_init_quot_diff_param[2][2] = {
+ [MSMCOBALT_KBSS_POWER_CLUSTER_ID] = {
+ {69, 6, 13},
+ {},
+ },
+ [MSMCOBALT_KBSS_PERFORMANCE_CLUSTER_ID] = {
+ {71, 25, 32},
+ {},
+ },
+};
+
/*
* Open loop voltage fuse reference voltages in microvolts for MSMCOBALT v1
*/
@@ -225,6 +240,8 @@ msmcobalt_v2_kbss_fuse_ref_volt[2][MSMCOBALT_KBSS_FUSE_CORNERS] = {
#define MSMCOBALT_KBSS_FUSE_STEP_VOLT 10000
#define MSMCOBALT_KBSS_VOLTAGE_FUSE_SIZE 6
#define MSMCOBALT_KBSS_QUOT_OFFSET_SCALE 5
+#define MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SIZE 8
+#define MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SCALE 1
#define MSMCOBALT_KBSS_POWER_CPR_SENSOR_COUNT 6
#define MSMCOBALT_KBSS_PERFORMANCE_CPR_SENSOR_COUNT 9
@@ -242,6 +259,12 @@ msmcobalt_v2_kbss_fuse_ref_volt[2][MSMCOBALT_KBSS_FUSE_CORNERS] = {
#define MSMCOBALT_KBSS_PERFORMANCE_TEMP_SENSOR_ID_START 6
#define MSMCOBALT_KBSS_PERFORMANCE_TEMP_SENSOR_ID_END 11
+#define MSMCOBALT_KBSS_POWER_AGING_SENSOR_ID 0
+#define MSMCOBALT_KBSS_POWER_AGING_BYPASS_MASK0 0
+
+#define MSMCOBALT_KBSS_PERFORMANCE_AGING_SENSOR_ID 0
+#define MSMCOBALT_KBSS_PERFORMANCE_AGING_BYPASS_MASK0 0
+
/**
* cprh_msmcobalt_kbss_read_fuse_data() - load KBSS specific fuse parameter values
* @vreg: Pointer to the CPR3 regulator
@@ -321,6 +344,15 @@ static int cprh_msmcobalt_kbss_read_fuse_data(struct cpr3_regulator *vreg)
}
rc = cpr3_read_fuse_param(base,
+ msmcobalt_kbss_aging_init_quot_diff_param[id],
+ &fuse->aging_init_quot_diff);
+ if (rc) {
+ cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = cpr3_read_fuse_param(base,
msmcobalt_cpr_force_highest_corner_param,
&fuse->force_highest_corner);
if (rc) {
@@ -826,6 +858,7 @@ static int cprh_kbss_apm_crossover_as_corner(struct cpr3_regulator *vreg)
corner->floor_volt = ctrl->apm_crossover_volt;
corner->ceiling_volt = ctrl->apm_crossover_volt;
corner->open_loop_volt = ctrl->apm_crossover_volt;
+ corner->abs_ceiling_volt = ctrl->apm_crossover_volt;
corner->use_open_loop = true;
vreg->corner_count++;
@@ -833,79 +866,6 @@ static int cprh_kbss_apm_crossover_as_corner(struct cpr3_regulator *vreg)
}
/**
- * cprh_kbss_adjust_voltages_for_apm() - adjust per-corner floor and ceiling
- * voltages so that they do not overlap the APM threshold voltage.
- * @vreg: Pointer to the CPR3 regulator
- *
- * The KBSS memory array power mux (APM) must be configured for a specific
- * supply based upon where the VDD voltage lies with respect to the APM
- * threshold voltage. When using CPR hardware closed-loop, the voltage may vary
- * anywhere between the floor and ceiling voltage without software notification.
- * Therefore, it is required that the floor to ceiling range for every corner
- * not intersect the APM threshold voltage. This function adjusts the floor to
- * ceiling range for each corner which violates this requirement.
- *
- * The following algorithm is applied in the case that
- * floor < threshold <= ceiling:
- * if open_loop >= threshold, then floor = threshold - adj
- * else ceiling = threshold - step
- * where adj = APM hysteresis voltage established to minimize number
- * of corners with artificially increased floor voltages
- * and step = voltage in microvolts of a single step of the VDD supply
- *
- * The open-loop voltage is also bounded by the new floor or ceiling value as
- * needed.
- *
- * Return: 0 on success, errno on failure
- */
-static int cprh_kbss_adjust_voltages_for_apm(struct cpr3_regulator *vreg)
-{
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- struct cpr3_corner *corner;
- int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop;
-
- if (!ctrl->apm_threshold_volt) {
- /* APM not being used. */
- return 0;
- }
-
- ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt,
- ctrl->step_volt);
- ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt);
-
- threshold = ctrl->apm_threshold_volt;
- adj = ctrl->apm_adj_volt;
-
- for (i = 0; i < vreg->corner_count; i++) {
- corner = &vreg->corner[i];
-
- if (threshold <= corner->floor_volt
- || threshold > corner->ceiling_volt)
- continue;
-
- prev_floor = corner->floor_volt;
- prev_ceiling = corner->ceiling_volt;
- prev_open_loop = corner->open_loop_volt;
-
- if (corner->open_loop_volt >= threshold) {
- corner->floor_volt = max(corner->floor_volt,
- threshold - adj);
- if (corner->open_loop_volt < corner->floor_volt)
- corner->open_loop_volt = corner->floor_volt;
- } else {
- corner->ceiling_volt = threshold - ctrl->step_volt;
- }
-
- cpr3_debug(vreg, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n",
- threshold, adj, i, prev_floor, prev_ceiling,
- prev_open_loop, corner->floor_volt,
- corner->ceiling_volt, corner->open_loop_volt);
- }
-
- return 0;
-}
-
-/**
* cprh_msmcobalt_kbss_set_no_interpolation_quotients() - use the fused target
* quotient values for lower frequencies.
* @vreg: Pointer to the CPR3 regulator
@@ -1235,12 +1195,7 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg)
return rc;
}
- rc = cprh_kbss_adjust_voltages_for_apm(vreg);
- if (rc) {
- cpr3_err(vreg, "unable to adjust voltages for APM\n, rc=%d\n",
- rc);
- return rc;
- }
+ cprh_adjust_voltages_for_apm(vreg);
cpr3_open_loop_voltage_as_ceiling(vreg);
@@ -1299,6 +1254,80 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg)
}
/**
+ * cprh_kbss_init_aging() - perform KBSS CPRh controller specific aging
+ * initializations
+ * @ctrl: Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_init_aging(struct cpr3_controller *ctrl)
+{
+ struct cprh_msmcobalt_kbss_fuses *fuse = NULL;
+ struct cpr3_regulator *vreg;
+ u32 aging_ro_scale;
+ int i, j, rc;
+
+ for (i = 0; i < ctrl->thread_count; i++) {
+ for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+ if (ctrl->thread[i].vreg[j].aging_allowed) {
+ ctrl->aging_required = true;
+ vreg = &ctrl->thread[i].vreg[j];
+ fuse = vreg->platform_fuses;
+ break;
+ }
+ }
+ }
+
+ if (!ctrl->aging_required || !fuse || !vreg)
+ return 0;
+
+ rc = cpr3_parse_array_property(vreg, "qcom,cpr-aging-ro-scaling-factor",
+ 1, &aging_ro_scale);
+ if (rc)
+ return rc;
+
+ if (aging_ro_scale == 0) {
+ cpr3_err(ctrl, "aging RO scaling factor is invalid: %u\n",
+ aging_ro_scale);
+ return -EINVAL;
+ }
+
+ ctrl->aging_vdd_mode = REGULATOR_MODE_NORMAL;
+ ctrl->aging_complete_vdd_mode = REGULATOR_MODE_IDLE;
+
+ ctrl->aging_sensor_count = 1;
+ ctrl->aging_sensor = kzalloc(sizeof(*ctrl->aging_sensor), GFP_KERNEL);
+ if (!ctrl->aging_sensor)
+ return -ENOMEM;
+
+ if (ctrl->ctrl_id == MSMCOBALT_KBSS_POWER_CLUSTER_ID) {
+ ctrl->aging_sensor->sensor_id
+ = MSMCOBALT_KBSS_POWER_AGING_SENSOR_ID;
+ ctrl->aging_sensor->bypass_mask[0]
+ = MSMCOBALT_KBSS_POWER_AGING_BYPASS_MASK0;
+ } else {
+ ctrl->aging_sensor->sensor_id
+ = MSMCOBALT_KBSS_PERFORMANCE_AGING_SENSOR_ID;
+ ctrl->aging_sensor->bypass_mask[0]
+ = MSMCOBALT_KBSS_PERFORMANCE_AGING_BYPASS_MASK0;
+ }
+ ctrl->aging_sensor->ro_scale = aging_ro_scale;
+
+ ctrl->aging_sensor->init_quot_diff
+ = cpr3_convert_open_loop_voltage_fuse(0,
+ MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SCALE,
+ fuse->aging_init_quot_diff,
+ MSMCOBALT_KBSS_AGING_INIT_QUOT_DIFF_SIZE);
+
+ cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n",
+ ctrl->aging_sensor->sensor_id,
+ ctrl->aging_sensor->init_quot_diff,
+ ctrl->aging_sensor->ro_scale);
+
+ return 0;
+}
+
+/**
* cprh_kbss_init_controller() - perform KBSS CPRh controller specific
* initializations
* @ctrl: Pointer to the CPR3 controller
@@ -1566,6 +1595,13 @@ static int cprh_kbss_regulator_probe(struct platform_device *pdev)
return rc;
}
+ rc = cprh_kbss_init_aging(ctrl);
+ if (rc) {
+ cpr3_err(ctrl, "failed to initialize aging configurations, rc=%d\n",
+ rc);
+ return rc;
+ }
+
platform_set_drvdata(pdev, ctrl);
rc = cprh_kbss_populate_opp_table(ctrl);
diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c
index d6e372fc7922..0547853c4f3a 100644
--- a/drivers/scsi/ufs/ufs-debugfs.c
+++ b/drivers/scsi/ufs/ufs-debugfs.c
@@ -1479,8 +1479,8 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba)
char root_name[sizeof("ufshcd00")];
if (!hba) {
- dev_err(hba->dev, "%s: NULL hba, exiting", __func__);
- goto err_no_root;
+ pr_err("%s: NULL hba, exiting", __func__);
+ return;
}
snprintf(root_name, ARRAY_SIZE(root_name), "%s%d", UFSHCD,
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/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 9c27344165be..2bc74941abc8 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -571,6 +571,39 @@ config MSM_QDSP6_APRV3_GLINK
QDSP6. APR is used by audio driver to
configure QDSP6v2's ASM, ADM and AFE.
+config MSM_QDSP6_SSR
+ bool "Audio QDSP6 SSR support"
+ depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3 || \
+ MSM_QDSP6_APRV2_GLINK || MSM_QDSP6_APRV3_GLINK
+ help
+ Enable Subsystem Restart. Reset audio
+ clients when the ADSP subsystem is
+ restarted. Subsystem Restart for audio
+ is only used for processes on the ADSP
+ and signals audio drivers through APR.
+
+
+config MSM_QDSP6_PDR
+ bool "Audio QDSP6 PDR support"
+ depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3 || \
+ MSM_QDSP6_APRV2_GLINK || MSM_QDSP6_APRV3_GLINK
+ help
+ Enable Protection Domain Restart. Reset
+ audio clients when a process on the ADSP
+ is restarted. PDR for audio is only used
+ for processes on the ADSP and signals
+ audio drivers through APR.
+
+config MSM_QDSP6_NOTIFIER
+ bool "Audio QDSP6 PDR support"
+ depends on MSM_QDSP6_SSR || MSM_QDSP6_PDR
+ help
+ Enable notifier which decides whether
+ to use SSR or PDR and notifies all
+ audio clients of the event. Both SSR
+ and PDR are recovery methods when
+ there is a crash on ADSP. Audio drivers
+ are contacted by ADSP through APR.
config MSM_ADSP_LOADER
tristate "ADSP loader support"
@@ -716,14 +749,6 @@ config MSM_KERNEL_PROTECT_TEST
read-only. This test is FATAL whether it passes or fails!
Success is signaled by a stage-2 fault.
-config MSM_CORE_CTL_HELPER
- tristate "Core control helper functions for dynamically hotplug CPUs"
- help
- Provide helper functions for core control driver. Core control
- driver dynamically hotplugs CPUs from kernel based on current
- system load and state. It also supports limiting min and
- max online CPUs from userspace.
-
config QCOM_REMOTEQDSS
bool "Allow debug tools to enable events on other processors"
depends on QCOM_SCM && DEBUG_FS
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index d9134a558be6..434a114c000f 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -38,7 +38,6 @@ obj-$(CONFIG_MEM_SHARE_QMI_SERVICE) += memshare/
obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o
obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-msa.o pil-q6v5-mss.o
obj-$(CONFIG_MSM_PIL) += peripheral-loader.o
-obj-$(CONFIG_MSM_CORE_CTL_HELPER) += core_ctl_helper.o
obj-$(CONFIG_MSM_PFE_WA) += pfe-wa.o
obj-$(CONFIG_ARCH_MSM8996) += msm_cpu_voltage.o
diff --git a/drivers/soc/qcom/common_log.c b/drivers/soc/qcom/common_log.c
index d2d877ef427b..ecf89b2b3b37 100644
--- a/drivers/soc/qcom/common_log.c
+++ b/drivers/soc/qcom/common_log.c
@@ -20,10 +20,9 @@
#include <soc/qcom/memory_dump.h>
#define MISC_DUMP_DATA_LEN 4096
-#define PMIC_DUMP_DATA_LEN 4096
+#define PMIC_DUMP_DATA_LEN (64 * 1024)
#define VSENSE_DUMP_DATA_LEN 4096
#define RPM_DUMP_DATA_LEN (160 * 1024)
-#define SCAN_DUMP_DATA_LEN (256 * 1024)
void register_misc_dump(void)
{
@@ -154,38 +153,6 @@ err0:
}
}
-void register_scan_dump(void)
-{
- static void *dump_addr;
- int ret;
- struct msm_dump_entry dump_entry;
- struct msm_dump_data *dump_data;
-
- if (MSM_DUMP_MAJOR(msm_dump_table_version()) > 1) {
- dump_data = kzalloc(sizeof(struct msm_dump_data), GFP_KERNEL);
- if (!dump_data)
- return;
- dump_addr = kzalloc(SCAN_DUMP_DATA_LEN, GFP_KERNEL);
- if (!dump_addr)
- goto err0;
-
- dump_data->addr = virt_to_phys(dump_addr);
- dump_data->len = SCAN_DUMP_DATA_LEN;
- dump_entry.id = MSM_DUMP_DATA_SCANDUMP;
- dump_entry.addr = virt_to_phys(dump_data);
- ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
- if (ret) {
- pr_err("Registering scandump region failed\n");
- goto err1;
- }
- return;
-err1:
- kfree(dump_addr);
-err0:
- kfree(dump_data);
- }
-}
-
static void __init common_log_register_log_buf(void)
{
char **log_bufp;
@@ -257,7 +224,6 @@ static int __init msm_common_log_init(void)
register_pmic_dump();
register_vsense_dump();
register_rpm_dump();
- register_scan_dump();
return 0;
}
late_initcall(msm_common_log_init);
diff --git a/drivers/soc/qcom/core_ctl_helper.c b/drivers/soc/qcom/core_ctl_helper.c
deleted file mode 100644
index 88201412128e..000000000000
--- a/drivers/soc/qcom/core_ctl_helper.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/cpu.h>
-#include <linux/cpufreq.h>
-#include <linux/ktime.h>
-#include <linux/hrtimer.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <trace/events/power.h>
-#include <soc/qcom/core_ctl.h>
-
-void core_ctl_trace(int type, int cpu, int arg1, int arg2, int arg3)
-{
- switch (type) {
- case CORE_CTL_EVAL_NEED:
- trace_core_ctl_eval_need(cpu, arg1, arg2, arg3);
- break;
-
- case CORE_CTL_SET_BUSY:
- trace_core_ctl_set_busy(cpu, arg1, arg2, arg3);
- break;
- };
-}
-EXPORT_SYMBOL(core_ctl_trace);
-
-void core_ctl_block_hotplug(void)
-{
- get_online_cpus();
-}
-EXPORT_SYMBOL(core_ctl_block_hotplug);
-
-void core_ctl_unblock_hotplug(void)
-{
- put_online_cpus();
-}
-EXPORT_SYMBOL(core_ctl_unblock_hotplug);
-
-s64 core_ctl_get_time(void)
-{
- return ktime_to_ms(ktime_get());
-}
-EXPORT_SYMBOL(core_ctl_get_time);
-
-struct cpufreq_policy *core_ctl_get_policy(int cpu)
-{
- return cpufreq_cpu_get(cpu);
-}
-EXPORT_SYMBOL(core_ctl_get_policy);
-
-void core_ctl_put_policy(struct cpufreq_policy *policy)
-{
- cpufreq_cpu_put(policy);
-}
-EXPORT_SYMBOL(core_ctl_put_policy);
-
-struct device *core_ctl_find_cpu_device(unsigned cpu)
-{
- return get_cpu_device(cpu);
-}
-EXPORT_SYMBOL(core_ctl_find_cpu_device);
-
-int __ref core_ctl_online_core(unsigned int cpu)
-{
- int ret = -EINVAL;
- struct device *dev = get_cpu_device(cpu);
-
- if (dev) {
- lock_device_hotplug();
- ret = device_online(dev);
- unlock_device_hotplug();
- }
- return ret;
-}
-EXPORT_SYMBOL(core_ctl_online_core);
-
-int __ref core_ctl_offline_core(unsigned int cpu)
-{
- int ret = -EINVAL;
- struct device *dev = get_cpu_device(cpu);
-
- if (dev) {
- lock_device_hotplug();
- ret = device_offline(dev);
- unlock_device_hotplug();
- }
- return ret;
-}
-EXPORT_SYMBOL(core_ctl_offline_core);
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 5612075ba60c..b7fa71dd0695 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -2712,7 +2712,7 @@ int glink_close(void *handle)
{
struct glink_core_xprt_ctx *xprt_ctx = NULL;
struct channel_ctx *ctx = (struct channel_ctx *)handle;
- int ret;
+ int ret = 0;
unsigned long flags;
bool is_empty = false;
diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c
index 4952e12ffe3c..a14d912b7536 100644
--- a/drivers/soc/qcom/glink_ssr.c
+++ b/drivers/soc/qcom/glink_ssr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, 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
@@ -769,7 +769,7 @@ static int glink_ssr_probe(struct platform_device *pdev)
struct device_node *phandle_node;
struct restart_notifier_block *nb;
struct subsys_info *ss_info;
- struct subsys_info_leaf *ss_info_leaf;
+ struct subsys_info_leaf *ss_info_leaf = NULL;
struct glink_link_info *link_info;
char *key;
const char *edge;
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index e56030e3b116..25b522806c3e 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,13 +42,23 @@
#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>
#include "wlan_firmware_service_v01.h"
+#ifdef CONFIG_ICNSS_DEBUG
+unsigned long qmi_timeout = 3000;
+module_param(qmi_timeout, ulong, 0600);
+
+#define WLFW_TIMEOUT_MS qmi_timeout
+#else
#define WLFW_TIMEOUT_MS 3000
+#endif
#define WLFW_SERVICE_INS_ID_V01 0
+#define WLFW_CLIENT_ID 0x4b4e454c
#define MAX_PROP_SIZE 32
#define NUM_LOG_PAGES 10
#define NUM_REG_LOG_PAGES 4
@@ -235,9 +246,10 @@ enum icnss_debug_quirks {
RECOVERY_DISABLE,
SSR_ONLY,
PDR_ONLY,
+ VBATT_DISABLE,
};
-#define ICNSS_QUIRKS_DEFAULT 0
+#define ICNSS_QUIRKS_DEFAULT BIT(VBATT_DISABLE)
unsigned long quirks = ICNSS_QUIRKS_DEFAULT;
module_param(quirks, ulong, 0600);
@@ -248,7 +260,12 @@ void *icnss_ipc_log_context;
void *icnss_ipc_log_long_context;
#endif
-#define ICNSS_EVENT_PENDING 2989
+#define ICNSS_EVENT_PENDING 2989
+
+#define ICNSS_EVENT_SYNC BIT(0)
+#define ICNSS_EVENT_UNINTERRUPTIBLE BIT(1)
+#define ICNSS_EVENT_SYNC_UNINTERRUPTIBLE (ICNSS_EVENT_UNINTERRUPTIBLE | \
+ ICNSS_EVENT_SYNC)
enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
@@ -260,6 +277,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;
@@ -275,13 +296,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 {
@@ -342,6 +363,8 @@ struct icnss_stats {
uint32_t pm_suspend_noirq_err;
uint32_t pm_resume_noirq;
uint32_t pm_resume_noirq_err;
+ uint32_t pm_stay_awake;
+ uint32_t pm_relax;
uint32_t ind_register_req;
uint32_t ind_register_resp;
@@ -419,7 +442,6 @@ static struct icnss_priv {
struct notifier_block get_service_nb;
void *modem_notify_handler;
struct notifier_block modem_ssr_nb;
- struct wakeup_source ws;
uint32_t diag_reg_read_addr;
uint32_t diag_reg_read_mem_type;
uint32_t diag_reg_read_len;
@@ -428,6 +450,7 @@ static struct icnss_priv {
struct qpnp_adc_tm_chip *adc_tm_dev;
struct qpnp_vadc_chip *vadc_dev;
uint64_t vph_pwr;
+ atomic_t pm_count;
} *penv;
static void icnss_hw_write_reg(void *base, u32 offset, u32 val)
@@ -495,6 +518,35 @@ static int icnss_hw_poll_reg_field(void *base, u32 offset, u32 mask, u32 val,
return 0;
}
+static void icnss_pm_stay_awake(struct icnss_priv *priv)
+{
+ if (atomic_inc_return(&priv->pm_count) != 1)
+ return;
+
+ icnss_pr_dbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
+ atomic_read(&priv->pm_count));
+
+ pm_stay_awake(&priv->pdev->dev);
+
+ priv->stats.pm_stay_awake++;
+}
+
+static void icnss_pm_relax(struct icnss_priv *priv)
+{
+ int r = atomic_dec_return(&priv->pm_count);
+
+ WARN_ON(r < 0);
+
+ if (r != 0)
+ return;
+
+ icnss_pr_dbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
+ atomic_read(&priv->pm_count));
+
+ pm_relax(&priv->pdev->dev);
+ priv->stats.pm_relax++;
+}
+
static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
{
switch (type) {
@@ -518,16 +570,16 @@ static char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
};
static int icnss_driver_event_post(enum icnss_driver_event_type type,
- bool sync, void *data)
+ u32 flags, void *data)
{
struct icnss_driver_event *event;
- unsigned long flags;
+ unsigned long irq_flags;
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(%d), %s, flags: 0x%x, state: 0x%lx\n",
+ icnss_driver_event_to_str(type), type, current->comm,
+ flags, penv->state);
if (type >= ICNSS_DRIVER_EVENT_MAX) {
icnss_pr_err("Invalid Event type: %d, can't post", type);
@@ -541,39 +593,47 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
if (event == NULL)
return -ENOMEM;
+ icnss_pm_stay_awake(penv);
+
event->type = type;
event->data = data;
init_completion(&event->complete);
event->ret = ICNSS_EVENT_PENDING;
- event->sync = sync;
+ event->sync = !!(flags & ICNSS_EVENT_SYNC);
- spin_lock_irqsave(&penv->event_lock, flags);
+ spin_lock_irqsave(&penv->event_lock, irq_flags);
list_add_tail(&event->list, &penv->event_list);
- spin_unlock_irqrestore(&penv->event_lock, flags);
+ spin_unlock_irqrestore(&penv->event_lock, irq_flags);
penv->stats.events[type].posted++;
queue_work(penv->event_wq, &penv->event_work);
- if (!sync)
- return ret;
+ if (!(flags & ICNSS_EVENT_SYNC))
+ goto out;
- ret = wait_for_completion_interruptible(&event->complete);
+ if (flags & ICNSS_EVENT_UNINTERRUPTIBLE)
+ wait_for_completion(&event->complete);
+ else
+ ret = wait_for_completion_interruptible(&event->complete);
icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n",
icnss_driver_event_to_str(type), type, penv->state, ret,
event->ret);
- spin_lock_irqsave(&penv->event_lock, flags);
+ spin_lock_irqsave(&penv->event_lock, irq_flags);
if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) {
event->sync = false;
- spin_unlock_irqrestore(&penv->event_lock, flags);
- return ret;
+ spin_unlock_irqrestore(&penv->event_lock, irq_flags);
+ ret = -EINTR;
+ goto out;
}
- spin_unlock_irqrestore(&penv->event_lock, flags);
+ spin_unlock_irqrestore(&penv->event_lock, irq_flags);
ret = event->ret;
kfree(event);
+out:
+ icnss_pm_relax(penv);
return ret;
}
@@ -616,7 +676,7 @@ static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI vbatt request failed %d %d\n",
+ icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
goto out;
@@ -746,6 +806,9 @@ static int icnss_init_vph_monitor(struct icnss_priv *priv)
{
int ret = 0;
+ if (test_bit(VBATT_DISABLE, &quirks))
+ goto out;
+
ret = icnss_get_phone_power(priv, &priv->vph_pwr);
if (ret)
goto out;
@@ -1631,7 +1694,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI MSA Mem info request failed %d %d\n",
+ icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
goto out;
@@ -1702,7 +1765,7 @@ static int wlfw_msa_ready_send_sync_msg(void)
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI MSA ready request failed %d %d\n",
+ icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
goto out;
@@ -1733,6 +1796,8 @@ static int wlfw_ind_register_send_sync_msg(void)
memset(&req, 0, sizeof(req));
memset(&resp, 0, sizeof(resp));
+ req.client_id_valid = 1;
+ req.client_id = WLFW_CLIENT_ID;
req.fw_ready_enable_valid = 1;
req.fw_ready_enable = 1;
req.msa_ready_enable_valid = 1;
@@ -1759,7 +1824,7 @@ static int wlfw_ind_register_send_sync_msg(void)
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI indication register request failed %d %d\n",
+ icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
goto out;
@@ -1806,7 +1871,7 @@ static int wlfw_cap_send_sync_msg(void)
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI capability request failed %d %d\n",
+ icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
goto out;
@@ -1885,7 +1950,7 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI mode request failed mode: %d, %d %d\n",
+ icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
mode, resp.resp.result, resp.resp.error);
ret = resp.resp.result;
goto out;
@@ -1935,7 +2000,7 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI config request failed %d %d\n",
+ icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
goto out;
@@ -1988,7 +2053,7 @@ static int wlfw_ini_send_sync_msg(bool enable_fw_log)
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI INI request failed fw_log: %d, %d %d\n",
+ icnss_pr_err("QMI INI request rejected, fw_log:%d result:%d error:%d\n",
enable_fw_log, resp.resp.result, resp.resp.error);
ret = resp.resp.result;
goto out;
@@ -2048,7 +2113,7 @@ static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
}
if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI athdiag read request failed %d %d\n",
+ icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
resp->resp.result, resp->resp.error);
ret = resp->resp.result;
goto out;
@@ -2114,7 +2179,7 @@ static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI athdiag write request failed %d %d\n",
+ icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
goto out;
@@ -2172,7 +2237,7 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
switch (msg_id) {
case QMI_WLFW_FW_READY_IND_V01:
icnss_driver_event_post(ICNSS_DRIVER_EVENT_FW_READY_IND,
- false, NULL);
+ 0, NULL);
break;
case QMI_WLFW_MSA_READY_IND_V01:
icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n",
@@ -2197,6 +2262,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");
@@ -2277,7 +2344,7 @@ static int icnss_driver_event_server_exit(void *data)
icnss_pr_info("QMI Service Disconnected: 0x%lx\n", penv->state);
- if (penv->adc_tm_dev)
+ if (!test_bit(VBATT_DISABLE, &quirks) && penv->adc_tm_dev)
qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
&penv->vph_monitor_params);
@@ -2296,6 +2363,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);
@@ -2324,6 +2393,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);
@@ -2354,8 +2425,6 @@ static int icnss_driver_event_fw_ready_ind(void *data)
if (!penv)
return -ENODEV;
- __pm_stay_awake(&penv->ws);
-
set_bit(ICNSS_FW_READY, &penv->state);
icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
@@ -2373,10 +2442,7 @@ static int icnss_driver_event_fw_ready_ind(void *data)
else
ret = icnss_call_driver_probe(penv);
- __pm_relax(&penv->ws);
-
out:
- __pm_relax(&penv->ws);
return ret;
}
@@ -2387,8 +2453,6 @@ static int icnss_driver_event_register_driver(void *data)
if (penv->ops)
return -EEXIST;
- __pm_stay_awake(&penv->ws);
-
penv->ops = data;
if (test_bit(SKIP_QMI, &quirks))
@@ -2414,21 +2478,16 @@ static int icnss_driver_event_register_driver(void *data)
set_bit(ICNSS_DRIVER_PROBED, &penv->state);
- __pm_relax(&penv->ws);
-
return 0;
power_off:
icnss_hw_power_off(penv);
out:
- __pm_relax(&penv->ws);
return ret;
}
static int icnss_driver_event_unregister_driver(void *data)
{
- __pm_stay_awake(&penv->ws);
-
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
penv->ops = NULL;
goto out;
@@ -2444,40 +2503,73 @@ static int icnss_driver_event_unregister_driver(void *data)
icnss_hw_power_off(penv);
out:
- __pm_relax(&penv->ws);
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;
}
@@ -2488,6 +2580,8 @@ static void icnss_driver_event_work(struct work_struct *work)
unsigned long flags;
int ret;
+ icnss_pm_stay_awake(penv);
+
spin_lock_irqsave(&penv->event_lock, flags);
while (!list_empty(&penv->event_list)) {
@@ -2518,7 +2612,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);
@@ -2528,6 +2623,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;
@@ -2541,6 +2641,8 @@ static void icnss_driver_event_work(struct work_struct *work)
spin_lock_irqsave(&penv->event_lock, flags);
}
spin_unlock_irqrestore(&penv->event_lock, flags);
+
+ icnss_pm_relax(penv);
}
static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
@@ -2557,12 +2659,12 @@ static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
switch (code) {
case QMI_SERVER_ARRIVE:
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
- false, NULL);
+ 0, NULL);
break;
case QMI_SERVER_EXIT:
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_SERVER_EXIT,
- false, NULL);
+ 0, NULL);
break;
default:
icnss_pr_dbg("Invalid code: %ld", code);
@@ -2575,23 +2677,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,
+ ICNSS_EVENT_SYNC, event_data);
return NOTIFY_OK;
}
@@ -2650,14 +2760,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);
+ ICNSS_EVENT_SYNC, event_data);
break;
case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
icnss_pr_dbg("Service up, state: 0x%lx\n", priv->state);
@@ -2803,8 +2922,6 @@ enable_pdr:
if (ret)
return ret;
- icnss_modem_ssr_unregister_notifier(priv);
-
return 0;
}
@@ -2831,7 +2948,7 @@ int icnss_register_driver(struct icnss_driver_ops *ops)
}
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
- true, ops);
+ ICNSS_EVENT_SYNC, ops);
if (ret == -ERESTARTSYS)
ret = 0;
@@ -2859,7 +2976,7 @@ int icnss_unregister_driver(struct icnss_driver_ops *ops)
}
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
- true, NULL);
+ ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
out:
return ret;
}
@@ -3187,6 +3304,12 @@ int icnss_wlan_disable(enum icnss_driver_mode mode)
}
EXPORT_SYMBOL(icnss_wlan_disable);
+bool icnss_is_qmi_disable(void)
+{
+ return test_bit(SKIP_QMI, &quirks) ? true : false;
+}
+EXPORT_SYMBOL(icnss_is_qmi_disable);
+
int icnss_get_ce_id(int irq)
{
int i;
@@ -3281,6 +3404,12 @@ int icnss_smmu_map(struct device *dev,
}
EXPORT_SYMBOL(icnss_smmu_map);
+unsigned int icnss_socinfo_get_serial_number(struct device *dev)
+{
+ return socinfo_get_serial_number();
+}
+EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
+
static int icnss_bw_vote(struct icnss_priv *priv, int index)
{
int ret = 0;
@@ -3732,9 +3861,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;
@@ -3753,6 +3879,9 @@ 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:
+ seq_puts(s, "WLAN FW EXISTS");
+ continue;
}
seq_printf(s, "UNKNOWN-%d", i);
@@ -3856,6 +3985,8 @@ static int icnss_stats_show(struct seq_file *s, void *data)
ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
+ ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
+ ICNSS_STATS_DUMP(s, priv, pm_relax);
icnss_stats_show_irqs(s, priv);
@@ -4307,8 +4438,6 @@ static int icnss_probe(struct platform_device *pdev)
spin_lock_init(&priv->event_lock);
spin_lock_init(&priv->on_off_lock);
- wakeup_source_init(&priv->ws, "icnss_ws");
-
priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
if (!priv->event_wq) {
icnss_pr_err("Workqueue creation failed\n");
@@ -4370,8 +4499,6 @@ static int icnss_remove(struct platform_device *pdev)
icnss_bw_deinit(penv);
- wakeup_source_trash(&penv->ws);
-
icnss_hw_power_off(penv);
dev_set_drvdata(&pdev->dev, NULL);
@@ -4379,55 +4506,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)
{
@@ -4563,8 +4641,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/ipc_router_glink_xprt.c b/drivers/soc/qcom/ipc_router_glink_xprt.c
index 791221971e1c..1f36dd0ba07e 100644
--- a/drivers/soc/qcom/ipc_router_glink_xprt.c
+++ b/drivers/soc/qcom/ipc_router_glink_xprt.c
@@ -43,8 +43,14 @@ if (ipc_router_glink_xprt_debug_mask) \
#define MIN_FRAG_SZ (IPC_ROUTER_HDR_SIZE + sizeof(union rr_control_msg))
#define IPC_RTR_XPRT_NAME_LEN (2 * GLINK_NAME_SIZE)
#define PIL_SUBSYSTEM_NAME_LEN 32
-#define DEFAULT_NUM_INTENTS 5
-#define DEFAULT_RX_INTENT_SIZE 2048
+
+#define MAX_NUM_LO_INTENTS 5
+#define MAX_NUM_MD_INTENTS 3
+#define MAX_NUM_HI_INTENTS 2
+#define LO_RX_INTENT_SIZE 2048
+#define MD_RX_INTENT_SIZE 8192
+#define HI_RX_INTENT_SIZE (17 * 1024)
+
/**
* ipc_router_glink_xprt - IPC Router's GLINK XPRT structure
* @list: IPC router's GLINK XPRT list.
@@ -82,6 +88,9 @@ struct ipc_router_glink_xprt {
unsigned xprt_version;
unsigned xprt_option;
bool disable_pil_loading;
+ uint32_t cur_lo_intents_cnt;
+ uint32_t cur_md_intents_cnt;
+ uint32_t cur_hi_intents_cnt;
};
struct ipc_router_glink_xprt_work {
@@ -340,7 +349,7 @@ static void glink_xprt_read_data(struct work_struct *work)
}
D("%s %zu bytes @ %p\n", __func__, rx_work->iovec_size, rx_work->iovec);
- if (rx_work->iovec_size <= DEFAULT_RX_INTENT_SIZE)
+ if (rx_work->iovec_size <= HI_RX_INTENT_SIZE)
reuse_intent = true;
pkt = glink_xprt_copy_data(rx_work);
@@ -369,9 +378,14 @@ static void glink_xprt_open_event(struct work_struct *work)
IPC_ROUTER_XPRT_EVENT_OPEN, NULL);
D("%s: Notified IPC Router of %s OPEN\n",
__func__, glink_xprtp->xprt.name);
- for (i = 0; i < DEFAULT_NUM_INTENTS; i++)
+ glink_xprtp->cur_lo_intents_cnt = 0;
+ glink_xprtp->cur_md_intents_cnt = 0;
+ glink_xprtp->cur_hi_intents_cnt = 0;
+ for (i = 0; i < MAX_NUM_LO_INTENTS; i++) {
glink_queue_rx_intent(glink_xprtp->ch_hndl, (void *)glink_xprtp,
- DEFAULT_RX_INTENT_SIZE);
+ LO_RX_INTENT_SIZE);
+ glink_xprtp->cur_lo_intents_cnt++;
+ }
kfree(xprt_work);
}
@@ -392,13 +406,32 @@ static void glink_xprt_close_event(struct work_struct *work)
static void glink_xprt_qrx_intent_worker(struct work_struct *work)
{
+ size_t sz;
struct queue_rx_intent_work *qrx_intent_work =
container_of(work, struct queue_rx_intent_work, work);
struct ipc_router_glink_xprt *glink_xprtp =
qrx_intent_work->glink_xprtp;
+ uint32_t *cnt = NULL;
+ int ret;
+
+ sz = qrx_intent_work->intent_size;
+ if (sz <= MD_RX_INTENT_SIZE) {
+ if (glink_xprtp->cur_md_intents_cnt >= MAX_NUM_MD_INTENTS)
+ goto qrx_intent_worker_out;
+ sz = MD_RX_INTENT_SIZE;
+ cnt = &glink_xprtp->cur_md_intents_cnt;
+ } else if (sz <= HI_RX_INTENT_SIZE) {
+ if (glink_xprtp->cur_hi_intents_cnt >= MAX_NUM_HI_INTENTS)
+ goto qrx_intent_worker_out;
+ sz = HI_RX_INTENT_SIZE;
+ cnt = &glink_xprtp->cur_hi_intents_cnt;
+ }
- glink_queue_rx_intent(glink_xprtp->ch_hndl, (void *)glink_xprtp,
- qrx_intent_work->intent_size);
+ ret = glink_queue_rx_intent(glink_xprtp->ch_hndl, (void *)glink_xprtp,
+ sz);
+ if (!ret && cnt)
+ (*cnt)++;
+qrx_intent_worker_out:
kfree(qrx_intent_work);
}
@@ -468,7 +501,7 @@ static bool glink_xprt_notify_rx_intent_req(void *handle, const void *priv,
struct ipc_router_glink_xprt *glink_xprtp =
(struct ipc_router_glink_xprt *)priv;
- if (sz <= DEFAULT_RX_INTENT_SIZE)
+ if (sz <= LO_RX_INTENT_SIZE)
return true;
qrx_intent_work = kmalloc(sizeof(struct queue_rx_intent_work),
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/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c
index e1e91f56526d..00cc5e12709b 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.c
+++ b/drivers/soc/qcom/memshare/msm_memshare.c
@@ -26,6 +26,7 @@
#include "heap_mem_ext_v01.h"
#include <soc/qcom/secure_buffer.h>
+#include <soc/qcom/ramdump.h>
/* Macros */
#define MEMSHARE_DEV_NAME "memshare"
@@ -37,6 +38,7 @@ static void mem_share_svc_recv_msg(struct work_struct *work);
static DECLARE_DELAYED_WORK(work_recv_msg, mem_share_svc_recv_msg);
static struct workqueue_struct *mem_share_svc_workqueue;
static uint64_t bootup_request;
+static void *memshare_ramdump_dev[MAX_CLIENTS];
/* Memshare Driver Structure */
struct memshare_driver {
@@ -114,9 +116,51 @@ static struct msg_desc mem_share_svc_size_query_resp_desc = {
.ei_array = mem_query_size_resp_msg_data_v01_ei,
};
+/*
+ * This API creates ramdump dev handlers
+ * for each of the memshare clients.
+ * These dev handlers will be used for
+ * extracting the ramdump for loaned memory
+ * segments.
+ */
+
+static int mem_share_configure_ramdump(void)
+{
+ char client_name[18] = "memshare_";
+ char *clnt = NULL;
+
+ switch (num_clients) {
+ case 0:
+ clnt = "GPS";
+ break;
+ case 1:
+ clnt = "FTM";
+ break;
+ case 2:
+ clnt = "DIAG";
+ break;
+ default:
+ pr_info("memshare: no memshare clients registered\n");
+ return -EINVAL;
+ }
+
+ snprintf(client_name, 18, "memshare_%s", clnt);
+
+ memshare_ramdump_dev[num_clients] = create_ramdump_device(client_name,
+ NULL);
+ if (IS_ERR_OR_NULL(memshare_ramdump_dev[num_clients])) {
+ pr_err("memshare: %s: Unable to create memshare ramdump device.\n",
+ __func__);
+ memshare_ramdump_dev[num_clients] = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static int check_client(int client_id, int proc, int request)
{
- int i = 0;
+ int i = 0, rc;
int found = DHMS_MEM_CLIENT_INVALID;
for (i = 0; i < MAX_CLIENTS; i++) {
@@ -127,7 +171,7 @@ static int check_client(int client_id, int proc, int request)
}
}
if ((found == DHMS_MEM_CLIENT_INVALID) && !request) {
- pr_debug("No registered client, adding a new client\n");
+ pr_debug("memshare: No registered client, adding a new client\n");
/* Add a new client */
for (i = 0; i < MAX_CLIENTS; i++) {
if (memblock[i].client_id == DHMS_MEM_CLIENT_INVALID) {
@@ -136,6 +180,16 @@ static int check_client(int client_id, int proc, int request)
memblock[i].guarantee = 0;
memblock[i].peripheral = proc;
found = i;
+
+ if (!memblock[i].file_created) {
+ rc = mem_share_configure_ramdump();
+ if (rc)
+ pr_err("In %s, Cannot create ramdump for client: %d\n",
+ __func__, client_id);
+ else
+ memblock[i].file_created = 1;
+ }
+
break;
}
}
@@ -190,10 +244,75 @@ void initialize_client(void)
memblock[i].memory_type = MEMORY_CMA;
memblock[i].free_memory = 0;
memblock[i].hyp_mapping = 0;
+ memblock[i].file_created = 0;
}
dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
}
+/*
+ * This API initializes the ramdump segments
+ * with the physical address and size of
+ * the memshared clients. Extraction of ramdump
+ * is skipped if memshare client is not alloted
+ * This calls the ramdump api in extracting the
+ * ramdump in elf format.
+ */
+
+static int mem_share_do_ramdump(void)
+{
+ int i = 0, ret;
+ char *client_name = NULL;
+
+ for (i = 0; i < num_clients; i++) {
+
+ struct ramdump_segment *ramdump_segments_tmp = NULL;
+
+ switch (i) {
+ case 0:
+ client_name = "GPS";
+ break;
+ case 1:
+ client_name = "FTM";
+ break;
+ case 2:
+ client_name = "DIAG";
+ break;
+ default:
+ pr_info("memshare: no memshare clients registered\n");
+ break;
+ }
+
+ if (!memblock[i].alloted) {
+ pr_err("memshare:%s memblock is not alloted\n",
+ client_name);
+ continue;
+ }
+
+ ramdump_segments_tmp = kcalloc(1,
+ sizeof(struct ramdump_segment),
+ GFP_KERNEL);
+ if (!ramdump_segments_tmp)
+ return -ENOMEM;
+
+ ramdump_segments_tmp[0].size = memblock[i].size;
+ ramdump_segments_tmp[0].address = memblock[i].phy_addr;
+
+ pr_debug("memshare: %s:%s client:phy_address = %llx, size = %d\n",
+ __func__, client_name,
+ (unsigned long long) memblock[i].phy_addr, memblock[i].size);
+
+ ret = do_elf_ramdump(memshare_ramdump_dev[i],
+ ramdump_segments_tmp, 1);
+ if (ret < 0) {
+ pr_err("memshare: Unable to dump: %d\n", ret);
+ kfree(ramdump_segments_tmp);
+ return ret;
+ }
+ kfree(ramdump_segments_tmp);
+ }
+ return 0;
+}
+
static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
void *_cmd)
{
@@ -202,8 +321,10 @@ static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
u32 source_vmlist[2] = {VMID_HLOS, VMID_MSS_MSA};
int dest_vmids[1] = {VMID_HLOS};
int dest_perms[1] = {PERM_READ|PERM_WRITE};
+ struct notif_data *notifdata = NULL;
mutex_lock(&memsh_drv->mem_share);
+
switch (code) {
case SUBSYS_BEFORE_SHUTDOWN:
@@ -260,6 +381,22 @@ static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
bootup_request++;
break;
+ case SUBSYS_RAMDUMP_NOTIFICATION:
+ if (_cmd)
+ notifdata = (struct notif_data *) _cmd;
+ else
+ break;
+
+ if (!(notifdata->enable_ramdump)) {
+ pr_info("In %s, Ramdump collection is disabled\n",
+ __func__);
+ } else {
+ ret = mem_share_do_ramdump();
+ if (ret)
+ pr_err("Ramdump collection failed\n");
+ }
+ break;
+
default:
pr_debug("Memshare: code: %lu\n", code);
break;
@@ -800,6 +937,9 @@ static int memshare_child_probe(struct platform_device *pdev)
memblock[num_clients].size = size;
memblock[num_clients].client_id = client_id;
+ /*
+ * Memshare allocation for guaranteed clients
+ */
if (memblock[num_clients].guarantee) {
rc = memshare_alloc(memsh_child->dev,
memblock[num_clients].size,
@@ -812,6 +952,21 @@ static int memshare_child_probe(struct platform_device *pdev)
memblock[num_clients].alloted = 1;
}
+ /*
+ * call for creating ramdump dev handlers for
+ * memshare clients
+ */
+
+ if (!memblock[num_clients].file_created) {
+ rc = mem_share_configure_ramdump();
+ if (rc)
+ pr_err("In %s, cannot collect dumps for client id: %d\n",
+ __func__,
+ memblock[num_clients].client_id);
+ else
+ memblock[num_clients].file_created = 1;
+ }
+
num_clients++;
return 0;
diff --git a/drivers/soc/qcom/memshare/msm_memshare.h b/drivers/soc/qcom/memshare/msm_memshare.h
index 68a143907976..398907532977 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.h
+++ b/drivers/soc/qcom/memshare/msm_memshare.h
@@ -51,6 +51,9 @@ struct mem_blocks {
uint8_t free_memory;
/* Need Hypervisor mapping*/
uint8_t hyp_mapping;
+ /* Status flag which checks if ramdump file is created*/
+ int file_created;
+
};
int memshare_alloc(struct device *dev,
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index 3873a34c60fb..00ab1ce3b6b4 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -582,7 +582,8 @@ int pil_mss_reset_load_mba(struct pil_desc *pil)
mba_dp_virt = dma_alloc_attrs(&md->mba_mem_dev, drv->mba_dp_size,
&mba_dp_phys, GFP_KERNEL, &md->attrs_dma);
if (!mba_dp_virt) {
- dev_err(pil->dev, "MBA metadata buffer allocation failed\n");
+ dev_err(pil->dev, "%s MBA metadata buffer allocation %zx bytes failed\n",
+ __func__, drv->mba_dp_size);
ret = -ENOMEM;
goto err_invalid_fw;
}
@@ -595,8 +596,8 @@ int pil_mss_reset_load_mba(struct pil_desc *pil)
drv->mba_dp_virt = mba_dp_virt;
mba_dp_phys_end = mba_dp_phys + drv->mba_dp_size;
- dev_info(pil->dev, "Loading MBA and DP (if present) from %pa to %pa\n",
- &mba_dp_phys, &mba_dp_phys_end);
+ dev_info(pil->dev, "Loading MBA and DP (if present) from %pa to %pa size %zx\n",
+ &mba_dp_phys, &mba_dp_phys_end, drv->mba_dp_size);
/* Load the MBA image into memory */
count = fw->size;
@@ -666,7 +667,8 @@ static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata,
mdata_virt = dma_alloc_attrs(&drv->mba_mem_dev, size, &mdata_phys,
GFP_KERNEL, &attrs);
if (!mdata_virt) {
- dev_err(pil->dev, "MBA metadata buffer allocation failed\n");
+ dev_err(pil->dev, "%s MBA metadata buffer allocation %zx bytes failed\n",
+ __func__, size);
ret = -ENOMEM;
goto fail;
}
diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile
index d78328191bfe..f3505bab1a34 100644
--- a/drivers/soc/qcom/qdsp6v2/Makefile
+++ b/drivers/soc/qcom/qdsp6v2/Makefile
@@ -4,3 +4,6 @@ obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o voice_svc.
obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.o voice_svc.o
obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += msm_audio_ion.o
obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o
+obj-$(CONFIG_MSM_QDSP6_SSR) += audio_ssr.o
+obj-$(CONFIG_MSM_QDSP6_PDR) += audio_pdr.o
+obj-$(CONFIG_MSM_QDSP6_NOTIFIER) += audio_notifier.o
diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c
index bbe686f4bc42..ee9b054dcc24 100644
--- a/drivers/soc/qcom/qdsp6v2/apr.c
+++ b/drivers/soc/qcom/qdsp6v2/apr.c
@@ -27,16 +27,15 @@
#include <linux/device.h>
#include <linux/slab.h>
#include <soc/qcom/subsystem_restart.h>
-#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/scm.h>
#include <sound/apr_audio-v2.h>
#include <soc/qcom/smd.h>
#include <linux/qdsp6v2/apr.h>
#include <linux/qdsp6v2/apr_tal.h>
#include <linux/qdsp6v2/dsp_debug.h>
+#include <linux/qdsp6v2/audio_notifier.h>
#include <linux/ipc_logging.h>
-#define SCM_Q6_NMI_CMD 0x1
#define APR_PKT_IPC_LOG_PAGE_CNT 2
static struct apr_q6 q6;
@@ -45,9 +44,11 @@ static void *apr_pkt_ctx;
static wait_queue_head_t dsp_wait;
static wait_queue_head_t modem_wait;
static bool is_modem_up;
+static bool is_initial_boot;
/* Subsystem restart: QDSP6 data, functions */
static struct workqueue_struct *apr_reset_workqueue;
static void apr_reset_deregister(struct work_struct *work);
+static void dispatch_event(unsigned long code, uint16_t proc);
struct apr_reset_work {
void *handle;
struct work_struct work;
@@ -202,6 +203,20 @@ enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev,
return atomic_cmpxchg(&q6.modem_state, prev, new);
}
+static void apr_modem_down(unsigned long opcode)
+{
+ apr_set_modem_state(APR_SUBSYS_DOWN);
+ dispatch_event(opcode, APR_DEST_MODEM);
+}
+
+static void apr_modem_up(void)
+{
+ if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) ==
+ APR_SUBSYS_DOWN)
+ wake_up(&modem_wait);
+ is_modem_up = 1;
+}
+
enum apr_subsys_state apr_get_q6_state(void)
{
return atomic_read(&q6.q6_state);
@@ -224,6 +239,19 @@ enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev,
return atomic_cmpxchg(&q6.q6_state, prev, new);
}
+static void apr_adsp_down(unsigned long opcode)
+{
+ apr_set_q6_state(APR_SUBSYS_DOWN);
+ dispatch_event(opcode, APR_DEST_QDSP6);
+}
+
+static void apr_adsp_up(void)
+{
+ if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) ==
+ APR_SUBSYS_DOWN)
+ wake_up(&dsp_wait);
+}
+
int apr_wait_for_device_up(int dest_id)
{
int rc = -1;
@@ -276,6 +304,7 @@ int apr_send_pkt(void *handle, uint32_t *buf)
uint16_t dest_id;
uint16_t client_id;
uint16_t w_len;
+ int rc;
unsigned long flags;
if (!handle || !buf) {
@@ -317,14 +346,23 @@ int apr_send_pkt(void *handle, uint32_t *buf)
APR_PKT_INFO("Tx: dest_svc[%d], opcode[0x%X], size[%d]",
hdr->dest_svc, hdr->opcode, hdr->pkt_size);
- w_len = apr_tal_write(clnt->handle, buf,
+ rc = apr_tal_write(clnt->handle, buf,
(struct apr_pkt_priv *)&svc->pkt_owner,
hdr->pkt_size);
- if (w_len != hdr->pkt_size)
- pr_err("Unable to write APR pkt successfully: %d\n", w_len);
+ if (rc >= 0) {
+ w_len = rc;
+ if (w_len != hdr->pkt_size) {
+ pr_err("%s: Unable to write whole APR pkt successfully: %d\n",
+ __func__, rc);
+ rc = -EINVAL;
+ }
+ } else {
+ pr_err("%s: Write APR pkt failed with error %d\n",
+ __func__, rc);
+ }
spin_unlock_irqrestore(&svc->w_lock, flags);
- return w_len;
+ return rc;
}
int apr_pkt_config(void *handle, struct apr_pkt_cfg *cfg)
@@ -730,7 +768,7 @@ void apr_reset(void *handle)
}
/* Dispatch the Reset events to Modem and audio clients */
-void dispatch_event(unsigned long code, uint16_t proc)
+static void dispatch_event(unsigned long code, uint16_t proc)
{
struct apr_client *apr_client;
struct apr_client_data data;
@@ -783,128 +821,51 @@ void dispatch_event(unsigned long code, uint16_t proc)
}
}
-static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
- void *_cmd)
+static int apr_notifier_service_cb(struct notifier_block *this,
+ unsigned long opcode, void *data)
{
- static int boot_count = 2;
-
- if (boot_count) {
- boot_count--;
- return NOTIFY_OK;
- }
+ struct audio_notifier_cb_data *cb_data = data;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("M-Notify: Shutdown started\n");
- apr_set_modem_state(APR_SUBSYS_DOWN);
- dispatch_event(code, APR_DEST_MODEM);
- break;
- case SUBSYS_AFTER_SHUTDOWN:
- pr_debug("M-Notify: Shutdown Completed\n");
- break;
- case SUBSYS_BEFORE_POWERUP:
- pr_debug("M-notify: Bootup started\n");
- break;
- case SUBSYS_AFTER_POWERUP:
- if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) ==
- APR_SUBSYS_DOWN)
- wake_up(&modem_wait);
- is_modem_up = 1;
- pr_debug("M-Notify: Bootup Completed\n");
- break;
- default:
- pr_err("M-Notify: General: %lu\n", code);
- break;
+ if (cb_data == NULL) {
+ pr_err("%s: Callback data is NULL!\n", __func__);
+ goto done;
}
- return NOTIFY_DONE;
-}
-
-static struct notifier_block mnb = {
- .notifier_call = modem_notifier_cb,
-};
-static bool powered_on;
+ pr_debug("%s: Service opcode 0x%lx, domain %d\n",
+ __func__, opcode, cb_data->domain);
-static int lpass_notifier_cb(struct notifier_block *this, unsigned long code,
- void *_cmd)
-{
- static int boot_count = 2;
- struct notif_data *data = (struct notif_data *)_cmd;
- struct scm_desc desc;
-
- if (boot_count) {
- boot_count--;
- return NOTIFY_OK;
- }
-
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("L-Notify: Shutdown started\n");
- apr_set_q6_state(APR_SUBSYS_DOWN);
- dispatch_event(code, APR_DEST_QDSP6);
- if (data && data->crashed) {
- /* Send NMI to QDSP6 via an SCM call. */
- if (!is_scm_armv8()) {
- scm_call_atomic1(SCM_SVC_UTIL,
- SCM_Q6_NMI_CMD, 0x1);
- } else {
- desc.args[0] = 0x1;
- desc.arginfo = SCM_ARGS(1);
- scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_UTIL,
- SCM_Q6_NMI_CMD), &desc);
- }
- /* The write should go through before q6 is shutdown */
- mb();
- pr_debug("L-Notify: Q6 NMI was sent.\n");
- }
- break;
- case SUBSYS_AFTER_SHUTDOWN:
- powered_on = false;
- pr_debug("L-Notify: Shutdown Completed\n");
- break;
- case SUBSYS_BEFORE_POWERUP:
- pr_debug("L-notify: Bootup started\n");
+ switch (opcode) {
+ case AUDIO_NOTIFIER_SERVICE_DOWN:
+ /*
+ * Use flag to ignore down notifications during
+ * initial boot. There is no benefit from error
+ * recovery notifications during initial boot
+ * up since everything is expected to be down.
+ */
+ if (is_initial_boot)
+ break;
+ if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
+ apr_modem_down(opcode);
+ else
+ apr_adsp_down(opcode);
break;
- case SUBSYS_AFTER_POWERUP:
- if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN,
- APR_SUBSYS_LOADED) == APR_SUBSYS_DOWN)
- wake_up(&dsp_wait);
- powered_on = true;
- pr_debug("L-Notify: Bootup Completed\n");
+ case AUDIO_NOTIFIER_SERVICE_UP:
+ is_initial_boot = false;
+ if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
+ apr_modem_up();
+ else
+ apr_adsp_up();
break;
default:
- pr_err("L-Notify: Generel: %lu\n", code);
break;
}
- return NOTIFY_DONE;
+done:
+ return NOTIFY_OK;
}
-static struct notifier_block lnb = {
+static struct notifier_block service_nb = {
+ .notifier_call = apr_notifier_service_cb,
.priority = 0,
- .notifier_call = lpass_notifier_cb,
-};
-
-static int panic_handler(struct notifier_block *this,
- unsigned long event, void *ptr)
-{
- struct scm_desc desc;
-
- if (powered_on) {
- /* Send NMI to QDSP6 via an SCM call. */
- if (!is_scm_armv8()) {
- scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1);
- } else {
- desc.args[0] = 0x1;
- desc.arginfo = SCM_ARGS(1);
- scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_UTIL,
- SCM_Q6_NMI_CMD), &desc);
- }
- }
- return NOTIFY_DONE;
-}
-
-static struct notifier_block panic_nb = {
- .notifier_call = panic_handler,
};
static int __init apr_init(void)
@@ -924,13 +885,18 @@ static int __init apr_init(void)
apr_reset_workqueue = create_singlethread_workqueue("apr_driver");
if (!apr_reset_workqueue)
return -ENOMEM;
- atomic_notifier_chain_register(&panic_notifier_list, &panic_nb);
apr_pkt_ctx = ipc_log_context_create(APR_PKT_IPC_LOG_PAGE_CNT,
"apr", 0);
if (!apr_pkt_ctx)
pr_err("%s: Unable to create ipc log context\n", __func__);
+ is_initial_boot = true;
+ subsys_notif_register("apr_adsp", AUDIO_NOTIFIER_ADSP_DOMAIN,
+ &service_nb);
+ subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN,
+ &service_nb);
+
return 0;
}
device_initcall(apr_init);
@@ -940,7 +906,7 @@ static int __init apr_late_init(void)
int ret = 0;
init_waitqueue_head(&dsp_wait);
init_waitqueue_head(&modem_wait);
- subsys_notif_register(&mnb, &lnb);
+
return ret;
}
late_initcall(apr_late_init);
diff --git a/drivers/soc/qcom/qdsp6v2/apr_v2.c b/drivers/soc/qcom/qdsp6v2/apr_v2.c
index 2d6ea825c811..037fb3327ef0 100644
--- a/drivers/soc/qcom/qdsp6v2/apr_v2.c
+++ b/drivers/soc/qcom/qdsp6v2/apr_v2.c
@@ -18,8 +18,7 @@
#include <linux/qdsp6v2/apr.h>
#include <linux/qdsp6v2/apr_tal.h>
#include <linux/qdsp6v2/dsp_debug.h>
-
-static const char *lpass_subsys_name = "adsp";
+#include <linux/qdsp6v2/audio_notifier.h>
enum apr_subsys_state apr_get_subsys_state(void)
{
@@ -32,11 +31,6 @@ void apr_set_subsys_state(void)
apr_set_modem_state(APR_SUBSYS_UP);
}
-const char *apr_get_lpass_subsys_name(void)
-{
- return lpass_subsys_name;
-}
-
uint16_t apr_get_data_src(struct apr_hdr *hdr)
{
if (hdr->src_domain == APR_DOMAIN_MODEM)
@@ -57,11 +51,15 @@ int apr_get_dest_id(char *dest)
return APR_DEST_MODEM;
}
-void subsys_notif_register(struct notifier_block *mod_notif,
- struct notifier_block *lp_notif)
+void subsys_notif_register(char *client_name, int domain,
+ struct notifier_block *nb)
{
- subsys_notif_register_notifier("modem", mod_notif);
- subsys_notif_register_notifier(apr_get_lpass_subsys_name(), lp_notif);
+ int ret;
+
+ ret = audio_notifier_register(client_name, domain, nb);
+ if (ret < 0)
+ pr_err("%s: Audio notifier register failed for domain %d ret = %d\n",
+ __func__, domain, ret);
}
uint16_t apr_get_reset_domain(uint16_t proc)
diff --git a/drivers/soc/qcom/qdsp6v2/apr_v3.c b/drivers/soc/qcom/qdsp6v2/apr_v3.c
index 6bb913edf3ec..2bfc518841c9 100644
--- a/drivers/soc/qcom/qdsp6v2/apr_v3.c
+++ b/drivers/soc/qcom/qdsp6v2/apr_v3.c
@@ -18,6 +18,7 @@
#include <linux/qdsp6v2/apr.h>
#include <linux/qdsp6v2/apr_tal.h>
#include <linux/qdsp6v2/dsp_debug.h>
+#include <linux/qdsp6v2/audio_notifier.h>
#define DEST_ID APR_DEST_MODEM
@@ -41,10 +42,21 @@ int apr_get_dest_id(char *dest)
return DEST_ID;
}
-void subsys_notif_register(struct notifier_block *mod_notif,
- struct notifier_block *lp_notif)
+void subsys_notif_register(char *client_name, int domain,
+ struct notifier_block *nb)
{
- subsys_notif_register_notifier("modem", mod_notif);
+ int ret;
+
+ if (domain != AUDIO_NOTIFIER_MODEM_DOMAIN) {
+ pr_debug("%s: Unused domain %d not registering with notifier\n",
+ __func__, domain);
+ return;
+ }
+
+ ret = audio_notifier_register(client_name, domain, nb);
+ if (ret < 0)
+ pr_err("%s: Audio notifier register failed for domain %d ret = %d\n",
+ __func__, domain, ret);
}
uint16_t apr_get_reset_domain(uint16_t proc)
diff --git a/drivers/soc/qcom/qdsp6v2/audio_notifier.c b/drivers/soc/qcom/qdsp6v2/audio_notifier.c
new file mode 100644
index 000000000000..47adc3bb3f40
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/audio_notifier.c
@@ -0,0 +1,635 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/qdsp6v2/audio_pdr.h>
+#include <linux/qdsp6v2/audio_ssr.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/subsystem_notif.h>
+#include <soc/qcom/service-notifier.h>
+
+/* Audio states internal to notifier. Client */
+/* used states defined in audio_notifier.h */
+/* for AUDIO_NOTIFIER_SERVICE_DOWN & UP */
+#define NO_SERVICE -2
+#define UNINIT_SERVICE -1
+
+/*
+ * Used for each client registered with audio notifier
+ */
+struct client_data {
+ struct list_head list;
+ /* Notifier block given by client */
+ struct notifier_block *nb;
+ char client_name[20];
+ int service;
+ int domain;
+};
+
+/*
+ * Used for each service and domain combination
+ * Tracks information specific to the underlying
+ * service.
+ */
+struct service_info {
+ const char name[20];
+ int domain_id;
+ int state;
+ void *handle;
+ /* Notifier block registered to service */
+ struct notifier_block *nb;
+ /* Used to determine when to register and deregister service */
+ int num_of_clients;
+ /* List of all clients registered to the service and domain */
+ struct srcu_notifier_head client_nb_list;
+};
+
+static int audio_notifer_ssr_adsp_cb(struct notifier_block *this,
+ unsigned long opcode, void *data);
+static int audio_notifer_ssr_modem_cb(struct notifier_block *this,
+ unsigned long opcode, void *data);
+static int audio_notifer_pdr_adsp_cb(struct notifier_block *this,
+ unsigned long opcode, void *data);
+
+static struct notifier_block notifier_ssr_adsp_nb = {
+ .notifier_call = audio_notifer_ssr_adsp_cb,
+ .priority = 0,
+};
+
+static struct notifier_block notifier_ssr_modem_nb = {
+ .notifier_call = audio_notifer_ssr_modem_cb,
+ .priority = 0,
+};
+
+static struct notifier_block notifier_pdr_adsp_nb = {
+ .notifier_call = audio_notifer_pdr_adsp_cb,
+ .priority = 0,
+};
+
+static struct service_info service_data[AUDIO_NOTIFIER_MAX_SERVICES]
+ [AUDIO_NOTIFIER_MAX_DOMAINS] = {
+
+ {{
+ .name = "SSR_ADSP",
+ .domain_id = AUDIO_SSR_DOMAIN_ADSP,
+ .state = AUDIO_NOTIFIER_SERVICE_DOWN,
+ .nb = &notifier_ssr_adsp_nb
+ },
+ {
+ .name = "SSR_MODEM",
+ .domain_id = AUDIO_SSR_DOMAIN_MODEM,
+ .state = AUDIO_NOTIFIER_SERVICE_DOWN,
+ .nb = &notifier_ssr_modem_nb
+ } },
+
+ {{
+ .name = "PDR_ADSP",
+ .domain_id = AUDIO_PDR_DOMAIN_ADSP,
+ .state = UNINIT_SERVICE,
+ .nb = &notifier_pdr_adsp_nb
+ },
+ { /* PDR MODEM service not enabled */
+ .name = "INVALID",
+ .state = NO_SERVICE,
+ .nb = NULL
+ } }
+};
+
+/* Master list of all audio notifier clients */
+struct list_head client_list;
+struct mutex notifier_mutex;
+
+static int audio_notifer_get_default_service(int domain)
+{
+ int service = NO_SERVICE;
+
+ /* initial service to connect per domain */
+ switch (domain) {
+ case AUDIO_NOTIFIER_ADSP_DOMAIN:
+ service = AUDIO_NOTIFIER_PDR_SERVICE;
+ break;
+ case AUDIO_NOTIFIER_MODEM_DOMAIN:
+ service = AUDIO_NOTIFIER_SSR_SERVICE;
+ break;
+ }
+
+ return service;
+}
+
+static void audio_notifer_disable_service(int service)
+{
+ int i;
+
+ for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
+ service_data[service][i].state = NO_SERVICE;
+}
+
+static bool audio_notifer_is_service_enabled(int service)
+{
+ int i;
+
+ for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
+ if (service_data[service][i].state != NO_SERVICE)
+ return true;
+ return false;
+}
+
+static void audio_notifer_init_service(int service)
+{
+ int i;
+
+ for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) {
+ if (service_data[service][i].state == UNINIT_SERVICE)
+ service_data[service][i].state =
+ AUDIO_NOTIFIER_SERVICE_DOWN;
+ }
+}
+
+static int audio_notifer_reg_service(int service, int domain)
+{
+ void *handle;
+ int ret = 0;
+ int curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
+
+ switch (service) {
+ case AUDIO_NOTIFIER_SSR_SERVICE:
+ handle = audio_ssr_register(
+ service_data[service][domain].domain_id,
+ service_data[service][domain].nb);
+ break;
+ case AUDIO_NOTIFIER_PDR_SERVICE:
+ handle = audio_pdr_service_register(
+ service_data[service][domain].domain_id,
+ service_data[service][domain].nb, &curr_state);
+
+ if (curr_state == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
+ curr_state = AUDIO_NOTIFIER_SERVICE_UP;
+ else
+ curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
+ break;
+ default:
+ pr_err("%s: Invalid service %d\n",
+ __func__, service);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (IS_ERR_OR_NULL(handle)) {
+ pr_err("%s: handle is incorrect for service %s\n",
+ __func__, service_data[service][domain].name);
+ ret = -EINVAL;
+ goto done;
+ }
+ service_data[service][domain].state = curr_state;
+ service_data[service][domain].handle = handle;
+
+ pr_info("%s: service %s is in use\n",
+ __func__, service_data[service][domain].name);
+ pr_debug("%s: service %s has current state %d, handle 0x%pK\n",
+ __func__, service_data[service][domain].name,
+ service_data[service][domain].state,
+ service_data[service][domain].handle);
+done:
+ return ret;
+}
+
+static int audio_notifer_dereg_service(int service, int domain)
+{
+ int ret;
+
+ switch (service) {
+ case AUDIO_NOTIFIER_SSR_SERVICE:
+ ret = audio_ssr_deregister(
+ service_data[service][domain].handle,
+ service_data[service][domain].nb);
+ break;
+ case AUDIO_NOTIFIER_PDR_SERVICE:
+ ret = audio_pdr_service_deregister(
+ service_data[service][domain].handle,
+ service_data[service][domain].nb);
+ break;
+ default:
+ pr_err("%s: Invalid service %d\n",
+ __func__, service);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: deregister failed for service %s, ret %d\n",
+ __func__, service_data[service][domain].name, ret);
+ goto done;
+ }
+
+ pr_debug("%s: service %s with handle 0x%pK deregistered\n",
+ __func__, service_data[service][domain].name,
+ service_data[service][domain].handle);
+
+ service_data[service][domain].state = AUDIO_NOTIFIER_SERVICE_DOWN;
+ service_data[service][domain].handle = NULL;
+done:
+ return ret;
+}
+
+static int audio_notifer_reg_client_service(struct client_data *client_data,
+ int service)
+{
+ int ret = 0;
+ int domain = client_data->domain;
+ struct audio_notifier_cb_data data;
+
+ switch (service) {
+ case AUDIO_NOTIFIER_SSR_SERVICE:
+ case AUDIO_NOTIFIER_PDR_SERVICE:
+ if (service_data[service][domain].num_of_clients == 0)
+ ret = audio_notifer_reg_service(service, domain);
+ break;
+ default:
+ pr_err("%s: Invalid service for client %s, service %d, domain %d\n",
+ __func__, client_data->client_name, service, domain);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: service registration failed on service %s for client %s\n",
+ __func__, service_data[service][domain].name,
+ client_data->client_name);
+ goto done;
+ }
+
+ client_data->service = service;
+ srcu_notifier_chain_register(
+ &service_data[service][domain].client_nb_list,
+ client_data->nb);
+ service_data[service][domain].num_of_clients++;
+
+ pr_debug("%s: registered client %s on service %s, current state 0x%x\n",
+ __func__, client_data->client_name,
+ service_data[service][domain].name,
+ service_data[service][domain].state);
+
+ /*
+ * PDR registration returns current state
+ * Force callback of client with current state for PDR
+ */
+ if (client_data->service == AUDIO_NOTIFIER_PDR_SERVICE) {
+ data.service = service;
+ data.domain = domain;
+ (void)client_data->nb->notifier_call(client_data->nb,
+ service_data[service][domain].state, &data);
+ }
+done:
+ return ret;
+}
+
+static int audio_notifer_reg_client(struct client_data *client_data)
+{
+ int ret = 0;
+ int service;
+ int domain = client_data->domain;
+
+ service = audio_notifer_get_default_service(domain);
+ if (service < 0) {
+ pr_err("%s: service %d is incorrect\n", __func__, service);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Search through services to find a valid one to register client on. */
+ for (; service >= 0; service--) {
+ /* If a service is not initialized, wait for it to come up. */
+ if (service_data[service][domain].state == UNINIT_SERVICE)
+ goto done;
+ /* Skip unsupported service and domain combinations. */
+ if (service_data[service][domain].state < 0)
+ continue;
+ /* Only register clients who have not acquired a service. */
+ if (client_data->service != NO_SERVICE)
+ continue;
+
+ /*
+ * Only register clients, who have not acquired a service, on
+ * the best available service for their domain. Uninitialized
+ * services will try to register all of their clients after
+ * they initialize correctly or will disable their service and
+ * register clients on the next best avaialable service.
+ */
+ pr_debug("%s: register client %s on service %s",
+ __func__, client_data->client_name,
+ service_data[service][domain].name);
+
+ ret = audio_notifer_reg_client_service(client_data, service);
+ if (IS_ERR_VALUE(ret))
+ pr_err("%s: client %s failed to register on service %s",
+ __func__, client_data->client_name,
+ service_data[service][domain].name);
+ }
+
+done:
+ return ret;
+}
+
+static int audio_notifer_dereg_client(struct client_data *client_data)
+{
+ int ret = 0;
+ int service = client_data->service;
+ int domain = client_data->domain;
+
+ switch (client_data->service) {
+ case AUDIO_NOTIFIER_SSR_SERVICE:
+ case AUDIO_NOTIFIER_PDR_SERVICE:
+ if (service_data[service][domain].num_of_clients == 1)
+ ret = audio_notifer_dereg_service(service, domain);
+ break;
+ case NO_SERVICE:
+ goto done;
+ default:
+ pr_err("%s: Invalid service for client %s, service %d\n",
+ __func__, client_data->client_name,
+ client_data->service);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: deregister failed for client %s on service %s, ret %d\n",
+ __func__, client_data->client_name,
+ service_data[service][domain].name, ret);
+ goto done;
+ }
+
+ ret = srcu_notifier_chain_unregister(&service_data[service][domain].
+ client_nb_list, client_data->nb);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: srcu_notifier_chain_unregister failed, ret %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ pr_debug("%s: deregistered client %s on service %s\n",
+ __func__, client_data->client_name,
+ service_data[service][domain].name);
+
+ client_data->service = NO_SERVICE;
+ if (service_data[service][domain].num_of_clients > 0)
+ service_data[service][domain].num_of_clients--;
+done:
+ return ret;
+}
+
+static void audio_notifer_reg_all_clients(void)
+{
+ struct list_head *ptr, *next;
+ struct client_data *client_data;
+ int ret;
+
+ list_for_each_safe(ptr, next, &client_list) {
+ client_data = list_entry(ptr,
+ struct client_data, list);
+ ret = audio_notifer_reg_client(client_data);
+ if (IS_ERR_VALUE(ret))
+ pr_err("%s: audio_notifer_reg_client failed for client %s, ret %d\n",
+ __func__, client_data->client_name,
+ ret);
+ }
+}
+
+static int audio_notifer_pdr_callback(struct notifier_block *this,
+ unsigned long opcode, void *data)
+{
+ pr_debug("%s: Audio PDR framework state 0x%lx\n",
+ __func__, opcode);
+ mutex_lock(&notifier_mutex);
+ if (opcode == AUDIO_PDR_FRAMEWORK_DOWN)
+ audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
+ else
+ audio_notifer_init_service(AUDIO_NOTIFIER_PDR_SERVICE);
+
+ audio_notifer_reg_all_clients();
+ mutex_unlock(&notifier_mutex);
+ return 0;
+}
+
+static struct notifier_block pdr_nb = {
+ .notifier_call = audio_notifer_pdr_callback,
+ .priority = 0,
+};
+
+static int audio_notifer_convert_opcode(unsigned long opcode,
+ unsigned long *notifier_opcode)
+{
+ int ret = 0;
+
+ switch (opcode) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
+ *notifier_opcode = AUDIO_NOTIFIER_SERVICE_DOWN;
+ break;
+ case SUBSYS_AFTER_POWERUP:
+ case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
+ *notifier_opcode = AUDIO_NOTIFIER_SERVICE_UP;
+ break;
+ default:
+ pr_debug("%s: Unused opcode 0x%lx\n", __func__, opcode);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int audio_notifer_service_cb(unsigned long opcode,
+ int service, int domain)
+{
+ int ret = 0;
+ unsigned long notifier_opcode;
+ struct audio_notifier_cb_data data;
+
+ if (audio_notifer_convert_opcode(opcode, &notifier_opcode) < 0)
+ goto done;
+
+ data.service = service;
+ data.domain = domain;
+
+ pr_debug("%s: service %s, opcode 0x%lx\n",
+ __func__, service_data[service][domain].name, notifier_opcode);
+
+ mutex_lock(&notifier_mutex);
+
+ service_data[service][domain].state = notifier_opcode;
+ ret = srcu_notifier_call_chain(&service_data[service][domain].
+ client_nb_list, notifier_opcode, &data);
+ if (IS_ERR_VALUE(ret))
+ pr_err("%s: srcu_notifier_call_chain returned %d, service %s, opcode 0x%lx\n",
+ __func__, ret, service_data[service][domain].name,
+ notifier_opcode);
+
+ mutex_unlock(&notifier_mutex);
+done:
+ return NOTIFY_OK;
+}
+
+static int audio_notifer_pdr_adsp_cb(struct notifier_block *this,
+ unsigned long opcode, void *data)
+{
+ return audio_notifer_service_cb(opcode,
+ AUDIO_NOTIFIER_PDR_SERVICE,
+ AUDIO_NOTIFIER_ADSP_DOMAIN);
+}
+
+static int audio_notifer_ssr_adsp_cb(struct notifier_block *this,
+ unsigned long opcode, void *data)
+{
+ if (opcode == SUBSYS_BEFORE_SHUTDOWN)
+ audio_ssr_send_nmi(data);
+
+ return audio_notifer_service_cb(opcode,
+ AUDIO_NOTIFIER_SSR_SERVICE,
+ AUDIO_NOTIFIER_ADSP_DOMAIN);
+}
+
+static int audio_notifer_ssr_modem_cb(struct notifier_block *this,
+ unsigned long opcode, void *data)
+{
+ return audio_notifer_service_cb(opcode,
+ AUDIO_NOTIFIER_SSR_SERVICE,
+ AUDIO_NOTIFIER_MODEM_DOMAIN);
+}
+
+int audio_notifier_deregister(char *client_name)
+{
+ int ret = 0;
+ int ret2;
+ struct list_head *ptr, *next;
+ struct client_data *client_data;
+
+ if (client_name == NULL) {
+ pr_err("%s: client_name is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ mutex_lock(&notifier_mutex);
+ list_for_each_safe(ptr, next, &client_data->list) {
+ client_data = list_entry(ptr, struct client_data,
+ list);
+ if (!strcmp(client_name, client_data->client_name)) {
+ ret2 = audio_notifer_dereg_client(client_data);
+ if (ret2 < 0) {
+ pr_err("%s: audio_notifer_dereg_client failed, ret %d\n, service %d, domain %d",
+ __func__, ret2, client_data->service,
+ client_data->domain);
+ ret = ret2;
+ continue;
+ }
+ list_del(&client_data->list);
+ kfree(client_data);
+ }
+ }
+ mutex_unlock(&notifier_mutex);
+done:
+ return ret;
+}
+EXPORT_SYMBOL(audio_notifier_deregister);
+
+int audio_notifier_register(char *client_name, int domain,
+ struct notifier_block *nb)
+{
+ int ret;
+ struct client_data *client_data;
+
+ if (client_name == NULL) {
+ pr_err("%s: client_name is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ } else if (nb == NULL) {
+ pr_err("%s: Notifier block is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ client_data = kmalloc(sizeof(*client_data), GFP_KERNEL);
+ if (client_data == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ INIT_LIST_HEAD(&client_data->list);
+ client_data->nb = nb;
+ strlcpy(client_data->client_name, client_name,
+ sizeof(client_data->client_name));
+ client_data->service = NO_SERVICE;
+ client_data->domain = domain;
+
+ mutex_lock(&notifier_mutex);
+ ret = audio_notifer_reg_client(client_data);
+ if (IS_ERR_VALUE(ret)) {
+ mutex_unlock(&notifier_mutex);
+ pr_err("%s: audio_notifer_reg_client for client %s failed ret = %d\n",
+ __func__, client_data->client_name,
+ ret);
+ kfree(client_data);
+ goto done;
+ }
+ list_add_tail(&client_data->list, &client_list);
+ mutex_unlock(&notifier_mutex);
+done:
+ return ret;
+}
+EXPORT_SYMBOL(audio_notifier_register);
+
+static int __init audio_notifier_subsys_init(void)
+{
+ int i, j;
+
+ mutex_init(&notifier_mutex);
+ INIT_LIST_HEAD(&client_list);
+ for (i = 0; i < AUDIO_NOTIFIER_MAX_SERVICES; i++) {
+ for (j = 0; j < AUDIO_NOTIFIER_MAX_DOMAINS; j++) {
+ if (service_data[i][j].state <= NO_SERVICE)
+ continue;
+
+ srcu_init_notifier_head(
+ &service_data[i][j].client_nb_list);
+ }
+ }
+
+ return 0;
+}
+subsys_initcall(audio_notifier_subsys_init);
+
+static int __init audio_notifier_init(void)
+{
+ int ret;
+
+ ret = audio_pdr_register(&pdr_nb);
+ if (IS_ERR_VALUE(ret)) {
+ pr_debug("%s: PDR register failed, ret = %d, disable service\n",
+ __func__, ret);
+ audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
+ }
+
+ /* Do not return error since PDR enablement is not critical */
+ return 0;
+}
+module_init(audio_notifier_init);
+
+static int __init audio_notifier_late_init(void)
+{
+ /*
+ * If pdr registration failed, register clients on next service
+ * Do in late init to ensure that SSR subsystem is initialized
+ */
+ if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE))
+ audio_notifer_reg_all_clients();
+
+ return 0;
+}
+late_initcall(audio_notifier_late_init);
diff --git a/drivers/soc/qcom/qdsp6v2/audio_pdr.c b/drivers/soc/qcom/qdsp6v2/audio_pdr.c
new file mode 100644
index 000000000000..642ceb5533b0
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/audio_pdr.c
@@ -0,0 +1,148 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/qdsp6v2/audio_pdr.h>
+#include <soc/qcom/service-locator.h>
+#include <soc/qcom/service-notifier.h>
+
+static struct pd_qmi_client_data audio_pdr_services[AUDIO_PDR_DOMAIN_MAX] = {
+ { /* AUDIO_PDR_DOMAIN_ADSP */
+ .client_name = "audio_pdr_adsp",
+ .service_name = "avs/audio"
+ }
+};
+
+struct srcu_notifier_head audio_pdr_cb_list;
+
+static int audio_pdr_locator_callback(struct notifier_block *this,
+ unsigned long opcode, void *data)
+{
+ unsigned long pdr_state = AUDIO_PDR_FRAMEWORK_DOWN;
+
+ if (opcode == LOCATOR_DOWN) {
+ pr_debug("%s: Service %s is down!", __func__,
+ audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
+ service_name);
+ goto done;
+ }
+
+ memcpy(&audio_pdr_services, data,
+ sizeof(audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP]));
+ if (audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].total_domains == 1) {
+ pr_debug("%s: Service %s, returned total domains %d, ",
+ __func__,
+ audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
+ audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
+ total_domains);
+ pdr_state = AUDIO_PDR_FRAMEWORK_UP;
+ goto done;
+ } else
+ pr_err("%s: Service %s returned invalid total domains %d",
+ __func__,
+ audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
+ audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
+ total_domains);
+done:
+ srcu_notifier_call_chain(&audio_pdr_cb_list, pdr_state, NULL);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block audio_pdr_locator_nb = {
+ .notifier_call = audio_pdr_locator_callback,
+ .priority = 0,
+};
+
+int audio_pdr_register(struct notifier_block *nb)
+{
+ if (nb == NULL) {
+ pr_err("%s: Notifier block is NULL\n", __func__);
+ return -EINVAL;
+ }
+ return srcu_notifier_chain_register(&audio_pdr_cb_list, nb);
+}
+EXPORT_SYMBOL(audio_pdr_register);
+
+void *audio_pdr_service_register(int domain_id,
+ struct notifier_block *nb, int *curr_state)
+{
+ void *handle;
+
+ if ((domain_id < 0) ||
+ (domain_id >= AUDIO_PDR_DOMAIN_MAX)) {
+ pr_err("%s: Invalid service ID %d\n", __func__, domain_id);
+ return ERR_PTR(-EINVAL);
+ }
+
+ handle = service_notif_register_notifier(
+ audio_pdr_services[domain_id].domain_list[0].name,
+ audio_pdr_services[domain_id].domain_list[0].instance_id,
+ nb, curr_state);
+ if (IS_ERR_OR_NULL(handle)) {
+ pr_err("%s: Failed to register for service %s, instance %d\n",
+ __func__,
+ audio_pdr_services[domain_id].domain_list[0].name,
+ audio_pdr_services[domain_id].domain_list[0].
+ instance_id);
+ }
+ return handle;
+}
+EXPORT_SYMBOL(audio_pdr_service_register);
+
+int audio_pdr_service_deregister(void *service_handle,
+ struct notifier_block *nb)
+{
+ int ret;
+
+ if (service_handle == NULL) {
+ pr_err("%s: service handle is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = service_notif_unregister_notifier(
+ service_handle, nb);
+ if (IS_ERR_VALUE(ret))
+ pr_err("%s: Failed to deregister service ret %d\n",
+ __func__, ret);
+done:
+ return ret;
+}
+EXPORT_SYMBOL(audio_pdr_service_deregister);
+
+static int __init audio_pdr_subsys_init(void)
+{
+ srcu_init_notifier_head(&audio_pdr_cb_list);
+ return 0;
+}
+subsys_initcall(audio_pdr_subsys_init);
+
+static int __init audio_pdr_late_init(void)
+{
+ int ret;
+
+ ret = get_service_location(
+ audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].client_name,
+ audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
+ &audio_pdr_locator_nb);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s get_service_location failed ret %d\n",
+ __func__, ret);
+ srcu_notifier_call_chain(&audio_pdr_cb_list,
+ AUDIO_PDR_FRAMEWORK_DOWN, NULL);
+ }
+
+ return ret;
+}
+late_initcall(audio_pdr_late_init);
+
diff --git a/drivers/soc/qcom/qdsp6v2/audio_ssr.c b/drivers/soc/qcom/qdsp6v2/audio_ssr.c
new file mode 100644
index 000000000000..a66fb2a63fae
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/audio_ssr.c
@@ -0,0 +1,66 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/qdsp6v2/audio_ssr.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/subsystem_notif.h>
+
+#define SCM_Q6_NMI_CMD 0x1
+
+static char *audio_ssr_domains[] = {
+ "adsp",
+ "modem"
+};
+
+void *audio_ssr_register(int domain_id, struct notifier_block *nb)
+{
+ if ((domain_id < 0) ||
+ (domain_id >= AUDIO_SSR_DOMAIN_MAX)) {
+ pr_err("%s: Invalid service ID %d\n", __func__, domain_id);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return subsys_notif_register_notifier(
+ audio_ssr_domains[domain_id], nb);
+}
+EXPORT_SYMBOL(audio_ssr_register);
+
+int audio_ssr_deregister(void *handle, struct notifier_block *nb)
+{
+ return subsys_notif_unregister_notifier(handle, nb);
+}
+EXPORT_SYMBOL(audio_ssr_deregister);
+
+void audio_ssr_send_nmi(void *ssr_cb_data)
+{
+ struct notif_data *data = (struct notif_data *)ssr_cb_data;
+ struct scm_desc desc;
+
+ if (data && data->crashed) {
+ /* Send NMI to QDSP6 via an SCM call. */
+ if (!is_scm_armv8()) {
+ scm_call_atomic1(SCM_SVC_UTIL,
+ SCM_Q6_NMI_CMD, 0x1);
+ } else {
+ desc.args[0] = 0x1;
+ desc.arginfo = SCM_ARGS(1);
+ scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_UTIL,
+ SCM_Q6_NMI_CMD), &desc);
+ }
+ /* The write should go through before q6 is shutdown */
+ mb();
+ pr_debug("%s: Q6 NMI was sent.\n", __func__);
+ }
+}
+EXPORT_SYMBOL(audio_ssr_send_nmi);
diff --git a/drivers/soc/qcom/rpm-smd-debug.c b/drivers/soc/qcom/rpm-smd-debug.c
index c08668149636..4e406f7cd379 100644
--- a/drivers/soc/qcom/rpm-smd-debug.c
+++ b/drivers/soc/qcom/rpm-smd-debug.c
@@ -104,8 +104,6 @@ static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer,
if (msm_rpm_wait_for_ack(msm_rpm_send_request(req)))
pr_err("Sending the RPM message failed\n");
- else
- pr_info("RPM message sent succesfully\n");
err_request:
msm_rpm_free_request(req);
diff --git a/drivers/soc/qcom/secure_buffer.c b/drivers/soc/qcom/secure_buffer.c
index e7dbcac064aa..d9ebc1edda9c 100644
--- a/drivers/soc/qcom/secure_buffer.c
+++ b/drivers/soc/qcom/secure_buffer.c
@@ -418,6 +418,8 @@ const char *msm_secure_vmid_to_string(int secure_vmid)
return "VMID_WLAN";
case VMID_WLAN_CE:
return "VMID_WLAN_CE";
+ case VMID_CP_CAMERA_PREVIEW:
+ return "VMID_CP_CAMERA_PREVIEW";
case VMID_INVAL:
return "VMID_INVAL";
default:
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index 81dde8ca1ae8..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);
@@ -386,7 +387,8 @@ static void root_service_service_arrive(struct work_struct *work)
mutex_unlock(&notif_add_lock);
}
-static void root_service_service_exit(struct qmi_client_info *data)
+static void root_service_service_exit(struct qmi_client_info *data,
+ enum pd_subsys_state state)
{
struct service_notif_info *service_notif = NULL;
int rc;
@@ -401,7 +403,7 @@ static void root_service_service_exit(struct qmi_client_info *data)
if (service_notif->instance_id == data->instance_id) {
rc = service_notif_queue_notification(service_notif,
SERVREG_NOTIF_SERVICE_STATE_DOWN_V01,
- NULL);
+ &state);
if (rc & NOTIFY_STOP_MASK)
pr_err("Notifier callback aborted for %s with error %d\n",
service_notif->service_path, rc);
@@ -416,16 +418,18 @@ 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)
{
struct qmi_client_info *data = container_of(work,
struct qmi_client_info, svc_exit);
- root_service_service_exit(data);
+ root_service_service_exit(data, UNKNOWN);
}
static int service_event_notify(struct notifier_block *this,
@@ -456,10 +460,15 @@ static int ssr_event_notify(struct notifier_block *this,
{
struct qmi_client_info *info = container_of(this,
struct qmi_client_info, ssr_notifier);
+ struct notif_data *notif = data;
switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("Root PD service Down (SSR notification)\n");
- root_service_service_exit(info);
+ case SUBSYS_AFTER_SHUTDOWN:
+ pr_debug("Root PD DOWN(SSR notification), crashed?%d\n",
+ notif->crashed);
+ if (notif->crashed)
+ root_service_service_exit(info, CRASHED);
+ else
+ root_service_service_exit(info, SHUTDOWN);
break;
default:
break;
diff --git a/drivers/soc/qcom/smcinvoke.c b/drivers/soc/qcom/smcinvoke.c
index a1344f0780b0..6de73217bf86 100644
--- a/drivers/soc/qcom/smcinvoke.c
+++ b/drivers/soc/qcom/smcinvoke.c
@@ -375,23 +375,27 @@ long smcinvoke_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
nr_args = object_counts_num_buffers(req.counts) +
object_counts_num_objects(req.counts);
- if (!nr_args || req.argsize != sizeof(union smcinvoke_arg)) {
+ if (req.argsize != sizeof(union smcinvoke_arg)) {
ret = -EINVAL;
goto out;
}
- args_buf = kzalloc(nr_args * req.argsize, GFP_KERNEL);
- if (!args_buf) {
- ret = -ENOMEM;
- goto out;
- }
+ if (nr_args) {
- ret = copy_from_user(args_buf, (void __user *)(req.args),
+ args_buf = kzalloc(nr_args * req.argsize, GFP_KERNEL);
+ if (!args_buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = copy_from_user(args_buf,
+ (void __user *)(req.args),
nr_args * req.argsize);
- if (ret) {
- ret = -EFAULT;
- goto out;
+ if (ret) {
+ ret = -EFAULT;
+ goto out;
+ }
}
inmsg_size = compute_in_msg_size(&req, args_buf);
diff --git a/drivers/soc/qcom/spss_utils.c b/drivers/soc/qcom/spss_utils.c
index 9f799dfd9003..e54819b4837b 100644
--- a/drivers/soc/qcom/spss_utils.c
+++ b/drivers/soc/qcom/spss_utils.c
@@ -97,9 +97,9 @@ static ssize_t test_fuse_state_show(struct device *dev,
}
if (is_test_fuse_set)
- ret = snprintf(buf, PAGE_SIZE, "%s\n", "test");
+ ret = snprintf(buf, PAGE_SIZE, "%s", "test");
else
- ret = snprintf(buf, PAGE_SIZE, "%s\n", "prod");
+ ret = snprintf(buf, PAGE_SIZE, "%s", "prod");
return ret;
}
@@ -280,7 +280,7 @@ static int __init spss_init(void)
{
int ret = 0;
- pr_info("spss-utils driver Ver 1.0 12-Sep-2016.\n");
+ pr_info("spss-utils driver Ver 1.1 18-Sep-2016.\n");
ret = platform_driver_register(&spss_driver);
if (ret)
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index 6a1a87ead6e4..1fa731776926 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -518,8 +518,7 @@ err:
static void disable_unprepare_clocks(struct clk **clks, int clk_count)
{
int i;
-
- for (i = 0; i < clk_count; i++)
+ for (i = --clk_count; i >= 0; i--)
clk_disable_unprepare(clks[i]);
}
@@ -918,8 +917,24 @@ static void check_pbl_done(struct pil_tz_data *d)
err_value = __raw_readl(d->err_status);
pr_debug("PBL_DONE received from %s!\n", d->subsys_desc.name);
- if (err_value)
+ if (err_value) {
+ uint32_t rmb_err_spare0;
+ uint32_t rmb_err_spare1;
+ uint32_t rmb_err_spare2;
+
+ rmb_err_spare2 = __raw_readl(d->err_status_spare);
+ rmb_err_spare1 = __raw_readl(d->err_status_spare-4);
+ rmb_err_spare0 = __raw_readl(d->err_status_spare-8);
+
pr_err("PBL error status register: 0x%08x\n", err_value);
+
+ pr_err("PBL error status spare0 register: 0x%08x\n",
+ rmb_err_spare0);
+ pr_err("PBL error status spare1 register: 0x%08x\n",
+ rmb_err_spare1);
+ pr_err("PBL error status spare2 register: 0x%08x\n",
+ rmb_err_spare2);
+ }
__raw_writel(BIT(d->bits_arr[PBL_DONE]), d->irq_clear);
}
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index 76d941ceb77e..015e60ac622c 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -572,25 +572,6 @@ static void disable_all_irqs(struct subsys_device *dev)
}
}
-int wait_for_shutdown_ack(struct subsys_desc *desc)
-{
- int count;
-
- if (desc && !desc->shutdown_ack_gpio)
- return 0;
-
- for (count = SHUTDOWN_ACK_MAX_LOOPS; count > 0; count--) {
- if (gpio_get_value(desc->shutdown_ack_gpio))
- return count;
- msleep(SHUTDOWN_ACK_DELAY_MS);
- }
-
- pr_err("[%s]: Timed out waiting for shutdown ack\n", desc->name);
-
- return -ETIMEDOUT;
-}
-EXPORT_SYMBOL(wait_for_shutdown_ack);
-
static int wait_for_err_ready(struct subsys_device *subsys)
{
int ret;
@@ -730,6 +711,7 @@ static void subsys_stop(struct subsys_device *subsys)
{
const char *name = subsys->desc->name;
+ notify_each_subsys_device(&subsys, 1, SUBSYS_BEFORE_SHUTDOWN, NULL);
if (!of_property_read_bool(subsys->desc->dev->of_node,
"qcom,pil-force-shutdown")) {
subsys_set_state(subsys, SUBSYS_OFFLINING);
@@ -739,7 +721,6 @@ static void subsys_stop(struct subsys_device *subsys)
pr_debug("Graceful shutdown failed for %s\n", name);
}
- notify_each_subsys_device(&subsys, 1, SUBSYS_BEFORE_SHUTDOWN, NULL);
subsys->desc->shutdown(subsys->desc, false);
subsys_set_state(subsys, SUBSYS_OFFLINE);
disable_all_irqs(subsys);
@@ -768,6 +749,31 @@ int subsystem_set_fwname(const char *name, const char *fw_name)
}
EXPORT_SYMBOL(subsystem_set_fwname);
+int wait_for_shutdown_ack(struct subsys_desc *desc)
+{
+ int count;
+ struct subsys_device *dev;
+
+ if (!desc || !desc->shutdown_ack_gpio)
+ return 0;
+
+ dev = find_subsys(desc->name);
+ if (!dev)
+ return 0;
+
+ for (count = SHUTDOWN_ACK_MAX_LOOPS; count > 0; count--) {
+ if (gpio_get_value(desc->shutdown_ack_gpio))
+ return count;
+ else if (subsys_get_crash_status(dev))
+ break;
+ msleep(SHUTDOWN_ACK_DELAY_MS);
+ }
+
+ pr_err("[%s]: Timed out waiting for shutdown ack\n", desc->name);
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL(wait_for_shutdown_ack);
+
void *__subsystem_get(const char *name, const char *fw_name)
{
struct subsys_device *subsys;
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/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 65eda2de9586..8f58eaa537b1 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -90,6 +90,7 @@ struct msm_watchdog_data {
bool timer_expired;
bool user_pet_complete;
+ unsigned int scandump_size;
};
/*
@@ -359,7 +360,7 @@ static void ping_other_cpus(struct msm_watchdog_data *wdog_dd)
cpumask_clear(&wdog_dd->alive_mask);
smp_mb();
for_each_cpu(cpu, cpu_online_mask) {
- if (!cpu_idle_pc_state[cpu])
+ if (!cpu_idle_pc_state[cpu] && !cpu_isolated(cpu))
smp_call_function_single(cpu, keep_alive_response,
wdog_dd, 1);
}
@@ -501,6 +502,39 @@ static irqreturn_t wdog_ppi_bark(int irq, void *dev_id)
return wdog_bark_handler(irq, wdog_dd);
}
+void register_scan_dump(struct msm_watchdog_data *wdog_dd)
+{
+ static void *dump_addr;
+ int ret;
+ struct msm_dump_entry dump_entry;
+ struct msm_dump_data *dump_data;
+
+ if (!wdog_dd->scandump_size)
+ return;
+
+ dump_data = kzalloc(sizeof(struct msm_dump_data), GFP_KERNEL);
+ if (!dump_data)
+ return;
+ dump_addr = kzalloc(wdog_dd->scandump_size, GFP_KERNEL);
+ if (!dump_addr)
+ goto err0;
+
+ dump_data->addr = virt_to_phys(dump_addr);
+ dump_data->len = wdog_dd->scandump_size;
+ dump_entry.id = MSM_DUMP_DATA_SCANDUMP;
+ dump_entry.addr = virt_to_phys(dump_data);
+ ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
+ if (ret) {
+ pr_err("Registering scandump region failed\n");
+ goto err1;
+ }
+ return;
+err1:
+ kfree(dump_addr);
+err0:
+ kfree(dump_data);
+}
+
static void configure_bark_dump(struct msm_watchdog_data *wdog_dd)
{
int ret;
@@ -582,6 +616,8 @@ static void configure_bark_dump(struct msm_watchdog_data *wdog_dd)
if (ret)
pr_err("cpu %d reg dump setup failed\n", cpu);
}
+
+ register_scan_dump(wdog_dd);
}
return;
@@ -770,6 +806,11 @@ static int msm_wdog_dt_to_pdata(struct platform_device *pdev,
pdata->wakeup_irq_enable = of_property_read_bool(node,
"qcom,wakeup-enable");
+ if (of_property_read_u32(node, "qcom,scandump-size",
+ &pdata->scandump_size))
+ dev_info(&pdev->dev,
+ "No need to allocate memory for scandumps\n");
+
pdata->irq_ppi = irq_is_percpu(pdata->bark_irq);
dump_pdata(pdata);
return 0;
diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c
index 310903b10a98..92cdadef715d 100644
--- a/drivers/soc/qcom/wcd-dsp-glink.c
+++ b/drivers/soc/qcom/wcd-dsp-glink.c
@@ -32,6 +32,7 @@
#define WDSP_EDGE "wdsp"
#define RESP_QUEUE_SIZE 3
#define QOS_PKT_SIZE 1024
+#define TIMEOUT_MS 1000
struct wdsp_glink_dev {
struct class *cls;
@@ -71,7 +72,19 @@ struct wdsp_glink_ch {
/* To free up the channel memory */
bool free_mem;
- /* Glink channel configuration */
+ /* Glink local channel open work */
+ struct work_struct lcl_ch_open_wrk;
+
+ /* Glink local channel close work */
+ struct work_struct lcl_ch_cls_wrk;
+
+ /* Wait for ch connect state before sending any command */
+ wait_queue_head_t ch_connect_wait;
+
+ /*
+ * Glink channel configuration. This has to be the last
+ * member of the strucuture as it has variable size
+ */
struct wdsp_glink_ch_cfg ch_cfg;
};
@@ -89,12 +102,15 @@ struct wdsp_glink_priv {
struct mutex rsp_mutex;
/* Glink channel related */
+ struct mutex glink_mutex;
struct wdsp_glink_state glink_state;
struct wdsp_glink_ch **ch;
u8 no_of_channels;
struct work_struct ch_open_cls_wrk;
struct workqueue_struct *work_queue;
+ wait_queue_head_t link_state_wait;
+
struct device *dev;
};
@@ -214,6 +230,36 @@ done:
}
/*
+ * wdsp_glink_lcl_ch_open_wrk - Work function to open channel again
+ * when local disconnect event happens
+ * work: Work structure
+ */
+static void wdsp_glink_lcl_ch_open_wrk(struct work_struct *work)
+{
+ struct wdsp_glink_ch *ch;
+
+ ch = container_of(work, struct wdsp_glink_ch,
+ lcl_ch_open_wrk);
+
+ wdsp_glink_open_ch(ch);
+}
+
+/*
+ * wdsp_glink_lcl_ch_cls_wrk - Work function to close channel locally
+ * when remote disconnect event happens
+ * work: Work structure
+ */
+static void wdsp_glink_lcl_ch_cls_wrk(struct work_struct *work)
+{
+ struct wdsp_glink_ch *ch;
+
+ ch = container_of(work, struct wdsp_glink_ch,
+ lcl_ch_cls_wrk);
+
+ wdsp_glink_close_ch(ch);
+}
+
+/*
* wdsp_glink_notify_state - Glink channel state information event callback
* handle: Opaque Channel handle returned by GLink
* priv: Private pointer to the channel
@@ -258,6 +304,7 @@ static void wdsp_glink_notify_state(void *handle, const void *priv,
__func__, ch->ch_cfg.latency_in_us,
ch->ch_cfg.name);
+ wake_up(&ch->ch_connect_wait);
mutex_unlock(&ch->mutex);
} else if (event == GLINK_LOCAL_DISCONNECTED) {
/*
@@ -271,6 +318,9 @@ static void wdsp_glink_notify_state(void *handle, const void *priv,
if (ch->free_mem) {
kfree(ch);
ch = NULL;
+ } else {
+ /* Open the glink channel again */
+ queue_work(wpriv->work_queue, &ch->lcl_ch_open_wrk);
}
} else if (event == GLINK_REMOTE_DISCONNECTED) {
dev_dbg(wpriv->dev, "%s: remote channel: %s disconnected remotely\n",
@@ -278,10 +328,10 @@ static void wdsp_glink_notify_state(void *handle, const void *priv,
mutex_unlock(&ch->mutex);
/*
* If remote disconnect happens, local side also has
- * to close the channel and reopen again as per glink
+ * to close the channel as per glink design in a
+ * separate work_queue.
*/
- if (!wdsp_glink_close_ch(ch))
- wdsp_glink_open_ch(ch);
+ queue_work(wpriv->work_queue, &ch->lcl_ch_cls_wrk);
}
}
@@ -294,16 +344,23 @@ static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch)
struct wdsp_glink_priv *wpriv = ch->wpriv;
int ret = 0;
+ mutex_lock(&wpriv->glink_mutex);
+ if (ch->handle) {
+ ret = glink_close(ch->handle);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(wpriv->dev, "%s: glink_close is failed, ret = %d\n",
+ __func__, ret);
+ } else {
+ ch->handle = NULL;
+ dev_dbg(wpriv->dev, "%s: ch %s is closed\n", __func__,
+ ch->ch_cfg.name);
+ }
+ } else {
+ dev_dbg(wpriv->dev, "%s: ch %s is already closed\n", __func__,
+ ch->ch_cfg.name);
+ }
+ mutex_unlock(&wpriv->glink_mutex);
- mutex_lock(&ch->mutex);
-
- dev_dbg(wpriv->dev, "%s: ch %s closing\n", __func__, ch->ch_cfg.name);
- ret = glink_close(ch->handle);
- if (IS_ERR_VALUE(ret))
- dev_err(wpriv->dev, "%s: glink_close is failed, ret = %d\n",
- __func__, ret);
-
- mutex_unlock(&ch->mutex);
return ret;
}
@@ -318,29 +375,34 @@ static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch)
struct glink_open_config open_cfg;
int ret = 0;
- memset(&open_cfg, 0, sizeof(open_cfg));
- open_cfg.options = GLINK_OPT_INITIAL_XPORT;
- open_cfg.edge = WDSP_EDGE;
- open_cfg.notify_rx = wdsp_glink_notify_rx;
- open_cfg.notify_tx_done = wdsp_glink_notify_tx_done;
- open_cfg.notify_state = wdsp_glink_notify_state;
- open_cfg.notify_rx_intent_req = wdsp_glink_notify_rx_intent_req;
- open_cfg.priv = ch;
- open_cfg.name = ch->ch_cfg.name;
-
- dev_dbg(wpriv->dev, "%s: ch->ch_cfg.name = %s, latency_in_us = %d, intents = %d\n",
- __func__, ch->ch_cfg.name, ch->ch_cfg.latency_in_us,
- ch->ch_cfg.no_of_intents);
-
- mutex_lock(&ch->mutex);
- ch->handle = glink_open(&open_cfg);
- if (IS_ERR_OR_NULL(ch->handle)) {
- dev_err(wpriv->dev, "%s: glink_open failed %s\n",
- __func__, ch->ch_cfg.name);
- ch->handle = NULL;
- ret = -EINVAL;
+ mutex_lock(&wpriv->glink_mutex);
+ if (!ch->handle) {
+ memset(&open_cfg, 0, sizeof(open_cfg));
+ open_cfg.options = GLINK_OPT_INITIAL_XPORT;
+ open_cfg.edge = WDSP_EDGE;
+ open_cfg.notify_rx = wdsp_glink_notify_rx;
+ open_cfg.notify_tx_done = wdsp_glink_notify_tx_done;
+ open_cfg.notify_state = wdsp_glink_notify_state;
+ open_cfg.notify_rx_intent_req = wdsp_glink_notify_rx_intent_req;
+ open_cfg.priv = ch;
+ open_cfg.name = ch->ch_cfg.name;
+
+ dev_dbg(wpriv->dev, "%s: ch->ch_cfg.name = %s, latency_in_us = %d, intents = %d\n",
+ __func__, ch->ch_cfg.name, ch->ch_cfg.latency_in_us,
+ ch->ch_cfg.no_of_intents);
+
+ ch->handle = glink_open(&open_cfg);
+ if (IS_ERR_OR_NULL(ch->handle)) {
+ dev_err(wpriv->dev, "%s: glink_open failed for ch %s\n",
+ __func__, ch->ch_cfg.name);
+ ch->handle = NULL;
+ ret = -EINVAL;
+ }
+ } else {
+ dev_err(wpriv->dev, "%s: ch %s is already opened\n", __func__,
+ ch->ch_cfg.name);
}
- mutex_unlock(&ch->mutex);
+ mutex_unlock(&wpriv->glink_mutex);
return ret;
}
@@ -354,7 +416,7 @@ static void wdsp_glink_close_all_ch(struct wdsp_glink_priv *wpriv)
int i;
for (i = 0; i < wpriv->no_of_channels; i++)
- if (wpriv->ch[i])
+ if (wpriv->ch && wpriv->ch[i])
wdsp_glink_close_ch(wpriv->ch[i]);
}
@@ -425,7 +487,12 @@ static void wdsp_glink_link_state_cb(struct glink_link_state_cb_info *cb_info,
}
wpriv = (struct wdsp_glink_priv *)priv;
+
+ mutex_lock(&wpriv->glink_mutex);
wpriv->glink_state.link_state = cb_info->link_state;
+ wake_up(&wpriv->link_state_wait);
+ mutex_unlock(&wpriv->glink_mutex);
+
queue_work(wpriv->work_queue, &wpriv->ch_open_cls_wrk);
}
@@ -477,6 +544,9 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv,
mutex_init(&ch[i]->mutex);
ch[i]->wpriv = wpriv;
+ INIT_WORK(&ch[i]->lcl_ch_open_wrk, wdsp_glink_lcl_ch_open_wrk);
+ INIT_WORK(&ch[i]->lcl_ch_cls_wrk, wdsp_glink_lcl_ch_cls_wrk);
+ init_waitqueue_head(&ch[i]->ch_connect_wait);
}
wpriv->ch = ch;
wpriv->no_of_channels = no_of_channels;
@@ -540,15 +610,26 @@ static void wdsp_glink_tx_buf_work(struct work_struct *work)
ret = glink_tx(ch->handle, tx_buf,
cpkt->payload, cpkt->payload_size,
GLINK_TX_REQ_INTENT);
- if (IS_ERR_VALUE(ret))
+ if (IS_ERR_VALUE(ret)) {
dev_err(wpriv->dev, "%s: glink tx failed, ret = %d\n",
__func__, ret);
+ /*
+ * If glink_tx() is failed then free tx_buf here as
+ * there won't be any tx_done notification to
+ * free the buffer.
+ */
+ kfree(tx_buf);
+ }
} else {
dev_err(wpriv->dev, "%s: channel %s is not in connected state\n",
__func__, ch->ch_cfg.name);
+ /*
+ * Free tx_buf here as there won't be any tx_done
+ * notification in this case also.
+ */
+ kfree(tx_buf);
}
mutex_unlock(&tx_buf->ch->mutex);
-
}
/*
@@ -678,7 +759,32 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf,
__func__, ret);
kfree(tx_buf);
break;
+ case WDSP_READY_PKT:
+ ret = wait_event_timeout(wpriv->link_state_wait,
+ (wpriv->glink_state.link_state ==
+ GLINK_LINK_STATE_UP),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ dev_err(wpriv->dev, "%s: Link state wait timeout\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ goto free_buf;
+ }
+ ret = 0;
+ kfree(tx_buf);
+ break;
case WDSP_CMD_PKT:
+ mutex_lock(&wpriv->glink_mutex);
+ if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) {
+ mutex_unlock(&wpriv->glink_mutex);
+ dev_err(wpriv->dev, "%s: Link state is Down\n",
+ __func__);
+
+ ret = -ENETRESET;
+ goto free_buf;
+ }
+ mutex_unlock(&wpriv->glink_mutex);
+
cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
dev_dbg(wpriv->dev, "%s: requested ch_name: %s\n", __func__,
cpkt->ch_name);
@@ -696,6 +802,20 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf,
ret = -EINVAL;
goto free_buf;
}
+
+ ret = wait_event_timeout(tx_buf->ch->ch_connect_wait,
+ (tx_buf->ch->channel_state ==
+ GLINK_CONNECTED),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ dev_err(wpriv->dev, "%s: glink channel %s is not in connected state %d\n",
+ __func__, tx_buf->ch->ch_cfg.name,
+ tx_buf->ch->channel_state);
+ ret = -ETIMEDOUT;
+ goto free_buf;
+ }
+ ret = 0;
+
INIT_WORK(&tx_buf->tx_work, wdsp_glink_tx_buf_work);
queue_work(wpriv->work_queue, &tx_buf->tx_work);
break;
@@ -747,7 +867,9 @@ static int wdsp_glink_open(struct inode *inode, struct file *file)
}
init_completion(&wpriv->rsp_complete);
+ init_waitqueue_head(&wpriv->link_state_wait);
mutex_init(&wpriv->rsp_mutex);
+ mutex_init(&wpriv->glink_mutex);
file->private_data = wpriv;
goto done;
@@ -801,28 +923,39 @@ static int wdsp_glink_release(struct inode *inode, struct file *file)
goto done;
}
+ if (wpriv->glink_state.handle)
+ glink_unregister_link_state_cb(wpriv->glink_state.handle);
+
flush_workqueue(wpriv->work_queue);
+ destroy_workqueue(wpriv->work_queue);
+
/*
* Clean up glink channel memory in channel state
* callback only if close channels are called from here.
*/
if (wpriv->ch) {
- for (i = 0; i < wpriv->no_of_channels; i++)
- if (wpriv->ch[i])
+ for (i = 0; i < wpriv->no_of_channels; i++) {
+ if (wpriv->ch[i]) {
wpriv->ch[i]->free_mem = true;
+ /*
+ * Channel handle NULL means channel is already
+ * closed. Free the channel memory here itself.
+ */
+ if (!wpriv->ch[i]->handle) {
+ kfree(wpriv->ch[i]);
+ wpriv->ch[i] = NULL;
+ } else {
+ wdsp_glink_close_ch(wpriv->ch[i]);
+ }
+ }
+ }
- wdsp_glink_close_all_ch(wpriv);
kfree(wpriv->ch);
wpriv->ch = NULL;
}
- if (wpriv->glink_state.handle)
- glink_unregister_link_state_cb(wpriv->glink_state.handle);
-
+ mutex_destroy(&wpriv->glink_mutex);
mutex_destroy(&wpriv->rsp_mutex);
- if (wpriv->work_queue)
- destroy_workqueue(wpriv->work_queue);
-
kfree(wpriv);
file->private_data = NULL;
diff --git a/drivers/soc/qcom/wlan_firmware_service_v01.c b/drivers/soc/qcom/wlan_firmware_service_v01.c
index b40e0671c691..f5f7ae8c9901 100644
--- a/drivers/soc/qcom/wlan_firmware_service_v01.c
+++ b/drivers/soc/qcom/wlan_firmware_service_v01.c
@@ -343,6 +343,24 @@ struct elem_info wlfw_ind_register_req_msg_v01_ei[] = {
pin_connect_result_enable),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ client_id_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ client_id),
+ },
+ {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
.is_array = QMI_COMMON_TLV_TYPE,
@@ -361,6 +379,26 @@ struct elem_info wlfw_ind_register_resp_msg_v01_ei[] = {
.ei_array = get_qmi_response_type_v01_ei(),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct wlfw_ind_register_resp_msg_v01,
+ fw_status_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct wlfw_ind_register_resp_msg_v01,
+ fw_status),
+ },
+ {
.data_type = QMI_EOTI,
.is_array = NO_ARRAY,
.is_array = QMI_COMMON_TLV_TYPE,
diff --git a/drivers/soc/qcom/wlan_firmware_service_v01.h b/drivers/soc/qcom/wlan_firmware_service_v01.h
index b994762db7af..29bdfb23480a 100644
--- a/drivers/soc/qcom/wlan_firmware_service_v01.h
+++ b/drivers/soc/qcom/wlan_firmware_service_v01.h
@@ -97,6 +97,9 @@ enum wlfw_pipedir_enum_v01 {
#define QMI_WLFW_CE_ATTR_DISABLE_INTR_V01 ((uint32_t)0x08)
#define QMI_WLFW_CE_ATTR_ENABLE_POLL_V01 ((uint32_t)0x10)
+#define QMI_WLFW_ALREADY_REGISTERED_V01 ((uint64_t)0x01ULL)
+#define QMI_WLFW_FW_READY_V01 ((uint64_t)0x02ULL)
+
struct wlfw_ce_tgt_pipe_cfg_s_v01 {
uint32_t pipe_num;
enum wlfw_pipedir_enum_v01 pipe_dir;
@@ -151,14 +154,18 @@ struct wlfw_ind_register_req_msg_v01 {
uint8_t msa_ready_enable;
uint8_t pin_connect_result_enable_valid;
uint8_t pin_connect_result_enable;
+ uint8_t client_id_valid;
+ uint32_t client_id;
};
-#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 20
+#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 27
extern struct elem_info wlfw_ind_register_req_msg_v01_ei[];
struct wlfw_ind_register_resp_msg_v01 {
struct qmi_response_type_v01 resp;
+ uint8_t fw_status_valid;
+ uint64_t fw_status;
};
-#define WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN 7
+#define WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN 18
extern struct elem_info wlfw_ind_register_resp_msg_v01_ei[];
struct wlfw_fw_ready_ind_msg_v01 {
diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c
index 266091486bf1..424ad7c57b43 100644
--- a/drivers/soundwire/swr-wcd-ctrl.c
+++ b/drivers/soundwire/swr-wcd-ctrl.c
@@ -61,6 +61,10 @@ struct usecase uc[] = {
{6, 14, 6600}, /* UC11: 2*(Spkr + Comp + SB) */
{2, 3, 2700}, /* UC12: Spkr + SB */
{4, 6, 5400}, /* UC13: 2*(Spkr + SB) */
+ {3, 5, 3900}, /* UC14: Spkr + SB + VI */
+ {6, 10, 7800}, /* UC15: 2*(Spkr + SB + VI) */
+ {2, 3, 3600}, /* UC16: Spkr + VI */
+ {4, 6, 7200}, /* UC17: 2*(Spkr + VI) */
};
#define MAX_USECASE ARRAY_SIZE(uc)
@@ -148,6 +152,33 @@ struct port_params pp[MAX_USECASE][SWR_MSTR_PORT_LEN] = {
{7, 6, 0},
{63, 13, 31},
},
+ /* UC 14 */
+ {
+ {7, 1, 0},
+ {63, 12, 31},
+ {15, 7, 0},
+ },
+ /* UC 15 */
+ {
+ {7, 1, 0},
+ {63, 12, 31},
+ {15, 7, 0},
+ {7, 6, 0},
+ {63, 13, 31},
+ {15, 10, 0},
+ },
+ /* UC 16 */
+ {
+ {7, 1, 0},
+ {15, 7, 0},
+ },
+ /* UC 17 */
+ {
+ {7, 1, 0},
+ {15, 7, 0},
+ {7, 6, 0},
+ {15, 10, 0},
+ },
};
enum {
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 cd420c429031..176f22ba570c 100644
--- a/drivers/staging/android/ion/msm/msm_ion.c
+++ b/drivers/staging/android/ion/msm/msm_ion.c
@@ -632,7 +632,8 @@ bool is_secure_vmid_valid(int vmid)
vmid == VMID_CP_NON_PIXEL ||
vmid == VMID_CP_CAMERA ||
vmid == VMID_CP_SEC_DISPLAY ||
- vmid == VMID_CP_APP);
+ vmid == VMID_CP_APP ||
+ vmid == VMID_CP_CAMERA_PREVIEW);
}
int get_secure_vmid(unsigned long flags)
@@ -651,6 +652,8 @@ int get_secure_vmid(unsigned long flags)
return VMID_CP_SEC_DISPLAY;
if (flags & ION_FLAG_CP_APP)
return VMID_CP_APP;
+ if (flags & ION_FLAG_CP_CAMERA_PREVIEW)
+ return VMID_CP_CAMERA_PREVIEW;
return -EINVAL;
}
/* fix up the cases where the ioctl direction bits are incorrect */
@@ -708,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;
}
@@ -721,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/staging/android/uapi/msm_ion.h b/drivers/staging/android/uapi/msm_ion.h
index b3c29826834e..54a4672ff143 100644
--- a/drivers/staging/android/uapi/msm_ion.h
+++ b/drivers/staging/android/uapi/msm_ion.h
@@ -3,6 +3,8 @@
#include "ion.h"
+#define ION_BIT(nr) (1UL << (nr))
+
enum msm_ion_heap_types {
ION_HEAP_TYPE_MSM_START = ION_HEAP_TYPE_CUSTOM + 1,
ION_HEAP_TYPE_SECURE_DMA = ION_HEAP_TYPE_MSM_START,
@@ -76,39 +78,41 @@ enum cp_mem_usage {
* Flags to be used when allocating from the secure heap for
* content protection
*/
-#define ION_FLAG_CP_TOUCH (1 << 17)
-#define ION_FLAG_CP_BITSTREAM (1 << 18)
-#define ION_FLAG_CP_PIXEL (1 << 19)
-#define ION_FLAG_CP_NON_PIXEL (1 << 20)
-#define ION_FLAG_CP_CAMERA (1 << 21)
-#define ION_FLAG_CP_HLOS (1 << 22)
-#define ION_FLAG_CP_HLOS_FREE (1 << 23)
-#define ION_FLAG_CP_SEC_DISPLAY (1 << 25)
-#define ION_FLAG_CP_APP (1 << 26)
+#define ION_FLAG_CP_TOUCH ION_BIT(17)
+#define ION_FLAG_CP_BITSTREAM ION_BIT(18)
+#define ION_FLAG_CP_PIXEL ION_BIT(19)
+#define ION_FLAG_CP_NON_PIXEL ION_BIT(20)
+#define ION_FLAG_CP_CAMERA ION_BIT(21)
+#define ION_FLAG_CP_HLOS ION_BIT(22)
+#define ION_FLAG_CP_HLOS_FREE ION_BIT(23)
+#define ION_FLAG_CP_SEC_DISPLAY ION_BIT(25)
+#define ION_FLAG_CP_APP ION_BIT(26)
+#define ION_FLAG_CP_CAMERA_PREVIEW ION_BIT(27)
+
/**
* Flag to allow non continguous allocation of memory from secure
* heap
*/
-#define ION_FLAG_ALLOW_NON_CONTIG (1 << 24)
+#define ION_FLAG_ALLOW_NON_CONTIG ION_BIT(24)
/**
* Flag to use when allocating to indicate that a heap is secure.
*/
-#define ION_FLAG_SECURE (1 << ION_HEAP_ID_RESERVED)
+#define ION_FLAG_SECURE ION_BIT(ION_HEAP_ID_RESERVED)
/**
* Flag for clients to force contiguous memort allocation
*
* Use of this flag is carefully monitored!
*/
-#define ION_FLAG_FORCE_CONTIGUOUS (1 << 30)
+#define ION_FLAG_FORCE_CONTIGUOUS ION_BIT(30)
/*
* Used in conjunction with heap which pool memory to force an allocation
* to come from the page allocator directly instead of from the pool allocation
*/
-#define ION_FLAG_POOL_FORCE_ALLOC (1 << 16)
+#define ION_FLAG_POOL_FORCE_ALLOC ION_BIT(16)
/**
* Deprecated! Please use the corresponding ION_FLAG_*
@@ -119,7 +123,7 @@ enum cp_mem_usage {
/**
* Macro should be used with ion_heap_ids defined above.
*/
-#define ION_HEAP(bit) (1 << (bit))
+#define ION_HEAP(bit) ION_BIT(bit)
#define ION_ADSP_HEAP_NAME "adsp"
#define ION_SYSTEM_HEAP_NAME "system"
diff --git a/drivers/thermal/lmh_lite.c b/drivers/thermal/lmh_lite.c
index bd456d25b124..32a573d22270 100644
--- a/drivers/thermal/lmh_lite.c
+++ b/drivers/thermal/lmh_lite.c
@@ -640,7 +640,7 @@ sens_exit:
static int lmh_get_sensor_list(void)
{
- int ret = 0;
+ int ret = 0, buf_size = 0;
uint32_t size = 0, next = 0, idx = 0, count = 0;
struct scm_desc desc_arg;
struct lmh_sensor_packet *payload = NULL;
@@ -649,12 +649,13 @@ static int lmh_get_sensor_list(void)
uint32_t size;
} cmd_buf;
- payload = kzalloc(sizeof(*payload), GFP_KERNEL);
+ buf_size = PAGE_ALIGN(sizeof(*payload));
+ payload = kzalloc(buf_size, GFP_KERNEL);
if (!payload)
return -ENOMEM;
do {
- memset(payload, 0, sizeof(*payload));
+ memset(payload, 0, buf_size);
payload->count = next;
cmd_buf.addr = SCM_BUFFER_PHYS(payload);
/* payload_phys may be a physical address > 4 GB */
@@ -663,7 +664,7 @@ static int lmh_get_sensor_list(void)
lmh_sensor_packet);
desc_arg.arginfo = SCM_ARGS(2, SCM_RW, SCM_VAL);
trace_lmh_event_call("GET_SENSORS enter");
- dmac_flush_range(payload, payload + sizeof(*payload));
+ dmac_flush_range(payload, payload + buf_size);
if (!is_scm_armv8())
ret = scm_call(SCM_SVC_LMH, LMH_GET_SENSORS,
(void *) &cmd_buf,
@@ -881,7 +882,8 @@ static int lmh_debug_read(struct lmh_debug_ops *ops, uint32_t **buf)
if (curr_size != size) {
if (payload)
devm_kfree(lmh_data->dev, payload);
- payload = devm_kzalloc(lmh_data->dev, size, GFP_KERNEL);
+ payload = devm_kzalloc(lmh_data->dev, PAGE_ALIGN(size),
+ GFP_KERNEL);
if (!payload) {
pr_err("payload buffer alloc failed\n");
ret = -ENOMEM;
@@ -948,7 +950,8 @@ static int lmh_debug_config_write(uint32_t cmd_id, uint32_t *buf, int size)
trace_lmh_debug_data("Config LMH", buf, size);
size_bytes = (size - 3) * sizeof(uint32_t);
- payload = devm_kzalloc(lmh_data->dev, size_bytes, GFP_KERNEL);
+ payload = devm_kzalloc(lmh_data->dev, PAGE_ALIGN(size_bytes),
+ GFP_KERNEL);
if (!payload) {
ret = -ENOMEM;
goto set_cfg_exit;
diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c
index 97ab02dfc753..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,
+ },
{}
};
@@ -1066,68 +1069,68 @@ static int tsens_get_sw_id_mapping_for_controller(
return 0;
}
-int tsens_get_hw_id_mapping(int sensor_sw_id, int *sensor_client_id)
+int tsens_get_hw_id_mapping(int thermal_sensor_num, int *sensor_client_id)
{
- int i = 0;
- bool id_found = false;
struct tsens_tm_device *tmdev = NULL;
struct device_node *of_node = NULL;
const struct of_device_id *id;
+ uint32_t tsens_max_sensors = 0, idx = 0, i = 0;
- tmdev = get_tsens_controller_for_client_id(sensor_sw_id);
- if (tmdev == NULL) {
- pr_debug("TSENS early init not done\n");
+ if (list_empty(&tsens_device_list)) {
+ pr_err("%s: TSENS controller not available\n", __func__);
return -EPROBE_DEFER;
}
- of_node = tmdev->pdev->dev.of_node;
- if (of_node == NULL) {
- pr_err("Invalid of_node??\n");
+ list_for_each_entry(tmdev, &tsens_device_list, list)
+ tsens_max_sensors += tmdev->tsens_num_sensor;
+
+ if (tsens_max_sensors != thermal_sensor_num) {
+ pr_err("TSENS total sensors is %d, thermal expects:%d\n",
+ tsens_max_sensors, thermal_sensor_num);
return -EINVAL;
}
- if (!of_match_node(tsens_match, of_node)) {
- pr_err("Need to read SoC specific fuse map\n");
- return -ENODEV;
- }
+ list_for_each_entry(tmdev, &tsens_device_list, list) {
+ of_node = tmdev->pdev->dev.of_node;
+ if (of_node == NULL) {
+ pr_err("Invalid of_node??\n");
+ return -EINVAL;
+ }
- id = of_match_node(tsens_match, of_node);
- if (id == NULL) {
- pr_err("can not find tsens_match of_node\n");
- return -ENODEV;
- }
+ if (!of_match_node(tsens_match, of_node)) {
+ pr_err("Need to read SoC specific fuse map\n");
+ return -ENODEV;
+ }
- if (!strcmp(id->compatible, "qcom,msm8996-tsens") ||
- (!strcmp(id->compatible, "qcom,msmcobalt-tsens")) ||
- (!strcmp(id->compatible, "qcom,msmhamster-tsens"))) {
- /* Assign a client id which will be used to get the
- * controller and hw_sensor details
- */
- while (i < tmdev->tsens_num_sensor && !id_found) {
- if (sensor_sw_id == tmdev->sensor[i].sensor_client_id) {
- *sensor_client_id =
+ id = of_match_node(tsens_match, of_node);
+ if (id == NULL) {
+ pr_err("can not find tsens_match of_node\n");
+ return -ENODEV;
+ }
+
+ if (!strcmp(id->compatible, "qcom,msm8996-tsens") ||
+ (!strcmp(id->compatible, "qcom,msmcobalt-tsens")) ||
+ (!strcmp(id->compatible, "qcom,msmhamster-tsens"))) {
+ /* Assign client id's that is used to get the
+ * controller and hw_sensor details
+ */
+ for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+ sensor_client_id[idx] =
tmdev->sensor[i].sensor_client_id;
- id_found = true;
+ idx++;
}
- i++;
- }
- } else {
- /* Assign the corresponding hw sensor number which is done
- * prior to support for multiple controllres
- */
- while (i < tmdev->tsens_num_sensor && !id_found) {
- if (sensor_sw_id == tmdev->sensor[i].sensor_client_id) {
- *sensor_client_id =
+ } else {
+ /* Assign the corresponding hw sensor number
+ * prior to support for multiple controllres
+ */
+ for (i = 0; i < tmdev->tsens_num_sensor; i++) {
+ sensor_client_id[idx] =
tmdev->sensor[i].sensor_hw_num;
- id_found = true;
+ idx++;
}
- i++;
}
}
- if (!id_found)
- return -EINVAL;
-
return 0;
}
EXPORT_SYMBOL(tsens_get_hw_id_mapping);
@@ -1383,7 +1386,7 @@ static int msm_tsens_get_temp(int sensor_client_id, int *temp)
}
if ((!tmdev->prev_reading_avail) && !tmdev->tsens_valid_status_check) {
- while (!((readl_relaxed(trdy_addr)) & TSENS_TRDY_MASK))
+ while (!((readl_relaxed_no_log(trdy_addr)) & TSENS_TRDY_MASK))
usleep_range(TSENS_TRDY_RDY_MIN_TIME,
TSENS_TRDY_RDY_MAX_TIME);
tmdev->prev_reading_avail = true;
@@ -1394,7 +1397,7 @@ static int msm_tsens_get_temp(int sensor_client_id, int *temp)
else
last_temp_mask = TSENS_SN_STATUS_TEMP_MASK;
- code = readl_relaxed(sensor_addr +
+ code = readl_relaxed_no_log(sensor_addr +
(sensor_hw_num << TSENS_STATUS_ADDR_OFFSET));
last_temp = code & last_temp_mask;
@@ -1406,14 +1409,14 @@ static int msm_tsens_get_temp(int sensor_client_id, int *temp)
if (code & valid_status_mask)
last_temp_valid = true;
else {
- code = readl_relaxed(sensor_addr +
+ code = readl_relaxed_no_log(sensor_addr +
(sensor_hw_num << TSENS_STATUS_ADDR_OFFSET));
last_temp2 = code & last_temp_mask;
if (code & valid_status_mask) {
last_temp = last_temp2;
last_temp2_valid = true;
} else {
- code = readl_relaxed(sensor_addr +
+ code = readl_relaxed_no_log(sensor_addr +
(sensor_hw_num <<
TSENS_STATUS_ADDR_OFFSET));
last_temp3 = code & last_temp_mask;
@@ -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");
@@ -5700,8 +5706,6 @@ static void tsens_debugfs_init(void)
}
}
-int tsens_sensor_sw_idx = 0;
-
static int tsens_thermal_zone_register(struct tsens_tm_device *tmdev)
{
int rc = 0, i = 0;
@@ -5715,7 +5719,7 @@ static int tsens_thermal_zone_register(struct tsens_tm_device *tmdev)
char name[18];
snprintf(name, sizeof(name), "tsens_tz_sensor%d",
- tsens_sensor_sw_idx);
+ tmdev->sensor[i].sensor_client_id);
tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED;
tmdev->sensor[i].tm = tmdev;
if (tmdev->tsens_type == TSENS_TYPE3) {
@@ -5741,7 +5745,6 @@ static int tsens_thermal_zone_register(struct tsens_tm_device *tmdev)
goto fail;
}
}
- tsens_sensor_sw_idx++;
}
if (tmdev->tsens_type == TSENS_TYPE3) {
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index ff40d6fad922..17ecd61e9ee6 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -2460,7 +2460,6 @@ fail:
static int create_sensor_id_map(struct device *dev)
{
- int i = 0;
int ret = 0;
tsens_id_map = devm_kzalloc(dev,
@@ -2471,19 +2470,10 @@ static int create_sensor_id_map(struct device *dev)
return -ENOMEM;
}
- for (i = 0; i < max_tsens_num; i++) {
- ret = tsens_get_hw_id_mapping(i, &tsens_id_map[i]);
- /* If return -ENXIO, hw_id is default in sequence */
- if (ret) {
- if (ret == -ENXIO) {
- tsens_id_map[i] = i;
- ret = 0;
- } else {
- pr_err("Failed to get hw id for id:%d.err:%d\n",
- i, ret);
- goto fail;
- }
- }
+ ret = tsens_get_hw_id_mapping(max_tsens_num, tsens_id_map);
+ if (ret) {
+ pr_err("Failed to get tsens id's:%d\n", ret);
+ goto fail;
}
return ret;
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
index 84ab45fde4ae..c78406cb3325 100644
--- a/drivers/thermal/qpnp-adc-tm.c
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -492,7 +492,7 @@ static int32_t qpnp_adc_tm_rc_check_channel_en(struct qpnp_adc_tm_chip *chip)
{
u8 adc_tm_ctl = 0, status_low = 0, status_high = 0;
int rc = 0, i = 0;
- bool ldo_en;
+ bool ldo_en = false;
for (i = 0; i < chip->max_channels_available; i++) {
rc = qpnp_adc_tm_read_reg(chip, QPNP_BTM_Mn_CTL(i),
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 51dce6d43890..fa3c9e511663 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -2213,12 +2213,12 @@ void enable_wakeup_interrupt(struct msm_hs_port *msm_uport)
return;
if (!(msm_uport->wakeup.enabled)) {
- enable_irq(msm_uport->wakeup.irq);
- disable_irq(uport->irq);
spin_lock_irqsave(&uport->lock, flags);
msm_uport->wakeup.ignore = 1;
msm_uport->wakeup.enabled = true;
spin_unlock_irqrestore(&uport->lock, flags);
+ disable_irq(uport->irq);
+ enable_irq(msm_uport->wakeup.irq);
} else {
MSM_HS_WARN("%s:Wake up IRQ already enabled", __func__);
}
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 5381a728d23e..e5139402e7f8 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -133,6 +133,12 @@ static void sysrq_handle_crash(int key)
{
char *killer = NULL;
+ /* we need to release the RCU read lock here,
+ * otherwise we get an annoying
+ * 'BUG: sleeping function called from invalid context'
+ * complaint from the kernel before the panic.
+ */
+ rcu_read_unlock();
panic_on_oops = 1; /* force panic */
wmb();
*killer = 1;
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 3df80c73b74a..ac0eb0939ecf 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2990,6 +2990,9 @@ void usb_remove_hcd(struct usb_hcd *hcd)
cancel_work_sync(&hcd->wakeup_work);
#endif
+ /* handle any pending hub events before XHCI stops */
+ usb_flush_hub_wq();
+
mutex_lock(&usb_bus_list_lock);
usb_disconnect(&rhdev); /* Sets rhdev to NULL */
mutex_unlock(&usb_bus_list_lock);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 84df093639ac..269c1ee2da44 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -610,6 +610,12 @@ void usb_kick_hub_wq(struct usb_device *hdev)
kick_hub_wq(hub);
}
+void usb_flush_hub_wq(void)
+{
+ flush_workqueue(hub_wq);
+}
+EXPORT_SYMBOL(usb_flush_hub_wq);
+
/*
* Let the USB core know that a USB 3.0 device has sent a Function Wake Device
* Notification, which indicates it had initiated remote wakeup.
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 7585c603cb3d..07867ead2413 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -123,6 +123,9 @@ static int dwc3_init_usb_phys(struct dwc3 *dwc)
return ret;
}
+ if (dwc->maximum_speed == USB_SPEED_HIGH)
+ goto generic_phy_init;
+
ret = usb_phy_init(dwc->usb3_phy);
if (ret == -EBUSY) {
/*
@@ -135,6 +138,8 @@ static int dwc3_init_usb_phys(struct dwc3 *dwc)
__func__, ret);
return ret;
}
+
+generic_phy_init:
ret = phy_init(dwc->usb2_generic_phy);
if (ret < 0)
return ret;
@@ -155,10 +160,13 @@ static int dwc3_init_usb_phys(struct dwc3 *dwc)
static int dwc3_core_reset(struct dwc3 *dwc)
{
int ret;
+ u32 reg;
/* Reset PHYs */
usb_phy_reset(dwc->usb2_phy);
- usb_phy_reset(dwc->usb3_phy);
+
+ if (dwc->maximum_speed == USB_SPEED_SUPER)
+ usb_phy_reset(dwc->usb3_phy);
/* Initialize PHYs */
ret = dwc3_init_usb_phys(dwc);
@@ -168,6 +176,10 @@ static int dwc3_core_reset(struct dwc3 *dwc)
return ret;
}
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg &= ~DWC3_GUSB3PIPECTL_DELAYP1TRANS;
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+
dwc3_notify_event(dwc, DWC3_CONTROLLER_RESET_EVENT);
dwc3_notify_event(dwc, DWC3_CONTROLLER_POST_RESET_EVENT);
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 83a265f0211e..08006d84fb38 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1249,6 +1249,7 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep,
struct usb_gsi_request *request;
struct gsi_channel_info *ch_info;
bool block_db, f_suspend;
+ unsigned long flags;
switch (op) {
case GSI_EP_OP_PREPARE_TRBS:
@@ -1263,11 +1264,15 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep,
case GSI_EP_OP_CONFIG:
request = (struct usb_gsi_request *)op_data;
dev_dbg(mdwc->dev, "EP_OP_CONFIG for %s\n", ep->name);
+ spin_lock_irqsave(&dwc->lock, flags);
gsi_configure_ep(ep, request);
+ spin_unlock_irqrestore(&dwc->lock, flags);
break;
case GSI_EP_OP_STARTXFER:
dev_dbg(mdwc->dev, "EP_OP_STARTXFER for %s\n", ep->name);
+ spin_lock_irqsave(&dwc->lock, flags);
ret = gsi_startxfer_for_ep(ep);
+ spin_unlock_irqrestore(&dwc->lock, flags);
break;
case GSI_EP_OP_GET_XFER_IDX:
dev_dbg(mdwc->dev, "EP_OP_GET_XFER_IDX for %s\n", ep->name);
@@ -1293,12 +1298,16 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep,
case GSI_EP_OP_UPDATEXFER:
request = (struct usb_gsi_request *)op_data;
dev_dbg(mdwc->dev, "EP_OP_UPDATEXFER\n");
+ spin_lock_irqsave(&dwc->lock, flags);
ret = gsi_updatexfer_for_ep(ep, request);
+ spin_unlock_irqrestore(&dwc->lock, flags);
break;
case GSI_EP_OP_ENDXFER:
request = (struct usb_gsi_request *)op_data;
dev_dbg(mdwc->dev, "EP_OP_ENDXFER for %s\n", ep->name);
+ spin_lock_irqsave(&dwc->lock, flags);
gsi_endxfer_for_ep(ep);
+ spin_unlock_irqrestore(&dwc->lock, flags);
break;
case GSI_EP_OP_SET_CLR_BLOCK_DBL:
block_db = *((bool *)op_data);
@@ -1923,7 +1932,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
usb_phy_set_suspend(mdwc->hs_phy, 1);
/* Suspend SS PHY */
- if (can_suspend_ssphy) {
+ if (dwc->maximum_speed == USB_SPEED_SUPER && can_suspend_ssphy) {
/* indicate phy about SS mode */
if (dwc3_msm_is_superspeed(mdwc))
mdwc->ss_phy->flags |= DEVICE_IN_SS_MODE;
@@ -2068,7 +2077,8 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
clk_prepare_enable(mdwc->bus_aggr_clk);
/* Resume SS PHY */
- if (mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND) {
+ if (dwc->maximum_speed == USB_SPEED_SUPER &&
+ mdwc->lpm_flags & MDWC3_SS_PHY_SUSPEND) {
mdwc->ss_phy->flags &= ~(PHY_LANE_A | PHY_LANE_B);
if (mdwc->typec_orientation == ORIENTATION_CC1)
mdwc->ss_phy->flags |= PHY_LANE_A;
@@ -2365,36 +2375,21 @@ static int dwc3_msm_get_clk_gdsc(struct dwc3_msm *mdwc)
(u32 *)&mdwc->core_clk_rate)) {
mdwc->core_clk_rate = clk_round_rate(mdwc->core_clk,
mdwc->core_clk_rate);
- } else {
- /*
- * Get Max supported clk frequency for USB Core CLK and request
- * to set the same.
- */
- mdwc->core_clk_rate = clk_round_rate(mdwc->core_clk, LONG_MAX);
}
+ dev_dbg(mdwc->dev, "USB core frequency = %ld\n",
+ mdwc->core_clk_rate);
+ ret = clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate);
+ if (ret)
+ dev_err(mdwc->dev, "fail to set core_clk freq:%d\n", ret);
+
+
mdwc->core_reset = devm_reset_control_get(mdwc->dev, "core_reset");
if (IS_ERR(mdwc->core_reset)) {
dev_err(mdwc->dev, "failed to get core_reset\n");
return PTR_ERR(mdwc->core_reset);
}
- /*
- * Get Max supported clk frequency for USB Core CLK and request
- * to set the same.
- */
- mdwc->core_clk_rate = clk_round_rate(mdwc->core_clk, LONG_MAX);
- if (IS_ERR_VALUE(mdwc->core_clk_rate)) {
- dev_err(mdwc->dev, "fail to get core clk max freq.\n");
- } else {
- dev_dbg(mdwc->dev, "USB core frequency = %ld\n",
- mdwc->core_clk_rate);
- ret = clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate);
- if (ret)
- dev_err(mdwc->dev, "fail to set core_clk freq:%d\n",
- ret);
- }
-
mdwc->sleep_clk = devm_clk_get(mdwc->dev, "sleep_clk");
if (IS_ERR(mdwc->sleep_clk)) {
dev_err(mdwc->dev, "failed to get sleep_clk\n");
@@ -2437,9 +2432,11 @@ static int dwc3_msm_id_notifier(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct dwc3_msm *mdwc = container_of(nb, struct dwc3_msm, id_nb);
+ struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
struct extcon_dev *edev = ptr;
enum dwc3_id_state id;
int cc_state;
+ int speed;
if (!edev) {
dev_err(mdwc->dev, "%s: edev null\n", __func__);
@@ -2459,6 +2456,9 @@ static int dwc3_msm_id_notifier(struct notifier_block *nb,
dbg_event(0xFF, "cc_state", mdwc->typec_orientation);
+ speed = extcon_get_cable_state_(edev, EXTCON_USB_SPEED);
+ dwc->maximum_speed = (speed == 0) ? USB_SPEED_HIGH : USB_SPEED_SUPER;
+
if (mdwc->id_state != id) {
mdwc->id_state = id;
dbg_event(0xFF, "id_state", mdwc->id_state);
@@ -2476,6 +2476,7 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
struct extcon_dev *edev = ptr;
int cc_state;
+ int speed;
if (!edev) {
dev_err(mdwc->dev, "%s: edev null\n", __func__);
@@ -2496,6 +2497,9 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
dbg_event(0xFF, "cc_state", mdwc->typec_orientation);
+ speed = extcon_get_cable_state_(edev, EXTCON_USB_SPEED);
+ dwc->maximum_speed = (speed == 0) ? USB_SPEED_HIGH : USB_SPEED_SUPER;
+
mdwc->vbus_active = event;
if (dwc->is_drd && !mdwc->in_restart) {
dbg_event(0xFF, "Q RW (vbus)", mdwc->vbus_active);
@@ -3041,11 +3045,13 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
if (on) {
dev_dbg(mdwc->dev, "%s: turn on host\n", __func__);
+ mdwc->hs_phy->flags |= PHY_HOST_MODE;
+ if (dwc->maximum_speed == USB_SPEED_SUPER)
+ mdwc->ss_phy->flags |= PHY_HOST_MODE;
+
pm_runtime_get_sync(mdwc->dev);
dbg_event(0xFF, "StrtHost gync",
atomic_read(&mdwc->dev->power.usage_count));
- mdwc->hs_phy->flags |= PHY_HOST_MODE;
- mdwc->ss_phy->flags |= PHY_HOST_MODE;
usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH);
if (!IS_ERR(mdwc->vbus_reg))
ret = regulator_enable(mdwc->vbus_reg);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a798c4fa8812..4ad994972b19 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1233,20 +1233,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
list_add_tail(&req->list, &dep->request_list);
/*
- * If there are no pending requests and the endpoint isn't already
- * busy, we will just start the request straight away.
- *
- * This will save one IRQ (XFER_NOT_READY) and possibly make it a
- * little bit faster.
- */
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- !usb_endpoint_xfer_int(dep->endpoint.desc) &&
- !(dep->flags & DWC3_EP_BUSY)) {
- ret = __dwc3_gadget_kick_transfer(dep, 0, true);
- goto out;
- }
-
- /*
* There are a few special cases:
*
* 1. XferNotReady with empty list of requests. We need to kick the
@@ -2529,14 +2515,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
dwc->u1u2 = 0;
}
-
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- int ret;
-
- ret = __dwc3_gadget_kick_transfer(dep, 0, is_xfer_complete);
- if (!ret || ret == -EBUSY)
- return;
- }
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
@@ -2585,16 +2563,23 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dwc3_gadget_start_isoc(dwc, dep, event);
} else {
- int active;
int ret;
- active = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
-
dwc3_trace(trace_dwc3_gadget, "%s: reason %s",
- dep->name, active ? "Transfer Active"
+ dep->name, event->status &
+ DEPEVT_STATUS_TRANSFER_ACTIVE
+ ? "Transfer Active"
: "Transfer Not Active");
- ret = __dwc3_gadget_kick_transfer(dep, 0, !active);
+ /*
+ * If XFERNOTREADY interrupt is received with event
+ * status as TRANSFER ACTIVE, don't kick next transfer.
+ * otherwise data stall is seen on that endpoint.
+ */
+ if (event->status & DEPEVT_STATUS_TRANSFER_ACTIVE)
+ return;
+
+ ret = __dwc3_gadget_kick_transfer(dep, 0, 1);
if (!ret || ret == -EBUSY)
return;
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/composite.c b/drivers/usb/gadget/composite.c
index a99405261306..a480b0a9a238 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -35,9 +35,10 @@
(speed == USB_SPEED_SUPER ?\
SSUSB_GADGET_VBUS_DRAW : CONFIG_USB_GADGET_VBUS_DRAW)
-static bool enable_l1_for_hs;
-module_param(enable_l1_for_hs, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(enable_l1_for_hs, "Enable support for L1 LPM for HS devices");
+static bool disable_l1_for_hs;
+module_param(disable_l1_for_hs, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(disable_l1_for_hs,
+ "Disable support for L1 LPM for HS devices");
/**
* struct usb_os_string - represents OS String to be reported by a gadget
@@ -1637,13 +1638,12 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (gadget->speed >= USB_SPEED_SUPER) {
cdev->desc.bcdUSB = cpu_to_le16(0x0310);
cdev->desc.bMaxPacketSize0 = 9;
- } else if (gadget->l1_supported ||
- enable_l1_for_hs) {
+ } else if (!disable_l1_for_hs) {
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
DBG(cdev,
"Config HS device with LPM(L1)\n");
}
- } else if (gadget->l1_supported) {
+ } else if (!disable_l1_for_hs) {
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
DBG(cdev, "Config HS device with LPM(L1)\n");
}
@@ -1678,7 +1678,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
case USB_DT_BOS:
if ((gadget_is_superspeed(gadget) &&
(gadget->speed >= USB_SPEED_SUPER))
- || gadget->l1_supported) {
+ || !disable_l1_for_hs) {
value = bos_desc(cdev);
value = min(w_length, (u16) value);
}
@@ -1963,6 +1963,8 @@ unknown:
break;
case USB_RECIP_ENDPOINT:
+ if (!cdev->config)
+ break;
endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
list_for_each_entry(f, &cdev->config->functions, list) {
if (test_bit(endp, f->endpoints))
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_cdev.c b/drivers/usb/gadget/function/f_cdev.c
index e1302108a917..3b7b23cfde44 100644
--- a/drivers/usb/gadget/function/f_cdev.c
+++ b/drivers/usb/gadget/function/f_cdev.c
@@ -50,6 +50,9 @@
#define MODULE_NAME "msm_usb_bridge"
#define NUM_INSTANCE 2
+#define MAX_CDEV_INST_NAME 15
+#define MAX_CDEV_FUNC_NAME 5
+
#define BRIDGE_RX_QUEUE_SIZE 8
#define BRIDGE_RX_BUF_SIZE 2048
#define BRIDGE_TX_QUEUE_SIZE 8
@@ -823,6 +826,7 @@ static void cser_free_inst(struct usb_function_instance *fi)
device_destroy(fcdev_classp, MKDEV(major, opts->port->minor));
cdev_del(&opts->port->fcdev_cdev);
usb_cser_chardev_deinit();
+ kfree(opts->func_name);
kfree(opts->port);
kfree(opts);
}
@@ -1647,16 +1651,6 @@ static struct configfs_item_operations cserial_item_ops = {
.release = cserial_attr_release,
};
-static ssize_t usb_cser_port_num_show(struct config_item *item, char *page)
-{
- return sprintf(page, "%u\n", to_f_cdev_opts(item)->port_num);
-}
-
-static ssize_t usb_cser_func_name_show(struct config_item *item, char *page)
-{
- return sprintf(page, "%s\n", to_f_cdev_opts(item)->func_name);
-}
-
static ssize_t usb_cser_status_show(struct config_item *item, char *page)
{
struct f_cdev *port = to_f_cdev_opts(item)->port;
@@ -1722,12 +1716,8 @@ static ssize_t usb_cser_status_store(struct config_item *item,
return len;
}
-CONFIGFS_ATTR_RO(usb_cser_, port_num);
-CONFIGFS_ATTR_RO(usb_cser_, func_name);
CONFIGFS_ATTR(usb_cser_, status);
static struct configfs_attribute *cserial_attrs[] = {
- &usb_cser_attr_port_num,
- &usb_cser_attr_func_name,
&usb_cser_attr_status,
NULL,
};
@@ -1748,7 +1738,7 @@ static int cser_set_inst_name(struct usb_function_instance *f, const char *name)
struct f_cdev *port;
name_len = strlen(name) + 1;
- if (name_len > 15)
+ if (name_len > MAX_CDEV_INST_NAME)
return -ENAMETOOLONG;
/* expect name as cdev.<func>.<port_num> */
@@ -1760,6 +1750,9 @@ static int cser_set_inst_name(struct usb_function_instance *f, const char *name)
/* get function name */
str_size = name_len - strlen(str);
+ if (str_size > MAX_CDEV_FUNC_NAME)
+ return -ENAMETOOLONG;
+
ptr = kstrndup(name, str_size - 1, GFP_KERNEL);
if (!ptr) {
pr_err("error:%ld\n", PTR_ERR(ptr));
@@ -1770,6 +1763,10 @@ static int cser_set_inst_name(struct usb_function_instance *f, const char *name)
/* get port number */
str = strrchr(name, '.');
+ if (!str) {
+ pr_err("err: port number not found\n");
+ return -EINVAL;
+ }
pr_debug("str:%s\n", str);
*str = '\0';
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index c5fd3ce3ed9a..fd2157c8e8c2 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -2275,7 +2275,7 @@ static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
return -EINVAL;
}
- ffs_log("exit: size of desc %lu", sizeof(*desc));
+ ffs_log("exit: size of desc %zu", sizeof(*desc));
return sizeof(*desc);
}
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
index d56012779c78..f058ab4cedaa 100644
--- a/drivers/usb/gadget/function/f_gsi.h
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -35,9 +35,9 @@
#define GSI_CTRL_DTR (1 << 0)
-#define GSI_NUM_IN_BUFFERS 7
+#define GSI_NUM_IN_BUFFERS 15
#define GSI_IN_BUFF_SIZE 2048
-#define GSI_NUM_OUT_BUFFERS 7
+#define GSI_NUM_OUT_BUFFERS 15
#define GSI_ECM_NUM_OUT_BUFFERS 31
#define GSI_OUT_AGGR_SIZE 24576
@@ -461,7 +461,7 @@ static struct usb_gadget_strings *rmnet_gsi_strings[] = {
/* rndis device descriptors */
-/* interface descriptor: */
+/* interface descriptor: Supports "Wireless" RNDIS; auto-detected by Windows*/
static struct usb_interface_descriptor rndis_gsi_control_intf = {
.bLength = sizeof(rndis_gsi_control_intf),
.bDescriptorType = USB_DT_INTERFACE,
@@ -469,9 +469,9 @@ static struct usb_interface_descriptor rndis_gsi_control_intf = {
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_COMM,
- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
- .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+ .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bInterfaceSubClass = 0x01,
+ .bInterfaceProtocol = 0x03,
/* .iInterface = DYNAMIC */
};
@@ -522,15 +522,16 @@ static struct usb_interface_descriptor rndis_gsi_data_intf = {
/* .iInterface = DYNAMIC */
};
+/* Supports "Wireless" RNDIS; auto-detected by Windows */
static struct usb_interface_assoc_descriptor
rndis_gsi_iad_descriptor = {
.bLength = sizeof(rndis_gsi_iad_descriptor),
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
.bFirstInterface = 0, /* XXX, hardcoded */
.bInterfaceCount = 2, /* control + data */
- .bFunctionClass = USB_CLASS_COMM,
- .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
- .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ .bFunctionClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bFunctionSubClass = 0x01,
+ .bFunctionProtocol = 0x03,
/* .iFunction = DYNAMIC */
};
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index aa186781ef22..5e50fe245a59 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -1876,7 +1876,8 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi,
dev->function.disable = mtp_function_disable;
dev->function.setup = mtp_ctrlreq_configfs;
dev->function.free_func = mtp_free;
- dev->is_ptp = mtp_config;
+ dev->is_ptp = !mtp_config;
+ fi->f = &dev->function;
return &dev->function;
}
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 1bb7082be8e6..12b98017beb2 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -242,6 +242,12 @@ static void *usbpd_ipc_log;
static int min_sink_current = 900;
module_param(min_sink_current, int, S_IRUSR | S_IWUSR);
+static bool ss_host;
+module_param(ss_host, bool, S_IRUSR | S_IWUSR);
+
+static bool ss_dev = true;
+module_param(ss_dev, bool, S_IRUSR | S_IWUSR);
+
static const u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */
static const u32 default_snk_caps[] = { 0x2601905A, /* 5V @ 900mA */
@@ -318,6 +324,7 @@ static const unsigned int usbpd_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
EXTCON_USB_CC,
+ EXTCON_USB_SPEED,
EXTCON_NONE,
};
@@ -685,6 +692,10 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
SVDM_CMD_TYPE_INITIATOR, 0,
NULL, 0);
+ extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC,
+ is_cable_flipped(pd));
+ extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED,
+ ss_host);
extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1);
}
@@ -755,6 +766,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
EXTCON_USB_CC,
is_cable_flipped(pd));
extcon_set_cable_state_(pd->extcon,
+ EXTCON_USB_SPEED, ss_dev);
+ extcon_set_cable_state_(pd->extcon,
EXTCON_USB, 1);
}
}
@@ -852,6 +865,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->current_dr = DR_UFP;
extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC,
is_cable_flipped(pd));
+ extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED,
+ ss_dev);
extcon_set_cable_state_(pd->extcon, EXTCON_USB, 1);
pd_phy_update_roles(pd->current_dr, pd->current_pr);
}
@@ -925,7 +940,8 @@ int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos)
return -ENOMEM;
vdm_tx->data[0] = vdm_hdr;
- memcpy(&vdm_tx->data[1], vdos, num_vdos * sizeof(u32));
+ if (vdos && num_vdos)
+ memcpy(&vdm_tx->data[1], vdos, num_vdos * sizeof(u32));
vdm_tx->size = num_vdos + 1; /* include the header */
/* VDM will get sent in PE_SRC/SNK_READY state handling */
@@ -1193,12 +1209,14 @@ static void dr_swap(struct usbpd *pd)
extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 0);
extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC,
is_cable_flipped(pd));
+ extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED, ss_dev);
extcon_set_cable_state_(pd->extcon, EXTCON_USB, 1);
pd->current_dr = DR_UFP;
} else if (pd->current_dr == DR_UFP) {
extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0);
extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC,
is_cable_flipped(pd));
+ extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED, ss_host);
extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1);
pd->current_dr = DR_DFP;
@@ -1250,6 +1268,8 @@ static void usbpd_sm(struct work_struct *w)
pd->caps_count = 0;
pd->hard_reset_count = 0;
pd->src_cap_id = 0;
+ pd->requested_voltage = 0;
+ pd->requested_current = 0;
memset(&pd->received_pdos, 0, sizeof(pd->received_pdos));
val.intval = 0;
@@ -1344,6 +1364,8 @@ static void usbpd_sm(struct work_struct *w)
extcon_set_cable_state_(pd->extcon,
EXTCON_USB_CC, is_cable_flipped(pd));
extcon_set_cable_state_(pd->extcon,
+ EXTCON_USB_SPEED, ss_host);
+ extcon_set_cable_state_(pd->extcon,
EXTCON_USB_HOST, 1);
} else if (pd->caps_count >= PD_CAPS_COUNT) {
usbpd_dbg(&pd->dev, "Src CapsCounter exceeded, disabling PD\n");
@@ -1747,6 +1769,20 @@ static void usbpd_sm(struct work_struct *w)
pd->rx_msg_type = pd->rx_msg_len = 0;
}
+static inline const char *src_current(enum power_supply_typec_mode typec_mode)
+{
+ switch (typec_mode) {
+ case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
+ return "default";
+ case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
+ return "medium - 1.5A";
+ case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
+ return "high - 3.0A";
+ default:
+ return "";
+ }
+}
+
static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
{
struct usbpd *pd = container_of(nb, struct usbpd, psy_nb);
@@ -1847,7 +1883,8 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- usbpd_info(&pd->dev, "Type-C Source connected\n");
+ usbpd_info(&pd->dev, "Type-C Source (%s) connected\n",
+ src_current(typec_mode));
if (pd->current_pr != PR_SINK) {
pd->current_pr = PR_SINK;
queue_work(pd->wq, &pd->sm_work);
@@ -1857,7 +1894,9 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
/* Source states */
case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE:
case POWER_SUPPLY_TYPEC_SINK:
- usbpd_info(&pd->dev, "Type-C Sink connected\n");
+ usbpd_info(&pd->dev, "Type-C Sink%s connected\n",
+ typec_mode == POWER_SUPPLY_TYPEC_SINK ?
+ "" : " (powered)");
if (pd->current_pr != PR_SRC) {
pd->current_pr = PR_SRC;
queue_work(pd->wq, &pd->sm_work);
@@ -2228,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)
@@ -2243,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);
@@ -2255,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/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c
index 8cf294306efd..5b5e6210a1bb 100644
--- a/drivers/usb/pd/qpnp-pdphy.c
+++ b/drivers/usb/pd/qpnp-pdphy.c
@@ -445,8 +445,10 @@ int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
dev_dbg(pdphy->dev, "%s: hdr %x frame type %d timeout %u\n",
__func__, hdr, type, timeout_ms);
- print_hex_dump_debug("tx data obj:", DUMP_PREFIX_NONE, 32, 4,
- data, data_len, false);
+
+ if (data && data_len)
+ print_hex_dump_debug("tx data obj:", DUMP_PREFIX_NONE, 32, 4,
+ data, data_len, false);
if (!pdphy) {
pr_err("%s: pdphy not found\n", __func__);
@@ -472,7 +474,7 @@ int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
if (ret)
return ret;
- if (data_len) {
+ if (data && data_len) {
/* write data objects of SOP message */
ret = pdphy_bulk_reg_write(pdphy, USB_PDPHY_TX_BUFFER_DATA,
data, data_len);
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index 4ecdc350fbd4..5a768ee4d061 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -63,6 +63,7 @@
#define LINESTATE_DM BIT(1)
#define QUSB2PHY_PLL_ANALOG_CONTROLS_ONE 0x0
+#define QUSB2PHY_PLL_ANALOG_CONTROLS_TWO 0x4
unsigned int phy_tune1;
module_param(phy_tune1, uint, S_IRUGO | S_IWUSR);
@@ -541,6 +542,7 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
{
struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
u32 linestate = 0, intr_mask = 0;
+ static u8 analog_ctrl_two;
int ret;
if (qphy->suspended && suspend) {
@@ -554,6 +556,14 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
if (qphy->cable_connected ||
(qphy->phy.flags & PHY_HOST_MODE)) {
+ /* store clock settings like cmos/cml */
+ analog_ctrl_two =
+ readl_relaxed(qphy->base +
+ QUSB2PHY_PLL_ANALOG_CONTROLS_TWO);
+
+ writel_relaxed(0x1b,
+ qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_TWO);
+
/* enable clock bypass */
writel_relaxed(0x90,
qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_ONE);
@@ -599,6 +609,9 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
dev_err(phy->dev, "%s: phy_reset deassert failed\n",
__func__);
+ writel_relaxed(0x1b,
+ qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_TWO);
+
/* enable clock bypass */
writel_relaxed(0x90,
qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_ONE);
@@ -620,6 +633,10 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
(qphy->phy.flags & PHY_HOST_MODE)) {
qusb_phy_enable_clocks(qphy, true);
+ /* restore the default clock settings */
+ writel_relaxed(analog_ctrl_two,
+ qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_TWO);
+
/* disable clock bypass */
writel_relaxed(0x80,
qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_ONE);
@@ -639,12 +656,17 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
*/
wmb();
+ qusb_phy_enable_power(qphy, true, true);
+ ret = reset_control_assert(qphy->phy_reset);
+ if (ret)
+ dev_err(phy->dev, "%s: phy_reset assert failed\n",
+ __func__);
+ usleep_range(100, 150);
ret = reset_control_deassert(qphy->phy_reset);
if (ret)
dev_err(phy->dev, "%s: phy_reset deassert failed\n",
__func__);
- qusb_phy_enable_power(qphy, true, true);
qusb_phy_enable_clocks(qphy, true);
}
qphy->suspended = false;
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 609a7aed4977..a5368cdf2254 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -163,6 +163,7 @@ enum mdss_hw_quirk {
MDSS_QUIRK_FMT_PACK_PATTERN,
MDSS_QUIRK_NEED_SECURE_MAP,
MDSS_QUIRK_SRC_SPLIT_ALWAYS,
+ MDSS_QUIRK_MMSS_GDSC_COLLAPSE,
MDSS_QUIRK_MAX,
};
@@ -405,6 +406,7 @@ struct mdss_data_type {
u32 enable_gate;
u32 enable_bw_release;
u32 enable_rotator_bw_release;
+ u32 enable_cdp;
u32 serialize_wait4pp;
u32 wait4autorefresh;
u32 lines_before_active;
diff --git a/drivers/video/fbdev/msm/mdss_debug.h b/drivers/video/fbdev/msm/mdss_debug.h
index 59ba4f3e5578..1a44ab5c44a2 100644
--- a/drivers/video/fbdev/msm/mdss_debug.h
+++ b/drivers/video/fbdev/msm/mdss_debug.h
@@ -57,6 +57,7 @@ struct vbif_debug_bus {
u32 block_bus_addr;
u32 bit_offset;
u32 block_cnt;
+ u32 test_pnt_start;
u32 test_pnt_cnt;
};
diff --git a/drivers/video/fbdev/msm/mdss_debug_xlog.c b/drivers/video/fbdev/msm/mdss_debug_xlog.c
index cfcc96aafffb..0a45ce036cf6 100644
--- a/drivers/video/fbdev/msm/mdss_debug_xlog.c
+++ b/drivers/video/fbdev/msm/mdss_debug_xlog.c
@@ -312,7 +312,7 @@ static void __vbif_debug_bus(struct vbif_debug_bus *head,
vbif_base + head->block_bus_addr);
/* make sure that current bus blcok enable */
wmb();
- for (j = 0; j < head->test_pnt_cnt; j++) {
+ for (j = head->test_pnt_start; j < head->test_pnt_cnt; j++) {
writel_relaxed(j, vbif_base + head->block_bus_addr + 4);
/* make sure that test point is enabled */
wmb();
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index f25a6e185051..57e18a7dc5e1 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -856,10 +856,12 @@ static int dp_audio_info_setup(struct platform_device *pdev,
return -ENODEV;
}
- mdss_dp_audio_enable(&dp_ctrl->ctrl_io, true);
- mdss_dp_config_audio_acr_ctrl(&dp_ctrl->ctrl_io,
- dp_ctrl->link_rate);
mdss_dp_audio_setup_sdps(&dp_ctrl->ctrl_io);
+ mdss_dp_audio_set_sample_rate(&dp_ctrl->ctrl_io,
+ dp_ctrl->link_rate, params->sample_rate_hz);
+ mdss_dp_config_audio_acr_ctrl(&dp_ctrl->ctrl_io, dp_ctrl->link_rate);
+ mdss_dp_set_safe_to_exit_level(&dp_ctrl->ctrl_io, dp_ctrl->lane_cnt);
+ mdss_dp_audio_enable(&dp_ctrl->ctrl_io, true);
return rc;
} /* dp_audio_info_setup */
@@ -1055,11 +1057,13 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
pr_debug("link_rate = 0x%x\n", dp_drv->link_rate);
dp_drv->power_data[DP_CTRL_PM].clk_config[0].rate =
- dp_drv->link_rate * DP_LINK_RATE_MULTIPLIER;
+ ((dp_drv->link_rate * DP_LINK_RATE_MULTIPLIER) /
+ 1000); /* KHz */
dp_drv->pixel_rate = dp_drv->panel_data.panel_info.clk_rate;
dp_drv->power_data[DP_CTRL_PM].clk_config[3].rate =
- dp_drv->pixel_rate;
+ (dp_drv->pixel_rate /
+ 1000); /* KHz */
ret = mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, true);
if (ret) {
@@ -1394,6 +1398,8 @@ static int mdss_dp_hdcp_init(struct mdss_panel_data *pdata)
goto error;
}
+ dp_drv->panel_data.panel_info.hdcp_1x_data = dp_drv->hdcp_data;
+
pr_debug("HDCP 1.3 initialized\n");
dp_drv->hdcp_ops = hdcp_1x_start(dp_drv->hdcp_data);
@@ -1774,7 +1780,7 @@ irqreturn_t dp_isr(int irq, void *ptr)
{
struct mdss_dp_drv_pdata *dp = (struct mdss_dp_drv_pdata *)ptr;
unsigned char *base = dp->base;
- u32 isr1, isr2, mask1, mask2;
+ u32 isr1, isr2, mask1;
u32 ack;
spin_lock(&dp->lock);
@@ -1782,13 +1788,11 @@ irqreturn_t dp_isr(int irq, void *ptr)
isr2 = dp_read(base + DP_INTR_STATUS2);
mask1 = isr1 & dp->mask1;
- mask2 = isr2 & dp->mask2;
isr1 &= ~mask1; /* remove masks bit */
- isr2 &= ~mask2;
- pr_debug("isr=%x mask=%x isr2=%x mask2=%x\n",
- isr1, mask1, isr2, mask2);
+ pr_debug("isr=%x mask=%x isr2=%x\n",
+ isr1, mask1, isr2);
ack = isr1 & EDP_INTR_STATUS1;
ack <<= 1; /* ack bits */
@@ -1797,7 +1801,7 @@ irqreturn_t dp_isr(int irq, void *ptr)
ack = isr2 & EDP_INTR_STATUS2;
ack <<= 1; /* ack bits */
- ack |= mask2;
+ ack |= isr2;
dp_write(base + DP_INTR_STATUS2, ack);
spin_unlock(&dp->lock);
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index 9a8534677c5e..4710cf7a98e2 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -368,7 +368,7 @@ struct mdss_dp_drv_pdata {
int dp_on_cnt;
int dp_off_cnt;
- u32 pixel_rate;
+ u32 pixel_rate; /* KHz */
u32 aux_rate;
char link_rate; /* X 27000000 for real rate */
char lane_cnt;
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 0bbcd0c9041e..d9297a7af764 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -230,7 +230,9 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep,
int dp_aux_write(void *ep, struct edp_cmd *cmd)
{
- return dp_aux_write_cmds(ep, cmd);
+ int rc = dp_aux_write_cmds(ep, cmd);
+
+ return rc < 0 ? -EINVAL : 0;
}
static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
@@ -291,7 +293,9 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
int dp_aux_read(void *ep, struct edp_cmd *cmds)
{
- return dp_aux_read_cmds(ep, cmds);
+ int rc = dp_aux_read_cmds(ep, cmds);
+
+ return rc < 0 ? -EINVAL : 0;
}
void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
@@ -506,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_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index 62b76199959c..bdf5d92f7053 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -24,6 +24,33 @@
#define PARITY_BYTE_1_BIT 24
#define HEADER_BYTE_3_BIT 16
#define PARITY_BYTE_3_BIT 24
+#define DP_LS_FREQ_162 162000000
+#define DP_LS_FREQ_270 270000000
+#define DP_LS_FREQ_540 540000000
+#define AUDIO_FREQ_32 32000
+#define AUDIO_FREQ_44_1 44100
+#define AUDIO_FREQ_48 48000
+#define DP_AUDIO_FREQ_COUNT 3
+
+static const uint32_t naud_value[DP_AUDIO_FREQ_COUNT][DP_AUDIO_FREQ_COUNT] = {
+ { 10125, 16875, 33750 },
+ { 5625, 9375, 18750 },
+ { 3375, 5625, 11250 }
+};
+
+static const uint32_t maud_rate[DP_AUDIO_FREQ_COUNT] = { 1024, 784, 512 };
+
+static const uint32_t audio_timing_rbr[DP_AUDIO_FREQ_COUNT] = {
+ MMSS_DP_AUDIO_TIMING_RBR_32,
+ MMSS_DP_AUDIO_TIMING_RBR_44,
+ MMSS_DP_AUDIO_TIMING_RBR_48
+};
+
+static const uint32_t std_audio_freq_list[DP_AUDIO_FREQ_COUNT] = {
+ AUDIO_FREQ_32,
+ AUDIO_FREQ_44_1,
+ AUDIO_FREQ_48
+};
struct mdss_hw mdss_dp_hw = {
.hw_ndx = MDSS_HW_EDP,
@@ -31,6 +58,42 @@ struct mdss_hw mdss_dp_hw = {
.irq_handler = dp_isr,
};
+static int mdss_dp_get_rate_index(uint32_t rate)
+{
+ int index = 0;
+
+ switch (rate) {
+ case DP_LS_FREQ_162:
+ case AUDIO_FREQ_32:
+ index = 0;
+ break;
+ case DP_LS_FREQ_270:
+ case AUDIO_FREQ_44_1:
+ index = 1;
+ break;
+ case DP_LS_FREQ_540:
+ case AUDIO_FREQ_48:
+ index = 2;
+ break;
+ default:
+ index = 0;
+ pr_err("unsupported rate\n");
+ break;
+ }
+
+ return index;
+}
+
+static bool match_std_freq(uint32_t audio_freq, uint32_t std_freq)
+{
+ int quotient = audio_freq / std_freq;
+
+ if (quotient & (quotient - 1))
+ return false;
+ else
+ return true;
+}
+
/* DP retrieve ctrl HW version */
u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io)
{
@@ -378,123 +441,294 @@ u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp)
return config;
}
-void mdss_dp_config_audio_acr_ctrl(struct dss_io_data *ctrl_io,
- char link_rate)
+void mdss_dp_config_audio_acr_ctrl(struct dss_io_data *ctrl_io, char link_rate)
{
u32 acr_ctrl = 0;
+ u32 select = 0;
+
+ acr_ctrl = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_ACR_CTRL);
switch (link_rate) {
case DP_LINK_RATE_162:
- acr_ctrl = 0;
+ select = 0;
break;
case DP_LINK_RATE_270:
- acr_ctrl = 1;
+ select = 1;
break;
case DP_LINK_RATE_540:
- acr_ctrl = 2;
+ select = 2;
break;
default:
pr_debug("Unknown link rate\n");
- acr_ctrl = 1;
+ select = 0;
break;
}
+ acr_ctrl |= select << 4 | BIT(31) | BIT(8) | BIT(14);
+
+ pr_debug("select = 0x%x, acr_ctrl = 0x%x\n", select, acr_ctrl);
+
writel_relaxed(acr_ctrl, ctrl_io->base + MMSS_DP_AUDIO_ACR_CTRL);
}
-static void mdss_dp_audio_config_parity_settings(struct dss_io_data *ctrl_io)
+static u8 mdss_dp_get_g0_value(u8 data)
+{
+ u8 c[4];
+ u8 g[4];
+ u8 rData = 0;
+ u8 i;
+
+ for (i = 0; i < 4; i++)
+ c[i] = (data >> i) & 0x01;
+
+ g[0] = c[3];
+ g[1] = c[0] ^ c[3];
+ g[2] = c[1];
+ g[3] = c[2];
+
+ for (i = 0; i < 4; i++)
+ rData = ((g[i] & 0x01) << i) | rData;
+
+ return rData;
+}
+
+static u8 mdss_dp_get_g1_value(u8 data)
+{
+ u8 c[4];
+ u8 g[4];
+ u8 rData = 0;
+ u8 i;
+
+ for (i = 0; i < 4; i++)
+ c[i] = (data >> i) & 0x01;
+
+ g[0] = c[0] ^ c[3];
+ g[1] = c[0] ^ c[1] ^ c[3];
+ g[2] = c[1] ^ c[2];
+ g[3] = c[2] ^ c[3];
+
+ for (i = 0; i < 4; i++)
+ rData = ((g[i] & 0x01) << i) | rData;
+
+ return rData;
+}
+
+static u8 mdss_dp_calculate_parity_byte(u32 data)
+{
+ u8 x0 = 0;
+ u8 x1 = 0;
+ u8 ci = 0;
+ u8 iData = 0;
+ u8 i = 0;
+ u8 parityByte;
+
+ for (i = 0; i < 8; i++) {
+ iData = (data >> i*4) & 0xF;
+
+ ci = iData ^ x1;
+ x1 = x0 ^ mdss_dp_get_g1_value(ci);
+ x0 = mdss_dp_get_g0_value(ci);
+ }
+
+ parityByte = x1 | (x0 << 4);
+
+ return parityByte;
+}
+
+static void mdss_dp_audio_setup_audio_stream_sdp(struct dss_io_data *ctrl_io)
{
u32 value = 0;
+ u32 new_value = 0;
+ u8 parity_byte = 0;
- value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_STREAM_0);
/* Config header and parity byte 1 */
- value |= ((0x2 << HEADER_BYTE_1_BIT)
- | (0x13 << PARITY_BYTE_1_BIT));
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_STREAM_0);
+ new_value = 0x02;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_1_BIT)
+ | (parity_byte << PARITY_BYTE_1_BIT));
+ pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_STREAM_0);
- value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_STREAM_1);
/* Config header and parity byte 2 */
- value |= ((0x28 << HEADER_BYTE_2_BIT)
- | (0xf5 << PARITY_BYTE_2_BIT));
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_STREAM_1);
+ new_value = 0x0;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_2_BIT)
+ | (parity_byte << PARITY_BYTE_2_BIT));
+ pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_STREAM_1);
- value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_STREAM_1);
/* Config header and parity byte 3 */
- value |= ((0x97 << HEADER_BYTE_3_BIT)
- | (0xc2 << PARITY_BYTE_3_BIT));
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_STREAM_1);
+ new_value = 0x01;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_3_BIT)
+ | (parity_byte << PARITY_BYTE_3_BIT));
+ pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_STREAM_1);
- value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_TIMESTAMP_0);
+}
+
+static void mdss_dp_audio_setup_audio_timestamp_sdp(struct dss_io_data *ctrl_io)
+{
+ u32 value = 0;
+ u32 new_value = 0;
+ u8 parity_byte = 0;
+
/* Config header and parity byte 1 */
- value |= ((0x1 << HEADER_BYTE_1_BIT)
- | (0x98 << PARITY_BYTE_1_BIT));
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_TIMESTAMP_0);
+ new_value = 0x1;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_1_BIT)
+ | (parity_byte << PARITY_BYTE_1_BIT));
+ pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_TIMESTAMP_0);
- value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_TIMESTAMP_1);
/* Config header and parity byte 2 */
- value |= ((0x17 << HEADER_BYTE_2_BIT)
- | (0x60 << PARITY_BYTE_2_BIT));
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_TIMESTAMP_1);
+ new_value = 0x17;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_2_BIT)
+ | (parity_byte << PARITY_BYTE_2_BIT));
+ pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_TIMESTAMP_1);
- value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_INFOFRAME_0);
+ /* Config header and parity byte 3 */
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_TIMESTAMP_1);
+ new_value = (0x0 | (0x12 << 2));
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_3_BIT)
+ | (parity_byte << PARITY_BYTE_3_BIT));
+ pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
+ writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_TIMESTAMP_1);
+}
+
+static void mdss_dp_audio_setup_audio_infoframe_sdp(struct dss_io_data *ctrl_io)
+{
+ u32 value = 0;
+ u32 new_value = 0;
+ u8 parity_byte = 0;
+
/* Config header and parity byte 1 */
- value |= ((0x84 << HEADER_BYTE_1_BIT)
- | (0x84 << PARITY_BYTE_1_BIT));
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_INFOFRAME_0);
+ new_value = 0x84;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_1_BIT)
+ | (parity_byte << PARITY_BYTE_1_BIT));
+ pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_INFOFRAME_0);
- value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_INFOFRAME_1);
/* Config header and parity byte 2 */
- value |= ((0xb1 << HEADER_BYTE_2_BIT)
- | (0x4e << PARITY_BYTE_2_BIT));
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_INFOFRAME_1);
+ new_value = 0x1b;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_2_BIT)
+ | (parity_byte << PARITY_BYTE_2_BIT));
+ pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_INFOFRAME_1);
+ /* Config header and parity byte 3 */
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_INFOFRAME_1);
+ new_value = (0x0 | (0x12 << 2));
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_3_BIT)
+ | (parity_byte << PARITY_BYTE_3_BIT));
+ pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
+ new_value, parity_byte);
+ writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_INFOFRAME_1);
+
+ /* Config Data Byte 0 - 2 as "Refer to Stream Header" */
+ writel_relaxed(0x0, ctrl_io->base + MMSS_DP_AUDIO_INFOFRAME_2);
+}
+
+static void mdss_dp_audio_setup_copy_management_sdp(struct dss_io_data *ctrl_io)
+{
+ u32 value = 0;
+ u32 new_value = 0;
+ u8 parity_byte = 0;
+
+ /* Config header and parity byte 1 */
value = readl_relaxed(ctrl_io->base +
MMSS_DP_AUDIO_COPYMANAGEMENT_0);
- /* Config header and parity byte 1 */
- value |= ((0x5 << HEADER_BYTE_1_BIT)
- | (0xbe << PARITY_BYTE_1_BIT));
+ new_value = 0x05;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_1_BIT)
+ | (parity_byte << PARITY_BYTE_1_BIT));
+ pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base +
MMSS_DP_AUDIO_COPYMANAGEMENT_0);
+ /* Config header and parity byte 2 */
value = readl_relaxed(ctrl_io->base +
MMSS_DP_AUDIO_COPYMANAGEMENT_1);
- /* Config header and parity byte 2 */
- value |= ((0x0b << HEADER_BYTE_2_BIT)
- | (0xc7 << PARITY_BYTE_2_BIT));
+ new_value = 0x0F;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_2_BIT)
+ | (parity_byte << PARITY_BYTE_2_BIT));
+ pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base +
MMSS_DP_AUDIO_COPYMANAGEMENT_1);
+ /* Config header and parity byte 3 */
value = readl_relaxed(ctrl_io->base +
MMSS_DP_AUDIO_COPYMANAGEMENT_1);
- /* Config header and parity byte 3 */
- value |= ((0x1 << HEADER_BYTE_3_BIT)
- | (0x98 << PARITY_BYTE_3_BIT));
+ new_value = 0x0;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_3_BIT)
+ | (parity_byte << PARITY_BYTE_3_BIT));
+ pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base +
MMSS_DP_AUDIO_COPYMANAGEMENT_1);
- writel_relaxed(0x22222222, ctrl_io->base +
+ writel_relaxed(0x0, ctrl_io->base +
MMSS_DP_AUDIO_COPYMANAGEMENT_2);
- writel_relaxed(0x22222222, ctrl_io->base +
+ writel_relaxed(0x0, ctrl_io->base +
MMSS_DP_AUDIO_COPYMANAGEMENT_3);
- writel_relaxed(0x22222222, ctrl_io->base +
+ writel_relaxed(0x0, ctrl_io->base +
MMSS_DP_AUDIO_COPYMANAGEMENT_4);
+}
+
+static void mdss_dp_audio_setup_isrc_sdp(struct dss_io_data *ctrl_io)
+{
+ u32 value = 0;
+ u32 new_value = 0;
+ u8 parity_byte = 0;
- value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_ISRC_0);
/* Config header and parity byte 1 */
- value |= ((0x6 << HEADER_BYTE_1_BIT)
- | (0x35 << PARITY_BYTE_1_BIT));
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_ISRC_0);
+ new_value = 0x06;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_1_BIT)
+ | (parity_byte << PARITY_BYTE_1_BIT));
+ pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_ISRC_0);
- value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_ISRC_1);
/* Config header and parity byte 2 */
- value |= ((0x0b << HEADER_BYTE_2_BIT)
- | (0xc7 << PARITY_BYTE_2_BIT));
+ value = readl_relaxed(ctrl_io->base + MMSS_DP_AUDIO_ISRC_1);
+ new_value = 0x0F;
+ parity_byte = mdss_dp_calculate_parity_byte(new_value);
+ value |= ((new_value << HEADER_BYTE_2_BIT)
+ | (parity_byte << PARITY_BYTE_2_BIT));
+ pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+ value, parity_byte);
writel_relaxed(value, ctrl_io->base + MMSS_DP_AUDIO_ISRC_1);
- writel_relaxed(0x33333333, ctrl_io->base + MMSS_DP_AUDIO_ISRC_2);
- writel_relaxed(0x33333333, ctrl_io->base + MMSS_DP_AUDIO_ISRC_3);
- writel_relaxed(0x33333333, ctrl_io->base + MMSS_DP_AUDIO_ISRC_4);
-
+ writel_relaxed(0x0, ctrl_io->base + MMSS_DP_AUDIO_ISRC_2);
+ writel_relaxed(0x0, ctrl_io->base + MMSS_DP_AUDIO_ISRC_3);
+ writel_relaxed(0x0, ctrl_io->base + MMSS_DP_AUDIO_ISRC_4);
}
void mdss_dp_audio_setup_sdps(struct dss_io_data *ctrl_io)
@@ -523,7 +757,90 @@ void mdss_dp_audio_setup_sdps(struct dss_io_data *ctrl_io)
writel_relaxed(sdp_cfg2, ctrl_io->base + MMSS_DP_SDP_CFG2);
- mdss_dp_audio_config_parity_settings(ctrl_io);
+ mdss_dp_audio_setup_audio_stream_sdp(ctrl_io);
+ mdss_dp_audio_setup_audio_timestamp_sdp(ctrl_io);
+ mdss_dp_audio_setup_audio_infoframe_sdp(ctrl_io);
+ mdss_dp_audio_setup_copy_management_sdp(ctrl_io);
+ mdss_dp_audio_setup_isrc_sdp(ctrl_io);
+}
+
+void mdss_dp_audio_set_sample_rate(struct dss_io_data *ctrl_io,
+ char dp_link_rate, uint32_t audio_freq)
+{
+ uint32_t link_rate;
+ uint32_t default_audio_freq = AUDIO_FREQ_32;
+ int i, multiplier = 1;
+ uint32_t maud_index, lrate_index, register_index, value;
+
+ link_rate = (uint32_t)dp_link_rate * DP_LINK_RATE_MULTIPLIER;
+
+ pr_debug("link_rate = %u, audio_freq = %u\n", link_rate, audio_freq);
+
+ for (i = 0; i < DP_AUDIO_FREQ_COUNT; i++) {
+ if (audio_freq % std_audio_freq_list[i])
+ continue;
+
+ if (match_std_freq(audio_freq, std_audio_freq_list[i])) {
+ default_audio_freq = std_audio_freq_list[i];
+ multiplier = audio_freq / default_audio_freq;
+ break;
+ }
+ }
+
+ pr_debug("default_audio_freq = %u, multiplier = %d\n",
+ default_audio_freq, multiplier);
+
+ lrate_index = mdss_dp_get_rate_index(link_rate);
+ maud_index = mdss_dp_get_rate_index(default_audio_freq);
+
+ pr_debug("lrate_index = %u, maud_index = %u, maud = %u, naud = %u\n",
+ lrate_index, maud_index,
+ maud_rate[maud_index] * multiplier,
+ naud_value[maud_index][lrate_index]);
+
+ register_index = mdss_dp_get_rate_index(default_audio_freq);
+ value = ((maud_rate[maud_index] * multiplier) << 16) |
+ naud_value[maud_index][lrate_index];
+
+ pr_debug("reg index = %d, offset = 0x%x, value = 0x%x\n",
+ (int)register_index, audio_timing_rbr[register_index],
+ value);
+
+ writel_relaxed(value, ctrl_io->base +
+ audio_timing_rbr[register_index]);
+}
+
+void mdss_dp_set_safe_to_exit_level(struct dss_io_data *ctrl_io,
+ uint32_t lane_cnt)
+{
+ u32 safe_to_exit_level = 0;
+ u32 mainlink_levels = 0;
+
+ switch (lane_cnt) {
+ case 1:
+ safe_to_exit_level = 14;
+ break;
+ case 2:
+ safe_to_exit_level = 8;
+ break;
+ case 4:
+ safe_to_exit_level = 5;
+ break;
+ default:
+ pr_debug("setting the default safe_to_exit_level = %u\n",
+ safe_to_exit_level);
+ safe_to_exit_level = 14;
+ break;
+ }
+
+ mainlink_levels = readl_relaxed(ctrl_io->base + DP_MAINLINK_LEVELS);
+ mainlink_levels &= 0xFF0;
+ mainlink_levels |= safe_to_exit_level;
+
+ pr_debug("mainlink_level = 0x%x, safe_to_exit_level = 0x%x\n",
+ mainlink_levels, safe_to_exit_level);
+
+ writel_relaxed(mainlink_levels, ctrl_io->base + DP_MAINLINK_LEVELS);
}
void mdss_dp_audio_enable(struct dss_io_data *ctrl_io, bool enable)
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
index 96664d1f9954..5eb9d092476f 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.h
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -54,6 +54,7 @@
#define DP_LOGICAL2PHYSCIAL_LANE_MAPPING (0x00000438)
#define DP_MAINLINK_READY (0x00000440)
+#define DP_MAINLINK_LEVELS (0x00000444)
#define DP_TU (0x0000044C)
#define MMSS_DP_AUDIO_TIMING_GEN (0x00000480)
@@ -102,7 +103,8 @@
#define MMSS_DP_AUDIO_ISRC_4 (0x000006A0)
#define MMSS_DP_AUDIO_ISRC_5 (0x000006A4)
#define MMSS_DP_AUDIO_INFOFRAME_0 (0x000006A8)
-#define MMSS_DP_AUDIO_INFOFRAME_1 (0x000006B0)
+#define MMSS_DP_AUDIO_INFOFRAME_1 (0x000006AC)
+#define MMSS_DP_AUDIO_INFOFRAME_2 (0x000006B0)
#define MMSS_DP_GENERIC0_0 (0x00000700)
#define MMSS_DP_GENERIC0_1 (0x00000704)
@@ -181,8 +183,6 @@
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11 (0x01C)
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12 (0x020)
-#define DP_INTERRUPT_STATUS_2 (0x024)
-
struct lane_mapping {
char lane0;
char lane1;
@@ -235,5 +235,10 @@ void mdss_dp_config_audio_acr_ctrl(struct dss_io_data *ctrl_io,
char link_rate);
void mdss_dp_audio_setup_sdps(struct dss_io_data *ctrl_io);
void mdss_dp_audio_enable(struct dss_io_data *ctrl_io, bool enable);
+void mdss_dp_audio_select_core(struct dss_io_data *ctrl_io);
+void mdss_dp_audio_set_sample_rate(struct dss_io_data *ctrl_io,
+ char dp_link_rate, uint32_t audio_freq);
+void mdss_dp_set_safe_to_exit_level(struct dss_io_data *ctrl_io,
+ uint32_t lane_cnt);
#endif /* __DP_UTIL_H__ */
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_phy_v3.c b/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c
index 94554bd5b423..d4ff82a11a09 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c
@@ -183,7 +183,7 @@ static void mdss_dsi_phy_v3_config_lane_settings(
struct mdss_dsi_ctrl_pdata *ctrl)
{
int i;
- u32 tx_dctrl[] = {0x18, 0x19, 0x18, 0x02, 0x18};
+ u32 tx_dctrl[] = {0x00, 0x00, 0x00, 0x02, 0x01};
struct mdss_dsi_phy_ctrl *pd =
&(((ctrl->panel_data).panel_info.mipi).dsi_phy_db);
@@ -407,8 +407,8 @@ int mdss_dsi_phy_v3_init(struct mdss_dsi_ctrl_pdata *ctrl,
mdss_dsi_phy_v3_lanes_enable(ctrl);
- /* Select hybrid mode and use local-wordclk */
- DSI_PHY_W32(ctrl->phy_io.base, CMN_CTRL_2, 0x08);
+ /* Select full-rate mode */
+ DSI_PHY_W32(ctrl->phy_io.base, CMN_CTRL_2, 0x40);
mdss_dsi_phy_v3_set_pll_source(ctrl);
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 e0f1a37ac84e..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);
}
@@ -4664,6 +4667,9 @@ static int mdss_fb_mode_switch(struct msm_fb_data_type *mfd, u32 mode)
if (!mfd || !mfd->panel_info)
return -EINVAL;
+ /* make sure that we are idle while switching */
+ mdss_fb_wait_for_kickoff(mfd);
+
pinfo = mfd->panel_info;
if (pinfo->mipi.dms_mode == DYNAMIC_MODE_SWITCH_SUSPEND_RESUME) {
ret = mdss_fb_blanking_mode_switch(mfd, mode);
diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c
index 7f26007fab13..5b490ad67e65 100644
--- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c
+++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c
@@ -19,6 +19,7 @@
#include <soc/qcom/scm.h>
#include <linux/hdcp_qseecom.h>
#include "mdss_hdcp_1x.h"
+#include "mdss_fb.h"
#include "mdss_dp_util.h"
#include "video/msm_hdmi_hdcp_mgr.h"
@@ -41,14 +42,14 @@
#define HDCP_INT_CLR (isr->auth_success_ack | isr->auth_fail_ack | \
isr->auth_fail_info_ack | isr->tx_req_ack | \
isr->encryption_ready_ack | \
- isr->encryption_not_ready | isr->tx_req_done_ack)
+ isr->encryption_not_ready_ack | isr->tx_req_done_ack)
#define HDCP_INT_EN (isr->auth_success_mask | isr->auth_fail_mask | \
isr->encryption_ready_mask | \
isr->encryption_not_ready_mask)
#define HDCP_POLL_SLEEP_US (20 * 1000)
-#define HDCP_POLL_TIMEOUT_US (HDCP_POLL_SLEEP_US * 1000)
+#define HDCP_POLL_TIMEOUT_US (HDCP_POLL_SLEEP_US * 100)
#define reg_set_data(x) \
(hdcp_ctrl->init_data.sec_access ? reg_set->sec_data##x : \
@@ -198,15 +199,15 @@ struct hdcp_reg_set {
{{"bcaps", 0x40, 1}, {"bksv", 0x00, 5}, {"r0'", 0x08, 2}, \
{"bstatus", 0x41, 2}, {"ksv-fifo", 0x43, 0}, {"v_h0", 0x20, 4}, \
{"v_h1", 0x24, 4}, {"v_h2", 0x28, 4}, {"v_h3", 0x2c, 4}, \
- {"v_h4", 0x30, 4}, {"an", 0x16, 8}, {"aksv", 0x10, 5}, \
- {"repater", 0x00, 0} }
+ {"v_h4", 0x30, 4}, {"an", 0x18, 8}, {"aksv", 0x10, 5}, \
+ {"repeater", 0x00, 0} }
#define HDCP_DP_SINK_ADDR_MAP \
{{"bcaps", 0x68028, 1}, {"bksv", 0x68000, 5}, {"r0'", 0x68005, 2}, \
{"bstatus", 0x6802A, 2}, {"ksv-fifo", 0x6802A, 0}, \
{"v_h0", 0x68014, 4}, {"v_h1", 0x68018, 4}, {"v_h2", 0x6801C, 4}, \
{"v_h3", 0x68020, 4}, {"v_h4", 0x68024, 4}, {"an", 0x6800C, 8}, \
- {"aksv", 0x68007, 5}, {"repater", 0x68028, 1} }
+ {"aksv", 0x68007, 5}, {"repeater", 0x68028, 1} }
#define HDCP_HDMI_INT_SET \
{HDMI_HDCP_INT_CTRL, \
@@ -215,7 +216,7 @@ struct hdcp_reg_set {
BIT(0), BIT(4), 0, 0, 0, 0}
#define HDCP_DP_INT_SET \
- {DP_INTERRUPT_STATUS_2, \
+ {DP_INTR_STATUS2, \
BIT(17), BIT(20), BIT(24), BIT(27), 0, 0, \
BIT(16), BIT(19), BIT(21), BIT(23), BIT(26), 0, 0, \
BIT(15), BIT(18), BIT(22), BIT(25), 0, 0}
@@ -554,7 +555,7 @@ static int hdcp_1x_read(struct hdcp_1x_ctrl *hdcp_ctrl,
cmd.out_buf = buf;
cmd.len = sink->len;
- rc = dp_aux_read(hdcp_ctrl->init_data.dp_data, &cmd);
+ rc = dp_aux_read(hdcp_ctrl->init_data.cb_data, &cmd);
if (rc)
DEV_ERR("%s: %s: %s read failed\n", __func__,
HDCP_STATE_NAME, sink->name);
@@ -564,7 +565,7 @@ static int hdcp_1x_read(struct hdcp_1x_ctrl *hdcp_ctrl,
}
static int hdcp_1x_write(struct hdcp_1x_ctrl *hdcp_ctrl,
- u32 offset, u32 len, u8 *buf, char *name)
+ struct hdcp_sink_addr *sink, u8 *buf)
{
int rc = 0;
struct hdmi_tx_ddc_data ddc_data;
@@ -573,28 +574,28 @@ static int hdcp_1x_write(struct hdcp_1x_ctrl *hdcp_ctrl,
memset(&ddc_data, 0, sizeof(ddc_data));
ddc_data.dev_addr = 0x74;
- ddc_data.offset = offset;
+ ddc_data.offset = sink->addr;
ddc_data.data_buf = buf;
- ddc_data.data_len = len;
- ddc_data.what = name;
+ ddc_data.data_len = sink->len;
+ ddc_data.what = sink->name;
hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;
rc = hdmi_ddc_write(hdcp_ctrl->init_data.ddc_ctrl);
if (rc)
DEV_ERR("%s: %s: %s write failed\n", __func__,
- HDCP_STATE_NAME, name);
+ HDCP_STATE_NAME, sink->name);
} else if (IS_ENABLED(CONFIG_FB_MSM_MDSS_DP_PANEL) &&
hdcp_ctrl->init_data.client_id == HDCP_CLIENT_DP) {
struct edp_cmd cmd = {0};
- cmd.addr = offset;
- cmd.len = len;
+ cmd.addr = sink->addr;
+ cmd.len = sink->len;
cmd.datap = buf;
- rc = dp_aux_write(hdcp_ctrl->init_data.dp_data, &cmd);
+ rc = dp_aux_write(hdcp_ctrl->init_data.cb_data, &cmd);
if (rc)
DEV_ERR("%s: %s: %s read failed\n", __func__,
- HDCP_STATE_NAME, name);
+ HDCP_STATE_NAME, sink->name);
}
return rc;
@@ -731,7 +732,7 @@ static int hdcp_1x_authentication_part1(struct hdcp_1x_ctrl *hdcp_ctrl)
link0_an_1 = DSS_REG_R(io, reg_set->data6);
if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_DP) {
udelay(1);
- link0_an_0 = DSS_REG_R(io, reg_set->data6);
+ link0_an_1 = DSS_REG_R(io, reg_set->data6);
}
/* Read AKSV */
@@ -754,13 +755,13 @@ static int hdcp_1x_authentication_part1(struct hdcp_1x_ctrl *hdcp_ctrl)
an[6] = (link0_an_1 >> 16) & 0xFF;
an[7] = (link0_an_1 >> 24) & 0xFF;
- rc = hdcp_1x_write(hdcp_ctrl, 0x18, 8, an, "an");
+ rc = hdcp_1x_write(hdcp_ctrl, &hdcp_ctrl->sink_addr.an, an);
if (IS_ERR_VALUE(rc)) {
DEV_ERR("%s: error writing an to sink\n", __func__);
goto error;
}
- rc = hdcp_1x_write(hdcp_ctrl, 0x10, 5, aksv, "aksv");
+ rc = hdcp_1x_write(hdcp_ctrl, &hdcp_ctrl->sink_addr.aksv, aksv);
if (IS_ERR_VALUE(rc)) {
DEV_ERR("%s: error writing aksv to sink\n", __func__);
goto error;
@@ -1325,10 +1326,12 @@ static void hdcp_1x_auth_work(struct work_struct *work)
}
io = hdcp_ctrl->init_data.core_io;
- /* Enabling Software DDC */
+ /* Enabling Software DDC for HDMI and REF timer for DP */
if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_HDMI)
DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io,
HDMI_DDC_ARBITRATION) & ~(BIT(4)));
+ else if (hdcp_ctrl->init_data.client_id == HDCP_CLIENT_DP)
+ DSS_REG_W(io, DP_DP_HPD_REFTIMER, 0x10013);
rc = hdcp_1x_authentication_part1(hdcp_ctrl);
if (rc) {
@@ -1628,12 +1631,45 @@ error:
return rc;
} /* hdcp_1x_isr */
+static struct hdcp_1x_ctrl *hdcp_1x_get_ctrl(struct device *dev)
+{
+ struct fb_info *fbi;
+ struct msm_fb_data_type *mfd;
+ struct mdss_panel_info *pinfo;
+
+ if (!dev) {
+ pr_err("invalid input\n");
+ goto error;
+ }
+
+ fbi = dev_get_drvdata(dev);
+ if (!fbi) {
+ pr_err("invalid fbi\n");
+ goto error;
+ }
+
+ mfd = fbi->par;
+ if (!mfd) {
+ pr_err("invalid mfd\n");
+ goto error;
+ }
+
+ pinfo = mfd->panel_info;
+ if (!pinfo) {
+ pr_err("invalid pinfo\n");
+ goto error;
+ }
+
+ return pinfo->hdcp_1x_data;
+
+error:
+ return NULL;
+}
static ssize_t hdcp_1x_sysfs_rda_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t ret;
- struct hdcp_1x_ctrl *hdcp_ctrl =
- hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP);
+ struct hdcp_1x_ctrl *hdcp_ctrl = hdcp_1x_get_ctrl(dev);
if (!hdcp_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -1652,8 +1688,7 @@ static ssize_t hdcp_1x_sysfs_rda_tp(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t ret = 0;
- struct hdcp_1x_ctrl *hdcp_ctrl =
- hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP);
+ struct hdcp_1x_ctrl *hdcp_ctrl = hdcp_1x_get_ctrl(dev);
if (!hdcp_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -1687,8 +1722,7 @@ static ssize_t hdcp_1x_sysfs_wta_tp(struct device *dev,
{
int msgid = 0;
ssize_t ret = count;
- struct hdcp_1x_ctrl *hdcp_ctrl =
- hdmi_get_featuredata_from_sysfs_dev(dev, HDMI_TX_FEAT_HDCP);
+ struct hdcp_1x_ctrl *hdcp_ctrl = hdcp_1x_get_ctrl(dev);
if (!hdcp_ctrl || !buf) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -1774,9 +1808,8 @@ void *hdcp_1x_init(struct hdcp_init_data *init_data)
};
if (!init_data || !init_data->core_io || !init_data->qfprom_io ||
- !init_data->mutex || !init_data->ddc_ctrl ||
- !init_data->notify_status || !init_data->workq ||
- !init_data->cb_data) {
+ !init_data->mutex || !init_data->notify_status ||
+ !init_data->workq || !init_data->cb_data) {
DEV_ERR("%s: invalid input\n", __func__);
goto error;
}
diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.h b/drivers/video/fbdev/msm/mdss_hdcp_1x.h
index dbe91b459a9f..426b13a340f4 100644
--- a/drivers/video/fbdev/msm/mdss_hdcp_1x.h
+++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.h
@@ -42,7 +42,6 @@ struct hdcp_init_data {
void *cb_data;
void (*notify_status)(void *cb_data, enum hdcp_states status);
struct hdmi_tx_ddc_ctrl *ddc_ctrl;
- void *dp_data;
u32 phy_addr;
u32 hdmi_tx_ver;
struct msm_hdmi_mode_timing_info *timing;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_panel.c b/drivers/video/fbdev/msm/mdss_hdmi_panel.c
index 0335bf900866..522debfba8ce 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_panel.c
@@ -150,74 +150,6 @@ enum hdmi_scaling_info {
HDMI_SCALING_HORZ_VERT,
};
-int hdmi_panel_get_vic(struct mdss_panel_info *pinfo,
- struct hdmi_util_ds_data *ds_data)
-{
- int new_vic = -1;
- u32 h_total, v_total;
- struct msm_hdmi_mode_timing_info timing;
-
- if (!pinfo) {
- pr_err("invalid panel data\n");
- return -EINVAL;
- }
-
- if (pinfo->vic) {
- struct msm_hdmi_mode_timing_info info = {0};
- u32 ret = hdmi_get_supported_mode(&info, ds_data, pinfo->vic);
- u32 supported = info.supported;
-
- if (!ret && supported) {
- new_vic = pinfo->vic;
- } else {
- pr_err("invalid or not supported vic %d\n",
- pinfo->vic);
- return -EPERM;
- }
- } else {
- timing.active_h = pinfo->xres;
- timing.back_porch_h = pinfo->lcdc.h_back_porch;
- timing.front_porch_h = pinfo->lcdc.h_front_porch;
- timing.pulse_width_h = pinfo->lcdc.h_pulse_width;
-
- h_total = timing.active_h + timing.back_porch_h +
- timing.front_porch_h + timing.pulse_width_h;
-
- pr_debug("ah=%d bph=%d fph=%d pwh=%d ht=%d\n",
- timing.active_h, timing.back_porch_h,
- timing.front_porch_h, timing.pulse_width_h,
- h_total);
-
- timing.active_v = pinfo->yres;
- timing.back_porch_v = pinfo->lcdc.v_back_porch;
- timing.front_porch_v = pinfo->lcdc.v_front_porch;
- timing.pulse_width_v = pinfo->lcdc.v_pulse_width;
-
- v_total = timing.active_v + timing.back_porch_v +
- timing.front_porch_v + timing.pulse_width_v;
-
- pr_debug("av=%d bpv=%d fpv=%d pwv=%d vt=%d\n",
- timing.active_v, timing.back_porch_v,
- timing.front_porch_v, timing.pulse_width_v, v_total);
-
- timing.pixel_freq = ((unsigned long int)pinfo->clk_rate / 1000);
- if (h_total && v_total) {
- timing.refresh_rate = ((timing.pixel_freq * 1000) /
- (h_total * v_total)) * 1000;
- } else {
- pr_err("cannot cal refresh rate\n");
- return -EPERM;
- }
-
- pr_debug("pixel_freq=%d refresh_rate=%d\n",
- timing.pixel_freq, timing.refresh_rate);
-
- new_vic = hdmi_get_video_id_code(&timing, ds_data);
- }
-
- return new_vic;
-}
-
static void hdmi_panel_update_dfps_data(struct hdmi_panel *panel)
{
struct mdss_panel_info *pinfo = panel->data->pinfo;
@@ -916,7 +848,6 @@ void *hdmi_panel_init(struct hdmi_panel_init_data *data)
if (data->ops) {
data->ops->on = hdmi_panel_power_on;
data->ops->off = hdmi_panel_power_off;
- data->ops->get_vic = hdmi_panel_get_vic;
data->ops->vendor = hdmi_panel_set_vendor_specific_infoframe;
data->ops->update_fps = hdmi_panel_update_fps;
}
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_panel.h b/drivers/video/fbdev/msm/mdss_hdmi_panel.h
index e5cc1486f222..6fa9af13d46e 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_panel.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_panel.h
@@ -48,15 +48,12 @@ struct hdmi_panel_data {
* @off: pointer to a function which powers off the panel
* @vendor: pointer to a function which programs vendor specific infoframe
* @update_fps: pointer to a function which updates fps
- * @get_vic: pointer to a function which get the vic from panel information.
*/
struct hdmi_panel_ops {
int (*on)(void *input);
int (*off)(void *input);
void (*vendor)(void *input);
int (*update_fps)(void *input, u32 fps);
- int (*get_vic)(struct mdss_panel_info *pinfo,
- struct hdmi_util_ds_data *ds_data);
};
/**
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index 37081e5e4a0b..ace796163fa4 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -1814,6 +1814,8 @@ static int hdmi_tx_init_hdcp(struct hdmi_tx_ctrl *hdmi_ctrl)
goto end;
} else {
hdmi_tx_set_fd(HDMI_TX_FEAT_HDCP, hdcp_data);
+ hdmi_ctrl->panel_data.panel_info.hdcp_1x_data =
+ hdcp_data;
DEV_DBG("%s: HDCP 1.4 initialized\n", __func__);
}
}
@@ -3115,9 +3117,8 @@ static int hdmi_tx_power_on(struct hdmi_tx_ctrl *hdmi_ctrl)
void *pdata = hdmi_tx_get_fd(HDMI_TX_FEAT_PANEL);
void *edata = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID);
- if (hdmi_ctrl->panel_ops.get_vic)
- hdmi_ctrl->vic = hdmi_ctrl->panel_ops.get_vic(
- &panel_data->panel_info, &hdmi_ctrl->ds_data);
+ hdmi_panel_get_vic(&panel_data->panel_info,
+ &hdmi_ctrl->ds_data);
if (hdmi_ctrl->vic <= 0) {
DEV_ERR("%s: invalid vic\n", __func__);
@@ -3703,9 +3704,7 @@ static int hdmi_tx_evt_handle_check_param(struct hdmi_tx_ctrl *hdmi_ctrl)
int new_vic = -1;
int rc = 0;
- if (hdmi_ctrl->panel_ops.get_vic)
- new_vic = hdmi_ctrl->panel_ops.get_vic(
- hdmi_ctrl->evt_arg, &hdmi_ctrl->ds_data);
+ new_vic = hdmi_panel_get_vic(hdmi_ctrl->evt_arg, &hdmi_ctrl->ds_data);
if ((new_vic < 0) || (new_vic > HDMI_VFRMT_MAX)) {
DEV_ERR("%s: invalid or not supported vic\n", __func__);
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c
index 555ba1ba5b1c..9ed909e9a387 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_util.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c
@@ -33,6 +33,74 @@ enum trigger_mode {
TRIGGER_READ
};
+int hdmi_panel_get_vic(struct mdss_panel_info *pinfo,
+ struct hdmi_util_ds_data *ds_data)
+{
+ int new_vic = -1;
+ u32 h_total, v_total;
+ struct msm_hdmi_mode_timing_info timing;
+
+ if (!pinfo) {
+ pr_err("invalid panel data\n");
+ return -EINVAL;
+ }
+
+ if (pinfo->vic) {
+ struct msm_hdmi_mode_timing_info info = {0};
+ u32 ret = hdmi_get_supported_mode(&info, ds_data, pinfo->vic);
+ u32 supported = info.supported;
+
+ if (!ret && supported) {
+ new_vic = pinfo->vic;
+ } else {
+ pr_err("invalid or not supported vic %d\n",
+ pinfo->vic);
+ return -EPERM;
+ }
+ } else {
+ timing.active_h = pinfo->xres;
+ timing.back_porch_h = pinfo->lcdc.h_back_porch;
+ timing.front_porch_h = pinfo->lcdc.h_front_porch;
+ timing.pulse_width_h = pinfo->lcdc.h_pulse_width;
+
+ h_total = timing.active_h + timing.back_porch_h +
+ timing.front_porch_h + timing.pulse_width_h;
+
+ pr_debug("ah=%d bph=%d fph=%d pwh=%d ht=%d\n",
+ timing.active_h, timing.back_porch_h,
+ timing.front_porch_h, timing.pulse_width_h,
+ h_total);
+
+ timing.active_v = pinfo->yres;
+ timing.back_porch_v = pinfo->lcdc.v_back_porch;
+ timing.front_porch_v = pinfo->lcdc.v_front_porch;
+ timing.pulse_width_v = pinfo->lcdc.v_pulse_width;
+
+ v_total = timing.active_v + timing.back_porch_v +
+ timing.front_porch_v + timing.pulse_width_v;
+
+ pr_debug("av=%d bpv=%d fpv=%d pwv=%d vt=%d\n",
+ timing.active_v, timing.back_porch_v,
+ timing.front_porch_v, timing.pulse_width_v, v_total);
+
+ timing.pixel_freq = ((unsigned long int)pinfo->clk_rate / 1000);
+ if (h_total && v_total) {
+ timing.refresh_rate = ((timing.pixel_freq * 1000) /
+ (h_total * v_total)) * 1000;
+ } else {
+ pr_err("cannot cal refresh rate\n");
+ return -EPERM;
+ }
+
+ pr_debug("pixel_freq=%d refresh_rate=%d\n",
+ timing.pixel_freq, timing.refresh_rate);
+
+ new_vic = hdmi_get_video_id_code(&timing, ds_data);
+ }
+
+ return new_vic;
+}
+
int hdmi_utils_get_timeout_in_hysnc(struct msm_hdmi_mode_timing_info *timing,
u32 timeout_ms)
{
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.h b/drivers/video/fbdev/msm/mdss_hdmi_util.h
index ceb171417822..e65cf915fe92 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_util.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_util.h
@@ -15,6 +15,8 @@
#include <linux/mdss_io_util.h>
#include "video/msm_hdmi_modes.h"
+#include "mdss_panel.h"
+
/* HDMI_TX Registers */
#define HDMI_CTRL (0x00000000)
#define HDMI_TEST_PATTERN (0x00000010)
@@ -489,6 +491,8 @@ const char *msm_hdmi_mode_2string(u32 mode);
int hdmi_set_resv_timing_info(struct msm_hdmi_mode_timing_info *mode);
bool hdmi_is_valid_resv_timing(int mode);
void hdmi_reset_resv_timing_info(void);
+int hdmi_panel_get_vic(struct mdss_panel_info *pinfo,
+ struct hdmi_util_ds_data *ds_data);
/* todo: Fix this. Right now this is defined in mdss_hdmi_tx.c */
void *hdmi_get_featuredata_from_sysfs_dev(struct device *device, u32 type);
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 1b5c1b7d51e1..81e3438befca 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -1831,6 +1831,20 @@ static u32 mdss_get_props(void)
return props;
}
+static void mdss_rpm_set_msg_ram(bool enable)
+{
+ u32 read_reg = 0;
+ void __iomem *rpm_msg_ram = ioremap(0x7781FC, 4);
+
+ if (rpm_msg_ram) {
+ writel_relaxed(enable, rpm_msg_ram);
+ read_reg = readl_relaxed(rpm_msg_ram);
+ pr_debug("%s enable=%d read_val=%x\n", __func__, enable,
+ read_reg);
+ iounmap(rpm_msg_ram);
+ }
+}
+
void mdss_mdp_init_default_prefill_factors(struct mdss_data_type *mdata)
{
mdata->prefill_data.prefill_factors.fmt_mt_nv12_factor = 8;
@@ -1883,7 +1897,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdata->pixel_ram_size = 50 * 1024;
set_bit(MDSS_QOS_PER_PIPE_IB, mdata->mdss_qos_map);
set_bit(MDSS_QOS_OVERHEAD_FACTOR, mdata->mdss_qos_map);
- set_bit(MDSS_QOS_CDP, mdata->mdss_qos_map);
+ set_bit(MDSS_QOS_CDP, mdata->mdss_qos_map); /* cdp supported */
+ mdata->enable_cdp = true; /* enable cdp */
set_bit(MDSS_QOS_OTLIM, mdata->mdss_qos_map);
set_bit(MDSS_QOS_PER_PIPE_LUT, mdata->mdss_qos_map);
set_bit(MDSS_QOS_SIMPLIFIED_PREFILL, mdata->mdss_qos_map);
@@ -1978,7 +1993,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_QOS_PER_PIPE_IB, mdata->mdss_qos_map);
set_bit(MDSS_QOS_REMAPPER, mdata->mdss_qos_map);
set_bit(MDSS_QOS_OVERHEAD_FACTOR, mdata->mdss_qos_map);
- set_bit(MDSS_QOS_CDP, mdata->mdss_qos_map);
+ set_bit(MDSS_QOS_CDP, mdata->mdss_qos_map); /* cdp supported */
+ mdata->enable_cdp = false; /* disable cdp */
set_bit(MDSS_QOS_OTLIM, mdata->mdss_qos_map);
set_bit(MDSS_QOS_PER_PIPE_LUT, mdata->mdss_qos_map);
set_bit(MDSS_QOS_SIMPLIFIED_PREFILL, mdata->mdss_qos_map);
@@ -1998,6 +2014,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdss_mdp_init_default_prefill_factors(mdata);
mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU);
mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT);
+ mdss_set_quirk(mdata, MDSS_QUIRK_MMSS_GDSC_COLLAPSE);
mdata->has_wb_ubwc = true;
set_bit(MDSS_CAPS_10_BIT_SUPPORTED, mdata->mdss_caps_map);
set_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map);
@@ -3382,14 +3399,17 @@ static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev)
mdata->has_panic_ctrl = of_property_read_bool(pdev->dev.of_node,
"qcom,mdss-has-panic-ctrl");
if (mdata->has_panic_ctrl) {
- mdss_mdp_parse_dt_pipe_panic_ctrl(pdev,
- "qcom,mdss-pipe-vig-panic-ctrl-offsets",
+ if (mdata->vig_pipes)
+ mdss_mdp_parse_dt_pipe_panic_ctrl(pdev,
+ "qcom,mdss-pipe-vig-panic-ctrl-offsets",
mdata->vig_pipes, mdata->nvig_pipes);
- mdss_mdp_parse_dt_pipe_panic_ctrl(pdev,
- "qcom,mdss-pipe-rgb-panic-ctrl-offsets",
+ if (mdata->rgb_pipes)
+ mdss_mdp_parse_dt_pipe_panic_ctrl(pdev,
+ "qcom,mdss-pipe-rgb-panic-ctrl-offsets",
mdata->rgb_pipes, mdata->nrgb_pipes);
- mdss_mdp_parse_dt_pipe_panic_ctrl(pdev,
- "qcom,mdss-pipe-dma-panic-ctrl-offsets",
+ if (mdata->dma_pipes)
+ mdss_mdp_parse_dt_pipe_panic_ctrl(pdev,
+ "qcom,mdss-pipe-dma-panic-ctrl-offsets",
mdata->dma_pipes, mdata->ndma_pipes);
}
@@ -4880,6 +4900,13 @@ static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on)
active_cnt = atomic_read(&mdata->active_intf_cnt);
if (active_cnt != 0) {
/*
+ * Advise RPM to not turn MMSS GDSC off during
+ * idle case.
+ */
+ if (mdss_has_quirk(mdata,
+ MDSS_QUIRK_MMSS_GDSC_COLLAPSE))
+ mdss_rpm_set_msg_ram(true);
+ /*
* Turning off GDSC while overlays are still
* active.
*/
@@ -4888,6 +4915,14 @@ static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on)
active_cnt);
mdss_mdp_memory_retention_enter();
} else {
+ /*
+ * Advise RPM to turn MMSS GDSC off during
+ * suspend case
+ */
+ if (mdss_has_quirk(mdata,
+ MDSS_QUIRK_MMSS_GDSC_COLLAPSE))
+ mdss_rpm_set_msg_ram(false);
+
mdss_mdp_cx_ctrl(mdata, false);
mdss_mdp_batfet_ctrl(mdata, false);
}
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_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index abc048866313..ebc7d2144eb9 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -91,7 +91,7 @@ static inline u64 apply_inverse_fudge_factor(u64 val,
static DEFINE_MUTEX(mdss_mdp_ctl_lock);
-static inline u32 mdss_mdp_get_pclk_rate(struct mdss_mdp_ctl *ctl)
+static inline u64 mdss_mdp_get_pclk_rate(struct mdss_mdp_ctl *ctl)
{
struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
@@ -1564,9 +1564,9 @@ static void __mdss_mdp_perf_calc_ctl_helper(struct mdss_mdp_ctl *ctl,
perf->prefill_bytes += tmp.prefill_bytes;
if (ctl->intf_type) {
- u32 clk_rate = mdss_mdp_get_pclk_rate(ctl);
+ u64 clk_rate = mdss_mdp_get_pclk_rate(ctl);
/* minimum clock rate due to inefficiency in 3dmux */
- clk_rate = mult_frac(clk_rate >> 1, 9, 8);
+ clk_rate = DIV_ROUND_UP_ULL((clk_rate >> 1) * 9, 8);
if (clk_rate > perf->mdp_clk_rate)
perf->mdp_clk_rate = clk_rate;
}
@@ -2040,12 +2040,11 @@ static u64 mdss_mdp_ctl_calc_client_vote(struct mdss_data_type *mdata,
return bw_sum_of_intfs;
}
-static void mdss_mdp_ctl_update_client_vote(struct mdss_data_type *mdata,
+/* apply any adjustments to the ib quota */
+static inline u64 __calc_bus_ib_quota(struct mdss_data_type *mdata,
struct mdss_mdp_perf_params *perf, bool nrt_client, u64 bw_vote)
{
- u64 bus_ab_quota, bus_ib_quota;
-
- bus_ab_quota = max(bw_vote, mdata->perf_tune.min_bus_vote);
+ u64 bus_ib_quota;
if (test_bit(MDSS_QOS_PER_PIPE_IB, mdata->mdss_qos_map)) {
if (!nrt_client)
@@ -2071,6 +2070,18 @@ static void mdss_mdp_ctl_update_client_vote(struct mdss_data_type *mdata,
bus_ib_quota = apply_fudge_factor(bus_ib_quota,
&mdata->per_pipe_ib_factor);
+ return bus_ib_quota;
+}
+
+static void mdss_mdp_ctl_update_client_vote(struct mdss_data_type *mdata,
+ struct mdss_mdp_perf_params *perf, bool nrt_client, u64 bw_vote)
+{
+ u64 bus_ab_quota, bus_ib_quota;
+
+ bus_ab_quota = max(bw_vote, mdata->perf_tune.min_bus_vote);
+ bus_ib_quota = __calc_bus_ib_quota(mdata, perf, nrt_client, bw_vote);
+
+
bus_ab_quota = apply_fudge_factor(bus_ab_quota, &mdss_res->ab_factor);
ATRACE_INT("bus_quota", bus_ib_quota);
@@ -2232,6 +2243,46 @@ static bool is_traffic_shaper_enabled(struct mdss_data_type *mdata)
return false;
}
+static bool __mdss_mdp_compare_bw(
+ struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_perf_params *new_perf,
+ struct mdss_mdp_perf_params *old_perf,
+ bool params_changed,
+ bool stop_req)
+{
+ struct mdss_data_type *mdata = ctl->mdata;
+ bool is_nrt = mdss_mdp_is_nrt_ctl_path(ctl);
+ u64 new_ib =
+ __calc_bus_ib_quota(mdata, new_perf, is_nrt, new_perf->bw_ctl);
+ u64 old_ib =
+ __calc_bus_ib_quota(mdata, old_perf, is_nrt, old_perf->bw_ctl);
+ u64 max_new_bw = max(new_perf->bw_ctl, new_ib);
+ u64 max_old_bw = max(old_perf->bw_ctl, old_ib);
+ bool update_bw = false;
+
+ /*
+ * three cases for bus bandwidth update.
+ * 1. new bandwidth vote (ab or ib) or writeback output vote
+ * are higher than current vote for update request.
+ * 2. new bandwidth vote or writeback output vote are
+ * lower than current vote at end of commit or stop.
+ * 3. end of writeback/rotator session - last chance to
+ * non-realtime remove vote.
+ */
+ if ((params_changed && ((max_new_bw > max_old_bw) || /* ab and ib bw */
+ (new_perf->bw_writeback > old_perf->bw_writeback))) ||
+ (!params_changed && ((max_new_bw < max_old_bw) ||
+ (new_perf->bw_writeback < old_perf->bw_writeback))) ||
+ (stop_req && is_nrt))
+ update_bw = true;
+
+ trace_mdp_compare_bw(new_perf->bw_ctl, new_ib, new_perf->bw_writeback,
+ max_new_bw, old_perf->bw_ctl, old_ib, old_perf->bw_writeback,
+ max_old_bw, params_changed, update_bw);
+
+ return update_bw;
+}
+
static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl,
int params_changed, bool stop_req)
{
@@ -2267,20 +2318,8 @@ static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl,
else if (is_bw_released || params_changed)
mdss_mdp_perf_calc_ctl(ctl, new);
- /*
- * three cases for bus bandwidth update.
- * 1. new bandwidth vote or writeback output vote
- * are higher than current vote for update request.
- * 2. new bandwidth vote or writeback output vote are
- * lower than current vote at end of commit or stop.
- * 3. end of writeback/rotator session - last chance to
- * non-realtime remove vote.
- */
- if ((params_changed && ((new->bw_ctl > old->bw_ctl) ||
- (new->bw_writeback > old->bw_writeback))) ||
- (!params_changed && ((new->bw_ctl < old->bw_ctl) ||
- (new->bw_writeback < old->bw_writeback))) ||
- (stop_req && mdss_mdp_is_nrt_ctl_path(ctl))) {
+ if (__mdss_mdp_compare_bw(ctl, new, old, params_changed,
+ stop_req)) {
pr_debug("c=%d p=%d new_bw=%llu,old_bw=%llu\n",
ctl->num, params_changed, new->bw_ctl,
@@ -2925,6 +2964,7 @@ static u32 __dsc_get_common_mode(struct mdss_mdp_ctl *ctl, bool mux_3d)
static void __dsc_get_pic_dim(struct mdss_mdp_mixer *mixer_l,
struct mdss_mdp_mixer *mixer_r, u32 *pic_w, u32 *pic_h)
{
+ struct mdss_data_type *mdata = NULL;
bool valid_l = mixer_l && mixer_l->valid_roi;
bool valid_r = mixer_r && mixer_r->valid_roi;
@@ -2932,13 +2972,29 @@ static void __dsc_get_pic_dim(struct mdss_mdp_mixer *mixer_l,
*pic_h = 0;
if (valid_l) {
- *pic_w = mixer_l->roi.w;
- *pic_h = mixer_l->roi.h;
+ mdata = mixer_l->ctl->mdata;
+ if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) &&
+ mixer_l->ds &&
+ (mixer_l->ds->flags & DS_ENABLE)) {
+ *pic_w = mixer_l->ds->scaler.dst_width;
+ *pic_h = mixer_l->ds->scaler.dst_height;
+ } else {
+ *pic_w = mixer_l->roi.w;
+ *pic_h = mixer_l->roi.h;
+ }
}
if (valid_r) {
- *pic_w += mixer_r->roi.w;
- *pic_h = mixer_r->roi.h;
+ mdata = mixer_r->ctl->mdata;
+ if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) &&
+ mixer_r->ds &&
+ (mixer_r->ds->flags & DS_ENABLE)) {
+ *pic_w += mixer_r->ds->scaler.dst_width;
+ *pic_h = mixer_r->ds->scaler.dst_height;
+ } else {
+ *pic_w += mixer_r->roi.w;
+ *pic_h = mixer_r->roi.h;
+ }
}
}
@@ -5302,7 +5358,8 @@ int mdss_mdp_display_wakeup_time(struct mdss_mdp_ctl *ctl,
ktime_t *wakeup_time)
{
struct mdss_panel_info *pinfo;
- u32 clk_rate, clk_period;
+ u64 clk_rate;
+ u32 clk_period;
u32 current_line, total_line;
u32 time_of_line, time_to_vsync, adjust_line_ns;
@@ -5317,7 +5374,7 @@ int mdss_mdp_display_wakeup_time(struct mdss_mdp_ctl *ctl,
clk_rate = mdss_mdp_get_pclk_rate(ctl);
- clk_rate /= 1000; /* in kHz */
+ clk_rate = DIV_ROUND_UP_ULL(clk_rate, 1000); /* in kHz */
if (!clk_rate)
return -EINVAL;
@@ -5326,7 +5383,7 @@ int mdss_mdp_display_wakeup_time(struct mdss_mdp_ctl *ctl,
* accuracy with high pclk rate and this number is in 17 bit
* range.
*/
- clk_period = 1000000000 / clk_rate;
+ clk_period = DIV_ROUND_UP_ULL(1000000000, clk_rate);
if (!clk_period)
return -EINVAL;
@@ -5365,7 +5422,7 @@ int mdss_mdp_display_wakeup_time(struct mdss_mdp_ctl *ctl,
*wakeup_time = ktime_add_ns(current_time, time_to_vsync);
- pr_debug("clk_rate=%dkHz clk_period=%d cur_line=%d tot_line=%d\n",
+ pr_debug("clk_rate=%lldkHz clk_period=%d cur_line=%d tot_line=%d\n",
clk_rate, clk_period, current_line, total_line);
pr_debug("time_to_vsync=%d current_time=%d wakeup_time=%d\n",
time_to_vsync, (int)ktime_to_ms(current_time),
diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c
index 9e3c2b7cdd1b..4c4fa9ea98d0 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_debug.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c
@@ -1715,17 +1715,26 @@ static struct debug_bus dbg_bus_msmcobalt[] = {
{ 0x418, 60, 0},
};
-
static struct vbif_debug_bus vbif_dbg_bus_8996[] = {
- {0x214, 0x21c, 16, 2, 0x10}, /* arb clients */
- {0x214, 0x21c, 0, 14, 0x13}, /* xin blocks - axi side */
- {0x21c, 0x214, 0, 14, 0xc}, /* xin blocks - clock side */
+ {0x214, 0x21c, 16, 2, 0, 0x10}, /* arb clients */
+ {0x214, 0x21c, 0, 14, 0, 0x13}, /* xin blocks - axi side */
+ {0x21c, 0x214, 0, 14, 0, 0xc}, /* xin blocks - clock side */
};
static struct vbif_debug_bus nrt_vbif_dbg_bus_8996[] = {
- {0x214, 0x21c, 16, 1, 0x10}, /* arb clients */
- {0x214, 0x21c, 0, 12, 0x13}, /* xin blocks - axi side */
- {0x21c, 0x214, 0, 12, 0xc}, /* xin blocks - clock side */
+ {0x214, 0x21c, 16, 1, 0, 0x10}, /* arb clients */
+ {0x214, 0x21c, 0, 12, 0, 0x13}, /* xin blocks - axi side */
+ {0x21c, 0x214, 0, 12, 0, 0xc}, /* xin blocks - clock side */
+};
+
+static struct vbif_debug_bus vbif_dbg_bus_msmcobalt[] = {
+ {0x214, 0x21c, 16, 2, 0x0, 0xd}, /* arb clients */
+ {0x214, 0x21c, 16, 2, 0x80, 0xc0}, /* arb clients */
+ {0x214, 0x21c, 16, 2, 0x100, 0x140}, /* arb clients */
+ {0x214, 0x21c, 0, 16, 0x0, 0xf}, /* xin blocks - axi side */
+ {0x214, 0x21c, 0, 16, 0x80, 0xa4}, /* xin blocks - axi side */
+ {0x214, 0x21c, 0, 15, 0x100, 0x124}, /* xin blocks - axi side */
+ {0x21c, 0x214, 0, 14, 0, 0xc}, /* xin blocks - clock side */
};
void mdss_mdp_hw_rev_debug_caps_init(struct mdss_data_type *mdata)
@@ -1750,8 +1759,8 @@ void mdss_mdp_hw_rev_debug_caps_init(struct mdss_data_type *mdata)
case MDSS_MDP_HW_REV_301:
mdata->dbg_bus = dbg_bus_msmcobalt;
mdata->dbg_bus_size = ARRAY_SIZE(dbg_bus_msmcobalt);
- mdata->vbif_dbg_bus = vbif_dbg_bus_8996;
- mdata->vbif_dbg_bus_size = ARRAY_SIZE(vbif_dbg_bus_8996);
+ mdata->vbif_dbg_bus = vbif_dbg_bus_msmcobalt;
+ mdata->vbif_dbg_bus_size = ARRAY_SIZE(vbif_dbg_bus_msmcobalt);
mdata->nrt_vbif_dbg_bus = nrt_vbif_dbg_bus_8996;
mdata->nrt_vbif_dbg_bus_size =
ARRAY_SIZE(nrt_vbif_dbg_bus_8996);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
index 5d8c83126b1b..76fd2d12ac95 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
@@ -822,6 +822,7 @@ enum mdss_mdp_pingpong_index {
#define MDSS_MDP_DSPP_DEBUGBUS_STATUS 0x34C
/* Following offsets are with respect to MDP base */
+#define MDSS_MDP_HDMI_DP_CORE_SELECT 0x408
#define MDSS_MDP_MDP_OUT_CTL_0 0x410
#define MDSS_MDP_INTF_CMD_MISR_CTRL (MDSS_MDP_INTF_MISR_CTRL + 0x8)
#define MDSS_MDP_INTF_CMD_MISR_SIGNATURE (MDSS_MDP_INTF_MISR_CTRL + 0xC)
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_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index 3aadb3950442..d316ab6d263a 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -316,7 +316,8 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
struct mdss_mdp_ctl *ctl = data;
struct mdss_panel_info *pinfo;
u32 line_cnt, min_ln_cnt, active_lns_cnt;
- u32 clk_rate, clk_period, time_of_line;
+ u64 clk_rate;
+ u32 clk_period, time_of_line;
u32 delay;
if (!data) {
@@ -344,7 +345,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
pinfo->mipi.dsi_pclk_rate :
pinfo->clk_rate);
- clk_rate /= 1000; /* in kHz */
+ clk_rate = DIV_ROUND_UP_ULL(clk_rate, 1000); /* in kHz */
if (!clk_rate) {
pr_err("Unable to get proper clk_rate\n");
return;
@@ -354,7 +355,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
* accuracy with high pclk rate and this number is in 17 bit
* range.
*/
- clk_period = 1000000000 / clk_rate;
+ clk_period = DIV_ROUND_UP_ULL(1000000000, clk_rate);
if (!clk_period) {
pr_err("Unable to calculate clock period\n");
return;
@@ -1851,6 +1852,7 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
u32 dst_bpp;
struct mdss_data_type *mdata = ctl->mdata;
struct dsc_desc *dsc = NULL;
+ u32 hdmi_dp_core;
ctx->ctl = ctl;
ctx->intf_type = ctl->intf_type;
@@ -1972,6 +1974,11 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
mdp_video_write(ctx, MDSS_MDP_REG_INTF_PANEL_FORMAT, ctl->dst_format);
+ hdmi_dp_core = (ctx->intf_type == MDSS_INTF_EDP) ? 1 : 0;
+
+ writel_relaxed(hdmi_dp_core, mdata->mdp_base +
+ MDSS_MDP_HDMI_DP_CORE_SELECT);
+
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index d3a836ed2519..91d4332700b6 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -137,10 +137,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
if ((ds_data->lm_width > get_panel_xres(pinfo)) ||
(ds_data->lm_height > get_panel_yres(pinfo)) ||
(ds_data->lm_width == 0) ||
- (ds_data->lm_height == 0) ||
- (is_dsc_compression(pinfo) &&
- !is_lm_configs_dsc_compatible(pinfo,
- ds_data->lm_width, ds_data->lm_height))) {
+ (ds_data->lm_height == 0)) {
pr_err("Invalid left LM {%d,%d} setting\n",
ds_data->lm_width, ds_data->lm_height);
return -EINVAL;
@@ -167,10 +164,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
if ((ds_data->lm_width > get_panel_xres(pinfo)) ||
(ds_data->lm_height > get_panel_yres(pinfo)) ||
(ds_data->lm_width == 0) ||
- (ds_data->lm_height == 0) ||
- (is_dsc_compression(pinfo) &&
- !is_lm_configs_dsc_compatible(pinfo,
- ds_data->lm_width, ds_data->lm_height))) {
+ (ds_data->lm_height == 0)) {
pr_err("Invalid right LM {%d,%d} setting\n",
ds_data->lm_width, ds_data->lm_height);
return -EINVAL;
@@ -217,6 +211,8 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd,
struct mdss_mdp_ctl *ctl;
struct mdss_mdp_destination_scaler *ds_left = NULL;
struct mdss_mdp_destination_scaler *ds_right = NULL;
+ struct mdss_panel_info *pinfo;
+ u32 scaler_width, scaler_height;
if (ds_data) {
mdata = mfd_to_mdata(mfd);
@@ -293,6 +289,31 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd,
if (ds_right)
pr_debug("DS_RIGHT: flags=0x%X\n", ds_right->flags);
+ /*
+ * When DSC is enabled, make sure the scaler output dimension is
+ * correctly setup.
+ */
+ pinfo = &ctl->panel_data->panel_info;
+ scaler_width = 0;
+ scaler_height = 0;
+ if (ds_left && ds_left->flags) {
+ scaler_width += ds_left->scaler.dst_width;
+ scaler_height = ds_left->scaler.dst_height;
+ }
+ if (ds_right && ds_right->flags) {
+ scaler_width += ds_right->scaler.dst_width;
+ scaler_height = ds_right->scaler.dst_height;
+ }
+ pr_debug("DS output dimension: %dx%d\n", scaler_width, scaler_height);
+
+ if (ds_data[0].flags && (is_dsc_compression(pinfo) &&
+ !is_lm_configs_dsc_compatible(pinfo,
+ scaler_width, scaler_height))) {
+ pr_err("Invalid Dest-scaler output width/height: %d/%d\n",
+ scaler_width, scaler_height);
+ ret = -EINVAL;
+ }
+
return ret;
}
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_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
index 1eb695200dfe..8f211a977aa4 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
@@ -2066,8 +2066,9 @@ static void mdss_mdp_set_pipe_cdp(struct mdss_mdp_pipe *pipe)
u32 cdp_settings = 0x0;
bool is_rotator = (pipe->mixer_left && pipe->mixer_left->rotator_mode);
- /* Disable CDP for rotator pipe in v1 */
- if (is_rotator && mdss_has_quirk(mdata, MDSS_QUIRK_ROTCDP))
+ /* Disable CDP for rotator pipe or if not requested for the target */
+ if (!mdata->enable_cdp || (is_rotator &&
+ mdss_has_quirk(mdata, MDSS_QUIRK_ROTCDP)))
goto exit;
cdp_settings = MDSS_MDP_CDP_ENABLE;
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_mdp_trace.h b/drivers/video/fbdev/msm/mdss_mdp_trace.h
index 85829fbba075..648e4fcd1cd2 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_trace.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_trace.h
@@ -295,6 +295,46 @@ TRACE_EVENT(mdp_perf_update_bus,
__entry->ib_quota)
);
+TRACE_EVENT(mdp_compare_bw,
+ TP_PROTO(unsigned long long new_ab, unsigned long long new_ib,
+ unsigned long long new_wb, unsigned long long new_max,
+ unsigned long long old_ab, unsigned long long old_ib,
+ unsigned long long old_wb, unsigned long long old_max,
+ u32 params_changed, bool update_bw),
+ TP_ARGS(new_ab, new_ib, new_wb, new_max,
+ old_ab, old_ib, old_wb, old_max,
+ params_changed, update_bw),
+ TP_STRUCT__entry(
+ __field(u64, new_ab)
+ __field(u64, new_ib)
+ __field(u64, new_wb)
+ __field(u64, new_max)
+ __field(u64, old_ab)
+ __field(u64, old_ib)
+ __field(u64, old_wb)
+ __field(u64, old_max)
+ __field(u32, params_changed)
+ __field(bool, update_bw)
+ ),
+ TP_fast_assign(
+ __entry->new_ab = new_ab;
+ __entry->new_ib = new_ib;
+ __entry->new_wb = new_wb;
+ __entry->new_max = new_max;
+ __entry->old_ab = old_ab;
+ __entry->old_ib = old_ib;
+ __entry->old_wb = old_wb;
+ __entry->old_max = old_max;
+ __entry->params_changed = params_changed;
+ __entry->update_bw = update_bw;
+ ),
+ TP_printk("[ab,ib,wb,max] new[%llu, %llu, %llu, %llu] old[%llu, %llu, %llu, %llu] parm:%d ret:%d",
+ __entry->new_ab, __entry->new_ib, __entry->new_wb,
+ __entry->new_max, __entry->old_ab, __entry->old_ib,
+ __entry->old_wb, __entry->old_max, __entry->params_changed,
+ __entry->update_bw)
+);
+
TRACE_EVENT(mdp_misr_crc,
TP_PROTO(u32 block_id, u32 vsync_cnt, u32 crc),
TP_ARGS(block_id, vsync_cnt, crc),
diff --git a/drivers/video/fbdev/msm/mdss_mdp_wfd.c b/drivers/video/fbdev/msm/mdss_mdp_wfd.c
index 656967831810..f04450e9974c 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_wfd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_wfd.c
@@ -430,7 +430,7 @@ int mdss_mdp_cwb_validate(struct msm_fb_data_type *mfd,
return rc;
fmt = mdss_mdp_get_format_params(layer->buffer.format);
- if (!(fmt->flag & VALID_MDP_WB_INTF_FORMAT)) {
+ if (!fmt || (fmt && !(fmt->flag & VALID_MDP_WB_INTF_FORMAT))) {
pr_err("wb does not support dst fmt:%d\n",
layer->buffer.format);
return -EINVAL;
diff --git a/drivers/video/fbdev/msm/mdss_panel.c b/drivers/video/fbdev/msm/mdss_panel.c
index 97025b3a9c23..16c2d4e6e92d 100644
--- a/drivers/video/fbdev/msm/mdss_panel.c
+++ b/drivers/video/fbdev/msm/mdss_panel.c
@@ -426,8 +426,8 @@ int mdss_panel_debugfs_panel_setup(struct mdss_panel_debugfs_info *debugfs_info,
(u32 *)&debugfs_info->panel_info.min_fps);
debugfs_create_u32("max_refresh_rate", 0644, debugfs_info->root,
(u32 *)&debugfs_info->panel_info.max_fps);
- debugfs_create_u32("clk_rate", 0644, debugfs_info->root,
- (u32 *)&debugfs_info->panel_info.clk_rate);
+ debugfs_create_u64("clk_rate", 0644, debugfs_info->root,
+ (u64 *)&debugfs_info->panel_info.clk_rate);
debugfs_create_u32("bl_min", 0644, debugfs_info->root,
(u32 *)&debugfs_info->panel_info.bl_min);
debugfs_create_u32("bl_max", 0644, debugfs_info->root,
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index 81b6fa7d35b3..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,
};
@@ -688,9 +690,11 @@ struct mdss_panel_info {
void *edid_data;
void *dba_data;
void *cec_data;
+ void *hdcp_1x_data;
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
@@ -788,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/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index 1f62232e196b..b5b5a026733e 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -2341,18 +2341,8 @@ int mdss_dsi_post_clkoff_cb(void *priv,
pdata = &ctrl->panel_data;
for (i = DSI_MAX_PM - 1; i >= DSI_CORE_PM; i--) {
- /*
- * if DSI state is active
- * 1. allow to turn off the core power module.
- * 2. allow to turn off phy power module if it is
- * turned off
- *
- * allow to turn off all power modules if DSI is not
- * active
- */
if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) &&
- (i != DSI_CORE_PM) &&
- (ctrl->phy_power_off && (i != DSI_PHY_PM)))
+ (i != DSI_CORE_PM))
continue;
rc = msm_dss_enable_vreg(
sdata->power_data[i].vreg_config,
@@ -2397,15 +2387,12 @@ int mdss_dsi_pre_clkon_cb(void *priv,
* 3.> CTRL_PM need to be enabled/disabled
* only during unblank/blank. Their state should
* not be changed during static screen.
- * 4.> PHY_PM can be turned enabled/disabled
- * if phy regulators are enabled/disabled.
*/
pr_debug("%s: Enable DSI core power\n", __func__);
for (i = DSI_CORE_PM; i < DSI_MAX_PM; i++) {
if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) &&
(!pdata->panel_info.cont_splash_enabled) &&
- (i != DSI_CORE_PM) &&
- (ctrl->phy_power_off && (i != DSI_PHY_PM)))
+ (i != DSI_CORE_PM))
continue;
rc = msm_dss_enable_vreg(
sdata->power_data[i].vreg_config,
diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c
index 866bb18efefe..e818f5ac7a26 100644
--- a/fs/ecryptfs/kthread.c
+++ b/fs/ecryptfs/kthread.c
@@ -25,6 +25,7 @@
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/mount.h>
+#include <linux/file.h>
#include "ecryptfs_kernel.h"
struct ecryptfs_open_req {
@@ -147,7 +148,7 @@ int ecryptfs_privileged_open(struct file **lower_file,
flags |= IS_RDONLY(d_inode(lower_dentry)) ? O_RDONLY : O_RDWR;
(*lower_file) = dentry_open(&req.path, flags, cred);
if (!IS_ERR(*lower_file))
- goto out;
+ goto have_file;
if ((flags & O_ACCMODE) == O_RDONLY) {
rc = PTR_ERR((*lower_file));
goto out;
@@ -165,8 +166,16 @@ int ecryptfs_privileged_open(struct file **lower_file,
mutex_unlock(&ecryptfs_kthread_ctl.mux);
wake_up(&ecryptfs_kthread_ctl.wait);
wait_for_completion(&req.done);
- if (IS_ERR(*lower_file))
+ if (IS_ERR(*lower_file)) {
rc = PTR_ERR(*lower_file);
+ goto out;
+ }
+have_file:
+ if ((*lower_file)->f_op->mmap == NULL) {
+ fput(*lower_file);
+ *lower_file = NULL;
+ rc = -EMEDIUMTYPE;
+ }
out:
return rc;
}
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/fs/proc/root.c b/fs/proc/root.c
index 361ab4ee42fc..ec649c92d270 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -121,6 +121,13 @@ static struct dentry *proc_mount(struct file_system_type *fs_type,
if (IS_ERR(sb))
return ERR_CAST(sb);
+ /*
+ * procfs isn't actually a stacking filesystem; however, there is
+ * too much magic going on inside it to permit stacking things on
+ * top of it
+ */
+ sb->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
+
if (!proc_parse_options(options, ns)) {
deactivate_locked_super(sb);
return ERR_PTR(-EINVAL);
diff --git a/include/dt-bindings/clock/qcom,gcc-msmfalcon.h b/include/dt-bindings/clock/qcom,gcc-msmfalcon.h
index 6860d78e020e..609a20422ed1 100644
--- a/include/dt-bindings/clock/qcom,gcc-msmfalcon.h
+++ b/include/dt-bindings/clock/qcom,gcc-msmfalcon.h
@@ -14,173 +14,189 @@
#ifndef _DT_BINDINGS_CLK_MSM_GCC_FALCON_H
#define _DT_BINDINGS_CLK_MSM_GCC_FALCON_H
-#define BIMC_HMSS_AXI_CLK_SRC 0
-#define BLSP1_QUP1_I2C_APPS_CLK_SRC 1
-#define BLSP1_QUP1_SPI_APPS_CLK_SRC 2
-#define BLSP1_QUP2_I2C_APPS_CLK_SRC 3
-#define BLSP1_QUP2_SPI_APPS_CLK_SRC 4
-#define BLSP1_QUP3_I2C_APPS_CLK_SRC 5
-#define BLSP1_QUP3_SPI_APPS_CLK_SRC 6
-#define BLSP1_QUP4_I2C_APPS_CLK_SRC 7
-#define BLSP1_QUP4_SPI_APPS_CLK_SRC 8
-#define BLSP1_UART1_APPS_CLK_SRC 9
-#define BLSP1_UART2_APPS_CLK_SRC 10
-#define BLSP2_QUP1_I2C_APPS_CLK_SRC 11
-#define BLSP2_QUP1_SPI_APPS_CLK_SRC 12
-#define BLSP2_QUP2_I2C_APPS_CLK_SRC 13
-#define BLSP2_QUP2_SPI_APPS_CLK_SRC 14
-#define BLSP2_QUP3_I2C_APPS_CLK_SRC 15
-#define BLSP2_QUP3_SPI_APPS_CLK_SRC 16
-#define BLSP2_QUP4_I2C_APPS_CLK_SRC 17
-#define BLSP2_QUP4_SPI_APPS_CLK_SRC 18
-#define BLSP2_UART1_APPS_CLK_SRC 19
-#define BLSP2_UART2_APPS_CLK_SRC 20
-#define GCC_AGGRE2_UFS_AXI_CLK 21
-#define GCC_AGGRE2_USB3_AXI_CLK 22
-#define GCC_BIMC_GFX_CLK 23
-#define GCC_BIMC_HMSS_AXI_CLK 24
-#define GCC_BIMC_MSS_Q6_AXI_CLK 25
-#define GCC_BLSP1_AHB_CLK 26
-#define GCC_BLSP1_QUP1_I2C_APPS_CLK 27
-#define GCC_BLSP1_QUP1_SPI_APPS_CLK 28
-#define GCC_BLSP1_QUP2_I2C_APPS_CLK 29
-#define GCC_BLSP1_QUP2_SPI_APPS_CLK 30
-#define GCC_BLSP1_QUP3_I2C_APPS_CLK 31
-#define GCC_BLSP1_QUP3_SPI_APPS_CLK 32
-#define GCC_BLSP1_QUP4_I2C_APPS_CLK 33
-#define GCC_BLSP1_QUP4_SPI_APPS_CLK 34
-#define GCC_BLSP1_UART1_APPS_CLK 35
-#define GCC_BLSP1_UART2_APPS_CLK 36
-#define GCC_BLSP2_AHB_CLK 37
-#define GCC_BLSP2_QUP1_I2C_APPS_CLK 38
-#define GCC_BLSP2_QUP1_SPI_APPS_CLK 39
-#define GCC_BLSP2_QUP2_I2C_APPS_CLK 40
-#define GCC_BLSP2_QUP2_SPI_APPS_CLK 41
-#define GCC_BLSP2_QUP3_I2C_APPS_CLK 42
-#define GCC_BLSP2_QUP3_SPI_APPS_CLK 43
-#define GCC_BLSP2_QUP4_I2C_APPS_CLK 44
-#define GCC_BLSP2_QUP4_SPI_APPS_CLK 45
-#define GCC_BLSP2_UART1_APPS_CLK 46
-#define GCC_BLSP2_UART2_APPS_CLK 47
-#define GCC_BOOT_ROM_AHB_CLK 48
-#define GCC_CFG_NOC_USB2_AXI_CLK 49
-#define GCC_CFG_NOC_USB3_AXI_CLK 50
-#define GCC_GLM_AHB_CLK 51
-#define GCC_GLM_CLK 52
-#define GCC_GP1_CLK 53
-#define GCC_GP2_CLK 54
-#define GCC_GP3_CLK 55
-#define GCC_GPU_BIMC_GFX_CLK 56
-#define GCC_GPU_BIMC_GFX_SRC_CLK 57
-#define GCC_GPU_CFG_AHB_CLK 58
-#define GCC_GPU_SNOC_DVM_GFX_CLK 59
-#define GCC_HMSS_AHB_CLK 60
-#define GCC_HMSS_DVM_BUS_CLK 61
-#define GCC_HMSS_RBCPR_CLK 62
-#define GCC_MMSS_NOC_CFG_AHB_CLK 63
-#define GCC_MMSS_QM_AHB_CLK 64
-#define GCC_MMSS_QM_CORE_CLK 65
-#define GCC_MMSS_SYS_NOC_AXI_CLK 66
-#define GCC_PDM2_CLK 67
-#define GCC_PDM_AHB_CLK 68
-#define GCC_PRNG_AHB_CLK 69
-#define GCC_QSPI_AHB_CLK 70
-#define GCC_QSPI_SER_CLK 71
-#define GCC_SDCC1_AHB_CLK 72
-#define GCC_SDCC1_APPS_CLK 73
-#define GCC_SDCC1_ICE_CORE_CLK 74
-#define GCC_SDCC2_AHB_CLK 75
-#define GCC_SDCC2_APPS_CLK 76
-#define GCC_UFS_AHB_CLK 77
-#define GCC_UFS_AXI_CLK 78
-#define GCC_UFS_ICE_CORE_CLK 79
-#define GCC_UFS_PHY_AUX_CLK 80
-#define GCC_UFS_RX_SYMBOL_0_CLK 81
-#define GCC_UFS_RX_SYMBOL_1_CLK 82
-#define GCC_UFS_TX_SYMBOL_0_CLK 83
-#define GCC_UFS_UNIPRO_CORE_CLK 84
-#define GCC_USB20_MASTER_CLK 85
-#define GCC_USB20_MOCK_UTMI_CLK 86
-#define GCC_USB20_SLEEP_CLK 87
-#define GCC_USB30_MASTER_CLK 88
-#define GCC_USB30_MOCK_UTMI_CLK 89
-#define GCC_USB30_SLEEP_CLK 90
-#define GCC_USB3_PHY_AUX_CLK 91
-#define GCC_USB3_PHY_PIPE_CLK 92
-#define GCC_USB_PHY_CFG_AHB2PHY_CLK 93
-#define GCC_WCSS_AHB_S0_CLK 94
-#define GCC_WCSS_AXI_M_CLK 95
-#define GCC_WCSS_ECAHB_CLK 96
-#define GCC_WCSS_SHDREG_AHB_CLK 97
-#define GLM_CLK_SRC 98
-#define GP1_CLK_SRC 99
-#define GP2_CLK_SRC 100
-#define GP3_CLK_SRC 101
-#define GPLL0 102
-#define GPLL0_OUT_AUX 103
-#define GPLL0_OUT_AUX2 104
-#define GPLL0_OUT_EARLY 105
-#define GPLL0_OUT_MAIN 106
-#define GPLL0_OUT_TEST 107
-#define GPLL1 108
-#define GPLL1_OUT_AUX 109
-#define GPLL1_OUT_AUX2 110
-#define GPLL1_OUT_EARLY 111
-#define GPLL1_OUT_MAIN 112
-#define GPLL1_OUT_TEST 113
-#define GPLL2 114
-#define GPLL2_OUT_AUX 115
-#define GPLL2_OUT_AUX2 116
-#define GPLL2_OUT_EARLY 117
-#define GPLL2_OUT_MAIN 118
-#define GPLL2_OUT_TEST 119
-#define GPLL3 120
-#define GPLL3_OUT_AUX 121
-#define GPLL3_OUT_AUX2 122
-#define GPLL3_OUT_EARLY 123
-#define GPLL3_OUT_MAIN 124
-#define GPLL3_OUT_TEST 125
-#define GPLL4 126
-#define GPLL4_OUT_AUX 127
-#define GPLL4_OUT_AUX2 128
-#define GPLL4_OUT_EARLY 129
-#define GPLL4_OUT_MAIN 130
-#define GPLL4_OUT_TEST 131
-#define GPLL5 132
-#define GPLL5_OUT_AUX 133
-#define GPLL5_OUT_AUX2 134
-#define GPLL5_OUT_EARLY 135
-#define GPLL5_OUT_MAIN 136
-#define GPLL5_OUT_TEST 137
-#define GPLL6 138
-#define GPLL6_OUT_AUX 139
-#define GPLL6_OUT_AUX2 140
-#define GPLL6_OUT_EARLY 141
-#define GPLL6_OUT_MAIN 142
-#define GPLL6_OUT_TEST 143
-#define HMSS_AHB_CLK_SRC 144
-#define HMSS_GPLL0_CLK_SRC 145
-#define HMSS_GPLL4_CLK_SRC 146
-#define HMSS_RBCPR_CLK_SRC 147
-#define MMSS_QM_CORE_CLK_SRC 148
-#define PDM2_CLK_SRC 149
-#define QSPI_SER_CLK_SRC 150
-#define SDCC1_APPS_CLK_SRC 151
-#define SDCC1_ICE_CORE_CLK_SRC 152
-#define SDCC2_APPS_CLK_SRC 153
-#define UFS_AXI_CLK_SRC 154
-#define UFS_ICE_CORE_CLK_SRC 155
-#define UFS_PHY_AUX_CLK_SRC 156
-#define UFS_UNIPRO_CORE_CLK_SRC 157
-#define USB20_MASTER_CLK_SRC 158
-#define USB20_MOCK_UTMI_CLK_SRC 159
-#define USB30_MASTER_CLK_SRC 160
-#define USB30_MOCK_UTMI_CLK_SRC 161
-#define USB3_PHY_AUX_CLK_SRC 162
+/* Hardware/Dummy/Voter clocks */
+#define GCC_XO 0
+#define GCC_GPLL0_EARLY_DIV 1
+#define GCC_GPLL1_EARLY_DIV 2
+#define GCC_CE1_AHB_M_CLK 3
+#define GCC_CE1_AXI_M_CLK 4
-#define UFS_GDSC 0
-#define USB_30_GDSC 1
+/* RCGs and Branches */
+#define BLSP1_QUP1_I2C_APPS_CLK_SRC 10
+#define BLSP1_QUP1_SPI_APPS_CLK_SRC 11
+#define BLSP1_QUP2_I2C_APPS_CLK_SRC 12
+#define BLSP1_QUP2_SPI_APPS_CLK_SRC 13
+#define BLSP1_QUP3_I2C_APPS_CLK_SRC 14
+#define BLSP1_QUP3_SPI_APPS_CLK_SRC 15
+#define BLSP1_QUP4_I2C_APPS_CLK_SRC 16
+#define BLSP1_QUP4_SPI_APPS_CLK_SRC 17
+#define BLSP1_UART1_APPS_CLK_SRC 18
+#define BLSP1_UART2_APPS_CLK_SRC 19
+#define BLSP2_QUP1_I2C_APPS_CLK_SRC 20
+#define BLSP2_QUP1_SPI_APPS_CLK_SRC 21
+#define BLSP2_QUP2_I2C_APPS_CLK_SRC 22
+#define BLSP2_QUP2_SPI_APPS_CLK_SRC 23
+#define BLSP2_QUP3_I2C_APPS_CLK_SRC 24
+#define BLSP2_QUP3_SPI_APPS_CLK_SRC 25
+#define BLSP2_QUP4_I2C_APPS_CLK_SRC 26
+#define BLSP2_QUP4_SPI_APPS_CLK_SRC 27
+#define BLSP2_UART1_APPS_CLK_SRC 28
+#define BLSP2_UART2_APPS_CLK_SRC 29
+#define GCC_AGGRE2_UFS_AXI_CLK 30
+#define GCC_AGGRE2_USB3_AXI_CLK 31
+#define GCC_BIMC_GFX_CLK 32
+#define GCC_BIMC_HMSS_AXI_CLK 33
+#define GCC_BIMC_MSS_Q6_AXI_CLK 34
+#define GCC_BLSP1_AHB_CLK 35
+#define GCC_BLSP1_QUP1_I2C_APPS_CLK 36
+#define GCC_BLSP1_QUP1_SPI_APPS_CLK 37
+#define GCC_BLSP1_QUP2_I2C_APPS_CLK 38
+#define GCC_BLSP1_QUP2_SPI_APPS_CLK 39
+#define GCC_BLSP1_QUP3_I2C_APPS_CLK 40
+#define GCC_BLSP1_QUP3_SPI_APPS_CLK 41
+#define GCC_BLSP1_QUP4_I2C_APPS_CLK 42
+#define GCC_BLSP1_QUP4_SPI_APPS_CLK 43
+#define GCC_BLSP1_UART1_APPS_CLK 44
+#define GCC_BLSP1_UART2_APPS_CLK 45
+#define GCC_BLSP2_AHB_CLK 46
+#define GCC_BLSP2_QUP1_I2C_APPS_CLK 47
+#define GCC_BLSP2_QUP1_SPI_APPS_CLK 48
+#define GCC_BLSP2_QUP2_I2C_APPS_CLK 49
+#define GCC_BLSP2_QUP2_SPI_APPS_CLK 50
+#define GCC_BLSP2_QUP3_I2C_APPS_CLK 51
+#define GCC_BLSP2_QUP3_SPI_APPS_CLK 52
+#define GCC_BLSP2_QUP4_I2C_APPS_CLK 53
+#define GCC_BLSP2_QUP4_SPI_APPS_CLK 54
+#define GCC_BLSP2_UART1_APPS_CLK 55
+#define GCC_BLSP2_UART2_APPS_CLK 56
+#define GCC_BOOT_ROM_AHB_CLK 57
+#define GCC_CFG_NOC_USB2_AXI_CLK 58
+#define GCC_CFG_NOC_USB3_AXI_CLK 59
+#define GCC_DCC_AHB_CLK 60
+#define GCC_GP1_CLK 61
+#define GCC_GP2_CLK 62
+#define GCC_GP3_CLK 63
+#define GCC_GPU_BIMC_GFX_CLK 64
+#define GCC_GPU_BIMC_GFX_SRC_CLK 65
+#define GCC_GPU_CFG_AHB_CLK 66
+#define GCC_GPU_GPLL0_CLK 67
+#define GCC_GPU_GPLL0_DIV_CLK 68
+#define GCC_GPU_SNOC_DVM_GFX_CLK 69
+#define GCC_HMSS_AHB_CLK 70
+#define GCC_HMSS_DVM_BUS_CLK 71
+#define GCC_HMSS_RBCPR_CLK 72
+#define GCC_MMSS_GPLL0_CLK 73
+#define GCC_MMSS_GPLL0_DIV_CLK 74
+#define GCC_MMSS_NOC_CFG_AHB_CLK 75
+#define GCC_MMSS_SYS_NOC_AXI_CLK 76
+#define GCC_MSS_CFG_AHB_CLK 77
+#define GCC_MSS_GPLL0_DIV_CLK 78
+#define GCC_MSS_MNOC_BIMC_AXI_CLK 79
+#define GCC_MSS_Q6_BIMC_AXI_CLK 80
+#define GCC_MSS_SNOC_AXI_CLK 81
+#define GCC_PDM2_CLK 82
+#define GCC_PDM_AHB_CLK 83
+#define GCC_PRNG_AHB_CLK 84
+#define GCC_QSPI_AHB_CLK 85
+#define GCC_QSPI_SER_CLK 86
+#define GCC_RX0_USB2_CLKREF_CLK 87
+#define GCC_RX1_USB2_CLKREF_CLK 88
+#define GCC_RX2_QLINK_CLKREF_CLK 89
+#define GCC_SDCC1_AHB_CLK 90
+#define GCC_SDCC1_APPS_CLK 91
+#define GCC_SDCC1_ICE_CORE_CLK 92
+#define GCC_SDCC2_AHB_CLK 93
+#define GCC_SDCC2_APPS_CLK 94
+#define GCC_UFS_AHB_CLK 95
+#define GCC_UFS_AXI_CLK 96
+#define GCC_UFS_CLKREF_CLK 97
+#define GCC_UFS_ICE_CORE_CLK 98
+#define GCC_UFS_PHY_AUX_CLK 99
+#define GCC_UFS_RX_SYMBOL_0_CLK 100
+#define GCC_UFS_RX_SYMBOL_1_CLK 101
+#define GCC_UFS_TX_SYMBOL_0_CLK 102
+#define GCC_UFS_UNIPRO_CORE_CLK 103
+#define GCC_USB20_MASTER_CLK 104
+#define GCC_USB20_MOCK_UTMI_CLK 105
+#define GCC_USB20_SLEEP_CLK 106
+#define GCC_USB30_MASTER_CLK 107
+#define GCC_USB30_MOCK_UTMI_CLK 108
+#define GCC_USB30_SLEEP_CLK 109
+#define GCC_USB3_CLKREF_CLK 110
+#define GCC_USB3_PHY_AUX_CLK 111
+#define GCC_USB3_PHY_PIPE_CLK 112
+#define GCC_USB_PHY_CFG_AHB2PHY_CLK 113
+#define GP1_CLK_SRC 114
+#define GP2_CLK_SRC 115
+#define GP3_CLK_SRC 116
+#define GPLL0 117
+#define GPLL0_OUT_AUX 118
+#define GPLL0_OUT_AUX2 119
+#define GPLL0_OUT_EARLY 120
+#define GPLL0_OUT_MAIN 121
+#define GPLL0_OUT_TEST 122
+#define GPLL1 123
+#define GPLL1_OUT_AUX 124
+#define GPLL1_OUT_AUX2 125
+#define GPLL1_OUT_EARLY 126
+#define GPLL1_OUT_MAIN 127
+#define GPLL1_OUT_TEST 128
+#define GPLL2 129
+#define GPLL2_OUT_AUX 130
+#define GPLL2_OUT_AUX2 131
+#define GPLL2_OUT_EARLY 132
+#define GPLL2_OUT_MAIN 133
+#define GPLL2_OUT_TEST 134
+#define GPLL3 135
+#define GPLL3_OUT_AUX 136
+#define GPLL3_OUT_AUX2 137
+#define GPLL3_OUT_EARLY 138
+#define GPLL3_OUT_MAIN 139
+#define GPLL3_OUT_TEST 140
+#define GPLL4 141
+#define GPLL4_OUT_AUX 142
+#define GPLL4_OUT_AUX2 143
+#define GPLL4_OUT_EARLY 144
+#define GPLL4_OUT_MAIN 145
+#define GPLL4_OUT_TEST 146
+#define GPLL5 147
+#define GPLL5_OUT_AUX 148
+#define GPLL5_OUT_AUX2 149
+#define GPLL5_OUT_EARLY 150
+#define GPLL5_OUT_MAIN 151
+#define GPLL5_OUT_TEST 152
+#define GPLL6 153
+#define GPLL6_OUT_AUX 154
+#define GPLL6_OUT_AUX2 155
+#define GPLL6_OUT_EARLY 156
+#define GPLL6_OUT_MAIN 157
+#define GPLL6_OUT_TEST 158
+#define HLOS1_VOTE_LPASS_ADSP_SMMU_CLK 159
+#define HMSS_AHB_CLK_SRC 160
+#define HMSS_GPLL0_CLK_SRC 161
+#define HMSS_GPLL4_CLK_SRC 162
+#define HMSS_RBCPR_CLK_SRC 163
+#define PDM2_CLK_SRC 164
+#define QSPI_SER_CLK_SRC 165
+#define SDCC1_APPS_CLK_SRC 166
+#define SDCC1_ICE_CORE_CLK_SRC 167
+#define SDCC2_APPS_CLK_SRC 168
+#define UFS_AXI_CLK_SRC 169
+#define UFS_ICE_CORE_CLK_SRC 170
+#define UFS_PHY_AUX_CLK_SRC 171
+#define UFS_UNIPRO_CORE_CLK_SRC 172
+#define USB20_MASTER_CLK_SRC 173
+#define USB20_MOCK_UTMI_CLK_SRC 174
+#define USB30_MASTER_CLK_SRC 175
+#define USB30_MOCK_UTMI_CLK_SRC 176
+#define USB3_PHY_AUX_CLK_SRC 177
+#define GPLL0_OUT_MSSCC 178
+#define GCC_UFS_AXI_HW_CTL_CLK 179
+#define GCC_UFS_ICE_CORE_HW_CTL_CLK 180
+#define GCC_UFS_PHY_AUX_HW_CTL_CLK 181
+#define GCC_UFS_UNIPRO_CORE_HW_CTL_CLK 182
+/* Block resets */
#define GCC_QUSB2PHY_PRIM_BCR 0
#define GCC_QUSB2PHY_SEC_BCR 1
#define GCC_UFS_BCR 2
@@ -191,52 +207,4 @@
#define GCC_USB_30_BCR 7
#define GCC_USB_PHY_CFG_AHB2PHY_BCR 8
-/* RPM controlled clocks */
-#define RPM_CE1_CLK 1
-#define RPM_CE1_A_CLK 2
-#define RPM_CXO_CLK_SRC 3
-#define RPM_BIMC_CLK 4
-#define RPM_BIMC_A_CLK 5
-#define RPM_CNOC_CLK 6
-#define RPM_CNOC_A_CLK 7
-#define RPM_SNOC_CLK 8
-#define RPM_SNOC_A_CLK 9
-#define RPM_CNOC_PERIPH_CLK 10
-#define RPM_CNOC_PERIPH_A_CLK 11
-#define RPM_CNOC_PERIPH_KEEPALIVE_A_CLK 12
-#define RPM_LN_BB_CLK1 13
-#define RPM_LN_BB_CLK1_AO 14
-#define RPM_LN_BB_CLK1_PIN 15
-#define RPM_LN_BB_CLK1_PIN_AO 16
-#define RPM_BIMC_MSMBUS_CLK 17
-#define RPM_BIMC_MSMBUS_A_CLK 18
-#define RPM_CNOC_MSMBUS_CLK 19
-#define RPM_CNOC_MSMBUS_A_CLK 20
-#define RPM_CXO_CLK_SRC_AO 21
-#define RPM_CXO_DWC3_CLK 22
-#define RPM_CXO_LPM_CLK 23
-#define RPM_CXO_OTG_CLK 24
-#define RPM_CXO_PIL_LPASS_CLK 25
-#define RPM_CXO_PIL_SSC_CLK 26
-#define RPM_CXO_PIL_SPSS_CLK 27
-#define RPM_DIV_CLK1 28
-#define RPM_DIV_CLK1_AO 29
-#define RPM_IPA_CLK 30
-#define RPM_IPA_A_CLK 31
-#define RPM_MCD_CE1_CLK 32
-#define RPM_MMSSNOC_AXI_CLK 33
-#define RPM_MMSSNOC_AXI_A_CLK 34
-#define RPM_QCEDEV_CE1_CLK 35
-#define RPM_QCRYPTO_CE1_CLK 36
-#define RPM_QDSS_CLK 37
-#define RPM_QDSS_A_CLK 38
-#define RPM_QSEECOM_CE1_CLK 39
-#define RPM_RF_CLK2 40
-#define RPM_RF_CLK2_AO 41
-#define RPM_SCM_CE1_CLK 42
-#define RPM_SNOC_MSMBUS_CLK 43
-#define RPM_SNOC_MSMBUS_A_CLK 44
-#define RPM_AGGRE2_NOC_CLK 45
-#define RPM_AGGRE2_NOC_A_CLK 46
-
#endif
diff --git a/include/dt-bindings/clock/qcom,gpu-msmfalcon.h b/include/dt-bindings/clock/qcom,gpu-msmfalcon.h
index 427c6aae05d3..2ef1e34db3a1 100644
--- a/include/dt-bindings/clock/qcom,gpu-msmfalcon.h
+++ b/include/dt-bindings/clock/qcom,gpu-msmfalcon.h
@@ -14,27 +14,32 @@
#ifndef _DT_BINDINGS_CLK_MSM_GPU_FALCON_H
#define _DT_BINDINGS_CLK_MSM_GPU_FALCON_H
-#define GFX3D_CLK_SRC 0
-#define GPU_PLL0_PLL 1
-#define GPU_PLL0_PLL_OUT_AUX 2
-#define GPU_PLL0_PLL_OUT_AUX2 3
-#define GPU_PLL0_PLL_OUT_EARLY 4
-#define GPU_PLL0_PLL_OUT_MAIN 5
-#define GPU_PLL0_PLL_OUT_TEST 6
-#define GPU_PLL1_PLL 7
-#define GPU_PLL1_PLL_OUT_AUX 8
-#define GPU_PLL1_PLL_OUT_AUX2 9
-#define GPU_PLL1_PLL_OUT_EARLY 10
-#define GPU_PLL1_PLL_OUT_MAIN 11
-#define GPU_PLL1_PLL_OUT_TEST 12
-#define GPUCC_CXO_CLK 13
-#define GPUCC_GFX3D_CLK 14
-#define GPUCC_RBBMTIMER_CLK 15
-#define GPUCC_RBCPR_CLK 16
-#define RBBMTIMER_CLK_SRC 18
-#define RBCPR_CLK_SRC 19
+#define GFX3D_CLK_SRC 0
+#define GPU_PLL0_PLL 1
+#define GPU_PLL0_PLL_OUT_AUX 2
+#define GPU_PLL0_PLL_OUT_AUX2 3
+#define GPU_PLL0_PLL_OUT_EARLY 4
+#define GPU_PLL0_PLL_OUT_MAIN 5
+#define GPU_PLL0_PLL_OUT_TEST 6
+#define GPU_PLL1_PLL 7
+#define GPU_PLL1_PLL_OUT_AUX 8
+#define GPU_PLL1_PLL_OUT_AUX2 9
+#define GPU_PLL1_PLL_OUT_EARLY 10
+#define GPU_PLL1_PLL_OUT_MAIN 11
+#define GPU_PLL1_PLL_OUT_TEST 12
+#define GPUCC_CXO_CLK 13
+#define GPUCC_GFX3D_CLK 14
+#define GPUCC_RBBMTIMER_CLK 15
+#define GPUCC_RBCPR_CLK 16
+#define RBBMTIMER_CLK_SRC 17
+#define RBCPR_CLK_SRC 18
-#define GPU_CX_GDSC 0
-#define GPU_GX_GDSC 1
+#define GPU_CX_GDSC 0
+#define GPU_GX_GDSC 1
+
+#define GPUCC_GPU_CX_BCR 0
+#define GPUCC_GPU_GX_BCR 1
+#define GPUCC_RBCPR_BCR 2
+#define GPUCC_SPDM_BCR 3
#endif
diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h
index ee37c5c22f5f..bcaa1a552e8e 100644
--- a/include/dt-bindings/clock/qcom,rpmcc.h
+++ b/include/dt-bindings/clock/qcom,rpmcc.h
@@ -1,5 +1,6 @@
/*
* Copyright 2015 Linaro Limited
+ * Copyright (c) 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
@@ -38,47 +39,31 @@
#define RPM_SFPB_CLK 20
#define RPM_SFPB_A_CLK 21
-/* msm8916 */
-#define RPM_SMD_XO_CLK_SRC 0
-#define RPM_SMD_XO_A_CLK_SRC 1
-#define RPM_SMD_PCNOC_CLK 2
-#define RPM_SMD_PCNOC_A_CLK 3
-#define RPM_SMD_SNOC_CLK 4
-#define RPM_SMD_SNOC_A_CLK 5
-#define RPM_SMD_BIMC_CLK 6
-#define RPM_SMD_BIMC_A_CLK 7
-#define RPM_SMD_QDSS_CLK 8
-#define RPM_SMD_QDSS_A_CLK 9
-#define RPM_SMD_BB_CLK1 10
-#define RPM_SMD_BB_CLK1_A 11
-#define RPM_SMD_BB_CLK2 12
-#define RPM_SMD_BB_CLK2_A 13
-#define RPM_SMD_RF_CLK1 14
-#define RPM_SMD_RF_CLK1_A 15
-#define RPM_SMD_RF_CLK2 16
-#define RPM_SMD_RF_CLK2_A 17
-#define RPM_SMD_BB_CLK1_PIN 18
-#define RPM_SMD_BB_CLK1_A_PIN 19
-#define RPM_SMD_BB_CLK2_PIN 20
-#define RPM_SMD_BB_CLK2_A_PIN 21
-#define RPM_SMD_RF_CLK1_PIN 22
-#define RPM_SMD_RF_CLK1_A_PIN 23
-#define RPM_SMD_RF_CLK2_PIN 24
-#define RPM_SMD_RF_CLK2_A_PIN 25
+/* msm8916 and msm8996 */
+#define RPM_XO_CLK_SRC 0
+#define RPM_XO_A_CLK_SRC 1
+#define RPM_PCNOC_CLK 2
+#define RPM_PCNOC_A_CLK 3
+#define RPM_SNOC_CLK 4
+#define RPM_SNOC_A_CLK 5
+#define RPM_BIMC_CLK 6
+#define RPM_BIMC_A_CLK 7
+#define RPM_QDSS_CLK 8
+#define RPM_QDSS_A_CLK 9
#define RPM_BB_CLK1 10
#define RPM_BB_CLK1_A 11
-#define RPM_BB_CLK2 12
-#define RPM_BB_CLK2_A 13
-#define RPM_RF_CLK1 14
-#define RPM_RF_CLK1_A 15
-#define RPM_RF_CLK2 16
-#define RPM_RF_CLK2_A 17
-#define RPM_BB_CLK1_PIN 18
-#define RPM_BB_CLK1_A_PIN 19
-#define RPM_BB_CLK2_PIN 20
-#define RPM_BB_CLK2_A_PIN 21
-#define RPM_RF_CLK1_PIN 22
-#define RPM_RF_CLK1_A_PIN 23
+#define RPM_BB_CLK1_PIN 12
+#define RPM_BB_CLK1_A_PIN 13
+#define RPM_BB_CLK2 14
+#define RPM_BB_CLK2_A 15
+#define RPM_BB_CLK2_PIN 16
+#define RPM_BB_CLK2_A_PIN 17
+#define RPM_RF_CLK1 18
+#define RPM_RF_CLK1_A 19
+#define RPM_RF_CLK1_PIN 20
+#define RPM_RF_CLK1_A_PIN 21
+#define RPM_RF_CLK2 22
+#define RPM_RF_CLK2_A 23
#define RPM_RF_CLK2_PIN 24
#define RPM_RF_CLK2_A_PIN 25
#define RPM_AGGR1_NOC_CLK 26
@@ -101,5 +86,47 @@
#define RPM_DIV_CLK3_AO 43
#define RPM_LN_BB_CLK 44
#define RPM_LN_BB_A_CLK 45
+#define RPM_LN_BB_CLK1 46
+#define RPM_LN_BB_CLK1_AO 47
+#define RPM_LN_BB_CLK1_PIN 48
+#define RPM_LN_BB_CLK1_PIN_AO 49
+#define RPM_LN_BB_CLK2 50
+#define RPM_LN_BB_CLK2_AO 51
+#define RPM_LN_BB_CLK2_PIN 52
+#define RPM_LN_BB_CLK2_PIN_AO 53
+#define RPM_LN_BB_CLK3 54
+#define RPM_LN_BB_CLK3_AO 55
+#define RPM_LN_BB_CLK3_PIN 56
+#define RPM_LN_BB_CLK3_PIN_AO 57
+#define RPM_CNOC_PERIPH_CLK 58
+#define RPM_CNOC_PERIPH_A_CLK 59
+
+/* Voter clocks */
+#define MMSSNOC_AXI_CLK 60
+#define MMSSNOC_AXI_A_CLK 61
+#define MMSSNOC_GDS_CLK 62
+#define BIMC_MSMBUS_CLK 63
+#define BIMC_MSMBUS_A_CLK 64
+#define CNOC_MSMBUS_CLK 65
+#define CNOC_MSMBUS_A_CLK 66
+#define PNOC_KEEPALIVE_A_CLK 67
+#define PNOC_MSMBUS_CLK 68
+#define PNOC_MSMBUS_A_CLK 69
+#define PNOC_PM_CLK 70
+#define PNOC_SPS_CLK 71
+#define MCD_CE1_CLK 72
+#define QCEDEV_CE1_CLK 73
+#define QCRYPTO_CE1_CLK 74
+#define QSEECOM_CE1_CLK 75
+#define SCM_CE1_CLK 76
+#define SNOC_MSMBUS_CLK 77
+#define SNOC_MSMBUS_A_CLK 78
+#define CXO_DWC3_CLK 79
+#define CXO_LPM_CLK 80
+#define CXO_OTG_CLK 81
+#define CXO_PIL_LPASS_CLK 82
+#define CXO_PIL_SSC_CLK 83
+#define CXO_PIL_CDSP_CLK 84
+#define CNOC_PERIPH_KEEPALIVE_A_CLK 85
#endif
diff --git a/include/dt-bindings/msm/msm-bus-ids.h b/include/dt-bindings/msm/msm-bus-ids.h
index f7139500e866..bfd774a99963 100644
--- a/include/dt-bindings/msm/msm-bus-ids.h
+++ b/include/dt-bindings/msm/msm-bus-ids.h
@@ -162,7 +162,9 @@
#define MSM_BUS_MASTER_CNOC_A2NOC 118
#define MSM_BUS_MASTER_WLAN 119
#define MSM_BUS_MASTER_MSS_CE 120
-#define MSM_BUS_MASTER_MASTER_LAST 121
+#define MSM_BUS_MASTER_CDSP_PROC 121
+#define MSM_BUS_MASTER_GNOC_SNOC 122
+#define MSM_BUS_MASTER_MASTER_LAST 123
#define MSM_BUS_SYSTEM_FPB_MASTER_SYSTEM MSM_BUS_SYSTEM_MASTER_SYSTEM_FPB
#define MSM_BUS_CPSS_FPB_MASTER_SYSTEM MSM_BUS_SYSTEM_MASTER_CPSS_FPB
@@ -463,7 +465,16 @@
#define MSM_BUS_SLAVE_TLMM_NORTH 731
#define MSM_BUS_SLAVE_TLMM_WEST 732
#define MSM_BUS_SLAVE_SKL 733
-#define MSM_BUS_SLAVE_LAST 734
+#define MSM_BUS_SLAVE_LPASS_TCM 734
+#define MSM_BUS_SLAVE_TLMM_SOUTH 735
+#define MSM_BUS_SLAVE_TLMM_CENTER 736
+#define MSM_BUS_MSS_NAV_CE_MPU_CFG 737
+#define MSM_BUS_SLAVE_A2NOC_THROTTLE_CFG 738
+#define MSM_BUS_SLAVE_CDSP 739
+#define MSM_BUS_SLAVE_CDSP_SMMU_CFG 740
+#define MSM_BUS_SLAVE_LPASS_MPU_CFG 741
+#define MSM_BUS_SLAVE_CSI_PHY_CFG 742
+#define MSM_BUS_SLAVE_LAST 743
#define MSM_BUS_SYSTEM_FPB_SLAVE_SYSTEM MSM_BUS_SYSTEM_SLAVE_SYSTEM_FPB
#define MSM_BUS_CPSS_FPB_SLAVE_SYSTEM MSM_BUS_SYSTEM_SLAVE_CPSS_FPB
@@ -633,6 +644,8 @@
#define ICBID_MASTER_CNOC_A2NOC 146
#define ICBID_MASTER_WLAN 147
#define ICBID_MASTER_MSS_CE 148
+#define ICBID_MASTER_CDSP_PROC 149
+#define ICBID_MASTER_GNOC_SNOC 150
#define ICBID_SLAVE_EBI1 0
#define ICBID_SLAVE_APPSS_L2 1
@@ -861,4 +874,13 @@
#define ICBID_SLAVE_TLMM_EAST 213
#define ICBID_SLAVE_TLMM_NORTH 214
#define ICBID_SLAVE_TLMM_WEST 215
+#define ICBID_SLAVE_LPASS_TCM 216
+#define ICBID_SLAVE_TLMM_SOUTH 217
+#define ICBID_SLAVE_TLMM_CENTER 218
+#define ICBID_SLAVE_MSS_NAV_CE_MPU_CFG 219
+#define ICBID_SLAVE_A2NOC_THROTTLE_CFG 220
+#define ICBID_SLAVE_CDSP 221
+#define ICBID_SLAVE_CDSP_SMMU_CFG 222
+#define ICBID_SLAVE_LPASS_MPU_CFG 223
+#define ICBID_SLAVE_CSI_PHY_CFG 224
#endif
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index 59915ea5373c..0eab4811ee92 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -53,6 +53,7 @@ extern int nr_cpu_ids;
* cpu_present_mask - has bit 'cpu' set iff cpu is populated
* cpu_online_mask - has bit 'cpu' set iff cpu available to scheduler
* cpu_active_mask - has bit 'cpu' set iff cpu available to migration
+ * cpu_isolated_mask- has bit 'cpu' set iff cpu isolated
*
* If !CONFIG_HOTPLUG_CPU, present == possible, and active == online.
*
@@ -89,25 +90,30 @@ extern const struct cpumask *const cpu_possible_mask;
extern const struct cpumask *const cpu_online_mask;
extern const struct cpumask *const cpu_present_mask;
extern const struct cpumask *const cpu_active_mask;
+extern const struct cpumask *const cpu_isolated_mask;
#if NR_CPUS > 1
#define num_online_cpus() cpumask_weight(cpu_online_mask)
#define num_possible_cpus() cpumask_weight(cpu_possible_mask)
#define num_present_cpus() cpumask_weight(cpu_present_mask)
#define num_active_cpus() cpumask_weight(cpu_active_mask)
+#define num_isolated_cpus() cpumask_weight(cpu_isolated_mask)
#define cpu_online(cpu) cpumask_test_cpu((cpu), cpu_online_mask)
#define cpu_possible(cpu) cpumask_test_cpu((cpu), cpu_possible_mask)
#define cpu_present(cpu) cpumask_test_cpu((cpu), cpu_present_mask)
#define cpu_active(cpu) cpumask_test_cpu((cpu), cpu_active_mask)
+#define cpu_isolated(cpu) cpumask_test_cpu((cpu), cpu_isolated_mask)
#else
#define num_online_cpus() 1U
#define num_possible_cpus() 1U
#define num_present_cpus() 1U
#define num_active_cpus() 1U
+#define num_isolated_cpus() 0U
#define cpu_online(cpu) ((cpu) == 0)
#define cpu_possible(cpu) ((cpu) == 0)
#define cpu_present(cpu) ((cpu) == 0)
#define cpu_active(cpu) ((cpu) == 0)
+#define cpu_isolated(cpu) ((cpu) == 0)
#endif
/* verify cpu argument to cpumask_* operators */
@@ -714,12 +720,14 @@ extern const DECLARE_BITMAP(cpu_all_bits, NR_CPUS);
#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
#define for_each_online_cpu(cpu) for_each_cpu((cpu), cpu_online_mask)
#define for_each_present_cpu(cpu) for_each_cpu((cpu), cpu_present_mask)
+#define for_each_isolated_cpu(cpu) for_each_cpu((cpu), cpu_isolated_mask)
/* Wrappers for arch boot code to manipulate normally-constant masks */
void set_cpu_possible(unsigned int cpu, bool possible);
void set_cpu_present(unsigned int cpu, bool present);
void set_cpu_online(unsigned int cpu, bool online);
void set_cpu_active(unsigned int cpu, bool active);
+void set_cpu_isolated(unsigned int cpu, bool isolated);
void init_cpu_present(const struct cpumask *src);
void init_cpu_possible(const struct cpumask *src);
void init_cpu_online(const struct cpumask *src);
diff --git a/include/linux/device.h b/include/linux/device.h
index 9f27351c6b9c..4b4e2d5ce6e7 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -1023,6 +1023,7 @@ static inline bool device_supports_offline(struct device *dev)
extern void lock_device_hotplug(void);
extern void unlock_device_hotplug(void);
extern int lock_device_hotplug_sysfs(void);
+extern void lock_device_hotplug_assert(void);
extern int device_offline(struct device *dev);
extern int device_online(struct device *dev);
extern void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
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/hrtimer.h b/include/linux/hrtimer.h
index 2ead22dd74a0..952adcacc4cf 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -53,6 +53,7 @@ enum hrtimer_restart {
*
* 0x00 inactive
* 0x01 enqueued into rbtree
+ * 0x02 timer is pinned to a cpu
*
* The callback state is not part of the timer->state because clearing it would
* mean touching the timer after the callback, this makes it impossible to free
@@ -72,6 +73,8 @@ enum hrtimer_restart {
*/
#define HRTIMER_STATE_INACTIVE 0x00
#define HRTIMER_STATE_ENQUEUED 0x01
+#define HRTIMER_PINNED_SHIFT 1
+#define HRTIMER_STATE_PINNED (1 << HRTIMER_PINNED_SHIFT)
/**
* struct hrtimer - the basic hrtimer structure
@@ -357,6 +360,9 @@ DECLARE_PER_CPU(struct tick_device, tick_cpu_device);
/* Exported timer functions: */
+/* To be used from cpusets, only */
+extern void hrtimer_quiesce_cpu(void *cpup);
+
/* Initialize timers: */
extern void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,
enum hrtimer_mode mode);
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/ipa.h b/include/linux/ipa.h
index 81da2aaa01e5..e66d388651b8 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -551,6 +551,7 @@ struct ipa_sys_connect_params {
bool skip_ep_cfg;
bool keep_ipa_awake;
bool napi_enabled;
+ bool recycle_enabled;
};
/**
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/perf_event.h b/include/linux/perf_event.h
index 95fd207e63ca..4f28b91f49c5 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -992,6 +992,11 @@ extern int perf_cpu_time_max_percent_handler(struct ctl_table *table, int write,
loff_t *ppos);
+static inline bool perf_paranoid_any(void)
+{
+ return sysctl_perf_event_paranoid > 2;
+}
+
static inline bool perf_paranoid_tracepoint_raw(void)
{
return sysctl_perf_event_paranoid > -1;
diff --git a/include/linux/qcom_tspp.h b/include/linux/qcom_tspp.h
new file mode 100644
index 000000000000..28e6695fb057
--- /dev/null
+++ b/include/linux/qcom_tspp.h
@@ -0,0 +1,99 @@
+/* Copyright (c) 2012-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 _MSM_TSPP_H_
+#define _MSM_TSPP_H_
+
+struct tspp_data_descriptor {
+ void *virt_base; /* logical address of the actual data */
+ phys_addr_t phys_base; /* physical address of the actual data */
+ u32 size; /* size of buffer in bytes */
+ int id; /* unique identifier */
+ void *user; /* user-defined data */
+};
+
+enum tspp_key_parity {
+ TSPP_KEY_PARITY_EVEN,
+ TSPP_KEY_PARITY_ODD
+};
+
+struct tspp_key {
+ enum tspp_key_parity parity;
+ int lsb;
+ int msb;
+};
+
+enum tspp_source {
+ TSPP_SOURCE_TSIF0,
+ TSPP_SOURCE_TSIF1,
+ TSPP_SOURCE_MEM,
+ TSPP_SOURCE_NONE = -1
+};
+
+enum tspp_mode {
+ TSPP_MODE_DISABLED,
+ TSPP_MODE_PES,
+ TSPP_MODE_RAW,
+ TSPP_MODE_RAW_NO_SUFFIX
+};
+
+enum tspp_tsif_mode {
+ TSPP_TSIF_MODE_LOOPBACK, /* loopback mode */
+ TSPP_TSIF_MODE_1, /* without sync */
+ TSPP_TSIF_MODE_2 /* with sync signal */
+};
+
+struct tspp_filter {
+ int pid;
+ int mask;
+ enum tspp_mode mode;
+ unsigned int priority; /* 0 - 15 */
+ int decrypt;
+ enum tspp_source source;
+};
+
+struct tspp_select_source {
+ enum tspp_source source;
+ enum tspp_tsif_mode mode;
+ int clk_inverse;
+ int data_inverse;
+ int sync_inverse;
+ int enable_inverse;
+};
+
+typedef void (tspp_notifier)(int channel_id, void *user);
+typedef void* (tspp_allocator)(int channel_id, u32 size,
+ phys_addr_t *phys_base, void *user);
+typedef void (tspp_memfree)(int channel_id, u32 size,
+ void *virt_base, phys_addr_t phys_base, void *user);
+
+/* Kernel API functions */
+int tspp_open_stream(u32 dev, u32 channel_id,
+ struct tspp_select_source *source);
+int tspp_close_stream(u32 dev, u32 channel_id);
+int tspp_open_channel(u32 dev, u32 channel_id);
+int tspp_close_channel(u32 dev, u32 channel_id);
+int tspp_get_ref_clk_counter(u32 dev,
+ enum tspp_source source, u32 *tcr_counter);
+int tspp_add_filter(u32 dev, u32 channel_id, struct tspp_filter *filter);
+int tspp_remove_filter(u32 dev, u32 channel_id, struct tspp_filter *filter);
+int tspp_set_key(u32 dev, u32 channel_id, struct tspp_key *key);
+int tspp_register_notification(u32 dev, u32 channel_id, tspp_notifier *notify,
+ void *data, u32 timer_ms);
+int tspp_unregister_notification(u32 dev, u32 channel_id);
+const struct tspp_data_descriptor *tspp_get_buffer(u32 dev, u32 channel_id);
+int tspp_release_buffer(u32 dev, u32 channel_id, u32 descriptor_id);
+int tspp_allocate_buffers(u32 dev, u32 channel_id, u32 count,
+ u32 size, u32 int_freq, tspp_allocator *alloc,
+ tspp_memfree *memfree, void *user);
+
+#endif /* _MSM_TSPP_H_ */
diff --git a/include/linux/qdsp6v2/apr.h b/include/linux/qdsp6v2/apr.h
index 84c822234e00..29deb3ca5ac7 100644
--- a/include/linux/qdsp6v2/apr.h
+++ b/include/linux/qdsp6v2/apr.h
@@ -172,8 +172,8 @@ inline int apr_fill_hdr(void *handle, uint32_t *buf, uint16_t src_port,
int apr_send_pkt(void *handle, uint32_t *buf);
int apr_deregister(void *handle);
-void subsys_notif_register(struct notifier_block *mod_notif,
- struct notifier_block *lp_notif);
+void subsys_notif_register(char *client_name, int domain,
+ struct notifier_block *nb);
int apr_get_dest_id(char *dest);
uint16_t apr_get_data_src(struct apr_hdr *hdr);
void change_q6_state(int state);
diff --git a/include/linux/qdsp6v2/audio_notifier.h b/include/linux/qdsp6v2/audio_notifier.h
new file mode 100644
index 000000000000..3587b49a05c6
--- /dev/null
+++ b/include/linux/qdsp6v2/audio_notifier.h
@@ -0,0 +1,105 @@
+/* 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 __AUDIO_NOTIFIER_H_
+#define __AUDIO_NOTIFIER_H_
+
+/* State of the notifier domain */
+enum {
+ AUDIO_NOTIFIER_SERVICE_DOWN,
+ AUDIO_NOTIFIER_SERVICE_UP
+};
+
+/* Service order determines connection priority
+ * Highest number connected first
+ */
+enum {
+ AUDIO_NOTIFIER_SSR_SERVICE,
+ AUDIO_NOTIFIER_PDR_SERVICE,
+ AUDIO_NOTIFIER_MAX_SERVICES
+};
+
+enum {
+ AUDIO_NOTIFIER_ADSP_DOMAIN,
+ AUDIO_NOTIFIER_MODEM_DOMAIN,
+ AUDIO_NOTIFIER_MAX_DOMAINS
+};
+
+/* Structure populated in void *data of nb function
+ * callback used for audio_notifier_register
+ */
+struct audio_notifier_cb_data {
+ int service;
+ int domain;
+};
+
+#ifdef CONFIG_MSM_QDSP6_NOTIFIER
+
+/*
+ * Use audio_notifier_register to register any audio
+ * clients who need to be notified of a remote process.
+ * This API will determine and register the client with
+ * the best available subsystem (SSR or PDR) for that
+ * domain (Adsp or Modem). When an event is sent from that
+ * domain the notifier block callback function will be called.
+ *
+ * client_name - A unique user name defined by the client.
+ * If the same name is used for multiple calls each will
+ * be tracked & called back separately and a single call
+ * to deregister will delete them all.
+ * domain - Domain the client wants to get events from.
+ * AUDIO_NOTIFIER_ADSP_DOMAIN
+ * AUDIO_NOTIFIER_MODEM_DOMAIN
+ * *nb - Pointer to a notifier block. Provide a callback function
+ * to be notified of an even on that domain.
+ *
+ * nb_func(struct notifier_block *this, unsigned long opcode, void *data)
+ * this - pointer to own nb
+ * opcode - event from registered domain
+ * AUDIO_NOTIFIER_SERVICE_DOWN
+ * AUDIO_NOTIFIER_SERVICE_UP
+ * *data - pointer to struct audio_notifier_cb_data
+ *
+ * Returns: Success: 0
+ * Error: -#
+ */
+int audio_notifier_register(char *client_name, int domain,
+ struct notifier_block *nb);
+
+/*
+ * Use audio_notifier_deregister to deregister the clients from
+ * all domains registered using audio_notifier_register that
+ * match the client name.
+ *
+ * client_name - Unique user name used in audio_notifier_register.
+ * Returns: Success: 0
+ * Error: -#
+ */
+int audio_notifier_deregister(char *client_name);
+
+#else
+
+static inline int audio_notifier_register(char *client_name, int domain,
+ struct notifier_block *nb)
+{
+ return -ENODEV;
+}
+
+static inline int audio_notifier_deregister(char *client_name)
+{
+ return 0;
+}
+
+#endif /* CONFIG_MSM_QDSP6_PDR */
+
+#endif
diff --git a/include/linux/qdsp6v2/audio_pdr.h b/include/linux/qdsp6v2/audio_pdr.h
new file mode 100644
index 000000000000..b8eb1be3ee64
--- /dev/null
+++ b/include/linux/qdsp6v2/audio_pdr.h
@@ -0,0 +1,101 @@
+/* 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 __AUDIO_PDR_H_
+#define __AUDIO_PDR_H_
+
+enum {
+ AUDIO_PDR_DOMAIN_ADSP,
+ AUDIO_PDR_DOMAIN_MAX
+};
+
+enum {
+ AUDIO_PDR_FRAMEWORK_DOWN,
+ AUDIO_PDR_FRAMEWORK_UP
+};
+
+#ifdef CONFIG_MSM_QDSP6_PDR
+
+/*
+ * Use audio_pdr_register to register with the PDR subsystem this
+ * should be done before module late init otherwise notification
+ * of the AUDIO_PDR_FRAMEWORK_UP cannot be guaranteed.
+ *
+ * *nb - Pointer to a notifier block. Provide a callback function
+ * to be notified once the PDR framework has been initialized.
+ * Callback will receive either the AUDIO_PDR_FRAMEWORK_DOWN
+ * or AUDIO_PDR_FRAMEWORK_UP ioctl depending on the state of
+ * the PDR framework.
+ *
+ * Returns: Success: 0
+ * Failure: Error code
+ */
+int audio_pdr_register(struct notifier_block *nb);
+
+/*
+ * Use audio_pdr_service_register to register with a PDR service
+ * Function should be called after nb callback registered with
+ * audio_pdr_register has been called back with the
+ * AUDIO_PDR_FRAMEWORK_UP ioctl.
+ *
+ * domain_id - Domain to use, example: AUDIO_PDR_ADSP
+ * *nb - Pointer to a notifier block. Provide a callback function
+ * that will be notified of the state of the domain
+ * requested. The ioctls received by the callback are
+ * defined in service-notifier.h.
+ *
+ * Returns: Success: Client handle
+ * Failure: Pointer error code
+ */
+void *audio_pdr_service_register(int domain_id,
+ struct notifier_block *nb, int *curr_state);
+
+ /*
+ * Use audio_pdr_service_deregister to deregister with a PDR
+ * service that was registered using the audio_pdr_service_register
+ * API.
+ *
+ * *service_handle - Service handle returned by audio_pdr_service_register
+ * *nb - Pointer to the notifier block. Used in the call to
+ * audio_pdr_service_register.
+ *
+ * Returns: Success: Client handle
+ * Failure: Error code
+ */
+int audio_pdr_service_deregister(void *service_handle,
+ struct notifier_block *nb);
+
+#else
+
+static inline int audio_pdr_register(struct notifier_block *nb)
+{
+ return -ENODEV;
+}
+
+
+static inline void *audio_pdr_service_register(int domain_id,
+ struct notifier_block *nb,
+ int *curr_state)
+{
+ return NULL;
+}
+
+static inline int audio_pdr_service_deregister(void *service_handle,
+ struct notifier_block *nb)
+{
+ return 0;
+}
+
+#endif /* CONFIG_MSM_QDSP6_PDR */
+
+#endif
diff --git a/include/linux/qdsp6v2/audio_ssr.h b/include/linux/qdsp6v2/audio_ssr.h
new file mode 100644
index 000000000000..a807021ba7ca
--- /dev/null
+++ b/include/linux/qdsp6v2/audio_ssr.h
@@ -0,0 +1,78 @@
+/* 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 __AUDIO_SSR_H_
+#define __AUDIO_SSR_H_
+
+enum {
+ AUDIO_SSR_DOMAIN_ADSP,
+ AUDIO_SSR_DOMAIN_MODEM,
+ AUDIO_SSR_DOMAIN_MAX
+};
+
+#ifdef CONFIG_MSM_QDSP6_SSR
+
+/*
+ * Use audio_ssr_register to register with the SSR subsystem
+ *
+ * domain_id - Service to use, example: AUDIO_SSR_DOMAIN_ADSP
+ * *nb - Pointer to a notifier block. Provide a callback function
+ * to be notified of an event for that service. The ioctls
+ * used by the callback are defined in subsystem_notif.h.
+ *
+ * Returns: Success: Client handle
+ * Failure: Pointer error code
+ */
+void *audio_ssr_register(int domain_id, struct notifier_block *nb);
+
+/*
+ * Use audio_ssr_deregister to register with the SSR subsystem
+ *
+ * handle - Handle received from audio_ssr_register
+ * *nb - Pointer to a notifier block. Callback function
+ * Used from audio_ssr_register.
+ *
+ * Returns: Success: 0
+ * Failure: Error code
+ */
+int audio_ssr_deregister(void *handle, struct notifier_block *nb);
+
+
+/*
+ * Use audio_ssr_send_nmi to force a RAM dump on ADSP
+ * down event.
+ *
+ * *ssr_cb_data - *data received from notifier callback
+ */
+void audio_ssr_send_nmi(void *ssr_cb_data);
+
+#else
+
+static inline void *audio_ssr_register(int domain_id,
+ struct notifier_block *nb)
+{
+ return NULL;
+}
+
+static inline int audio_ssr_deregister(void *handle, struct notifier_block *nb)
+{
+ return 0;
+}
+
+static inline void audio_ssr_send_nmi(void *ssr_cb_data)
+{
+}
+
+#endif /* CONFIG_MSM_QDSP6_SSR */
+
+#endif
diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h
index b13ebe50c3d6..7c12823894df 100644
--- a/include/linux/qpnp/qpnp-revid.h
+++ b/include/linux/qpnp/qpnp-revid.h
@@ -212,6 +212,7 @@ struct pmic_revid_data {
u8 pmic_type;
u8 pmic_subtype;
const char *pmic_name;
+ int fab_id;
};
#ifdef CONFIG_QPNP_REVID
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/linux/sched.h b/include/linux/sched.h
index 4701e0403167..a395d8a9ff73 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -363,6 +363,41 @@ extern cpumask_var_t cpu_isolated_map;
extern int runqueue_is_locked(int cpu);
+#ifdef CONFIG_HOTPLUG_CPU
+extern int sched_isolate_count(const cpumask_t *mask, bool include_offline);
+extern int sched_isolate_cpu(int cpu);
+extern int sched_unisolate_cpu(int cpu);
+extern int sched_unisolate_cpu_unlocked(int cpu);
+#else
+static inline int sched_isolate_count(const cpumask_t *mask,
+ bool include_offline)
+{
+ cpumask_t count_mask;
+
+ if (include_offline)
+ cpumask_andnot(&count_mask, mask, cpu_online_mask);
+ else
+ return 0;
+
+ return cpumask_weight(&count_mask);
+}
+
+static inline int sched_isolate_cpu(int cpu)
+{
+ return 0;
+}
+
+static inline int sched_unisolate_cpu(int cpu)
+{
+ return 0;
+}
+
+static inline int sched_unisolate_cpu_unlocked(int cpu)
+{
+ return 0;
+}
+#endif
+
#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
extern void nohz_balance_enter_idle(int cpu);
extern void set_cpu_sd_state_idle(void);
@@ -409,6 +444,8 @@ extern int proc_dowatchdog_thresh(struct ctl_table *table, int write,
extern unsigned int softlockup_panic;
extern unsigned int hardlockup_panic;
void lockup_detector_init(void);
+extern void watchdog_enable(unsigned int cpu);
+extern void watchdog_disable(unsigned int cpu);
#else
static inline void touch_softlockup_watchdog_sched(void)
{
@@ -425,6 +462,12 @@ static inline void touch_all_softlockup_watchdogs(void)
static inline void lockup_detector_init(void)
{
}
+static inline void watchdog_enable(unsigned int cpu)
+{
+}
+static inline void watchdog_disable(unsigned int cpu)
+{
+}
#endif
#ifdef CONFIG_DETECT_HUNG_TASK
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 1f9c2c734b20..9fe71c774543 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -64,6 +64,7 @@ extern unsigned int sysctl_sched_pred_alert_freq;
extern unsigned int sysctl_sched_freq_aggregate;
extern unsigned int sysctl_sched_enable_thread_grouping;
extern unsigned int sysctl_sched_freq_aggregate_threshold_pct;
+extern unsigned int sysctl_sched_prefer_sync_wakee_to_waker;
#else /* CONFIG_SCHED_HMP */
@@ -120,6 +121,22 @@ extern int sysctl_sched_rt_runtime;
extern unsigned int sysctl_sched_cfs_bandwidth_slice;
#endif
+#ifdef CONFIG_SCHED_TUNE
+extern unsigned int sysctl_sched_cfs_boost;
+int sysctl_sched_cfs_boost_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *length,
+ loff_t *ppos);
+static inline unsigned int get_sysctl_sched_cfs_boost(void)
+{
+ return sysctl_sched_cfs_boost;
+}
+#else
+static inline unsigned int get_sysctl_sched_cfs_boost(void)
+{
+ return 0;
+}
+#endif
+
#ifdef CONFIG_SCHED_AUTOGROUP
extern unsigned int sysctl_sched_autogroup_enabled;
#endif
diff --git a/include/linux/tick.h b/include/linux/tick.h
index 5bf3ddade19c..1732697ea419 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -161,7 +161,15 @@ extern void __tick_nohz_task_switch(void);
#else
static inline int housekeeping_any_cpu(void)
{
- return smp_processor_id();
+ cpumask_t available;
+ int cpu;
+
+ cpumask_andnot(&available, cpu_online_mask, cpu_isolated_mask);
+ cpu = cpumask_any(&available);
+ if (cpu >= nr_cpu_ids)
+ cpu = smp_processor_id();
+
+ return cpu;
}
static inline bool tick_nohz_full_enabled(void) { return false; }
static inline bool tick_nohz_full_cpu(int cpu) { return false; }
@@ -187,7 +195,7 @@ static inline bool is_housekeeping_cpu(int cpu)
if (tick_nohz_full_enabled())
return cpumask_test_cpu(cpu, housekeeping_mask);
#endif
- return true;
+ return !cpu_isolated(cpu);
}
static inline void housekeeping_affine(struct task_struct *t)
diff --git a/include/linux/timer.h b/include/linux/timer.h
index 7a5602e19e87..b1617e8932b2 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -182,6 +182,9 @@ extern void set_timer_slack(struct timer_list *time, int slack_hz);
*/
#define NEXT_TIMER_MAX_DELTA ((1UL << 30) - 1)
+/* To be used from cpusets, only */
+extern void timer_quiesce_cpu(void *cpup);
+
/*
* Timer-statistics info:
*/
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 2c64620254eb..143e556f141d 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -752,7 +752,6 @@ struct usb_gadget {
unsigned is_selfpowered:1;
unsigned deactivated:1;
unsigned connected:1;
- bool l1_supported;
bool remote_wakeup;
};
#define work_to_gadget(w) (container_of((w), struct usb_gadget, work))
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 3740366d9fc5..cef429cf3dce 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -502,7 +502,7 @@ extern void usb_hc_died(struct usb_hcd *hcd);
extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
extern void usb_wakeup_notification(struct usb_device *hdev,
unsigned int portnum);
-
+extern void usb_flush_hub_wq(void);
extern void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum);
extern void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum);
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
index fb650ab2693f..75d0912aa459 100644
--- a/include/media/msm_cam_sensor.h
+++ b/include/media/msm_cam_sensor.h
@@ -2,6 +2,7 @@
#define __LINUX_MSM_CAM_SENSOR_H
#include <uapi/media/msm_cam_sensor.h>
+#include <uapi/media/msm_camsensor_sdk.h>
#include <linux/compat.h>
@@ -72,6 +73,16 @@ struct csid_cfg_data32 {
} cfg;
};
+struct msm_ir_led_cfg_data_t32 {
+ enum msm_ir_led_cfg_type_t cfg_type;
+ int32_t pwm_duty_on_ns;
+ int32_t pwm_period_ns;
+};
+
+struct msm_ir_cut_cfg_data_t32 {
+ enum msm_ir_cut_cfg_type_t cfg_type;
+};
+
struct eeprom_read_t32 {
compat_uptr_t dbuffer;
uint32_t num_bytes;
@@ -258,6 +269,12 @@ struct msm_flash_cfg_data_t32 {
#define VIDIOC_MSM_FLASH_CFG32 \
_IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_flash_cfg_data_t32)
+
+#define VIDIOC_MSM_IR_LED_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct msm_ir_led_cfg_data_t32)
+
+#define VIDIOC_MSM_IR_CUT_CFG32 \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_cut_cfg_data_t32)
#endif
#endif
diff --git a/include/net/cnss.h b/include/net/cnss.h
index 5508d5e46d18..be58e32e6c7a 100644
--- a/include/net/cnss.h
+++ b/include/net/cnss.h
@@ -108,6 +108,7 @@ enum cnss_runtime_request {
CNSS_PM_RUNTIME_PUT_NOIDLE,
CNSS_PM_REQUEST_RESUME,
CNSS_PM_RUNTIME_PUT_AUTO,
+ CNSS_PM_GET_NORESUME,
};
extern int cnss_get_fw_image(struct image_desc_info *image_desc_info);
@@ -120,6 +121,8 @@ extern void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver);
extern int cnss_get_fw_files(struct cnss_fw_files *pfw_files);
extern int cnss_get_fw_files_for_target(struct cnss_fw_files *pfw_files,
u32 target_type, u32 target_version);
+extern void cnss_get_qca9377_fw_files(struct cnss_fw_files *pfw_files,
+ u32 size, u32 tufello_dual_fw);
extern int cnss_request_bus_bandwidth(int bandwidth);
diff --git a/include/soc/qcom/core_ctl.h b/include/soc/qcom/core_ctl.h
deleted file mode 100644
index 08b43058b37c..000000000000
--- a/include/soc/qcom/core_ctl.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2014-2015, 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 __SOC_QCOM_CORE_CTL_H
-#define __SOC_QCOM_CORE_CTL_H
-
-enum {
- CORE_CTL_EVAL_NEED,
- CORE_CTL_SET_BUSY,
- CORE_CTL_N_TRACE_EVENTS,
-};
-
-extern void core_ctl_block_hotplug(void);
-extern void core_ctl_unblock_hotplug(void);
-extern s64 core_ctl_get_time(void);
-extern struct cpufreq_policy *core_ctl_get_policy(int cpu);
-extern void core_ctl_put_policy(struct cpufreq_policy *policy);
-extern struct device *core_ctl_find_cpu_device(unsigned cpu);
-extern int core_ctl_online_core(unsigned int cpu);
-extern int core_ctl_offline_core(unsigned int cpu);
-
-#define USE_CORE_CTL_TRACE
-extern void core_ctl_trace(int type, int cpu, int arg1, int arg2, int arg3);
-
-#endif
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index efc5425cf17d..7e2f32883aa4 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);
@@ -119,10 +117,12 @@ extern int icnss_power_off(struct device *dev);
extern struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev);
extern int icnss_smmu_map(struct device *dev, phys_addr_t paddr,
uint32_t *iova_addr, size_t size);
+extern unsigned int icnss_socinfo_get_serial_number(struct device *dev);
extern int icnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count);
extern int icnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 *ch_count,
u16 buf_len);
extern int icnss_wlan_set_dfs_nol(const void *info, u16 info_len);
extern int icnss_wlan_get_dfs_nol(void *info, u16 info_len);
+extern bool icnss_is_qmi_disable(void);
#endif /* _ICNSS_WLAN_H_ */
diff --git a/include/soc/qcom/secure_buffer.h b/include/soc/qcom/secure_buffer.h
index ffa65569ce18..59971c08ed74 100644
--- a/include/soc/qcom/secure_buffer.h
+++ b/include/soc/qcom/secure_buffer.h
@@ -37,6 +37,7 @@ enum vmid {
VMID_CP_APP = 0x12,
VMID_WLAN = 0x18,
VMID_WLAN_CE = 0x19,
+ VMID_CP_CAMERA_PREVIEW = 0x1D,
VMID_LAST,
VMID_INVAL = -1
};
diff --git a/include/soc/qcom/service-notifier.h b/include/soc/qcom/service-notifier.h
index 598c91f7c9e2..eae879786d59 100644
--- a/include/soc/qcom/service-notifier.h
+++ b/include/soc/qcom/service-notifier.h
@@ -24,6 +24,11 @@ enum qmi_servreg_notif_service_state_enum_type_v01 {
SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01 = 0x7FFFFFFF,
};
+enum pd_subsys_state {
+ CRASHED,
+ SHUTDOWN,
+ UNKNOWN,
+};
#if defined(CONFIG_MSM_SERVICE_NOTIFIER)
/* service_notif_register_notifier() - Register a notifier for a service
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 636ae899c304..06b72b262395 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -896,20 +896,24 @@ struct adm_cmd_connect_afe_port_v5 {
#define AFE_PORT_ID_TERTIARY_MI2S_TX 0x1005
#define AFE_PORT_ID_QUATERNARY_MI2S_RX 0x1006
#define AFE_PORT_ID_QUATERNARY_MI2S_TX 0x1007
-#define AUDIO_PORT_ID_I2S_RX 0x1008
+#define AUDIO_PORT_ID_I2S_RX 0x1008
#define AFE_PORT_ID_DIGITAL_MIC_TX 0x1009
#define AFE_PORT_ID_PRIMARY_PCM_RX 0x100A
#define AFE_PORT_ID_PRIMARY_PCM_TX 0x100B
#define AFE_PORT_ID_SECONDARY_PCM_RX 0x100C
#define AFE_PORT_ID_SECONDARY_PCM_TX 0x100D
#define AFE_PORT_ID_MULTICHAN_HDMI_RX 0x100E
-#define AFE_PORT_ID_SECONDARY_MI2S_RX_SD1 0x1010
-#define AFE_PORT_ID_QUINARY_MI2S_RX 0x1016
-#define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017
+#define AFE_PORT_ID_SECONDARY_MI2S_RX_SD1 0x1010
+#define AFE_PORT_ID_TERTIARY_PCM_RX 0x1012
+#define AFE_PORT_ID_TERTIARY_PCM_TX 0x1013
+#define AFE_PORT_ID_QUATERNARY_PCM_RX 0x1014
+#define AFE_PORT_ID_QUATERNARY_PCM_TX 0x1015
+#define AFE_PORT_ID_QUINARY_MI2S_RX 0x1016
+#define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017
/* ID of the senary MI2S Rx port. */
-#define AFE_PORT_ID_SENARY_MI2S_RX 0x1018
+#define AFE_PORT_ID_SENARY_MI2S_RX 0x1018
/* ID of the senary MI2S Tx port. */
-#define AFE_PORT_ID_SENARY_MI2S_TX 0x1019
+#define AFE_PORT_ID_SENARY_MI2S_TX 0x1019
#define AFE_PORT_ID_SPDIF_RX 0x5000
#define AFE_PORT_ID_RT_PROXY_PORT_001_RX 0x2000
#define AFE_PORT_ID_RT_PROXY_PORT_001_TX 0x2001
@@ -2939,7 +2943,7 @@ struct asm_aac_enc_cfg_v2_t {
* number of channels at the input.
* The number of channels must not change during encoding.
*/
- uint32_t channel_cfg;
+ uint16_t channel_cfg;
/*
* Number of samples per second.
@@ -3674,6 +3678,8 @@ struct asm_softvolume_params {
#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 0x00010DDC
+#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 0x0001320C
+
#define ASM_MEDIA_FMT_EVRCB_FS 0x00010BEF
#define ASM_MEDIA_FMT_EVRCWB_FS 0x00010BF0
@@ -3776,6 +3782,56 @@ struct asm_multi_channel_pcm_fmt_blk_v3 {
*/
} __packed;
+struct asm_multi_channel_pcm_fmt_blk_v4 {
+ uint16_t num_channels;
+/*
+ * Number of channels
+ * Supported values: 1 to 8
+ */
+
+ uint16_t bits_per_sample;
+/*
+ * Number of bits per sample per channel
+ * Supported values: 16, 24, 32
+ */
+
+ uint32_t sample_rate;
+/*
+ * Number of samples per second
+ * Supported values: 2000 to 48000, 96000,192000 Hz
+ */
+
+ uint16_t is_signed;
+/* Flag that indicates that PCM samples are signed (1) */
+
+ uint16_t sample_word_size;
+/*
+ * Size in bits of the word that holds a sample of a channel.
+ * Supported values: 12,24,32
+ */
+
+ uint8_t channel_mapping[8];
+/*
+ * Each element, i, in the array describes channel i inside the buffer where
+ * 0 <= i < num_channels. Unused channels are set to 0.
+ */
+ uint16_t endianness;
+/*
+ * Flag to indicate the endianness of the pcm sample
+ * Supported values: 0 - Little endian (all other formats)
+ * 1 - Big endian (AIFF)
+ */
+ uint16_t mode;
+/*
+ * Mode to provide additional info about the pcm input data.
+ * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b,
+ * Q31 for unpacked 24b or 32b)
+ * 15 - for 16 bit
+ * 23 - for 24b packed or 8.24 format
+ * 31 - for 24b unpacked or 32bit
+ */
+} __packed;
+
/*
* Payload of the multichannel PCM configuration parameters in
* the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 media format.
@@ -3786,6 +3842,16 @@ struct asm_multi_channel_pcm_fmt_blk_param_v3 {
struct asm_multi_channel_pcm_fmt_blk_v3 param;
} __packed;
+/*
+ * Payload of the multichannel PCM configuration parameters in
+ * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 media format.
+ */
+struct asm_multi_channel_pcm_fmt_blk_param_v4 {
+ struct apr_hdr hdr;
+ struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+ struct asm_multi_channel_pcm_fmt_blk_v4 param;
+} __packed;
+
struct asm_stream_cmd_set_encdec_param {
u32 param_id;
/* ID of the parameter. */
@@ -3821,6 +3887,79 @@ struct asm_dec_ddp_endp_param_v2 {
int endp_param_value;
} __packed;
+/*
+ * Payload of the multichannel PCM encoder configuration parameters in
+ * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 media format.
+ */
+
+struct asm_multi_channel_pcm_enc_cfg_v4 {
+ struct apr_hdr hdr;
+ struct asm_stream_cmd_set_encdec_param encdec;
+ struct asm_enc_cfg_blk_param_v2 encblk;
+ uint16_t num_channels;
+ /*
+ * Number of PCM channels.
+ * @values
+ * - 0 -- Native mode
+ * - 1 -- 8 channels
+ * Native mode indicates that encoding must be performed with the number
+ * of channels at the input.
+ */
+ uint16_t bits_per_sample;
+ /*
+ * Number of bits per sample per channel.
+ * @values 16, 24
+ */
+ uint32_t sample_rate;
+ /*
+ * Number of samples per second.
+ * @values 0, 8000 to 48000 Hz
+ * A value of 0 indicates the native sampling rate. Encoding is
+ * performed at the input sampling rate.
+ */
+ uint16_t is_signed;
+ /*
+ * Flag that indicates the PCM samples are signed (1). Currently, only
+ * signed PCM samples are supported.
+ */
+ uint16_t sample_word_size;
+ /*
+ * The size in bits of the word that holds a sample of a channel.
+ * @values 16, 24, 32
+ * 16-bit samples are always placed in 16-bit words:
+ * sample_word_size = 1.
+ * 24-bit samples can be placed in 32-bit words or in consecutive
+ * 24-bit words.
+ * - If sample_word_size = 32, 24-bit samples are placed in the
+ * most significant 24 bits of a 32-bit word.
+ * - If sample_word_size = 24, 24-bit samples are placed in
+ * 24-bit words. @tablebulletend
+ */
+ uint8_t channel_mapping[8];
+ /*
+ * Channel mapping array expected at the encoder output.
+ * Channel[i] mapping describes channel i inside the buffer, where
+ * 0 @le i < num_channels. All valid used channels must be present at
+ * the beginning of the array.
+ * If Native mode is set for the channels, this field is ignored.
+ * @values See Section @xref{dox:PcmChannelDefs}
+ */
+ uint16_t endianness;
+ /*
+ * Flag to indicate the endianness of the pcm sample
+ * Supported values: 0 - Little endian (all other formats)
+ * 1 - Big endian (AIFF)
+ */
+ uint16_t mode;
+ /*
+ * Mode to provide additional info about the pcm input data.
+ * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b,
+ * Q31 for unpacked 24b or 32b)
+ * 15 - for 16 bit
+ * 23 - for 24b packed or 8.24 format
+ * 31 - for 24b unpacked or 32bit
+ */
+} __packed;
/*
* Payload of the multichannel PCM encoder configuration parameters in
diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h
index b252463b72a2..31f7c02b54b3 100644
--- a/include/sound/q6afe-v2.h
+++ b/include/sound/q6afe-v2.h
@@ -183,6 +183,10 @@ enum {
IDX_AFE_PORT_ID_USB_TX,
/* IDX 124 */
IDX_DISPLAY_PORT_RX,
+ IDX_AFE_PORT_ID_TERTIARY_PCM_RX,
+ IDX_AFE_PORT_ID_TERTIARY_PCM_TX,
+ IDX_AFE_PORT_ID_QUATERNARY_PCM_RX,
+ IDX_AFE_PORT_ID_QUATERNARY_PCM_TX,
AFE_MAX_PORTS
};
@@ -277,7 +281,7 @@ void afe_set_cal_mode(u16 port_id, enum afe_cal_mode afe_cal_mode);
int afe_port_start(u16 port_id, union afe_port_config *afe_config,
u32 rate);
int afe_port_start_v2(u16 port_id, union afe_port_config *afe_config,
- u32 rate, u16 afe_in_channels,
+ u32 rate, u16 afe_in_channels, u16 afe_in_bit_width,
struct afe_enc_config *enc_config);
int afe_spk_prot_feed_back_cfg(int src_port, int dst_port,
int l_ch, int r_ch, u32 enable);
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 8525f2e7f738..f08bd73edb59 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -97,6 +97,24 @@
#define ASM_SHIFT_GAPLESS_MODE_FLAG 31
#define ASM_SHIFT_LAST_BUFFER_FLAG 30
+#define ASM_LITTLE_ENDIAN 0
+#define ASM_BIG_ENDIAN 1
+
+/* PCM_MEDIA_FORMAT_Version */
+enum {
+ PCM_MEDIA_FORMAT_V2 = 0,
+ PCM_MEDIA_FORMAT_V3,
+ PCM_MEDIA_FORMAT_V4,
+};
+
+/* PCM format modes in DSP */
+enum {
+ DEFAULT_QF = 0,
+ Q15 = 15,
+ Q23 = 23,
+ Q31 = 31,
+};
+
/* payload structure bytes */
#define READDONE_IDX_STATUS 0
#define READDONE_IDX_BUFADD_LSW 1
@@ -245,6 +263,9 @@ int q6asm_open_read_v2(struct audio_client *ac, uint32_t format,
int q6asm_open_read_v3(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample);
+int q6asm_open_read_v4(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample);
+
int q6asm_open_write(struct audio_client *ac, uint32_t format
/*, uint16_t bits_per_sample*/);
@@ -257,6 +278,9 @@ int q6asm_open_shared_io(struct audio_client *ac,
int q6asm_open_write_v3(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample);
+int q6asm_open_write_v4(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample);
+
int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample, int32_t stream_id,
bool is_gapless_mode);
@@ -265,6 +289,10 @@ int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample, int32_t stream_id,
bool is_gapless_mode);
+int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample, int32_t stream_id,
+ bool is_gapless_mode);
+
int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format,
uint32_t passthrough_flag);
@@ -339,6 +367,8 @@ int q6asm_stream_cmd_nowait(struct audio_client *ac, int cmd,
void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac,
uint32_t *size, uint32_t *idx);
+int q6asm_cpu_buf_release(int dir, struct audio_client *ac);
+
void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac,
uint32_t *size, uint32_t *idx);
@@ -367,6 +397,13 @@ int q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac,
bool use_back_flavor, u8 *channel_map,
uint16_t sample_word_size);
+int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample, bool use_default_chmap,
+ bool use_back_flavor, u8 *channel_map,
+ uint16_t sample_word_size, uint16_t endianness,
+ uint16_t mode);
+
int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
uint32_t rate, uint32_t channels,
uint16_t bits_per_sample);
@@ -376,6 +413,13 @@ int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac,
uint16_t bits_per_sample,
uint16_t sample_word_size);
+int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode);
+
int q6asm_set_encdec_chan_map(struct audio_client *ac,
uint32_t num_channels);
@@ -425,6 +469,17 @@ int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac,
char *channel_map,
uint16_t sample_word_size);
+int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac,
+ uint32_t rate,
+ uint32_t channels,
+ uint16_t bits_per_sample,
+ int stream_id,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode);
+
int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
uint32_t rate, uint32_t channels,
bool use_default_chmap, char *channel_map);
@@ -442,6 +497,15 @@ int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac,
uint16_t bits_per_sample,
uint16_t sample_word_size);
+int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode);
+
int q6asm_media_format_block_aac(struct audio_client *ac,
struct asm_aac_cfg *cfg);
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/power.h b/include/trace/events/power.h
index 2df03af6f328..e8bc27f3467e 100644
--- a/include/trace/events/power.h
+++ b/include/trace/events/power.h
@@ -1082,49 +1082,6 @@ DEFINE_EVENT(perf_cl_peak_timer_status, perf_cl_peak_exit_timer_stop,
timer_rate, mode)
);
-TRACE_EVENT(core_ctl_eval_need,
-
- TP_PROTO(unsigned int cpu, unsigned int old_need,
- unsigned int new_need, unsigned int updated),
- TP_ARGS(cpu, old_need, new_need, updated),
- TP_STRUCT__entry(
- __field(u32, cpu)
- __field(u32, old_need)
- __field(u32, new_need)
- __field(u32, updated)
- ),
- TP_fast_assign(
- __entry->cpu = cpu;
- __entry->old_need = old_need;
- __entry->new_need = new_need;
- __entry->updated = updated;
- ),
- TP_printk("cpu=%u, old_need=%u, new_need=%u, updated=%u", __entry->cpu,
- __entry->old_need, __entry->new_need, __entry->updated)
-);
-
-TRACE_EVENT(core_ctl_set_busy,
-
- TP_PROTO(unsigned int cpu, unsigned int busy,
- unsigned int old_is_busy, unsigned int is_busy),
- TP_ARGS(cpu, busy, old_is_busy, is_busy),
- TP_STRUCT__entry(
- __field(u32, cpu)
- __field(u32, busy)
- __field(u32, old_is_busy)
- __field(u32, is_busy)
- ),
- TP_fast_assign(
- __entry->cpu = cpu;
- __entry->busy = busy;
- __entry->old_is_busy = old_is_busy;
- __entry->is_busy = is_busy;
- ),
- TP_printk("cpu=%u, busy=%u, old_is_busy=%u, new_is_busy=%u",
- __entry->cpu, __entry->busy, __entry->old_is_busy,
- __entry->is_busy)
-);
-
#endif /* _TRACE_POWER_H */
/* This part must be outside protection */
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index 1ef5ec3eaf70..daf69b7df534 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -1244,6 +1244,84 @@ TRACE_EVENT(sched_get_nr_running_avg,
__entry->avg, __entry->big_avg, __entry->iowait_avg)
);
+TRACE_EVENT(core_ctl_eval_need,
+
+ TP_PROTO(unsigned int cpu, unsigned int old_need,
+ unsigned int new_need, unsigned int updated),
+ TP_ARGS(cpu, old_need, new_need, updated),
+ TP_STRUCT__entry(
+ __field(u32, cpu)
+ __field(u32, old_need)
+ __field(u32, new_need)
+ __field(u32, updated)
+ ),
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->old_need = old_need;
+ __entry->new_need = new_need;
+ __entry->updated = updated;
+ ),
+ TP_printk("cpu=%u, old_need=%u, new_need=%u, updated=%u", __entry->cpu,
+ __entry->old_need, __entry->new_need, __entry->updated)
+);
+
+TRACE_EVENT(core_ctl_set_busy,
+
+ TP_PROTO(unsigned int cpu, unsigned int busy,
+ unsigned int old_is_busy, unsigned int is_busy),
+ TP_ARGS(cpu, busy, old_is_busy, is_busy),
+ TP_STRUCT__entry(
+ __field(u32, cpu)
+ __field(u32, busy)
+ __field(u32, old_is_busy)
+ __field(u32, is_busy)
+ ),
+ TP_fast_assign(
+ __entry->cpu = cpu;
+ __entry->busy = busy;
+ __entry->old_is_busy = old_is_busy;
+ __entry->is_busy = is_busy;
+ ),
+ TP_printk("cpu=%u, busy=%u, old_is_busy=%u, new_is_busy=%u",
+ __entry->cpu, __entry->busy, __entry->old_is_busy,
+ __entry->is_busy)
+);
+
+/**
+ * sched_isolate - called when cores are isolated/unisolated
+ *
+ * @acutal_mask: mask of cores actually isolated/unisolated
+ * @req_mask: mask of cores requested isolated/unisolated
+ * @online_mask: cpu online mask
+ * @time: amount of time in us it took to isolate/unisolate
+ * @isolate: 1 if isolating, 0 if unisolating
+ *
+ */
+TRACE_EVENT(sched_isolate,
+
+ TP_PROTO(unsigned int requested_cpu, unsigned int isolated_cpus,
+ u64 start_time, unsigned char isolate),
+
+ TP_ARGS(requested_cpu, isolated_cpus, start_time, isolate),
+
+ TP_STRUCT__entry(
+ __field(u32, requested_cpu)
+ __field(u32, isolated_cpus)
+ __field(u32, time)
+ __field(unsigned char, isolate)
+ ),
+
+ TP_fast_assign(
+ __entry->requested_cpu = requested_cpu;
+ __entry->isolated_cpus = isolated_cpus;
+ __entry->time = div64_u64(sched_clock() - start_time, 1000);
+ __entry->isolate = isolate;
+ ),
+
+ TP_printk("iso cpu=%u cpus=0x%x time=%u us isolated=%d",
+ __entry->requested_cpu, __entry->isolated_cpus,
+ __entry->time, __entry->isolate)
+);
#endif /* _TRACE_SCHED_H */
/* This part must be outside protection */
diff --git a/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h
index 691df1b2689b..97eefc665130 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),
@@ -144,6 +192,64 @@ TRACE_EVENT(cluster_exit,
__entry->from_idle)
);
+TRACE_EVENT(cluster_pred_select,
+
+ TP_PROTO(const char *name, int index, u32 sleep_us,
+ u32 latency, int pred, u32 pred_us),
+
+ TP_ARGS(name, index, sleep_us, latency, pred, pred_us),
+
+ TP_STRUCT__entry(
+ __field(const char *, name)
+ __field(int, index)
+ __field(u32, sleep_us)
+ __field(u32, latency)
+ __field(int, pred)
+ __field(u32, pred_us)
+ ),
+
+ TP_fast_assign(
+ __entry->name = name;
+ __entry->index = index;
+ __entry->sleep_us = sleep_us;
+ __entry->latency = latency;
+ __entry->pred = pred;
+ __entry->pred_us = pred_us;
+ ),
+
+ TP_printk("name:%s idx:%d sleep_time:%u latency:%u pred:%d pred_us:%u",
+ __entry->name, __entry->index, __entry->sleep_us,
+ __entry->latency, __entry->pred, __entry->pred_us)
+);
+
+TRACE_EVENT(cluster_pred_hist,
+
+ TP_PROTO(const char *name, int idx, u32 resi,
+ u32 sample, u32 tmr),
+
+ TP_ARGS(name, idx, resi, sample, tmr),
+
+ TP_STRUCT__entry(
+ __field(const char *, name)
+ __field(int, idx)
+ __field(u32, resi)
+ __field(u32, sample)
+ __field(u32, tmr)
+ ),
+
+ TP_fast_assign(
+ __entry->name = name;
+ __entry->idx = idx;
+ __entry->resi = resi;
+ __entry->sample = sample;
+ __entry->tmr = tmr;
+ ),
+
+ TP_printk("name:%s idx:%d resi:%u sample:%u tmr:%u",
+ __entry->name, __entry->idx, __entry->resi,
+ __entry->sample, __entry->tmr)
+);
+
TRACE_EVENT(pre_pc_cb,
TP_PROTO(int tzflag),
diff --git a/include/uapi/linux/dvb/dmx.h b/include/uapi/linux/dvb/dmx.h
index 427e4899ed69..a768696c90f8 100644
--- a/include/uapi/linux/dvb/dmx.h
+++ b/include/uapi/linux/dvb/dmx.h
@@ -32,6 +32,11 @@
#define DMX_FILTER_SIZE 16
+/* Min recording chunk upon which event is generated */
+#define DMX_REC_BUFF_CHUNK_MIN_SIZE (100*188)
+
+#define DMX_MAX_DECODER_BUFFER_NUM (32)
+
enum dmx_output
{
DMX_OUT_DECODER, /* Streaming directly to decoder. */
@@ -108,6 +113,41 @@ struct dmx_sct_filter_params
#define DMX_KERNEL_CLIENT 0x8000
};
+enum dmx_video_codec {
+ DMX_VIDEO_CODEC_MPEG2,
+ DMX_VIDEO_CODEC_H264,
+ DMX_VIDEO_CODEC_VC1
+};
+
+/* Index entries types */
+#define DMX_IDX_RAI 0x00000001
+#define DMX_IDX_PUSI 0x00000002
+#define DMX_IDX_MPEG_SEQ_HEADER 0x00000004
+#define DMX_IDX_MPEG_GOP 0x00000008
+#define DMX_IDX_MPEG_FIRST_SEQ_FRAME_START 0x00000010
+#define DMX_IDX_MPEG_FIRST_SEQ_FRAME_END 0x00000020
+#define DMX_IDX_MPEG_I_FRAME_START 0x00000040
+#define DMX_IDX_MPEG_I_FRAME_END 0x00000080
+#define DMX_IDX_MPEG_P_FRAME_START 0x00000100
+#define DMX_IDX_MPEG_P_FRAME_END 0x00000200
+#define DMX_IDX_MPEG_B_FRAME_START 0x00000400
+#define DMX_IDX_MPEG_B_FRAME_END 0x00000800
+#define DMX_IDX_H264_SPS 0x00001000
+#define DMX_IDX_H264_PPS 0x00002000
+#define DMX_IDX_H264_FIRST_SPS_FRAME_START 0x00004000
+#define DMX_IDX_H264_FIRST_SPS_FRAME_END 0x00008000
+#define DMX_IDX_H264_IDR_START 0x00010000
+#define DMX_IDX_H264_IDR_END 0x00020000
+#define DMX_IDX_H264_NON_IDR_START 0x00040000
+#define DMX_IDX_H264_NON_IDR_END 0x00080000
+#define DMX_IDX_VC1_SEQ_HEADER 0x00100000
+#define DMX_IDX_VC1_ENTRY_POINT 0x00200000
+#define DMX_IDX_VC1_FIRST_SEQ_FRAME_START 0x00400000
+#define DMX_IDX_VC1_FIRST_SEQ_FRAME_END 0x00800000
+#define DMX_IDX_VC1_FRAME_START 0x01000000
+#define DMX_IDX_VC1_FRAME_END 0x02000000
+#define DMX_IDX_H264_ACCESS_UNIT_DEL 0x04000000
+#define DMX_IDX_H264_SEI 0x08000000
struct dmx_pes_filter_params
{
@@ -116,11 +156,457 @@ struct dmx_pes_filter_params
dmx_output_t output;
dmx_pes_type_t pes_type;
__u32 flags;
+
+ /*
+ * The following configures when the event
+ * DMX_EVENT_NEW_REC_CHUNK will be triggered.
+ * When new recorded data is received with size
+ * equal or larger than this value a new event
+ * will be triggered. This is relevant when
+ * output is DMX_OUT_TS_TAP or DMX_OUT_TSDEMUX_TAP,
+ * size must be at least DMX_REC_BUFF_CHUNK_MIN_SIZE
+ * and smaller than buffer size.
+ */
+ __u32 rec_chunk_size;
+
+ enum dmx_video_codec video_codec;
+};
+
+struct dmx_buffer_status {
+ /* size of buffer in bytes */
+ unsigned int size;
+
+ /* fullness of buffer in bytes */
+ unsigned int fullness;
+
+ /*
+ * How many bytes are free
+ * It's the same as: size-fullness-1
+ */
+ unsigned int free_bytes;
+
+ /* read pointer offset in bytes */
+ unsigned int read_offset;
+
+ /* write pointer offset in bytes */
+ unsigned int write_offset;
+
+ /* non-zero if data error occurred */
+ int error;
+};
+
+/* Events associated with each demux filter */
+enum dmx_event {
+ /* New PES packet is ready to be consumed */
+ DMX_EVENT_NEW_PES = 0x00000001,
+
+ /* New section is ready to be consumed */
+ DMX_EVENT_NEW_SECTION = 0x00000002,
+
+ /* New recording chunk is ready to be consumed */
+ DMX_EVENT_NEW_REC_CHUNK = 0x00000004,
+
+ /* New PCR value is ready */
+ DMX_EVENT_NEW_PCR = 0x00000008,
+
+ /* Overflow */
+ DMX_EVENT_BUFFER_OVERFLOW = 0x00000010,
+
+ /* Section was dropped due to CRC error */
+ DMX_EVENT_SECTION_CRC_ERROR = 0x00000020,
+
+ /* End-of-stream, no more data from this filter */
+ DMX_EVENT_EOS = 0x00000040,
+
+ /* New Elementary Stream data is ready */
+ DMX_EVENT_NEW_ES_DATA = 0x00000080,
+
+ /* Data markers */
+ DMX_EVENT_MARKER = 0x00000100,
+
+ /* New indexing entry is ready */
+ DMX_EVENT_NEW_INDEX_ENTRY = 0x00000200,
+
+ /*
+ * Section filter timer expired. This is notified
+ * when timeout is configured to section filter
+ * (dmx_sct_filter_params) and no sections were
+ * received for the given time.
+ */
+ DMX_EVENT_SECTION_TIMEOUT = 0x00000400,
+
+ /* Scrambling bits change between clear and scrambled */
+ DMX_EVENT_SCRAMBLING_STATUS_CHANGE = 0x00000800
+};
+
+enum dmx_oob_cmd {
+ /* End-of-stream, no more data from this filter */
+ DMX_OOB_CMD_EOS,
+
+ /* Data markers */
+ DMX_OOB_CMD_MARKER,
+};
+
+/* Flags passed in filter events */
+
+/* Continuity counter error was detected */
+#define DMX_FILTER_CC_ERROR 0x01
+
+/* Discontinuity indicator was set */
+#define DMX_FILTER_DISCONTINUITY_INDICATOR 0x02
+
+/* PES length in PES header is not correct */
+#define DMX_FILTER_PES_LENGTH_ERROR 0x04
+
+
+/* PES info associated with DMX_EVENT_NEW_PES event */
+struct dmx_pes_event_info {
+ /* Offset at which PES information starts */
+ __u32 base_offset;
+
+ /*
+ * Start offset at which PES data
+ * from the stream starts.
+ * Equal to base_offset if PES data
+ * starts from the beginning.
+ */
+ __u32 start_offset;
+
+ /* Total length holding the PES information */
+ __u32 total_length;
+
+ /* Actual length holding the PES data */
+ __u32 actual_length;
+
+ /* Local receiver timestamp in 27MHz */
+ __u64 stc;
+
+ /* Flags passed in filter events */
+ __u32 flags;
+
+ /*
+ * Number of TS packets with Transport Error Indicator (TEI)
+ * found while constructing the PES.
+ */
+ __u32 transport_error_indicator_counter;
+
+ /* Number of continuity errors found while constructing the PES */
+ __u32 continuity_error_counter;
+
+ /* Total number of TS packets holding the PES */
+ __u32 ts_packets_num;
+};
+
+/* Section info associated with DMX_EVENT_NEW_SECTION event */
+struct dmx_section_event_info {
+ /* Offset at which section information starts */
+ __u32 base_offset;
+
+ /*
+ * Start offset at which section data
+ * from the stream starts.
+ * Equal to base_offset if section data
+ * starts from the beginning.
+ */
+ __u32 start_offset;
+
+ /* Total length holding the section information */
+ __u32 total_length;
+
+ /* Actual length holding the section data */
+ __u32 actual_length;
+
+ /* Flags passed in filter events */
+ __u32 flags;
+};
+
+/* Recording info associated with DMX_EVENT_NEW_REC_CHUNK event */
+struct dmx_rec_chunk_event_info {
+ /* Offset at which recording chunk starts */
+ __u32 offset;
+
+ /* Size of recording chunk in bytes */
+ __u32 size;
+};
+
+/* PCR info associated with DMX_EVENT_NEW_PCR event */
+struct dmx_pcr_event_info {
+ /* Local timestamp in 27MHz
+ * when PCR packet was received
+ */
+ __u64 stc;
+
+ /* PCR value in 27MHz */
+ __u64 pcr;
+
+ /* Flags passed in filter events */
+ __u32 flags;
+};
+
+/*
+ * Elementary stream data information associated
+ * with DMX_EVENT_NEW_ES_DATA event
+ */
+struct dmx_es_data_event_info {
+ /* Buffer user-space handle */
+ int buf_handle;
+
+ /*
+ * Cookie to provide when releasing the buffer
+ * using the DMX_RELEASE_DECODER_BUFFER ioctl command
+ */
+ int cookie;
+
+ /* Offset of data from the beginning of the buffer */
+ __u32 offset;
+
+ /* Length of data in buffer (in bytes) */
+ __u32 data_len;
+
+ /* Indication whether PTS value is valid */
+ int pts_valid;
+
+ /* PTS value associated with the buffer */
+ __u64 pts;
+
+ /* Indication whether DTS value is valid */
+ int dts_valid;
+
+ /* DTS value associated with the buffer */
+ __u64 dts;
+
+ /* STC value associated with the buffer in 27MHz */
+ __u64 stc;
+
+ /*
+ * Number of TS packets with Transport Error Indicator (TEI) set
+ * in the TS packet header since last reported event
+ */
+ __u32 transport_error_indicator_counter;
+
+ /* Number of continuity errors since last reported event */
+ __u32 continuity_error_counter;
+
+ /* Total number of TS packets processed since last reported event */
+ __u32 ts_packets_num;
+
+ /*
+ * Number of dropped bytes due to insufficient buffer space,
+ * since last reported event
+ */
+ __u32 ts_dropped_bytes;
+};
+
+/* Marker details associated with DMX_EVENT_MARKER event */
+struct dmx_marker_event_info {
+ /* Marker id */
+ __u64 id;
+};
+
+/* Indexing information associated with DMX_EVENT_NEW_INDEX_ENTRY event */
+struct dmx_index_event_info {
+ /* Index entry type, one of DMX_IDX_* */
+ __u64 type;
+
+ /*
+ * The PID the index entry belongs to.
+ * In case of recording filter, multiple PIDs may exist in the same
+ * filter through DMX_ADD_PID ioctl and each can be indexed separately.
+ */
+ __u16 pid;
+
+ /*
+ * The TS packet number in the recorded data at which
+ * the indexing event is found.
+ */
+ __u64 match_tsp_num;
+
+ /*
+ * The TS packet number in the recorded data preceding
+ * match_tsp_num and has PUSI set.
+ */
+ __u64 last_pusi_tsp_num;
+
+ /* STC associated with match_tsp_num, in 27MHz */
+ __u64 stc;
+};
+
+/* Scrambling information associated with DMX_EVENT_SCRAMBLING_STATUS_CHANGE */
+struct dmx_scrambling_status_event_info {
+ /*
+ * The PID which its scrambling bit status changed.
+ * In case of recording filter, multiple PIDs may exist in the same
+ * filter through DMX_ADD_PID ioctl, each may have
+ * different scrambling bits status.
+ */
+ __u16 pid;
+
+ /* old value of scrambling bits */
+ __u8 old_value;
+
+ /* new value of scrambling bits */
+ __u8 new_value;
+};
+
+/*
+ * Filter's event returned through DMX_GET_EVENT.
+ * poll with POLLPRI would block until events are available.
+ */
+struct dmx_filter_event {
+ enum dmx_event type;
+
+ union {
+ struct dmx_pes_event_info pes;
+ struct dmx_section_event_info section;
+ struct dmx_rec_chunk_event_info recording_chunk;
+ struct dmx_pcr_event_info pcr;
+ struct dmx_es_data_event_info es_data;
+ struct dmx_marker_event_info marker;
+ struct dmx_index_event_info index;
+ struct dmx_scrambling_status_event_info scrambling_status;
+ } params;
+};
+
+/* Filter's buffer requirement returned in dmx_caps */
+struct dmx_buffer_requirement {
+ /* Buffer size alignment, 0 means no special requirement */
+ __u32 size_alignment;
+
+ /* Maximum buffer size allowed */
+ __u32 max_size;
+
+ /* Maximum number of linear buffers handled by demux */
+ __u32 max_buffer_num;
+
+ /* Feature support bitmap as detailed below */
+ __u32 flags;
+
+/* Buffer must be allocated as physically contiguous memory */
+#define DMX_BUFFER_CONTIGUOUS_MEM 0x1
+
+/* If the filter's data is decrypted, the buffer should be secured one */
+#define DMX_BUFFER_SECURED_IF_DECRYPTED 0x2
+
+/* Buffer can be allocated externally */
+#define DMX_BUFFER_EXTERNAL_SUPPORT 0x4
+
+/* Buffer can be allocated internally */
+#define DMX_BUFFER_INTERNAL_SUPPORT 0x8
+
+/* Filter output can be output to a linear buffer group */
+#define DMX_BUFFER_LINEAR_GROUP_SUPPORT 0x10
+
+/* Buffer may be allocated as cached buffer */
+#define DMX_BUFFER_CACHED 0x20
+};
+
+/* Out-of-band (OOB) command */
+struct dmx_oob_command {
+ enum dmx_oob_cmd type;
+
+ union {
+ struct dmx_marker_event_info marker;
+ } params;
};
typedef struct dmx_caps {
__u32 caps;
+
+/* Indicates whether demux support playback from memory in pull mode */
+#define DMX_CAP_PULL_MODE 0x01
+
+/* Indicates whether demux support indexing of recorded video stream */
+#define DMX_CAP_VIDEO_INDEXING 0x02
+
+/* Indicates whether demux support sending data directly to video decoder */
+#define DMX_CAP_VIDEO_DECODER_DATA 0x04
+
+/* Indicates whether demux support sending data directly to audio decoder */
+#define DMX_CAP_AUDIO_DECODER_DATA 0x08
+
+/* Indicates whether demux support sending data directly to subtitle decoder */
+#define DMX_CAP_SUBTITLE_DECODER_DATA 0x10
+
+/* Indicates whether TS insertion is supported */
+#define DMX_CAP_TS_INSERTION 0x20
+
+/* Indicates whether playback from secured input is supported */
+#define DMX_CAP_SECURED_INPUT_PLAYBACK 0x40
+
+/* Indicates whether automatic buffer flush upon overflow is allowed */
+#define DMX_CAP_AUTO_BUFFER_FLUSH 0x80
+
+ /* Number of decoders demux can output data to */
int num_decoders;
+
+ /* Number of demux devices */
+ int num_demux_devices;
+
+ /* Max number of PID filters */
+ int num_pid_filters;
+
+ /* Max number of section filters */
+ int num_section_filters;
+
+ /*
+ * Max number of section filters using same PID,
+ * 0 if not supported
+ */
+ int num_section_filters_per_pid;
+
+ /*
+ * Length of section filter, not including section
+ * length field (2 bytes).
+ */
+ int section_filter_length;
+
+ /* Max number of demod based input */
+ int num_demod_inputs;
+
+ /* Max number of memory based input */
+ int num_memory_inputs;
+
+ /* Overall bitrate from all inputs concurrently. Mbit/sec */
+ int max_bitrate;
+
+ /* Max bitrate from single demod input. Mbit/sec */
+ int demod_input_max_bitrate;
+
+ /* Max bitrate from single memory input. Mbit/sec */
+ int memory_input_max_bitrate;
+
+ /* Max number of supported cipher operations per PID */
+ int num_cipher_ops;
+
+ /* Max possible value of STC reported by demux, in 27MHz */
+ __u64 max_stc;
+
+ /*
+ * For indexing support (DMX_CAP_VIDEO_INDEXING capability) this is
+ * the max number of video pids that can be indexed for a single
+ * recording filter. If 0, means there is not limitation.
+ */
+ int recording_max_video_pids_indexed;
+
+ struct dmx_buffer_requirement section;
+
+ /* For PES not sent to decoder */
+ struct dmx_buffer_requirement pes;
+
+ /* For PES sent to decoder */
+ struct dmx_buffer_requirement decoder;
+
+ /* Recording buffer for recording of 188 bytes packets */
+ struct dmx_buffer_requirement recording_188_tsp;
+
+ /* Recording buffer for recording of 192 bytes packets */
+ struct dmx_buffer_requirement recording_192_tsp;
+
+ /* DVR input buffer for playback of 188 bytes packets */
+ struct dmx_buffer_requirement playback_188_tsp;
+
+ /* DVR input buffer for playback of 192 bytes packets */
+ struct dmx_buffer_requirement playback_192_tsp;
} dmx_caps_t;
typedef enum dmx_source {
@@ -134,12 +620,229 @@ typedef enum dmx_source {
DMX_SOURCE_DVR3
} dmx_source_t;
+enum dmx_tsp_format_t {
+ DMX_TSP_FORMAT_188 = 0,
+ DMX_TSP_FORMAT_192_TAIL,
+ DMX_TSP_FORMAT_192_HEAD,
+ DMX_TSP_FORMAT_204,
+};
+
+enum dmx_playback_mode_t {
+ /*
+ * In push mode, if one of output buffers
+ * is full, the buffer would overflow
+ * and demux continue processing incoming stream.
+ * This is the default mode. When playing from frontend,
+ * this is the only mode that is allowed.
+ */
+ DMX_PB_MODE_PUSH = 0,
+
+ /*
+ * In pull mode, if one of output buffers
+ * is full, demux stalls waiting for free space,
+ * this would cause DVR input buffer fullness
+ * to accumulate.
+ * This mode is possible only when playing
+ * from DVR.
+ */
+ DMX_PB_MODE_PULL,
+};
+
struct dmx_stc {
unsigned int num; /* input : which STC? 0..N */
unsigned int base; /* output: divisor for stc to get 90 kHz clock */
__u64 stc; /* output: stc in 'base'*90 kHz units */
};
+enum dmx_buffer_mode {
+ /*
+ * demux buffers are allocated internally
+ * by the demux driver. This is the default mode.
+ * DMX_SET_BUFFER_SIZE can be used to set the size of
+ * this buffer.
+ */
+ DMX_BUFFER_MODE_INTERNAL,
+
+ /*
+ * demux buffers are allocated externally and provided
+ * to demux through DMX_SET_BUFFER.
+ * When this mode is used DMX_SET_BUFFER_SIZE and
+ * mmap are prohibited.
+ */
+ DMX_BUFFER_MODE_EXTERNAL,
+};
+
+struct dmx_buffer {
+ unsigned int size;
+ int handle;
+
+ /*
+ * The following indication is relevant only when setting
+ * DVR input buffer. It indicates whether the input buffer
+ * being set is secured one or not. Secured (locked) buffers
+ * are required for playback from secured input. In such case
+ * write() syscall is not allowed.
+ */
+ int is_protected;
+};
+
+struct dmx_decoder_buffers {
+ /*
+ * Specify if linear buffer support is requested. If set, buffers_num
+ * must be greater than 1
+ */
+ int is_linear;
+
+ /*
+ * Specify number of external buffers allocated by user.
+ * If set to 0 means internal buffer allocation is requested
+ */
+ __u32 buffers_num;
+
+ /* Specify buffer size, either external or internal */
+ __u32 buffers_size;
+
+ /* Array of externally allocated buffer handles */
+ int handles[DMX_MAX_DECODER_BUFFER_NUM];
+};
+
+struct dmx_secure_mode {
+ /*
+ * Specifies whether the filter is secure or not.
+ * Filter should be set as secured if the filter's data *may* include
+ * encrypted data that would require decryption configured through
+ * DMX_SET_CIPHER ioctl. The setting may be done while
+ * filter is in idle state only.
+ */
+ int is_secured;
+};
+
+struct dmx_cipher_operation {
+ /* Indication whether the operation is encryption or decryption */
+ int encrypt;
+
+ /* The ID of the key used for decryption or encryption */
+ __u32 key_ladder_id;
+};
+
+#define DMX_MAX_CIPHER_OPERATIONS_COUNT 5
+struct dmx_cipher_operations {
+ /*
+ * The PID to perform the cipher operations on.
+ * In case of recording filter, multiple PIDs may exist in the same
+ * filter through DMX_ADD_PID ioctl, each may have different
+ * cipher operations.
+ */
+ __u16 pid;
+
+ /* Total number of operations */
+ __u8 operations_count;
+
+ /*
+ * Cipher operation to perform on the given PID.
+ * The operations are performed in the order they are given.
+ */
+ struct dmx_cipher_operation operations[DMX_MAX_CIPHER_OPERATIONS_COUNT];
+};
+
+struct dmx_events_mask {
+ /*
+ * Bitmask of events to be disabled (dmx_event).
+ * Disabled events will not be notified to the user.
+ * By default all events are enabled except for
+ * DMX_EVENT_NEW_ES_DATA.
+ * Overflow event can't be disabled.
+ */
+ __u32 disable_mask;
+
+ /*
+ * Bitmask of events that will not wake-up the user
+ * when user calls poll with POLLPRI flag.
+ * Events that are used as wake-up source should not be
+ * disabled in disable_mask or they would not be used
+ * as a wake-up source.
+ * By default all enabled events are set as wake-up events.
+ * Overflow event can't be disabled as a wake-up source.
+ */
+ __u32 no_wakeup_mask;
+
+ /*
+ * Number of ready wake-up events which will trigger
+ * a wake-up when user calls poll with POLLPRI flag.
+ * Default is set to 1.
+ */
+ __u32 wakeup_threshold;
+};
+
+struct dmx_indexing_params {
+ /*
+ * PID to index. In case of recording filter, multiple PIDs
+ * may exist in the same filter through DMX_ADD_PID ioctl.
+ * It is assumed that the PID was already added using DMX_ADD_PID
+ * or an error will be reported.
+ */
+ __u16 pid;
+
+ /* enable or disable indexing, default is disabled */
+ int enable;
+
+ /* combination of DMX_IDX_* bits */
+ __u64 types;
+};
+
+struct dmx_set_ts_insertion {
+ /*
+ * Unique identifier managed by the caller.
+ * This identifier can be used later to remove the
+ * insertion using DMX_ABORT_TS_INSERTION ioctl.
+ */
+ __u32 identifier;
+
+ /*
+ * Repetition time in msec, minimum allowed value is 25msec.
+ * 0 repetition time means one-shot insertion is done.
+ * Insertion done based on wall-clock.
+ */
+ __u32 repetition_time;
+
+ /*
+ * TS packets buffer to be inserted.
+ * The buffer is inserted as-is to the recording buffer
+ * without any modification.
+ * It is advised to set discontinuity flag in the very
+ * first TS packet in the buffer.
+ */
+ const __u8 *ts_packets;
+
+ /*
+ * Size in bytes of the TS packets buffer to be inserted.
+ * Should be in multiples of 188 or 192 bytes
+ * depending on recording filter output format.
+ */
+ size_t size;
+};
+
+struct dmx_abort_ts_insertion {
+ /*
+ * Identifier of the insertion buffer previously set
+ * using DMX_SET_TS_INSERTION.
+ */
+ __u32 identifier;
+};
+
+struct dmx_scrambling_bits {
+ /*
+ * The PID to return its scrambling bit value.
+ * In case of recording filter, multiple PIDs may exist in the same
+ * filter through DMX_ADD_PID ioctl, each may have different
+ * scrambling bits status.
+ */
+ __u16 pid;
+
+ /* Current value of scrambling bits: 0, 1, 2 or 3 */
+ __u8 value;
+};
+
#define DMX_START _IO('o', 41)
#define DMX_STOP _IO('o', 42)
#define DMX_SET_FILTER _IOW('o', 43, struct dmx_sct_filter_params)
@@ -151,5 +854,27 @@ struct dmx_stc {
#define DMX_GET_STC _IOWR('o', 50, struct dmx_stc)
#define DMX_ADD_PID _IOW('o', 51, __u16)
#define DMX_REMOVE_PID _IOW('o', 52, __u16)
+#define DMX_SET_TS_PACKET_FORMAT _IOW('o', 53, enum dmx_tsp_format_t)
+#define DMX_SET_TS_OUT_FORMAT _IOW('o', 54, enum dmx_tsp_format_t)
+#define DMX_SET_DECODER_BUFFER_SIZE _IO('o', 55)
+#define DMX_GET_BUFFER_STATUS _IOR('o', 56, struct dmx_buffer_status)
+#define DMX_RELEASE_DATA _IO('o', 57)
+#define DMX_FEED_DATA _IO('o', 58)
+#define DMX_SET_PLAYBACK_MODE _IOW('o', 59, enum dmx_playback_mode_t)
+#define DMX_GET_EVENT _IOR('o', 60, struct dmx_filter_event)
+#define DMX_SET_BUFFER_MODE _IOW('o', 61, enum dmx_buffer_mode)
+#define DMX_SET_BUFFER _IOW('o', 62, struct dmx_buffer)
+#define DMX_SET_DECODER_BUFFER _IOW('o', 63, struct dmx_decoder_buffers)
+#define DMX_REUSE_DECODER_BUFFER _IO('o', 64)
+#define DMX_SET_SECURE_MODE _IOW('o', 65, struct dmx_secure_mode)
+#define DMX_SET_EVENTS_MASK _IOW('o', 66, struct dmx_events_mask)
+#define DMX_GET_EVENTS_MASK _IOR('o', 67, struct dmx_events_mask)
+#define DMX_PUSH_OOB_COMMAND _IOW('o', 68, struct dmx_oob_command)
+#define DMX_SET_INDEXING_PARAMS _IOW('o', 69, struct dmx_indexing_params)
+#define DMX_SET_TS_INSERTION _IOW('o', 70, struct dmx_set_ts_insertion)
+#define DMX_ABORT_TS_INSERTION _IOW('o', 71, struct dmx_abort_ts_insertion)
+#define DMX_GET_SCRAMBLING_BITS _IOWR('o', 72, struct dmx_scrambling_bits)
+#define DMX_SET_CIPHER _IOW('o', 73, struct dmx_cipher_operations)
+#define DMX_FLUSH_BUFFER _IO('o', 74)
#endif /* _UAPI_DVBDMX_H_ */
diff --git a/include/uapi/linux/ipa_qmi_service_v01.h b/include/uapi/linux/ipa_qmi_service_v01.h
index c26a3ccf3645..60867630e1a1 100644
--- a/include/uapi/linux/ipa_qmi_service_v01.h
+++ b/include/uapi/linux/ipa_qmi_service_v01.h
@@ -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
@@ -44,6 +44,7 @@
#define QMI_IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS_V01 2
#define QMI_IPA_IPFLTR_NUM_MEQ_128_EQNS_V01 2
#define QMI_IPA_MAX_FILTERS_V01 64
+#define QMI_IPA_MAX_FILTERS_EX_V01 128
#define QMI_IPA_MAX_PIPES_V01 20
#define QMI_IPA_MAX_APN_V01 8
@@ -1543,6 +1544,84 @@ struct ipa_stop_data_usage_quota_resp_msg_v01 {
/**< Standard response type.*/
}; /* Message */
+/* Request Message; Request from Modem IPA driver to set DPL peripheral pipe */
+struct ipa_install_fltr_rule_req_ex_msg_v01 {
+
+ /* Optional */
+ /* Extended Filter Specification */
+ uint8_t filter_spec_ex_list_valid;
+ uint32_t filter_spec_ex_list_len;
+ struct ipa_filter_spec_ex_type_v01
+ filter_spec_ex_list[QMI_IPA_MAX_FILTERS_EX_V01];
+ /* List of filter specifications of filters that must be installed in
+ the IPAv3.x hardware.
+ The driver installing these rules must do so in the same order as
+ specified in this list.
+ */
+
+ /* Optional */
+ /* Pipe Index to Install Rule */
+ uint8_t source_pipe_index_valid;
+ uint32_t source_pipe_index;
+ /* Pipe index to install the filter rule.
+ The requester may not always know the pipe indices. If not specified,
+ the receiver must install this rule on all pipes that it controls,
+ through which data may be fed into the IPA.
+ */
+
+ /* Optional */
+ /* Total Number of IPv4 Filters in the Filter Spec List */
+ uint8_t num_ipv4_filters_valid;
+ uint32_t num_ipv4_filters;
+ /* Number of IPv4 rules included in the filter specification list.
+ */
+
+ /* Optional */
+ /* Total Number of IPv6 Filters in the Filter Spec List */
+ uint8_t num_ipv6_filters_valid;
+ uint32_t num_ipv6_filters;
+ /* Number of IPv6 rules included in the filter specification list.
+ */
+
+ /* Optional */
+ /* List of XLAT Filter Indices in the Filter Spec List */
+ uint8_t xlat_filter_indices_list_valid;
+ uint32_t xlat_filter_indices_list_len;
+ uint32_t xlat_filter_indices_list[QMI_IPA_MAX_FILTERS_EX_V01];
+ /* List of XLAT filter indices.
+ Filter rules at specified indices must be modified by the
+ receiver if the PDN is XLAT before installing them on the associated
+ IPA consumer pipe.
+ */
+}; /* Message */
+
+/* Response Message; Requests installation of filtering rules in the hardware
+ * block on the remote side.
+ */
+struct ipa_install_fltr_rule_resp_ex_msg_v01 {
+ /* Mandatory */
+ /* Result Code */
+ struct ipa_qmi_response_type_v01 resp;
+ /* Standard response type.
+ Standard response type. Contains the following data members:
+ - qmi_result_type -- QMI_RESULT_SUCCESS or QMI_RESULT_FAILURE
+ - qmi_error_type -- Error code. Possible error code values are
+ described in the error codes
+ section of each message
+ definition.
+ */
+
+ /* Optional */
+ /* Rule ID List */
+ uint8_t rule_id_valid;
+ uint32_t rule_id_len;
+ uint32_t rule_id[QMI_IPA_MAX_FILTERS_EX_V01];
+ /* List of rule IDs returned to the control point.
+ Any further reference to the rule is done using the filter rule ID
+ specified in this list.
+ */
+}; /* Message */
+
/*Service Message Definition*/
#define QMI_IPA_INDICATION_REGISTER_REQ_V01 0x0020
#define QMI_IPA_INDICATION_REGISTER_RESP_V01 0x0020
@@ -1574,6 +1653,8 @@ struct ipa_stop_data_usage_quota_resp_msg_v01 {
#define QMI_IPA_STOP_DATA_USAGE_QUOTA_RESP_V01 0x0034
#define QMI_IPA_INIT_MODEM_DRIVER_CMPLT_REQ_V01 0x0035
#define QMI_IPA_INIT_MODEM_DRIVER_CMPLT_RESP_V01 0x0035
+#define QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_V01 0x0037
+#define QMI_IPA_INSTALL_FILTER_RULE_EX_RESP_V01 0x0037
/* add for max length*/
#define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 134
@@ -1612,6 +1693,9 @@ struct ipa_stop_data_usage_quota_resp_msg_v01 {
#define QMI_IPA_INIT_MODEM_DRIVER_CMPLT_REQ_MAX_MSG_LEN_V01 4
#define QMI_IPA_INIT_MODEM_DRIVER_CMPLT_RESP_MAX_MSG_LEN_V01 7
+#define QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_MAX_MSG_LEN_V01 22685
+#define QMI_IPA_INSTALL_FILTER_RULE_EX_RESP_MAX_MSG_LEN_V01 523
+
/* Service Object Accessor */
#endif/* IPA_QMI_SERVICE_V01_H */
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/include/uapi/linux/rmnet_ipa_fd_ioctl.h b/include/uapi/linux/rmnet_ipa_fd_ioctl.h
index cbffbf6c2925..228bfe8274c6 100644
--- a/include/uapi/linux/rmnet_ipa_fd_ioctl.h
+++ b/include/uapi/linux/rmnet_ipa_fd_ioctl.h
@@ -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
@@ -31,6 +31,7 @@
#define WAN_IOCTL_QUERY_TETHER_STATS 6
#define WAN_IOCTL_RESET_TETHER_STATS 7
#define WAN_IOCTL_QUERY_DL_FILTER_STATS 8
+#define WAN_IOCTL_ADD_FLT_RULE_EX 9
/* User space may not have this defined. */
#ifndef IFNAMSIZ
@@ -150,4 +151,8 @@ struct wan_ioctl_query_dl_filter_stats {
WAN_IOCTL_QUERY_DL_FILTER_STATS, \
struct wan_ioctl_query_dl_filter_stats *)
+#define WAN_IOC_ADD_FLT_RULE_EX _IOWR(WAN_IOC_MAGIC, \
+ WAN_IOCTL_ADD_FLT_RULE_EX, \
+ struct ipa_install_fltr_rule_req_ex_msg_v01 *)
+
#endif /* _RMNET_IPA_FD_IOCTL_H */
diff --git a/include/uapi/media/msm_cam_sensor.h b/include/uapi/media/msm_cam_sensor.h
index 2c7ada5d02cf..172545d34b7d 100644
--- a/include/uapi/media/msm_cam_sensor.h
+++ b/include/uapi/media/msm_cam_sensor.h
@@ -86,6 +86,8 @@ enum sensor_sub_module_t {
SUB_MODULE_CSIPHY_3D,
SUB_MODULE_OIS,
SUB_MODULE_EXT,
+ SUB_MODULE_IR_LED,
+ SUB_MODULE_IR_CUT,
SUB_MODULE_MAX,
};
@@ -289,6 +291,16 @@ struct msm_eeprom_info_t {
struct msm_eeprom_memory_map_array *mem_map_array;
};
+struct msm_ir_led_cfg_data_t {
+ enum msm_ir_led_cfg_type_t cfg_type;
+ int32_t pwm_duty_on_ns;
+ int32_t pwm_period_ns;
+};
+
+struct msm_ir_cut_cfg_data_t {
+ enum msm_ir_cut_cfg_type_t cfg_type;
+};
+
struct msm_eeprom_cfg_data {
enum eeprom_cfg_type_t cfgtype;
uint8_t is_supported;
@@ -598,5 +610,11 @@ struct sensor_init_cfg_data {
#define VIDIOC_MSM_FLASH_QUERY_DATA \
_IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_flash_query_data_t)
+#define VIDIOC_MSM_IR_LED_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_led_cfg_data_t)
+
+#define VIDIOC_MSM_IR_CUT_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_ir_cut_cfg_data_t)
+
#endif
diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h
index 01e52b6f7b44..ad0825e33217 100644
--- a/include/uapi/media/msm_camsensor_sdk.h
+++ b/include/uapi/media/msm_camsensor_sdk.h
@@ -114,6 +114,15 @@ enum msm_sensor_power_seq_gpio_t {
SENSOR_GPIO_MAX,
};
+enum msm_ir_cut_filter_gpio_t {
+ IR_CUT_FILTER_GPIO_P = 0,
+ IR_CUT_FILTER_GPIO_M,
+ IR_CUT_FILTER_GPIO_MAX,
+};
+#define IR_CUT_FILTER_GPIO_P IR_CUT_FILTER_GPIO_P
+#define IR_CUT_FILTER_GPIO_M IR_CUT_FILTER_GPIO_M
+#define R_CUT_FILTER_GPIO_MAX IR_CUT_FILTER_GPIO_MAX
+
enum msm_camera_vreg_name_t {
CAM_VDIG,
CAM_VIO,
@@ -182,6 +191,28 @@ enum msm_flash_cfg_type_t {
CFG_FLASH_HIGH,
};
+enum msm_ir_led_cfg_type_t {
+ CFG_IR_LED_INIT = 0,
+ CFG_IR_LED_RELEASE,
+ CFG_IR_LED_OFF,
+ CFG_IR_LED_ON,
+};
+#define CFG_IR_LED_INIT CFG_IR_LED_INIT
+#define CFG_IR_LED_RELEASE CFG_IR_LED_RELEASE
+#define CFG_IR_LED_OFF CFG_IR_LED_OFF
+#define CFG_IR_LED_ON CFG_IR_LED_ON
+
+enum msm_ir_cut_cfg_type_t {
+ CFG_IR_CUT_INIT = 0,
+ CFG_IR_CUT_RELEASE,
+ CFG_IR_CUT_OFF,
+ CFG_IR_CUT_ON,
+};
+#define CFG_IR_CUT_INIT CFG_IR_CUT_INIT
+#define CFG_IR_CUT_RELEASE CFG_IR_CUT_RELEASE
+#define CFG_IR_CUT_OFF CFG_IR_CUT_OFF
+#define CFG_IR_CUT_ON CFG_IR_CUT_ON
+
enum msm_sensor_output_format_t {
MSM_SENSOR_BAYER,
MSM_SENSOR_YCBCR,
diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h
index fe70daa772df..071331ef6882 100644
--- a/include/uapi/media/msmb_camera.h
+++ b/include/uapi/media/msmb_camera.h
@@ -48,7 +48,9 @@
#define MSM_CAMERA_SUBDEV_SENSOR_INIT 14
#define MSM_CAMERA_SUBDEV_OIS 15
#define MSM_CAMERA_SUBDEV_FLASH 16
-#define MSM_CAMERA_SUBDEV_EXT 17
+#define MSM_CAMERA_SUBDEV_IR_LED 17
+#define MSM_CAMERA_SUBDEV_IR_CUT 18
+#define MSM_CAMERA_SUBDEV_EXT 19
#define MSM_MAX_CAMERA_SENSORS 5
diff --git a/include/uapi/sound/wcd-dsp-glink.h b/include/uapi/sound/wcd-dsp-glink.h
index db92e6b41340..39d128d370a0 100644
--- a/include/uapi/sound/wcd-dsp-glink.h
+++ b/include/uapi/sound/wcd-dsp-glink.h
@@ -8,7 +8,9 @@
enum {
WDSP_REG_PKT = 1,
WDSP_CMD_PKT,
+ WDSP_READY_PKT,
};
+#define WDSP_READY_PKT WDSP_READY_PKT
/*
* struct wdsp_reg_pkt - Glink channel information structure format
diff --git a/init/Kconfig b/init/Kconfig
index 9ad1ae9d9da8..311669332867 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1170,6 +1170,16 @@ config SCHED_HMP_CSTATE_AWARE
with CPUs C-state. If this is enabled, scheduler places tasks
onto the shallowest C-state CPU among the most power efficient CPUs.
+config SCHED_CORE_CTL
+ bool "QTI Core Control"
+ depends on SMP
+ help
+ This options enables the core control functionality in
+ the scheduler. Core control automatically offline and
+ online cores based on cpu load and utilization.
+
+ If unsure, say N here.
+
config CHECKPOINT_RESTORE
bool "Checkpoint/restore support" if EXPERT
select PROC_CHILDREN
@@ -1254,6 +1264,32 @@ config SCHED_AUTOGROUP
desktop applications. Task group autogeneration is currently based
upon task session.
+config SCHED_TUNE
+ bool "Boosting for CFS tasks (EXPERIMENTAL)"
+ help
+ This option enables the system-wide support for task boosting.
+ When this support is enabled a new sysctl interface is exposed to
+ userspace via:
+ /proc/sys/kernel/sched_cfs_boost
+ which allows to set a system-wide boost value in range [0..100].
+
+ The currently boosting strategy is implemented in such a way that:
+ - a 0% boost value requires to operate in "standard" mode by
+ scheduling all tasks at the minimum capacities required by their
+ workload demand
+ - a 100% boost value requires to push at maximum the task
+ performances, "regardless" of the incurred energy consumption
+
+ A boost value in between these two boundaries is used to bias the
+ power/performance trade-off, the higher the boost value the more the
+ scheduler is biased toward performance boosting instead of energy
+ efficiency.
+
+ Since this support exposes a single system-wide knob, the specified
+ boost value is applied to all (CFS) tasks in the system.
+
+ If unsure, say N.
+
config SYSFS_DEPRECATED
bool "Enable deprecated sysfs features to support old userspace tools"
depends on SYSFS
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index e8d71110ed2a..cc6c7d0a6758 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -4744,14 +4744,15 @@ static void css_free_work_fn(struct work_struct *work)
if (ss) {
/* css free path */
+ struct cgroup_subsys_state *parent = css->parent;
int id = css->id;
- if (css->parent)
- css_put(css->parent);
-
ss->css_free(css);
cgroup_idr_remove(&ss->css_idr, id);
cgroup_put(cgrp);
+
+ if (parent)
+ css_put(parent);
} else {
/* cgroup free path */
atomic_dec(&cgrp->root->nr_cgrps);
@@ -5375,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/cpu.c b/kernel/cpu.c
index 1cfd381642da..3c97f5b88a07 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -768,6 +768,10 @@ static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits);
EXPORT_SYMBOL(cpu_active_mask);
+static DECLARE_BITMAP(cpu_isolated_bits, CONFIG_NR_CPUS) __read_mostly;
+const struct cpumask *const cpu_isolated_mask = to_cpumask(cpu_isolated_bits);
+EXPORT_SYMBOL(cpu_isolated_mask);
+
void set_cpu_possible(unsigned int cpu, bool possible)
{
if (possible)
@@ -802,6 +806,14 @@ void set_cpu_active(unsigned int cpu, bool active)
cpumask_clear_cpu(cpu, to_cpumask(cpu_active_bits));
}
+void set_cpu_isolated(unsigned int cpu, bool isolated)
+{
+ if (isolated)
+ cpumask_set_cpu(cpu, to_cpumask(cpu_isolated_bits));
+ else
+ cpumask_clear_cpu(cpu, to_cpumask(cpu_isolated_bits));
+}
+
void init_cpu_present(const struct cpumask *src)
{
cpumask_copy(to_cpumask(cpu_present_bits), src);
@@ -817,6 +829,11 @@ void init_cpu_online(const struct cpumask *src)
cpumask_copy(to_cpumask(cpu_online_bits), src);
}
+void init_cpu_isolated(const struct cpumask *src)
+{
+ cpumask_copy(to_cpumask(cpu_isolated_bits), src);
+}
+
static ATOMIC_NOTIFIER_HEAD(idle_notifier);
void idle_notifier_register(struct notifier_block *n)
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index 69d25607d41b..a7ec545308a6 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -326,8 +326,7 @@ static struct file_system_type cpuset_fs_type = {
/*
* Return in pmask the portion of a cpusets's cpus_allowed that
* are online. If none are online, walk up the cpuset hierarchy
- * until we find one that does have some online cpus. The top
- * cpuset always has some cpus online.
+ * until we find one that does have some online cpus.
*
* One way or another, we guarantee to return some non-empty subset
* of cpu_online_mask.
@@ -336,8 +335,20 @@ static struct file_system_type cpuset_fs_type = {
*/
static void guarantee_online_cpus(struct cpuset *cs, struct cpumask *pmask)
{
- while (!cpumask_intersects(cs->effective_cpus, cpu_online_mask))
+ while (!cpumask_intersects(cs->effective_cpus, cpu_online_mask)) {
cs = parent_cs(cs);
+ if (unlikely(!cs)) {
+ /*
+ * The top cpuset doesn't have any online cpu as a
+ * consequence of a race between cpuset_hotplug_work
+ * and cpu hotplug notifier. But we know the top
+ * cpuset's effective_cpus is on its way to to be
+ * identical to cpu_online_mask.
+ */
+ cpumask_copy(pmask, cpu_online_mask);
+ return;
+ }
+ }
cpumask_and(pmask, cs->effective_cpus, cpu_online_mask);
}
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 32e2617d654f..5beb88f11671 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -176,9 +176,12 @@ static struct srcu_struct pmus_srcu;
* 0 - disallow raw tracepoint access for unpriv
* 1 - disallow cpu events for unpriv
* 2 - disallow kernel profiling for unpriv
+ * 3 - disallow all unpriv perf event use
*/
#ifdef CONFIG_PERF_EVENTS_USERMODE
int sysctl_perf_event_paranoid __read_mostly = -1;
+#elif defined CONFIG_SECURITY_PERF_EVENTS_RESTRICT
+int sysctl_perf_event_paranoid __read_mostly = 3;
#else
int sysctl_perf_event_paranoid __read_mostly = 1;
#endif
@@ -3383,7 +3386,8 @@ static int perf_event_read(struct perf_event *event, bool group)
* If event is enabled and currently active on a CPU, update the
* value in the event structure:
*/
- if (event->state == PERF_EVENT_STATE_ACTIVE) {
+ if (event->state == PERF_EVENT_STATE_ACTIVE &&
+ !cpu_isolated(event->oncpu)) {
struct perf_read_data data = {
.event = event,
.group = group,
@@ -8325,6 +8329,9 @@ SYSCALL_DEFINE5(perf_event_open,
if (flags & ~PERF_FLAG_ALL)
return -EINVAL;
+ if (perf_paranoid_any() && !capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
err = perf_copy_attr(attr_uptr, &attr);
if (err)
return err;
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/irq/cpuhotplug.c b/kernel/irq/cpuhotplug.c
index 011f8c4c63da..dac3724e4c1e 100644
--- a/kernel/irq/cpuhotplug.c
+++ b/kernel/irq/cpuhotplug.c
@@ -11,6 +11,7 @@
#include <linux/interrupt.h>
#include <linux/ratelimit.h>
#include <linux/irq.h>
+#include <linux/cpumask.h>
#include "internals.h"
@@ -20,6 +21,7 @@ static bool migrate_one_irq(struct irq_desc *desc)
const struct cpumask *affinity = d->common->affinity;
struct irq_chip *c;
bool ret = false;
+ struct cpumask available_cpus;
/*
* If this is a per-CPU interrupt, or the affinity does not
@@ -29,8 +31,15 @@ static bool migrate_one_irq(struct irq_desc *desc)
!cpumask_test_cpu(smp_processor_id(), affinity))
return false;
+ cpumask_copy(&available_cpus, affinity);
+ cpumask_andnot(&available_cpus, &available_cpus, cpu_isolated_mask);
+ affinity = &available_cpus;
+
if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
- affinity = cpu_online_mask;
+ cpumask_andnot(&available_cpus, cpu_online_mask,
+ cpu_isolated_mask);
+ if (cpumask_empty(affinity))
+ affinity = cpu_online_mask;
ret = true;
}
@@ -69,6 +78,9 @@ void irq_migrate_all_off_this_cpu(void)
bool affinity_broken;
desc = irq_to_desc(irq);
+ if (!desc)
+ continue;
+
raw_spin_lock(&desc->lock);
affinity_broken = migrate_one_irq(desc);
raw_spin_unlock(&desc->lock);
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/power/qos.c b/kernel/power/qos.c
index 8ecc7b3f7dd9..69c32c42080f 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -45,6 +45,7 @@
#include <linux/seq_file.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
+#include <linux/cpumask.h>
#include <linux/uaccess.h>
#include <linux/export.h>
@@ -447,6 +448,9 @@ EXPORT_SYMBOL_GPL(pm_qos_request);
int pm_qos_request_for_cpu(int pm_qos_class, int cpu)
{
+ if (cpu_isolated(cpu))
+ return INT_MAX;
+
return pm_qos_array[pm_qos_class]->constraints->target_per_cpu[cpu];
}
EXPORT_SYMBOL(pm_qos_request_for_cpu);
@@ -469,6 +473,9 @@ int pm_qos_request_for_cpumask(int pm_qos_class, struct cpumask *mask)
val = c->default_value;
for_each_cpu(cpu, mask) {
+ if (cpu_isolated(cpu))
+ continue;
+
switch (c->type) {
case PM_QOS_MIN:
if (c->target_per_cpu[cpu] < val)
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/Makefile b/kernel/sched/Makefile
index 1f159743ebfc..7d0d34c53e08 100644
--- a/kernel/sched/Makefile
+++ b/kernel/sched/Makefile
@@ -19,4 +19,6 @@ obj-$(CONFIG_SCHED_HMP) += hmp.o
obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o
obj-$(CONFIG_SCHEDSTATS) += stats.o
obj-$(CONFIG_SCHED_DEBUG) += debug.o
+obj-$(CONFIG_SCHED_TUNE) += tune.o
obj-$(CONFIG_CGROUP_CPUACCT) += cpuacct.o
+obj-$(CONFIG_SCHED_CORE_CTL) += core_ctl.o
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 7474463b9835..ff7f6f35fc8f 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -74,6 +74,7 @@
#include <linux/binfmts.h>
#include <linux/context_tracking.h>
#include <linux/compiler.h>
+#include <linux/irq.h>
#include <asm/switch_to.h>
#include <asm/tlb.h>
@@ -84,6 +85,7 @@
#endif
#include "sched.h"
+#include "core_ctl.h"
#include "../workqueue_internal.h"
#include "../smpboot.h"
@@ -1229,6 +1231,7 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
struct rq *rq;
unsigned int dest_cpu;
int ret = 0;
+ cpumask_t allowed_mask;
rq = task_rq_lock(p, &flags);
@@ -1244,16 +1247,22 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
if (cpumask_equal(&p->cpus_allowed, new_mask))
goto out;
- dest_cpu = cpumask_any_and(cpu_active_mask, new_mask);
+ cpumask_andnot(&allowed_mask, new_mask, cpu_isolated_mask);
+
+ dest_cpu = cpumask_any_and(cpu_active_mask, &allowed_mask);
if (dest_cpu >= nr_cpu_ids) {
- ret = -EINVAL;
- goto out;
+ dest_cpu = cpumask_any_and(cpu_active_mask, new_mask);
+ if (dest_cpu >= nr_cpu_ids) {
+ ret = -EINVAL;
+ goto out;
+ }
+ cpumask_copy(&allowed_mask, new_mask);
}
do_set_cpus_allowed(p, new_mask);
/* Can the task run on the task's current CPU? If so, we're done */
- if (cpumask_test_cpu(task_cpu(p), new_mask))
+ if (cpumask_test_cpu(task_cpu(p), &allowed_mask))
goto out;
if (task_running(rq, p) || p->state == TASK_WAKING) {
@@ -1577,12 +1586,13 @@ EXPORT_SYMBOL_GPL(kick_process);
/*
* ->cpus_allowed is protected by both rq->lock and p->pi_lock
*/
-static int select_fallback_rq(int cpu, struct task_struct *p)
+static int select_fallback_rq(int cpu, struct task_struct *p, bool allow_iso)
{
int nid = cpu_to_node(cpu);
const struct cpumask *nodemask = NULL;
- enum { cpuset, possible, fail } state = cpuset;
+ enum { cpuset, possible, fail, bug } state = cpuset;
int dest_cpu;
+ int isolated_candidate = -1;
/*
* If the node that the cpu is on has been offlined, cpu_to_node()
@@ -1598,6 +1608,8 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
continue;
if (!cpu_active(dest_cpu))
continue;
+ if (cpu_isolated(dest_cpu))
+ continue;
if (cpumask_test_cpu(dest_cpu, tsk_cpus_allowed(p)))
return dest_cpu;
}
@@ -1610,6 +1622,16 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
continue;
if (!cpu_active(dest_cpu))
continue;
+ if (cpu_isolated(dest_cpu)) {
+ if (allow_iso)
+ isolated_candidate = dest_cpu;
+ continue;
+ }
+ goto out;
+ }
+
+ if (isolated_candidate != -1) {
+ dest_cpu = isolated_candidate;
goto out;
}
@@ -1628,6 +1650,11 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
break;
case fail:
+ allow_iso = true;
+ state = bug;
+ break;
+
+ case bug:
BUG();
break;
}
@@ -1655,6 +1682,8 @@ out:
static inline
int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags)
{
+ bool allow_isolated = (p->flags & PF_KTHREAD);
+
lockdep_assert_held(&p->pi_lock);
if (p->nr_cpus_allowed > 1)
@@ -1671,8 +1700,9 @@ int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags)
* not worry about this generic constraint ]
*/
if (unlikely(!cpumask_test_cpu(cpu, tsk_cpus_allowed(p)) ||
- !cpu_online(cpu)))
- cpu = select_fallback_rq(task_cpu(p), p);
+ !cpu_online(cpu)) ||
+ (cpu_isolated(cpu) && !allow_isolated))
+ cpu = select_fallback_rq(task_cpu(p), p, allow_isolated);
return cpu;
}
@@ -2956,7 +2986,7 @@ void sched_exec(void)
if (dest_cpu == smp_processor_id())
goto unlock;
- if (likely(cpu_active(dest_cpu))) {
+ if (likely(cpu_active(dest_cpu) && likely(!cpu_isolated(dest_cpu)))) {
struct migration_arg arg = { p, dest_cpu };
raw_spin_unlock_irqrestore(&p->pi_lock, flags);
@@ -3066,6 +3096,8 @@ void scheduler_tick(void)
if (curr->sched_class == &fair_sched_class)
check_for_migration(rq, curr);
+
+ core_ctl_check(wallclock);
}
#ifdef CONFIG_NO_HZ_FULL
@@ -3197,7 +3229,8 @@ static noinline void __schedule_bug(struct task_struct *prev)
static inline void schedule_debug(struct task_struct *prev)
{
#ifdef CONFIG_SCHED_STACK_END_CHECK
- BUG_ON(task_stack_end_corrupted(prev));
+ if (task_stack_end_corrupted(prev))
+ panic("corrupted stack end detected inside scheduler\n");
#endif
if (unlikely(in_atomic_preempt_off())) {
@@ -3357,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;
@@ -3377,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);
}
@@ -5414,18 +5449,22 @@ static struct task_struct fake_task = {
};
/*
- * Migrate all tasks from the rq, sleeping tasks will be migrated by
- * try_to_wake_up()->select_task_rq().
+ * Migrate all tasks (not pinned if pinned argument say so) from the rq,
+ * sleeping tasks will be migrated by try_to_wake_up()->select_task_rq().
*
* Called with rq->lock held even though we'er in stop_machine() and
* there's no concurrency possible, we hold the required locks anyway
* because of lock validation efforts.
*/
-static void migrate_tasks(struct rq *dead_rq)
+static void migrate_tasks(struct rq *dead_rq, bool migrate_pinned_tasks)
{
struct rq *rq = dead_rq;
struct task_struct *next, *stop = rq->stop;
int dest_cpu;
+ unsigned int num_pinned_kthreads = 1; /* this thread */
+ cpumask_t avail_cpus;
+
+ cpumask_andnot(&avail_cpus, cpu_online_mask, cpu_isolated_mask);
/*
* Fudge the rq selection such that the below task selection loop
@@ -5447,10 +5486,12 @@ static void migrate_tasks(struct rq *dead_rq)
for (;;) {
/*
- * There's this thread running, bail when that's the only
- * remaining thread.
+ * There's this thread running + pinned threads, bail when
+ * that's the only remaining threads.
*/
- if (rq->nr_running == 1)
+ if ((migrate_pinned_tasks && rq->nr_running == 1) ||
+ (!migrate_pinned_tasks &&
+ rq->nr_running == num_pinned_kthreads))
break;
/*
@@ -5461,6 +5502,13 @@ static void migrate_tasks(struct rq *dead_rq)
BUG_ON(!next);
next->sched_class->put_prev_task(rq, next);
+ if (!migrate_pinned_tasks && next->flags & PF_KTHREAD &&
+ !cpumask_intersects(&avail_cpus, &next->cpus_allowed)) {
+ lockdep_unpin_lock(&rq->lock);
+ num_pinned_kthreads += 1;
+ continue;
+ }
+
/*
* Rules for changing task_struct::cpus_allowed are holding
* both pi_lock and rq->lock, such that holding either
@@ -5486,7 +5534,7 @@ static void migrate_tasks(struct rq *dead_rq)
}
/* Find suitable destination for @next, with force if needed. */
- dest_cpu = select_fallback_rq(dead_rq->cpu, next);
+ dest_cpu = select_fallback_rq(dead_rq->cpu, next, false);
rq = __migrate_task(rq, next, dest_cpu);
if (rq != dead_rq) {
@@ -5502,6 +5550,222 @@ static void migrate_tasks(struct rq *dead_rq)
rq->stop = stop;
}
+
+static void set_rq_online(struct rq *rq);
+static void set_rq_offline(struct rq *rq);
+
+int do_isolation_work_cpu_stop(void *data)
+{
+ unsigned long flags;
+ unsigned int cpu = smp_processor_id();
+ struct rq *rq = cpu_rq(cpu);
+
+ watchdog_disable(cpu);
+
+ irq_migrate_all_off_this_cpu();
+
+ sched_ttwu_pending();
+ /* Update our root-domain */
+ raw_spin_lock_irqsave(&rq->lock, flags);
+
+ if (rq->rd) {
+ BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span));
+ set_rq_offline(rq);
+ }
+
+ migrate_tasks(rq, false);
+ raw_spin_unlock_irqrestore(&rq->lock, flags);
+
+ /*
+ * We might have been in tickless state. Clear NOHZ flags to avoid
+ * us being kicked for helping out with balancing
+ */
+ nohz_balance_clear_nohz_mask(cpu);
+ return 0;
+}
+
+int do_unisolation_work_cpu_stop(void *data)
+{
+ watchdog_enable(smp_processor_id());
+ return 0;
+}
+
+static void init_sched_groups_capacity(int cpu, struct sched_domain *sd);
+
+static void sched_update_group_capacities(int cpu)
+{
+ struct sched_domain *sd;
+
+ mutex_lock(&sched_domains_mutex);
+ rcu_read_lock();
+
+ for_each_domain(cpu, sd) {
+ int balance_cpu = group_balance_cpu(sd->groups);
+
+ init_sched_groups_capacity(cpu, sd);
+ /*
+ * Need to ensure this is also called with balancing
+ * cpu.
+ */
+ if (cpu != balance_cpu)
+ init_sched_groups_capacity(balance_cpu, sd);
+ }
+
+ rcu_read_unlock();
+ mutex_unlock(&sched_domains_mutex);
+}
+
+static unsigned int cpu_isolation_vote[NR_CPUS];
+
+int sched_isolate_count(const cpumask_t *mask, bool include_offline)
+{
+ cpumask_t count_mask = CPU_MASK_NONE;
+
+ if (include_offline) {
+ cpumask_complement(&count_mask, cpu_online_mask);
+ cpumask_or(&count_mask, &count_mask, cpu_isolated_mask);
+ cpumask_and(&count_mask, &count_mask, mask);
+ } else {
+ cpumask_and(&count_mask, mask, cpu_isolated_mask);
+ }
+
+ return cpumask_weight(&count_mask);
+}
+
+/*
+ * 1) CPU is isolated and cpu is offlined:
+ * Unisolate the core.
+ * 2) CPU is not isolated and CPU is offlined:
+ * No action taken.
+ * 3) CPU is offline and request to isolate
+ * Request ignored.
+ * 4) CPU is offline and isolated:
+ * Not a possible state.
+ * 5) CPU is online and request to isolate
+ * Normal case: Isolate the CPU
+ * 6) CPU is not isolated and comes back online
+ * Nothing to do
+ *
+ * Note: The client calling sched_isolate_cpu() is repsonsible for ONLY
+ * calling sched_unisolate_cpu() on a CPU that the client previously isolated.
+ * Client is also responsible for unisolating when a core goes offline
+ * (after CPU is marked offline).
+ */
+int sched_isolate_cpu(int cpu)
+{
+ struct rq *rq = cpu_rq(cpu);
+ cpumask_t avail_cpus;
+ int ret_code = 0;
+ u64 start_time;
+
+ if (trace_sched_isolate_enabled())
+ start_time = sched_clock();
+
+ lock_device_hotplug();
+
+ cpumask_andnot(&avail_cpus, cpu_online_mask, cpu_isolated_mask);
+
+ /* We cannot isolate ALL cpus in the system */
+ if (cpumask_weight(&avail_cpus) == 1) {
+ ret_code = -EINVAL;
+ goto out;
+ }
+
+ if (!cpu_online(cpu)) {
+ ret_code = -EINVAL;
+ goto out;
+ }
+
+ if (++cpu_isolation_vote[cpu] > 1)
+ goto out;
+
+ set_cpu_isolated(cpu, true);
+ cpumask_clear_cpu(cpu, &avail_cpus);
+
+ /* Migrate timers */
+ smp_call_function_any(&avail_cpus, hrtimer_quiesce_cpu, &cpu, 1);
+ smp_call_function_any(&avail_cpus, timer_quiesce_cpu, &cpu, 1);
+
+ migrate_sync_cpu(cpu, cpumask_first(&avail_cpus));
+ stop_cpus(cpumask_of(cpu), do_isolation_work_cpu_stop, 0);
+
+ clear_hmp_request(cpu);
+ calc_load_migrate(rq);
+ update_max_interval();
+ sched_update_group_capacities(cpu);
+
+out:
+ unlock_device_hotplug();
+ trace_sched_isolate(cpu, cpumask_bits(cpu_isolated_mask)[0],
+ start_time, 1);
+ return ret_code;
+}
+
+/*
+ * Note: The client calling sched_isolate_cpu() is repsonsible for ONLY
+ * calling sched_unisolate_cpu() on a CPU that the client previously isolated.
+ * Client is also responsible for unisolating when a core goes offline
+ * (after CPU is marked offline).
+ */
+int sched_unisolate_cpu_unlocked(int cpu)
+{
+ int ret_code = 0;
+ struct rq *rq = cpu_rq(cpu);
+ u64 start_time;
+
+ if (trace_sched_isolate_enabled())
+ start_time = sched_clock();
+
+ lock_device_hotplug_assert();
+
+ if (!cpu_isolation_vote[cpu]) {
+ ret_code = -EINVAL;
+ goto out;
+ }
+
+ if (--cpu_isolation_vote[cpu])
+ goto out;
+
+ if (cpu_online(cpu)) {
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&rq->lock, flags);
+ rq->age_stamp = sched_clock_cpu(cpu);
+ if (rq->rd) {
+ BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span));
+ set_rq_online(rq);
+ }
+ raw_spin_unlock_irqrestore(&rq->lock, flags);
+ }
+
+ set_cpu_isolated(cpu, false);
+ update_max_interval();
+ sched_update_group_capacities(cpu);
+
+ if (cpu_online(cpu)) {
+ stop_cpus(cpumask_of(cpu), do_unisolation_work_cpu_stop, 0);
+
+ /* Kick CPU to immediately do load balancing */
+ if (!test_and_set_bit(NOHZ_BALANCE_KICK, nohz_flags(cpu)))
+ smp_send_reschedule(cpu);
+ }
+
+out:
+ trace_sched_isolate(cpu, cpumask_bits(cpu_isolated_mask)[0],
+ start_time, 0);
+ return ret_code;
+}
+
+int sched_unisolate_cpu(int cpu)
+{
+ int ret_code;
+
+ lock_device_hotplug();
+ ret_code = sched_unisolate_cpu_unlocked(cpu);
+ unlock_device_hotplug();
+ return ret_code;
+}
+
#endif /* CONFIG_HOTPLUG_CPU */
#if defined(CONFIG_SCHED_DEBUG) && defined(CONFIG_SYSCTL)
@@ -5748,13 +6012,13 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
sched_ttwu_pending();
/* Update our root-domain */
raw_spin_lock_irqsave(&rq->lock, flags);
- migrate_sync_cpu(cpu);
+ migrate_sync_cpu(cpu, smp_processor_id());
if (rq->rd) {
BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span));
set_rq_offline(rq);
}
- migrate_tasks(rq);
+ migrate_tasks(rq, true);
BUG_ON(rq->nr_running != 1); /* the migration thread */
raw_spin_unlock_irqrestore(&rq->lock, flags);
break;
@@ -6509,11 +6773,14 @@ build_sched_groups(struct sched_domain *sd, int cpu)
static void init_sched_groups_capacity(int cpu, struct sched_domain *sd)
{
struct sched_group *sg = sd->groups;
+ cpumask_t avail_mask;
WARN_ON(!sg);
do {
- sg->group_weight = cpumask_weight(sched_group_cpus(sg));
+ cpumask_andnot(&avail_mask, sched_group_cpus(sg),
+ cpu_isolated_mask);
+ sg->group_weight = cpumask_weight(&avail_mask);
sg = sg->next;
} while (sg != sd->groups);
@@ -7547,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();
@@ -7595,9 +7863,8 @@ void __init sched_init(void)
BUG_ON(num_possible_cpus() > BITS_PER_LONG);
-#ifdef CONFIG_SCHED_HMP
+ sched_hmp_parse_dt();
init_clusters();
-#endif
#ifdef CONFIG_FAIR_GROUP_SCHED
alloc_size += 2 * nr_cpu_ids * sizeof(void **);
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
new file mode 100644
index 000000000000..d81886da7ca2
--- /dev/null
+++ b/kernel/sched/core_ctl.c
@@ -0,0 +1,1055 @@
+/* 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/cpufreq.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/sched/rt.h>
+
+#include <trace/events/sched.h>
+
+#define MAX_CPUS_PER_CLUSTER 4
+#define MAX_CLUSTERS 2
+
+struct cluster_data {
+ bool inited;
+ unsigned int min_cpus;
+ unsigned int max_cpus;
+ unsigned int offline_delay_ms;
+ unsigned int busy_up_thres[MAX_CPUS_PER_CLUSTER];
+ unsigned int busy_down_thres[MAX_CPUS_PER_CLUSTER];
+ unsigned int active_cpus;
+ unsigned int num_cpus;
+ cpumask_t cpu_mask;
+ unsigned int need_cpus;
+ unsigned int task_thres;
+ s64 last_isolate_ts;
+ struct list_head lru;
+ bool pending;
+ spinlock_t pending_lock;
+ bool is_big_cluster;
+ int nrrun;
+ bool nrrun_changed;
+ struct task_struct *core_ctl_thread;
+ unsigned int first_cpu;
+ bool boost;
+ struct kobject kobj;
+};
+
+struct cpu_data {
+ bool online;
+ bool is_busy;
+ unsigned int busy;
+ unsigned int cpu;
+ bool not_preferred;
+ struct cluster_data *cluster;
+ struct list_head sib;
+ bool isolated_by_us;
+};
+
+static DEFINE_PER_CPU(struct cpu_data, cpu_state);
+static struct cluster_data cluster_state[MAX_CLUSTERS];
+static unsigned int num_clusters;
+
+#define for_each_cluster(cluster, idx) \
+ for ((cluster) = &cluster_state[idx]; (idx) < num_clusters;\
+ (idx)++, (cluster) = &cluster_state[idx])
+
+static DEFINE_SPINLOCK(state_lock);
+static void apply_need(struct cluster_data *state);
+static void wake_up_core_ctl_thread(struct cluster_data *state);
+static bool initialized;
+
+static unsigned int get_active_cpu_count(const struct cluster_data *cluster);
+
+/* ========================= sysfs interface =========================== */
+
+static ssize_t store_min_cpus(struct cluster_data *state,
+ const char *buf, size_t count)
+{
+ unsigned int val;
+
+ if (sscanf(buf, "%u\n", &val) != 1)
+ return -EINVAL;
+
+ state->min_cpus = min(val, state->max_cpus);
+ wake_up_core_ctl_thread(state);
+
+ return count;
+}
+
+static ssize_t show_min_cpus(const struct cluster_data *state, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", state->min_cpus);
+}
+
+static ssize_t store_max_cpus(struct cluster_data *state,
+ const char *buf, size_t count)
+{
+ unsigned int val;
+
+ if (sscanf(buf, "%u\n", &val) != 1)
+ return -EINVAL;
+
+ val = min(val, state->num_cpus);
+ state->max_cpus = val;
+ state->min_cpus = min(state->min_cpus, state->max_cpus);
+ wake_up_core_ctl_thread(state);
+
+ return count;
+}
+
+static ssize_t show_max_cpus(const struct cluster_data *state, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", state->max_cpus);
+}
+
+static ssize_t store_offline_delay_ms(struct cluster_data *state,
+ const char *buf, size_t count)
+{
+ unsigned int val;
+
+ if (sscanf(buf, "%u\n", &val) != 1)
+ return -EINVAL;
+
+ state->offline_delay_ms = val;
+ apply_need(state);
+
+ return count;
+}
+
+static ssize_t show_task_thres(const struct cluster_data *state, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", state->task_thres);
+}
+
+static ssize_t store_task_thres(struct cluster_data *state,
+ const char *buf, size_t count)
+{
+ unsigned int val;
+
+ if (sscanf(buf, "%u\n", &val) != 1)
+ return -EINVAL;
+
+ if (val < state->num_cpus)
+ return -EINVAL;
+
+ state->task_thres = val;
+ apply_need(state);
+
+ return count;
+}
+
+static ssize_t show_offline_delay_ms(const struct cluster_data *state,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", state->offline_delay_ms);
+}
+
+static ssize_t store_busy_up_thres(struct cluster_data *state,
+ const char *buf, size_t count)
+{
+ unsigned int val[MAX_CPUS_PER_CLUSTER];
+ int ret, i;
+
+ ret = sscanf(buf, "%u %u %u %u\n", &val[0], &val[1], &val[2], &val[3]);
+ if (ret != 1 && ret != state->num_cpus)
+ return -EINVAL;
+
+ if (ret == 1) {
+ for (i = 0; i < state->num_cpus; i++)
+ state->busy_up_thres[i] = val[0];
+ } else {
+ for (i = 0; i < state->num_cpus; i++)
+ state->busy_up_thres[i] = val[i];
+ }
+ apply_need(state);
+ return count;
+}
+
+static ssize_t show_busy_up_thres(const struct cluster_data *state, char *buf)
+{
+ int i, count = 0;
+
+ for (i = 0; i < state->num_cpus; i++)
+ count += snprintf(buf + count, PAGE_SIZE - count, "%u ",
+ state->busy_up_thres[i]);
+
+ count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+ return count;
+}
+
+static ssize_t store_busy_down_thres(struct cluster_data *state,
+ const char *buf, size_t count)
+{
+ unsigned int val[MAX_CPUS_PER_CLUSTER];
+ int ret, i;
+
+ ret = sscanf(buf, "%u %u %u %u\n", &val[0], &val[1], &val[2], &val[3]);
+ if (ret != 1 && ret != state->num_cpus)
+ return -EINVAL;
+
+ if (ret == 1) {
+ for (i = 0; i < state->num_cpus; i++)
+ state->busy_down_thres[i] = val[0];
+ } else {
+ for (i = 0; i < state->num_cpus; i++)
+ state->busy_down_thres[i] = val[i];
+ }
+ apply_need(state);
+ return count;
+}
+
+static ssize_t show_busy_down_thres(const struct cluster_data *state, char *buf)
+{
+ int i, count = 0;
+
+ for (i = 0; i < state->num_cpus; i++)
+ count += snprintf(buf + count, PAGE_SIZE - count, "%u ",
+ state->busy_down_thres[i]);
+
+ count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+ return count;
+}
+
+static ssize_t store_is_big_cluster(struct cluster_data *state,
+ const char *buf, size_t count)
+{
+ unsigned int val;
+
+ if (sscanf(buf, "%u\n", &val) != 1)
+ return -EINVAL;
+
+ state->is_big_cluster = val ? 1 : 0;
+ return count;
+}
+
+static ssize_t show_is_big_cluster(const struct cluster_data *state, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", state->is_big_cluster);
+}
+
+static ssize_t show_cpus(const struct cluster_data *state, char *buf)
+{
+ struct cpu_data *c;
+ ssize_t count = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state_lock, flags);
+ list_for_each_entry(c, &state->lru, sib) {
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "CPU%u (%s)\n", c->cpu,
+ c->online ? "Online" : "Offline");
+ }
+ spin_unlock_irqrestore(&state_lock, flags);
+ return count;
+}
+
+static ssize_t show_need_cpus(const struct cluster_data *state, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", state->need_cpus);
+}
+
+static ssize_t show_active_cpus(const struct cluster_data *state, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", state->active_cpus);
+}
+
+static ssize_t show_global_state(const struct cluster_data *state, char *buf)
+{
+ struct cpu_data *c;
+ struct cluster_data *cluster;
+ ssize_t count = 0;
+ unsigned int cpu;
+
+ for_each_possible_cpu(cpu) {
+ c = &per_cpu(cpu_state, cpu);
+ if (!c->cluster)
+ continue;
+
+ cluster = c->cluster;
+ if (!cluster || !cluster->inited)
+ continue;
+
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "CPU%u\n", cpu);
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tCPU: %u\n", c->cpu);
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tOnline: %u\n", c->online);
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tActive: %u\n",
+ !cpu_isolated(c->cpu));
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tFirst CPU: %u\n",
+ cluster->first_cpu);
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tBusy%%: %u\n", c->busy);
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tIs busy: %u\n", c->is_busy);
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tNr running: %u\n", cluster->nrrun);
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tActive CPUs: %u\n", get_active_cpu_count(cluster));
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tNeed CPUs: %u\n", cluster->need_cpus);
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tBoost: %u\n", (unsigned int) cluster->boost);
+ }
+
+ return count;
+}
+
+static ssize_t store_not_preferred(struct cluster_data *state,
+ const char *buf, size_t count)
+{
+ struct cpu_data *c;
+ unsigned int i;
+ unsigned int val[MAX_CPUS_PER_CLUSTER];
+ unsigned long flags;
+ int ret;
+
+ ret = sscanf(buf, "%u %u %u %u\n", &val[0], &val[1], &val[2], &val[3]);
+ if (ret != 1 && ret != state->num_cpus)
+ return -EINVAL;
+
+ i = 0;
+ spin_lock_irqsave(&state_lock, flags);
+ list_for_each_entry(c, &state->lru, sib)
+ c->not_preferred = val[i++];
+ spin_unlock_irqrestore(&state_lock, flags);
+
+ return count;
+}
+
+static ssize_t show_not_preferred(const struct cluster_data *state, char *buf)
+{
+ struct cpu_data *c;
+ ssize_t count = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state_lock, flags);
+ list_for_each_entry(c, &state->lru, sib)
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "\tCPU:%d %u\n", c->cpu, c->not_preferred);
+ spin_unlock_irqrestore(&state_lock, flags);
+
+ return count;
+}
+
+
+struct core_ctl_attr {
+ struct attribute attr;
+ ssize_t (*show)(const struct cluster_data *, char *);
+ ssize_t (*store)(struct cluster_data *, const char *, size_t count);
+};
+
+#define core_ctl_attr_ro(_name) \
+static struct core_ctl_attr _name = \
+__ATTR(_name, 0444, show_##_name, NULL)
+
+#define core_ctl_attr_rw(_name) \
+static struct core_ctl_attr _name = \
+__ATTR(_name, 0644, show_##_name, store_##_name)
+
+core_ctl_attr_rw(min_cpus);
+core_ctl_attr_rw(max_cpus);
+core_ctl_attr_rw(offline_delay_ms);
+core_ctl_attr_rw(busy_up_thres);
+core_ctl_attr_rw(busy_down_thres);
+core_ctl_attr_rw(task_thres);
+core_ctl_attr_rw(is_big_cluster);
+core_ctl_attr_ro(cpus);
+core_ctl_attr_ro(need_cpus);
+core_ctl_attr_ro(active_cpus);
+core_ctl_attr_ro(global_state);
+core_ctl_attr_rw(not_preferred);
+
+static struct attribute *default_attrs[] = {
+ &min_cpus.attr,
+ &max_cpus.attr,
+ &offline_delay_ms.attr,
+ &busy_up_thres.attr,
+ &busy_down_thres.attr,
+ &task_thres.attr,
+ &is_big_cluster.attr,
+ &cpus.attr,
+ &need_cpus.attr,
+ &active_cpus.attr,
+ &global_state.attr,
+ &not_preferred.attr,
+ NULL
+};
+
+#define to_cluster_data(k) container_of(k, struct cluster_data, kobj)
+#define to_attr(a) container_of(a, struct core_ctl_attr, attr)
+static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct cluster_data *data = to_cluster_data(kobj);
+ struct core_ctl_attr *cattr = to_attr(attr);
+ ssize_t ret = -EIO;
+
+ if (cattr->show)
+ ret = cattr->show(data, buf);
+
+ return ret;
+}
+
+static ssize_t store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cluster_data *data = to_cluster_data(kobj);
+ struct core_ctl_attr *cattr = to_attr(attr);
+ ssize_t ret = -EIO;
+
+ if (cattr->store)
+ ret = cattr->store(data, buf, count);
+
+ return ret;
+}
+
+static const struct sysfs_ops sysfs_ops = {
+ .show = show,
+ .store = store,
+};
+
+static struct kobj_type ktype_core_ctl = {
+ .sysfs_ops = &sysfs_ops,
+ .default_attrs = default_attrs,
+};
+
+/* ==================== runqueue based core count =================== */
+
+#define RQ_AVG_TOLERANCE 2
+#define RQ_AVG_DEFAULT_MS 20
+#define NR_RUNNING_TOLERANCE 5
+static unsigned int rq_avg_period_ms = RQ_AVG_DEFAULT_MS;
+
+static s64 rq_avg_timestamp_ms;
+
+static void update_running_avg(bool trigger_update)
+{
+ int avg, iowait_avg, big_avg, old_nrrun;
+ s64 now;
+ unsigned long flags;
+ struct cluster_data *cluster;
+ unsigned int index = 0;
+
+ spin_lock_irqsave(&state_lock, flags);
+
+ now = ktime_to_ms(ktime_get());
+ if (now - rq_avg_timestamp_ms < rq_avg_period_ms - RQ_AVG_TOLERANCE) {
+ spin_unlock_irqrestore(&state_lock, flags);
+ return;
+ }
+ rq_avg_timestamp_ms = now;
+ sched_get_nr_running_avg(&avg, &iowait_avg, &big_avg);
+
+ spin_unlock_irqrestore(&state_lock, flags);
+
+ /*
+ * Round up to the next integer if the average nr running tasks
+ * is within NR_RUNNING_TOLERANCE/100 of the next integer.
+ * If normal rounding up is used, it will allow a transient task
+ * to trigger online event. By the time core is onlined, the task
+ * has finished.
+ * Rounding to closest suffers same problem because scheduler
+ * might only provide running stats per jiffy, and a transient
+ * task could skew the number for one jiffy. If core control
+ * samples every 2 jiffies, it will observe 0.5 additional running
+ * average which rounds up to 1 task.
+ */
+ avg = (avg + NR_RUNNING_TOLERANCE) / 100;
+ big_avg = (big_avg + NR_RUNNING_TOLERANCE) / 100;
+
+ for_each_cluster(cluster, index) {
+ if (!cluster->inited)
+ continue;
+ old_nrrun = cluster->nrrun;
+ /*
+ * Big cluster only need to take care of big tasks, but if
+ * there are not enough big cores, big tasks need to be run
+ * on little as well. Thus for little's runqueue stat, it
+ * has to use overall runqueue average, or derive what big
+ * tasks would have to be run on little. The latter approach
+ * is not easy to get given core control reacts much slower
+ * than scheduler, and can't predict scheduler's behavior.
+ */
+ cluster->nrrun = cluster->is_big_cluster ? big_avg : avg;
+ if (cluster->nrrun != old_nrrun) {
+ if (trigger_update)
+ apply_need(cluster);
+ else
+ cluster->nrrun_changed = true;
+ }
+ }
+ return;
+}
+
+/* adjust needed CPUs based on current runqueue information */
+static unsigned int apply_task_need(const struct cluster_data *cluster,
+ unsigned int new_need)
+{
+ /* unisolate all cores if there are enough tasks */
+ if (cluster->nrrun >= cluster->task_thres)
+ return cluster->num_cpus;
+
+ /* only unisolate more cores if there are tasks to run */
+ if (cluster->nrrun > new_need)
+ return new_need + 1;
+
+ return new_need;
+}
+
+/* ======================= load based core count ====================== */
+
+static unsigned int apply_limits(const struct cluster_data *cluster,
+ unsigned int need_cpus)
+{
+ return min(max(cluster->min_cpus, need_cpus), cluster->max_cpus);
+}
+
+static unsigned int get_active_cpu_count(const struct cluster_data *cluster)
+{
+ return cluster->num_cpus -
+ sched_isolate_count(&cluster->cpu_mask, true);
+}
+
+static bool is_active(const struct cpu_data *state)
+{
+ return state->online && !cpu_isolated(state->cpu);
+}
+
+static bool adjustment_possible(const struct cluster_data *cluster,
+ unsigned int need)
+{
+ return (need < cluster->active_cpus || (need > cluster->active_cpus &&
+ sched_isolate_count(&cluster->cpu_mask, false)));
+}
+
+static bool eval_need(struct cluster_data *cluster)
+{
+ unsigned long flags;
+ struct cpu_data *c;
+ unsigned int need_cpus = 0, last_need, thres_idx;
+ int ret = 0;
+ bool need_flag = false;
+ unsigned int active_cpus;
+ unsigned int new_need;
+
+ if (unlikely(!cluster->inited))
+ return 0;
+
+ spin_lock_irqsave(&state_lock, flags);
+
+ if (cluster->boost) {
+ need_cpus = cluster->max_cpus;
+ } else {
+ active_cpus = get_active_cpu_count(cluster);
+ thres_idx = active_cpus ? active_cpus - 1 : 0;
+ list_for_each_entry(c, &cluster->lru, sib) {
+ if (c->busy >= cluster->busy_up_thres[thres_idx])
+ c->is_busy = true;
+ else if (c->busy < cluster->busy_down_thres[thres_idx])
+ c->is_busy = false;
+ need_cpus += c->is_busy;
+ }
+ need_cpus = apply_task_need(cluster, need_cpus);
+ }
+ new_need = apply_limits(cluster, need_cpus);
+ need_flag = adjustment_possible(cluster, new_need);
+
+ last_need = cluster->need_cpus;
+ cluster->need_cpus = new_need;
+
+ if (!need_flag) {
+ spin_unlock_irqrestore(&state_lock, flags);
+ return 0;
+ }
+
+ if (need_cpus > cluster->active_cpus) {
+ ret = 1;
+ } else if (need_cpus < cluster->active_cpus) {
+ s64 now = ktime_to_ms(ktime_get());
+ s64 elapsed = now - cluster->last_isolate_ts;
+
+ ret = elapsed >= cluster->offline_delay_ms;
+ }
+
+ trace_core_ctl_eval_need(cluster->first_cpu, last_need, need_cpus,
+ ret && need_flag);
+ spin_unlock_irqrestore(&state_lock, flags);
+
+ return ret && need_flag;
+}
+
+static void apply_need(struct cluster_data *cluster)
+{
+ if (eval_need(cluster))
+ wake_up_core_ctl_thread(cluster);
+}
+
+static int core_ctl_set_busy(unsigned int cpu, unsigned int busy)
+{
+ struct cpu_data *c = &per_cpu(cpu_state, cpu);
+ struct cluster_data *cluster = c->cluster;
+ unsigned int old_is_busy = c->is_busy;
+
+ if (!cluster || !cluster->inited)
+ return 0;
+
+ update_running_avg(false);
+ if (c->busy == busy && !cluster->nrrun_changed)
+ return 0;
+ c->busy = busy;
+ cluster->nrrun_changed = false;
+
+ apply_need(cluster);
+ trace_core_ctl_set_busy(cpu, busy, old_is_busy, c->is_busy);
+ return 0;
+}
+
+/* ========================= core count enforcement ==================== */
+
+static void wake_up_core_ctl_thread(struct cluster_data *cluster)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cluster->pending_lock, flags);
+ cluster->pending = true;
+ spin_unlock_irqrestore(&cluster->pending_lock, flags);
+
+ wake_up_process_no_notif(cluster->core_ctl_thread);
+}
+
+static u64 core_ctl_check_timestamp;
+static u64 core_ctl_check_interval;
+
+static bool do_check(u64 wallclock)
+{
+ bool do_check = false;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state_lock, flags);
+ if ((wallclock - core_ctl_check_timestamp) >= core_ctl_check_interval) {
+ core_ctl_check_timestamp = wallclock;
+ do_check = true;
+ }
+ spin_unlock_irqrestore(&state_lock, flags);
+ return do_check;
+}
+
+void core_ctl_set_boost(bool boost)
+{
+ unsigned int index = 0;
+ struct cluster_data *cluster;
+
+ for_each_cluster(cluster, index) {
+ if (cluster->is_big_cluster && cluster->boost != boost) {
+ cluster->boost = boost;
+ apply_need(cluster);
+ }
+ }
+}
+
+void core_ctl_check(u64 wallclock)
+{
+ if (unlikely(!initialized))
+ return;
+
+ if (do_check(wallclock)) {
+ unsigned int index = 0;
+ struct cluster_data *cluster;
+
+ update_running_avg(true);
+
+ for_each_cluster(cluster, index) {
+ if (eval_need(cluster))
+ wake_up_core_ctl_thread(cluster);
+ }
+ }
+}
+
+static void move_cpu_lru(struct cpu_data *cpu_data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&state_lock, flags);
+ list_del(&cpu_data->sib);
+ list_add_tail(&cpu_data->sib, &cpu_data->cluster->lru);
+ spin_unlock_irqrestore(&state_lock, flags);
+}
+
+static void try_to_isolate(struct cluster_data *cluster, unsigned int need)
+{
+ struct cpu_data *c, *tmp;
+
+ list_for_each_entry_safe(c, tmp, &cluster->lru, sib) {
+ if (!is_active(c))
+ continue;
+ if (cluster->active_cpus == need)
+ break;
+ /* Don't offline busy CPUs. */
+ if (c->is_busy)
+ continue;
+
+ pr_debug("Trying to isolate CPU%u\n", c->cpu);
+ if (!sched_isolate_cpu(c->cpu)) {
+ c->isolated_by_us = true;
+ move_cpu_lru(c);
+ cluster->last_isolate_ts = ktime_to_ms(ktime_get());
+ } else {
+ pr_debug("Unable to isolate CPU%u\n", c->cpu);
+ }
+ cluster->active_cpus = get_active_cpu_count(cluster);
+ }
+
+ /*
+ * If the number of active CPUs is within the limits, then
+ * don't force isolation of any busy CPUs.
+ */
+ if (cluster->active_cpus <= cluster->max_cpus)
+ return;
+
+ list_for_each_entry_safe(c, tmp, &cluster->lru, sib) {
+ if (!is_active(c))
+ continue;
+ if (cluster->active_cpus <= cluster->max_cpus)
+ break;
+
+ pr_debug("Trying to isolate CPU%u\n", c->cpu);
+ if (!sched_isolate_cpu(c->cpu)) {
+ c->isolated_by_us = true;
+ move_cpu_lru(c);
+ cluster->last_isolate_ts = ktime_to_ms(ktime_get());
+ } else {
+ pr_debug("Unable to isolate CPU%u\n", c->cpu);
+ }
+ cluster->active_cpus = get_active_cpu_count(cluster);
+ }
+}
+
+static void __try_to_unisolate(struct cluster_data *cluster,
+ unsigned int need, bool force)
+{
+ struct cpu_data *c, *tmp;
+
+ list_for_each_entry_safe(c, tmp, &cluster->lru, sib) {
+ if (!c->isolated_by_us)
+ continue;
+ if ((c->online && !cpu_isolated(c->cpu)) ||
+ (!force && c->not_preferred))
+ continue;
+ if (cluster->active_cpus == need)
+ break;
+
+ pr_debug("Trying to unisolate CPU%u\n", c->cpu);
+ if (!sched_unisolate_cpu(c->cpu)) {
+ c->isolated_by_us = false;
+ move_cpu_lru(c);
+ } else {
+ pr_debug("Unable to unisolate CPU%u\n", c->cpu);
+ }
+ cluster->active_cpus = get_active_cpu_count(cluster);
+ }
+}
+
+static void try_to_unisolate(struct cluster_data *cluster, unsigned int need)
+{
+ bool force_use_non_preferred = false;
+
+ __try_to_unisolate(cluster, need, force_use_non_preferred);
+
+ if (cluster->active_cpus == need)
+ return;
+
+ force_use_non_preferred = true;
+ __try_to_unisolate(cluster, need, force_use_non_preferred);
+}
+
+static void __ref do_core_ctl(struct cluster_data *cluster)
+{
+ unsigned int need;
+
+ need = apply_limits(cluster, cluster->need_cpus);
+
+ if (adjustment_possible(cluster, need)) {
+ pr_debug("Trying to adjust group %u from %u to %u\n",
+ cluster->first_cpu, cluster->active_cpus, need);
+
+ if (cluster->active_cpus > need)
+ try_to_isolate(cluster, need);
+ else if (cluster->active_cpus < need)
+ try_to_unisolate(cluster, need);
+ }
+}
+
+static int __ref try_core_ctl(void *data)
+{
+ struct cluster_data *cluster = data;
+ unsigned long flags;
+
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_irqsave(&cluster->pending_lock, flags);
+ if (!cluster->pending) {
+ spin_unlock_irqrestore(&cluster->pending_lock, flags);
+ schedule();
+ if (kthread_should_stop())
+ break;
+ spin_lock_irqsave(&cluster->pending_lock, flags);
+ }
+ set_current_state(TASK_RUNNING);
+ cluster->pending = false;
+ spin_unlock_irqrestore(&cluster->pending_lock, flags);
+
+ do_core_ctl(cluster);
+ }
+
+ return 0;
+}
+
+static int __ref cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ uint32_t cpu = (uintptr_t)hcpu;
+ struct cpu_data *state = &per_cpu(cpu_state, cpu);
+ struct cluster_data *cluster = state->cluster;
+ unsigned int need;
+ int ret = NOTIFY_OK;
+
+ /* Don't affect suspend resume */
+ if (action & CPU_TASKS_FROZEN)
+ return NOTIFY_OK;
+
+ if (unlikely(!cluster || !cluster->inited))
+ return NOTIFY_OK;
+
+ switch (action) {
+ case CPU_UP_PREPARE:
+
+ /* If online state of CPU somehow got out of sync, fix it. */
+ if (state->online) {
+ state->online = false;
+ cluster->active_cpus = get_active_cpu_count(cluster);
+ pr_warn("CPU%d offline when state is online\n", cpu);
+ }
+ break;
+
+ case CPU_ONLINE:
+
+ state->online = true;
+ cluster->active_cpus = get_active_cpu_count(cluster);
+
+ /*
+ * Moving to the end of the list should only happen in
+ * CPU_ONLINE and not on CPU_UP_PREPARE to prevent an
+ * infinite list traversal when thermal (or other entities)
+ * reject trying to online CPUs.
+ */
+ move_cpu_lru(state);
+ break;
+
+ case CPU_DEAD:
+ /*
+ * We don't want to have a CPU both offline and isolated.
+ * So unisolate a CPU that went down if it was isolated by us.
+ */
+ if (state->isolated_by_us) {
+ sched_unisolate_cpu_unlocked(cpu);
+ state->isolated_by_us = false;
+ }
+
+ /* Move a CPU to the end of the LRU when it goes offline. */
+ move_cpu_lru(state);
+
+ /* Fall through */
+
+ case CPU_UP_CANCELED:
+
+ /* If online state of CPU somehow got out of sync, fix it. */
+ if (!state->online)
+ pr_warn("CPU%d online when state is offline\n", cpu);
+
+ state->online = false;
+ state->busy = 0;
+ cluster->active_cpus = get_active_cpu_count(cluster);
+ break;
+ }
+
+ need = apply_limits(cluster, cluster->need_cpus);
+ if (adjustment_possible(cluster, need))
+ wake_up_core_ctl_thread(cluster);
+
+ return ret;
+}
+
+static struct notifier_block __refdata cpu_notifier = {
+ .notifier_call = cpu_callback,
+};
+
+/* ============================ init code ============================== */
+
+static struct cluster_data *find_cluster_by_first_cpu(unsigned int first_cpu)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_clusters; ++i) {
+ if (cluster_state[i].first_cpu == first_cpu)
+ return &cluster_state[i];
+ }
+
+ return NULL;
+}
+
+static int cluster_init(const struct cpumask *mask)
+{
+ struct device *dev;
+ unsigned int first_cpu = cpumask_first(mask);
+ struct cluster_data *cluster;
+ struct cpu_data *state;
+ unsigned int cpu;
+ struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
+
+ if (find_cluster_by_first_cpu(first_cpu))
+ return 0;
+
+ dev = get_cpu_device(first_cpu);
+ if (!dev)
+ return -ENODEV;
+
+ pr_info("Creating CPU group %d\n", first_cpu);
+
+ if (num_clusters == MAX_CLUSTERS) {
+ pr_err("Unsupported number of clusters. Only %u supported\n",
+ MAX_CLUSTERS);
+ return -EINVAL;
+ }
+ cluster = &cluster_state[num_clusters];
+ ++num_clusters;
+
+ cpumask_copy(&cluster->cpu_mask, mask);
+ cluster->num_cpus = cpumask_weight(mask);
+ if (cluster->num_cpus > MAX_CPUS_PER_CLUSTER) {
+ pr_err("HW configuration not supported\n");
+ return -EINVAL;
+ }
+ cluster->first_cpu = first_cpu;
+ cluster->min_cpus = 1;
+ cluster->max_cpus = cluster->num_cpus;
+ cluster->need_cpus = cluster->num_cpus;
+ cluster->offline_delay_ms = 100;
+ cluster->task_thres = UINT_MAX;
+ cluster->nrrun = cluster->num_cpus;
+ INIT_LIST_HEAD(&cluster->lru);
+ spin_lock_init(&cluster->pending_lock);
+
+ for_each_cpu(cpu, mask) {
+ pr_info("Init CPU%u state\n", cpu);
+
+ state = &per_cpu(cpu_state, cpu);
+ state->cluster = cluster;
+ state->cpu = cpu;
+ if (cpu_online(cpu))
+ state->online = true;
+ list_add_tail(&state->sib, &cluster->lru);
+ }
+ cluster->active_cpus = get_active_cpu_count(cluster);
+
+ cluster->core_ctl_thread = kthread_run(try_core_ctl, (void *) cluster,
+ "core_ctl/%d", first_cpu);
+ if (IS_ERR(cluster->core_ctl_thread))
+ return PTR_ERR(cluster->core_ctl_thread);
+
+ sched_setscheduler_nocheck(cluster->core_ctl_thread, SCHED_FIFO,
+ &param);
+
+ cluster->inited = true;
+
+ kobject_init(&cluster->kobj, &ktype_core_ctl);
+ return kobject_add(&cluster->kobj, &dev->kobj, "core_ctl");
+}
+
+static int cpufreq_policy_cb(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct cpufreq_policy *policy = data;
+ int ret;
+
+ switch (val) {
+ case CPUFREQ_CREATE_POLICY:
+ ret = cluster_init(policy->related_cpus);
+ if (ret)
+ pr_warn("unable to create core ctl group: %d\n", ret);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cpufreq_pol_nb = {
+ .notifier_call = cpufreq_policy_cb,
+};
+
+static int cpufreq_gov_cb(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct cpufreq_govinfo *info = data;
+
+ switch (val) {
+ case CPUFREQ_LOAD_CHANGE:
+ core_ctl_set_busy(info->cpu, info->load);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cpufreq_gov_nb = {
+ .notifier_call = cpufreq_gov_cb,
+};
+
+static int __init core_ctl_init(void)
+{
+ unsigned int cpu;
+
+ core_ctl_check_interval = (rq_avg_period_ms - RQ_AVG_TOLERANCE)
+ * NSEC_PER_MSEC;
+
+ register_cpu_notifier(&cpu_notifier);
+ cpufreq_register_notifier(&cpufreq_pol_nb, CPUFREQ_POLICY_NOTIFIER);
+ cpufreq_register_notifier(&cpufreq_gov_nb, CPUFREQ_GOVINFO_NOTIFIER);
+
+ lock_device_hotplug();
+ for_each_online_cpu(cpu) {
+ struct cpufreq_policy *policy;
+ int ret;
+
+ policy = cpufreq_cpu_get(cpu);
+ if (policy) {
+ ret = cluster_init(policy->related_cpus);
+ if (ret)
+ pr_warn("unable to create core ctl group: %d\n"
+ , ret);
+ cpufreq_cpu_put(policy);
+ }
+ }
+ unlock_device_hotplug();
+ initialized = true;
+ return 0;
+}
+
+late_initcall(core_ctl_init);
diff --git a/kernel/sched/core_ctl.h b/kernel/sched/core_ctl.h
new file mode 100644
index 000000000000..3b0c12acb9c0
--- /dev/null
+++ b/kernel/sched/core_ctl.h
@@ -0,0 +1,24 @@
+/*
+ * 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 __CORE_CTL_H
+#define __CORE_CTL_H
+
+#ifdef CONFIG_SCHED_CORE_CTL
+void core_ctl_check(u64 wallclock);
+void core_ctl_set_boost(bool boost);
+#else
+static inline void core_ctl_check(u64 wallclock) {}
+static inline void core_ctl_set_boost(bool boost) {}
+#endif
+#endif
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index e893b0fcac6b..df23b0365527 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2590,6 +2590,7 @@ static u32 __compute_runnable_contrib(u64 n)
#define SBC_FLAG_COST_CSTATE_PREV_CPU_TIE_BREAKER 0x80
#define SBC_FLAG_CSTATE_LOAD 0x100
#define SBC_FLAG_BEST_SIBLING 0x200
+#define SBC_FLAG_WAKER_CPU 0x400
/* Cluster selection flag */
#define SBC_FLAG_COLOC_CLUSTER 0x10000
@@ -2941,6 +2942,8 @@ static void find_best_cpu_in_cluster(struct sched_cluster *c,
struct cpumask search_cpus;
cpumask_and(&search_cpus, tsk_cpus_allowed(env->p), &c->cpus);
+ cpumask_andnot(&search_cpus, &search_cpus, cpu_isolated_mask);
+
if (env->ignore_prev_cpu)
cpumask_clear_cpu(env->prev_cpu, &search_cpus);
@@ -3009,7 +3012,8 @@ bias_to_prev_cpu(struct cpu_select_env *env, struct cluster_cpu_stats *stats)
prev_cpu = env->prev_cpu;
if (!cpumask_test_cpu(prev_cpu, tsk_cpus_allowed(task)) ||
- unlikely(!cpu_active(prev_cpu)))
+ unlikely(!cpu_active(prev_cpu)) ||
+ cpu_isolated(prev_cpu))
return false;
if (task->ravg.mark_start - task->last_cpu_selected_ts >=
@@ -3057,6 +3061,15 @@ wake_to_waker_cluster(struct cpu_select_env *env)
task_load(env->p) < sched_small_wakee_task_load;
}
+static inline bool
+bias_to_waker_cpu(struct task_struct *p, int cpu)
+{
+ return sysctl_sched_prefer_sync_wakee_to_waker &&
+ cpu_rq(cpu)->nr_running == 1 &&
+ cpumask_test_cpu(cpu, tsk_cpus_allowed(p)) &&
+ cpu_active(cpu) && !cpu_isolated(cpu);
+}
+
static inline int
cluster_allowed(struct task_struct *p, struct sched_cluster *cluster)
{
@@ -3077,6 +3090,7 @@ static int select_best_cpu(struct task_struct *p, int target, int reason,
struct cluster_cpu_stats stats;
struct related_thread_group *grp;
unsigned int sbc_flag = 0;
+ int cpu = raw_smp_processor_id();
struct cpu_select_env env = {
.p = p,
@@ -3108,14 +3122,20 @@ static int select_best_cpu(struct task_struct *p, int target, int reason,
else
env.rtg = grp;
} else {
- cluster = cpu_rq(smp_processor_id())->cluster;
- if (wake_to_waker_cluster(&env) &&
- cluster_allowed(p, cluster)) {
- env.need_waker_cluster = 1;
- bitmap_zero(env.candidate_list, NR_CPUS);
- __set_bit(cluster->id, env.candidate_list);
- env.sbc_best_cluster_flag = SBC_FLAG_WAKER_CLUSTER;
-
+ cluster = cpu_rq(cpu)->cluster;
+ if (wake_to_waker_cluster(&env)) {
+ if (bias_to_waker_cpu(p, cpu)) {
+ target = cpu;
+ sbc_flag = SBC_FLAG_WAKER_CLUSTER |
+ SBC_FLAG_WAKER_CPU;
+ goto out;
+ } else if (cluster_allowed(p, cluster)) {
+ env.need_waker_cluster = 1;
+ bitmap_zero(env.candidate_list, NR_CPUS);
+ __set_bit(cluster->id, env.candidate_list);
+ env.sbc_best_cluster_flag =
+ SBC_FLAG_WAKER_CLUSTER;
+ }
} else if (bias_to_prev_cpu(&env, &stats)) {
sbc_flag = SBC_FLAG_PREV_CPU;
goto out;
@@ -7200,6 +7220,10 @@ static inline void init_sd_lb_stats(struct sd_lb_stats *sds)
.avg_load = 0UL,
.sum_nr_running = 0,
.group_type = group_other,
+#ifdef CONFIG_SCHED_HMP
+ .sum_nr_big_tasks = 0UL,
+ .group_cpu_load = 0ULL,
+#endif
},
};
}
@@ -7226,9 +7250,7 @@ bail_inter_cluster_balance(struct lb_env *env, struct sd_lb_stats *sds)
local_pwr_cost = cpu_max_power_cost(local_cpu);
busiest_pwr_cost = cpu_max_power_cost(busiest_cpu);
- if (local_capacity < busiest_capacity ||
- (local_capacity == busiest_capacity &&
- local_pwr_cost <= busiest_pwr_cost))
+ if (local_pwr_cost <= busiest_pwr_cost)
return 0;
if (local_capacity > busiest_capacity &&
@@ -7354,6 +7376,8 @@ void update_group_capacity(struct sched_domain *sd, int cpu)
struct sched_group_capacity *sgc;
struct rq *rq = cpu_rq(cpu);
+ if (cpumask_test_cpu(cpu, cpu_isolated_mask))
+ continue;
/*
* build_sched_domains() -> init_sched_groups_capacity()
* gets here before we've attached the domains to the
@@ -7381,7 +7405,11 @@ void update_group_capacity(struct sched_domain *sd, int cpu)
group = child->groups;
do {
- capacity += group->sgc->capacity;
+ cpumask_t *cpus = sched_group_cpus(group);
+
+ /* Revisit this later. This won't work for MT domain */
+ if (!cpu_isolated(cpumask_first(cpus)))
+ capacity += group->sgc->capacity;
group = group->next;
} while (group != child->groups);
}
@@ -7521,6 +7549,9 @@ static inline void update_sg_lb_stats(struct lb_env *env,
power_cost(i, 0),
cpu_temp(i));
+ if (cpu_isolated(i))
+ continue;
+
/* Bias balancing toward cpus of our domain */
if (local_group)
load = target_load(i, load_idx);
@@ -7548,17 +7579,27 @@ static inline void update_sg_lb_stats(struct lb_env *env,
sgs->idle_cpus++;
}
- /* Adjust by relative CPU capacity of the group */
- sgs->group_capacity = group->sgc->capacity;
- sgs->avg_load = (sgs->group_load*SCHED_CAPACITY_SCALE) / sgs->group_capacity;
+ /* Isolated CPU has no weight */
+ if (!group->group_weight) {
+ sgs->group_capacity = 0;
+ sgs->avg_load = 0;
+ sgs->group_no_capacity = 1;
+ sgs->group_type = group_other;
+ sgs->group_weight = group->group_weight;
+ } else {
+ /* Adjust by relative CPU capacity of the group */
+ sgs->group_capacity = group->sgc->capacity;
+ sgs->avg_load = (sgs->group_load*SCHED_CAPACITY_SCALE) /
+ sgs->group_capacity;
- if (sgs->sum_nr_running)
- sgs->load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running;
+ sgs->group_weight = group->group_weight;
- sgs->group_weight = group->group_weight;
+ sgs->group_no_capacity = group_is_overloaded(env, sgs);
+ sgs->group_type = group_classify(group, sgs, env);
+ }
- sgs->group_no_capacity = group_is_overloaded(env, sgs);
- sgs->group_type = group_classify(group, sgs, env);
+ if (sgs->sum_nr_running)
+ sgs->load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running;
}
#ifdef CONFIG_SCHED_HMP
@@ -8601,6 +8642,9 @@ static int idle_balance(struct rq *this_rq)
int pulled_task = 0;
u64 curr_cost = 0;
+ if (cpu_isolated(this_cpu))
+ return 0;
+
idle_enter_fair(this_rq);
/*
@@ -8843,9 +8887,6 @@ static inline int find_new_hmp_ilb(int type)
for_each_cpu_and(ilb, nohz.idle_cpus_mask,
sched_domain_span(sd)) {
if (idle_cpu(ilb) && (type != NOHZ_KICK_RESTRICT ||
- (hmp_capable() &&
- cpu_max_possible_capacity(ilb) <=
- cpu_max_possible_capacity(call_cpu)) ||
cpu_max_power_cost(ilb) <=
cpu_max_power_cost(call_cpu))) {
rcu_read_unlock();
@@ -8908,16 +8949,21 @@ static void nohz_balancer_kick(int type)
return;
}
+void nohz_balance_clear_nohz_mask(int cpu)
+{
+ if (likely(cpumask_test_cpu(cpu, nohz.idle_cpus_mask))) {
+ cpumask_clear_cpu(cpu, nohz.idle_cpus_mask);
+ atomic_dec(&nohz.nr_cpus);
+ }
+}
+
static inline void nohz_balance_exit_idle(int cpu)
{
if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) {
/*
* Completely isolated CPUs don't ever set, so we must test.
*/
- if (likely(cpumask_test_cpu(cpu, nohz.idle_cpus_mask))) {
- cpumask_clear_cpu(cpu, nohz.idle_cpus_mask);
- atomic_dec(&nohz.nr_cpus);
- }
+ nohz_balance_clear_nohz_mask(cpu);
clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu));
}
}
@@ -8974,7 +9020,7 @@ void nohz_balance_enter_idle(int cpu)
/*
* If we're a completely isolated CPU, we don't play.
*/
- if (on_null_domain(cpu_rq(cpu)))
+ if (on_null_domain(cpu_rq(cpu)) || cpu_isolated(cpu))
return;
cpumask_set_cpu(cpu, nohz.idle_cpus_mask);
@@ -9003,7 +9049,13 @@ static DEFINE_SPINLOCK(balancing);
*/
void update_max_interval(void)
{
- max_load_balance_interval = HZ*num_online_cpus()/10;
+ cpumask_t avail_mask;
+ unsigned int available_cpus;
+
+ cpumask_andnot(&avail_mask, cpu_online_mask, cpu_isolated_mask);
+ available_cpus = cpumask_weight(&avail_mask);
+
+ max_load_balance_interval = HZ*available_cpus/10;
}
/*
@@ -9188,8 +9240,7 @@ static inline int _nohz_kick_needed_hmp(struct rq *rq, int cpu, int *type)
if (!sysctl_sched_restrict_cluster_spill || sched_boost())
return 1;
- if (hmp_capable() && cpu_max_possible_capacity(cpu) ==
- max_possible_capacity)
+ if (cpu_max_power_cost(cpu) == max_power_cost)
return 1;
rcu_read_lock();
@@ -9342,8 +9393,10 @@ void trigger_load_balance(struct rq *rq)
{
int type = NOHZ_KICK_ANY;
- /* Don't need to rebalance while attached to NULL domain */
- if (unlikely(on_null_domain(rq)))
+ /* Don't need to rebalance while attached to NULL domain or
+ * cpu is isolated.
+ */
+ if (unlikely(on_null_domain(rq)) || cpu_isolated(cpu_of(rq)))
return;
if (time_after_eq(jiffies, rq->next_balance))
diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c
index 5002619961ce..a0686ea29243 100644
--- a/kernel/sched/hmp.c
+++ b/kernel/sched/hmp.c
@@ -17,8 +17,10 @@
#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"
#include <trace/events/sched.h>
@@ -224,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;
@@ -356,6 +404,8 @@ DECLARE_BITMAP(all_cluster_ids, NR_CPUS);
struct sched_cluster *sched_cluster[NR_CPUS];
int num_clusters;
+unsigned int max_power_cost = 1;
+
struct sched_cluster init_cluster = {
.list = LIST_HEAD_INIT(init_cluster.list),
.id = 0,
@@ -465,6 +515,7 @@ static void sort_clusters(void)
{
struct sched_cluster *cluster;
struct list_head new_head;
+ unsigned int tmp_max = 1;
INIT_LIST_HEAD(&new_head);
@@ -473,7 +524,11 @@ static void sort_clusters(void)
max_task_load());
cluster->min_power_cost = power_cost(cluster_first_cpu(cluster),
0);
+
+ if (cluster->max_power_cost > tmp_max)
+ tmp_max = cluster->max_power_cost;
}
+ max_power_cost = tmp_max;
move_list(&new_head, &cluster_head, true);
@@ -889,6 +944,13 @@ unsigned int __read_mostly sched_spill_load;
unsigned int __read_mostly sysctl_sched_spill_load_pct = 100;
/*
+ * Prefer the waker CPU for sync wakee task, if the CPU has only 1 runnable
+ * task. This eliminates the LPM exit latency associated with the idle
+ * CPUs in the waker cluster.
+ */
+unsigned int __read_mostly sysctl_sched_prefer_sync_wakee_to_waker;
+
+/*
* Tasks whose bandwidth consumption on a cpu is more than
* sched_upmigrate are considered "big" tasks. Big tasks will be
* considered for "up" migration, i.e migrating to a cpu with better
@@ -1090,6 +1152,8 @@ int sched_set_boost(int enable)
if (!old_refcount && boost_refcount)
boost_kick_cpus();
+ if (boost_refcount <= 1)
+ core_ctl_set_boost(boost_refcount == 1);
trace_sched_set_boost(boost_refcount);
spin_unlock_irqrestore(&boost_lock, flags);
@@ -1152,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;
}
@@ -1499,28 +1560,10 @@ int sched_hmp_proc_update_handler(struct ctl_table *table, int write,
if (write && (old_val == *data))
goto done;
- /*
- * Special handling for sched_freq_aggregate_threshold_pct
- * which can be greater than 100. Use 1000 as an upper bound
- * value which works for all practical use cases.
- */
- if (data == &sysctl_sched_freq_aggregate_threshold_pct) {
- if (*data > 1000) {
- *data = old_val;
- ret = -EINVAL;
- goto done;
- }
- } else if (data != &sysctl_sched_select_prev_cpu_us) {
- /*
- * all tunables other than sched_select_prev_cpu_us are
- * in percentage.
- */
- if (sysctl_sched_downmigrate_pct >
- sysctl_sched_upmigrate_pct || *data > 100) {
- *data = old_val;
- ret = -EINVAL;
- goto done;
- }
+ if (sysctl_sched_downmigrate_pct > sysctl_sched_upmigrate_pct) {
+ *data = old_val;
+ ret = -EINVAL;
+ goto done;
}
/*
@@ -2698,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);
@@ -2828,10 +2872,10 @@ void set_window_start(struct rq *rq)
rq->curr->ravg.mark_start = rq->window_start;
}
-void migrate_sync_cpu(int cpu)
+void migrate_sync_cpu(int cpu, int new_cpu)
{
if (cpu == sync_cpu)
- sync_cpu = smp_processor_id();
+ sync_cpu = new_cpu;
}
static void reset_all_task_stats(void)
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index cfec881491ef..ba4403e910d8 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -265,8 +265,12 @@ static void pull_rt_task(struct rq *this_rq);
static inline bool need_pull_rt_task(struct rq *rq, struct task_struct *prev)
{
- /* Try to pull RT tasks here if we lower this rq's prio */
- return rq->rt.highest_prio.curr > prev->prio;
+ /*
+ * Try to pull RT tasks here if we lower this rq's prio and cpu is not
+ * isolated
+ */
+ return rq->rt.highest_prio.curr > prev->prio &&
+ !cpu_isolated(cpu_of(rq));
}
static inline int rt_overloaded(struct rq *rq)
@@ -1694,6 +1698,8 @@ static int find_lowest_rq_hmp(struct task_struct *task)
for_each_sched_cluster(cluster) {
cpumask_and(&candidate_mask, &cluster->cpus, lowest_mask);
+ cpumask_andnot(&candidate_mask, &candidate_mask,
+ cpu_isolated_mask);
if (cpumask_empty(&candidate_mask))
continue;
@@ -2282,7 +2288,8 @@ static void switched_from_rt(struct rq *rq, struct task_struct *p)
* we may need to handle the pulling of RT tasks
* now.
*/
- if (!task_on_rq_queued(p) || rq->rt.rt_nr_running)
+ if (!task_on_rq_queued(p) || rq->rt.rt_nr_running ||
+ cpu_isolated(cpu_of(rq)))
return;
queue_pull_task(rq);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index ec7721112b05..ada5e580e968 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1039,6 +1039,7 @@ extern unsigned int min_capacity;
extern unsigned int max_load_scale_factor;
extern unsigned int max_possible_capacity;
extern unsigned int min_max_possible_capacity;
+extern unsigned int max_power_cost;
extern unsigned int sched_upmigrate;
extern unsigned int sched_downmigrate;
extern unsigned int sched_init_task_load_windows;
@@ -1069,7 +1070,7 @@ extern void clear_boost_kick(int cpu);
extern void clear_hmp_request(int cpu);
extern void mark_task_starting(struct task_struct *p);
extern void set_window_start(struct rq *rq);
-extern void migrate_sync_cpu(int cpu);
+extern void migrate_sync_cpu(int cpu, int new_cpu);
extern void update_cluster_topology(void);
extern void set_task_last_wake(struct task_struct *p, u64 wallclock);
extern void set_task_last_switch_out(struct task_struct *p, u64 wallclock);
@@ -1398,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 */
@@ -1424,7 +1427,8 @@ static inline void clear_boost_kick(int cpu) { }
static inline void clear_hmp_request(int cpu) { }
static inline void mark_task_starting(struct task_struct *p) { }
static inline void set_window_start(struct rq *rq) { }
-static inline void migrate_sync_cpu(int cpu) { }
+static inline void migrate_sync_cpu(int cpu, int new_cpu) {}
+static inline void init_clusters(void) {}
static inline void update_cluster_topology(void) { }
static inline void set_task_last_wake(struct task_struct *p, u64 wallclock) { }
static inline void set_task_last_switch_out(struct task_struct *p,
@@ -1587,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(...)
@@ -1953,6 +1959,7 @@ extern const struct sched_class idle_sched_class;
extern void update_group_capacity(struct sched_domain *sd, int cpu);
extern void trigger_load_balance(struct rq *rq);
+extern void nohz_balance_clear_nohz_mask(int cpu);
extern void idle_enter_fair(struct rq *this_rq);
extern void idle_exit_fair(struct rq *this_rq);
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/kernel/sched/tune.c b/kernel/sched/tune.c
new file mode 100644
index 000000000000..4c44b1a4ad98
--- /dev/null
+++ b/kernel/sched/tune.c
@@ -0,0 +1,17 @@
+#include "sched.h"
+
+unsigned int sysctl_sched_cfs_boost __read_mostly;
+
+int
+sysctl_sched_cfs_boost_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+
+ if (ret || !write)
+ return ret;
+
+ return 0;
+}
+
diff --git a/kernel/smp.c b/kernel/smp.c
index abdc48cd79a3..b2ec21c5c9d6 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -766,8 +766,8 @@ void wake_up_all_idle_cpus(void)
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
-
- wake_up_if_idle(cpu);
+ if (!cpu_isolated(cpu))
+ wake_up_if_idle(cpu);
}
preempt_enable();
}
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 07fef40d1274..8e2f4ab15498 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -130,6 +130,9 @@ static int one_hundred = 100;
#ifdef CONFIG_PRINTK
static int ten_thousand = 10000;
#endif
+#ifdef CONFIG_SCHED_HMP
+static int one_thousand = 1000;
+#endif
/* this is needed for the proc_doulongvec_minmax of vm_dirty_bytes */
static unsigned long dirty_bytes_min = 2 * PAGE_SIZE;
@@ -336,6 +339,8 @@ static struct ctl_table kern_table[] = {
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = sched_hmp_proc_update_handler,
+ .extra1 = &zero,
+ .extra2 = &one_hundred,
},
{
.procname = "sched_spill_nr_run",
@@ -351,6 +356,8 @@ static struct ctl_table kern_table[] = {
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = sched_hmp_proc_update_handler,
+ .extra1 = &zero,
+ .extra2 = &one_hundred,
},
{
.procname = "sched_downmigrate",
@@ -358,6 +365,8 @@ static struct ctl_table kern_table[] = {
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = sched_hmp_proc_update_handler,
+ .extra1 = &zero,
+ .extra2 = &one_hundred,
},
{
.procname = "sched_init_task_load",
@@ -365,6 +374,8 @@ static struct ctl_table kern_table[] = {
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = sched_hmp_proc_update_handler,
+ .extra1 = &zero,
+ .extra2 = &one_hundred,
},
{
.procname = "sched_select_prev_cpu_us",
@@ -372,6 +383,7 @@ static struct ctl_table kern_table[] = {
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = sched_hmp_proc_update_handler,
+ .extra1 = &zero,
},
{
.procname = "sched_enable_colocation",
@@ -397,6 +409,8 @@ static struct ctl_table kern_table[] = {
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = sched_hmp_proc_update_handler,
+ .extra1 = &zero,
+ .extra2 = &one_hundred,
},
{
.procname = "sched_big_waker_task_load",
@@ -404,6 +418,17 @@ static struct ctl_table kern_table[] = {
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = sched_hmp_proc_update_handler,
+ .extra1 = &zero,
+ .extra2 = &one_hundred,
+ },
+ {
+ .procname = "sched_prefer_sync_wakee_to_waker",
+ .data = &sysctl_sched_prefer_sync_wakee_to_waker,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
},
{
.procname = "sched_enable_thread_grouping",
@@ -440,6 +465,13 @@ static struct ctl_table kern_table[] = {
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = sched_hmp_proc_update_handler,
+ .extra1 = &zero,
+ /*
+ * Special handling for sched_freq_aggregate_threshold_pct
+ * which can be greater than 100. Use 1000 as an upper bound
+ * value which works for all practical use cases.
+ */
+ .extra2 = &one_thousand,
},
{
.procname = "sched_boost",
@@ -599,6 +631,17 @@ static struct ctl_table kern_table[] = {
.extra1 = &one,
},
#endif
+#ifdef CONFIG_SCHED_TUNE
+ {
+ .procname = "sched_cfs_boost",
+ .data = &sysctl_sched_cfs_boost,
+ .maxlen = sizeof(sysctl_sched_cfs_boost),
+ .mode = 0644,
+ .proc_handler = &sysctl_sched_cfs_boost_handler,
+ .extra1 = &zero,
+ .extra2 = &one_hundred,
+ },
+#endif
#ifdef CONFIG_PROVE_LOCKING
{
.procname = "prove_locking",
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index fa909f9fd559..9e1349fc5bbe 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -880,7 +880,7 @@ static int enqueue_hrtimer(struct hrtimer *timer,
base->cpu_base->active_bases |= 1 << base->index;
- timer->state = HRTIMER_STATE_ENQUEUED;
+ timer->state |= HRTIMER_STATE_ENQUEUED;
return timerqueue_add(&base->active, &timer->node);
}
@@ -900,11 +900,9 @@ static void __remove_hrtimer(struct hrtimer *timer,
u8 newstate, int reprogram)
{
struct hrtimer_cpu_base *cpu_base = base->cpu_base;
- u8 state = timer->state;
- timer->state = newstate;
- if (!(state & HRTIMER_STATE_ENQUEUED))
- return;
+ if (!(timer->state & HRTIMER_STATE_ENQUEUED))
+ goto out;
if (!timerqueue_del(&base->active, &timer->node))
cpu_base->active_bases &= ~(1 << base->index);
@@ -921,6 +919,13 @@ static void __remove_hrtimer(struct hrtimer *timer,
if (reprogram && timer == cpu_base->next_timer)
hrtimer_force_reprogram(cpu_base, 1);
#endif
+
+out:
+ /*
+ * We need to preserve PINNED state here, otherwise we may end up
+ * migrating pinned hrtimers as well.
+ */
+ timer->state = newstate | (timer->state & HRTIMER_STATE_PINNED);
}
/*
@@ -949,6 +954,7 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, bool rest
state = HRTIMER_STATE_INACTIVE;
__remove_hrtimer(timer, base, state, reprogram);
+ timer->state &= ~HRTIMER_STATE_PINNED;
return 1;
}
return 0;
@@ -1002,6 +1008,10 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
timer_stats_hrtimer_set_start_info(timer);
+ /* Update pinned state */
+ timer->state &= ~HRTIMER_STATE_PINNED;
+ timer->state |= (!!(mode & HRTIMER_MODE_PINNED)) << HRTIMER_PINNED_SHIFT;
+
leftmost = enqueue_hrtimer(timer, new_base);
if (!leftmost)
goto unlock;
@@ -1176,8 +1186,8 @@ bool hrtimer_active(const struct hrtimer *timer)
cpu_base = READ_ONCE(timer->base->cpu_base);
seq = raw_read_seqcount_begin(&cpu_base->seq);
- if (timer->state != HRTIMER_STATE_INACTIVE ||
- cpu_base->running == timer)
+ if (((timer->state & ~HRTIMER_STATE_PINNED) !=
+ HRTIMER_STATE_INACTIVE) || cpu_base->running == timer)
return true;
} while (read_seqcount_retry(&cpu_base->seq, seq) ||
@@ -1614,17 +1624,37 @@ static void init_hrtimers_cpu(int cpu)
hrtimer_init_hres(cpu_base);
}
-#ifdef CONFIG_HOTPLUG_CPU
-
-static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
- struct hrtimer_clock_base *new_base)
+#if defined(CONFIG_HOTPLUG_CPU)
+static void migrate_hrtimer_list(struct hrtimer_cpu_base *old_base,
+ struct hrtimer_cpu_base *new_base,
+ unsigned int i,
+ bool wait,
+ bool remove_pinned)
{
struct hrtimer *timer;
struct timerqueue_node *node;
+ struct timerqueue_head pinned;
+ int is_pinned;
+ struct hrtimer_clock_base *old_c_base = &old_base->clock_base[i];
+ struct hrtimer_clock_base *new_c_base = &new_base->clock_base[i];
+
+ timerqueue_init_head(&pinned);
- while ((node = timerqueue_getnext(&old_base->active))) {
+ while ((node = timerqueue_getnext(&old_c_base->active))) {
timer = container_of(node, struct hrtimer, node);
- BUG_ON(hrtimer_callback_running(timer));
+ if (wait) {
+ /* Ensure timers are done running before continuing */
+ while (hrtimer_callback_running(timer)) {
+ raw_spin_unlock(&old_base->lock);
+ raw_spin_unlock(&new_base->lock);
+ cpu_relax();
+ raw_spin_lock(&new_base->lock);
+ raw_spin_lock_nested(&old_base->lock,
+ SINGLE_DEPTH_NESTING);
+ }
+ } else {
+ BUG_ON(hrtimer_callback_running(timer));
+ }
debug_deactivate(timer);
/*
@@ -1632,8 +1662,15 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
* timer could be seen as !active and just vanish away
* under us on another CPU
*/
- __remove_hrtimer(timer, old_base, HRTIMER_STATE_ENQUEUED, 0);
- timer->base = new_base;
+ __remove_hrtimer(timer, old_c_base, HRTIMER_STATE_ENQUEUED, 0);
+
+ is_pinned = timer->state & HRTIMER_STATE_PINNED;
+ if (!remove_pinned && is_pinned) {
+ timerqueue_add(&pinned, &timer->node);
+ continue;
+ }
+
+ timer->base = new_c_base;
/*
* Enqueue the timers on the new cpu. This does not
* reprogram the event device in case the timer
@@ -1642,19 +1679,25 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
* sort out already expired timers and reprogram the
* event device.
*/
- enqueue_hrtimer(timer, new_base);
+ enqueue_hrtimer(timer, new_c_base);
+ }
+
+ /* Re-queue pinned timers for non-hotplug usecase */
+ while ((node = timerqueue_getnext(&pinned))) {
+ timer = container_of(node, struct hrtimer, node);
+
+ timerqueue_del(&pinned, &timer->node);
+ enqueue_hrtimer(timer, old_c_base);
}
}
-static void migrate_hrtimers(int scpu)
+static void __migrate_hrtimers(int scpu, bool wait, bool remove_pinned)
{
struct hrtimer_cpu_base *old_base, *new_base;
+ unsigned long flags;
int i;
- BUG_ON(cpu_online(scpu));
- tick_cancel_sched_timer(scpu);
-
- local_irq_disable();
+ local_irq_save(flags);
old_base = &per_cpu(hrtimer_bases, scpu);
new_base = this_cpu_ptr(&hrtimer_bases);
/*
@@ -1665,8 +1708,8 @@ static void migrate_hrtimers(int scpu)
raw_spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
- migrate_hrtimer_list(&old_base->clock_base[i],
- &new_base->clock_base[i]);
+ migrate_hrtimer_list(old_base, new_base, i, wait,
+ remove_pinned);
}
raw_spin_unlock(&old_base->lock);
@@ -1674,7 +1717,20 @@ static void migrate_hrtimers(int scpu)
/* Check, if we got expired work to do */
__hrtimer_peek_ahead_timers();
- local_irq_enable();
+ local_irq_restore(flags);
+}
+
+static void migrate_hrtimers(int scpu)
+{
+ BUG_ON(cpu_online(scpu));
+ tick_cancel_sched_timer(scpu);
+
+ __migrate_hrtimers(scpu, false, true);
+}
+
+void hrtimer_quiesce_cpu(void *cpup)
+{
+ __migrate_hrtimers(*(int *)cpup, true, false);
}
#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 51896272fcde..0efb3916f5a4 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1620,56 +1620,86 @@ signed long __sched schedule_timeout_uninterruptible(signed long timeout)
}
EXPORT_SYMBOL(schedule_timeout_uninterruptible);
-#ifdef CONFIG_HOTPLUG_CPU
-static void migrate_timer_list(struct tvec_base *new_base, struct hlist_head *head)
+#if defined(CONFIG_HOTPLUG_CPU)
+static void migrate_timer_list(struct tvec_base *new_base,
+ struct hlist_head *head, bool remove_pinned)
{
struct timer_list *timer;
int cpu = new_base->cpu;
+ struct hlist_node *n;
+ int is_pinned;
- while (!hlist_empty(head)) {
- timer = hlist_entry(head->first, struct timer_list, entry);
- /* We ignore the accounting on the dying cpu */
- detach_timer(timer, false);
+ hlist_for_each_entry_safe(timer, n, head, entry) {
+ is_pinned = timer->flags & TIMER_PINNED_ON_CPU;
+ if (!remove_pinned && is_pinned)
+ continue;
+
+ detach_if_pending(timer, get_timer_base(timer->flags), false);
timer->flags = (timer->flags & ~TIMER_BASEMASK) | cpu;
internal_add_timer(new_base, timer);
}
}
-static void migrate_timers(int cpu)
+static void __migrate_timers(int cpu, bool wait, bool remove_pinned)
{
struct tvec_base *old_base;
struct tvec_base *new_base;
+ unsigned long flags;
int i;
- BUG_ON(cpu_online(cpu));
old_base = per_cpu_ptr(&tvec_bases, cpu);
new_base = get_cpu_ptr(&tvec_bases);
/*
* The caller is globally serialized and nobody else
* takes two locks at once, deadlock is not possible.
*/
- spin_lock_irq(&new_base->lock);
+ spin_lock_irqsave(&new_base->lock, flags);
spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
- BUG_ON(old_base->running_timer);
+ if (wait) {
+ /* Ensure timers are done running before continuing */
+ while (old_base->running_timer) {
+ spin_unlock(&old_base->lock);
+ spin_unlock_irqrestore(&new_base->lock, flags);
+ cpu_relax();
+ spin_lock_irqsave(&new_base->lock, flags);
+ spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
+ }
+ } else {
+ BUG_ON(old_base->running_timer);
+ }
for (i = 0; i < TVR_SIZE; i++)
- migrate_timer_list(new_base, old_base->tv1.vec + i);
+ migrate_timer_list(new_base, old_base->tv1.vec + i,
+ remove_pinned);
for (i = 0; i < TVN_SIZE; i++) {
- migrate_timer_list(new_base, old_base->tv2.vec + i);
- migrate_timer_list(new_base, old_base->tv3.vec + i);
- migrate_timer_list(new_base, old_base->tv4.vec + i);
- migrate_timer_list(new_base, old_base->tv5.vec + i);
+ migrate_timer_list(new_base, old_base->tv2.vec + i,
+ remove_pinned);
+ migrate_timer_list(new_base, old_base->tv3.vec + i,
+ remove_pinned);
+ migrate_timer_list(new_base, old_base->tv4.vec + i,
+ remove_pinned);
+ migrate_timer_list(new_base, old_base->tv5.vec + i,
+ remove_pinned);
}
- old_base->active_timers = 0;
- old_base->all_timers = 0;
-
spin_unlock(&old_base->lock);
- spin_unlock_irq(&new_base->lock);
+ spin_unlock_irqrestore(&new_base->lock, flags);
put_cpu_ptr(&tvec_bases);
}
+/* Migrate timers from 'cpu' to this_cpu */
+static void migrate_timers(int cpu)
+{
+ BUG_ON(cpu_online(cpu));
+ __migrate_timers(cpu, false, true);
+}
+
+void timer_quiesce_cpu(void *cpup)
+{
+ __migrate_timers(*(int *)cpup, true, false);
+}
+
static int timer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
diff --git a/kernel/trace/power-traces.c b/kernel/trace/power-traces.c
index 9270e1ac6460..49fa2e6eea98 100644
--- a/kernel/trace/power-traces.c
+++ b/kernel/trace/power-traces.c
@@ -15,5 +15,3 @@
EXPORT_TRACEPOINT_SYMBOL_GPL(suspend_resume);
EXPORT_TRACEPOINT_SYMBOL_GPL(cpu_idle);
-EXPORT_TRACEPOINT_SYMBOL(core_ctl_set_busy);
-EXPORT_TRACEPOINT_SYMBOL(core_ctl_eval_need);
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 029da92fb712..7f21591c8ec5 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -13,6 +13,7 @@
#include <linux/mm.h>
#include <linux/cpu.h>
+#include <linux/device.h>
#include <linux/nmi.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -95,6 +96,7 @@ static u64 __read_mostly sample_period;
static DEFINE_PER_CPU(unsigned long, watchdog_touch_ts);
static DEFINE_PER_CPU(struct task_struct *, softlockup_watchdog);
static DEFINE_PER_CPU(struct hrtimer, watchdog_hrtimer);
+static DEFINE_PER_CPU(unsigned int, watchdog_en);
static DEFINE_PER_CPU(bool, softlockup_touch_sync);
static DEFINE_PER_CPU(bool, soft_watchdog_warn);
static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts);
@@ -586,9 +588,17 @@ static void watchdog_set_prio(unsigned int policy, unsigned int prio)
sched_setscheduler(current, policy, &param);
}
-static void watchdog_enable(unsigned int cpu)
+/* Must be called with hotplug lock (lock_device_hotplug()) held. */
+void watchdog_enable(unsigned int cpu)
{
struct hrtimer *hrtimer = raw_cpu_ptr(&watchdog_hrtimer);
+ unsigned int *enabled = raw_cpu_ptr(&watchdog_en);
+
+ lock_device_hotplug_assert();
+
+ if (*enabled)
+ return;
+ *enabled = 1;
/* kick off the timer for the hardlockup detector */
hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
@@ -606,9 +616,17 @@ static void watchdog_enable(unsigned int cpu)
__touch_watchdog();
}
-static void watchdog_disable(unsigned int cpu)
+/* Must be called with hotplug lock (lock_device_hotplug()) held. */
+void watchdog_disable(unsigned int cpu)
{
struct hrtimer *hrtimer = raw_cpu_ptr(&watchdog_hrtimer);
+ unsigned int *enabled = raw_cpu_ptr(&watchdog_en);
+
+ lock_device_hotplug_assert();
+
+ if (!*enabled)
+ return;
+ *enabled = 0;
watchdog_set_prio(SCHED_NORMAL, 0);
hrtimer_cancel(hrtimer);
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/mm/vmstat.c b/mm/vmstat.c
index ca75eeecbad1..77b8eabd5446 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1390,7 +1390,7 @@ static cpumask_var_t cpu_stat_off;
static void vmstat_update(struct work_struct *w)
{
- if (refresh_cpu_vm_stats(true)) {
+ if (refresh_cpu_vm_stats(true) && !cpu_isolated(smp_processor_id())) {
/*
* Counters were updated so we expect more updates
* to occur in the future. Keep on running the
@@ -1402,7 +1402,8 @@ static void vmstat_update(struct work_struct *w)
} else {
/*
* We did not update any counters so the app may be in
- * a mode where it does not cause counter updates.
+ * a mode where it does not cause counter updates or the cpu
+ * was isolated.
* We may be uselessly running vmstat_update.
* Defer the checking for differentials to the
* shepherd thread on a different processor.
@@ -1469,7 +1470,7 @@ static void vmstat_shepherd(struct work_struct *w)
get_online_cpus();
/* Check processors whose vmstat worker threads have been disabled */
for_each_cpu(cpu, cpu_stat_off)
- if (need_update(cpu) &&
+ if (!cpu_isolated(cpu) && need_update(cpu) &&
cpumask_test_and_clear_cpu(cpu, cpu_stat_off))
queue_delayed_work_on(cpu, vmstat_wq,
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index ca966f7de351..87b91ffbdec3 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1175,14 +1175,16 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
static int rtnl_fill_link_ifmap(struct sk_buff *skb, struct net_device *dev)
{
- struct rtnl_link_ifmap map = {
- .mem_start = dev->mem_start,
- .mem_end = dev->mem_end,
- .base_addr = dev->base_addr,
- .irq = dev->irq,
- .dma = dev->dma,
- .port = dev->if_port,
- };
+ struct rtnl_link_ifmap map;
+
+ memset(&map, 0, sizeof(map));
+ map.mem_start = dev->mem_start;
+ map.mem_end = dev->mem_end;
+ map.base_addr = dev->base_addr;
+ map.irq = dev->irq;
+ map.dma = dev->dma;
+ map.port = dev->if_port;
+
if (nla_put(skb, IFLA_MAP, sizeof(map), &map))
return -EMSGSIZE;
diff --git a/net/ipc_router/ipc_router_socket.c b/net/ipc_router/ipc_router_socket.c
index b05ab20a6f13..b15356ae26fc 100644
--- a/net/ipc_router/ipc_router_socket.c
+++ b/net/ipc_router/ipc_router_socket.c
@@ -541,10 +541,18 @@ static unsigned int msm_ipc_router_poll(struct file *file,
static int msm_ipc_router_close(struct socket *sock)
{
struct sock *sk = sock->sk;
- struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
+ struct msm_ipc_port *port_ptr;
int ret;
+ if (!sk)
+ return -EINVAL;
+
lock_sock(sk);
+ port_ptr = msm_ipc_sk_port(sk);
+ if (!port_ptr) {
+ release_sock(sk);
+ return -EINVAL;
+ }
ret = msm_ipc_router_close_port(port_ptr);
msm_ipc_unload_default_node(msm_ipc_sk(sk)->default_node_vote_info);
release_sock(sk);
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 11dccba474b7..36a30fab8625 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -359,11 +359,12 @@ unsigned int arpt_do_table(struct sk_buff *skb,
}
/* All zeroes == unconditional rule. */
-static inline bool unconditional(const struct arpt_arp *arp)
+static inline bool unconditional(const struct arpt_entry *e)
{
static const struct arpt_arp uncond;
- return memcmp(arp, &uncond, sizeof(uncond)) == 0;
+ return e->target_offset == sizeof(struct arpt_entry) &&
+ memcmp(&e->arp, &uncond, sizeof(uncond)) == 0;
}
/* Figures out from what hook each rule can be called: returns 0 if
@@ -402,11 +403,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
|= ((1 << hook) | (1 << NF_ARP_NUMHOOKS));
/* Unconditional return/END. */
- if ((e->target_offset == sizeof(struct arpt_entry) &&
+ if ((unconditional(e) &&
(strcmp(t->target.u.user.name,
XT_STANDARD_TARGET) == 0) &&
- t->verdict < 0 && unconditional(&e->arp)) ||
- visited) {
+ t->verdict < 0) || visited) {
unsigned int oldpos, size;
if ((strcmp(t->target.u.user.name,
@@ -474,14 +474,12 @@ next:
return 1;
}
-static inline int check_entry(const struct arpt_entry *e, const char *name)
+static inline int check_entry(const struct arpt_entry *e)
{
const struct xt_entry_target *t;
- if (!arp_checkentry(&e->arp)) {
- duprintf("arp_tables: arp check failed %p %s.\n", e, name);
+ if (!arp_checkentry(&e->arp))
return -EINVAL;
- }
if (e->target_offset + sizeof(struct xt_entry_target) > e->next_offset)
return -EINVAL;
@@ -522,10 +520,6 @@ find_check_entry(struct arpt_entry *e, const char *name, unsigned int size)
struct xt_target *target;
int ret;
- ret = check_entry(e, name);
- if (ret)
- return ret;
-
e->counters.pcnt = xt_percpu_counter_alloc();
if (IS_ERR_VALUE(e->counters.pcnt))
return -ENOMEM;
@@ -557,7 +551,7 @@ static bool check_underflow(const struct arpt_entry *e)
const struct xt_entry_target *t;
unsigned int verdict;
- if (!unconditional(&e->arp))
+ if (!unconditional(e))
return false;
t = arpt_get_target_c(e);
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
@@ -576,9 +570,11 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
unsigned int valid_hooks)
{
unsigned int h;
+ int err;
if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 ||
- (unsigned char *)e + sizeof(struct arpt_entry) >= limit) {
+ (unsigned char *)e + sizeof(struct arpt_entry) >= limit ||
+ (unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p\n", e);
return -EINVAL;
}
@@ -590,6 +586,10 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
return -EINVAL;
}
+ err = check_entry(e);
+ if (err)
+ return err;
+
/* Check hooks & underflows */
for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
if (!(valid_hooks & (1 << h)))
@@ -598,9 +598,9 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
newinfo->hook_entry[h] = hook_entries[h];
if ((unsigned char *)e - base == underflows[h]) {
if (!check_underflow(e)) {
- pr_err("Underflows must be unconditional and "
- "use the STANDARD target with "
- "ACCEPT/DROP\n");
+ pr_debug("Underflows must be unconditional and "
+ "use the STANDARD target with "
+ "ACCEPT/DROP\n");
return -EINVAL;
}
newinfo->underflow[h] = underflows[h];
@@ -1233,7 +1233,8 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 ||
- (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit) {
+ (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit ||
+ (unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p, limit = %p\n", e, limit);
return -EINVAL;
}
@@ -1246,7 +1247,7 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
}
/* For purposes of check_entry casting the compat entry is fine */
- ret = check_entry((struct arpt_entry *)e, name);
+ ret = check_entry((struct arpt_entry *)e);
if (ret)
return ret;
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index b99affad6ba1..99d46b0a4ead 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -168,11 +168,12 @@ get_entry(const void *base, unsigned int offset)
/* All zeroes == unconditional rule. */
/* Mildly perf critical (only if packet tracing is on) */
-static inline bool unconditional(const struct ipt_ip *ip)
+static inline bool unconditional(const struct ipt_entry *e)
{
static const struct ipt_ip uncond;
- return memcmp(ip, &uncond, sizeof(uncond)) == 0;
+ return e->target_offset == sizeof(struct ipt_entry) &&
+ memcmp(&e->ip, &uncond, sizeof(uncond)) == 0;
#undef FWINV
}
@@ -229,11 +230,10 @@ get_chainname_rulenum(const struct ipt_entry *s, const struct ipt_entry *e,
} else if (s == e) {
(*rulenum)++;
- if (s->target_offset == sizeof(struct ipt_entry) &&
+ if (unconditional(s) &&
strcmp(t->target.u.kernel.target->name,
XT_STANDARD_TARGET) == 0 &&
- t->verdict < 0 &&
- unconditional(&s->ip)) {
+ t->verdict < 0) {
/* Tail of chains: STANDARD target (return/policy) */
*comment = *chainname == hookname
? comments[NF_IP_TRACE_COMMENT_POLICY]
@@ -476,11 +476,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
/* Unconditional return/END. */
- if ((e->target_offset == sizeof(struct ipt_entry) &&
+ if ((unconditional(e) &&
(strcmp(t->target.u.user.name,
XT_STANDARD_TARGET) == 0) &&
- t->verdict < 0 && unconditional(&e->ip)) ||
- visited) {
+ t->verdict < 0) || visited) {
unsigned int oldpos, size;
if ((strcmp(t->target.u.user.name,
@@ -569,14 +568,12 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net)
}
static int
-check_entry(const struct ipt_entry *e, const char *name)
+check_entry(const struct ipt_entry *e)
{
const struct xt_entry_target *t;
- if (!ip_checkentry(&e->ip)) {
- duprintf("ip check failed %p %s.\n", e, name);
+ if (!ip_checkentry(&e->ip))
return -EINVAL;
- }
if (e->target_offset + sizeof(struct xt_entry_target) >
e->next_offset)
@@ -666,10 +663,6 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;
- ret = check_entry(e, name);
- if (ret)
- return ret;
-
e->counters.pcnt = xt_percpu_counter_alloc();
if (IS_ERR_VALUE(e->counters.pcnt))
return -ENOMEM;
@@ -721,7 +714,7 @@ static bool check_underflow(const struct ipt_entry *e)
const struct xt_entry_target *t;
unsigned int verdict;
- if (!unconditional(&e->ip))
+ if (!unconditional(e))
return false;
t = ipt_get_target_c(e);
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
@@ -741,9 +734,11 @@ check_entry_size_and_hooks(struct ipt_entry *e,
unsigned int valid_hooks)
{
unsigned int h;
+ int err;
if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 ||
- (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
+ (unsigned char *)e + sizeof(struct ipt_entry) >= limit ||
+ (unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p\n", e);
return -EINVAL;
}
@@ -755,6 +750,10 @@ check_entry_size_and_hooks(struct ipt_entry *e,
return -EINVAL;
}
+ err = check_entry(e);
+ if (err)
+ return err;
+
/* Check hooks & underflows */
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
if (!(valid_hooks & (1 << h)))
@@ -763,9 +762,9 @@ check_entry_size_and_hooks(struct ipt_entry *e,
newinfo->hook_entry[h] = hook_entries[h];
if ((unsigned char *)e - base == underflows[h]) {
if (!check_underflow(e)) {
- pr_err("Underflows must be unconditional and "
- "use the STANDARD target with "
- "ACCEPT/DROP\n");
+ pr_debug("Underflows must be unconditional and "
+ "use the STANDARD target with "
+ "ACCEPT/DROP\n");
return -EINVAL;
}
newinfo->underflow[h] = underflows[h];
@@ -1493,7 +1492,8 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
- (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
+ (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit ||
+ (unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p, limit = %p\n", e, limit);
return -EINVAL;
}
@@ -1506,7 +1506,7 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
}
/* For purposes of check_entry casting the compat entry is fine */
- ret = check_entry((struct ipt_entry *)e, name);
+ ret = check_entry((struct ipt_entry *)e);
if (ret)
return ret;
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 99425cf2819b..6198807e06f4 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -198,11 +198,12 @@ get_entry(const void *base, unsigned int offset)
/* All zeroes == unconditional rule. */
/* Mildly perf critical (only if packet tracing is on) */
-static inline bool unconditional(const struct ip6t_ip6 *ipv6)
+static inline bool unconditional(const struct ip6t_entry *e)
{
static const struct ip6t_ip6 uncond;
- return memcmp(ipv6, &uncond, sizeof(uncond)) == 0;
+ return e->target_offset == sizeof(struct ip6t_entry) &&
+ memcmp(&e->ipv6, &uncond, sizeof(uncond)) == 0;
}
static inline const struct xt_entry_target *
@@ -258,11 +259,10 @@ get_chainname_rulenum(const struct ip6t_entry *s, const struct ip6t_entry *e,
} else if (s == e) {
(*rulenum)++;
- if (s->target_offset == sizeof(struct ip6t_entry) &&
+ if (unconditional(s) &&
strcmp(t->target.u.kernel.target->name,
XT_STANDARD_TARGET) == 0 &&
- t->verdict < 0 &&
- unconditional(&s->ipv6)) {
+ t->verdict < 0) {
/* Tail of chains: STANDARD target (return/policy) */
*comment = *chainname == hookname
? comments[NF_IP6_TRACE_COMMENT_POLICY]
@@ -488,11 +488,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
/* Unconditional return/END. */
- if ((e->target_offset == sizeof(struct ip6t_entry) &&
+ if ((unconditional(e) &&
(strcmp(t->target.u.user.name,
XT_STANDARD_TARGET) == 0) &&
- t->verdict < 0 &&
- unconditional(&e->ipv6)) || visited) {
+ t->verdict < 0) || visited) {
unsigned int oldpos, size;
if ((strcmp(t->target.u.user.name,
@@ -581,14 +580,12 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net)
}
static int
-check_entry(const struct ip6t_entry *e, const char *name)
+check_entry(const struct ip6t_entry *e)
{
const struct xt_entry_target *t;
- if (!ip6_checkentry(&e->ipv6)) {
- duprintf("ip_tables: ip check failed %p %s.\n", e, name);
+ if (!ip6_checkentry(&e->ipv6))
return -EINVAL;
- }
if (e->target_offset + sizeof(struct xt_entry_target) >
e->next_offset)
@@ -679,10 +676,6 @@ find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;
- ret = check_entry(e, name);
- if (ret)
- return ret;
-
e->counters.pcnt = xt_percpu_counter_alloc();
if (IS_ERR_VALUE(e->counters.pcnt))
return -ENOMEM;
@@ -733,7 +726,7 @@ static bool check_underflow(const struct ip6t_entry *e)
const struct xt_entry_target *t;
unsigned int verdict;
- if (!unconditional(&e->ipv6))
+ if (!unconditional(e))
return false;
t = ip6t_get_target_c(e);
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
@@ -753,9 +746,11 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
unsigned int valid_hooks)
{
unsigned int h;
+ int err;
if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 ||
- (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
+ (unsigned char *)e + sizeof(struct ip6t_entry) >= limit ||
+ (unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p\n", e);
return -EINVAL;
}
@@ -767,6 +762,10 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
return -EINVAL;
}
+ err = check_entry(e);
+ if (err)
+ return err;
+
/* Check hooks & underflows */
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
if (!(valid_hooks & (1 << h)))
@@ -775,9 +774,9 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
newinfo->hook_entry[h] = hook_entries[h];
if ((unsigned char *)e - base == underflows[h]) {
if (!check_underflow(e)) {
- pr_err("Underflows must be unconditional and "
- "use the STANDARD target with "
- "ACCEPT/DROP\n");
+ pr_debug("Underflows must be unconditional and "
+ "use the STANDARD target with "
+ "ACCEPT/DROP\n");
return -EINVAL;
}
newinfo->underflow[h] = underflows[h];
@@ -1505,7 +1504,8 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
- (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
+ (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit ||
+ (unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p, limit = %p\n", e, limit);
return -EINVAL;
}
@@ -1518,7 +1518,7 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
}
/* For purposes of check_entry casting the compat entry is fine */
- ret = check_entry((struct ip6t_entry *)e, name);
+ ret = check_entry((struct ip6t_entry *)e);
if (ret)
return ret;
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/security/Kconfig b/security/Kconfig
index 18568c21e564..c4f83485bc1f 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -23,6 +23,15 @@ config SECURITY_DMESG_RESTRICT
If you are unsure how to answer this question, answer N.
+config SECURITY_PERF_EVENTS_RESTRICT
+ bool "Restrict unprivileged use of performance events"
+ depends on PERF_EVENTS
+ help
+ If you say Y here, the kernel.perf_event_paranoid sysctl
+ will be set to 3 by default, and no unprivileged use of the
+ perf_event_open syscall will be permitted unless it is
+ changed.
+
config SECURITY
bool "Enable different security models"
depends on SYSFS
diff --git a/security/keys/key.c b/security/keys/key.c
index ab7997ded725..534808915371 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -578,7 +578,7 @@ int key_reject_and_link(struct key *key,
mutex_unlock(&key_construction_mutex);
- if (keyring)
+ if (keyring && link_ret == 0)
__key_link_end(keyring, &key->index_key, edit);
/* wake up anyone waiting for a key to be constructed */
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 5b6af14e1d94..3cbc1e7821cf 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -38,7 +38,7 @@
#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
SND_JACK_BTN_4 | SND_JACK_BTN_5 )
-#define OCP_ATTEMPT 1
+#define OCP_ATTEMPT 20
#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
#define SPECIAL_HS_DETECT_TIME_MS (2 * 1000)
#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
@@ -226,6 +226,10 @@ static const char *wcd_mbhc_get_event_string(int event)
return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
case WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF:
return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF);
+ case WCD_EVENT_OCP_OFF:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_OFF);
+ case WCD_EVENT_OCP_ON:
+ return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_ON);
case WCD_EVENT_INVALID:
default:
return WCD_MBHC_STRINGIFY(WCD_EVENT_INVALID);
@@ -394,6 +398,16 @@ out_micb_en:
/* Disable micbias, enable pullup & cs */
wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
break;
+ case WCD_EVENT_OCP_OFF:
+ mbhc->mbhc_cb->irq_control(mbhc->codec,
+ mbhc->intr_ids->hph_left_ocp,
+ false);
+ break;
+ case WCD_EVENT_OCP_ON:
+ mbhc->mbhc_cb->irq_control(mbhc->codec,
+ mbhc->intr_ids->hph_left_ocp,
+ true);
+ break;
default:
break;
}
@@ -461,6 +475,7 @@ static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc)
&mbhc->hph_pa_dac_state)) {
pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_PA_EN, 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 1);
pa_turned_on = true;
}
mutex_unlock(&mbhc->hphr_pa_lock);
@@ -469,6 +484,7 @@ static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc)
&mbhc->hph_pa_dac_state)) {
pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 1);
pa_turned_on = true;
}
mutex_unlock(&mbhc->hphl_pa_lock);
@@ -502,6 +518,8 @@ static void wcd_mbhc_set_and_turnoff_hph_padac(struct wcd_mbhc *mbhc)
pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
set_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
set_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 0);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 0);
} else {
pr_debug("%s PA is off\n", __func__);
}
@@ -2014,13 +2032,24 @@ exit:
static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
{
struct wcd_mbhc *mbhc = data;
+ int val;
pr_debug("%s: received HPHL OCP irq\n", __func__);
if (mbhc) {
- if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
- (!mbhc->hphrocp_cnt)) {
- pr_debug("%s: retry\n", __func__);
+ if (mbhc->mbhc_cb->hph_register_recovery) {
+ if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) {
+ WCD_MBHC_REG_READ(WCD_MBHC_HPHR_OCP_STATUS,
+ val);
+ if ((val != -EINVAL) && val)
+ mbhc->is_hph_ocp_pending = true;
+ goto done;
+ }
+ }
+
+ if (mbhc->hphlocp_cnt < OCP_ATTEMPT) {
mbhc->hphlocp_cnt++;
+ pr_debug("%s: retry, hphlocp_cnt: %d\n", __func__,
+ mbhc->hphlocp_cnt);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
} else {
@@ -2035,6 +2064,7 @@ static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
} else {
pr_err("%s: Bad wcd9xxx_spmi private data\n", __func__);
}
+done:
return IRQ_HANDLED;
}
@@ -2043,10 +2073,26 @@ static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
struct wcd_mbhc *mbhc = data;
pr_debug("%s: received HPHR OCP irq\n", __func__);
- if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
- (!mbhc->hphlocp_cnt)) {
- pr_debug("%s: retry\n", __func__);
+
+ if (!mbhc) {
+ pr_err("%s: Bad mbhc private data\n", __func__);
+ goto done;
+ }
+
+ if (mbhc->is_hph_ocp_pending) {
+ mbhc->is_hph_ocp_pending = false;
+ goto done;
+ }
+
+ if (mbhc->mbhc_cb->hph_register_recovery) {
+ if (mbhc->mbhc_cb->hph_register_recovery(mbhc))
+ /* register corruption, hence reset registers */
+ goto done;
+ }
+ if (mbhc->hphrocp_cnt < OCP_ATTEMPT) {
mbhc->hphrocp_cnt++;
+ pr_debug("%s: retry, hphrocp_cnt: %d\n", __func__,
+ mbhc->hphrocp_cnt);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
} else {
@@ -2057,6 +2103,7 @@ static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
mbhc->hph_status, WCD_MBHC_JACK_MASK);
}
+done:
return IRQ_HANDLED;
}
@@ -2271,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-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h
index ab42b3bb6e7d..676ec342a30a 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.h
+++ b/sound/soc/codecs/wcd-mbhc-v2.h
@@ -66,6 +66,10 @@ enum wcd_mbhc_register_function {
WCD_MBHC_ANC_DET_EN,
WCD_MBHC_FSM_STATUS,
WCD_MBHC_MUX_CTL,
+ WCD_MBHC_HPHL_OCP_DET_EN,
+ WCD_MBHC_HPHR_OCP_DET_EN,
+ WCD_MBHC_HPHL_OCP_STATUS,
+ WCD_MBHC_HPHR_OCP_STATUS,
WCD_MBHC_REG_FUNC_MAX,
};
@@ -127,6 +131,8 @@ enum wcd_notify_event {
WCD_EVENT_POST_HPHR_PA_OFF,
WCD_EVENT_PRE_HPHL_PA_OFF,
WCD_EVENT_PRE_HPHR_PA_OFF,
+ WCD_EVENT_OCP_OFF,
+ WCD_EVENT_OCP_ON,
WCD_EVENT_LAST,
};
@@ -322,7 +328,9 @@ do { \
mbhc->wcd_mbhc_regs[function].reg)) & \
(mbhc->wcd_mbhc_regs[function].mask)) >> \
(mbhc->wcd_mbhc_regs[function].offset)); \
- } \
+ } else { \
+ val = -EINVAL; \
+ } \
} while (0)
struct wcd_mbhc_cb {
@@ -365,6 +373,7 @@ struct wcd_mbhc_cb {
void (*mbhc_gnd_det_ctrl)(struct snd_soc_codec *, bool);
void (*hph_pull_down_ctrl)(struct snd_soc_codec *, bool);
void (*mbhc_moisture_config)(struct wcd_mbhc *);
+ bool (*hph_register_recovery)(struct wcd_mbhc *);
};
struct wcd_mbhc {
@@ -430,6 +439,7 @@ struct wcd_mbhc {
struct mutex hphr_pa_lock;
unsigned long intr_status;
+ bool is_hph_ocp_pending;
};
#define WCD_MBHC_CAL_SIZE(buttons, rload) ( \
sizeof(struct wcd_mbhc_general_cfg) + \
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 a4d92fdd3e7c..46b8e7f72eb8 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -832,6 +832,7 @@ struct tasha_priv {
enum wcd9335_codec_event);
int spkr_gain_offset;
int spkr_mode;
+ int ear_spkr_gain;
struct hpf_work tx_hpf_work[TASHA_NUM_DECIMATORS];
struct tx_mute_work tx_mute_dwork[TASHA_NUM_DECIMATORS];
struct mutex codec_mutex;
@@ -3134,8 +3135,13 @@ static int tasha_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
dai->rate, dai->bit_width,
&dai->grph);
break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (!test_bit(SB_CLK_GEAR, &tasha_p->status_mask)) {
+ tasha_codec_vote_max_bw(codec, true);
+ set_bit(SB_CLK_GEAR, &tasha_p->status_mask);
+ }
+ break;
case SND_SOC_DAPM_POST_PMD:
- tasha_codec_vote_max_bw(codec, true);
ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list,
dai->grph);
dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n",
@@ -3149,7 +3155,6 @@ static int tasha_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
__func__);
ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
dai->grph);
- tasha_codec_vote_max_bw(codec, false);
break;
}
return ret;
@@ -5108,6 +5113,58 @@ static int tasha_codec_enable_swr(struct snd_soc_dapm_widget *w,
return 0;
}
+static int tasha_codec_config_ear_spkr_gain(struct snd_soc_codec *codec,
+ int event, int gain_reg)
+{
+ int comp_gain_offset, val;
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ switch (tasha->spkr_mode) {
+ /* Compander gain in SPKR_MODE1 case is 12 dB */
+ case SPKR_MODE_1:
+ comp_gain_offset = -12;
+ break;
+ /* Default case compander gain is 15 dB */
+ default:
+ comp_gain_offset = -15;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Apply ear spkr gain only if compander is enabled */
+ if (tasha->comp_enabled[COMPANDER_7] &&
+ (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) &&
+ (tasha->ear_spkr_gain != 0)) {
+ /* For example, val is -8(-12+5-1) for 4dB of gain */
+ val = comp_gain_offset + tasha->ear_spkr_gain - 1;
+ snd_soc_write(codec, gain_reg, val);
+
+ dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n",
+ __func__, val);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * Reset RX7 volume to 0 dB if compander is enabled and
+ * ear_spkr_gain is non-zero.
+ */
+ if (tasha->comp_enabled[COMPANDER_7] &&
+ (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) &&
+ (tasha->ear_spkr_gain != 0)) {
+ snd_soc_write(codec, gain_reg, 0x0);
+
+ dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n",
+ __func__);
+ }
+ break;
+ }
+
+ return 0;
+}
+
static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -5175,6 +5232,7 @@ static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
val = snd_soc_read(codec, gain_reg);
val += offset_val;
snd_soc_write(codec, gain_reg, val);
+ tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
break;
case SND_SOC_DAPM_POST_PMD:
if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
@@ -5197,6 +5255,7 @@ static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
val += offset_val;
snd_soc_write(codec, gain_reg, val);
}
+ tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
break;
};
@@ -5404,6 +5463,7 @@ static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
val = snd_soc_read(codec, gain_reg);
val += offset_val;
snd_soc_write(codec, gain_reg, val);
+ tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
break;
case SND_SOC_DAPM_POST_PMD:
tasha_config_compander(codec, w->shift, event);
@@ -5428,6 +5488,7 @@ static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
val += offset_val;
snd_soc_write(codec, gain_reg, val);
}
+ tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
break;
};
@@ -8601,6 +8662,34 @@ static int tasha_ear_pa_gain_put(struct snd_kcontrol *kcontrol,
return 0;
}
+static int tasha_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tasha->ear_spkr_gain;
+
+ dev_dbg(codec->dev, "%s: ear_spkr_gain = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int tasha_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ tasha->ear_spkr_gain = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
static int tasha_config_compander(struct snd_soc_codec *codec, int interp_n,
int event)
{
@@ -8892,14 +8981,26 @@ static const char * const tasha_ear_pa_gain_text[] = {
"G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB"
};
+static const char * const tasha_ear_spkr_pa_gain_text[] = {
+ "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", "G_4_DB",
+ "G_5_DB", "G_6_DB"
+};
+
static const struct soc_enum tasha_ear_pa_gain_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_pa_gain_text),
tasha_ear_pa_gain_text);
+static const struct soc_enum tasha_ear_spkr_pa_gain_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_spkr_pa_gain_text),
+ tasha_ear_spkr_pa_gain_text);
+
static const struct snd_kcontrol_new tasha_analog_gain_controls[] = {
SOC_ENUM_EXT("EAR PA Gain", tasha_ear_pa_gain_enum,
tasha_ear_pa_gain_get, tasha_ear_pa_gain_put),
+ SOC_ENUM_EXT("EAR SPKR PA Gain", tasha_ear_spkr_pa_gain_enum,
+ tasha_ear_spkr_pa_gain_get, tasha_ear_spkr_pa_gain_put),
+
SOC_SINGLE_TLV("HPHL Volume", WCD9335_HPH_L_EN, 0, 20, 1,
line_gain),
SOC_SINGLE_TLV("HPHR Volume", WCD9335_HPH_R_EN, 0, 20, 1,
@@ -10189,20 +10290,25 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("ANC EAR"),
SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
AIF1_PB, 0, tasha_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
AIF2_PB, 0, tasha_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
AIF3_PB, 0, tasha_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM,
AIF4_PB, 0, tasha_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_AIF_IN_E("AIF MIX1 PB", "AIF Mix Playback", 0,
SND_SOC_NOPM, AIF_MIX1_PB, 0,
tasha_codec_enable_slimrx,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, TASHA_RX0, 0,
&slim_rx_mux[TASHA_RX0]),
@@ -10974,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;
}
@@ -10993,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;
}
@@ -11031,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;
}
@@ -11069,8 +11175,19 @@ static int tasha_startup(struct snd_pcm_substream *substream,
static void tasha_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec);
+
pr_debug("%s(): substream = %s stream = %d\n" , __func__,
substream->name, substream->stream);
+
+ if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C)
+ return;
+
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ test_bit(SB_CLK_GEAR, &tasha->status_mask)) {
+ tasha_codec_vote_max_bw(dai->codec, false);
+ clear_bit(SB_CLK_GEAR, &tasha->status_mask);
+ }
}
static int tasha_set_decimator_rate(struct snd_soc_dai *dai,
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
index 246b3bfab876..55072466af55 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-dsd.c
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
@@ -378,6 +378,14 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
int interp_idx;
u8 pcm_rate_val;
+ if (!dsd_conf) {
+ dev_err(codec->dev, "%s: null dsd_config pointer\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "%s: DSD%d, event: %d\n", __func__,
+ w->shift, event);
+
if (w->shift == DSD0) {
/* Read out select */
if (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x02)
@@ -422,10 +430,6 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1,
dsd_conf->volume[DSD0]);
- if (clk_users > 1)
- snd_soc_update_bits(codec,
- WCD934X_CDC_DSD0_CFG2,
- 0x04, 0x00);
} else if (w->shift == DSD1) {
snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
0x02, 0x02);
@@ -436,26 +440,35 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
/* Apply Gain */
snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1,
dsd_conf->volume[DSD1]);
+ }
+ /* 10msec sleep required after DSD clock is set */
+ usleep_range(10000, 10100);
- if (clk_users > 1)
+ if (clk_users > 1) {
+ snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
+ if (w->shift == DSD0)
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x00);
+ if (w->shift == DSD1)
snd_soc_update_bits(codec,
WCD934X_CDC_DSD1_CFG2,
0x04, 0x00);
+
}
- /* 10msec sleep required after DSD clock is set */
- usleep_range(10000, 10100);
break;
case SND_SOC_DAPM_POST_PMD:
if (w->shift == DSD0) {
- snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
- 0x01, 0x00);
snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
0x04, 0x04);
- } else if (w->shift == DSD1) {
- snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
0x01, 0x00);
+ } else if (w->shift == DSD1) {
snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+ 0x01, 0x00);
}
tavil_codec_enable_interp_clk(codec, event, interp_idx);
@@ -466,6 +479,7 @@ static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL,
0x01, 0x00);
tavil_dsd_data_pull(codec, 0x03, 0x04, false);
+ tavil_dsd_reset(dsd_conf);
}
break;
}
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-mbhc.c b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c
index 20f3043656e5..b3a30eb10b92 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c
+++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c
@@ -118,6 +118,14 @@ static struct wcd_mbhc_register
WCD934X_MBHC_STATUS_SPARE_1, 0x01, 0, 0),
WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL",
WCD934X_MBHC_NEW_CTL_2, 0x70, 4, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN",
+ WCD934X_HPH_L_TEST, 0x01, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN",
+ WCD934X_HPH_R_TEST, 0x01, 0, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS",
+ WCD934X_INTR_PIN1_STATUS0, 0x04, 2, 0),
+ WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS",
+ WCD934X_INTR_PIN1_STATUS0, 0x08, 3, 0),
};
static const struct wcd_mbhc_intr intr_ids = {
@@ -778,6 +786,26 @@ static void tavil_mbhc_moisture_config(struct wcd_mbhc *mbhc)
0x0C, TAVIL_MBHC_MOISTURE_RREF << 2);
}
+static bool tavil_hph_register_recovery(struct wcd_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+ if (!wcd934x_mbhc)
+ return false;
+
+ wcd934x_mbhc->is_hph_recover = false;
+ snd_soc_dapm_force_enable_pin(snd_soc_codec_get_dapm(codec),
+ "RESET_HPH_REGISTERS");
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+
+ snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
+ "RESET_HPH_REGISTERS");
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+
+ return wcd934x_mbhc->is_hph_recover;
+}
+
static const struct wcd_mbhc_cb mbhc_cb = {
.request_irq = tavil_mbhc_request_irq,
.irq_control = tavil_mbhc_irq_control,
@@ -800,6 +828,7 @@ static const struct wcd_mbhc_cb mbhc_cb = {
.mbhc_gnd_det_ctrl = tavil_mbhc_gnd_det_ctrl,
.hph_pull_down_ctrl = tavil_mbhc_hph_pull_down_ctrl,
.mbhc_moisture_config = tavil_mbhc_moisture_config,
+ .hph_register_recovery = tavil_hph_register_recovery,
};
static struct regulator *tavil_codec_find_ondemand_regulator(
diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h
index b747b120b605..120a7b0f8177 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h
+++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h
@@ -32,6 +32,7 @@ struct wcd934x_mbhc {
struct wcd9xxx *wcd9xxx;
struct fw_info *fw_data;
bool mbhc_started;
+ bool is_hph_recover;
};
extern int tavil_mbhc_init(struct wcd934x_mbhc **mbhc,
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 28914ed3f937..9e18c17d6f1c 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.c
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -123,6 +123,9 @@ static const struct snd_kcontrol_new name##_mux = \
#define WCD934X_DEC_PWR_LVL_DF 0x00
#define WCD934X_STRING_LEN 100
+#define WCD934X_DIG_CORE_REG_MIN WCD934X_CDC_ANC0_CLK_RESET_CTL
+#define WCD934X_DIG_CORE_REG_MAX 0xFFF
+
#define WCD934X_MAX_MICBIAS 4
#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone"
#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone"
@@ -141,6 +144,32 @@ static const struct snd_kcontrol_new name##_mux = \
#define TAVIL_VERSION_ENTRY_SIZE 17
+#define WCD934X_DIG_CORE_COLLAPSE_TIMER_MS (5 * 1000)
+
+enum {
+ POWER_COLLAPSE,
+ POWER_RESUME,
+};
+
+static int dig_core_collapse_enable = 1;
+module_param(dig_core_collapse_enable, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating");
+
+/* dig_core_collapse timer in seconds */
+static int dig_core_collapse_timer = (WCD934X_DIG_CORE_COLLAPSE_TIMER_MS/1000);
+module_param(dig_core_collapse_timer, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating");
+
+#define TAVIL_HPH_REG_RANGE_1 (WCD934X_HPH_R_DAC_CTL - WCD934X_HPH_CNP_EN + 1)
+#define TAVIL_HPH_REG_RANGE_2 (WCD934X_HPH_NEW_ANA_HPH3 -\
+ WCD934X_HPH_NEW_ANA_HPH2 + 1)
+#define TAVIL_HPH_REG_RANGE_3 (WCD934X_HPH_NEW_INT_PA_RDAC_MISC3 -\
+ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL + 1)
+#define TAVIL_HPH_TOTAL_REG (TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2 +\
+ TAVIL_HPH_REG_RANGE_3)
+
enum {
VI_SENSE_1,
VI_SENSE_2,
@@ -157,6 +186,7 @@ enum {
AIF3_CAP,
AIF4_PB,
AIF4_VIFEED,
+ AIF4_MAD_TX,
NUM_CODEC_DAIS,
};
@@ -272,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 */
@@ -418,6 +448,30 @@ static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = {
WCD934X_CDC_ANC0_FF_A_GAIN_CTL),
AANC_GAIN_CONTROL, 0xFF, WCD934X_REG_BITS, 0
},
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ SB_PGD_TX_PORT_MULTI_CHANNEL_0(0)),
+ SB_PGD_TX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ SB_PGD_TX_PORT_MULTI_CHANNEL_1(0)),
+ SB_PGD_TX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x180, 0)),
+ SB_PGD_RX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x181, 0)),
+ SB_PGD_RX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4
+ },
};
static struct afe_param_cdc_reg_cfg_data tavil_audio_reg_cfg = {
@@ -530,6 +584,9 @@ struct tavil_priv {
struct wcd934x_swr swr;
struct mutex micb_lock;
+ struct delayed_work power_gate_work;
+ struct mutex power_lock;
+
struct clk *wcd_ext_clk;
/* mbhc module */
@@ -563,6 +620,8 @@ struct tavil_priv {
int main_clk_users[WCD934X_NUM_INTERPOLATORS];
struct tavil_dsd_config *dsd_config;
struct tavil_idle_detect_config idle_det_cfg;
+
+ int power_active_ref;
};
static const struct tavil_reg_mask_val tavil_spkr_default[] = {
@@ -1033,17 +1092,15 @@ static int tavil_codec_enable_anc(struct snd_soc_dapm_widget *w,
snd_soc_write(codec, reg, (val & mask));
}
+ /* Rate converter clk enable and set bypass mode */
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_RC_COMMON_CTL,
+ 0x05, 0x05);
if (!hwdep_cal)
release_firmware(fw);
break;
- case SND_SOC_DAPM_POST_PMU:
- /* Remove ANC Rx from reset */
- snd_soc_update_bits(codec, WCD934X_CDC_ANC0_CLK_RESET_CTL,
- 0x08, 0x00);
- snd_soc_update_bits(codec, WCD934X_CDC_ANC1_CLK_RESET_CTL,
- 0x08, 0x00);
- break;
case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_RC_COMMON_CTL,
+ 0x05, 0x00);
if (!strcmp(w->name, "ANC EAR PA") ||
!strcmp(w->name, "ANC SPK1 PA")) {
snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL,
@@ -1215,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);
@@ -1404,6 +1463,34 @@ static int tavil_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai,
return ret;
}
+static void tavil_codec_mute_dsd(struct snd_soc_codec *codec,
+ struct list_head *ch_list)
+{
+ u8 dsd0_in;
+ u8 dsd1_in;
+ struct wcd9xxx_ch *ch;
+
+ /* Read DSD Input Ports */
+ dsd0_in = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x3C) >> 2;
+ dsd1_in = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x3C) >> 2;
+
+ if ((dsd0_in == 0) && (dsd1_in == 0))
+ return;
+
+ /*
+ * Check if the ports getting disabled are connected to DSD inputs.
+ * If connected, enable DSD mute to avoid DC entering into DSD Filter
+ */
+ list_for_each_entry(ch, ch_list, list) {
+ if (ch->port == (dsd0_in + WCD934X_RX_PORT_START_NUMBER - 1))
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+ 0x04, 0x04);
+ if (ch->port == (dsd1_in + WCD934X_RX_PORT_START_NUMBER - 1))
+ snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+ 0x04, 0x04);
+ }
+}
+
static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
@@ -1413,6 +1500,7 @@ static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
int ret = 0;
struct wcd9xxx_codec_dai_data *dai;
+ struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config;
core = dev_get_drvdata(codec->dev->parent);
@@ -1435,6 +1523,9 @@ static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
&dai->grph);
break;
case SND_SOC_DAPM_POST_PMD:
+ if (dsd_conf)
+ tavil_codec_mute_dsd(codec, &dai->wcd9xxx_ch_list);
+
ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list,
dai->grph);
dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n",
@@ -1771,6 +1862,12 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
0x06, (0x03 << 1));
set_bit(HPH_PA_DELAY, &tavil->status_mask);
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) {
+ /* Set regulator mode to AB if DSD is enabled */
+ snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
+ }
break;
case SND_SOC_DAPM_POST_PMU:
/*
@@ -1781,6 +1878,9 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
usleep_range(7000, 7100);
clear_bit(HPH_PA_DELAY, &tavil->status_mask);
}
+
+ snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x01);
+
/* Remove mute */
snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL,
0x10, 0x00);
@@ -1797,28 +1897,33 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
WCD934X_CDC_RX2_RX_PATH_MIX_CTL,
0x10, 0x00);
if (dsd_conf &&
- (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) {
- /* Set regulator mode to AB if DSD is enabled */
- snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
- 0x02, 0x02);
+ (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
0x04, 0x00);
- }
tavil_codec_override(codec, tavil->hph_mode, event);
break;
case SND_SOC_DAPM_PRE_PMD:
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ WCD_EVENT_PRE_HPHR_PA_OFF,
+ &tavil->mbhc->wcd_mbhc);
/* Enable DSD Mute before PA disable */
if (dsd_conf &&
(snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL,
+ 0x10, 0x10);
break;
case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA disable */
+ usleep_range(5000, 5100);
tavil_codec_override(codec, tavil->hph_mode, event);
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ WCD_EVENT_POST_HPHR_PA_OFF,
+ &tavil->mbhc->wcd_mbhc);
snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
0x06, 0x0);
- /* 5ms sleep is required after PA disable */
- usleep_range(5000, 5100);
break;
};
@@ -1840,6 +1945,12 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
0x06, (0x03 << 1));
set_bit(HPH_PA_DELAY, &tavil->status_mask);
+ if (dsd_conf &&
+ (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) {
+ /* Set regulator mode to AB if DSD is enabled */
+ snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+ 0x02, 0x02);
+ }
break;
case SND_SOC_DAPM_POST_PMU:
/*
@@ -1850,6 +1961,7 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
usleep_range(7000, 7100);
clear_bit(HPH_PA_DELAY, &tavil->status_mask);
}
+ snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x01);
/* Remove Mute on primary path */
snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL,
0x10, 0x00);
@@ -1866,28 +1978,34 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
WCD934X_CDC_RX1_RX_PATH_MIX_CTL,
0x10, 0x00);
if (dsd_conf &&
- (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) {
- /* Set regulator mode to AB if DSD is enabled */
- snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
- 0x02, 0x02);
+ (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
0x04, 0x00);
- }
tavil_codec_override(codec, tavil->hph_mode, event);
break;
case SND_SOC_DAPM_PRE_PMD:
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ WCD_EVENT_PRE_HPHL_PA_OFF,
+ &tavil->mbhc->wcd_mbhc);
/* Enable DSD Mute before PA disable */
if (dsd_conf &&
(snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
0x04, 0x04);
+
+ snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL,
+ 0x10, 0x10);
break;
case SND_SOC_DAPM_POST_PMD:
+ /* 5ms sleep is required after PA disable */
+ usleep_range(5000, 5100);
tavil_codec_override(codec, tavil->hph_mode, event);
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ WCD_EVENT_POST_HPHL_PA_OFF,
+ &tavil->mbhc->wcd_mbhc);
snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
0x06, 0x0);
- /* 5ms sleep is required after PA disable */
- usleep_range(5000, 5100);
break;
};
@@ -2359,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;
@@ -2404,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)
{
@@ -2418,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;
@@ -2431,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:
@@ -4060,13 +4201,12 @@ int tavil_codec_enable_standalone_micbias(struct snd_soc_codec *codec,
}
if (enable)
- rc = snd_soc_dapm_force_enable_pin_unlocked(
+ rc = snd_soc_dapm_force_enable_pin(
snd_soc_codec_get_dapm(codec),
micb_names[micb_index]);
else
- rc = snd_soc_dapm_disable_pin_unlocked(
- snd_soc_codec_get_dapm(codec),
- micb_names[micb_index]);
+ rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
+ micb_names[micb_index]);
if (!rc)
snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
@@ -4110,6 +4250,244 @@ static int tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w,
return __tavil_codec_enable_micbias(w, event);
}
+
+static const struct reg_sequence tavil_hph_reset_tbl[] = {
+ { WCD934X_HPH_CNP_EN, 0x80 },
+ { WCD934X_HPH_CNP_WG_CTL, 0x9A },
+ { WCD934X_HPH_CNP_WG_TIME, 0x14 },
+ { WCD934X_HPH_OCP_CTL, 0x28 },
+ { WCD934X_HPH_AUTO_CHOP, 0x16 },
+ { WCD934X_HPH_CHOP_CTL, 0x83 },
+ { WCD934X_HPH_PA_CTL1, 0x46 },
+ { WCD934X_HPH_PA_CTL2, 0x50 },
+ { WCD934X_HPH_L_EN, 0x80 },
+ { WCD934X_HPH_L_TEST, 0xE0 },
+ { WCD934X_HPH_L_ATEST, 0x50 },
+ { WCD934X_HPH_R_EN, 0x80 },
+ { WCD934X_HPH_R_TEST, 0xE0 },
+ { WCD934X_HPH_R_ATEST, 0x54 },
+ { WCD934X_HPH_RDAC_CLK_CTL1, 0x99 },
+ { WCD934X_HPH_RDAC_CLK_CTL2, 0x9B },
+ { WCD934X_HPH_RDAC_LDO_CTL, 0x33 },
+ { WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 },
+ { WCD934X_HPH_REFBUFF_UHQA_CTL, 0xA8 },
+ { WCD934X_HPH_REFBUFF_LP_CTL, 0x0A },
+ { WCD934X_HPH_L_DAC_CTL, 0x00 },
+ { WCD934X_HPH_R_DAC_CTL, 0x00 },
+ { WCD934X_HPH_NEW_ANA_HPH2, 0x00 },
+ { WCD934X_HPH_NEW_ANA_HPH3, 0x00 },
+ { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 },
+ { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0xA0 },
+ { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 },
+ { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 },
+ { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 },
+ { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 },
+ { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE },
+ { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 },
+ { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e},
+ { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 },
+ { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 },
+};
+
+static const struct tavil_reg_mask_val tavil_pa_disable[] = {
+ { WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x10 }, /* RX1 mute enable */
+ { WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x10 }, /* RX2 mute enable */
+ { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, /* GM3 boost disable */
+ { WCD934X_ANA_HPH, 0x80, 0x00 }, /* HPHL PA disable */
+ { WCD934X_ANA_HPH, 0x40, 0x00 }, /* HPHR PA disable */
+ { WCD934X_ANA_HPH, 0x20, 0x00 }, /* HPHL REF dsable */
+ { WCD934X_ANA_HPH, 0x10, 0x00 }, /* HPHR REF disable */
+};
+
+static const struct tavil_reg_mask_val tavil_ocp_en_seq[] = {
+ { WCD934X_RX_OCP_CTL, 0x0F, 0x01 }, /* OCP number of attempts is 1 */
+ { WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */
+ { WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */
+ { WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */
+};
+
+static const struct tavil_reg_mask_val tavil_ocp_en_seq_1[] = {
+ { WCD934X_RX_OCP_CTL, 0x0F, 0x01 }, /* OCP number of attempts is 1 */
+ { WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */
+};
+
+/* LO-HIFI */
+static const struct tavil_reg_mask_val tavil_pre_pa_en_lohifi[] = {
+ { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x20 },
+ { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 },
+ { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 },
+ { WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0xc0 },
+ { WCD934X_HPH_PA_CTL1, 0x0e, 0x02 },
+ { WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 },
+};
+
+static const struct tavil_reg_mask_val tavil_pre_pa_en[] = {
+ { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 },
+ { WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x0 },
+ { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 },
+ { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 },
+ { WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0x80 },
+ { WCD934X_HPH_PA_CTL1, 0x0e, 0x06 },
+ { WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 },
+};
+
+static const struct tavil_reg_mask_val tavil_post_pa_en[] = {
+ { WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */
+ { WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */
+ { WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x20 }, /* RX1 mute disable */
+ { WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x20 }, /* RX2 mute disable */
+ { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x80 }, /* GM3 boost enable */
+ { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x02 },
+};
+
+static void tavil_codec_hph_reg_range_read(struct regmap *map, u8 *buf)
+{
+ regmap_bulk_read(map, WCD934X_HPH_CNP_EN, buf, TAVIL_HPH_REG_RANGE_1);
+ regmap_bulk_read(map, WCD934X_HPH_NEW_ANA_HPH2,
+ buf + TAVIL_HPH_REG_RANGE_1, TAVIL_HPH_REG_RANGE_2);
+ regmap_bulk_read(map, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ buf + TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2,
+ TAVIL_HPH_REG_RANGE_3);
+}
+
+static void tavil_codec_hph_reg_recover(struct tavil_priv *tavil,
+ struct regmap *map, int pa_status)
+{
+ int i;
+
+ blocking_notifier_call_chain(&tavil->mbhc->notifier,
+ WCD_EVENT_OCP_OFF,
+ &tavil->mbhc->wcd_mbhc);
+
+ if (pa_status & 0xC0)
+ goto pa_en_restore;
+
+ dev_dbg(tavil->dev, "%s: HPH PA in disable state (0x%x)\n",
+ __func__, pa_status);
+
+ regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x10);
+ regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x10);
+ regmap_write_bits(map, WCD934X_ANA_HPH, 0xC0, 0x00);
+ regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x00);
+ regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x00);
+ regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x00);
+
+ /* Restore to HW defaults */
+ regmap_multi_reg_write(map, tavil_hph_reset_tbl,
+ ARRAY_SIZE(tavil_hph_reset_tbl));
+
+ for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq); i++)
+ regmap_write_bits(map, tavil_ocp_en_seq[i].reg,
+ tavil_ocp_en_seq[i].mask,
+ tavil_ocp_en_seq[i].val);
+ goto end;
+
+
+pa_en_restore:
+ dev_dbg(tavil->dev, "%s: HPH PA in enable state (0x%x)\n",
+ __func__, pa_status);
+
+ /* Disable PA and other registers before restoring */
+ for (i = 0; i < ARRAY_SIZE(tavil_pa_disable); i++)
+ regmap_write_bits(map, tavil_pa_disable[i].reg,
+ tavil_pa_disable[i].mask,
+ tavil_pa_disable[i].val);
+
+ regmap_multi_reg_write(map, tavil_hph_reset_tbl,
+ ARRAY_SIZE(tavil_hph_reset_tbl));
+
+ for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq_1); i++)
+ regmap_write_bits(map, tavil_ocp_en_seq_1[i].reg,
+ tavil_ocp_en_seq_1[i].mask,
+ tavil_ocp_en_seq_1[i].val);
+
+ if (tavil->hph_mode == CLS_H_LOHIFI) {
+ for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en_lohifi); i++)
+ regmap_write_bits(map,
+ tavil_pre_pa_en_lohifi[i].reg,
+ tavil_pre_pa_en_lohifi[i].mask,
+ tavil_pre_pa_en_lohifi[i].val);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en); i++)
+ regmap_write_bits(map, tavil_pre_pa_en[i].reg,
+ tavil_pre_pa_en[i].mask,
+ tavil_pre_pa_en[i].val);
+ }
+ regmap_write_bits(map, WCD934X_ANA_HPH, 0x0C, pa_status & 0x0C);
+ regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x30);
+ /* wait for 100usec after HPH DAC is enabled */
+ usleep_range(100, 110);
+ regmap_write(map, WCD934X_ANA_HPH, pa_status);
+ /* Sleep for 7msec after PA is enabled */
+ usleep_range(7000, 7100);
+
+ for (i = 0; i < ARRAY_SIZE(tavil_post_pa_en); i++)
+ regmap_write_bits(map, tavil_post_pa_en[i].reg,
+ tavil_post_pa_en[i].mask,
+ tavil_post_pa_en[i].val);
+
+end:
+ tavil->mbhc->is_hph_recover = true;
+ blocking_notifier_call_chain(
+ &tavil->mbhc->notifier,
+ WCD_EVENT_OCP_ON,
+ &tavil->mbhc->wcd_mbhc);
+}
+
+static int tavil_codec_reset_hph_registers(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);
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+ u8 cache_val[TAVIL_HPH_TOTAL_REG];
+ u8 hw_val[TAVIL_HPH_TOTAL_REG];
+ int pa_status;
+ int ret;
+
+ dev_dbg(wcd9xxx->dev, "%s: event: %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ memset(cache_val, 0, TAVIL_HPH_TOTAL_REG);
+ memset(hw_val, 0, TAVIL_HPH_TOTAL_REG);
+
+ regmap_read(wcd9xxx->regmap, WCD934X_ANA_HPH, &pa_status);
+
+ tavil_codec_hph_reg_range_read(wcd9xxx->regmap, cache_val);
+
+ /* Read register values from HW directly */
+ regcache_cache_bypass(wcd9xxx->regmap, true);
+ tavil_codec_hph_reg_range_read(wcd9xxx->regmap, hw_val);
+ regcache_cache_bypass(wcd9xxx->regmap, false);
+
+ /* compare both the registers to know if there is corruption */
+ ret = memcmp(cache_val, hw_val, TAVIL_HPH_TOTAL_REG);
+
+ /* If both the values are same, it means no corruption */
+ if (ret) {
+ dev_dbg(codec->dev, "%s: cache and hw reg are not same\n",
+ __func__);
+ tavil_codec_hph_reg_recover(tavil, wcd9xxx->regmap,
+ pa_status);
+ } else {
+ dev_dbg(codec->dev, "%s: cache and hw reg are same\n",
+ __func__);
+ tavil->mbhc->is_hph_recover = false;
+ }
+ break;
+ default:
+ break;
+ };
+
+ return 0;
+}
+
static int tavil_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -4870,6 +5248,10 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = {
-84, 40, digital_gain),
SOC_SINGLE_SX_TLV("DEC4 Volume", WCD934X_CDC_TX4_TX_VOL_CTL, 0,
-84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC5 Volume", WCD934X_CDC_TX5_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
+ SOC_SINGLE_SX_TLV("DEC6 Volume", WCD934X_CDC_TX6_TX_VOL_CTL, 0,
+ -84, 40, digital_gain),
SOC_SINGLE_SX_TLV("DEC7 Volume", WCD934X_CDC_TX7_TX_VOL_CTL, 0,
-84, 40, digital_gain),
SOC_SINGLE_SX_TLV("DEC8 Volume", WCD934X_CDC_TX8_TX_VOL_CTL, 0,
@@ -5433,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,
@@ -5754,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);
@@ -6140,6 +6530,14 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ /*
+ * Not supply widget, this is used to recover HPH registers.
+ * It is not connected to any other widgets
+ */
+ SND_SOC_DAPM_SUPPLY("RESET_HPH_REGISTERS", SND_SOC_NOPM,
+ 0, 0, tavil_codec_reset_hph_registers,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0,
tavil_codec_force_enable_micbias,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
@@ -6168,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"),
@@ -6306,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),
@@ -6462,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",
@@ -6500,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);
@@ -6514,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;
}
@@ -6844,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),
@@ -6874,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);
@@ -7031,8 +7449,152 @@ 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)
+{
+ struct snd_soc_codec *codec = tavil->codec;
+
+ if (!codec)
+ return;
+
+ mutex_lock(&tavil->power_lock);
+ dev_dbg(codec->dev, "%s: Entering power gating function, %d\n",
+ __func__, tavil->power_active_ref);
+
+ if (tavil->power_active_ref > 0)
+ goto exit;
+
+ wcd9xxx_set_power_state(tavil->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_BEGIN,
+ WCD9XXX_DIG_CORE_REGION_1);
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+ 0x02, 0x00);
+ wcd9xxx_set_power_state(tavil->wcd9xxx, WCD_REGION_POWER_DOWN,
+ WCD9XXX_DIG_CORE_REGION_1);
+exit:
+ dev_dbg(codec->dev, "%s: Exiting power gating function, %d\n",
+ __func__, tavil->power_active_ref);
+ mutex_unlock(&tavil->power_lock);
+}
+
+static void tavil_codec_power_gate_work(struct work_struct *work)
+{
+ struct tavil_priv *tavil;
+ struct delayed_work *dwork;
+ struct snd_soc_codec *codec;
+
+ dwork = to_delayed_work(work);
+ tavil = container_of(dwork, struct tavil_priv, power_gate_work);
+ codec = tavil->codec;
+
+ if (!codec)
+ return;
+
+ tavil_codec_power_gate_digital_core(tavil);
+}
+
+/* called under power_lock acquisition */
+static int tavil_dig_core_remove_power_collapse(struct snd_soc_codec *codec)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_write(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);
+ snd_soc_write(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);
+ snd_soc_write(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x00);
+ snd_soc_update_bits(codec, WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x02);
+
+ wcd9xxx_set_power_state(tavil->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_REMOVE,
+ WCD9XXX_DIG_CORE_REGION_1);
+ regcache_mark_dirty(codec->component.regmap);
+ regcache_sync_region(codec->component.regmap,
+ WCD934X_DIG_CORE_REG_MIN,
+ WCD934X_DIG_CORE_REG_MAX);
+
+ return 0;
+}
+
+static int tavil_dig_core_power_collapse(struct tavil_priv *tavil,
+ int req_state)
+{
+ struct snd_soc_codec *codec;
+ int cur_state;
+
+ /* Exit if feature is disabled */
+ if (!dig_core_collapse_enable)
+ return 0;
+
+ mutex_lock(&tavil->power_lock);
+ if (req_state == POWER_COLLAPSE)
+ tavil->power_active_ref--;
+ else if (req_state == POWER_RESUME)
+ tavil->power_active_ref++;
+ else
+ goto unlock_mutex;
+
+ if (tavil->power_active_ref < 0) {
+ dev_dbg(tavil->dev, "%s: power_active_ref is negative\n",
+ __func__);
+ goto unlock_mutex;
+ }
+
+ codec = tavil->codec;
+ if (!codec)
+ goto unlock_mutex;
+
+ if (req_state == POWER_COLLAPSE) {
+ if (tavil->power_active_ref == 0) {
+ schedule_delayed_work(&tavil->power_gate_work,
+ msecs_to_jiffies(dig_core_collapse_timer * 1000));
+ }
+ } else if (req_state == POWER_RESUME) {
+ if (tavil->power_active_ref == 1) {
+ /*
+ * At this point, there can be two cases:
+ * 1. Core already in power collapse state
+ * 2. Timer kicked in and still did not expire or
+ * waiting for the power_lock
+ */
+ cur_state = wcd9xxx_get_current_power_state(
+ tavil->wcd9xxx,
+ WCD9XXX_DIG_CORE_REGION_1);
+ if (cur_state == WCD_REGION_POWER_DOWN) {
+ tavil_dig_core_remove_power_collapse(codec);
+ } else {
+ mutex_unlock(&tavil->power_lock);
+ cancel_delayed_work_sync(
+ &tavil->power_gate_work);
+ mutex_lock(&tavil->power_lock);
+ }
+ }
+ }
+
+unlock_mutex:
+ mutex_unlock(&tavil->power_lock);
+
+ return 0;
+}
+
static int tavil_cdc_req_mclk_enable(struct tavil_priv *tavil,
bool enable)
{
@@ -7074,15 +7636,15 @@ static int __tavil_cdc_mclk_enable_locked(struct tavil_priv *tavil,
dev_dbg(tavil->dev, "%s: mclk_enable = %u\n", __func__, enable);
if (enable) {
+ tavil_dig_core_power_collapse(tavil, POWER_RESUME);
tavil_vote_svs(tavil, true);
ret = tavil_cdc_req_mclk_enable(tavil, true);
if (ret)
goto done;
-
- set_bit(AUDIO_NOMINAL, &tavil->status_mask);
} else {
tavil_cdc_req_mclk_enable(tavil, false);
tavil_vote_svs(tavil, false);
+ tavil_dig_core_power_collapse(tavil, POWER_COLLAPSE);
}
done:
@@ -7328,6 +7890,10 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = {
{WCD934X_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00},
{WCD934X_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00},
{WCD934X_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_RX_OCP_CTL, 0x0F, 0x01}, /* OCP number of attempts is 1 */
+ {WCD934X_HPH_OCP_CTL, 0xFF, 0x3A}, /* OCP current limit */
+ {WCD934X_HPH_L_TEST, 0x01, 0x01},
+ {WCD934X_HPH_R_TEST, 0x01, 0x01},
};
static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {
@@ -7933,6 +8499,15 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA");
mutex_unlock(&tavil->codec_mutex);
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "VIfeed");
+
snd_soc_dapm_sync(dapm);
tavil_wdsp_initialize(codec);
@@ -8007,6 +8582,9 @@ static int tavil_suspend(struct device *dev)
return -EINVAL;
}
dev_dbg(dev, "%s: system suspend\n", __func__);
+ if (delayed_work_pending(&tavil->power_gate_work) &&
+ cancel_delayed_work_sync(&tavil->power_gate_work))
+ tavil_codec_power_gate_digital_core(tavil);
return 0;
}
@@ -8508,6 +9086,7 @@ static int tavil_probe(struct platform_device *pdev)
struct tavil_priv *tavil;
struct clk *wcd_ext_clk;
struct wcd9xxx_resmgr_v2 *resmgr;
+ struct wcd9xxx_power_region *cdc_pwr;
tavil = devm_kzalloc(&pdev->dev, sizeof(struct tavil_priv),
GFP_KERNEL);
@@ -8518,6 +9097,8 @@ static int tavil_probe(struct platform_device *pdev)
tavil->wcd9xxx = dev_get_drvdata(pdev->dev.parent);
tavil->dev = &pdev->dev;
+ INIT_DELAYED_WORK(&tavil->power_gate_work, tavil_codec_power_gate_work);
+ mutex_init(&tavil->power_lock);
INIT_WORK(&tavil->tavil_add_child_devices_work,
tavil_add_child_devices);
mutex_init(&tavil->micb_lock);
@@ -8534,6 +9115,18 @@ static int tavil_probe(struct platform_device *pdev)
*/
tavil->svs_ref_cnt = 1;
+ cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region),
+ GFP_KERNEL);
+ if (!cdc_pwr) {
+ ret = -ENOMEM;
+ goto err_resmgr;
+ }
+ tavil->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr;
+ cdc_pwr->pwr_collapse_reg_min = WCD934X_DIG_CORE_REG_MIN;
+ cdc_pwr->pwr_collapse_reg_max = WCD934X_DIG_CORE_REG_MAX;
+ wcd9xxx_set_power_state(tavil->wcd9xxx,
+ WCD_REGION_POWER_COLLAPSE_REMOVE,
+ WCD9XXX_DIG_CORE_REGION_1);
/*
* Init resource manager so that if child nodes such as SoundWire
* requests for clock, resource manager can honor the request
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 b97b73dc4191..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;
}
@@ -1043,7 +1043,6 @@ static int wsa881x_probe(struct snd_soc_codec *codec)
"%s.%x", "wsatz", (u8)dev->addr);
wsa881x->bg_cnt = 0;
wsa881x->clk_cnt = 0;
- wsa881x->state = WSA881X_DEV_UP;
wsa881x->tz_pdata.codec = codec;
wsa881x->tz_pdata.wsa_temp_reg_read = wsa881x_temp_reg_read;
wsa881x_init_thermal(&wsa881x->tz_pdata);
@@ -1229,6 +1228,7 @@ static int wsa881x_swr_probe(struct swr_device *pdev)
goto err;
}
wsa881x_gpio_ctrl(wsa881x, true);
+ wsa881x->state = WSA881X_DEV_UP;
if (!debugfs_wsa881x_dent) {
dbgwsa881x = wsa881x;
@@ -1305,7 +1305,8 @@ static int wsa881x_swr_down(struct swr_device *pdev)
dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
return -EINVAL;
}
- cancel_delayed_work_sync(&wsa881x->ocp_ctl_work);
+ if (delayed_work_pending(&wsa881x->ocp_ctl_work))
+ cancel_delayed_work_sync(&wsa881x->ocp_ctl_work);
ret = wsa881x_gpio_ctrl(wsa881x, false);
if (ret)
dev_err(&pdev->dev, "%s: Failed to disable gpio\n", __func__);
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
index e740a24704b7..64a1fa76604d 100644
--- a/sound/soc/msm/Kconfig
+++ b/sound/soc/msm/Kconfig
@@ -79,6 +79,15 @@ config QTI_PP
tuning parameters of various modules such as equalizer,
customized mixing.
+config QTI_PP_AUDIOSPHERE
+ bool "Enable QTI AUDIOSPHERE PP"
+ depends on SND_SOC_MSM_QDSP6V2_INTF
+ help
+ To add support for QTI audio sphere post processing.
+ This support is to configure the post processing
+ parameters to DSP. The configuration includes sending
+ tuning parameters of audio sphere module.
+
config SND_SOC_CPE
tristate "CPE drivers"
depends on SND_SOC_WCD_CPE
@@ -104,6 +113,7 @@ config SND_SOC_MSM8996
select SND_SOC_MSM_HDMI_CODEC_RX
select DTS_SRS_TM
select QTI_PP
+ select QTI_PP_AUDIOSPHERE
select SND_SOC_CPE
select MSM_ULTRASOUND
select DOLBY_DS2
@@ -125,6 +135,9 @@ config SND_SOC_MSMCOBALT
select SND_SOC_MSM_HOSTLESS_PCM
select SND_DYNAMIC_MINORS
select MSM_QDSP6_APRV2_GLINK
+ select MSM_QDSP6_SSR
+ select MSM_QDSP6_PDR
+ select MSM_QDSP6_NOTIFIER
select MSM_QDSP6V2_CODECS
select SND_SOC_WCD9335
select SND_SOC_WCD934X
diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c
index 8270cfb98de8..ef4c9b01d91e 100644
--- a/sound/soc/msm/msm-cpe-lsm.c
+++ b/sound/soc/msm/msm-cpe-lsm.c
@@ -496,7 +496,7 @@ static int msm_cpe_lab_buf_alloc(struct snd_pcm_substream *substream,
pcm_buf[count].mem = pcm_buf[0].mem + (count * bufsz);
pcm_buf[count].phys = pcm_buf[0].phys + (count * bufsz);
dev_dbg(rtd->dev,
- "%s: pcm_buf[%d].mem %p pcm_buf[%d].phys %pa\n",
+ "%s: pcm_buf[%d].mem %pK pcm_buf[%d].phys %pK\n",
__func__, count,
(void *)pcm_buf[count].mem,
count, &(pcm_buf[count].phys));
@@ -722,7 +722,7 @@ static int msm_cpe_lab_thread(void *data)
cur_buf = &lab_d->pcm_buf[buf_count % prd_cnt];
next_buf = &lab_d->pcm_buf[(buf_count + 2) % prd_cnt];
dev_dbg(rtd->dev,
- "%s: Cur buf.mem = %p Next Buf.mem = %p\n"
+ "%s: Cur buf.mem = %pK Next Buf.mem = %pK\n"
" buf count = 0x%x\n", __func__,
cur_buf->mem, next_buf->mem, buf_count);
} else {
@@ -1544,7 +1544,7 @@ static int msm_cpe_lsm_lab_start(struct snd_pcm_substream *substream,
int rc;
if (!substream || !substream->private_data) {
- pr_err("%s: invalid substream (%p)\n",
+ pr_err("%s: invalid substream (%pK)\n",
__func__, substream);
return -EINVAL;
}
@@ -1634,7 +1634,7 @@ static bool msm_cpe_lsm_is_valid_stream(struct snd_pcm_substream *substream,
struct wcd_cpe_lsm_ops *lsm_ops;
if (!substream || !substream->private_data) {
- pr_err("%s: invalid substream (%p)\n",
+ pr_err("%s: invalid substream (%pK)\n",
func, substream);
return false;
}
@@ -2075,7 +2075,7 @@ static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream,
struct wcd_cpe_lsm_ops *lsm_ops;
if (!substream || !substream->private_data) {
- pr_err("%s: invalid substream (%p)\n",
+ pr_err("%s: invalid substream (%pK)\n",
__func__, substream);
return -EINVAL;
}
@@ -2347,7 +2347,7 @@ static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream,
struct wcd_cpe_lsm_ops *lsm_ops;
if (!substream || !substream->private_data) {
- pr_err("%s: invalid substream (%p)\n",
+ pr_err("%s: invalid substream (%pK)\n",
__func__, substream);
return -EINVAL;
}
@@ -2997,7 +2997,7 @@ static int msm_cpe_lsm_copy(struct snd_pcm_substream *substream, int a,
if (lab_d->buf_idx >= (lsm_d->hw_params.period_count))
lab_d->buf_idx = 0;
pcm_buf = (lab_d->pcm_buf[lab_d->buf_idx].mem);
- pr_debug("%s: Buf IDX = 0x%x pcm_buf %p\n",
+ pr_debug("%s: Buf IDX = 0x%x pcm_buf %pK\n",
__func__, lab_d->buf_idx, pcm_buf);
if (pcm_buf) {
if (copy_to_user(buf, pcm_buf, fbytes)) {
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 4cb62a6b3e7d..ee9dcacdd5c9 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -96,7 +96,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -108,8 +109,9 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.rates = (SNDRV_PCM_RATE_8000_384000|
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE|
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 4,
.rate_min = 8000,
@@ -127,7 +129,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -140,7 +143,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -210,7 +214,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 6,
.rate_min = 8000,
@@ -222,7 +227,9 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.rates = (SNDRV_PCM_RATE_8000_384000|
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE),
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -240,7 +247,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -259,7 +267,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -271,8 +280,9 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.rates = (SNDRV_PCM_RATE_8000_48000|
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE|
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -290,7 +300,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -303,7 +314,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -321,7 +333,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -340,7 +353,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -353,7 +367,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -2220,7 +2235,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -2239,7 +2255,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -2258,7 +2275,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -2277,7 +2295,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -2296,7 +2315,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -2315,7 +2335,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
@@ -2334,7 +2355,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c
index 63367ce07f5a..a2bd3be62175 100644
--- a/sound/soc/msm/msmcobalt.c
+++ b/sound/soc/msm/msmcobalt.c
@@ -33,6 +33,7 @@
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <device_event.h>
+#include <linux/qdsp6v2/audio_notifier.h>
#include "qdsp6v2/msm-pcm-routing-v2.h"
#include "../codecs/wcd9335.h"
#include "../codecs/wcd934x/wcd934x.h"
@@ -70,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,
@@ -95,6 +99,46 @@ enum {
SLIM_TX_MAX,
};
+enum {
+ PRIM_MI2S = 0,
+ SEC_MI2S,
+ TERT_MI2S,
+ QUAT_MI2S,
+ MI2S_MAX,
+};
+
+enum {
+ PRIM_AUX_PCM = 0,
+ SEC_AUX_PCM,
+ TERT_AUX_PCM,
+ QUAT_AUX_PCM,
+ AUX_PCM_MAX,
+};
+
+enum {
+ PCM_I2S_SEL_PRIM = 0,
+ PCM_I2S_SEL_SEC,
+ PCM_I2S_SEL_TERT,
+ PCM_I2S_SEL_QUAT,
+ PCM_I2S_SEL_MAX,
+};
+
+struct mi2s_aux_pcm_common_conf {
+ struct mutex lock;
+ void *pcm_i2s_sel_vt_addr;
+};
+
+struct mi2s_conf {
+ struct mutex lock;
+ u32 ref_cnt;
+ u32 msm_is_mi2s_master;
+};
+
+struct auxpcm_conf {
+ struct mutex lock;
+ u32 ref_cnt;
+};
+
struct dev_config {
u32 sample_rate;
u32 bit_format;
@@ -129,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},
@@ -178,13 +347,43 @@ static struct dev_config proxy_rx_cfg = {
.channels = 2,
};
+/* Default configuration of MI2S channels */
+static struct dev_config mi2s_rx_cfg[] = {
+ [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config mi2s_tx_cfg[] = {
+ [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_rx_cfg[] = {
+ [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_tx_cfg[] = {
+ [PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [SEC_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+ [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
static int msm_vi_feed_tx_ch = 2;
static const char *const slim_rx_ch_text[] = {"One", "Two"};
static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four",
"Five", "Six", "Seven",
"Eight"};
static const char *const vi_feed_ch_text[] = {"One", "Two"};
-static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE",
+ "S32_LE"};
static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"};
static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16",
"KHZ_32", "KHZ_44P1", "KHZ_48",
@@ -199,9 +398,22 @@ static char const *ch_text[] = {"Two", "Three", "Four", "Five",
static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025",
"KHZ_16", "KHZ_22P05",
"KHZ_32", "KHZ_44P1", "KHZ_48",
- "KHZ_96", "KHZ_192"};
+ "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",
+ "KHZ_96", "KHZ_192"};
+static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven",
+ "Eight"};
static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text);
@@ -231,14 +443,44 @@ 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);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text);
static struct platform_device *spdev;
+static bool is_initial_boot;
static bool codec_reg_done;
static struct snd_soc_aux_dev *msm_aux_dev;
static struct snd_soc_codec_conf *msm_codec_conf;
static struct msm_asoc_wcd93xx_codec msm_codec_fn;
-static void *adsp_state_notifier;
static void *def_tasha_mbhc_cal(void);
static void *def_tavil_mbhc_cal(void);
@@ -276,6 +518,45 @@ static struct snd_soc_dapm_route wcd_audio_paths[] = {
{"MIC BIAS4", NULL, "MCLK"},
};
+static struct afe_clk_set mi2s_clk[MI2S_MAX] = {
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ },
+ {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+ }
+};
+
+static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX];
+static struct mi2s_conf mi2s_intf_conf[MI2S_MAX];
+static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX];
+
static int slim_get_sample_rate_val(int sample_rate)
{
int sample_rate_val = 0;
@@ -371,6 +652,9 @@ static int slim_get_bit_format_val(int bit_format)
int val = 0;
switch (bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val = 3;
+ break;
case SNDRV_PCM_FORMAT_S24_3LE:
val = 2;
break;
@@ -399,6 +683,9 @@ static int slim_get_bit_format(int val)
case 2:
bit_fmt = SNDRV_PCM_FORMAT_S24_3LE;
break;
+ case 3:
+ bit_fmt = SNDRV_PCM_FORMAT_S32_LE;
+ break;
default:
bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
break;
@@ -736,6 +1023,9 @@ static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
int sample_rate_val;
switch (usb_rx_cfg.sample_rate) {
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 9;
+ break;
case SAMPLING_RATE_192KHZ:
sample_rate_val = 8;
break;
@@ -776,6 +1066,9 @@ static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
switch (ucontrol->value.integer.value[0]) {
+ case 9:
+ usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+ break;
case 8:
usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
break;
@@ -818,6 +1111,9 @@ static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
switch (usb_rx_cfg.bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ucontrol->value.integer.value[0] = 3;
+ break;
case SNDRV_PCM_FORMAT_S24_3LE:
ucontrol->value.integer.value[0] = 2;
break;
@@ -842,6 +1138,9 @@ static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol,
int rc = 0;
switch (ucontrol->value.integer.value[0]) {
+ case 3:
+ usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
case 2:
usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
break;
@@ -884,6 +1183,9 @@ static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
int sample_rate_val;
switch (usb_tx_cfg.sample_rate) {
+ case SAMPLING_RATE_384KHZ:
+ sample_rate_val = 9;
+ break;
case SAMPLING_RATE_192KHZ:
sample_rate_val = 8;
break;
@@ -926,6 +1228,9 @@ static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
switch (ucontrol->value.integer.value[0]) {
+ case 9:
+ usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+ break;
case 8:
usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
break;
@@ -968,6 +1273,9 @@ static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
switch (usb_tx_cfg.bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ucontrol->value.integer.value[0] = 3;
+ break;
case SNDRV_PCM_FORMAT_S24_3LE:
ucontrol->value.integer.value[0] = 2;
break;
@@ -992,6 +1300,9 @@ static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol,
int rc = 0;
switch (ucontrol->value.integer.value[0]) {
+ case 3:
+ usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
case 2:
usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
break;
@@ -1188,6 +1499,803 @@ 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;
+
+ switch (value) {
+ case 1:
+ sample_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 0:
+ default:
+ sample_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ 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;
+
+ switch (sample_rate) {
+ case SAMPLING_RATE_16KHZ:
+ sample_rate_val = 1;
+ break;
+ case SAMPLING_RATE_8KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+ 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;
+
+ if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM",
+ sizeof("PRIM_AUX_PCM")))
+ idx = PRIM_AUX_PCM;
+ else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM",
+ sizeof("SEC_AUX_PCM")))
+ idx = SEC_AUX_PCM;
+ else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM",
+ sizeof("TERT_AUX_PCM")))
+ idx = TERT_AUX_PCM;
+ else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM",
+ sizeof("QUAT_AUX_PCM")))
+ idx = QUAT_AUX_PCM;
+ else {
+ pr_err("%s: unsupported port: %s",
+ __func__, kcontrol->id.name);
+ idx = -EINVAL;
+ }
+
+ return idx;
+}
+
+static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ aux_pcm_rx_cfg[idx].sample_rate =
+ aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ aux_pcm_tx_cfg[idx].sample_rate =
+ aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = aux_pcm_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, aux_pcm_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+ int idx;
+
+ if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX",
+ sizeof("PRIM_MI2S_RX")))
+ idx = PRIM_MI2S;
+ else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX",
+ sizeof("SEC_MI2S_RX")))
+ idx = SEC_MI2S;
+ else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX",
+ sizeof("TERT_MI2S_RX")))
+ idx = TERT_MI2S;
+ else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX",
+ sizeof("QUAT_MI2S_RX")))
+ idx = QUAT_MI2S;
+ else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX",
+ sizeof("PRIM_MI2S_TX")))
+ idx = PRIM_MI2S;
+ else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX",
+ sizeof("SEC_MI2S_TX")))
+ idx = SEC_MI2S;
+ else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX",
+ sizeof("TERT_MI2S_TX")))
+ idx = TERT_MI2S;
+ else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX",
+ sizeof("QUAT_MI2S_TX")))
+ idx = QUAT_MI2S;
+ else {
+ pr_err("%s: unsupported channel: %s",
+ __func__, kcontrol->id.name);
+ idx = -EINVAL;
+ }
+
+ return idx;
+}
+
+static int mi2s_get_sample_rate_val(int sample_rate)
+{
+ int sample_rate_val;
+
+ 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;
+ default:
+ sample_rate_val = 4;
+ break;
+ }
+ return sample_rate_val;
+}
+
+static int mi2s_get_sample_rate(int value)
+{
+ int sample_rate;
+
+ 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;
+ default:
+ sample_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ return sample_rate;
+}
+
+static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_rx_cfg[idx].sample_rate =
+ mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_tx_cfg[idx].sample_rate =
+ mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate);
+
+ pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].sample_rate,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].channels);
+ ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1;
+
+ return 0;
+}
+
+static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: msm_mi2s_[%d]_rx_ch = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].channels);
+
+ return 1;
+}
+
+static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].channels);
+ ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1;
+
+ return 0;
+}
+
+static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+ pr_debug("%s: msm_mi2s_[%d]_tx_ch = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].channels);
+
+ return 1;
+}
+
static const struct snd_kcontrol_new msm_snd_controls[] = {
SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs,
msm_slim_rx_ch_get, msm_slim_rx_ch_put),
@@ -1254,6 +2362,88 @@ 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),
+ SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate,
+ aux_pcm_rx_sample_rate_get,
+ aux_pcm_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate,
+ aux_pcm_tx_sample_rate_get,
+ aux_pcm_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate,
+ mi2s_rx_sample_rate_get,
+ mi2s_rx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate,
+ mi2s_tx_sample_rate_get,
+ mi2s_tx_sample_rate_put),
+ SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs,
+ msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+ SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs,
+ msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
};
static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
@@ -1527,6 +2717,126 @@ 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;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[PRIM_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[PRIM_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SEC_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[SEC_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SEC_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[SEC_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[TERT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_TERT_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[TERT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_QUAT_AUXPCM_RX:
+ rate->min = rate->max =
+ aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_rx_cfg[QUAT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_QUAT_AUXPCM_TX:
+ rate->min = rate->max =
+ aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate;
+ channels->min = channels->max =
+ aux_pcm_tx_cfg[QUAT_AUX_PCM].channels;
+ break;
+
+ case MSM_BACKEND_DAI_PRI_MI2S_RX:
+ rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[PRIM_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_PRI_MI2S_TX:
+ rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[PRIM_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+ rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[SEC_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+ rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[SEC_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+ rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[TERT_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+ rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[TERT_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+ rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_rx_cfg[QUAT_MI2S].channels;
+ break;
+
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+ rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate;
+ channels->min = channels->max =
+ mi2s_tx_cfg[QUAT_MI2S].channels;
+ break;
+
default:
rate->min = rate->max = SAMPLING_RATE_48KHZ;
break;
@@ -1655,49 +2965,65 @@ err_fail:
return ret;
}
-static int msm_adsp_state_callback(struct notifier_block *nb,
- unsigned long value, void *priv)
+static int msmcobalt_notifier_service_cb(struct notifier_block *this,
+ unsigned long opcode, void *ptr)
{
- int ret = NOTIFY_OK;
+ int ret;
struct snd_soc_card *card = NULL;
const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_codec *codec;
- if (!spdev)
- return -EINVAL;
-
- card = platform_get_drvdata(spdev);
- rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
- if (!rtd) {
- dev_err(card->dev,
- "%s: snd_soc_get_pcm_runtime for %s failed!\n",
- __func__, be_dl_name);
- ret = -EINVAL;
- goto err_pcm_runtime;
- }
+ pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode);
- codec = rtd->codec;
- if (value == SUBSYS_BEFORE_SHUTDOWN) {
- pr_debug("%s: ADSP is about to shutdown. Clearing AFE config\n",
- __func__);
+ switch (opcode) {
+ case AUDIO_NOTIFIER_SERVICE_DOWN:
+ /*
+ * Use flag to ignore initial boot notifications
+ * On initial boot msm_adsp_power_up_config is
+ * called on init. There is no need to clear
+ * and set the config again on initial boot.
+ */
+ if (is_initial_boot)
+ break;
msm_afe_clear_config();
- } else if (value == SUBSYS_AFTER_POWERUP) {
- ret = msm_adsp_power_up_config(codec);
- if (ret) {
+ break;
+ case AUDIO_NOTIFIER_SERVICE_UP:
+ if (is_initial_boot) {
+ is_initial_boot = false;
+ break;
+ }
+ if (!spdev)
+ return -EINVAL;
+
+ card = platform_get_drvdata(spdev);
+ rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+ if (!rtd) {
+ dev_err(card->dev,
+ "%s: snd_soc_get_pcm_runtime for %s failed!\n",
+ __func__, be_dl_name);
ret = -EINVAL;
- } else {
- pr_debug("%s: ADSP is up\n", __func__);
- ret = NOTIFY_OK;
+ goto done;
}
- }
+ codec = rtd->codec;
-err_pcm_runtime:
- return ret;
+ ret = msm_adsp_power_up_config(codec);
+ if (ret < 0) {
+ dev_err(card->dev,
+ "%s: msm_adsp_power_up_config failed ret = %d!\n",
+ __func__, ret);
+ goto done;
+ }
+ break;
+ default:
+ break;
+ }
+done:
+ return NOTIFY_OK;
}
-static struct notifier_block adsp_state_notifier_block = {
- .notifier_call = msm_adsp_state_callback,
+static struct notifier_block service_nb = {
+ .notifier_call = msmcobalt_notifier_service_cb,
.priority = -INT_MAX,
};
@@ -1815,17 +3141,18 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
goto err_afe_cfg;
}
- if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) {
- config_data = msm_codec_fn.get_afe_config_fn(codec,
- AFE_AANC_VERSION);
- if (config_data) {
- ret = afe_set_config(AFE_AANC_VERSION, config_data, 0);
- if (ret) {
- pr_err("%s: Failed to set aanc version %d\n",
- __func__, ret);
- goto err_afe_cfg;
- }
+ config_data = msm_codec_fn.get_afe_config_fn(codec,
+ AFE_AANC_VERSION);
+ if (config_data) {
+ ret = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+ if (ret) {
+ pr_err("%s: Failed to set aanc version %d\n",
+ __func__, ret);
+ goto err_afe_cfg;
}
+ }
+
+ if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) {
config_data = msm_codec_fn.get_afe_config_fn(codec,
AFE_CDC_CLIP_REGISTERS_CONFIG);
if (config_data) {
@@ -1848,14 +3175,7 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
}
}
}
- adsp_state_notifier = subsys_notif_register_notifier("adsp",
- &adsp_state_notifier_block);
- if (!adsp_state_notifier) {
- pr_err("%s: Failed to register adsp state notifier\n",
- __func__);
- ret = -EFAULT;
- goto err_adsp_notify;
- }
+
/*
* Send speaker configuration only for WSA8810.
* Defalut configuration is for WSA8815.
@@ -1904,7 +3224,6 @@ done:
return 0;
err_snd_module:
-err_adsp_notify:
err_afe_cfg:
return ret;
}
@@ -1931,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);
@@ -2204,6 +3523,448 @@ exit:
return ret;
}
+static int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int index = cpu_dai->id - 1;
+ return ret = 0;
+
+ dev_dbg(rtd->card->dev,
+ "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
+ __func__, substream->name, substream->stream,
+ cpu_dai->name, cpu_dai->id);
+
+ if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
+ ret = -EINVAL;
+ dev_err(rtd->card->dev,
+ "%s: CPU DAI id (%d) out of range\n",
+ __func__, cpu_dai->id);
+ goto done;
+ }
+
+ mutex_lock(&auxpcm_intf_conf[index].lock);
+ if (++auxpcm_intf_conf[index].ref_cnt == 1) {
+ if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+ mutex_lock(&mi2s_auxpcm_conf[index].lock);
+ iowrite32(1,
+ mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+ mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+ } else {
+ dev_err(rtd->card->dev,
+ "%s lpaif_tert_muxsel_virt_addr is NULL\n",
+ __func__);
+ ret = -EINVAL;
+ }
+ }
+ if (IS_ERR_VALUE(ret))
+ auxpcm_intf_conf[index].ref_cnt--;
+
+ mutex_unlock(&auxpcm_intf_conf[index].lock);
+
+done:
+ return ret;
+}
+
+static void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int index = rtd->cpu_dai->id - 1;
+
+ dev_dbg(rtd->card->dev,
+ "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
+ __func__,
+ substream->name, substream->stream,
+ rtd->cpu_dai->name, rtd->cpu_dai->id);
+
+ if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
+ dev_err(rtd->card->dev,
+ "%s: CPU DAI id (%d) out of range\n",
+ __func__, rtd->cpu_dai->id);
+ return;
+ }
+
+ mutex_lock(&auxpcm_intf_conf[index].lock);
+ if (--auxpcm_intf_conf[index].ref_cnt == 0) {
+ if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+ mutex_lock(&mi2s_auxpcm_conf[index].lock);
+ iowrite32(0,
+ mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+ mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+ } else {
+ dev_err(rtd->card->dev,
+ "%s lpaif_tert_muxsel_virt_addr is NULL\n",
+ __func__);
+ auxpcm_intf_conf[index].ref_cnt++;
+ }
+ }
+ mutex_unlock(&auxpcm_intf_conf[index].lock);
+}
+
+static int msm_get_port_id(int be_id)
+{
+ int afe_port_id;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_PRI_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_PRI_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+ break;
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+ afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+ break;
+ case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+ afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ break;
+ default:
+ pr_err("%s: Invalid be_id: %d\n", __func__, be_id);
+ afe_port_id = -EINVAL;
+ }
+
+ return afe_port_id;
+}
+
+static u32 get_mi2s_bits_per_sample(u32 bit_format)
+{
+ u32 bit_per_sample;
+
+ switch (bit_format) {
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bit_per_sample = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ bit_per_sample = 16;
+ break;
+ }
+
+ return bit_per_sample;
+}
+
+static void update_mi2s_clk_val(int dai_id, int stream)
+{
+ u32 bit_per_sample;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ bit_per_sample =
+ get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format);
+ mi2s_clk[dai_id].clk_freq_in_hz =
+ mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+ } else {
+ bit_per_sample =
+ get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format);
+ mi2s_clk[dai_id].clk_freq_in_hz =
+ mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+ }
+
+ if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master)
+ mi2s_clk[dai_id].clk_freq_in_hz = 0;
+}
+
+static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int port_id = 0;
+ int index = cpu_dai->id;
+
+ port_id = msm_get_port_id(rtd->dai_link->be_id);
+ if (IS_ERR_VALUE(port_id)) {
+ dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+ ret = port_id;
+ goto done;
+ }
+
+ if (enable) {
+ update_mi2s_clk_val(index, substream->stream);
+ dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__,
+ mi2s_clk[index].clk_freq_in_hz);
+ }
+
+ mi2s_clk[index].enable = enable;
+ ret = afe_set_lpass_clock_v2(port_id,
+ &mi2s_clk[index]);
+ if (ret < 0) {
+ dev_err(rtd->card->dev,
+ "%s: afe lpass clock failed for port 0x%x , err:%d\n",
+ __func__, port_id, ret);
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int index = cpu_dai->id;
+ unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS;
+
+ dev_dbg(rtd->card->dev,
+ "%s: substream = %s stream = %d, dai name %s, dai ID %d\n",
+ __func__, substream->name, substream->stream,
+ cpu_dai->name, cpu_dai->id);
+
+ if (index < PRIM_MI2S || index > QUAT_MI2S) {
+ ret = -EINVAL;
+ dev_err(rtd->card->dev,
+ "%s: CPU DAI id (%d) out of range\n",
+ __func__, cpu_dai->id);
+ goto done;
+ }
+ /*
+ * Muxtex protection in case the same MI2S
+ * interface using for both TX and RX so
+ * that the same clock won't be enable twice.
+ */
+ mutex_lock(&mi2s_intf_conf[index].lock);
+ if (++mi2s_intf_conf[index].ref_cnt == 1) {
+ ret = msm_mi2s_set_sclk(substream, true);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(rtd->card->dev,
+ "%s: afe lpass clock failed to enable MI2S clock, err:%d\n",
+ __func__, ret);
+ goto clean_up;
+ }
+ if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+ mutex_lock(&mi2s_auxpcm_conf[index].lock);
+ iowrite32(0,
+ mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+ mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+ } else {
+ dev_err(rtd->card->dev,
+ "%s lpaif_muxsel_virt_addr is NULL for dai %d\n",
+ __func__, index);
+ ret = -EINVAL;
+ goto clk_off;
+ }
+ /* Check if msm needs to provide the clock to the interface */
+ if (!mi2s_intf_conf[index].msm_is_mi2s_master)
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n",
+ __func__, index, ret);
+ goto clk_off;
+ }
+ }
+clk_off:
+ if (IS_ERR_VALUE(ret))
+ msm_mi2s_set_sclk(substream, false);
+clean_up:
+ if (IS_ERR_VALUE(ret))
+ mi2s_intf_conf[index].ref_cnt--;
+ mutex_unlock(&mi2s_intf_conf[index].lock);
+done:
+ return ret;
+}
+
+static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ int ret;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int index = rtd->cpu_dai->id;
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+ if (index < PRIM_MI2S || index > QUAT_MI2S) {
+ pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index);
+ return;
+ }
+
+ mutex_lock(&mi2s_intf_conf[index].lock);
+ if (--mi2s_intf_conf[index].ref_cnt == 0) {
+ ret = msm_mi2s_set_sclk(substream, false);
+ if (ret < 0) {
+ pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n",
+ __func__, index, ret);
+ mi2s_intf_conf[index].ref_cnt++;
+ }
+ }
+ mutex_unlock(&mi2s_intf_conf[index].lock);
+}
+
+static struct snd_soc_ops msm_mi2s_be_ops = {
+ .startup = msm_mi2s_snd_startup,
+ .shutdown = msm_mi2s_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_aux_pcm_be_ops = {
+ .startup = msm_aux_pcm_snd_startup,
+ .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,
};
@@ -2220,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 */
@@ -3020,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[] = {
@@ -3318,6 +5111,21 @@ static struct snd_soc_dai_link msm_tavil_be_dai_links[] = {
.ignore_pmdown_time = 1,
.ignore_suspend = 1,
},
+ /* MAD BE */
+ {
+ .name = LPASS_BE_SLIMBUS_5_TX,
+ .stream_name = "Slimbus5 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16395",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tavil_codec",
+ .codec_dai_name = "tavil_mad1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ },
{
.name = LPASS_BE_SLIMBUS_6_RX,
.stream_name = "Slimbus6 Playback",
@@ -3421,13 +5229,261 @@ static struct snd_soc_dai_link ext_disp_be_dai_link[] = {
},
};
+static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = {
+ {
+ .name = LPASS_BE_PRI_MI2S_RX,
+ .stream_name = "Primary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .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_PRI_MI2S_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_MI2S_TX,
+ .stream_name = "Primary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .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_PRI_MI2S_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_RX,
+ .stream_name = "Secondary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .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_SECONDARY_MI2S_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_MI2S_TX,
+ .stream_name = "Secondary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .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_SECONDARY_MI2S_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_RX,
+ .stream_name = "Tertiary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .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_TERTIARY_MI2S_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX,
+ .stream_name = "Tertiary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .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_TERTIARY_MI2S_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_RX,
+ .stream_name = "Quaternary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .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_QUATERNARY_MI2S_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_TX,
+ .stream_name = "Quaternary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .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_QUATERNARY_MI2S_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = {
+ /* Primary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .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_AUXPCM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .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_AUXPCM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Secondary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_SEC_AUXPCM_RX,
+ .stream_name = "Sec AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.2",
+ .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_SEC_AUXPCM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SEC_AUXPCM_TX,
+ .stream_name = "Sec AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.2",
+ .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_SEC_AUXPCM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Tertiary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_TERT_AUXPCM_RX,
+ .stream_name = "Tert AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.3",
+ .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_AUXPCM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_TERT_AUXPCM_TX,
+ .stream_name = "Tert AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.3",
+ .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_AUXPCM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ /* Quaternary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_QUAT_AUXPCM_RX,
+ .stream_name = "Quat AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.4",
+ .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_QUAT_AUXPCM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_QUAT_AUXPCM_TX,
+ .stream_name = "Quat AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.4",
+ .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_QUAT_AUXPCM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .ops = &msm_aux_pcm_be_ops,
+ },
+};
+
static struct snd_soc_dai_link msm_tasha_dai_links[
ARRAY_SIZE(msm_common_dai_links) +
ARRAY_SIZE(msm_tasha_fe_dai_links) +
ARRAY_SIZE(msm_common_be_dai_links) +
ARRAY_SIZE(msm_tasha_be_dai_links) +
ARRAY_SIZE(msm_wcn_be_dai_links) +
- ARRAY_SIZE(ext_disp_be_dai_link)];
+ ARRAY_SIZE(ext_disp_be_dai_link) +
+ ARRAY_SIZE(msm_mi2s_be_dai_links) +
+ ARRAY_SIZE(msm_auxpcm_be_dai_links)];
static struct snd_soc_dai_link msm_tavil_dai_links[
ARRAY_SIZE(msm_common_dai_links) +
@@ -3435,7 +5491,9 @@ static struct snd_soc_dai_link msm_tavil_dai_links[
ARRAY_SIZE(msm_common_be_dai_links) +
ARRAY_SIZE(msm_tavil_be_dai_links) +
ARRAY_SIZE(msm_wcn_be_dai_links) +
- ARRAY_SIZE(ext_disp_be_dai_link)];
+ ARRAY_SIZE(ext_disp_be_dai_link) +
+ ARRAY_SIZE(msm_mi2s_be_dai_links) +
+ ARRAY_SIZE(msm_auxpcm_be_dai_links)];
static int msm_snd_card_late_probe(struct snd_soc_card *card)
{
@@ -3844,6 +5902,20 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
sizeof(ext_disp_be_dai_link));
total_links += ARRAY_SIZE(ext_disp_be_dai_link);
}
+ if (of_property_read_bool(dev->of_node,
+ "qcom,mi2s-audio-intf")) {
+ memcpy(msm_tasha_dai_links + total_links,
+ msm_mi2s_be_dai_links,
+ sizeof(msm_mi2s_be_dai_links));
+ total_links += ARRAY_SIZE(msm_mi2s_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,auxpcm-audio-intf")) {
+ memcpy(msm_tasha_dai_links + total_links,
+ msm_auxpcm_be_dai_links,
+ sizeof(msm_auxpcm_be_dai_links));
+ total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+ }
dailink = msm_tasha_dai_links;
} else if (!strcmp(match->data, "tavil_codec")) {
card = &snd_soc_card_tavil_msm;
@@ -3878,10 +5950,24 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
dev_dbg(dev, "%s(): ext disp audio support present\n",
__func__);
memcpy(msm_tavil_dai_links + total_links,
- ext_disp_be_dai_link,
- sizeof(ext_disp_be_dai_link));
+ ext_disp_be_dai_link,
+ sizeof(ext_disp_be_dai_link));
total_links += ARRAY_SIZE(ext_disp_be_dai_link);
}
+ if (of_property_read_bool(dev->of_node,
+ "qcom,mi2s-audio-intf")) {
+ memcpy(msm_tavil_dai_links + total_links,
+ msm_mi2s_be_dai_links,
+ sizeof(msm_mi2s_be_dai_links));
+ total_links += ARRAY_SIZE(msm_mi2s_be_dai_links);
+ }
+ if (of_property_read_bool(dev->of_node,
+ "qcom,auxpcm-audio-intf")) {
+ memcpy(msm_tavil_dai_links + total_links,
+ msm_auxpcm_be_dai_links,
+ sizeof(msm_auxpcm_be_dai_links));
+ total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+ }
dailink = msm_tavil_dai_links;
} else if (!strcmp(match->data, "stub_codec")) {
card = &snd_soc_card_stub_msm;
@@ -4147,6 +6233,68 @@ err_dt:
return ret;
}
+static void i2s_auxpcm_init(struct platform_device *pdev)
+{
+ struct resource *muxsel;
+ int count;
+ u32 mi2s_master_slave[MI2S_MAX];
+ int ret;
+ char *str[PCM_I2S_SEL_MAX] = {
+ "lpaif_pri_mode_muxsel",
+ "lpaif_sec_mode_muxsel",
+ "lpaif_tert_mode_muxsel",
+ "lpaif_quat_mode_muxsel"
+ };
+
+ for (count = 0; count < MI2S_MAX; count++) {
+ mutex_init(&mi2s_intf_conf[count].lock);
+ mi2s_intf_conf[count].ref_cnt = 0;
+ }
+
+ for (count = 0; count < AUX_PCM_MAX; count++) {
+ mutex_init(&auxpcm_intf_conf[count].lock);
+ auxpcm_intf_conf[count].ref_cnt = 0;
+ }
+
+ for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+ mutex_init(&mi2s_auxpcm_conf[count].lock);
+ mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL;
+ }
+
+ for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+ muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ str[count]);
+ if (muxsel) {
+ mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr
+ = ioremap(muxsel->start, resource_size(muxsel));
+ }
+ }
+
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-mi2s-master",
+ mi2s_master_slave, MI2S_MAX);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n",
+ __func__);
+ } else {
+ for (count = 0; count < MI2S_MAX; count++) {
+ mi2s_intf_conf[count].msm_is_mi2s_master =
+ mi2s_master_slave[count];
+ }
+ }
+}
+
+static void i2s_auxpcm_deinit(void)
+{
+ int count;
+
+ for (count = 0; count < PCM_I2S_SEL_MAX; count++)
+ if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr !=
+ NULL)
+ iounmap(
+ mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr);
+}
+
static int msm_asoc_machine_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
@@ -4306,6 +6454,16 @@ static int msm_asoc_machine_probe(struct platform_device *pdev)
if (ret)
dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n",
ret);
+
+ i2s_auxpcm_init(pdev);
+
+ is_initial_boot = true;
+ ret = audio_notifier_register("msmcobalt", AUDIO_NOTIFIER_ADSP_DOMAIN,
+ &service_nb);
+ if (ret < 0)
+ pr_err("%s: Audio notifier register failed ret = %d\n",
+ __func__, ret);
+
return 0;
err:
if (pdata->us_euro_gpio > 0) {
@@ -4339,6 +6497,7 @@ static int msm_asoc_machine_remove(struct platform_device *pdev)
gpio_free(pdata->us_euro_gpio);
gpio_free(pdata->hph_en1_gpio);
gpio_free(pdata->hph_en0_gpio);
+ i2s_auxpcm_deinit();
snd_soc_unregister_card(card);
return 0;
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index 26528e6a2bb8..58a4de5af145 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -1024,6 +1024,7 @@ static int msm_compr_ioctl_shared(struct snd_pcm_substream *substream,
struct snd_dec_ddp *ddp =
&compr->info.codec_param.codec.options.ddp;
uint32_t params_length = 0;
+ memset(params_value, 0, MAX_AC3_PARAM_SIZE);
/* check integer overflow */
if (ddp->params_length > UINT_MAX/sizeof(int)) {
pr_err("%s: Integer overflow ddp->params_length %d\n",
@@ -1064,6 +1065,7 @@ static int msm_compr_ioctl_shared(struct snd_pcm_substream *substream,
struct snd_dec_ddp *ddp =
&compr->info.codec_param.codec.options.ddp;
uint32_t params_length = 0;
+ memset(params_value, 0, MAX_AC3_PARAM_SIZE);
/* check integer overflow */
if (ddp->params_length > UINT_MAX/sizeof(int)) {
pr_err("%s: Integer overflow ddp->params_length %d\n",
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 70ed8dd3e038..770bd12eb501 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -709,6 +709,10 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream,
}
switch (prtd->codec_param.codec.format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bit_width = 32;
+ sample_word_size = 32;
+ break;
case SNDRV_PCM_FORMAT_S24_LE:
bit_width = 24;
sample_word_size = 32;
@@ -723,14 +727,16 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream,
sample_word_size = 16;
break;
}
- ret = q6asm_media_format_block_pcm_format_support_v3(
+ ret = q6asm_media_format_block_pcm_format_support_v4(
prtd->audio_client,
prtd->sample_rate,
prtd->num_channels,
bit_width, stream_id,
use_default_chmap,
chmap,
- sample_word_size);
+ sample_word_size,
+ ASM_LITTLE_ENDIAN,
+ DEFAULT_QF);
if (ret < 0)
pr_err("%s: CMD Format block failed\n", __func__);
@@ -1010,7 +1016,7 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream)
} else {
pr_debug("%s: stream_id %d bits_per_sample %d\n",
__func__, ac->stream_id, bits_per_sample);
- ret = q6asm_stream_open_write_v3(ac,
+ ret = q6asm_stream_open_write_v4(ac,
prtd->codec, bits_per_sample,
ac->stream_id,
prtd->gapless_state.use_dsp_gapless_mode);
@@ -1762,7 +1768,12 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
prtd->app_pointer = 0;
prtd->first_buffer = 1;
prtd->last_buffer = 0;
- prtd->gapless_state.gapless_transition = 1;
+ /*
+ * Set gapless transition flag only if EOS hasn't been
+ * acknowledged already.
+ */
+ if (atomic_read(&prtd->eos))
+ prtd->gapless_state.gapless_transition = 1;
prtd->marker_timestamp = 0;
/*
@@ -1937,7 +1948,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
pr_debug("%s: open_write stream_id %d bits_per_sample %d",
__func__, stream_id, bits_per_sample);
- rc = q6asm_stream_open_write_v3(prtd->audio_client,
+ rc = q6asm_stream_open_write_v4(prtd->audio_client,
prtd->codec, bits_per_sample,
stream_id,
prtd->gapless_state.use_dsp_gapless_mode);
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 5fa3b6fb0885..7eb4a10b83c7 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -30,6 +30,9 @@
#define MSM_DAI_PRI_AUXPCM_DT_DEV_ID 1
#define MSM_DAI_SEC_AUXPCM_DT_DEV_ID 2
+#define MSM_DAI_TERT_AUXPCM_DT_DEV_ID 3
+#define MSM_DAI_QUAT_AUXPCM_DT_DEV_ID 4
+
#define spdif_clock_value(rate) (2*rate*32*2)
#define CHANNEL_STATUS_SIZE 24
@@ -170,6 +173,7 @@ struct msm_dai_q6_dai_data {
u32 bitwidth;
u32 cal_mode;
u32 afe_in_channels;
+ u16 afe_in_bitformat;
struct afe_enc_config enc_config;
union afe_port_config port_config;
};
@@ -873,6 +877,22 @@ static int msm_dai_q6_auxpcm_prepare(struct snd_pcm_substream *substream,
aux_dai_data->clk_set.clk_id =
Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT;
break;
+ case MSM_DAI_TERT_AUXPCM_DT_DEV_ID:
+ if (pcm_clk_rate)
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT;
+ else
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT;
+ break;
+ case MSM_DAI_QUAT_AUXPCM_DT_DEV_ID:
+ if (pcm_clk_rate)
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT;
+ else
+ aux_dai_data->clk_set.clk_id =
+ Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT;
+ break;
default:
dev_err(dai->dev, "%s: AUXPCM id: %d not supported\n",
__func__, dai->id);
@@ -1058,7 +1078,59 @@ static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_dai[] = {
.ops = &msm_dai_q6_auxpcm_ops,
.probe = msm_dai_q6_aux_pcm_probe,
.remove = msm_dai_q6_dai_auxpcm_remove,
- }
+ },
+ {
+ .playback = {
+ .stream_name = "Tert AUX PCM Playback",
+ .aif_name = "TERT_AUX_PCM_RX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .capture = {
+ .stream_name = "Tert AUX PCM Capture",
+ .aif_name = "TERT_AUX_PCM_TX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .id = MSM_DAI_TERT_AUXPCM_DT_DEV_ID,
+ .ops = &msm_dai_q6_auxpcm_ops,
+ .probe = msm_dai_q6_aux_pcm_probe,
+ .remove = msm_dai_q6_dai_auxpcm_remove,
+ },
+ {
+ .playback = {
+ .stream_name = "Quat AUX PCM Playback",
+ .aif_name = "QUAT_AUX_PCM_RX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .capture = {
+ .stream_name = "Quat AUX PCM Capture",
+ .aif_name = "QUAT_AUX_PCM_TX",
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID,
+ .ops = &msm_dai_q6_auxpcm_ops,
+ .probe = msm_dai_q6_aux_pcm_probe,
+ .remove = msm_dai_q6_dai_auxpcm_remove,
+ },
};
static int msm_dai_q6_spdif_format_put(struct snd_kcontrol *kcontrol,
@@ -1346,11 +1418,20 @@ static int msm_dai_q6_prepare(struct snd_pcm_substream *substream,
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
if (dai_data->enc_config.format != ENC_FMT_NONE) {
+ int bitwidth = 0;
+
+ if (dai_data->afe_in_bitformat ==
+ SNDRV_PCM_FORMAT_S24_LE)
+ bitwidth = 24;
+ else if (dai_data->afe_in_bitformat ==
+ SNDRV_PCM_FORMAT_S16_LE)
+ bitwidth = 16;
pr_debug("%s: calling AFE_PORT_START_V2 with enc_format: %d\n",
__func__, dai_data->enc_config.format);
rc = afe_port_start_v2(dai->id, &dai_data->port_config,
dai_data->rate,
dai_data->afe_in_channels,
+ bitwidth,
&dai_data->enc_config);
if (rc < 0)
pr_err("%s: afe_port_start_v2 failed error: %d\n",
@@ -1536,8 +1617,13 @@ static int msm_dai_q6_usb_audio_hw_params(struct snd_pcm_hw_params *params,
dai_data->port_config.usb_audio.bit_width = 16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
dai_data->port_config.usb_audio.bit_width = 24;
break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dai_data->port_config.usb_audio.bit_width = 32;
+ break;
+
default:
dev_err(dai->dev, "%s: invalid format %d\n",
__func__, params_format(params));
@@ -2069,6 +2155,12 @@ static const struct soc_enum afe_input_chs_enum[] = {
SOC_ENUM_SINGLE_EXT(3, afe_input_chs_text),
};
+static const char *const afe_input_bit_format_text[] = {"S16_LE", "S24_LE"};
+
+static const struct soc_enum afe_input_bit_format_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, afe_input_bit_format_text),
+};
+
static int msm_dai_q6_afe_input_channel_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -2097,6 +2189,58 @@ static int msm_dai_q6_afe_input_channel_put(struct snd_kcontrol *kcontrol,
return 0;
}
+static int msm_dai_q6_afe_input_bit_format_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (!dai_data) {
+ pr_err("%s: Invalid dai data\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (dai_data->afe_in_bitformat) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: afe input bit format : %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_dai_q6_afe_input_bit_format_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+ if (!dai_data) {
+ pr_err("%s: Invalid dai data\n", __func__);
+ return -EINVAL;
+ }
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: updating afe input bit format : %d\n",
+ __func__, dai_data->afe_in_bitformat);
+
+ return 0;
+}
+
+
static const struct snd_kcontrol_new afe_enc_config_controls[] = {
{
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -2110,6 +2254,9 @@ static const struct snd_kcontrol_new afe_enc_config_controls[] = {
SOC_ENUM_EXT("AFE Input Channels", afe_input_chs_enum[0],
msm_dai_q6_afe_input_channel_get,
msm_dai_q6_afe_input_channel_put),
+ SOC_ENUM_EXT("AFE Input Bit Format", afe_input_bit_format_enum[0],
+ msm_dai_q6_afe_input_bit_format_get,
+ msm_dai_q6_afe_input_bit_format_put),
};
static const char * const afe_cal_mode_text[] = {
@@ -2499,11 +2646,12 @@ static struct snd_soc_dai_driver msm_dai_q6_usb_rx_dai = {
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 1,
.channels_max = 8,
- .rate_max = 192000,
+ .rate_max = 384000,
.rate_min = 8000,
},
.ops = &msm_dai_q6_ops,
@@ -2520,11 +2668,12 @@ static struct snd_soc_dai_driver msm_dai_q6_usb_tx_dai = {
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 1,
.channels_max = 8,
- .rate_max = 192000,
+ .rate_max = 384000,
.rate_min = 8000,
},
.ops = &msm_dai_q6_ops,
@@ -2723,16 +2872,26 @@ static int msm_auxpcm_dev_probe(struct platform_device *pdev)
goto fail_nodev_intf;
}
- if (!strncmp(intf_name, "primary", sizeof("primary"))) {
+ if (!strcmp(intf_name, "primary")) {
dai_data->rx_pid = AFE_PORT_ID_PRIMARY_PCM_RX;
dai_data->tx_pid = AFE_PORT_ID_PRIMARY_PCM_TX;
pdev->id = MSM_DAI_PRI_AUXPCM_DT_DEV_ID;
i = 0;
- } else if (!strncmp(intf_name, "secondary", sizeof("secondary"))) {
+ } else if (!strcmp(intf_name, "secondary")) {
dai_data->rx_pid = AFE_PORT_ID_SECONDARY_PCM_RX;
dai_data->tx_pid = AFE_PORT_ID_SECONDARY_PCM_TX;
pdev->id = MSM_DAI_SEC_AUXPCM_DT_DEV_ID;
i = 1;
+ } else if (!strcmp(intf_name, "tertiary")) {
+ dai_data->rx_pid = AFE_PORT_ID_TERTIARY_PCM_RX;
+ dai_data->tx_pid = AFE_PORT_ID_TERTIARY_PCM_TX;
+ pdev->id = MSM_DAI_TERT_AUXPCM_DT_DEV_ID;
+ i = 2;
+ } else if (!strcmp(intf_name, "quaternary")) {
+ dai_data->rx_pid = AFE_PORT_ID_QUATERNARY_PCM_RX;
+ dai_data->tx_pid = AFE_PORT_ID_QUATERNARY_PCM_TX;
+ pdev->id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID;
+ i = 3;
} else {
dev_err(&pdev->dev, "%s: invalid DT intf name %s\n",
__func__, intf_name);
@@ -3639,22 +3798,28 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
.playback = {
.stream_name = "Primary MI2S Playback",
.aif_name = "PRI_MI2S_RX",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S24_3LE,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.capture = {
.stream_name = "Primary MI2S Capture",
.aif_name = "PRI_MI2S_TX",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.ops = &msm_dai_q6_mi2s_ops,
.id = MSM_PRIM_MI2S,
@@ -3665,9 +3830,11 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
.playback = {
.stream_name = "Secondary MI2S Playback",
.aif_name = "SEC_MI2S_RX",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_min = 8000,
.rate_max = 192000,
@@ -3675,11 +3842,14 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
.capture = {
.stream_name = "Secondary MI2S Capture",
.aif_name = "SEC_MI2S_TX",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.ops = &msm_dai_q6_mi2s_ops,
.id = MSM_SEC_MI2S,
@@ -3690,20 +3860,26 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
.playback = {
.stream_name = "Tertiary MI2S Playback",
.aif_name = "TERT_MI2S_RX",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.capture = {
.stream_name = "Tertiary MI2S Capture",
.aif_name = "TERT_MI2S_TX",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.ops = &msm_dai_q6_mi2s_ops,
.id = MSM_TERT_MI2S,
@@ -3714,9 +3890,11 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
.playback = {
.stream_name = "Quaternary MI2S Playback",
.aif_name = "QUAT_MI2S_RX",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_min = 8000,
.rate_max = 192000,
@@ -3724,11 +3902,14 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
.capture = {
.stream_name = "Quaternary MI2S Capture",
.aif_name = "QUAT_MI2S_TX",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.ops = &msm_dai_q6_mi2s_ops,
.id = MSM_QUAT_MI2S,
@@ -5722,11 +5903,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/msm-pcm-q6-noirq.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
index 972cacb50f47..1fdb878a1a1f 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
@@ -395,6 +395,30 @@ static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
+static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
+ struct audio_buffer *buf;
+
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL1_RESET:
+ pr_debug("%s: %s SNDRV_PCM_IOCTL1_RESET\n", __func__,
+ dir == 0 ? "P" : "C");
+ buf = q6asm_shared_io_buf(prtd->audio_client, dir);
+
+ if (buf && buf->data)
+ memset(buf->data, 0, buf->actual_size);
+ break;
+ default:
+ break;
+ }
+
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -728,7 +752,7 @@ static struct snd_pcm_ops msm_pcm_ops = {
.prepare = msm_pcm_prepare,
.copy = msm_pcm_copy,
.hw_params = msm_pcm_hw_params,
- .ioctl = snd_pcm_lib_ioctl,
+ .ioctl = msm_pcm_ioctl,
.trigger = msm_pcm_trigger,
.pointer = msm_pcm_pointer,
.mmap = msm_pcm_mmap,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index 4e3745d4d976..c5baf0e63732 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -57,6 +57,7 @@ struct snd_msm {
#define CMD_EOS_MIN_TIMEOUT_LENGTH 50
#define CMD_EOS_TIMEOUT_MULTIPLIER (HZ * 50)
+#define MAX_PB_COPY_RETRIES 3
static struct snd_pcm_hardware msm_pcm_hardware_capture = {
.info = (SNDRV_PCM_INFO_MMAP |
@@ -66,10 +67,11 @@ static struct snd_pcm_hardware msm_pcm_hardware_capture = {
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
- .rates = SNDRV_PCM_RATE_8000_48000,
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = SNDRV_PCM_RATE_8000_384000,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 384000,
.channels_min = 1,
.channels_max = 4,
.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS *
@@ -89,10 +91,11 @@ static struct snd_pcm_hardware msm_pcm_hardware_playback = {
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S24_3LE),
- .rates = SNDRV_PCM_RATE_8000_192000,
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .rates = SNDRV_PCM_RATE_8000_384000,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
.channels_min = 1,
.channels_max = 8,
.buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS *
@@ -107,7 +110,7 @@ static struct snd_pcm_hardware msm_pcm_hardware_playback = {
/* Conventional and unconventional sample rate supported */
static unsigned int supported_sample_rates[] = {
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
- 88200, 96000, 176400, 192000
+ 88200, 96000, 176400, 192000, 384000
};
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
@@ -312,6 +315,10 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
pr_debug("%s: perf: %x\n", __func__, pdata->perf_mode);
switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bits_per_sample = 32;
+ sample_word_size = 32;
+ break;
case SNDRV_PCM_FORMAT_S24_LE:
bits_per_sample = 24;
sample_word_size = 32;
@@ -327,7 +334,7 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
break;
}
- ret = q6asm_open_write_v3(prtd->audio_client,
+ ret = q6asm_open_write_v4(prtd->audio_client,
FORMAT_LINEAR_PCM, bits_per_sample);
if (ret < 0) {
@@ -352,11 +359,12 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
return ret;
}
- ret = q6asm_media_format_block_multi_ch_pcm_v3(
+ ret = q6asm_media_format_block_multi_ch_pcm_v4(
prtd->audio_client, runtime->rate,
runtime->channels, !prtd->set_channel_map,
prtd->channel_map, bits_per_sample,
- sample_word_size);
+ sample_word_size, ASM_LITTLE_ENDIAN,
+ DEFAULT_QF);
if (ret < 0)
pr_info("%s: CMD Format block failed\n", __func__);
@@ -401,6 +409,8 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
if ((params_format(params) == SNDRV_PCM_FORMAT_S24_LE) ||
(params_format(params) == SNDRV_PCM_FORMAT_S24_3LE))
bits_per_sample = 24;
+ else if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE)
+ bits_per_sample = 32;
/* ULL mode is not supported in capture path */
if (pdata->perf_mode == LEGACY_PCM_MODE)
@@ -412,7 +422,7 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
__func__, params_channels(params),
prtd->audio_client->perf_mode);
- ret = q6asm_open_read_v3(prtd->audio_client, FORMAT_LINEAR_PCM,
+ ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM,
bits_per_sample);
if (ret < 0) {
pr_err("%s: q6asm_open_read failed\n", __func__);
@@ -458,6 +468,10 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
return 0;
switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bits_per_sample = 32;
+ sample_word_size = 32;
+ break;
case SNDRV_PCM_FORMAT_S24_LE:
bits_per_sample = 24;
sample_word_size = 32;
@@ -476,11 +490,13 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
pr_debug("%s: Samp_rate = %d Channel = %d bit width = %d, word size = %d\n",
__func__, prtd->samp_rate, prtd->channel_mode,
bits_per_sample, sample_word_size);
- ret = q6asm_enc_cfg_blk_pcm_format_support_v3(prtd->audio_client,
+ ret = q6asm_enc_cfg_blk_pcm_format_support_v4(prtd->audio_client,
prtd->samp_rate,
prtd->channel_mode,
bits_per_sample,
- sample_word_size);
+ sample_word_size,
+ ASM_LITTLE_ENDIAN,
+ DEFAULT_QF);
if (ret < 0)
pr_debug("%s: cmd cfg pcm was block failed", __func__);
@@ -629,6 +645,7 @@ static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
void *data = NULL;
uint32_t idx = 0;
uint32_t size = 0;
+ uint32_t retries = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
@@ -637,7 +654,7 @@ static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
pr_debug("%s: prtd->out_count = %d\n",
__func__, atomic_read(&prtd->out_count));
- while (fbytes > 0) {
+ while ((fbytes > 0) && (retries < MAX_PB_COPY_RETRIES)) {
if (prtd->reset_event) {
pr_err("%s: In SSR return ENETRESET before wait\n",
__func__);
@@ -666,6 +683,13 @@ static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size,
&idx);
+ if (data == NULL) {
+ retries++;
+ continue;
+ } else {
+ retries = 0;
+ }
+
if (fbytes > size)
xfer = size;
else
@@ -677,6 +701,9 @@ static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
__func__, fbytes, xfer, size);
if (copy_from_user(bufptr, buf, xfer)) {
ret = -EFAULT;
+ pr_err("%s: copy_from_user failed\n",
+ __func__);
+ q6asm_cpu_buf_release(IN, prtd->audio_client);
goto fail;
}
buf += xfer;
@@ -690,6 +717,8 @@ static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
0, 0, NO_TIMESTAMP);
if (ret < 0) {
ret = -EFAULT;
+ q6asm_cpu_buf_release(IN,
+ prtd->audio_client);
goto fail;
}
} else
@@ -698,6 +727,9 @@ static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
}
}
fail:
+ if (retries >= MAX_PB_COPY_RETRIES)
+ ret = -ENOMEM;
+
return ret;
}
@@ -802,6 +834,7 @@ static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
if (copy_to_user(buf, bufptr+offset, xfer)) {
pr_err("Failed to copy buf to user\n");
ret = -EFAULT;
+ q6asm_cpu_buf_release(OUT, prtd->audio_client);
goto fail;
}
fbytes -= xfer;
@@ -817,6 +850,7 @@ static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
if (ret < 0) {
pr_err("q6asm read failed\n");
ret = -EFAULT;
+ q6asm_cpu_buf_release(OUT, prtd->audio_client);
goto fail;
}
} else
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
index 72418ea56bb9..8fe31394eef0 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
@@ -59,11 +59,11 @@ struct msm_audio_in_frame_info {
#define PLAYBACK_MIN_NUM_PERIODS 2
#define PLAYBACK_MAX_NUM_PERIODS 8
-#define PLAYBACK_MAX_PERIOD_SIZE 12288
+#define PLAYBACK_MAX_PERIOD_SIZE 122880
#define PLAYBACK_MIN_PERIOD_SIZE 128
#define CAPTURE_MIN_NUM_PERIODS 2
#define CAPTURE_MAX_NUM_PERIODS 8
-#define CAPTURE_MAX_PERIOD_SIZE 61440
+#define CAPTURE_MAX_PERIOD_SIZE 122880
#define CAPTURE_MIN_PERIOD_SIZE 320
struct msm_audio {
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index aea60f1fa044..5c40c55a4a0c 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -177,7 +177,10 @@ static void msm_pcm_routing_cfg_pp(int port_id, int copp_idx, int topology,
break;
case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE:
pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__);
- msm_qti_pp_asphere_init(port_id, copp_idx);
+ rc = msm_qti_pp_asphere_init(port_id, copp_idx);
+ if (rc < 0)
+ pr_err("%s: topo_id 0x%x, port %d, copp %d, rc %d\n",
+ __func__, topology, port_id, copp_idx, rc);
break;
default:
/* custom topology specific feature param handlers */
@@ -223,22 +226,27 @@ static void msm_pcm_routing_deinit_pp(int port_id, int topology)
static void msm_pcm_routng_cfg_matrix_map_pp(struct route_payload payload,
int path_type, int perf_mode)
{
- int itr = 0;
+ int itr = 0, rc = 0;
if ((path_type == ADM_PATH_PLAYBACK) &&
(perf_mode == LEGACY_PCM_MODE) &&
is_custom_stereo_on) {
for (itr = 0; itr < payload.num_copps; itr++) {
- if ((payload.port_id[itr] == SLIMBUS_0_RX) ||
- (payload.port_id[itr] == RT_PROXY_PORT_001_RX)) {
- msm_qti_pp_send_stereo_to_custom_stereo_cmd(
- payload.port_id[itr],
- payload.copp_idx[itr],
- payload.session_id,
- Q14_GAIN_ZERO_POINT_FIVE,
- Q14_GAIN_ZERO_POINT_FIVE,
- Q14_GAIN_ZERO_POINT_FIVE,
- Q14_GAIN_ZERO_POINT_FIVE);
+ if ((payload.port_id[itr] != SLIMBUS_0_RX) &&
+ (payload.port_id[itr] != RT_PROXY_PORT_001_RX)) {
+ continue;
}
+
+ rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd(
+ payload.port_id[itr],
+ payload.copp_idx[itr],
+ payload.session_id,
+ Q14_GAIN_ZERO_POINT_FIVE,
+ Q14_GAIN_ZERO_POINT_FIVE,
+ Q14_GAIN_ZERO_POINT_FIVE,
+ Q14_GAIN_ZERO_POINT_FIVE);
+ if (rc < 0)
+ pr_err("%s: err setting custom stereo\n",
+ __func__);
}
}
}
@@ -450,6 +458,14 @@ struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
{ AFE_PORT_ID_USB_RX, 0, 0, 0, 0, 0, 0, 0, 0, LPASS_BE_USB_AUDIO_RX},
{ AFE_PORT_ID_USB_TX, 0, 0, 0, 0, 0, 0, 0, 0, LPASS_BE_USB_AUDIO_TX},
{ DISPLAY_PORT_RX, 0, 0, 0, 0, 0, 0, 0, 0, LPASS_BE_DISPLAY_PORT},
+ { AFE_PORT_ID_TERTIARY_PCM_RX, 0, 0, 0, 0, 0, 0, 0, 0,
+ LPASS_BE_TERT_AUXPCM_RX},
+ { AFE_PORT_ID_TERTIARY_PCM_TX, 0, 0, 0, 0, 0, 0, 0, 0,
+ LPASS_BE_TERT_AUXPCM_TX},
+ { AFE_PORT_ID_QUATERNARY_PCM_RX, 0, 0, 0, 0, 0, 0, 0, 0,
+ LPASS_BE_QUAT_AUXPCM_RX},
+ { AFE_PORT_ID_QUATERNARY_PCM_TX, 0, 0, 0, 0, 0, 0, 0, 0,
+ LPASS_BE_QUAT_AUXPCM_TX},
};
/* Track ASM playback & capture sessions of DAI */
@@ -3491,6 +3507,108 @@ static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
msm_routing_put_audio_mixer),
};
+static const struct snd_kcontrol_new tert_auxpcm_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_0 ,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
@@ -3927,6 +4045,12 @@ static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
@@ -4065,6 +4189,12 @@ static const struct snd_kcontrol_new mmul3_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
@@ -4179,6 +4309,12 @@ static const struct snd_kcontrol_new mmul5_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
@@ -4251,6 +4387,12 @@ static const struct snd_kcontrol_new mmul6_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0,
MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
@@ -4874,6 +5016,72 @@ static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = {
msm_routing_put_voice_mixer),
};
+static const struct snd_kcontrol_new tert_aux_pcm_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new quat_aux_pcm_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_HDMI_RX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
@@ -5049,6 +5257,12 @@ static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("PRI_MI2S_TX_Voice", MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -5091,6 +5305,12 @@ static const struct snd_kcontrol_new tx_voice2_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("PRI_MI2S_TX_Voice2", MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -5127,6 +5347,12 @@ static const struct snd_kcontrol_new tx_volte_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_VoLTE", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -5166,6 +5392,12 @@ static const struct snd_kcontrol_new tx_vowlan_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_VoWLAN", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -5208,6 +5440,12 @@ static const struct snd_kcontrol_new tx_voicemmode1_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_TX_MMode1",
MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_TX_MMode1",
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_MMode1",
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("PRI_MI2S_TX_MMode1",
MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0,
msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
@@ -5247,6 +5485,12 @@ static const struct snd_kcontrol_new tx_voicemmode2_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_TX_MMode2",
MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_TX_MMode2",
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_MMode2",
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+ 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("PRI_MI2S_TX_MMode2",
MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0,
msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
@@ -5286,6 +5530,12 @@ static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("PRI_MI2S_TX_Voip", MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -5334,6 +5584,12 @@ static const struct snd_kcontrol_new tx_voice_stub_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
@@ -5364,6 +5620,15 @@ static const struct snd_kcontrol_new tx_voice2_stub_mixer_controls[] = {
SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
@@ -5400,6 +5665,15 @@ static const struct snd_kcontrol_new tx_volte_stub_mixer_controls[] = {
SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+ msm_routing_put_voice_stub_mixer),
SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
@@ -5442,6 +5716,12 @@ static const struct snd_kcontrol_new tx_qchat_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+ MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("MI2S_TX_QCHAT", MSM_BACKEND_DAI_MI2S_TX,
MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -5484,6 +5764,12 @@ static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
@@ -5540,6 +5826,30 @@ static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = {
msm_routing_put_port_mixer),
};
+static const struct snd_kcontrol_new tert_auxpcm_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("TERT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_auxpcm_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
static const struct snd_kcontrol_new sbus_1_rx_port_mixer_controls[] = {
SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
@@ -5553,6 +5863,12 @@ static const struct snd_kcontrol_new sbus_1_rx_port_mixer_controls[] = {
SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("TERT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("QUAT_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new sbus_3_rx_port_mixer_controls[] = {
@@ -7689,6 +8005,14 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SEC_AUX_PCM_TX", "Sec AUX PCM Capture",
0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_AUX_PCM_RX", "Tert AUX PCM Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_AUX_PCM_TX", "Tert AUX PCM Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("QUAT_AUX_PCM_RX", "Quat AUX PCM Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("QUAT_AUX_PCM_TX", "Quat AUX PCM Capture",
+ 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VOICE_STUB_DL", "VOICE_STUB Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("VOICE_STUB_UL", "VOICE_STUB Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VOICE2_STUB_DL", "VOICE2_STUB Playback",
@@ -7840,6 +8164,12 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
sec_auxpcm_rx_mixer_controls, ARRAY_SIZE(sec_auxpcm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tert_auxpcm_rx_mixer_controls,
+ ARRAY_SIZE(tert_auxpcm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ quat_auxpcm_rx_mixer_controls,
+ ARRAY_SIZE(quat_auxpcm_rx_mixer_controls)),
/* incall */
SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0,
incall_music_delivery_mixer_controls,
@@ -7888,6 +8218,14 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
SND_SOC_NOPM, 0, 0,
sec_aux_pcm_rx_voice_mixer_controls,
ARRAY_SIZE(sec_aux_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_AUX_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ tert_aux_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(tert_aux_pcm_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_AUX_PCM_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ quat_aux_pcm_rx_voice_mixer_controls,
+ ARRAY_SIZE(quat_aux_pcm_rx_voice_mixer_controls)),
SND_SOC_DAPM_MIXER("HDMI_RX_Voice Mixer",
SND_SOC_NOPM, 0, 0,
hdmi_rx_voice_mixer_controls,
@@ -7975,6 +8313,12 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
SND_SOC_DAPM_MIXER("SEC_AUXPCM_RX Port Mixer",
SND_SOC_NOPM, 0, 0, sec_auxpcm_rx_port_mixer_controls,
ARRAY_SIZE(sec_auxpcm_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_AUXPCM_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, tert_auxpcm_rx_port_mixer_controls,
+ ARRAY_SIZE(tert_auxpcm_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_AUXPCM_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, quat_auxpcm_rx_port_mixer_controls,
+ ARRAY_SIZE(quat_auxpcm_rx_port_mixer_controls)),
SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Port Mixer", SND_SOC_NOPM, 0, 0,
sbus_1_rx_port_mixer_controls,
ARRAY_SIZE(sbus_1_rx_port_mixer_controls)),
@@ -8556,7 +8900,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
{"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Audio Mixer"},
- {"TERT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
{"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
@@ -8564,6 +8907,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"MultiMedia2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"MultiMedia1 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"},
{"MultiMedia2 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"},
{"MultiMedia1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
@@ -8575,6 +8919,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia1 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
{"MultiMedia3 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
{"MultiMedia5 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
+ {"MultiMedia1 Mixer", "TERT_AUX_PCM_UL_TX", "TERT_AUX_PCM_TX"},
+ {"MultiMedia3 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"},
+ {"MultiMedia5 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"},
+ {"MultiMedia1 Mixer", "QUAT_AUX_PCM_UL_TX", "QUAT_AUX_PCM_TX"},
+ {"MultiMedia3 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"},
+ {"MultiMedia5 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"},
{"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia2 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"MultiMedia1 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
@@ -8772,7 +9122,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
- {"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_UL6"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
@@ -8791,9 +9140,44 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
- {"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_UL6"},
{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX Audio Mixer"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"TERT_AUX_PCM_RX", NULL, "TERT_AUX_PCM_RX Audio Mixer"},
+
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+ {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+ {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX Audio Mixer"},
+
{"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
@@ -8925,6 +9309,30 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"TERT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"TERT_AUX_PCM_RX", NULL, "TERT_AUX_PCM_RX_Voice Mixer"},
+
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+ {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX_Voice Mixer"},
+
{"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"HDMI_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"HDMI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
@@ -9076,6 +9484,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Voice_Tx Mixer", "AFE_PCM_TX_Voice", "PCM_TX"},
{"Voice_Tx Mixer", "AUX_PCM_TX_Voice", "AUX_PCM_TX"},
{"Voice_Tx Mixer", "SEC_AUX_PCM_TX_Voice", "SEC_AUX_PCM_TX"},
+ {"Voice_Tx Mixer", "TERT_AUX_PCM_TX_Voice", "TERT_AUX_PCM_TX"},
+ {"Voice_Tx Mixer", "QUAT_AUX_PCM_TX_Voice", "QUAT_AUX_PCM_TX"},
{"Voice_Tx Mixer", "SEC_MI2S_TX_Voice", "SEC_MI2S_TX"},
{"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
@@ -9091,6 +9501,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Voice2_Tx Mixer", "AFE_PCM_TX_Voice2", "PCM_TX"},
{"Voice2_Tx Mixer", "AUX_PCM_TX_Voice2", "AUX_PCM_TX"},
{"Voice2_Tx Mixer", "SEC_AUX_PCM_TX_Voice2", "SEC_AUX_PCM_TX"},
+ {"Voice2_Tx Mixer", "TERT_AUX_PCM_TX_Voice2", "TERT_AUX_PCM_TX"},
+ {"Voice2_Tx Mixer", "QUAT_AUX_PCM_TX_Voice2", "QUAT_AUX_PCM_TX"},
{"VOICE2_UL", NULL, "Voice2_Tx Mixer"},
{"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"},
@@ -9102,6 +9514,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"VoLTE_Tx Mixer", "AFE_PCM_TX_VoLTE", "PCM_TX"},
{"VoLTE_Tx Mixer", "AUX_PCM_TX_VoLTE", "AUX_PCM_TX"},
{"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
+ {"VoLTE_Tx Mixer", "TERT_AUX_PCM_TX_VoLTE", "TERT_AUX_PCM_TX"},
+ {"VoLTE_Tx Mixer", "QUAT_AUX_PCM_TX_VoLTE", "QUAT_AUX_PCM_TX"},
{"VoLTE_Tx Mixer", "MI2S_TX_VoLTE", "MI2S_TX"},
{"VoLTE_Tx Mixer", "PRI_MI2S_TX_VoLTE", "PRI_MI2S_TX"},
{"VoLTE_Tx Mixer", "TERT_MI2S_TX_VoLTE", "TERT_MI2S_TX"},
@@ -9116,6 +9530,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"VoWLAN_Tx Mixer", "AFE_PCM_TX_VoWLAN", "PCM_TX"},
{"VoWLAN_Tx Mixer", "AUX_PCM_TX_VoWLAN", "AUX_PCM_TX"},
{"VoWLAN_Tx Mixer", "SEC_AUX_PCM_TX_VoWLAN", "SEC_AUX_PCM_TX"},
+ {"VoWLAN_Tx Mixer", "TERT_AUX_PCM_TX_VoWLAN", "TERT_AUX_PCM_TX"},
+ {"VoWLAN_Tx Mixer", "QUAT_AUX_PCM_TX_VoWLAN", "QUAT_AUX_PCM_TX"},
{"VoWLAN_Tx Mixer", "MI2S_TX_VoWLAN", "MI2S_TX"},
{"VoWLAN_Tx Mixer", "PRI_MI2S_TX_VoWLAN", "PRI_MI2S_TX"},
{"VoWLAN_Tx Mixer", "TERT_MI2S_TX_VoWLAN", "TERT_MI2S_TX"},
@@ -9133,6 +9549,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"VoiceMMode1_Tx Mixer", "AFE_PCM_TX_MMode1", "PCM_TX"},
{"VoiceMMode1_Tx Mixer", "AUX_PCM_TX_MMode1", "AUX_PCM_TX"},
{"VoiceMMode1_Tx Mixer", "SEC_AUX_PCM_TX_MMode1", "SEC_AUX_PCM_TX"},
+ {"VoiceMMode1_Tx Mixer", "TERT_AUX_PCM_TX_MMode1", "TERT_AUX_PCM_TX"},
+ {"VoiceMMode1_Tx Mixer", "QUAT_AUX_PCM_TX_MMode1", "QUAT_AUX_PCM_TX"},
{"VOICEMMODE1_UL", NULL, "VoiceMMode1_Tx Mixer"},
{"VoiceMMode2_Tx Mixer", "PRI_TX_MMode2", "PRI_I2S_TX"},
@@ -9147,6 +9565,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"VoiceMMode2_Tx Mixer", "AFE_PCM_TX_MMode2", "PCM_TX"},
{"VoiceMMode2_Tx Mixer", "AUX_PCM_TX_MMode2", "AUX_PCM_TX"},
{"VoiceMMode2_Tx Mixer", "SEC_AUX_PCM_TX_MMode2", "SEC_AUX_PCM_TX"},
+ {"VoiceMMode2_Tx Mixer", "TERT_AUX_PCM_TX_MMode2", "TERT_AUX_PCM_TX"},
+ {"VoiceMMode2_Tx Mixer", "QUAT_AUX_PCM_TX_MMode2", "QUAT_AUX_PCM_TX"},
{"VOICEMMODE2_UL", NULL, "VoiceMMode2_Tx Mixer"},
{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
@@ -9160,6 +9580,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Voip_Tx Mixer", "AFE_PCM_TX_Voip", "PCM_TX"},
{"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"},
{"Voip_Tx Mixer", "SEC_AUX_PCM_TX_Voip", "SEC_AUX_PCM_TX"},
+ {"Voip_Tx Mixer", "TERT_AUX_PCM_TX_Voip", "TERT_AUX_PCM_TX"},
+ {"Voip_Tx Mixer", "QUAT_AUX_PCM_TX_Voip", "QUAT_AUX_PCM_TX"},
{"Voip_Tx Mixer", "PRI_MI2S_TX_Voip", "PRI_MI2S_TX"},
{"VOIP_UL", NULL, "Voip_Tx Mixer"},
@@ -9254,6 +9676,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"QCHAT_Tx Mixer", "AFE_PCM_TX_QCHAT", "PCM_TX"},
{"QCHAT_Tx Mixer", "AUX_PCM_TX_QCHAT", "AUX_PCM_TX"},
{"QCHAT_Tx Mixer", "SEC_AUX_PCM_TX_QCHAT", "SEC_AUX_PCM_TX"},
+ {"QCHAT_Tx Mixer", "TERT_AUX_PCM_TX_QCHAT", "TERT_AUX_PCM_TX"},
+ {"QCHAT_Tx Mixer", "QUAT_AUX_PCM_TX_QCHAT", "QUAT_AUX_PCM_TX"},
{"QCHAT_Tx Mixer", "MI2S_TX_QCHAT", "MI2S_TX"},
{"QCHAT_Tx Mixer", "PRI_MI2S_TX_QCHAT", "PRI_MI2S_TX"},
{"QCHAT_Tx Mixer", "TERT_MI2S_TX_QCHAT", "TERT_MI2S_TX"},
@@ -9460,6 +9884,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SLIMBUS_0_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"SLIMBUS_0_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"SLIMBUS_0_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "TERT_AUX_PCM_UL_TX", "TERT_AUX_PCM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "QUAT_AUX_PCM_UL_TX", "QUAT_AUX_PCM_TX"},
{"SLIMBUS_0_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
{"SLIMBUS_0_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"SLIMBUS_0_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
@@ -9482,12 +9908,24 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SEC_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_RX Port Mixer"},
+ {"TERT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"TERT_AUXPCM_RX Port Mixer", "TERT_AUX_PCM_UL_TX", "TERT_AUX_PCM_TX"},
+ {"TERT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"TERT_AUX_PCM_RX", NULL, "TERT_AUXPCM_RX Port Mixer"},
+
+ {"QUAT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"QUAT_AUXPCM_RX Port Mixer", "QUAT_AUX_PCM_UL_TX", "QUAT_AUX_PCM_TX"},
+ {"QUAT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUXPCM_RX Port Mixer"},
+
{"Voice Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"},
{"Voice Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"Voice Stub Tx Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
{"Voice Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"},
{"Voice Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"Voice Stub Tx Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+ {"Voice Stub Tx Mixer", "TERT_AUX_PCM_UL_TX", "TERT_AUX_PCM_TX"},
+ {"Voice Stub Tx Mixer", "QUAT_AUX_PCM_UL_TX", "QUAT_AUX_PCM_TX"},
{"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"},
{"Voice Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"Voice Stub Tx Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
@@ -9675,12 +10113,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{"BE_OUT", NULL, "SLIMBUS_3_RX"},
{"BE_OUT", NULL, "AUX_PCM_RX"},
{"BE_OUT", NULL, "SEC_AUX_PCM_RX"},
+ {"BE_OUT", NULL, "TERT_AUX_PCM_RX"},
+ {"BE_OUT", NULL, "QUAT_AUX_PCM_RX"},
{"BE_OUT", NULL, "INT_BT_SCO_RX"},
{"BE_OUT", NULL, "INT_FM_RX"},
{"BE_OUT", NULL, "PCM_RX"},
{"BE_OUT", NULL, "SLIMBUS_3_RX"},
- {"BE_OUT", NULL, "AUX_PCM_RX"},
- {"BE_OUT", NULL, "SEC_AUX_PCM_RX"},
{"BE_OUT", NULL, "VOICE_PLAYBACK_TX"},
{"BE_OUT", NULL, "VOICE2_PLAYBACK_TX"},
{"BE_OUT", NULL, "TERT_TDM_RX_0"},
@@ -9719,6 +10157,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"BE_OUT", NULL, "AUX_PCM_RX"},
{"AUX_PCM_TX", NULL, "BE_IN"},
{"SEC_AUX_PCM_TX", NULL, "BE_IN"},
+ {"TERT_AUX_PCM_TX", NULL, "BE_IN"},
+ {"QUAT_AUX_PCM_TX", NULL, "BE_IN"},
{"INCALL_RECORD_TX", NULL, "BE_IN"},
{"INCALL_RECORD_RX", NULL, "BE_IN"},
{"SLIM0_RX_VI_FB_LCH_MUX", "SLIM4_TX", "SLIMBUS_4_TX"},
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index d8cec53c0d9e..6b7f2113e0f6 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -30,6 +30,10 @@
#define LPASS_BE_AUXPCM_TX "AUX_PCM_TX"
#define LPASS_BE_SEC_AUXPCM_RX "SEC_AUX_PCM_RX"
#define LPASS_BE_SEC_AUXPCM_TX "SEC_AUX_PCM_TX"
+#define LPASS_BE_TERT_AUXPCM_RX "TERT_AUX_PCM_RX"
+#define LPASS_BE_TERT_AUXPCM_TX "TERT_AUX_PCM_TX"
+#define LPASS_BE_QUAT_AUXPCM_RX "QUAT_AUX_PCM_RX"
+#define LPASS_BE_QUAT_AUXPCM_TX "QUAT_AUX_PCM_TX"
#define LPASS_BE_VOICE_PLAYBACK_TX "VOICE_PLAYBACK_TX"
#define LPASS_BE_VOICE2_PLAYBACK_TX "VOICE2_PLAYBACK_TX"
#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_RX"
@@ -313,6 +317,10 @@ enum {
MSM_BACKEND_DAI_USB_RX,
MSM_BACKEND_DAI_USB_TX,
MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+ MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+ MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
MSM_BACKEND_DAI_MAX,
};
diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
index d4d22d3587ba..7c8af09a8793 100644
--- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
+++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
@@ -241,6 +241,7 @@ static int msm_qti_pp_put_eq_band_audio_mixer(struct snd_kcontrol *kcontrol,
return 0;
}
+#ifdef CONFIG_QTI_PP
void msm_qti_pp_send_eq_values(int fedai_id)
{
if (eq_data[fedai_id].enable)
@@ -325,6 +326,7 @@ skip_send_cmd:
kfree(params_value);
return -ENOMEM;
}
+#endif /* CONFIG_QTI_PP */
/* RMS */
static int msm_qti_pp_get_rms_value_control(struct snd_kcontrol *kcontrol,
@@ -682,6 +684,7 @@ static int msm_qti_pp_asphere_send_params(int port_id, int copp_idx, bool force)
return 0;
}
+#if defined(CONFIG_QTI_PP) && defined(CONFIG_QTI_PP_AUDIOSPHERE)
int msm_qti_pp_asphere_init(int port_id, int copp_idx)
{
int index = adm_validate_and_get_port_index(port_id);
@@ -719,6 +722,7 @@ void msm_qti_pp_asphere_deinit(int port_id)
asphere_state.copp_idx[index] = -1;
}
}
+#endif
static int msm_qti_pp_asphere_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -976,6 +980,7 @@ static const struct snd_kcontrol_new asphere_mixer_controls[] = {
0xFFFFFFFF, 0, 2, msm_qti_pp_asphere_get, msm_qti_pp_asphere_set),
};
+#ifdef CONFIG_QTI_PP
void msm_qti_pp_add_controls(struct snd_soc_platform *platform)
{
snd_soc_add_platform_controls(platform, int_fm_vol_mixer_controls,
@@ -1023,3 +1028,4 @@ void msm_qti_pp_add_controls(struct snd_soc_platform *platform)
snd_soc_add_platform_controls(platform, asphere_mixer_controls,
ARRAY_SIZE(asphere_mixer_controls));
}
+#endif /* CONFIG_QTI_PP */
diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h
index 5c600f0ae866..f8a1da5e7702 100644
--- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h
+++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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.
@@ -15,7 +15,6 @@
#include <sound/soc.h>
#ifdef CONFIG_QTI_PP
-
void msm_qti_pp_send_eq_values(int fedai_id);
int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx,
unsigned int session_id,
@@ -24,32 +23,22 @@ int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx,
uint16_t op_FR_ip_FL_weight,
uint16_t op_FR_ip_FR_weight);
void msm_qti_pp_add_controls(struct snd_soc_platform *platform);
+#else /* CONFIG_QTI_PP */
+#define msm_qti_pp_send_eq_values(fedai_id) do {} while (0)
+#define msm_qti_pp_send_stereo_to_custom_stereo_cmd(port_id, copp_idx, \
+ session_id, op_FL_ip_FL_weight, op_FL_ip_FR_weight, \
+ op_FR_ip_FL_weight, op_FR_ip_FR_weight) (0)
+#define msm_qti_pp_add_controls(platform) do {} while (0)
+#endif /* CONFIG_QTI_PP */
+
+#if defined(CONFIG_QTI_PP) && defined(CONFIG_QTI_PP_AUDIOSPHERE)
int msm_qti_pp_asphere_init(int port_id, int copp_idx);
void msm_qti_pp_asphere_deinit(int port_id);
-
#else
-
-void msm_qti_pp_send_eq_values(int fedai_id) { }
-int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx,
- unsigned int session_id,
- uint16_t op_FL_ip_FL_weight,
- uint16_t op_FL_ip_FR_weight,
- uint16_t op_FR_ip_FL_weight,
- uint16_t op_FR_ip_FR_weight)
-{
- return 0;
-}
-
-void msm_qti_pp_add_controls(struct snd_soc_platform *platform) { }
-
-int msm_qti_pp_asphere_init(int port_id, int copp_idx)
-{
- return 0;
-}
-void msm_qti_pp_asphere_deinit(int port_id) { }
-
+#define msm_qti_pp_asphere_init(port_id, copp_idx) (0)
+#define msm_qti_pp_asphere_deinit(port_id) do {} while (0)
#endif
-#endif
+#endif /* _MSM_QTI_PP_H_ */
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:
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index f6a687611f8a..be0a8b2e3abe 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -419,7 +419,6 @@ int afe_get_port_type(u16 port_id)
switch (port_id) {
case PRIMARY_I2S_RX:
- case AFE_PORT_ID_PRIMARY_PCM_RX:
case SECONDARY_I2S_RX:
case MI2S_RX:
case HDMI_RX:
@@ -446,8 +445,11 @@ int afe_get_port_type(u16 port_id)
case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
case AFE_PORT_ID_TERTIARY_MI2S_RX:
case AFE_PORT_ID_QUATERNARY_MI2S_RX:
- case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_QUINARY_MI2S_RX:
+ case AFE_PORT_ID_PRIMARY_PCM_RX:
+ case AFE_PORT_ID_SECONDARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
case AFE_PORT_ID_PRIMARY_TDM_RX:
case AFE_PORT_ID_PRIMARY_TDM_RX_1:
case AFE_PORT_ID_PRIMARY_TDM_RX_2:
@@ -485,7 +487,6 @@ int afe_get_port_type(u16 port_id)
break;
case PRIMARY_I2S_TX:
- case AFE_PORT_ID_PRIMARY_PCM_TX:
case SECONDARY_I2S_TX:
case MI2S_TX:
case DIGI_MIC_TX:
@@ -507,9 +508,12 @@ int afe_get_port_type(u16 port_id)
case AFE_PORT_ID_SECONDARY_MI2S_TX:
case AFE_PORT_ID_TERTIARY_MI2S_TX:
case AFE_PORT_ID_QUATERNARY_MI2S_TX:
- case AFE_PORT_ID_SECONDARY_PCM_TX:
case AFE_PORT_ID_QUINARY_MI2S_TX:
case AFE_PORT_ID_SENARY_MI2S_TX:
+ case AFE_PORT_ID_PRIMARY_PCM_TX:
+ case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
case AFE_PORT_ID_PRIMARY_TDM_TX:
case AFE_PORT_ID_PRIMARY_TDM_TX_1:
case AFE_PORT_ID_PRIMARY_TDM_TX_2:
@@ -617,8 +621,12 @@ int afe_sizeof_cfg_cmd(u16 port_id)
case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
default:
- pr_err("%s: default case 0x%x\n", __func__, port_id);
+ pr_debug("%s: default case 0x%x\n", __func__, port_id);
ret_size = SIZEOF_CFG_CMD(afe_param_id_pcm_cfg);
break;
}
@@ -2637,8 +2645,9 @@ exit:
}
static int q6afe_send_enc_config(u16 port_id,
- union afe_enc_config_data *cfg, u32 format,
- union afe_port_config afe_config, u16 afe_in_channels)
+ union afe_enc_config_data *cfg, u32 format,
+ union afe_port_config afe_config,
+ u16 afe_in_channels, u16 afe_in_bit_width)
{
struct afe_audioif_config_command config;
int index;
@@ -2720,8 +2729,13 @@ static int q6afe_send_enc_config(u16 port_id,
config.pdata.param_id = AFE_PARAM_ID_PORT_MEDIA_TYPE;
config.port.media_type.minor_version = AFE_API_VERSION_PORT_MEDIA_TYPE;
config.port.media_type.sample_rate = afe_config.slim_sch.sample_rate;
- config.port.media_type.bit_width = afe_config.slim_sch.bit_width;
- if (afe_in_channels != 0)
+ if (afe_in_bit_width)
+ config.port.media_type.bit_width = afe_in_bit_width;
+ else
+ config.port.media_type.bit_width =
+ afe_config.slim_sch.bit_width;
+
+ if (afe_in_channels)
config.port.media_type.num_channels = afe_in_channels;
else
config.port.media_type.num_channels =
@@ -2741,8 +2755,8 @@ exit:
}
static int __afe_port_start(u16 port_id, union afe_port_config *afe_config,
- u32 rate, u16 afe_in_channels,
- union afe_enc_config_data *cfg, u32 enc_format)
+ u32 rate, u16 afe_in_channels, u16 afe_in_bit_width,
+ union afe_enc_config_data *cfg, u32 enc_format)
{
struct afe_audioif_config_command config;
int ret = 0;
@@ -2876,6 +2890,10 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config,
case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
cfg_type = AFE_PARAM_ID_PCM_CONFIG;
break;
case PRIMARY_I2S_RX:
@@ -2977,7 +2995,8 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config,
pr_debug("%s: Found AFE encoder support for SLIMBUS enc_format = %d\n",
__func__, enc_format);
ret = q6afe_send_enc_config(port_id, cfg, enc_format,
- *afe_config, afe_in_channels);
+ *afe_config, afe_in_channels,
+ afe_in_bit_width);
if (ret) {
pr_err("%s: AFE encoder config for port 0x%x failed %d\n",
__func__, port_id, ret);
@@ -3031,7 +3050,7 @@ int afe_port_start(u16 port_id, union afe_port_config *afe_config,
u32 rate)
{
return __afe_port_start(port_id, afe_config, rate,
- 0, NULL, ASM_MEDIA_FMT_NONE);
+ 0, 0, NULL, ASM_MEDIA_FMT_NONE);
}
EXPORT_SYMBOL(afe_port_start);
@@ -3049,12 +3068,12 @@ EXPORT_SYMBOL(afe_port_start);
* Returns 0 on success or error value on port start failure.
*/
int afe_port_start_v2(u16 port_id, union afe_port_config *afe_config,
- u32 rate, u16 afe_in_channels,
+ u32 rate, u16 afe_in_channels, u16 afe_in_bit_width,
struct afe_enc_config *enc_cfg)
{
return __afe_port_start(port_id, afe_config, rate,
- afe_in_channels, &enc_cfg->data,
- enc_cfg->format);
+ afe_in_channels, afe_in_bit_width,
+ &enc_cfg->data, enc_cfg->format);
}
EXPORT_SYMBOL(afe_port_start_v2);
@@ -3071,6 +3090,14 @@ int afe_get_port_index(u16 port_id)
return IDX_AFE_PORT_ID_SECONDARY_PCM_RX;
case AFE_PORT_ID_SECONDARY_PCM_TX:
return IDX_AFE_PORT_ID_SECONDARY_PCM_TX;
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_PCM_RX;
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_PCM_TX;
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX;
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX;
case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX;
case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX;
case MI2S_RX: return IDX_MI2S_RX;
@@ -3340,6 +3367,10 @@ int afe_open(u16 port_id,
case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
cfg_type = AFE_PARAM_ID_PCM_CONFIG;
break;
case SECONDARY_I2S_RX:
@@ -4009,6 +4040,14 @@ int afe_cmd_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz)
}
rtac_set_afe_handle(this_afe.apr);
}
+ if (dma_buf_sz % SZ_4K != 0) {
+ /*
+ * The memory allocated by msm_audio_ion_alloc is always 4kB
+ * aligned, ADSP expects the size to be 4kB aligned as well
+ * so re-adjusts the buffer size before passing to ADSP.
+ */
+ dma_buf_sz = PAGE_ALIGN(dma_buf_sz);
+ }
cmd_size = sizeof(struct afe_service_cmd_shared_mem_map_regions) \
+ sizeof(struct afe_service_shared_map_region_payload);
@@ -4809,6 +4848,10 @@ int afe_validate_port(u16 port_id)
case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
case SECONDARY_I2S_RX:
case SECONDARY_I2S_TX:
case MI2S_RX:
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 206fbec249fa..88c27339b299 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -183,6 +183,25 @@ static inline void q6asm_update_token(u32 *token, u8 session_id, u8 stream_id,
*token = asm_token.token;
}
+static inline uint32_t q6asm_get_pcm_format_id(uint32_t media_format_block_ver)
+{
+ uint32_t pcm_format_id;
+
+ switch (media_format_block_ver) {
+ case PCM_MEDIA_FORMAT_V4:
+ pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4;
+ break;
+ case PCM_MEDIA_FORMAT_V3:
+ pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3;
+ break;
+ case PCM_MEDIA_FORMAT_V2:
+ default:
+ pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+ break;
+ }
+ return pcm_format_id;
+}
+
/*
* q6asm_get_buf_index_from_token:
* Retrieve buffer index from token.
@@ -1996,6 +2015,40 @@ void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, uint32_t *size,
return NULL;
}
+int q6asm_cpu_buf_release(int dir, struct audio_client *ac)
+{
+ struct audio_port_data *port;
+ int ret = 0;
+ int idx;
+
+ if (!ac || ((dir != IN) && (dir != OUT))) {
+ pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (ac->io_mode & SYNC_IO_MODE) {
+ port = &ac->port[dir];
+ mutex_lock(&port->lock);
+ idx = port->cpu_buf;
+ if (port->cpu_buf == 0) {
+ port->cpu_buf = port->max_buf_cnt - 1;
+ } else if (port->cpu_buf < port->max_buf_cnt) {
+ port->cpu_buf = port->cpu_buf - 1;
+ } else {
+ pr_err("%s: buffer index(%d) out of range\n",
+ __func__, port->cpu_buf);
+ ret = -EINVAL;
+ mutex_unlock(&port->lock);
+ goto exit;
+ }
+ port->buf[port->cpu_buf].used = dir ^ 1;
+ mutex_unlock(&port->lock);
+ }
+exit:
+ return ret;
+}
+
void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac,
uint32_t *size, uint32_t *index)
{
@@ -2229,7 +2282,7 @@ static void q6asm_add_mmaphdr(struct audio_client *ac, struct apr_hdr *hdr,
static int __q6asm_open_read(struct audio_client *ac,
uint32_t format, uint16_t bits_per_sample,
- bool use_v3_format)
+ uint32_t pcm_format_block_ver)
{
int rc = 0x00;
struct asm_stream_cmd_open_read_v3 open;
@@ -2272,10 +2325,7 @@ static int __q6asm_open_read(struct audio_client *ac,
switch (format) {
case FORMAT_LINEAR_PCM:
open.mode_flags |= 0x00;
- if (use_v3_format)
- open.enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3;
- else
- open.enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+ open.enc_cfg_id = q6asm_get_pcm_format_id(pcm_format_block_ver);
break;
case FORMAT_MPEG4_AAC:
open.mode_flags |= BUFFER_META_ENABLE;
@@ -2338,14 +2388,14 @@ int q6asm_open_read(struct audio_client *ac,
uint32_t format)
{
return __q6asm_open_read(ac, format, 16,
- false /*use_v3_format*/);
+ PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/);
}
int q6asm_open_read_v2(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample)
{
return __q6asm_open_read(ac, format, bits_per_sample,
- false /*use_v3_format*/);
+ PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/);
}
/*
@@ -2359,10 +2409,25 @@ int q6asm_open_read_v3(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample)
{
return __q6asm_open_read(ac, format, bits_per_sample,
- true /*use_v3_format*/);
+ PCM_MEDIA_FORMAT_V3/*media fmt block ver*/);
}
EXPORT_SYMBOL(q6asm_open_read_v3);
+/*
+ * asm_open_read_v4 - Opens audio capture session
+ *
+ * @ac: Client session handle
+ * @format: encoder format
+ * @bits_per_sample: bit width of capture session
+ */
+int q6asm_open_read_v4(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_open_read(ac, format, bits_per_sample,
+ PCM_MEDIA_FORMAT_V4 /*media fmt block ver*/);
+}
+EXPORT_SYMBOL(q6asm_open_read_v4);
+
int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format,
uint32_t passthrough_flag)
{
@@ -2454,7 +2519,8 @@ fail_cmd:
static int __q6asm_open_write(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample, uint32_t stream_id,
- bool is_gapless_mode, bool use_v3_format)
+ bool is_gapless_mode,
+ uint32_t pcm_format_block_ver)
{
int rc = 0x00;
struct asm_stream_cmd_open_write_v3 open;
@@ -2530,11 +2596,7 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format,
}
switch (format) {
case FORMAT_LINEAR_PCM:
- if (use_v3_format)
- open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3;
- else
- open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
-
+ open.dec_fmt_id = q6asm_get_pcm_format_id(pcm_format_block_ver);
break;
case FORMAT_MPEG4_AAC:
open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2;
@@ -2613,7 +2675,7 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format)
{
return __q6asm_open_write(ac, format, 16, ac->stream_id,
false /*gapless*/,
- false /*use_v3_format*/);
+ PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/);
}
int q6asm_open_write_v2(struct audio_client *ac, uint32_t format,
@@ -2621,7 +2683,7 @@ int q6asm_open_write_v2(struct audio_client *ac, uint32_t format,
{
return __q6asm_open_write(ac, format, bits_per_sample,
ac->stream_id, false /*gapless*/,
- false /*use_v3_format*/);
+ PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/);
}
/*
@@ -2636,17 +2698,33 @@ int q6asm_open_write_v3(struct audio_client *ac, uint32_t format,
{
return __q6asm_open_write(ac, format, bits_per_sample,
ac->stream_id, false /*gapless*/,
- true /*use_v3_format*/);
+ PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/);
}
EXPORT_SYMBOL(q6asm_open_write_v3);
+/*
+ * q6asm_open_write_v4 - Opens audio playback session
+ *
+ * @ac: Client session handle
+ * @format: decoder format
+ * @bits_per_sample: bit width of playback session
+ */
+int q6asm_open_write_v4(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample)
+{
+ return __q6asm_open_write(ac, format, bits_per_sample,
+ ac->stream_id, false /*gapless*/,
+ PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_open_write_v4);
+
int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format,
uint16_t bits_per_sample, int32_t stream_id,
bool is_gapless_mode)
{
return __q6asm_open_write(ac, format, bits_per_sample,
stream_id, is_gapless_mode,
- false /*use_v3_format*/);
+ PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/);
}
/*
@@ -2664,10 +2742,29 @@ int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format,
{
return __q6asm_open_write(ac, format, bits_per_sample,
stream_id, is_gapless_mode,
- true /*use_v3_format*/);
+ PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/);
}
EXPORT_SYMBOL(q6asm_stream_open_write_v3);
+/*
+ * q6asm_stream_open_write_v4 - Creates audio stream for playback
+ *
+ * @ac: Client session handle
+ * @format: asm playback format
+ * @bits_per_sample: bit width of requested stream
+ * @stream_id: stream id of stream to be associated with this session
+ * @is_gapless_mode: true if gapless mode needs to be enabled
+ */
+int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format,
+ uint16_t bits_per_sample, int32_t stream_id,
+ bool is_gapless_mode)
+{
+ return __q6asm_open_write(ac, format, bits_per_sample,
+ stream_id, is_gapless_mode,
+ PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_stream_open_write_v4);
+
static int __q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format,
uint32_t wr_format, bool is_meta_data_mode,
uint32_t bits_per_sample,
@@ -3491,6 +3588,108 @@ fail_cmd:
}
/*
+ * q6asm_enc_cfg_blk_pcm_v4 - sends encoder configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map to be used
+ * @use_back_flavor: to configure back left and right channel
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample, bool use_default_chmap,
+ bool use_back_flavor, u8 *channel_map,
+ uint16_t sample_word_size, uint16_t endianness,
+ uint16_t mode)
+{
+ struct asm_multi_channel_pcm_enc_cfg_v4 enc_cfg;
+ struct asm_enc_cfg_blk_param_v2 enc_fg_blk;
+ u8 *channel_mapping;
+ u32 frames_per_buf = 0;
+ int rc;
+
+ if (!use_default_chmap && (channel_map == NULL)) {
+ pr_err("%s: No valid chan map and can't use default\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&enc_cfg, 0, sizeof(enc_cfg));
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+ enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
+ sizeof(enc_cfg.encdec);
+ enc_cfg.encblk.frames_per_buf = frames_per_buf;
+ enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+ sizeof(enc_fg_blk);
+ enc_cfg.num_channels = channels;
+ enc_cfg.bits_per_sample = bits_per_sample;
+ enc_cfg.sample_rate = rate;
+ enc_cfg.is_signed = 1;
+ enc_cfg.sample_word_size = sample_word_size;
+ enc_cfg.endianness = endianness;
+ enc_cfg.mode = mode;
+ channel_mapping = enc_cfg.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ pr_debug("%s: setting default channel map for %d channels",
+ __func__, channels);
+ if (q6asm_map_channels(channel_mapping, channels,
+ use_back_flavor)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ pr_debug("%s: Using pre-defined channel map", __func__);
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("%s: Command open failed %d\n", __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, enc_cfg.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v4);
+
+/*
* q6asm_enc_cfg_blk_pcm_v3 - sends encoder configuration parameters
*
* @ac: Client session handle
@@ -3666,6 +3865,18 @@ fail_cmd:
return rc;
}
+static int __q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ return q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels,
+ bits_per_sample, true, false, NULL,
+ sample_word_size, endianness, mode);
+}
+
static int __q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac,
uint32_t rate, uint32_t channels,
uint16_t bits_per_sample,
@@ -3715,6 +3926,31 @@ int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac,
}
EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v3);
+/*
+ * q6asm_enc_cfg_blk_pcm_format_support_v4 - sends encoder configuration
+ * parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ return __q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels,
+ bits_per_sample, sample_word_size,
+ endianness, mode);
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v4);
+
int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac,
uint32_t rate, uint32_t channels)
{
@@ -4347,6 +4583,91 @@ fail_cmd:
return rc;
}
+static int __q6asm_media_format_block_pcm_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ uint16_t bits_per_sample,
+ int stream_id,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt;
+ u8 *channel_mapping;
+ int rc;
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&fmt, 0, sizeof(fmt));
+ q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+ atomic_set(&ac->cmd_state, -1);
+ /*
+ * Updated the token field with stream/session for compressed playback
+ * Platform driver must know the the stream with which the command is
+ * associated
+ */
+ if (ac->io_mode & COMPRESSED_STREAM_IO)
+ fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) |
+ (stream_id & 0xFF);
+
+ pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n",
+ __func__, fmt.hdr.token, stream_id, ac->session);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.param.num_channels = channels;
+ fmt.param.bits_per_sample = bits_per_sample;
+ fmt.param.sample_rate = rate;
+ fmt.param.is_signed = 1;
+ fmt.param.sample_word_size = sample_word_size;
+ fmt.param.endianness = endianness;
+ fmt.param.mode = mode;
+ channel_mapping = fmt.param.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
+
int q6asm_media_format_block_pcm(struct audio_client *ac,
uint32_t rate, uint32_t channels)
{
@@ -4414,6 +4735,47 @@ int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac,
}
EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v3);
+/*
+ * q6asm_media_format_block_pcm_format_support_v4- sends pcm decoder
+ * configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @stream_id: stream id of stream to be associated with this session
+ * @use_default_chmap: true if default channel map to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac,
+ uint32_t rate,
+ uint32_t channels,
+ uint16_t bits_per_sample,
+ int stream_id,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ if (!use_default_chmap && (channel_map == NULL)) {
+ pr_err("%s: No valid chan map and can't use default\n",
+ __func__);
+ return -EINVAL;
+ }
+ return __q6asm_media_format_block_pcm_v4(ac, rate,
+ channels, bits_per_sample, stream_id,
+ use_default_chmap, channel_map,
+ sample_word_size, endianness,
+ mode);
+
+}
+EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v4);
+
+
static int __q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
uint32_t rate, uint32_t channels,
bool use_default_chmap, char *channel_map,
@@ -4547,6 +4909,78 @@ fail_cmd:
return rc;
}
+static int __q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac,
+ uint32_t rate,
+ uint32_t channels,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt;
+ u8 *channel_mapping;
+ int rc;
+
+ pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+ ac->session, rate, channels,
+ bits_per_sample, sample_word_size);
+
+ memset(&fmt, 0, sizeof(fmt));
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, -1);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.param.num_channels = channels;
+ fmt.param.bits_per_sample = bits_per_sample;
+ fmt.param.sample_rate = rate;
+ fmt.param.is_signed = 1;
+ fmt.param.sample_word_size = sample_word_size;
+ fmt.param.endianness = endianness;
+ fmt.param.mode = mode;
+ channel_mapping = fmt.param.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels, false)) {
+ pr_err("%s: map channels failed %d\n",
+ __func__, channels);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for format update\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return rc;
+}
int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
uint32_t rate, uint32_t channels,
@@ -4594,6 +5028,39 @@ int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac,
}
EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v3);
+/*
+ * q6asm_media_format_block_multi_ch_pcm_v4 - sends pcm decoder configuration
+ * parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap,
+ char *channel_map,
+ uint16_t bits_per_sample,
+ uint16_t sample_word_size,
+ uint16_t endianness,
+ uint16_t mode)
+{
+ return __q6asm_media_format_block_multi_ch_pcm_v4(ac, rate, channels,
+ use_default_chmap,
+ channel_map,
+ bits_per_sample,
+ sample_word_size,
+ endianness,
+ mode);
+}
+EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v4);
+
static int __q6asm_media_format_block_multi_aac(struct audio_client *ac,
struct asm_aac_cfg *cfg, int stream_id)
{
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
index a737a27cc327..99f5f0e1a5a5 100644
--- a/sound/soc/msm/qdsp6v2/q6audio-v2.c
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c
@@ -32,6 +32,14 @@ int q6audio_get_port_index(u16 port_id)
return IDX_AFE_PORT_ID_SECONDARY_PCM_RX;
case AFE_PORT_ID_SECONDARY_PCM_TX:
return IDX_AFE_PORT_ID_SECONDARY_PCM_TX;
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_PCM_RX;
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_PCM_TX;
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX;
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX;
case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX;
case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX;
case MI2S_RX: return IDX_MI2S_RX;
@@ -242,6 +250,14 @@ int q6audio_get_port_id(u16 port_id)
return AFE_PORT_ID_SECONDARY_PCM_RX;
case AFE_PORT_ID_SECONDARY_PCM_TX:
return AFE_PORT_ID_SECONDARY_PCM_TX;
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ return AFE_PORT_ID_TERTIARY_PCM_RX;
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ return AFE_PORT_ID_TERTIARY_PCM_TX;
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ return AFE_PORT_ID_QUATERNARY_PCM_RX;
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
+ return AFE_PORT_ID_QUATERNARY_PCM_TX;
case SECONDARY_I2S_RX: return AFE_PORT_ID_SECONDARY_MI2S_RX;
case SECONDARY_I2S_TX: return AFE_PORT_ID_SECONDARY_MI2S_TX;
case MI2S_RX: return AFE_PORT_ID_PRIMARY_MI2S_RX;
@@ -473,6 +489,10 @@ int q6audio_is_digital_pcm_interface(u16 port_id)
case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
case SECONDARY_I2S_RX:
case SECONDARY_I2S_TX:
case MI2S_RX:
@@ -571,6 +591,10 @@ int q6audio_validate_port(u16 port_id)
case AFE_PORT_ID_PRIMARY_PCM_TX:
case AFE_PORT_ID_SECONDARY_PCM_RX:
case AFE_PORT_ID_SECONDARY_PCM_TX:
+ case AFE_PORT_ID_TERTIARY_PCM_RX:
+ case AFE_PORT_ID_TERTIARY_PCM_TX:
+ case AFE_PORT_ID_QUATERNARY_PCM_RX:
+ case AFE_PORT_ID_QUATERNARY_PCM_TX:
case SECONDARY_I2S_RX:
case SECONDARY_I2S_TX:
case MI2S_RX:
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 94806ad6437b..0700b4c00aeb 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -4034,10 +4034,8 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
{
struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
- mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
if (!w) {
dev_err(dapm->dev, "ASoC: unknown pin %s\n", pin);
- mutex_unlock(&dapm->card->dapm_mutex);
return -EINVAL;
}
@@ -4053,7 +4051,6 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
}
w->force = 1;
dapm_mark_dirty(w, "force enable");
- mutex_unlock(&dapm->card->dapm_mutex);
return 0;
}
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 397fb4ed3c97..d4913a46ee1c 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -2313,12 +2313,15 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
case EPERM:
case EACCES:
return scnprintf(msg, size,
- "You may not have permission to collect %sstats.\n"
- "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n"
- " -1 - Not paranoid at all\n"
- " 0 - Disallow raw tracepoint access for unpriv\n"
- " 1 - Disallow cpu events for unpriv\n"
- " 2 - Disallow kernel profiling for unpriv",
+ "You may not have permission to collect %sstats.\n\n"
+ "Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n"
+ "which controls use of the performance events system by\n"
+ "unprivileged users (without CAP_SYS_ADMIN).\n\n"
+ "The default value is 1:\n\n"
+ " -1: Allow use of (almost) all events by all users\n"
+ ">= 0: Disallow raw tracepoint access by users without CAP_IOC_LOCK\n"
+ ">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n"
+ ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN",
target->system_wide ? "system-wide " : "");
case ENOENT:
return scnprintf(msg, size, "The %s event is not supported.",