summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/DocBook/80211.tmpl1
-rw-r--r--Documentation/block/00-INDEX6
-rw-r--r--Documentation/block/mmc-max-speed.txt38
-rw-r--r--Documentation/device-mapper/verity.txt40
-rw-r--r--Documentation/devicetree/bindings/arm/msm/msm.txt4
-rw-r--r--Documentation/devicetree/bindings/arm/msm/msm_watchdog.txt1
-rw-r--r--Documentation/devicetree/bindings/arm/msm/qcom,osm.txt16
-rw-r--r--Documentation/devicetree/bindings/arm/msm/spss_utils.txt27
-rw-r--r--Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt3
-rw-r--r--Documentation/devicetree/bindings/cnss/icnss.txt2
-rw-r--r--Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt7
-rw-r--r--Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt25
-rw-r--r--Documentation/devicetree/bindings/iio/adc/qcom-tadc.txt141
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt10
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt93
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-cci.txt3
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-csi-phy.txt1
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt48
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt17
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt37
-rw-r--r--Documentation/devicetree/bindings/power/qcom-charger/smb138x-charger.txt16
-rw-r--r--Documentation/devicetree/bindings/regulator/cpr3-regulator.txt19
-rwxr-xr-xDocumentation/devicetree/bindings/sound/qcom-audio-dev.txt12
-rw-r--r--Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt5
-rw-r--r--Documentation/devicetree/bindings/usb/msm-phy.txt47
-rw-r--r--Documentation/devicetree/bindings/usb/msm-ssusb.txt7
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt1
-rw-r--r--Documentation/sysctl/kernel.txt40
-rw-r--r--android/configs/android-base.cfg2
-rw-r--r--arch/arm/boot/dts/qcom/Makefile8
-rw-r--r--arch/arm/boot/dts/qcom/apqcobalt-v2.1-cdp.dts22
-rw-r--r--arch/arm/boot/dts/qcom/apqcobalt-v2.1-mtp.dts22
-rw-r--r--arch/arm/boot/dts/qcom/apqcobalt-v2.1-qrd.dts22
-rw-r--r--arch/arm/boot/dts/qcom/apqcobalt-v2.1.dtsi18
-rw-r--r--arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi5
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi12
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi30
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi23
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi62
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi10
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi5
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi115
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi28
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts23
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi102
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi13
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi12
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi111
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi266
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi115
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi194
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-regulator.dtsi358
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-rumi.dts2
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon-sim.dts2
-rw-r--r--arch/arm/boot/dts/qcom/msmfalcon.dtsi110
-rw-r--r--arch/arm/boot/dts/qcom/msmhamster.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msmtriton-rumi.dts29
-rw-r--r--arch/arm/boot/dts/qcom/msmtriton.dtsi268
-rw-r--r--arch/arm/configs/msmcortex_defconfig1
-rw-r--r--arch/arm/configs/msmfalcon_defconfig1
-rw-r--r--arch/arm/mach-qcom/Kconfig1
-rw-r--r--arch/arm/mm/mmap.c2
-rw-r--r--arch/arm64/Kconfig.platforms9
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig5
-rw-r--r--arch/arm64/configs/msmcortex_defconfig6
-rw-r--r--arch/arm64/configs/msmfalcon-perf_defconfig1
-rw-r--r--arch/arm64/configs/msmfalcon_defconfig1
-rw-r--r--arch/arm64/include/asm/arch_gicv3.h4
-rw-r--r--arch/arm64/kernel/io.c18
-rw-r--r--arch/arm64/mm/mmap.c4
-rw-r--r--arch/mips/mm/mmap.c4
-rw-r--r--arch/powerpc/kernel/process.c4
-rw-r--r--arch/powerpc/mm/mmap.c4
-rw-r--r--arch/sparc/kernel/sys_sparc_64.c2
-rw-r--r--arch/x86/mm/mmap.c6
-rw-r--r--block/blk-merge.c16
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/base/firmware_class.c1
-rw-r--r--drivers/base/platform.c20
-rw-r--r--drivers/base/power/main.c4
-rw-r--r--drivers/char/diag/diag_masks.c9
-rw-r--r--drivers/char/diag/diag_usb.c6
-rw-r--r--drivers/char/diag/diagchar.h3
-rw-r--r--drivers/char/diag/diagchar_core.c45
-rw-r--r--drivers/char/diag/diagfwd.c17
-rw-r--r--drivers/char/diag/diagfwd_bridge.c2
-rw-r--r--drivers/char/diag/diagfwd_glink.c18
-rw-r--r--drivers/char/diag/diagfwd_glink.h1
-rw-r--r--drivers/char/diag/diagfwd_mhi.c5
-rw-r--r--drivers/char/random.c22
-rw-r--r--drivers/clk/msm/Kconfig1
-rw-r--r--drivers/clk/msm/clock-gcc-cobalt.c44
-rw-r--r--drivers/clk/msm/clock-mmss-cobalt.c40
-rw-r--r--drivers/clk/msm/clock-osm.c231
-rw-r--r--drivers/clk/msm/vdd-level-cobalt.h18
-rw-r--r--drivers/clk/qcom/gcc-msm8996.c560
-rw-r--r--drivers/crypto/msm/qce50.c52
-rw-r--r--drivers/devfreq/bimc-bwmon.c266
-rw-r--r--drivers/devfreq/governor_bw_hwmon.c89
-rw-r--r--drivers/devfreq/governor_bw_hwmon.h7
-rw-r--r--drivers/edac/cortex_arm64_edac.c1
-rw-r--r--drivers/gpu/msm/adreno-gpulist.h2
-rw-r--r--drivers/gpu/msm/adreno.c15
-rw-r--r--drivers/gpu/msm/adreno_a3xx.c2
-rw-r--r--drivers/gpu/msm/adreno_a4xx.c2
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c11
-rw-r--r--drivers/gpu/msm/adreno_a5xx_preempt.c11
-rw-r--r--drivers/gpu/msm/adreno_a5xx_snapshot.c4
-rw-r--r--drivers/gpu/msm/adreno_profile.c3
-rw-r--r--drivers/gpu/msm/adreno_ringbuffer.c6
-rw-r--r--drivers/gpu/msm/kgsl.c9
-rw-r--r--drivers/gpu/msm/kgsl_debugfs.c26
-rw-r--r--drivers/gpu/msm/kgsl_iommu.c123
-rw-r--r--drivers/gpu/msm/kgsl_mmu.c6
-rw-r--r--drivers/gpu/msm/kgsl_mmu.h5
-rw-r--r--drivers/gpu/msm/kgsl_pool.c14
-rw-r--r--drivers/gpu/msm/kgsl_sharedmem.h4
-rw-r--r--drivers/gpu/msm/kgsl_trace.h35
-rw-r--r--drivers/hid/hid-sensor-hub.c3
-rw-r--r--drivers/hwtracing/coresight/coresight-stm.c23
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.c12
-rw-r--r--drivers/hwtracing/coresight/coresight-tpda.c6
-rw-r--r--drivers/iio/adc/Kconfig12
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/qcom-rradc.c2
-rw-r--r--drivers/iio/adc/qcom-tadc.c742
-rw-r--r--drivers/input/touchscreen/Kconfig42
-rw-r--r--drivers/input/touchscreen/Makefile2
-rw-r--r--drivers/input/touchscreen/ft5x06_ts.c1025
-rw-r--r--drivers/input/touchscreen/gt9xx/Kconfig51
-rw-r--r--drivers/input/touchscreen/gt9xx/Makefile8
-rw-r--r--drivers/input/touchscreen/gt9xx/goodix_tool.c166
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.c963
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.h122
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_update.c254
-rw-r--r--drivers/input/touchscreen/it7258_ts_i2c.c4
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c185
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h7
-rw-r--r--drivers/iommu/msm_dma_iommu_mapping.c4
-rw-r--r--drivers/irqchip/irq-gic-v3.c4
-rw-r--r--drivers/md/Kconfig12
-rw-r--r--drivers/md/Makefile5
-rw-r--r--drivers/md/dm-cache-target.c3
-rw-r--r--drivers/md/dm-crypt.c14
-rw-r--r--drivers/md/dm-snap.c6
-rw-r--r--drivers/md/dm-verity-fec.c861
-rw-r--r--drivers/md/dm-verity-fec.h155
-rw-r--r--drivers/md/dm-verity-target.c (renamed from drivers/md/dm-verity.c)602
-rw-r--r--drivers/md/dm-verity.h129
-rw-r--r--drivers/media/platform/msm/camera_v2/Kconfig48
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c163
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h16
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.h79
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp32.c71
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp40.c198
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp44.c203
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp46.c196
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c193
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.h6
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c3088
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h101
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c1153
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h54
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c85
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h2
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c76
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h160
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h57
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c259
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h6
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c153
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.h9
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/io/Makefile3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h56
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c1093
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c46
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c12
-rw-r--r--drivers/media/platform/msm/sde/Kconfig11
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_base.h29
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_core.c24
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c671
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h26
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r1_wb.c2
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c64
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c10
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_util.c2
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c121
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.h1
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_res_parse.c52
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_res_parse.h1
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c27
-rw-r--r--drivers/mfd/wcd934x-regmap.c14
-rw-r--r--drivers/mfd/wcd9xxx-irq.c33
-rw-r--r--drivers/misc/qcom/Kconfig2
-rw-r--r--drivers/misc/qcom/qdsp6v2/aac_in.c4
-rw-r--r--drivers/misc/qcom/qdsp6v2/amrnb_in.c5
-rw-r--r--drivers/misc/qcom/qdsp6v2/amrwb_in.c5
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_alac.c4
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c6
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_ape.c4
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c2
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_multi_aac.c4
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils.c15
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c2
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_wmapro.c4
-rw-r--r--drivers/misc/qcom/qdsp6v2/evrc_in.c4
-rw-r--r--drivers/misc/qcom/qdsp6v2/qcelp_in.c4
-rw-r--r--drivers/misc/qseecom.c204
-rw-r--r--drivers/misc/uid_stat.c153
-rw-r--r--drivers/mmc/card/Kconfig12
-rw-r--r--drivers/mmc/card/block.c300
-rw-r--r--drivers/mmc/card/queue.h8
-rw-r--r--drivers/mmc/core/core.c6
-rw-r--r--drivers/mmc/core/sdio.c1
-rw-r--r--drivers/mmc/host/sdhci-msm.c14
-rw-r--r--drivers/mmc/host/sdhci.c7
-rw-r--r--drivers/net/ppp/Kconfig4
-rw-r--r--drivers/net/ppp/pppolac.c9
-rw-r--r--drivers/net/ppp/pppopns.c9
-rw-r--r--drivers/net/wireless/cnss/cnss_pci.c45
-rw-r--r--drivers/net/wireless/cnss/cnss_sdio.c212
-rw-r--r--drivers/pci/host/pci-msm.c123
-rw-r--r--drivers/phy/phy-qcom-ufs-i.h5
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-v3.c17
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-v3.h16
-rw-r--r--drivers/phy/phy-qcom-ufs.c33
-rw-r--r--drivers/pinctrl/qcom/pinctrl-spmi-gpio.c333
-rw-r--r--drivers/pinctrl/qcom/pinctrl-spmi-mpp.c67
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c21
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c21
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c20
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_usb.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_rm.c7
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c226
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_intf.c23
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c48
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa.c257
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c31
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_intf.c21
-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/rmnet_ipa.c48
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c25
-rw-r--r--drivers/platform/msm/ipa/test/Makefile2
-rw-r--r--drivers/platform/msm/ipa/test/ipa_test_dma.c931
-rw-r--r--drivers/platform/msm/ipa/test/ipa_ut_suite_list.h2
-rw-r--r--drivers/platform/msm/sps/sps_bam.c3
-rw-r--r--drivers/power/power_supply_sysfs.c2
-rw-r--r--drivers/power/qcom-charger/fg-memif.c4
-rw-r--r--drivers/power/qcom-charger/qpnp-smb2.c276
-rw-r--r--drivers/power/qcom-charger/smb-lib.c347
-rw-r--r--drivers/power/qcom-charger/smb-lib.h35
-rw-r--r--drivers/power/qcom-charger/smb-reg.h30
-rw-r--r--drivers/power/qcom-charger/smb138x-charger.c44
-rw-r--r--drivers/power/qcom/debug_core.c63
-rw-r--r--drivers/regulator/core.c2
-rw-r--r--drivers/regulator/cpr3-mmss-regulator.c4
-rw-r--r--drivers/regulator/cpr3-regulator.c33
-rw-r--r--drivers/regulator/cpr3-regulator.h11
-rw-r--r--drivers/regulator/cpr3-util.c18
-rw-r--r--drivers/regulator/cprh-kbss-regulator.c28
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c151
-rw-r--r--drivers/scsi/ufs/ufs-qcom.h1
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c23
-rw-r--r--drivers/scsi/ufs/ufshcd.c34
-rw-r--r--drivers/scsi/ufs/ufshcd.h11
-rw-r--r--drivers/slimbus/slim-msm-ngd.c34
-rw-r--r--drivers/slimbus/slim-msm.h1
-rw-r--r--drivers/soc/qcom/Kconfig20
-rw-r--r--drivers/soc/qcom/Makefile3
-rw-r--r--drivers/soc/qcom/common_log.c34
-rw-r--r--drivers/soc/qcom/glink.c289
-rw-r--r--drivers/soc/qcom/icnss.c1220
-rw-r--r--drivers/soc/qcom/icnss_utils.c132
-rw-r--r--drivers/soc/qcom/ipc_router_glink_xprt.c49
-rw-r--r--drivers/soc/qcom/irq-helper.c4
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.c2
-rw-r--r--drivers/soc/qcom/remoteqdss.c36
-rw-r--r--drivers/soc/qcom/rpm-smd.c34
-rw-r--r--drivers/soc/qcom/service-notifier.c16
-rw-r--r--drivers/soc/qcom/socinfo.c7
-rw-r--r--drivers/soc/qcom/spss_utils.c294
-rw-r--r--drivers/soc/qcom/subsys-pil-tz.c3
-rw-r--r--drivers/soc/qcom/subsystem_restart.c24
-rw-r--r--drivers/soc/qcom/watchdog_v2.c41
-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/tty/serial/msm_serial.c187
-rw-r--r--drivers/usb/core/devio.c9
-rw-r--r--drivers/usb/dwc3/core.h1
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c43
-rw-r--r--drivers/usb/dwc3/gadget.c68
-rw-r--r--drivers/usb/gadget/composite.c23
-rw-r--r--drivers/usb/gadget/function/f_accessory.c22
-rw-r--r--drivers/usb/gadget/function/f_audio_source.c6
-rw-r--r--drivers/usb/gadget/function/f_gsi.c39
-rw-r--r--drivers/usb/gadget/function/f_mtp.c88
-rw-r--r--drivers/usb/gadget/function/u_ether.c1
-rw-r--r--drivers/usb/pd/policy_engine.c5
-rw-r--r--drivers/usb/phy/phy-msm-qusb-v2.c201
-rw-r--r--drivers/usb/phy/phy-msm-qusb.c20
-rw-r--r--drivers/usb/phy/phy-msm-ssusb-qmp.c189
-rw-r--r--drivers/video/adf/Kconfig2
-rw-r--r--drivers/video/adf/Makefile6
-rw-r--r--drivers/video/adf/adf_client.c4
-rw-r--r--drivers/video/fbdev/msm/mdss.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c52
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c419
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h9
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c7
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_hwio.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c38
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c8
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h3
-rw-r--r--drivers/video/fbdev/msm/msm_mdss_io_8974.c17
-rw-r--r--fs/Kconfig1
-rw-r--r--fs/Makefile5
-rw-r--r--fs/binfmt_elf.c2
-rw-r--r--fs/dcache.c1
-rw-r--r--fs/ext4/Kconfig10
-rw-r--r--fs/ext4/Makefile2
-rw-r--r--fs/ext4/crypto.c98
-rw-r--r--fs/ext4/crypto_key.c72
-rw-r--r--fs/ext4/dir.c13
-rw-r--r--fs/ext4/ext4.h33
-rw-r--r--fs/ext4/ext4_crypto.h6
-rw-r--r--fs/ext4/ext4_ice.c109
-rw-r--r--fs/ext4/ext4_ice.h104
-rw-r--r--fs/ext4/inline.c10
-rw-r--r--fs/ext4/inode.c7
-rw-r--r--fs/ext4/namei.c33
-rw-r--r--fs/ext4/page-io.c19
-rw-r--r--fs/ext4/readpage.c19
-rw-r--r--fs/ext4/xattr.c32
-rw-r--r--fs/notify/inotify/inotify_user.c15
-rw-r--r--fs/pstore/ram.c3
-rw-r--r--fs/sdcardfs/Kconfig13
-rw-r--r--fs/sdcardfs/Makefile7
-rw-r--r--fs/sdcardfs/dentry.c182
-rw-r--r--fs/sdcardfs/derived_perm.c265
-rw-r--r--fs/sdcardfs/file.c356
-rw-r--r--fs/sdcardfs/inode.c802
-rw-r--r--fs/sdcardfs/lookup.c384
-rw-r--r--fs/sdcardfs/main.c402
-rw-r--r--fs/sdcardfs/mmap.c81
-rw-r--r--fs/sdcardfs/multiuser.h37
-rw-r--r--fs/sdcardfs/packagelist.c437
-rw-r--r--fs/sdcardfs/sdcardfs.h530
-rw-r--r--fs/sdcardfs/super.c222
-rw-r--r--include/dt-bindings/clock/msm-clocks-cobalt.h21
-rw-r--r--include/dt-bindings/clock/qcom,gcc-msm8996.h23
-rw-r--r--include/dt-bindings/pinctrl/qcom,pmic-gpio.h15
-rw-r--r--include/linux/dcache.h1
-rw-r--r--include/linux/ecryptfs.h53
-rw-r--r--include/linux/input/ft5x06_ts.h13
-rw-r--r--include/linux/lsm_hooks.h2
-rw-r--r--include/linux/mfd/wcd9xxx/core.h53
-rw-r--r--include/linux/namei.h2
-rw-r--r--include/linux/perf_event.h5
-rw-r--r--include/linux/pfk.h6
-rw-r--r--include/linux/pft.h5
-rw-r--r--include/linux/phy/phy-qcom-ufs.h1
-rw-r--r--include/linux/platform_device.h1
-rw-r--r--include/linux/power_supply.h2
-rw-r--r--include/linux/random.h1
-rw-r--r--include/linux/sched.h2
-rw-r--r--include/linux/security.h6
-rw-r--r--include/linux/socket.h4
-rw-r--r--include/linux/wakeup_reason.h7
-rw-r--r--include/net/cfg80211.h51
-rw-r--r--include/net/rmnet_config.h27
-rw-r--r--include/soc/qcom/icnss.h15
-rw-r--r--include/soc/qcom/service-notifier.h5
-rw-r--r--include/soc/qcom/socinfo.h4
-rw-r--r--include/soc/qcom/subsystem_restart.h6
-rw-r--r--include/sound/apr_audio-v2.h3
-rw-r--r--include/sound/pcm.h6
-rw-r--r--include/sound/q6afe-v2.h6
-rw-r--r--include/sound/q6asm-v2.h2
-rw-r--r--include/trace/events/power.h2
-rw-r--r--include/trace/events/sched.h8
-rw-r--r--include/trace/events/skb.h13
-rw-r--r--include/uapi/linux/ipa_qmi_service_v01.h86
-rw-r--r--include/uapi/linux/magic.h2
-rw-r--r--include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h6
-rw-r--r--include/uapi/linux/nl80211.h10
-rw-r--r--include/uapi/linux/rmnet_ipa_fd_ioctl.h7
-rw-r--r--include/uapi/media/msm_cam_sensor.h12
-rw-r--r--include/uapi/media/msmb_isp.h126
-rw-r--r--kernel/cpuset.c17
-rw-r--r--kernel/events/core.c6
-rw-r--r--kernel/locking/mutex.c12
-rw-r--r--kernel/power/Kconfig1
-rw-r--r--kernel/sched/core.c7
-rw-r--r--kernel/sched/hmp.c20
-rw-r--r--kernel/sched/sched.h13
-rw-r--r--kernel/watchdog.c2
-rw-r--r--mm/mmap.c1
-rw-r--r--net/core/dev.c8
-rw-r--r--net/core/rtnetlink.c18
-rw-r--r--net/ipc_router/ipc_router_core.c17
-rw-r--r--net/ipv4/af_inet.c25
-rw-r--r--net/ipv6/ip6_offload.c3
-rw-r--r--net/netfilter/xt_qtaguid.c152
-rw-r--r--net/netfilter/xt_quota2.c134
-rw-r--r--net/rmnet_data/rmnet_data_config.c47
-rw-r--r--net/rmnet_data/rmnet_data_config.h6
-rw-r--r--net/rmnet_data/rmnet_data_handlers.c19
-rw-r--r--net/rmnet_data/rmnet_data_stats.c15
-rw-r--r--net/rmnet_data/rmnet_map.h9
-rw-r--r--net/rmnet_data/rmnet_map_command.c7
-rw-r--r--net/rmnet_data/rmnet_map_data.c9
-rw-r--r--net/wireless/core.h1
-rw-r--r--net/wireless/nl80211.c36
-rw-r--r--net/wireless/rdev-ops.h8
-rw-r--r--net/wireless/scan.c2
-rw-r--r--net/wireless/sme.c30
-rw-r--r--net/wireless/trace.h10
-rw-r--r--net/wireless/util.c2
-rw-r--r--security/Kconfig9
-rw-r--r--security/pfe/Kconfig1
-rw-r--r--security/pfe/Makefile3
-rw-r--r--security/pfe/pfk.c746
-rw-r--r--security/pfe/pfk_ecryptfs.c630
-rw-r--r--security/pfe/pfk_ecryptfs.h39
-rw-r--r--security/pfe/pfk_ext4.c212
-rw-r--r--security/pfe/pfk_ext4.h37
-rw-r--r--security/pfe/pfk_ice.h3
-rw-r--r--security/pfe/pfk_internal.h34
-rw-r--r--security/pfe/pft.c4
-rw-r--r--security/security.c6
-rw-r--r--security/selinux/hooks.c7
-rw-r--r--sound/core/timer.c9
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/audio-ext-clk.c8
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/msm_hdmi_codec_rx.c336
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c7
-rw-r--r--sound/soc/codecs/wcd9335.c250
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-routing.h60
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c1246
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.h12
-rw-r--r--sound/soc/codecs/wcd9xxx-common-v2.c121
-rw-r--r--sound/soc/codecs/wcd9xxx-common-v2.h4
-rw-r--r--sound/soc/codecs/wcd_cpe_core.c1
-rw-r--r--sound/soc/codecs/wsa881x.c64
-rw-r--r--sound/soc/msm/Kconfig1
-rw-r--r--sound/soc/msm/msm-dai-fe.c118
-rw-r--r--sound/soc/msm/msmcobalt.c307
-rw-r--r--sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c31
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c152
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c89
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c8
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c95
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h4
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c4
-rw-r--r--sound/soc/msm/qdsp6v2/q6afe.c12
-rw-r--r--sound/soc/msm/qdsp6v2/q6asm.c4
-rw-r--r--sound/soc/msm/qdsp6v2/q6audio-v2.c4
-rw-r--r--sound/soc/msm/qdsp6v2/q6voice.c1
-rw-r--r--sound/soc/msm/qdsp6v2/q6voice.h4
-rw-r--r--sound/soc/soc-ops.c10
-rw-r--r--sound/soc/soc-utils.c3
-rw-r--r--sound/usb/card.c5
-rw-r--r--sound/usb/usb_audio_qmi_svc.c42
-rw-r--r--tools/perf/util/evsel.c15
476 files changed, 30036 insertions, 7384 deletions
diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl
index f9b9ad7894f5..02431aeca15f 100644
--- a/Documentation/DocBook/80211.tmpl
+++ b/Documentation/DocBook/80211.tmpl
@@ -136,6 +136,7 @@
!Finclude/net/cfg80211.h cfg80211_tx_mlme_mgmt
!Finclude/net/cfg80211.h cfg80211_ibss_joined
!Finclude/net/cfg80211.h cfg80211_connect_result
+!Finclude/net/cfg80211.h cfg80211_connect_bss
!Finclude/net/cfg80211.h cfg80211_roamed
!Finclude/net/cfg80211.h cfg80211_disconnected
!Finclude/net/cfg80211.h cfg80211_ready_on_channel
diff --git a/Documentation/block/00-INDEX b/Documentation/block/00-INDEX
index e840b47613f7..bc5148757edb 100644
--- a/Documentation/block/00-INDEX
+++ b/Documentation/block/00-INDEX
@@ -26,3 +26,9 @@ switching-sched.txt
- Switching I/O schedulers at runtime
writeback_cache_control.txt
- Control of volatile write back caches
+mmc-max-speed.txt
+ - eMMC layer speed simulation, related to /sys/block/mmcblk*/
+ attributes:
+ max_read_speed
+ max_write_speed
+ cache_size
diff --git a/Documentation/block/mmc-max-speed.txt b/Documentation/block/mmc-max-speed.txt
new file mode 100644
index 000000000000..3f052b9fb999
--- /dev/null
+++ b/Documentation/block/mmc-max-speed.txt
@@ -0,0 +1,38 @@
+eMMC Block layer simulation speed controls in /sys/block/mmcblk*/
+===============================================
+
+Turned on with CONFIG_MMC_SIMULATE_MAX_SPEED which enables MMC device speed
+limiting. Used to test and simulate the behavior of the system when
+confronted with a slow MMC.
+
+Enables max_read_speed, max_write_speed and cache_size attributes and module
+default parameters to control the write or read maximum KB/second speed
+behaviors.
+
+NB: There is room for improving the algorithm for aspects tied directly to
+eMMC specific behavior. For instance, wear leveling and stalls from an
+exhausted erase pool. We would expect that if there was a need to provide
+similar speed simulation controls to other types of block devices, aspects of
+their behavior are modelled separately (e.g. head seek times, heat assist,
+shingling and rotational latency).
+
+/sys/block/mmcblk0/max_read_speed:
+
+Number of KB/second reads allowed to the block device. Used to test and
+simulate the behavior of the system when confronted with a slow reading MMC.
+Set to 0 or "off" to place no speed limit.
+
+/sys/block/mmcblk0/max_write_speed:
+
+Number of KB/second writes allowed to the block device. Used to test and
+simulate the behavior of the system when confronted with a slow writing MMC.
+Set to 0 or "off" to place no speed limit.
+
+/sys/block/mmcblk0/cache_size:
+
+Number of MB of high speed memory or high speed SLC cache expected on the
+eMMC device being simulated. Used to help simulate the write-back behavior
+more accurately. The assumption is the cache has no delay, but draws down
+in the background to the MLC/TLC primary store at the max_write_speed rate.
+Any write speed delays will show up when the cache is full, or when an I/O
+request to flush is issued.
diff --git a/Documentation/device-mapper/verity.txt b/Documentation/device-mapper/verity.txt
index e15bc1a0fb98..89fd8f9a259f 100644
--- a/Documentation/device-mapper/verity.txt
+++ b/Documentation/device-mapper/verity.txt
@@ -18,11 +18,11 @@ Construction Parameters
0 is the original format used in the Chromium OS.
The salt is appended when hashing, digests are stored continuously and
- the rest of the block is padded with zeros.
+ the rest of the block is padded with zeroes.
1 is the current format that should be used for new devices.
The salt is prepended when hashing and each digest is
- padded with zeros to the power of two.
+ padded with zeroes to the power of two.
<dev>
This is the device containing data, the integrity of which needs to be
@@ -79,6 +79,37 @@ restart_on_corruption
not compatible with ignore_corruption and requires user space support to
avoid restart loops.
+ignore_zero_blocks
+ Do not verify blocks that are expected to contain zeroes and always return
+ zeroes instead. This may be useful if the partition contains unused blocks
+ that are not guaranteed to contain zeroes.
+
+use_fec_from_device <fec_dev>
+ Use forward error correction (FEC) to recover from corruption if hash
+ verification fails. Use encoding data from the specified device. This
+ may be the same device where data and hash blocks reside, in which case
+ fec_start must be outside data and hash areas.
+
+ If the encoding data covers additional metadata, it must be accessible
+ on the hash device after the hash blocks.
+
+ Note: block sizes for data and hash devices must match. Also, if the
+ verity <dev> is encrypted the <fec_dev> should be too.
+
+fec_roots <num>
+ Number of generator roots. This equals to the number of parity bytes in
+ the encoding data. For example, in RS(M, N) encoding, the number of roots
+ is M-N.
+
+fec_blocks <num>
+ The number of encoding data blocks on the FEC device. The block size for
+ the FEC device is <data_block_size>.
+
+fec_start <offset>
+ This is the offset, in <data_block_size> blocks, from the start of the
+ FEC device to the beginning of the encoding data.
+
+
Theory of operation
===================
@@ -98,6 +129,11 @@ per-block basis. This allows for a lightweight hash computation on first read
into the page cache. Block hashes are stored linearly, aligned to the nearest
block size.
+If forward error correction (FEC) support is enabled any recovery of
+corrupted data will be verified using the cryptographic hash of the
+corresponding data. This is why combining error correction with
+integrity checking is essential.
+
Hash Tree
---------
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index 42daa8a61e4c..7b1c081ef586 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -92,6 +92,9 @@ SoCs:
- MSMFALCON
compatible = "qcom,msmfalcon"
+- MSMTRITON
+ compatible = "qcom,msmtriton"
+
- MSM8952
compatible = "qcom,msm8952"
@@ -259,6 +262,7 @@ compatible = "qcom,msmhamster-cdp"
compatible = "qcom,msmhamster-mtp"
compatible = "qcom,msmfalcon-sim"
compatible = "qcom,msmfalcon-rumi"
+compatible = "qcom,msmtriton-rumi"
compatible = "qcom,msm8952-rumi"
compatible = "qcom,msm8952-sim"
compatible = "qcom,msm8952-qrd"
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 cee9b942a9e3..518cc6f85f95 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
@@ -9,19 +9,22 @@ Properties:
- compatible
Usage: required
Value type: <string>
- Definition: must be "qcom,cpu-clock-osm".
+ Definition: must be "qcom,cpu-clock-osm-msmcobalt-v1" or
+ "qcom,cpu-clock-osm-msmcobalt-v2".
- reg
Usage: required
Value type: <prop-encoded-array>
Definition: Addresses and sizes for the memory of the OSM controller,
cluster PLL management, and APCS common register regions.
-
+ Optionally, the address of the efuse registers used to
+ determine the pwrcl or perfcl speed-bins.
- reg-names
Usage: required
Value type: <stringlist>
Definition: Address names. Must be "osm", "pwrcl_pll", "perfcl_pll",
- and "apcs_common".
+ "apcs_common" and "debug". Optionally, "pwrcl_efuse" or
+ "perfcl_efuse".
Must be specified in the same order as the corresponding
addresses are specified in the reg property.
@@ -297,13 +300,14 @@ Properties:
Example:
clock_cpu: qcom,cpu-clock-cobalt@179c0000 {
- compatible = "qcom,cpu-clock-osm";
+ compatible = "qcom,cpu-clock-osm-msmcobalt-v1";
reg = <0x179C0000 0x4000>,
<0x17916000 0x1000>,
<0x17816000 0x1000>,
- <0x179D1000 0x1000>;
+ <0x179D1000 0x1000>,
+ <0x1791101c 0x8>;
reg-names = "osm", "pwrcl_pll", "perfcl_pll",
- "apcs_common";
+ "apcs_common", "debug";
vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
vdd-perfcl-supply = <&apc1_perfcl_vreg>;
diff --git a/Documentation/devicetree/bindings/arm/msm/spss_utils.txt b/Documentation/devicetree/bindings/arm/msm/spss_utils.txt
new file mode 100644
index 000000000000..21b96377e5e4
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/spss_utils.txt
@@ -0,0 +1,27 @@
+Qualcomm Technologies, Inc. Secure Processor SubSystem Utilities (spss_utils)
+
+The Secure Processor SubSystem (SPSS) is a dedicated subsystem for security.
+It has its own CPU, memories, and cryptographic engine.
+It shall provide cryptographic services to other subsystems.
+The SPSS firmware is loaded by PIL driver.
+The communication with SPSS is done via spcom driver, using glink.
+
+The spss_utils driver selects the SPSS firmware file,
+according to a dedicated fuse and the platform HW version.
+
+Required properties:
+-compatible : should be "qcom,spss_utils"
+-qcom,spss-fuse-addr: fuse register physical address
+-qcom,spss-fuse-bit: fuse relevant bit
+-qcom,spss-test-firmware-name: test firmware file name
+-qcom,spss-prod-firmware-name: production firmware file name
+
+Example:
+ qcom,spss_utils {
+ compatible = "qcom,spss-utils";
+
+ qcom,spss-fuse-addr = <0x007841c4 0x4>; /* spss test fuse physical address */
+ qcom,spss-fuse-bit = <27>;
+ qcom,spss-test-firmware-name = "spss1t"; /* 8 chars max */
+ qcom,spss-prod-firmware-name = "spss1p"; /* 8 chars max */
+ };
diff --git a/Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt b/Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt
index 72bbb6180258..187c5604b521 100644
--- a/Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt
+++ b/Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt
@@ -29,7 +29,8 @@ Optional properties:
- qcom,msm-bus,num-cases: number of cases for bus scaling.
- qcom,msm-bus,num-paths: number of paths for bus scale vector.
- qcom,msm-bus,vectors-KBps: bus scale vector table.
-
+ - qcom,skip-wlan-en-toggle: Boolean property to be enabled for platforms where
+ wlan_en toggling is not supported.
Example:
qcom,cnss-sdio {
compatible = "qcom,cnss_sdio";
diff --git a/Documentation/devicetree/bindings/cnss/icnss.txt b/Documentation/devicetree/bindings/cnss/icnss.txt
index da5159006a98..505966fb9226 100644
--- a/Documentation/devicetree/bindings/cnss/icnss.txt
+++ b/Documentation/devicetree/bindings/cnss/icnss.txt
@@ -25,6 +25,8 @@ Required properties:
uS.
Optional properties:
+ - qcom,icnss-vadc: VADC handle for vph_pwr read APIs.
+ - qcom,icnss-adc_tm: VADC handle for vph_pwr notification APIs.
Example:
diff --git a/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt b/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt
index d96c174d2581..c77f84b61eaf 100644
--- a/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt
+++ b/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt
@@ -5,8 +5,8 @@ can be used to measure the bandwidth of read/write traffic from the BIMC
master ports. For example, the CPU subsystem sits on one BIMC master port.
Required properties:
-- compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2" or
- "qcom,bimc-bwmon3"
+- compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2",
+ "qcom,bimc-bwmon3" or "qcom,bimc-bwmon4"
- reg: Pairs of physical base addresses and region sizes of
memory mapped registers.
- reg-names: Names of the bases for the above registers. Expected
@@ -14,6 +14,8 @@ Required properties:
- interrupts: Lists the threshold IRQ.
- qcom,mport: The hardware master port that this device can monitor
- qcom,target-dev: The DT device that corresponds to this master port
+- qcom,hw-timer-hz: Hardware sampling rate in Hz. This field must be
+ specified for "qcom,bimc-bwmon4"
Example:
qcom,cpu-bwmon {
@@ -23,4 +25,5 @@ Example:
interrupts = <0 183 1>;
qcom,mport = <0>;
qcom,target-dev = <&cpubw>;
+ qcom,hw-timer-hz = <19200000>;
};
diff --git a/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt
index da4cdf253b2c..628b4df9fd7d 100644
--- a/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt
+++ b/Documentation/devicetree/bindings/fb/msm-hdmi-tx.txt
@@ -53,23 +53,29 @@ Optional properties:
- "secondary"
- "tertiary"
-[Optional child nodes]: These nodes are for devices which are
-dependent on HDMI Tx controller. If HDMI Tx controller is disabled then
-these devices will be disabled as well. Ex. HDMI Audio Codec device.
-
-- qcom,msm-hdmi-audio-rx: Node for HDMI audio codec.
-Required properties:
-- compatible : "msm-hdmi-audio-codec-rx";
-
msm_ext_disp is a device which manages the interaction between external
displays (HDMI and Display Port) and the audio and display frameworks.
Required properties:
- compatible: Must be "qcom,msm-ext-disp"
+[Required child nodes]: These nodes are for devices which are
+dependent on msm_ext_disp. If msm_ext_disp is disabled then
+these devices will be disabled as well. Ex. Audio Codec device.
+
+- ext_disp_audio_codec: Node for Audio Codec.
+
+Required properties:
+- compatible : "qcom,msm-ext-disp-audio-codec-rx";
+
Example:
msm_ext_disp: qcom,msm_ext_disp {
compatible = "qcom,msm-ext-disp";
+
+ ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx {
+ compatible = "qcom,msm-ext-disp-audio-codec-rx";
+ qcom,msm_ext_disp = <&msm_ext_disp>;
+ };
};
mdss_hdmi_tx: qcom,hdmi_tx@fd922100 {
@@ -109,9 +115,6 @@ Example:
qcom,pluggable;
qcom,display-id = "secondary";
- qcom,msm-hdmi-audio-rx {
- compatible = "qcom,msm-hdmi-audio-codec-rx";
- };
pinctrl-names = "hdmi_hpd_active", "hdmi_ddc_active",
"hdmi_cec_active", "hdmi_active",
"hdmi_sleep";
diff --git a/Documentation/devicetree/bindings/iio/adc/qcom-tadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom-tadc.txt
new file mode 100644
index 000000000000..6880d304367d
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/qcom-tadc.txt
@@ -0,0 +1,141 @@
+Qualcomm Technologies, Inc. TADC Specific Bindings
+
+TADC (Telemetry ADC) is a 10 bit resolution ADC which has 8 channels: battery
+temperature, skin temperature, die temperature, battery current, battery
+voltage, input current, input voltage, and OTG current.
+
+=======================
+Required Node Structure
+=======================
+
+A TADC must be described in two levels of devices nodes.
+
+=======================
+First Level Node - TADC
+=======================
+
+- reg
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Address and size of the TADC register block.
+
+TADC specific properties:
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,tadc".
+
+- interrupts
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Peripheral interrupt specifier.
+
+- interrupt-names
+ Usage: required
+ Value type: <stringlist>
+ Definition: Interrupt names. This list must match up 1-to-1 with the
+ interrupts specified in the 'interrupts' property.
+
+=============================================
+Second Level Nodes - TADC Thermistor Channels
+=============================================
+
+- reg
+ Usage: required
+ Value type: <u32>
+ Definition: The 0 based channel number.
+
+TADC thermistor channel specific properties:
+- qcom,rbias
+ Usage: required
+ Value type: <u32>
+ Definition: The bias resistor value.
+
+- qcom,therm-at-25degc
+ Usage: required
+ Value type: <u32>
+ Definition: The thermistor resistance at 25 DegC.
+
+- qcom,beta-coefficient
+ Usage: required
+ Value type: <u32>
+ Definition: The beta coefficeent or B-parameter of the thermistor.
+
+===============================================
+Second Level Nodes - TADC Scale/Offset Channels
+===============================================
+
+- reg
+ Usage: required
+ Value type: <u32>
+ Definition: The 0 based channel number.
+
+TADC scale/offset channel specific properties:
+- qcom,scale
+ Usage: required
+ Value type: <s32>
+ Definition: The RAW scaling factor.
+
+- qcom,offset
+ Usage: optional
+ Value type: <s32>
+ Definition: The offset after scaling.
+
+=======
+Example
+=======
+
+smb138x_tadc: qcom,tadc@3600 {
+ compatible = "qcom,tadc";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #io-channel-cells = <1>;
+ interrupts = <0x36 0x0 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "eoc";
+
+ batt_temp@0 {
+ reg = <0>;
+ qcom,rbias = <68100>;
+ qcom,rtherm-at-25degc = <68000>;
+ qcom,beta-coefficient = <3450>;
+ };
+
+ skin_temp@1 {
+ reg = <1>;
+ qcom,rbias = <33000>;
+ qcom,rtherm-at-25degc = <68000>;
+ qcom,beta-coefficient = <3450>;
+ };
+
+ die_temp@2 {
+ reg = <2>;
+ qcom,scale = <(-1032)>;
+ qcom,offset = <344125>;
+ };
+
+ batt_i@3 {
+ reg = <3>;
+ qcom,channel = <3>;
+ qcom,scale = <20000000>;
+ };
+
+ batt_v@4 {
+ reg = <4>;
+ qcom,scale = <5000000>;
+ };
+
+ input_i@5 {
+ reg = <5>;
+ qcom,scale = <14285714>;
+ };
+
+ input_v@6 {
+ reg = <6>;
+ qcom,scale = <25000000>;
+ };
+
+ otg_i@7 {
+ reg = <7>;
+ qcom,scale = <5714286>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
index c852394254ff..f7494c4c6e2b 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
@@ -70,6 +70,11 @@ Optional properties:
min y, max x and max y values
- focaltech,fw-name : specify the firmware file name
- focaltech,psensor-support : specify whether support the proximity sensor
+ - focaltech,gesture-support : specify whether support gesture feature
+ - focaltech,resume-in-workqueue : specifiy whether to defer the resume to workqueue
+ - clock-names: : Clock names used for secure touch. They are: "iface_clk", "core_clk"
+ - clocks : Defined if 'clock-names' DT property is defined. These clocks
+ are associated with the underlying I2C bus.
Example:
i2c@f9923000{
@@ -106,5 +111,10 @@ Example:
focaltech,fw-delay-era-flsh-ms = <2000>;
focaltech,fw-auto-cal;
focaltech,psensor-support;
+ focaltech,gesture-support;
+ /* Underlying clocks used by secure touch */
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
+ <&clock_gcc clk_gcc_blsp1_qup3_i2c_apps_clk>;
};
};
diff --git a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
new file mode 100644
index 000000000000..4de22947b333
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
@@ -0,0 +1,93 @@
+Goodix GT9xx series touch controller
+
+The Goodix GT9xx series touch controller is connected to the host processor via
+I2C. The controller generates interrupts when the user touches the panel. The
+host controller is expected to read the touch coordinates over I2C and pass
+the coordinates to the rest of the system.
+
+Required properties:
+
+ - compatible : Should be "goodix,gt9xx"
+ - reg : I2C slave address of the device.
+ - interrupt-parent : Parent of interrupt.
+ - interrupts : Configuration of touch panel controller interrupt
+ GPIO.
+ - goodix,product-id : Product identification of the controller.
+ - interrupt-gpios : Interrupt gpio which is to provide interrupts to
+ host, same as "interrupts" node.
+ - reset-gpios : Reset gpio to control the reset of chip.
+ - goodix,display-coords : Display coordinates in pixels. It is a four
+ tuple consisting of min x, min y, max x and
+ max y values.
+
+Optional properties:
+
+ - avdd-supply : Power supply needed to power up the device, this is
+ for fixed voltage external regulator.
+ - vdd-supply : Power supply needed to power up the device, when use
+ external regulator, do not add this property.
+ - vcc-i2c-supply : Power source required to power up i2c bus.
+ GT9xx series can provide 1.8V from internal
+ LDO, add this properties base on hardware
+ design.
+ - goodix,panel-coords : Panel coordinates for the chip in pixels.
+ It is a four tuple consisting of min x,
+ 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,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
+ to provide that.
+ Driver supports maximum six config groups. If more than one
+ groups are defined, driver will select config group depending
+ on hardware configuration. If only config group 0 is defined,
+ it will be used for all hardware configurations.
+ Touch screen controller will use its onchip default config data
+ if this property is not present.
+ - goodix,cfg-data1 : Touch screen controller config data group 1. Ask vendor
+ to provide that.
+ - goodix,cfg-data2 : Touch screen controller config data group 2. Ask vendor
+ to provide that.
+ - goodix,cfg-data3 : Touch screen controller config data group 3. Ask vendor
+ to provide that.
+ - goodix,cfg-data4 : Touch screen controller config data group 4. Ask vendor
+ to provide that.
+ - goodix,cfg-data5 : Touch screen controller config data group 5. Ask vendor
+ to provide that.
+Example:
+i2c@f9927000 {
+ goodix@5d {
+ compatible = "goodix,gt9xx";
+ reg = <0x5d>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <17 0x2008>;
+ reset-gpios = <&msmgpio 16 0x00>;
+ interrupt-gpios = <&msmgpio 17 0x00>;
+ avdd-supply = <&tp_power>;
+ goodix,panel-coords = <0 0 720 1200>;
+ goodix,display-coords = <0 0 720 1080>;
+ goodix,button-map= <158 102 139>;
+ goodix,product-id = "915";
+ goodix,cfg-data0 = [
+ 41 D0 02 00 05 0A 05 01 01 08
+ 12 58 50 41 03 05 00 00 00 00
+ 00 00 00 00 00 00 00 8C 2E 0E
+ 28 24 73 13 00 00 00 83 03 1D
+ 40 02 00 00 00 03 64 32 00 00
+ 00 1A 38 94 C0 02 00 00 00 04
+ 9E 1C 00 8D 20 00 7A 26 00 6D
+ 2C 00 60 34 00 60 10 38 68 00
+ F0 50 35 FF FF 27 00 00 00 00
+ 00 01 1B 14 0C 14 00 00 01 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 02 04 06 08 0A 0C 0E 10
+ 12 14 16 18 1A 1C FF FF FF FF
+ FF FF FF FF FF FF FF FF FF FF
+ FF FF 00 02 04 06 08 0A 0C 0F
+ 10 12 13 14 16 18 1C 1D 1E 1F
+ 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];
+ };
+};
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
index 086af9b337f4..fb1ca0261f9c 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -106,6 +106,8 @@ Optional properties:
- qcom,mount-angle : should contain the physical mount angle of the sensor on
the target
- 0, 90, 180, 360
+- qcom,secure : should be enabled to operate the camera in secure mode
+ - 0, 1
- qcom,mclk-23880000 : should be enabled if the supported mclk is 23.88Mhz and
not 24 Mhz.
- qcom,gpio-no-mux : should contain field to indicate whether gpio mux table is
@@ -273,6 +275,7 @@ Example:
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
qcom,mount-angle = <90>;
+ qcom,secure = <1>;
qcom,led-flash-src = <&led_flash0>;
qcom,actuator-src = <&actuator0>;
qcom,eeprom-src = <&eeprom0>;
diff --git a/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt b/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt
index e22435bb0be6..8a6b3c4355af 100644
--- a/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-csi-phy.txt
@@ -13,6 +13,7 @@ Required properties:
- "qcom,csiphy-v3.4.2"
- "qcom,csiphy-v3.5"
- "qcom,csiphy-v5.0"
+ - "qcom,csiphy-v5.01"
- reg : offset and length of the register set for the device
for the csiphy operating in compatible mode.
- reg-names : should specify relevant names to each reg property defined.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
index 1ae63c0acd40..8198a13081b8 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
@@ -16,6 +16,9 @@ PMIC's from Qualcomm.
"qcom,pm8941-gpio"
"qcom,pma8084-gpio"
+ And must contain either "qcom,spmi-gpio" or "qcom,ssbi-gpio"
+ if the device is on an spmi bus or an ssbi bus respectively
+
- reg:
Usage: required
Value type: <prop-encoded-array>
@@ -86,14 +89,18 @@ to specify in a pin configuration subnode:
Value type: <string>
Definition: Specify the alternative function to be configured for the
specified pins. Valid values are:
- "normal",
- "paired",
- "func1",
- "func2",
- "dtest1",
- "dtest2",
- "dtest3",
- "dtest4"
+ "normal",
+ "paired",
+ "func1",
+ "func2",
+ "dtest1",
+ "dtest2",
+ "dtest3",
+ "dtest4",
+ And following values are supported by LV/MV GPIO subtypes:
+ "func3",
+ "func4",
+ "analog"
- bias-disable:
Usage: optional
@@ -178,10 +185,33 @@ to specify in a pin configuration subnode:
Value type: <none>
Definition: The specified pins are configured in open-source mode.
+- qcom,atest:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects ATEST rail to route to GPIO when it's configured
+ in analog-pass-through mode by specifying "analog" function.
+ Valid values are 0-3 corresponding to PMIC_GPIO_AOUT_ATESTx
+ defined in <dt-bindings/pinctrl/qcom,pmic-gpio.h>.
+
+- qcom,dtest-buffer:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects DTEST rail to route to GPIO when it's configured
+ as a digital input.
+ For LV/MV GPIO subtypes, the valid values are 0-3
+ corresponding to PMIC_GPIO_DIN_DTESTx defined in
+ <dt-bindings/pinctrl/qcom,pmic-gpio.h>. Only one
+ DTEST rail can be selected at a time.
+ For 4CH/8CH GPIO subtypes, supported values are 1-15.
+ 4 DTEST rails are supported in total and more than 1 DTEST
+ rail can be selected simultaneously. Each bit of the
+ 4 LSBs represent one DTEST rail, such as [3:0] = 0101
+ means both DTEST1 and DTEST3 are selected.
+
Example:
pm8921_gpio: gpio@150 {
- compatible = "qcom,pm8921-gpio";
+ compatible = "qcom,pm8921-gpio", "qcom,ssbi-gpio";
reg = <0x150 0x160>;
interrupts = <192 1>, <193 1>, <194 1>,
<195 1>, <196 1>, <197 1>,
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
index d7803a2a94e9..42e504a27fa0 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
@@ -17,6 +17,9 @@ of PMIC's from Qualcomm.
"qcom,pm8941-mpp",
"qcom,pma8084-mpp",
+ And must contain either "qcom,spmi-mpp" or "qcom,ssbi-mpp"
+ if the device is on an spmi bus or an ssbi bus respectively.
+
- reg:
Usage: required
Value type: <prop-encoded-array>
@@ -139,7 +142,7 @@ to specify in a pin configuration subnode:
- qcom,dtest:
Usage: optional
Value type: <u32>
- Definition: Selects which dtest rail to be routed in the various functions.
+ Definition: Selects which dtest rail to be routed for digital output.
Valid values are 1-4
- qcom,amux-route:
@@ -153,10 +156,20 @@ to specify in a pin configuration subnode:
Value type: <none>
Definition: Indicates that the pin should be operating in paired mode.
+- qcom,dtest-buffer:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects which dtest rail to be routed for digital input.
+ It's also valid when the pin is configured as digital
+ input and output.
+ 4 dtest rails supported in total and more than one rail
+ could be selected simultaneously. Each bit of the 4 LSBs
+ represent one dtest rail, such as [3:0] = 0101 means both
+ dtest1 and dtest3 are selected. Valid values are 1-15.
Example:
mpps@a000 {
- compatible = "qcom,pm8841-mpp";
+ compatible = "qcom,pm8841-mpp", "qcom,spmi-mpp";
reg = <0xa000>;
gpio-controller;
#gpio-cells = <2>;
diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
index 5a415d04fbcf..510a824fda79 100644
--- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt
@@ -65,6 +65,37 @@ Charger specific properties:
maximum charge current in mA for each thermal
level.
+- qcom,step-soc-thresholds
+ Usage: optional
+ Value type: Array of <u32>
+ Definition: Array of SOC threshold values, size of 4. This should be a
+ flat array that denotes the percentage ranging from 0 to 100.
+ If the array is not present, step charging is disabled.
+
+- qcom,step-current-deltas
+ Usage: optional
+ Value type: Array of <s32>
+ Definition: Array of delta values for charging current, size of 5, with
+ FCC as base. This should be a flat array that denotes the
+ offset of charging current in uA, from -3100000 to 3200000.
+ If the array is not present, step charging is disabled.
+
+- io-channels
+ Usage: optional
+ Value type: List of <phandle u32>
+ Definition: List of phandle and IIO specifier pairs, one pair
+ for each IIO input to the device. Note: if the
+ IIO provider specifies '0' for #io-channel-cells,
+ then only the phandle portion of the pair will appear.
+
+- io-channel-names
+ Usage: optional
+ Value type: List of <string>
+ Definition: List of IIO input name strings sorted in the same
+ order as the io-channels property. Consumer drivers
+ will use io-channel-names to match IIO input names
+ with IIO specifiers.
+
=============================================
Second Level Nodes - SMB2 Charger Peripherals
=============================================
@@ -95,9 +126,15 @@ pmicobalt_charger: qcom,qpnp-smb2 {
#address-cells = <1>;
#size-cells = <1>;
+ io-channels = <&pmic_rradc 0>;
+ io-channel-names = "rradc_batt_id";
+
qcom,suspend-input;
dpdm-supply = <&qusb_phy0>;
+ qcom,step-soc-thresholds = <60 70 80 90>;
+ qcom,step-current-deltas = <500000 250000 150000 0 (-150000)>;
+
qcom,chgr@1000 {
reg = <0x1000 0x100>;
interrupts = <0x2 0x10 0x0 IRQ_TYPE_NONE>,
diff --git a/Documentation/devicetree/bindings/power/qcom-charger/smb138x-charger.txt b/Documentation/devicetree/bindings/power/qcom-charger/smb138x-charger.txt
index 0549c439460c..0244f910017a 100644
--- a/Documentation/devicetree/bindings/power/qcom-charger/smb138x-charger.txt
+++ b/Documentation/devicetree/bindings/power/qcom-charger/smb138x-charger.txt
@@ -52,6 +52,22 @@ Charger specific properties:
Value type: <u32>
Definition: Specifies the DC input current limit in micro-amps.
+- io-channels
+ Usage: optional
+ Value type: List of <phandle u32>
+ Definition: List of phandle and IIO specifier pairs, one pair
+ for each IIO input to the device. Note: if the
+ IIO provider specifies '0' for #io-channel-cells,
+ then only the phandle portion of the pair will appear.
+
+- io-channel-names
+ Usage: optional
+ Value type: List of <string>
+ Definition: List of IIO input name strings sorted in the same
+ order as the io-channels property. Consumer drivers
+ will use io-channel-names to match IIO input names
+ with IIO specifiers.
+
================================================
Second Level Nodes - SMB138X Charger Peripherals
================================================
diff --git a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
index 7d88e9fbd9c6..af53e59cd87f 100644
--- a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
@@ -35,13 +35,15 @@ Platform independent properties:
- reg
Usage: required
Value type: <prop-encoded-array>
- Definition: Addresses and sizes for the memory of the CPR3 controller
- and the first fuse row
+ Definition: Addresses and sizes for the memory of the CPR3 controller,
+ the first fuse row, and optionally a register used to check
+ if aging measurements are possible.
- reg-names
Usage: required
Value type: <stringlist>
- Definition: Address names. Must be "cpr_ctrl" and "fuse_base". Must be
+ Definition: Address names. Must include "cpr_ctrl" and "fuse_base".
+ "aging_allowed" may also be specified. The strings must be
specified in the same order as the corresponding addresses
are specified in the reg property.
@@ -183,6 +185,17 @@ Platform independent properties:
This is the voltage that vdd-supply must be set to when
performing an aging measurement.
+- qcom,cpr-aging-allowed-reg-mask
+ Usage: required if "aging_allowed" register is specified
+ Value type: <u32>
+ Definition: Bitmask used to mask off the "aging_allowed" register.
+
+- qcom,cpr-aging-allowed-reg-value
+ Usage: required if "aging_allowed" register is specified
+ Value type: <u32>
+ Definition: Value required in the masked off "aging_allowed" register
+ bits in order for a CPR aging measurement to be possible.
+
- qcom,cpr-panic-reg-addr-list
Usage: optional
Value type: <prop-encoded-array>
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index f6dadb738175..a01bd451c577 100755
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -470,6 +470,11 @@ Example:
qcom,msm-dai-q6-dev-id = <8>;
};
+ dai_dp: qcom,msm-dai-q6-dp {
+ compatible = "qcom,msm-dai-q6-hdmi";
+ qcom,msm-dai-q6-dev-id = <24608>;
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
@@ -784,7 +789,7 @@ Example:
"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
"msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa";
- asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>,
+ asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>, <&dai_dp>,
<&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>,
<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
<&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>,
@@ -793,6 +798,7 @@ Example:
<&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>,
<&incall_music_2_rx>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "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-dev.16384", "msm-dai-q6-dev.16385",
@@ -1157,7 +1163,7 @@ Example:
<&bt_sco_tx>,<&int_fm_rx>,<&int_fm_tx>,<&afe_pcm_rx>,
<&afe_pcm_tx>,<&afe_proxy_rx>,<&afe_proxy_tx>,
<&incall_record_rx>,<&incall_record_tx>,<&incall_music_rx>,
- <&incall_music2_rx>;
+ <&incall_music2_rx>,<&dai_dp>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1","msm-dai-q6-auxpcm.2",
"msm-dai-q6-hdmi.8","msm-dai-q6-mi2s.0",
"msm-dai-q6-dev.16384","msm-dai-q6-dev.16385",
@@ -1171,7 +1177,7 @@ Example:
"msm-dai-q6-dev.225","msm-dai-q6-dev.241",
"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.32770","msm-dai-q6-dp.24608";
asoc-codec = <&stub>;
asoc-codec-names = "msm-stub-codec.1";
};
diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 48fdd3e03947..bceee5e1747d 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -13,6 +13,9 @@ Required properties:
- reg : <registers mapping>
first entry should contain UFS host controller register address space (mandatory),
second entry is the device ref. clock control register map (optional).
+- reset : reset specifier pair consists of phandle for the reset provider
+ and reset lines used by this controller.
+- reset-names : reset signal name strings sorted in the same order as the resets property.
Optional properties:
- phys : phandle to UFS PHY node
@@ -76,6 +79,8 @@ Example:
clocks = <&core 0>, <&ref 0>, <&iface 0>;
clock-names = "core_clk", "ref_clk", "iface_clk";
freq-table-hz = <100000000 200000000>, <0 0>, <0 0>;
+ resets = <clock_gcc GCC_UFS_BCR>;
+ reset-names = "core_reset";
phys = <&ufsphy1>;
phy-names = "ufsphy";
rpm-level = <3>;
diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt
index dd9c13b4b5ff..b45ee910258e 100644
--- a/Documentation/devicetree/bindings/usb/msm-phy.txt
+++ b/Documentation/devicetree/bindings/usb/msm-phy.txt
@@ -118,6 +118,10 @@ Required properties:
USB3_PHY_POWER_DOWN_CONTROL,
USB3_PHY_SW_RESET,
USB3_PHY_START
+- 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.
Optional properties:
- reg: Additional register set of address and length to control QMP PHY are:
@@ -126,7 +130,7 @@ Optional properties:
- clocks: a list of phandles to the PHY clocks. Use as per
Documentation/devicetree/bindings/clock/clock-bindings.txt
- clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
- property. Required clocks are "cfg_ahb_clk", "phy_reset" and "phy_phy_reset".
+ property. "cfg_ahb_clk" is an optional clock.
- qcom,vbus-valid-override: If present, indicates VBUS pin is not connected to
the USB PHY and the controller must rely on external VBUS notification in
order to manually relay the notification to the SSPHY.
@@ -150,13 +154,17 @@ Example:
clocks = <&clock_gcc clk_gcc_usb3_phy_aux_clk>,
<&clock_gcc clk_gcc_usb3_phy_pipe_clk>,
<&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
- <&clock_gcc clk_gcc_usb3_phy_reset>,
- <&clock_gcc clk_gcc_usb3phy_phy_reset>,
<&clock_gcc clk_ln_bb_clk1>,
<&clock_gcc clk_gcc_usb3_clkref_clk>;
- clock-names = "aux_clk", "pipe_clk", "cfg_ahb_clk", "phy_reset",
- "phy_phy_reset", "ref_clk_src", "ref_clk";
+ clock-names = "aux_clk", "pipe_clk", "cfg_ahb_clk",
+ "ref_clk_src", "ref_clk";
+
+ resets = <&clock_gcc GCC_USB3_PHY_BCR>,
+ <&clock_gcc GCC_USB3PHY_PHY_BCR>;
+ reset-names = "phy_reset",
+ "phy_phy_reset";
+
};
QUSB2 High-Speed PHY
@@ -173,18 +181,20 @@ Required properties:
- qcom,vdd-voltage-level: This property must be a list of three integer
values (no, min, max) where each value represents either a voltage in
microvolts or a value corresponding to voltage corner
- - clocks: a list of phandles to the PHY clocks. Use as per
- Documentation/devicetree/bindings/clock/clock-bindings.txt
- - clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
- property. Required clock is "phy_reset".
- phy_type: Should be one of "ulpi" or "utmi". ChipIdea core uses "ulpi" mode.
+ - 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.
Optional properties:
- reg-names: Additional registers corresponding with the following:
- "tune2_efuse_addr": EFUSE based register address to read TUNE2 parameter.
- via the QSCRATCH interface.
+ "efuse_addr": EFUSE address to read and update analog tune parameter.
"emu_phy_base" : phy base address used for programming emulation target phy.
"ref_clk_addr" : ref_clk bcr address used for on/off ref_clk before reset.
+ "tcsr_clamp_dig_n" : To enable/disable digital clamp to the phy. When
+ de-asserted, it will prevent random leakage from qusb2 phy resulting from
+ out of sequence turn on/off of 1p8, 3p3 and DVDD regulators.
- clocks: a list of phandles to the PHY clocks. Use as per
Documentation/devicetree/bindings/clock/clock-bindings.txt
- clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
@@ -195,8 +205,8 @@ Optional properties:
- qcom,emu-init-seq : emulation initialization sequence with value,reg pair.
- qcom,phy-pll-reset-seq : emulation PLL reset sequence with value,reg pair.
- qcom,emu-dcm-reset-seq : emulation DCM reset sequence with value,reg pair.
- - qcom,tune2-efuse-bit-pos: TUNE2 parameter related start bit position with EFUSE register
- - qcom,tune2-efuse-num-bits: Number of bits based value to use for TUNE2 high nibble
+ - qcom,efuse-bit-pos: start bit position within EFUSE register
+ - qcom,efuse-num-bits: Number of bits to read from EFUSE register
- qcom,emulation: Indicates that we are running on emulation platform.
- qcom,hold-reset: Indicates that hold QUSB PHY into reset state.
- qcom,phy-clk-scheme: Should be one of "cml" or "cmos" if ref_clk_addr is provided.
@@ -211,12 +221,13 @@ Example:
vdda18-supply = <&pm8994_l6>;
vdda33-supply = <&pm8994_l24>;
qcom,vdd-voltage-level = <1 5 7>;
- qcom,tune2-efuse-bit-pos = <21>;
- qcom,tune2-efuse-num-bits = <3>;
+ qcom,efuse-bit-pos = <21>;
+ qcom,efuse-num-bits = <3>;
clocks = <&clock_rpm clk_ln_bb_clk>,
<&clock_gcc clk_gcc_rx2_usb1_clkref_clk>,
- <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
- <&clock_gcc clk_gcc_qusb2_phy_reset>;
- clock-names = "ref_clk_src", "ref_clk", "cfg_ahb_clk", "phy_reset";
+ <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>;
+ clock-names = "ref_clk_src", "ref_clk", "cfg_ahb_clk";
+ resets = <&clock_gcc GCC_QUSB2PHY_PRIM_BCR>;
+ reset-names = "phy_reset";
};
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index c5e5f1851fc2..f4d10908f4ff 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -15,6 +15,10 @@ Required properties :
- clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
property. Required clocks are "xo", "iface_clk", "core_clk", "sleep_clk"
and "utmi_clk".
+- resets: reset specifier pair consists of phandle for the reset provider
+ and reset lines used by this controller.
+- reset-names: reset signal name strings sorted in the same order as the resets
+ property.
Optional properties :
- reg: Additional registers
@@ -99,6 +103,9 @@ Example MSM USB3.0 controller device node :
clock-names = "core_clk", "iface_clk", "bus_aggr_clk",
"utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo";
+ resets = <&clock_gcc GCC_USB_30_BCR>;
+ reset-names = "core_reset";
+
dwc3@f9200000 {
compatible = "synopsys,dwc3";
reg = <0xf9200000 0xfc000>;
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index aca2dd3e4ccb..1d7e54f68ee4 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -87,6 +87,7 @@ firefly Firefly
focaltech FocalTech Systems Co.,Ltd
fsl Freescale Semiconductor
GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+goodix Goodix. Ltd.
gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
geniatech Geniatech, Inc.
giantplus Giantplus Technology Co., Ltd.
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/android/configs/android-base.cfg b/android/configs/android-base.cfg
index a4cffb1840d7..fa53af0c37ad 100644
--- a/android/configs/android-base.cfg
+++ b/android/configs/android-base.cfg
@@ -21,6 +21,7 @@ CONFIG_CGROUP_SCHED=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_DM_CRYPT=y
CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
CONFIG_EMBEDDED=y
CONFIG_FB=y
CONFIG_HIGH_RES_TIMERS=y
@@ -28,6 +29,7 @@ CONFIG_INET6_AH=y
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_INET=y
+CONFIG_INET_DIAG_DESTROY=y
CONFIG_INET_ESP=y
CONFIG_INET_XFRM_MODE_TUNNEL=y
CONFIG_IP6_NF_FILTER=y
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index 6b94608ee2c7..bffa21a06462 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -113,6 +113,7 @@ dtb-$(CONFIG_ARCH_MSMCOBALT) += msmcobalt-sim.dtb \
msmcobalt-v2-cdp.dtb \
msmcobalt-v2-qrd.dtb \
msmcobalt-qrd-skuk.dtb \
+ msmcobalt-qrd-vr1.dtb \
apqcobalt-mtp.dtb \
apqcobalt-cdp.dtb \
apqcobalt-v2-mtp.dtb \
@@ -120,13 +121,18 @@ dtb-$(CONFIG_ARCH_MSMCOBALT) += msmcobalt-sim.dtb \
apqcobalt-v2-qrd.dtb \
msmcobalt-v2.1-mtp.dtb \
msmcobalt-v2.1-cdp.dtb \
- msmcobalt-v2.1-qrd.dtb
+ msmcobalt-v2.1-qrd.dtb \
+ apqcobalt-v2.1-mtp.dtb \
+ apqcobalt-v2.1-cdp.dtb \
+ apqcobalt-v2.1-qrd.dtb
dtb-$(CONFIG_ARCH_MSMHAMSTER) += msmhamster-rumi.dtb
dtb-$(CONFIG_ARCH_MSMFALCON) += msmfalcon-sim.dtb \
msmfalcon-rumi.dtb
+dtb-$(CONFIG_ARCH_MSMTRITON) += msmtriton-rumi.dtb
+
ifeq ($(CONFIG_ARM64),y)
always := $(dtb-y)
subdir-y := $(dts-dirs)
diff --git a/arch/arm/boot/dts/qcom/apqcobalt-v2.1-cdp.dts b/arch/arm/boot/dts/qcom/apqcobalt-v2.1-cdp.dts
new file mode 100644
index 000000000000..f0ab8e0afc78
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apqcobalt-v2.1-cdp.dts
@@ -0,0 +1,22 @@
+/* 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.
+ */
+
+/dts-v1/;
+
+#include "apqcobalt-v2.1.dtsi"
+#include "msmcobalt-cdp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ COBALT V2.1 CDP";
+ compatible = "qcom,apqcobalt-cdp", "qcom,apqcobalt", "qcom,cdp";
+ qcom,board-id = <1 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/apqcobalt-v2.1-mtp.dts b/arch/arm/boot/dts/qcom/apqcobalt-v2.1-mtp.dts
new file mode 100644
index 000000000000..e23134f8897b
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apqcobalt-v2.1-mtp.dts
@@ -0,0 +1,22 @@
+/* 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.
+ */
+
+/dts-v1/;
+
+#include "apqcobalt-v2.1.dtsi"
+#include "msmcobalt-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ COBALT V2.1 MTP";
+ compatible = "qcom,apqcobalt-mtp", "qcom,apqcobalt", "qcom,mtp";
+ qcom,board-id = <8 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/apqcobalt-v2.1-qrd.dts b/arch/arm/boot/dts/qcom/apqcobalt-v2.1-qrd.dts
new file mode 100644
index 000000000000..8da6f90958d2
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apqcobalt-v2.1-qrd.dts
@@ -0,0 +1,22 @@
+/* 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.
+ */
+
+/dts-v1/;
+
+#include "apqcobalt-v2.1.dtsi"
+#include "msmcobalt-qrd.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ COBALT V2.1 QRD";
+ compatible = "qcom,apqcobalt-qrd", "qcom,apqcobalt", "qcom,qrd";
+ qcom,board-id = <11 0>;
+};
diff --git a/arch/arm/boot/dts/qcom/apqcobalt-v2.1.dtsi b/arch/arm/boot/dts/qcom/apqcobalt-v2.1.dtsi
new file mode 100644
index 000000000000..5a49afecd60b
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apqcobalt-v2.1.dtsi
@@ -0,0 +1,18 @@
+/* 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 "msmcobalt-v2.1.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ COBALT V2.1";
+ qcom,msm-id = <319 0x20001>;
+};
diff --git a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi
index f3233948d457..41b6f50c520b 100644
--- a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi
@@ -76,6 +76,11 @@
qcom,msm-dai-q6-dev-id = <8>;
};
+ dai_dp: qcom,msm-dai-q6-dp {
+ compatible = "qcom,msm-dai-q6-hdmi";
+ qcom,msm-dai-q6-dev-id = <24608>;
+ };
+
loopback: qcom,msm-pcm-loopback {
compatible = "qcom,msm-pcm-loopback";
};
diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
index d99749a01f6c..3f1ffd497f2c 100644
--- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
@@ -145,6 +145,16 @@
qcom,pmic-revid = <&pmicobalt_revid>;
+ io-channels = <&pmicobalt_rradc 8>,
+ <&pmicobalt_rradc 10>,
+ <&pmicobalt_rradc 3>,
+ <&pmicobalt_rradc 4>;
+ io-channel-names = "charger_temp",
+ "charger_temp_max",
+ "usbin_i",
+ "usbin_v";
+
+ qcom,wipower-max-uw = <5000000>;
dpdm-supply = <&qusb_phy0>;
qcom,thermal-mitigation
@@ -262,7 +272,7 @@
reg = <0x1700 0x100>;
vdd-pdphy-supply = <&pmcobalt_l24>;
vbus-supply = <&smb2_vbus>;
- vcon-supply = <&smb2_vconn>;
+ vconn-supply = <&smb2_vconn>;
interrupts = <0x2 0x17 0x0 IRQ_TYPE_EDGE_RISING>,
<0x2 0x17 0x1 IRQ_TYPE_EDGE_RISING>,
<0x2 0x17 0x2 IRQ_TYPE_EDGE_RISING>,
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index aa973e4ee3d6..4b1b9796ebe6 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.dtsi
@@ -1246,6 +1246,9 @@
"HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1",
"MAX";
+ resets = <&clock_gcc UFS_BCR>;
+ reset-names = "core_reset";
+
/* PM QoS */
qcom,pm-qos-cpu-groups = <0x03 0x0C>;
qcom,pm-qos-cpu-group-latency-us = <70 70>;
@@ -1934,6 +1937,9 @@
clock-names = "core_clk", "iface_clk", "bus_aggr_clk", "utmi_clk",
"sleep_clk", "xo", "cfg_ahb_clk";
+ resets = <&clock_gcc USB_30_BCR>;
+ reset-names = "core_reset";
+
dwc3@6a00000 {
compatible = "snps,dwc3";
reg = <0x06a00000 0xc8d0>;
@@ -2039,6 +2045,8 @@
<&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>;
clock-names = "core_clk", "iface_clk", "utmi_clk", "sleep_clk",
"xo", "cfg_ahb_clk";
+ resets = <&clock_gcc USB_20_BCR>;
+ reset-names = "core_reset";
dwc3@7600000 {
compatible = "snps,dwc3";
@@ -2088,10 +2096,11 @@
qcom,major-rev = <1>;
clocks = <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
- <&clock_gcc clk_gcc_qusb2phy_prim_reset>,
<&clock_gcc clk_ln_bb_clk>;
+ clock-names = "cfg_ahb_clk", "ref_clk_src";
- clock-names = "cfg_ahb_clk", "phy_reset", "ref_clk_src";
+ resets = <&clock_gcc QUSB2PHY_PRIM_BCR>;
+ reset-names = "phy_reset";
};
qusb_phy1: qusb@7412000 {
@@ -2124,10 +2133,11 @@
qcom,hold-reset;
clocks = <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
- <&clock_gcc clk_gcc_qusb2phy_sec_reset>,
- <&clock_gcc clk_ln_bb_clk>;
+ <&clock_gcc clk_ln_bb_clk>;
+ clock-names = "cfg_ahb_clk", "ref_clk_src";
- clock-names = "cfg_ahb_clk", "phy_reset", "ref_clk_src";
+ resets = <&clock_gcc QUSB2PHY_SEC_BCR>;
+ reset-names = "phy_reset";
};
ssphy: ssphy@7410000 {
@@ -2209,13 +2219,15 @@
clocks = <&clock_gcc clk_gcc_usb3_phy_aux_clk>,
<&clock_gcc clk_gcc_usb3_phy_pipe_clk>,
<&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
- <&clock_gcc clk_gcc_usb3_phy_reset>,
- <&clock_gcc clk_gcc_usb3phy_phy_reset>,
<&clock_gcc clk_ln_bb_clk>,
<&clock_gcc clk_gcc_usb3_clkref_clk>;
- clock-names = "aux_clk", "pipe_clk", "cfg_ahb_clk", "phy_reset",
- "phy_phy_reset", "ref_clk_src", "ref_clk";
+ clock-names = "aux_clk", "pipe_clk", "cfg_ahb_clk",
+ "ref_clk_src", "ref_clk";
+
+ resets = <&clock_gcc USB3_PHY_BCR>,
+ <&clock_gcc USB3PHY_PHY_BCR>;
+ reset-names = "phy_reset", "phy_phy_reset";
};
usb_nop_phy: usb_nop_phy {
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
index 05328a0f29dc..9b50bb96750a 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi
@@ -34,7 +34,7 @@
sound-9335 {
compatible = "qcom,msmcobalt-asoc-snd-tasha";
qcom,model = "msmcobalt-tasha-snd-card";
- qcom,hdmi-audio-rx;
+ qcom,ext-disp-audio-rx;
qcom,audio-routing =
"AIF4 VI", "MCLK",
@@ -80,7 +80,7 @@
"msm-pcm-afe", "msm-lsm-client",
"msm-pcm-routing", "msm-cpe-lsm",
"msm-compr-dsp", "msm-pcm-dsp-noirq";
- asoc-cpu = <&dai_hdmi>,
+ asoc-cpu = <&dai_hdmi>, <&dai_dp>,
<&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>,
@@ -90,7 +90,7 @@
<&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>;
- asoc-cpu-names = "msm-dai-q6-hdmi.8",
+ asoc-cpu-names = "msm-dai-q6-hdmi.8", "msm-dai-q6-dp.24608",
"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",
@@ -104,9 +104,9 @@
"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";
- asoc-codec = <&stub_codec>, <&hdmi_audio>;
+ asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>;
asoc-codec-names = "msm-stub-codec.1",
- "msm-hdmi-audio-codec-rx";
+ "msm-ext-disp-audio-codec-rx";
qcom,wsa-max-devs = <2>;
qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>,
<&wsa881x_213>, <&wsa881x_214>;
@@ -117,6 +117,7 @@
sound-tavil {
compatible = "qcom,msmcobalt-asoc-snd-tavil";
qcom,model = "msmcobalt-tavil-snd-card";
+ qcom,ext-disp-audio-rx;
qcom,audio-routing =
"RX_BIAS", "MCLK",
@@ -160,7 +161,7 @@
"msm-pcm-afe", "msm-lsm-client",
"msm-pcm-routing", "msm-cpe-lsm",
"msm-compr-dsp", "msm-pcm-dsp-noirq";
- asoc-cpu = <&dai_hdmi>,
+ asoc-cpu = <&dai_hdmi>, <&dai_dp>,
<&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>,
@@ -169,7 +170,7 @@
<&incall_record_tx>, <&incall_music_rx>,
<&incall_music_2_rx>, <&sb_5_rx>,
<&usb_audio_rx>, <&usb_audio_tx>, <&sb_6_rx>;
- asoc-cpu-names = "msm-dai-q6-hdmi.8",
+ asoc-cpu-names = "msm-dai-q6-hdmi.8", "msm-dai-q6-dp.24608",
"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",
@@ -182,8 +183,9 @@
"msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394",
"msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673",
"msm-dai-q6-dev.16396";
- asoc-codec = <&stub_codec>;
- asoc-codec-names = "msm-stub-codec.1";
+ asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>;
+ asoc-codec-names = "msm-stub-codec.1",
+ "msm-ext-disp-audio-codec-rx";
qcom,wsa-max-devs = <2>;
qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0212>,
<&wsa881x_0213>, <&wsa881x_0214>;
@@ -229,6 +231,9 @@
clocks = <&clock_gcc clk_div_clk1>;
qcom,node_has_rpm_clock;
#clock-cells = <1>;
+ pinctrl-names = "sleep", "active";
+ pinctrl-0 = <&spkr_i2s_clk_sleep>;
+ pinctrl-1 = <&spkr_i2s_clk_active>;
};
clock_audio_lnbb: audio_ext_clk_lnbb {
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi
index def8ed6e07a7..b4516f381c0c 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi
@@ -752,7 +752,7 @@
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
<&clock_mmss clk_mmss_camss_ahb_clk>,
<&clock_mmss clk_mmss_camss_top_ahb_clk>,
- <&clock_mmss clk_mmss_camss_jpeg0_clk>,
+ <&clock_mmss clk_mmss_camss_jpeg0_vote_clk>,
<&clock_mmss clk_mmss_camss_jpeg_ahb_clk>,
<&clock_mmss clk_mmss_camss_jpeg_axi_clk>;
qcom,clock-rates = <0 0 0 0 0 0 480000000 0 0>;
@@ -766,7 +766,7 @@
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps = <62 512 0 0>,
- <62 512 666675 666675>;
+ <62 512 1920000 2880000>;
status = "ok";
};
@@ -796,7 +796,7 @@
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
<&clock_mmss clk_mmss_camss_ahb_clk>,
<&clock_mmss clk_mmss_camss_top_ahb_clk>,
- <&clock_mmss clk_mmss_camss_jpeg0_clk>,
+ <&clock_mmss clk_mmss_camss_jpeg0_dma_vote_clk>,
<&clock_mmss clk_mmss_camss_jpeg_ahb_clk>,
<&clock_mmss clk_mmss_camss_jpeg_axi_clk>;
qcom,clock-rates = <0 0 0 0 0 0 480000000 0 0>;
@@ -808,7 +808,7 @@
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps = <62 512 0 0>,
- <62 512 666675 666675>;
+ <62 512 1920000 2880000>;
qcom,max-ds-factor = <128>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
index d1a8ae03cde2..d44002efea11 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi
@@ -205,6 +205,68 @@
};
};
+&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_tadc: qcom,tadc@3600 {
+ compatible = "qcom,tadc";
+ reg = <0x3600 0x100>;
+
+ interrupts = <0x36 0x0 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "eoc";
+
+ batt_therm {
+ qcom,rbias = <68100>;
+ qcom,rtherm-at-25degc = <68000>;
+ qcom,beta-coefficient = <3450>;
+ };
+
+ skin_temp {
+ qcom,rbias = <33000>;
+ qcom,rtherm-at-25degc = <68000>;
+ qcom,beta-coefficient = <3450>;
+ };
+
+ die_temp {
+ qcom,scale = <(-1032)>;
+ qcom,offset = <344125>;
+ };
+
+ batt_i {
+ qcom,channel = <3>;
+ qcom,scale = <20000000>;
+ };
+
+ batt_v {
+ qcom,scale = <5000000>;
+ };
+
+ input_i {
+ qcom,scale = <14285714>;
+ };
+
+ input_v {
+ qcom,scale = <25000000>;
+ };
+
+ otg_i {
+ qcom,scale = <5714286>;
+ };
+ };
+ };
+};
+
&mdss_mdp {
qcom,mdss-pref-prim-intf = "dsi";
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi
index 4afaa3aa51be..aeb6bf6141d8 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi
@@ -39,10 +39,18 @@
};
replicator: replicator@6046000 {
- compatible = "arm,coresight-replicator";
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b909>;
+
+ reg = <0x6046000 0x1000>;
+ reg-names = "replicator-base";
coresight-name = "coresight-replicator";
+ clocks = <&clock_gcc clk_qdss_clk>,
+ <&clock_gcc clk_qdss_a_clk>;
+ clock-names = "apb_pclk", "core_a_clk";
+
ports{
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi
index 617e7ddd9730..1267e578f9b4 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-gpu.dtsi
@@ -15,7 +15,7 @@
pil_gpu: qcom,kgsl-hyp {
compatible = "qcom,pil-tz-generic";
qcom,pas-id = <13>;
- qcom,firmware-name = "a530_zap";
+ qcom,firmware-name = "a540_zap";
};
msm_bus: qcom,kgsl-busmon{
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
index 85d6b1d5fba3..fd930d3d1644 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi
@@ -451,8 +451,9 @@
msm_ext_disp: qcom,msm_ext_disp {
compatible = "qcom,msm-ext-disp";
- hdmi_audio: qcom,msm-hdmi-audio-rx {
- compatible = "qcom,msm-hdmi-audio-codec-rx";
+ ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx {
+ compatible = "qcom,msm-ext-disp-audio-codec-rx";
+ qcom,msm_ext_disp = <&msm_ext_disp>;
};
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
index b84f63ecbd1e..70755ec1b8f5 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi
@@ -206,6 +206,94 @@
};
};
+&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_revid: qcom,revid@100 {
+ compatible = "qcom,qpnp-revid";
+ reg = <0x100 0x100>;
+ };
+
+ smb138x_tadc: qcom,tadc@3600 {
+ compatible = "qcom,tadc";
+ reg = <0x3600 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #io-channel-cells = <1>;
+ interrupts = <0x36 0x0 IRQ_TYPE_EDGE_BOTH>;
+ interrupt-names = "eoc";
+
+ batt_temp@0 {
+ reg = <0>;
+ qcom,rbias = <68100>;
+ qcom,rtherm-at-25degc = <68000>;
+ qcom,beta-coefficient = <3450>;
+ };
+
+ skin_temp@1 {
+ reg = <1>;
+ qcom,rbias = <33000>;
+ qcom,rtherm-at-25degc = <68000>;
+ qcom,beta-coefficient = <3450>;
+ };
+
+ die_temp@2 {
+ reg = <2>;
+ qcom,scale = <(-1032)>;
+ qcom,offset = <344125>;
+ };
+
+ batt_i@3 {
+ reg = <3>;
+ qcom,channel = <3>;
+ qcom,scale = <20000000>;
+ };
+
+ batt_v@4 {
+ reg = <4>;
+ qcom,scale = <5000000>;
+ };
+
+ input_i@5 {
+ reg = <5>;
+ qcom,scale = <14285714>;
+ };
+
+ input_v@6 {
+ reg = <6>;
+ qcom,scale = <25000000>;
+ };
+
+ otg_i@7 {
+ reg = <7>;
+ qcom,scale = <5714286>;
+ };
+ };
+
+ smb138x_parallel_slave: qcom,smb138x-parallel-slave@1000 {
+ compatible = "qcom,smb138x-parallel-slave";
+ qcom,pmic-revid = <&smb138x_revid>;
+ reg = <0x1000 0x700>;
+
+ io-channels = <&smb138x_tadc 2>,
+ <&smb138x_tadc 12>;
+ io-channel-names = "charger_temp",
+ "charger_temp_max";
+ };
+ };
+};
+
&mdss_hdmi_tx {
pinctrl-names = "hdmi_hpd_active", "hdmi_ddc_active", "hdmi_cec_active",
"hdmi_active", "hdmi_sleep";
@@ -356,33 +444,6 @@
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_revid: qcom,revid@100 {
- compatible = "qcom,qpnp-revid";
- reg = <0x100 0x100>;
- };
-
- smb138x_parallel_slave: qcom,smb138x-parallel-slave@1000 {
- compatible = "qcom,smb138x-parallel-slave";
- qcom,pmic-revid = <&smb138x_revid>;
- reg = <0x1000 0x700>;
- };
- };
-};
-
&pmicobalt_haptics {
status = "okay";
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
index c0f465b0eba5..f4f47bc461fc 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi
@@ -2715,5 +2715,33 @@
};
};
};
+
+ spkr_i2s_clk_pin {
+ spkr_i2s_clk_sleep: spkr_i2s_clk_sleep {
+ mux {
+ pins = "gpio69";
+ function = "spkr_i2s";
+ };
+
+ config {
+ pins = "gpio69";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* PULL DOWN */
+ };
+ };
+
+ spkr_i2s_clk_active: spkr_i2s_clk_active {
+ mux {
+ pins = "gpio69";
+ function = "spkr_i2s";
+ };
+
+ config {
+ pins = "gpio69";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable; /* NO PULL */
+ };
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts
new file mode 100644
index 000000000000..e53912071502
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dts
@@ -0,0 +1,23 @@
+/* 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.
+ */
+
+
+/dts-v1/;
+
+#include "msmcobalt.dtsi"
+#include "msmcobalt-qrd-vr1.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM COBALT VR1 Board";
+ compatible = "qcom,msmcobalt-qrd", "qcom,msmcobalt", "qcom,qrd";
+ qcom,board-id = <0x02000b 0x80>;
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi
new file mode 100644
index 000000000000..c028ea0eeab3
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd-vr1.dtsi
@@ -0,0 +1,102 @@
+/* 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 "msmcobalt-pinctrl.dtsi"
+
+&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>;
+ };
+};
+
+&soc {
+ 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>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi
index 425a902568ae..e0ae9a8873a7 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi
@@ -69,3 +69,16 @@
qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
};
+
+&soc {
+ sound-tavil {
+ qcom,msm-mbhc-hphl-swh = <1>;
+ /delete-property/ qcom,us-euro-gpios;
+ };
+
+ sound-9335 {
+ qcom,msm-mbhc-hphl-swh = <1>;
+ /delete-property/ qcom,us-euro-gpios;
+ };
+};
+
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi
index 256c404bb972..86bc048adeb5 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi
@@ -900,8 +900,10 @@
gfx_cpr: cpr4-ctrl@5061000 {
compatible = "qcom,cpr4-msmcobalt-v1-mmss-regulator";
- reg = <0x05061000 0x4000>, <0x00784000 0x1000>;
- reg-names = "cpr_ctrl", "fuse_base";
+ reg = <0x05061000 0x4000>,
+ <0x00784000 0x1000>,
+ <0x05065204 0x4>;
+ reg-names = "cpr_ctrl", "fuse_base", "aging_allowed";
clocks = <&clock_gpu clk_gpucc_rbcpr_clk>,
<&clock_gcc clk_cnoc_clk>;
clock-names = "core_clk", "bus_clk";
@@ -912,14 +914,16 @@
qcom,cpr-sensor-time = <1000>;
qcom,cpr-loop-time = <5000000>;
qcom,cpr-idle-cycles = <15>;
- qcom,cpr-step-quot-init-min = <10>;
- qcom,cpr-step-quot-init-max = <13>;
+ qcom,cpr-step-quot-init-min = <8>;
+ qcom,cpr-step-quot-init-max = <12>;
qcom,cpr-count-mode = <0>; /* All-at-once min */
vdd-supply = <&pm8005_s1>;
qcom,voltage-step = <4000>;
mem-acc-supply = <&gfx_mem_acc_vreg>;
qcom,cpr-aging-ref-voltage = <1032000>;
+ qcom,cpr-aging-allowed-reg-mask = <0x00000003>;
+ qcom,cpr-aging-allowed-reg-value = <0x00000003>;
qcom,cpr-enable;
diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi
new file mode 100644
index 000000000000..99d80a3b3848
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-camera.dtsi
@@ -0,0 +1,111 @@
+/*
+ * 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,csiphy@ca34000 {
+ cell-index = <0>;
+ compatible = "qcom,csiphy-v5.01", "qcom,csiphy";
+ reg = <0xca34000 0x1000>;
+ reg-names = "csiphy";
+ interrupts = <0 78 0>;
+ interrupt-names = "csiphy";
+ clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ <&clock_mmss clk_mmss_mnoc_ahb_clk>,
+ <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
+ <&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
+ <&clock_mmss clk_mmss_camss_ahb_clk>,
+ <&clock_mmss clk_mmss_camss_top_ahb_clk>,
+ <&clock_mmss clk_csi0_clk_src>,
+ <&clock_mmss clk_mmss_camss_csi0_clk>,
+ <&clock_mmss clk_mmss_camss_cphy_csid0_clk>,
+ <&clock_mmss clk_csi0phytimer_clk_src>,
+ <&clock_mmss clk_mmss_camss_csi0phytimer_clk>,
+ <&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",
+ "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
+ 0 256000000 0>;
+ status = "ok";
+ };
+
+ qcom,csiphy@ca35000 {
+ cell-index = <1>;
+ compatible = "qcom,csiphy-v5.01", "qcom,csiphy";
+ reg = <0xca35000 0x1000>;
+ reg-names = "csiphy";
+ interrupts = <0 79 0>;
+ interrupt-names = "csiphy";
+ clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ <&clock_mmss clk_mmss_mnoc_ahb_clk>,
+ <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
+ <&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
+ <&clock_mmss clk_mmss_camss_ahb_clk>,
+ <&clock_mmss clk_mmss_camss_top_ahb_clk>,
+ <&clock_mmss clk_csi1_clk_src>,
+ <&clock_mmss clk_mmss_camss_csi1_clk>,
+ <&clock_mmss clk_mmss_camss_cphy_csid1_clk>,
+ <&clock_mmss clk_csi1phytimer_clk_src>,
+ <&clock_mmss clk_mmss_camss_csi1phytimer_clk>,
+ <&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",
+ "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
+ 0 256000000 0>;
+ status = "ok";
+ };
+
+ qcom,csiphy@ca36000 {
+ cell-index = <2>;
+ compatible = "qcom,csiphy-v5.01", "qcom,csiphy";
+ reg = <0xca36000 0x1000>;
+ reg-names = "csiphy";
+ interrupts = <0 80 0>;
+ interrupt-names = "csiphy";
+ clocks = <&clock_mmss clk_mmss_mnoc_maxi_clk>,
+ <&clock_mmss clk_mmss_mnoc_ahb_clk>,
+ <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>,
+ <&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
+ <&clock_mmss clk_mmss_camss_ahb_clk>,
+ <&clock_mmss clk_mmss_camss_top_ahb_clk>,
+ <&clock_mmss clk_csi2_clk_src>,
+ <&clock_mmss clk_mmss_camss_csi2_clk>,
+ <&clock_mmss clk_mmss_camss_cphy_csid2_clk>,
+ <&clock_mmss clk_csi2phytimer_clk_src>,
+ <&clock_mmss clk_mmss_camss_csi2phytimer_clk>,
+ <&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",
+ "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
+ 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 f4d5e106e403..db50038b297e 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi
@@ -17,6 +17,7 @@
*/
#include "msmcobalt.dtsi"
+#include "msmcobalt-v2-camera.dtsi"
/ {
model = "Qualcomm Technologies, Inc. MSMCOBALT v2";
@@ -24,6 +25,8 @@
};
&clock_cpu {
+ 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 >,
@@ -79,6 +82,34 @@
< 2342400000 0x0404007a 0x0a620062 0x3 >,
< 2419200000 0x0404007e 0x0a650065 0x3 >,
< 2496000000 0x04040082 0x0a680068 0x3 >;
+
+ 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 >;
};
&msm_cpufreq {
@@ -194,28 +225,64 @@
&apc0_cpr {
compatible = "qcom,cprh-msmcobalt-v2-kbss-regulator";
+ qcom,cpr-corner-switch-delay-time = <1042>;
};
&apc0_pwrcl_vreg {
regulator-max-microvolt = <23>;
- qcom,cpr-corners = <22>;
- qcom,cpr-corner-fmax-map = <8 11 18 22>;
+
+ qcom,cpr-fuse-combos = <16>;
+ qcom,cpr-speed-bins = <2>;
+ qcom,cpr-speed-bin-corners = <22 22>;
+ qcom,cpr-corners =
+ /* Speed bin 0 */
+ <22 22 22 22 22 22 22 22>,
+ /* Speed bin 1 */
+ <22 22 22 22 22 22 22 22>;
+
+ qcom,cpr-corner-fmax-map =
+ /* Speed bin 0 */
+ <8 11 18 22>,
+ /* Speed bin 1 */
+ <8 11 18 22>;
qcom,cpr-voltage-ceiling =
- <688000 688000 688000 688000 688000
- 688000 688000 688000 756000 756000
- 756000 828000 828000 828000 828000
+ /* Speed bin 0 */
+ <828000 828000 828000 828000 828000
+ 828000 828000 828000 828000 828000
+ 828000 828000 828000 828000 828000
828000 828000 828000 952000 952000
- 1024000 1024000>;
+ 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
+ 1056000 1056000>;
qcom,cpr-voltage-floor =
+ /* Speed bin 0 */
+ <568000 568000 568000 568000 568000
+ 568000 568000 568000 568000 568000
+ 568000 632000 632000 632000 632000
+ 632000 632000 632000 712000 712000
+ 772000 772000>,
+ /* Speed bin 1 */
<568000 568000 568000 568000 568000
568000 568000 568000 568000 568000
568000 632000 632000 632000 632000
632000 632000 632000 712000 712000
- 756000 756000>;
+ 772000 772000>;
qcom,cpr-floor-to-ceiling-max-range =
+ /* Speed bin 0 */
+ <55000 55000 55000 55000
+ 55000 55000 55000 55000
+ 55000 55000 55000 65000
+ 65000 65000 65000 65000
+ 65000 65000 65000 65000
+ 65000 65000>,
+ /* Speed bin 1 */
<55000 55000 55000 55000
55000 55000 55000 55000
55000 55000 55000 65000
@@ -224,6 +291,16 @@
65000 65000>;
qcom,corner-frequencies =
+ /* Speed bin 0 */
+ <300000000 364800000 441600000
+ 518400000 595200000 672000000
+ 748800000 825600000 883200000
+ 960000000 1036800000 1094400000
+ 1171200000 1248000000 1324800000
+ 1401600000 1478400000 1555200000
+ 1670400000 1747200000 1824000000
+ 1900800000>,
+ /* Speed bin 1 */
<300000000 364800000 441600000
518400000 595200000 672000000
748800000 825600000 883200000
@@ -248,6 +325,16 @@
1559 1392>;
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>,
+ /* Speed bin 1 */
<40000 24000 0 0>,
<40000 24000 0 0>,
<40000 24000 0 0>,
@@ -258,6 +345,16 @@
<40000 24000 0 0>;
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>,
+ /* Speed bin 1 */
<20000 26000 0 0>,
<20000 26000 0 0>,
<20000 26000 0 0>,
@@ -274,30 +371,61 @@
&apc1_cpr {
compatible = "qcom,cprh-msmcobalt-v2-kbss-regulator";
+ qcom,cpr-corner-switch-delay-time = <1042>;
};
&apc1_perfcl_vreg {
regulator-max-microvolt = <31>;
- qcom,cpr-corners = <30>;
- qcom,cpr-corner-fmax-map = <8 12 20 30>;
+
+ qcom,cpr-fuse-combos = <16>;
+ qcom,cpr-speed-bins = <2>;
+ qcom,cpr-speed-bin-corners = <30 26>;
+ qcom,cpr-corners =
+ /* Speed bin 0 */
+ <30 30 30 30 30 30 30 30>,
+ /* Speed bin 1 */
+ <26 26 26 26 26 26 26 26>;
+
+ qcom,cpr-corner-fmax-map =
+ /* Speed bin 0 */
+ <8 12 20 30>,
+ /* Speed bin 1 */
+ <8 12 20 26>;
qcom,cpr-voltage-ceiling =
- <688000 688000 688000 688000 688000
- 688000 688000 688000 756000 756000
- 756000 756000 828000 828000 828000
+ /* Speed bin 0 */
+ <828000 828000 828000 828000 828000
+ 828000 828000 828000 828000 828000
+ 828000 828000 828000 828000 828000
+ 828000 828000 828000 828000 828000
+ 952000 952000 952000 1056000 1056000
+ 1056000 1056000 1056000 1056000 1056000>,
+ /* Speed bin 1 */
+ <828000 828000 828000 828000 828000
828000 828000 828000 828000 828000
- 952000 952000 952000 1024000 1024000
- 1024000 1024000 1024000 1024000 1024000>;
+ 828000 828000 828000 828000 828000
+ 828000 828000 828000 828000 828000
+ 952000 952000 952000 1056000 1056000
+ 1056000>;
qcom,cpr-voltage-floor =
+ /* Speed bin 0 */
<568000 568000 568000 568000 568000
568000 568000 568000 568000 568000
568000 568000 632000 632000 632000
632000 632000 632000 632000 632000
- 712000 712000 712000 756000 756000
- 756000 756000 756000 756000 756000>;
+ 712000 712000 712000 772000 772000
+ 772000 772000 772000 772000 772000>,
+ /* Speed bin 1 */
+ <568000 568000 568000 568000 568000
+ 568000 568000 568000 568000 568000
+ 568000 568000 632000 632000 632000
+ 632000 632000 632000 632000 632000
+ 712000 712000 712000 772000 772000
+ 772000>;
qcom,cpr-floor-to-ceiling-max-range =
+ /* Speed bin 0 */
<55000 55000 55000 55000
55000 55000 55000 55000
55000 55000 55000 55000
@@ -305,9 +433,18 @@
65000 65000 65000 65000
65000 65000 65000 65000
65000 65000 65000 65000
+ 65000 65000>,
+ /* Speed bin 1 */
+ <55000 55000 55000 55000
+ 55000 55000 55000 55000
+ 55000 55000 55000 55000
+ 65000 65000 65000 65000
+ 65000 65000 65000 65000
+ 65000 65000 65000 65000
65000 65000>;
qcom,corner-frequencies =
+ /* Speed bin 0 */
<300000000 345600000 422400000
499200000 576000000 652800000
729600000 806400000 902400000
@@ -317,7 +454,17 @@
1651200000 1728000000 1804800000
1881600000 1958400000 2035200000
2112000000 2188800000 2265600000
- 2342400000 2419200000 2496000000>;
+ 2342400000 2419200000 2496000000>,
+ /* Speed bin 1 */
+ <300000000 345600000 422400000
+ 499200000 576000000 652800000
+ 729600000 806400000 902400000
+ 979200000 1056000000 1132800000
+ 1190400000 1267200000 1344000000
+ 1420800000 1497600000 1574400000
+ 1651200000 1728000000 1804800000
+ 1881600000 1958400000 2035200000
+ 2112000000 2208000000>;
qcom,cpr-ro-scaling-factor =
<4001 4019 3747 3758 3564 3480 2336
@@ -334,6 +481,16 @@
1559 1392>;
qcom,cpr-open-loop-voltage-fuse-adjustment =
+ /* Speed bin 0 */
+ <8000 0 0 52000>,
+ <8000 0 0 52000>,
+ <8000 0 0 52000>,
+ <8000 0 0 52000>,
+ <8000 0 0 52000>,
+ <8000 0 0 52000>,
+ <8000 0 0 52000>,
+ <8000 0 0 52000>,
+ /* Speed bin 1 */
<8000 0 0 52000>,
<8000 0 0 52000>,
<8000 0 0 52000>,
@@ -344,6 +501,16 @@
<8000 0 0 52000>;
qcom,cpr-closed-loop-voltage-fuse-adjustment =
+ /* Speed bin 0 */
+ <0 0 0 50000>,
+ <0 0 0 50000>,
+ <0 0 0 50000>,
+ <0 0 0 50000>,
+ <0 0 0 50000>,
+ <0 0 0 50000>,
+ <0 0 0 50000>,
+ <0 0 0 50000>,
+ /* Speed bin 1 */
<0 0 0 50000>,
<0 0 0 50000>,
<0 0 0 50000>,
@@ -373,8 +540,8 @@
qcom,cpr-corner-fmax-map = <1 3 5 8>;
qcom,cpr-voltage-ceiling =
- <616000 676000 740000 800000 828000
- 884000 952000 1024000>;
+ <656000 716000 772000 880000 908000
+ 948000 1016000 1088000>;
qcom,cpr-voltage-floor =
<516000 516000 532000 584000 632000
@@ -445,14 +612,23 @@
};
&qusb_phy0 {
+ reg = <0x0c012000 0x2a8>,
+ <0x01fcb24c 0x4>,
+ <0x00784238 0x4>;
+ reg-names = "qusb_phy_base",
+ "tcsr_clamp_dig_n_1p8",
+ "efuse_addr";
+ qcom,efuse-bit-pos = <16>;
+ qcom,efuse-num-bits = <4>;
qcom,qusb-phy-init-seq =
/* <value reg_offset> */
- <0x13 0x04
- 0x7c 0x18c
- 0x80 0x2c
- 0x0a 0x184
- 0x00 0x240
- 0x19 0xb4>;
+ <0x13 0x04 /* analog_controls_two */
+ 0x7c 0x18c /* pll_clock_inverter */
+ 0x80 0x2c /* pll_cmode */
+ 0x0a 0x184 /* pll_lock_delay */
+ 0xa5 0x23c /* tune1 */
+ 0x09 0x240 /* tune2 */
+ 0x19 0xb4>; /* digital_timers_two */
};
&msm_vidc {
@@ -570,3 +746,43 @@
};
};
};
+
+&spss_utils {
+ qcom,spss-test-firmware-name = "spss2t"; /* 8 chars max */
+ qcom,spss-prod-firmware-name = "spss2p"; /* 8 chars max */
+};
+
+&ufs1 {
+ clock-names =
+ "core_clk",
+ "bus_aggr_clk",
+ "iface_clk",
+ "core_clk_unipro",
+ "core_clk_ice",
+ "ref_clk",
+ "tx_lane0_sync_clk",
+ "rx_lane0_sync_clk",
+ "rx_lane1_sync_clk";
+ clocks =
+ <&clock_gcc clk_gcc_ufs_axi_hw_ctl_clk>,
+ <&clock_gcc clk_gcc_aggre1_ufs_axi_clk>,
+ <&clock_gcc clk_gcc_ufs_ahb_clk>,
+ <&clock_gcc clk_gcc_ufs_unipro_core_hw_ctl_clk>,
+ <&clock_gcc clk_gcc_ufs_ice_core_hw_ctl_clk>,
+ <&clock_gcc clk_ln_bb_clk1>,
+ <&clock_gcc clk_gcc_ufs_tx_symbol_0_clk>,
+ <&clock_gcc clk_gcc_ufs_rx_symbol_0_clk>,
+ <&clock_gcc clk_gcc_ufs_rx_symbol_1_clk>;
+ freq-table-hz =
+ <50000000 200000000>,
+ <0 0>,
+ <0 0>,
+ <37500000 150000000>,
+ <75000000 300000000>,
+ <0 0>,
+ <0 0>,
+ <0 0>,
+ <0 0>;
+
+ lanes-per-direction = <2>;
+};
diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
index f830b2172050..a16894dd4765 100644
--- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi
@@ -542,12 +542,13 @@
};
qcom,cpu-bwmon {
- compatible = "qcom,bimc-bwmon3";
+ compatible = "qcom,bimc-bwmon4";
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 {
@@ -737,6 +738,7 @@
vdd_dig-supply = <&pmcobalt_s1_level>;
vdd_dig_ao-supply = <&pmcobalt_s1_level_ao>;
#clock-cells = <1>;
+ #reset-cells = <1>;
};
clock_mmss: qcom,mmsscc@c8c0000 {
@@ -744,6 +746,7 @@
reg = <0xc8c0000 0x40000>;
reg-names = "cc_base";
vdd_dig-supply = <&pmcobalt_s1_level>;
+ vdd_mmsscc_mx-supply = <&pmcobalt_s9_level>;
clock-names = "xo", "gpll0", "gpll0_div",
"pclk0_src", "pclk1_src",
"byte0_src", "byte1_src",
@@ -760,6 +763,7 @@
<&mdss_dp_pll clk_vco_divided_clk_src_mux>,
<&mdss_hdmi_pll clk_hdmi_vco_clk>;
#clock-cells = <1>;
+ #reset-cells = <1>;
};
clock_gpu: qcom,gpucc@5065000 {
@@ -801,13 +805,15 @@
};
clock_cpu: qcom,cpu-clock-cobalt@179c0000 {
- compatible = "qcom,cpu-clock-osm";
- reg = <0x179C0000 0x4000>,
+ compatible = "qcom,cpu-clock-osm-msmcobalt-v1";
+ reg = <0x179c0000 0x4000>,
<0x17916000 0x1000>,
<0x17816000 0x1000>,
- <0x179D1000 0x1000>;
+ <0x179d1000 0x1000>,
+ <0x00784130 0x8>,
+ <0x1791101c 0x8>;
reg-names = "osm", "pwrcl_pll", "perfcl_pll",
- "apcs_common";
+ "apcs_common", "perfcl_efuse", "debug";
vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
vdd-perfcl-supply = <&apc1_perfcl_vreg>;
@@ -940,10 +946,11 @@
reg = <0x162000 0x4>;
reg-names = "cc_base";
clock-names = "debug_gpu_clk", "debug_gfx_clk",
- "debug_mmss_clk";
+ "debug_mmss_clk", "debug_cpu_clk";
clocks = <&clock_gpu clk_gpucc_gcc_dbg_clk>,
<&clock_gfx clk_gfxcc_dbg_clk>,
- <&clock_mmss clk_mmss_debug_mux>;
+ <&clock_mmss clk_mmss_debug_mux>,
+ <&clock_cpu clk_cpu_debug_mux>;
#clock-cells = <1>;
};
@@ -1396,6 +1403,27 @@
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";
+ };
};
pcie0: qcom,pcie@01c00000 {
@@ -1518,7 +1546,7 @@
gdsc-vdd-supply = <&gdsc_pcie_0>;
vreg-1.8-supply = <&pmcobalt_l2>;
vreg-0.9-supply = <&pmcobalt_l1>;
- vreg-cx-supply = <&pmcobalt_s1_level_ao>;
+ vreg-cx-supply = <&pmcobalt_s1_level>;
qcom,vreg-1.8-voltage-level = <1200000 1200000 24000>;
qcom,vreg-0.9-voltage-level = <880000 880000 24000>;
@@ -1608,6 +1636,16 @@
status = "ok";
};
+ spss_utils: qcom,spss_utils {
+ compatible = "qcom,spss-utils";
+ /* spss test fuse physical address */
+ qcom,spss-fuse-addr = <0x007841c4 0x4>;
+ qcom,spss-fuse-bit = <27>;
+ qcom,spss-test-firmware-name = "spss"; /* default name */
+ qcom,spss-prod-firmware-name = "spss1p"; /* 8 chars max */
+ status = "ok";
+ };
+
sdhc_2: sdhci@c0a4900 {
compatible = "qcom,sdhci-msm";
reg = <0xc0a4900 0x314>, <0xc0a4000 0x800>;
@@ -1705,7 +1743,7 @@
"rx_lane0_sync_clk";
clocks =
<&clock_gcc clk_gcc_ufs_axi_hw_ctl_clk>,
- <&clock_gcc clk_gcc_aggre1_ufs_axi_clk>,
+ <&clock_gcc clk_gcc_aggre1_ufs_axi_hw_ctl_clk>,
<&clock_gcc clk_gcc_ufs_ahb_clk>,
<&clock_gcc clk_gcc_ufs_unipro_core_hw_ctl_clk>,
<&clock_gcc clk_gcc_ufs_ice_core_hw_ctl_clk>,
@@ -1723,8 +1761,9 @@
<0 0>;
lanes-per-direction = <1>;
+
qcom,msm-bus,name = "ufs1";
- qcom,msm-bus,num-cases = <12>;
+ qcom,msm-bus,num-cases = <22>;
qcom,msm-bus,num-paths = <2>;
qcom,msm-bus,vectors-KBps =
<95 512 0 0>, <1 650 0 0>, /* No vote */
@@ -1732,17 +1771,30 @@
<95 512 1844 0>, <1 650 1000 0>, /* PWM G2 */
<95 512 3688 0>, <1 650 1000 0>, /* PWM G3 */
<95 512 7376 0>, <1 650 1000 0>, /* PWM G4 */
+ <95 512 1844 0>, <1 650 1000 0>, /* PWM G1 L2 */
+ <95 512 3688 0>, <1 650 1000 0>, /* PWM G2 L2 */
+ <95 512 7376 0>, <1 650 1000 0>, /* PWM G3 L2 */
+ <95 512 14752 0>, <1 650 1000 0>, /* PWM G4 L2 */
<95 512 127796 0>, <1 650 1000 0>, /* HS G1 RA */
<95 512 255591 0>, <1 650 1000 0>, /* HS G2 RA */
<95 512 511181 0>, <1 650 1000 0>, /* HS G3 RA */
+ <95 512 255591 0>, <1 650 1000 0>, /* HS G1 RA L2 */
+ <95 512 511181 0>, <1 650 1000 0>, /* HS G2 RA L2 */
+ <95 512 1022362 0>, <1 650 1000 0>, /* HS G3 RA L2 */
<95 512 149422 0>, <1 650 1000 0>, /* HS G1 RB */
<95 512 298189 0>, <1 650 1000 0>, /* HS G2 RB */
<95 512 596378 0>, <1 650 1000 0>, /* HS G3 RB */
+ <95 512 298189 0>, <1 650 1000 0>, /* HS G1 RB L2 */
+ <95 512 596378 0>, <1 650 1000 0>, /* HS G2 RB L2 */
+ <95 512 1192756 0>, <1 650 1000 0>, /* HS G3 RB L2 */
<95 512 4096000 0>, <1 650 1000 0>; /* Max. bandwidth */
qcom,bus-vector-names = "MIN",
"PWM_G1_L1", "PWM_G2_L1", "PWM_G3_L1", "PWM_G4_L1",
+ "PWM_G1_L2", "PWM_G2_L2", "PWM_G3_L2", "PWM_G4_L2",
"HS_RA_G1_L1", "HS_RA_G2_L1", "HS_RA_G3_L1",
+ "HS_RA_G1_L2", "HS_RA_G2_L2", "HS_RA_G3_L2",
"HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1",
+ "HS_RB_G1_L2", "HS_RB_G2_L2", "HS_RB_G3_L2",
"MAX";
/* PM QoS */
@@ -1750,6 +1802,9 @@
qcom,pm-qos-cpu-group-latency-us = <70 70>;
qcom,pm-qos-default-cpu = <0>;
+ resets = <&clock_gcc UFS_BCR>;
+ reset-names = "core_reset";
+
status = "disabled";
};
@@ -1788,6 +1843,10 @@
"utmi_clk", "sleep_clk", "xo";
qcom,core-clk-rate = <120000000>;
+
+ resets = <&clock_gcc USB_30_BCR>;
+ reset-names = "core_reset";
+
dwc3@a800000 {
compatible = "snps,dwc3";
reg = <0x0a800000 0xcd00>;
@@ -1835,15 +1894,18 @@
qusb_phy0: qusb@c012000 {
compatible = "qcom,qusb2phy-v2";
- reg = <0x0c012000 0x2a8>;
- reg-names = "qusb_phy_base";
+ reg = <0x0c012000 0x2a8>,
+ <0x01fcb24c 0x4>;
+ reg-names = "qusb_phy_base",
+ "tcsr_clamp_dig_n_1p8";
vdd-supply = <&pmcobalt_l1>;
vdda18-supply = <&pmcobalt_l12>;
vdda33-supply = <&pmcobalt_l24>;
qcom,vdd-voltage-level = <0 880000 880000>;
qcom,qusb-phy-init-seq =
/* <value reg_offset> */
- <0x13 0x04
+ <0x80 0x0
+ 0x13 0x04
0x7c 0x18c
0x80 0x2c
0x0a 0x184
@@ -1851,10 +1913,11 @@
phy_type= "utmi";
clocks = <&clock_gcc clk_ln_bb_clk1>,
- <&clock_gcc clk_gcc_rx1_usb2_clkref_clk>,
- <&clock_gcc clk_gcc_qusb2phy_prim_reset>;
+ <&clock_gcc clk_gcc_rx1_usb2_clkref_clk>;
+ clock-names = "ref_clk_src", "ref_clk";
- clock-names = "ref_clk_src", "ref_clk", "phy_reset";
+ resets = <&clock_gcc QUSB2PHY_PRIM_BCR>;
+ reset-names = "phy_reset";
};
ssphy: ssphy@c010000 {
@@ -2002,13 +2065,15 @@
clocks = <&clock_gcc clk_gcc_usb3_phy_aux_clk>,
<&clock_gcc clk_gcc_usb3_phy_pipe_clk>,
- <&clock_gcc clk_gcc_usb3_phy_reset>,
- <&clock_gcc clk_gcc_usb3phy_phy_reset>,
<&clock_gcc clk_ln_bb_clk1>,
<&clock_gcc clk_gcc_usb3_clkref_clk>;
- clock-names = "aux_clk", "pipe_clk", "phy_reset",
- "phy_phy_reset", "ref_clk_src", "ref_clk";
+ clock-names = "aux_clk", "pipe_clk", "ref_clk_src",
+ "ref_clk";
+
+ resets = <&clock_gcc USB3_PHY_BCR>,
+ <&clock_gcc USB3PHY_PHY_BCR>;
+ reset-names = "phy_reset", "phy_phy_reset";
};
usb_audio_qmi_dev {
@@ -2556,7 +2621,12 @@
"iface_clk", "noc_axi_clk", "bus_clk", "maxi_clk";
qcom,pas-id = <9>;
- qcom,proxy-timeout-ms = <10000>;
+ 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,proxy-timeout-ms = <100>;
qcom,firmware-name = "venus";
memory-region = <&pil_video_mem>;
status = "ok";
@@ -2571,6 +2641,7 @@
qcom,pet-time = <10000>;
qcom,ipi-ping;
qcom,wakeup-enable;
+ qcom,scandump-size = <0x40000>;
};
qcom,spss@1d00000 {
@@ -2811,6 +2882,8 @@
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps = <81 10065 0 0>,
<81 10065 0 16000>;
+ qcom,icnss-vadc = <&pmcobalt_vadc>;
+ qcom,icnss-adc_tm = <&pmcobalt_adc_tm>;
};
wil6210: qcom,wil6210 {
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi
new file mode 100644
index 000000000000..352856965373
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmfalcon-coresight.dtsi
@@ -0,0 +1,194 @@
+/* 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/clock/qcom,gcc-msmfalcon.h>
+#include <dt-bindings/clock/qcom,gpu-msmfalcon.h>
+#include <dt-bindings/clock/qcom,mmcc-msmfalcon.h>
+
+&soc {
+ tmc_etr: tmc@6048000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b961>;
+
+ reg = <0x6048000 0x1000>,
+ <0x6064000 0x15000>;
+ reg-names = "tmc-base", "bam-base";
+
+ arm,buffer-size = <0x400000>;
+ arm,sg-enable;
+
+ coresight-name = "coresight-tmc-etr";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ port{
+ tmc_etr_in_replicator: endpoint {
+ slave-mode;
+ remote-endpoint = <&replicator_out_tmc_etr>;
+ };
+ };
+ };
+
+ replicator: replicator@6046000 {
+ compatible = "arm,coresight-replicator";
+
+ coresight-name = "coresight-replicator";
+
+ ports{
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ replicator_out_tmc_etr:endpoint {
+ remote-endpoint =
+ <&tmc_etr_in_replicator>;
+ };
+ };
+ port@1 {
+ reg = <0>;
+ replicator_in_tmc_etf:endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tmc_etf_out_replicator>;
+ };
+ };
+ };
+ };
+
+ tmc_etf: tmc@6047000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b961>;
+
+ reg = <0x6047000 0x1000>;
+ reg-names = "tmc-base";
+
+ coresight-name = "coresight-tmc-etf";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ ports{
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ tmc_etf_out_replicator:endpoint {
+ remote-endpoint =
+ <&replicator_in_tmc_etf>;
+ };
+ };
+ port@1 {
+ reg = <0>;
+ tmc_etf_in_funnel_merg:endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_merg_out_tmc_etf>;
+ };
+ };
+ };
+ };
+
+ funnel_merg: funnel@6045000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6045000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-merg";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_merg_out_tmc_etf:endpoint {
+ remote-endpoint =
+ <&tmc_etf_in_funnel_merg>;
+ };
+ };
+ port@1 {
+ reg = <0>;
+ funnel_merg_in_funnel_in0:endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_in0_out_funnel_merg>;
+ };
+ };
+ };
+ };
+
+ funnel_in0: funnel@6041000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6041000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-in0";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_in0_out_funnel_merg: endpoint {
+ remote-endpoint =
+ <&funnel_merg_in_funnel_in0>;
+ };
+ };
+ port@4 {
+ reg = <7>;
+ funnel_in0_in_stm: endpoint {
+ slave-mode;
+ remote-endpoint = <&stm_out_funnel_in0>;
+ };
+ };
+ };
+ };
+
+ stm: stm@6002000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b962>;
+
+ reg = <0x6002000 0x1000>,
+ <0x16280000 0x180000>;
+ reg-names = "stm-base", "stm-data-base";
+
+ coresight-name = "coresight-stm";
+
+ clocks = <&clock_gcc RPM_QDSS_CLK>,
+ <&clock_gcc RPM_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ port{
+ stm_out_funnel_in0: endpoint {
+ remote-endpoint = <&funnel_in0_in_stm>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-regulator.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-regulator.dtsi
new file mode 100644
index 000000000000..2c09774c1391
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmfalcon-regulator.dtsi
@@ -0,0 +1,358 @@
+/* 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.
+ */
+
+/* Stub regulators */
+
+/ {
+ /* PM660A S1 - VDD_APC0 supply */
+ pm660_s1a: regulator-pm660-s1a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_s2a";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <565000>;
+ regulator-max-microvolt = <1170000>;
+ };
+
+ pm660_s4a: regulator-pm660-s4a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_s4a";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <1805000>;
+ regulator-max-microvolt = <2040000>;
+ };
+
+ pm660_s5a: regulator-pm660-s5a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_s5a";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <1350000>;
+ regulator-max-microvolt = <1350000>;
+ };
+
+ pm660_s6a: regulator-pm660-s6a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_s6a";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <504000>;
+ regulator-max-microvolt = <992000>;
+ };
+
+ pm660_s1b: regulator-pm660-s1b {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_s1b";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <1125000>;
+ regulator-max-microvolt = <1125000>;
+ };
+
+ pm660_s2b: regulator-pm660-s2b {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l1a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1150000>;
+ regulator-max-microvolt = <1250000>;
+ };
+
+ pm660_l2a: regulator-pm660-l2a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l2a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1010000>;
+ };
+
+ pm660_l3a: regulator-pm660-l3a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l5a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <525000>;
+ regulator-max-microvolt = <950000>;
+ };
+
+ pm660_l6a: regulator-pm660-l6a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l6a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1370000>;
+ };
+
+ pm660_l7a: regulator-pm660-l7a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l7a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ };
+
+ pm660_l8a: regulator-pm660-l8a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l8a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1750000>;
+ regulator-max-microvolt = <1900000>;
+ };
+
+ pm660_l9a: regulator-pm660-l9a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l9a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1750000>;
+ regulator-max-microvolt = <1900000>;
+ };
+
+ pm660_l10a: regulator-pm660-l10a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l10a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1780000>;
+ regulator-max-microvolt = <1950000>;
+ };
+
+ pm660_l11a: regulator-pm660-l11a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l11a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1780000>;
+ regulator-max-microvolt = <1950000>;
+ };
+
+ pm660_l12a: regulator-pm660-l12a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l12a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1780000>;
+ regulator-max-microvolt = <1950000>;
+ };
+
+ pm660_l13a: regulator-pm660-l13a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l13a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1750000>;
+ regulator-max-microvolt = <1950000>;
+ };
+
+ pm660_l14a: regulator-pm660-l14a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l14a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1710000>;
+ regulator-max-microvolt = <1900000>;
+ };
+
+ pm660_l15a: regulator-pm660-l15a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l15a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1650000>;
+ regulator-max-microvolt = <2950000>;
+ };
+
+ pm660_l17a: regulator-pm660-l17a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l17a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1650000>;
+ regulator-max-microvolt = <2950000>;
+ };
+
+ pm660_l19a: regulator-pm660-l19a {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l19a";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <3200000>;
+ regulator-max-microvolt = <3400000>;
+ };
+
+ pm660_l1b: regulator-pm660-l1b {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l1b";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <925000>;
+ };
+
+ pm660_l2b: regulator-pm660-l2b {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l2b";
+ qcom,hpm-min-load = <5000>;
+ regulator-min-microvolt = <350000>;
+ regulator-max-microvolt = <3100000>;
+ };
+
+ pm660_l3b: regulator-pm660-l3b {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l3b";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1710000>;
+ regulator-max-microvolt = <3600000>;
+ };
+
+ pm660_l4b: regulator-pm660-l4b {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l4b";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1700000>;
+ regulator-max-microvolt = <2950000>;
+ };
+
+ pm660_l5b: regulator-pm660-l5b {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l5b";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1721000>;
+ regulator-max-microvolt = <3600000>;
+ };
+
+ pm660_l6b: regulator-pm660-l6b {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l6b";
+ qcom,hpm-min-load = <5000>;
+ regulator-min-microvolt = <1700000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ pm660_l7b: regulator-pm660-l7b {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l7b";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <2700000>;
+ regulator-max-microvolt = <3125000>;
+ };
+
+ pm660_l8b: regulator-pm660-l8b {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_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 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "pm660_l10b_floor_level";
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <RPM_SMD_REGULATOR_LEVEL_RETENTION>;
+ regulator-max-microvolt = <RPM_SMD_REGULATOR_LEVEL_TURBO>;
+ };
+
+ /* GFX Supply */
+ gfx_vreg_corner: regulator-gfx-corner {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "gfx_corner";
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts b/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts
index 6631d31bac6d..0d694a6cd9fa 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts
+++ b/arch/arm/boot/dts/qcom/msmfalcon-rumi.dts
@@ -22,7 +22,7 @@
qcom,board-id = <15 0>;
};
-&uartblsp2dm1 {
+&uartblsp1dm1 {
status = "ok";
pinctrl-names = "default";
pinctrl-0 = <&uart_console_active>;
diff --git a/arch/arm/boot/dts/qcom/msmfalcon-sim.dts b/arch/arm/boot/dts/qcom/msmfalcon-sim.dts
index 9840343fc3a7..eaaa1b407425 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon-sim.dts
+++ b/arch/arm/boot/dts/qcom/msmfalcon-sim.dts
@@ -22,7 +22,7 @@
qcom,board-id = <16 0>;
};
-&uartblsp2dm1 {
+&uartblsp1dm1 {
status = "ok";
pinctrl-names = "default";
pinctrl-0 = <&uart_console_active>;
diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
index e46041cdd501..246a6cf5371e 100644
--- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi
+++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi
@@ -15,6 +15,7 @@
#include <dt-bindings/clock/qcom,gpu-msmfalcon.h>
#include <dt-bindings/clock/qcom,mmcc-msmfalcon.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
/ {
model = "Qualcomm Technologies, Inc. MSM FALCON";
@@ -23,7 +24,7 @@
interrupt-parent = <&intc>;
aliases {
- serial0 = &uartblsp2dm1;
+ serial0 = &uartblsp1dm1;
};
chosen {
@@ -200,6 +201,7 @@
};
#include "msmfalcon-smp2p.dtsi"
+#include "msmfalcon-coresight.dtsi"
&soc {
#address-cells = <1>;
#size-cells = <1>;
@@ -246,6 +248,16 @@
3200 3200 3200 3200 3200 3200>;
};
+ uartblsp1dm1: serial@0c170000 {
+ compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+ reg = <0xc170000 0x1000>;
+ interrupts = <0 108 0>;
+ status = "disabled";
+ clocks = <&clock_gcc GCC_BLSP1_UART2_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ };
+
uartblsp2dm1: serial@0c1b0000 {
compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
reg = <0xc1b0000 0x1000>;
@@ -472,6 +484,27 @@
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";
+ };
};
qcom,ipc_router {
@@ -511,3 +544,78 @@
};
#include "msmfalcon-ion.dtsi"
+#include "msmfalcon-regulator.dtsi"
+#include "msm-gdsc-cobalt.dtsi"
+
+&gdsc_usb30 {
+ clock-names = "core_clk";
+ clocks = <&clock_gcc GCC_USB30_MASTER_CLK>;
+ status = "ok";
+};
+
+&gdsc_ufs {
+ status = "ok";
+};
+
+&gdsc_bimc_smmu {
+ clock-names = "bus_clk";
+ clocks = <&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>;
+ proxy-supply = <&gdsc_bimc_smmu>;
+ qcom,proxy-consumer-enable;
+ status = "ok";
+};
+
+&gdsc_hlos1_vote_lpass_adsp {
+ status = "ok";
+};
+
+&gdsc_venus {
+ status = "ok";
+};
+
+&gdsc_venus_core0 {
+ qcom,support-hw-trigger;
+ status = "ok";
+};
+
+&gdsc_camss_top {
+ status = "ok";
+};
+
+&gdsc_vfe0 {
+ parent-supply = <&gdsc_camss_top>;
+ status = "ok";
+};
+
+&gdsc_vfe1 {
+ parent-supply = <&gdsc_camss_top>;
+ status = "ok";
+};
+
+&gdsc_cpp {
+ parent-supply = <&gdsc_camss_top>;
+ status = "ok";
+};
+
+&gdsc_mdss {
+ clock-names = "bus_clk", "rot_clk";
+ clocks = <&clock_mmss MMSS_MDSS_AXI_CLK>,
+ <&clock_mmss 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 GCC_GPU_BIMC_GFX_CLK>,
+ <&clock_gfx GPUCC_GFX3D_CLK>,
+ <&clock_gfx GFX3D_CLK_SRC>;
+ qcom,force-enable-root-clk;
+ parent-supply = <&gfx_vreg_corner>;
+ status = "ok";
+};
+
+&gdsc_gpu_cx {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/qcom/msmhamster.dtsi b/arch/arm/boot/dts/qcom/msmhamster.dtsi
index 4669fa519f5d..952740ae54e8 100644
--- a/arch/arm/boot/dts/qcom/msmhamster.dtsi
+++ b/arch/arm/boot/dts/qcom/msmhamster.dtsi
@@ -70,3 +70,7 @@
qcom,sensors = <7>;
qcom,slope = <2901 2846 3200 3200 3200 3200 3200>;
};
+
+&slv_ebi {
+ qcom,buswidth = <4>;
+};
diff --git a/arch/arm/boot/dts/qcom/msmtriton-rumi.dts b/arch/arm/boot/dts/qcom/msmtriton-rumi.dts
new file mode 100644
index 000000000000..d3c62dbf99f2
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmtriton-rumi.dts
@@ -0,0 +1,29 @@
+/* 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.
+ */
+
+
+/dts-v1/;
+
+#include "msmtriton.dtsi"
+#include "msmfalcon-pinctrl.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM TRITON RUMI";
+ compatible = "qcom,msmtriton-rumi", "qcom,msmtriton", "qcom,rumi";
+ qcom,board-id = <15 0>;
+};
+
+&uartblsp1dm1 {
+ status = "ok";
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart_console_active>;
+};
diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi
new file mode 100644
index 000000000000..17198e462024
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi
@@ -0,0 +1,268 @@
+/* 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 "skeleton64.dtsi"
+#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/interrupt-controller/arm-gic.h>
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSMTRITON";
+ compatible = "qcom,msmtriton";
+ qcom,msm-id = <318 0x0>;
+ interrupt-parent = <&intc>;
+
+ aliases {
+ serial0 = &uartblsp1dm1;
+ };
+
+ chosen {
+ stdout-path = "serial0";
+ };
+
+ psci {
+ compatible = "arm,psci-1.0";
+ method = "smc";
+ };
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ CPU0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ };
+
+ CPU1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ };
+
+ CPU2: cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x2>;
+ enable-method = "psci";
+ };
+
+ CPU3: cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x3>;
+ enable-method = "psci";
+ };
+
+ CPU4: cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x100>;
+ enable-method = "psci";
+ };
+
+ CPU5: cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x101>;
+ enable-method = "psci";
+ };
+
+ CPU6: cpu@102 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x102>;
+ enable-method = "psci";
+ };
+
+ CPU7: cpu@103 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x103>;
+ enable-method = "psci";
+ };
+
+ cpu-map {
+ cluster0 {
+ core0 {
+ cpu = <&CPU4>;
+ };
+
+ core1 {
+ cpu = <&CPU5>;
+ };
+
+ core2 {
+ cpu = <&CPU6>;
+ };
+
+ core3 {
+ cpu = <&CPU7>;
+ };
+ };
+
+ cluster1 {
+ core0 {
+ cpu = <&CPU0>;
+ };
+
+ core1 {
+ cpu = <&CPU1>;
+ };
+
+ core2 {
+ cpu = <&CPU2>;
+ };
+
+ core3 {
+ cpu = <&CPU3>;
+ };
+ };
+ };
+ };
+
+ soc: soc { };
+
+};
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0xffffffff>;
+ compatible = "simple-bus";
+
+ intc: interrupt-controller@17a00000 {
+ compatible = "arm,gic-v3";
+ reg = <0x17a00000 0x10000>, /* GICD */
+ <0x17b00000 0x100000>; /* GICR * 8 */
+ #interrupt-cells = <3>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ interrupt-controller;
+ #redistributor-regions = <1>;
+ redistributor-stride = <0x0 0x20000>;
+ interrupts = <1 9 4>;
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <1 1 0xf08>,
+ <1 2 0xf08>,
+ <1 3 0xf08>,
+ <1 0 0xf08>;
+ clock-frequency = <19200000>;
+ };
+
+ qcom,sps {
+ compatible = "qcom,msm_sps_4k";
+ qcom,pipe-attr-ee;
+ };
+
+ uartblsp1dm1: serial@0c170000 {
+ compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+ reg = <0xc170000 0x1000>;
+ interrupts = <0 108 0>;
+ status = "disabled";
+ clocks = <&clock_gcc GCC_BLSP1_UART2_APPS_CLK>,
+ <&clock_gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ };
+
+ uartblsp2dm1: serial@0c1b0000 {
+ compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+ reg = <0xc1b0000 0x1000>;
+ interrupts = <0 114 0>;
+ status = "disabled";
+ clocks = <&clock_gcc GCC_BLSP2_UART2_APPS_CLK>,
+ <&clock_gcc GCC_BLSP2_AHB_CLK>;
+ clock-names = "core", "iface";
+ };
+
+ timer@17920000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ compatible = "arm,armv7-timer-mem";
+ reg = <0x17920000 0x1000>;
+ clock-frequency = <19200000>;
+
+ frame@17921000 {
+ frame-number = <0>;
+ interrupts = <0 8 0x4>,
+ <0 7 0x4>;
+ reg = <0x17921000 0x1000>,
+ <0x17922000 0x1000>;
+ };
+
+ frame@17923000 {
+ frame-number = <1>;
+ interrupts = <0 9 0x4>;
+ reg = <0x17923000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17924000 {
+ frame-number = <2>;
+ interrupts = <0 10 0x4>;
+ reg = <0x17924000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17925000 {
+ frame-number = <3>;
+ interrupts = <0 11 0x4>;
+ reg = <0x17925000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17926000 {
+ frame-number = <4>;
+ interrupts = <0 12 0x4>;
+ reg = <0x17926000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17927000 {
+ frame-number = <5>;
+ interrupts = <0 13 0x4>;
+ reg = <0x17927000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17928000 {
+ frame-number = <6>;
+ interrupts = <0 14 0x4>;
+ reg = <0x17928000 0x1000>;
+ status = "disabled";
+ };
+ };
+
+ clock_gcc: qcom,dummycc {
+ compatible = "qcom,dummycc";
+ #clock-cells = <1>;
+ };
+
+ clock_mmss: qcom,dummycc {
+ compatible = "qcom,dummycc";
+ #clock-cells = <1>;
+ };
+
+ clock_gfx: qcom,dummycc {
+ compatible = "qcom,dummycc";
+ #clock-cells = <1>;
+ };
+};
diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig
index 1e6a1c66ed82..0a20c52bd3b2 100644
--- a/arch/arm/configs/msmcortex_defconfig
+++ b/arch/arm/configs/msmcortex_defconfig
@@ -460,6 +460,7 @@ 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
CONFIG_QCOM_BIMC_BWMON=y
diff --git a/arch/arm/configs/msmfalcon_defconfig b/arch/arm/configs/msmfalcon_defconfig
index 1e6a1c66ed82..0a20c52bd3b2 100644
--- a/arch/arm/configs/msmfalcon_defconfig
+++ b/arch/arm/configs/msmfalcon_defconfig
@@ -460,6 +460,7 @@ 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
CONFIG_QCOM_BIMC_BWMON=y
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index 69261c70e1dd..d6ed9ac56bf1 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -16,6 +16,7 @@ config ARCH_MSMFALCON
select MULTI_IRQ_HANDLER
select HAVE_ARM_ARCH_TIMER
select MAY_HAVE_SPARSE_IRQ
+ select COMMON_CLK_MSM
select PINCTRL_MSM_TLMM
select USE_PINCTRL_IRQ
select MSM_PM if PM
diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c
index 4b4058db0781..66353caa35b9 100644
--- a/arch/arm/mm/mmap.c
+++ b/arch/arm/mm/mmap.c
@@ -173,7 +173,7 @@ unsigned long arch_mmap_rnd(void)
{
unsigned long rnd;
- rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
+ rnd = get_random_long() & ((1UL << mmap_rnd_bits) - 1);
return rnd << PAGE_SHIFT;
}
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index eb02bc09b63d..ee4efe58d0c8 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -95,6 +95,15 @@ config ARCH_MSMFALCON
If you do not wish to build a kernel that runs
on this chipset,say 'N' here.
+config ARCH_MSMTRITON
+ bool "Enable Support for Qualcomm Technologies Inc MSMTRITON"
+ depends on ARCH_QCOM
+ select COMMON_CLK_MSM
+ help
+ This enables support for the MSMTRITON chipset.
+ If you do not wish to build a kernel that runs
+ on this chipset,say 'N' here.
+
config ARCH_ROCKCHIP
bool "Rockchip Platforms"
select ARCH_HAS_RESET_CONTROLLER
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 97b6c27bd2df..08288e1b5c25 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -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
@@ -380,6 +381,7 @@ CONFIG_MSM_VIDC_V4L2=m
CONFIG_MSM_VIDC_VMEM=m
CONFIG_MSM_VIDC_GOVERNORS=m
CONFIG_MSM_SDE_ROTATOR=y
+CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
CONFIG_QCOM_KGSL=y
CONFIG_FB=y
CONFIG_FB_ARMCLCD=y
@@ -492,6 +494,7 @@ CONFIG_MSM_GLINK_SMD_XPRT=y
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
CONFIG_MSM_GLINK_SPI_XPRT=y
CONFIG_MSM_SPCOM=y
+CONFIG_MSM_SPSS_UTILS=y
CONFIG_MSM_SMEM_LOGGING=y
CONFIG_MSM_SMP2P=y
CONFIG_MSM_SMP2P_TEST=y
@@ -541,11 +544,13 @@ CONFIG_DEVFREQ_SPDM=y
CONFIG_EXTCON=y
CONFIG_IIO=y
CONFIG_QCOM_RRADC=y
+CONFIG_QCOM_TADC=y
CONFIG_PWM=y
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
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index 99c584c323d8..9e2727c4fe1e 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -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
@@ -383,6 +384,7 @@ CONFIG_MSM_VIDC_V4L2=m
CONFIG_MSM_VIDC_VMEM=m
CONFIG_MSM_VIDC_GOVERNORS=m
CONFIG_MSM_SDE_ROTATOR=y
+CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
CONFIG_QCOM_KGSL=y
CONFIG_FB=y
CONFIG_FB_VIRTUAL=y
@@ -460,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
@@ -505,6 +506,7 @@ CONFIG_MSM_GLINK_SMD_XPRT=y
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
CONFIG_MSM_GLINK_SPI_XPRT=y
CONFIG_MSM_SPCOM=y
+CONFIG_MSM_SPSS_UTILS=y
CONFIG_MSM_SMEM_LOGGING=y
CONFIG_MSM_SMP2P=y
CONFIG_MSM_SMP2P_TEST=y
@@ -523,6 +525,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
@@ -560,6 +563,7 @@ CONFIG_DEVFREQ_SPDM=y
CONFIG_EXTCON=y
CONFIG_IIO=y
CONFIG_QCOM_RRADC=y
+CONFIG_QCOM_TADC=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_ARM_GIC_V3_ACL=y
diff --git a/arch/arm64/configs/msmfalcon-perf_defconfig b/arch/arm64/configs/msmfalcon-perf_defconfig
index f2eafd610cac..39c2d3f71c5a 100644
--- a/arch/arm64/configs/msmfalcon-perf_defconfig
+++ b/arch/arm64/configs/msmfalcon-perf_defconfig
@@ -44,6 +44,7 @@ CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSMCOBALT=y
CONFIG_ARCH_MSMHAMSTER=y
CONFIG_ARCH_MSMFALCON=y
+CONFIG_ARCH_MSMTRITON=y
CONFIG_PCI=y
CONFIG_PCI_MSM=y
CONFIG_SCHED_MC=y
diff --git a/arch/arm64/configs/msmfalcon_defconfig b/arch/arm64/configs/msmfalcon_defconfig
index 3742fe210dc2..a277038b3fc3 100644
--- a/arch/arm64/configs/msmfalcon_defconfig
+++ b/arch/arm64/configs/msmfalcon_defconfig
@@ -45,6 +45,7 @@ CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSMCOBALT=y
CONFIG_ARCH_MSMHAMSTER=y
CONFIG_ARCH_MSMFALCON=y
+CONFIG_ARCH_MSMTRITON=y
CONFIG_PCI=y
CONFIG_PCI_MSM=y
CONFIG_SCHED_MC=y
diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h
index d925715c822f..30cf6f5961ef 100644
--- a/arch/arm64/include/asm/arch_gicv3.h
+++ b/arch/arm64/include/asm/arch_gicv3.h
@@ -172,8 +172,8 @@ static inline void gic_write_sre(u32 val)
isb();
}
-#define gic_read_typer(c) readq_relaxed(c)
-#define gic_write_irouter(v, c) writeq_relaxed(v, c)
+#define gic_read_typer(c) readq_relaxed_no_log(c)
+#define gic_write_irouter(v, c) writeq_relaxed_no_log(v, c)
#endif /* __ASSEMBLY__ */
#endif /* __ASM_ARCH_GICV3_H */
diff --git a/arch/arm64/kernel/io.c b/arch/arm64/kernel/io.c
index aeb0f25c9289..471fb3cb8c5f 100644
--- a/arch/arm64/kernel/io.c
+++ b/arch/arm64/kernel/io.c
@@ -28,21 +28,21 @@ void __memcpy_fromio(void *to, const volatile void __iomem *from, size_t count)
{
while (count && (!IS_ALIGNED((unsigned long)from, 8) ||
!IS_ALIGNED((unsigned long)to, 8))) {
- *(u8 *)to = __raw_readb(from);
+ *(u8 *)to = __raw_readb_no_log(from);
from++;
to++;
count--;
}
while (count >= 8) {
- *(u64 *)to = __raw_readq(from);
+ *(u64 *)to = __raw_readq_no_log(from);
from += 8;
to += 8;
count -= 8;
}
while (count) {
- *(u8 *)to = __raw_readb(from);
+ *(u8 *)to = __raw_readb_no_log(from);
from++;
to++;
count--;
@@ -57,21 +57,21 @@ void __memcpy_toio(volatile void __iomem *to, const void *from, size_t count)
{
while (count && (!IS_ALIGNED((unsigned long)to, 8) ||
!IS_ALIGNED((unsigned long)from, 8))) {
- __raw_writeb(*(volatile u8 *)from, to);
+ __raw_writeb_no_log(*(volatile u8 *)from, to);
from++;
to++;
count--;
}
while (count >= 8) {
- __raw_writeq(*(volatile u64 *)from, to);
+ __raw_writeq_no_log(*(volatile u64 *)from, to);
from += 8;
to += 8;
count -= 8;
}
while (count) {
- __raw_writeb(*(volatile u8 *)from, to);
+ __raw_writeb_no_log(*(volatile u8 *)from, to);
from++;
to++;
count--;
@@ -91,19 +91,19 @@ void __memset_io(volatile void __iomem *dst, int c, size_t count)
qc |= qc << 32;
while (count && !IS_ALIGNED((unsigned long)dst, 8)) {
- __raw_writeb(c, dst);
+ __raw_writeb_no_log(c, dst);
dst++;
count--;
}
while (count >= 8) {
- __raw_writeq(qc, dst);
+ __raw_writeq_no_log(qc, dst);
dst += 8;
count -= 8;
}
while (count) {
- __raw_writeb(c, dst);
+ __raw_writeb_no_log(c, dst);
dst++;
count--;
}
diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c
index 4c893b5189dd..232f787a088a 100644
--- a/arch/arm64/mm/mmap.c
+++ b/arch/arm64/mm/mmap.c
@@ -53,10 +53,10 @@ unsigned long arch_mmap_rnd(void)
#ifdef CONFIG_COMPAT
if (test_thread_flag(TIF_32BIT))
- rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_compat_bits) - 1);
+ rnd = get_random_long() & ((1UL << mmap_rnd_compat_bits) - 1);
else
#endif
- rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
+ rnd = get_random_long() & ((1UL << mmap_rnd_bits) - 1);
return rnd << PAGE_SHIFT;
}
diff --git a/arch/mips/mm/mmap.c b/arch/mips/mm/mmap.c
index 5c81fdd032c3..353037699512 100644
--- a/arch/mips/mm/mmap.c
+++ b/arch/mips/mm/mmap.c
@@ -146,7 +146,7 @@ unsigned long arch_mmap_rnd(void)
{
unsigned long rnd;
- rnd = (unsigned long)get_random_int();
+ rnd = get_random_long();
rnd <<= PAGE_SHIFT;
if (TASK_IS_32BIT_ADDR)
rnd &= 0xfffffful;
@@ -174,7 +174,7 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
static inline unsigned long brk_rnd(void)
{
- unsigned long rnd = get_random_int();
+ unsigned long rnd = get_random_long();
rnd = rnd << PAGE_SHIFT;
/* 8MB for 32bit, 256MB for 64bit */
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index ef2ad2d682da..36795d1e7558 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -1659,9 +1659,9 @@ static inline unsigned long brk_rnd(void)
/* 8MB for 32bit, 1GB for 64bit */
if (is_32bit_task())
- rnd = (long)(get_random_int() % (1<<(23-PAGE_SHIFT)));
+ rnd = (get_random_long() % (1UL<<(23-PAGE_SHIFT)));
else
- rnd = (long)(get_random_int() % (1<<(30-PAGE_SHIFT)));
+ rnd = (get_random_long() % (1UL<<(30-PAGE_SHIFT)));
return rnd << PAGE_SHIFT;
}
diff --git a/arch/powerpc/mm/mmap.c b/arch/powerpc/mm/mmap.c
index 0f0502e12f6c..4087705ba90f 100644
--- a/arch/powerpc/mm/mmap.c
+++ b/arch/powerpc/mm/mmap.c
@@ -59,9 +59,9 @@ unsigned long arch_mmap_rnd(void)
/* 8MB for 32bit, 1GB for 64bit */
if (is_32bit_task())
- rnd = (unsigned long)get_random_int() % (1<<(23-PAGE_SHIFT));
+ rnd = get_random_long() % (1<<(23-PAGE_SHIFT));
else
- rnd = (unsigned long)get_random_int() % (1<<(30-PAGE_SHIFT));
+ rnd = get_random_long() % (1UL<<(30-PAGE_SHIFT));
return rnd << PAGE_SHIFT;
}
diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c
index c690c8e16a96..b489e9759518 100644
--- a/arch/sparc/kernel/sys_sparc_64.c
+++ b/arch/sparc/kernel/sys_sparc_64.c
@@ -264,7 +264,7 @@ static unsigned long mmap_rnd(void)
unsigned long rnd = 0UL;
if (current->flags & PF_RANDOMIZE) {
- unsigned long val = get_random_int();
+ unsigned long val = get_random_long();
if (test_thread_flag(TIF_32BIT))
rnd = (val % (1UL << (23UL-PAGE_SHIFT)));
else
diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
index 96bd1e2bffaf..72bb52f93c3d 100644
--- a/arch/x86/mm/mmap.c
+++ b/arch/x86/mm/mmap.c
@@ -71,12 +71,12 @@ unsigned long arch_mmap_rnd(void)
if (mmap_is_ia32())
#ifdef CONFIG_COMPAT
- rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_compat_bits) - 1);
+ rnd = get_random_long() & ((1UL << mmap_rnd_compat_bits) - 1);
#else
- rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
+ rnd = get_random_long() & ((1UL << mmap_rnd_bits) - 1);
#endif
else
- rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
+ rnd = get_random_long() & ((1UL << mmap_rnd_bits) - 1);
return rnd << PAGE_SHIFT;
}
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 6e189a49dca6..2b27747b46d3 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -6,7 +6,8 @@
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/scatterlist.h>
-#include <linux/security.h>
+#include <linux/pfk.h>
+#include <linux/pft.h>
#include "blk.h"
@@ -729,6 +730,12 @@ static void blk_account_io_merge(struct request *req)
}
}
+static bool crypto_not_mergeable(const struct bio *bio, const struct bio *nxt)
+{
+ return (!pft_allow_merge_bio(bio, nxt) ||
+ !pfk_allow_merge_bio(bio, nxt));
+}
+
/*
* Has to be called with the request spinlock acquired
*/
@@ -756,6 +763,9 @@ static int attempt_merge(struct request_queue *q, struct request *req,
!blk_write_same_mergeable(req->bio, next->bio))
return 0;
+ if (crypto_not_mergeable(req->bio, next->bio))
+ return 0;
+
/*
* If we are allowed to merge, then append bio list
* from next to rq and release next. merge_requests_fn
@@ -860,11 +870,9 @@ bool blk_rq_merge_ok(struct request *rq, struct bio *bio)
!blk_write_same_mergeable(rq->bio, bio))
return false;
- /* Don't merge bios of files with different encryption */
- if (!security_allow_merge_bio(rq->bio, bio))
+ if (crypto_not_mergeable(rq->bio, bio))
return false;
-
return true;
}
diff --git a/drivers/Makefile b/drivers/Makefile
index 1761f8f2cda7..eb67aadf2ee0 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/
obj-$(CONFIG_NUBUS) += nubus/
obj-y += macintosh/
obj-$(CONFIG_IDE) += ide/
+obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SCSI) += scsi/
obj-y += nvme/
obj-$(CONFIG_ATA) += ata/
@@ -126,7 +127,6 @@ obj-$(CONFIG_SWITCH) += switch/
obj-$(CONFIG_INFINIBAND) += infiniband/
obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
-obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SUPERH) += sh/
obj-$(CONFIG_ARCH_SHMOBILE) += sh/
ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 5645850cb4d3..152c81ca50ea 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -1555,6 +1555,7 @@ _request_firmware_nowait(
get_device(desc->device);
INIT_WORK(&desc->work, request_firmware_work_func);
+ schedule_work(&desc->work);
return 0;
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 176b59f5bc47..bd70f8db469b 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -117,6 +117,26 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
EXPORT_SYMBOL_GPL(platform_get_irq);
/**
+ * platform_irq_count - Count the number of IRQs a platform device uses
+ * @dev: platform device
+ *
+ * Return: Number of IRQs a platform device uses or EPROBE_DEFER
+ */
+int platform_irq_count(struct platform_device *dev)
+{
+ int ret, nr = 0;
+
+ while ((ret = platform_get_irq(dev, nr)) >= 0)
+ nr++;
+
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ return nr;
+}
+EXPORT_SYMBOL_GPL(platform_irq_count);
+
+/**
* platform_get_resource_byname - get a resource for a device by name
* @dev: platform device
* @type: resource type
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index a54d810f2966..6ed8b9326629 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -35,6 +35,8 @@
#include <linux/timer.h>
#include <linux/wakeup_reason.h>
+#include <asm/current.h>
+
#include "../base.h"
#include "power.h"
@@ -1412,7 +1414,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
goto Complete;
data.dev = dev;
- data.tsk = get_current();
+ data.tsk = current;
init_timer_on_stack(&timer);
timer.expires = jiffies + HZ * 12;
timer.function = dpm_drv_timeout;
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index 7cb64e012f1f..a1721a3b80cc 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -1780,6 +1780,15 @@ int diag_copy_to_user_msg_mask(char __user *buf, size_t count,
if (!mask_info)
return -EIO;
+ mutex_lock(&driver->diag_maskclear_mutex);
+ if (driver->mask_clear) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag:%s: count = %zu\n", __func__, count);
+ mutex_unlock(&driver->diag_maskclear_mutex);
+ return -EIO;
+ }
+ mutex_unlock(&driver->diag_maskclear_mutex);
+
mutex_lock(&mask_info->lock);
mask = (struct diag_msg_mask_t *)(mask_info->ptr);
for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
diff --git a/drivers/char/diag/diag_usb.c b/drivers/char/diag/diag_usb.c
index eb715cc8501c..ca54b24ec604 100644
--- a/drivers/char/diag/diag_usb.c
+++ b/drivers/char/diag/diag_usb.c
@@ -215,6 +215,12 @@ static void usb_connect_work_fn(struct work_struct *work)
*/
static void usb_disconnect(struct diag_usb_info *ch)
{
+ if (!ch)
+ return;
+
+ if (!atomic_read(&ch->connected) && driver->usb_connected)
+ diag_clear_masks(NULL);
+
if (ch && ch->ops && ch->ops->close)
ch->ops->close(ch->ctxt, DIAG_USB_MODE);
}
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index dccaa6a0d9c4..2aef98f4fe04 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -467,6 +467,8 @@ struct diagchar_dev {
struct class *diagchar_class;
struct device *diag_dev;
int ref_count;
+ int mask_clear;
+ struct mutex diag_maskclear_mutex;
struct mutex diagchar_mutex;
struct mutex diag_file_mutex;
wait_queue_head_t wait_q;
@@ -625,6 +627,7 @@ void diag_cmd_remove_reg(struct diag_cmd_reg_entry_t *entry, uint8_t proc);
void diag_cmd_remove_reg_by_pid(int pid);
void diag_cmd_remove_reg_by_proc(int proc);
int diag_cmd_chk_polling(struct diag_cmd_reg_entry_t *entry);
+void diag_clear_masks(struct diag_md_session_t *info);
void diag_record_stats(int type, int flag);
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index a39e4929d999..39be6ef3735e 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -389,6 +389,27 @@ static uint32_t diag_translate_kernel_to_user_mask(uint32_t peripheral_mask)
return ret;
}
+void diag_clear_masks(struct diag_md_session_t *info)
+{
+ int ret;
+ char cmd_disable_log_mask[] = { 0x73, 0, 0, 0, 0, 0, 0, 0};
+ char cmd_disable_msg_mask[] = { 0x7D, 0x05, 0, 0, 0, 0, 0, 0};
+ char cmd_disable_event_mask[] = { 0x60, 0};
+
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag: %s: masks clear request upon %s\n", __func__,
+ ((info) ? "ODL exit" : "USB Disconnection"));
+
+ ret = diag_process_apps_masks(cmd_disable_log_mask,
+ sizeof(cmd_disable_log_mask), info);
+ ret = diag_process_apps_masks(cmd_disable_msg_mask,
+ sizeof(cmd_disable_msg_mask), info);
+ ret = diag_process_apps_masks(cmd_disable_event_mask,
+ sizeof(cmd_disable_event_mask), info);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "diag:%s: masks cleared successfully\n", __func__);
+}
+
static void diag_close_logging_process(const int pid)
{
int i;
@@ -400,6 +421,12 @@ static void diag_close_logging_process(const int pid)
if (!session_info)
return;
+ diag_clear_masks(session_info);
+
+ mutex_lock(&driver->diag_maskclear_mutex);
+ driver->mask_clear = 1;
+ mutex_unlock(&driver->diag_maskclear_mutex);
+
session_peripheral_mask = session_info->peripheral_mask;
diag_md_session_close(session_info);
for (i = 0; i < NUM_MD_SESSIONS; i++)
@@ -475,9 +502,14 @@ static int diag_remove_client_entry(struct file *file)
}
static int diagchar_close(struct inode *inode, struct file *file)
{
+ int ret;
DIAG_LOG(DIAG_DEBUG_USERSPACE, "diag: process exit %s\n",
current->comm);
- return diag_remove_client_entry(file);
+ ret = diag_remove_client_entry(file);
+ mutex_lock(&driver->diag_maskclear_mutex);
+ driver->mask_clear = 0;
+ mutex_unlock(&driver->diag_maskclear_mutex);
+ return ret;
}
void diag_record_stats(int type, int flag)
@@ -967,7 +999,7 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len,
(void *)driver->hdlc_encode_buf);
send_data:
- err = diagfwd_bridge_write(proc, driver->hdlc_encode_buf,
+ err = diagfwd_bridge_write(bridge_index, driver->hdlc_encode_buf,
driver->hdlc_encode_buf_len);
if (err) {
pr_err_ratelimited("diag: Error writing Callback packet to proc: %d, err: %d\n",
@@ -2538,7 +2570,7 @@ static int diag_user_process_raw_data(const char __user *buf, int len)
}
}
if (remote_proc) {
- ret = diag_send_raw_data_remote(remote_proc - 1,
+ ret = diag_send_raw_data_remote(remote_proc,
(void *)(user_space_data + token_offset),
len, USER_SPACE_RAW_DATA);
if (ret) {
@@ -3358,6 +3390,7 @@ static int __init diagchar_init(void)
non_hdlc_data.len = 0;
mutex_init(&driver->hdlc_disable_mutex);
mutex_init(&driver->diagchar_mutex);
+ mutex_init(&driver->diag_maskclear_mutex);
mutex_init(&driver->diag_file_mutex);
mutex_init(&driver->delayed_rsp_mutex);
mutex_init(&apps_data_mutex);
@@ -3391,13 +3424,13 @@ static int __init diagchar_init(void)
ret = diag_masks_init();
if (ret)
goto fail;
- ret = diag_mux_init();
+ ret = diag_remote_init();
if (ret)
goto fail;
- ret = diagfwd_init();
+ ret = diag_mux_init();
if (ret)
goto fail;
- ret = diag_remote_init();
+ ret = diagfwd_init();
if (ret)
goto fail;
ret = diagfwd_bridge_init();
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 8205e5b05d85..0111b02634c8 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1232,8 +1232,6 @@ static int diagfwd_mux_open(int id, int mode)
static int diagfwd_mux_close(int id, int mode)
{
- uint8_t i;
-
switch (mode) {
case DIAG_USB_MODE:
driver->usb_connected = 0;
@@ -1248,15 +1246,16 @@ static int diagfwd_mux_close(int id, int mode)
driver->md_session_mode == DIAG_MD_NONE) ||
(driver->md_session_mode == DIAG_MD_PERIPHERAL)) {
/*
- * In this case the channel must not be closed. This case
- * indicates that the USB is removed but there is a client
- * running in background with Memory Device mode
+ * This case indicates that the USB is removed
+ * but there is a client running in background
+ * with Memory Device mode.
*/
} else {
- for (i = 0; i < NUM_PERIPHERALS; i++) {
- diagfwd_close(i, TYPE_DATA);
- diagfwd_close(i, TYPE_CMD);
- }
+ /*
+ * With clearing of masks on ODL exit and
+ * USB disconnection, closing of the channel is
+ * not needed.This enables read and drop of stale packets.
+ */
/* Re enable HDLC encoding */
pr_debug("diag: In %s, re-enabling HDLC encoding\n",
__func__);
diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c
index 701677b30a71..0462a64614f3 100644
--- a/drivers/char/diag/diagfwd_bridge.c
+++ b/drivers/char/diag/diagfwd_bridge.c
@@ -107,7 +107,7 @@ static int diagfwd_bridge_mux_write_done(unsigned char *buf, int len,
if (id < 0 || id >= NUM_REMOTE_DEV)
return -EINVAL;
- ch = &bridge_info[id];
+ ch = &bridge_info[buf_ctx];
if (ch->dev_ops && ch->dev_ops->fwd_complete)
ch->dev_ops->fwd_complete(ch->ctxt, buf, len, 0);
return 0;
diff --git a/drivers/char/diag/diagfwd_glink.c b/drivers/char/diag/diagfwd_glink.c
index 0a6d8bb7b21f..fea1b74aacae 100644
--- a/drivers/char/diag/diagfwd_glink.c
+++ b/drivers/char/diag/diagfwd_glink.c
@@ -475,7 +475,7 @@ static void diag_glink_open_work_fn(struct work_struct *work)
struct glink_open_config open_cfg;
void *handle = NULL;
- if (!glink_info)
+ if (!glink_info || glink_info->hdl)
return;
memset(&open_cfg, 0, sizeof(struct glink_open_config));
@@ -499,11 +499,12 @@ static void diag_glink_close_work_fn(struct work_struct *work)
struct diag_glink_info *glink_info = container_of(work,
struct diag_glink_info,
close_work);
- if (!glink_info->inited)
+ if (!glink_info || !glink_info->inited || !glink_info->hdl)
return;
glink_close(glink_info->hdl);
atomic_set(&glink_info->opened, 0);
+ glink_info->hdl = NULL;
diagfwd_channel_close(glink_info->fwd_ctxt);
}
@@ -586,6 +587,7 @@ static void __diag_glink_init(struct diag_glink_info *glink_info)
{
char wq_name[DIAG_GLINK_NAME_SZ + 12];
struct glink_link_info link_info;
+ void *link_state_handle = NULL;
if (!glink_info)
return;
@@ -606,23 +608,25 @@ static void __diag_glink_init(struct diag_glink_info *glink_info)
INIT_WORK(&(glink_info->read_work), diag_glink_read_work_fn);
link_info.glink_link_state_notif_cb = diag_glink_notify_cb;
link_info.transport = NULL;
- strlcpy((char *)link_info.edge, glink_info->edge,
- sizeof(link_info.edge));
- glink_info->hdl = glink_register_link_state_cb(&link_info,
+ link_info.edge = glink_info->edge;
+ glink_info->link_state_handle = NULL;
+ link_state_handle = glink_register_link_state_cb(&link_info,
(void *)glink_info);
- if (IS_ERR_OR_NULL(glink_info->hdl)) {
+ if (IS_ERR_OR_NULL(link_state_handle)) {
pr_err("diag: In %s, unable to register for glink channel %s\n",
__func__, glink_info->name);
destroy_workqueue(glink_info->wq);
return;
}
+ glink_info->link_state_handle = link_state_handle;
glink_info->fwd_ctxt = NULL;
atomic_set(&glink_info->tx_intent_ready, 0);
atomic_set(&glink_info->opened, 0);
atomic_set(&glink_info->diag_state, 0);
DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
"%s initialized fwd_ctxt: %pK hdl: %pK\n",
- glink_info->name, glink_info->fwd_ctxt, glink_info->hdl);
+ glink_info->name, glink_info->fwd_ctxt,
+ glink_info->link_state_handle);
}
void diag_glink_invalidate(void *ctxt, struct diagfwd_info *fwd_ctxt)
diff --git a/drivers/char/diag/diagfwd_glink.h b/drivers/char/diag/diagfwd_glink.h
index 3f00a7ed60a8..bad4629b5ab8 100644
--- a/drivers/char/diag/diagfwd_glink.h
+++ b/drivers/char/diag/diagfwd_glink.h
@@ -25,6 +25,7 @@ struct diag_glink_info {
uint32_t fifo_size;
atomic_t tx_intent_ready;
void *hdl;
+ void *link_state_handle;
char edge[DIAG_GLINK_NAME_SZ];
char name[DIAG_GLINK_NAME_SZ];
struct mutex lock;
diff --git a/drivers/char/diag/diagfwd_mhi.c b/drivers/char/diag/diagfwd_mhi.c
index b8ed216faaf6..f7b1e98f22b0 100644
--- a/drivers/char/diag/diagfwd_mhi.c
+++ b/drivers/char/diag/diagfwd_mhi.c
@@ -404,8 +404,11 @@ static void mhi_read_done_work_fn(struct work_struct *work)
* buffers here and do not forward them to the mux layer.
*/
if ((atomic_read(&(mhi_info->read_ch.opened)))) {
- diag_remote_dev_read_done(mhi_info->dev_id, buf,
+ err = diag_remote_dev_read_done(mhi_info->dev_id, buf,
result.bytes_xferd);
+ if (err)
+ mhi_buf_tbl_remove(mhi_info, TYPE_MHI_READ_CH,
+ buf, result.bytes_xferd);
} else {
mhi_buf_tbl_remove(mhi_info, TYPE_MHI_READ_CH, buf,
result.bytes_xferd);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index d0da5d852d41..b583e5336630 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1819,6 +1819,28 @@ unsigned int get_random_int(void)
EXPORT_SYMBOL(get_random_int);
/*
+ * Same as get_random_int(), but returns unsigned long.
+ */
+unsigned long get_random_long(void)
+{
+ __u32 *hash;
+ unsigned long ret;
+
+ if (arch_get_random_long(&ret))
+ return ret;
+
+ hash = get_cpu_var(get_random_int_hash);
+
+ hash[0] += current->pid + jiffies + random_get_entropy();
+ md5_transform(hash, random_int_secret);
+ ret = *(unsigned long *)hash;
+ put_cpu_var(get_random_int_hash);
+
+ return ret;
+}
+EXPORT_SYMBOL(get_random_long);
+
+/*
* randomize_range() returns a start address such that
*
* [...... <range> .....]
diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig
index d94678e9e4fc..650a23ea1ead 100644
--- a/drivers/clk/msm/Kconfig
+++ b/drivers/clk/msm/Kconfig
@@ -2,6 +2,7 @@ config COMMON_CLK_MSM
tristate "Support for MSM clock controllers"
depends on OF
depends on ARCH_QCOM
+ select RATIONAL
help
This support clock controller used by MSM devices which support
global, mmss and gpu clock controller.
diff --git a/drivers/clk/msm/clock-gcc-cobalt.c b/drivers/clk/msm/clock-gcc-cobalt.c
index 2049a0bf7dd2..05272118af16 100644
--- a/drivers/clk/msm/clock-gcc-cobalt.c
+++ b/drivers/clk/msm/clock-gcc-cobalt.c
@@ -32,6 +32,7 @@
#include <dt-bindings/clock/msm-clocks-hwio-cobalt.h>
#include "vdd-level-cobalt.h"
+#include "reset.h"
static void __iomem *virt_base;
static void __iomem *virt_dbgbase;
@@ -240,8 +241,8 @@ DEFINE_EXT_CLK(gpll4_out_main, &gpll4.c);
static struct clk_freq_tbl ftbl_hmss_ahb_clk_src[] = {
F( 19200000, cxo_clk_src_ao, 1, 0, 0),
- F( 37500000, gpll0_out_main, 16, 0, 0),
- F( 75000000, gpll0_out_main, 8, 0, 0),
+ F( 50000000, gpll0_out_main, 12, 0, 0),
+ F( 100000000, gpll0_out_main, 6, 0, 0),
F_END
};
@@ -1199,6 +1200,17 @@ static struct branch_clk gcc_aggre1_ufs_axi_clk = {
},
};
+static struct hw_ctl_clk gcc_aggre1_ufs_axi_hw_ctl_clk = {
+ .cbcr_reg = GCC_AGGRE1_UFS_AXI_CBCR,
+ .base = &virt_base,
+ .c = {
+ .dbg_name = "gcc_aggre1_ufs_axi_hw_ctl_clk",
+ .parent = &gcc_aggre1_ufs_axi_clk.c,
+ .ops = &clk_ops_branch_hw_ctl,
+ CLK_INIT(gcc_aggre1_ufs_axi_hw_ctl_clk.c),
+ },
+};
+
static struct branch_clk gcc_aggre1_usb3_axi_clk = {
.cbcr_reg = GCC_AGGRE1_USB3_AXI_CBCR,
.has_sibling = 1,
@@ -2353,11 +2365,13 @@ static struct mux_clk gcc_debug_mux = {
&gpu_gcc_debug_clk.c,
&gfx_gcc_debug_clk.c,
&debug_mmss_clk.c,
+ &debug_cpu_clk.c,
),
MUX_SRC_LIST(
{ &gpu_gcc_debug_clk.c, 0x013d },
{ &gfx_gcc_debug_clk.c, 0x013d },
{ &debug_mmss_clk.c, 0x0022 },
+ { &debug_cpu_clk.c, 0x00c0 },
{ &snoc_clk.c, 0x0000 },
{ &cnoc_clk.c, 0x000e },
{ &bimc_clk.c, 0x00a9 },
@@ -2594,6 +2608,7 @@ static struct clk_lookup msm_clocks_gcc_cobalt[] = {
CLK_LIST(gcc_qusb2phy_sec_reset),
CLK_LIST(gpll0_out_msscc),
CLK_LIST(gcc_aggre1_ufs_axi_clk),
+ CLK_LIST(gcc_aggre1_ufs_axi_hw_ctl_clk),
CLK_LIST(gcc_aggre1_usb3_axi_clk),
CLK_LIST(gcc_bimc_mss_q6_axi_clk),
CLK_LIST(gcc_blsp1_ahb_clk),
@@ -2695,6 +2710,23 @@ static struct clk_lookup msm_clocks_gcc_cobalt[] = {
CLK_LIST(gcc_qspi_ref_clk),
};
+static const struct msm_reset_map gcc_cobalt_resets[] = {
+ [QUSB2PHY_PRIM_BCR] = { 0x12000 },
+ [QUSB2PHY_SEC_BCR] = { 0x12004 },
+ [BLSP1_BCR] = { 0x17000 },
+ [BLSP2_BCR] = { 0x25000 },
+ [BOOT_ROM_BCR] = { 0x38000 },
+ [PRNG_BCR] = { 0x34000 },
+ [UFS_BCR] = { 0x75000 },
+ [USB_30_BCR] = { 0x0f000 },
+ [USB3_PHY_BCR] = { 0x50020 },
+ [USB3PHY_PHY_BCR] = { 0x50024 },
+ [PCIE_0_PHY_BCR] = { 0x6c01c },
+ [PCIE_PHY_BCR] = { 0x6f000 },
+ [PCIE_PHY_NOCSR_COM_PHY_BCR] = { 0x6f00C },
+ [PCIE_PHY_COM_BCR] = { 0x6f014 },
+};
+
static void msm_gcc_cobalt_v1_fixup(void)
{
gcc_ufs_rx_symbol_1_clk.c.ops = &clk_ops_dummy;
@@ -2800,6 +2832,10 @@ static int msm_gcc_cobalt_probe(struct platform_device *pdev)
clk_set_flags(&gcc_gpu_bimc_gfx_clk.c, CLKFLAG_RETAIN_MEM);
+ /* Register block resets */
+ msm_reset_controller_register(pdev, gcc_cobalt_resets,
+ ARRAY_SIZE(gcc_cobalt_resets), virt_base);
+
dev_info(&pdev->dev, "Registered GCC clocks\n");
return 0;
}
@@ -2831,6 +2867,7 @@ static struct clk_lookup msm_clocks_measure_cobalt[] = {
CLK_LIST(gpu_gcc_debug_clk),
CLK_LIST(gfx_gcc_debug_clk),
CLK_LIST(debug_mmss_clk),
+ CLK_LIST(debug_cpu_clk),
CLK_LOOKUP_OF("measure", gcc_debug_mux, "debug"),
};
@@ -2867,6 +2904,9 @@ static int msm_clock_debug_cobalt_probe(struct platform_device *pdev)
debug_mmss_clk.dev = &pdev->dev;
debug_mmss_clk.clk_id = "debug_mmss_clk";
+ debug_cpu_clk.dev = &pdev->dev;
+ debug_cpu_clk.clk_id = "debug_cpu_clk";
+
ret = of_msm_clock_register(pdev->dev.of_node,
msm_clocks_measure_cobalt,
ARRAY_SIZE(msm_clocks_measure_cobalt));
diff --git a/drivers/clk/msm/clock-mmss-cobalt.c b/drivers/clk/msm/clock-mmss-cobalt.c
index 37ce3798694d..1a8083e74f5f 100644
--- a/drivers/clk/msm/clock-mmss-cobalt.c
+++ b/drivers/clk/msm/clock-mmss-cobalt.c
@@ -30,6 +30,7 @@
#include <dt-bindings/clock/msm-clocks-hwio-cobalt.h>
#include "vdd-level-cobalt.h"
+#include "reset.h"
static void __iomem *virt_base;
@@ -82,6 +83,7 @@ DEFINE_EXT_CLK(ext_dp_phy_pll_vco, NULL);
DEFINE_EXT_CLK(ext_dp_phy_pll_link, NULL);
static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL);
+static DEFINE_VDD_REGULATORS(vdd_mmsscc_mx, VDD_DIG_NUM, 1, vdd_corner, NULL);
static struct alpha_pll_masks pll_masks_p = {
.lock_mask = BIT(31),
@@ -101,7 +103,7 @@ static struct pll_vote_clk mmpll0_pll = {
.parent = &mmsscc_xo.c,
.dbg_name = "mmpll0",
.ops = &clk_ops_pll_vote,
- VDD_DIG_FMAX_MAP2(LOWER, 404000000, NOMINAL, 808000000),
+ VDD_MM_PLL_FMAX_MAP2(LOWER, 404000000, NOMINAL, 808000000),
CLK_INIT(mmpll0_pll.c),
},
};
@@ -118,7 +120,7 @@ static struct pll_vote_clk mmpll1_pll = {
.parent = &mmsscc_xo.c,
.dbg_name = "mmpll1_pll",
.ops = &clk_ops_pll_vote,
- VDD_DIG_FMAX_MAP2(LOWER, 406000000, NOMINAL, 812000000),
+ VDD_MM_PLL_FMAX_MAP2(LOWER, 406000000, NOMINAL, 812000000),
CLK_INIT(mmpll1_pll.c),
},
};
@@ -135,7 +137,7 @@ static struct alpha_pll_clk mmpll3_pll = {
.parent = &mmsscc_xo.c,
.dbg_name = "mmpll3_pll",
.ops = &clk_ops_fixed_fabia_alpha_pll,
- VDD_DIG_FMAX_MAP2(LOWER, 465000000, LOW, 930000000),
+ VDD_MM_PLL_FMAX_MAP2(LOWER, 465000000, LOW, 930000000),
CLK_INIT(mmpll3_pll.c),
},
};
@@ -152,7 +154,7 @@ static struct alpha_pll_clk mmpll4_pll = {
.parent = &mmsscc_xo.c,
.dbg_name = "mmpll4_pll",
.ops = &clk_ops_fixed_fabia_alpha_pll,
- VDD_DIG_FMAX_MAP2(LOWER, 384000000, LOW, 768000000),
+ VDD_MM_PLL_FMAX_MAP2(LOWER, 384000000, LOW, 768000000),
CLK_INIT(mmpll4_pll.c),
},
};
@@ -169,7 +171,7 @@ static struct alpha_pll_clk mmpll5_pll = {
.parent = &mmsscc_xo.c,
.dbg_name = "mmpll5_pll",
.ops = &clk_ops_fixed_fabia_alpha_pll,
- VDD_DIG_FMAX_MAP2(LOWER, 412500000, LOW, 825000000),
+ VDD_MM_PLL_FMAX_MAP2(LOWER, 412500000, LOW, 825000000),
CLK_INIT(mmpll5_pll.c),
},
};
@@ -186,7 +188,7 @@ static struct alpha_pll_clk mmpll6_pll = {
.parent = &mmsscc_xo.c,
.dbg_name = "mmpll6_pll",
.ops = &clk_ops_fixed_fabia_alpha_pll,
- VDD_DIG_FMAX_MAP2(LOWER, 360000000, NOMINAL, 720000000),
+ VDD_MM_PLL_FMAX_MAP2(LOWER, 360000000, NOMINAL, 720000000),
CLK_INIT(mmpll6_pll.c),
},
};
@@ -203,7 +205,7 @@ static struct alpha_pll_clk mmpll7_pll = {
.parent = &mmsscc_xo.c,
.dbg_name = "mmpll7_pll",
.ops = &clk_ops_fixed_fabia_alpha_pll,
- VDD_DIG_FMAX_MAP2(LOWER, 480000000, NOMINAL, 960000000),
+ VDD_MM_PLL_FMAX_MAP1(LOW, 960000000),
CLK_INIT(mmpll7_pll.c),
},
};
@@ -220,7 +222,7 @@ static struct alpha_pll_clk mmpll10_pll = {
.parent = &mmsscc_xo.c,
.dbg_name = "mmpll10_pll",
.ops = &clk_ops_fixed_fabia_alpha_pll,
- VDD_DIG_FMAX_MAP2(LOWER, 288000000, NOMINAL, 576000000),
+ VDD_MM_PLL_FMAX_MAP2(LOWER, 288000000, NOMINAL, 576000000),
CLK_INIT(mmpll10_pll.c),
},
};
@@ -2625,7 +2627,6 @@ static struct clk_lookup msm_clocks_mmss_cobalt[] = {
CLK_LIST(mmss_misc_ahb_clk),
CLK_LIST(mmss_misc_cxo_clk),
CLK_LIST(mmss_mnoc_ahb_clk),
- CLK_LIST(mmss_mnoc_maxi_clk),
CLK_LIST(mmss_video_subcore0_clk),
CLK_LIST(mmss_video_subcore1_clk),
CLK_LIST(mmss_video_ahb_clk),
@@ -2634,9 +2635,14 @@ static struct clk_lookup msm_clocks_mmss_cobalt[] = {
CLK_LIST(mmss_video_maxi_clk),
CLK_LIST(mmss_vmem_ahb_clk),
CLK_LIST(mmss_vmem_maxi_clk),
+ CLK_LIST(mmss_mnoc_maxi_clk),
CLK_LIST(mmss_debug_mux),
};
+static const struct msm_reset_map mmss_cobalt_resets[] = {
+ [CAMSS_MICRO_BCR] = { 0x3490 },
+};
+
static void msm_mmsscc_hamster_fixup(void)
{
mmpll3_pll.c.rate = 1066000000;
@@ -2740,10 +2746,6 @@ static void msm_mmsscc_v2_fixup(void)
csiphy_clk_src.c.fmax[VDD_DIG_LOW] = 256000000;
dp_pixel_clk_src.c.fmax[VDD_DIG_LOWER] = 148380000;
-
- video_subcore0_clk_src.c.fmax[VDD_DIG_LOW_L1] = 355200000;
- video_subcore1_clk_src.c.fmax[VDD_DIG_LOW_L1] = 355200000;
- video_core_clk_src.c.fmax[VDD_DIG_LOW_L1] = 355200000;
}
int msm_mmsscc_cobalt_probe(struct platform_device *pdev)
@@ -2778,6 +2780,14 @@ int msm_mmsscc_cobalt_probe(struct platform_device *pdev)
return PTR_ERR(reg);
}
+ reg = vdd_mmsscc_mx.regulator[0] = devm_regulator_get(&pdev->dev,
+ "vdd_mmsscc_mx");
+ if (IS_ERR(reg)) {
+ if (PTR_ERR(reg) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Unable to get vdd_mmsscc_mx regulator!");
+ return PTR_ERR(reg);
+ }
+
tmp = mmsscc_xo.c.parent = devm_clk_get(&pdev->dev, "xo");
if (IS_ERR(tmp)) {
if (PTR_ERR(tmp) != -EPROBE_DEFER)
@@ -2838,6 +2848,10 @@ int msm_mmsscc_cobalt_probe(struct platform_device *pdev)
if (rc)
return rc;
+ /* Register block resets */
+ msm_reset_controller_register(pdev, mmss_cobalt_resets,
+ ARRAY_SIZE(mmss_cobalt_resets), virt_base);
+
dev_info(&pdev->dev, "Registered MMSS clocks.\n");
return 0;
}
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index 9d605503520f..8ae6a4e994f0 100644
--- a/drivers/clk/msm/clock-osm.c
+++ b/drivers/clk/msm/clock-osm.c
@@ -48,6 +48,7 @@
enum clk_osm_bases {
OSM_BASE,
PLL_BASE,
+ EFUSE_BASE,
NUM_BASES,
};
@@ -76,6 +77,7 @@ enum clk_osm_trace_packet_id {
#define MEM_ACC_SEQ_CONST(n) (n)
#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 OSM_TABLE_SIZE 40
#define MAX_CLUSTER_CNT 2
@@ -115,7 +117,7 @@ enum clk_osm_trace_packet_id {
#define PLL_TEST_CTL_HI 0x1C
#define PLL_STATUS 0x2C
#define PLL_LOCK_DET_MASK BIT(16)
-#define PLL_WAIT_LOCK_TIME_US 5
+#define PLL_WAIT_LOCK_TIME_US 10
#define PLL_WAIT_LOCK_TIME_NS (PLL_WAIT_LOCK_TIME_US * 1000)
#define PLL_MIN_LVAL 43
@@ -164,7 +166,8 @@ enum clk_osm_trace_packet_id {
#define DCVS_DROOP_EN_MASK BIT(5)
#define LMH_PS_EN_MASK BIT(6)
#define IGNORE_PLL_LOCK_MASK BIT(15)
-#define SAFE_FREQ_WAIT_NS 1000
+#define SAFE_FREQ_WAIT_NS 5000
+#define DEXT_DECREMENT_WAIT_NS 1000
#define DCVS_BOOST_TIMER_REG0 0x1084
#define DCVS_BOOST_TIMER_REG1 0x1088
#define DCVS_BOOST_TIMER_REG2 0x108C
@@ -173,7 +176,8 @@ enum clk_osm_trace_packet_id {
#define PS_BOOST_TIMER_REG2 0x109C
#define BOOST_PROG_SYNC_DELAY_REG 0x10A0
#define DROOP_CTRL_REG 0x10A4
-#define DROOP_PROG_SYNC_DELAY_REG 0x10B8
+#define DROOP_RELEASE_TIMER_CTRL 0x10A8
+#define DROOP_PROG_SYNC_DELAY_REG 0x10BC
#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
@@ -208,7 +212,13 @@ enum clk_osm_trace_packet_id {
#define PLL_DD_D0_USER_CTL_LO 0x17916208
#define PLL_DD_D1_USER_CTL_LO 0x17816208
+#define PWRCL_EFUSE_SHIFT 0
+#define PWRCL_EFUSE_MASK 0
+#define PERFCL_EFUSE_SHIFT 29
+#define PERFCL_EFUSE_MASK 0x7
+
static void __iomem *virt_base;
+static void __iomem *debug_base;
#define lmh_lite_clk_src_source_val 1
@@ -335,6 +345,9 @@ struct clk_osm {
bool trace_en;
};
+static bool msmcobalt_v1;
+static bool msmcobalt_v2;
+
static inline void clk_osm_masked_write_reg(struct clk_osm *c, u32 val,
u32 offset, u32 mask)
{
@@ -525,14 +538,45 @@ static struct clk_osm perfcl_clk = {
},
};
+static struct clk_ops clk_ops_cpu_dbg_mux;
+
+static struct mux_clk cpu_debug_mux = {
+ .offset = 0x0,
+ .mask = 0x3,
+ .shift = 8,
+ .ops = &mux_reg_ops,
+ MUX_SRC_LIST(
+ { &pwrcl_clk.c, 0x00 },
+ { &perfcl_clk.c, 0x01 },
+ ),
+ .base = &debug_base,
+ .c = {
+ .dbg_name = "cpu_debug_mux",
+ .ops = &clk_ops_cpu_dbg_mux,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(cpu_debug_mux.c),
+ },
+};
+
static struct clk_lookup cpu_clocks_osm[] = {
CLK_LIST(pwrcl_clk),
CLK_LIST(perfcl_clk),
CLK_LIST(sys_apcsaux_clk_gcc),
CLK_LIST(xo_ao),
CLK_LIST(osm_clk_src),
+ CLK_LIST(cpu_debug_mux),
};
+static unsigned long cpu_dbg_mux_get_rate(struct clk *clk)
+{
+ /* Account for the divider between the clock and the debug mux */
+ if (!strcmp(clk->parent->dbg_name, "pwrcl_clk"))
+ return clk->rate/4;
+ else if (!strcmp(clk->parent->dbg_name, "perfcl_clk"))
+ return clk->rate/8;
+ return clk->rate;
+}
+
static void clk_osm_print_osm_table(struct clk_osm *c)
{
int i;
@@ -901,6 +945,22 @@ static int clk_osm_resources_init(struct platform_device *pdev)
}
}
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "debug");
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get debug mux base\n");
+ return -EINVAL;
+ }
+
+ debug_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!debug_base) {
+ dev_err(&pdev->dev, "Unable to map in debug mux base\n");
+ return -ENOMEM;
+ }
+
+ clk_ops_cpu_dbg_mux = clk_ops_gen_mux;
+ clk_ops_cpu_dbg_mux.get_rate = cpu_dbg_mux_get_rate;
+
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apcs_common");
if (!res) {
dev_err(&pdev->dev, "Failed to get apcs common base\n");
@@ -913,6 +973,35 @@ static int clk_osm_resources_init(struct platform_device *pdev)
return -ENOMEM;
}
+ /* efuse speed bin fuses are optional */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "pwrcl_efuse");
+ if (res) {
+ pbase = (unsigned long)res->start;
+ vbase = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!vbase) {
+ dev_err(&pdev->dev, "Unable to map in pwrcl_efuse base\n");
+ return -ENOMEM;
+ }
+ pwrcl_clk.pbases[EFUSE_BASE] = pbase;
+ pwrcl_clk.vbases[EFUSE_BASE] = vbase;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "perfcl_efuse");
+ if (res) {
+ pbase = (unsigned long)res->start;
+ vbase = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!vbase) {
+ dev_err(&pdev->dev, "Unable to map in perfcl_efuse base\n");
+ return -ENOMEM;
+ }
+ perfcl_clk.pbases[EFUSE_BASE] = pbase;
+ perfcl_clk.vbases[EFUSE_BASE] = vbase;
+ }
+
vdd_pwrcl = devm_regulator_get(&pdev->dev, "vdd-pwrcl");
if (IS_ERR(vdd_pwrcl)) {
rc = PTR_ERR(vdd_pwrcl);
@@ -1582,6 +1671,9 @@ static void clk_osm_setup_osm_was(struct clk_osm *c)
u32 cc_hyst;
u32 val;
+ if (msmcobalt_v2)
+ return;
+
val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
val |= IGNORE_PLL_LOCK_MASK;
cc_hyst = clk_osm_read_reg(c, SPM_CC_HYSTERESIS);
@@ -1673,19 +1765,19 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
if (c->boost_fsm_en) {
val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
clk_osm_write_reg(c, val | CC_BOOST_EN_MASK, PDN_FSM_CTRL_REG);
+
val = clk_osm_read_reg(c, CC_BOOST_TIMER_REG0);
val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
- val |= BVAL(31, 16, clk_osm_count_ns(c,
- SAFE_FREQ_WAIT_NS));
+ val |= BVAL(31, 16, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
clk_osm_write_reg(c, val, CC_BOOST_TIMER_REG0);
val = clk_osm_read_reg(c, CC_BOOST_TIMER_REG1);
val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
- val |= BVAL(31, 16, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
+ val |= BVAL(31, 16, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
clk_osm_write_reg(c, val, CC_BOOST_TIMER_REG1);
val = clk_osm_read_reg(c, CC_BOOST_TIMER_REG2);
- val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
+ val |= BVAL(15, 0, clk_osm_count_ns(c, DEXT_DECREMENT_WAIT_NS));
clk_osm_write_reg(c, val, CC_BOOST_TIMER_REG2);
}
@@ -1696,12 +1788,19 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
PDN_FSM_CTRL_REG);
val = clk_osm_read_reg(c, DCVS_BOOST_TIMER_REG0);
+ val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
val |= BVAL(31, 16, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
clk_osm_write_reg(c, val, DCVS_BOOST_TIMER_REG0);
val = clk_osm_read_reg(c, DCVS_BOOST_TIMER_REG1);
val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
+ val |= BVAL(31, 16, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
clk_osm_write_reg(c, val, DCVS_BOOST_TIMER_REG1);
+
+ val = clk_osm_read_reg(c, DCVS_BOOST_TIMER_REG2);
+ val |= BVAL(15, 0, clk_osm_count_ns(c, DEXT_DECREMENT_WAIT_NS));
+ clk_osm_write_reg(c, val, DCVS_BOOST_TIMER_REG2);
+
}
/* PS FSM */
@@ -1709,13 +1808,19 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
clk_osm_write_reg(c, val | PS_BOOST_EN_MASK, PDN_FSM_CTRL_REG);
- val = clk_osm_read_reg(c, PS_BOOST_TIMER_REG0) |
- BVAL(31, 16, clk_osm_count_ns(c, 1000));
+ val = clk_osm_read_reg(c, PS_BOOST_TIMER_REG0);
+ val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
+ val |= BVAL(31, 16, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
clk_osm_write_reg(c, val, PS_BOOST_TIMER_REG0);
- val = clk_osm_read_reg(c, PS_BOOST_TIMER_REG1) |
- clk_osm_count_ns(c, 1000);
+ val = clk_osm_read_reg(c, PS_BOOST_TIMER_REG1);
+ val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
+ val |= BVAL(31, 16, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
clk_osm_write_reg(c, val, PS_BOOST_TIMER_REG1);
+
+ val = clk_osm_read_reg(c, PS_BOOST_TIMER_REG2);
+ val |= BVAL(15, 0, clk_osm_count_ns(c, DEXT_DECREMENT_WAIT_NS));
+ clk_osm_write_reg(c, val, PS_BOOST_TIMER_REG2);
}
/* PLL signal timing control */
@@ -1728,15 +1833,15 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
clk_osm_write_reg(c, val | WFX_DROOP_EN_MASK, PDN_FSM_CTRL_REG);
- val = clk_osm_read_reg(c, DROOP_UNSTALL_TIMER_CTRL_REG) |
- BVAL(31, 16, clk_osm_count_ns(c, 1000));
+ val = clk_osm_read_reg(c, DROOP_UNSTALL_TIMER_CTRL_REG);
+ val |= BVAL(31, 16, clk_osm_count_ns(c, 500));
clk_osm_write_reg(c, val, DROOP_UNSTALL_TIMER_CTRL_REG);
val = clk_osm_read_reg(c,
- DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG) |
- BVAL(31, 16, clk_osm_count_ns(c, 1000));
+ DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
+ val |= BVAL(31, 16, clk_osm_count_ns(c, 500));
clk_osm_write_reg(c, val,
- DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
+ DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
}
/* PC/RET FSM */
@@ -1745,9 +1850,15 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
clk_osm_write_reg(c, val | PC_RET_EXIT_DROOP_EN_MASK,
PDN_FSM_CTRL_REG);
- val = clk_osm_read_reg(c, DROOP_UNSTALL_TIMER_CTRL_REG) |
- BVAL(15, 0, clk_osm_count_ns(c, 5000));
+ val = clk_osm_read_reg(c, DROOP_UNSTALL_TIMER_CTRL_REG);
+ val |= BVAL(15, 0, clk_osm_count_ns(c, 500));
clk_osm_write_reg(c, val, DROOP_UNSTALL_TIMER_CTRL_REG);
+
+ val = clk_osm_read_reg(c,
+ DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
+ val |= BVAL(15, 0, clk_osm_count_ns(c, 500));
+ clk_osm_write_reg(c, val,
+ DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
}
/* DCVS droop FSM - only if RCGwRC is not used for di/dt control */
@@ -1758,14 +1869,14 @@ static void clk_osm_setup_fsms(struct clk_osm *c)
}
if (c->wfx_fsm_en || c->ps_fsm_en || c->droop_fsm_en) {
- val = clk_osm_read_reg(c,
- DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG) |
- BVAL(15, 0, clk_osm_count_ns(c, 1000));
- clk_osm_write_reg(c, val,
- DROOP_WAIT_TO_RELEASE_TIMER_CTRL0_REG);
clk_osm_write_reg(c, 0x1, DROOP_PROG_SYNC_DELAY_REG);
- val = clk_osm_read_reg(c, DROOP_CTRL_REG) |
- BVAL(22, 16, 0x2);
+ clk_osm_write_reg(c, clk_osm_count_ns(c, 250),
+ DROOP_RELEASE_TIMER_CTRL);
+ clk_osm_write_reg(c, clk_osm_count_ns(c, 500),
+ DCVS_DROOP_TIMER_CTRL);
+ val = clk_osm_read_reg(c, DROOP_CTRL_REG);
+ val |= BIT(31) | BVAL(22, 16, 0x2) |
+ BVAL(6, 0, 0x8);
clk_osm_write_reg(c, val, DROOP_CTRL_REG);
}
}
@@ -1803,6 +1914,9 @@ static void clk_osm_do_additional_setup(struct clk_osm *c,
clk_osm_write_reg(c, RCG_UPDATE_SUCCESS, SEQ_REG(84));
clk_osm_write_reg(c, RCG_UPDATE, SEQ_REG(85));
+ /* ITM to OSM handoff */
+ clk_osm_setup_itm_to_osm_handoff();
+
pr_debug("seq_size: %lu, seqbr_size: %lu\n", ARRAY_SIZE(seq_instr),
ARRAY_SIZE(seq_br_instr));
clk_osm_setup_sequencer(&pwrcl_clk);
@@ -1835,18 +1949,22 @@ static void clk_osm_apm_vc_setup(struct clk_osm *c)
/* Ensure writes complete before returning */
mb();
} else {
- scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(1),
- c->apm_threshold_vc);
+ if (msmcobalt_v1) {
+ scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(1),
+ c->apm_threshold_vc);
+ scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(73),
+ 0x3b | c->apm_threshold_vc << 6);
+ } else if (msmcobalt_v2) {
+ clk_osm_write_reg(c, c->apm_threshold_vc,
+ SEQ_REG1_MSMCOBALT_V2);
+ }
scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(72),
c->apm_crossover_vc);
- /* SEQ_REG(8) = address of SEQ_REG(1) init by TZ */
clk_osm_write_reg(c, c->apm_threshold_vc,
SEQ_REG(15));
scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(31),
c->apm_threshold_vc != 0 ?
c->apm_threshold_vc - 1 : 0xff);
- scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(73),
- 0x3b | c->apm_threshold_vc << 6);
scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(76),
0x39 | c->apm_threshold_vc << 6);
}
@@ -2441,13 +2559,23 @@ static unsigned long osm_clk_init_rate = 200000000;
static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
{
- char perfclspeedbinstr[] = "qcom,perfcl-speedbin0-v0";
- char pwrclspeedbinstr[] = "qcom,pwrcl-speedbin0-v0";
int rc, cpu;
+ int speedbin = 0, pvs_ver = 0;
+ u32 pte_efuse;
+ char pwrclspeedbinstr[] = "qcom,pwrcl-speedbin0-v0";
+ char perfclspeedbinstr[] = "qcom,perfcl-speedbin0-v0";
struct cpu_cycle_counter_cb cb = {
.get_cpu_cycle_counter = clk_osm_get_cpu_cycle_counter,
};
+ if (of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-osm-msmcobalt-v1")) {
+ msmcobalt_v1 = true;
+ } else if (of_find_compatible_node(NULL, NULL,
+ "qcom,cpu-clock-osm-msmcobalt-v2")) {
+ msmcobalt_v2 = true;
+ }
+
rc = clk_osm_resources_init(pdev);
if (rc) {
if (rc != -EPROBE_DEFER)
@@ -2462,6 +2590,24 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
return rc;
}
+ if ((pwrcl_clk.secure_init || perfcl_clk.secure_init) &&
+ msmcobalt_v2) {
+ pr_err("unsupported configuration for msmcobalt v2\n");
+ return -EINVAL;
+ }
+
+ if (pwrcl_clk.vbases[EFUSE_BASE]) {
+ /* Multiple speed-bins are supported */
+ pte_efuse = readl_relaxed(pwrcl_clk.vbases[EFUSE_BASE]);
+ speedbin = ((pte_efuse >> PWRCL_EFUSE_SHIFT) &
+ PWRCL_EFUSE_MASK);
+ snprintf(pwrclspeedbinstr, ARRAY_SIZE(pwrclspeedbinstr),
+ "qcom,pwrcl-speedbin%d-v%d", speedbin, pvs_ver);
+ }
+
+ dev_info(&pdev->dev, "using pwrcl speed bin %u and pvs_ver %d\n",
+ speedbin, pvs_ver);
+
rc = clk_osm_get_lut(pdev, &pwrcl_clk,
pwrclspeedbinstr);
if (rc) {
@@ -2470,6 +2616,18 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
return rc;
}
+ if (perfcl_clk.vbases[EFUSE_BASE]) {
+ /* Multiple speed-bins are supported */
+ pte_efuse = readl_relaxed(perfcl_clk.vbases[EFUSE_BASE]);
+ speedbin = ((pte_efuse >> PERFCL_EFUSE_SHIFT) &
+ PERFCL_EFUSE_MASK);
+ snprintf(perfclspeedbinstr, ARRAY_SIZE(perfclspeedbinstr),
+ "qcom,perfcl-speedbin%d-v%d", speedbin, pvs_ver);
+ }
+
+ dev_info(&pdev->dev, "using perfcl speed bin %u and pvs_ver %d\n",
+ speedbin, pvs_ver);
+
rc = clk_osm_get_lut(pdev, &perfcl_clk, perfclspeedbinstr);
if (rc) {
dev_err(&pdev->dev, "Unable to get OSM LUT for perf cluster, rc=%d\n",
@@ -2509,10 +2667,6 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
clk_osm_print_osm_table(&pwrcl_clk);
clk_osm_print_osm_table(&perfcl_clk);
- /* Program the minimum PLL frequency */
- clk_osm_write_reg(&pwrcl_clk, PLL_MIN_LVAL, SEQ_REG(27));
- clk_osm_write_reg(&perfcl_clk, PLL_MIN_LVAL, SEQ_REG(27));
-
rc = clk_osm_setup_hw_table(&pwrcl_clk);
if (rc) {
dev_err(&pdev->dev, "failed to setup power cluster hardware table\n");
@@ -2531,8 +2685,6 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
goto exit;
}
- clk_osm_setup_itm_to_osm_handoff();
-
/* LLM Freq Policy Tuning */
rc = clk_osm_set_llm_freq_policy(pdev);
if (rc < 0) {
@@ -2666,7 +2818,8 @@ exit:
}
static struct of_device_id match_table[] = {
- { .compatible = "qcom,cpu-clock-osm" },
+ { .compatible = "qcom,cpu-clock-osm-msmcobalt-v1" },
+ { .compatible = "qcom,cpu-clock-osm-msmcobalt-v2" },
{}
};
diff --git a/drivers/clk/msm/vdd-level-cobalt.h b/drivers/clk/msm/vdd-level-cobalt.h
index f847a4104d4d..c1897b7da7f7 100644
--- a/drivers/clk/msm/vdd-level-cobalt.h
+++ b/drivers/clk/msm/vdd-level-cobalt.h
@@ -24,6 +24,7 @@
[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]) { \
@@ -40,6 +41,7 @@
[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]) { \
@@ -66,6 +68,22 @@
}, \
.num_fmax = VDD_DIG_NUM
+#define VDD_MM_PLL_FMAX_MAP1(l1, f1) \
+ .vdd_class = &vdd_mmsscc_mx, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+#define VDD_MM_PLL_FMAX_MAP2(l1, f1, l2, f2) \
+ .vdd_class = &vdd_mmsscc_mx, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
+
+
#define VDD_GPU_PLL_FMAX_MAP1(l1, f1) \
.vdd_class = &vdd_gpucc_mx, \
.fmax = (unsigned long[VDD_DIG_NUM]) { \
diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c
index a63680290219..a7b8ac07e73a 100644
--- a/drivers/clk/qcom/gcc-msm8996.c
+++ b/drivers/clk/qcom/gcc-msm8996.c
@@ -21,6 +21,7 @@
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
+#include <linux/clk.h>
#include <dt-bindings/clock/qcom,gcc-msm8996.h>
@@ -30,7 +31,6 @@
#include "clk-rcg.h"
#include "clk-branch.h"
#include "reset.h"
-#include "gdsc.h"
#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
@@ -48,14 +48,6 @@ enum {
P_GPLL1_EARLY_DIV
};
-static const struct parent_map gcc_sleep_clk_map[] = {
- { P_SLEEP_CLK, 5 }
-};
-
-static const char * const gcc_sleep_clk[] = {
- "sleep_clk"
-};
-
static const struct parent_map gcc_xo_gpll0_map[] = {
{ P_XO, 0 },
{ P_GPLL0, 1 }
@@ -284,71 +276,6 @@ static struct clk_alpha_pll_postdiv gpll4 = {
},
};
-static const struct freq_tbl ftbl_system_noc_clk_src[] = {
- F(19200000, P_XO, 1, 0, 0),
- F(50000000, P_GPLL0_EARLY_DIV, 6, 0, 0),
- F(100000000, P_GPLL0, 6, 0, 0),
- F(150000000, P_GPLL0, 4, 0, 0),
- F(200000000, P_GPLL0, 3, 0, 0),
- F(240000000, P_GPLL0, 2.5, 0, 0),
- { }
-};
-
-static struct clk_rcg2 system_noc_clk_src = {
- .cmd_rcgr = 0x0401c,
- .hid_width = 5,
- .parent_map = gcc_xo_gpll0_gpll2_gpll3_gpll1_gpll2_early_gpll0_early_div_map,
- .freq_tbl = ftbl_system_noc_clk_src,
- .clkr.hw.init = &(struct clk_init_data){
- .name = "system_noc_clk_src",
- .parent_names = gcc_xo_gpll0_gpll2_gpll3_gpll1_gpll2_early_gpll0_early_div,
- .num_parents = 7,
- .ops = &clk_rcg2_ops,
- },
-};
-
-static const struct freq_tbl ftbl_config_noc_clk_src[] = {
- F(19200000, P_XO, 1, 0, 0),
- F(37500000, P_GPLL0, 16, 0, 0),
- F(75000000, P_GPLL0, 8, 0, 0),
- { }
-};
-
-static struct clk_rcg2 config_noc_clk_src = {
- .cmd_rcgr = 0x0500c,
- .hid_width = 5,
- .parent_map = gcc_xo_gpll0_map,
- .freq_tbl = ftbl_config_noc_clk_src,
- .clkr.hw.init = &(struct clk_init_data){
- .name = "config_noc_clk_src",
- .parent_names = gcc_xo_gpll0,
- .num_parents = 2,
- .ops = &clk_rcg2_ops,
- },
-};
-
-static const struct freq_tbl ftbl_periph_noc_clk_src[] = {
- F(19200000, P_XO, 1, 0, 0),
- F(37500000, P_GPLL0, 16, 0, 0),
- F(50000000, P_GPLL0, 12, 0, 0),
- F(75000000, P_GPLL0, 8, 0, 0),
- F(100000000, P_GPLL0, 6, 0, 0),
- { }
-};
-
-static struct clk_rcg2 periph_noc_clk_src = {
- .cmd_rcgr = 0x06014,
- .hid_width = 5,
- .parent_map = gcc_xo_gpll0_map,
- .freq_tbl = ftbl_periph_noc_clk_src,
- .clkr.hw.init = &(struct clk_init_data){
- .name = "periph_noc_clk_src",
- .parent_names = gcc_xo_gpll0,
- .num_parents = 2,
- .ops = &clk_rcg2_ops,
- },
-};
-
static const struct freq_tbl ftbl_usb30_master_clk_src[] = {
F(19200000, P_XO, 1, 0, 0),
F(120000000, P_GPLL0, 5, 0, 0),
@@ -464,10 +391,18 @@ static struct clk_rcg2 sdcc1_apps_clk_src = {
},
};
+static struct freq_tbl ftbl_sdcc1_ice_core_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(150000000, P_GPLL0, 4, 0, 0),
+ F(300000000, P_GPLL0, 2, 0, 0),
+ { }
+};
+
static struct clk_rcg2 sdcc1_ice_core_clk_src = {
.cmd_rcgr = 0x13024,
.hid_width = 5,
.parent_map = gcc_xo_gpll0_gpll4_gpll0_early_div_map,
+ .freq_tbl = ftbl_sdcc1_ice_core_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "sdcc1_ice_core_clk_src",
.parent_names = gcc_xo_gpll0_gpll4_gpll0_early_div,
@@ -1104,18 +1039,6 @@ static struct clk_rcg2 tsif_ref_clk_src = {
},
};
-static struct clk_rcg2 gcc_sleep_clk_src = {
- .cmd_rcgr = 0x43014,
- .hid_width = 5,
- .parent_map = gcc_sleep_clk_map,
- .clkr.hw.init = &(struct clk_init_data){
- .name = "gcc_sleep_clk_src",
- .parent_names = gcc_sleep_clk,
- .num_parents = 1,
- .ops = &clk_rcg2_ops,
- },
-};
-
static struct clk_rcg2 hmss_rbcpr_clk_src = {
.cmd_rcgr = 0x48040,
.hid_width = 5,
@@ -1230,10 +1153,18 @@ static struct clk_rcg2 ufs_axi_clk_src = {
},
};
+static const struct freq_tbl ftbl_ufs_ice_core_clk_src[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(150000000, P_GPLL0, 4, 0, 0),
+ F(300000000, P_GPLL0, 2, 0, 0),
+ { }
+};
+
static struct clk_rcg2 ufs_ice_core_clk_src = {
.cmd_rcgr = 0x76014,
.hid_width = 5,
.parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_ufs_ice_core_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "ufs_ice_core_clk_src",
.parent_names = gcc_xo_gpll0,
@@ -1242,10 +1173,19 @@ static struct clk_rcg2 ufs_ice_core_clk_src = {
},
};
+static const struct freq_tbl ftbl_qspi_ser_clk_src[] = {
+ F(75000000, P_GPLL0, 8, 0, 0),
+ F(150000000, P_GPLL0, 4, 0, 0),
+ F(256000000, P_GPLL4, 1.5, 0, 0),
+ F(300000000, P_GPLL0, 2, 0, 0),
+ { }
+};
+
static struct clk_rcg2 qspi_ser_clk_src = {
.cmd_rcgr = 0x8b00c,
.hid_width = 5,
.parent_map = gcc_xo_gpll0_gpll1_early_div_gpll1_gpll4_gpll0_early_div_map,
+ .freq_tbl = ftbl_qspi_ser_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "qspi_ser_clk_src",
.parent_names = gcc_xo_gpll0_gpll1_early_div_gpll1_gpll4_gpll0_early_div,
@@ -1306,9 +1246,6 @@ static struct clk_branch gcc_mmss_noc_cfg_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_mmss_noc_cfg_ahb_clk",
- .parent_names = (const char *[]){ "config_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
.ops = &clk_branch2_ops,
},
},
@@ -1316,12 +1253,12 @@ static struct clk_branch gcc_mmss_noc_cfg_ahb_clk = {
static struct clk_branch gcc_mmss_bimc_gfx_clk = {
.halt_reg = 0x9010,
+ .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE,
.clkr = {
.enable_reg = 0x9010,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_mmss_bimc_gfx_clk",
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1349,9 +1286,6 @@ static struct clk_branch gcc_usb30_sleep_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_usb30_sleep_clk",
- .parent_names = (const char *[]){ "gcc_sleep_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1387,17 +1321,26 @@ static struct clk_branch gcc_usb3_phy_aux_clk = {
},
};
-static struct clk_branch gcc_usb3_phy_pipe_clk = {
- .halt_reg = 0x50004,
+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",
- .parent_names = (const char *[]){ "usb3_phy_pipe_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_ops,
+ .ops = &clk_gate2_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,
},
},
};
@@ -1424,9 +1367,6 @@ static struct clk_branch gcc_usb20_sleep_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_usb20_sleep_clk",
- .parent_names = (const char *[]){ "gcc_sleep_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1454,9 +1394,6 @@ static struct clk_branch gcc_usb_phy_cfg_ahb2phy_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_usb_phy_cfg_ahb2phy_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1484,9 +1421,6 @@ static struct clk_branch gcc_sdcc1_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_sdcc1_ahb_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1529,9 +1463,6 @@ static struct clk_branch gcc_sdcc2_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_sdcc2_ahb_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1559,9 +1490,6 @@ static struct clk_branch gcc_sdcc3_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_sdcc3_ahb_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1589,9 +1517,6 @@ static struct clk_branch gcc_sdcc4_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_sdcc4_ahb_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1605,9 +1530,6 @@ static struct clk_branch gcc_blsp1_ahb_clk = {
.enable_mask = BIT(17),
.hw.init = &(struct clk_init_data){
.name = "gcc_blsp1_ahb_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1621,9 +1543,6 @@ static struct clk_branch gcc_blsp1_sleep_clk = {
.enable_mask = BIT(16),
.hw.init = &(struct clk_init_data){
.name = "gcc_blsp1_sleep_clk",
- .parent_names = (const char *[]){ "gcc_sleep_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1907,9 +1826,6 @@ static struct clk_branch gcc_blsp2_ahb_clk = {
.enable_mask = BIT(15),
.hw.init = &(struct clk_init_data){
.name = "gcc_blsp2_ahb_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -1923,9 +1839,6 @@ static struct clk_branch gcc_blsp2_sleep_clk = {
.enable_mask = BIT(14),
.hw.init = &(struct clk_init_data){
.name = "gcc_blsp2_sleep_clk",
- .parent_names = (const char *[]){ "gcc_sleep_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2208,9 +2121,6 @@ static struct clk_branch gcc_pdm_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pdm_ahb_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2239,9 +2149,6 @@ static struct clk_branch gcc_prng_ahb_clk = {
.enable_mask = BIT(13),
.hw.init = &(struct clk_init_data){
.name = "gcc_prng_ahb_clk",
- .parent_names = (const char *[]){ "config_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2254,9 +2161,6 @@ static struct clk_branch gcc_tsif_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_tsif_ahb_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2277,21 +2181,6 @@ static struct clk_branch gcc_tsif_ref_clk = {
},
};
-static struct clk_branch gcc_tsif_inactivity_timers_clk = {
- .halt_reg = 0x3600c,
- .clkr = {
- .enable_reg = 0x3600c,
- .enable_mask = BIT(0),
- .hw.init = &(struct clk_init_data){
- .name = "gcc_tsif_inactivity_timers_clk",
- .parent_names = (const char *[]){ "gcc_sleep_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,
@@ -2300,9 +2189,6 @@ static struct clk_branch gcc_boot_rom_ahb_clk = {
.enable_mask = BIT(10),
.hw.init = &(struct clk_init_data){
.name = "gcc_boot_rom_ahb_clk",
- .parent_names = (const char *[]){ "config_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2315,7 +2201,6 @@ static struct clk_branch gcc_bimc_gfx_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_bimc_gfx_clk",
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2388,9 +2273,6 @@ static struct clk_branch gcc_pcie_0_slv_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_0_slv_axi_clk",
- .parent_names = (const char *[]){ "system_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2403,9 +2285,6 @@ static struct clk_branch gcc_pcie_0_mstr_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_0_mstr_axi_clk",
- .parent_names = (const char *[]){ "system_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2418,9 +2297,6 @@ static struct clk_branch gcc_pcie_0_cfg_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_0_cfg_ahb_clk",
- .parent_names = (const char *[]){ "config_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2441,17 +2317,14 @@ static struct clk_branch gcc_pcie_0_aux_clk = {
},
};
-static struct clk_branch gcc_pcie_0_pipe_clk = {
- .halt_reg = 0x6b018,
+static struct clk_gate2 gcc_pcie_0_pipe_clk = {
+ .udelay = 500,
.clkr = {
.enable_reg = 0x6b018,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_0_pipe_clk",
- .parent_names = (const char *[]){ "pcie_0_pipe_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_ops,
+ .ops = &clk_gate2_ops,
},
},
};
@@ -2463,9 +2336,6 @@ static struct clk_branch gcc_pcie_1_slv_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_1_slv_axi_clk",
- .parent_names = (const char *[]){ "system_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2478,9 +2348,6 @@ static struct clk_branch gcc_pcie_1_mstr_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_1_mstr_axi_clk",
- .parent_names = (const char *[]){ "system_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2493,9 +2360,6 @@ static struct clk_branch gcc_pcie_1_cfg_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_1_cfg_ahb_clk",
- .parent_names = (const char *[]){ "config_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2516,17 +2380,14 @@ static struct clk_branch gcc_pcie_1_aux_clk = {
},
};
-static struct clk_branch gcc_pcie_1_pipe_clk = {
- .halt_reg = 0x6d018,
+static struct clk_gate2 gcc_pcie_1_pipe_clk = {
+ .udelay = 500,
.clkr = {
.enable_reg = 0x6d018,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_1_pipe_clk",
- .parent_names = (const char *[]){ "pcie_1_pipe_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_ops,
+ .ops = &clk_gate2_ops,
},
},
};
@@ -2538,9 +2399,6 @@ static struct clk_branch gcc_pcie_2_slv_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_2_slv_axi_clk",
- .parent_names = (const char *[]){ "system_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2553,9 +2411,6 @@ static struct clk_branch gcc_pcie_2_mstr_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_2_mstr_axi_clk",
- .parent_names = (const char *[]){ "system_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2568,9 +2423,6 @@ static struct clk_branch gcc_pcie_2_cfg_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_2_cfg_ahb_clk",
- .parent_names = (const char *[]){ "config_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2591,17 +2443,14 @@ static struct clk_branch gcc_pcie_2_aux_clk = {
},
};
-static struct clk_branch gcc_pcie_2_pipe_clk = {
- .halt_reg = 0x6e108,
+static struct clk_gate2 gcc_pcie_2_pipe_clk = {
+ .udelay = 500,
.clkr = {
- .enable_reg = 0x6e108,
+ .enable_reg = 0x6e018,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_2_pipe_clk",
- .parent_names = (const char *[]){ "pcie_2_pipe_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_ops,
+ .ops = &clk_gate2_ops,
},
},
};
@@ -2613,9 +2462,6 @@ static struct clk_branch gcc_pcie_phy_cfg_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_phy_cfg_ahb_clk",
- .parent_names = (const char *[]){ "config_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2658,9 +2504,6 @@ static struct clk_branch gcc_ufs_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_ufs_ahb_clk",
- .parent_names = (const char *[]){ "config_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2720,47 +2563,39 @@ static struct clk_branch gcc_ufs_rx_cfg_clk = {
},
};
-static struct clk_branch gcc_ufs_tx_symbol_0_clk = {
- .halt_reg = 0x75018,
+static struct clk_gate2 gcc_ufs_tx_symbol_0_clk = {
+ .udelay = 500,
.clkr = {
.enable_reg = 0x75018,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_ufs_tx_symbol_0_clk",
- .parent_names = (const char *[]){ "ufs_tx_symbol_0_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_ops,
+ .ops = &clk_gate2_ops,
},
},
};
-static struct clk_branch gcc_ufs_rx_symbol_0_clk = {
- .halt_reg = 0x7501c,
+static struct clk_gate2 gcc_ufs_rx_symbol_0_clk = {
+ .udelay = 500,
.clkr = {
.enable_reg = 0x7501c,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_ufs_rx_symbol_0_clk",
- .parent_names = (const char *[]){ "ufs_rx_symbol_0_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_ops,
+ .ops = &clk_gate2_ops,
},
},
};
-static struct clk_branch gcc_ufs_rx_symbol_1_clk = {
- .halt_reg = 0x75020,
+static struct clk_gate2 gcc_ufs_rx_symbol_1_clk = {
+ .udelay = 500,
.clkr = {
.enable_reg = 0x75020,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_ufs_rx_symbol_1_clk",
- .parent_names = (const char *[]){ "ufs_rx_symbol_1_clk_src" },
- .num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_branch2_ops,
+ .ops = &clk_gate2_ops,
},
},
};
@@ -2807,26 +2642,26 @@ static struct clk_branch gcc_ufs_ice_core_clk = {
},
};
-static struct clk_branch gcc_ufs_sys_clk_core_clk = {
- .halt_check = BRANCH_HALT_DELAY,
+static struct clk_gate2 gcc_ufs_sys_clk_core_clk = {
+ .udelay = 500,
.clkr = {
.enable_reg = 0x76030,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_ufs_sys_clk_core_clk",
- .ops = &clk_branch2_ops,
+ .ops = &clk_gate2_ops,
},
},
};
-static struct clk_branch gcc_ufs_tx_symbol_clk_core_clk = {
- .halt_check = BRANCH_HALT_DELAY,
+static struct clk_gate2 gcc_ufs_tx_symbol_clk_core_clk = {
+ .udelay = 500,
.clkr = {
.enable_reg = 0x76034,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_ufs_tx_symbol_clk_core_clk",
- .ops = &clk_branch2_ops,
+ .ops = &clk_gate2_ops,
},
},
};
@@ -2838,9 +2673,6 @@ static struct clk_branch gcc_aggre0_snoc_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_aggre0_snoc_axi_clk",
- .parent_names = (const char *[]){ "system_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2853,9 +2685,6 @@ static struct clk_branch gcc_aggre0_cnoc_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_aggre0_cnoc_ahb_clk",
- .parent_names = (const char *[]){ "config_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2863,29 +2692,37 @@ static struct clk_branch gcc_aggre0_cnoc_ahb_clk = {
static struct clk_branch gcc_smmu_aggre0_axi_clk = {
.halt_reg = 0x81014,
+ .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE,
.clkr = {
.enable_reg = 0x81014,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_smmu_aggre0_axi_clk",
- .parent_names = (const char *[]){ "system_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
};
+static struct clk_gate2 gcc_aggre0_noc_qosgen_extref_clk = {
+ .udelay = 500,
+ .clkr = {
+ .enable_reg = 0x8101c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_aggre0_noc_qosgen_extref_clk",
+ .ops = &clk_gate2_ops,
+ },
+ },
+};
+
static struct clk_branch gcc_smmu_aggre0_ahb_clk = {
.halt_reg = 0x81018,
+ .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE,
.clkr = {
.enable_reg = 0x81018,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_smmu_aggre0_ahb_clk",
- .parent_names = (const char *[]){ "config_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2898,9 +2735,6 @@ static struct clk_branch gcc_aggre1_pnoc_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_aggre1_pnoc_ahb_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2943,9 +2777,6 @@ static struct clk_branch gcc_qspi_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_qspi_ahb_clk",
- .parent_names = (const char *[]){ "periph_noc_clk_src" },
- .num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
.ops = &clk_branch2_ops,
},
},
@@ -2973,8 +2804,6 @@ static struct clk_branch gcc_usb3_clkref_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_usb3_clkref_clk",
- .parent_names = (const char *[]){ "xo" },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
@@ -2987,8 +2816,6 @@ static struct clk_branch gcc_hdmi_clkref_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_hdmi_clkref_clk",
- .parent_names = (const char *[]){ "xo" },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
@@ -3001,8 +2828,6 @@ static struct clk_branch gcc_ufs_clkref_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_ufs_clkref_clk",
- .parent_names = (const char *[]){ "xo" },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
@@ -3015,8 +2840,6 @@ static struct clk_branch gcc_pcie_clkref_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_clkref_clk",
- .parent_names = (const char *[]){ "xo" },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
@@ -3029,8 +2852,6 @@ static struct clk_branch gcc_rx2_usb2_clkref_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_rx2_usb2_clkref_clk",
- .parent_names = (const char *[]){ "xo" },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
@@ -3043,96 +2864,142 @@ static struct clk_branch gcc_rx1_usb2_clkref_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_rx1_usb2_clkref_clk",
- .parent_names = (const char *[]){ "xo" },
- .num_parents = 1,
.ops = &clk_branch2_ops,
},
},
};
-static struct clk_hw *gcc_msm8996_hws[] = {
- &xo.hw,
- &gpll0_early_div.hw,
- &ufs_tx_cfg_clk_src.hw,
- &ufs_rx_cfg_clk_src.hw,
- &ufs_ice_core_postdiv_clk_src.hw,
+static struct clk_branch hlos1_vote_lpass_core_smmu_clk = {
+ .halt_reg = 0x7d010,
+ .halt_check = BRANCH_HALT_NO_CHECK_ON_DISABLE,
+ .clkr = {
+ .enable_reg = 0x7d010,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "hlos1_vote_lpass_core_smmu_clk",
+ .ops = &clk_branch2_ops,
+ },
+ },
};
-static struct gdsc aggre0_noc_gdsc = {
- .gdscr = 0x81004,
- .gds_hw_ctrl = 0x81028,
- .pd = {
- .name = "aggre0_noc",
+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,
+ },
},
- .pwrsts = PWRSTS_OFF_ON,
- .flags = VOTABLE,
};
-static struct gdsc hlos1_vote_aggre0_noc_gdsc = {
- .gdscr = 0x7d024,
- .pd = {
- .name = "hlos1_vote_aggre0_noc",
+static struct clk_branch gcc_edp_clkref_clk = {
+ .halt_reg = 0x88004,
+ .clkr = {
+ .enable_reg = 0x88004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_edp_clkref_clk",
+ .ops = &clk_branch2_ops,
+ },
},
- .pwrsts = PWRSTS_OFF_ON,
- .flags = VOTABLE,
};
-static struct gdsc hlos1_vote_lpass_adsp_gdsc = {
- .gdscr = 0x7d034,
- .pd = {
- .name = "hlos1_vote_lpass_adsp",
+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,
+ },
},
- .pwrsts = PWRSTS_OFF_ON,
- .flags = VOTABLE,
};
-static struct gdsc hlos1_vote_lpass_core_gdsc = {
- .gdscr = 0x7d038,
- .pd = {
- .name = "hlos1_vote_lpass_core",
+static struct clk_branch gcc_mss_q6_bimc_axi_clk = {
+ .halt_reg = 0x8a028,
+ .clkr = {
+ .enable_reg = 0x8a028,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mss_q6_bimc_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
},
- .pwrsts = PWRSTS_OFF_ON,
- .flags = VOTABLE,
};
-static struct gdsc usb30_gdsc = {
- .gdscr = 0xf004,
- .pd = {
- .name = "usb30",
+static struct clk_branch gcc_mss_snoc_axi_clk = {
+ .halt_reg = 0x8a024,
+ .clkr = {
+ .enable_reg = 0x8a024,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mss_snoc_axi_clk",
+ .ops = &clk_branch2_ops,
+ },
},
- .pwrsts = PWRSTS_OFF_ON,
};
-static struct gdsc pcie0_gdsc = {
- .gdscr = 0x6b004,
- .pd = {
- .name = "pcie0",
+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,
+ },
},
- .pwrsts = PWRSTS_OFF_ON,
};
-static struct gdsc pcie1_gdsc = {
- .gdscr = 0x6d004,
- .pd = {
- .name = "pcie1",
+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,
+ },
},
- .pwrsts = PWRSTS_OFF_ON,
};
-static struct gdsc pcie2_gdsc = {
- .gdscr = 0x6e004,
- .pd = {
- .name = "pcie2",
+static struct clk_branch gcc_aggre0_noc_mpu_cfg_ahb_clk = {
+ .halt_reg = 0x85000,
+ .clkr = {
+ .enable_reg = 0x85000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_aggre0_noc_mpu_cfg_ahb_clk",
+ .ops = &clk_branch2_ops,
+ },
},
- .pwrsts = PWRSTS_OFF_ON,
};
-static struct gdsc ufs_gdsc = {
- .gdscr = 0x75004,
- .pd = {
- .name = "ufs",
+static struct clk_branch gcc_mmss_gpll0_div_clk = {
+ .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" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
},
- .pwrsts = PWRSTS_OFF_ON,
+};
+
+static struct clk_hw *gcc_msm8996_hws[] = {
+ &xo.hw,
+ &gpll0_early_div.hw,
+ &ufs_tx_cfg_clk_src.hw,
+ &ufs_rx_cfg_clk_src.hw,
+ &ufs_ice_core_postdiv_clk_src.hw,
};
static struct clk_regmap *gcc_msm8996_clocks[] = {
@@ -3140,9 +3007,6 @@ static struct clk_regmap *gcc_msm8996_clocks[] = {
[GPLL0] = &gpll0.clkr,
[GPLL4_EARLY] = &gpll4_early.clkr,
[GPLL4] = &gpll4.clkr,
- [SYSTEM_NOC_CLK_SRC] = &system_noc_clk_src.clkr,
- [CONFIG_NOC_CLK_SRC] = &config_noc_clk_src.clkr,
- [PERIPH_NOC_CLK_SRC] = &periph_noc_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,
@@ -3191,7 +3055,6 @@ static struct clk_regmap *gcc_msm8996_clocks[] = {
[BLSP2_UART6_APPS_CLK_SRC] = &blsp2_uart6_apps_clk_src.clkr,
[PDM2_CLK_SRC] = &pdm2_clk_src.clkr,
[TSIF_REF_CLK_SRC] = &tsif_ref_clk_src.clkr,
- [GCC_SLEEP_CLK_SRC] = &gcc_sleep_clk_src.clkr,
[HMSS_RBCPR_CLK_SRC] = &hmss_rbcpr_clk_src.clkr,
[HMSS_GPLL0_CLK_SRC] = &hmss_gpll0_clk_src.clkr,
[GP1_CLK_SRC] = &gp1_clk_src.clkr,
@@ -3269,7 +3132,6 @@ static struct clk_regmap *gcc_msm8996_clocks[] = {
[GCC_PRNG_AHB_CLK] = &gcc_prng_ahb_clk.clkr,
[GCC_TSIF_AHB_CLK] = &gcc_tsif_ahb_clk.clkr,
[GCC_TSIF_REF_CLK] = &gcc_tsif_ref_clk.clkr,
- [GCC_TSIF_INACTIVITY_TIMERS_CLK] = &gcc_tsif_inactivity_timers_clk.clkr,
[GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr,
[GCC_BIMC_GFX_CLK] = &gcc_bimc_gfx_clk.clkr,
[GCC_HMSS_RBCPR_CLK] = &gcc_hmss_rbcpr_clk.clkr,
@@ -3319,18 +3181,21 @@ static struct clk_regmap *gcc_msm8996_clocks[] = {
[GCC_PCIE_CLKREF_CLK] = &gcc_pcie_clkref_clk.clkr,
[GCC_RX2_USB2_CLKREF_CLK] = &gcc_rx2_usb2_clkref_clk.clkr,
[GCC_RX1_USB2_CLKREF_CLK] = &gcc_rx1_usb2_clkref_clk.clkr,
-};
-
-static struct gdsc *gcc_msm8996_gdscs[] = {
- [AGGRE0_NOC_GDSC] = &aggre0_noc_gdsc,
- [HLOS1_VOTE_AGGRE0_NOC_GDSC] = &hlos1_vote_aggre0_noc_gdsc,
- [HLOS1_VOTE_LPASS_ADSP_GDSC] = &hlos1_vote_lpass_adsp_gdsc,
- [HLOS1_VOTE_LPASS_CORE_GDSC] = &hlos1_vote_lpass_core_gdsc,
- [USB30_GDSC] = &usb30_gdsc,
- [PCIE0_GDSC] = &pcie0_gdsc,
- [PCIE1_GDSC] = &pcie1_gdsc,
- [PCIE2_GDSC] = &pcie2_gdsc,
- [UFS_GDSC] = &ufs_gdsc,
+ [GCC_AGGRE0_NOC_QOSGEN_EXTREF_CLK] =
+ &gcc_aggre0_noc_qosgen_extref_clk.clkr,
+ [GCC_HLOS1_VOTE_LPASS_CORE_SMMU_CLK] =
+ &hlos1_vote_lpass_core_smmu_clk.clkr,
+ [GCC_HLOS1_VOTE_LPASS_ADSP_SMMU_CLK] =
+ &hlos1_vote_lpass_adsp_smmu_clk.clkr,
+ [GCC_EDP_CLKREF_CLK] = &gcc_edp_clkref_clk.clkr,
+ [GCC_MSS_CFG_AHB_CLK] = &gcc_mss_cfg_ahb_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_MSS_MNOC_BIMC_AXI_CLK] = &gcc_mss_mnoc_bimc_axi_clk.clkr,
+ [GCC_DCC_AHB_ALK] = &gcc_dcc_ahb_clk.clkr,
+ [GCC_AGGRE0_NOC_MPU_CFG_AHB_CLK] = &gcc_aggre0_noc_mpu_cfg_ahb_clk.clkr,
+ [GCC_MMSS_GPLL0_DIV_CLK] = &gcc_mmss_gpll0_div_clk.clkr,
+ [GPLL0_OUT_MSSCC] = &gpll0_out_msscc.clkr,
};
static const struct qcom_reset_map gcc_msm8996_resets[] = {
@@ -3455,8 +3320,6 @@ static const struct qcom_cc_desc gcc_msm8996_desc = {
.num_clks = ARRAY_SIZE(gcc_msm8996_clocks),
.resets = gcc_msm8996_resets,
.num_resets = ARRAY_SIZE(gcc_msm8996_resets),
- .gdscs = gcc_msm8996_gdscs,
- .num_gdscs = ARRAY_SIZE(gcc_msm8996_gdscs),
};
static const struct of_device_id gcc_msm8996_match_table[] = {
@@ -3469,18 +3332,15 @@ static int gcc_msm8996_probe(struct platform_device *pdev)
{
struct clk *clk;
struct device *dev = &pdev->dev;
- int i;
+ int i, ret = 0;
struct regmap *regmap;
regmap = qcom_cc_map(pdev, &gcc_msm8996_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));
+ /* Set the HMSS_AHB_CLK_ENA bit to enable the hmss_ahb_clk */
+ regmap_update_bits(regmap, 0x52004, BIT(21), BIT(21));
for (i = 0; i < ARRAY_SIZE(gcc_msm8996_hws); i++) {
clk = devm_clk_register(dev, gcc_msm8996_hws[i]);
@@ -3488,7 +3348,19 @@ static int gcc_msm8996_probe(struct platform_device *pdev)
return PTR_ERR(clk);
}
- return qcom_cc_really_probe(pdev, &gcc_msm8996_desc, regmap);
+ ret = qcom_cc_really_probe(pdev, &gcc_msm8996_desc, regmap);
+
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register GCC clocks\n");
+ return ret;
+ }
+
+ /* This clock is used for all MMSS register access */
+ clk_prepare_enable(gcc_mmss_noc_cfg_ahb_clk.clkr.hw.clk);
+
+ dev_info(&pdev->dev, "Registered GCC clocks\n");
+
+ return ret;
}
static struct platform_driver gcc_msm8996_driver = {
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 61f99370863d..3562de7fc967 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -84,6 +84,8 @@ static LIST_HEAD(qce50_bam_list);
/* Index to point the dummy request */
#define DUMMY_REQ_INDEX MAX_QCE_BAM_REQ
+#define TOTAL_IOVEC_SPACE_PER_PIPE (QCE_MAX_NUM_DSCR * sizeof(struct sps_iovec))
+
enum qce_owner {
QCE_OWNER_NONE = 0,
QCE_OWNER_CLIENT = 1,
@@ -92,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;
};
@@ -110,6 +111,8 @@ struct qce_device {
unsigned char *coh_vmem; /* Allocated coherent virtual memory */
dma_addr_t coh_pmem; /* Allocated coherent physical memory */
int memsize; /* Memory allocated */
+ unsigned char *iovec_vmem; /* Allocate iovec virtual memory */
+ int iovec_memsize; /* Memory allocated */
uint32_t bam_mem; /* bam physical address, from DT */
uint32_t bam_mem_size; /* bam io size, from DT */
int is_shared; /* CE HW is shared */
@@ -150,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);
@@ -4299,24 +4303,30 @@ static int qce_setup_ce_sps_data(struct qce_device *pce_dev)
{
unsigned char *vaddr;
int i;
+ unsigned char *iovec_vaddr;
+ int iovec_memsize;
vaddr = pce_dev->coh_vmem;
vaddr = (unsigned char *)ALIGN(((uintptr_t)vaddr),
pce_dev->ce_bam_info.ce_burst_size);
+ iovec_vaddr = pce_dev->iovec_vmem;
+ iovec_memsize = pce_dev->iovec_memsize;
for (i = 0; i < MAX_QCE_ALLOC_BAM_REQ; i++) {
/* Allow for 256 descriptor (cmd and data) entries per pipe */
pce_dev->ce_request_info[i].ce_sps.in_transfer.iovec =
- (struct sps_iovec *)vaddr;
+ (struct sps_iovec *)iovec_vaddr;
pce_dev->ce_request_info[i].ce_sps.in_transfer.iovec_phys =
- (uintptr_t)GET_PHYS_ADDR(vaddr);
- vaddr += QCE_MAX_NUM_DSCR * sizeof(struct sps_iovec);
-
+ virt_to_phys(pce_dev->ce_request_info[i].
+ ce_sps.in_transfer.iovec);
+ iovec_vaddr += TOTAL_IOVEC_SPACE_PER_PIPE;
+ iovec_memsize -= TOTAL_IOVEC_SPACE_PER_PIPE;
pce_dev->ce_request_info[i].ce_sps.out_transfer.iovec =
- (struct sps_iovec *)vaddr;
+ (struct sps_iovec *)iovec_vaddr;
pce_dev->ce_request_info[i].ce_sps.out_transfer.iovec_phys =
- (uintptr_t)GET_PHYS_ADDR(vaddr);
- vaddr += QCE_MAX_NUM_DSCR * sizeof(struct sps_iovec);
-
+ virt_to_phys(pce_dev->ce_request_info[i].
+ ce_sps.out_transfer.iovec);
+ iovec_vaddr += TOTAL_IOVEC_SPACE_PER_PIPE;
+ iovec_memsize -= TOTAL_IOVEC_SPACE_PER_PIPE;
if (pce_dev->support_cmd_dscr)
qce_setup_cmdlistptrs(pce_dev, i, &vaddr);
vaddr = (unsigned char *)ALIGN(((uintptr_t)vaddr),
@@ -4341,9 +4351,8 @@ 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)
+ if ((vaddr - pce_dev->coh_vmem) > pce_dev->memsize ||
+ iovec_memsize < 0)
panic("qce50: Not enough coherent memory. Allocate %x , need %lx\n",
pce_dev->memsize, (uintptr_t)vaddr -
(uintptr_t)pce_dev->coh_vmem);
@@ -5876,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;
@@ -5933,12 +5942,23 @@ void *qce_open(struct platform_device *pdev, int *rc)
pce_dev->memsize = 10 * PAGE_SIZE * MAX_QCE_ALLOC_BAM_REQ;
pce_dev->coh_vmem = dma_alloc_coherent(pce_dev->pdev,
pce_dev->memsize, &pce_dev->coh_pmem, GFP_KERNEL);
+
if (pce_dev->coh_vmem == NULL) {
*rc = -ENOMEM;
pr_err("Can not allocate coherent memory for sps data\n");
goto err_iobase;
}
+ pce_dev->iovec_memsize = TOTAL_IOVEC_SPACE_PER_PIPE *
+ MAX_QCE_ALLOC_BAM_REQ * 2;
+ pce_dev->iovec_vmem = kzalloc(pce_dev->iovec_memsize, GFP_KERNEL);
+ 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;
@@ -5978,6 +5998,8 @@ 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,
pce_dev->coh_vmem, pce_dev->coh_pmem);
@@ -6008,6 +6030,8 @@ 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);
__qce_deinit_clk(pce_dev);
diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c
index 2b0bacdb5f6a..707a244e62e9 100644
--- a/drivers/devfreq/bimc-bwmon.c
+++ b/drivers/devfreq/bimc-bwmon.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -18,6 +18,7 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
@@ -40,10 +41,24 @@
#define MON_MASK(m) ((m)->base + 0x298)
#define MON_MATCH(m) ((m)->base + 0x29C)
+#define MON2_EN(m) ((m)->base + 0x2A0)
+#define MON2_CLEAR(m) ((m)->base + 0x2A4)
+#define MON2_SW(m) ((m)->base + 0x2A8)
+#define MON2_THRES_HI(m) ((m)->base + 0x2AC)
+#define MON2_THRES_MED(m) ((m)->base + 0x2B0)
+#define MON2_THRES_LO(m) ((m)->base + 0x2B4)
+#define MON2_ZONE_ACTIONS(m) ((m)->base + 0x2B8)
+#define MON2_ZONE_CNT_THRES(m) ((m)->base + 0x2BC)
+#define MON2_BYTE_CNT(m) ((m)->base + 0x2D0)
+#define MON2_WIN_TIMER(m) ((m)->base + 0x2D4)
+#define MON2_ZONE_CNT(m) ((m)->base + 0x2D8)
+#define MON2_ZONE_MAX(m, zone) ((m)->base + 0x2E0 + 0x4 * zone)
+
struct bwmon_spec {
bool wrap_on_thres;
bool overflow;
bool throt_adj;
+ bool hw_sampling;
};
struct bwmon {
@@ -54,24 +69,37 @@ struct bwmon {
const struct bwmon_spec *spec;
struct device *dev;
struct bw_hwmon hw;
+ u32 hw_timer_hz;
u32 throttle_adj;
+ u32 sample_size_ms;
+ u32 intr_status;
};
#define to_bwmon(ptr) container_of(ptr, struct bwmon, hw)
+#define has_hw_sampling(m) (m->spec->hw_sampling)
#define ENABLE_MASK BIT(0)
#define THROTTLE_MASK 0x1F
#define THROTTLE_SHIFT 16
+#define INT_ENABLE_V1 0x1
+#define INT_STATUS_MASK 0x03
+#define INT_STATUS_MASK_HWS 0xF0
static DEFINE_SPINLOCK(glb_lock);
static void mon_enable(struct bwmon *m)
{
- writel_relaxed((ENABLE_MASK | m->throttle_adj), MON_EN(m));
+ if (has_hw_sampling(m))
+ writel_relaxed((ENABLE_MASK | m->throttle_adj), MON2_EN(m));
+ else
+ writel_relaxed((ENABLE_MASK | m->throttle_adj), MON_EN(m));
}
static void mon_disable(struct bwmon *m)
{
- writel_relaxed(m->throttle_adj, MON_EN(m));
+ if (has_hw_sampling(m))
+ writel_relaxed(m->throttle_adj, MON2_EN(m));
+ else
+ writel_relaxed(m->throttle_adj, MON_EN(m));
/*
* mon_disable() and mon_irq_clear(),
* If latter goes first and count happen to trigger irq, we would
@@ -80,17 +108,46 @@ static void mon_disable(struct bwmon *m)
mb();
}
-static void mon_clear(struct bwmon *m)
+#define MON_CLEAR_BIT 0x1
+#define MON_CLEAR_ALL_BIT 0x2
+static void mon_clear(struct bwmon *m, bool clear_all)
{
- writel_relaxed(0x1, MON_CLEAR(m));
+ if (!has_hw_sampling(m)) {
+ writel_relaxed(MON_CLEAR_BIT, MON_CLEAR(m));
+ goto out;
+ }
+
+ if (clear_all)
+ writel_relaxed(MON_CLEAR_ALL_BIT, MON2_CLEAR(m));
+ else
+ writel_relaxed(MON_CLEAR_BIT, MON2_CLEAR(m));
+
/*
* The counter clear and IRQ clear bits are not in the same 4KB
* region. So, we need to make sure the counter clear is completed
* before we try to clear the IRQ or do any other counter operations.
*/
+out:
mb();
}
+#define SAMPLE_WIN_LIM 0xFFFFF
+static void mon_set_hw_sampling_window(struct bwmon *m, unsigned int sample_ms)
+{
+ u32 rate;
+
+ if (unlikely(sample_ms != m->sample_size_ms)) {
+ rate = mult_frac(sample_ms, m->hw_timer_hz, MSEC_PER_SEC);
+ m->sample_size_ms = sample_ms;
+ if (unlikely(rate > SAMPLE_WIN_LIM)) {
+ rate = SAMPLE_WIN_LIM;
+ pr_warn("Sample window %u larger than hw limit: %u\n",
+ rate, SAMPLE_WIN_LIM);
+ }
+ writel_relaxed(rate, MON2_SW(m));
+ }
+}
+
static void mon_irq_enable(struct bwmon *m)
{
u32 val;
@@ -99,11 +156,11 @@ static void mon_irq_enable(struct bwmon *m)
val = readl_relaxed(GLB_INT_EN(m));
val |= 1 << m->mport;
writel_relaxed(val, GLB_INT_EN(m));
- spin_unlock(&glb_lock);
val = readl_relaxed(MON_INT_EN(m));
- val |= 0x1;
+ val |= has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_ENABLE_V1;
writel_relaxed(val, MON_INT_EN(m));
+ spin_unlock(&glb_lock);
/*
* make Sure irq enable complete for local and global
* to avoid race with other monitor calls
@@ -119,11 +176,11 @@ static void mon_irq_disable(struct bwmon *m)
val = readl_relaxed(GLB_INT_EN(m));
val &= ~(1 << m->mport);
writel_relaxed(val, GLB_INT_EN(m));
- spin_unlock(&glb_lock);
val = readl_relaxed(MON_INT_EN(m));
- val &= ~0x1;
+ val &= has_hw_sampling(m) ? ~INT_STATUS_MASK_HWS : ~INT_ENABLE_V1;
writel_relaxed(val, MON_INT_EN(m));
+ spin_unlock(&glb_lock);
/*
* make Sure irq disable complete for local and global
* to avoid race with other monitor calls
@@ -140,12 +197,18 @@ static unsigned int mon_irq_status(struct bwmon *m)
dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval,
readl_relaxed(GLB_INT_STATUS(m)));
+ mval &= has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_STATUS_MASK;
+
return mval;
}
static void mon_irq_clear(struct bwmon *m)
{
- writel_relaxed(0x3, MON_INT_CLR(m));
+ u32 intclr;
+
+ intclr = has_hw_sampling(m) ? INT_STATUS_MASK_HWS : INT_STATUS_MASK;
+
+ writel_relaxed(intclr, MON_INT_CLR(m));
mb();
writel_relaxed(1 << m->mport, GLB_INT_CLR(m));
mb();
@@ -171,6 +234,90 @@ static u32 mon_get_throttle_adj(struct bw_hwmon *hw)
return m->throttle_adj >> THROTTLE_SHIFT;
}
+#define ZONE1_SHIFT 8
+#define ZONE2_SHIFT 16
+#define ZONE3_SHIFT 24
+#define ZONE0_ACTION 0x01 /* Increment zone 0 count */
+#define ZONE1_ACTION 0x09 /* Increment zone 1 & clear lower zones */
+#define ZONE2_ACTION 0x25 /* Increment zone 2 & clear lower zones */
+#define ZONE3_ACTION 0x95 /* Increment zone 3 & clear lower zones */
+static u32 calc_zone_actions(void)
+{
+ u32 zone_actions;
+
+ zone_actions = ZONE0_ACTION;
+ zone_actions |= ZONE1_ACTION << ZONE1_SHIFT;
+ zone_actions |= ZONE2_ACTION << ZONE2_SHIFT;
+ zone_actions |= ZONE3_ACTION << ZONE3_SHIFT;
+
+ return zone_actions;
+}
+
+#define ZONE_CNT_LIM 0xFFU
+#define UP_CNT_1 1
+static u32 calc_zone_counts(struct bw_hwmon *hw)
+{
+ u32 zone_counts;
+
+ zone_counts = ZONE_CNT_LIM;
+ zone_counts |= min(hw->down_cnt, ZONE_CNT_LIM) << ZONE1_SHIFT;
+ zone_counts |= ZONE_CNT_LIM << ZONE2_SHIFT;
+ zone_counts |= UP_CNT_1 << ZONE3_SHIFT;
+
+ return zone_counts;
+}
+
+static unsigned int mbps_to_mb(unsigned long mbps, unsigned int ms)
+{
+ mbps *= ms;
+ mbps = DIV_ROUND_UP(mbps, MSEC_PER_SEC);
+ return mbps;
+}
+
+/*
+ * Define the 4 zones using HI, MED & LO thresholds:
+ * Zone 0: byte count < THRES_LO
+ * Zone 1: THRES_LO < byte count < THRES_MED
+ * Zone 2: THRES_MED < byte count < THRES_HI
+ * Zone 3: byte count > THRES_HI
+ */
+#define THRES_LIM 0x7FFU
+static void set_zone_thres(struct bwmon *m, unsigned int sample_ms)
+{
+ struct bw_hwmon *hw = &(m->hw);
+ u32 hi, med, lo;
+
+ hi = mbps_to_mb(hw->up_wake_mbps, sample_ms);
+ med = mbps_to_mb(hw->down_wake_mbps, sample_ms);
+ lo = 0;
+
+ if (unlikely((hi > THRES_LIM) || (med > hi) || (lo > med))) {
+ pr_warn("Zone thres larger than hw limit: hi:%u med:%u lo:%u\n",
+ hi, med, lo);
+ hi = min(hi, THRES_LIM);
+ med = min(med, hi - 1);
+ lo = min(lo, med-1);
+ }
+
+ writel_relaxed(hi, MON2_THRES_HI(m));
+ writel_relaxed(med, MON2_THRES_MED(m));
+ writel_relaxed(lo, MON2_THRES_LO(m));
+ dev_dbg(m->dev, "Thres: hi:%u med:%u lo:%u\n", hi, med, lo);
+}
+
+static void mon_set_zones(struct bwmon *m, unsigned int sample_ms)
+{
+ struct bw_hwmon *hw = &(m->hw);
+ u32 zone_cnt_thres = calc_zone_counts(hw);
+
+ mon_set_hw_sampling_window(m, sample_ms);
+ set_zone_thres(m, sample_ms);
+ /* Set the zone count thresholds for interrupts */
+ writel_relaxed(zone_cnt_thres, MON2_ZONE_CNT_THRES(m));
+
+ dev_dbg(m->dev, "Zone Count Thres: %0x\n", zone_cnt_thres);
+}
+
static void mon_set_limit(struct bwmon *m, u32 count)
{
writel_relaxed(count, MON_THRES(m));
@@ -203,6 +350,41 @@ static unsigned long mon_get_count(struct bwmon *m)
return count;
}
+static unsigned int get_zone(struct bwmon *m)
+{
+ u32 zone_counts;
+ u32 zone;
+
+ zone = get_bitmask_order((m->intr_status & INT_STATUS_MASK_HWS) >> 4);
+ if (zone) {
+ zone--;
+ } else {
+ zone_counts = readl_relaxed(MON2_ZONE_CNT(m));
+ if (zone_counts) {
+ zone = get_bitmask_order(zone_counts) - 1;
+ zone /= 8;
+ }
+ }
+
+ m->intr_status = 0;
+ return zone;
+}
+
+static unsigned long mon_get_zone_stats(struct bwmon *m)
+{
+ unsigned int zone;
+ unsigned long count = 0;
+
+ zone = get_zone(m);
+
+ count = readl_relaxed(MON2_ZONE_MAX(m, zone));
+ count *= SZ_1M;
+
+ dev_dbg(m->dev, "Zone%d Max byte count: %08lx\n", zone, count);
+
+ return count;
+}
+
/* ********** CPUBW specific code ********** */
/* Returns MBps of read/writes for the sampling window. */
@@ -222,8 +404,8 @@ static unsigned long get_bytes_and_clear(struct bw_hwmon *hw)
unsigned long count;
mon_disable(m);
- count = mon_get_count(m);
- mon_clear(m);
+ count = has_hw_sampling(m) ? mon_get_zone_stats(m) : mon_get_count(m);
+ mon_clear(m, false);
mon_irq_clear(m);
mon_enable(m);
@@ -238,7 +420,7 @@ static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes)
mon_disable(m);
count = mon_get_count(m);
- mon_clear(m);
+ mon_clear(m, false);
mon_irq_clear(m);
if (likely(!m->spec->wrap_on_thres))
@@ -252,11 +434,26 @@ static unsigned long set_thres(struct bw_hwmon *hw, unsigned long bytes)
return count;
}
+static unsigned long set_hw_events(struct bw_hwmon *hw, unsigned sample_ms)
+{
+ struct bwmon *m = to_bwmon(hw);
+
+ mon_disable(m);
+ mon_clear(m, false);
+ mon_irq_clear(m);
+
+ mon_set_zones(m, sample_ms);
+ mon_enable(m);
+
+ return 0;
+}
+
static irqreturn_t bwmon_intr_handler(int irq, void *dev)
{
struct bwmon *m = dev;
- if (!mon_irq_status(m))
+ m->intr_status = mon_irq_status(m);
+ if (!m->intr_status)
return IRQ_NONE;
if (bw_hwmon_sample_end(&m->hw) > 0)
@@ -277,6 +474,7 @@ static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps)
{
struct bwmon *m = to_bwmon(hw);
u32 limit;
+ u32 zone_actions = calc_zone_actions();
int ret;
ret = request_threaded_irq(m->irq, bwmon_intr_handler,
@@ -291,10 +489,16 @@ static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps)
mon_disable(m);
+ mon_clear(m, true);
limit = mbps_to_bytes(mbps, hw->df->profile->polling_ms, 0);
- mon_set_limit(m, limit);
+ if (has_hw_sampling(m)) {
+ mon_set_zones(m, hw->df->profile->polling_ms);
+ /* Set the zone actions to increment appropriate counters */
+ writel_relaxed(zone_actions, MON2_ZONE_ACTIONS(m));
+ } else {
+ mon_set_limit(m, limit);
+ }
- mon_clear(m);
mon_irq_clear(m);
mon_irq_enable(m);
mon_enable(m);
@@ -309,7 +513,7 @@ static void stop_bw_hwmon(struct bw_hwmon *hw)
mon_irq_disable(m);
free_irq(m->irq, m);
mon_disable(m);
- mon_clear(m);
+ mon_clear(m, true);
mon_irq_clear(m);
}
@@ -330,7 +534,7 @@ static int resume_bw_hwmon(struct bw_hwmon *hw)
struct bwmon *m = to_bwmon(hw);
int ret;
- mon_clear(m);
+ mon_clear(m, false);
ret = request_threaded_irq(m->irq, bwmon_intr_handler,
bwmon_intr_thread,
IRQF_ONESHOT | IRQF_SHARED,
@@ -350,15 +554,21 @@ static int resume_bw_hwmon(struct bw_hwmon *hw)
/*************************************************************************/
static const struct bwmon_spec spec[] = {
- { .wrap_on_thres = true, .overflow = false, .throt_adj = false},
- { .wrap_on_thres = false, .overflow = true, .throt_adj = false},
- { .wrap_on_thres = false, .overflow = true, .throt_adj = true},
+ { .wrap_on_thres = true, .overflow = false, .throt_adj = false,
+ .hw_sampling = false},
+ { .wrap_on_thres = false, .overflow = true, .throt_adj = false,
+ .hw_sampling = false},
+ { .wrap_on_thres = false, .overflow = true, .throt_adj = true,
+ .hw_sampling = false},
+ { .wrap_on_thres = false, .overflow = true, .throt_adj = true,
+ .hw_sampling = true},
};
static struct of_device_id match_table[] = {
{ .compatible = "qcom,bimc-bwmon", .data = &spec[0] },
{ .compatible = "qcom,bimc-bwmon2", .data = &spec[1] },
{ .compatible = "qcom,bimc-bwmon3", .data = &spec[2] },
+ { .compatible = "qcom,bimc-bwmon4", .data = &spec[3] },
{}
};
@@ -390,6 +600,16 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev)
}
m->spec = id->data;
+ if (has_hw_sampling(m)) {
+ ret = of_property_read_u32(dev->of_node,
+ "qcom,hw-timer-hz", &data);
+ if (ret) {
+ dev_err(dev, "HW sampling rate not specified!\n");
+ return ret;
+ }
+ m->hw_timer_hz = data;
+ }
+
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
if (!res) {
dev_err(dev, "base not found!\n");
@@ -426,7 +646,9 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev)
m->hw.suspend_hwmon = &suspend_bw_hwmon;
m->hw.resume_hwmon = &resume_bw_hwmon;
m->hw.get_bytes_and_clear = &get_bytes_and_clear;
- m->hw.set_thres = &set_thres;
+ m->hw.set_thres = &set_thres;
+ if (has_hw_sampling(m))
+ m->hw.set_hw_events = &set_hw_events;
if (m->spec->throt_adj) {
m->hw.set_throttle_adj = &mon_set_throttle_adj;
m->hw.get_throttle_adj = &mon_get_throttle_adj;
diff --git a/drivers/devfreq/governor_bw_hwmon.c b/drivers/devfreq/governor_bw_hwmon.c
index e0f75b0022fc..b997e79e3d73 100644
--- a/drivers/devfreq/governor_bw_hwmon.c
+++ b/drivers/devfreq/governor_bw_hwmon.c
@@ -67,8 +67,6 @@ struct hwmon_node {
unsigned long hyst_en;
unsigned long above_low_power;
unsigned long prev_req;
- unsigned long up_wake_mbps;
- unsigned long down_wake_mbps;
unsigned int wake;
unsigned int down_cnt;
ktime_t prev_ts;
@@ -191,7 +189,7 @@ static unsigned int mbps_to_bytes(unsigned long mbps, unsigned int ms)
return mbps;
}
-static int __bw_hwmon_sample_end(struct bw_hwmon *hwmon)
+static int __bw_hwmon_sw_sample_end(struct bw_hwmon *hwmon)
{
struct devfreq *df;
struct hwmon_node *node;
@@ -220,9 +218,9 @@ static int __bw_hwmon_sample_end(struct bw_hwmon *hwmon)
* bandwidth usage and do the bandwidth calculation based on just
* this micro sample.
*/
- if (mbps > node->up_wake_mbps) {
+ if (mbps > node->hw->up_wake_mbps) {
wake = UP_WAKE;
- } else if (mbps < node->down_wake_mbps) {
+ } else if (mbps < node->hw->down_wake_mbps) {
if (node->down_cnt)
node->down_cnt--;
if (node->down_cnt <= 0)
@@ -241,6 +239,50 @@ static int __bw_hwmon_sample_end(struct bw_hwmon *hwmon)
return wake;
}
+static int __bw_hwmon_hw_sample_end(struct bw_hwmon *hwmon)
+{
+ struct devfreq *df;
+ struct hwmon_node *node;
+ unsigned long bytes, mbps;
+ int wake = 0;
+
+ df = hwmon->df;
+ node = df->data;
+
+ /*
+ * If this read is in response to an IRQ, the HW monitor should
+ * return the measurement in the micro sample that triggered the IRQ.
+ * Otherwise, it should return the maximum measured value in any
+ * micro sample since the last time we called get_bytes_and_clear()
+ */
+ bytes = hwmon->get_bytes_and_clear(hwmon);
+ mbps = bytes_to_mbps(bytes, node->sample_ms * USEC_PER_MSEC);
+ node->max_mbps = mbps;
+
+ if (mbps > node->hw->up_wake_mbps)
+ wake = UP_WAKE;
+ else if (mbps < node->hw->down_wake_mbps)
+ wake = DOWN_WAKE;
+
+ node->wake = wake;
+ node->sampled = true;
+
+ trace_bw_hwmon_meas(dev_name(df->dev.parent),
+ mbps,
+ node->sample_ms * USEC_PER_MSEC,
+ wake);
+
+ return 1;
+}
+
+static int __bw_hwmon_sample_end(struct bw_hwmon *hwmon)
+{
+ if (hwmon->set_hw_events)
+ return __bw_hwmon_hw_sample_end(hwmon);
+ else
+ return __bw_hwmon_sw_sample_end(hwmon);
+}
+
int bw_hwmon_sample_end(struct bw_hwmon *hwmon)
{
unsigned long flags;
@@ -275,12 +317,14 @@ static unsigned long get_bw_and_set_irq(struct hwmon_node *node,
struct bw_hwmon *hw = node->hw;
unsigned int new_bw, io_percent;
ktime_t ts;
- unsigned int ms;
+ unsigned int ms = 0;
spin_lock_irqsave(&irq_lock, flags);
- ts = ktime_get();
- ms = ktime_to_ms(ktime_sub(ts, node->prev_ts));
+ if (!hw->set_hw_events) {
+ ts = ktime_get();
+ ms = ktime_to_ms(ktime_sub(ts, node->prev_ts));
+ }
if (!node->sampled || ms >= node->sample_ms)
__bw_hwmon_sample_end(node->hw);
node->sampled = false;
@@ -388,9 +432,10 @@ static unsigned long get_bw_and_set_irq(struct hwmon_node *node,
/* Stretch the short sample window size, if the traffic is too low */
if (meas_mbps < MIN_MBPS) {
- node->up_wake_mbps = (max(MIN_MBPS, req_mbps)
+ hw->up_wake_mbps = (max(MIN_MBPS, req_mbps)
* (100 + node->up_thres)) / 100;
- node->down_wake_mbps = 0;
+ hw->down_wake_mbps = 0;
+ hw->undo_over_req_mbps = 0;
thres = mbps_to_bytes(max(MIN_MBPS, req_mbps / 2),
node->sample_ms);
} else {
@@ -401,13 +446,22 @@ static unsigned long get_bw_and_set_irq(struct hwmon_node *node,
* reduce the vote based on the measured mbps being less than
* the previous measurement that caused the "over request".
*/
- node->up_wake_mbps = (req_mbps * (100 + node->up_thres)) / 100;
- node->down_wake_mbps = (meas_mbps * node->down_thres) / 100;
+ hw->up_wake_mbps = (req_mbps * (100 + node->up_thres)) / 100;
+ hw->down_wake_mbps = (meas_mbps * node->down_thres) / 100;
+ if (node->wake == UP_WAKE)
+ hw->undo_over_req_mbps = min(req_mbps, meas_mbps_zone);
+ else
+ hw->undo_over_req_mbps = 0;
thres = mbps_to_bytes(meas_mbps, node->sample_ms);
}
- node->down_cnt = node->down_count;
- node->bytes = hw->set_thres(hw, thres);
+ if (hw->set_hw_events) {
+ hw->down_cnt = node->down_count;
+ hw->set_hw_events(hw, node->sample_ms);
+ } else {
+ node->down_cnt = node->down_count;
+ node->bytes = hw->set_thres(hw, thres);
+ }
node->wake = 0;
node->prev_req = req_mbps;
@@ -432,8 +486,8 @@ static unsigned long get_bw_and_set_irq(struct hwmon_node *node,
trace_bw_hwmon_update(dev_name(node->hw->df->dev.parent),
new_bw,
*freq,
- node->up_wake_mbps,
- node->down_wake_mbps);
+ hw->up_wake_mbps,
+ hw->down_wake_mbps);
return req_mbps;
}
@@ -503,6 +557,9 @@ static int start_monitor(struct devfreq *df, bool init)
node->resume_freq = 0;
node->resume_ab = 0;
mbps = (df->previous_freq * node->io_percent) / 100;
+ hw->up_wake_mbps = mbps;
+ hw->down_wake_mbps = MIN_MBPS;
+ hw->undo_over_req_mbps = 0;
ret = hw->start_hwmon(hw, mbps);
} else {
ret = hw->resume_hwmon(hw);
diff --git a/drivers/devfreq/governor_bw_hwmon.h b/drivers/devfreq/governor_bw_hwmon.h
index 59bf48ac5af7..7578399cfb88 100644
--- a/drivers/devfreq/governor_bw_hwmon.h
+++ b/drivers/devfreq/governor_bw_hwmon.h
@@ -48,6 +48,8 @@ struct bw_hwmon {
int (*suspend_hwmon)(struct bw_hwmon *hw);
int (*resume_hwmon)(struct bw_hwmon *hw);
unsigned long (*set_thres)(struct bw_hwmon *hw, unsigned long bytes);
+ unsigned long (*set_hw_events)(struct bw_hwmon *hw,
+ unsigned int sample_ms);
unsigned long (*get_bytes_and_clear)(struct bw_hwmon *hw);
int (*set_throttle_adj)(struct bw_hwmon *hw, uint adj);
u32 (*get_throttle_adj)(struct bw_hwmon *hw);
@@ -55,6 +57,11 @@ struct bw_hwmon {
struct device_node *of_node;
struct devfreq_governor *gov;
+ unsigned long up_wake_mbps;
+ unsigned long undo_over_req_mbps;
+ unsigned long down_wake_mbps;
+ unsigned int down_cnt;
+
struct devfreq *df;
};
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/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index 778c76f52d0b..a3b25b3d8dd1 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -250,7 +250,7 @@ static const struct adreno_gpu_core adreno_gpulist[] = {
ADRENO_GPMU | ADRENO_SPTP_PC,
.pm4fw_name = "a530_pm4.fw",
.pfpfw_name = "a530_pfp.fw",
- .zap_name = "a530_zap",
+ .zap_name = "a540_zap",
.gpudev = &adreno_a5xx_gpudev,
.gmem_size = SZ_1M,
.num_protected_regs = 0x20,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 18fdd400ac7a..362493118670 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1193,7 +1193,8 @@ static int adreno_init(struct kgsl_device *device)
if (!adreno_is_a3xx(adreno_dev)) {
int r = kgsl_allocate_global(device,
- &adreno_dev->cmdbatch_profile_buffer, PAGE_SIZE, 0, 0);
+ &adreno_dev->cmdbatch_profile_buffer, PAGE_SIZE,
+ 0, 0, "alwayson");
adreno_dev->cmdbatch_profile_index = 0;
@@ -1279,7 +1280,7 @@ static void _setup_throttling_counters(struct adreno_device *adreno_dev)
static uint64_t _read_throttling_counters(struct adreno_device *adreno_dev)
{
- int i;
+ int i, adj;
uint32_t th[ADRENO_GPMU_THROTTLE_COUNTERS];
struct adreno_busy_data *busy = &adreno_dev->busy_data;
@@ -1300,8 +1301,14 @@ static uint64_t _read_throttling_counters(struct adreno_device *adreno_dev)
adreno_dev->gpmu_throttle_counters[i],
&busy->throttle_cycles[i]);
}
- i = th[CRC_MORE50PCT] - th[IDLE_10PCT];
- return th[CRC_50PCT] + th[CRC_LESS50PCT] / 3 + (i < 0 ? 0 : i) * 3;
+ adj = th[CRC_MORE50PCT] - th[IDLE_10PCT];
+ adj = th[CRC_50PCT] + th[CRC_LESS50PCT] / 3 + (adj < 0 ? 0 : adj) * 3;
+
+ trace_kgsl_clock_throttling(
+ th[IDLE_10PCT], th[CRC_50PCT],
+ th[CRC_MORE50PCT], th[CRC_LESS50PCT],
+ adj);
+ return adj;
}
static void _update_threshold_count(struct adreno_device *adreno_dev,
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 2accbe5c5764..97e71464c2df 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -174,7 +174,7 @@ static int _a3xx_pwron_fixup(struct adreno_device *adreno_dev)
ret = kgsl_allocate_global(KGSL_DEVICE(adreno_dev),
&adreno_dev->pwron_fixup, PAGE_SIZE,
- KGSL_MEMFLAGS_GPUREADONLY, 0);
+ KGSL_MEMFLAGS_GPUREADONLY, 0, "pwron_fixup");
if (ret)
return ret;
diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c
index b15d23cfbe0a..7a691667e59f 100644
--- a/drivers/gpu/msm/adreno_a4xx.c
+++ b/drivers/gpu/msm/adreno_a4xx.c
@@ -1360,7 +1360,7 @@ static int _a4xx_pwron_fixup(struct adreno_device *adreno_dev)
ret = kgsl_allocate_global(KGSL_DEVICE(adreno_dev),
&adreno_dev->pwron_fixup, PAGE_SIZE,
- KGSL_MEMFLAGS_GPUREADONLY, 0);
+ KGSL_MEMFLAGS_GPUREADONLY, 0, "pwron_fixup");
if (ret)
return ret;
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 3252bfb764f2..583de85678fc 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -244,7 +244,8 @@ static int a5xx_critical_packet_construct(struct adreno_device *adreno_dev)
ret = kgsl_allocate_global(&adreno_dev->dev,
&crit_pkts, PAGE_SIZE,
- KGSL_MEMFLAGS_GPUREADONLY, 0);
+ KGSL_MEMFLAGS_GPUREADONLY,
+ 0, "crit_pkts");
if (ret)
return ret;
@@ -258,19 +259,19 @@ static int a5xx_critical_packet_construct(struct adreno_device *adreno_dev)
ret = kgsl_allocate_global(&adreno_dev->dev,
&crit_pkts_refbuf1,
- PAGE_SIZE, 0, 0);
+ PAGE_SIZE, 0, 0, "crit_pkts_refbuf1");
if (ret)
return ret;
ret = kgsl_allocate_global(&adreno_dev->dev,
&crit_pkts_refbuf2,
- PAGE_SIZE, 0, 0);
+ PAGE_SIZE, 0, 0, "crit_pkts_refbuf2");
if (ret)
return ret;
ret = kgsl_allocate_global(&adreno_dev->dev,
&crit_pkts_refbuf3,
- PAGE_SIZE, 0, 0);
+ PAGE_SIZE, 0, 0, "crit_pkts_refbuf3");
if (ret)
return ret;
@@ -2366,7 +2367,7 @@ static int _load_firmware(struct kgsl_device *device, const char *fwfile,
}
ret = kgsl_allocate_global(device, ucode, fw->size - 4,
- KGSL_MEMFLAGS_GPUREADONLY, 0);
+ KGSL_MEMFLAGS_GPUREADONLY, 0, "ucode");
if (ret)
goto done;
diff --git a/drivers/gpu/msm/adreno_a5xx_preempt.c b/drivers/gpu/msm/adreno_a5xx_preempt.c
index c1463b824c67..4baee4a5c0b1 100644
--- a/drivers/gpu/msm/adreno_a5xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a5xx_preempt.c
@@ -311,6 +311,8 @@ void a5xx_preempt_callback(struct adreno_device *adreno_dev, int bit)
adreno_dev->cur_rb->dispatch_q.expires);
adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE);
+
+ a5xx_preemption_trigger(adreno_dev);
}
void a5xx_preemption_schedule(struct adreno_device *adreno_dev)
@@ -490,7 +492,8 @@ static int a5xx_preemption_ringbuffer_init(struct adreno_device *adreno_dev,
int ret;
ret = kgsl_allocate_global(device, &rb->preemption_desc,
- A5XX_CP_CTXRECORD_SIZE_IN_BYTES, 0, KGSL_MEMDESC_PRIVILEGED);
+ A5XX_CP_CTXRECORD_SIZE_IN_BYTES, 0, KGSL_MEMDESC_PRIVILEGED,
+ "preemption_desc");
if (ret)
return ret;
@@ -525,7 +528,8 @@ static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev)
/* Allocate mem for storing preemption smmu record */
return kgsl_allocate_global(device, &iommu->smmu_info, PAGE_SIZE,
- KGSL_MEMFLAGS_GPUREADONLY, KGSL_MEMDESC_PRIVILEGED);
+ KGSL_MEMFLAGS_GPUREADONLY, KGSL_MEMDESC_PRIVILEGED,
+ "smmu_info");
}
#else
static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev)
@@ -555,7 +559,8 @@ int a5xx_preemption_init(struct adreno_device *adreno_dev)
/* Allocate mem for storing preemption counters */
ret = kgsl_allocate_global(device, &preempt->counters,
adreno_dev->num_ringbuffers *
- A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE, 0, 0);
+ A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE, 0, 0,
+ "preemption_counters");
if (ret)
return ret;
diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c
index 04d82844a5e9..aeffeab2f6dc 100644
--- a/drivers/gpu/msm/adreno_a5xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c
@@ -1033,11 +1033,11 @@ void a5xx_crashdump_init(struct adreno_device *adreno_dev)
/* The script buffers needs 2 extra qwords on the end */
if (kgsl_allocate_global(device, &capturescript,
script_size + 16, KGSL_MEMFLAGS_GPUREADONLY,
- KGSL_MEMDESC_PRIVILEGED))
+ KGSL_MEMDESC_PRIVILEGED, "capturescript"))
return;
if (kgsl_allocate_global(device, &registers, data_size, 0,
- KGSL_MEMDESC_PRIVILEGED)) {
+ KGSL_MEMDESC_PRIVILEGED, "capturescript_regs")) {
kgsl_free_global(KGSL_DEVICE(adreno_dev), &capturescript);
return;
}
diff --git a/drivers/gpu/msm/adreno_profile.c b/drivers/gpu/msm/adreno_profile.c
index c4fab8a5528a..d8af520b2fe6 100644
--- a/drivers/gpu/msm/adreno_profile.c
+++ b/drivers/gpu/msm/adreno_profile.c
@@ -1071,7 +1071,8 @@ void adreno_profile_init(struct adreno_device *adreno_dev)
/* allocate shared_buffer, which includes pre_ib and post_ib */
profile->shared_size = ADRENO_PROFILE_SHARED_BUF_SIZE_DWORDS;
ret = kgsl_allocate_global(device, &profile->shared_buffer,
- profile->shared_size * sizeof(unsigned int), 0, 0);
+ profile->shared_size * sizeof(unsigned int),
+ 0, 0, "profile");
if (ret) {
profile->shared_size = 0;
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 5ffb0b2513f3..07ef09034d7c 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -250,12 +250,12 @@ static int _adreno_ringbuffer_probe(struct adreno_device *adreno_dev,
* switch pagetable
*/
ret = kgsl_allocate_global(KGSL_DEVICE(adreno_dev), &rb->pagetable_desc,
- PAGE_SIZE, 0, KGSL_MEMDESC_PRIVILEGED);
+ PAGE_SIZE, 0, KGSL_MEMDESC_PRIVILEGED, "pagetable_desc");
if (ret)
return ret;
-
return kgsl_allocate_global(KGSL_DEVICE(adreno_dev), &rb->buffer_desc,
- KGSL_RB_SIZE, KGSL_MEMFLAGS_GPUREADONLY, 0);
+ KGSL_RB_SIZE, KGSL_MEMFLAGS_GPUREADONLY,
+ 0, "ringbuffer");
}
int adreno_ringbuffer_probe(struct adreno_device *adreno_dev, bool nopreempt)
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 691f687cd839..6e9abc99bcc4 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -4474,13 +4474,13 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
goto error_close_mmu;
status = kgsl_allocate_global(device, &device->memstore,
- KGSL_MEMSTORE_SIZE, 0, KGSL_MEMDESC_CONTIG);
+ KGSL_MEMSTORE_SIZE, 0, KGSL_MEMDESC_CONTIG, "memstore");
if (status != 0)
goto error_close_mmu;
status = kgsl_allocate_global(device, &device->scratch,
- PAGE_SIZE, 0, 0);
+ PAGE_SIZE, 0, 0, "scratch");
if (status != 0)
goto error_free_memstore;
@@ -4519,7 +4519,7 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
}
device->events_wq = alloc_workqueue("kgsl-events",
- WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0);
/* Initalize the snapshot engine */
kgsl_device_snapshot_init(device);
@@ -4662,7 +4662,8 @@ static int __init kgsl_core_init(void)
INIT_LIST_HEAD(&kgsl_driver.pagetable_list);
- kgsl_driver.workqueue = create_singlethread_workqueue("kgsl-workqueue");
+ kgsl_driver.workqueue = alloc_workqueue("kgsl-workqueue",
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0);
kgsl_driver.mem_workqueue = alloc_workqueue("kgsl-mementry",
WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index df9eb9ebd779..2f293e4da398 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -280,6 +280,29 @@ static const struct file_operations process_sparse_mem_fops = {
.release = process_mem_release,
};
+static int globals_print(struct seq_file *s, void *unused)
+{
+ kgsl_print_global_pt_entries(s);
+ return 0;
+}
+
+static int globals_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, globals_print, NULL);
+}
+
+static int globals_release(struct inode *inode, struct file *file)
+{
+ return single_release(inode, file);
+}
+
+static const struct file_operations global_fops = {
+ .open = globals_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = globals_release,
+};
+
/**
* kgsl_process_init_debugfs() - Initialize debugfs for a process
* @private: Pointer to process private structure created for the process
@@ -336,6 +359,9 @@ void kgsl_core_debugfs_init(void)
kgsl_debugfs_dir = debugfs_create_dir("kgsl", NULL);
+ debugfs_create_file("globals", 0444, kgsl_debugfs_dir, NULL,
+ &global_fops);
+
debug_dir = debugfs_create_dir("debug", kgsl_debugfs_dir);
debugfs_create_file("strict_memory", 0644, debug_dir, NULL,
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 166bb68e64a1..71b6086423d6 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -38,6 +38,10 @@
#define _IOMMU_PRIV(_mmu) (&((_mmu)->priv.iommu))
+#define ADDR_IN_GLOBAL(_a) \
+ (((_a) >= KGSL_IOMMU_GLOBAL_MEM_BASE) && \
+ ((_a) < (KGSL_IOMMU_GLOBAL_MEM_BASE + KGSL_IOMMU_GLOBAL_MEM_SIZE)))
+
static struct kgsl_mmu_pt_ops iommu_pt_ops;
static bool need_iommu_sync;
@@ -92,19 +96,41 @@ static struct kmem_cache *addr_entry_cache;
#define GLOBAL_PT_ENTRIES 32
-static struct kgsl_memdesc *global_pt_entries[GLOBAL_PT_ENTRIES];
+struct global_pt_entry {
+ struct kgsl_memdesc *memdesc;
+ char name[32];
+};
+
+static struct global_pt_entry global_pt_entries[GLOBAL_PT_ENTRIES];
static struct kgsl_memdesc *kgsl_global_secure_pt_entry;
static int global_pt_count;
uint64_t global_pt_alloc;
static struct kgsl_memdesc gpu_qdss_desc;
+void kgsl_print_global_pt_entries(struct seq_file *s)
+{
+ int i;
+
+ for (i = 0; i < global_pt_count; i++) {
+ struct kgsl_memdesc *memdesc = global_pt_entries[i].memdesc;
+
+ if (memdesc == NULL)
+ continue;
+
+ seq_printf(s, "0x%16.16llX-0x%16.16llX %16llu %s\n",
+ memdesc->gpuaddr, memdesc->gpuaddr + memdesc->size - 1,
+ memdesc->size, global_pt_entries[i].name);
+ }
+}
+
static void kgsl_iommu_unmap_globals(struct kgsl_pagetable *pagetable)
{
unsigned int i;
for (i = 0; i < global_pt_count; i++) {
- if (global_pt_entries[i] != NULL)
- kgsl_mmu_unmap(pagetable, global_pt_entries[i]);
+ if (global_pt_entries[i].memdesc != NULL)
+ kgsl_mmu_unmap(pagetable,
+ global_pt_entries[i].memdesc);
}
}
@@ -113,8 +139,9 @@ static void kgsl_iommu_map_globals(struct kgsl_pagetable *pagetable)
unsigned int i;
for (i = 0; i < global_pt_count; i++) {
- if (global_pt_entries[i] != NULL) {
- int ret = kgsl_mmu_map(pagetable, global_pt_entries[i]);
+ if (global_pt_entries[i].memdesc != NULL) {
+ int ret = kgsl_mmu_map(pagetable,
+ global_pt_entries[i].memdesc);
BUG_ON(ret);
}
@@ -152,17 +179,17 @@ static void kgsl_iommu_remove_global(struct kgsl_mmu *mmu,
return;
for (i = 0; i < global_pt_count; i++) {
- if (global_pt_entries[i] == memdesc) {
+ if (global_pt_entries[i].memdesc == memdesc) {
memdesc->gpuaddr = 0;
memdesc->priv &= ~KGSL_MEMDESC_GLOBAL;
- global_pt_entries[i] = NULL;
+ global_pt_entries[i].memdesc = NULL;
return;
}
}
}
static void kgsl_iommu_add_global(struct kgsl_mmu *mmu,
- struct kgsl_memdesc *memdesc)
+ struct kgsl_memdesc *memdesc, const char *name)
{
if (memdesc->gpuaddr != 0)
return;
@@ -174,7 +201,10 @@ static void kgsl_iommu_add_global(struct kgsl_mmu *mmu,
memdesc->priv |= KGSL_MEMDESC_GLOBAL;
global_pt_alloc += memdesc->size;
- global_pt_entries[global_pt_count++] = memdesc;
+ global_pt_entries[global_pt_count].memdesc = memdesc;
+ strlcpy(global_pt_entries[global_pt_count].name, name,
+ sizeof(global_pt_entries[global_pt_count].name));
+ global_pt_count++;
}
void kgsl_add_global_secure_entry(struct kgsl_device *device,
@@ -220,7 +250,7 @@ static void kgsl_setup_qdss_desc(struct kgsl_device *device)
return;
}
- kgsl_mmu_add_global(device, &gpu_qdss_desc);
+ kgsl_mmu_add_global(device, &gpu_qdss_desc, "gpu-qdss");
}
static inline void kgsl_cleanup_qdss_desc(struct kgsl_mmu *mmu)
@@ -493,8 +523,62 @@ struct _mem_entry {
unsigned int priv;
int pending_free;
pid_t pid;
+ char name[32];
};
+static void _get_global_entries(uint64_t faultaddr,
+ struct _mem_entry *prev,
+ struct _mem_entry *next)
+{
+ int i;
+ uint64_t prevaddr = 0;
+ struct global_pt_entry *p = NULL;
+
+ uint64_t nextaddr = (uint64_t) -1;
+ struct global_pt_entry *n = NULL;
+
+ for (i = 0; i < global_pt_count; i++) {
+ uint64_t addr;
+
+ if (global_pt_entries[i].memdesc == NULL)
+ continue;
+
+ addr = global_pt_entries[i].memdesc->gpuaddr;
+ if ((addr < faultaddr) && (addr > prevaddr)) {
+ prevaddr = addr;
+ p = &global_pt_entries[i];
+ }
+
+ if ((addr > faultaddr) && (addr < nextaddr)) {
+ nextaddr = addr;
+ n = &global_pt_entries[i];
+ }
+ }
+
+ if (p != NULL) {
+ prev->gpuaddr = p->memdesc->gpuaddr;
+ prev->size = p->memdesc->size;
+ prev->flags = p->memdesc->flags;
+ prev->priv = p->memdesc->priv;
+ prev->pid = 0;
+ strlcpy(prev->name, p->name, sizeof(prev->name));
+ }
+
+ if (n != NULL) {
+ next->gpuaddr = n->memdesc->gpuaddr;
+ next->size = n->memdesc->size;
+ next->flags = n->memdesc->flags;
+ next->priv = n->memdesc->priv;
+ next->pid = 0;
+ strlcpy(next->name, n->name, sizeof(next->name));
+ }
+}
+
+void __kgsl_get_memory_usage(struct _mem_entry *entry)
+{
+ kgsl_get_memory_usage(entry->name, sizeof(entry->name), entry->flags);
+}
+
static void _get_entries(struct kgsl_process_private *private,
uint64_t faultaddr, struct _mem_entry *prev,
struct _mem_entry *next)
@@ -529,6 +613,7 @@ static void _get_entries(struct kgsl_process_private *private,
prev->priv = p->memdesc.priv;
prev->pending_free = p->pending_free;
prev->pid = private->pid;
+ __kgsl_get_memory_usage(prev);
}
if (n != NULL) {
@@ -538,6 +623,7 @@ static void _get_entries(struct kgsl_process_private *private,
next->priv = n->memdesc.priv;
next->pending_free = n->pending_free;
next->pid = private->pid;
+ __kgsl_get_memory_usage(next);
}
}
@@ -553,7 +639,9 @@ static void _find_mem_entries(struct kgsl_mmu *mmu, uint64_t faultaddr,
/* Set the maximum possible size as an initial value */
nextentry->gpuaddr = (uint64_t) -1;
- if (context) {
+ if (ADDR_IN_GLOBAL(faultaddr)) {
+ _get_global_entries(faultaddr, preventry, nextentry);
+ } else if (context) {
private = context->proc_priv;
spin_lock(&private->mem_lock);
_get_entries(private, faultaddr, preventry, nextentry);
@@ -563,18 +651,13 @@ static void _find_mem_entries(struct kgsl_mmu *mmu, uint64_t faultaddr,
static void _print_entry(struct kgsl_device *device, struct _mem_entry *entry)
{
- char name[32];
- memset(name, 0, sizeof(name));
-
- kgsl_get_memory_usage(name, sizeof(name) - 1, entry->flags);
-
KGSL_LOG_DUMP(device,
"[%016llX - %016llX] %s %s (pid = %d) (%s)\n",
entry->gpuaddr,
entry->gpuaddr + entry->size,
entry->priv & KGSL_MEMDESC_GUARD_PAGE ? "(+guard)" : "",
entry->pending_free ? "(pending free)" : "",
- entry->pid, name);
+ entry->pid, entry->name);
}
static void _check_if_freed(struct kgsl_iommu_context *ctx,
@@ -1395,7 +1478,7 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu)
}
}
- kgsl_iommu_add_global(mmu, &iommu->setstate);
+ kgsl_iommu_add_global(mmu, &iommu->setstate, "setstate");
kgsl_setup_qdss_desc(device);
done:
@@ -2220,10 +2303,6 @@ static uint64_t kgsl_iommu_find_svm_region(struct kgsl_pagetable *pagetable,
return addr;
}
-#define ADDR_IN_GLOBAL(_a) \
- (((_a) >= KGSL_IOMMU_GLOBAL_MEM_BASE) && \
- ((_a) < (KGSL_IOMMU_GLOBAL_MEM_BASE + KGSL_IOMMU_GLOBAL_MEM_SIZE)))
-
static int kgsl_iommu_set_svm_region(struct kgsl_pagetable *pagetable,
uint64_t gpuaddr, uint64_t size)
{
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 10f6b8049d36..4371c9a1b87e 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -543,12 +543,12 @@ void kgsl_mmu_remove_global(struct kgsl_device *device,
EXPORT_SYMBOL(kgsl_mmu_remove_global);
void kgsl_mmu_add_global(struct kgsl_device *device,
- struct kgsl_memdesc *memdesc)
+ struct kgsl_memdesc *memdesc, const char *name)
{
struct kgsl_mmu *mmu = &device->mmu;
if (MMU_OP_VALID(mmu, mmu_add_global))
- mmu->mmu_ops->mmu_add_global(mmu, memdesc);
+ mmu->mmu_ops->mmu_add_global(mmu, memdesc, name);
}
EXPORT_SYMBOL(kgsl_mmu_add_global);
@@ -620,7 +620,7 @@ static struct kgsl_mmu_pt_ops nommu_pt_ops = {
};
static void nommu_add_global(struct kgsl_mmu *mmu,
- struct kgsl_memdesc *memdesc)
+ struct kgsl_memdesc *memdesc, const char *name)
{
memdesc->gpuaddr = (uint64_t) sg_phys(memdesc->sgt->sgl);
}
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 53645cc1741c..acbc0e784cf2 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -75,7 +75,7 @@ struct kgsl_mmu_ops {
(struct kgsl_mmu *mmu);
int (*mmu_init_pt)(struct kgsl_mmu *mmu, struct kgsl_pagetable *);
void (*mmu_add_global)(struct kgsl_mmu *mmu,
- struct kgsl_memdesc *memdesc);
+ struct kgsl_memdesc *memdesc, const char *name);
void (*mmu_remove_global)(struct kgsl_mmu *mmu,
struct kgsl_memdesc *memdesc);
struct kgsl_pagetable * (*mmu_getpagetable)(struct kgsl_mmu *mmu,
@@ -174,6 +174,7 @@ struct kgsl_pagetable *kgsl_mmu_getpagetable_ptbase(struct kgsl_mmu *,
void kgsl_add_global_secure_entry(struct kgsl_device *device,
struct kgsl_memdesc *memdesc);
+void kgsl_print_global_pt_entries(struct seq_file *s);
void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable);
int kgsl_mmu_get_gpuaddr(struct kgsl_pagetable *pagetable,
@@ -198,7 +199,7 @@ int kgsl_mmu_find_region(struct kgsl_pagetable *pagetable,
uint64_t *gpuaddr, uint64_t size, unsigned int align);
void kgsl_mmu_add_global(struct kgsl_device *device,
- struct kgsl_memdesc *memdesc);
+ struct kgsl_memdesc *memdesc, const char *name);
void kgsl_mmu_remove_global(struct kgsl_device *device,
struct kgsl_memdesc *memdesc);
diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c
index 7967b19779db..f5402fdc7e57 100644
--- a/drivers/gpu/msm/kgsl_pool.c
+++ b/drivers/gpu/msm/kgsl_pool.c
@@ -197,7 +197,7 @@ _kgsl_pool_shrink(struct kgsl_page_pool *pool, int num_pages)
* starting from higher order pool.
*/
static unsigned long
-kgsl_pool_reduce(unsigned int target_pages)
+kgsl_pool_reduce(unsigned int target_pages, bool exit)
{
int total_pages = 0;
int i;
@@ -210,6 +210,14 @@ kgsl_pool_reduce(unsigned int target_pages)
for (i = (KGSL_NUM_POOLS - 1); i >= 0; i--) {
pool = &kgsl_pools[i];
+ /*
+ * Only reduce the pool sizes for pools which are allowed to
+ * allocate memory unless we are at close, in which case the
+ * reserved memory for all pools needs to be freed
+ */
+ if (!pool->allocation_allowed && !exit)
+ continue;
+
total_pages -= pcount;
nr_removed = total_pages - target_pages;
@@ -418,7 +426,7 @@ kgsl_pool_shrink_scan_objects(struct shrinker *shrinker,
int target_pages = (nr > total_pages) ? 0 : (total_pages - nr);
/* Reduce pool size to target_pages */
- return kgsl_pool_reduce(target_pages);
+ return kgsl_pool_reduce(target_pages, false);
}
static unsigned long
@@ -449,7 +457,7 @@ void kgsl_init_page_pools(void)
void kgsl_exit_page_pools(void)
{
/* Release all pages in pools, if any.*/
- kgsl_pool_reduce(0);
+ kgsl_pool_reduce(0, true);
/* Unregister shrinker */
unregister_shrinker(&kgsl_pool_shrinker);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 565ae4c39fdd..9e6817c76df8 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -279,7 +279,7 @@ kgsl_memdesc_footprint(const struct kgsl_memdesc *memdesc)
*/
static inline int kgsl_allocate_global(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, uint64_t size, uint64_t flags,
- unsigned int priv)
+ unsigned int priv, const char *name)
{
int ret;
@@ -297,7 +297,7 @@ static inline int kgsl_allocate_global(struct kgsl_device *device,
}
if (ret == 0)
- kgsl_mmu_add_global(device, memdesc);
+ kgsl_mmu_add_global(device, memdesc, name);
return ret;
}
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index 1b51eb591036..4ef9f80177d6 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -1192,6 +1192,41 @@ TRACE_EVENT(sparse_unbind,
);
+TRACE_EVENT(kgsl_clock_throttling,
+ TP_PROTO(
+ int idle_10pct,
+ int crc_50pct,
+ int crc_more50pct,
+ int crc_less50pct,
+ int adj
+ ),
+ TP_ARGS(
+ idle_10pct,
+ crc_50pct,
+ crc_more50pct,
+ crc_less50pct,
+ adj
+ ),
+ TP_STRUCT__entry(
+ __field(int, idle_10pct)
+ __field(int, crc_50pct)
+ __field(int, crc_more50pct)
+ __field(int, crc_less50pct)
+ __field(int, adj)
+ ),
+ TP_fast_assign(
+ __entry->idle_10pct = idle_10pct;
+ __entry->crc_50pct = crc_50pct;
+ __entry->crc_more50pct = crc_more50pct;
+ __entry->crc_less50pct = crc_less50pct;
+ __entry->adj = adj;
+ ),
+ TP_printk("idle_10=%d crc_50=%d crc_more50=%d crc_less50=%d adj=%d",
+ __entry->idle_10pct, __entry->crc_50pct, __entry->crc_more50pct,
+ __entry->crc_less50pct, __entry->adj
+ )
+);
+
#endif /* _KGSL_TRACE_H */
/* This part must be outside protection */
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index 92870cdb52d9..8efaa88329aa 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -218,7 +218,8 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
goto done_proc;
}
- remaining_bytes = do_div(buffer_size, sizeof(__s32));
+ remaining_bytes = buffer_size % sizeof(__s32);
+ buffer_size = buffer_size / sizeof(__s32);
if (buffer_size) {
for (i = 0; i < buffer_size; ++i) {
hid_set_field(report->field[field_index], i,
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index 26fbfba23c94..a234d61802ce 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -22,6 +22,7 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/bitmap.h>
#include <linux/of.h>
@@ -135,7 +136,6 @@ struct stm_drvdata {
struct device *dev;
struct coresight_device *csdev;
struct miscdevice miscdev;
- struct clk *clk;
spinlock_t spinlock;
struct channel_space chs;
bool enable;
@@ -270,8 +270,8 @@ static int stm_enable(struct coresight_device *csdev)
int ret;
unsigned long flags;
- ret = clk_prepare_enable(drvdata->clk);
- if (ret)
+ ret = pm_runtime_get_sync(drvdata->dev);
+ if (ret < 0)
return ret;
spin_lock_irqsave(&drvdata->spinlock, flags);
@@ -349,7 +349,7 @@ static void stm_disable(struct coresight_device *csdev)
/* Wait for 100ms so that pending data has been written to HW */
msleep(100);
- clk_disable_unprepare(drvdata->clk);
+ pm_runtime_put(drvdata->dev);
dev_info(drvdata->dev, "STM tracing disabled\n");
}
@@ -360,7 +360,7 @@ static int stm_trace_id(struct coresight_device *csdev)
unsigned long flags;
int trace_id = -1;
- if (clk_prepare_enable(drvdata->clk))
+ if (pm_runtime_get_sync(drvdata->dev) < 0)
goto out;
spin_lock_irqsave(&drvdata->spinlock, flags);
@@ -370,7 +370,7 @@ static int stm_trace_id(struct coresight_device *csdev)
CS_LOCK(drvdata->base);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
- clk_disable_unprepare(drvdata->clk);
+ pm_runtime_put(drvdata->dev);
out:
return trace_id;
}
@@ -806,19 +806,14 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
spin_lock_init(&drvdata->spinlock);
- drvdata->clk = adev->pclk;
- ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(drvdata->clk);
+ ret = clk_set_rate(adev->pclk, CORESIGHT_CLK_RATE_TRACE);
if (ret)
return ret;
if (!coresight_authstatus_enabled(drvdata->base))
goto err1;
- clk_disable_unprepare(drvdata->clk);
+ pm_runtime_put(&adev->dev);
bitmap_fill(drvdata->entities, OST_ENTITY_MAX);
@@ -856,7 +851,7 @@ err:
coresight_unregister(drvdata->csdev);
return ret;
err1:
- clk_disable_unprepare(drvdata->clk);
+ pm_runtime_put(&adev->dev);
return -EPERM;
}
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index d48d8485f979..306465ededf9 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -95,6 +95,7 @@
#define TMC_ETR_BAM_PIPE_INDEX 0
#define TMC_ETR_BAM_NR_PIPES 2
+#define TMC_ETFETB_DUMP_MAGIC_V2 (0x42445953)
#define TMC_REG_DUMP_MAGIC_V2 (0x42445953)
#define TMC_REG_DUMP_VER (1)
@@ -178,6 +179,7 @@ struct tmc_drvdata {
spinlock_t spinlock;
int read_count;
bool reading;
+ bool aborting;
char *buf;
dma_addr_t paddr;
void __iomem *vaddr;
@@ -883,11 +885,15 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
for (i = 0; i < memwords; i++) {
read_data = readl_relaxed(drvdata->base + TMC_RRD);
if (read_data == 0xFFFFFFFF)
- return;
+ goto out;
memcpy(bufp, &read_data, 4);
bufp += 4;
}
}
+
+out:
+ if (drvdata->aborting)
+ drvdata->buf_data.magic = TMC_ETFETB_DUMP_MAGIC_V2;
}
static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
@@ -1070,6 +1076,8 @@ static void tmc_abort(struct coresight_device *csdev)
unsigned long flags;
enum tmc_mode mode;
+ drvdata->aborting = true;
+
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading)
goto out0;
@@ -1728,7 +1736,7 @@ static void __tmc_reg_dump(struct tmc_drvdata *drvdata)
if (!drvdata->reg_buf)
return;
- else if (!drvdata->dump_reg)
+ else if (!drvdata->aborting && !drvdata->dump_reg)
return;
drvdata->reg_data.version = TMC_REG_DUMP_VER;
diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c
index e4e188fc67fc..c43d8596a203 100644
--- a/drivers/hwtracing/coresight/coresight-tpda.c
+++ b/drivers/hwtracing/coresight/coresight-tpda.c
@@ -94,6 +94,10 @@ static void __tpda_enable_pre_port(struct tpda_drvdata *drvdata)
val = val | BIT(2);
else
val = val & ~BIT(2);
+
+ /* Force ASYNC-VERSION-FREQTS sequence */
+ val = val | BIT(21);
+
tpda_writel(drvdata, val, TPDA_CR);
/*
@@ -154,8 +158,6 @@ static void __tpda_enable_post_port(struct tpda_drvdata *drvdata)
if (drvdata->freq_req_val)
tpda_writel(drvdata, drvdata->freq_req_val, TPDA_FREQREQ_VAL);
- else
- tpda_writel(drvdata, 0x0, TPDA_FREQREQ_VAL);
val = tpda_readl(drvdata, TPDA_CR);
if (drvdata->freq_req)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 8a0e2c809fb2..90135f496aaf 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -318,6 +318,18 @@ config QCOM_RRADC
To compile this driver as a module, choose M here: the module will
be called qcom-rradc.
+config QCOM_TADC
+ tristate "Qualcomm Technologies Inc. TADC driver"
+ depends on MFD_I2C_PMIC
+ help
+ Say yes here to support the Qualcomm Technologies Inc. telemetry ADC.
+ The TADC provides battery temperature, skin temperature,
+ die temperature, battery voltage, battery current, input voltage,
+ input current, and OTG current.
+
+ The driver can also be built as a module. If so, the module will be
+ called qcom-tadc.
+
config ROCKCHIP_SARADC
tristate "Rockchip SARADC driver"
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 59cac58d769e..9124e49a5f65 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_QCOM_RRADC) += qcom-rradc.o
+obj-$(CONFIG_QCOM_TADC) += qcom-tadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
index ae2df4f7ff0d..a0fcad198f62 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>
diff --git a/drivers/iio/adc/qcom-tadc.c b/drivers/iio/adc/qcom-tadc.c
new file mode 100644
index 000000000000..3cc2694f9a03
--- /dev/null
+++ b/drivers/iio/adc/qcom-tadc.c
@@ -0,0 +1,742 @@
+/* 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/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define TADC_REVISION1_REG 0x00
+#define TADC_REVISION2_REG 0x01
+#define TADC_REVISION3_REG 0x02
+#define TADC_REVISION4_REG 0x03
+#define TADC_PERPH_TYPE_REG 0x04
+#define TADC_PERPH_SUBTYPE_REG 0x05
+
+/* TADC register definitions */
+#define TADC_SW_CH_CONV_REG(chip) (chip->tadc_base + 0x06)
+#define TADC_MBG_ERR_REG(chip) (chip->tadc_base + 0x07)
+#define TADC_EN_CTL_REG(chip) (chip->tadc_base + 0x46)
+#define TADC_CONV_REQ_REG(chip) (chip->tadc_base + 0x51)
+#define TADC_HWTRIG_CONV_CH_EN_REG(chip) (chip->tadc_base + 0x52)
+#define TADC_HW_SETTLE_DELAY_REG(chip) (chip->tadc_base + 0x53)
+#define TADC_LONG_HW_SETTLE_DLY_EN_REG(chip) (chip->tadc_base + 0x54)
+#define TADC_LONG_HW_SETTLE_DLY_REG(chip) (chip->tadc_base + 0x55)
+#define TADC_ADC_BUF_CH_REG(chip) (chip->tadc_base + 0x56)
+#define TADC_ADC_AAF_CH_REG(chip) (chip->tadc_base + 0x57)
+#define TADC_ADC_DATA_RDBK_REG(chip) (chip->tadc_base + 0x58)
+#define TADC_CH1_ADC_LO_REG(chip) (chip->tadc_base + 0x60)
+#define TADC_CH1_ADC_HI_REG(chip) (chip->tadc_base + 0x61)
+#define TADC_CH2_ADC_LO_REG(chip) (chip->tadc_base + 0x62)
+#define TADC_CH2_ADC_HI_REG(chip) (chip->tadc_base + 0x63)
+#define TADC_CH3_ADC_LO_REG(chip) (chip->tadc_base + 0x64)
+#define TADC_CH3_ADC_HI_REG(chip) (chip->tadc_base + 0x65)
+#define TADC_CH4_ADC_LO_REG(chip) (chip->tadc_base + 0x66)
+#define TADC_CH4_ADC_HI_REG(chip) (chip->tadc_base + 0x67)
+#define TADC_CH5_ADC_LO_REG(chip) (chip->tadc_base + 0x68)
+#define TADC_CH5_ADC_HI_REG(chip) (chip->tadc_base + 0x69)
+#define TADC_CH6_ADC_LO_REG(chip) (chip->tadc_base + 0x70)
+#define TADC_CH6_ADC_HI_REG(chip) (chip->tadc_base + 0x71)
+#define TADC_CH7_ADC_LO_REG(chip) (chip->tadc_base + 0x72)
+#define TADC_CH7_ADC_HI_REG(chip) (chip->tadc_base + 0x73)
+#define TADC_CH8_ADC_LO_REG(chip) (chip->tadc_base + 0x74)
+#define TADC_CH8_ADC_HI_REG(chip) (chip->tadc_base + 0x75)
+
+/* TADC_CMP register definitions */
+#define TADC_CMP_THR1_CMP_REG(chip) (chip->tadc_cmp_base + 0x51)
+#define TADC_CMP_THR1_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x52)
+#define TADC_CMP_THR1_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x53)
+#define TADC_CMP_THR1_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x54)
+#define TADC_CMP_THR1_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x55)
+#define TADC_CMP_THR1_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x56)
+#define TADC_CMP_THR1_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x57)
+#define TADC_CMP_THR2_CMP_REG(chip) (chip->tadc_cmp_base + 0x67)
+#define TADC_CMP_THR2_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x68)
+#define TADC_CMP_THR2_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x69)
+#define TADC_CMP_THR2_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x6A)
+#define TADC_CMP_THR2_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x6B)
+#define TADC_CMP_THR2_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x6C)
+#define TADC_CMP_THR2_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x6D)
+#define TADC_CMP_THR3_CMP_REG(chip) (chip->tadc_cmp_base + 0x7D)
+#define TADC_CMP_THR3_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x7E)
+#define TADC_CMP_THR3_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x7F)
+#define TADC_CMP_THR3_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x80)
+#define TADC_CMP_THR3_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x81)
+#define TADC_CMP_THR3_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x82)
+#define TADC_CMP_THR3_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x83)
+#define TADC_CMP_THR4_CMP_REG(chip) (chip->tadc_cmp_base + 0x93)
+#define TADC_CMP_THR4_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x94)
+#define TADC_CMP_THR4_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x95)
+#define TADC_CMP_THR1_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB0)
+#define TADC_CMP_THR2_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB1)
+#define TADC_CMP_THR3_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB2)
+#define TADC_CMP_THR4_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB3)
+
+/* 10 bits of resolution */
+#define TADC_RESOLUTION 1024
+/* number of hardware channels */
+#define TADC_NUM_CH 8
+
+enum tadc_chan_id {
+ TADC_THERM1 = 0,
+ TADC_THERM2,
+ TADC_DIE_TEMP,
+ TADC_BATT_I,
+ TADC_BATT_V,
+ TADC_INPUT_I,
+ TADC_INPUT_V,
+ TADC_OTG_I,
+ /* virtual channels */
+ TADC_BATT_P,
+ TADC_INPUT_P,
+ TADC_THERM1_THR1,
+ TADC_THERM2_THR1,
+ TADC_DIE_TEMP_THR1,
+};
+
+#define TADC_CHAN(_name, _type, _channel, _info_mask) \
+{ \
+ .type = _type, \
+ .channel = _channel, \
+ .info_mask_separate = _info_mask, \
+ .extend_name = _name, \
+}
+
+#define TADC_THERM_CHAN(_name, _channel) \
+TADC_CHAN(_name, IIO_TEMP, _channel, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED))
+
+#define TADC_TEMP_CHAN(_name, _channel) \
+TADC_CHAN(_name, IIO_TEMP, _channel, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET))
+
+#define TADC_CURRENT_CHAN(_name, _channel) \
+TADC_CHAN(_name, IIO_CURRENT, _channel, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED) | \
+ BIT(IIO_CHAN_INFO_SCALE))
+
+
+#define TADC_VOLTAGE_CHAN(_name, _channel) \
+TADC_CHAN(_name, IIO_VOLTAGE, _channel, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED) | \
+ BIT(IIO_CHAN_INFO_SCALE))
+
+#define TADC_POWER_CHAN(_name, _channel) \
+TADC_CHAN(_name, IIO_POWER, _channel, \
+ BIT(IIO_CHAN_INFO_PROCESSED))
+
+static const struct iio_chan_spec tadc_iio_chans[] = {
+ [TADC_THERM1] = TADC_THERM_CHAN(
+ "batt", TADC_THERM1),
+ [TADC_THERM2] = TADC_THERM_CHAN(
+ "skin", TADC_THERM2),
+ [TADC_DIE_TEMP] = TADC_TEMP_CHAN(
+ "die", TADC_DIE_TEMP),
+ [TADC_BATT_I] = TADC_CURRENT_CHAN(
+ "batt", TADC_BATT_I),
+ [TADC_BATT_V] = TADC_VOLTAGE_CHAN(
+ "batt", TADC_BATT_V),
+ [TADC_INPUT_I] = TADC_CURRENT_CHAN(
+ "input", TADC_INPUT_I),
+ [TADC_INPUT_V] = TADC_VOLTAGE_CHAN(
+ "input", TADC_INPUT_V),
+ [TADC_OTG_I] = TADC_CURRENT_CHAN(
+ "otg", TADC_OTG_I),
+ [TADC_BATT_P] = TADC_POWER_CHAN(
+ "batt", TADC_BATT_P),
+ [TADC_INPUT_P] = TADC_POWER_CHAN(
+ "input", TADC_INPUT_P),
+ [TADC_THERM1_THR1] = TADC_THERM_CHAN(
+ "batt_hot", TADC_THERM1_THR1),
+ [TADC_THERM2_THR1] = TADC_THERM_CHAN(
+ "skin_hot", TADC_THERM2_THR1),
+ [TADC_DIE_TEMP_THR1] = TADC_THERM_CHAN(
+ "die_hot", TADC_DIE_TEMP_THR1),
+};
+
+struct tadc_chan_data {
+ s32 scale;
+ s32 offset;
+ u32 rbias;
+ const struct tadc_pt *table;
+ size_t tablesize;
+};
+
+struct tadc_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ u32 tadc_base;
+ u32 tadc_cmp_base;
+ struct tadc_chan_data chans[TADC_NUM_CH];
+ struct completion eoc_complete;
+};
+
+struct tadc_pt {
+ s32 x;
+ s32 y;
+};
+
+/*
+ * Thermistor tables are generated by the B-parameter equation which is a
+ * simplifed version of the Steinhart-Hart equation.
+ *
+ * (1 / T) = (1 / T0) + (1 / B) * ln(R / R0)
+ *
+ * Where R0 is the resistance at temperature T0, and T0 is typically room
+ * temperature (25C).
+ */
+static const struct tadc_pt tadc_therm_3450b_68k[] = {
+ { 4151, 120000 },
+ { 4648, 115000 },
+ { 5220, 110000 },
+ { 5880, 105000 },
+ { 6644, 100000 },
+ { 7533, 95000 },
+ { 8571, 90000 },
+ { 9786, 85000 },
+ { 11216, 80000 },
+ { 12906, 75000 },
+ { 14910, 70000 },
+ { 17300, 65000 },
+ { 20163, 60000 },
+ { 23609, 55000 },
+ { 27780, 50000 },
+ { 32855, 45000 },
+ { 39065, 40000 },
+ { 46712, 35000 },
+ { 56185, 30000 },
+ { 68000, 25000 },
+ { 82837, 20000 },
+ { 101604, 15000 },
+ { 125525, 10000 },
+ { 156261, 5000 },
+ { 196090, 0 },
+ { 248163, -5000 },
+ { 316887, -10000 },
+ { 408493, -15000 },
+ { 531889, -20000 },
+ { 699966, -25000 },
+ { 931618, -30000 },
+ { 1254910, -35000 },
+ { 1712127, -40000 },
+};
+
+static int tadc_read(struct tadc_chip *chip, u16 reg, u8 *val,
+ size_t val_count)
+{
+ int rc = 0;
+
+ rc = regmap_bulk_read(chip->regmap, reg, val, val_count);
+ if (rc < 0)
+ pr_err("Couldn't read %04x rc=%d\n", reg, rc);
+
+ return rc;
+}
+
+static int tadc_write(struct tadc_chip *chip, u16 reg, u8 data)
+{
+ int rc = 0;
+
+ rc = regmap_write(chip->regmap, reg, data);
+ if (rc < 0)
+ pr_err("Couldn't write %02x to %04x rc=%d\n",
+ data, reg, rc);
+
+ return rc;
+}
+
+static int tadc_lerp(const struct tadc_pt *pts, size_t tablesize, s32 input,
+ s32 *output)
+{
+ int i;
+ s64 temp;
+
+ if (pts == NULL) {
+ pr_err("Table is NULL\n");
+ return -EINVAL;
+ }
+
+ if (tablesize < 1) {
+ pr_err("Table has no entries\n");
+ return -ENOENT;
+ }
+
+ if (tablesize == 1) {
+ *output = pts[0].y;
+ return 0;
+ }
+
+ if (pts[0].x > pts[1].x) {
+ pr_err("Table is not in acending order\n");
+ return -EINVAL;
+ }
+
+ if (input <= pts[0].x) {
+ *output = pts[0].y;
+ return 0;
+ }
+
+ if (input >= pts[tablesize - 1].x) {
+ *output = pts[tablesize - 1].y;
+ return 0;
+ }
+
+ for (i = 1; i < tablesize; i++)
+ if (input <= pts[i].x)
+ break;
+
+ temp = (s64)(pts[i].y - pts[i - 1].y) * (s64)(input - pts[i - 1].x);
+ temp = div_s64(temp, pts[i].x - pts[i - 1].x);
+ *output = temp + pts[i - 1].y;
+ return 0;
+}
+
+/*
+ * Process the result of a thermistor reading.
+ *
+ * The voltage input to the ADC is a result of a voltage divider circuit.
+ * Vout = (Rtherm / (Rbias + Rtherm)) * Vbias
+ *
+ * The ADC value is based on the output voltage of the voltage divider, and the
+ * bias voltage.
+ * ADC = (Vin * 1024) / Vbias
+ *
+ * Combine these equations and solve for Rtherm
+ * Rtherm = (ADC * Rbias) / (1024 - ADC)
+ */
+static int tadc_process_therm(const struct tadc_chan_data *chan_data,
+ s16 adc, s32 *result)
+{
+ s64 rtherm;
+
+ rtherm = (s64)adc * (s64)chan_data->rbias;
+ rtherm = div_s64(rtherm, TADC_RESOLUTION - adc);
+ return tadc_lerp(chan_data->table, chan_data->tablesize, rtherm,
+ result);
+}
+
+static int tadc_read_channel(struct tadc_chip *chip, u16 address, int *adc)
+{
+ u8 val[2];
+ int rc;
+
+ rc = tadc_read(chip, address, val, ARRAY_SIZE(val));
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read channel rc=%d\n", rc);
+ return rc;
+ }
+
+ *adc = (s16)(val[0] | val[1] << BITS_PER_BYTE);
+ return 0;
+}
+
+#define CONVERSION_TIMEOUT_MS 100
+static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
+{
+ unsigned long timeout, timeleft;
+ u8 val[TADC_NUM_CH * 2];
+ int rc, i;
+
+ rc = tadc_read(chip, TADC_MBG_ERR_REG(chip), val, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read mbg error status rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (val[0] != 0) {
+ tadc_write(chip, TADC_EN_CTL_REG(chip), 0);
+ tadc_write(chip, TADC_EN_CTL_REG(chip), 0x80);
+ }
+
+ rc = tadc_write(chip, TADC_CONV_REQ_REG(chip), channels);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't write conversion request rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
+ timeleft = wait_for_completion_timeout(&chip->eoc_complete, timeout);
+
+ if (timeleft == 0) {
+ rc = tadc_read(chip, TADC_SW_CH_CONV_REG(chip), val, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read conversion status rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (val[0] != channels) {
+ dev_err(chip->dev, "Conversion timed out\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ rc = tadc_read(chip, TADC_CH1_ADC_LO_REG(chip), val, ARRAY_SIZE(val));
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read adc channels rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ for (i = 0; i < TADC_NUM_CH; i++)
+ adc[i] = val[i * 2] | val[i * 2 + 1] << BITS_PER_BYTE;
+
+ return jiffies_to_msecs(timeout - timeleft);
+}
+
+static int tadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct tadc_chip *chip = iio_priv(indio_dev);
+ const struct tadc_chan_data *chan_data = &chip->chans[chan->channel];
+ int rc = 0, offset = 0, scale, scale2, scale_type;
+ s16 adc[TADC_NUM_CH];
+
+ switch (chan->channel) {
+ case TADC_THERM1_THR1:
+ chan_data = &chip->chans[TADC_THERM1];
+ break;
+ case TADC_THERM2_THR1:
+ chan_data = &chip->chans[TADC_THERM2];
+ break;
+ case TADC_DIE_TEMP_THR1:
+ chan_data = &chip->chans[TADC_DIE_TEMP];
+ break;
+ default:
+ break;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->channel) {
+ case TADC_THERM1_THR1:
+ rc = tadc_read_channel(chip,
+ TADC_CMP_THR1_CH1_CMP_LO_REG(chip), val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read THERM1 threshold rc=%d\n",
+ rc);
+ return rc;
+ }
+ break;
+ case TADC_THERM2_THR1:
+ rc = tadc_read_channel(chip,
+ TADC_CMP_THR1_CH2_CMP_LO_REG(chip), val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read THERM2 threshold rc=%d\n",
+ rc);
+ return rc;
+ }
+ break;
+ case TADC_DIE_TEMP_THR1:
+ rc = tadc_read_channel(chip,
+ TADC_CMP_THR1_CH3_CMP_LO_REG(chip), val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read DIE_TEMP threshold rc=%d\n",
+ rc);
+ return rc;
+ }
+ break;
+ default:
+ rc = tadc_do_conversion(chip, BIT(chan->channel), adc);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read channel %d\n",
+ chan->channel);
+ return rc;
+ }
+ *val = adc[chan->channel];
+ break;
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->channel) {
+ case TADC_THERM1:
+ case TADC_THERM2:
+ case TADC_THERM1_THR1:
+ case TADC_THERM2_THR1:
+ rc = tadc_read_raw(indio_dev, chan, val, NULL,
+ IIO_CHAN_INFO_RAW);
+ if (rc < 0)
+ return rc;
+
+ rc = tadc_process_therm(chan_data, *val, val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't process 0x%04x from channel %d rc=%d\n",
+ *val, chan->channel, rc);
+ return rc;
+ }
+ break;
+ case TADC_BATT_P:
+ rc = tadc_do_conversion(chip,
+ BIT(TADC_BATT_I) | BIT(TADC_BATT_V), adc);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read battery current and voltage channels\n");
+ return rc;
+ }
+
+ *val = adc[TADC_BATT_I] * adc[TADC_BATT_V];
+ break;
+ case TADC_INPUT_P:
+ rc = tadc_do_conversion(chip,
+ BIT(TADC_INPUT_I) | BIT(TADC_INPUT_V), adc);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read input current and voltage channels\n");
+ return rc;
+ }
+
+ *val = adc[TADC_INPUT_I] * adc[TADC_INPUT_V];
+ break;
+ default:
+ rc = tadc_read_raw(indio_dev, chan, val, NULL,
+ IIO_CHAN_INFO_RAW);
+ if (rc < 0)
+ return rc;
+
+ /* offset is optional */
+ rc = tadc_read_raw(indio_dev, chan, &offset, NULL,
+ IIO_CHAN_INFO_OFFSET);
+ if (rc < 0)
+ return rc;
+
+ scale_type = tadc_read_raw(indio_dev, chan,
+ &scale, &scale2, IIO_CHAN_INFO_SCALE);
+ switch (scale_type) {
+ case IIO_VAL_INT:
+ *val = *val * scale + offset;
+ break;
+ case IIO_VAL_FRACTIONAL:
+ *val = div_s64((s64)*val * scale + offset,
+ scale2);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel) {
+ case TADC_DIE_TEMP:
+ case TADC_DIE_TEMP_THR1:
+ *val = chan_data->scale;
+ return IIO_VAL_INT;
+ case TADC_BATT_I:
+ case TADC_BATT_V:
+ case TADC_INPUT_I:
+ case TADC_INPUT_V:
+ case TADC_OTG_I:
+ *val = chan_data->scale;
+ *val2 = TADC_RESOLUTION;
+ return IIO_VAL_FRACTIONAL;
+ }
+ return -EINVAL;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = chan_data->offset;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static irqreturn_t handle_eoc(int irq, void *dev_id)
+{
+ struct tadc_chip *chip = dev_id;
+
+ complete(&chip->eoc_complete);
+ return IRQ_HANDLED;
+}
+
+static int tadc_set_therm_table(struct tadc_chan_data *chan_data, u32 beta,
+ u32 rtherm)
+{
+ if (beta == 3450 && rtherm == 68000) {
+ chan_data->table = tadc_therm_3450b_68k;
+ chan_data->tablesize = ARRAY_SIZE(tadc_therm_3450b_68k);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static int tadc_parse_dt(struct tadc_chip *chip)
+{
+ struct device_node *child, *node;
+ struct tadc_chan_data *chan_data;
+ u32 chan_id, rtherm, beta;
+ int rc = 0;
+
+ node = chip->dev->of_node;
+ for_each_available_child_of_node(node, child) {
+ rc = of_property_read_u32(child, "reg", &chan_id);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't find channel for %s rc=%d",
+ child->name, rc);
+ return rc;
+ }
+
+ if (chan_id > TADC_NUM_CH - 1) {
+ dev_err(chip->dev, "Channel %d is out of range [0, %d]\n",
+ chan_id, TADC_NUM_CH - 1);
+ return -EINVAL;
+ }
+
+ chan_data = &chip->chans[chan_id];
+ switch (chan_id) {
+ case TADC_THERM1:
+ case TADC_THERM2:
+ rc = of_property_read_u32(child,
+ "qcom,rbias", &chan_data->rbias);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read qcom,rbias rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(child,
+ "qcom,beta-coefficient", &beta);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read qcom,beta-coefficient rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(child,
+ "qcom,rtherm-at-25degc", &rtherm);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read qcom,rtherm-at-25degc rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = tadc_set_therm_table(chan_data, beta, rtherm);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't set therm table rc=%d\n",
+ rc);
+ return rc;
+ }
+ break;
+ default:
+ rc = of_property_read_s32(child, "qcom,scale",
+ &chan_data->scale);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read scale rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ of_property_read_s32(child, "qcom,offset",
+ &chan_data->offset);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static const struct iio_info tadc_info = {
+ .read_raw = &tadc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int tadc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct iio_dev *indio_dev;
+ struct tadc_chip *chip;
+ int rc = 0, irq;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+ chip->dev = &pdev->dev;
+ init_completion(&chip->eoc_complete);
+
+ rc = of_property_read_u32(node, "reg", &chip->tadc_base);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read base address rc=%d\n", rc);
+ return rc;
+ }
+ chip->tadc_cmp_base = chip->tadc_base + 0x100;
+
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ pr_err("Couldn't get regmap\n");
+ return -ENODEV;
+ }
+
+ rc = tadc_parse_dt(chip);
+ if (rc < 0) {
+ pr_err("Couldn't parse device tree rc=%d\n", rc);
+ return rc;
+ }
+
+ irq = of_irq_get_byname(node, "eoc");
+ if (irq < 0) {
+ pr_err("Couldn't get eoc irq rc=%d\n", irq);
+ return irq;
+ }
+
+ rc = devm_request_threaded_irq(chip->dev, irq, NULL, handle_eoc,
+ IRQF_ONESHOT, "eoc", chip);
+ if (rc < 0) {
+ pr_err("Couldn't request irq %d rc=%d\n", irq, rc);
+ return rc;
+ }
+
+ indio_dev->dev.parent = chip->dev;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &tadc_info;
+ indio_dev->channels = tadc_iio_chans;
+ indio_dev->num_channels = ARRAY_SIZE(tadc_iio_chans);
+
+ rc = devm_iio_device_register(chip->dev, indio_dev);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't register IIO device rc=%d\n", rc);
+
+ return rc;
+}
+
+static int tadc_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id tadc_match_table[] = {
+ { .compatible = "qcom,tadc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tadc_match_table);
+
+static struct platform_driver tadc_driver = {
+ .driver = {
+ .name = "qcom-tadc",
+ .of_match_table = tadc_match_table,
+ },
+ .probe = tadc_probe,
+ .remove = tadc_remove,
+};
+module_platform_driver(tadc_driver);
+
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. TADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 7c4a7c7c16e7..49df5e0afbfb 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1119,6 +1119,15 @@ config TOUCHSCREEN_FT5X06_PSENSOR
If unsure, say N.
+config TOUCHSCREEN_FT5X06_GESTURE
+ tristate "FocalTech gesture feature support"
+ depends on TOUCHSCREEN_FT5X06
+ help
+ Say Y here if you want to support ft5x06's gesture
+ feature.
+
+ If unsure, say N.
+
config TOUCHSCREEN_MSTAR21XX
tristate "Mstar touchscreens"
depends on I2C
@@ -1186,4 +1195,37 @@ config TOUCHSCREEN_FT5X06
To compile this driver as a module, choose M here: the
module will be called ft5x06_ts.
+config FT_SECURE_TOUCH
+ bool "Secure Touch support for Focaltech Touchscreen"
+ depends on TOUCHSCREEN_FT5X06
+ help
+ Say Y here
+ -Focaltech touch driver is connected
+ -To enable secure touch for Focaltech touch driver
+
+ If unsure, say N.
+
+config TOUCHSCREEN_IT7260_I2C
+ tristate "IT7260 Touchscreen Driver"
+ depends on I2C
+ help
+ Say Y here if you have a IT7260 Touchscreen Driver
+ connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called it7258_ts_i2c.
+
+config TOUCHSCREEN_GT9XX
+ bool "Goodix touchpanel GT9xx series"
+ depends on I2C
+ help
+ Say Y here if you have a Goodix GT9xx touchscreen.
+ Gt9xx controllers are multi touch controllers which can
+ report 5 touches at a time.
+
+ If unsure, say N.
+
+source "drivers/input/touchscreen/gt9xx/Kconfig"
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index e1777f11d77b..06953a69123f 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
+obj-$(CONFIG_TOUCHSCREEN_IT7260_I2C) += it7258_ts_i2c.o
obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
@@ -98,3 +99,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
obj-$(CONFIG_TOUCHSCREEN_MSTAR21XX) += msg21xx_ts.o
+obj-$(CONFIG_TOUCHSCREEN_GT9XX) += gt9xx/
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index beb86c9dea11..499ba692d53c 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -29,8 +29,13 @@
#include <linux/regulator/consumer.h>
#include <linux/firmware.h>
#include <linux/debugfs.h>
-#include <linux/sensors.h>
#include <linux/input/ft5x06_ts.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+
#if defined(CONFIG_FB)
#include <linux/notifier.h>
@@ -42,6 +47,14 @@
#define FT_SUSPEND_LEVEL 1
#endif
+#if defined(CONFIG_FT_SECURE_TOUCH)
+#include <linux/completion.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+static irqreturn_t ft5x06_ts_interrupt(int irq, void *data);
+#endif
+
#define FT_DRIVER_VERSION 0x02
#define FT_META_REGS 3
@@ -74,18 +87,26 @@
#define FT_REG_FW_MIN_VER 0xB2
#define FT_REG_FW_SUB_MIN_VER 0xB3
-/* psensor register address*/
-#define FT_REG_PSENSOR_ENABLE 0xB0
-#define FT_REG_PSENSOR_STATUS 0x01
-
-/* psensor register bits*/
-#define FT_PSENSOR_ENABLE_MASK 0x01
-#define FT_PSENSOR_STATUS_NEAR 0xC0
-#define FT_PSENSOR_STATUS_FAR 0xE0
-#define FT_PSENSOR_FAR_TO_NEAR 0
-#define FT_PSENSOR_NEAR_TO_FAR 1
-#define FT_PSENSOR_ORIGINAL_STATE_FAR 1
-#define FT_PSENSOR_WAKEUP_TIMEOUT 100
+/* gesture register address*/
+#define FT_REG_GESTURE_ENABLE 0xD0
+#define FT_REG_GESTURE_OUTPUT 0xD3
+
+/* gesture register bits*/
+#define FT_GESTURE_DOUBLECLICK_COORD_X 100
+#define FT_GESTURE_DOUBLECLICK_COORD_Y 100
+#define FT_GESTURE_WAKEUP_TIMEOUT 500
+#define FT_GESTURE_DEFAULT_TRACKING_ID 0x0A
+#define FT_GESTURE_DOUBLECLICK_ID 0x24
+#define FT_GESTURE_POINTER_NUM_MAX 128
+#define FT_GESTURE_POINTER_SIZEOF 4
+#define FT_GESTURE_ID_FLAG_SIZE 1
+#define FT_GESTURE_POINTER_NUM_FLAG_SIZE 1
+/* 6 bytes are taken to mark which gesture is supported in firmware */
+#define FT_GESTURE_SET_FLAG_SIZE 6
+#define I2C_TRANSFER_MAX_BYTE 255
+#define FT_GESTURE_DATA_HEADER (FT_GESTURE_ID_FLAG_SIZE + \
+ FT_GESTURE_POINTER_NUM_FLAG_SIZE + \
+ FT_GESTURE_SET_FLAG_SIZE)
/* power register bits*/
#define FT_PMODE_ACTIVE 0x00
@@ -186,6 +207,8 @@
#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend"
#define PINCTRL_STATE_RELEASE "pmx_ts_release"
+static irqreturn_t ft5x06_ts_interrupt(int irq, void *data);
+
enum {
FT_BLOADER_VERSION_LZ4 = 0,
FT_BLOADER_VERSION_Z7 = 1,
@@ -199,8 +222,17 @@ enum {
FT_FT5336_FAMILY_ID_0x14 = 0x14,
};
-#define FT_STORE_TS_INFO(buf, id, name, max_tch, group_id, fw_vkey_support, \
- fw_name, fw_maj, fw_min, fw_sub_min) \
+#define FT_STORE_TS_INFO(buf, id, fw_maj, fw_min, fw_sub_min) \
+ snprintf(buf, FT_INFO_MAX_LEN, \
+ "vendor name = Focaltech\n" \
+ "model = 0x%x\n" \
+ "fw_version = %d.%d.%d\n", \
+ id, fw_maj, fw_min, fw_sub_min)
+#define FT_TS_INFO_SYSFS_DIR_NAME "ts_info"
+static char *ts_info_buff;
+
+#define FT_STORE_TS_DBG_INFO(buf, id, name, max_tch, group_id, \
+ fw_vkey_support, fw_name, fw_maj, fw_min, fw_sub_min) \
snprintf(buf, FT_INFO_MAX_LEN, \
"controller\t= focaltech\n" \
"model\t\t= 0x%x\n" \
@@ -221,9 +253,10 @@ struct ft5x06_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
const struct ft5x06_ts_platform_data *pdata;
- struct ft5x06_psensor_platform_data *psensor_pdata;
+ struct ft5x06_gesture_platform_data *gesture_pdata;
struct regulator *vdd;
struct regulator *vcc_i2c;
+ struct mutex ft_clk_io_ctrl_mutex;
char fw_name[FT_FW_NAME_MAX_LEN];
bool loading_fw;
u8 family_id;
@@ -235,7 +268,9 @@ struct ft5x06_ts_data {
u32 tch_data_len;
u8 fw_ver[3];
u8 fw_vendor_id;
+ struct kobject *ts_info_kobj;
#if defined(CONFIG_FB)
+ struct work_struct fb_notify_work;
struct notifier_block fb_notif;
#elif defined(CONFIG_HAS_EARLYSUSPEND)
struct early_suspend early_suspend;
@@ -244,29 +279,258 @@ struct ft5x06_ts_data {
struct pinctrl_state *pinctrl_state_active;
struct pinctrl_state *pinctrl_state_suspend;
struct pinctrl_state *pinctrl_state_release;
+#if defined(CONFIG_FT_SECURE_TOUCH)
+ atomic_t st_enabled;
+ atomic_t st_pending_irqs;
+ struct completion st_powerdown;
+ struct completion st_irq_processed;
+ bool st_initialized;
+ struct clk *core_clk;
+ struct clk *iface_clk;
+#endif
};
-static struct sensors_classdev __maybe_unused sensors_proximity_cdev = {
- .name = "ft5x06-proximity",
- .vendor = "FocalTech",
- .version = 1,
- .handle = SENSORS_PROXIMITY_HANDLE,
- .type = SENSOR_TYPE_PROXIMITY,
- .max_range = "5.0",
- .resolution = "5.0",
- .sensor_power = "0.1",
- .min_delay = 0,
- .fifo_reserved_event_count = 0,
- .fifo_max_event_count = 0,
- .enabled = 0,
- .delay_msec = 200,
- .sensors_enable = NULL,
- .sensors_poll_delay = NULL,
+static int ft5x06_ts_start(struct device *dev);
+static int ft5x06_ts_stop(struct device *dev);
+
+#if defined(CONFIG_FT_SECURE_TOUCH)
+static void ft5x06_secure_touch_init(struct ft5x06_ts_data *data)
+{
+ data->st_initialized = 0;
+
+ init_completion(&data->st_powerdown);
+ init_completion(&data->st_irq_processed);
+
+ /* Get clocks */
+ data->core_clk = devm_clk_get(&data->client->dev, "core_clk");
+ if (IS_ERR(data->core_clk)) {
+ data->core_clk = NULL;
+ dev_warn(&data->client->dev,
+ "%s: core_clk is not defined\n", __func__);
+ }
+
+ data->iface_clk = devm_clk_get(&data->client->dev, "iface_clk");
+ if (IS_ERR(data->iface_clk)) {
+ data->iface_clk = NULL;
+ dev_warn(&data->client->dev,
+ "%s: iface_clk is not defined", __func__);
+ }
+ data->st_initialized = 1;
+}
+
+static void ft5x06_secure_touch_notify(struct ft5x06_ts_data *data)
+{
+ sysfs_notify(&data->input_dev->dev.kobj, NULL, "secure_touch");
+}
+
+static irqreturn_t ft5x06_filter_interrupt(struct ft5x06_ts_data *data)
+{
+ if (atomic_read(&data->st_enabled)) {
+ if (atomic_cmpxchg(&data->st_pending_irqs, 0, 1) == 0) {
+ reinit_completion(&data->st_irq_processed);
+ ft5x06_secure_touch_notify(data);
+ wait_for_completion_interruptible(
+ &data->st_irq_processed);
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+/*
+ * 'blocking' variable will have value 'true' when we want to prevent the driver
+ * from accessing the xPU/SMMU protected HW resources while the session is
+ * active.
+ */
+static void ft5x06_secure_touch_stop(struct ft5x06_ts_data *data, bool blocking)
+{
+ if (atomic_read(&data->st_enabled)) {
+ atomic_set(&data->st_pending_irqs, -1);
+ ft5x06_secure_touch_notify(data);
+ if (blocking)
+ wait_for_completion_interruptible(
+ &data->st_powerdown);
+ }
+}
+
+static int ft5x06_clk_prepare_enable(struct ft5x06_ts_data *data)
+{
+ int ret;
+
+ ret = clk_prepare_enable(data->iface_clk);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "error on clk_prepare_enable(iface_clk):%d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(data->core_clk);
+ if (ret) {
+ clk_disable_unprepare(data->iface_clk);
+ dev_err(&data->client->dev,
+ "error clk_prepare_enable(core_clk):%d\n", ret);
+ }
+ return ret;
+}
+
+static void ft5x06_clk_disable_unprepare(struct ft5x06_ts_data *data)
+{
+ clk_disable_unprepare(data->core_clk);
+ clk_disable_unprepare(data->iface_clk);
+}
+
+static int ft5x06_bus_get(struct ft5x06_ts_data *data)
+{
+ int retval;
+
+ mutex_lock(&data->ft_clk_io_ctrl_mutex);
+ retval = pm_runtime_get_sync(data->client->adapter->dev.parent);
+ if (retval >= 0 && data->core_clk != NULL && data->iface_clk != NULL) {
+ retval = ft5x06_clk_prepare_enable(data);
+ if (retval)
+ pm_runtime_put_sync(data->client->adapter->dev.parent);
+ }
+ mutex_unlock(&data->ft_clk_io_ctrl_mutex);
+ return retval;
+}
+
+static void ft5x06_bus_put(struct ft5x06_ts_data *data)
+{
+ mutex_lock(&data->ft_clk_io_ctrl_mutex);
+ if (data->core_clk != NULL && data->iface_clk != NULL)
+ ft5x06_clk_disable_unprepare(data);
+ pm_runtime_put_sync(data->client->adapter->dev.parent);
+ mutex_unlock(&data->ft_clk_io_ctrl_mutex);
+}
+
+static ssize_t ft5x06_secure_touch_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&data->st_enabled));
+}
+
+/*
+ * Accept only "0" and "1" valid values.
+ * "0" will reset the st_enabled flag, then wake up the reading process and
+ * the interrupt handler.
+ * The bus driver is notified via pm_runtime that it is not required to stay
+ * awake anymore.
+ * It will also make sure the queue of events is emptied in the controller,
+ * in case a touch happened in between the secure touch being disabled and
+ * the local ISR being ungated.
+ * "1" will set the st_enabled flag and clear the st_pending_irqs flag.
+ * The bus driver is requested via pm_runtime to stay awake.
+ */
+static ssize_t ft5x06_secure_touch_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ unsigned long value;
+ int err = 0;
+
+ if (count > 2)
+ return -EINVAL;
+ err = kstrtoul(buf, 10, &value);
+ if (err != 0)
+ return err;
+
+ if (!data->st_initialized)
+ return -EIO;
+
+ err = count;
+ switch (value) {
+ case 0:
+ if (atomic_read(&data->st_enabled) == 0)
+ break;
+ ft5x06_bus_put(data);
+ atomic_set(&data->st_enabled, 0);
+ ft5x06_secure_touch_notify(data);
+ complete(&data->st_irq_processed);
+ ft5x06_ts_interrupt(data->client->irq, data);
+ complete(&data->st_powerdown);
+ break;
+
+ case 1:
+ if (atomic_read(&data->st_enabled)) {
+ err = -EBUSY;
+ break;
+ }
+ synchronize_irq(data->client->irq);
+ if (ft5x06_bus_get(data) < 0) {
+ dev_err(&data->client->dev, "ft5x06_bus_get failed\n");
+ err = -EIO;
+ break;
+ }
+ reinit_completion(&data->st_powerdown);
+ reinit_completion(&data->st_irq_processed);
+ atomic_set(&data->st_enabled, 1);
+ atomic_set(&data->st_pending_irqs, 0);
+ break;
+
+ default:
+ dev_err(&data->client->dev, "unsupported value: %lu\n", value);
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+/*
+ * This function returns whether there are pending interrupts, or
+ * other error conditions that need to be signaled to the userspace library,
+ * according tot he following logic:
+ * - st_enabled is 0 if secure touch is not enabled, returning -EBADF
+ * - st_pending_irqs is -1 to signal that secure touch is in being stopped,
+ * returning -EINVAL
+ * - st_pending_irqs is 1 to signal that there is a pending irq, returning
+ * the value "1" to the sysfs read operation
+ * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt
+ * has been processed, so the interrupt handler can be allowed to continue.
+ */
+static ssize_t ft5x06_secure_touch_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ int val = 0;
+
+ if (atomic_read(&data->st_enabled) == 0)
+ return -EBADF;
+ if (atomic_cmpxchg(&data->st_pending_irqs, -1, 0) == -1)
+ return -EINVAL;
+ if (atomic_cmpxchg(&data->st_pending_irqs, 1, 0) == 1)
+ val = 1;
+ else
+ complete(&data->st_irq_processed);
+ return scnprintf(buf, PAGE_SIZE, "%u", val);
+}
+#else
+static void ft5x06_secure_touch_init(struct ft5x06_ts_data *data)
+{
+}
+static irqreturn_t ft5x06_filter_interrupt(struct ft5x06_ts_data *data)
+{
+ return IRQ_NONE;
+}
+static void ft5x06_secure_touch_stop(struct ft5x06_ts_data *data, bool blocking)
+{
+}
+#endif
+
+static struct device_attribute attrs[] = {
+#if defined(CONFIG_FT_SECURE_TOUCH)
+ __ATTR(secure_touch_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
+ ft5x06_secure_touch_enable_show,
+ ft5x06_secure_touch_enable_store),
+ __ATTR(secure_touch, S_IRUGO,
+ ft5x06_secure_touch_show, NULL),
+#endif
};
-static inline bool ft5x06_psensor_support_enabled(void)
+static inline bool ft5x06_gesture_support_enabled(void)
{
- return config_enabled(CONFIG_TOUCHSCREEN_FT5X06_PSENSOR);
+ return config_enabled(CONFIG_TOUCHSCREEN_FT5X06_GESTURE);
}
static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf,
@@ -344,79 +608,196 @@ static int ft5x0x_read_reg(struct i2c_client *client, u8 addr, u8 *val)
return ft5x06_i2c_read(client, &addr, 1, val, 1);
}
-#ifdef CONFIG_TOUCHSCREEN_FT5X06_PSENSOR
-static void ft5x06_psensor_enable(struct ft5x06_ts_data *data, int enable)
+#ifdef CONFIG_TOUCHSCREEN_FT5X06_GESTURE
+static ssize_t ft5x06_gesture_enable_to_set_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- u8 state;
- int ret = -1;
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ data->gesture_pdata->gesture_enable_to_set);
+}
+
+static ssize_t ft5x06_gesture_enable_to_set_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
+
+ if (data->suspended)
+ return -EINVAL;
- if (data->client == NULL)
- return;
+ ret = kstrtoul(buf, 16, &value);
+ if (ret < 0) {
+ dev_err(dev, "%s:kstrtoul failed, ret=0x%x\n",
+ __func__, ret);
+ return ret;
+ }
- ft5x0x_read_reg(data->client, FT_REG_PSENSOR_ENABLE, &state);
- if (enable)
- state |= FT_PSENSOR_ENABLE_MASK;
+ if (value == 1)
+ data->gesture_pdata->gesture_enable_to_set = 1;
else
- state &= ~FT_PSENSOR_ENABLE_MASK;
+ data->gesture_pdata->gesture_enable_to_set = 0;
+ return size;
+}
- ret = ft5x0x_write_reg(data->client, FT_REG_PSENSOR_ENABLE, state);
- if (ret < 0)
+static DEVICE_ATTR(enable, 0664,
+ ft5x06_gesture_enable_to_set_show,
+ ft5x06_gesture_enable_to_set_store);
+
+static int ft5x06_entry_pocket(struct device *dev)
+{
+ return ft5x06_ts_stop(dev);
+}
+
+static int ft5x06_leave_pocket(struct device *dev)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ int err;
+
+ ft5x06_ts_start(dev);
+ ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 1);
+ err = enable_irq_wake(data->client->irq);
+ if (err)
dev_err(&data->client->dev,
- "write psensor switch command failed\n");
+ "%s: set_irq_wake failed\n", __func__);
+ data->suspended = true;
+
+ return err;
}
-static int ft5x06_psensor_enable_set(struct sensors_classdev *sensors_cdev,
- unsigned int enable)
+static ssize_t gesture_in_pocket_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- struct ft5x06_psensor_platform_data *psensor_pdata =
- container_of(sensors_cdev,
- struct ft5x06_psensor_platform_data, ps_cdev);
- struct ft5x06_ts_data *data = psensor_pdata->data;
- struct input_dev *input_dev = data->psensor_pdata->input_psensor_dev;
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
- mutex_lock(&input_dev->mutex);
- ft5x06_psensor_enable(data, enable);
- psensor_pdata->tp_psensor_data = FT_PSENSOR_ORIGINAL_STATE_FAR;
- if (enable)
- psensor_pdata->tp_psensor_opened = 1;
- else
- psensor_pdata->tp_psensor_opened = 0;
- mutex_unlock(&input_dev->mutex);
- return enable;
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ data->gesture_pdata->in_pocket);
}
-static int ft5x06_read_tp_psensor_data(struct ft5x06_ts_data *data)
+static ssize_t gesture_in_pocket_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
{
- u8 psensor_status;
- char tmp;
- int ret = 0;
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
- ft5x0x_read_reg(data->client,
- FT_REG_PSENSOR_STATUS, &psensor_status);
+ ret = kstrtoul(buf, 16, &value);
+ if (ret < 0) {
+ dev_err(dev, "%s:kstrtoul failed, ret=0x%x\n",
+ __func__, ret);
+ return ret;
+ }
- tmp = data->psensor_pdata->tp_psensor_data;
- if (psensor_status == FT_PSENSOR_STATUS_NEAR)
- data->psensor_pdata->tp_psensor_data =
- FT_PSENSOR_FAR_TO_NEAR;
- else if (psensor_status == FT_PSENSOR_STATUS_FAR)
- data->psensor_pdata->tp_psensor_data =
- FT_PSENSOR_NEAR_TO_FAR;
+ if (value == 1 && data->gesture_pdata->in_pocket == 0) {
+ data->gesture_pdata->in_pocket = 1;
+ ft5x06_entry_pocket(dev);
+ } else if (value == 0 && data->gesture_pdata->in_pocket == 1) {
+ ft5x06_leave_pocket(dev);
+ data->gesture_pdata->in_pocket = 0;
+ }
+ return size;
+}
- if (tmp != data->psensor_pdata->tp_psensor_data) {
- dev_info(&data->client->dev,
- "%s sensor data changed\n", __func__);
- ret = 1;
+static DEVICE_ATTR(pocket, 0664,
+ gesture_in_pocket_mode_show,
+ gesture_in_pocket_mode_store);
+
+static int ft5x06_report_gesture_doubleclick(struct input_dev *ip_dev)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID);
+ input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1);
+ input_report_abs(ip_dev, ABS_MT_POSITION_X,
+ FT_GESTURE_DOUBLECLICK_COORD_X);
+ input_report_abs(ip_dev, ABS_MT_POSITION_Y,
+ FT_GESTURE_DOUBLECLICK_COORD_Y);
+ input_mt_report_pointer_emulation(ip_dev, false);
+ input_sync(ip_dev);
+ input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID);
+ input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0);
+ input_mt_report_pointer_emulation(ip_dev, false);
+ input_sync(ip_dev);
}
- return ret;
+ return 0;
}
-#else
-static int ft5x06_psensor_enable_set(struct sensors_classdev *sensors_cdev,
- unsigned int enable)
+
+static int ft5x06_report_gesture(struct i2c_client *i2c_client,
+ struct input_dev *ip_dev)
{
- return enable;
+ int i, temp, gesture_data_size;
+ int gesture_coord_x, gesture_coord_y;
+ int ret = -1;
+ short pointnum = 0;
+ unsigned char buf[FT_GESTURE_POINTER_NUM_MAX *
+ FT_GESTURE_POINTER_SIZEOF + FT_GESTURE_DATA_HEADER];
+
+ buf[0] = FT_REG_GESTURE_OUTPUT;
+ ret = ft5x06_i2c_read(i2c_client, buf, 1,
+ buf, FT_GESTURE_DATA_HEADER);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "%s read touchdata failed.\n",
+ __func__);
+ return ret;
+ }
+
+ /* FW support doubleclick */
+ if (buf[0] == FT_GESTURE_DOUBLECLICK_ID) {
+ ft5x06_report_gesture_doubleclick(ip_dev);
+ return 0;
+ }
+
+ pointnum = (short)(buf[1]) & 0xff;
+ gesture_data_size = pointnum * FT_GESTURE_POINTER_SIZEOF +
+ FT_GESTURE_DATA_HEADER;
+ buf[0] = FT_REG_GESTURE_OUTPUT;
+ temp = gesture_data_size / I2C_TRANSFER_MAX_BYTE;
+ for (i = 0; i < temp; i++)
+ ret = ft5x06_i2c_read(i2c_client, buf, ((i == 0) ? 1 : 0),
+ buf + I2C_TRANSFER_MAX_BYTE * i, I2C_TRANSFER_MAX_BYTE);
+ ret = ft5x06_i2c_read(i2c_client, buf, ((temp == 0) ? 1 : 0),
+ buf + I2C_TRANSFER_MAX_BYTE * temp,
+ gesture_data_size - I2C_TRANSFER_MAX_BYTE * temp);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "%s read touchdata failed.\n",
+ __func__);
+ return ret;
+ }
+
+ for (i = 0; i < pointnum; i++) {
+ gesture_coord_x = (((s16) buf[FT_GESTURE_DATA_HEADER +
+ (FT_GESTURE_POINTER_SIZEOF * i)]) & 0x0F) << 8 |
+ (((s16) buf[FT_GESTURE_DATA_HEADER + 1 +
+ (FT_GESTURE_POINTER_SIZEOF * i)]) & 0xFF);
+ gesture_coord_y = (((s16) buf[FT_GESTURE_DATA_HEADER + 2 +
+ (FT_GESTURE_POINTER_SIZEOF * i)]) & 0x0F) << 8 |
+ (((s16) buf[FT_GESTURE_DATA_HEADER + 3 +
+ (FT_GESTURE_POINTER_SIZEOF * i)]) & 0xFF);
+ input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID);
+ input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1);
+ input_report_abs(ip_dev, ABS_MT_POSITION_X, gesture_coord_x);
+ input_report_abs(ip_dev, ABS_MT_POSITION_Y, gesture_coord_y);
+ input_mt_report_pointer_emulation(ip_dev, false);
+ input_sync(ip_dev);
+ }
+ input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID);
+ input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0);
+ input_mt_report_pointer_emulation(ip_dev, false);
+ input_sync(ip_dev);
+
+ return 0;
}
+#else
+static DEVICE_ATTR(pocket, 0664, NULL, NULL);
+static DEVICE_ATTR(enable, 0664, NULL, NULL);
-static int ft5x06_read_tp_psensor_data(struct ft5x06_ts_data *data)
+static int ft5x06_report_gesture(struct i2c_client *i2c_client,
+ struct input_dev *ip_dev)
{
return 0;
}
@@ -465,7 +846,7 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
struct input_dev *ip_dev;
int rc, i;
u32 id, x, y, status, num_touches;
- u8 reg, *buf;
+ u8 reg, *buf, gesture_is_active;
bool update_input = false;
if (!data) {
@@ -473,28 +854,24 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+ if (ft5x06_filter_interrupt(data) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
ip_dev = data->input_dev;
buf = data->tch_data;
- if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support &&
- data->psensor_pdata->tp_psensor_opened) {
- rc = ft5x06_read_tp_psensor_data(data);
- if (rc) {
- if (data->suspended)
- pm_wakeup_event(&data->client->dev,
- FT_PSENSOR_WAKEUP_TIMEOUT);
- input_report_abs(data->psensor_pdata->input_psensor_dev,
- ABS_DISTANCE,
- data->psensor_pdata->tp_psensor_data);
- input_sync(data->psensor_pdata->input_psensor_dev);
- if (data->suspended)
- return IRQ_HANDLED;
- }
- if (data->suspended)
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) {
+ ft5x0x_read_reg(data->client, FT_REG_GESTURE_ENABLE,
+ &gesture_is_active);
+ if (gesture_is_active) {
+ pm_wakeup_event(&(data->client->dev),
+ FT_GESTURE_WAKEUP_TIMEOUT);
+ ft5x06_report_gesture(data->client, ip_dev);
return IRQ_HANDLED;
+ }
}
- /**
+ /*
* Read touch data start from register FT_REG_DEV_MODE.
* The touch x/y value start from FT_TOUCH_X_H/L_POS and
* FT_TOUCH_Y_H/L_POS in buf.
@@ -507,6 +884,10 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
}
for (i = 0; i < data->pdata->num_max_touches; i++) {
+ /*
+ * Getting the finger ID of the touch event incase of
+ * multiple touch events
+ */
id = (buf[FT_TOUCH_ID_POS + FT_ONE_TCH_LEN * i]) >> 4;
if (id >= FT_MAX_ID)
break;
@@ -784,32 +1165,77 @@ err_pinctrl_get:
}
#ifdef CONFIG_PM
-static int ft5x06_ts_suspend(struct device *dev)
+static int ft5x06_ts_start(struct device *dev)
{
struct ft5x06_ts_data *data = dev_get_drvdata(dev);
- char txbuf[2], i;
int err;
- if (data->loading_fw) {
- dev_info(dev, "Firmware loading in process...\n");
- return 0;
+ if (data->pdata->power_on) {
+ err = data->pdata->power_on(true);
+ if (err) {
+ dev_err(dev, "power on failed");
+ return err;
+ }
+ } else {
+ err = ft5x06_power_on(data, true);
+ if (err) {
+ dev_err(dev, "power on failed");
+ return err;
+ }
}
- if (data->suspended) {
- dev_info(dev, "Already in suspend state\n");
- return 0;
+ if (data->ts_pinctrl) {
+ err = pinctrl_select_state(data->ts_pinctrl,
+ data->pinctrl_state_active);
+ if (err < 0)
+ dev_err(dev, "Cannot get active pinctrl state\n");
}
- if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support &&
- device_may_wakeup(dev) &&
- data->psensor_pdata->tp_psensor_opened) {
- err = enable_irq_wake(data->client->irq);
+ err = ft5x06_gpio_configure(data, true);
+ if (err < 0) {
+ dev_err(&data->client->dev,
+ "failed to put gpios in resue state\n");
+ goto err_gpio_configuration;
+ }
+
+ if (gpio_is_valid(data->pdata->reset_gpio)) {
+ gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
+ msleep(data->pdata->hard_rst_dly);
+ gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+ }
+
+ msleep(data->pdata->soft_rst_dly);
+
+ enable_irq(data->client->irq);
+ data->suspended = false;
+
+ return 0;
+
+err_gpio_configuration:
+ if (data->ts_pinctrl) {
+ err = pinctrl_select_state(data->ts_pinctrl,
+ data->pinctrl_state_suspend);
+ if (err < 0)
+ dev_err(dev, "Cannot get suspend pinctrl state\n");
+ }
+ if (data->pdata->power_on) {
+ err = data->pdata->power_on(false);
if (err)
- dev_err(&data->client->dev,
- "%s: set_irq_wake failed\n", __func__);
- data->suspended = true;
- return err;
+ dev_err(dev, "power off failed");
+ } else {
+ err = ft5x06_power_on(data, false);
+ if (err)
+ dev_err(dev, "power off failed");
}
+ return err;
+}
+
+static int ft5x06_ts_stop(struct device *dev)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ char txbuf[2];
+ int i, err;
+
disable_irq(data->client->irq);
/* release all touches */
@@ -884,87 +1310,83 @@ pwr_off_fail:
return err;
}
-static int ft5x06_ts_resume(struct device *dev)
+static int ft5x06_ts_suspend(struct device *dev)
{
struct ft5x06_ts_data *data = dev_get_drvdata(dev);
int err;
- if (!data->suspended) {
- dev_dbg(dev, "Already in awake state\n");
+ if (data->loading_fw) {
+ dev_info(dev, "Firmware loading in process...\n");
+ return 0;
+ }
+
+ if (data->suspended) {
+ dev_info(dev, "Already in suspend state\n");
return 0;
}
- if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support &&
+ ft5x06_secure_touch_stop(data, true);
+
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support &&
device_may_wakeup(dev) &&
- data->psensor_pdata->tp_psensor_opened) {
- err = disable_irq_wake(data->client->irq);
+ data->gesture_pdata->gesture_enable_to_set) {
+
+ ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 1);
+ err = enable_irq_wake(data->client->irq);
if (err)
dev_err(&data->client->dev,
- "%s: disable_irq_wake failed\n",
- __func__);
- data->suspended = false;
+ "%s: set_irq_wake failed\n", __func__);
+ data->suspended = true;
return err;
}
- if (data->pdata->power_on) {
- err = data->pdata->power_on(true);
- if (err) {
- dev_err(dev, "power on failed");
- return err;
- }
- } else {
- err = ft5x06_power_on(data, true);
- if (err) {
- dev_err(dev, "power on failed");
- return err;
- }
- }
+ return ft5x06_ts_stop(dev);
+}
- if (data->ts_pinctrl) {
- err = pinctrl_select_state(data->ts_pinctrl,
- data->pinctrl_state_active);
- if (err < 0)
- dev_err(dev, "Cannot get active pinctrl state\n");
- }
+static int ft5x06_ts_resume(struct device *dev)
+{
+ struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+ int err;
- err = ft5x06_gpio_configure(data, true);
- if (err < 0) {
- dev_err(&data->client->dev,
- "failed to put gpios in resue state\n");
- goto err_gpio_configuration;
+ if (!data->suspended) {
+ dev_dbg(dev, "Already in awake state\n");
+ return 0;
}
- if (gpio_is_valid(data->pdata->reset_gpio)) {
- gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
- msleep(data->pdata->hard_rst_dly);
- gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
- }
+ ft5x06_secure_touch_stop(data, true);
- msleep(data->pdata->soft_rst_dly);
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support &&
+ device_may_wakeup(dev) &&
+ !(data->gesture_pdata->in_pocket) &&
+ data->gesture_pdata->gesture_enable_to_set) {
- enable_irq(data->client->irq);
+ ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 0);
+ err = disable_irq_wake(data->client->irq);
+ if (err)
+ dev_err(dev, "%s: disable_irq_wake failed\n",
+ __func__);
+ data->suspended = false;
+ return err;
+ }
- data->suspended = false;
+ err = ft5x06_ts_start(dev);
+ if (err < 0)
+ return err;
- return 0;
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support &&
+ device_may_wakeup(dev) &&
+ data->gesture_pdata->in_pocket &&
+ data->gesture_pdata->gesture_enable_to_set) {
-err_gpio_configuration:
- if (data->ts_pinctrl) {
- err = pinctrl_select_state(data->ts_pinctrl,
- data->pinctrl_state_suspend);
- if (err < 0)
- dev_err(dev, "Cannot get suspend pinctrl state\n");
- }
- if (data->pdata->power_on) {
- err = data->pdata->power_on(false);
- if (err)
- dev_err(dev, "power off failed");
- } else {
- err = ft5x06_power_on(data, false);
+ ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 0);
+ err = disable_irq_wake(data->client->irq);
if (err)
- dev_err(dev, "power off failed");
+ dev_err(dev, "%s: disable_irq_wake failed\n",
+ __func__);
+ data->suspended = false;
+ data->gesture_pdata->in_pocket = 0;
}
- return err;
+ return 0;
}
static const struct dev_pm_ops ft5x06_ts_pm_ops = {
@@ -988,6 +1410,13 @@ static int ft5x06_ts_resume(struct device *dev)
#endif
#if defined(CONFIG_FB)
+static void fb_notify_resume_work(struct work_struct *work)
+{
+ struct ft5x06_ts_data *ft5x06_data =
+ container_of(work, struct ft5x06_ts_data, fb_notify_work);
+ ft5x06_ts_resume(&ft5x06_data->client->dev);
+}
+
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
@@ -996,13 +1425,27 @@ static int fb_notifier_callback(struct notifier_block *self,
struct ft5x06_ts_data *ft5x06_data =
container_of(self, struct ft5x06_ts_data, fb_notif);
- if (evdata && evdata->data && event == FB_EVENT_BLANK &&
- ft5x06_data && ft5x06_data->client) {
+ if (evdata && evdata->data && ft5x06_data && ft5x06_data->client) {
blank = evdata->data;
- if (*blank == FB_BLANK_UNBLANK)
- ft5x06_ts_resume(&ft5x06_data->client->dev);
- else if (*blank == FB_BLANK_POWERDOWN)
- ft5x06_ts_suspend(&ft5x06_data->client->dev);
+ if (ft5x06_data->pdata->resume_in_workqueue) {
+ if (event == FB_EARLY_EVENT_BLANK &&
+ *blank == FB_BLANK_UNBLANK)
+ schedule_work(&ft5x06_data->fb_notify_work);
+ else if (event == FB_EVENT_BLANK &&
+ *blank == FB_BLANK_POWERDOWN) {
+ flush_work(&ft5x06_data->fb_notify_work);
+ ft5x06_ts_suspend(&ft5x06_data->client->dev);
+ }
+ } else {
+ if (event == FB_EVENT_BLANK) {
+ if (*blank == FB_BLANK_UNBLANK)
+ ft5x06_ts_resume(
+ &ft5x06_data->client->dev);
+ else if (*blank == FB_BLANK_POWERDOWN)
+ ft5x06_ts_suspend(
+ &ft5x06_data->client->dev);
+ }
+ }
}
return 0;
@@ -1014,6 +1457,13 @@ static void ft5x06_ts_early_suspend(struct early_suspend *handler)
struct ft5x06_ts_data,
early_suspend);
+ /*
+ * During early suspend/late resume, the driver doesn't access xPU/SMMU
+ * protected HW resources. So, there is no compelling need to block,
+ * but notifying the userspace that a power event has occurred is
+ * enough. Hence 'blocking' variable can be set to false.
+ */
+ ft5x06_secure_touch_stop(data, false);
ft5x06_ts_suspend(&data->client->dev);
}
@@ -1023,6 +1473,7 @@ static void ft5x06_ts_late_resume(struct early_suspend *handler)
struct ft5x06_ts_data,
early_suspend);
+ ft5x06_secure_touch_stop(data, false);
ft5x06_ts_resume(&data->client->dev);
}
#endif
@@ -1339,11 +1790,13 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force)
ft5x06_update_fw_ver(data);
- FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name,
+ FT_STORE_TS_DBG_INFO(data->ts_info, data->family_id, data->pdata->name,
data->pdata->num_max_touches, data->pdata->group_id,
data->pdata->fw_vkey_support ? "yes" : "no",
data->pdata->fw_name, data->fw_ver[0],
data->fw_ver[1], data->fw_ver[2]);
+ FT_STORE_TS_INFO(ts_info_buff, data->family_id, data->fw_ver[0],
+ data->fw_ver[1], data->fw_ver[2]);
rel_fw:
release_firmware(fw);
return rc;
@@ -1446,6 +1899,14 @@ static ssize_t ft5x06_fw_name_store(struct device *dev,
static DEVICE_ATTR(fw_name, 0664, ft5x06_fw_name_show, ft5x06_fw_name_store);
+static ssize_t ts_info_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ strlcpy(buf, ts_info_buff, FT_INFO_MAX_LEN);
+ return strnlen(buf, FT_INFO_MAX_LEN);
+}
+static struct kobj_attribute ts_info_attr = __ATTR_RO(ts_info);
+
static bool ft5x06_debug_addr_is_valid(int addr)
{
if (addr < 0 || addr > 0xFF) {
@@ -1749,8 +2210,12 @@ static int ft5x06_parse_dt(struct device *dev,
pdata->ignore_id_check = of_property_read_bool(np,
"focaltech,ignore-id-check");
- pdata->psensor_support = of_property_read_bool(np,
- "focaltech,psensor-support");
+ pdata->gesture_support = of_property_read_bool(np,
+ "focaltech,gesture-support");
+
+ pdata->resume_in_workqueue = of_property_read_bool(np,
+ "focaltech,resume-in-workqueue");
+
rc = of_property_read_u32(np, "focaltech,family-id", &temp_val);
if (!rc)
pdata->family_id = temp_val;
@@ -1786,14 +2251,13 @@ static int ft5x06_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ft5x06_ts_platform_data *pdata;
- struct ft5x06_psensor_platform_data *psensor_pdata;
+ struct ft5x06_gesture_platform_data *gesture_pdata;
struct ft5x06_ts_data *data;
struct input_dev *input_dev;
- struct input_dev *psensor_input_dev;
struct dentry *temp;
u8 reg_value;
u8 reg_addr;
- int err, len;
+ int err, len, retval, attr_count;
if (client->dev.of_node) {
pdata = devm_kzalloc(&client->dev,
@@ -1871,7 +2335,8 @@ static int ft5x06_ts_probe(struct i2c_client *client,
err = input_register_device(input_dev);
if (err) {
dev_err(&client->dev, "Input device registration failed\n");
- goto free_inputdev;
+ input_free_device(input_dev);
+ return err;
}
if (pdata->power_init) {
@@ -1946,6 +2411,10 @@ static int ft5x06_ts_probe(struct i2c_client *client,
err = request_threaded_irq(client->irq, NULL,
ft5x06_ts_interrupt,
+ /*
+ * the interrupt trigger mode will be set in Device Tree with property
+ * "interrupts", so here we just need to set the flag IRQF_ONESHOT
+ */
IRQF_ONESHOT,
client->dev.driver->name, data);
if (err) {
@@ -1953,54 +2422,55 @@ static int ft5x06_ts_probe(struct i2c_client *client,
goto free_gpio;
}
- if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) {
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) {
device_init_wakeup(&client->dev, 1);
- psensor_pdata = devm_kzalloc(&client->dev,
- sizeof(struct ft5x06_psensor_platform_data),
+ gesture_pdata = devm_kzalloc(&client->dev,
+ sizeof(struct ft5x06_gesture_platform_data),
GFP_KERNEL);
- if (!psensor_pdata) {
+ if (!gesture_pdata) {
dev_err(&client->dev, "Failed to allocate memory\n");
- goto irq_free;
+ goto free_gesture_dev;
}
- data->psensor_pdata = psensor_pdata;
-
- psensor_input_dev = input_allocate_device();
- if (!psensor_input_dev) {
- dev_err(&data->client->dev,
- "Failed to allocate device\n");
- goto free_psensor_pdata;
+ data->gesture_pdata = gesture_pdata;
+ gesture_pdata->data = data;
+
+ gesture_pdata->gesture_class =
+ class_create(THIS_MODULE, "gesture");
+ if (IS_ERR(gesture_pdata->gesture_class)) {
+ err = PTR_ERR(gesture_pdata->gesture_class);
+ dev_err(&client->dev, "Failed to create class.\n");
+ goto free_gesture_pdata;
}
- __set_bit(EV_ABS, psensor_input_dev->evbit);
- input_set_abs_params(psensor_input_dev,
- ABS_DISTANCE, 0, 1, 0, 0);
- psensor_input_dev->name = "proximity";
- psensor_input_dev->id.bustype = BUS_I2C;
- psensor_input_dev->dev.parent = &data->client->dev;
- data->psensor_pdata->input_psensor_dev = psensor_input_dev;
+ gesture_pdata->dev = device_create(gesture_pdata->gesture_class,
+ NULL, 0, NULL, "gesture_ft5x06");
+ if (IS_ERR(gesture_pdata->dev)) {
+ err = PTR_ERR(gesture_pdata->dev);
+ dev_err(&client->dev, "Failed to create device.\n");
+ goto free_gesture_class;
+ }
- err = input_register_device(psensor_input_dev);
+ dev_set_drvdata(gesture_pdata->dev, data);
+ err = device_create_file(gesture_pdata->dev,
+ &dev_attr_enable);
if (err) {
- dev_err(&data->client->dev,
- "Unable to register device, err=%d\n", err);
- goto free_psensor_input_dev;
+ dev_err(gesture_pdata->dev,
+ "sys file creation failed\n");
+ goto free_gesture_dev;
+ }
+ err = device_create_file(gesture_pdata->dev,
+ &dev_attr_pocket);
+ if (err) {
+ dev_err(gesture_pdata->dev,
+ "sys file creation failed\n");
+ goto free_enable_sys;
}
-
- psensor_pdata->ps_cdev = sensors_proximity_cdev;
- psensor_pdata->ps_cdev.sensors_enable =
- ft5x06_psensor_enable_set;
- psensor_pdata->data = data;
-
- err = sensors_classdev_register(&client->dev,
- &psensor_pdata->ps_cdev);
- if (err)
- goto unregister_psensor_input_device;
}
err = device_create_file(&client->dev, &dev_attr_fw_name);
if (err) {
dev_err(&client->dev, "sys file creation failed\n");
- goto free_psensor_class_sysfs;
+ goto free_pocket_sys;
}
err = device_create_file(&client->dev, &dev_attr_update_fw);
@@ -2074,16 +2544,53 @@ static int ft5x06_ts_probe(struct i2c_client *client,
dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4);
+ /*creation touch panel info kobj*/
+ data->ts_info_kobj = kobject_create_and_add(FT_TS_INFO_SYSFS_DIR_NAME,
+ kernel_kobj);
+ if (!data->ts_info_kobj) {
+ dev_err(&client->dev, "kobject creation failed.\n");
+ } else {
+ err = sysfs_create_file(data->ts_info_kobj, &ts_info_attr.attr);
+ if (err) {
+ kobject_put(data->ts_info_kobj);
+ dev_err(&client->dev, "sysfs creation failed.\n");
+ } else {
+ ts_info_buff = devm_kzalloc(&client->dev,
+ FT_INFO_MAX_LEN, GFP_KERNEL);
+ if (!ts_info_buff)
+ goto free_debug_dir;
+ }
+ }
+
+ /*Initialize secure touch */
+ ft5x06_secure_touch_init(data);
+ ft5x06_secure_touch_stop(data, true);
+ mutex_init(&(data->ft_clk_io_ctrl_mutex));
+
+ /* Creation of secure touch sysfs files */
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ retval = sysfs_create_file(&data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ if (retval < 0) {
+ dev_err(&client->dev,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ goto free_secure_touch_sysfs;
+ }
+ }
+
ft5x06_update_fw_ver(data);
ft5x06_update_fw_vendor_id(data);
- FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name,
+ FT_STORE_TS_DBG_INFO(data->ts_info, data->family_id, data->pdata->name,
data->pdata->num_max_touches, data->pdata->group_id,
data->pdata->fw_vkey_support ? "yes" : "no",
data->pdata->fw_name, data->fw_ver[0],
data->fw_ver[1], data->fw_ver[2]);
-
+ FT_STORE_TS_INFO(ts_info_buff, data->family_id, data->fw_ver[0],
+ data->fw_ver[1], data->fw_ver[2]);
#if defined(CONFIG_FB)
+ INIT_WORK(&data->fb_notify_work, fb_notify_resume_work);
data->fb_notif.notifier_call = fb_notifier_callback;
err = fb_register_client(&data->fb_notif);
@@ -2098,9 +2605,13 @@ static int ft5x06_ts_probe(struct i2c_client *client,
data->early_suspend.resume = ft5x06_ts_late_resume;
register_early_suspend(&data->early_suspend);
#endif
-
return 0;
+free_secure_touch_sysfs:
+ for (attr_count--; attr_count >= 0; attr_count--) {
+ sysfs_remove_file(&data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
free_debug_dir:
debugfs_remove_recursive(data->dir);
free_force_update_fw_sys:
@@ -2109,24 +2620,24 @@ free_update_fw_sys:
device_remove_file(&client->dev, &dev_attr_update_fw);
free_fw_name_sys:
device_remove_file(&client->dev, &dev_attr_fw_name);
-free_psensor_class_sysfs:
- if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support)
- sensors_classdev_unregister(&psensor_pdata->ps_cdev);
-unregister_psensor_input_device:
- if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support)
- input_unregister_device(data->psensor_pdata->input_psensor_dev);
-free_psensor_input_dev:
- if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support)
- input_free_device(data->psensor_pdata->input_psensor_dev);
-free_psensor_pdata:
- if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) {
- devm_kfree(&client->dev, psensor_pdata);
- data->psensor_pdata = NULL;
- }
-irq_free:
- if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support)
- device_init_wakeup(&client->dev, 0);
- free_irq(client->irq, data);
+free_pocket_sys:
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support)
+ device_remove_file(&client->dev, &dev_attr_pocket);
+free_enable_sys:
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support)
+ device_remove_file(&client->dev, &dev_attr_enable);
+free_gesture_dev:
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support)
+ device_destroy(gesture_pdata->gesture_class, 0);
+free_gesture_class:
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support)
+ class_destroy(gesture_pdata->gesture_class);
+free_gesture_pdata:
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) {
+ devm_kfree(&client->dev, gesture_pdata);
+ data->gesture_pdata = NULL;
+ }
+
free_gpio:
if (gpio_is_valid(pdata->reset_gpio))
gpio_free(pdata->reset_gpio);
@@ -2155,25 +2666,22 @@ pwr_deinit:
ft5x06_power_init(data, false);
unreg_inputdev:
input_unregister_device(input_dev);
-free_inputdev:
- input_free_device(input_dev);
- input_dev = NULL;
return err;
}
static int ft5x06_ts_remove(struct i2c_client *client)
{
struct ft5x06_ts_data *data = i2c_get_clientdata(client);
- int retval;
-
- if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) {
+ int retval, attr_count;
+ if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) {
device_init_wakeup(&client->dev, 0);
- sensors_classdev_unregister(&data->psensor_pdata->ps_cdev);
- input_unregister_device(data->psensor_pdata->input_psensor_dev);
- input_free_device(data->psensor_pdata->input_psensor_dev);
- devm_kfree(&client->dev, data->psensor_pdata);
- data->psensor_pdata = NULL;
+ device_remove_file(&client->dev, &dev_attr_pocket);
+ device_remove_file(&client->dev, &dev_attr_enable);
+ device_destroy(data->gesture_pdata->gesture_class, 0);
+ class_destroy(data->gesture_pdata->gesture_class);
+ devm_kfree(&client->dev, data->gesture_pdata);
+ data->gesture_pdata = NULL;
}
debugfs_remove_recursive(data->dir);
@@ -2207,6 +2715,11 @@ static int ft5x06_ts_remove(struct i2c_client *client)
}
}
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ sysfs_remove_file(&data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
if (data->pdata->power_on)
data->pdata->power_on(false);
else
@@ -2218,7 +2731,7 @@ static int ft5x06_ts_remove(struct i2c_client *client)
ft5x06_power_init(data, false);
input_unregister_device(data->input_dev);
-
+ kobject_put(data->ts_info_kobj);
return 0;
}
diff --git a/drivers/input/touchscreen/gt9xx/Kconfig b/drivers/input/touchscreen/gt9xx/Kconfig
new file mode 100644
index 000000000000..2e1b5ba567a0
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx/Kconfig
@@ -0,0 +1,51 @@
+#
+# Goodix GT9xx Touchscreen driver
+#
+
+config GT9XX_TOUCHPANEL_DRIVER
+ tristate "Goodix GT9xx touchpanel driver"
+ depends on TOUCHSCREEN_GT9XX
+ default n
+ help
+ This is the main file for touchpanel driver for Goodix GT9xx
+ touchscreens.
+
+ Say Y here if you have a Goodix GT9xx touchscreen connected
+ to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gt9xx.
+
+config GT9XX_TOUCHPANEL_UPDATE
+ tristate "Goodix GT9xx touchpanel auto update support"
+ depends on GT9XX_TOUCHPANEL_DRIVER
+ default n
+ help
+ This enables support for firmware update for Goodix GT9xx
+ touchscreens.
+
+ Say Y here if you have a Goodix GT9xx touchscreen connected
+ to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gt9xx_update.
+
+config GT9XX_TOUCHPANEL_DEBUG
+ tristate "Goodix GT9xx Tools for debuging"
+ depends on GT9XX_TOUCHPANEL_DRIVER
+ default n
+ help
+ This is application debug interface support for Goodix GT9xx
+ touchscreens.
+
+ Say Y here if you want to have a Android app debug interface
+ to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gt9xx_tool.
diff --git a/drivers/input/touchscreen/gt9xx/Makefile b/drivers/input/touchscreen/gt9xx/Makefile
new file mode 100644
index 000000000000..482d869a2d37
--- /dev/null
+++ b/drivers/input/touchscreen/gt9xx/Makefile
@@ -0,0 +1,8 @@
+#gt915 touchpanel driver
+
+
+obj-$(CONFIG_GT9XX_TOUCHPANEL_DRIVER) += gt9xx.o
+#gt915 update file
+obj-$(CONFIG_GT9XX_TOUCHPANEL_UPDATE) += gt9xx_update.o
+#debug tool
+obj-$(CONFIG_GT9XX_TOUCHPANEL_DEBUG) += goodix_tool.o
diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c
index bef080671ab0..c67c4c8f1207 100644
--- a/drivers/input/touchscreen/gt9xx/goodix_tool.c
+++ b/drivers/input/touchscreen/gt9xx/goodix_tool.c
@@ -22,6 +22,7 @@
*/
#include "gt9xx.h"
+#include <linux/mutex.h>
#define DATA_LENGTH_UINT 512
#define CMD_HEAD_LENGTH (sizeof(st_cmd_head) - sizeof(u8 *))
@@ -53,6 +54,8 @@ static struct i2c_client *gt_client;
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,
@@ -88,18 +91,21 @@ static void tool_set_proc_name(char *procname)
static s32 tool_i2c_read_no_extra(u8 *buf, u16 len)
{
s32 ret = -1;
- s32 i = 0;
- struct i2c_msg msgs[2];
-
- msgs[0].flags = !I2C_M_RD;
- msgs[0].addr = gt_client->addr;
- msgs[0].len = cmd_head.addr_len;
- msgs[0].buf = &buf[0];
-
- msgs[1].flags = I2C_M_RD;
- msgs[1].addr = gt_client->addr;
- msgs[1].len = len;
- msgs[1].buf = &buf[GTP_ADDR_LENGTH];
+ u8 i = 0;
+ struct i2c_msg msgs[2] = {
+ {
+ .flags = !I2C_M_RD,
+ .addr = gt_client->addr,
+ .len = cmd_head.addr_len,
+ .buf = &buf[0],
+ },
+ {
+ .flags = I2C_M_RD,
+ .addr = gt_client->addr,
+ .len = len,
+ .buf = &buf[GTP_ADDR_LENGTH],
+ },
+ };
for (i = 0; i < cmd_head.retry; i++) {
ret = i2c_transfer(gt_client->adapter, msgs, 2);
@@ -107,25 +113,35 @@ static s32 tool_i2c_read_no_extra(u8 *buf, u16 len)
break;
}
+ if (i == cmd_head.retry) {
+ dev_err(&client->dev, "I2C read retry limit over.\n");
+ ret = -EIO;
+ }
+
return ret;
}
static s32 tool_i2c_write_no_extra(u8 *buf, u16 len)
{
s32 ret = -1;
- s32 i = 0;
- struct i2c_msg msg;
-
- msg.flags = !I2C_M_RD;
- msg.addr = gt_client->addr;
- msg.len = len;
- msg.buf = buf;
+ u8 i = 0;
+ struct i2c_msg msg = {
+ .flags = !I2C_M_RD,
+ .addr = gt_client->addr,
+ .len = len,
+ .buf = buf,
+ };
for (i = 0; i < cmd_head.retry; i++) {
ret = i2c_transfer(gt_client->adapter, &msg, 1);
if (ret > 0)
break;
- }
+ }
+
+ if (i == cmd_head.retry) {
+ dev_err(&client->dev, "I2C write retry limit over.\n");
+ ret = -EIO;
+ }
return ret;
}
@@ -181,7 +197,7 @@ static void unregister_i2c_func(void)
s32 init_wr_node(struct i2c_client *client)
{
- s32 i;
+ u8 i;
gt_client = client;
memset(&cmd_head, 0, sizeof(cmd_head));
@@ -189,14 +205,15 @@ s32 init_wr_node(struct i2c_client *client)
i = 5;
while ((!cmd_head.data) && i) {
- cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL);
- if (cmd_head.data != NULL)
+ 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 + GTP_ADDR_LENGTH;
- GTP_INFO("Applied memory size:%d.", DATA_LENGTH);
+ 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;
@@ -207,8 +224,9 @@ s32 init_wr_node(struct i2c_client *client)
register_i2c_func();
+ mutex_init(&lock);
tool_set_proc_name(procname);
- goodix_proc_entry = create_proc_entry(procname, 0666, NULL);
+ goodix_proc_entry = create_proc_entry(procname, 0660, NULL);
if (goodix_proc_entry == NULL) {
GTP_ERROR("Couldn't create proc entry!");
return FAIL;
@@ -222,7 +240,6 @@ s32 init_wr_node(struct i2c_client *client)
void uninit_wr_node(void)
{
- kfree(cmd_head.data);
cmd_head.data = NULL;
unregister_i2c_func();
remove_proc_entry(procname, NULL);
@@ -330,9 +347,13 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
GTP_DEBUG_FUNC();
GTP_DEBUG_ARRAY((u8 *)buff, len);
+ mutex_lock(&lock);
ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
- if (ret)
+ if (ret) {
GTP_ERROR("copy_from_user failed.");
+ ret = -EACCES;
+ goto exit;
+ }
GTP_DEBUG("wr :0x%02x.", cmd_head.wr);
GTP_DEBUG("flag:0x%02x.", cmd_head.flag);
@@ -350,6 +371,19 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
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);
+ 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);
@@ -370,7 +404,8 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
if (cmd_head.flag == 1) {
if (comfirm() == FAIL) {
GTP_ERROR("[WRITE]Comfirm fail!");
- return FAIL;
+ ret = -EINVAL;
+ goto exit;
}
} else if (cmd_head.flag == 2) {
/* Need interrupt! */
@@ -379,7 +414,8 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
&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!");
- return FAIL;
+ ret = -EIO;
+ goto exit;
}
GTP_DEBUG_ARRAY(
@@ -388,7 +424,8 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
if (cmd_head.delay)
msleep(cmd_head.delay);
- return cmd_head.data_len + CMD_HEAD_LENGTH;
+ 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],
@@ -396,30 +433,40 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
if (ret)
GTP_ERROR("copy_from_user failed.");
+ 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));
+ ret = -EINVAL;
+ goto exit;
+ }
memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
register_i2c_func();
- return cmd_head.data_len + CMD_HEAD_LENGTH;
- } else if (cmd_head.wr == 3) {
+ 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); */
- return cmd_head.data_len + CMD_HEAD_LENGTH;
+ ret = cmd_head.data_len + CMD_HEAD_LENGTH;
+ goto exit;
} else if (cmd_head.wr == 7) { /* disable irq! */
gtp_irq_disable(i2c_get_clientdata(gt_client));
#if GTP_ESD_PROTECT
gtp_esd_switch(gt_client, SWITCH_OFF);
#endif
- return CMD_HEAD_LENGTH;
+ ret = CMD_HEAD_LENGTH;
+ goto exit;
} else if (cmd_head.wr == 9) { /* enable irq! */
gtp_irq_enable(i2c_get_clientdata(gt_client));
#if GTP_ESD_PROTECT
gtp_esd_switch(gt_client, SWITCH_ON);
#endif
- return CMD_HEAD_LENGTH;
+ ret = CMD_HEAD_LENGTH;
+ goto exit;
} else if (cmd_head.wr == 17) {
struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
@@ -434,27 +481,40 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
ts->gtp_rawdiff_mode = false;
GTP_DEBUG("gtp leave rawdiff.");
}
- return CMD_HEAD_LENGTH;
+ 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)
- return 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! */
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);
+ ret = -EINVAL;
+ goto exit;
+ }
memset(cmd_head.data, 0, cmd_head.data_len + 1);
memcpy(cmd_head.data, &buff[CMD_HEAD_LENGTH],
cmd_head.data_len);
- if (gup_update_proc((void *)cmd_head.data) == FAIL)
- return FAIL;
+ if (gup_update_proc((void *)cmd_head.data) == FAIL) {
+ ret = -EBUSY;
+ goto exit;
+ }
}
#endif
+ ret = CMD_HEAD_LENGTH;
- return CMD_HEAD_LENGTH;
+exit:
+ mutex_unlock(&lock);
+ return ret;
}
/*******************************************************
@@ -469,10 +529,14 @@ static s32 goodix_tool_write(struct file *filp, const char __user *buff,
static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
int *eof, void *data)
{
+ s32 ret;
GTP_DEBUG_FUNC();
+ mutex_lock(&lock);
if (cmd_head.wr % 2) {
- return FAIL;
+ pr_err("<< [READ]command head wrong\n");
+ ret = -EINVAL;
+ goto exit;
} else if (!cmd_head.wr) {
u16 len = 0;
s16 data_len = 0;
@@ -481,7 +545,8 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
if (cmd_head.flag == 1) {
if (comfirm() == FAIL) {
GTP_ERROR("[READ]Comfirm fail!");
- return FAIL;
+ ret = -EINVAL;
+ goto exit;
}
} else if (cmd_head.flag == 2) {
/* Need interrupt! */
@@ -504,11 +569,12 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
else
len = data_len;
- data_len -= DATA_LENGTH;
+ data_len -= len;
if (tool_i2c_read(cmd_head.data, len) <= 0) {
GTP_ERROR("[READ]Read data failed!");
- return FAIL;
+ ret = -EINVAL;
+ goto exit;
}
memcpy(&page[loc], &cmd_head.data[GTP_ADDR_LENGTH],
len);
@@ -525,15 +591,14 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
GTP_DEBUG("Return ic type:%s len:%d.", page,
(s32)cmd_head.data_len);
- return 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;
-
- return cmd_head.data_len;
} else if (cmd_head.wr == 6) {
/* Read error code! */
} else if (cmd_head.wr == 8) { /*Read driver version */
@@ -546,6 +611,9 @@ static s32 goodix_tool_read(char *page, char **start, off_t off, int count,
memcpy(page, GTP_DRIVER_VERSION, tmp_len);
page[tmp_len] = 0;
}
+ ret = cmd_head.data_len;
- return cmd_head.data_len;
+exit:
+ mutex_unlock(&lock);
+ return ret;
}
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c
index 6615c3a039a0..6c3747108d75 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.c
@@ -40,13 +40,14 @@
* By Meta, 2013/06/08
*/
+#include <linux/regulator/consumer.h>
#include "gt9xx.h"
-#if GTP_ICS_SLOT_REPORT
+#include <linux/of_gpio.h>
+
#include <linux/input/mt.h>
-#endif
-#define GOODIX_DEV_NAME "Goodix Capacitive TouchScreen"
+#define GOODIX_DEV_NAME "Goodix-CTP"
#define CFG_MAX_TOUCH_POINTS 5
#define GOODIX_COORDS_ARR_SIZE 4
#define MAX_BUTTONS 4
@@ -55,10 +56,20 @@
#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
+#define GOODIX_I2C_VTG_MAX_UV 1800000
+#define GOODIX_VDD_LOAD_MIN_UA 0
+#define GOODIX_VDD_LOAD_MAX_UA 10000
+#define GOODIX_VIO_LOAD_MIN_UA 0
+#define GOODIX_VIO_LOAD_MAX_UA 10000
+
#define RESET_DELAY_T3_US 200 /* T3: > 100us */
#define RESET_DELAY_T4 20 /* T4: > 5ms */
-#define PHY_BUF_SIZE 32
+#define PHY_BUF_SIZE 32
+#define PROP_NAME_SIZE 24
#define GTP_MAX_TOUCH 5
#define GTP_ESD_CHECK_CIRCLE_MS 2000
@@ -80,7 +91,10 @@ 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);
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data);
+#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);
#endif
@@ -121,40 +135,40 @@ Output:
int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len)
{
struct goodix_ts_data *ts = i2c_get_clientdata(client);
- struct i2c_msg msgs[2];
int ret = -EIO;
- int retries = 0;
-
- GTP_DEBUG_FUNC();
-
- msgs[0].flags = !I2C_M_RD;
- msgs[0].addr = client->addr;
- msgs[0].len = GTP_ADDR_LENGTH;
- msgs[0].buf = &buf[0];
-
- msgs[1].flags = I2C_M_RD;
- msgs[1].addr = client->addr;
- msgs[1].len = len - GTP_ADDR_LENGTH;
- msgs[1].buf = &buf[GTP_ADDR_LENGTH];
-
- while (retries < 5) {
+ u8 retries;
+ struct i2c_msg msgs[2] = {
+ {
+ .flags = !I2C_M_RD,
+ .addr = client->addr,
+ .len = GTP_ADDR_LENGTH,
+ .buf = &buf[0],
+ },
+ {
+ .flags = I2C_M_RD,
+ .addr = client->addr,
+ .len = len - GTP_ADDR_LENGTH,
+ .buf = &buf[GTP_ADDR_LENGTH],
+ },
+ };
+
+ for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) {
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret == 2)
break;
- retries++;
+ dev_err(&client->dev, "I2C retry: %d\n", retries + 1);
}
- if (retries >= 5) {
+ if (retries == GTP_I2C_RETRY_5) {
#if GTP_SLIDE_WAKEUP
/* reset chip would quit doze mode */
if (doze_status == DOZE_ENABLED)
return ret;
#endif
- GTP_DEBUG("I2C communication timeout, resetting chip...");
if (init_done)
gtp_reset_guitar(ts, 10);
else
dev_warn(&client->dev,
- "<GTP> gtp_reset_guitar exit init_done=%d:\n",
+ "gtp_reset_guitar exit init_done=%d:\n",
init_done);
}
return ret;
@@ -175,38 +189,36 @@ Output:
int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len)
{
struct goodix_ts_data *ts = i2c_get_clientdata(client);
- struct i2c_msg msg;
int ret = -EIO;
- int retries = 0;
-
- GTP_DEBUG_FUNC();
-
- msg.flags = !I2C_M_RD;
- msg.addr = client->addr;
- msg.len = len;
- msg.buf = buf;
-
- while (retries < 5) {
+ u8 retries;
+ struct i2c_msg msg = {
+ .flags = !I2C_M_RD,
+ .addr = client->addr,
+ .len = len,
+ .buf = buf,
+ };
+
+ for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret == 1)
break;
- retries++;
+ dev_err(&client->dev, "I2C retry: %d\n", retries + 1);
}
- if ((retries >= 5)) {
+ if (retries == GTP_I2C_RETRY_5) {
#if GTP_SLIDE_WAKEUP
if (doze_status == DOZE_ENABLED)
return ret;
#endif
- GTP_DEBUG("I2C communication timeout, resetting chip...");
if (init_done)
gtp_reset_guitar(ts, 10);
else
dev_warn(&client->dev,
- "<GTP> gtp_reset_guitar exit init_done=%d:\n",
+ "gtp_reset_guitar exit init_done=%d:\n",
init_done);
}
return ret;
}
+
/*******************************************************
Function:
i2c read twice, compare the results
@@ -226,7 +238,7 @@ int gtp_i2c_read_dbl_check(struct i2c_client *client,
u8 confirm_buf[16] = {0};
u8 retry = 0;
- while (retry++ < 3) {
+ while (retry++ < GTP_I2C_RETRY_3) {
memset(buf, 0xAA, 16);
buf[0] = (u8)(addr >> 8);
buf[1] = (u8)(addr & 0xFF);
@@ -240,7 +252,7 @@ int gtp_i2c_read_dbl_check(struct i2c_client *client,
if (!memcmp(buf, confirm_buf, len + 2))
break;
}
- if (retry < 3) {
+ if (retry < GTP_I2C_RETRY_3) {
memcpy(rxbuf, confirm_buf + 2, len);
return SUCCESS;
}
@@ -269,7 +281,7 @@ static int gtp_send_cfg(struct goodix_ts_data *ts)
"Ic fixed config, no config sent!");
ret = 2;
} else {
- for (retry = 0; retry < 5; retry++) {
+ for (retry = 0; retry < GTP_I2C_RETRY_5; retry++) {
ret = gtp_i2c_write(ts->client,
ts->config_data,
GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);
@@ -294,8 +306,6 @@ void gtp_irq_disable(struct goodix_ts_data *ts)
{
unsigned long irqflags;
- GTP_DEBUG_FUNC();
-
spin_lock_irqsave(&ts->irq_lock, irqflags);
if (!ts->irq_is_disabled) {
ts->irq_is_disabled = true;
@@ -316,8 +326,6 @@ void gtp_irq_enable(struct goodix_ts_data *ts)
{
unsigned long irqflags = 0;
- GTP_DEBUG_FUNC();
-
spin_lock_irqsave(&ts->irq_lock, irqflags);
if (ts->irq_is_disabled) {
enable_irq(ts->client->irq);
@@ -342,26 +350,15 @@ static void gtp_touch_down(struct goodix_ts_data *ts, int id, int x, int y,
int w)
{
#if GTP_CHANGE_X2Y
- GTP_SWAP(x, y);
+ swap(x, y);
#endif
-#if GTP_ICS_SLOT_REPORT
input_mt_slot(ts->input_dev, id);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
-#else
- input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
- input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
- input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
- input_mt_sync(ts->input_dev);
-#endif
-
- GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w);
}
/*******************************************************
@@ -374,15 +371,8 @@ Output:
*********************************************************/
static void gtp_touch_up(struct goodix_ts_data *ts, int id)
{
-#if GTP_ICS_SLOT_REPORT
input_mt_slot(ts->input_dev, id);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
- GTP_DEBUG("Touch id[%2d] release!", id);
-#else
- input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
- input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0);
- input_mt_sync(ts->input_dev);
-#endif
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
}
@@ -423,8 +413,6 @@ static void goodix_ts_work_func(struct work_struct *work)
u8 doze_buf[3] = {0x81, 0x4B};
#endif
- GTP_DEBUG_FUNC();
-
ts = container_of(work, struct goodix_ts_data, work);
#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
if (ts->enter_update)
@@ -434,7 +422,6 @@ static void goodix_ts_work_func(struct work_struct *work)
#if GTP_SLIDE_WAKEUP
if (doze_status == DOZE_ENABLED) {
ret = gtp_i2c_read(ts->client, doze_buf, 3);
- GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]);
if (ret > 0) {
if (doze_buf[2] == 0xAA) {
dev_dbg(&ts->client->dev,
@@ -532,12 +519,9 @@ static void goodix_ts_work_func(struct work_struct *work)
#endif
pre_key = key_value;
- GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger);
-
-#if GTP_ICS_SLOT_REPORT
#if GTP_WITH_PEN
if (pre_pen && (touch_num == 0)) {
- GTP_DEBUG("Pen touch UP(Slot)!");
+ dev_dbg(&ts->client->dev, "Pen touch UP(Slot)!");
input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
input_mt_slot(ts->input_dev, 5);
input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
@@ -554,7 +538,8 @@ static void goodix_ts_work_func(struct work_struct *work)
#if GTP_WITH_PEN
id = coor_data[pos];
if (id == 128) {
- GTP_DEBUG("Pen touch DOWN(Slot)!");
+ dev_dbg(&ts->client->dev,
+ "Pen touch DOWN(Slot)!");
input_x = coor_data[pos + 1]
| (coor_data[pos + 2] << 8);
input_y = coor_data[pos + 3]
@@ -573,7 +558,8 @@ static void goodix_ts_work_func(struct work_struct *work)
ABS_MT_POSITION_Y, input_y);
input_report_abs(ts->input_dev,
ABS_MT_TOUCH_MAJOR, input_w);
- GTP_DEBUG("Pen/Stylus: (%d, %d)[%d]",
+ dev_dbg(&ts->client->dev,
+ "Pen/Stylus: (%d, %d)[%d]",
input_x, input_y, input_w);
pre_pen = 1;
pre_touch = 0;
@@ -583,8 +569,6 @@ static void goodix_ts_work_func(struct work_struct *work)
touch_index |= (0x01<<id);
}
- GTP_DEBUG("id = %d,touch_index = 0x%x, pre_touch = 0x%x\n",
- id, touch_index, pre_touch);
for (i = 0; i < GTP_MAX_TOUCH; i++) {
#if GTP_WITH_PEN
if (pre_pen == 1)
@@ -611,42 +595,6 @@ static void goodix_ts_work_func(struct work_struct *work)
}
}
}
-#else
- input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value));
- if (touch_num) {
- for (i = 0; i < touch_num; i++) {
- coor_data = &point_data[i * 8 + 3];
-
- id = coor_data[0];
- input_x = coor_data[1] | coor_data[2] << 8;
- input_y = coor_data[3] | coor_data[4] << 8;
- input_w = coor_data[5] | coor_data[6] << 8;
-#if GTP_WITH_PEN
- if (id == 128) {
- GTP_DEBUG("Pen touch DOWN!");
- input_report_key(ts->input_dev,
- BTN_TOOL_PEN, 1);
- pre_pen = 1;
- id = 0;
- }
-#endif
- gtp_touch_down(ts, id, input_x, input_y, input_w);
- }
- } else if (pre_touch) {
-#if GTP_WITH_PEN
- if (pre_pen == 1) {
- GTP_DEBUG("Pen touch UP!");
- input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
- pre_pen = 0;
- }
-#endif
- GTP_DEBUG("Touch Released!");
- gtp_touch_up(ts, 0);
- }
-
- pre_touch = touch_num;
-#endif
-
input_sync(ts->input_dev);
exit_work_func:
@@ -676,8 +624,6 @@ static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer)
struct goodix_ts_data
*ts = container_of(timer, struct goodix_ts_data, timer);
- GTP_DEBUG_FUNC();
-
queue_work(ts->goodix_wq, &ts->work);
hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME + 6) * 1000000),
HRTIMER_MODE_REL);
@@ -698,8 +644,6 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
{
struct goodix_ts_data *ts = dev_id;
- GTP_DEBUG_FUNC();
-
gtp_irq_disable(ts);
queue_work(ts->goodix_wq, &ts->work);
@@ -731,8 +675,6 @@ Output:
*******************************************************/
static void gtp_reset_guitar(struct goodix_ts_data *ts, int ms)
{
- GTP_DEBUG_FUNC();
-
/* This reset sequence will selcet I2C slave address */
gpio_direction_output(ts->pdata->reset_gpio, 0);
msleep(ms);
@@ -755,7 +697,7 @@ static void gtp_reset_guitar(struct goodix_ts_data *ts, int ms)
#endif
}
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB)
#if GTP_SLIDE_WAKEUP
/*******************************************************
Function:
@@ -773,20 +715,17 @@ static s8 gtp_enter_doze(struct goodix_ts_data *ts)
(u8)(GTP_REG_SLEEP >> 8),
(u8)GTP_REG_SLEEP, 8};
- GTP_DEBUG_FUNC();
-
#if GTP_DBL_CLK_WAKEUP
i2c_control_buf[2] = 0x09;
#endif
gtp_irq_disable(ts);
- GTP_DEBUG("entering doze mode...");
- while (retry++ < 5) {
+ while (retry++ < GTP_I2C_RETRY_3) {
i2c_control_buf[0] = 0x80;
i2c_control_buf[1] = 0x46;
ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
if (ret < 0) {
- GTP_DEBUG(
+ dev_err(&ts->client->dev,
"failed to set doze flag into 0x8046, %d",
retry);
continue;
@@ -825,11 +764,9 @@ static s8 gtp_enter_sleep(struct goodix_ts_data *ts)
(u8)(GTP_REG_SLEEP >> 8),
(u8)GTP_REG_SLEEP, 5};
- GTP_DEBUG_FUNC();
-
ret = gpio_direction_output(ts->pdata->irq_gpio, 0);
usleep(5000);
- while (retry++ < 5) {
+ while (retry++ < GTP_I2C_RETRY_5) {
ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
if (ret > 0) {
dev_dbg(&ts->client->dev,
@@ -857,23 +794,17 @@ static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts)
u8 retry = 0;
s8 ret = -1;
- GTP_DEBUG_FUNC();
-
#if GTP_POWER_CTRL_SLEEP
- while (retry++ < 5) {
- gtp_reset_guitar(ts, 20);
+ gtp_reset_guitar(ts, 20);
- ret = gtp_send_cfg(ts);
- if (ret > 0) {
- dev_dbg(&ts->client->dev,
- "Wakeup sleep send config success.");
- continue;
- }
- dev_dbg(&ts->client->dev, "GTP Wakeup!");
+ ret = gtp_send_cfg(ts);
+ if (ret > 0) {
+ dev_dbg(&ts->client->dev,
+ "Wakeup sleep send config success.");
return 1;
}
#else
- while (retry++ < 10) {
+ while (retry++ < GTP_I2C_RETRY_10) {
#if GTP_SLIDE_WAKEUP
/* wakeup not by slide */
if (doze_status != DOZE_WAKEUP)
@@ -910,7 +841,7 @@ static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts)
dev_err(&ts->client->dev, "GTP wakeup sleep failed.\n");
return ret;
}
-#endif /* !CONFIG_HAS_EARLYSUSPEND */
+#endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/
/*******************************************************
Function:
@@ -933,26 +864,9 @@ static int gtp_init_panel(struct goodix_ts_data *ts)
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[] = {ARRAY_SIZE(cfg_info_group1),
- ARRAY_SIZE(cfg_info_group2),
- ARRAY_SIZE(cfg_info_group3),
- ARRAY_SIZE(cfg_info_group4),
- ARRAY_SIZE(cfg_info_group5),
- ARRAY_SIZE(cfg_info_group6)};
-
- GTP_DEBUG("Config Groups\' Lengths: %d, %d, %d, %d, %d, %d",
- cfg_info_len[0], 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++)
+ dev_dbg(&client->dev, "Config Groups(%d) Lengths: %d",
+ i, ts->pdata->config_data_len[i]);
ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
if (ret == SUCCESS) {
@@ -963,14 +877,18 @@ static int gtp_init_panel(struct goodix_ts_data *ts)
return -EINVAL;
}
}
- if ((!cfg_info_len[1]) && (!cfg_info_len[2]) && (!cfg_info_len[3])
- && (!cfg_info_len[4]) && (!cfg_info_len[5])) {
+
+ for (i = 1; 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,
&sensor_id, 1);
if (ret == SUCCESS) {
- if (sensor_id >= 0x06) {
+ if (sensor_id >= GOODIX_MAX_CFG_GROUP) {
dev_err(&client->dev,
"Invalid sensor_id(0x%02X), No Config Sent!",
sensor_id);
@@ -982,24 +900,26 @@ static int gtp_init_panel(struct goodix_ts_data *ts)
return -EINVAL;
}
}
- GTP_DEBUG("Sensor_ID: %d", sensor_id);
- ts->gtp_cfg_len = cfg_info_len[sensor_id];
+ dev_dbg(&client->dev, "Sensor ID selected: %d", sensor_id);
- if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) {
+ if (ts->pdata->config_data_len[sensor_id] < GTP_CONFIG_MIN_LENGTH ||
+ !ts->pdata->config_data[sensor_id]) {
dev_err(&client->dev,
- "Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!\n",
+ "Sensor_ID(%d) matches with NULL or invalid config group!\n",
sensor_id);
return -EINVAL;
}
+
ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA,
&opr_buf[0], 1);
-
if (ret == SUCCESS) {
if (opr_buf[0] < 90) {
/* backup group config version */
- grp_cfg_version = send_cfg_buf[sensor_id][0];
- send_cfg_buf[sensor_id][0] = 0x00;
+ grp_cfg_version =
+ ts->pdata->config_data[sensor_id][GTP_ADDR_LENGTH];
+ ts->pdata->config_data[sensor_id][GTP_ADDR_LENGTH] =
+ 0x00;
ts->fixed_cfg = 0;
} else {
/* treated as fixed config, not send config */
@@ -1014,21 +934,9 @@ static int gtp_init_panel(struct goodix_ts_data *ts)
return -EINVAL;
}
- config_data = devm_kzalloc(&client->dev,
- GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH,
- GFP_KERNEL);
- if (!config_data) {
- dev_err(&client->dev,
- "Not enough memory for panel config data\n");
- return -ENOMEM;
- }
-
- ts->config_data = config_data;
- config_data[0] = GTP_REG_CONFIG_DATA >> 8;
- config_data[1] = GTP_REG_CONFIG_DATA & 0xff;
- memset(&config_data[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
- memcpy(&config_data[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];
#if GTP_CUSTOM_CFG
config_data[RESOLUTION_LOC] =
@@ -1065,7 +973,6 @@ static int gtp_init_panel(struct goodix_ts_data *ts)
}
#endif /* !DRIVER NOT SEND CONFIG */
- GTP_DEBUG_FUNC();
if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) {
ts->abs_x_max = (config_data[RESOLUTION_LOC + 1] << 8)
+ config_data[RESOLUTION_LOC];
@@ -1077,49 +984,79 @@ static int gtp_init_panel(struct goodix_ts_data *ts)
if (ret < 0)
dev_err(&client->dev, "%s: Send config error.\n", __func__);
- GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
- ts->abs_x_max, ts->abs_y_max,
- ts->int_trigger_type);
-
msleep(20);
return ret;
}
/*******************************************************
Function:
- Read chip version.
+ Read firmware version
Input:
client: i2c device
version: buffer to keep ic firmware version
Output:
read operation return.
- 2: succeed, otherwise: failed
+ 0: succeed, otherwise: failed
*******************************************************/
-int gtp_read_version(struct i2c_client *client, u16 *version)
+static int gtp_read_fw_version(struct i2c_client *client, u16 *version)
{
- int ret = -EIO;
- u8 buf[8] = { GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff };
-
- GTP_DEBUG_FUNC();
+ int ret = 0;
+ u8 buf[GTP_FW_VERSION_BUFFER_MAXSIZE] = {
+ GTP_REG_FW_VERSION >> 8, GTP_REG_FW_VERSION & 0xff };
ret = gtp_i2c_read(client, buf, sizeof(buf));
if (ret < 0) {
dev_err(&client->dev, "GTP read version failed.\n");
- return ret;
+ return -EIO;
}
if (version)
- *version = (buf[7] << 8) | buf[6];
+ *version = (buf[3] << 8) | buf[2];
+
+ return ret;
+}
+/*
+ * Function:
+ * Read and check chip id.
+ * Input:
+ * client: i2c device
+ * Output:
+ * read operation return.
+ * 0: succeed, otherwise: failed
+ */
+static int gtp_check_product_id(struct i2c_client *client)
+{
+ int ret = 0;
+ char product_id[GTP_PRODUCT_ID_MAXSIZE];
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
+ /* 04 bytes are used for the Product-id in the register space.*/
+ u8 buf[GTP_PRODUCT_ID_BUFFER_MAXSIZE] = {
+ GTP_REG_PRODUCT_ID >> 8, GTP_REG_PRODUCT_ID & 0xff };
+
+ ret = gtp_i2c_read(client, buf, sizeof(buf));
+ if (ret < 0) {
+ dev_err(&client->dev, "GTP read product_id failed.\n");
+ return -EIO;
+ }
if (buf[5] == 0x00) {
- dev_dbg(&client->dev, "IC Version: %c%c%c_%02x%02x\n", buf[2],
- buf[3], buf[4], buf[7], buf[6]);
+ /* copy (GTP_PRODUCT_ID_MAXSIZE - 1) from buffer. Ex: 915 */
+ strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE - 1);
} else {
if (buf[5] == 'S' || buf[5] == 's')
chip_gt9xxs = 1;
- dev_dbg(&client->dev, "IC Version: %c%c%c%c_%02x%02x\n", buf[2],
- buf[3], buf[4], buf[5], buf[7], buf[6]);
+ /* copy GTP_PRODUCT_ID_MAXSIZE from buffer. Ex: 915s */
+ strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE);
}
+
+ 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);
+
+ if (ret != 0)
+ return -EINVAL;
+
return ret;
}
@@ -1135,11 +1072,9 @@ Output:
static int gtp_i2c_test(struct i2c_client *client)
{
u8 buf[3] = { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff };
- int retry = 5;
+ int retry = GTP_I2C_RETRY_5;
int ret = -EIO;
- GTP_DEBUG_FUNC();
-
while (retry--) {
ret = gtp_i2c_read(client, buf, 3);
if (ret > 0)
@@ -1168,50 +1103,66 @@ static int gtp_request_io_port(struct goodix_ts_data *ts)
if (gpio_is_valid(pdata->irq_gpio)) {
ret = gpio_request(pdata->irq_gpio, "goodix_ts_irq_gpio");
if (ret) {
- dev_err(&client->dev, "irq gpio request failed\n");
- goto pwr_off;
+ dev_err(&client->dev, "Unable to request irq gpio [%d]\n",
+ pdata->irq_gpio);
+ goto err_pwr_off;
}
ret = gpio_direction_input(pdata->irq_gpio);
if (ret) {
- dev_err(&client->dev,
- "set_direction for irq gpio failed\n");
- goto free_irq_gpio;
+ dev_err(&client->dev, "Unable to set direction for irq gpio [%d]\n",
+ pdata->irq_gpio);
+ goto err_free_irq_gpio;
}
} else {
- dev_err(&client->dev, "irq gpio is invalid!\n");
+ dev_err(&client->dev, "Invalid irq gpio [%d]!\n",
+ pdata->irq_gpio);
ret = -EINVAL;
- goto free_irq_gpio;
+ goto err_pwr_off;
}
if (gpio_is_valid(pdata->reset_gpio)) {
- ret = gpio_request(pdata->reset_gpio, "goodix_ts__reset_gpio");
+ ret = gpio_request(pdata->reset_gpio, "goodix_ts_reset_gpio");
if (ret) {
- dev_err(&client->dev, "reset gpio request failed\n");
- goto free_irq_gpio;
+ dev_err(&client->dev, "Unable to request reset gpio [%d]\n",
+ pdata->reset_gpio);
+ goto err_free_irq_gpio;
}
ret = gpio_direction_output(pdata->reset_gpio, 0);
if (ret) {
- dev_err(&client->dev,
- "set_direction for reset gpio failed\n");
- goto free_reset_gpio;
+ dev_err(&client->dev, "Unable to set direction for reset gpio [%d]\n",
+ pdata->reset_gpio);
+ goto err_free_reset_gpio;
}
} else {
- dev_err(&client->dev, "reset gpio is invalid!\n");
+ dev_err(&client->dev, "Invalid irq gpio [%d]!\n",
+ pdata->reset_gpio);
ret = -EINVAL;
- goto free_reset_gpio;
+ goto err_free_irq_gpio;
}
- gpio_direction_input(pdata->reset_gpio);
+ /* IRQ GPIO is an input signal, but we are setting it to output
+ * direction and pulling it down, to comply with power up timing
+ * requirements, mentioned in power up timing section of device
+ * datasheet.
+ */
+ ret = gpio_direction_output(pdata->irq_gpio, 0);
+ if (ret)
+ dev_warn(&client->dev,
+ "pull down interrupt gpio failed\n");
+ ret = gpio_direction_output(pdata->reset_gpio, 0);
+ if (ret)
+ dev_warn(&client->dev,
+ "pull down reset gpio failed\n");
return ret;
-free_reset_gpio:
+err_free_reset_gpio:
if (gpio_is_valid(pdata->reset_gpio))
gpio_free(pdata->reset_gpio);
-free_irq_gpio:
+err_free_irq_gpio:
if (gpio_is_valid(pdata->irq_gpio))
gpio_free(pdata->irq_gpio);
-pwr_off:
+err_pwr_off:
return ret;
}
@@ -1229,9 +1180,6 @@ static int gtp_request_irq(struct goodix_ts_data *ts)
int ret;
const u8 irq_table[] = GTP_IRQ_TAB;
- GTP_DEBUG("INT trigger type:%x, irq=%d", ts->int_trigger_type,
- ts->client->irq);
-
ret = request_irq(ts->client->irq, goodix_ts_irq_handler,
irq_table[ts->int_trigger_type],
ts->client->name, ts);
@@ -1270,8 +1218,6 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts)
int index = 0;
#endif
- GTP_DEBUG_FUNC();
-
ts->input_dev = input_allocate_device();
if (ts->input_dev == NULL) {
dev_err(&ts->client->dev,
@@ -1281,12 +1227,10 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts)
ts->input_dev->evbit[0] =
BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-#if GTP_ICS_SLOT_REPORT
+ set_bit(BTN_TOOL_FINGER, ts->input_dev->keybit);
__set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
- input_mt_init_slots(ts->input_dev, 10);/* in case of "out of memory" */
-#else
- ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
-#endif
+ /* 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++) {
@@ -1307,7 +1251,7 @@ static int gtp_request_input_dev(struct goodix_ts_data *ts)
#endif
#if GTP_CHANGE_X2Y
- GTP_SWAP(ts->abs_x_max, ts->abs_y_max);
+ swap(ts->abs_x_max, ts->abs_y_max);
#endif
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
@@ -1345,6 +1289,327 @@ exit_free_inputdev:
return ret;
}
+static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
+{
+ return (regulator_count_voltages(reg) > 0) ?
+ regulator_set_optimum_mode(reg, load_uA) : 0;
+}
+
+/**
+ * goodix_power_on - Turn device power ON
+ * @ts: driver private data
+ *
+ * Returns zero on success, else an error.
+ */
+static int goodix_power_on(struct goodix_ts_data *ts)
+{
+ int ret;
+
+ if (!IS_ERR(ts->avdd)) {
+ ret = reg_set_optimum_mode_check(ts->avdd,
+ GOODIX_VDD_LOAD_MAX_UA);
+ if (ret < 0) {
+ dev_err(&ts->client->dev,
+ "Regulator avdd set_opt failed rc=%d\n", ret);
+ goto err_set_opt_avdd;
+ }
+ ret = regulator_enable(ts->avdd);
+ if (ret) {
+ dev_err(&ts->client->dev,
+ "Regulator avdd enable failed ret=%d\n", ret);
+ goto err_enable_avdd;
+ }
+ }
+
+ if (!IS_ERR(ts->vdd)) {
+ ret = regulator_set_voltage(ts->vdd, GOODIX_VTG_MIN_UV,
+ GOODIX_VTG_MAX_UV);
+ if (ret) {
+ dev_err(&ts->client->dev,
+ "Regulator set_vtg failed vdd ret=%d\n", ret);
+ goto err_set_vtg_vdd;
+ }
+ ret = reg_set_optimum_mode_check(ts->vdd,
+ GOODIX_VDD_LOAD_MAX_UA);
+ if (ret < 0) {
+ dev_err(&ts->client->dev,
+ "Regulator vdd set_opt failed rc=%d\n", ret);
+ goto err_set_opt_vdd;
+ }
+ ret = regulator_enable(ts->vdd);
+ if (ret) {
+ dev_err(&ts->client->dev,
+ "Regulator vdd enable failed ret=%d\n", ret);
+ goto err_enable_vdd;
+ }
+ }
+
+ if (!IS_ERR(ts->vcc_i2c)) {
+ ret = regulator_set_voltage(ts->vcc_i2c, GOODIX_I2C_VTG_MIN_UV,
+ GOODIX_I2C_VTG_MAX_UV);
+ if (ret) {
+ dev_err(&ts->client->dev,
+ "Regulator set_vtg failed vcc_i2c ret=%d\n",
+ ret);
+ goto err_set_vtg_vcc_i2c;
+ }
+ ret = reg_set_optimum_mode_check(ts->vcc_i2c,
+ GOODIX_VIO_LOAD_MAX_UA);
+ if (ret < 0) {
+ dev_err(&ts->client->dev,
+ "Regulator vcc_i2c set_opt failed rc=%d\n",
+ ret);
+ goto err_set_opt_vcc_i2c;
+ }
+ ret = regulator_enable(ts->vcc_i2c);
+ if (ret) {
+ dev_err(&ts->client->dev,
+ "Regulator vcc_i2c enable failed ret=%d\n",
+ ret);
+ regulator_disable(ts->vdd);
+ goto err_enable_vcc_i2c;
+ }
+ }
+
+ return 0;
+
+err_enable_vcc_i2c:
+err_set_opt_vcc_i2c:
+ if (!IS_ERR(ts->vcc_i2c))
+ regulator_set_voltage(ts->vcc_i2c, 0, GOODIX_I2C_VTG_MAX_UV);
+err_set_vtg_vcc_i2c:
+ if (!IS_ERR(ts->vdd))
+ regulator_disable(ts->vdd);
+err_enable_vdd:
+err_set_opt_vdd:
+ if (!IS_ERR(ts->vdd))
+ regulator_set_voltage(ts->vdd, 0, GOODIX_VTG_MAX_UV);
+err_set_vtg_vdd:
+ if (!IS_ERR(ts->avdd))
+ regulator_disable(ts->avdd);
+err_enable_avdd:
+err_set_opt_avdd:
+ return ret;
+}
+
+/**
+ * goodix_power_off - Turn device power OFF
+ * @ts: driver private data
+ *
+ * Returns zero on success, else an error.
+ */
+static int goodix_power_off(struct goodix_ts_data *ts)
+{
+ int ret;
+
+ if (!IS_ERR(ts->vcc_i2c)) {
+ ret = regulator_set_voltage(ts->vcc_i2c, 0,
+ GOODIX_I2C_VTG_MAX_UV);
+ if (ret < 0)
+ dev_err(&ts->client->dev,
+ "Regulator vcc_i2c set_vtg failed ret=%d\n",
+ ret);
+ ret = regulator_disable(ts->vcc_i2c);
+ if (ret)
+ dev_err(&ts->client->dev,
+ "Regulator vcc_i2c disable failed ret=%d\n",
+ ret);
+ }
+
+ if (!IS_ERR(ts->vdd)) {
+ ret = regulator_set_voltage(ts->vdd, 0, GOODIX_VTG_MAX_UV);
+ if (ret < 0)
+ dev_err(&ts->client->dev,
+ "Regulator vdd set_vtg failed ret=%d\n", ret);
+ ret = regulator_disable(ts->vdd);
+ if (ret)
+ dev_err(&ts->client->dev,
+ "Regulator vdd disable failed ret=%d\n", ret);
+ }
+
+ if (!IS_ERR(ts->avdd)) {
+ ret = regulator_disable(ts->avdd);
+ if (ret)
+ dev_err(&ts->client->dev,
+ "Regulator avdd disable failed ret=%d\n", ret);
+ }
+
+ return 0;
+}
+
+/**
+ * goodix_power_init - Initialize device power
+ * @ts: driver private data
+ *
+ * Returns zero on success, else an error.
+ */
+static int goodix_power_init(struct goodix_ts_data *ts)
+{
+ int ret;
+
+ ts->avdd = regulator_get(&ts->client->dev, "avdd");
+ if (IS_ERR(ts->avdd)) {
+ ret = PTR_ERR(ts->avdd);
+ dev_info(&ts->client->dev,
+ "Regulator get failed avdd ret=%d\n", ret);
+ }
+
+ ts->vdd = regulator_get(&ts->client->dev, "vdd");
+ if (IS_ERR(ts->vdd)) {
+ ret = PTR_ERR(ts->vdd);
+ dev_info(&ts->client->dev,
+ "Regulator get failed vdd ret=%d\n", ret);
+ }
+
+ ts->vcc_i2c = regulator_get(&ts->client->dev, "vcc-i2c");
+ if (IS_ERR(ts->vcc_i2c)) {
+ ret = PTR_ERR(ts->vcc_i2c);
+ dev_info(&ts->client->dev,
+ "Regulator get failed vcc_i2c ret=%d\n", ret);
+ }
+
+ return 0;
+}
+
+/**
+ * goodix_power_deinit - Deinitialize device power
+ * @ts: driver private data
+ *
+ * Returns zero on success, else an error.
+ */
+static int goodix_power_deinit(struct goodix_ts_data *ts)
+{
+ regulator_put(ts->vdd);
+ regulator_put(ts->vcc_i2c);
+ regulator_put(ts->avdd);
+
+ return 0;
+}
+
+static int goodix_ts_get_dt_coords(struct device *dev, char *name,
+ struct goodix_ts_platform_data *pdata)
+{
+ struct property *prop;
+ struct device_node *np = dev->of_node;
+ int rc;
+ u32 coords[GOODIX_COORDS_ARR_SIZE];
+
+ prop = of_find_property(np, name, NULL);
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ rc = of_property_read_u32_array(np, name, coords,
+ GOODIX_COORDS_ARR_SIZE);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read %s\n", name);
+ return rc;
+ }
+
+ if (!strcmp(name, "goodix,panel-coords")) {
+ pdata->panel_minx = coords[0];
+ pdata->panel_miny = coords[1];
+ pdata->panel_maxx = coords[2];
+ pdata->panel_maxy = coords[3];
+ } else if (!strcmp(name, "goodix,display-coords")) {
+ pdata->x_min = coords[0];
+ pdata->y_min = coords[1];
+ pdata->x_max = coords[2];
+ pdata->y_max = coords[3];
+ } else {
+ dev_err(dev, "unsupported property %s\n", name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int goodix_parse_dt(struct device *dev,
+ struct goodix_ts_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ struct property *prop;
+ u32 temp_val, num_buttons;
+ u32 button_map[MAX_BUTTONS];
+ char prop_name[PROP_NAME_SIZE];
+ int i, read_cfg_num;
+
+ rc = goodix_ts_get_dt_coords(dev, "goodix,panel-coords", pdata);
+ if (rc && (rc != -EINVAL))
+ return rc;
+
+ rc = goodix_ts_get_dt_coords(dev, "goodix,display-coords", pdata);
+ if (rc)
+ return rc;
+
+ pdata->i2c_pull_up = of_property_read_bool(np,
+ "goodix,i2c-pull-up");
+
+ pdata->no_force_update = of_property_read_bool(np,
+ "goodix,no-force-update");
+ /* reset, irq gpio info */
+ pdata->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios",
+ 0, &pdata->reset_gpio_flags);
+ if (pdata->reset_gpio < 0)
+ return pdata->reset_gpio;
+
+ pdata->irq_gpio = of_get_named_gpio_flags(np, "interrupt-gpios",
+ 0, &pdata->irq_gpio_flags);
+ if (pdata->irq_gpio < 0)
+ return pdata->irq_gpio;
+
+ 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;
+
+ prop = of_find_property(np, "goodix,button-map", NULL);
+ if (prop) {
+ num_buttons = prop->length / sizeof(temp_val);
+ if (num_buttons > MAX_BUTTONS)
+ return -EINVAL;
+
+ rc = of_property_read_u32_array(np,
+ "goodix,button-map", button_map,
+ num_buttons);
+ if (rc) {
+ dev_err(dev, "Unable to read key codes\n");
+ return rc;
+ }
+ }
+
+ read_cfg_num = 0;
+ for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) {
+ snprintf(prop_name, sizeof(prop_name), "goodix,cfg-data%d", i);
+ prop = of_find_property(np, prop_name,
+ &pdata->config_data_len[i]);
+ if (!prop || !prop->value) {
+ pdata->config_data_len[i] = 0;
+ pdata->config_data[i] = NULL;
+ continue;
+ }
+ pdata->config_data[i] = devm_kzalloc(dev,
+ GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH,
+ GFP_KERNEL);
+ if (!pdata->config_data[i]) {
+ dev_err(dev,
+ "Not enough memory for panel config data %d\n",
+ i);
+ return -ENOMEM;
+ }
+ pdata->config_data[i][0] = GTP_REG_CONFIG_DATA >> 8;
+ pdata->config_data[i][1] = GTP_REG_CONFIG_DATA & 0xff;
+ memcpy(&pdata->config_data[i][GTP_ADDR_LENGTH],
+ prop->value, pdata->config_data_len[i]);
+ read_cfg_num++;
+ }
+ dev_dbg(dev, "%d config data read from device tree.\n", read_cfg_num);
+
+ return 0;
+}
+
/*******************************************************
Function:
I2c probe.
@@ -1359,38 +1624,69 @@ Output:
static int goodix_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct goodix_ts_platform_data *pdata;
struct goodix_ts_data *ts;
u16 version_info;
int ret;
dev_dbg(&client->dev, "GTP I2C Address: 0x%02x\n", client->addr);
+ if (client->dev.of_node) {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct goodix_ts_platform_data), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ ret = goodix_parse_dt(&client->dev, pdata);
+ if (ret)
+ return ret;
+ } else {
+ pdata = client->dev.platform_data;
+ }
+
+ if (!pdata) {
+ dev_err(&client->dev, "GTP invalid pdata\n");
+ return -EINVAL;
+ }
#if GTP_ESD_PROTECT
i2c_connect_client = client;
#endif
+
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "GTP I2C not supported\n");
return -ENODEV;
}
- ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
return -ENOMEM;
memset(ts, 0, sizeof(*ts));
ts->client = client;
- /* For kernel 2.6.39 later we spin_lock_init(&ts->irq_lock)
+ ts->pdata = pdata;
+ /* For 2.6.39 & later use spin_lock_init(&ts->irq_lock)
* For 2.6.39 & before, use ts->irq_lock = SPIN_LOCK_UNLOCKED
*/
spin_lock_init(&ts->irq_lock);
i2c_set_clientdata(client, ts);
-
ts->gtp_rawdiff_mode = 0;
ret = gtp_request_io_port(ts);
if (ret) {
dev_err(&client->dev, "GTP request IO port failed.\n");
- goto exit_power_off;
+ goto exit_free_client_data;
+ }
+
+ ret = goodix_power_init(ts);
+ if (ret) {
+ dev_err(&client->dev, "GTP power init failed\n");
+ goto exit_free_io_port;
+ }
+
+ ret = goodix_power_on(ts);
+ if (ret) {
+ dev_err(&client->dev, "GTP power on failed\n");
+ goto exit_deinit_power;
}
gtp_reset_guitar(ts, 20);
@@ -1398,7 +1694,7 @@ 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");
- goto exit_free_io_port;
+ goto exit_power_off;
}
#if GTP_AUTO_UPDATE
@@ -1424,6 +1720,20 @@ static int goodix_ts_probe(struct i2c_client *client,
goto exit_free_inputdev;
}
+#if defined(CONFIG_FB)
+ ts->fb_notif.notifier_call = fb_notifier_callback;
+ ret = fb_register_client(&ts->fb_notif);
+ if (ret)
+ dev_err(&ts->client->dev,
+ "Unable to register fb_notifier: %d\n",
+ ret);
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = goodix_ts_early_suspend;
+ ts->early_suspend.resume = goodix_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
ts->goodix_wq = create_singlethread_workqueue("goodix_wq");
INIT_WORK(&ts->work, goodix_ts_work_func);
@@ -1433,9 +1743,13 @@ static int goodix_ts_probe(struct i2c_client *client,
else
dev_info(&client->dev, "GTP works in interrupt mode.\n");
- ret = gtp_read_version(client, &version_info);
- if (ret != 2) {
- dev_err(&client->dev, "Read version failed.\n");
+ ret = gtp_read_fw_version(client, &version_info);
+ if (ret != 2)
+ 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");
goto exit_free_irq;
}
if (ts->use_irq)
@@ -1451,6 +1765,13 @@ static int goodix_ts_probe(struct i2c_client *client,
init_done = true;
return 0;
exit_free_irq:
+#if defined(CONFIG_FB)
+ if (fb_unregister_client(&ts->fb_notif))
+ dev_err(&client->dev,
+ "Error occurred while unregistering fb_notifier.\n");
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ unregister_early_suspend(&ts->early_suspend);
+#endif
if (ts->use_irq)
free_irq(client->irq, ts);
else
@@ -1466,10 +1787,17 @@ exit_free_irq:
}
exit_free_inputdev:
kfree(ts->config_data);
-exit_free_io_port:
exit_power_off:
+ goodix_power_off(ts);
+exit_deinit_power:
+ goodix_power_deinit(ts);
+exit_free_io_port:
+ if (gpio_is_valid(pdata->reset_gpio))
+ gpio_free(pdata->reset_gpio);
+ if (gpio_is_valid(pdata->irq_gpio))
+ gpio_free(pdata->irq_gpio);
+exit_free_client_data:
i2c_set_clientdata(client, NULL);
- kfree(ts);
return ret;
}
@@ -1485,8 +1813,11 @@ static int goodix_ts_remove(struct i2c_client *client)
{
struct goodix_ts_data *ts = i2c_get_clientdata(client);
- GTP_DEBUG_FUNC();
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#if defined(CONFIG_FB)
+ if (fb_unregister_client(&ts->fb_notif))
+ dev_err(&client->dev,
+ "Error occurred while unregistering fb_notifier.\n");
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
unregister_early_suspend(&ts->early_suspend);
#endif
@@ -1515,21 +1846,21 @@ static int goodix_ts_remove(struct i2c_client *client)
input_free_device(ts->input_dev);
ts->input_dev = NULL;
}
- kfree(ts->config_data);
if (gpio_is_valid(ts->pdata->reset_gpio))
gpio_free(ts->pdata->reset_gpio);
if (gpio_is_valid(ts->pdata->irq_gpio))
gpio_free(ts->pdata->irq_gpio);
+ goodix_power_off(ts);
+ goodix_power_deinit(ts);
i2c_set_clientdata(client, NULL);
- kfree(ts);
}
return 0;
}
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB)
/*******************************************************
Function:
Early suspend function.
@@ -1538,14 +1869,9 @@ Input:
Output:
None.
*******************************************************/
-static void goodix_ts_early_suspend(struct early_suspend *h)
+static void goodix_ts_suspend(struct goodix_ts_data *ts)
{
- struct goodix_ts_data *ts;
- s8 ret = -1;
-
- ts = container_of(h, struct goodix_ts_data, early_suspend);
-
- GTP_DEBUG_FUNC();
+ int ret = -1, i;
#if GTP_ESD_PROTECT
ts->gtp_is_suspend = 1;
@@ -1559,6 +1885,12 @@ static void goodix_ts_early_suspend(struct early_suspend *h)
gtp_irq_disable(ts);
else
hrtimer_cancel(&ts->timer);
+
+ for (i = 0; i < GTP_MAX_TOUCH; i++)
+ gtp_touch_up(ts, i);
+
+ input_sync(ts->input_dev);
+
ret = gtp_enter_sleep(ts);
#endif
if (ret < 0)
@@ -1577,14 +1909,9 @@ Input:
Output:
None.
*******************************************************/
-static void goodix_ts_late_resume(struct early_suspend *h)
+static void goodix_ts_resume(struct goodix_ts_data *ts)
{
- struct goodix_ts_data *ts;
- s8 ret = -1;
-
- ts = container_of(h, struct goodix_ts_data, early_suspend);
-
- GTP_DEBUG_FUNC();
+ int ret = -1;
ret = gtp_wakeup_sleep(ts);
@@ -1593,7 +1920,7 @@ static void goodix_ts_late_resume(struct early_suspend *h)
#endif
if (ret < 0)
- dev_err(&ts->client->dev, "GTP later resume failed.\n");
+ dev_err(&ts->client->dev, "GTP resume failed.\n");
if (ts->use_irq)
gtp_irq_enable(ts);
@@ -1606,18 +1933,72 @@ static void goodix_ts_late_resume(struct early_suspend *h)
gtp_esd_switch(ts->client, SWITCH_ON);
#endif
}
+
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+ struct goodix_ts_data *ts =
+ container_of(self, struct goodix_ts_data, fb_notif);
+
+ if (evdata && evdata->data && event == FB_EVENT_BLANK &&
+ ts && ts->client) {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK)
+ goodix_ts_resume(ts);
+ else if (*blank == FB_BLANK_POWERDOWN)
+ goodix_ts_suspend(ts);
+ }
+
+ return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+/*
+ * Function:
+ * Early suspend function.
+ * Input:
+ * h: early_suspend struct.
+ * Output:
+ * None.
+ */
+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);
+}
+
+/*
+ * Function:
+ * Late resume function.
+ * Input:
+ * h: early_suspend struct.
+ * Output:
+ * None.
+ */
+static void goodix_ts_late_resume(struct early_suspend *h)
+{
+ struct goodix_ts_data *ts;
+
+ ts = container_of(h, struct goodix_ts_data, early_suspend);
+ goodix_ts_late_resume(ts);
+}
#endif
+#endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/
#if GTP_ESD_PROTECT
-/*******************************************************
-Function:
- switch on & off esd delayed work
-Input:
- client: i2c device
- on: SWITCH_ON / SWITCH_OFF
-Output:
- void
-*********************************************************/
+/*
+ * Function:
+ * switch on & off esd delayed work
+ * Input:
+ * client: i2c device
+ * on: SWITCH_ON / SWITCH_OFF
+ * Output:
+ * void
+ */
void gtp_esd_switch(struct i2c_client *client, int on)
{
struct goodix_ts_data *ts;
@@ -1658,21 +2039,18 @@ static int gtp_init_ext_watchdog(struct i2c_client *client)
int ret;
int retries = 0;
- GTP_DEBUG("Init external watchdog...");
- GTP_DEBUG_FUNC();
-
msg.flags = !I2C_M_RD;
msg.addr = client->addr;
msg.len = 4;
msg.buf = opr_buffer;
- while (retries < 5) {
+ while (retries < GTP_I2C_RETRY_5) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret == 1)
return 1;
retries++;
}
- if (retries >= 5)
+ if (retries == GTP_I2C_RETRY_5)
dev_err(&client->dev, "init external watchdog failed!");
return 0;
}
@@ -1688,13 +2066,11 @@ Output:
*******************************************************/
static void gtp_esd_check_func(struct work_struct *work)
{
- s32 i;
+ s32 retry;
s32 ret = -1;
struct goodix_ts_data *ts = NULL;
u8 test[4] = {0x80, 0x40};
- GTP_DEBUG_FUNC();
-
ts = i2c_get_clientdata(i2c_connect_client);
if (ts->gtp_is_suspend) {
@@ -1707,17 +2083,16 @@ static void gtp_esd_check_func(struct work_struct *work)
return;
#endif
- for (i = 0; i < 3; i++) {
+ for (retry = 0; retry < GTP_I2C_RETRY_3; retry++) {
ret = gtp_i2c_read(ts->client, test, 4);
- GTP_DEBUG("0x8040 = 0x%02X, 0x8041 = 0x%02X", test[2], test[3]);
if ((ret < 0)) {
/* IC works abnormally..*/
continue;
} else {
if ((test[2] == 0xAA) || (test[3] != 0xAA)) {
/* IC works abnormally..*/
- i = 3;
+ retry = GTP_I2C_RETRY_3;
break;
}
/* IC works normally, Write 0x8040 0xAA*/
@@ -1726,7 +2101,7 @@ static void gtp_esd_check_func(struct work_struct *work)
break;
}
}
- if (i >= 3) {
+ if (retry == GTP_I2C_RETRY_3) {
dev_err(&ts->client->dev,
"IC Working ABNORMALLY, Resetting Guitar...\n");
gtp_reset_guitar(ts, 50);
@@ -1749,6 +2124,11 @@ static const struct i2c_device_id goodix_ts_id[] = {
{ }
};
+static const struct of_device_id goodix_match_table[] = {
+ { .compatible = "goodix,gt9xx", },
+ { },
+};
+
static struct i2c_driver goodix_ts_driver = {
.probe = goodix_ts_probe,
.remove = goodix_ts_remove,
@@ -1760,6 +2140,7 @@ static struct i2c_driver goodix_ts_driver = {
.driver = {
.name = GTP_I2C_NAME,
.owner = THIS_MODULE,
+ .of_match_table = goodix_match_table,
},
};
@@ -1775,7 +2156,6 @@ static int __init goodix_ts_init(void)
{
int ret;
- GTP_DEBUG_FUNC();
#if GTP_ESD_PROTECT
INIT_DELAYED_WORK(&gtp_esd_check_work, gtp_esd_check_func);
gtp_esd_check_workqueue = create_workqueue("gtp_esd_check");
@@ -1794,7 +2174,6 @@ Output:
********************************************************/
static void __exit goodix_ts_exit(void)
{
- GTP_DEBUG_FUNC();
i2c_del_driver(&goodix_ts_driver);
}
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h
index 48fa2ad2faca..843e3d6c05b2 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.h
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.h
@@ -33,19 +33,22 @@
#include <linux/regulator/consumer.h>
#include <linux/firmware.h>
#include <linux/debugfs.h>
-#if defined(CONFIG_HAS_EARLYSUSPEND)
+
+#if defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
#include <linux/earlysuspend.h>
#define GOODIX_SUSPEND_LEVEL 1
#endif
+#define GOODIX_MAX_CFG_GROUP 6
struct goodix_ts_platform_data {
int irq_gpio;
u32 irq_gpio_flags;
int reset_gpio;
u32 reset_gpio_flags;
- int ldo_en_gpio;
- u32 ldo_en_gpio_flags;
- u32 family_id;
+ const char *product_id;
u32 x_max;
u32 y_max;
u32 x_min;
@@ -56,6 +59,8 @@ struct goodix_ts_platform_data {
u32 panel_maxy;
bool no_force_update;
bool i2c_pull_up;
+ size_t config_data_len[GOODIX_MAX_CFG_GROUP];
+ u8 *config_data[GOODIX_MAX_CFG_GROUP];
};
struct goodix_ts_data {
spinlock_t irq_lock;
@@ -65,9 +70,6 @@ struct goodix_ts_data {
struct hrtimer timer;
struct workqueue_struct *goodix_wq;
struct work_struct work;
-#if defined(CONFIG_HAS_EARLYSUSPEND)
- struct early_suspend early_suspend;
-#endif
s32 irq_is_disabled;
s32 use_irq;
u16 abs_x_max;
@@ -84,6 +86,14 @@ struct goodix_ts_data {
u8 fixed_cfg;
u8 esd_running;
u8 fw_error;
+ struct regulator *avdd;
+ struct regulator *vdd;
+ struct regulator *vcc_i2c;
+#if defined(CONFIG_FB)
+ struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
};
extern u16 show_len;
@@ -94,8 +104,7 @@ 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 1
-#define GTP_ICS_SLOT_REPORT 0
+#define GTP_POWER_CTRL_SLEEP 0
/* auto updated by .bin file as default */
#define GTP_AUTO_UPDATE 0
@@ -112,7 +121,7 @@ extern u16 total_len;
/* double-click wakeup, function together with GTP_SLIDE_WAKEUP */
#define GTP_DBL_CLK_WAKEUP 0
-#define GTP_DEBUG_ON 1
+#define GTP_DEBUG_ON 0
#define GTP_DEBUG_ARRAY_ON 0
#define GTP_DEBUG_FUNC_ON 0
@@ -126,56 +135,7 @@ extern u16 total_len;
* GND NC/300K 3
* VDDIO NC/300K 4
* NC NC/300K 5
-*/
-/* Define your own default or for Sensor_ID == 0 config here */
-/* The predefined one is just a sample config,
- * which is not suitable for your tp in most cases.
*/
-#define CTP_CFG_GROUP1 {\
- 0x41, 0x1C, 0x02, 0xC0, 0x03, 0x0A, 0x05, 0x01, 0x01, 0x0F,\
- 0x23, 0x0F, 0x5F, 0x41, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00,\
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x0A,\
- 0x28, 0x00, 0xB8, 0x0B, 0x00, 0x00, 0x00, 0x9A, 0x03, 0x25,\
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x64, 0x32, 0x00, 0x00,\
- 0x00, 0x32, 0x8C, 0x94, 0x05, 0x01, 0x05, 0x00, 0x00, 0x96,\
- 0x0C, 0x22, 0xD8, 0x0E, 0x23, 0x56, 0x11, 0x25, 0xFF, 0x13,\
- 0x28, 0xA7, 0x15, 0x2E, 0x00, 0x00, 0x10, 0x30, 0x48, 0x00,\
- 0x56, 0x4A, 0x3A, 0xFF, 0xFF, 0x16, 0x00, 0x00, 0x00, 0x00,\
- 0x00, 0x01, 0x1B, 0x14, 0x0D, 0x19, 0x00, 0x00, 0x01, 0x00,\
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
- 0x00, 0x00, 0x1A, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0E, 0x0C,\
- 0x0A, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\
- 0xFF, 0xFF, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24, 0x28, 0x29,\
- 0x0C, 0x0A, 0x08, 0x00, 0x02, 0x04, 0x05, 0x06, 0x0E, 0xFF,\
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\
- 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x01\
- }
-
-/* Define your config for Sensor_ID == 1 here, if needed */
-#define CTP_CFG_GROUP2 {\
- }
-
-/* Define your config for Sensor_ID == 2 here, if needed */
-#define CTP_CFG_GROUP3 {\
- }
-
-/* Define your config for Sensor_ID == 3 here, if needed */
-#define CTP_CFG_GROUP4 {\
- }
-
-/* Define your config for Sensor_ID == 4 here, if needed */
-#define CTP_CFG_GROUP5 {\
- }
-
-/* Define your config for Sensor_ID == 5 here, if needed */
-#define CTP_CFG_GROUP6 {\
- }
#define GTP_IRQ_TAB {\
IRQ_TYPE_EDGE_RISING,\
@@ -197,30 +157,38 @@ extern u16 total_len;
#define GTP_INT_TRIGGER GTP_IRQ_TAB_FALLING
#endif
-#define GTP_MAX_TOUCH 5
-#define GTP_ESD_CHECK_CIRCLE 2000 /* jiffy: ms */
+#define GTP_PRODUCT_ID_MAXSIZE 5
+#define GTP_PRODUCT_ID_BUFFER_MAXSIZE 6
+#define GTP_FW_VERSION_BUFFER_MAXSIZE 4
+#define GTP_MAX_TOUCH 5
+#define GTP_ESD_CHECK_CIRCLE 2000 /* jiffy: ms */
/***************************PART3:OTHER define*********************************/
-#define GTP_DRIVER_VERSION "V1.8<2013/06/08>"
-#define GTP_I2C_NAME "Goodix-TS"
-#define GTP_POLL_TIME 10 /* jiffy: ms*/
-#define GTP_ADDR_LENGTH 2
+#define GTP_DRIVER_VERSION "V1.8.1<2013/09/01>"
+#define GTP_I2C_NAME "Goodix-TS"
+#define GTP_POLL_TIME 10 /* jiffy: ms*/
+#define GTP_ADDR_LENGTH 2
#define GTP_CONFIG_MIN_LENGTH 186
#define GTP_CONFIG_MAX_LENGTH 240
-#define FAIL 0
-#define SUCCESS 1
-#define SWITCH_OFF 0
-#define SWITCH_ON 1
+#define FAIL 0
+#define SUCCESS 1
+#define SWITCH_OFF 0
+#define SWITCH_ON 1
/* Registers define */
-#define GTP_READ_COOR_ADDR 0x814E
-#define GTP_REG_SLEEP 0x8040
-#define GTP_REG_SENSOR_ID 0x814A
-#define GTP_REG_CONFIG_DATA 0x8047
-#define GTP_REG_VERSION 0x8140
-
-#define RESOLUTION_LOC 3
-#define TRIGGER_LOC 8
+#define GTP_READ_COOR_ADDR 0x814E
+#define GTP_REG_SLEEP 0x8040
+#define GTP_REG_SENSOR_ID 0x814A
+#define GTP_REG_CONFIG_DATA 0x8047
+#define GTP_REG_FW_VERSION 0x8144
+#define GTP_REG_PRODUCT_ID 0x8140
+
+#define GTP_I2C_RETRY_3 3
+#define GTP_I2C_RETRY_5 5
+#define GTP_I2C_RETRY_10 10
+
+#define RESOLUTION_LOC 3
+#define TRIGGER_LOC 8
/* Log define */
#define GTP_DEBUG(fmt, arg...) do {\
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
index 29459b6d9ad1..9fcf7f0bef86 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
@@ -109,24 +109,25 @@ Output:
*********************************************************/
s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
{
- struct i2c_msg msgs[2];
s32 ret = -1;
- s32 retries = 0;
+ u8 retries = 0;
+ struct i2c_msg msgs[2] = {
+ {
+ .flags = !I2C_M_RD,
+ .addr = client->addr,
+ .len = GTP_ADDR_LENGTH,
+ .buf = &buf[0],
+ },
+ {
+ .flags = I2C_M_RD,
+ .addr = client->addr,
+ .len = len - GTP_ADDR_LENGTH,
+ .buf = &buf[GTP_ADDR_LENGTH],
+ },
+ };
GTP_DEBUG_FUNC();
- msgs[0].flags = !I2C_M_RD;
- msgs[0].addr = client->addr;
- msgs[0].len = GTP_ADDR_LENGTH;
- msgs[0].buf = &buf[0];
- /* msgs[0].scl_rate = 300 * 1000; (for Rockchip) */
-
- msgs[1].flags = I2C_M_RD;
- msgs[1].addr = client->addr;
- msgs[1].len = len - GTP_ADDR_LENGTH;
- msgs[1].buf = &buf[GTP_ADDR_LENGTH];
- /* msgs[1].scl_rate = 300 * 1000; */
-
while (retries < 5) {
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret == 2)
@@ -134,6 +135,11 @@ s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
retries++;
}
+ if (retries == 5) {
+ dev_err(&client->dev, "I2C read retry limit over.\n");
+ ret = -EIO;
+ }
+
return ret;
}
@@ -151,18 +157,17 @@ Output:
*********************************************************/
s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len)
{
- struct i2c_msg msg;
s32 ret = -1;
- s32 retries = 0;
+ u8 retries = 0;
+ struct i2c_msg msg = {
+ .flags = !I2C_M_RD,
+ .addr = client->addr,
+ .len = len,
+ .buf = buf,
+ };
GTP_DEBUG_FUNC();
- msg.flags = !I2C_M_RD;
- msg.addr = client->addr;
- msg.len = len;
- msg.buf = buf;
- /* msg.scl_rate = 300 * 1000; (for Rockchip) */
-
while (retries < 5) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret == 1)
@@ -170,6 +175,11 @@ s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len)
retries++;
}
+ if (retries == 5) {
+ dev_err(&client->dev, "I2C write retry limit over.\n");
+ ret = -EIO;
+ }
+
return ret;
}
@@ -288,7 +298,7 @@ static s32 gup_init_panel(struct goodix_ts_data *ts)
static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len)
{
- s32 i = 0;
+ u8 i = 0;
msg[0] = (addr >> 8) & 0xff;
msg[1] = addr & 0xff;
@@ -307,12 +317,12 @@ static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len)
static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val)
{
- s32 i = 0;
- u8 msg[3];
-
- msg[0] = (addr >> 8) & 0xff;
- msg[1] = addr & 0xff;
- msg[2] = val;
+ u8 i = 0;
+ u8 msg[3] = {
+ (addr >> 8) & 0xff,
+ addr & 0xff,
+ val,
+ };
for (i = 0; i < 5; i++)
if (gup_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0)
@@ -411,7 +421,7 @@ static u8 gup_get_ic_fw_msg(struct i2c_client *client)
s32 gup_enter_update_mode(struct i2c_client *client)
{
s32 ret = -1;
- s32 retry = 0;
+ u8 retry = 0;
u8 rd_buf[3];
/* step1:RST output low last at least 2ms */
@@ -575,11 +585,11 @@ static u8 ascii2hex(u8 a)
static s8 gup_update_config(struct i2c_client *client)
{
- s32 file_len = 0;
+ u32 file_len = 0;
s32 ret = 0;
s32 i = 0;
s32 file_cfg_len = 0;
- s32 chip_cfg_len = 0;
+ u32 chip_cfg_len = 0;
s32 count = 0;
u8 *buf;
u8 *pre_buf;
@@ -615,9 +625,19 @@ static s8 gup_update_config(struct i2c_client *client)
return -EINVAL;
}
- buf = kzalloc(file_len, GFP_KERNEL);
- pre_buf = kzalloc(file_len, GFP_KERNEL);
- file_config = kzalloc(chip_cfg_len + GTP_ADDR_LENGTH, GFP_KERNEL);
+ buf = devm_kzalloc(&client->dev, file_len, 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.");
@@ -625,7 +645,7 @@ static s8 gup_update_config(struct i2c_client *client)
(char *)pre_buf, file_len, &update_msg.cfg_file->f_pos);
if (ret < 0) {
GTP_ERROR("[update_cfg]Read config file failed.");
- goto update_cfg_file_failed;
+ return ret;
}
GTP_DEBUG("[update_cfg]Delete illegal character.");
@@ -650,13 +670,13 @@ static s8 gup_update_config(struct i2c_client *client)
if ((high == 0xFF) || (low == 0xFF)) {
ret = 0;
GTP_ERROR("[update_cfg]Illegal config file.");
- goto update_cfg_file_failed;
+ return ret;
}
file_config[file_cfg_len++] = (high<<4) + low;
} else {
ret = 0;
GTP_ERROR("[update_cfg]Illegal config file.");
- goto update_cfg_file_failed;
+ return ret;
}
}
@@ -680,10 +700,6 @@ static s8 gup_update_config(struct i2c_client *client)
GTP_ERROR("[update_cfg]Send config i2c error.");
}
-update_cfg_file_failed:
- kfree(pre_buf);
- kfree(buf);
- kfree(file_config);
return ret;
}
@@ -787,16 +803,22 @@ static u8 gup_check_update_file(struct i2c_client *client, st_fw_head *fw_head,
sizeof(UPDATE_FILE_PATH_2));
u8 cfp_len = max(sizeof(CONFIG_FILE_PATH_1),
sizeof(CONFIG_FILE_PATH_2));
- u8 *search_update_path = kzalloc(fp_len, GFP_KERNEL);
- u8 *search_cfg_path = kzalloc(cfp_len, GFP_KERNEL);
+
+ 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) {
- kfree(search_update_path);
- kfree(search_cfg_path);
GTP_INFO(".bin/.cfg update file search ",
"forcely terminated!");
return FAIL;
@@ -843,8 +865,6 @@ static u8 gup_check_update_file(struct i2c_client *client, st_fw_head *fw_head,
}
searching_file = 0;
- kfree(search_update_path);
- kfree(search_cfg_path);
if (!got_file_flag) {
GTP_ERROR("Can't find update file.");
@@ -1168,7 +1188,8 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
/* step1:alloc memory */
GTP_DEBUG("[burn_dsp_isp]step1:alloc memory");
while (retry++ < 5) {
- fw_dsp_isp = kzalloc(FW_DSP_ISP_LENGTH, GFP_KERNEL);
+ fw_dsp_isp = devm_kzalloc(&client->dev, FW_DSP_ISP_LENGTH,
+ GFP_KERNEL);
if (fw_dsp_isp == NULL) {
continue;
} else {
@@ -1177,7 +1198,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
break;
}
}
- if (retry >= 5) {
+ if (retry == 5) {
GTP_ERROR("[burn_dsp_isp]Alloc memory fail,exit.");
return FAIL;
}
@@ -1188,7 +1209,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
FW_DSP_LENGTH + FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH);
if (ret == FAIL) {
GTP_ERROR("[burn_dsp_isp]load firmware dsp_isp fail.");
- goto exit_burn_dsp_isp;
+ return FAIL;
}
/* step3:disable wdt,clear cache enable */
@@ -1196,14 +1217,12 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00);
if (ret <= 0) {
GTP_ERROR("[burn_dsp_isp]disable wdt fail.");
- ret = FAIL;
- goto exit_burn_dsp_isp;
+ 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.");
- ret = FAIL;
- goto exit_burn_dsp_isp;
+ return FAIL;
}
/* step4:hold ss51 & dsp */
@@ -1211,8 +1230,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
if (ret <= 0) {
GTP_ERROR("[burn_dsp_isp]hold ss51 & dsp fail.");
- ret = FAIL;
- goto exit_burn_dsp_isp;
+ return FAIL;
}
/* step5:set boot from sram */
@@ -1220,8 +1238,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02);
if (ret <= 0) {
GTP_ERROR("[burn_dsp_isp]set boot from sram fail.");
- ret = FAIL;
- goto exit_burn_dsp_isp;
+ return FAIL;
}
/* step6:software reboot */
@@ -1229,8 +1246,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01);
if (ret <= 0) {
GTP_ERROR("[burn_dsp_isp]software reboot fail.");
- ret = FAIL;
- goto exit_burn_dsp_isp;
+ return FAIL;
}
/* step7:select bank2 */
@@ -1238,8 +1254,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02);
if (ret <= 0) {
GTP_ERROR("[burn_dsp_isp]select bank2 fail.");
- ret = FAIL;
- goto exit_burn_dsp_isp;
+ return FAIL;
}
/* step8:enable accessing code */
@@ -1247,8 +1262,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
if (ret <= 0) {
GTP_ERROR("[burn_dsp_isp]enable accessing code fail.");
- ret = FAIL;
- goto exit_burn_dsp_isp;
+ return FAIL;
}
/* step9:burn 4k dsp_isp */
@@ -1256,7 +1270,7 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
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.");
- goto exit_burn_dsp_isp;
+ return FAIL;
}
/* step10:set scramble */
@@ -1264,14 +1278,10 @@ static u8 gup_burn_dsp_isp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
if (ret <= 0) {
GTP_ERROR("[burn_dsp_isp]set scramble fail.");
- ret = FAIL;
- goto exit_burn_dsp_isp;
+ return FAIL;
}
- ret = SUCCESS;
-exit_burn_dsp_isp:
- kfree(fw_dsp_isp);
- return ret;
+ return SUCCESS;
}
static u8 gup_burn_fw_ss51(struct i2c_client *client)
@@ -1285,7 +1295,8 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
/* step1:alloc memory */
GTP_DEBUG("[burn_fw_ss51]step1:alloc memory");
while (retry++ < 5) {
- fw_ss51 = kzalloc(FW_SECTION_LENGTH, GFP_KERNEL);
+ fw_ss51 = devm_kzalloc(&client->dev, FW_SECTION_LENGTH,
+ GFP_KERNEL);
if (fw_ss51 == NULL) {
continue;
} else {
@@ -1294,7 +1305,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
break;
}
}
- if (retry >= 5) {
+ if (retry == 5) {
GTP_ERROR("[burn_fw_ss51]Alloc memory fail,exit.");
return FAIL;
}
@@ -1304,7 +1315,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
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.");
- goto exit_burn_fw_ss51;
+ return FAIL;
}
/* step3:clear control flag */
@@ -1312,8 +1323,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00);
if (ret <= 0) {
GTP_ERROR("[burn_fw_ss51]clear control flag fail.");
- ret = FAIL;
- goto exit_burn_fw_ss51;
+ return FAIL;
}
/* step4:burn ss51 firmware section 1 */
@@ -1321,7 +1331,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01);
if (ret == FAIL) {
GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 1 fail.");
- goto exit_burn_fw_ss51;
+ return FAIL;
}
/* step5:load ss51 firmware section 2 file data */
@@ -1330,7 +1340,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
FW_SECTION_LENGTH);
if (ret == FAIL) {
GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 2 fail.");
- goto exit_burn_fw_ss51;
+ return FAIL;
}
/* step6:burn ss51 firmware section 2 */
@@ -1338,7 +1348,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02);
if (ret == FAIL) {
GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 2 fail.");
- goto exit_burn_fw_ss51;
+ return FAIL;
}
/* step7:load ss51 firmware section 3 file data */
@@ -1347,7 +1357,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
FW_SECTION_LENGTH);
if (ret == FAIL) {
GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 3 fail.");
- goto exit_burn_fw_ss51;
+ return FAIL;
}
/* step8:burn ss51 firmware section 3 */
@@ -1355,7 +1365,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13);
if (ret == FAIL) {
GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 3 fail.");
- goto exit_burn_fw_ss51;
+ return FAIL;
}
/* step9:load ss51 firmware section 4 file data */
@@ -1364,7 +1374,7 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
FW_SECTION_LENGTH);
if (ret == FAIL) {
GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 4 fail.");
- goto exit_burn_fw_ss51;
+ return FAIL;
}
/* step10:burn ss51 firmware section 4 */
@@ -1372,14 +1382,10 @@ static u8 gup_burn_fw_ss51(struct i2c_client *client)
ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14);
if (ret == FAIL) {
GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 4 fail.");
- goto exit_burn_fw_ss51;
+ return FAIL;
}
- ret = SUCCESS;
-
-exit_burn_fw_ss51:
- kfree(fw_ss51);
- return ret;
+ return SUCCESS;
}
static u8 gup_burn_fw_dsp(struct i2c_client *client)
@@ -1393,7 +1399,8 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
/* step1:alloc memory */
GTP_DEBUG("[burn_fw_dsp]step1:alloc memory");
while (retry++ < 5) {
- fw_dsp = kzalloc(FW_DSP_LENGTH, GFP_KERNEL);
+ fw_dsp = devm_kzalloc(&client->dev, FW_DSP_LENGTH,
+ GFP_KERNEL);
if (fw_dsp == NULL) {
continue;
} else {
@@ -1402,7 +1409,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
break;
}
}
- if (retry >= 5) {
+ if (retry == 5) {
GTP_ERROR("[burn_fw_dsp]Alloc memory fail,exit.");
return FAIL;
}
@@ -1412,7 +1419,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
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.");
- goto exit_burn_fw_dsp;
+ return ret;
}
/* step3:select bank3 */
@@ -1420,8 +1427,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
if (ret <= 0) {
GTP_ERROR("[burn_fw_dsp]select bank3 fail.");
- ret = FAIL;
- goto exit_burn_fw_dsp;
+ return FAIL;
}
/* Step4:hold ss51 & dsp */
@@ -1429,8 +1435,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
if (ret <= 0) {
GTP_ERROR("[burn_fw_dsp]hold ss51 & dsp fail.");
- ret = FAIL;
- goto exit_burn_fw_dsp;
+ return FAIL;
}
/* step5:set scramble */
@@ -1438,8 +1443,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
if (ret <= 0) {
GTP_ERROR("[burn_fw_dsp]set scramble fail.");
- ret = FAIL;
- goto exit_burn_fw_dsp;
+ return FAIL;
}
/* step6:release ss51 & dsp */
@@ -1447,8 +1451,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
if (ret <= 0) {
GTP_ERROR("[burn_fw_dsp]release ss51 & dsp fail.");
- ret = FAIL;
- goto exit_burn_fw_dsp;
+ return FAIL;
}
/* must delay */
msleep(20);
@@ -1458,7 +1461,7 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
if (ret == FAIL) {
GTP_ERROR("[burn_fw_dsp]burn fw_section fail.");
- goto exit_burn_fw_dsp;
+ return ret;
}
/* step8:send burn cmd to move data to flash from sram */
@@ -1467,14 +1470,14 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05);
if (ret <= 0) {
GTP_ERROR("[burn_fw_dsp]send burn cmd fail.");
- goto exit_burn_fw_dsp;
+ return ret;
}
GTP_DEBUG("[burn_fw_dsp]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");
- goto exit_burn_fw_dsp;
+ return ret;
}
msleep(20);
/* GTP_DEBUG("[burn_fw_dsp]Get burn state:%d.",
@@ -1487,14 +1490,10 @@ static u8 gup_burn_fw_dsp(struct i2c_client *client)
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.");
- goto exit_burn_fw_dsp;
+ return ret;
}
ret = SUCCESS;
-
-exit_burn_fw_dsp:
- kfree(fw_dsp);
- return ret;
}
static u8 gup_burn_fw_boot(struct i2c_client *client)
@@ -1509,7 +1508,8 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
/* step1:Alloc memory */
GTP_DEBUG("[burn_fw_boot]step1:Alloc memory");
while (retry++ < 5) {
- fw_boot = kzalloc(FW_BOOT_LENGTH, GFP_KERNEL);
+ fw_boot = devm_kzalloc(&client->dev, FW_BOOT_LENGTH,
+ GFP_KERNEL);
if (fw_boot == NULL) {
continue;
} else {
@@ -1518,7 +1518,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
break;
}
}
- if (retry >= 5) {
+ if (retry == 5) {
GTP_ERROR("[burn_fw_boot]Alloc memory fail,exit.");
return FAIL;
}
@@ -1529,7 +1529,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
FW_DSP_LENGTH), FW_BOOT_LENGTH);
if (ret == FAIL) {
GTP_ERROR("[burn_fw_boot]load firmware dsp fail.");
- goto exit_burn_fw_boot;
+ return ret;
}
/* step3:hold ss51 & dsp */
@@ -1537,8 +1537,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
if (ret <= 0) {
GTP_ERROR("[burn_fw_boot]hold ss51 & dsp fail.");
- ret = FAIL;
- goto exit_burn_fw_boot;
+ return FAIL;
}
/* step4:set scramble */
@@ -1546,8 +1545,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
if (ret <= 0) {
GTP_ERROR("[burn_fw_boot]set scramble fail.");
- ret = FAIL;
- goto exit_burn_fw_boot;
+ return FAIL;
}
/* step5:release ss51 & dsp */
@@ -1555,8 +1553,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
if (ret <= 0) {
GTP_ERROR("[burn_fw_boot]release ss51 & dsp fail.");
- ret = FAIL;
- goto exit_burn_fw_boot;
+ return FAIL;
}
/* must delay */
msleep(20);
@@ -1566,8 +1563,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
if (ret <= 0) {
GTP_ERROR("[burn_fw_boot]select bank3 fail.");
- ret = FAIL;
- goto exit_burn_fw_boot;
+ return FAIL;
}
/* step7:burn 2k bootloader firmware */
@@ -1575,7 +1571,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
if (ret == FAIL) {
GTP_ERROR("[burn_fw_boot]burn fw_section fail.");
- goto exit_burn_fw_boot;
+ return ret;
}
/* step7:send burn cmd to move data to flash from sram */
@@ -1584,14 +1580,14 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06);
if (ret <= 0) {
GTP_ERROR("[burn_fw_boot]send burn cmd fail.");
- goto exit_burn_fw_boot;
+ return ret;
}
GTP_DEBUG("[burn_fw_boot]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");
- goto exit_burn_fw_boot;
+ return ret;
}
msleep(20);
/* GTP_DEBUG("[burn_fw_boot]Get burn state:%d.",
@@ -1604,7 +1600,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
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.");
- goto exit_burn_fw_boot;
+ return ret;
}
/* step9:enable download DSP code */
@@ -1612,8 +1608,7 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99);
if (ret <= 0) {
GTP_ERROR("[burn_fw_boot]enable download DSP code fail.");
- ret = FAIL;
- goto exit_burn_fw_boot;
+ return FAIL;
}
/* step10:release ss51 & hold dsp */
@@ -1621,15 +1616,10 @@ static u8 gup_burn_fw_boot(struct i2c_client *client)
ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08);
if (ret <= 0) {
GTP_ERROR("[burn_fw_boot]release ss51 & hold dsp fail.");
- ret = FAIL;
- goto exit_burn_fw_boot;
+ return FAIL;
}
- ret = SUCCESS;
-
-exit_burn_fw_boot:
- kfree(fw_boot);
- return ret;
+ return SUCCESS;
}
s32 gup_update_proc(void *dir)
diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c
index 88b1d3c013c7..1a2afd1c1117 100644
--- a/drivers/input/touchscreen/it7258_ts_i2c.c
+++ b/drivers/input/touchscreen/it7258_ts_i2c.c
@@ -1383,7 +1383,7 @@ static int it7260_ts_chip_identify(struct it7260_ts_data *ts_data)
static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
{
return (regulator_count_voltages(reg) > 0) ?
- regulator_set_optimum_mode(reg, load_uA) : 0;
+ regulator_set_load(reg, load_uA) : 0;
}
static int it7260_regulator_configure(struct it7260_ts_data *ts_data, bool on)
@@ -1950,7 +1950,7 @@ static int it7260_ts_probe(struct i2c_client *client,
dev_err(&client->dev, "Unable to register fb_notifier %d\n",
ret);
#endif
-
+
it7260_i2c_write_no_ready_check(ts_data, BUF_COMMAND, cmd_start,
sizeof(cmd_start));
msleep(pdata->reset_delay);
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
index eb4947536230..d358f329e7a8 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
@@ -117,7 +117,6 @@
static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data,
bool *was_in_bl_mode);
static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data);
-static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data);
static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data,
bool rebuild);
@@ -652,8 +651,6 @@ static struct kobj_attribute virtual_key_map_attr = {
#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
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);
@@ -661,24 +658,21 @@ static void synaptics_secure_touch_init(struct synaptics_rmi4_data *data)
/* Get clocks */
data->core_clk = devm_clk_get(data->pdev->dev.parent, "core_clk");
if (IS_ERR(data->core_clk)) {
- ret = PTR_ERR(data->core_clk);
- data->core_clk = NULL;
dev_warn(data->pdev->dev.parent,
- "%s: error on clk_get(core_clk): %d\n", __func__, ret);
- return;
+ "%s: error on clk_get(core_clk): %ld\n", __func__,
+ PTR_ERR(data->core_clk));
+ data->core_clk = NULL;
}
data->iface_clk = devm_clk_get(data->pdev->dev.parent, "iface_clk");
if (IS_ERR(data->iface_clk)) {
- ret = PTR_ERR(data->iface_clk);
- data->iface_clk = NULL;
dev_warn(data->pdev->dev.parent,
- "%s: error on clk_get(iface_clk): %d\n", __func__, ret);
- return;
+ "%s: error on clk_get(iface_clk): %ld\n", __func__,
+ PTR_ERR(data->iface_clk));
+ data->iface_clk = NULL;
}
data->st_initialized = 1;
- return;
}
static void synaptics_secure_touch_notify(struct synaptics_rmi4_data *rmi4_data)
@@ -1691,12 +1685,6 @@ static void synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data,
}
if (status.unconfigured && !status.flash_prog) {
pr_notice("%s: spontaneous reset detected\n", __func__);
- retval = synaptics_rmi4_reinit_device(rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to reinit device\n",
- __func__);
- }
}
if (!report)
@@ -3665,49 +3653,6 @@ exit:
return;
}
-static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
- struct synaptics_rmi4_device_info *rmi;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
- mutex_lock(&(rmi4_data->rmi4_reset_mutex));
-
- synaptics_rmi4_free_fingers(rmi4_data);
-
- if (!list_empty(&rmi->support_fn_list)) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F12) {
- synaptics_rmi4_f12_set_enables(rmi4_data, 0);
- break;
- }
- }
- }
-
- retval = synaptics_rmi4_int_enable(rmi4_data, true);
- if (retval < 0)
- goto exit;
-
- mutex_lock(&exp_data.mutex);
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link)
- if (exp_fhandler->exp_fn->reinit != NULL)
- exp_fhandler->exp_fn->reinit(rmi4_data);
- }
- mutex_unlock(&exp_data.mutex);
-
- synaptics_rmi4_set_configured(rmi4_data);
-
- retval = 0;
-
-exit:
- mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
- return retval;
-}
-
static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data,
bool rebuild)
{
@@ -3919,6 +3864,57 @@ exit:
}
EXPORT_SYMBOL(synaptics_rmi4_new_function);
+static int synaptics_dsx_pinctrl_init(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+
+ /* Get pinctrl if target uses pinctrl */
+ rmi4_data->ts_pinctrl = devm_pinctrl_get((rmi4_data->pdev->dev.parent));
+ if (IS_ERR_OR_NULL(rmi4_data->ts_pinctrl)) {
+ retval = PTR_ERR(rmi4_data->ts_pinctrl);
+ dev_err(rmi4_data->pdev->dev.parent,
+ "Target does not use pinctrl %d\n", retval);
+ goto err_pinctrl_get;
+ }
+
+ rmi4_data->pinctrl_state_active
+ = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_active");
+ if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_active)) {
+ retval = PTR_ERR(rmi4_data->pinctrl_state_active);
+ dev_err(rmi4_data->pdev->dev.parent,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_ACTIVE, retval);
+ goto err_pinctrl_lookup;
+ }
+
+ rmi4_data->pinctrl_state_suspend
+ = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_suspend");
+ if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_suspend)) {
+ retval = PTR_ERR(rmi4_data->pinctrl_state_suspend);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_SUSPEND, retval);
+ goto err_pinctrl_lookup;
+ }
+
+ rmi4_data->pinctrl_state_release
+ = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_release");
+ if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) {
+ retval = PTR_ERR(rmi4_data->pinctrl_state_release);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_RELEASE, retval);
+ }
+
+ return 0;
+
+err_pinctrl_lookup:
+ devm_pinctrl_put(rmi4_data->ts_pinctrl);
+err_pinctrl_get:
+ rmi4_data->ts_pinctrl = NULL;
+ return retval;
+}
+
static int synaptics_rmi4_probe(struct platform_device *pdev)
{
int retval;
@@ -3988,6 +3984,21 @@ static int synaptics_rmi4_probe(struct platform_device *pdev)
goto err_enable_reg;
}
+ retval = synaptics_dsx_pinctrl_init(rmi4_data);
+ if (!retval && rmi4_data->ts_pinctrl) {
+ /*
+ * Pinctrl handle is optional. If pinctrl handle is found
+ * let pins to be configured in active state. If not
+ * found continue further without error.
+ */
+ retval = pinctrl_select_state(rmi4_data->ts_pinctrl,
+ rmi4_data->pinctrl_state_active);
+ if (retval < 0) {
+ dev_err(&pdev->dev,
+ "%s: Failed to select %s pinstate %d\n",
+ __func__, PINCTRL_STATE_ACTIVE, retval);
+ }
+ }
retval = synaptics_rmi4_set_gpio(rmi4_data);
if (retval < 0) {
dev_err(&pdev->dev,
@@ -4173,6 +4184,21 @@ err_ui_hw_init:
err_set_gpio:
synaptics_rmi4_enable_reg(rmi4_data, false);
+ if (rmi4_data->ts_pinctrl) {
+ if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) {
+ devm_pinctrl_put(rmi4_data->ts_pinctrl);
+ rmi4_data->ts_pinctrl = NULL;
+ } else {
+ retval = pinctrl_select_state(
+ rmi4_data->ts_pinctrl,
+ rmi4_data->pinctrl_state_release);
+ if (retval)
+ dev_err(&pdev->dev,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ }
+ }
+
err_enable_reg:
synaptics_rmi4_get_reg(rmi4_data, false);
@@ -4185,6 +4211,7 @@ err_get_reg:
static int synaptics_rmi4_remove(struct platform_device *pdev)
{
unsigned char attr_count;
+ int err;
struct synaptics_rmi4_data *rmi4_data = platform_get_drvdata(pdev);
const struct synaptics_dsx_board_data *bdata =
rmi4_data->hw_if->board_data;
@@ -4240,6 +4267,22 @@ static int synaptics_rmi4_remove(struct platform_device *pdev)
if (bdata->power_gpio >= 0)
synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0);
+
+ if (rmi4_data->ts_pinctrl) {
+ if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) {
+ devm_pinctrl_put(rmi4_data->ts_pinctrl);
+ rmi4_data->ts_pinctrl = NULL;
+ } else {
+ err = pinctrl_select_state(
+ rmi4_data->ts_pinctrl,
+ rmi4_data->pinctrl_state_release);
+ if (err)
+ dev_err(&pdev->dev,
+ "Failed to select release pinctrl state %d\n",
+ err);
+ }
+ }
+
synaptics_rmi4_enable_reg(rmi4_data, false);
synaptics_rmi4_get_reg(rmi4_data, false);
@@ -4512,6 +4555,7 @@ static int synaptics_rmi4_suspend(struct device *dev)
{
struct synaptics_rmi4_exp_fhandler *exp_fhandler;
struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ int retval;
if (rmi4_data->stay_awake)
return 0;
@@ -4530,6 +4574,13 @@ static int synaptics_rmi4_suspend(struct device *dev)
synaptics_rmi4_free_fingers(rmi4_data);
}
+ if (rmi4_data->ts_pinctrl) {
+ retval = pinctrl_select_state(rmi4_data->ts_pinctrl,
+ rmi4_data->pinctrl_state_suspend);
+ if (retval < 0)
+ dev_err(dev, "Cannot get idle pinctrl state\n");
+ goto err_pinctrl;
+ }
exit:
mutex_lock(&exp_data.mutex);
if (!list_empty(&exp_data.list)) {
@@ -4546,6 +4597,12 @@ exit:
rmi4_data->suspend = true;
return 0;
+
+err_pinctrl:
+ synaptics_rmi4_sleep_enable(rmi4_data, false);
+ synaptics_rmi4_irq_enable(rmi4_data, true, false);
+ return retval;
+
}
static int synaptics_rmi4_resume(struct device *dev)
@@ -4576,6 +4633,12 @@ static int synaptics_rmi4_resume(struct device *dev)
synaptics_rmi4_sleep_enable(rmi4_data, false);
synaptics_rmi4_irq_enable(rmi4_data, true, false);
+ if (rmi4_data->ts_pinctrl) {
+ retval = pinctrl_select_state(rmi4_data->ts_pinctrl,
+ rmi4_data->pinctrl_state_active);
+ if (retval < 0)
+ dev_err(dev, "Cannot get default pinctrl state\n");
+ }
exit:
#ifdef FB_READY_RESET
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
index 6fa392473885..7d92791afb25 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
@@ -122,6 +122,9 @@
#define MASK_2BIT 0x03
#define MASK_1BIT 0x01
+#define PINCTRL_STATE_ACTIVE "pmx_ts_active"
+#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend"
+#define PINCTRL_STATE_RELEASE "pmx_ts_release"
enum exp_fn {
RMI_DEV = 0,
RMI_FW_UPDATER,
@@ -382,6 +385,10 @@ struct synaptics_rmi4_data {
bool enable);
void (*report_touch)(struct synaptics_rmi4_data *rmi4_data,
struct synaptics_rmi4_fn *fhandler);
+ struct pinctrl *ts_pinctrl;
+ struct pinctrl_state *pinctrl_state_active;
+ struct pinctrl_state *pinctrl_state_suspend;
+ struct pinctrl_state *pinctrl_state_release;
#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
atomic_t st_enabled;
atomic_t st_pending_irqs;
diff --git a/drivers/iommu/msm_dma_iommu_mapping.c b/drivers/iommu/msm_dma_iommu_mapping.c
index a035ebd5af7e..334f4e95c068 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))
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 190d294197a7..0608e08b4427 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -96,7 +96,7 @@ static void gic_do_wait_for_rwp(void __iomem *base)
{
u32 count = 1000000; /* 1s! */
- while (readl_relaxed(base + GICD_CTLR) & GICD_CTLR_RWP) {
+ while (readl_relaxed_no_log(base + GICD_CTLR) & GICD_CTLR_RWP) {
count--;
if (!count) {
pr_err_ratelimited("RWP timeout, gone fishing\n");
@@ -182,7 +182,7 @@ static int gic_peek_irq(struct irq_data *d, u32 offset)
else
base = gic_data.dist_base;
- return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask);
+ return !!(readl_relaxed_no_log(base + offset + (gic_irq(d) / 32) * 4) & mask);
}
static void gic_poke_irq(struct irq_data *d, u32 offset)
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 8810c9fb981a..998bd1ec0415 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -475,6 +475,18 @@ config DM_VERITY
If unsure, say N.
+config DM_VERITY_FEC
+ bool "Verity forward error correction support"
+ depends on DM_VERITY
+ select REED_SOLOMON
+ select REED_SOLOMON_DEC8
+ ---help---
+ Add forward error correction support to dm-verity. This option
+ makes it possible to use pre-generated error correction data to
+ recover from corrupted blocks.
+
+ If unsure, say N.
+
config DM_SWITCH
tristate "Switch target support (EXPERIMENTAL)"
depends on BLK_DEV_DM
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 02de7d19853e..d470143dcf40 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -16,6 +16,7 @@ dm-cache-mq-y += dm-cache-policy-mq.o
dm-cache-smq-y += dm-cache-policy-smq.o
dm-cache-cleaner-y += dm-cache-policy-cleaner.o
dm-era-y += dm-era-target.o
+dm-verity-y += dm-verity-target.o
md-mod-y += md.o bitmap.o
raid456-y += raid5.o raid5-cache.o
@@ -64,3 +65,7 @@ obj-$(CONFIG_DM_REQ_CRYPT) += dm-req-crypt.o
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
endif
+
+ifeq ($(CONFIG_DM_VERITY_FEC),y)
+dm-verity-objs += dm-verity-fec.o
+endif
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 515f83e7d9ab..bb9b92ebbf8e 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -118,14 +118,12 @@ static void iot_io_end(struct io_tracker *iot, sector_t len)
*/
struct dm_hook_info {
bio_end_io_t *bi_end_io;
- void *bi_private;
};
static void dm_hook_bio(struct dm_hook_info *h, struct bio *bio,
bio_end_io_t *bi_end_io, void *bi_private)
{
h->bi_end_io = bio->bi_end_io;
- h->bi_private = bio->bi_private;
bio->bi_end_io = bi_end_io;
bio->bi_private = bi_private;
@@ -134,7 +132,6 @@ static void dm_hook_bio(struct dm_hook_info *h, struct bio *bio,
static void dm_unhook_bio(struct dm_hook_info *h, struct bio *bio)
{
bio->bi_end_io = h->bi_end_io;
- bio->bi_private = h->bi_private;
}
/*----------------------------------------------------------------*/
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 3147c8d09ea8..e85bcae50f65 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1864,16 +1864,24 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
ret = -ENOMEM;
- cc->io_queue = alloc_workqueue("kcryptd_io", WQ_MEM_RECLAIM, 1);
+ cc->io_queue = alloc_workqueue("kcryptd_io",
+ WQ_HIGHPRI |
+ WQ_MEM_RECLAIM,
+ 1);
if (!cc->io_queue) {
ti->error = "Couldn't create kcryptd io queue";
goto bad;
}
if (test_bit(DM_CRYPT_SAME_CPU, &cc->flags))
- cc->crypt_queue = alloc_workqueue("kcryptd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM, 1);
+ cc->crypt_queue = alloc_workqueue("kcryptd",
+ WQ_HIGHPRI |
+ WQ_MEM_RECLAIM, 1);
else
- cc->crypt_queue = alloc_workqueue("kcryptd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND,
+ cc->crypt_queue = alloc_workqueue("kcryptd",
+ WQ_HIGHPRI |
+ WQ_MEM_RECLAIM |
+ WQ_UNBOUND,
num_online_cpus());
if (!cc->crypt_queue) {
ti->error = "Couldn't create kcryptd queue";
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index e108deebbaaa..e4d1bafe78c1 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -207,7 +207,6 @@ struct dm_snap_pending_exception {
*/
struct bio *full_bio;
bio_end_io_t *full_bio_end_io;
- void *full_bio_private;
};
/*
@@ -1495,10 +1494,8 @@ out:
snapshot_bios = bio_list_get(&pe->snapshot_bios);
origin_bios = bio_list_get(&pe->origin_bios);
full_bio = pe->full_bio;
- if (full_bio) {
+ if (full_bio)
full_bio->bi_end_io = pe->full_bio_end_io;
- full_bio->bi_private = pe->full_bio_private;
- }
increment_pending_exceptions_done_count();
up_write(&s->lock);
@@ -1604,7 +1601,6 @@ static void start_full_bio(struct dm_snap_pending_exception *pe,
pe->full_bio = bio;
pe->full_bio_end_io = bio->bi_end_io;
- pe->full_bio_private = bio->bi_private;
callback_data = dm_kcopyd_prepare_callback(s->kcopyd_client,
copy_callback, pe);
diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c
new file mode 100644
index 000000000000..ad10d6d8ed28
--- /dev/null
+++ b/drivers/md/dm-verity-fec.c
@@ -0,0 +1,861 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Author: Sami Tolvanen <samitolvanen@google.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include "dm-verity-fec.h"
+#include <linux/math64.h>
+#include <linux/sysfs.h>
+
+#define DM_MSG_PREFIX "verity-fec"
+
+/*
+ * If error correction has been configured, returns true.
+ */
+bool verity_fec_is_enabled(struct dm_verity *v)
+{
+ return v->fec && v->fec->dev;
+}
+
+/*
+ * Return a pointer to dm_verity_fec_io after dm_verity_io and its variable
+ * length fields.
+ */
+static inline struct dm_verity_fec_io *fec_io(struct dm_verity_io *io)
+{
+ return (struct dm_verity_fec_io *) verity_io_digest_end(io->v, io);
+}
+
+/*
+ * Return an interleaved offset for a byte in RS block.
+ */
+static inline u64 fec_interleave(struct dm_verity *v, u64 offset)
+{
+ u32 mod;
+
+ mod = do_div(offset, v->fec->rsn);
+ return offset + mod * (v->fec->rounds << v->data_dev_block_bits);
+}
+
+/*
+ * Decode an RS block using Reed-Solomon.
+ */
+static int fec_decode_rs8(struct dm_verity *v, struct dm_verity_fec_io *fio,
+ u8 *data, u8 *fec, int neras)
+{
+ int i;
+ uint16_t par[DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN];
+
+ for (i = 0; i < v->fec->roots; i++)
+ par[i] = fec[i];
+
+ return decode_rs8(fio->rs, data, par, v->fec->rsn, NULL, neras,
+ fio->erasures, 0, NULL);
+}
+
+/*
+ * Read error-correcting codes for the requested RS block. Returns a pointer
+ * to the data block. Caller is responsible for releasing buf.
+ */
+static u8 *fec_read_parity(struct dm_verity *v, u64 rsb, int index,
+ unsigned *offset, struct dm_buffer **buf)
+{
+ u64 position, block;
+ u8 *res;
+
+ position = (index + rsb) * v->fec->roots;
+ block = position >> v->data_dev_block_bits;
+ *offset = (unsigned)(position - (block << v->data_dev_block_bits));
+
+ res = dm_bufio_read(v->fec->bufio, v->fec->start + block, buf);
+ if (unlikely(IS_ERR(res))) {
+ DMERR("%s: FEC %llu: parity read failed (block %llu): %ld",
+ v->data_dev->name, (unsigned long long)rsb,
+ (unsigned long long)(v->fec->start + block),
+ PTR_ERR(res));
+ *buf = NULL;
+ }
+
+ return res;
+}
+
+/* Loop over each preallocated buffer slot. */
+#define fec_for_each_prealloc_buffer(__i) \
+ for (__i = 0; __i < DM_VERITY_FEC_BUF_PREALLOC; __i++)
+
+/* Loop over each extra buffer slot. */
+#define fec_for_each_extra_buffer(io, __i) \
+ for (__i = DM_VERITY_FEC_BUF_PREALLOC; __i < DM_VERITY_FEC_BUF_MAX; __i++)
+
+/* Loop over each allocated buffer. */
+#define fec_for_each_buffer(io, __i) \
+ for (__i = 0; __i < (io)->nbufs; __i++)
+
+/* Loop over each RS block in each allocated buffer. */
+#define fec_for_each_buffer_rs_block(io, __i, __j) \
+ fec_for_each_buffer(io, __i) \
+ for (__j = 0; __j < 1 << DM_VERITY_FEC_BUF_RS_BITS; __j++)
+
+/*
+ * Return a pointer to the current RS block when called inside
+ * fec_for_each_buffer_rs_block.
+ */
+static inline u8 *fec_buffer_rs_block(struct dm_verity *v,
+ struct dm_verity_fec_io *fio,
+ unsigned i, unsigned j)
+{
+ return &fio->bufs[i][j * v->fec->rsn];
+}
+
+/*
+ * Return an index to the current RS block when called inside
+ * fec_for_each_buffer_rs_block.
+ */
+static inline unsigned fec_buffer_rs_index(unsigned i, unsigned j)
+{
+ return (i << DM_VERITY_FEC_BUF_RS_BITS) + j;
+}
+
+/*
+ * Decode all RS blocks from buffers and copy corrected bytes into fio->output
+ * starting from block_offset.
+ */
+static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio,
+ u64 rsb, int byte_index, unsigned block_offset,
+ int neras)
+{
+ int r, corrected = 0, res;
+ struct dm_buffer *buf;
+ unsigned n, i, offset;
+ u8 *par, *block;
+
+ par = fec_read_parity(v, rsb, block_offset, &offset, &buf);
+ if (IS_ERR(par))
+ return PTR_ERR(par);
+
+ /*
+ * Decode the RS blocks we have in bufs. Each RS block results in
+ * one corrected target byte and consumes fec->roots parity bytes.
+ */
+ fec_for_each_buffer_rs_block(fio, n, i) {
+ block = fec_buffer_rs_block(v, fio, n, i);
+ res = fec_decode_rs8(v, fio, block, &par[offset], neras);
+ if (res < 0) {
+ dm_bufio_release(buf);
+
+ r = res;
+ goto error;
+ }
+
+ corrected += res;
+ fio->output[block_offset] = block[byte_index];
+
+ block_offset++;
+ if (block_offset >= 1 << v->data_dev_block_bits)
+ goto done;
+
+ /* read the next block when we run out of parity bytes */
+ offset += v->fec->roots;
+ if (offset >= 1 << v->data_dev_block_bits) {
+ dm_bufio_release(buf);
+
+ par = fec_read_parity(v, rsb, block_offset, &offset, &buf);
+ if (unlikely(IS_ERR(par)))
+ return PTR_ERR(par);
+ }
+ }
+done:
+ r = corrected;
+error:
+ if (r < 0 && neras)
+ DMERR_LIMIT("%s: FEC %llu: failed to correct: %d",
+ v->data_dev->name, (unsigned long long)rsb, r);
+ else if (r > 0) {
+ DMWARN_LIMIT("%s: FEC %llu: corrected %d errors",
+ v->data_dev->name, (unsigned long long)rsb, r);
+ atomic_add_unless(&v->fec->corrected, 1, INT_MAX);
+ }
+
+ return r;
+}
+
+/*
+ * Locate data block erasures using verity hashes.
+ */
+static int fec_is_erasure(struct dm_verity *v, struct dm_verity_io *io,
+ u8 *want_digest, u8 *data)
+{
+ if (unlikely(verity_hash(v, verity_io_hash_desc(v, io),
+ data, 1 << v->data_dev_block_bits,
+ verity_io_real_digest(v, io))))
+ return 0;
+
+ return memcmp(verity_io_real_digest(v, io), want_digest,
+ v->digest_size) != 0;
+}
+
+/*
+ * Read data blocks that are part of the RS block and deinterleave as much as
+ * fits into buffers. Check for erasure locations if @neras is non-NULL.
+ */
+static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io,
+ u64 rsb, u64 target, unsigned block_offset,
+ int *neras)
+{
+ bool is_zero;
+ int i, j, target_index = -1;
+ struct dm_buffer *buf;
+ struct dm_bufio_client *bufio;
+ struct dm_verity_fec_io *fio = fec_io(io);
+ u64 block, ileaved;
+ u8 *bbuf, *rs_block;
+ u8 want_digest[v->digest_size];
+ unsigned n, k;
+
+ if (neras)
+ *neras = 0;
+
+ /*
+ * read each of the rsn data blocks that are part of the RS block, and
+ * interleave contents to available bufs
+ */
+ for (i = 0; i < v->fec->rsn; i++) {
+ ileaved = fec_interleave(v, rsb * v->fec->rsn + i);
+
+ /*
+ * target is the data block we want to correct, target_index is
+ * the index of this block within the rsn RS blocks
+ */
+ if (ileaved == target)
+ target_index = i;
+
+ block = ileaved >> v->data_dev_block_bits;
+ bufio = v->fec->data_bufio;
+
+ if (block >= v->data_blocks) {
+ block -= v->data_blocks;
+
+ /*
+ * blocks outside the area were assumed to contain
+ * zeros when encoding data was generated
+ */
+ if (unlikely(block >= v->fec->hash_blocks))
+ continue;
+
+ block += v->hash_start;
+ bufio = v->bufio;
+ }
+
+ bbuf = dm_bufio_read(bufio, block, &buf);
+ if (unlikely(IS_ERR(bbuf))) {
+ DMWARN_LIMIT("%s: FEC %llu: read failed (%llu): %ld",
+ v->data_dev->name,
+ (unsigned long long)rsb,
+ (unsigned long long)block, PTR_ERR(bbuf));
+
+ /* assume the block is corrupted */
+ if (neras && *neras <= v->fec->roots)
+ fio->erasures[(*neras)++] = i;
+
+ continue;
+ }
+
+ /* locate erasures if the block is on the data device */
+ if (bufio == v->fec->data_bufio &&
+ verity_hash_for_block(v, io, block, want_digest,
+ &is_zero) == 0) {
+ /* skip known zero blocks entirely */
+ if (is_zero)
+ continue;
+
+ /*
+ * skip if we have already found the theoretical
+ * maximum number (i.e. fec->roots) of erasures
+ */
+ if (neras && *neras <= v->fec->roots &&
+ fec_is_erasure(v, io, want_digest, bbuf))
+ fio->erasures[(*neras)++] = i;
+ }
+
+ /*
+ * deinterleave and copy the bytes that fit into bufs,
+ * starting from block_offset
+ */
+ fec_for_each_buffer_rs_block(fio, n, j) {
+ k = fec_buffer_rs_index(n, j) + block_offset;
+
+ if (k >= 1 << v->data_dev_block_bits)
+ goto done;
+
+ rs_block = fec_buffer_rs_block(v, fio, n, j);
+ rs_block[i] = bbuf[k];
+ }
+done:
+ dm_bufio_release(buf);
+ }
+
+ return target_index;
+}
+
+/*
+ * Allocate RS control structure and FEC buffers from preallocated mempools,
+ * and attempt to allocate as many extra buffers as available.
+ */
+static int fec_alloc_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio)
+{
+ unsigned n;
+
+ if (!fio->rs) {
+ fio->rs = mempool_alloc(v->fec->rs_pool, 0);
+ if (unlikely(!fio->rs)) {
+ DMERR("failed to allocate RS");
+ return -ENOMEM;
+ }
+ }
+
+ fec_for_each_prealloc_buffer(n) {
+ if (fio->bufs[n])
+ continue;
+
+ fio->bufs[n] = mempool_alloc(v->fec->prealloc_pool, GFP_NOIO);
+ if (unlikely(!fio->bufs[n])) {
+ DMERR("failed to allocate FEC buffer");
+ return -ENOMEM;
+ }
+ }
+
+ /* try to allocate the maximum number of buffers */
+ fec_for_each_extra_buffer(fio, n) {
+ if (fio->bufs[n])
+ continue;
+
+ fio->bufs[n] = mempool_alloc(v->fec->extra_pool, GFP_NOIO);
+ /* we can manage with even one buffer if necessary */
+ if (unlikely(!fio->bufs[n]))
+ break;
+ }
+ fio->nbufs = n;
+
+ if (!fio->output) {
+ fio->output = mempool_alloc(v->fec->output_pool, GFP_NOIO);
+
+ if (!fio->output) {
+ DMERR("failed to allocate FEC page");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize buffers and clear erasures. fec_read_bufs() assumes buffers are
+ * zeroed before deinterleaving.
+ */
+static void fec_init_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio)
+{
+ unsigned n;
+
+ fec_for_each_buffer(fio, n)
+ memset(fio->bufs[n], 0, v->fec->rsn << DM_VERITY_FEC_BUF_RS_BITS);
+
+ memset(fio->erasures, 0, sizeof(fio->erasures));
+}
+
+/*
+ * Decode all RS blocks in a single data block and return the target block
+ * (indicated by @offset) in fio->output. If @use_erasures is non-zero, uses
+ * hashes to locate erasures.
+ */
+static int fec_decode_rsb(struct dm_verity *v, struct dm_verity_io *io,
+ struct dm_verity_fec_io *fio, u64 rsb, u64 offset,
+ bool use_erasures)
+{
+ int r, neras = 0;
+ unsigned pos;
+
+ r = fec_alloc_bufs(v, fio);
+ if (unlikely(r < 0))
+ return r;
+
+ for (pos = 0; pos < 1 << v->data_dev_block_bits; ) {
+ fec_init_bufs(v, fio);
+
+ r = fec_read_bufs(v, io, rsb, offset, pos,
+ use_erasures ? &neras : NULL);
+ if (unlikely(r < 0))
+ return r;
+
+ r = fec_decode_bufs(v, fio, rsb, r, pos, neras);
+ if (r < 0)
+ return r;
+
+ pos += fio->nbufs << DM_VERITY_FEC_BUF_RS_BITS;
+ }
+
+ /* Always re-validate the corrected block against the expected hash */
+ r = verity_hash(v, verity_io_hash_desc(v, io), fio->output,
+ 1 << v->data_dev_block_bits,
+ verity_io_real_digest(v, io));
+ if (unlikely(r < 0))
+ return r;
+
+ if (memcmp(verity_io_real_digest(v, io), verity_io_want_digest(v, io),
+ v->digest_size)) {
+ DMERR_LIMIT("%s: FEC %llu: failed to correct (%d erasures)",
+ v->data_dev->name, (unsigned long long)rsb, neras);
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+static int fec_bv_copy(struct dm_verity *v, struct dm_verity_io *io, u8 *data,
+ size_t len)
+{
+ struct dm_verity_fec_io *fio = fec_io(io);
+
+ memcpy(data, &fio->output[fio->output_pos], len);
+ fio->output_pos += len;
+
+ return 0;
+}
+
+/*
+ * Correct errors in a block. Copies corrected block to dest if non-NULL,
+ * otherwise to a bio_vec starting from iter.
+ */
+int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
+ enum verity_block_type type, sector_t block, u8 *dest,
+ struct bvec_iter *iter)
+{
+ int r;
+ struct dm_verity_fec_io *fio = fec_io(io);
+ u64 offset, res, rsb;
+
+ if (!verity_fec_is_enabled(v))
+ return -EOPNOTSUPP;
+
+ if (type == DM_VERITY_BLOCK_TYPE_METADATA)
+ block += v->data_blocks;
+
+ /*
+ * For RS(M, N), the continuous FEC data is divided into blocks of N
+ * bytes. Since block size may not be divisible by N, the last block
+ * is zero padded when decoding.
+ *
+ * Each byte of the block is covered by a different RS(M, N) code,
+ * and each code is interleaved over N blocks to make it less likely
+ * that bursty corruption will leave us in unrecoverable state.
+ */
+
+ offset = block << v->data_dev_block_bits;
+
+ res = offset;
+ div64_u64(res, v->fec->rounds << v->data_dev_block_bits);
+
+ /*
+ * The base RS block we can feed to the interleaver to find out all
+ * blocks required for decoding.
+ */
+ rsb = offset - res * (v->fec->rounds << v->data_dev_block_bits);
+
+ /*
+ * Locating erasures is slow, so attempt to recover the block without
+ * them first. Do a second attempt with erasures if the corruption is
+ * bad enough.
+ */
+ r = fec_decode_rsb(v, io, fio, rsb, offset, false);
+ if (r < 0) {
+ r = fec_decode_rsb(v, io, fio, rsb, offset, true);
+ if (r < 0)
+ return r;
+ }
+
+ if (dest)
+ memcpy(dest, fio->output, 1 << v->data_dev_block_bits);
+ else if (iter) {
+ fio->output_pos = 0;
+ r = verity_for_bv_block(v, io, iter, fec_bv_copy);
+ }
+
+ return r;
+}
+
+/*
+ * Clean up per-bio data.
+ */
+void verity_fec_finish_io(struct dm_verity_io *io)
+{
+ unsigned n;
+ struct dm_verity_fec *f = io->v->fec;
+ struct dm_verity_fec_io *fio = fec_io(io);
+
+ if (!verity_fec_is_enabled(io->v))
+ return;
+
+ mempool_free(fio->rs, f->rs_pool);
+
+ fec_for_each_prealloc_buffer(n)
+ mempool_free(fio->bufs[n], f->prealloc_pool);
+
+ fec_for_each_extra_buffer(fio, n)
+ mempool_free(fio->bufs[n], f->extra_pool);
+
+ mempool_free(fio->output, f->output_pool);
+}
+
+/*
+ * Initialize per-bio data.
+ */
+void verity_fec_init_io(struct dm_verity_io *io)
+{
+ struct dm_verity_fec_io *fio = fec_io(io);
+
+ if (!verity_fec_is_enabled(io->v))
+ return;
+
+ fio->rs = NULL;
+ memset(fio->bufs, 0, sizeof(fio->bufs));
+ fio->nbufs = 0;
+ fio->output = NULL;
+}
+
+/*
+ * Append feature arguments and values to the status table.
+ */
+unsigned verity_fec_status_table(struct dm_verity *v, unsigned sz,
+ char *result, unsigned maxlen)
+{
+ if (!verity_fec_is_enabled(v))
+ return sz;
+
+ DMEMIT(" " DM_VERITY_OPT_FEC_DEV " %s "
+ DM_VERITY_OPT_FEC_BLOCKS " %llu "
+ DM_VERITY_OPT_FEC_START " %llu "
+ DM_VERITY_OPT_FEC_ROOTS " %d",
+ v->fec->dev->name,
+ (unsigned long long)v->fec->blocks,
+ (unsigned long long)v->fec->start,
+ v->fec->roots);
+
+ return sz;
+}
+
+void verity_fec_dtr(struct dm_verity *v)
+{
+ struct dm_verity_fec *f = v->fec;
+ struct kobject *kobj = &f->kobj_holder.kobj;
+
+ if (!verity_fec_is_enabled(v))
+ goto out;
+
+ mempool_destroy(f->rs_pool);
+ mempool_destroy(f->prealloc_pool);
+ mempool_destroy(f->extra_pool);
+ kmem_cache_destroy(f->cache);
+
+ if (f->data_bufio)
+ dm_bufio_client_destroy(f->data_bufio);
+ if (f->bufio)
+ dm_bufio_client_destroy(f->bufio);
+
+ if (f->dev)
+ dm_put_device(v->ti, f->dev);
+
+ if (kobj->state_initialized) {
+ kobject_put(kobj);
+ wait_for_completion(dm_get_completion_from_kobject(kobj));
+ }
+
+out:
+ kfree(f);
+ v->fec = NULL;
+}
+
+static void *fec_rs_alloc(gfp_t gfp_mask, void *pool_data)
+{
+ struct dm_verity *v = (struct dm_verity *)pool_data;
+
+ return init_rs(8, 0x11d, 0, 1, v->fec->roots);
+}
+
+static void fec_rs_free(void *element, void *pool_data)
+{
+ struct rs_control *rs = (struct rs_control *)element;
+
+ if (rs)
+ free_rs(rs);
+}
+
+bool verity_is_fec_opt_arg(const char *arg_name)
+{
+ return (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_DEV) ||
+ !strcasecmp(arg_name, DM_VERITY_OPT_FEC_BLOCKS) ||
+ !strcasecmp(arg_name, DM_VERITY_OPT_FEC_START) ||
+ !strcasecmp(arg_name, DM_VERITY_OPT_FEC_ROOTS));
+}
+
+int verity_fec_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
+ unsigned *argc, const char *arg_name)
+{
+ int r;
+ struct dm_target *ti = v->ti;
+ const char *arg_value;
+ unsigned long long num_ll;
+ unsigned char num_c;
+ char dummy;
+
+ if (!*argc) {
+ ti->error = "FEC feature arguments require a value";
+ return -EINVAL;
+ }
+
+ arg_value = dm_shift_arg(as);
+ (*argc)--;
+
+ if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_DEV)) {
+ r = dm_get_device(ti, arg_value, FMODE_READ, &v->fec->dev);
+ if (r) {
+ ti->error = "FEC device lookup failed";
+ return r;
+ }
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_BLOCKS)) {
+ if (sscanf(arg_value, "%llu%c", &num_ll, &dummy) != 1 ||
+ ((sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
+ >> (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll)) {
+ ti->error = "Invalid " DM_VERITY_OPT_FEC_BLOCKS;
+ return -EINVAL;
+ }
+ v->fec->blocks = num_ll;
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_START)) {
+ if (sscanf(arg_value, "%llu%c", &num_ll, &dummy) != 1 ||
+ ((sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT)) >>
+ (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll)) {
+ ti->error = "Invalid " DM_VERITY_OPT_FEC_START;
+ return -EINVAL;
+ }
+ v->fec->start = num_ll;
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_ROOTS)) {
+ if (sscanf(arg_value, "%hhu%c", &num_c, &dummy) != 1 || !num_c ||
+ num_c < (DM_VERITY_FEC_RSM - DM_VERITY_FEC_MAX_RSN) ||
+ num_c > (DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN)) {
+ ti->error = "Invalid " DM_VERITY_OPT_FEC_ROOTS;
+ return -EINVAL;
+ }
+ v->fec->roots = num_c;
+
+ } else {
+ ti->error = "Unrecognized verity FEC feature request";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t corrected_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ struct dm_verity_fec *f = container_of(kobj, struct dm_verity_fec,
+ kobj_holder.kobj);
+
+ return sprintf(buf, "%d\n", atomic_read(&f->corrected));
+}
+
+static struct kobj_attribute attr_corrected = __ATTR_RO(corrected);
+
+static struct attribute *fec_attrs[] = {
+ &attr_corrected.attr,
+ NULL
+};
+
+static struct kobj_type fec_ktype = {
+ .sysfs_ops = &kobj_sysfs_ops,
+ .default_attrs = fec_attrs
+};
+
+/*
+ * Allocate dm_verity_fec for v->fec. Must be called before verity_fec_ctr.
+ */
+int verity_fec_ctr_alloc(struct dm_verity *v)
+{
+ struct dm_verity_fec *f;
+
+ f = kzalloc(sizeof(struct dm_verity_fec), GFP_KERNEL);
+ if (!f) {
+ v->ti->error = "Cannot allocate FEC structure";
+ return -ENOMEM;
+ }
+ v->fec = f;
+
+ return 0;
+}
+
+/*
+ * Validate arguments and preallocate memory. Must be called after arguments
+ * have been parsed using verity_fec_parse_opt_args.
+ */
+int verity_fec_ctr(struct dm_verity *v)
+{
+ int r;
+ struct dm_verity_fec *f = v->fec;
+ struct dm_target *ti = v->ti;
+ struct mapped_device *md = dm_table_get_md(ti->table);
+ u64 hash_blocks;
+
+ if (!verity_fec_is_enabled(v)) {
+ verity_fec_dtr(v);
+ return 0;
+ }
+
+ /* Create a kobject and sysfs attributes */
+ init_completion(&f->kobj_holder.completion);
+
+ r = kobject_init_and_add(&f->kobj_holder.kobj, &fec_ktype,
+ &disk_to_dev(dm_disk(md))->kobj, "%s", "fec");
+ if (r) {
+ ti->error = "Cannot create kobject";
+ return r;
+ }
+
+ /*
+ * FEC is computed over data blocks, possible metadata, and
+ * hash blocks. In other words, FEC covers total of fec_blocks
+ * blocks consisting of the following:
+ *
+ * data blocks | hash blocks | metadata (optional)
+ *
+ * We allow metadata after hash blocks to support a use case
+ * where all data is stored on the same device and FEC covers
+ * the entire area.
+ *
+ * If metadata is included, we require it to be available on the
+ * hash device after the hash blocks.
+ */
+
+ hash_blocks = v->hash_blocks - v->hash_start;
+
+ /*
+ * Require matching block sizes for data and hash devices for
+ * simplicity.
+ */
+ if (v->data_dev_block_bits != v->hash_dev_block_bits) {
+ ti->error = "Block sizes must match to use FEC";
+ return -EINVAL;
+ }
+
+ if (!f->roots) {
+ ti->error = "Missing " DM_VERITY_OPT_FEC_ROOTS;
+ return -EINVAL;
+ }
+ f->rsn = DM_VERITY_FEC_RSM - f->roots;
+
+ if (!f->blocks) {
+ ti->error = "Missing " DM_VERITY_OPT_FEC_BLOCKS;
+ return -EINVAL;
+ }
+
+ f->rounds = f->blocks;
+ if (sector_div(f->rounds, f->rsn))
+ f->rounds++;
+
+ /*
+ * Due to optional metadata, f->blocks can be larger than
+ * data_blocks and hash_blocks combined.
+ */
+ if (f->blocks < v->data_blocks + hash_blocks || !f->rounds) {
+ ti->error = "Invalid " DM_VERITY_OPT_FEC_BLOCKS;
+ return -EINVAL;
+ }
+
+ /*
+ * Metadata is accessed through the hash device, so we require
+ * it to be large enough.
+ */
+ f->hash_blocks = f->blocks - v->data_blocks;
+ if (dm_bufio_get_device_size(v->bufio) < f->hash_blocks) {
+ ti->error = "Hash device is too small for "
+ DM_VERITY_OPT_FEC_BLOCKS;
+ return -E2BIG;
+ }
+
+ f->bufio = dm_bufio_client_create(f->dev->bdev,
+ 1 << v->data_dev_block_bits,
+ 1, 0, NULL, NULL);
+ if (IS_ERR(f->bufio)) {
+ ti->error = "Cannot initialize FEC bufio client";
+ return PTR_ERR(f->bufio);
+ }
+
+ if (dm_bufio_get_device_size(f->bufio) <
+ ((f->start + f->rounds * f->roots) >> v->data_dev_block_bits)) {
+ ti->error = "FEC device is too small";
+ return -E2BIG;
+ }
+
+ f->data_bufio = dm_bufio_client_create(v->data_dev->bdev,
+ 1 << v->data_dev_block_bits,
+ 1, 0, NULL, NULL);
+ if (IS_ERR(f->data_bufio)) {
+ ti->error = "Cannot initialize FEC data bufio client";
+ return PTR_ERR(f->data_bufio);
+ }
+
+ if (dm_bufio_get_device_size(f->data_bufio) < v->data_blocks) {
+ ti->error = "Data device is too small";
+ return -E2BIG;
+ }
+
+ /* Preallocate an rs_control structure for each worker thread */
+ f->rs_pool = mempool_create(num_online_cpus(), fec_rs_alloc,
+ fec_rs_free, (void *) v);
+ if (!f->rs_pool) {
+ ti->error = "Cannot allocate RS pool";
+ return -ENOMEM;
+ }
+
+ f->cache = kmem_cache_create("dm_verity_fec_buffers",
+ f->rsn << DM_VERITY_FEC_BUF_RS_BITS,
+ 0, 0, NULL);
+ if (!f->cache) {
+ ti->error = "Cannot create FEC buffer cache";
+ return -ENOMEM;
+ }
+
+ /* Preallocate DM_VERITY_FEC_BUF_PREALLOC buffers for each thread */
+ f->prealloc_pool = mempool_create_slab_pool(num_online_cpus() *
+ DM_VERITY_FEC_BUF_PREALLOC,
+ f->cache);
+ if (!f->prealloc_pool) {
+ ti->error = "Cannot allocate FEC buffer prealloc pool";
+ return -ENOMEM;
+ }
+
+ f->extra_pool = mempool_create_slab_pool(0, f->cache);
+ if (!f->extra_pool) {
+ ti->error = "Cannot allocate FEC buffer extra pool";
+ return -ENOMEM;
+ }
+
+ /* Preallocate an output buffer for each thread */
+ f->output_pool = mempool_create_kmalloc_pool(num_online_cpus(),
+ 1 << v->data_dev_block_bits);
+ if (!f->output_pool) {
+ ti->error = "Cannot allocate FEC output pool";
+ return -ENOMEM;
+ }
+
+ /* Reserve space for our per-bio data */
+ ti->per_bio_data_size += sizeof(struct dm_verity_fec_io);
+
+ return 0;
+}
diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h
new file mode 100644
index 000000000000..8c4bee052a73
--- /dev/null
+++ b/drivers/md/dm-verity-fec.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Author: Sami Tolvanen <samitolvanen@google.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#ifndef DM_VERITY_FEC_H
+#define DM_VERITY_FEC_H
+
+#include "dm.h"
+#include "dm-verity.h"
+#include <linux/rslib.h>
+
+/* Reed-Solomon(M, N) parameters */
+#define DM_VERITY_FEC_RSM 255
+#define DM_VERITY_FEC_MAX_RSN 253
+#define DM_VERITY_FEC_MIN_RSN 231 /* ~10% space overhead */
+
+/* buffers for deinterleaving and decoding */
+#define DM_VERITY_FEC_BUF_PREALLOC 1 /* buffers to preallocate */
+#define DM_VERITY_FEC_BUF_RS_BITS 4 /* 1 << RS blocks per buffer */
+/* we need buffers for at most 1 << block size RS blocks */
+#define DM_VERITY_FEC_BUF_MAX \
+ (1 << (PAGE_SHIFT - DM_VERITY_FEC_BUF_RS_BITS))
+
+#define DM_VERITY_OPT_FEC_DEV "use_fec_from_device"
+#define DM_VERITY_OPT_FEC_BLOCKS "fec_blocks"
+#define DM_VERITY_OPT_FEC_START "fec_start"
+#define DM_VERITY_OPT_FEC_ROOTS "fec_roots"
+
+/* configuration */
+struct dm_verity_fec {
+ struct dm_dev *dev; /* parity data device */
+ struct dm_bufio_client *data_bufio; /* for data dev access */
+ struct dm_bufio_client *bufio; /* for parity data access */
+ sector_t start; /* parity data start in blocks */
+ sector_t blocks; /* number of blocks covered */
+ sector_t rounds; /* number of interleaving rounds */
+ sector_t hash_blocks; /* blocks covered after v->hash_start */
+ unsigned char roots; /* number of parity bytes, M-N of RS(M, N) */
+ unsigned char rsn; /* N of RS(M, N) */
+ mempool_t *rs_pool; /* mempool for fio->rs */
+ mempool_t *prealloc_pool; /* mempool for preallocated buffers */
+ mempool_t *extra_pool; /* mempool for extra buffers */
+ mempool_t *output_pool; /* mempool for output */
+ struct kmem_cache *cache; /* cache for buffers */
+ atomic_t corrected; /* corrected errors */
+ struct dm_kobject_holder kobj_holder; /* for sysfs attributes */
+};
+
+/* per-bio data */
+struct dm_verity_fec_io {
+ struct rs_control *rs; /* Reed-Solomon state */
+ int erasures[DM_VERITY_FEC_MAX_RSN]; /* erasures for decode_rs8 */
+ u8 *bufs[DM_VERITY_FEC_BUF_MAX]; /* bufs for deinterleaving */
+ unsigned nbufs; /* number of buffers allocated */
+ u8 *output; /* buffer for corrected output */
+ size_t output_pos;
+};
+
+#ifdef CONFIG_DM_VERITY_FEC
+
+/* each feature parameter requires a value */
+#define DM_VERITY_OPTS_FEC 8
+
+extern bool verity_fec_is_enabled(struct dm_verity *v);
+
+extern int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
+ enum verity_block_type type, sector_t block,
+ u8 *dest, struct bvec_iter *iter);
+
+extern unsigned verity_fec_status_table(struct dm_verity *v, unsigned sz,
+ char *result, unsigned maxlen);
+
+extern void verity_fec_finish_io(struct dm_verity_io *io);
+extern void verity_fec_init_io(struct dm_verity_io *io);
+
+extern bool verity_is_fec_opt_arg(const char *arg_name);
+extern int verity_fec_parse_opt_args(struct dm_arg_set *as,
+ struct dm_verity *v, unsigned *argc,
+ const char *arg_name);
+
+extern void verity_fec_dtr(struct dm_verity *v);
+
+extern int verity_fec_ctr_alloc(struct dm_verity *v);
+extern int verity_fec_ctr(struct dm_verity *v);
+
+#else /* !CONFIG_DM_VERITY_FEC */
+
+#define DM_VERITY_OPTS_FEC 0
+
+static inline bool verity_fec_is_enabled(struct dm_verity *v)
+{
+ return false;
+}
+
+static inline int verity_fec_decode(struct dm_verity *v,
+ struct dm_verity_io *io,
+ enum verity_block_type type,
+ sector_t block, u8 *dest,
+ struct bvec_iter *iter)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline unsigned verity_fec_status_table(struct dm_verity *v,
+ unsigned sz, char *result,
+ unsigned maxlen)
+{
+ return sz;
+}
+
+static inline void verity_fec_finish_io(struct dm_verity_io *io)
+{
+}
+
+static inline void verity_fec_init_io(struct dm_verity_io *io)
+{
+}
+
+static inline bool verity_is_fec_opt_arg(const char *arg_name)
+{
+ return false;
+}
+
+static inline int verity_fec_parse_opt_args(struct dm_arg_set *as,
+ struct dm_verity *v,
+ unsigned *argc,
+ const char *arg_name)
+{
+ return -EINVAL;
+}
+
+static inline void verity_fec_dtr(struct dm_verity *v)
+{
+}
+
+static inline int verity_fec_ctr_alloc(struct dm_verity *v)
+{
+ return 0;
+}
+
+static inline int verity_fec_ctr(struct dm_verity *v)
+{
+ return 0;
+}
+
+#endif /* CONFIG_DM_VERITY_FEC */
+
+#endif /* DM_VERITY_FEC_H */
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity-target.c
index ccf41886ebcf..5c5d30cb6ec5 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity-target.c
@@ -14,12 +14,11 @@
* access behavior.
*/
-#include "dm-bufio.h"
+#include "dm-verity.h"
+#include "dm-verity-fec.h"
#include <linux/module.h>
-#include <linux/device-mapper.h>
#include <linux/reboot.h>
-#include <crypto/hash.h>
#define DM_MSG_PREFIX "verity"
@@ -28,83 +27,18 @@
#define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
-#define DM_VERITY_MAX_LEVELS 63
#define DM_VERITY_MAX_CORRUPTED_ERRS 100
#define DM_VERITY_OPT_LOGGING "ignore_corruption"
#define DM_VERITY_OPT_RESTART "restart_on_corruption"
+#define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
+
+#define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC)
static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);
-enum verity_mode {
- DM_VERITY_MODE_EIO,
- DM_VERITY_MODE_LOGGING,
- DM_VERITY_MODE_RESTART
-};
-
-enum verity_block_type {
- DM_VERITY_BLOCK_TYPE_DATA,
- DM_VERITY_BLOCK_TYPE_METADATA
-};
-
-struct dm_verity {
- struct dm_dev *data_dev;
- struct dm_dev *hash_dev;
- struct dm_target *ti;
- struct dm_bufio_client *bufio;
- char *alg_name;
- struct crypto_shash *tfm;
- u8 *root_digest; /* digest of the root block */
- u8 *salt; /* salt: its size is salt_size */
- unsigned salt_size;
- sector_t data_start; /* data offset in 512-byte sectors */
- sector_t hash_start; /* hash start in blocks */
- sector_t data_blocks; /* the number of data blocks */
- sector_t hash_blocks; /* the number of hash blocks */
- unsigned char data_dev_block_bits; /* log2(data blocksize) */
- unsigned char hash_dev_block_bits; /* log2(hash blocksize) */
- unsigned char hash_per_block_bits; /* log2(hashes in hash block) */
- unsigned char levels; /* the number of tree levels */
- unsigned char version;
- unsigned digest_size; /* digest size for the current hash algorithm */
- unsigned shash_descsize;/* the size of temporary space for crypto */
- int hash_failed; /* set to 1 if hash of any block failed */
- enum verity_mode mode; /* mode for handling verification errors */
- unsigned corrupted_errs;/* Number of errors for corrupted blocks */
-
- struct workqueue_struct *verify_wq;
-
- /* starting blocks for each tree level. 0 is the lowest level. */
- sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
-};
-
-struct dm_verity_io {
- struct dm_verity *v;
-
- /* original values of bio->bi_end_io and bio->bi_private */
- bio_end_io_t *orig_bi_end_io;
- void *orig_bi_private;
-
- sector_t block;
- unsigned n_blocks;
-
- struct bvec_iter iter;
-
- struct work_struct work;
-
- /*
- * Three variably-size fields follow this struct:
- *
- * u8 hash_desc[v->shash_descsize];
- * u8 real_digest[v->digest_size];
- * u8 want_digest[v->digest_size];
- *
- * To access them use: io_hash_desc(), io_real_digest() and io_want_digest().
- */
-};
-
struct dm_verity_prefetch_work {
struct work_struct work;
struct dm_verity *v;
@@ -112,21 +46,6 @@ struct dm_verity_prefetch_work {
unsigned n_blocks;
};
-static struct shash_desc *io_hash_desc(struct dm_verity *v, struct dm_verity_io *io)
-{
- return (struct shash_desc *)(io + 1);
-}
-
-static u8 *io_real_digest(struct dm_verity *v, struct dm_verity_io *io)
-{
- return (u8 *)(io + 1) + v->shash_descsize;
-}
-
-static u8 *io_want_digest(struct dm_verity *v, struct dm_verity_io *io)
-{
- return (u8 *)(io + 1) + v->shash_descsize + v->digest_size;
-}
-
/*
* Auxiliary structure appended to each dm-bufio buffer. If the value
* hash_verified is nonzero, hash of the block has been verified.
@@ -173,6 +92,84 @@ static sector_t verity_position_at_level(struct dm_verity *v, sector_t block,
return block >> (level * v->hash_per_block_bits);
}
+/*
+ * Wrapper for crypto_shash_init, which handles verity salting.
+ */
+static int verity_hash_init(struct dm_verity *v, struct shash_desc *desc)
+{
+ int r;
+
+ desc->tfm = v->tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ r = crypto_shash_init(desc);
+
+ if (unlikely(r < 0)) {
+ DMERR("crypto_shash_init failed: %d", r);
+ return r;
+ }
+
+ if (likely(v->version >= 1)) {
+ r = crypto_shash_update(desc, v->salt, v->salt_size);
+
+ if (unlikely(r < 0)) {
+ DMERR("crypto_shash_update failed: %d", r);
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int verity_hash_update(struct dm_verity *v, struct shash_desc *desc,
+ const u8 *data, size_t len)
+{
+ int r = crypto_shash_update(desc, data, len);
+
+ if (unlikely(r < 0))
+ DMERR("crypto_shash_update failed: %d", r);
+
+ return r;
+}
+
+static int verity_hash_final(struct dm_verity *v, struct shash_desc *desc,
+ u8 *digest)
+{
+ int r;
+
+ if (unlikely(!v->version)) {
+ r = crypto_shash_update(desc, v->salt, v->salt_size);
+
+ if (r < 0) {
+ DMERR("crypto_shash_update failed: %d", r);
+ return r;
+ }
+ }
+
+ r = crypto_shash_final(desc, digest);
+
+ if (unlikely(r < 0))
+ DMERR("crypto_shash_final failed: %d", r);
+
+ return r;
+}
+
+int verity_hash(struct dm_verity *v, struct shash_desc *desc,
+ const u8 *data, size_t len, u8 *digest)
+{
+ int r;
+
+ r = verity_hash_init(v, desc);
+ if (unlikely(r < 0))
+ return r;
+
+ r = verity_hash_update(v, desc, data, len);
+ if (unlikely(r < 0))
+ return r;
+
+ return verity_hash_final(v, desc, digest);
+}
+
static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level,
sector_t *hash_block, unsigned *offset)
{
@@ -246,17 +243,17 @@ out:
* Verify hash of a metadata block pertaining to the specified data block
* ("block" argument) at a specified level ("level" argument).
*
- * On successful return, io_want_digest(v, io) contains the hash value for
- * a lower tree level or for the data block (if we're at the lowest leve).
+ * On successful return, verity_io_want_digest(v, io) contains the hash value
+ * for a lower tree level or for the data block (if we're at the lowest level).
*
* If "skip_unverified" is true, unverified buffer is skipped and 1 is returned.
* If "skip_unverified" is false, unverified buffer is hashed and verified
- * against current value of io_want_digest(v, io).
+ * against current value of verity_io_want_digest(v, io).
*/
-static int verity_verify_level(struct dm_verity_io *io, sector_t block,
- int level, bool skip_unverified)
+static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
+ sector_t block, int level, bool skip_unverified,
+ u8 *want_digest)
{
- struct dm_verity *v = io->v;
struct dm_buffer *buf;
struct buffer_aux *aux;
u8 *data;
@@ -273,72 +270,128 @@ static int verity_verify_level(struct dm_verity_io *io, sector_t block,
aux = dm_bufio_get_aux_data(buf);
if (!aux->hash_verified) {
- struct shash_desc *desc;
- u8 *result;
-
if (skip_unverified) {
r = 1;
goto release_ret_r;
}
- desc = io_hash_desc(v, io);
- desc->tfm = v->tfm;
- desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
- r = crypto_shash_init(desc);
- if (r < 0) {
- DMERR("crypto_shash_init failed: %d", r);
+ r = verity_hash(v, verity_io_hash_desc(v, io),
+ data, 1 << v->hash_dev_block_bits,
+ verity_io_real_digest(v, io));
+ if (unlikely(r < 0))
goto release_ret_r;
- }
-
- if (likely(v->version >= 1)) {
- r = crypto_shash_update(desc, v->salt, v->salt_size);
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
- goto release_ret_r;
- }
- }
- r = crypto_shash_update(desc, data, 1 << v->hash_dev_block_bits);
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
+ if (likely(memcmp(verity_io_real_digest(v, io), want_digest,
+ v->digest_size) == 0))
+ aux->hash_verified = 1;
+ else if (verity_fec_decode(v, io,
+ DM_VERITY_BLOCK_TYPE_METADATA,
+ hash_block, data, NULL) == 0)
+ aux->hash_verified = 1;
+ else if (verity_handle_err(v,
+ DM_VERITY_BLOCK_TYPE_METADATA,
+ hash_block)) {
+ r = -EIO;
goto release_ret_r;
}
+ }
- if (!v->version) {
- r = crypto_shash_update(desc, v->salt, v->salt_size);
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
- goto release_ret_r;
- }
- }
+ data += offset;
+ memcpy(want_digest, data, v->digest_size);
+ r = 0;
- result = io_real_digest(v, io);
- r = crypto_shash_final(desc, result);
- if (r < 0) {
- DMERR("crypto_shash_final failed: %d", r);
- goto release_ret_r;
- }
- if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
- if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_METADATA,
- hash_block)) {
- r = -EIO;
- goto release_ret_r;
- }
- } else
- aux->hash_verified = 1;
+release_ret_r:
+ dm_bufio_release(buf);
+ return r;
+}
+
+/*
+ * Find a hash for a given block, write it to digest and verify the integrity
+ * of the hash tree if necessary.
+ */
+int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
+ sector_t block, u8 *digest, bool *is_zero)
+{
+ int r = 0, i;
+
+ if (likely(v->levels)) {
+ /*
+ * First, we try to get the requested hash for
+ * the current block. If the hash block itself is
+ * verified, zero is returned. If it isn't, this
+ * function returns 1 and we fall back to whole
+ * chain verification.
+ */
+ r = verity_verify_level(v, io, block, 0, true, digest);
+ if (likely(r <= 0))
+ goto out;
}
- data += offset;
+ memcpy(digest, v->root_digest, v->digest_size);
- memcpy(io_want_digest(v, io), data, v->digest_size);
+ for (i = v->levels - 1; i >= 0; i--) {
+ r = verity_verify_level(v, io, block, i, false, digest);
+ if (unlikely(r))
+ goto out;
+ }
+out:
+ if (!r && v->zero_digest)
+ *is_zero = !memcmp(v->zero_digest, digest, v->digest_size);
+ else
+ *is_zero = false;
+
+ return r;
+}
+
+/*
+ * Calls function process for 1 << v->data_dev_block_bits bytes in the bio_vec
+ * starting from iter.
+ */
+int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io,
+ struct bvec_iter *iter,
+ int (*process)(struct dm_verity *v,
+ struct dm_verity_io *io, u8 *data,
+ size_t len))
+{
+ unsigned todo = 1 << v->data_dev_block_bits;
+ struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_bio_data_size);
+
+ do {
+ int r;
+ u8 *page;
+ unsigned len;
+ struct bio_vec bv = bio_iter_iovec(bio, *iter);
+
+ page = kmap_atomic(bv.bv_page);
+ len = bv.bv_len;
+
+ if (likely(len >= todo))
+ len = todo;
+
+ r = process(v, io, page + bv.bv_offset, len);
+ kunmap_atomic(page);
+
+ if (r < 0)
+ return r;
+
+ bio_advance_iter(bio, iter, len);
+ todo -= len;
+ } while (todo);
- dm_bufio_release(buf);
return 0;
+}
-release_ret_r:
- dm_bufio_release(buf);
+static int verity_bv_hash_update(struct dm_verity *v, struct dm_verity_io *io,
+ u8 *data, size_t len)
+{
+ return verity_hash_update(v, verity_io_hash_desc(v, io), data, len);
+}
- return r;
+static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io,
+ u8 *data, size_t len)
+{
+ memset(data, 0, len);
+ return 0;
}
/*
@@ -346,99 +399,56 @@ release_ret_r:
*/
static int verity_verify_io(struct dm_verity_io *io)
{
+ bool is_zero;
struct dm_verity *v = io->v;
- struct bio *bio = dm_bio_from_per_bio_data(io,
- v->ti->per_bio_data_size);
+ struct bvec_iter start;
unsigned b;
- int i;
for (b = 0; b < io->n_blocks; b++) {
- struct shash_desc *desc;
- u8 *result;
int r;
- unsigned todo;
+ struct shash_desc *desc = verity_io_hash_desc(v, io);
+
+ r = verity_hash_for_block(v, io, io->block + b,
+ verity_io_want_digest(v, io),
+ &is_zero);
+ if (unlikely(r < 0))
+ return r;
- if (likely(v->levels)) {
+ if (is_zero) {
/*
- * First, we try to get the requested hash for
- * the current block. If the hash block itself is
- * verified, zero is returned. If it isn't, this
- * function returns 0 and we fall back to whole
- * chain verification.
+ * If we expect a zero block, don't validate, just
+ * return zeros.
*/
- int r = verity_verify_level(io, io->block + b, 0, true);
- if (likely(!r))
- goto test_block_hash;
- if (r < 0)
+ r = verity_for_bv_block(v, io, &io->iter,
+ verity_bv_zero);
+ if (unlikely(r < 0))
return r;
- }
- memcpy(io_want_digest(v, io), v->root_digest, v->digest_size);
-
- for (i = v->levels - 1; i >= 0; i--) {
- int r = verity_verify_level(io, io->block + b, i, false);
- if (unlikely(r))
- return r;
+ continue;
}
-test_block_hash:
- desc = io_hash_desc(v, io);
- desc->tfm = v->tfm;
- desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
- r = crypto_shash_init(desc);
- if (r < 0) {
- DMERR("crypto_shash_init failed: %d", r);
+ r = verity_hash_init(v, desc);
+ if (unlikely(r < 0))
return r;
- }
-
- if (likely(v->version >= 1)) {
- r = crypto_shash_update(desc, v->salt, v->salt_size);
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
- return r;
- }
- }
- todo = 1 << v->data_dev_block_bits;
- do {
- u8 *page;
- unsigned len;
- struct bio_vec bv = bio_iter_iovec(bio, io->iter);
-
- page = kmap_atomic(bv.bv_page);
- len = bv.bv_len;
- if (likely(len >= todo))
- len = todo;
- r = crypto_shash_update(desc, page + bv.bv_offset, len);
- kunmap_atomic(page);
-
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
- return r;
- }
-
- bio_advance_iter(bio, &io->iter, len);
- todo -= len;
- } while (todo);
- if (!v->version) {
- r = crypto_shash_update(desc, v->salt, v->salt_size);
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
- return r;
- }
- }
+ start = io->iter;
+ r = verity_for_bv_block(v, io, &io->iter, verity_bv_hash_update);
+ if (unlikely(r < 0))
+ return r;
- result = io_real_digest(v, io);
- r = crypto_shash_final(desc, result);
- if (r < 0) {
- DMERR("crypto_shash_final failed: %d", r);
+ r = verity_hash_final(v, desc, verity_io_real_digest(v, io));
+ if (unlikely(r < 0))
return r;
- }
- if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
- if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
- io->block + b))
- return -EIO;
- }
+
+ if (likely(memcmp(verity_io_real_digest(v, io),
+ verity_io_want_digest(v, io), v->digest_size) == 0))
+ continue;
+ else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA,
+ io->block + b, NULL, &start) == 0)
+ continue;
+ else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
+ io->block + b))
+ return -EIO;
}
return 0;
@@ -453,9 +463,10 @@ static void verity_finish_io(struct dm_verity_io *io, int error)
struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_bio_data_size);
bio->bi_end_io = io->orig_bi_end_io;
- bio->bi_private = io->orig_bi_private;
bio->bi_error = error;
+ verity_fec_finish_io(io);
+
bio_endio(bio);
}
@@ -470,7 +481,7 @@ static void verity_end_io(struct bio *bio)
{
struct dm_verity_io *io = bio->bi_private;
- if (bio->bi_error) {
+ if (bio->bi_error && !verity_fec_is_enabled(io->v)) {
verity_finish_io(io, bio->bi_error);
return;
}
@@ -566,7 +577,6 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
io = dm_per_bio_data(bio, ti->per_bio_data_size);
io->v = v;
io->orig_bi_end_io = bio->bi_end_io;
- io->orig_bi_private = bio->bi_private;
io->block = bio->bi_iter.bi_sector >> (v->data_dev_block_bits - SECTOR_SHIFT);
io->n_blocks = bio->bi_iter.bi_size >> v->data_dev_block_bits;
@@ -574,6 +584,8 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
bio->bi_private = io;
io->iter = bio->bi_iter;
+ verity_fec_init_io(io);
+
verity_submit_prefetch(v, io);
generic_make_request(bio);
@@ -588,6 +600,7 @@ static void verity_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
{
struct dm_verity *v = ti->private;
+ unsigned args = 0;
unsigned sz = 0;
unsigned x;
@@ -614,8 +627,17 @@ static void verity_status(struct dm_target *ti, status_type_t type,
else
for (x = 0; x < v->salt_size; x++)
DMEMIT("%02x", v->salt[x]);
+ if (v->mode != DM_VERITY_MODE_EIO)
+ args++;
+ if (verity_fec_is_enabled(v))
+ args += DM_VERITY_OPTS_FEC;
+ if (v->zero_digest)
+ args++;
+ if (!args)
+ return;
+ DMEMIT(" %u", args);
if (v->mode != DM_VERITY_MODE_EIO) {
- DMEMIT(" 1 ");
+ DMEMIT(" ");
switch (v->mode) {
case DM_VERITY_MODE_LOGGING:
DMEMIT(DM_VERITY_OPT_LOGGING);
@@ -627,6 +649,9 @@ static void verity_status(struct dm_target *ti, status_type_t type,
BUG();
}
}
+ if (v->zero_digest)
+ DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
+ sz = verity_fec_status_table(v, sz, result, maxlen);
break;
}
}
@@ -677,6 +702,7 @@ static void verity_dtr(struct dm_target *ti)
kfree(v->salt);
kfree(v->root_digest);
+ kfree(v->zero_digest);
if (v->tfm)
crypto_free_shash(v->tfm);
@@ -689,9 +715,94 @@ static void verity_dtr(struct dm_target *ti)
if (v->data_dev)
dm_put_device(ti, v->data_dev);
+ verity_fec_dtr(v);
+
kfree(v);
}
+static int verity_alloc_zero_digest(struct dm_verity *v)
+{
+ int r = -ENOMEM;
+ struct shash_desc *desc;
+ u8 *zero_data;
+
+ v->zero_digest = kmalloc(v->digest_size, GFP_KERNEL);
+
+ if (!v->zero_digest)
+ return r;
+
+ desc = kmalloc(v->shash_descsize, GFP_KERNEL);
+
+ if (!desc)
+ return r; /* verity_dtr will free zero_digest */
+
+ zero_data = kzalloc(1 << v->data_dev_block_bits, GFP_KERNEL);
+
+ if (!zero_data)
+ goto out;
+
+ r = verity_hash(v, desc, zero_data, 1 << v->data_dev_block_bits,
+ v->zero_digest);
+
+out:
+ kfree(desc);
+ kfree(zero_data);
+
+ return r;
+}
+
+static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
+{
+ int r;
+ unsigned argc;
+ struct dm_target *ti = v->ti;
+ const char *arg_name;
+
+ static struct dm_arg _args[] = {
+ {0, DM_VERITY_OPTS_MAX, "Invalid number of feature args"},
+ };
+
+ r = dm_read_arg_group(_args, as, &argc, &ti->error);
+ if (r)
+ return -EINVAL;
+
+ if (!argc)
+ return 0;
+
+ do {
+ arg_name = dm_shift_arg(as);
+ argc--;
+
+ if (!strcasecmp(arg_name, DM_VERITY_OPT_LOGGING)) {
+ v->mode = DM_VERITY_MODE_LOGGING;
+ continue;
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_RESTART)) {
+ v->mode = DM_VERITY_MODE_RESTART;
+ continue;
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) {
+ r = verity_alloc_zero_digest(v);
+ if (r) {
+ ti->error = "Cannot allocate zero digest";
+ return r;
+ }
+ continue;
+
+ } else if (verity_is_fec_opt_arg(arg_name)) {
+ r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
+ if (r)
+ return r;
+ continue;
+ }
+
+ ti->error = "Unrecognized verity feature request";
+ return -EINVAL;
+ } while (argc && !r);
+
+ return r;
+}
+
/*
* Target parameters:
* <version> The current format is version 1.
@@ -710,18 +821,13 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
struct dm_verity *v;
struct dm_arg_set as;
- const char *opt_string;
- unsigned int num, opt_params;
+ unsigned int num;
unsigned long long num_ll;
int r;
int i;
sector_t hash_position;
char dummy;
- static struct dm_arg _args[] = {
- {0, 1, "Invalid number of feature args"},
- };
-
v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
if (!v) {
ti->error = "Cannot allocate verity structure";
@@ -730,6 +836,10 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
ti->private = v;
v->ti = ti;
+ r = verity_fec_ctr_alloc(v);
+ if (r)
+ goto bad;
+
if ((dm_table_get_mode(ti->table) & ~FMODE_READ)) {
ti->error = "Device must be readonly";
r = -EINVAL;
@@ -866,29 +976,9 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
as.argc = argc;
as.argv = argv;
- r = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
- if (r)
+ r = verity_parse_opt_args(&as, v);
+ if (r < 0)
goto bad;
-
- while (opt_params) {
- opt_params--;
- opt_string = dm_shift_arg(&as);
- if (!opt_string) {
- ti->error = "Not enough feature arguments";
- r = -EINVAL;
- goto bad;
- }
-
- if (!strcasecmp(opt_string, DM_VERITY_OPT_LOGGING))
- v->mode = DM_VERITY_MODE_LOGGING;
- else if (!strcasecmp(opt_string, DM_VERITY_OPT_RESTART))
- v->mode = DM_VERITY_MODE_RESTART;
- else {
- ti->error = "Invalid feature arguments";
- r = -EINVAL;
- goto bad;
- }
- }
}
v->hash_per_block_bits =
@@ -938,8 +1028,6 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad;
}
- ti->per_bio_data_size = roundup(sizeof(struct dm_verity_io) + v->shash_descsize + v->digest_size * 2, __alignof__(struct dm_verity_io));
-
/* WQ_UNBOUND greatly improves performance when running on ramdisk */
v->verify_wq = alloc_workqueue("kverityd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND, num_online_cpus());
if (!v->verify_wq) {
@@ -948,6 +1036,16 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad;
}
+ ti->per_bio_data_size = sizeof(struct dm_verity_io) +
+ v->shash_descsize + v->digest_size * 2;
+
+ r = verity_fec_ctr(v);
+ if (r)
+ goto bad;
+
+ ti->per_bio_data_size = roundup(ti->per_bio_data_size,
+ __alignof__(struct dm_verity_io));
+
return 0;
bad:
@@ -958,7 +1056,7 @@ bad:
static struct target_type verity_target = {
.name = "verity",
- .version = {1, 2, 0},
+ .version = {1, 3, 0},
.module = THIS_MODULE,
.ctr = verity_ctr,
.dtr = verity_dtr,
diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h
new file mode 100644
index 000000000000..fb419f422d73
--- /dev/null
+++ b/drivers/md/dm-verity.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Author: Mikulas Patocka <mpatocka@redhat.com>
+ *
+ * Based on Chromium dm-verity driver (C) 2011 The Chromium OS Authors
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef DM_VERITY_H
+#define DM_VERITY_H
+
+#include "dm-bufio.h"
+#include <linux/device-mapper.h>
+#include <crypto/hash.h>
+
+#define DM_VERITY_MAX_LEVELS 63
+
+enum verity_mode {
+ DM_VERITY_MODE_EIO,
+ DM_VERITY_MODE_LOGGING,
+ DM_VERITY_MODE_RESTART
+};
+
+enum verity_block_type {
+ DM_VERITY_BLOCK_TYPE_DATA,
+ DM_VERITY_BLOCK_TYPE_METADATA
+};
+
+struct dm_verity_fec;
+
+struct dm_verity {
+ struct dm_dev *data_dev;
+ struct dm_dev *hash_dev;
+ struct dm_target *ti;
+ struct dm_bufio_client *bufio;
+ char *alg_name;
+ struct crypto_shash *tfm;
+ u8 *root_digest; /* digest of the root block */
+ u8 *salt; /* salt: its size is salt_size */
+ u8 *zero_digest; /* digest for a zero block */
+ unsigned salt_size;
+ sector_t data_start; /* data offset in 512-byte sectors */
+ sector_t hash_start; /* hash start in blocks */
+ sector_t data_blocks; /* the number of data blocks */
+ sector_t hash_blocks; /* the number of hash blocks */
+ unsigned char data_dev_block_bits; /* log2(data blocksize) */
+ unsigned char hash_dev_block_bits; /* log2(hash blocksize) */
+ unsigned char hash_per_block_bits; /* log2(hashes in hash block) */
+ unsigned char levels; /* the number of tree levels */
+ unsigned char version;
+ unsigned digest_size; /* digest size for the current hash algorithm */
+ unsigned shash_descsize;/* the size of temporary space for crypto */
+ int hash_failed; /* set to 1 if hash of any block failed */
+ enum verity_mode mode; /* mode for handling verification errors */
+ unsigned corrupted_errs;/* Number of errors for corrupted blocks */
+
+ struct workqueue_struct *verify_wq;
+
+ /* starting blocks for each tree level. 0 is the lowest level. */
+ sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
+
+ struct dm_verity_fec *fec; /* forward error correction */
+};
+
+struct dm_verity_io {
+ struct dm_verity *v;
+
+ /* original value of bio->bi_end_io */
+ bio_end_io_t *orig_bi_end_io;
+
+ sector_t block;
+ unsigned n_blocks;
+
+ struct bvec_iter iter;
+
+ struct work_struct work;
+
+ /*
+ * Three variably-size fields follow this struct:
+ *
+ * u8 hash_desc[v->shash_descsize];
+ * u8 real_digest[v->digest_size];
+ * u8 want_digest[v->digest_size];
+ *
+ * To access them use: verity_io_hash_desc(), verity_io_real_digest()
+ * and verity_io_want_digest().
+ */
+};
+
+static inline struct shash_desc *verity_io_hash_desc(struct dm_verity *v,
+ struct dm_verity_io *io)
+{
+ return (struct shash_desc *)(io + 1);
+}
+
+static inline u8 *verity_io_real_digest(struct dm_verity *v,
+ struct dm_verity_io *io)
+{
+ return (u8 *)(io + 1) + v->shash_descsize;
+}
+
+static inline u8 *verity_io_want_digest(struct dm_verity *v,
+ struct dm_verity_io *io)
+{
+ return (u8 *)(io + 1) + v->shash_descsize + v->digest_size;
+}
+
+static inline u8 *verity_io_digest_end(struct dm_verity *v,
+ struct dm_verity_io *io)
+{
+ return verity_io_want_digest(v, io) + v->digest_size;
+}
+
+extern int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io,
+ struct bvec_iter *iter,
+ int (*process)(struct dm_verity *v,
+ struct dm_verity_io *io,
+ u8 *data, size_t len));
+
+extern int verity_hash(struct dm_verity *v, struct shash_desc *desc,
+ const u8 *data, size_t len, u8 *digest);
+
+extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
+ sector_t block, u8 *digest, bool *is_zero);
+
+#endif /* DM_VERITY_H */
diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig
index 7f7cc8ee9be3..568f817e8614 100644
--- a/drivers/media/platform/msm/camera_v2/Kconfig
+++ b/drivers/media/platform/msm/camera_v2/Kconfig
@@ -1,5 +1,5 @@
config MSM_CAMERA_SENSOR
- bool "Qualcomm MSM camera sensor support"
+ bool "QTI MSM camera sensor support"
depends on MSMB_CAMERA
select NEW_LEDS
select LEDS_CLASS
@@ -10,7 +10,7 @@ config MSM_CAMERA_SENSOR
subdev APIs.
config MSM_CPP
- bool "Qualcomm MSM Camera Post Processing Engine support"
+ bool "QTI MSM Camera Post Processing Engine support"
depends on MSMB_CAMERA
---help---
Enable support for Camera Post-processing Engine
@@ -19,7 +19,7 @@ config MSM_CPP
APIs.
config MSM_CCI
- bool "Qualcomm MSM Camera Control Interface support"
+ bool "QTI MSM Camera Control Interface support"
depends on MSMB_CAMERA
---help---
Enable support for Camera Control Interface driver only
@@ -29,7 +29,7 @@ config MSM_CCI
GPIO and data frames.
config MSM_CSI20_HEADER
- bool "Qualcomm MSM CSI 2.0 Header"
+ bool "QTI MSM CSI 2.0 Header"
depends on MSMB_CAMERA
---help---
Enable support for CSI drivers to include 2.0
@@ -39,7 +39,7 @@ config MSM_CSI20_HEADER
8930 and 8064 platforms.
config MSM_CSI22_HEADER
- bool "Qualcomm MSM CSI 2.2 Header"
+ bool "QTI MSM CSI 2.2 Header"
depends on MSMB_CAMERA
---help---
Enable support for CSI drivers to include 2.2
@@ -49,7 +49,7 @@ config MSM_CSI22_HEADER
platform.
config MSM_CSI30_HEADER
- bool "Qualcomm MSM CSI 3.0 Header"
+ bool "QTI MSM CSI 3.0 Header"
depends on MSMB_CAMERA
---help---
Enable support for CSI drivers to include 3.0
@@ -59,7 +59,7 @@ config MSM_CSI30_HEADER
8064 platforms.
config MSM_CSI31_HEADER
- bool "Qualcomm MSM CSI 3.1 Header"
+ bool "QTI MSM CSI 3.1 Header"
depends on MSMB_CAMERA
---help---
Enable support for CSI drivers to include 3.0
@@ -69,7 +69,7 @@ config MSM_CSI31_HEADER
APQ8084 platform.
config MSM_CSIPHY
- bool "Qualcomm MSM Camera Serial Interface Physical receiver support"
+ bool "QTI MSM Camera Serial Interface Physical receiver support"
depends on MSMB_CAMERA
---help---
Enable support for Camera Serial Interface
@@ -78,7 +78,7 @@ config MSM_CSIPHY
signalling.
config MSM_CSID
- bool "Qualcomm MSM Camera Serial Interface decoder support"
+ bool "QTI MSM Camera Serial Interface decoder support"
depends on MSMB_CAMERA
---help---
Enable support for Camera Serial Interface decoder.
@@ -87,7 +87,7 @@ config MSM_CSID
and datatype.
config MSM_EEPROM
- bool "Qualcomm MSM Camera ROM Interface for Calibration support"
+ bool "QTI MSM Camera ROM Interface for Calibration support"
depends on MSMB_CAMERA
---help---
Enable support for ROM Interface for Calibration
@@ -96,7 +96,7 @@ config MSM_EEPROM
Currently supports I2C, CCI and SPI protocol
config MSM_ISPIF
- bool "Qualcomm MSM Image Signal Processing interface support"
+ bool "QTI MSM Image Signal Processing interface support"
depends on MSMB_CAMERA
---help---
Enable support for Image Signal Processing interface module.
@@ -105,7 +105,7 @@ config MSM_ISPIF
data interface in VFE.
config MSM_ISPIF_V1
- bool "Qualcomm MSM Image Signal Processing interface support"
+ bool "QTI MSM Image Signal Processing interface support"
depends on MSMB_CAMERA
---help---
Enable support for Image Signal Processing interface module.
@@ -114,7 +114,7 @@ config MSM_ISPIF_V1
or raw data interface in VFE.
config MSM_ISPIF_V2
- bool "Qualcomm MSM Image Signal Processing interface support"
+ bool "QTI MSM Image Signal Processing interface support"
depends on MSMB_CAMERA
---help---
Enable support for Image Signal Processing interface module.
@@ -204,7 +204,7 @@ config OV12830
2 lanes max fps is 18, 4 lanes max fps is 24.
config MSM_V4L2_VIDEO_OVERLAY_DEVICE
- tristate "Qualcomm MSM V4l2 video overlay device"
+ tristate "QTI MSM V4l2 video overlay device"
---help---
Enables support for the MSM V4L2 video
overlay driver. This allows video rendering
@@ -212,7 +212,7 @@ config MSM_V4L2_VIDEO_OVERLAY_DEVICE
APIs, by using /dev/videoX device
config MSMB_JPEG
- tristate "Qualcomm MSM Jpeg Encoder Engine support"
+ tristate "QTI MSM Jpeg Encoder Engine support"
depends on MSMB_CAMERA && (ARCH_MSM8974 || ARCH_MSM8226 || ARCH_APQ8084 || ARCH_MSM8916 || ARCH_QCOM)
---help---
Enable support for Jpeg Encoder/Decoder
@@ -221,7 +221,7 @@ config MSMB_JPEG
for the JPEG 1.0 encoder and decoder.
config MSM_GEMINI
- tristate "Qualcomm MSM Gemini JPEG engine support"
+ tristate "QTI MSM Gemini JPEG engine support"
depends on MSMB_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM8960)
---help---
Enables support for the Gemini JPEG encoder
@@ -230,14 +230,26 @@ config MSM_GEMINI
for JPEG encoding functionality.
config MSM_FD
- tristate "Qualcomm MSM FD face detection engine support"
+ tristate "QTI MSM FD face detection engine support"
depends on MSMB_CAMERA
---help---
Enables support for the MSM FD face detection engine.
config MSM_JPEGDMA
- tristate "Qualcomm Technologies Inc. MSM Jpeg dma"
+ tristate "QTI MSM Jpeg dma"
depends on MSMB_CAMERA
select V4L2_MEM2MEM_DEV
---help---
Enable support for Jpeg dma engine.
+
+config MSM_SEC_CCI_TA_NAME
+ string "Name of TA to handle Secure CCI transactions"
+ depends on MSM_CCI
+ default "seccamdemo64"
+
+config MSM_SEC_CCI_DEBUG
+ bool "QTI MSM Secure CCI Relay Debug"
+ depends on MSM_CCI
+ ---help---
+ Enables simulation of secure camera for Secure CCI Realy
+ debugging.
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
index d3f6fa3fa52d..4200215705d0 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
@@ -463,7 +463,8 @@ static int msm_isp_buf_unprepare(struct msm_isp_buf_mgr *buf_mgr,
static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id,
- uint32_t bufq_handle, struct msm_isp_buffer **buf_info)
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct msm_isp_buffer **buf_info)
{
int rc = -1;
unsigned long flags;
@@ -513,8 +514,12 @@ static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id,
}
break;
case MSM_ISP_BUFFER_SRC_HAL:
- vb2_v4l2_buf = buf_mgr->vb2_ops->get_buf(
- bufq->session_id, bufq->stream_id);
+ if (buf_index == MSM_ISP_INVALID_BUF_INDEX)
+ vb2_v4l2_buf = buf_mgr->vb2_ops->get_buf(
+ bufq->session_id, bufq->stream_id);
+ else
+ vb2_v4l2_buf = buf_mgr->vb2_ops->get_buf_by_idx(
+ bufq->session_id, bufq->stream_id, buf_index);
if (vb2_v4l2_buf) {
if (vb2_v4l2_buf->vb2_buf.index < bufq->num_bufs) {
*buf_info = &bufq->bufs[vb2_v4l2_buf
@@ -649,95 +654,35 @@ static int msm_isp_put_buf(struct msm_isp_buf_mgr *buf_mgr,
return rc;
}
-static int msm_isp_update_put_buf_cnt_unsafe(
- struct msm_isp_buf_mgr *buf_mgr,
- uint32_t id, uint32_t bufq_handle, int32_t buf_index,
- struct timeval *tv, uint32_t frame_id, uint32_t pingpong_bit)
+static int msm_isp_buf_divert(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct timeval *tv, uint32_t frame_id)
{
- int rc = -1;
+ unsigned long flags;
struct msm_isp_bufq *bufq = NULL;
struct msm_isp_buffer *buf_info = NULL;
- uint8_t *put_buf_mask = NULL;
bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
if (!bufq) {
pr_err("Invalid bufq\n");
- return rc;
- }
-
- put_buf_mask = &bufq->put_buf_mask[pingpong_bit];
-
- if (buf_index >= 0) {
- buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
- if (!buf_info) {
- pr_err("%s: buf not found\n", __func__);
- return -EFAULT;
- }
- if (buf_info->state != MSM_ISP_BUFFER_STATE_DEQUEUED) {
- pr_err(
- "%s: Invalid state, bufq_handle %x stream id %x, state %d\n",
- __func__, bufq_handle,
- bufq->stream_id, buf_info->state);
- return -EFAULT;
- }
- if (buf_info->pingpong_bit != pingpong_bit) {
- pr_err("%s: Pingpong bit mismatch\n", __func__);
- return -EFAULT;
- }
- }
-
- if (bufq->buf_type != ISP_SHARE_BUF ||
- (*put_buf_mask == 0)) {
- if (buf_info)
- buf_info->frame_id = frame_id;
- }
-
- if (bufq->buf_type == ISP_SHARE_BUF &&
- ((*put_buf_mask & (1 << id)) == 0)) {
- *put_buf_mask |= (1 << id);
- if (*put_buf_mask != ISP_SHARE_BUF_MASK) {
- rc = *put_buf_mask;
- return 1;
- }
- *put_buf_mask = 0;
- rc = 0;
- } else if (bufq->buf_type == ISP_SHARE_BUF &&
- (*put_buf_mask & (1 << id)) != 0) {
- return -ENOTEMPTY;
+ return -EINVAL;
}
- if (buf_info &&
- MSM_ISP_BUFFER_SRC_NATIVE == BUF_SRC(bufq->stream_id)) {
- buf_info->state = MSM_ISP_BUFFER_STATE_DIVERTED;
- buf_info->tv = tv;
- }
- return 0;
-}
-
-static int msm_isp_update_put_buf_cnt(struct msm_isp_buf_mgr *buf_mgr,
- uint32_t id, uint32_t bufq_handle, int32_t buf_index,
- struct timeval *tv, uint32_t frame_id, uint32_t pingpong_bit)
-{
- int rc = -1;
- struct msm_isp_bufq *bufq = NULL;
- unsigned long flags;
-
- bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
- if (!bufq) {
- pr_err("Invalid bufq\n");
- return rc;
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return -EINVAL;
}
spin_lock_irqsave(&bufq->bufq_lock, flags);
- rc = msm_isp_update_put_buf_cnt_unsafe(buf_mgr, id, bufq_handle,
- buf_index, tv, frame_id, pingpong_bit);
- if (-ENOTEMPTY == rc) {
- pr_err("%s: Error! Uncleared put_buf_mask for pingpong(%d) from vfe %d bufq 0x%x buf_idx %d\n",
- __func__, pingpong_bit, id, bufq_handle, buf_index);
- rc = -EFAULT;
+
+ buf_info->frame_id = frame_id;
+ if (BUF_SRC(bufq->stream_id) == MSM_ISP_BUFFER_SRC_NATIVE) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_DIVERTED;
+ buf_info->tv = tv;
}
spin_unlock_irqrestore(&bufq->bufq_lock, flags);
- return rc;
+ return 0;
}
static int msm_isp_buf_done(struct msm_isp_buf_mgr *buf_mgr,
@@ -795,11 +740,11 @@ done:
return rc;
}
-static int msm_isp_flush_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id,
+static int msm_isp_flush_buf(struct msm_isp_buf_mgr *buf_mgr,
uint32_t bufq_handle, enum msm_isp_buffer_flush_t flush_type,
struct timeval *tv, uint32_t frame_id)
{
- int rc = 0, i;
+ int i;
struct msm_isp_bufq *bufq = NULL;
struct msm_isp_buffer *buf_info = NULL;
unsigned long flags;
@@ -817,43 +762,27 @@ static int msm_isp_flush_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id,
pr_err("%s: buf not found\n", __func__);
continue;
}
- if (flush_type == MSM_ISP_BUFFER_FLUSH_DIVERTED &&
- buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ switch (flush_type) {
+ case MSM_ISP_BUFFER_FLUSH_DIVERTED:
+ if (buf_info->state !=
+ MSM_ISP_BUFFER_STATE_DIVERTED)
+ continue;
buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
msm_isp_put_buf_unsafe(buf_mgr,
- bufq_handle, buf_info->buf_idx);
- } else if (flush_type == MSM_ISP_BUFFER_FLUSH_ALL) {
- if (buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED) {
- CDBG("%s: no need to queue Diverted buffer\n",
- __func__);
- } else if (buf_info->state ==
- MSM_ISP_BUFFER_STATE_DEQUEUED) {
- rc = msm_isp_update_put_buf_cnt_unsafe(buf_mgr,
- id, bufq_handle, buf_info->buf_idx, tv,
- frame_id, buf_info->pingpong_bit);
- if (-ENOTEMPTY == rc) {
- rc = 0;
- continue;
- }
-
- if (rc == 0) {
- buf_info->buf_debug.put_state[
- buf_info->buf_debug.
- put_state_last]
- = MSM_ISP_BUFFER_STATE_FLUSH;
- buf_info->buf_debug.put_state_last ^= 1;
- buf_info->state =
- MSM_ISP_BUFFER_STATE_PREPARED;
- rc = msm_isp_put_buf_unsafe(buf_mgr,
- bufq_handle, buf_info->buf_idx);
- if (rc == -EFAULT) {
- spin_unlock_irqrestore(
- &bufq->bufq_lock,
- flags);
- return rc;
- }
- }
- }
+ bufq_handle, buf_info->buf_idx);
+ break;
+ case MSM_ISP_BUFFER_FLUSH_ALL:
+ if (buf_info->state ==
+ MSM_ISP_BUFFER_STATE_DIVERTED)
+ continue;
+ if (buf_info->state !=
+ MSM_ISP_BUFFER_STATE_DEQUEUED)
+ continue;
+ msm_isp_put_buf_unsafe(buf_mgr,
+ bufq_handle, buf_info->buf_idx);
+ break;
+ default:
+ WARN(1, "Invalid flush type %d\n", flush_type);
}
}
@@ -1031,8 +960,6 @@ static int msm_isp_request_bufq(struct msm_isp_buf_mgr *buf_mgr,
bufq->stream_id = buf_request->stream_id;
bufq->num_bufs = buf_request->num_buf;
bufq->buf_type = buf_request->buf_type;
- for (i = 0; i < ISP_NUM_BUF_MASK; i++)
- bufq->put_buf_mask[i] = 0;
INIT_LIST_HEAD(&bufq->head);
for (i = 0; i < buf_request->num_buf; i++) {
@@ -1448,7 +1375,7 @@ static struct msm_isp_buf_ops isp_buf_ops = {
.buf_mgr_deinit = msm_isp_deinit_isp_buf_mgr,
.buf_mgr_debug = msm_isp_buf_mgr_debug,
.get_bufq = msm_isp_get_bufq,
- .update_put_buf_cnt = msm_isp_update_put_buf_cnt,
+ .buf_divert = msm_isp_buf_divert,
};
int msm_isp_create_isp_buf_mgr(
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h
index b22fb6a43145..43519ee74062 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.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
@@ -32,6 +32,8 @@
#define BUF_MGR_NUM_BUF_Q 28
#define MAX_IOMMU_CTX 2
+#define MSM_ISP_INVALID_BUF_INDEX 0xFFFFFFFF
+
struct msm_isp_buf_mgr;
enum msm_isp_buffer_src_t {
@@ -115,7 +117,6 @@ struct msm_isp_bufq {
enum msm_isp_buf_type buf_type;
struct msm_isp_buffer *bufs;
spinlock_t bufq_lock;
- uint8_t put_buf_mask[ISP_NUM_BUF_MASK];
/*Native buffer queue*/
struct list_head head;
};
@@ -140,7 +141,8 @@ struct msm_isp_buf_ops {
uint32_t bufq_handle, uint32_t *buf_src);
int (*get_buf)(struct msm_isp_buf_mgr *buf_mgr, uint32_t id,
- uint32_t bufq_handle, struct msm_isp_buffer **buf_info);
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct msm_isp_buffer **buf_info);
int (*get_buf_by_index)(struct msm_isp_buf_mgr *buf_mgr,
uint32_t bufq_handle, uint32_t buf_index,
@@ -154,7 +156,7 @@ struct msm_isp_buf_ops {
int (*put_buf)(struct msm_isp_buf_mgr *buf_mgr,
uint32_t bufq_handle, uint32_t buf_index);
- int (*flush_buf)(struct msm_isp_buf_mgr *buf_mgr, uint32_t id,
+ int (*flush_buf)(struct msm_isp_buf_mgr *buf_mgr,
uint32_t bufq_handle, enum msm_isp_buffer_flush_t flush_type,
struct timeval *tv, uint32_t frame_id);
@@ -171,9 +173,9 @@ struct msm_isp_buf_ops {
unsigned long fault_addr);
struct msm_isp_bufq * (*get_bufq)(struct msm_isp_buf_mgr *buf_mgr,
uint32_t bufq_handle);
- int (*update_put_buf_cnt)(struct msm_isp_buf_mgr *buf_mgr,
- uint32_t id, uint32_t bufq_handle, int32_t buf_index,
- struct timeval *tv, uint32_t frame_id, uint32_t pingpong_bit);
+ int (*buf_divert)(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct timeval *tv, uint32_t frame_id);
};
struct msm_isp_buf_mgr {
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
index d3c2d77b0107..094996b2d60b 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
@@ -498,7 +498,12 @@ static int vfe_probe(struct platform_device *pdev)
vfe_parent_dev->common_sd->common_data = &vfe_common_data;
memset(&vfe_common_data, 0, sizeof(vfe_common_data));
+ mutex_init(&vfe_common_data.vfe_common_mutex);
spin_lock_init(&vfe_common_data.common_dev_data_lock);
+ for (i = 0; i < (VFE_AXI_SRC_MAX * MAX_VFE); i++)
+ spin_lock_init(&(vfe_common_data.streams[i].lock));
+ for (i = 0; i < (MSM_ISP_STATS_MAX * MAX_VFE); i++)
+ spin_lock_init(&(vfe_common_data.stats_streams[i].lock));
of_property_read_u32(pdev->dev.of_node,
"num_child", &vfe_parent_dev->num_hw_sd);
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
index 763b6a575326..3b6a2eecb4b6 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -169,7 +169,7 @@ struct msm_vfe_axi_ops {
int32_t (*cfg_io_format)(struct vfe_device *vfe_dev,
enum msm_vfe_axi_stream_src stream_src,
uint32_t io_format);
- void (*cfg_framedrop)(void __iomem *vfe_base,
+ void (*cfg_framedrop)(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info,
uint32_t framedrop_pattern, uint32_t framedrop_period);
void (*clear_framedrop)(struct vfe_device *vfe_dev,
@@ -207,7 +207,7 @@ struct msm_vfe_axi_ops {
uint32_t (*get_comp_mask)(uint32_t irq_status0, uint32_t irq_status1);
uint32_t (*get_pingpong_status)(struct vfe_device *vfe_dev);
int (*halt)(struct vfe_device *vfe_dev, uint32_t blocking);
- int (*restart)(struct vfe_device *vfe_dev, uint32_t blocking,
+ void (*restart)(struct vfe_device *vfe_dev, uint32_t blocking,
uint32_t enable_camif);
void (*update_cgc_override)(struct vfe_device *vfe_dev,
uint8_t wm_idx, uint8_t cgc_override);
@@ -270,7 +270,7 @@ struct msm_vfe_stats_ops {
void (*enable_module)(struct vfe_device *vfe_dev,
uint32_t stats_mask, uint8_t enable);
- void (*update_ping_pong_addr)(void __iomem *vfe_base,
+ void (*update_ping_pong_addr)(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info,
uint32_t pingpong_status, dma_addr_t paddr);
@@ -373,12 +373,6 @@ enum msm_vfe_axi_state {
UPDATING,
};
-enum msm_vfe_axi_cfg_update_state {
- NO_AXI_CFG_UPDATE,
- APPLYING_UPDATE_RESUME,
- UPDATE_REQUESTED,
-};
-
#define VFE_NO_DROP 0xFFFFFFFF
#define VFE_DROP_EVERY_2FRAME 0x55555555
#define VFE_DROP_EVERY_4FRAME 0x11111111
@@ -394,9 +388,18 @@ enum msm_vfe_axi_stream_type {
struct msm_vfe_frame_request_queue {
struct list_head list;
enum msm_vfe_buff_queue_id buff_queue_id;
+ uint32_t buf_index;
uint8_t cmd_used;
};
+enum msm_isp_comp_irq_types {
+ MSM_ISP_COMP_IRQ_REG_UPD = 0,
+ MSM_ISP_COMP_IRQ_EPOCH = 1,
+ MSM_ISP_COMP_IRQ_PING_BUFDONE = 2,
+ MSM_ISP_COMP_IRQ_PONG_BUFDONE = 3,
+ MSM_ISP_COMP_IRQ_MAX = 4
+};
+
#define MSM_VFE_REQUESTQ_SIZE 8
struct msm_vfe_axi_stream {
@@ -404,10 +407,10 @@ struct msm_vfe_axi_stream {
enum msm_vfe_axi_state state;
enum msm_vfe_axi_stream_src stream_src;
uint8_t num_planes;
- uint8_t wm[MAX_PLANES_PER_STREAM];
+ uint8_t wm[MAX_VFE][MAX_PLANES_PER_STREAM];
uint32_t output_format;/*Planar/RAW/Misc*/
- struct msm_vfe_axi_plane_cfg plane_cfg[MAX_PLANES_PER_STREAM];
- uint8_t comp_mask_index;
+ struct msm_vfe_axi_plane_cfg plane_cfg[MAX_VFE][MAX_PLANES_PER_STREAM];
+ uint8_t comp_mask_index[MAX_VFE];
struct msm_isp_buffer *buf[2];
uint32_t session_id;
uint32_t stream_id;
@@ -419,7 +422,7 @@ struct msm_vfe_axi_stream {
struct list_head request_q;
struct msm_vfe_frame_request_queue
request_queue_cmd[MSM_VFE_REQUESTQ_SIZE];
- uint32_t stream_handle;
+ uint32_t stream_handle[MAX_VFE];
uint8_t buf_divert;
enum msm_vfe_axi_stream_type stream_type;
uint32_t frame_based;
@@ -432,16 +435,28 @@ struct msm_vfe_axi_stream {
spinlock_t lock;
/*Bandwidth calculation info*/
- uint32_t max_width;
+ uint32_t max_width[MAX_VFE];
/*Based on format plane size in Q2. e.g NV12 = 1.5*/
uint32_t format_factor;
- uint32_t bandwidth;
+ uint32_t bandwidth[MAX_VFE];
uint32_t runtime_num_burst_capture;
uint32_t runtime_output_format;
enum msm_stream_memory_input_t memory_input;
struct msm_isp_sw_framskip sw_skip;
uint8_t sw_ping_pong_bit;
+
+ struct vfe_device *vfe_dev[MAX_VFE];
+ int num_isp;
+ struct completion active_comp;
+ struct completion inactive_comp;
+ uint32_t update_vfe_mask;
+ /*
+ * bits in this mask are set that correspond to vfe_id of
+ * the vfe on which this stream operates
+ */
+ uint32_t vfe_mask;
+ uint32_t composite_irq[MSM_ISP_COMP_IRQ_MAX];
};
struct msm_vfe_axi_composite_info {
@@ -450,17 +465,15 @@ struct msm_vfe_axi_composite_info {
};
enum msm_vfe_camif_state {
- CAMIF_STOPPED,
CAMIF_ENABLE,
CAMIF_DISABLE,
- CAMIF_STOPPING,
};
struct msm_vfe_src_info {
uint32_t frame_id;
uint32_t reg_update_frame_id;
uint8_t active;
- uint8_t pix_stream_count;
+ uint8_t stream_count;
uint8_t raw_stream_count;
enum msm_vfe_inputmux input_mux;
uint32_t width;
@@ -491,7 +504,6 @@ enum msm_wm_ub_cfg_type {
struct msm_vfe_axi_shared_data {
struct msm_vfe_axi_hardware_info *hw_info;
- struct msm_vfe_axi_stream stream_info[VFE_AXI_SRC_MAX];
uint32_t free_wm[MAX_NUM_WM];
uint32_t wm_image_size[MAX_NUM_WM];
enum msm_wm_ub_cfg_type wm_ub_cfg_policy;
@@ -503,14 +515,11 @@ struct msm_vfe_axi_shared_data {
struct msm_vfe_axi_composite_info
composite_info[MAX_NUM_COMPOSITE_MASK];
uint8_t num_used_composite_mask;
- uint32_t stream_update[VFE_SRC_MAX];
atomic_t axi_cfg_update[VFE_SRC_MAX];
- enum msm_isp_camif_update_state pipeline_update;
struct msm_vfe_src_info src_info[VFE_SRC_MAX];
uint16_t stream_handle_cnt;
uint32_t event_mask;
uint8_t enable_frameid_recovery;
- enum msm_vfe_camif_state camif_state;
};
struct msm_vfe_stats_hardware_info {
@@ -522,7 +531,7 @@ struct msm_vfe_stats_hardware_info {
};
enum msm_vfe_stats_state {
- STATS_AVALIABLE,
+ STATS_AVAILABLE,
STATS_INACTIVE,
STATS_ACTIVE,
STATS_START_PENDING,
@@ -534,7 +543,7 @@ enum msm_vfe_stats_state {
struct msm_vfe_stats_stream {
uint32_t session_id;
uint32_t stream_id;
- uint32_t stream_handle;
+ uint32_t stream_handle[MAX_VFE];
uint32_t composite_flag;
enum msm_isp_stats_type stats_type;
enum msm_vfe_stats_state state;
@@ -544,17 +553,27 @@ struct msm_vfe_stats_stream {
uint32_t init_stats_frame_drop;
struct msm_isp_sw_framskip sw_skip;
- uint32_t buffer_offset;
+ uint32_t buffer_offset[MAX_VFE];
struct msm_isp_buffer *buf[2];
uint32_t bufq_handle;
+
+ spinlock_t lock;
+ struct vfe_device *vfe_dev[MAX_VFE];
+ int num_isp;
+ struct completion active_comp;
+ struct completion inactive_comp;
+ /*
+ * bits in this mask are set that correspond to vfe_id of
+ * the vfe on which this stream operates
+ */
+ uint32_t vfe_mask;
+ uint32_t composite_irq[MSM_ISP_COMP_IRQ_MAX];
};
struct msm_vfe_stats_shared_data {
- struct msm_vfe_stats_stream stream_info[MSM_ISP_STATS_MAX];
uint8_t num_active_stream;
atomic_t stats_comp_mask[MAX_NUM_STATS_COMP_MASK];
uint16_t stream_handle_cnt;
- atomic_t stats_update;
};
struct msm_vfe_tasklet_queue_cmd {
@@ -653,7 +672,6 @@ struct dual_vfe_resource {
struct msm_vfe_stats_shared_data *stats_data[MAX_VFE];
struct msm_vfe_axi_shared_data *axi_data[MAX_VFE];
uint32_t wm_reload_mask[MAX_VFE];
- uint32_t epoch_sync_mask;
};
struct master_slave_resource_info {
@@ -671,6 +689,9 @@ struct msm_vfe_common_dev_data {
spinlock_t common_dev_data_lock;
struct dual_vfe_resource *dual_vfe_res;
struct master_slave_resource_info ms_resource;
+ struct msm_vfe_axi_stream streams[VFE_AXI_SRC_MAX * MAX_VFE];
+ struct msm_vfe_stats_stream stats_streams[MSM_ISP_STATS_MAX * MAX_VFE];
+ struct mutex vfe_common_mutex;
};
struct msm_vfe_common_subdev {
@@ -713,8 +734,6 @@ struct vfe_device {
/* Sync variables*/
struct completion reset_complete;
struct completion halt_complete;
- struct completion stream_config_complete;
- struct completion stats_config_complete;
struct mutex realtime_mutex;
struct mutex core_mutex;
spinlock_t shared_data_lock;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
index 9481bede6417..8b5a3d8d508d 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
@@ -412,10 +412,9 @@ static void msm_vfe32_process_camif_irq(struct vfe_device *vfe_dev,
ISP_DBG("%s: SOF IRQ\n", __func__);
if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
&& vfe_dev->axi_data.src_info[VFE_PIX_0].
- pix_stream_count == 0) {
+ stream_count == 0) {
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
- if (vfe_dev->axi_data.stream_update[VFE_PIX_0])
- msm_isp_axi_stream_update(vfe_dev, VFE_PIX_0);
+ msm_isp_axi_stream_update(vfe_dev, VFE_PIX_0, ts);
msm_isp_update_framedrop_reg(vfe_dev, VFE_PIX_0);
}
}
@@ -608,15 +607,14 @@ static void msm_vfe32_process_reg_update(struct vfe_device *vfe_dev,
if ((rdi_status & BIT(7)) && (!(irq_status0 & 0x20)))
return;
}
- if (atomic_read(&vfe_dev->stats_data.stats_update))
- msm_isp_stats_stream_update(vfe_dev);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_REG_UPD);
}
for (i = VFE_RAW_0; i <= VFE_RAW_2; i++) {
if (irq_status1 & BIT(26 + (i - VFE_RAW_0))) {
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
- if (vfe_dev->axi_data.stream_update[i])
- msm_isp_axi_stream_update(vfe_dev, i);
+ msm_isp_axi_stream_update(vfe_dev, i, ts);
msm_isp_update_framedrop_reg(vfe_dev, i);
vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev,
@@ -693,8 +691,9 @@ static void msm_vfe32_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
uint32_t comp_mask, comp_mask_index =
- stream_info->comp_mask_index;
+ stream_info->comp_mask_index[vfe_idx];
uint32_t irq_mask;
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x34);
@@ -711,7 +710,9 @@ static void msm_vfe32_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
static void msm_vfe32_axi_clear_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index =
+ stream_info->comp_mask_index[vfe_idx];
uint32_t irq_mask;
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x34);
@@ -727,8 +728,10 @@ static void msm_vfe32_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
uint32_t irq_mask;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
- irq_mask |= BIT(stream_info->wm[0] + 6);
+ irq_mask |= BIT(stream_info->wm[vfe_idx][0] + 6);
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
}
@@ -736,15 +739,19 @@ static void msm_vfe32_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
uint32_t irq_mask;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
- irq_mask &= ~BIT(stream_info->wm[0] + 6);
+ irq_mask &= ~BIT(stream_info->wm[vfe_idx][0] + 6);
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
}
-static void msm_vfe32_cfg_framedrop(void __iomem *vfe_base,
+static void msm_vfe32_cfg_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
uint32_t framedrop_period)
{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+
if (stream_info->stream_src == PIX_ENCODER) {
msm_camera_io_w(framedrop_period - 1, vfe_base + 0x504);
msm_camera_io_w(framedrop_period - 1, vfe_base + 0x508);
@@ -929,7 +936,7 @@ static void msm_vfe32_update_camif_state(
VFE_PIX_0].raw_stream_count > 0) ? 1 : 0);
vfe_en =
((vfe_dev->axi_data.src_info[
- VFE_PIX_0].pix_stream_count > 0) ? 1 : 0);
+ VFE_PIX_0].stream_count > 0) ? 1 : 0);
val &= 0xFFFFFF3F;
val = val | bus_en << 7 | vfe_en << 6;
msm_camera_io_w(val, vfe_dev->vfe_base + 0x1E4);
@@ -971,16 +978,17 @@ static void msm_vfe32_axi_cfg_wm_reg(
uint8_t plane_idx)
{
uint32_t val;
- uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
if (!stream_info->frame_based) {
/*WR_IMAGE_SIZE*/
val =
((msm_isp_cal_word_per_line(
stream_info->output_format,
- stream_info->plane_cfg[plane_idx].
+ stream_info->plane_cfg[vfe_idx][plane_idx].
output_width)+1)/2 - 1) << 16 |
- (stream_info->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
output_height - 1);
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10);
@@ -988,9 +996,9 @@ static void msm_vfe32_axi_cfg_wm_reg(
val =
msm_isp_cal_word_per_line(
stream_info->output_format,
- stream_info->plane_cfg[plane_idx].
+ stream_info->plane_cfg[vfe_idx][plane_idx].
output_stride) << 16 |
- (stream_info->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
output_height - 1) << 4 | VFE32_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
} else {
@@ -998,9 +1006,9 @@ static void msm_vfe32_axi_cfg_wm_reg(
val =
msm_isp_cal_word_per_line(
stream_info->output_format,
- stream_info->plane_cfg[plane_idx].
+ stream_info->plane_cfg[vfe_idx][plane_idx].
output_width) << 16 |
- (stream_info->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
output_height - 1) << 4 | VFE32_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
}
@@ -1012,7 +1020,8 @@ static void msm_vfe32_axi_clear_wm_reg(
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
uint32_t val = 0;
- uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
/*WR_IMAGE_SIZE*/
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10);
/*WR_BUFFER_CFG*/
@@ -1024,9 +1033,10 @@ static void msm_vfe32_axi_cfg_wm_xbar_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
struct msm_vfe_axi_plane_cfg *plane_cfg =
- &stream_info->plane_cfg[plane_idx];
- uint8_t wm = stream_info->wm[plane_idx];
+ &stream_info->plane_cfg[vfe_idx][plane_idx];
+ uint8_t wm = stream_info->wm[vfe_idx][plane_idx];
uint32_t xbar_cfg = 0;
uint32_t xbar_reg_cfg = 0;
@@ -1080,7 +1090,8 @@ static void msm_vfe32_axi_clear_wm_xbar_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
- uint8_t wm = stream_info->wm[plane_idx];
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint8_t wm = stream_info->wm[vfe_idx][plane_idx];
uint32_t xbar_reg_cfg = 0;
xbar_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
@@ -1098,6 +1109,7 @@ static void msm_vfe32_cfg_axi_ub_equal_default(struct vfe_device *vfe_dev)
uint32_t prop_size = 0;
uint32_t wm_ub_size;
uint64_t delta;
+
for (i = 0; i < axi_data->hw_info->num_wm; i++) {
if (axi_data->free_wm[i] > 0) {
num_used_wms++;
@@ -1243,9 +1255,11 @@ static void msm_vfe32_stats_cfg_comp_mask(struct vfe_device *vfe_dev,
static void msm_vfe32_stats_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
uint32_t irq_mask;
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
- irq_mask |= BIT(STATS_IDX(stream_info->stream_handle) + 13);
+ irq_mask |= BIT(STATS_IDX(stream_info->stream_handle[vfe_idx]) + 13);
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
return;
}
@@ -1342,12 +1356,15 @@ static void msm_vfe32_stats_enable_module(struct vfe_device *vfe_dev,
msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x10);
}
-static void msm_vfe32_stats_update_ping_pong_addr(void __iomem *vfe_base,
+static void msm_vfe32_stats_update_ping_pong_addr(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status,
dma_addr_t paddr)
{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
uint32_t paddr32 = (paddr & 0xFFFFFFFF);
- int stats_idx = STATS_IDX(stream_info->stream_handle);
+ int stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
msm_camera_io_w(paddr32, vfe_base +
VFE32_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index d42ada769380..a2aa2983b056 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -599,7 +599,6 @@ static void msm_vfe40_process_reg_update(struct vfe_device *vfe_dev,
return;
/* Shift status bits so that PIX REG UPDATE is 1st bit */
shift_irq = ((irq_status0 & 0xF0) >> 4);
-
for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
if (shift_irq & BIT(i)) {
reg_updated |= BIT(i);
@@ -607,15 +606,17 @@ static void msm_vfe40_process_reg_update(struct vfe_device *vfe_dev,
(uint32_t)BIT(i));
switch (i) {
case VFE_PIX_0:
- msm_isp_save_framedrop_values(vfe_dev,
- VFE_PIX_0);
msm_isp_notify(vfe_dev, ISP_EVENT_REG_UPDATE,
VFE_PIX_0, ts);
- if (atomic_read(
- &vfe_dev->stats_data.stats_update))
- msm_isp_stats_stream_update(vfe_dev);
- if (vfe_dev->axi_data.camif_state ==
- CAMIF_STOPPING)
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_REG_UPD);
+ if (vfe_dev->axi_data.src_info[i].stream_count
+ == 0 &&
+ vfe_dev->axi_data.src_info[i].
+ raw_stream_count == 0 &&
+ vfe_dev->axi_data.src_info[i].active)
vfe_dev->hw_info->vfe_ops.core_ops.
reg_update(vfe_dev, i);
break;
@@ -624,29 +625,22 @@ static void msm_vfe40_process_reg_update(struct vfe_device *vfe_dev,
case VFE_RAW_2:
msm_isp_increment_frame_id(vfe_dev, i, ts);
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
- msm_isp_update_framedrop_reg(vfe_dev, i);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
/*
* Reg Update is pseudo SOF for RDI,
* so request every frame
*/
vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
vfe_dev, i);
+ /* reg upd is also epoch for RDI */
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
break;
default:
pr_err("%s: Error case\n", __func__);
return;
}
- if (vfe_dev->axi_data.stream_update[i])
- msm_isp_axi_stream_update(vfe_dev, i);
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i])) {
- msm_isp_axi_cfg_update(vfe_dev, i);
- if (atomic_read(
- &vfe_dev->axi_data.axi_cfg_update[i]) ==
- 0)
- msm_isp_notify(vfe_dev,
- ISP_EVENT_STREAM_UPDATE_DONE,
- i, ts);
- }
}
}
@@ -695,7 +689,9 @@ static void msm_vfe40_reg_update(struct vfe_device *vfe_dev,
vfe_dev->vfe_base + 0x378);
} else if (!vfe_dev->is_split ||
((frame_src == VFE_PIX_0) &&
- (vfe_dev->axi_data.camif_state == CAMIF_STOPPING)) ||
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].stream_count == 0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count == 0)) ||
(frame_src >= VFE_RAW_0 && frame_src <= VFE_SRC_MAX)) {
msm_camera_io_w_mb(update_mask,
vfe_dev->vfe_base + 0x378);
@@ -713,16 +709,18 @@ static void msm_vfe40_process_epoch_irq(struct vfe_device *vfe_dev,
if (irq_status0 & BIT(2)) {
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
ISP_DBG("%s: EPOCH0 IRQ\n", __func__);
- msm_isp_update_framedrop_reg(vfe_dev, VFE_PIX_0);
- msm_isp_update_stats_framedrop_reg(vfe_dev);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_EPOCH);
msm_isp_update_error_frame_count(vfe_dev);
if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
&& vfe_dev->axi_data.src_info[VFE_PIX_0].
- pix_stream_count == 0) {
+ stream_count == 0) {
ISP_DBG("%s: SOF IRQ\n", __func__);
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
- if (vfe_dev->axi_data.stream_update[VFE_PIX_0])
- msm_isp_axi_stream_update(vfe_dev, VFE_PIX_0);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
vfe_dev, VFE_PIX_0);
}
@@ -791,8 +789,10 @@ static void msm_vfe40_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- uint32_t comp_mask, comp_mask_index =
- stream_info->comp_mask_index;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
@@ -807,8 +807,11 @@ static void msm_vfe40_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
- vfe_dev->irq0_mask &= ~BIT(27);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
+ vfe_dev->irq0_mask &= ~BIT(27);
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
@@ -821,32 +824,38 @@ static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev,
static void msm_vfe40_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- msm_vfe40_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0,
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ msm_vfe40_config_irq(vfe_dev, 1 << (stream_info->wm[vfe_idx][0] + 8), 0,
MSM_ISP_IRQ_ENABLE);
}
static void msm_vfe40_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[0] + 8));
- msm_vfe40_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0,
- MSM_ISP_IRQ_DISABLE);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[vfe_idx][0] + 8));
+ msm_vfe40_config_irq(vfe_dev, (1 << (stream_info->wm[vfe_idx][0] + 8)),
+ 0, MSM_ISP_IRQ_DISABLE);
}
-static void msm_vfe40_cfg_framedrop(void __iomem *vfe_base,
+static void msm_vfe40_cfg_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
uint32_t framedrop_period)
{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
uint32_t i, temp;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
for (i = 0; i < stream_info->num_planes; i++) {
msm_camera_io_w(framedrop_pattern, vfe_base +
- VFE40_WM_BASE(stream_info->wm[i]) + 0x1C);
+ VFE40_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
temp = msm_camera_io_r(vfe_base +
- VFE40_WM_BASE(stream_info->wm[i]) + 0xC);
+ VFE40_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
temp &= 0xFFFFFF83;
msm_camera_io_w(temp | (framedrop_period - 1) << 2,
- vfe_base + VFE40_WM_BASE(stream_info->wm[i]) + 0xC);
+ vfe_base + VFE40_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
}
msm_camera_io_w_mb(0x1, vfe_base + 0x378);
@@ -856,9 +865,11 @@ static void msm_vfe40_clear_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
uint32_t i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
for (i = 0; i < stream_info->num_planes; i++)
msm_camera_io_w(0, vfe_dev->vfe_base +
- VFE40_WM_BASE(stream_info->wm[i]) + 0x1C);
+ VFE40_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
}
static int32_t msm_vfe40_convert_bpp_to_reg(int32_t bpp, uint32_t *bpp_reg)
@@ -1374,7 +1385,7 @@ static void msm_vfe40_update_camif_state(struct vfe_device *vfe_dev,
src_info[VFE_PIX_0].raw_stream_count > 0) ? 1 : 0);
vfe_en =
((vfe_dev->axi_data.
- src_info[VFE_PIX_0].pix_stream_count > 0) ? 1 : 0);
+ src_info[VFE_PIX_0].stream_count > 0) ? 1 : 0);
val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8);
val &= 0xFFFFFF3F;
val = val | bus_en << 7 | vfe_en << 6;
@@ -1443,7 +1454,10 @@ static void msm_vfe40_axi_cfg_wm_reg(
{
uint32_t val;
uint32_t burst_len, wm_bit_shift = VFE40_WM_BIT_SHIFT_8976_VERSION;
- uint32_t wm_base = VFE40_WM_BASE(stream_info->wm[plane_idx]);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE40_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
if (vfe_dev->vfe_hw_version == VFE40_8916_VERSION ||
vfe_dev->vfe_hw_version == VFE40_8939_VERSION) {
@@ -1468,18 +1482,18 @@ static void msm_vfe40_axi_cfg_wm_reg(
val =
((msm_isp_cal_word_per_line(
stream_info->output_format,
- stream_info->plane_cfg[plane_idx].
+ stream_info->plane_cfg[vfe_idx][plane_idx].
output_width)+1)/2 - 1) << 16 |
- (stream_info->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
output_height - 1);
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
/*WR_BUFFER_CFG*/
val =
msm_isp_cal_word_per_line(stream_info->output_format,
- stream_info->plane_cfg[
+ stream_info->plane_cfg[vfe_idx][
plane_idx].output_stride) << 16 |
- (stream_info->plane_cfg[
+ (stream_info->plane_cfg[vfe_idx][
plane_idx].output_height - 1) << wm_bit_shift |
burst_len;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
@@ -1487,9 +1501,9 @@ static void msm_vfe40_axi_cfg_wm_reg(
msm_camera_io_w(0x2, vfe_dev->vfe_base + wm_base);
val =
msm_isp_cal_word_per_line(stream_info->output_format,
- stream_info->plane_cfg[
+ stream_info->plane_cfg[vfe_idx][
plane_idx].output_width) << 16 |
- (stream_info->plane_cfg[
+ (stream_info->plane_cfg[vfe_idx][
plane_idx].output_height - 1) << 4 |
burst_len;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
@@ -1507,7 +1521,10 @@ static void msm_vfe40_axi_clear_wm_reg(
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
uint32_t val = 0;
- uint32_t wm_base = VFE40_WM_BASE(stream_info->wm[plane_idx]);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE40_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
/*WR_ADDR_CFG*/
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0xC);
/*WR_IMAGE_SIZE*/
@@ -1524,12 +1541,15 @@ static void msm_vfe40_axi_cfg_wm_xbar_reg(
struct msm_vfe_axi_stream *stream_info,
uint8_t plane_idx)
{
- struct msm_vfe_axi_plane_cfg *plane_cfg =
- &stream_info->plane_cfg[plane_idx];
- uint8_t wm = stream_info->wm[plane_idx];
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ struct msm_vfe_axi_plane_cfg *plane_cfg;
+ uint8_t wm;
uint32_t xbar_cfg = 0;
uint32_t xbar_reg_cfg = 0;
+ plane_cfg = &stream_info->plane_cfg[vfe_idx][plane_idx];
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
switch (stream_info->stream_src) {
case PIX_ENCODER:
case PIX_VIEWFINDER: {
@@ -1584,9 +1604,12 @@ static void msm_vfe40_axi_clear_wm_xbar_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
- uint8_t wm = stream_info->wm[plane_idx];
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint8_t wm;
uint32_t xbar_reg_cfg = 0;
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
xbar_reg_cfg =
msm_camera_io_r(vfe_dev->vfe_base + VFE40_XBAR_BASE(wm));
xbar_reg_cfg &= ~(0xFFFF << VFE40_XBAR_SHIFT(wm));
@@ -1714,6 +1737,7 @@ static int msm_vfe40_axi_halt(struct vfe_device *vfe_dev,
{
int rc = 0;
enum msm_vfe_input_src i;
+ struct msm_isp_timestamp ts;
/* Keep only halt and restart mask */
msm_vfe40_config_irq(vfe_dev, (1 << 31), (1 << 8),
@@ -1722,30 +1746,16 @@ static int msm_vfe40_axi_halt(struct vfe_device *vfe_dev,
msm_camera_io_w(0x7FFFFFFF, vfe_dev->vfe_base + 0x30);
msm_camera_io_w(0xFEFFFEFF, vfe_dev->vfe_base + 0x34);
msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x24);
+
+ msm_isp_get_timestamp(&ts);
/* if any stream is waiting for update, signal complete */
for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
- /* if any stream is waiting for update, signal complete */
- if (vfe_dev->axi_data.stream_update[i]) {
- ISP_DBG("%s: complete stream update\n", __func__);
- msm_isp_axi_stream_update(vfe_dev, i);
- if (vfe_dev->axi_data.stream_update[i])
- msm_isp_axi_stream_update(vfe_dev, i);
- }
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i])) {
- ISP_DBG("%s: complete on axi config update\n",
- __func__);
- msm_isp_axi_cfg_update(vfe_dev, i);
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i]))
- msm_isp_axi_cfg_update(vfe_dev, i);
- }
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
}
- if (atomic_read(&vfe_dev->stats_data.stats_update)) {
- ISP_DBG("%s: complete on stats update\n", __func__);
- msm_isp_stats_stream_update(vfe_dev);
- if (atomic_read(&vfe_dev->stats_data.stats_update))
- msm_isp_stats_stream_update(vfe_dev);
- }
+ msm_isp_stats_stream_update(vfe_dev);
+ msm_isp_stats_stream_update(vfe_dev);
if (blocking) {
init_completion(&vfe_dev->halt_complete);
@@ -1764,7 +1774,7 @@ static int msm_vfe40_axi_halt(struct vfe_device *vfe_dev,
return rc;
}
-static int msm_vfe40_axi_restart(struct vfe_device *vfe_dev,
+static void msm_vfe40_axi_restart(struct vfe_device *vfe_dev,
uint32_t blocking, uint32_t enable_camif)
{
msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask,
@@ -1786,8 +1796,6 @@ static int msm_vfe40_axi_restart(struct vfe_device *vfe_dev,
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev, ENABLE_CAMIF);
}
-
- return 0;
}
static uint32_t msm_vfe40_get_wm_mask(
@@ -1903,27 +1911,37 @@ static void msm_vfe40_stats_cfg_wm_irq_mask(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
msm_vfe40_config_irq(vfe_dev,
- 1 << (STATS_IDX(stream_info->stream_handle) + 16), 0,
- MSM_ISP_IRQ_ENABLE);
+ 1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 16), 0,
+ MSM_ISP_IRQ_ENABLE);
}
static void msm_vfe40_stats_clear_wm_irq_mask(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
msm_vfe40_config_irq(vfe_dev,
- (1 << (STATS_IDX(stream_info->stream_handle) + 16)), 0,
- MSM_ISP_IRQ_DISABLE);
+ (1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 16)), 0,
+ MSM_ISP_IRQ_DISABLE);
}
static void msm_vfe40_stats_cfg_wm_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
- int stats_idx = STATS_IDX(stream_info->stream_handle);
- uint32_t stats_base = VFE40_STATS_BASE(stats_idx);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ int stats_idx;
+ uint32_t stats_base;
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE40_STATS_BASE(stats_idx);
/*WR_ADDR_CFG*/
msm_camera_io_w(stream_info->framedrop_period << 2,
vfe_dev->vfe_base + stats_base + 0x8);
@@ -1939,9 +1957,14 @@ static void msm_vfe40_stats_clear_wm_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
uint32_t val = 0;
- int stats_idx = STATS_IDX(stream_info->stream_handle);
- uint32_t stats_base = VFE40_STATS_BASE(stats_idx);
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE40_STATS_BASE(stats_idx);
/*WR_ADDR_CFG*/
msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x8);
@@ -2095,11 +2118,16 @@ static void msm_vfe40_stats_enable_module(struct vfe_device *vfe_dev,
}
static void msm_vfe40_stats_update_ping_pong_addr(
- void __iomem *vfe_base, struct msm_vfe_stats_stream *stream_info,
+ struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
uint32_t pingpong_status, dma_addr_t paddr)
{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
uint32_t paddr32 = (paddr & 0xFFFFFFFF);
- int stats_idx = STATS_IDX(stream_info->stream_handle);
+ int stats_idx;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
msm_camera_io_w(paddr32, vfe_base +
VFE40_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
index 388656b9ca30..c77eff66ccca 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
@@ -437,15 +437,17 @@ static void msm_vfe44_process_reg_update(struct vfe_device *vfe_dev,
(uint32_t)BIT(i));
switch (i) {
case VFE_PIX_0:
- msm_isp_save_framedrop_values(vfe_dev,
- VFE_PIX_0);
msm_isp_notify(vfe_dev, ISP_EVENT_REG_UPDATE,
VFE_PIX_0, ts);
- if (atomic_read(
- &vfe_dev->stats_data.stats_update))
- msm_isp_stats_stream_update(vfe_dev);
- if (vfe_dev->axi_data.camif_state ==
- CAMIF_STOPPING)
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_REG_UPD);
+ if (vfe_dev->axi_data.src_info[i].stream_count
+ == 0 &&
+ vfe_dev->axi_data.src_info[i].
+ raw_stream_count == 0 &&
+ vfe_dev->axi_data.src_info[i].active)
vfe_dev->hw_info->vfe_ops.core_ops.
reg_update(vfe_dev, i);
break;
@@ -454,29 +456,22 @@ static void msm_vfe44_process_reg_update(struct vfe_device *vfe_dev,
case VFE_RAW_2:
msm_isp_increment_frame_id(vfe_dev, i, ts);
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
- msm_isp_update_framedrop_reg(vfe_dev, i);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
/*
* Reg Update is pseudo SOF for RDI,
* so request every frame
*/
vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
vfe_dev, i);
+ /* reg upd is epoch for rdi */
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
break;
default:
pr_err("%s: Error case\n", __func__);
return;
}
- if (vfe_dev->axi_data.stream_update[i])
- msm_isp_axi_stream_update(vfe_dev, i);
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i])) {
- msm_isp_axi_cfg_update(vfe_dev, i);
- if (atomic_read(
- &vfe_dev->axi_data.axi_cfg_update[i]) ==
- 0)
- msm_isp_notify(vfe_dev,
- ISP_EVENT_STREAM_UPDATE_DONE,
- i, ts);
- }
}
}
@@ -498,17 +493,19 @@ static void msm_vfe44_process_epoch_irq(struct vfe_device *vfe_dev,
if (irq_status0 & BIT(2)) {
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
ISP_DBG("%s: EPOCH0 IRQ\n", __func__);
- msm_isp_update_framedrop_reg(vfe_dev, VFE_PIX_0);
- msm_isp_update_stats_framedrop_reg(vfe_dev);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_EPOCH);
msm_isp_update_error_frame_count(vfe_dev);
if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
&& vfe_dev->axi_data.src_info[VFE_PIX_0].
- pix_stream_count == 0) {
+ stream_count == 0) {
ISP_DBG("%s: SOF IRQ\n", __func__);
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
- if (vfe_dev->axi_data.stream_update[VFE_PIX_0])
- msm_isp_axi_stream_update(vfe_dev, VFE_PIX_0);
- vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
vfe_dev, VFE_PIX_0);
}
}
@@ -550,7 +547,9 @@ static void msm_vfe44_reg_update(struct vfe_device *vfe_dev,
vfe_dev->vfe_base + 0x378);
} else if (!vfe_dev->is_split ||
((frame_src == VFE_PIX_0) &&
- (vfe_dev->axi_data.camif_state == CAMIF_STOPPING)) ||
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].stream_count == 0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count == 0)) ||
(frame_src >= VFE_RAW_0 && frame_src <= VFE_SRC_MAX)) {
msm_camera_io_w_mb(update_mask,
vfe_dev->vfe_base + 0x378);
@@ -628,8 +627,10 @@ static void msm_vfe44_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- uint32_t comp_mask, comp_mask_index =
- stream_info->comp_mask_index;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
@@ -644,7 +645,10 @@ static void msm_vfe44_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
static void msm_vfe44_axi_clear_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
@@ -657,31 +661,38 @@ static void msm_vfe44_axi_clear_comp_mask(struct vfe_device *vfe_dev,
static void msm_vfe44_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- msm_vfe44_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0,
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ msm_vfe44_config_irq(vfe_dev, 1 << (stream_info->wm[vfe_idx][0] + 8), 0,
MSM_ISP_IRQ_ENABLE);
}
static void msm_vfe44_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- msm_vfe44_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0,
- MSM_ISP_IRQ_DISABLE);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ msm_vfe44_config_irq(vfe_dev, (1 << (stream_info->wm[vfe_idx][0] + 8)),
+ 0, MSM_ISP_IRQ_DISABLE);
}
-static void msm_vfe44_cfg_framedrop(void __iomem *vfe_base,
+static void msm_vfe44_cfg_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
uint32_t framedrop_period)
{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
uint32_t i, temp;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
for (i = 0; i < stream_info->num_planes; i++) {
msm_camera_io_w(framedrop_pattern, vfe_base +
- VFE44_WM_BASE(stream_info->wm[i]) + 0x1C);
+ VFE44_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
temp = msm_camera_io_r(vfe_base +
- VFE44_WM_BASE(stream_info->wm[i]) + 0xC);
+ VFE44_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
temp &= 0xFFFFFF83;
msm_camera_io_w(temp | (framedrop_period - 1) << 2,
- vfe_base + VFE44_WM_BASE(stream_info->wm[i]) + 0xC);
+ vfe_base +
+ VFE44_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
}
}
@@ -689,9 +700,11 @@ static void msm_vfe44_clear_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
uint32_t i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
for (i = 0; i < stream_info->num_planes; i++)
msm_camera_io_w(0, vfe_dev->vfe_base +
- VFE44_WM_BASE(stream_info->wm[i]) + 0x1C);
+ VFE44_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
}
static int32_t msm_vfe44_convert_bpp_to_reg(int32_t bpp, uint32_t *bpp_reg)
@@ -1039,7 +1052,7 @@ static void msm_vfe44_update_camif_state(struct vfe_device *vfe_dev,
src_info[VFE_PIX_0].raw_stream_count > 0) ? 1 : 0);
vfe_en =
((vfe_dev->axi_data.
- src_info[VFE_PIX_0].pix_stream_count > 0) ? 1 : 0);
+ src_info[VFE_PIX_0].stream_count > 0) ? 1 : 0);
val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8);
val &= 0xFFFFFF3F;
val = val | bus_en << 7 | vfe_en << 6;
@@ -1101,7 +1114,10 @@ static void msm_vfe44_axi_cfg_wm_reg(
uint8_t plane_idx)
{
uint32_t val;
- uint32_t wm_base = VFE44_WM_BASE(stream_info->wm[plane_idx]);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE44_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
if (!stream_info->frame_based) {
msm_camera_io_w(0x0, vfe_dev->vfe_base + wm_base);
@@ -1109,28 +1125,30 @@ static void msm_vfe44_axi_cfg_wm_reg(
val =
((msm_isp_cal_word_per_line(
stream_info->output_format,
- stream_info->plane_cfg[plane_idx].
+ stream_info->plane_cfg[vfe_idx][plane_idx].
output_width)+1)/2 - 1) << 16 |
- (stream_info->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
output_height - 1);
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
/*WR_BUFFER_CFG*/
- val = (stream_info->plane_cfg[plane_idx].output_height - 1);
+ val = (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1);
val = (((val & 0xfff) << 2) | ((val >> 12) & 0x3));
val = val << 2 |
msm_isp_cal_word_per_line(stream_info->output_format,
- stream_info->plane_cfg[
+ stream_info->plane_cfg[vfe_idx][
plane_idx].output_stride) << 16 |
VFE44_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
} else {
msm_camera_io_w(0x2, vfe_dev->vfe_base + wm_base);
- val = (stream_info->plane_cfg[plane_idx].output_height - 1);
+ val = (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1);
val = (((val & 0xfff) << 2) | ((val >> 12) & 0x3));
val = val << 2 |
msm_isp_cal_word_per_line(stream_info->output_format,
- stream_info->plane_cfg[
+ stream_info->plane_cfg[vfe_idx][
plane_idx].output_width) << 16 |
VFE44_BURST_LEN;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
@@ -1147,8 +1165,10 @@ static void msm_vfe44_axi_clear_wm_reg(
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
uint32_t val = 0;
- uint32_t wm_base = VFE44_WM_BASE(stream_info->wm[plane_idx]);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+ wm_base = VFE44_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
/*WR_ADDR_CFG*/
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0xC);
/*WR_IMAGE_SIZE*/
@@ -1164,12 +1184,15 @@ static void msm_vfe44_axi_cfg_wm_xbar_reg(
struct msm_vfe_axi_stream *stream_info,
uint8_t plane_idx)
{
- struct msm_vfe_axi_plane_cfg *plane_cfg =
- &stream_info->plane_cfg[plane_idx];
- uint8_t wm = stream_info->wm[plane_idx];
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ struct msm_vfe_axi_plane_cfg *plane_cfg;
+ uint8_t wm;
uint32_t xbar_cfg = 0;
uint32_t xbar_reg_cfg = 0;
+ plane_cfg = &stream_info->plane_cfg[vfe_idx][plane_idx];
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
switch (stream_info->stream_src) {
case PIX_ENCODER:
case PIX_VIEWFINDER: {
@@ -1223,9 +1246,12 @@ static void msm_vfe44_axi_clear_wm_xbar_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
- uint8_t wm = stream_info->wm[plane_idx];
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint8_t wm;
uint32_t xbar_reg_cfg = 0;
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
xbar_reg_cfg =
msm_camera_io_r(vfe_dev->vfe_base + VFE44_XBAR_BASE(wm));
xbar_reg_cfg &= ~(0xFFFF << VFE44_XBAR_SHIFT(wm));
@@ -1245,6 +1271,7 @@ static void msm_vfe44_cfg_axi_ub_equal_default(
uint32_t prop_size = 0;
uint32_t wm_ub_size;
uint64_t delta;
+
for (i = 0; i < axi_data->hw_info->num_wm; i++) {
if (axi_data->free_wm[i] > 0) {
num_used_wms++;
@@ -1316,6 +1343,7 @@ static int msm_vfe44_axi_halt(struct vfe_device *vfe_dev,
{
int rc = 0;
enum msm_vfe_input_src i;
+ struct msm_isp_timestamp ts;
/* Keep only halt and restart mask */
msm_vfe44_config_irq(vfe_dev, (1 << 31), (1 << 8),
@@ -1349,34 +1377,20 @@ static int msm_vfe44_axi_halt(struct vfe_device *vfe_dev,
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0);
}
+ msm_isp_get_timestamp(&ts);
for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
/* if any stream is waiting for update, signal complete */
- if (vfe_dev->axi_data.stream_update[i]) {
- ISP_DBG("%s: complete stream update\n", __func__);
- msm_isp_axi_stream_update(vfe_dev, i);
- if (vfe_dev->axi_data.stream_update[i])
- msm_isp_axi_stream_update(vfe_dev, i);
- }
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i])) {
- ISP_DBG("%s: complete on axi config update\n",
- __func__);
- msm_isp_axi_cfg_update(vfe_dev, i);
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i]))
- msm_isp_axi_cfg_update(vfe_dev, i);
- }
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
}
- if (atomic_read(&vfe_dev->stats_data.stats_update)) {
- ISP_DBG("%s: complete on stats update\n", __func__);
- msm_isp_stats_stream_update(vfe_dev);
- if (atomic_read(&vfe_dev->stats_data.stats_update))
- msm_isp_stats_stream_update(vfe_dev);
- }
+ msm_isp_stats_stream_update(vfe_dev);
+ msm_isp_stats_stream_update(vfe_dev);
return rc;
}
-static int msm_vfe44_axi_restart(struct vfe_device *vfe_dev,
+static void msm_vfe44_axi_restart(struct vfe_device *vfe_dev,
uint32_t blocking, uint32_t enable_camif)
{
msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask,
@@ -1397,8 +1411,6 @@ static int msm_vfe44_axi_restart(struct vfe_device *vfe_dev,
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev, ENABLE_CAMIF);
}
-
- return 0;
}
static uint32_t msm_vfe44_get_wm_mask(
@@ -1450,15 +1462,15 @@ static int msm_vfe44_stats_check_streams(
struct msm_vfe_stats_stream *stream_info)
{
if (stream_info[STATS_IDX_BF].state ==
- STATS_AVALIABLE &&
+ STATS_AVAILABLE &&
stream_info[STATS_IDX_BF_SCALE].state !=
- STATS_AVALIABLE) {
+ STATS_AVAILABLE) {
pr_err("%s: does not support BF_SCALE while BF is disabled\n",
__func__);
return -EINVAL;
}
- if (stream_info[STATS_IDX_BF].state != STATS_AVALIABLE &&
- stream_info[STATS_IDX_BF_SCALE].state != STATS_AVALIABLE &&
+ if (stream_info[STATS_IDX_BF].state != STATS_AVAILABLE &&
+ stream_info[STATS_IDX_BF_SCALE].state != STATS_AVAILABLE &&
stream_info[STATS_IDX_BF].composite_flag !=
stream_info[STATS_IDX_BF_SCALE].composite_flag) {
pr_err("%s: Different composite flag for BF and BF_SCALE\n",
@@ -1541,27 +1553,37 @@ static void msm_vfe44_stats_cfg_wm_irq_mask(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
msm_vfe44_config_irq(vfe_dev,
- 1 << (STATS_IDX(stream_info->stream_handle) + 15), 0,
- MSM_ISP_IRQ_ENABLE);
+ 1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 15), 0,
+ MSM_ISP_IRQ_ENABLE);
}
static void msm_vfe44_stats_clear_wm_irq_mask(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
msm_vfe44_config_irq(vfe_dev,
- (1 << (STATS_IDX(stream_info->stream_handle) + 15)), 0,
- MSM_ISP_IRQ_DISABLE);
+ (1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 15)), 0,
+ MSM_ISP_IRQ_DISABLE);
}
static void msm_vfe44_stats_cfg_wm_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
- int stats_idx = STATS_IDX(stream_info->stream_handle);
- uint32_t stats_base = VFE44_STATS_BASE(stats_idx);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ int stats_idx;
+ uint32_t stats_base;
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE44_STATS_BASE(stats_idx);
/* BF_SCALE does not have its own WR_ADDR_CFG,
* IRQ_FRAMEDROP_PATTERN and IRQ_SUBSAMPLE_PATTERN;
* it's using the same from BF */
@@ -1582,9 +1604,14 @@ static void msm_vfe44_stats_clear_wm_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
uint32_t val = 0;
- int stats_idx = STATS_IDX(stream_info->stream_handle);
- uint32_t stats_base = VFE44_STATS_BASE(stats_idx);
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE44_STATS_BASE(stats_idx);
/* BF_SCALE does not have its own WR_ADDR_CFG,
* IRQ_FRAMEDROP_PATTERN and IRQ_SUBSAMPLE_PATTERN;
* it's using the same from BF */
@@ -1742,12 +1769,16 @@ static void msm_vfe44_stats_update_cgc_override(struct vfe_device *vfe_dev,
}
static void msm_vfe44_stats_update_ping_pong_addr(
- void __iomem *vfe_base, struct msm_vfe_stats_stream *stream_info,
+ struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
uint32_t pingpong_status, dma_addr_t paddr)
{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
uint32_t paddr32 = (paddr & 0xFFFFFFFF);
- int stats_idx = STATS_IDX(stream_info->stream_handle);
+ int stats_idx;
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
msm_camera_io_w(paddr32, vfe_base +
VFE44_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
index 40bb044fde47..6336892b1b4e 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
@@ -376,46 +376,40 @@ static void msm_vfe46_process_reg_update(struct vfe_device *vfe_dev,
switch (i) {
case VFE_PIX_0:
- msm_isp_save_framedrop_values(vfe_dev,
- VFE_PIX_0);
msm_isp_notify(vfe_dev, ISP_EVENT_REG_UPDATE,
VFE_PIX_0, ts);
- if (atomic_read(
- &vfe_dev->stats_data.stats_update))
- msm_isp_stats_stream_update(vfe_dev);
- if (vfe_dev->axi_data.camif_state ==
- CAMIF_STOPPING)
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_REG_UPD);
+ msm_isp_stats_stream_update(vfe_dev);
+ if (vfe_dev->axi_data.src_info[i].stream_count
+ == 0 &&
+ vfe_dev->axi_data.src_info[i].active)
vfe_dev->hw_info->vfe_ops.core_ops.
- reg_update(vfe_dev, i);
+ reg_update(vfe_dev, i);
break;
case VFE_RAW_0:
case VFE_RAW_1:
case VFE_RAW_2:
msm_isp_increment_frame_id(vfe_dev, i, ts);
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
- msm_isp_update_framedrop_reg(vfe_dev, i);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
/*
* Reg Update is pseudo SOF for RDI,
* so request every frame
*/
vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
vfe_dev, i);
+ /* reg upd is also epoch for rdi */
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
break;
default:
pr_err("%s: Error case\n", __func__);
return;
}
- if (vfe_dev->axi_data.stream_update[i])
- msm_isp_axi_stream_update(vfe_dev, i);
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i])) {
- msm_isp_axi_cfg_update(vfe_dev, i);
- if (atomic_read(
- &vfe_dev->axi_data.axi_cfg_update[i]) ==
- 0)
- msm_isp_notify(vfe_dev,
- ISP_EVENT_STREAM_UPDATE_DONE,
- i, ts);
- }
}
}
@@ -437,14 +431,16 @@ static void msm_vfe46_process_epoch_irq(struct vfe_device *vfe_dev,
if (irq_status0 & BIT(2)) {
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
ISP_DBG("%s: EPOCH0 IRQ\n", __func__);
- msm_isp_update_framedrop_reg(vfe_dev, VFE_PIX_0);
- msm_isp_update_stats_framedrop_reg(vfe_dev);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_EPOCH);
msm_isp_update_error_frame_count(vfe_dev);
if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
&& vfe_dev->axi_data.src_info[VFE_PIX_0].
- pix_stream_count == 0) {
- if (vfe_dev->axi_data.stream_update[VFE_PIX_0])
- msm_isp_axi_stream_update(vfe_dev, VFE_PIX_0);
+ stream_count == 0) {
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
vfe_dev, VFE_PIX_0);
}
@@ -488,7 +484,9 @@ static void msm_vfe46_reg_update(struct vfe_device *vfe_dev,
vfe_dev->vfe_base + 0x3D8);
} else if (!vfe_dev->is_split ||
((frame_src == VFE_PIX_0) &&
- (vfe_dev->axi_data.camif_state == CAMIF_STOPPING)) ||
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].stream_count == 0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count == 0)) ||
(frame_src >= VFE_RAW_0 && frame_src <= VFE_SRC_MAX)) {
msm_camera_io_w_mb(update_mask,
vfe_dev->vfe_base + 0x3D8);
@@ -567,8 +565,10 @@ static void msm_vfe46_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- uint32_t comp_mask, comp_mask_index =
- stream_info->comp_mask_index;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x74);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
@@ -583,7 +583,10 @@ static void msm_vfe46_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
static void msm_vfe46_axi_clear_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x74);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
@@ -596,31 +599,37 @@ static void msm_vfe46_axi_clear_comp_mask(struct vfe_device *vfe_dev,
static void msm_vfe46_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- msm_vfe46_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0,
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ msm_vfe46_config_irq(vfe_dev, 1 << (stream_info->wm[vfe_idx][0] + 8), 0,
MSM_ISP_IRQ_ENABLE);
}
static void msm_vfe46_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- msm_vfe46_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0,
- MSM_ISP_IRQ_DISABLE);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ msm_vfe46_config_irq(vfe_dev, (1 << (stream_info->wm[vfe_idx][0] + 8)),
+ 0, MSM_ISP_IRQ_DISABLE);
}
-static void msm_vfe46_cfg_framedrop(void __iomem *vfe_base,
+static void msm_vfe46_cfg_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
uint32_t framedrop_period)
{
uint32_t i, temp;
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
for (i = 0; i < stream_info->num_planes; i++) {
msm_camera_io_w(framedrop_pattern, vfe_base +
- VFE46_WM_BASE(stream_info->wm[i]) + 0x1C);
+ VFE46_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
temp = msm_camera_io_r(vfe_base +
- VFE46_WM_BASE(stream_info->wm[i]) + 0xC);
+ VFE46_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
temp &= 0xFFFFFF83;
msm_camera_io_w(temp | (framedrop_period - 1) << 2,
- vfe_base + VFE46_WM_BASE(stream_info->wm[i]) + 0xC);
+ vfe_base + VFE46_WM_BASE(stream_info->wm[vfe_idx][i]) + 0xC);
}
}
@@ -628,10 +637,11 @@ static void msm_vfe46_clear_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
uint32_t i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
for (i = 0; i < stream_info->num_planes; i++)
msm_camera_io_w(0, vfe_dev->vfe_base +
- VFE46_WM_BASE(stream_info->wm[i]) + 0x1C);
+ VFE46_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x1C);
}
static int32_t msm_vfe46_convert_bpp_to_reg(int32_t bpp, uint32_t *bpp_reg)
@@ -1114,7 +1124,7 @@ static void msm_vfe46_update_camif_state(struct vfe_device *vfe_dev,
src_info[VFE_PIX_0].raw_stream_count > 0) ? 1 : 0);
vfe_en =
((vfe_dev->axi_data.
- src_info[VFE_PIX_0].pix_stream_count > 0) ? 1 : 0);
+ src_info[VFE_PIX_0].stream_count > 0) ? 1 : 0);
val = msm_camera_io_r(vfe_dev->vfe_base + 0x3AC);
val &= 0xFFFFFF3F;
val = val | bus_en << 7 | vfe_en << 6;
@@ -1178,7 +1188,10 @@ static void msm_vfe46_axi_cfg_wm_reg(
uint8_t plane_idx)
{
uint32_t val;
- uint32_t wm_base = VFE46_WM_BASE(stream_info->wm[plane_idx]);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE46_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
val = msm_camera_io_r(vfe_dev->vfe_base + wm_base + 0xC);
val &= ~0x2;
@@ -1190,17 +1203,18 @@ static void msm_vfe46_axi_cfg_wm_reg(
val =
((msm_isp_cal_word_per_line(
stream_info->output_format,
- stream_info->plane_cfg[plane_idx].
+ stream_info->plane_cfg[vfe_idx][plane_idx].
output_width)+3)/4 - 1) << 16 |
- (stream_info->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
output_height - 1);
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
/* WR_BUFFER_CFG */
val = VFE46_BURST_LEN |
- (stream_info->plane_cfg[plane_idx].output_height - 1) <<
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1) <<
2 |
((msm_isp_cal_word_per_line(stream_info->output_format,
- stream_info->plane_cfg[plane_idx].
+ stream_info->plane_cfg[vfe_idx][plane_idx].
output_stride)+1)/2) << 16;
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
}
@@ -1215,7 +1229,10 @@ static void msm_vfe46_axi_clear_wm_reg(
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
uint32_t val = 0;
- uint32_t wm_base = VFE46_WM_BASE(stream_info->wm[plane_idx]);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+
+ wm_base = VFE46_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
/* WR_ADDR_CFG */
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0xC);
@@ -1232,12 +1249,15 @@ static void msm_vfe46_axi_cfg_wm_xbar_reg(
struct msm_vfe_axi_stream *stream_info,
uint8_t plane_idx)
{
- struct msm_vfe_axi_plane_cfg *plane_cfg =
- &stream_info->plane_cfg[plane_idx];
- uint8_t wm = stream_info->wm[plane_idx];
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ struct msm_vfe_axi_plane_cfg *plane_cfg;
+ uint8_t wm;
uint32_t xbar_cfg = 0;
uint32_t xbar_reg_cfg = 0;
+ plane_cfg = &stream_info->plane_cfg[vfe_idx][plane_idx];
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
switch (stream_info->stream_src) {
case PIX_VIDEO:
case PIX_ENCODER:
@@ -1295,9 +1315,12 @@ static void msm_vfe46_axi_clear_wm_xbar_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
- uint8_t wm = stream_info->wm[plane_idx];
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint8_t wm;
uint32_t xbar_reg_cfg = 0;
+ wm = stream_info->wm[vfe_idx][plane_idx];
+
xbar_reg_cfg =
msm_camera_io_r(vfe_dev->vfe_base + VFE46_XBAR_BASE(wm));
xbar_reg_cfg &= ~(0xFFFF << VFE46_XBAR_SHIFT(wm));
@@ -1407,6 +1430,7 @@ static int msm_vfe46_axi_halt(struct vfe_device *vfe_dev,
{
int rc = 0;
enum msm_vfe_input_src i;
+ struct msm_isp_timestamp ts;
/* Keep only halt and restart mask */
msm_vfe46_config_irq(vfe_dev, (1 << 31), (1 << 8),
@@ -1440,34 +1464,19 @@ static int msm_vfe46_axi_halt(struct vfe_device *vfe_dev,
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x374);
}
+ msm_isp_get_timestamp(&ts);
for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
- /* if any stream is waiting for update, signal complete */
- if (vfe_dev->axi_data.stream_update[i]) {
- ISP_DBG("%s: complete stream update\n", __func__);
- msm_isp_axi_stream_update(vfe_dev, i);
- if (vfe_dev->axi_data.stream_update[i])
- msm_isp_axi_stream_update(vfe_dev, i);
- }
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i])) {
- ISP_DBG("%s: complete on axi config update\n",
- __func__);
- msm_isp_axi_cfg_update(vfe_dev, i);
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i]))
- msm_isp_axi_cfg_update(vfe_dev, i);
- }
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
}
- if (atomic_read(&vfe_dev->stats_data.stats_update)) {
- ISP_DBG("%s: complete on stats update\n", __func__);
- msm_isp_stats_stream_update(vfe_dev);
- if (atomic_read(&vfe_dev->stats_data.stats_update))
- msm_isp_stats_stream_update(vfe_dev);
- }
+ msm_isp_stats_stream_update(vfe_dev);
+ msm_isp_stats_stream_update(vfe_dev);
return rc;
}
-static int msm_vfe46_axi_restart(struct vfe_device *vfe_dev,
+static void msm_vfe46_axi_restart(struct vfe_device *vfe_dev,
uint32_t blocking, uint32_t enable_camif)
{
msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask,
@@ -1488,8 +1497,6 @@ static int msm_vfe46_axi_restart(struct vfe_device *vfe_dev,
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev, ENABLE_CAMIF);
}
-
- return 0;
}
static uint32_t msm_vfe46_get_wm_mask(
@@ -1541,15 +1548,15 @@ static int msm_vfe46_stats_check_streams(
struct msm_vfe_stats_stream *stream_info)
{
if (stream_info[STATS_IDX_BF].state ==
- STATS_AVALIABLE &&
+ STATS_AVAILABLE &&
stream_info[STATS_IDX_BF_SCALE].state !=
- STATS_AVALIABLE) {
+ STATS_AVAILABLE) {
pr_err("%s: does not support BF_SCALE while BF is disabled\n",
__func__);
return -EINVAL;
}
- if (stream_info[STATS_IDX_BF].state != STATS_AVALIABLE &&
- stream_info[STATS_IDX_BF_SCALE].state != STATS_AVALIABLE &&
+ if (stream_info[STATS_IDX_BF].state != STATS_AVAILABLE &&
+ stream_info[STATS_IDX_BF_SCALE].state != STATS_AVAILABLE &&
stream_info[STATS_IDX_BF].composite_flag !=
stream_info[STATS_IDX_BF_SCALE].composite_flag) {
pr_err("%s: Different composite flag for BF and BF_SCALE\n",
@@ -1632,26 +1639,37 @@ static void msm_vfe46_stats_cfg_wm_irq_mask(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
msm_vfe46_config_irq(vfe_dev,
- 1 << (STATS_IDX(stream_info->stream_handle) + 15), 0,
- MSM_ISP_IRQ_ENABLE);
+ 1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 15), 0,
+ MSM_ISP_IRQ_ENABLE);
}
static void msm_vfe46_stats_clear_wm_irq_mask(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
msm_vfe46_config_irq(vfe_dev,
- 1 << (STATS_IDX(stream_info->stream_handle) + 15), 0,
- MSM_ISP_IRQ_DISABLE);
+ 1 << (STATS_IDX(stream_info->stream_handle[vfe_idx]) + 15), 0,
+ MSM_ISP_IRQ_DISABLE);
}
static void msm_vfe46_stats_cfg_wm_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
- int stats_idx = STATS_IDX(stream_info->stream_handle);
- uint32_t stats_base = VFE46_STATS_BASE(stats_idx);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE46_STATS_BASE(stats_idx);
/*
* BF_SCALE does not have its own WR_ADDR_CFG,
@@ -1676,10 +1694,14 @@ static void msm_vfe46_stats_clear_wm_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
uint32_t val = 0;
- int stats_idx = STATS_IDX(stream_info->stream_handle);
- uint32_t stats_base = VFE46_STATS_BASE(stats_idx);
+ int stats_idx;
+ uint32_t stats_base;
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE46_STATS_BASE(stats_idx);
/*
* BF_SCALE does not have its own WR_ADDR_CFG,
* IRQ_FRAMEDROP_PATTERN and IRQ_SUBSAMPLE_PATTERN;
@@ -1845,12 +1867,16 @@ static void msm_vfe46_stats_enable_module(struct vfe_device *vfe_dev,
}
static void msm_vfe46_stats_update_ping_pong_addr(
- void __iomem *vfe_base, struct msm_vfe_stats_stream *stream_info,
+ struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
uint32_t pingpong_status, dma_addr_t paddr)
{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
uint32_t paddr32 = (paddr & 0xFFFFFFFF);
- int stats_idx = STATS_IDX(stream_info->stream_handle);
+ int stats_idx;
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
msm_camera_io_w(paddr32, vfe_base +
VFE46_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
}
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 290f100ffeba..b434161f5599 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -562,19 +562,20 @@ void msm_vfe47_process_reg_update(struct vfe_device *vfe_dev,
for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
if (shift_irq & BIT(i)) {
reg_updated |= BIT(i);
- ISP_DBG("%s REG_UPDATE IRQ %x\n", __func__,
- (uint32_t)BIT(i));
+ ISP_DBG("%s REG_UPDATE IRQ %x vfe %d\n", __func__,
+ (uint32_t)BIT(i), vfe_dev->pdev->id);
switch (i) {
case VFE_PIX_0:
- msm_isp_save_framedrop_values(vfe_dev,
- VFE_PIX_0);
msm_isp_notify(vfe_dev, ISP_EVENT_REG_UPDATE,
VFE_PIX_0, ts);
- if (atomic_read(
- &vfe_dev->stats_data.stats_update))
- msm_isp_stats_stream_update(vfe_dev);
- if (vfe_dev->axi_data.camif_state ==
- CAMIF_STOPPING)
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_REG_UPD);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
+ /* if 0 streams then force reg update */
+ if (vfe_dev->axi_data.src_info
+ [i].stream_count == 0 &&
+ vfe_dev->axi_data.src_info[i].active)
vfe_dev->hw_info->vfe_ops.core_ops.
reg_update(vfe_dev, i);
break;
@@ -582,31 +583,23 @@ void msm_vfe47_process_reg_update(struct vfe_device *vfe_dev,
case VFE_RAW_1:
case VFE_RAW_2:
msm_isp_increment_frame_id(vfe_dev, i, ts);
- msm_isp_save_framedrop_values(vfe_dev, i);
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts);
- msm_isp_update_framedrop_reg(vfe_dev, i);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
/*
* Reg Update is pseudo SOF for RDI,
* so request every frame
*/
vfe_dev->hw_info->vfe_ops.core_ops.
reg_update(vfe_dev, i);
+ /* reg upd is also epoch for RDI */
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, i,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
break;
default:
pr_err("%s: Error case\n", __func__);
return;
}
- if (vfe_dev->axi_data.stream_update[i])
- msm_isp_axi_stream_update(vfe_dev, i);
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i])) {
- msm_isp_axi_cfg_update(vfe_dev, i);
- if (atomic_read(
- &vfe_dev->axi_data.axi_cfg_update[i]) ==
- 0)
- msm_isp_notify(vfe_dev,
- ISP_EVENT_STREAM_UPDATE_DONE,
- i, ts);
- }
}
}
@@ -627,15 +620,17 @@ void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev,
if (irq_status0 & BIT(2)) {
ISP_DBG("%s: EPOCH0 IRQ\n", __func__);
- msm_isp_update_framedrop_reg(vfe_dev, VFE_PIX_0);
- msm_isp_update_stats_framedrop_reg(vfe_dev);
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_EPOCH, ts);
+ msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev,
+ MSM_ISP_COMP_IRQ_EPOCH);
msm_isp_update_error_frame_count(vfe_dev);
msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts);
if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0
&& vfe_dev->axi_data.src_info[VFE_PIX_0].
- pix_stream_count == 0) {
- if (vfe_dev->axi_data.stream_update[VFE_PIX_0])
- msm_isp_axi_stream_update(vfe_dev, VFE_PIX_0);
+ stream_count == 0) {
+ msm_isp_process_reg_upd_epoch_irq(vfe_dev, VFE_PIX_0,
+ MSM_ISP_COMP_IRQ_REG_UPD, ts);
vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
vfe_dev, VFE_PIX_0);
}
@@ -679,7 +674,9 @@ void msm_vfe47_reg_update(struct vfe_device *vfe_dev,
vfe_dev->vfe_base + 0x4AC);
} else if (!vfe_dev->is_split ||
((frame_src == VFE_PIX_0) &&
- (vfe_dev->axi_data.camif_state == CAMIF_STOPPING)) ||
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].stream_count == 0) &&
+ (vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count == 0)) ||
(frame_src >= VFE_RAW_0 && frame_src <= VFE_SRC_MAX)) {
msm_camera_io_w_mb(update_mask,
vfe_dev->vfe_base + 0x4AC);
@@ -768,9 +765,10 @@ void msm_vfe47_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- uint32_t comp_mask, comp_mask_index =
- stream_info->comp_mask_index;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x74);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
comp_mask |= (axi_data->composite_info[comp_mask_index].
@@ -784,8 +782,10 @@ void msm_vfe47_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t comp_mask, comp_mask_index;
+ comp_mask_index = stream_info->comp_mask_index[vfe_idx];
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x74);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74);
@@ -797,31 +797,37 @@ void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev,
void msm_vfe47_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- msm_vfe47_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0,
+ 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,
MSM_ISP_IRQ_ENABLE);
}
void msm_vfe47_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- msm_vfe47_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0,
- MSM_ISP_IRQ_DISABLE);
+ 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, MSM_ISP_IRQ_DISABLE);
}
-void msm_vfe47_cfg_framedrop(void __iomem *vfe_base,
+void msm_vfe47_cfg_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
uint32_t framedrop_period)
{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
uint32_t i, temp;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
for (i = 0; i < stream_info->num_planes; i++) {
msm_camera_io_w(framedrop_pattern, vfe_base +
- VFE47_WM_BASE(stream_info->wm[i]) + 0x24);
+ VFE47_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x24);
temp = msm_camera_io_r(vfe_base +
- VFE47_WM_BASE(stream_info->wm[i]) + 0x14);
+ VFE47_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x14);
temp &= 0xFFFFFF83;
msm_camera_io_w(temp | (framedrop_period - 1) << 2,
- vfe_base + VFE47_WM_BASE(stream_info->wm[i]) + 0x14);
+ vfe_base + VFE47_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x14);
}
}
@@ -829,10 +835,11 @@ void msm_vfe47_clear_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
uint32_t i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
for (i = 0; i < stream_info->num_planes; i++)
msm_camera_io_w(0, vfe_dev->vfe_base +
- VFE47_WM_BASE(stream_info->wm[i]) + 0x24);
+ VFE47_WM_BASE(stream_info->wm[vfe_idx][i]) + 0x24);
}
static int32_t msm_vfe47_convert_bpp_to_reg(int32_t bpp, uint32_t *bpp_reg)
@@ -1395,7 +1402,7 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
src_info[VFE_PIX_0].raw_stream_count > 0) ? 1 : 0);
vfe_en =
((vfe_dev->axi_data.
- src_info[VFE_PIX_0].pix_stream_count > 0) ? 1 : 0);
+ src_info[VFE_PIX_0].stream_count > 0) ? 1 : 0);
val = msm_camera_io_r(vfe_dev->vfe_base + 0x47C);
val &= 0xFFFFFF3F;
val = val | bus_en << 7 | vfe_en << 6;
@@ -1404,7 +1411,6 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x478);
/* configure EPOCH0 for 20 lines */
msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x4A0);
- vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1;
/* testgen GO*/
if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
msm_camera_io_w(1, vfe_dev->vfe_base + 0xC58);
@@ -1427,7 +1433,6 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
poll_val, poll_val & 0x80000000, 1000, 2000000))
pr_err("%s: camif disable failed %x\n",
__func__, poll_val);
- vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
/* testgen OFF*/
if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN)
msm_camera_io_w(1 << 1, vfe_dev->vfe_base + 0xC58);
@@ -1469,8 +1474,10 @@ void msm_vfe47_axi_cfg_wm_reg(
uint8_t plane_idx)
{
uint32_t val;
- uint32_t wm_base = VFE47_WM_BASE(stream_info->wm[plane_idx]);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+ wm_base = VFE47_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
val = msm_camera_io_r(vfe_dev->vfe_base + wm_base + 0x14);
val &= ~0x2;
if (stream_info->frame_based)
@@ -1480,17 +1487,18 @@ void msm_vfe47_axi_cfg_wm_reg(
/* WR_IMAGE_SIZE */
val = ((msm_isp_cal_word_per_line(
stream_info->output_format,
- stream_info->plane_cfg[plane_idx].
+ stream_info->plane_cfg[vfe_idx][plane_idx].
output_width)+3)/4 - 1) << 16 |
- (stream_info->plane_cfg[plane_idx].
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
output_height - 1);
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x1C);
/* WR_BUFFER_CFG */
val = VFE47_BURST_LEN |
- (stream_info->plane_cfg[plane_idx].output_height - 1) <<
+ (stream_info->plane_cfg[vfe_idx][plane_idx].
+ output_height - 1) <<
2 |
((msm_isp_cal_word_per_line(stream_info->output_format,
- stream_info->plane_cfg[plane_idx].
+ stream_info->plane_cfg[vfe_idx][plane_idx].
output_stride)+1)/2) << 16;
}
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x20);
@@ -1504,8 +1512,10 @@ void msm_vfe47_axi_clear_wm_reg(
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
uint32_t val = 0;
- uint32_t wm_base = VFE47_WM_BASE(stream_info->wm[plane_idx]);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint32_t wm_base;
+ wm_base = VFE47_WM_BASE(stream_info->wm[vfe_idx][plane_idx]);
/* WR_ADDR_CFG */
msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
/* WR_IMAGE_SIZE */
@@ -1521,12 +1531,14 @@ void msm_vfe47_axi_cfg_wm_xbar_reg(
struct msm_vfe_axi_stream *stream_info,
uint8_t plane_idx)
{
- struct msm_vfe_axi_plane_cfg *plane_cfg =
- &stream_info->plane_cfg[plane_idx];
- uint8_t wm = stream_info->wm[plane_idx];
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ struct msm_vfe_axi_plane_cfg *plane_cfg;
+ uint8_t wm;
uint32_t xbar_cfg = 0;
uint32_t xbar_reg_cfg = 0;
+ plane_cfg = &stream_info->plane_cfg[vfe_idx][plane_idx];
+ wm = stream_info->wm[vfe_idx][plane_idx];
switch (stream_info->stream_src) {
case PIX_VIDEO:
case PIX_ENCODER:
@@ -1585,9 +1597,11 @@ void msm_vfe47_axi_clear_wm_xbar_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
{
- uint8_t wm = stream_info->wm[plane_idx];
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ uint8_t wm;
uint32_t xbar_reg_cfg = 0;
+ wm = stream_info->wm[vfe_idx][plane_idx];
xbar_reg_cfg =
msm_camera_io_r(vfe_dev->vfe_base + VFE47_XBAR_BASE(wm));
xbar_reg_cfg &= ~(0xFFFF << VFE47_XBAR_SHIFT(wm));
@@ -1707,6 +1721,7 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev,
int rc = 0;
enum msm_vfe_input_src i;
uint32_t val = 0;
+ struct msm_isp_timestamp ts;
val = msm_camera_io_r(vfe_dev->vfe_vbif_base + VFE47_VBIF_CLK_OFFSET);
val |= 0x1;
@@ -1746,34 +1761,20 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev,
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x400);
}
+ msm_isp_get_timestamp(&ts);
for (i = VFE_PIX_0; i <= VFE_RAW_2; i++) {
- /* if any stream is waiting for update, signal complete */
- if (vfe_dev->axi_data.stream_update[i]) {
- ISP_DBG("%s: complete stream update\n", __func__);
- msm_isp_axi_stream_update(vfe_dev, i);
- if (vfe_dev->axi_data.stream_update[i])
- msm_isp_axi_stream_update(vfe_dev, i);
- }
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i])) {
- ISP_DBG("%s: complete on axi config update\n",
- __func__);
- msm_isp_axi_cfg_update(vfe_dev, i);
- if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[i]))
- msm_isp_axi_cfg_update(vfe_dev, i);
- }
+ /* if any stream is waiting for update, signal fake completes */
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
+ msm_isp_axi_stream_update(vfe_dev, i, &ts);
}
- if (atomic_read(&vfe_dev->stats_data.stats_update)) {
- ISP_DBG("%s: complete on stats update\n", __func__);
- msm_isp_stats_stream_update(vfe_dev);
- if (atomic_read(&vfe_dev->stats_data.stats_update))
- msm_isp_stats_stream_update(vfe_dev);
- }
+ msm_isp_stats_stream_update(vfe_dev);
+ msm_isp_stats_stream_update(vfe_dev);
return rc;
}
-int msm_vfe47_axi_restart(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,
@@ -1793,8 +1794,6 @@ int msm_vfe47_axi_restart(struct vfe_device *vfe_dev,
vfe_dev->hw_info->vfe_ops.core_ops.
update_camif_state(vfe_dev, ENABLE_CAMIF);
}
-
- return 0;
}
uint32_t msm_vfe47_get_wm_mask(
@@ -1912,7 +1911,10 @@ void msm_vfe47_stats_cfg_wm_irq_mask(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
- switch (STATS_IDX(stream_info->stream_handle)) {
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+
+ 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);
break;
@@ -1943,7 +1945,7 @@ void msm_vfe47_stats_cfg_wm_irq_mask(
break;
default:
pr_err("%s: Invalid stats idx %d\n", __func__,
- STATS_IDX(stream_info->stream_handle));
+ STATS_IDX(stream_info->stream_handle[vfe_idx]));
}
}
@@ -1951,12 +1953,10 @@ void msm_vfe47_stats_clear_wm_irq_mask(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
- uint32_t irq_mask, irq_mask_1;
-
- irq_mask = vfe_dev->irq0_mask;
- irq_mask_1 = vfe_dev->irq1_mask;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
- switch (STATS_IDX(stream_info->stream_handle)) {
+ 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);
break;
@@ -1987,7 +1987,7 @@ void msm_vfe47_stats_clear_wm_irq_mask(
break;
default:
pr_err("%s: Invalid stats idx %d\n", __func__,
- STATS_IDX(stream_info->stream_handle));
+ STATS_IDX(stream_info->stream_handle[vfe_idx]));
}
}
@@ -1995,8 +1995,13 @@ void msm_vfe47_stats_cfg_wm_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
- int stats_idx = STATS_IDX(stream_info->stream_handle);
- uint32_t stats_base = VFE47_STATS_BASE(stats_idx);
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE47_STATS_BASE(stats_idx);
/* WR_ADDR_CFG */
msm_camera_io_w(stream_info->framedrop_period << 2,
@@ -2013,9 +2018,14 @@ void msm_vfe47_stats_clear_wm_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
uint32_t val = 0;
- int stats_idx = STATS_IDX(stream_info->stream_handle);
- uint32_t stats_base = VFE47_STATS_BASE(stats_idx);
+ int stats_idx;
+ uint32_t stats_base;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
+ stats_base = VFE47_STATS_BASE(stats_idx);
/* WR_ADDR_CFG */
msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x10);
@@ -2171,11 +2181,16 @@ void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev,
}
void msm_vfe47_stats_update_ping_pong_addr(
- void __iomem *vfe_base, struct msm_vfe_stats_stream *stream_info,
+ struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
uint32_t pingpong_status, dma_addr_t paddr)
{
+ void __iomem *vfe_base = vfe_dev->vfe_base;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
+ stream_info);
uint32_t paddr32 = (paddr & 0xFFFFFFFF);
- int stats_idx = STATS_IDX(stream_info->stream_handle);
+ int stats_idx;
+
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
msm_camera_io_w(paddr32, vfe_base +
VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
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 737f845c7272..8581373b3b71 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
@@ -56,7 +56,7 @@ void msm_vfe47_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info);
void msm_vfe47_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info);
-void msm_vfe47_cfg_framedrop(void __iomem *vfe_base,
+void msm_vfe47_cfg_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern,
uint32_t framedrop_period);
void msm_vfe47_clear_framedrop(struct vfe_device *vfe_dev,
@@ -107,7 +107,7 @@ void msm_vfe47_update_ping_pong_addr(
int32_t buf_size);
int msm_vfe47_axi_halt(struct vfe_device *vfe_dev,
uint32_t blocking);
-int msm_vfe47_axi_restart(struct vfe_device *vfe_dev,
+void msm_vfe47_axi_restart(struct vfe_device *vfe_dev,
uint32_t blocking, uint32_t enable_camif);
uint32_t msm_vfe47_get_wm_mask(
uint32_t irq_status0, uint32_t irq_status1);
@@ -141,7 +141,7 @@ bool msm_vfe47_is_module_cfg_lock_needed(
void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev,
uint32_t stats_mask, uint8_t enable);
void msm_vfe47_stats_update_ping_pong_addr(
- void __iomem *vfe_base, struct msm_vfe_stats_stream *stream_info,
+ struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
uint32_t pingpong_status, dma_addr_t paddr);
uint32_t msm_vfe47_stats_get_wm_mask(
uint32_t irq_status0, uint32_t irq_status1);
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 ac2d508269a4..39a0845a886f 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
@@ -22,9 +22,12 @@ static int msm_isp_update_dual_HW_ms_info_at_start(
struct vfe_device *vfe_dev,
enum msm_vfe_input_src stream_src);
-static int msm_isp_update_dual_HW_axi(
- struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream *stream_info);
+static void msm_isp_reload_ping_pong_offset(
+ struct msm_vfe_axi_stream *stream_info);
+
+static void __msm_isp_axi_stream_update(
+ struct msm_vfe_axi_stream *stream_info,
+ struct msm_isp_timestamp *ts);
#define DUAL_VFE_AND_VFE1(s, v) ((s->stream_src < RDI_INTF_0) && \
v->is_split && vfe_dev->pdev->id == ISP_VFE1)
@@ -33,105 +36,151 @@ static int msm_isp_update_dual_HW_axi(
((s->stream_src >= RDI_INTF_0) && \
(stream_info->stream_src <= RDI_INTF_2)))
-static inline struct msm_vfe_axi_stream *msm_isp_vfe_get_stream(
- struct dual_vfe_resource *dual_vfe_res,
- int vfe_id, uint32_t index)
-{
- struct msm_vfe_axi_shared_data *axi_data =
- dual_vfe_res->axi_data[vfe_id];
- return &axi_data->stream_info[index];
-}
-
-static inline struct msm_vfe_axi_stream *msm_isp_get_controllable_stream(
- struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream *stream_info)
-{
- if (vfe_dev->is_split && stream_info->stream_src < RDI_INTF_0 &&
- stream_info->controllable_output)
- return msm_isp_vfe_get_stream(
- vfe_dev->common_data->dual_vfe_res,
- ISP_VFE1,
- HANDLE_TO_IDX(
- stream_info->stream_handle));
- return stream_info;
-}
-
-int msm_isp_axi_create_stream(struct vfe_device *vfe_dev,
+static int msm_isp_axi_create_stream(struct vfe_device *vfe_dev,
struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ struct msm_vfe_axi_stream *stream_info)
{
- uint32_t i = stream_cfg_cmd->stream_src;
-
- if (i >= VFE_AXI_SRC_MAX) {
- pr_err("%s:%d invalid stream_src %d\n", __func__, __LINE__,
- stream_cfg_cmd->stream_src);
- return -EINVAL;
- }
+ uint32_t i;
+ int rc = 0;
- if (axi_data->stream_info[i].state != AVAILABLE) {
+ if (stream_info->state != AVAILABLE) {
pr_err("%s:%d invalid state %d expected %d for src %d\n",
- __func__, __LINE__, axi_data->stream_info[i].state,
+ __func__, __LINE__, stream_info->state,
AVAILABLE, i);
return -EINVAL;
}
+ if (stream_info->num_isp == 0) {
+ stream_info->session_id = stream_cfg_cmd->session_id;
+ stream_info->stream_id = stream_cfg_cmd->stream_id;
+ stream_info->buf_divert = stream_cfg_cmd->buf_divert;
+ stream_info->stream_src = stream_cfg_cmd->stream_src;
+ stream_info->controllable_output =
+ stream_cfg_cmd->controllable_output;
+ stream_info->activated_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+ if (stream_cfg_cmd->controllable_output)
+ stream_cfg_cmd->frame_skip_pattern = SKIP_ALL;
+ INIT_LIST_HEAD(&stream_info->request_q);
+ } else {
+ /* check if the stream has been added for the vfe-device */
+ if (stream_info->vfe_mask & (1 << vfe_dev->pdev->id)) {
+ pr_err("%s: stream %p/%x is already added for vfe dev %d vfe_mask %x\n",
+ __func__, stream_info, stream_info->stream_id,
+ vfe_dev->pdev->id, stream_info->vfe_mask);
+ return -EINVAL;
+ }
+ if (stream_info->session_id != stream_cfg_cmd->session_id) {
+ pr_err("%s: dual stream session id mismatch %d/%d\n",
+ __func__, stream_info->session_id,
+ stream_cfg_cmd->session_id);
+ rc = -EINVAL;
+ }
+ if (stream_info->stream_id != stream_cfg_cmd->stream_id) {
+ pr_err("%s: dual stream stream id mismatch %d/%d\n",
+ __func__, stream_info->stream_id,
+ stream_cfg_cmd->stream_id);
+ rc = -EINVAL;
+ }
+ if (stream_info->controllable_output !=
+ stream_cfg_cmd->controllable_output) {
+ pr_err("%s: dual stream controllable_op mismatch %d/%d\n",
+ __func__, stream_info->controllable_output,
+ stream_cfg_cmd->controllable_output);
+ rc = -EINVAL;
+ }
+ if (stream_info->buf_divert != stream_cfg_cmd->buf_divert) {
+ pr_err("%s: dual stream buf_divert mismatch %d/%d\n",
+ __func__, stream_info->buf_divert,
+ stream_cfg_cmd->buf_divert);
+ rc = -EINVAL;
+ }
+ if (rc)
+ return rc;
+ }
+ stream_info->vfe_dev[stream_info->num_isp] = vfe_dev;
+ stream_info->num_isp++;
+
if ((axi_data->stream_handle_cnt << 8) == 0)
axi_data->stream_handle_cnt++;
stream_cfg_cmd->axi_stream_handle =
- (++axi_data->stream_handle_cnt) << 8 | i;
+ (++axi_data->stream_handle_cnt) << 8 | stream_info->stream_src;
ISP_DBG("%s: vfe %d handle %x\n", __func__, vfe_dev->pdev->id,
stream_cfg_cmd->axi_stream_handle);
- memset(&axi_data->stream_info[i], 0,
- sizeof(struct msm_vfe_axi_stream));
- spin_lock_init(&axi_data->stream_info[i].lock);
- axi_data->stream_info[i].session_id = stream_cfg_cmd->session_id;
- axi_data->stream_info[i].stream_id = stream_cfg_cmd->stream_id;
- axi_data->stream_info[i].buf_divert = stream_cfg_cmd->buf_divert;
- axi_data->stream_info[i].state = INACTIVE;
- axi_data->stream_info[i].stream_handle =
+ stream_info->stream_handle[stream_info->num_isp - 1] =
stream_cfg_cmd->axi_stream_handle;
- axi_data->stream_info[i].controllable_output =
- stream_cfg_cmd->controllable_output;
- axi_data->stream_info[i].activated_framedrop_period =
- MSM_VFE_STREAM_STOP_PERIOD;
- if (stream_cfg_cmd->controllable_output)
- stream_cfg_cmd->frame_skip_pattern = SKIP_ALL;
- INIT_LIST_HEAD(&axi_data->stream_info[i].request_q);
+ stream_info->vfe_mask |= (1 << vfe_dev->pdev->id);
+
+ if (!vfe_dev->is_split || stream_cfg_cmd->stream_src >= RDI_INTF_0 ||
+ stream_info->num_isp == MAX_VFE) {
+ stream_info->state = INACTIVE;
+
+ for (i = 0; i < MSM_ISP_COMP_IRQ_MAX; i++)
+ stream_info->composite_irq[i] = 0;
+ }
return 0;
}
-void msm_isp_axi_destroy_stream(
- struct msm_vfe_axi_shared_data *axi_data, int stream_idx)
+static void msm_isp_axi_destroy_stream(
+ struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info)
{
- if (axi_data->stream_info[stream_idx].state != AVAILABLE) {
- axi_data->stream_info[stream_idx].state = AVAILABLE;
- axi_data->stream_info[stream_idx].stream_handle = 0;
- } else {
- pr_err("%s: stream does not exist\n", __func__);
+ int k;
+ int j;
+ int i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ /*
+ * For the index being removed, shift everything to it's right by 1
+ * so that the index being removed becomes the last index
+ */
+ for (i = vfe_idx, k = vfe_idx + 1; k < stream_info->num_isp; k++, i++) {
+ stream_info->vfe_dev[i] = stream_info->vfe_dev[k];
+ stream_info->stream_handle[i] = stream_info->stream_handle[k];
+ stream_info->bandwidth[i] = stream_info->bandwidth[k];
+ stream_info->max_width[i] = stream_info->max_width[k];
+ stream_info->comp_mask_index[i] =
+ stream_info->comp_mask_index[k];
+ for (j = 0; j < stream_info->num_planes; j++) {
+ stream_info->plane_cfg[i][j] =
+ stream_info->plane_cfg[k][j];
+ stream_info->wm[i][j] = stream_info->wm[k][j];
+ }
+ }
+
+ stream_info->num_isp--;
+ stream_info->vfe_dev[stream_info->num_isp] = NULL;
+ stream_info->stream_handle[stream_info->num_isp] = 0;
+ stream_info->bandwidth[stream_info->num_isp] = 0;
+ stream_info->max_width[stream_info->num_isp] = 0;
+ stream_info->comp_mask_index[stream_info->num_isp] = -1;
+ stream_info->vfe_mask &= ~(1 << vfe_dev->pdev->id);
+ for (j = 0; j < stream_info->num_planes; j++) {
+ stream_info->wm[stream_info->num_isp][j] = -1;
+ memset(&stream_info->plane_cfg[stream_info->num_isp][j],
+ 0, sizeof(
+ stream_info->plane_cfg[stream_info->num_isp][j]));
+ }
+
+ if (stream_info->num_isp == 0) {
+ /* release the bufq */
+ for (k = 0; k < VFE_BUF_QUEUE_MAX; k++)
+ stream_info->bufq_handle[k] = 0;
+ stream_info->vfe_mask = 0;
+ stream_info->state = AVAILABLE;
}
}
-int msm_isp_validate_axi_request(struct msm_vfe_axi_shared_data *axi_data,
+static int msm_isp_validate_axi_request(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
{
int rc = -1, i;
- struct msm_vfe_axi_stream *stream_info = NULL;
- if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)
- < VFE_AXI_SRC_MAX) {
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
- } else {
- pr_err("%s: Invalid axi_stream_handle\n", __func__);
- return rc;
- }
-
- if (!stream_info) {
- pr_err("%s: Stream info is NULL\n", __func__);
- return -EINVAL;
- }
+ int vfe_idx;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
switch (stream_cfg_cmd->output_format) {
case V4L2_PIX_FMT_YUYV:
@@ -236,9 +285,13 @@ int msm_isp_validate_axi_request(struct msm_vfe_axi_shared_data *axi_data,
return rc;
}
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
for (i = 0; i < stream_info->num_planes; i++) {
- stream_info->plane_cfg[i] = stream_cfg_cmd->plane_cfg[i];
- stream_info->max_width = max(stream_info->max_width,
+ stream_info->plane_cfg[vfe_idx][i] =
+ stream_cfg_cmd->plane_cfg[i];
+ stream_info->max_width[vfe_idx] =
+ max(stream_info->max_width[vfe_idx],
stream_cfg_cmd->plane_cfg[i].output_width);
}
@@ -250,10 +303,11 @@ int msm_isp_validate_axi_request(struct msm_vfe_axi_shared_data *axi_data,
}
static uint32_t msm_isp_axi_get_plane_size(
- struct msm_vfe_axi_stream *stream_info, int plane_idx)
+ struct msm_vfe_axi_stream *stream_info, int vfe_idx, int plane_idx)
{
uint32_t size = 0;
- struct msm_vfe_axi_plane_cfg *plane_cfg = stream_info->plane_cfg;
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ stream_info->plane_cfg[vfe_idx];
switch (stream_info->output_format) {
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
@@ -355,37 +409,41 @@ static uint32_t msm_isp_axi_get_plane_size(
return size;
}
-void msm_isp_axi_reserve_wm(struct vfe_device *vfe_dev,
- struct msm_vfe_axi_shared_data *axi_data,
+static void msm_isp_axi_reserve_wm(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
int i, j;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
for (i = 0; i < stream_info->num_planes; i++) {
for (j = 0; j < axi_data->hw_info->num_wm; j++) {
if (!axi_data->free_wm[j]) {
axi_data->free_wm[j] =
- stream_info->stream_handle;
+ stream_info->stream_handle[vfe_idx];
axi_data->wm_image_size[j] =
msm_isp_axi_get_plane_size(
- stream_info, i);
+ stream_info, vfe_idx, i);
axi_data->num_used_wm++;
break;
}
}
ISP_DBG("%s vfe %d stream_handle %x wm %d\n", __func__,
vfe_dev->pdev->id,
- stream_info->stream_handle, j);
- stream_info->wm[i] = j;
+ stream_info->stream_handle[vfe_idx], j);
+ stream_info->wm[vfe_idx][i] = j;
}
}
-void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data,
+void msm_isp_axi_free_wm(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
int i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
for (i = 0; i < stream_info->num_planes; i++) {
- axi_data->free_wm[stream_info->wm[i]] = 0;
+ axi_data->free_wm[stream_info->wm[vfe_idx][i]] = 0;
axi_data->num_used_wm--;
}
if (stream_info->stream_src <= IDEAL_RAW)
@@ -394,88 +452,47 @@ void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data,
axi_data->num_rdi_stream++;
}
-void msm_isp_axi_reserve_comp_mask(
- struct msm_vfe_axi_shared_data *axi_data,
+static void msm_isp_axi_reserve_comp_mask(
+ struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
int i;
uint8_t comp_mask = 0;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
for (i = 0; i < stream_info->num_planes; i++)
- comp_mask |= 1 << stream_info->wm[i];
+ comp_mask |= 1 << stream_info->wm[vfe_idx][i];
for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
if (!axi_data->composite_info[i].stream_handle) {
axi_data->composite_info[i].stream_handle =
- stream_info->stream_handle;
+ stream_info->stream_handle[vfe_idx];
axi_data->composite_info[i].
stream_composite_mask = comp_mask;
axi_data->num_used_composite_mask++;
break;
}
}
- stream_info->comp_mask_index = i;
+ stream_info->comp_mask_index[vfe_idx] = i;
return;
}
-void msm_isp_axi_free_comp_mask(struct msm_vfe_axi_shared_data *axi_data,
+static void msm_isp_axi_free_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
- axi_data->composite_info[stream_info->comp_mask_index].
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ axi_data->composite_info[stream_info->comp_mask_index[vfe_idx]].
stream_composite_mask = 0;
- axi_data->composite_info[stream_info->comp_mask_index].
+ axi_data->composite_info[stream_info->comp_mask_index[vfe_idx]].
stream_handle = 0;
axi_data->num_used_composite_mask--;
}
-int msm_isp_axi_check_stream_state(
- struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
-{
- int rc = 0, i;
- unsigned long flags;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- struct msm_vfe_axi_stream *stream_info;
- enum msm_vfe_axi_state valid_state =
- (stream_cfg_cmd->cmd == START_STREAM) ? INACTIVE : ACTIVE;
-
- if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
- return -EINVAL;
-
- 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 = &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
- spin_lock_irqsave(&stream_info->lock, flags);
- if (stream_info->state != valid_state) {
- if ((stream_info->state == PAUSING ||
- stream_info->state == PAUSED ||
- stream_info->state == RESUME_PENDING ||
- stream_info->state == RESUMING ||
- stream_info->state == UPDATING) &&
- (stream_cfg_cmd->cmd == STOP_STREAM ||
- stream_cfg_cmd->cmd == STOP_IMMEDIATELY)) {
- stream_info->state = ACTIVE;
- } else {
- pr_err("%s: Invalid stream state: %d\n",
- __func__, stream_info->state);
- spin_unlock_irqrestore(
- &stream_info->lock, flags);
- if (stream_cfg_cmd->cmd == START_STREAM)
- rc = -EINVAL;
- break;
- }
- }
- spin_unlock_irqrestore(&stream_info->lock, flags);
- }
- return rc;
-}
-
/**
* msm_isp_cfg_framedrop_reg() - Program the period and pattern
- * @vfe_dev: The device for which the period and pattern is programmed
* @stream_info: The stream for which programming is done
*
* This function calculates the period and pattern to be configured
@@ -484,15 +501,15 @@ int msm_isp_axi_check_stream_state(
*
* Returns void.
*/
-static void msm_isp_cfg_framedrop_reg(struct vfe_device *vfe_dev,
+static void msm_isp_cfg_framedrop_reg(
struct msm_vfe_axi_stream *stream_info)
{
- struct msm_vfe_axi_stream *vfe0_stream_info = NULL;
+ struct vfe_device *vfe_dev = stream_info->vfe_dev[0];
uint32_t runtime_init_frame_drop;
-
uint32_t framedrop_pattern = 0;
uint32_t framedrop_period = MSM_VFE_STREAM_STOP_PERIOD;
enum msm_vfe_input_src frame_src = SRC_TO_INTF(stream_info->stream_src);
+ int i;
if (vfe_dev->axi_data.src_info[frame_src].frame_id >=
stream_info->init_frame_drop)
@@ -507,127 +524,45 @@ static void msm_isp_cfg_framedrop_reg(struct vfe_device *vfe_dev,
if (MSM_VFE_STREAM_STOP_PERIOD != framedrop_period)
framedrop_pattern = 0x1;
- ISP_DBG("%s: stream %x framedrop pattern %x period %u\n", __func__,
- stream_info->stream_handle, framedrop_pattern,
- framedrop_period);
-
BUG_ON(0 == framedrop_period);
- if (DUAL_VFE_AND_VFE1(stream_info, vfe_dev)) {
- vfe0_stream_info = msm_isp_vfe_get_stream(
- vfe_dev->common_data->dual_vfe_res,
- ISP_VFE0,
- HANDLE_TO_IDX(
- stream_info->stream_handle));
+ for (i = 0; i < stream_info->num_isp; i++) {
+ vfe_dev = stream_info->vfe_dev[i];
vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(
- vfe_dev->common_data->dual_vfe_res->
- vfe_base[ISP_VFE0],
- vfe0_stream_info, framedrop_pattern,
- framedrop_period);
- vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(
- vfe_dev->vfe_base, stream_info,
- framedrop_pattern,
- framedrop_period);
+ vfe_dev, stream_info, framedrop_pattern,
+ framedrop_period);
+ }
- stream_info->requested_framedrop_period =
- framedrop_period;
- vfe0_stream_info->requested_framedrop_period =
- framedrop_period;
+ ISP_DBG("%s: stream %x src %x framedrop pattern %x period %u\n",
+ __func__,
+ stream_info->stream_handle[0], stream_info->stream_src,
+ framedrop_pattern, framedrop_period);
- } else if (RDI_OR_NOT_DUAL_VFE(vfe_dev, stream_info)) {
- vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(
- vfe_dev->vfe_base, stream_info, framedrop_pattern,
- framedrop_period);
- stream_info->requested_framedrop_period = framedrop_period;
- }
+ stream_info->requested_framedrop_period = framedrop_period;
}
-/**
- * msm_isp_check_epoch_status() - check the epock signal for framedrop
- *
- * @vfe_dev: The h/w on which the epoch signel is reveived
- * @frame_src: The source of the epoch signal for this frame
- *
- * For dual vfe case and pixel stream, if both vfe's epoch signal is
- * received, this function will return success.
- * It will also return the vfe1 for further process
- * For none dual VFE stream or none pixl source, this
- * funciton will just return success.
- *
- * Returns 1 - epoch received is complete.
- * 0 - epoch reveived is not complete.
- */
-static int msm_isp_check_epoch_status(struct vfe_device **vfe_dev,
- enum msm_vfe_input_src frame_src)
+static int msm_isp_composite_irq(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ enum msm_isp_comp_irq_types irq)
{
- struct vfe_device *vfe_dev_cur = *vfe_dev;
- struct vfe_device *vfe_dev_other = NULL;
- uint32_t vfe_id_other = 0;
- uint32_t vfe_id_cur = 0;
- uint32_t epoch_mask = 0;
- unsigned long flags;
- int completed = 0;
-
- spin_lock_irqsave(
- &vfe_dev_cur->common_data->common_dev_data_lock, flags);
-
- if (vfe_dev_cur->is_split &&
- frame_src == VFE_PIX_0) {
- if (vfe_dev_cur->pdev->id == ISP_VFE0) {
- vfe_id_cur = ISP_VFE0;
- vfe_id_other = ISP_VFE1;
- } else {
- vfe_id_cur = ISP_VFE1;
- vfe_id_other = ISP_VFE0;
- }
- vfe_dev_other = vfe_dev_cur->common_data->dual_vfe_res->
- vfe_dev[vfe_id_other];
-
- if (vfe_dev_cur->common_data->dual_vfe_res->
- epoch_sync_mask & (1 << vfe_id_cur)) {
- /* serious scheduling delay */
- pr_err("Missing epoch: vfe %d, epoch mask 0x%x\n",
- vfe_dev_cur->pdev->id,
- vfe_dev_cur->common_data->dual_vfe_res->
- epoch_sync_mask);
- goto fatal;
- }
-
- vfe_dev_cur->common_data->dual_vfe_res->
- epoch_sync_mask |= (1 << vfe_id_cur);
-
- epoch_mask = (1 << vfe_id_cur) | (1 << vfe_id_other);
- if ((vfe_dev_cur->common_data->dual_vfe_res->
- epoch_sync_mask & epoch_mask) == epoch_mask) {
-
- if (vfe_id_other == ISP_VFE0)
- *vfe_dev = vfe_dev_cur;
- else
- *vfe_dev = vfe_dev_other;
+ /* interrupt recv on same vfe w/o recv on other vfe */
+ if (stream_info->composite_irq[irq] & (1 << vfe_dev->pdev->id)) {
+ pr_err("%s: irq %d out of sync for dual vfe on vfe %d\n",
+ __func__, irq, vfe_dev->pdev->id);
+ return -EINVAL;
+ }
- vfe_dev_cur->common_data->dual_vfe_res->
- epoch_sync_mask &= ~epoch_mask;
- completed = 1;
- }
- } else
- completed = 1;
+ stream_info->composite_irq[irq] |= (1 << vfe_dev->pdev->id);
+ if (stream_info->composite_irq[irq] != stream_info->vfe_mask)
+ return 1;
- spin_unlock_irqrestore(
- &vfe_dev_cur->common_data->common_dev_data_lock, flags);
+ stream_info->composite_irq[irq] = 0;
- return completed;
-fatal:
- spin_unlock_irqrestore(
- &vfe_dev_cur->common_data->common_dev_data_lock, flags);
- /* new error event code will be added later */
- msm_isp_halt_send_error(vfe_dev_cur, ISP_EVENT_PING_PONG_MISMATCH);
return 0;
}
-
/**
* msm_isp_update_framedrop_reg() - Update frame period pattern on h/w
- * @vfe_dev: The h/w on which the perion pattern is updated.
- * @frame_src: Input source.
+ * @stream_info: Stream for which update is to be performed
*
* If the period and pattern needs to be updated for a stream then it is
* updated here. Updates happen if initial frame drop reaches 0 or burst
@@ -635,47 +570,75 @@ fatal:
*
* Returns void
*/
-void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev,
- enum msm_vfe_input_src frame_src)
+static void msm_isp_update_framedrop_reg(struct msm_vfe_axi_stream *stream_info)
+{
+ if (stream_info->stream_type == BURST_STREAM) {
+ if (stream_info->runtime_num_burst_capture == 0 ||
+ (stream_info->runtime_num_burst_capture == 1 &&
+ stream_info->activated_framedrop_period == 1))
+ stream_info->current_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+ }
+
+ if (stream_info->undelivered_request_cnt > 0)
+ stream_info->current_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+
+ /*
+ * re-configure the period pattern, only if it's not already
+ * set to what we want
+ */
+ if (stream_info->current_framedrop_period !=
+ stream_info->requested_framedrop_period) {
+ msm_isp_cfg_framedrop_reg(stream_info);
+ }
+}
+
+void msm_isp_process_reg_upd_epoch_irq(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src,
+ enum msm_isp_comp_irq_types irq,
+ struct msm_isp_timestamp *ts)
{
int i;
- struct msm_vfe_axi_shared_data *axi_data = NULL;
struct msm_vfe_axi_stream *stream_info;
unsigned long flags;
-
- if (msm_isp_check_epoch_status(&vfe_dev, frame_src) != 1)
- return;
-
- axi_data = &vfe_dev->axi_data;
+ int ret;
for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
- if (SRC_TO_INTF(axi_data->stream_info[i].stream_src) !=
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, i);
+ if (SRC_TO_INTF(stream_info->stream_src) !=
frame_src) {
continue;
}
- stream_info = &axi_data->stream_info[i];
- if (stream_info->state != ACTIVE)
+ if (stream_info->state == AVAILABLE ||
+ stream_info->state == INACTIVE)
continue;
spin_lock_irqsave(&stream_info->lock, flags);
- if (BURST_STREAM == stream_info->stream_type) {
- if (0 == stream_info->runtime_num_burst_capture)
- stream_info->current_framedrop_period =
- MSM_VFE_STREAM_STOP_PERIOD;
+ ret = msm_isp_composite_irq(vfe_dev, stream_info, irq);
+ if (ret) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (ret < 0) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return;
+ }
+ continue;
}
- if (stream_info->undelivered_request_cnt > 0)
- stream_info->current_framedrop_period =
- MSM_VFE_STREAM_STOP_PERIOD;
-
- /*
- * re-configure the period pattern, only if it's not already
- * set to what we want
- */
- if (stream_info->current_framedrop_period !=
- stream_info->requested_framedrop_period) {
- msm_isp_cfg_framedrop_reg(vfe_dev, stream_info);
+ switch (irq) {
+ case MSM_ISP_COMP_IRQ_REG_UPD:
+ stream_info->activated_framedrop_period =
+ stream_info->requested_framedrop_period;
+ __msm_isp_axi_stream_update(stream_info, ts);
+ break;
+ case MSM_ISP_COMP_IRQ_EPOCH:
+ if (stream_info->state == ACTIVE)
+ msm_isp_update_framedrop_reg(stream_info);
+ break;
+ default:
+ WARN(1, "Invalid irq %d\n", irq);
}
spin_unlock_irqrestore(&stream_info->lock, flags);
}
@@ -708,7 +671,7 @@ void msm_isp_reset_framedrop(struct vfe_device *vfe_dev,
stream_info->frame_skip_pattern);
}
- msm_isp_cfg_framedrop_reg(vfe_dev, stream_info);
+ msm_isp_cfg_framedrop_reg(stream_info);
ISP_DBG("%s: init frame drop: %d\n", __func__,
stream_info->init_frame_drop);
ISP_DBG("%s: num_burst_capture: %d\n", __func__,
@@ -741,10 +704,9 @@ void msm_isp_check_for_output_error(struct vfe_device *vfe_dev,
vfe_dev->reg_update_requested;
}
for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
- struct msm_vfe_axi_stream *temp_stream_info;
-
- stream_info = &axi_data->stream_info[i];
- stream_idx = HANDLE_TO_IDX(stream_info->stream_handle);
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ i);
+ stream_idx = HANDLE_TO_IDX(stream_info->stream_handle[0]);
/*
* Process drop only if controllable ACTIVE PIX stream &&
@@ -761,10 +723,8 @@ void msm_isp_check_for_output_error(struct vfe_device *vfe_dev,
if (stream_info->controllable_output &&
!vfe_dev->reg_updated) {
- temp_stream_info =
- msm_isp_get_controllable_stream(vfe_dev,
- stream_info);
- if (temp_stream_info->undelivered_request_cnt) {
+ if (stream_info->undelivered_request_cnt) {
+ pr_err("Drop frame no reg update\n");
if (msm_isp_drop_frame(vfe_dev, stream_info, ts,
sof_info)) {
pr_err("drop frame failed\n");
@@ -991,7 +951,7 @@ void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type,
spin_unlock_irqrestore(&vfe_dev->common_data->
common_dev_data_lock, flags);
} else {
- if (frame_src == VFE_PIX_0) {
+ if (frame_src <= VFE_RAW_2) {
msm_isp_check_for_output_error(vfe_dev, ts,
&event_data.u.sof_info);
}
@@ -1010,7 +970,7 @@ void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type,
/**
* msm_isp_calculate_framedrop() - Setup frame period and pattern
- * @axi_data: Structure describing the h/w streams.
+ * @vfe_dev: vfe device.
* @stream_cfg_cmd: User space input parameter for perion/pattern.
*
* Initialize the h/w stream framedrop period and pattern sent
@@ -1018,16 +978,16 @@ void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type,
*
* Returns 0 on success else error code.
*/
-int msm_isp_calculate_framedrop(
- struct msm_vfe_axi_shared_data *axi_data,
+static int msm_isp_calculate_framedrop(
+ struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
{
uint32_t framedrop_period = 0;
struct msm_vfe_axi_stream *stream_info = NULL;
if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)
< VFE_AXI_SRC_MAX) {
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle));
} else {
pr_err("%s: Invalid stream handle", __func__);
return -EINVAL;
@@ -1059,26 +1019,36 @@ int msm_isp_calculate_framedrop(
return 0;
}
-void msm_isp_calculate_bandwidth(
- struct msm_vfe_axi_shared_data *axi_data,
+static void msm_isp_calculate_bandwidth(
struct msm_vfe_axi_stream *stream_info)
{
int bpp = 0;
+ struct msm_vfe_axi_shared_data *axi_data;
+ int i;
+
if (stream_info->stream_src < RDI_INTF_0) {
- stream_info->bandwidth =
- (axi_data->src_info[VFE_PIX_0].pixel_clock /
- axi_data->src_info[VFE_PIX_0].width) *
- stream_info->max_width;
- stream_info->bandwidth = (unsigned long)stream_info->bandwidth *
- stream_info->format_factor / ISP_Q2;
+ for (i = 0; i < stream_info->num_isp; i++) {
+ axi_data = &stream_info->vfe_dev[i]->axi_data;
+ stream_info->bandwidth[i] =
+ (axi_data->src_info[VFE_PIX_0].pixel_clock /
+ 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)
- stream_info->bandwidth =
+ if (rdi < VFE_SRC_MAX) {
+ for (i = 0; i < stream_info->num_isp; i++) {
+ axi_data = &stream_info->vfe_dev[i]->axi_data;
+ stream_info->bandwidth[i] =
(axi_data->src_info[rdi].pixel_clock / 8) * bpp;
- else
+ }
+ } else {
pr_err("%s: Invalid rdi interface\n", __func__);
+ }
}
}
@@ -1133,37 +1103,40 @@ 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__,
+ stream_cfg_cmd->stream_src);
+ return -EINVAL;
+ }
+ 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);
+ &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;
}
rc = msm_isp_validate_axi_request(
- &vfe_dev->axi_data, stream_cfg_cmd);
+ 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__);
- if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle) <
- VFE_AXI_SRC_MAX)
- msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
- HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle));
return rc;
}
- stream_info = &vfe_dev->axi_data.
- stream_info[HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)];
- if (!stream_info) {
- pr_err("%s: can not find stream handle %x\n", __func__,
- stream_cfg_cmd->axi_stream_handle);
- return -EINVAL;
- }
stream_info->memory_input = stream_cfg_cmd->memory_input;
vfe_dev->reg_update_requested &=
~(BIT(SRC_TO_INTF(stream_info->stream_src)));
- msm_isp_axi_reserve_wm(vfe_dev, &vfe_dev->axi_data, stream_info);
+ msm_isp_axi_reserve_wm(vfe_dev, stream_info);
if (stream_info->stream_src < RDI_INTF_0) {
io_format = vfe_dev->axi_data.src_info[VFE_PIX_0].input_format;
@@ -1183,16 +1156,67 @@ int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
goto done;
}
}
- rc = msm_isp_calculate_framedrop(&vfe_dev->axi_data, stream_cfg_cmd);
- if (rc)
- goto done;
+
+ if (!stream_info->controllable_output) {
+ /*
+ * check that the parameters passed from second vfe is same
+ * as first vfe, do this only for non controllable stream
+ * right now because user driver has bug where it sends
+ * mismatch info for controllable streams
+ */
+ if (stream_info->num_isp > 1) {
+ if (stream_cfg_cmd->init_frame_drop !=
+ stream_info->init_frame_drop) {
+ pr_err("%s: stream %d init drop mismatch %d/%d\n",
+ __func__, stream_info->stream_id,
+ stream_info->init_frame_drop,
+ stream_cfg_cmd->init_frame_drop);
+ rc = -EINVAL;
+ }
+ if (stream_cfg_cmd->frame_skip_pattern !=
+ stream_info->frame_skip_pattern) {
+ pr_err("%s: stream %d skip pattern mismatch %d/%d\n",
+ __func__, stream_info->stream_id,
+ stream_info->frame_skip_pattern,
+ stream_cfg_cmd->frame_skip_pattern);
+ rc = -EINVAL;
+ }
+ if (stream_info->stream_type == CONTINUOUS_STREAM &&
+ stream_cfg_cmd->burst_count > 0) {
+ pr_err("%s: stream %d stream type mismatch\n",
+ __func__, stream_info->stream_id);
+ rc = -EINVAL;
+ }
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->num_burst_capture !=
+ stream_cfg_cmd->burst_count) {
+ pr_err("%s: stream %d stream burst count mismatch %d/%d\n",
+ __func__, stream_info->stream_id,
+ stream_info->num_burst_capture,
+ stream_cfg_cmd->burst_count);
+ rc = -EINVAL;
+ }
+ } else {
+ rc = msm_isp_calculate_framedrop(vfe_dev,
+ stream_cfg_cmd);
+ }
+ if (rc)
+ goto done;
+ } else {
+ stream_info->stream_type = BURST_STREAM;
+ stream_info->num_burst_capture = 0;
+ stream_info->frame_skip_pattern = NO_SKIP;
+ stream_info->init_frame_drop = stream_cfg_cmd->init_frame_drop;
+ stream_info->current_framedrop_period =
+ MSM_VFE_STREAM_STOP_PERIOD;
+ }
if (stream_cfg_cmd->vt_enable && !vfe_dev->vt_enable) {
vfe_dev->vt_enable = stream_cfg_cmd->vt_enable;
msm_isp_start_avtimer();
}
+
if (stream_info->num_planes > 1)
- msm_isp_axi_reserve_comp_mask(
- &vfe_dev->axi_data, stream_info);
+ msm_isp_axi_reserve_comp_mask(vfe_dev, stream_info);
for (i = 0; i < stream_info->num_planes; i++) {
vfe_dev->hw_info->vfe_ops.axi_ops.
@@ -1201,16 +1225,17 @@ int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
vfe_dev->hw_info->vfe_ops.axi_ops.
cfg_wm_xbar_reg(vfe_dev, stream_info, i);
}
- /* initialize the WM ping pong with scratch buffer */
- msm_isp_cfg_stream_scratch(vfe_dev, stream_info, VFE_PING_FLAG);
- msm_isp_cfg_stream_scratch(vfe_dev, stream_info, VFE_PONG_FLAG);
-
+ if (stream_info->state == INACTIVE) {
+ /* initialize the WM ping pong with scratch buffer */
+ msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG);
+ msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG);
+ }
done:
if (rc) {
- msm_isp_axi_free_wm(&vfe_dev->axi_data, stream_info);
- msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
- HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle));
+ 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;
}
@@ -1218,26 +1243,42 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0, i;
struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
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) {
pr_err("%s: Invalid stream handle\n", __func__);
return -EINVAL;
}
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(stream_release_cmd->stream_handle)];
- if (stream_info->state == AVAILABLE) {
- pr_err("%s: Stream already released\n", __func__);
+ 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,
+ vfe_idx != -ENOTTY ?
+ stream_info->stream_handle[vfe_idx] : 0, vfe_idx,
+ vfe_dev->pdev->id, stream_info->num_isp);
return -EINVAL;
- } else if (stream_info->state != INACTIVE) {
+ }
+
+ if (stream_info->state != INACTIVE && stream_info->state != AVAILABLE) {
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++) {
@@ -1249,33 +1290,75 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
}
if (stream_info->num_planes > 1)
- msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info);
+ msm_isp_axi_free_comp_mask(vfe_dev, stream_info);
vfe_dev->hw_info->vfe_ops.axi_ops.clear_framedrop(vfe_dev, stream_info);
- msm_isp_axi_free_wm(axi_data, stream_info);
+ msm_isp_axi_free_wm(vfe_dev, stream_info);
- msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
- HANDLE_TO_IDX(stream_release_cmd->stream_handle));
+ msm_isp_axi_destroy_stream(vfe_dev, stream_info);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
return rc;
}
-static int msm_isp_axi_stream_enable_cfg(
- struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream *stream_info, int32_t dual_vfe_sync)
+void msm_isp_release_all_axi_stream(struct vfe_device *vfe_dev)
{
- int i, vfe_id = 0, enable_wm = 0;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- uint32_t stream_idx = HANDLE_TO_IDX(stream_info->stream_handle);
- struct dual_vfe_resource *dual_vfe_res = NULL;
+ struct msm_vfe_axi_stream_release_cmd
+ stream_release_cmd[VFE_AXI_SRC_MAX];
+ struct msm_vfe_axi_stream_cfg_cmd stream_cfg_cmd;
+ struct msm_vfe_axi_stream *stream_info;
+ int i;
+ int vfe_idx;
+ int num_stream = 0;
+ unsigned long flags;
- if (stream_idx >= VFE_AXI_SRC_MAX) {
- pr_err("%s: Invalid stream_idx", __func__);
- goto error;
+ stream_cfg_cmd.cmd = STOP_STREAM;
+ stream_cfg_cmd.num_streams = 0;
+
+ for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, i);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ vfe_idx = msm_isp_get_vfe_idx_for_stream_user(
+ vfe_dev, stream_info);
+ if (-ENOTTY == vfe_idx) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ stream_release_cmd[num_stream++].stream_handle =
+ stream_info->stream_handle[vfe_idx];
+ if (stream_info->state == INACTIVE) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ stream_cfg_cmd.stream_handle[
+ stream_cfg_cmd.num_streams] =
+ stream_info->stream_handle[vfe_idx];
+ stream_cfg_cmd.num_streams++;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
}
+ if (stream_cfg_cmd.num_streams)
+ msm_isp_cfg_axi_stream(vfe_dev, (void *) &stream_cfg_cmd);
- if (stream_info->state == INACTIVE)
- goto error;
+ for (i = 0; i < num_stream; i++)
+ msm_isp_release_axi_stream(vfe_dev, &stream_release_cmd[i]);
+}
+
+static void msm_isp_axi_stream_enable_cfg(
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int enable_wm = 0;
+ struct vfe_device *vfe_dev;
+ struct msm_vfe_axi_shared_data *axi_data;
+ uint32_t stream_idx = stream_info->stream_src;
+ int k;
+ int i;
+
+ WARN_ON(stream_idx >= VFE_AXI_SRC_MAX);
+
+ WARN_ON(stream_info->state != START_PENDING &&
+ stream_info->state != RESUME_PENDING &&
+ stream_info->state != STOP_PENDING &&
+ stream_info->state != PAUSE_PENDING);
if (stream_info->state == START_PENDING ||
stream_info->state == RESUME_PENDING) {
@@ -1283,50 +1366,24 @@ static int msm_isp_axi_stream_enable_cfg(
} else {
enable_wm = 0;
}
- for (i = 0; i < stream_info->num_planes; i++) {
- /*
- * In case when sensor is streaming, use dual vfe sync mode
- * to enable wm together and avoid split.
- */
- if ((stream_info->stream_src < RDI_INTF_0) &&
- vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE1 &&
- dual_vfe_sync) {
- dual_vfe_res = vfe_dev->common_data->dual_vfe_res;
- if (!dual_vfe_res->vfe_base[ISP_VFE0] ||
- !dual_vfe_res->axi_data[ISP_VFE0] ||
- !dual_vfe_res->vfe_base[ISP_VFE1] ||
- !dual_vfe_res->axi_data[ISP_VFE1]) {
- pr_err("%s:%d failed vfe0 %pK %pK vfe %pK %pK\n",
- __func__, __LINE__,
- dual_vfe_res->vfe_base[ISP_VFE0],
- dual_vfe_res->axi_data[ISP_VFE0],
- dual_vfe_res->vfe_base[ISP_VFE1],
- dual_vfe_res->axi_data[ISP_VFE1]);
- goto error;
- }
- for (vfe_id = 0; vfe_id < MAX_VFE; vfe_id++) {
- vfe_dev->hw_info->vfe_ops.axi_ops.
- enable_wm(dual_vfe_res->vfe_base[vfe_id],
- dual_vfe_res->axi_data[vfe_id]->
- stream_info[stream_idx].wm[i],
- enable_wm);
- }
- } else if (!vfe_dev->is_split ||
- (stream_info->stream_src >= RDI_INTF_0 &&
- stream_info->stream_src <= RDI_INTF_2) ||
- !dual_vfe_sync) {
- vfe_dev->hw_info->vfe_ops.axi_ops.
- enable_wm(vfe_dev->vfe_base, stream_info->wm[i],
- enable_wm);
- }
- if (!enable_wm) {
- /* Issue a reg update for Raw Snapshot Case
+
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ axi_data = &vfe_dev->axi_data;
+ for (i = 0; i < stream_info->num_planes; i++) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.enable_wm(
+ vfe_dev->vfe_base,
+ stream_info->wm[k][i], enable_wm);
+ if (enable_wm)
+ continue;
+ /*
+ * Issue a reg update for Raw Snapshot Case
* since we dont have reg update ack
- */
+ */
if (vfe_dev->axi_data.src_info[VFE_PIX_0].
raw_stream_count > 0
&& vfe_dev->axi_data.src_info[VFE_PIX_0].
- pix_stream_count == 0) {
+ stream_count == 0) {
if (stream_info->stream_src == CAMIF_RAW ||
stream_info->stream_src == IDEAL_RAW) {
vfe_dev->hw_info->vfe_ops.core_ops.
@@ -1335,70 +1392,103 @@ static int msm_isp_axi_stream_enable_cfg(
}
}
}
+ if (stream_info->state == START_PENDING)
+ axi_data->num_active_stream++;
+ else if (stream_info->state == STOP_PENDING)
+ axi_data->num_active_stream--;
+ }
+}
+
+static void __msm_isp_axi_stream_update(
+ struct msm_vfe_axi_stream *stream_info,
+ struct msm_isp_timestamp *ts)
+{
+ int j;
+ int intf = SRC_TO_INTF(stream_info->stream_src);
+ struct vfe_device *vfe_dev;
+ int k;
+
+ switch (stream_info->state) {
+ case UPDATING:
+ stream_info->state = ACTIVE;
+ break;
+ case STOP_PENDING:
+ msm_isp_axi_stream_enable_cfg(stream_info);
+ stream_info->state = STOPPING;
+ break;
+ case START_PENDING:
+ msm_isp_axi_stream_enable_cfg(stream_info);
+ stream_info->state = STARTING;
+ break;
+ case STOPPING:
+ stream_info->state = INACTIVE;
+ for (k = 0; k < MSM_ISP_COMP_IRQ_MAX; k++)
+ stream_info->composite_irq[k] = 0;
+ complete_all(&stream_info->inactive_comp);
+ break;
+ case STARTING:
+ stream_info->state = ACTIVE;
+ complete_all(&stream_info->active_comp);
+ break;
+ case PAUSING:
+ stream_info->state = PAUSED;
+ msm_isp_reload_ping_pong_offset(stream_info);
+ for (j = 0; j < stream_info->num_planes; j++) {
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_info, j);
+ }
+ }
+ stream_info->state = RESUME_PENDING;
+ msm_isp_axi_stream_enable_cfg(stream_info);
+ stream_info->state = RESUMING;
+ break;
+ case RESUMING:
+ stream_info->runtime_output_format = stream_info->output_format;
+ stream_info->state = ACTIVE;
+ complete_all(&stream_info->active_comp);
+ for (j = 0; j < stream_info->num_isp; j++) {
+ /* notify that all streams have been updated */
+ msm_isp_notify(stream_info->vfe_dev[j],
+ ISP_EVENT_STREAM_UPDATE_DONE, intf, ts);
+ atomic_set(&stream_info->vfe_dev[j]->
+ axi_data.axi_cfg_update[intf], 0);
+ }
+ stream_info->update_vfe_mask = 0;
+ break;
+ default:
+ break;
}
- if (stream_info->state == START_PENDING)
- axi_data->num_active_stream++;
- else if (stream_info->state == STOP_PENDING)
- axi_data->num_active_stream--;
- return 0;
-error:
- return -EINVAL;
}
void msm_isp_axi_stream_update(struct vfe_device *vfe_dev,
- enum msm_vfe_input_src frame_src)
+ enum msm_vfe_input_src frame_src,
+ struct msm_isp_timestamp *ts)
{
int i;
unsigned long flags;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
- if (SRC_TO_INTF(axi_data->stream_info[i].stream_src) !=
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, i);
+ if (SRC_TO_INTF(stream_info->stream_src) !=
frame_src) {
ISP_DBG("%s stream_src %d frame_src %d\n", __func__,
SRC_TO_INTF(
- axi_data->stream_info[i].stream_src),
+ stream_info->stream_src),
frame_src);
continue;
}
- if (axi_data->stream_info[i].state == UPDATING)
- axi_data->stream_info[i].state = ACTIVE;
- else if (axi_data->stream_info[i].state == START_PENDING ||
- axi_data->stream_info[i].state == STOP_PENDING) {
- msm_isp_axi_stream_enable_cfg(
- vfe_dev, &axi_data->stream_info[i],
- axi_data->stream_info[i].state ==
- START_PENDING ? 1 : 0);
- axi_data->stream_info[i].state =
- axi_data->stream_info[i].state ==
- START_PENDING ? STARTING : STOPPING;
- } else if (axi_data->stream_info[i].state == STARTING ||
- axi_data->stream_info[i].state == STOPPING) {
- axi_data->stream_info[i].state =
- axi_data->stream_info[i].state == STARTING ?
- ACTIVE : INACTIVE;
- }
- }
-
- spin_lock_irqsave(&vfe_dev->shared_data_lock, flags);
- if (vfe_dev->axi_data.stream_update[frame_src]) {
- vfe_dev->axi_data.stream_update[frame_src]--;
- }
- spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags);
-
- if (vfe_dev->axi_data.pipeline_update == DISABLE_CAMIF ||
- (vfe_dev->axi_data.pipeline_update ==
- DISABLE_CAMIF_IMMEDIATELY)) {
- vfe_dev->hw_info->vfe_ops.stats_ops.
- enable_module(vfe_dev, 0xFF, 0);
- vfe_dev->axi_data.pipeline_update = NO_UPDATE;
+ if (stream_info->state == AVAILABLE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ __msm_isp_axi_stream_update(stream_info, ts);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
}
-
- if (vfe_dev->axi_data.stream_update[frame_src] == 0)
- complete(&vfe_dev->stream_config_complete);
}
-static void msm_isp_reload_ping_pong_offset(struct vfe_device *vfe_dev,
+static void msm_isp_reload_ping_pong_offset(
struct msm_vfe_axi_stream *stream_info)
{
int i, j;
@@ -1406,120 +1496,70 @@ static void msm_isp_reload_ping_pong_offset(struct vfe_device *vfe_dev,
struct msm_isp_buffer *buf;
int32_t buf_size_byte = 0;
int32_t word_per_line = 0;
+ int k;
+ struct vfe_device *vfe_dev;
+
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ for (i = 0; i < 2; i++) {
+ buf = stream_info->buf[i];
+ if (!buf)
+ continue;
- for (i = 0; i < 2; i++) {
- buf = stream_info->buf[i];
- if (!buf)
- continue;
-
- bit = i ? 0 : 1;
+ bit = i ? 0 : 1;
- for (j = 0; j < stream_info->num_planes; j++) {
- word_per_line = msm_isp_cal_word_per_line(
+ for (j = 0; j < stream_info->num_planes; j++) {
+ word_per_line = msm_isp_cal_word_per_line(
stream_info->output_format, stream_info->
- plane_cfg[j].output_stride);
- if (word_per_line < 0) {
- /* 0 means no prefetch*/
- word_per_line = 0;
- buf_size_byte = 0;
- } else {
- buf_size_byte = (word_per_line * 8 *
- stream_info->plane_cfg[j].
+ plane_cfg[k][j].output_stride);
+ if (word_per_line < 0) {
+ /* 0 means no prefetch*/
+ word_per_line = 0;
+ buf_size_byte = 0;
+ } else {
+ buf_size_byte = (word_per_line * 8 *
+ stream_info->plane_cfg[k][j].
output_scan_lines) - stream_info->
- plane_cfg[j].plane_addr_offset;
- }
-
- vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
- vfe_dev->vfe_base, stream_info->wm[j], bit,
- buf->mapped_info[j].paddr +
- stream_info->plane_cfg[j].plane_addr_offset,
- buf_size_byte);
- }
- }
-}
-
-void msm_isp_axi_cfg_update(struct vfe_device *vfe_dev,
- enum msm_vfe_input_src frame_src)
-{
- int i, j;
- uint32_t update_state;
- unsigned long flags;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- struct msm_vfe_axi_stream *stream_info;
- int num_stream = 0;
+ plane_cfg[k][j].plane_addr_offset;
+ }
- spin_lock_irqsave(&vfe_dev->common_data->common_dev_data_lock, flags);
- for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
- if (SRC_TO_INTF(axi_data->stream_info[i].stream_src) !=
- frame_src) {
- continue;
- }
- num_stream++;
- stream_info = &axi_data->stream_info[i];
- if ((stream_info->stream_type == BURST_STREAM &&
- !stream_info->controllable_output) ||
- stream_info->state == AVAILABLE)
- continue;
- spin_lock_irqsave(&stream_info->lock, flags);
- if (stream_info->state == PAUSING) {
- /*AXI Stopped, apply update*/
- stream_info->state = PAUSED;
- msm_isp_reload_ping_pong_offset(vfe_dev, stream_info);
- for (j = 0; j < stream_info->num_planes; j++)
vfe_dev->hw_info->vfe_ops.axi_ops.
- cfg_wm_reg(vfe_dev, stream_info, j);
- /*Resume AXI*/
- stream_info->state = RESUME_PENDING;
- if (vfe_dev->is_split) {
- msm_isp_update_dual_HW_axi(vfe_dev,
- stream_info);
- } else {
- msm_isp_axi_stream_enable_cfg(
- vfe_dev,
- &axi_data->stream_info[i], 1);
- stream_info->state = RESUMING;
+ update_ping_pong_addr(
+ vfe_dev->vfe_base,
+ stream_info->wm[k][j],
+ bit,
+ buf->mapped_info[j].paddr +
+ stream_info->plane_cfg[k][j].
+ plane_addr_offset,
+ buf_size_byte);
}
- } else if (stream_info->state == RESUMING) {
- stream_info->runtime_output_format =
- stream_info->output_format;
- stream_info->state = ACTIVE;
}
- spin_unlock_irqrestore(&stream_info->lock, flags);
}
- spin_unlock_irqrestore(&vfe_dev->common_data->common_dev_data_lock,
- flags);
- if (num_stream)
- update_state = atomic_dec_return(
- &axi_data->axi_cfg_update[frame_src]);
}
static int msm_isp_update_deliver_count(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_bit)
{
- struct msm_vfe_axi_stream *temp_stream_info;
int rc = 0;
if (!stream_info->controllable_output)
goto done;
- temp_stream_info =
- msm_isp_get_controllable_stream(vfe_dev, stream_info);
-
- if (!temp_stream_info->undelivered_request_cnt) {
+ if (!stream_info->undelivered_request_cnt) {
pr_err_ratelimited("%s:%d error undelivered_request_cnt 0\n",
__func__, __LINE__);
rc = -EINVAL;
goto done;
} else {
- temp_stream_info->undelivered_request_cnt--;
- if (pingpong_bit != temp_stream_info->sw_ping_pong_bit) {
+ stream_info->undelivered_request_cnt--;
+ if (pingpong_bit != stream_info->sw_ping_pong_bit) {
pr_err("%s:%d ping pong bit actual %d sw %d\n",
__func__, __LINE__, pingpong_bit,
- temp_stream_info->sw_ping_pong_bit);
+ stream_info->sw_ping_pong_bit);
rc = -EINVAL;
goto done;
}
- temp_stream_info->sw_ping_pong_bit ^= 1;
+ stream_info->sw_ping_pong_bit ^= 1;
}
done:
return rc;
@@ -1527,7 +1567,6 @@ done:
void msm_isp_halt_send_error(struct vfe_device *vfe_dev, uint32_t event)
{
- uint32_t i = 0;
struct msm_isp_event_data error_event;
struct msm_vfe_axi_halt_cmd halt_cmd;
@@ -1545,10 +1584,6 @@ void msm_isp_halt_send_error(struct vfe_device *vfe_dev, uint32_t event)
/* heavy spin lock in axi halt, avoid spin lock outside. */
msm_isp_axi_halt(vfe_dev, &halt_cmd);
- for (i = 0; i < VFE_AXI_SRC_MAX; i++)
- vfe_dev->axi_data.stream_info[i].state =
- INACTIVE;
-
error_event.frame_id =
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
@@ -1562,31 +1597,49 @@ int msm_isp_print_ping_pong_address(struct vfe_device *vfe_dev,
struct msm_isp_buffer *buf = NULL;
uint32_t pingpong_bit;
struct msm_vfe_axi_stream *stream_info = NULL;
+ int k;
for (j = 0; j < VFE_AXI_SRC_MAX; j++) {
- stream_info = &vfe_dev->axi_data.stream_info[j];
- if (stream_info->state == INACTIVE)
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, j);
+ if (stream_info->state == INACTIVE ||
+ stream_info->state == AVAILABLE)
continue;
for (pingpong_bit = 0; pingpong_bit < 2; pingpong_bit++) {
+ dma_addr_t temp;
+
+ buf = stream_info->buf[pingpong_bit];
+ if (buf == NULL) {
+ pr_err("%s: buf NULL for stream %x num_isp %d\n",
+ __func__,
+ stream_info->stream_src,
+ stream_info->num_isp);
+ continue;
+ }
+ temp = buf->mapped_info[0].paddr +
+ buf->mapped_info[0].len;
+ pr_err("%s: stream %x ping bit %d uses buffer %pa-%pa, num_isp %d\n",
+ __func__, stream_info->stream_src,
+ pingpong_bit,
+ &buf->mapped_info[0].paddr, &temp,
+ stream_info->num_isp);
+
for (i = 0; i < stream_info->num_planes; i++) {
- buf = stream_info->buf[pingpong_bit];
- if (buf == NULL) {
- pr_err("%s: buf NULL\n", __func__);
- continue;
- }
- pr_debug("%s: stream_id %x ping-pong %d plane %d start_addr %lu addr_offset %x len %zx stride %d scanline %d\n"
+ for (k = 0; k < stream_info->num_isp; k++) {
+ pr_debug(
+ "%s: stream_id %x ping-pong %d plane %d start_addr %lu addr_offset %x len %zx stride %d scanline %d\n"
, __func__, stream_info->stream_id,
pingpong_bit, i, (unsigned long)
buf->mapped_info[i].paddr,
stream_info->
- plane_cfg[i].plane_addr_offset,
+ plane_cfg[k][i].plane_addr_offset,
buf->mapped_info[i].len,
stream_info->
- plane_cfg[i].output_stride,
+ plane_cfg[k][i].output_stride,
stream_info->
- plane_cfg[i].output_scan_lines
+ plane_cfg[k][i].output_scan_lines
);
+ }
}
}
}
@@ -1601,36 +1654,35 @@ static struct msm_isp_buffer *msm_isp_get_stream_buffer(
int rc = 0;
uint32_t bufq_handle = 0;
struct msm_isp_buffer *buf = NULL;
- struct msm_vfe_axi_stream *temp_stream_info = NULL;
struct msm_vfe_frame_request_queue *queue_req;
+ uint32_t buf_index = MSM_ISP_INVALID_BUF_INDEX;
if (!stream_info->controllable_output) {
bufq_handle = stream_info->bufq_handle
[VFE_BUF_QUEUE_DEFAULT];
} else {
- temp_stream_info = msm_isp_get_controllable_stream(
- vfe_dev, stream_info);
queue_req = list_first_entry_or_null(
- &temp_stream_info->request_q,
+ &stream_info->request_q,
struct msm_vfe_frame_request_queue, list);
if (!queue_req)
return buf;
- bufq_handle = temp_stream_info->
+ bufq_handle = stream_info->
bufq_handle[queue_req->buff_queue_id];
if (!bufq_handle ||
- temp_stream_info->request_q_cnt <= 0) {
+ stream_info->request_q_cnt <= 0) {
pr_err_ratelimited("%s: Drop request. Shared stream is stopped.\n",
__func__);
return buf;
}
+ buf_index = queue_req->buf_index;
queue_req->cmd_used = 0;
list_del(&queue_req->list);
- temp_stream_info->request_q_cnt--;
+ stream_info->request_q_cnt--;
}
rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr,
- vfe_dev->pdev->id, bufq_handle, &buf);
+ vfe_dev->pdev->id, bufq_handle, buf_index, &buf);
if (rc == -EFAULT) {
msm_isp_halt_send_error(vfe_dev,
@@ -1649,144 +1701,74 @@ static struct msm_isp_buffer *msm_isp_get_stream_buffer(
return buf;
}
-static int msm_isp_cfg_ping_pong_address(struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
- int scratch)
+static int msm_isp_cfg_ping_pong_address(
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status)
{
int i;
- struct msm_isp_buffer *buf = NULL;
+ int j;
uint32_t pingpong_bit;
- uint32_t stream_idx = HANDLE_TO_IDX(stream_info->stream_handle);
+ struct vfe_device *vfe_dev = stream_info->vfe_dev[0];
uint32_t buffer_size_byte = 0;
int32_t word_per_line = 0;
dma_addr_t paddr;
- struct dual_vfe_resource *dual_vfe_res = NULL;
- uint32_t vfe_id = 0;
- unsigned long flags;
+ struct msm_isp_buffer *buf = NULL;
- if (stream_idx >= VFE_AXI_SRC_MAX) {
- pr_err("%s: Invalid stream_idx", __func__);
- return -EINVAL;
- }
- /* make sure that streams are in right state */
- if ((stream_info->stream_src < RDI_INTF_0) &&
- vfe_dev->is_split) {
- dual_vfe_res = vfe_dev->common_data->dual_vfe_res;
- if (!dual_vfe_res->vfe_base[ISP_VFE0] ||
- !dual_vfe_res->axi_data[ISP_VFE0] ||
- !dual_vfe_res->vfe_base[ISP_VFE1] ||
- !dual_vfe_res->axi_data[ISP_VFE1]) {
- pr_err("%s:%d failed vfe0 %pK %pK vfe %pK %pK\n",
- __func__, __LINE__,
- dual_vfe_res->vfe_base[ISP_VFE0],
- dual_vfe_res->axi_data[ISP_VFE0],
- dual_vfe_res->vfe_base[ISP_VFE1],
- dual_vfe_res->axi_data[ISP_VFE1]);
- return -EINVAL;
- }
- } else if (!vfe_dev->is_split ||
- (stream_info->stream_src >= RDI_INTF_0 &&
- stream_info->stream_src <= RDI_INTF_2)) {
- dual_vfe_res = NULL;
- } else {
- pr_err("%s: Error! Should not reach this case is_split %d stream_src %d\n",
- __func__, vfe_dev->is_split, stream_info->stream_src);
- msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
+ /* Isolate pingpong_bit from pingpong_status */
+ pingpong_bit = ((pingpong_status >>
+ stream_info->wm[0][0]) & 0x1);
+
+ /* return if buffer already present */
+ if (stream_info->buf[!pingpong_bit]) {
+ pr_err("stream %x buffer already set for pingpong %d\n",
+ stream_info->stream_src, pingpong_bit);
return 0;
}
- if (!scratch)
- buf = msm_isp_get_stream_buffer(vfe_dev, stream_info);
+ buf = msm_isp_get_stream_buffer(vfe_dev, stream_info);
- /* Isolate pingpong_bit from pingpong_status */
- pingpong_bit = ((pingpong_status >>
- stream_info->wm[0]) & 0x1);
+ if (!buf) {
+ msm_isp_cfg_stream_scratch(stream_info, pingpong_status);
+ return 0;
+ }
for (i = 0; i < stream_info->num_planes; i++) {
- if (buf) {
- word_per_line = msm_isp_cal_word_per_line(
- stream_info->output_format, stream_info->
- plane_cfg[i].output_stride);
+ paddr = buf->mapped_info[i].paddr;
+ ISP_DBG(
+ "%s: vfe %d config buf %d to pingpong %d stream %x\n",
+ __func__, vfe_dev->pdev->id,
+ buf->buf_idx, !pingpong_bit,
+ stream_info->stream_id);
+ for (j = 0; j < stream_info->num_isp; j++) {
+ vfe_dev = stream_info->vfe_dev[j];
+ word_per_line =
+ msm_isp_cal_word_per_line(
+ stream_info->output_format,
+ stream_info->plane_cfg[j][i].output_stride);
if (word_per_line < 0) {
/* 0 means no prefetch*/
word_per_line = 0;
buffer_size_byte = 0;
} else {
- buffer_size_byte = (word_per_line * 8 *
- stream_info->plane_cfg[i].
- output_scan_lines) - stream_info->
- plane_cfg[i].plane_addr_offset;
+ buffer_size_byte =
+ (word_per_line * 8 *
+ stream_info->plane_cfg[j][i].
+ output_scan_lines) -
+ stream_info->plane_cfg[j][i].
+ plane_addr_offset;
}
-
- paddr = buf->mapped_info[i].paddr;
- ISP_DBG(
- "%s: vfe %d config buf %d to pingpong %d stream %x\n",
- __func__, vfe_dev->pdev->id,
- buf->buf_idx, !pingpong_bit,
- stream_info->stream_id);
- }
-
- if (dual_vfe_res) {
- for (vfe_id = 0; vfe_id < MAX_VFE; vfe_id++) {
- if (vfe_id != vfe_dev->pdev->id)
- spin_lock_irqsave(
- &dual_vfe_res->
- axi_data[vfe_id]->
- stream_info[stream_idx].
- lock, flags);
-
- if (buf)
- vfe_dev->hw_info->vfe_ops.axi_ops.
- update_ping_pong_addr(
- dual_vfe_res->vfe_base[vfe_id],
- dual_vfe_res->axi_data[vfe_id]->
- stream_info[stream_idx].wm[i],
- pingpong_bit, paddr +
- dual_vfe_res->axi_data[vfe_id]->
- stream_info[stream_idx].
- plane_cfg[i].plane_addr_offset,
- buffer_size_byte);
- else
- msm_isp_cfg_stream_scratch(
- dual_vfe_res->vfe_dev[vfe_id],
- &(dual_vfe_res->axi_data
- [vfe_id]->
- stream_info[stream_idx]),
- pingpong_status);
-
- if (i == 0) {
- dual_vfe_res->axi_data[vfe_id]->
- stream_info[stream_idx].
- buf[!pingpong_bit] =
- buf;
- }
- if (vfe_id != vfe_dev->pdev->id)
- spin_unlock_irqrestore(
- &dual_vfe_res->
- axi_data[vfe_id]->
- stream_info[stream_idx].
- lock, flags);
- }
- } else {
- if (buf)
- vfe_dev->hw_info->vfe_ops.axi_ops.
+ vfe_dev->hw_info->vfe_ops.axi_ops.
update_ping_pong_addr(
- vfe_dev->vfe_base, stream_info->wm[i],
+ vfe_dev->vfe_base,
+ stream_info->wm[j][i],
pingpong_bit, paddr +
- stream_info->plane_cfg[i].
- plane_addr_offset,
+ stream_info->plane_cfg[j][i].
+ plane_addr_offset,
buffer_size_byte);
- else
- msm_isp_cfg_stream_scratch(vfe_dev,
- stream_info, pingpong_status);
- if (0 == i)
- stream_info->buf[!pingpong_bit] = buf;
}
- if (0 == i && buf)
- buf->pingpong_bit = !pingpong_bit;
}
-
+ stream_info->buf[!pingpong_bit] = buf;
+ buf->pingpong_bit = !pingpong_bit;
return 0;
}
@@ -1803,10 +1785,14 @@ static void msm_isp_handle_done_buf_frame_id_mismatch(
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
error_event.u.error_info.err_type =
ISP_ERROR_FRAME_ID_MISMATCH;
- ret = vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
- buf->bufq_handle, buf->buf_idx, time_stamp,
- frame_id,
- stream_info->runtime_output_format);
+ if (stream_info->buf_divert)
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ else
+ ret = vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx, time_stamp,
+ frame_id,
+ stream_info->runtime_output_format);
if (ret == -EFAULT) {
msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
return;
@@ -1825,7 +1811,7 @@ static int msm_isp_process_done_buf(struct vfe_device *vfe_dev,
int rc;
unsigned long flags;
struct msm_isp_event_data buf_event;
- uint32_t stream_idx = HANDLE_TO_IDX(stream_info->stream_handle);
+ uint32_t stream_idx = stream_info->stream_src;
uint32_t buf_src;
uint8_t drop_frame = 0;
struct msm_isp_bufq *bufq = NULL;
@@ -1875,11 +1861,16 @@ static int msm_isp_process_done_buf(struct vfe_device *vfe_dev,
buf->buf_debug.put_state_last] =
MSM_ISP_BUFFER_STATE_DROP_SKIP;
buf->buf_debug.put_state_last ^= 1;
- rc = vfe_dev->buf_mgr->ops->buf_done(
- vfe_dev->buf_mgr,
- buf->bufq_handle, buf->buf_idx,
- time_stamp, frame_id,
- stream_info->runtime_output_format);
+ if (stream_info->buf_divert)
+ vfe_dev->buf_mgr->ops->put_buf(
+ vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ else
+ rc = vfe_dev->buf_mgr->ops->buf_done(
+ vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx,
+ time_stamp, frame_id,
+ stream_info->runtime_output_format);
if (rc == -EFAULT) {
msm_isp_halt_send_error(vfe_dev,
@@ -1918,6 +1909,11 @@ static int msm_isp_process_done_buf(struct vfe_device *vfe_dev,
return -EINVAL;
}
+ /* divert native buffers */
+ vfe_dev->buf_mgr->ops->buf_divert(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx, time_stamp,
+ frame_id);
+
if ((bufq != NULL) && bufq->buf_type == ISP_SHARE_BUF)
msm_isp_send_event(vfe_dev->common_data->
dual_vfe_res->vfe_dev[ISP_VFE1],
@@ -1957,6 +1953,7 @@ int msm_isp_drop_frame(struct vfe_device *vfe_dev,
unsigned long flags;
struct msm_isp_bufq *bufq = NULL;
uint32_t pingpong_bit;
+ int vfe_idx;
if (!vfe_dev || !stream_info || !ts || !sof_info) {
pr_err("%s %d vfe_dev %pK stream_info %pK ts %pK op_info %pK\n",
@@ -1964,11 +1961,14 @@ int msm_isp_drop_frame(struct vfe_device *vfe_dev,
sof_info);
return -EINVAL;
}
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
pingpong_status =
~vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
spin_lock_irqsave(&stream_info->lock, flags);
- pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1);
+ pingpong_bit =
+ (~(pingpong_status >> stream_info->wm[vfe_idx][0]) & 0x1);
done_buf = stream_info->buf[pingpong_bit];
if (done_buf) {
bufq = vfe_dev->buf_mgr->ops->get_bufq(vfe_dev->buf_mgr,
@@ -1994,93 +1994,149 @@ int msm_isp_drop_frame(struct vfe_device *vfe_dev,
return 0;
}
-static void msm_isp_get_camif_update_state_and_halt(
- struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
- enum msm_isp_camif_update_state *camif_update,
- int *halt)
+/**
+ * msm_isp_input_disable() - Disable the input for given vfe
+ * @vfe_dev: The vfe device whose input is to be disabled
+ *
+ * Returns - void
+ *
+ * If stream count on an input line is 0 then disable the input
+ */
+static void msm_isp_input_disable(struct vfe_device *vfe_dev)
{
- int i;
- struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- uint8_t pix_stream_cnt = 0, cur_pix_stream_cnt;
- cur_pix_stream_cnt =
- axi_data->src_info[VFE_PIX_0].pix_stream_count +
- axi_data->src_info[VFE_PIX_0].raw_stream_count;
- for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
- stream_info =
- &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
- if (stream_info->stream_src < RDI_INTF_0)
- pix_stream_cnt++;
- }
+ int ext_read =
+ (axi_data->src_info[VFE_PIX_0].input_mux == EXTERNAL_READ);
+ int stream_count;
+ int total_stream_count = 0;
+ int i;
- if (vfe_dev->axi_data.num_active_stream == stream_cfg_cmd->num_streams
- && (stream_cfg_cmd->cmd == STOP_STREAM ||
- stream_cfg_cmd->cmd == STOP_IMMEDIATELY))
- *halt = 1;
- else
- *halt = 0;
-
- if ((pix_stream_cnt) &&
- (axi_data->src_info[VFE_PIX_0].input_mux != EXTERNAL_READ)) {
- if (cur_pix_stream_cnt == 0 && pix_stream_cnt &&
- stream_cfg_cmd->cmd == START_STREAM)
- *camif_update = ENABLE_CAMIF;
- else if (cur_pix_stream_cnt &&
- (cur_pix_stream_cnt - pix_stream_cnt) == 0 &&
- (stream_cfg_cmd->cmd == STOP_STREAM ||
- stream_cfg_cmd->cmd == STOP_IMMEDIATELY)) {
- if (*halt)
- *camif_update = DISABLE_CAMIF_IMMEDIATELY;
- else
- *camif_update = DISABLE_CAMIF;
- }
+ for (i = 0; i < VFE_SRC_MAX; i++)
+ total_stream_count += axi_data->src_info[i].stream_count +
+ axi_data->src_info[i].raw_stream_count;
+
+ for (i = 0; i < VFE_SRC_MAX; i++) {
+ stream_count = axi_data->src_info[i].stream_count +
+ axi_data->src_info[i].raw_stream_count;
+ if (stream_count)
+ continue;
+ if (axi_data->src_info[i].active == 0)
+ continue;
+ /* deactivate the input line */
+ axi_data->src_info[i].active = 0;
+
+ if (i != VFE_PIX_0 || ext_read)
+ continue;
+ /* halt camif */
+ if (total_stream_count == 0)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev,
+ DISABLE_CAMIF_IMMEDIATELY);
else
- *camif_update = NO_UPDATE;
- } else
- *camif_update = NO_UPDATE;
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, DISABLE_CAMIF);
+ }
+
+ /* halt and reset hardware if all streams are disabled */
+ if (total_stream_count == 0) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
+ vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 0, 1);
+ vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
+
+ }
}
-static void msm_isp_update_camif_output_count(
- struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+/**
+ * msm_isp_input_enable() - Enable the input for given vfe
+ * @vfe_dev: The vfe device whose input is to be enabled
+ *
+ * Returns - void
+ *
+ * Enable inout line if it is not enabled
+ */
+static void msm_isp_input_enable(struct vfe_device *vfe_dev,
+ int sync_frame_id_src)
{
- int i;
- struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int ext_read =
+ (axi_data->src_info[VFE_PIX_0].input_mux == EXTERNAL_READ);
+ int stream_count;
+ int i;
- if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
- return;
-
- 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;
+ for (i = 0; i < VFE_SRC_MAX; i++) {
+ stream_count = axi_data->src_info[i].stream_count +
+ axi_data->src_info[i].raw_stream_count;
+ if (stream_count == 0)
+ continue;
+ if (axi_data->src_info[i].active)
+ continue;
+ /* activate the input since it is deactivated */
+ axi_data->src_info[i].frame_id = 0;
+ axi_data->src_info[i].active = 1;
+ if (i >= VFE_RAW_0 && sync_frame_id_src) {
+ /*
+ * Incase PIX and RDI streams are part
+ * of same session, this will ensure
+ * RDI stream will have same frame id
+ * as of PIX stream
+ */
+ axi_data->src_info[i].frame_id =
+ axi_data->src_info[VFE_PIX_0].frame_id;
}
- stream_info =
- &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
- if (stream_info->stream_src >= RDI_INTF_0)
+ if (i != VFE_PIX_0 || ext_read)
continue;
- if (stream_info->stream_src == PIX_ENCODER ||
- stream_info->stream_src == PIX_VIEWFINDER ||
- stream_info->stream_src == PIX_VIDEO ||
- stream_info->stream_src == IDEAL_RAW) {
- if (stream_cfg_cmd->cmd == START_STREAM)
- vfe_dev->axi_data.src_info[VFE_PIX_0].
- pix_stream_count++;
+ /* for camif input the camif needs enabling */
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, ENABLE_CAMIF);
+ }
+}
+
+/**
+ * msm_isp_update_intf_stream_cnt() - Update the stream count in axi interface
+ * @stream_info: The stream that is either being enabled/disabled
+ * @enable: 0 means stream is being disabled, else enabled
+ *
+ * Returns - void
+ */
+static void msm_isp_update_intf_stream_cnt(
+ struct msm_vfe_axi_stream *stream_info,
+ int enable)
+{
+ int i;
+
+ switch (stream_info->stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER:
+ case PIX_VIDEO:
+ case IDEAL_RAW:
+ case RDI_INTF_0:
+ case RDI_INTF_1:
+ case RDI_INTF_2:
+ for (i = 0; i < stream_info->num_isp; i++) {
+ if (enable)
+ stream_info->vfe_dev[i]->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].
+ stream_count++;
else
- vfe_dev->axi_data.src_info[VFE_PIX_0].
- pix_stream_count--;
- } else if (stream_info->stream_src == CAMIF_RAW) {
- if (stream_cfg_cmd->cmd == START_STREAM)
- vfe_dev->axi_data.src_info[VFE_PIX_0].
+ stream_info->vfe_dev[i]->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].
+ stream_count--;
+ }
+ break;
+ case CAMIF_RAW:
+ for (i = 0; i < stream_info->num_isp; i++) {
+ if (enable)
+ stream_info->vfe_dev[i]->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].
raw_stream_count++;
else
- vfe_dev->axi_data.src_info[VFE_PIX_0].
+ stream_info->vfe_dev[i]->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].
raw_stream_count--;
}
+ break;
+ default:
+ WARN(1, "Invalid steam src %d\n", stream_info->stream_src);
}
}
@@ -2091,20 +2147,24 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev)
{
int i, rc = 0;
struct msm_vfe_axi_stream *stream_info;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
uint64_t total_pix_bandwidth = 0, total_rdi_bandwidth = 0;
uint32_t num_pix_streams = 0;
uint64_t total_bandwidth = 0;
+ int vfe_idx;
for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
- stream_info = &axi_data->stream_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, i);
if (stream_info->state == ACTIVE ||
stream_info->state == START_PENDING) {
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev,
+ stream_info);
if (stream_info->stream_src < RDI_INTF_0) {
- total_pix_bandwidth += stream_info->bandwidth;
+ total_pix_bandwidth +=
+ stream_info->bandwidth[vfe_idx];
num_pix_streams++;
} else {
- total_rdi_bandwidth += stream_info->bandwidth;
+ total_rdi_bandwidth +=
+ stream_info->bandwidth[vfe_idx];
}
}
}
@@ -2119,121 +2179,88 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev)
return rc;
}
-static int msm_isp_axi_wait_for_cfg_done(struct vfe_device *vfe_dev,
- enum msm_isp_camif_update_state camif_update,
- uint32_t src_mask, int regUpdateCnt)
-{
- int rc;
- unsigned long flags;
- enum msm_vfe_input_src i = 0;
- spin_lock_irqsave(&vfe_dev->shared_data_lock, flags);
-
- for (i = 0; i < VFE_SRC_MAX; i++) {
- if (src_mask & (1 << i)) {
- if (vfe_dev->axi_data.stream_update[i] > 0) {
- pr_err("%s:Stream Update in progress. cnt %d\n",
- __func__,
- vfe_dev->axi_data.stream_update[i]);
- spin_unlock_irqrestore(
- &vfe_dev->shared_data_lock, flags);
- return -EINVAL;
- }
- vfe_dev->axi_data.stream_update[i] = regUpdateCnt;
- }
- }
- if (src_mask) {
- init_completion(&vfe_dev->stream_config_complete);
- vfe_dev->axi_data.pipeline_update = camif_update;
- }
- spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags);
- rc = wait_for_completion_timeout(
- &vfe_dev->stream_config_complete,
- msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
- if (rc == 0) {
- for (i = 0; i < VFE_SRC_MAX; i++) {
- if (src_mask & (1 << i)) {
- spin_lock_irqsave(&vfe_dev->shared_data_lock,
- flags);
- vfe_dev->axi_data.stream_update[i] = 0;
- spin_unlock_irqrestore(&vfe_dev->
- shared_data_lock, flags);
- }
- }
- pr_err("%s: wait timeout\n", __func__);
- rc = -EBUSY;
- } else {
- rc = 0;
- }
- return rc;
-}
-
static int msm_isp_init_stream_ping_pong_reg(
- struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
int rc = 0;
- if ((vfe_dev->is_split && vfe_dev->pdev->id == 1 &&
- stream_info->stream_src < RDI_INTF_0) ||
- !vfe_dev->is_split || stream_info->stream_src >= RDI_INTF_0) {
- /* Set address for both PING & PONG register */
- rc = msm_isp_cfg_ping_pong_address(vfe_dev,
- stream_info, VFE_PING_FLAG, 0);
- if (rc < 0) {
- pr_err("%s: No free buffer for ping\n",
- __func__);
- return rc;
- }
-
- if (stream_info->stream_type != BURST_STREAM ||
- stream_info->runtime_num_burst_capture > 1)
- rc = msm_isp_cfg_ping_pong_address(vfe_dev,
- stream_info, VFE_PONG_FLAG, 0);
+ /* Set address for both PING & PO NG register */
+ rc = msm_isp_cfg_ping_pong_address(
+ stream_info, VFE_PING_FLAG);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for ping\n",
+ __func__);
+ return rc;
+ }
+ if (stream_info->stream_type != BURST_STREAM ||
+ stream_info->runtime_num_burst_capture > 1)
+ rc = msm_isp_cfg_ping_pong_address(
+ stream_info, VFE_PONG_FLAG);
- if (rc < 0) {
- pr_err("%s: No free buffer for pong\n",
- __func__);
- return rc;
- }
+ if (rc < 0) {
+ pr_err("%s: No free buffer for pong\n",
+ __func__);
+ return rc;
}
return rc;
}
static void msm_isp_get_stream_wm_mask(
+ struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info,
uint32_t *wm_reload_mask)
{
int i;
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
for (i = 0; i < stream_info->num_planes; i++)
- *wm_reload_mask |= (1 << stream_info->wm[i]);
+ *wm_reload_mask |= (1 << stream_info->wm[vfe_idx][i]);
}
int msm_isp_axi_halt(struct vfe_device *vfe_dev,
struct msm_vfe_axi_halt_cmd *halt_cmd)
{
int rc = 0;
+ int i;
+ struct vfe_device *halt_vfes[MAX_VFE] = { NULL, NULL };
- if (atomic_read(&vfe_dev->error_info.overflow_state) ==
- OVERFLOW_DETECTED) {
- ISP_DBG("%s: VFE%d already halted, direct return\n",
- __func__, vfe_dev->pdev->id);
- return rc;
- }
+ if (vfe_dev->is_split)
+ for (i = 0; i < MAX_VFE; i++)
+ halt_vfes[i] = vfe_dev->common_data->
+ dual_vfe_res->vfe_dev[i];
+ else
+ halt_vfes[vfe_dev->pdev->id] = vfe_dev;
- if (halt_cmd->overflow_detected) {
- atomic_cmpxchg(&vfe_dev->error_info.overflow_state,
- NO_OVERFLOW, OVERFLOW_DETECTED);
- pr_err("%s: VFE%d Bus overflow detected: start recovery!\n",
- __func__, vfe_dev->pdev->id);
- }
+ for (i = 0; i < MAX_VFE; i++) {
+ vfe_dev = halt_vfes[i];
+ if (!vfe_dev)
+ continue;
+ if (atomic_read(&vfe_dev->error_info.overflow_state) ==
+ OVERFLOW_DETECTED) {
+ ISP_DBG("%s: VFE%d already halted, direct return\n",
+ __func__, vfe_dev->pdev->id);
+ continue;
+ }
- if (halt_cmd->stop_camif) {
- vfe_dev->hw_info->vfe_ops.core_ops.
- update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY);
+ if (halt_cmd->overflow_detected) {
+ atomic_cmpxchg(&vfe_dev->error_info.overflow_state,
+ NO_OVERFLOW, OVERFLOW_DETECTED);
+ pr_err("%s: VFE%d Bus overflow detected: start recovery!\n",
+ __func__, vfe_dev->pdev->id);
+ }
+
+ if (halt_cmd->stop_camif) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev,
+ DISABLE_CAMIF_IMMEDIATELY);
+ }
+ rc |= vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev,
+ halt_cmd->blocking_halt);
+
+ /* take care of pending items in tasklet after halt */
+ msm_isp_flush_tasklet(vfe_dev);
}
- rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev,
- halt_cmd->blocking_halt);
return rc;
}
@@ -2241,12 +2268,13 @@ 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, j;
+ int rc = 0, i, k;
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};
if (!reset_cmd) {
pr_err("%s: NULL pointer reset cmd %pK\n", __func__, reset_cmd);
@@ -2256,49 +2284,74 @@ int msm_isp_axi_reset(struct vfe_device *vfe_dev,
rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev,
0, reset_cmd->blocking);
+ if (vfe_dev->is_split) {
+ for (i = 0; i < MAX_VFE; i++)
+ update_vfes[i] = vfe_dev->common_data->dual_vfe_res->
+ vfe_dev[i];
+ } else {
+ update_vfes[vfe_dev->pdev->id] = vfe_dev;
+ }
msm_isp_get_timestamp(&timestamp);
- for (i = 0, j = 0; j < axi_data->num_active_stream &&
- i < VFE_AXI_SRC_MAX; i++, j++) {
- stream_info = &axi_data->stream_info[i];
- if (stream_info->stream_src >= VFE_AXI_SRC_MAX) {
- rc = -1;
- pr_err("%s invalid stream src = %d\n", __func__,
- stream_info->stream_src);
- break;
- }
- if (stream_info->state != ACTIVE) {
- j--;
+ for (k = 0; k < MAX_VFE; k++) {
+ vfe_dev = update_vfes[k];
+ if (!vfe_dev)
continue;
- }
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev,
+ 0, reset_cmd->blocking);
- for (bufq_id = 0; bufq_id < VFE_BUF_QUEUE_MAX; bufq_id++) {
- bufq_handle = stream_info->bufq_handle[bufq_id];
- if (!bufq_handle)
- continue;
- /* set ping pong address to scratch before flush */
- spin_lock_irqsave(&stream_info->lock, flags);
- msm_isp_cfg_stream_scratch(vfe_dev, stream_info,
- VFE_PING_FLAG);
- msm_isp_cfg_stream_scratch(vfe_dev, stream_info,
- VFE_PONG_FLAG);
- spin_unlock_irqrestore(&stream_info->lock, flags);
- rc = vfe_dev->buf_mgr->ops->flush_buf(
- vfe_dev->buf_mgr, vfe_dev->pdev->id,
- bufq_handle, MSM_ISP_BUFFER_FLUSH_ALL,
- &timestamp.buf_time, reset_cmd->frame_id);
- if (rc == -EFAULT) {
- msm_isp_halt_send_error(vfe_dev,
- ISP_EVENT_BUF_FATAL_ERROR);
- return rc;
+ for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
+ stream_info = msm_isp_get_stream_common_data(
+ vfe_dev, i);
+ if (stream_info->stream_src >= VFE_AXI_SRC_MAX) {
+ rc = -1;
+ pr_err("%s invalid stream src = %d\n",
+ __func__,
+ stream_info->stream_src);
+ break;
}
+ if (stream_info->state == AVAILABLE ||
+ stream_info->state == INACTIVE)
+ continue;
- axi_data->src_info[SRC_TO_INTF(stream_info->
- stream_src)].frame_id = reset_cmd->frame_id;
- msm_isp_reset_burst_count_and_frame_drop(vfe_dev,
- stream_info);
+ /* handle dual stream on ISP_VFE1 turn */
+ if (stream_info->num_isp > 1 &&
+ vfe_dev->pdev->id == ISP_VFE0)
+ continue;
+
+ for (bufq_id = 0; bufq_id < VFE_BUF_QUEUE_MAX;
+ bufq_id++) {
+ bufq_handle = stream_info->bufq_handle[bufq_id];
+ if (!bufq_handle)
+ continue;
+
+ /* set ping pong to scratch before flush */
+ spin_lock_irqsave(&stream_info->lock, flags);
+ msm_isp_cfg_stream_scratch(stream_info,
+ VFE_PING_FLAG);
+ msm_isp_cfg_stream_scratch(stream_info,
+ VFE_PONG_FLAG);
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ rc = vfe_dev->buf_mgr->ops->flush_buf(
+ vfe_dev->buf_mgr,
+ bufq_handle, MSM_ISP_BUFFER_FLUSH_ALL,
+ &timestamp.buf_time,
+ reset_cmd->frame_id);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
+
+ axi_data->src_info[SRC_TO_INTF(stream_info->
+ stream_src)].frame_id =
+ reset_cmd->frame_id;
+ msm_isp_reset_burst_count_and_frame_drop(
+ vfe_dev, stream_info);
+ }
}
}
@@ -2308,46 +2361,67 @@ int msm_isp_axi_reset(struct vfe_device *vfe_dev,
return rc;
}
-int msm_isp_axi_restart(struct vfe_device *vfe_dev,
+int msm_isp_axi_restart(struct vfe_device *vfe_dev_ioctl,
struct msm_vfe_axi_restart_cmd *restart_cmd)
{
- int rc = 0, i, j;
+ 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 wm_reload_mask = 0x0;
+ uint32_t wm_reload_mask = 0;
unsigned long flags;
+ struct vfe_device *update_vfes[MAX_VFE] = {0, 0};
+ struct vfe_device *vfe_dev;
+
+ if (vfe_dev_ioctl->is_split) {
+ for (i = 0; i < MAX_VFE; i++)
+ update_vfes[i] = vfe_dev_ioctl->common_data->
+ dual_vfe_res->vfe_dev[i];
+ } else {
+ update_vfes[vfe_dev_ioctl->pdev->id] = vfe_dev_ioctl;
+ }
- vfe_dev->buf_mgr->frameId_mismatch_recovery = 0;
- for (i = 0, j = 0; j < axi_data->num_active_stream &&
- i < VFE_AXI_SRC_MAX; i++, j++) {
- stream_info = &axi_data->stream_info[i];
- if (stream_info->state != ACTIVE) {
- j--;
+ vfe_dev_ioctl->buf_mgr->frameId_mismatch_recovery = 0;
+ for (k = 0; k < MAX_VFE; k++) {
+ vfe_dev = update_vfes[k];
+ if (!vfe_dev)
continue;
+ vfe_dev->buf_mgr->frameId_mismatch_recovery = 0;
+ for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
+ stream_info = msm_isp_get_stream_common_data(
+ vfe_dev, i);
+ if (stream_info->state == AVAILABLE ||
+ stream_info->state == INACTIVE)
+ continue;
+ msm_isp_get_stream_wm_mask(vfe_dev, stream_info,
+ &wm_reload_mask);
+ /* handle dual stream on ISP_VFE1 turn */
+ if (stream_info->num_isp > 1 &&
+ vfe_dev->pdev->id == ISP_VFE0)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ for (j = 0; j < MSM_ISP_COMP_IRQ_MAX; j++)
+ stream_info->composite_irq[j] = 0;
+ msm_isp_init_stream_ping_pong_reg(stream_info);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
}
- msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask);
- spin_lock_irqsave(&stream_info->lock, flags);
- msm_isp_init_stream_ping_pong_reg(vfe_dev, stream_info);
- spin_unlock_irqrestore(&stream_info->lock, flags);
- }
- vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev,
- vfe_dev->vfe_base, wm_reload_mask);
- rc = vfe_dev->hw_info->vfe_ops.axi_ops.restart(vfe_dev, 0,
- restart_cmd->enable_camif);
- if (rc < 0)
- pr_err("%s Error restarting HW\n", __func__);
+ vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev,
+ vfe_dev->vfe_base, wm_reload_mask);
+ vfe_dev->hw_info->vfe_ops.axi_ops.restart(vfe_dev, 0,
+ restart_cmd->enable_camif);
+ }
return rc;
}
-static int msm_isp_axi_update_cgc_override(struct vfe_device *vfe_dev,
+static int msm_isp_axi_update_cgc_override(struct vfe_device *vfe_dev_ioctl,
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
uint8_t cgc_override)
{
int i = 0, j = 0;
struct msm_vfe_axi_stream *stream_info;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ int k;
+ struct vfe_device *vfe_dev;
+ int vfe_idx;
if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
return -EINVAL;
@@ -2357,14 +2431,21 @@ static int msm_isp_axi_update_cgc_override(struct vfe_device *vfe_dev,
VFE_AXI_SRC_MAX) {
return -EINVAL;
}
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl,
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
for (j = 0; j < stream_info->num_planes; j++) {
- if (vfe_dev->hw_info->vfe_ops.axi_ops.
- update_cgc_override)
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ if (!vfe_dev->hw_info->vfe_ops.axi_ops.
+ update_cgc_override)
+ continue;
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(
+ vfe_dev, stream_info);
vfe_dev->hw_info->vfe_ops.axi_ops.
update_cgc_override(vfe_dev,
- stream_info->wm[j], cgc_override);
+ stream_info->wm[vfe_idx][j],
+ cgc_override);
+ }
}
}
return 0;
@@ -2456,8 +2537,7 @@ static int msm_isp_update_dual_HW_ms_info_at_start(
static int msm_isp_update_dual_HW_ms_info_at_stop(
struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
- enum msm_isp_camif_update_state camif_update)
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
{
int i, rc = 0;
uint8_t slave_id;
@@ -2476,13 +2556,13 @@ static int msm_isp_update_dual_HW_ms_info_at_stop(
VFE_AXI_SRC_MAX) {
return -EINVAL;
}
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
stream_src = SRC_TO_INTF(stream_info->stream_src);
/* Remove PIX if DISABLE CAMIF */
- if (stream_src == VFE_PIX_0 && !((camif_update == DISABLE_CAMIF)
- || (camif_update == DISABLE_CAMIF_IMMEDIATELY)))
+ if (stream_src == VFE_PIX_0 &&
+ axi_data->src_info[VFE_PIX_0].active)
continue;
src_info = &axi_data->src_info[stream_src];
@@ -2517,404 +2597,489 @@ static int msm_isp_update_dual_HW_ms_info_at_stop(
return rc;
}
-static int msm_isp_update_dual_HW_axi(struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream *stream_info)
+/**
+ * msm_isp_axi_wait_for_stream_cfg_done() - Wait for a stream completion
+ * @stream_info: The stream to wait on
+ * @active: Reset means wait for stream to be INACTIVE else wait for ACTIVE
+ *
+ * Returns - 0 on success else error code
+ */
+static int msm_isp_axi_wait_for_stream_cfg_done(
+ struct msm_vfe_axi_stream *stream_info, int active)
+{
+ int rc = -1;
+ unsigned long flags;
+
+ /* No need to wait if stream is already in required state */
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (active && ACTIVE == stream_info->state)
+ rc = 0;
+ if (!active && INACTIVE == stream_info->state)
+ rc = 0;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (rc == 0)
+ return rc;
+
+ rc = wait_for_completion_timeout(
+ active ? &stream_info->active_comp :
+ &stream_info->inactive_comp,
+ msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
+
+ if (rc <= 0) {
+ rc = rc ? rc : -ETIMEDOUT;
+ pr_err("%s: wait for stream %x/%x state %d config failed %d\n",
+ __func__,
+ stream_info->stream_id,
+ stream_info->stream_src,
+ stream_info->state,
+ rc);
+ rc = -EINVAL;
+ } else {
+ rc = 0;
+ }
+ return rc;
+}
+
+/**
+ * msm_isp_axi_wait_for_streams() - Wait for completion of a number of streams
+ * @streams: The streams to wait on
+ * @num_stream: Number of streams to wait on
+ * @active: Reset means wait for stream to be INACTIVE else wait for ACTIVE
+ *
+ * Returns - 0 on success else error code
+ */
+static int msm_isp_axi_wait_for_streams(struct msm_vfe_axi_stream **streams,
+ int num_stream, int active)
{
+ int i;
int rc = 0;
- int vfe_id;
- uint32_t stream_idx = HANDLE_TO_IDX(stream_info->stream_handle);
- struct dual_vfe_resource *dual_vfe_res = NULL;
+ struct msm_vfe_axi_stream *stream_info;
- if (stream_idx >= VFE_AXI_SRC_MAX) {
- pr_err("%s: Invalid stream idx %d\n", __func__, stream_idx);
+ for (i = 0; i < num_stream; i++) {
+ stream_info = streams[i];
+ rc |= msm_isp_axi_wait_for_stream_cfg_done(stream_info, active);
+ }
+ return rc;
+}
+
+static int __msm_isp_check_stream_state(struct msm_vfe_axi_stream *stream_info,
+ int cmd)
+{
+ switch (stream_info->state) {
+ case AVAILABLE:
return -EINVAL;
+ case PAUSING:
+ case RESUMING:
+ case RESUME_PENDING:
+ case ACTIVE:
+ if (cmd != 0)
+ return -EALREADY;
+ break;
+ case INACTIVE:
+ if (cmd == 0)
+ return -EALREADY;
+ break;
+ /*
+ * stream cannot be in following states since we always
+ * wait in ioctl for stream to be active or inactive
+ */
+ case UPDATING:
+ case START_PENDING:
+ case STARTING:
+ case STOPPING:
+ case STOP_PENDING:
+ case PAUSE_PENDING:
+ default:
+ WARN(1, "Invalid state %d\n", stream_info->state);
}
+ return 0;
+}
- dual_vfe_res = vfe_dev->common_data->dual_vfe_res;
- if (!dual_vfe_res->vfe_dev[ISP_VFE0] ||
- !dual_vfe_res->vfe_dev[ISP_VFE1] ||
- !dual_vfe_res->axi_data[ISP_VFE0] ||
- !dual_vfe_res->axi_data[ISP_VFE1]) {
- pr_err("%s: Error in dual vfe resource\n", __func__);
- rc = -EINVAL;
- } else {
- if (stream_info->state == RESUME_PENDING &&
- (dual_vfe_res->axi_data[!vfe_dev->pdev->id]->
- stream_info[stream_idx].state == RESUME_PENDING)) {
- /* Update the AXI only after both ISPs receiving the
- Reg update interrupt*/
- for (vfe_id = 0; vfe_id < MAX_VFE; vfe_id++) {
- rc = msm_isp_axi_stream_enable_cfg(
- dual_vfe_res->vfe_dev[vfe_id],
- &dual_vfe_res->axi_data[vfe_id]->
- stream_info[stream_idx], 1);
- dual_vfe_res->axi_data[vfe_id]->
- stream_info[stream_idx].state =
- RESUMING;
+static void __msm_isp_stop_axi_streams(struct msm_vfe_axi_stream **streams,
+ int num_streams, int cmd_type)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data;
+ struct msm_isp_timestamp timestamp;
+ int total_stream_count = 0;
+ uint32_t bufq_id = 0, bufq_handle = 0;
+ struct msm_vfe_axi_stream *stream_info;
+ unsigned long flags;
+ uint32_t intf;
+ int rc;
+ struct vfe_device *vfe_dev;
+ struct vfe_device *update_vfes[MAX_VFE] = {0, 0};
+ int k;
+
+ msm_isp_get_timestamp(&timestamp);
+
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ spin_lock_irqsave(&stream_info->lock, flags);
+ /*
+ * since we can get here from start axi stream error path due
+ * to which the stream may be intermittent state like
+ * STARTING/START_PENDING, force the stream to move out of
+ * intermittent state so it can be made INACTIVE. The
+ * intermittent states update variables so better to go through
+ * those state transitions instead of directly forcing stream to
+ * be INACTIVE
+ */
+ while (stream_info->state != ACTIVE)
+ __msm_isp_axi_stream_update(stream_info,
+ &timestamp);
+ msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG);
+ msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ if (stream_info->num_planes > 1)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_comp_mask(vfe_dev, stream_info);
+ else
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_wm_irq_mask(vfe_dev, stream_info);
+ update_vfes[vfe_dev->pdev->id] = vfe_dev;
+ }
+ init_completion(&stream_info->inactive_comp);
+ stream_info->state = STOP_PENDING;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ msm_isp_update_intf_stream_cnt(stream_info, 0);
+ }
+
+ for (k = 0; k < MAX_VFE; k++) {
+ int ext_read;
+
+ if (!update_vfes[k])
+ continue;
+ vfe_dev = update_vfes[k];
+ axi_data = &vfe_dev->axi_data;
+ ext_read =
+ (axi_data->src_info[VFE_PIX_0].input_mux == EXTERNAL_READ);
+ for (i = 0; i < VFE_SRC_MAX; i++) {
+ total_stream_count +=
+ axi_data->src_info[i].stream_count +
+ axi_data->src_info[i].raw_stream_count;
+ if (i != VFE_PIX_0)
+ continue;
+ if (axi_data->src_info[i].stream_count == 0) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ enable_module(vfe_dev, 0xFF, 0);
+ /* reg update for PIX with 0 streams active */
+ if (ext_read == 0)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, VFE_PIX_0);
}
}
+
+ }
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ if (total_stream_count == 0 ||
+ ((stream_info->stream_type == BURST_STREAM) &&
+ stream_info->runtime_num_burst_capture == 0)) {
+ spin_lock_irqsave(&stream_info->lock, flags);
+ while (stream_info->state != INACTIVE)
+ __msm_isp_axi_stream_update(
+ stream_info, &timestamp);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ }
+
+ rc = msm_isp_axi_wait_for_streams(streams, num_streams, 0);
+ if (rc) {
+ pr_err("%s: wait for stream comp failed, retry...\n", __func__);
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ if (stream_info->state == INACTIVE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ __msm_isp_axi_stream_update(stream_info,
+ &timestamp);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ rc = msm_isp_axi_wait_for_streams(streams, num_streams, 0);
+ if (rc) {
+ pr_err("%s: wait for stream comp failed, force streams to inactive\n",
+ __func__);
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ if (stream_info->state == INACTIVE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ while (stream_info->state != INACTIVE)
+ __msm_isp_axi_stream_update(
+ stream_info, &timestamp);
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ }
+ }
+ }
+ /* clear buffers that are dequeued */
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ for (bufq_id = 0; bufq_id < VFE_BUF_QUEUE_MAX; bufq_id++) {
+ bufq_handle = stream_info->bufq_handle[bufq_id];
+ if (!bufq_handle)
+ continue;
+ vfe_dev = stream_info->vfe_dev[0];
+ rc = vfe_dev->buf_mgr->ops->flush_buf(
+ vfe_dev->buf_mgr,
+ bufq_handle, MSM_ISP_BUFFER_FLUSH_ALL,
+ &timestamp.buf_time, 0);
+ if (rc == -EFAULT)
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ }
+ }
+
+ for (k = 0; k < MAX_VFE; k++) {
+ if (!update_vfes[k])
+ continue;
+ msm_isp_update_stream_bandwidth(update_vfes[k]);
+ msm_isp_input_disable(update_vfes[k]);
+ }
+
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ intf = SRC_TO_INTF(stream_info->stream_src);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ axi_data = &vfe_dev->axi_data;
+ if (axi_data->src_info[intf].stream_count == 0)
+ vfe_dev->reg_update_requested &=
+ ~(BIT(intf));
+ }
}
- return rc;
}
-static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
- enum msm_isp_camif_update_state camif_update)
+static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
{
int i, rc = 0;
- uint8_t src_state, wait_for_complete = 0;
- uint32_t wm_reload_mask = 0x0;
+ uint8_t src_state;
+ uint32_t wm_reload_mask[MAX_VFE] = {0, 0};
struct msm_vfe_axi_stream *stream_info;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
uint32_t src_mask = 0;
unsigned long flags;
+ struct msm_vfe_axi_stream *streams[MAX_NUM_STREAM];
+ int num_streams = 0;
+ struct msm_isp_timestamp timestamp;
+ struct vfe_device *update_vfes[MAX_VFE] = {0, 0};
+ int k;
+ uint32_t num_active_streams[MAX_VFE] = {0, 0};
+ struct vfe_device *vfe_dev;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev_ioctl->axi_data;
if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM)
return -EINVAL;
- if (camif_update == ENABLE_CAMIF) {
- ISP_DBG("%s: vfe %d camif enable\n", __func__,
- vfe_dev->pdev->id);
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id = 0;
- }
+ msm_isp_get_timestamp(&timestamp);
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 = &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ 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)
src_state = axi_data->src_info[
SRC_TO_INTF(stream_info->stream_src)].active;
+
else {
ISP_DBG("%s: invalid src info index\n", __func__);
- return -EINVAL;
+ rc = -EINVAL;
+ goto error;
}
-
- msm_isp_calculate_bandwidth(axi_data, stream_info);
- msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask);
spin_lock_irqsave(&stream_info->lock, flags);
- msm_isp_reset_framedrop(vfe_dev, stream_info);
- rc = msm_isp_init_stream_ping_pong_reg(vfe_dev, stream_info);
+ rc = __msm_isp_check_stream_state(stream_info, 1);
+ if (-EALREADY == rc) {
+ rc = 0;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ goto error;
+ }
+
+ msm_isp_calculate_bandwidth(stream_info);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ msm_isp_get_stream_wm_mask(stream_info->vfe_dev[k],
+ stream_info, &wm_reload_mask[
+ stream_info->vfe_dev[k]->pdev->id]);
+ src_state = stream_info->vfe_dev[k]->axi_data.src_info[
+ SRC_TO_INTF(stream_info->stream_src)].active;
+ if (update_vfes[stream_info->vfe_dev[k]->pdev->id])
+ continue;
+ update_vfes[stream_info->vfe_dev[k]->pdev->id] =
+ stream_info->vfe_dev[k];
+ num_active_streams[stream_info->vfe_dev[k]->pdev->id] =
+ stream_info->vfe_dev[k]->axi_data.
+ num_active_stream;
+ }
+ msm_isp_reset_framedrop(vfe_dev_ioctl, stream_info);
+ rc = msm_isp_init_stream_ping_pong_reg(stream_info);
if (rc < 0) {
pr_err("%s: No buffer for stream%d\n", __func__,
HANDLE_TO_IDX(
stream_cfg_cmd->stream_handle[i]));
spin_unlock_irqrestore(&stream_info->lock, flags);
- return rc;
+ goto error;
}
- spin_unlock_irqrestore(&stream_info->lock, flags);
- 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);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ 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);
+ }
}
+ init_completion(&stream_info->active_comp);
stream_info->state = START_PENDING;
+ msm_isp_update_intf_stream_cnt(stream_info, 1);
- ISP_DBG("%s, Stream 0x%x src %d src_state %d on vfe %d\n",
- __func__, stream_info->stream_id,
- HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]),
- src_state, vfe_dev->pdev->id);
-
+ ISP_DBG("%s, Stream 0x%x src_state %d on vfe %d\n", __func__,
+ stream_info->stream_src, src_state,
+ vfe_dev_ioctl->pdev->id);
if (src_state) {
src_mask |= (1 << SRC_TO_INTF(stream_info->stream_src));
- wait_for_complete = 1;
} else {
- if (vfe_dev->dump_reg)
- msm_camera_io_dump(vfe_dev->vfe_base,
- 0x1000, 1);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+
+ if (vfe_dev->dump_reg)
+ msm_camera_io_dump(vfe_dev->vfe_base,
+ 0x1000, 1);
+ }
- /*Configure AXI start bits to start immediately*/
- msm_isp_axi_stream_enable_cfg(vfe_dev, stream_info, 0);
- stream_info->state = ACTIVE;
- vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev,
- SRC_TO_INTF(stream_info->stream_src));
+ /* Configure AXI start bits to start immediately */
+ while (stream_info->state != ACTIVE)
+ __msm_isp_axi_stream_update(
+ stream_info, &timestamp);
- /*
- * Active bit is set in enable_camif for PIX.
- * For RDI, set it here
- */
- if (SRC_TO_INTF(stream_info->stream_src) >= VFE_RAW_0 &&
- SRC_TO_INTF(stream_info->stream_src) <
- VFE_SRC_MAX) {
- /* Incase PIX and RDI streams are part of same
- * session, this will ensure RDI stream will
- * have same frame id as of PIX stream
- */
- if (stream_cfg_cmd->sync_frame_id_src)
- vfe_dev->axi_data.src_info[SRC_TO_INTF(
- stream_info->stream_src)].frame_id =
- vfe_dev->axi_data.src_info[VFE_PIX_0]
- .frame_id;
- else
- vfe_dev->axi_data.src_info[SRC_TO_INTF(
- stream_info->stream_src)].frame_id = 0;
- vfe_dev->axi_data.src_info[SRC_TO_INTF(
- stream_info->stream_src)].active = 1;
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
+ vfe_dev,
+ SRC_TO_INTF(stream_info->stream_src));
}
}
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ streams[num_streams++] = stream_info;
}
- msm_isp_update_stream_bandwidth(vfe_dev);
- vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev,
- vfe_dev->vfe_base, wm_reload_mask);
- msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
- if (camif_update == ENABLE_CAMIF) {
- vfe_dev->hw_info->vfe_ops.core_ops.
- update_camif_state(vfe_dev, camif_update);
- vfe_dev->axi_data.camif_state = CAMIF_ENABLE;
- vfe_dev->common_data->dual_vfe_res->epoch_sync_mask = 0;
+ for (i = 0; i < MAX_VFE; i++) {
+ vfe_dev = update_vfes[i];
+ if (!vfe_dev)
+ continue;
+ if (num_active_streams[i] == 0) {
+ /* Configure UB */
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_ub(vfe_dev);
+ /* when start reset overflow state */
+ atomic_set(&vfe_dev->error_info.overflow_state,
+ NO_OVERFLOW);
+ }
+ msm_isp_update_stream_bandwidth(vfe_dev);
+ vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev,
+ vfe_dev->vfe_base, wm_reload_mask[i]);
+
+ msm_isp_input_enable(vfe_dev,
+ stream_cfg_cmd->sync_frame_id_src);
}
- if (wait_for_complete) {
- rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update,
- src_mask, 2);
- if (rc < 0) {
- pr_err("%s: wait for config done failed\n", __func__);
- for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(
- stream_cfg_cmd->stream_handle[i])];
- stream_info->state = STOPPING;
- msm_isp_axi_stream_enable_cfg(
- vfe_dev, stream_info, 0);
- stream_cfg_cmd->cmd = STOP_IMMEDIATELY;
- msm_isp_update_camif_output_count(vfe_dev,
- stream_cfg_cmd);
- }
- }
+ rc = msm_isp_axi_wait_for_streams(streams, num_streams, 1);
+ if (rc < 0) {
+ pr_err("%s: wait for config done failed\n", __func__);
+ goto error;
}
+ return 0;
+error:
+ __msm_isp_stop_axi_streams(streams, num_streams,
+ STOP_STREAM);
+
return rc;
}
-static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev,
- struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd,
- enum msm_isp_camif_update_state camif_update,
- int halt)
+static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev_ioctl,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
{
int i, rc = 0;
- uint8_t wait_for_complete_for_this_stream = 0;
struct msm_vfe_axi_stream *stream_info = NULL;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- int ext_read =
- (axi_data->src_info[VFE_PIX_0].input_mux == EXTERNAL_READ);
- uint32_t src_mask = 0, intf, bufq_id = 0, bufq_handle = 0;
+ struct msm_vfe_axi_stream *streams[MAX_NUM_STREAM];
+ int num_streams = 0;
unsigned long flags;
- struct msm_isp_timestamp timestamp;
if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM ||
stream_cfg_cmd->num_streams == 0)
return -EINVAL;
- msm_isp_get_timestamp(&timestamp);
-
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 = &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl,
+ HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]));
- /* set ping pong address to scratch before stream stop */
spin_lock_irqsave(&stream_info->lock, flags);
- msm_isp_cfg_stream_scratch(vfe_dev, stream_info, VFE_PING_FLAG);
- msm_isp_cfg_stream_scratch(vfe_dev, stream_info, VFE_PONG_FLAG);
+ rc = __msm_isp_check_stream_state(stream_info, 0);
spin_unlock_irqrestore(&stream_info->lock, flags);
- wait_for_complete_for_this_stream = 0;
-
- if (stream_info->num_planes > 1)
- vfe_dev->hw_info->vfe_ops.axi_ops.
- clear_comp_mask(vfe_dev, stream_info);
- else
- vfe_dev->hw_info->vfe_ops.axi_ops.
- clear_wm_irq_mask(vfe_dev, stream_info);
-
- stream_info->state = STOP_PENDING;
-
- if (!halt && !ext_read &&
- !(stream_info->stream_type == BURST_STREAM &&
- stream_info->runtime_num_burst_capture == 0))
- wait_for_complete_for_this_stream = 1;
-
- ISP_DBG("%s: stream 0x%x, vfe %d camif %d halt %d wait %d\n",
- __func__,
- stream_info->stream_id,
- vfe_dev->pdev->id,
- camif_update,
- halt,
- wait_for_complete_for_this_stream);
-
- intf = SRC_TO_INTF(stream_info->stream_src);
- if (!wait_for_complete_for_this_stream ||
- stream_info->state == INACTIVE ||
- !vfe_dev->axi_data.src_info[intf].active) {
- msm_isp_axi_stream_enable_cfg(vfe_dev, stream_info, 0);
- stream_info->state = INACTIVE;
- vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev,
- SRC_TO_INTF(stream_info->stream_src));
-
+ if (rc) {
/*
- * Active bit is reset in disble_camif for PIX.
- * For RDI, reset it here for not wait_for_complete
- * This is assuming there is only 1 stream mapped to
- * each RDI.
+ * continue stopping other streams as error here means
+ * stream is already not active
*/
- if (intf >= VFE_RAW_0 &&
- intf < VFE_SRC_MAX) {
- vfe_dev->axi_data.src_info[intf].active = 0;
- }
- } else
- src_mask |= (1 << intf);
-
- }
-
- if (src_mask) {
- rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update,
- src_mask, 2);
- if (rc < 0) {
- pr_err("%s: wait for config done failed, retry...\n",
- __func__);
- for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(
- stream_cfg_cmd->stream_handle[i])];
- stream_info->state = STOPPING;
- msm_isp_axi_stream_enable_cfg(
- vfe_dev, stream_info, 0);
- vfe_dev->hw_info->vfe_ops.core_ops.reg_update(
- vfe_dev,
- SRC_TO_INTF(stream_info->stream_src));
- rc = msm_isp_axi_wait_for_cfg_done(vfe_dev,
- camif_update, src_mask, 1);
- if (rc < 0) {
- pr_err("%s: vfe%d cfg done failed\n",
- __func__, vfe_dev->pdev->id);
- stream_info->state = INACTIVE;
- } else
- pr_err("%s: vfe%d retry success! report err!\n",
- __func__, vfe_dev->pdev->id);
-
- rc = -EBUSY;
- }
- }
-
- /*
- * Active bit is reset in disble_camif for PIX.
- * For RDI, reset it here after wait_for_complete
- * This is assuming there is only 1 stream mapped to each RDI
- */
- for (i = VFE_RAW_0; i < VFE_SRC_MAX; i++) {
- if (src_mask & (1 << i)) {
- vfe_dev->axi_data.src_info[i].active = 0;
- }
- }
- }
-
- if (camif_update == DISABLE_CAMIF) {
- vfe_dev->hw_info->vfe_ops.core_ops.
- update_camif_state(vfe_dev, DISABLE_CAMIF);
- vfe_dev->axi_data.camif_state = CAMIF_DISABLE;
- } else if ((camif_update == DISABLE_CAMIF_IMMEDIATELY) ||
- (ext_read)) {
- if (!ext_read)
- vfe_dev->hw_info->vfe_ops.core_ops.
- update_camif_state(vfe_dev,
- DISABLE_CAMIF_IMMEDIATELY);
- vfe_dev->axi_data.camif_state = CAMIF_STOPPED;
- }
- if (halt) {
- /*during stop immediately, stop output then stop input*/
- vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
- vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 0, 1);
- vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
- }
-
- msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
- msm_isp_update_stream_bandwidth(vfe_dev);
-
- for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])];
- for (bufq_id = 0; bufq_id < VFE_BUF_QUEUE_MAX; bufq_id++) {
- bufq_handle = stream_info->bufq_handle[bufq_id];
- if (!bufq_handle)
- continue;
-
- rc = vfe_dev->buf_mgr->ops->flush_buf(
- vfe_dev->buf_mgr, vfe_dev->pdev->id,
- bufq_handle, MSM_ISP_BUFFER_FLUSH_ALL,
- &timestamp.buf_time, 0);
- if (rc == -EFAULT) {
- msm_isp_halt_send_error(vfe_dev,
- ISP_EVENT_BUF_FATAL_ERROR);
- return rc;
- }
+ rc = 0;
+ continue;
}
- vfe_dev->reg_update_requested &=
- ~(BIT(SRC_TO_INTF(stream_info->stream_src)));
+ streams[num_streams++] = stream_info;
}
+ __msm_isp_stop_axi_streams(streams, num_streams,
+ stream_cfg_cmd->cmd);
return rc;
}
-
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;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- enum msm_isp_camif_update_state camif_update;
- int halt = 0;
-
- rc = msm_isp_axi_check_stream_state(vfe_dev, stream_cfg_cmd);
- if (rc < 0) {
- pr_err("%s: Invalid stream state\n", __func__);
- return rc;
- }
+ int i;
- if (axi_data->num_active_stream == 0) {
- /*Configure UB*/
- vfe_dev->hw_info->vfe_ops.axi_ops.cfg_ub(vfe_dev);
- /*when start reset overflow state*/
- atomic_set(&vfe_dev->error_info.overflow_state,
- NO_OVERFLOW);
+ 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;
}
- msm_isp_get_camif_update_state_and_halt(vfe_dev, stream_cfg_cmd,
- &camif_update, &halt);
- if (camif_update == DISABLE_CAMIF)
- vfe_dev->axi_data.camif_state = CAMIF_STOPPING;
if (stream_cfg_cmd->cmd == START_STREAM) {
msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 1);
rc = msm_isp_start_axi_stream(
- vfe_dev, stream_cfg_cmd, camif_update);
+ vfe_dev, stream_cfg_cmd);
} else {
rc = msm_isp_stop_axi_stream(
- vfe_dev, stream_cfg_cmd, camif_update, halt);
+ vfe_dev, stream_cfg_cmd);
msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 0);
- if (axi_data->num_active_stream == 0) {
- /* Reset hvx state */
- vfe_dev->hvx_cmd = HVX_DISABLE;
- }
/*
* Use different ret value to not overwrite the error from
* msm_isp_stop_axi_stream
*/
ret = msm_isp_update_dual_HW_ms_info_at_stop(
- vfe_dev, stream_cfg_cmd, camif_update);
+ vfe_dev, stream_cfg_cmd);
if (ret < 0)
pr_warn("%s: Warning! Update dual_cam failed\n",
__func__);
+ if (vfe_dev->axi_data.num_active_stream == 0)
+ vfe_dev->hvx_cmd = HVX_DISABLE;
+ if (vfe_dev->is_split) {
+ struct vfe_device *vfe_temp =
+ vfe_dev->common_data->
+ dual_vfe_res->vfe_dev[ISP_VFE0];
+ if (vfe_temp->axi_data.num_active_stream == 0)
+ vfe_temp->hvx_cmd = HVX_DISABLE;
+ }
}
if (rc < 0)
@@ -2925,7 +3090,8 @@ int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg)
static int msm_isp_return_empty_buffer(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t user_stream_id,
- uint32_t frame_id, enum msm_vfe_input_src frame_src)
+ uint32_t frame_id, uint32_t buf_index,
+ enum msm_vfe_input_src frame_src)
{
int rc = -1;
struct msm_isp_buffer *buf = NULL;
@@ -2940,7 +3106,7 @@ static int msm_isp_return_empty_buffer(struct vfe_device *vfe_dev,
return -EINVAL;
}
- stream_idx = HANDLE_TO_IDX(stream_info->stream_handle);
+ stream_idx = stream_info->stream_src;
if (!stream_info->controllable_output)
return -EINVAL;
@@ -2961,7 +3127,7 @@ static int msm_isp_return_empty_buffer(struct vfe_device *vfe_dev,
rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr,
- vfe_dev->pdev->id, bufq_handle, &buf);
+ vfe_dev->pdev->id, bufq_handle, buf_index, &buf);
if (rc == -EFAULT) {
msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
return rc;
@@ -2999,7 +3165,7 @@ static int msm_isp_return_empty_buffer(struct vfe_device *vfe_dev,
static int msm_isp_request_frame(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t user_stream_id,
- uint32_t frame_id)
+ uint32_t frame_id, uint32_t buf_index)
{
struct msm_vfe_axi_stream_request_cmd stream_cfg_cmd;
struct msm_vfe_frame_request_queue *queue_req;
@@ -3007,10 +3173,9 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
unsigned long flags;
int rc = 0;
enum msm_vfe_input_src frame_src = 0;
- struct dual_vfe_resource *dual_vfe_res =
- vfe_dev->common_data->dual_vfe_res;
- uint32_t vfe_id = 0;
- bool dual_vfe = false;
+ int k;
+ uint32_t wm_mask = 0;
+ int vfe_idx;
if (!vfe_dev || !stream_info) {
pr_err("%s %d failed: vfe_dev %pK stream_info %pK\n", __func__,
@@ -3018,16 +3183,9 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
return -EINVAL;
}
- if (vfe_dev->is_split) {
- if (stream_info->stream_src < RDI_INTF_0) {
- if (vfe_dev->pdev->id == ISP_VFE1) {
- dual_vfe = true;
- } else {
- /* return early for dual vfe0 */
- return 0;
- }
- }
- }
+ /* return early for dual vfe0 */
+ if (stream_info->num_isp > 1 && vfe_dev->pdev->id == ISP_VFE0)
+ return 0;
if (stream_info->stream_src >= VFE_AXI_SRC_MAX) {
pr_err("%s:%d invalid stream src %d\n", __func__, __LINE__,
@@ -3052,7 +3210,7 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
vfe_dev->axi_data.src_info[VFE_PIX_0].active);
rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
- user_stream_id, frame_id, frame_src);
+ user_stream_id, frame_id, buf_index, frame_src);
if (rc < 0)
pr_err("%s:%d failed: return_empty_buffer src %d\n",
__func__, __LINE__, frame_src);
@@ -3067,13 +3225,13 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
stream_info->stream_id);
rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
- user_stream_id, frame_id, frame_src);
+ user_stream_id, frame_id, buf_index, frame_src);
if (rc < 0)
pr_err("%s:%d failed: return_empty_buffer src %d\n",
__func__, __LINE__, frame_src);
stream_info->current_framedrop_period =
MSM_VFE_STREAM_STOP_PERIOD;
- msm_isp_cfg_framedrop_reg(vfe_dev, stream_info);
+ msm_isp_cfg_framedrop_reg(stream_info);
return 0;
}
@@ -3093,10 +3251,12 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
if (!stream_info->bufq_handle[queue_req->buff_queue_id]) {
spin_unlock_irqrestore(&stream_info->lock, flags);
pr_err("%s:%d request frame failed on hw stream 0x%x, request stream %d due to no bufq idx: %d\n",
- __func__, __LINE__, stream_info->stream_handle,
+ __func__, __LINE__,
+ stream_info->stream_handle[0],
user_stream_id, queue_req->buff_queue_id);
return 0;
}
+ queue_req->buf_index = buf_index;
queue_req->cmd_used = 1;
stream_info->request_q_idx =
@@ -3105,14 +3265,15 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
stream_info->request_q_cnt++;
stream_info->undelivered_request_cnt++;
- stream_cfg_cmd.axi_stream_handle = stream_info->stream_handle;
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ stream_cfg_cmd.axi_stream_handle = stream_info->stream_handle[vfe_idx];
stream_cfg_cmd.frame_skip_pattern = NO_SKIP;
stream_cfg_cmd.init_frame_drop = 0;
stream_cfg_cmd.burst_count = stream_info->request_q_cnt;
if (stream_info->undelivered_request_cnt == 1) {
- rc = msm_isp_cfg_ping_pong_address(vfe_dev, stream_info,
- VFE_PING_FLAG, 0);
+ rc = msm_isp_cfg_ping_pong_address(stream_info,
+ VFE_PING_FLAG);
if (rc) {
spin_unlock_irqrestore(&stream_info->lock, flags);
stream_info->undelivered_request_cnt--;
@@ -3121,41 +3282,23 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
return rc;
}
- vfe_id = vfe_dev->pdev->id;
- if (dual_vfe) {
- struct msm_vfe_axi_stream *temp_stream_info;
-
- temp_stream_info = msm_isp_vfe_get_stream(dual_vfe_res,
- ISP_VFE0,
- HANDLE_TO_IDX(
- stream_info->stream_handle));
- msm_isp_get_stream_wm_mask(temp_stream_info,
- &dual_vfe_res->wm_reload_mask[ISP_VFE0]);
- msm_isp_get_stream_wm_mask(stream_info,
- &dual_vfe_res->wm_reload_mask[ISP_VFE1]);
- vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev,
- dual_vfe_res->vfe_base[ISP_VFE0],
- dual_vfe_res->wm_reload_mask[ISP_VFE0]);
- vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev,
- dual_vfe_res->vfe_base[ISP_VFE1],
- dual_vfe_res->wm_reload_mask[ISP_VFE1]);
- dual_vfe_res->wm_reload_mask[ISP_VFE0] = 0;
- dual_vfe_res->wm_reload_mask[ISP_VFE1] = 0;
- } else {
- msm_isp_get_stream_wm_mask(stream_info,
- &dual_vfe_res->wm_reload_mask[vfe_id]);
- vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev,
- vfe_dev->vfe_base,
- dual_vfe_res->wm_reload_mask[vfe_id]);
- dual_vfe_res->wm_reload_mask[vfe_id] = 0;
+ for (k = 0; k < stream_info->num_isp; k++) {
+ wm_mask = 0;
+ msm_isp_get_stream_wm_mask(stream_info->vfe_dev[k],
+ stream_info, &wm_mask);
+ stream_info->vfe_dev[k]->
+ hw_info->vfe_ops.axi_ops.reload_wm(
+ stream_info->vfe_dev[k],
+ stream_info->vfe_dev[k]->vfe_base, wm_mask);
+
}
stream_info->sw_ping_pong_bit = 0;
} else if (stream_info->undelivered_request_cnt == 2) {
pingpong_status =
vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(
vfe_dev);
- rc = msm_isp_cfg_ping_pong_address(vfe_dev,
- stream_info, pingpong_status, 0);
+ rc = msm_isp_cfg_ping_pong_address(
+ stream_info, pingpong_status);
if (rc) {
stream_info->undelivered_request_cnt--;
spin_unlock_irqrestore(&stream_info->lock,
@@ -3172,7 +3315,7 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
return -EINVAL;
}
- rc = msm_isp_calculate_framedrop(&vfe_dev->axi_data, &stream_cfg_cmd);
+ rc = msm_isp_calculate_framedrop(vfe_dev, &stream_cfg_cmd);
if (0 == rc)
msm_isp_reset_framedrop(vfe_dev, stream_info);
@@ -3186,25 +3329,45 @@ static int msm_isp_add_buf_queue(struct vfe_device *vfe_dev,
{
int rc = 0;
uint32_t bufq_id = 0;
+ unsigned long flags;
if (stream_id == stream_info->stream_id)
bufq_id = VFE_BUF_QUEUE_DEFAULT;
else
bufq_id = VFE_BUF_QUEUE_SHARED;
+ spin_lock_irqsave(&stream_info->lock, flags);
- stream_info->bufq_handle[bufq_id] =
- vfe_dev->buf_mgr->ops->get_bufq_handle(vfe_dev->buf_mgr,
- stream_info->session_id, stream_id);
if (stream_info->bufq_handle[bufq_id] == 0) {
- pr_err("%s: failed: No valid buffer queue for stream: 0x%x\n",
- __func__, stream_id);
- rc = -EINVAL;
+ stream_info->bufq_handle[bufq_id] =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(vfe_dev->buf_mgr,
+ stream_info->session_id, stream_id);
+ if (stream_info->bufq_handle[bufq_id] == 0) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("%s: failed: No valid buffer queue for stream: 0x%x\n",
+ __func__, stream_id);
+ return -EINVAL;
+ }
+ } else {
+ uint32_t bufq_handle = vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr,
+ stream_info->session_id,
+ stream_id);
+ if (bufq_handle != stream_info->bufq_handle[bufq_id]) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("%s: Stream %x already has buffer q %x cannot add handle %x\n",
+ __func__, stream_id,
+ stream_info->bufq_handle[bufq_id], bufq_handle);
+ return -EINVAL;
+ }
}
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+
ISP_DBG("%d: Add bufq handle:0x%x, idx:%d, for stream %d on VFE %d\n",
__LINE__, stream_info->bufq_handle[bufq_id],
- bufq_id, stream_info->stream_handle, vfe_dev->pdev->id);
+ bufq_id, stream_info->stream_handle[0],
+ vfe_dev->pdev->id);
return rc;
}
@@ -3221,18 +3384,102 @@ static void msm_isp_remove_buf_queue(struct vfe_device *vfe_dev,
bufq_id = VFE_BUF_QUEUE_SHARED;
spin_lock_irqsave(&stream_info->lock, flags);
- stream_info->bufq_handle[bufq_id] = 0;
+
+ if (stream_info->bufq_handle[bufq_id]) {
+ stream_info->bufq_handle[bufq_id] = 0;
+ if (stream_info->state == ACTIVE)
+ stream_info->state = UPDATING;
+ }
spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (stream_info->state == UPDATING)
+ msm_isp_axi_wait_for_stream_cfg_done(stream_info, 1);
+
+}
+
+/**
+ * msm_isp_stream_axi_cfg_update() - Apply axi config update to a stream
+ * @vfe_dev: The vfe device on which the update is to be applied
+ * @stream_info: Stream for which update is to be applied
+ * @update_info: Parameters of the update
+ *
+ * Returns - 0 on success else error code
+ *
+ * For dual vfe stream apply the update once update for both vfe is
+ * received.
+ */
+static int msm_isp_stream_axi_cfg_update(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ struct msm_vfe_axi_stream_cfg_update_info *update_info)
+{
+ int j;
+ int k;
+ unsigned long flags;
+ int vfe_idx;
+ if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[
+ SRC_TO_INTF(stream_info->stream_src)])) {
+ pr_err("%s: Update in progress for vfe %d intf %d\n",
+ __func__, vfe_dev->pdev->id,
+ SRC_TO_INTF(stream_info->stream_src));
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state != ACTIVE) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("Invalid stream state for axi update %d\n",
+ stream_info->state);
+ return -EINVAL;
+ }
+ if (stream_info->update_vfe_mask) {
+ if (stream_info->update_vfe_mask & (1 << vfe_dev->pdev->id)) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("%s: Stream %p/%x Update already in progress for vfe %d\n",
+ __func__, stream_info, stream_info->stream_src,
+ vfe_dev->pdev->id);
+ return -EINVAL;
+ }
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+
+ for (j = 0; j < stream_info->num_planes; j++)
+ stream_info->plane_cfg[vfe_idx][j] = update_info->plane_cfg[j];
+
+ stream_info->update_vfe_mask |= (1 << vfe_dev->pdev->id);
+ /* wait for update from all vfe's under stream before applying */
+ if (stream_info->update_vfe_mask != stream_info->vfe_mask) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ return 0;
+ }
+
+ atomic_set(&vfe_dev->axi_data.axi_cfg_update[
+ SRC_TO_INTF(stream_info->stream_src)], 1);
+ stream_info->output_format = update_info->output_format;
+ init_completion(&stream_info->active_comp);
+ if (((vfe_dev->hw_info->runtime_axi_update == 0) ||
+ (vfe_dev->dual_vfe_enable == 1))) {
+ stream_info->state = PAUSE_PENDING;
+ msm_isp_axi_stream_enable_cfg(stream_info);
+ stream_info->state = PAUSING;
+ } else {
+ for (j = 0; j < stream_info->num_planes; j++) {
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_info, j);
+ }
+ }
+ stream_info->state = RESUMING;
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ return 0;
}
int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
{
- int rc = 0, i, j;
+ int rc = 0, i;
struct msm_vfe_axi_stream *stream_info;
- struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
struct msm_vfe_axi_stream_update_cmd *update_cmd = arg;
- struct msm_vfe_axi_stream_cfg_update_info *update_info;
+ struct msm_vfe_axi_stream_cfg_update_info *update_info = NULL;
struct msm_isp_sw_framskip *sw_skip_info = NULL;
unsigned long flags;
struct msm_isp_timestamp timestamp;
@@ -3243,14 +3490,15 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
return -EINVAL;
for (i = 0; i < update_cmd->num_streams; i++) {
- update_info = &update_cmd->update_info[i];
+ update_info = (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
/*check array reference bounds*/
if (HANDLE_TO_IDX(update_info->stream_handle) >=
VFE_AXI_SRC_MAX) {
return -EINVAL;
}
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(update_info->stream_handle)];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
if (SRC_TO_INTF(stream_info->stream_src) >= VFE_SRC_MAX)
continue;
if (stream_info->state != ACTIVE &&
@@ -3267,37 +3515,45 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
return -EINVAL;
}
if (update_cmd->update_type == UPDATE_STREAM_AXI_CONFIG &&
- atomic_read(&axi_data->axi_cfg_update[
- SRC_TO_INTF(stream_info->stream_src)])) {
+ stream_info->state != ACTIVE) {
pr_err("%s: AXI stream config updating\n", __func__);
return -EBUSY;
}
}
- for (i = 0; i < update_cmd->num_streams; i++) {
- update_info = &update_cmd->update_info[i];
- stream_info = &axi_data->stream_info[
- HANDLE_TO_IDX(update_info->stream_handle)];
-
- switch (update_cmd->update_type) {
- case ENABLE_STREAM_BUF_DIVERT:
+ switch (update_cmd->update_type) {
+ case ENABLE_STREAM_BUF_DIVERT:
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
stream_info->buf_divert = 1;
- break;
- case DISABLE_STREAM_BUF_DIVERT:
+ }
+ break;
+ case DISABLE_STREAM_BUF_DIVERT:
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
stream_info->buf_divert = 0;
msm_isp_get_timestamp(&timestamp);
frame_id = vfe_dev->axi_data.src_info[
SRC_TO_INTF(stream_info->stream_src)].frame_id;
/* set ping pong address to scratch before flush */
spin_lock_irqsave(&stream_info->lock, flags);
- msm_isp_cfg_stream_scratch(vfe_dev, stream_info,
- VFE_PING_FLAG);
- msm_isp_cfg_stream_scratch(vfe_dev, stream_info,
- VFE_PONG_FLAG);
+ msm_isp_cfg_stream_scratch(stream_info,
+ VFE_PING_FLAG);
+ msm_isp_cfg_stream_scratch(stream_info,
+ VFE_PONG_FLAG);
spin_unlock_irqrestore(&stream_info->lock, flags);
- rc = vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
- vfe_dev->pdev->id,
- stream_info->bufq_handle[VFE_BUF_QUEUE_DEFAULT],
+ rc = vfe_dev->buf_mgr->ops->flush_buf(
+ vfe_dev->buf_mgr,
+ stream_info->bufq_handle
+ [VFE_BUF_QUEUE_DEFAULT],
MSM_ISP_BUFFER_FLUSH_DIVERTED,
&timestamp.buf_time, frame_id);
if (rc == -EFAULT) {
@@ -3305,11 +3561,18 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
ISP_EVENT_BUF_FATAL_ERROR);
return rc;
}
- break;
- case UPDATE_STREAM_FRAMEDROP_PATTERN: {
+ }
+ break;
+ case UPDATE_STREAM_FRAMEDROP_PATTERN: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
uint32_t framedrop_period =
msm_isp_get_framedrop_period(
- update_info->skip_pattern);
+ update_info->skip_pattern);
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
spin_lock_irqsave(&stream_info->lock, flags);
/* no change then break early */
if (stream_info->current_framedrop_period ==
@@ -3331,11 +3594,18 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
stream_info->current_framedrop_period =
framedrop_period;
if (stream_info->stream_type != BURST_STREAM)
- msm_isp_cfg_framedrop_reg(vfe_dev, stream_info);
+ msm_isp_cfg_framedrop_reg(stream_info);
spin_unlock_irqrestore(&stream_info->lock, flags);
- break;
}
- case UPDATE_STREAM_SW_FRAME_DROP: {
+ break;
+ }
+ case UPDATE_STREAM_SW_FRAME_DROP: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
sw_skip_info = &update_info->sw_skip_info;
if (sw_skip_info->stream_src_mask != 0) {
/* SW image buffer drop */
@@ -3350,88 +3620,88 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
spin_unlock_irqrestore(&stream_info->lock,
flags);
}
- break;
}
- case UPDATE_STREAM_AXI_CONFIG: {
- for (j = 0; j < stream_info->num_planes; j++) {
- stream_info->plane_cfg[j] =
- update_info->plane_cfg[j];
- }
- stream_info->output_format = update_info->output_format;
- if ((stream_info->state == ACTIVE) &&
- ((vfe_dev->hw_info->runtime_axi_update == 0) ||
- (vfe_dev->dual_vfe_enable == 1))) {
- spin_lock_irqsave(&stream_info->lock, flags);
- stream_info->state = PAUSE_PENDING;
- msm_isp_axi_stream_enable_cfg(
- vfe_dev, stream_info, 1);
- stream_info->state = PAUSING;
- atomic_set(&axi_data->
- axi_cfg_update[SRC_TO_INTF(
- stream_info->stream_src)],
- UPDATE_REQUESTED);
- spin_unlock_irqrestore(&stream_info->lock,
- flags);
- } else {
- for (j = 0; j < stream_info->num_planes; j++) {
- vfe_dev->hw_info->vfe_ops.axi_ops.
- cfg_wm_reg(vfe_dev, stream_info, j);
- }
-
- spin_lock_irqsave(&stream_info->lock, flags);
- if (stream_info->state != ACTIVE) {
- stream_info->runtime_output_format =
- stream_info->output_format;
- } else {
- stream_info->state = RESUMING;
- atomic_set(&axi_data->
- axi_cfg_update[SRC_TO_INTF(
- stream_info->stream_src)],
- APPLYING_UPDATE_RESUME);
- }
- spin_unlock_irqrestore(&stream_info->lock,
- flags);
- }
- break;
+ break;
+ }
+ case UPDATE_STREAM_AXI_CONFIG: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
+ rc = msm_isp_stream_axi_cfg_update(vfe_dev, stream_info,
+ update_info);
+ if (rc)
+ return rc;
}
- case UPDATE_STREAM_REQUEST_FRAMES: {
+ break;
+ }
+ case UPDATE_STREAM_REQUEST_FRAMES: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
rc = msm_isp_request_frame(vfe_dev, stream_info,
update_info->user_stream_id,
- update_info->frame_id);
+ update_info->frame_id,
+ MSM_ISP_INVALID_BUF_INDEX);
if (rc)
pr_err("%s failed to request frame!\n",
__func__);
- break;
}
- case UPDATE_STREAM_ADD_BUFQ: {
+ break;
+ }
+ case UPDATE_STREAM_ADD_BUFQ: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
rc = msm_isp_add_buf_queue(vfe_dev, stream_info,
update_info->user_stream_id);
if (rc)
pr_err("%s failed to add bufq!\n", __func__);
- break;
}
- case UPDATE_STREAM_REMOVE_BUFQ: {
+ break;
+ }
+ case UPDATE_STREAM_REMOVE_BUFQ: {
+ for (i = 0; i < update_cmd->num_streams; i++) {
+ update_info =
+ (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(update_info->stream_handle));
msm_isp_remove_buf_queue(vfe_dev, stream_info,
update_info->user_stream_id);
pr_debug("%s, Remove bufq for Stream 0x%x\n",
__func__, stream_info->stream_id);
- if (stream_info->state == ACTIVE) {
- stream_info->state = UPDATING;
- rc = msm_isp_axi_wait_for_cfg_done(vfe_dev,
- NO_UPDATE, (1 << SRC_TO_INTF(
- stream_info->stream_src)), 2);
- if (rc < 0)
- pr_err("%s: wait for update failed\n",
- __func__);
- }
-
- break;
- }
- default:
- pr_err("%s: Invalid update type\n", __func__);
- return -EINVAL;
}
+ break;
+ }
+ case UPDATE_STREAM_REQUEST_FRAMES_VER2: {
+ struct msm_vfe_axi_stream_cfg_update_info_req_frm *req_frm =
+ &update_cmd->req_frm_ver2;
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ HANDLE_TO_IDX(req_frm->stream_handle));
+ rc = msm_isp_request_frame(vfe_dev, stream_info,
+ req_frm->user_stream_id,
+ req_frm->frame_id,
+ req_frm->buf_index);
+ if (rc)
+ pr_err("%s failed to request frame!\n",
+ __func__);
+ break;
}
+ default:
+ pr_err("%s: Invalid update type %d\n", __func__,
+ update_cmd->update_type);
+ return -EINVAL;
+ }
+
return rc;
}
@@ -3446,7 +3716,7 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
unsigned long flags;
struct timeval *time_stamp;
uint32_t frame_id, buf_index = -1;
- struct msm_vfe_axi_stream *temp_stream;
+ int vfe_idx;
if (!ts) {
pr_err("%s: Error! Invalid argument\n", __func__);
@@ -3464,10 +3734,13 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
src_info[SRC_TO_INTF(stream_info->stream_src)].frame_id;
spin_lock_irqsave(&stream_info->lock, flags);
- pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1);
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ pingpong_bit = (~(pingpong_status >>
+ stream_info->wm[vfe_idx][0]) & 0x1);
for (i = 0; i < stream_info->num_planes; i++) {
if (pingpong_bit !=
- (~(pingpong_status >> stream_info->wm[i]) & 0x1)) {
+ (~(pingpong_status >>
+ stream_info->wm[vfe_idx][i]) & 0x1)) {
spin_unlock_irqrestore(&stream_info->lock, flags);
pr_err("%s: Write master ping pong mismatch. Status: 0x%x\n",
__func__, pingpong_status);
@@ -3476,15 +3749,23 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
return;
}
}
-
if (stream_info->state == INACTIVE) {
- msm_isp_cfg_stream_scratch(vfe_dev, stream_info,
- pingpong_status);
+ WARN_ON(stream_info->buf[pingpong_bit] != NULL);
spin_unlock_irqrestore(&stream_info->lock, flags);
- pr_err_ratelimited("%s: Warning! Stream already inactive. Drop irq handling\n",
- __func__);
return;
}
+
+ /* composite the irq for dual vfe */
+ rc = msm_isp_composite_irq(vfe_dev, stream_info,
+ MSM_ISP_COMP_IRQ_PING_BUFDONE + pingpong_bit);
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (rc < 0)
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return;
+ }
+
done_buf = stream_info->buf[pingpong_bit];
if (vfe_dev->buf_mgr->frameId_mismatch_recovery == 1) {
@@ -3494,46 +3775,28 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
return;
}
- stream_info->frame_id++;
if (done_buf)
buf_index = done_buf->buf_idx;
- rc = vfe_dev->buf_mgr->ops->update_put_buf_cnt(vfe_dev->buf_mgr,
+ ISP_DBG("%s: vfe %d: stream 0x%x, frame id %d, pingpong bit %d\n",
+ __func__,
vfe_dev->pdev->id,
- done_buf ? done_buf->bufq_handle :
- stream_info->bufq_handle[VFE_BUF_QUEUE_DEFAULT], buf_index,
- time_stamp, frame_id, pingpong_bit);
+ stream_info->stream_id,
+ frame_id,
+ pingpong_bit);
- if (rc < 0) {
- spin_unlock_irqrestore(&stream_info->lock, flags);
- /* this usually means a serious scheduling error */
- msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
- return;
- }
- /*
- * Buf divert return value represent whether the buf
- * can be diverted. A positive return value means
- * other ISP hardware is still processing the frame.
- * A negative value is error. Return in both cases.
- */
- if (rc != 0) {
- spin_unlock_irqrestore(&stream_info->lock, flags);
- return;
- }
+ stream_info->frame_id++;
+ stream_info->buf[pingpong_bit] = NULL;
if (stream_info->stream_type == CONTINUOUS_STREAM ||
stream_info->runtime_num_burst_capture > 1) {
- rc = msm_isp_cfg_ping_pong_address(vfe_dev,
- stream_info, pingpong_status, 0);
+ rc = msm_isp_cfg_ping_pong_address(
+ stream_info, pingpong_status);
if (rc < 0)
ISP_DBG("%s: Error configuring ping_pong\n",
__func__);
} else if (done_buf) {
- rc = msm_isp_cfg_ping_pong_address(vfe_dev,
- stream_info, pingpong_status, 1);
- if (rc < 0)
- ISP_DBG("%s: Error configuring ping_pong\n",
- __func__);
+ msm_isp_cfg_stream_scratch(stream_info, pingpong_status);
}
if (!done_buf) {
@@ -3547,28 +3810,12 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
return;
}
- temp_stream = msm_isp_get_controllable_stream(vfe_dev,
- stream_info);
- if (temp_stream->stream_type == BURST_STREAM &&
- temp_stream->runtime_num_burst_capture) {
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->runtime_num_burst_capture) {
ISP_DBG("%s: burst_frame_count: %d\n",
__func__,
- temp_stream->runtime_num_burst_capture);
- temp_stream->runtime_num_burst_capture--;
- /*
- * For non controllable stream decrement the burst count for
- * dual stream as well here
- */
- if (!stream_info->controllable_output && vfe_dev->is_split &&
- RDI_INTF_0 > stream_info->stream_src) {
- temp_stream = msm_isp_vfe_get_stream(
- vfe_dev->common_data->dual_vfe_res,
- ((vfe_dev->pdev->id == ISP_VFE0) ?
- ISP_VFE1 : ISP_VFE0),
- HANDLE_TO_IDX(
- stream_info->stream_handle));
- temp_stream->runtime_num_burst_capture--;
- }
+ stream_info->runtime_num_burst_capture);
+ stream_info->runtime_num_burst_capture--;
}
rc = msm_isp_update_deliver_count(vfe_dev, stream_info,
@@ -3637,7 +3884,8 @@ void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
continue;
}
stream_idx = HANDLE_TO_IDX(comp_info->stream_handle);
- stream_info = &axi_data->stream_info[stream_idx];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ stream_idx);
msm_isp_process_axi_irq_stream(vfe_dev, stream_info,
pingpong_status, ts);
@@ -3656,7 +3904,8 @@ void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
pingpong_status);
continue;
}
- stream_info = &axi_data->stream_info[stream_idx];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev,
+ stream_idx);
msm_isp_process_axi_irq_stream(vfe_dev, stream_info,
pingpong_status, ts);
@@ -3670,6 +3919,7 @@ void msm_isp_axi_disable_all_wm(struct vfe_device *vfe_dev)
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
int i, j;
+ int vfe_idx;
if (!vfe_dev || !axi_data) {
pr_err("%s: error %pK %pK\n", __func__, vfe_dev, axi_data);
@@ -3677,14 +3927,16 @@ void msm_isp_axi_disable_all_wm(struct vfe_device *vfe_dev)
}
for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
- stream_info = &axi_data->stream_info[i];
+ stream_info = msm_isp_get_stream_common_data(vfe_dev, i);
if (stream_info->state != ACTIVE)
continue;
+ 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[j], 0);
+ stream_info->wm[vfe_idx][j], 0);
}
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
index 08053aa410e7..84720f3d8625 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
@@ -14,37 +14,15 @@
#include "msm_isp.h"
+#define HANDLE_TO_IDX(handle) (handle & 0xFF)
#define SRC_TO_INTF(src) \
((src < RDI_INTF_0 || src == VFE_AXI_SRC_MAX) ? VFE_PIX_0 : \
(VFE_RAW_0 + src - RDI_INTF_0))
-int msm_isp_axi_create_stream(struct vfe_device *vfe_dev,
- struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
-
-void msm_isp_axi_destroy_stream(
- struct msm_vfe_axi_shared_data *axi_data, int stream_idx);
-
-int msm_isp_validate_axi_request(
- struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
-
-void msm_isp_axi_reserve_wm(
- struct vfe_device *vfe_dev,
- struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream *stream_info);
-
-void msm_isp_axi_reserve_comp_mask(
- struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream *stream_info);
-
int msm_isp_axi_check_stream_state(
struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd);
-int msm_isp_calculate_framedrop(
- struct msm_vfe_axi_shared_data *axi_data,
- struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
void msm_isp_reset_framedrop(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info);
@@ -62,10 +40,13 @@ int msm_isp_axi_restart(struct vfe_device *vfe_dev,
struct msm_vfe_axi_restart_cmd *restart_cmd);
void msm_isp_axi_stream_update(struct vfe_device *vfe_dev,
- enum msm_vfe_input_src frame_src);
+ enum msm_vfe_input_src frame_src,
+ struct msm_isp_timestamp *ts);
-void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev,
- enum msm_vfe_input_src frame_src);
+void msm_isp_process_reg_upd_epoch_irq(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src,
+ enum msm_isp_comp_irq_types irq,
+ struct msm_isp_timestamp *ts);
void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type,
enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts);
@@ -94,6 +75,34 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev,
uint32_t pingpong_status,
struct msm_isp_timestamp *ts);
+void msm_isp_release_all_axi_stream(struct vfe_device *vfe_dev);
+
+static inline int msm_isp_get_vfe_idx_for_stream_user(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx;
+
+ for (vfe_idx = 0; vfe_idx < stream_info->num_isp; vfe_idx++) {
+ if (stream_info->vfe_dev[vfe_idx] == vfe_dev)
+ return vfe_idx;
+ }
+ return -ENOTTY;
+}
+
+static inline int msm_isp_get_vfe_idx_for_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev, stream_info);
+
+ if (vfe_idx < 0) {
+ WARN(1, "%s vfe index misssing for stream %d, vfe %d\n",
+ __func__, stream_info->stream_src, vfe_dev->pdev->id);
+ vfe_idx = 0;
+ }
+ return vfe_idx;
+}
+
static inline void msm_isp_cfg_wm_scratch(struct vfe_device *vfe_dev,
int wm,
uint32_t pingpong_bit)
@@ -103,18 +112,48 @@ static inline void msm_isp_cfg_wm_scratch(struct vfe_device *vfe_dev,
pingpong_bit, vfe_dev->buf_mgr->scratch_buf_addr, 0);
}
-static inline void msm_isp_cfg_stream_scratch(struct vfe_device *vfe_dev,
+static inline void msm_isp_cfg_stream_scratch(
struct msm_vfe_axi_stream *stream_info,
uint32_t pingpong_status)
{
int i;
+ int j;
uint32_t pingpong_bit;
-
- pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1);
- for (i = 0; i < stream_info->num_planes; i++)
- msm_isp_cfg_wm_scratch(vfe_dev, stream_info->wm[i],
+ int vfe_idx;
+
+ pingpong_bit = (~(pingpong_status >> stream_info->wm[0][0]) & 0x1);
+ for (i = 0; i < stream_info->num_planes; i++) {
+ for (j = 0; j < stream_info->num_isp; j++) {
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(
+ stream_info->vfe_dev[j], stream_info);
+ msm_isp_cfg_wm_scratch(stream_info->vfe_dev[j],
+ stream_info->wm[vfe_idx][i],
~pingpong_bit);
+ }
+ }
stream_info->buf[pingpong_bit] = NULL;
}
+static inline struct msm_vfe_axi_stream *msm_isp_get_stream_common_data(
+ struct vfe_device *vfe_dev, int stream_idx)
+{
+ struct msm_vfe_common_dev_data *common_data = vfe_dev->common_data;
+ struct msm_vfe_axi_stream *stream_info;
+
+ if (vfe_dev->is_split && stream_idx < RDI_INTF_0)
+ stream_info = &common_data->streams[stream_idx];
+ else
+ stream_info = &common_data->streams[VFE_AXI_SRC_MAX *
+ vfe_dev->pdev->id + stream_idx];
+ return stream_info;
+}
+
+static inline struct msm_vfe_axi_stream *msm_isp_vfe_get_stream(
+ struct dual_vfe_resource *dual_vfe_res,
+ int vfe_id, uint32_t index)
+{
+ return msm_isp_get_stream_common_data(dual_vfe_res->vfe_dev[vfe_id],
+ index);
+}
+
#endif /* __MSM_ISP_AXI_UTIL_H__ */
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 4aef6b5c7f38..f851e8c9289e 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
@@ -22,59 +22,89 @@ static inline void msm_isp_stats_cfg_wm_scratch(struct vfe_device *vfe_dev,
uint32_t pingpong_status)
{
vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
- vfe_dev->vfe_base, stream_info,
+ vfe_dev, stream_info,
pingpong_status, vfe_dev->buf_mgr->scratch_buf_addr);
}
-static inline void msm_isp_stats_cfg_stream_scratch(struct vfe_device *vfe_dev,
+static inline void msm_isp_stats_cfg_stream_scratch(
struct msm_vfe_stats_stream *stream_info,
uint32_t pingpong_status)
{
- uint32_t stats_idx = STATS_IDX(stream_info->stream_handle);
+ uint32_t stats_idx = STATS_IDX(stream_info->stream_handle[0]);
uint32_t pingpong_bit;
- uint32_t stats_pingpong_offset =
- vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset[
- stats_idx];
+ uint32_t stats_pingpong_offset;
+ struct vfe_device *vfe_dev;
+ int i;
+ stats_pingpong_offset = stream_info->vfe_dev[0]->hw_info->
+ stats_hw_info->stats_ping_pong_offset[stats_idx];
pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
- msm_isp_stats_cfg_wm_scratch(vfe_dev, stream_info,
- pingpong_status);
+ for (i = 0; i < stream_info->num_isp; i++) {
+ vfe_dev = stream_info->vfe_dev[i];
+ msm_isp_stats_cfg_wm_scratch(vfe_dev, stream_info,
+ pingpong_status);
+ }
+
stream_info->buf[pingpong_bit] = NULL;
}
-static int msm_isp_stats_cfg_ping_pong_address(struct vfe_device *vfe_dev,
+static int msm_isp_composite_stats_irq(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info,
+ enum msm_isp_comp_irq_types irq)
+{
+ /* interrupt recv on same vfe w/o recv on other vfe */
+ if (stream_info->composite_irq[irq] & (1 << vfe_dev->pdev->id)) {
+ pr_err("%s: irq %d out of sync for dual vfe on vfe %d\n",
+ __func__, irq, vfe_dev->pdev->id);
+ return -EFAULT;
+ }
+
+ stream_info->composite_irq[irq] |= (1 << vfe_dev->pdev->id);
+ if (stream_info->composite_irq[irq] != stream_info->vfe_mask)
+ return 1;
+
+ stream_info->composite_irq[irq] = 0;
+
+ return 0;
+}
+
+static int msm_isp_stats_cfg_ping_pong_address(
struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status)
{
- int rc = -1, vfe_id = 0;
- struct msm_isp_buffer *buf;
- uint32_t pingpong_bit = 0;
- uint32_t stats_pingpong_offset;
+ int rc = -1;
+ struct msm_isp_buffer *buf = NULL;
uint32_t bufq_handle = stream_info->bufq_handle;
- uint32_t stats_idx = STATS_IDX(stream_info->stream_handle);
- struct dual_vfe_resource *dual_vfe_res = NULL;
- struct msm_vfe_stats_stream *dual_vfe_stream_info = NULL;
+ uint32_t stats_idx = STATS_IDX(stream_info->stream_handle[0]);
+ struct vfe_device *vfe_dev = stream_info->vfe_dev[0];
+ uint32_t stats_pingpong_offset;
+ uint32_t pingpong_bit;
+ int k;
if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type ||
stats_idx >= MSM_ISP_STATS_MAX) {
pr_err("%s Invalid stats index %d", __func__, stats_idx);
return -EINVAL;
}
-
- stats_pingpong_offset =
- vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset[
- stats_idx];
-
+ stats_pingpong_offset = vfe_dev->hw_info->stats_hw_info->
+ stats_ping_pong_offset[stats_idx];
pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
+ /* if buffer already exists then no need to replace */
+ if (stream_info->buf[pingpong_bit])
+ return 0;
rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr,
- vfe_dev->pdev->id, bufq_handle, &buf);
+ vfe_dev->pdev->id, bufq_handle,
+ MSM_ISP_INVALID_BUF_INDEX, &buf);
if (rc == -EFAULT) {
msm_isp_halt_send_error(vfe_dev, ISP_EVENT_BUF_FATAL_ERROR);
return rc;
}
- if (rc < 0 || NULL == buf)
- vfe_dev->error_info.stats_framedrop_count[stats_idx]++;
+ if (rc < 0 || NULL == buf) {
+ for (k = 0; k < stream_info->num_isp; k++)
+ stream_info->vfe_dev[k]->error_info.
+ stats_framedrop_count[stats_idx]++;
+ }
if (buf && buf->num_planes != 1) {
pr_err("%s: Invalid buffer\n", __func__);
@@ -82,58 +112,22 @@ static int msm_isp_stats_cfg_ping_pong_address(struct vfe_device *vfe_dev,
rc = -EINVAL;
goto buf_error;
}
- if (vfe_dev->is_split) {
- dual_vfe_res = vfe_dev->common_data->dual_vfe_res;
- if (!dual_vfe_res->vfe_base[ISP_VFE0] ||
- !dual_vfe_res->stats_data[ISP_VFE0] ||
- !dual_vfe_res->vfe_base[ISP_VFE1] ||
- !dual_vfe_res->stats_data[ISP_VFE1]) {
- pr_err("%s:%d error vfe0 %pK %pK vfe1 %pK %pK\n",
- __func__, __LINE__,
- dual_vfe_res->vfe_base[ISP_VFE0],
- dual_vfe_res->stats_data[ISP_VFE0],
- dual_vfe_res->vfe_base[ISP_VFE1],
- dual_vfe_res->stats_data[ISP_VFE1]);
- } else {
- for (vfe_id = 0; vfe_id < MAX_VFE; vfe_id++) {
- dual_vfe_stream_info = &dual_vfe_res->
- stats_data[vfe_id]->
- stream_info[stats_idx];
- if (buf)
- vfe_dev->hw_info->vfe_ops.stats_ops.
- update_ping_pong_addr(
- dual_vfe_res->vfe_base[vfe_id],
- dual_vfe_stream_info,
- pingpong_status,
- buf->mapped_info[0].paddr +
- dual_vfe_stream_info->
- buffer_offset);
- else
- msm_isp_stats_cfg_stream_scratch(
- vfe_dev,
- dual_vfe_stream_info,
- pingpong_status);
- dual_vfe_stream_info->buf[pingpong_bit]
- = buf;
- }
- }
- } else {
- if (buf)
- vfe_dev->hw_info->vfe_ops.stats_ops.
- update_ping_pong_addr(
- vfe_dev->vfe_base, stream_info,
- pingpong_status, buf->mapped_info[0].paddr +
- stream_info->buffer_offset);
- else
- msm_isp_stats_cfg_stream_scratch(vfe_dev,
- stream_info, pingpong_status);
-
- stream_info->buf[pingpong_bit] = buf;
+ if (!buf) {
+ msm_isp_stats_cfg_stream_scratch(stream_info,
+ pingpong_status);
+ return 0;
}
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
+ vfe_dev, stream_info, pingpong_status,
+ buf->mapped_info[0].paddr +
+ stream_info->buffer_offset[k]);
+ }
+ stream_info->buf[pingpong_bit] = buf;
+ buf->pingpong_bit = pingpong_bit;
- if (buf)
- buf->pingpong_bit = pingpong_bit;
return 0;
buf_error:
vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
@@ -155,6 +149,8 @@ static int32_t msm_isp_stats_buf_divert(struct vfe_device *vfe_dev,
struct msm_isp_buffer *done_buf;
uint32_t stats_pingpong_offset;
uint32_t stats_idx;
+ int vfe_idx;
+ unsigned long flags;
if (!vfe_dev || !ts || !buf_event || !stream_info) {
pr_err("%s:%d failed: invalid params %pK %pK %pK %pK\n",
@@ -163,6 +159,9 @@ static int32_t msm_isp_stats_buf_divert(struct vfe_device *vfe_dev,
return -EINVAL;
}
frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+
+ spin_lock_irqsave(&stream_info->lock, flags);
+
sw_skip = &stream_info->sw_skip;
stats_event = &buf_event->u.stats;
@@ -182,73 +181,62 @@ static int32_t msm_isp_stats_buf_divert(struct vfe_device *vfe_dev,
(struct msm_isp_sw_framskip));
}
}
- stats_idx = STATS_IDX(stream_info->stream_handle);
+ vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, stream_info);
+ stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
stats_pingpong_offset =
vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset[
stats_idx];
pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1);
- done_buf = stream_info->buf[pingpong_bit];
-
- if (done_buf)
- buf_index = done_buf->buf_idx;
-
- rc = vfe_dev->buf_mgr->ops->update_put_buf_cnt(
- vfe_dev->buf_mgr, vfe_dev->pdev->id, stream_info->bufq_handle,
- buf_index, &ts->buf_time,
- frame_id, pingpong_bit);
-
- if (rc < 0) {
- if (rc == -EFAULT)
+ rc = msm_isp_composite_stats_irq(vfe_dev, stream_info,
+ MSM_ISP_COMP_IRQ_PING_BUFDONE + pingpong_bit);
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (rc < 0)
msm_isp_halt_send_error(vfe_dev,
- ISP_EVENT_BUF_FATAL_ERROR);
- pr_err("stats_buf_divert: update put buf cnt fail\n");
- return rc;
- }
-
- if (rc > 0) {
- ISP_DBG("%s: vfe_id %d buf_id %d bufq %x put_cnt 1\n", __func__,
- vfe_dev->pdev->id, buf_index,
- stream_info->bufq_handle);
+ ISP_EVENT_BUF_FATAL_ERROR);
return rc;
}
+ done_buf = stream_info->buf[pingpong_bit];
/* Program next buffer */
- rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev, stream_info,
+ stream_info->buf[pingpong_bit] = NULL;
+ rc = msm_isp_stats_cfg_ping_pong_address(stream_info,
pingpong_status);
- if (rc)
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+
+ if (!done_buf)
return rc;
- if (drop_buffer && done_buf) {
- rc = vfe_dev->buf_mgr->ops->buf_done(
+ buf_index = done_buf->buf_idx;
+ if (drop_buffer) {
+ vfe_dev->buf_mgr->ops->put_buf(
vfe_dev->buf_mgr,
done_buf->bufq_handle,
- done_buf->buf_idx, &ts->buf_time, frame_id, 0);
- if (rc == -EFAULT)
- msm_isp_halt_send_error(vfe_dev,
- ISP_EVENT_BUF_FATAL_ERROR);
- return rc;
+ done_buf->buf_idx);
+ } else {
+ /* divert native buffers */
+ vfe_dev->buf_mgr->ops->buf_divert(vfe_dev->buf_mgr,
+ done_buf->bufq_handle, done_buf->buf_idx,
+ &ts->buf_time, frame_id);
}
-
- if (done_buf) {
- stats_event->stats_buf_idxs
- [stream_info->stats_type] =
- done_buf->buf_idx;
- if (NULL == comp_stats_type_mask) {
- stats_event->stats_mask =
- 1 << stream_info->stats_type;
- ISP_DBG("%s: stats frameid: 0x%x %d bufq %x\n",
- __func__, buf_event->frame_id,
- stream_info->stats_type, done_buf->bufq_handle);
- msm_isp_send_event(vfe_dev,
- ISP_EVENT_STATS_NOTIFY +
- stream_info->stats_type,
- buf_event);
- } else {
- *comp_stats_type_mask |=
- 1 << stream_info->stats_type;
- }
+ stats_event->stats_buf_idxs
+ [stream_info->stats_type] =
+ done_buf->buf_idx;
+ if (comp_stats_type_mask == NULL) {
+ stats_event->stats_mask =
+ 1 << stream_info->stats_type;
+ ISP_DBG("%s: stats frameid: 0x%x %d bufq %x\n",
+ __func__, buf_event->frame_id,
+ stream_info->stats_type, done_buf->bufq_handle);
+ msm_isp_send_event(vfe_dev,
+ ISP_EVENT_STATS_NOTIFY +
+ stream_info->stats_type,
+ buf_event);
+ } else {
+ *comp_stats_type_mask |=
+ 1 << stream_info->stats_type;
}
return rc;
@@ -275,8 +263,9 @@ static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev,
for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
if (!(stats_irq_mask & (1 << i)))
continue;
- stream_info = &vfe_dev->stats_data.stream_info[i];
- if (stream_info->state == STATS_INACTIVE) {
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, i);
+ if (stream_info->state == STATS_INACTIVE ||
+ stream_info->state == STATS_STOPPING) {
pr_debug("%s: Warning! Stream already inactive. Drop irq handling\n",
__func__);
continue;
@@ -353,12 +342,17 @@ void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
}
int msm_isp_stats_create_stream(struct vfe_device *vfe_dev,
- struct msm_vfe_stats_stream_request_cmd *stream_req_cmd)
+ struct msm_vfe_stats_stream_request_cmd *stream_req_cmd,
+ struct msm_vfe_stats_stream *stream_info)
{
- int rc = -1;
- struct msm_vfe_stats_stream *stream_info = NULL;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ int rc = 0;
uint32_t stats_idx;
+ uint32_t framedrop_pattern;
+ uint32_t framedrop_period;
+ int i;
+
+ stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_stats_idx(stream_req_cmd->stats_type);
if (!(vfe_dev->hw_info->stats_hw_info->stats_capability_mask &
(1 << stream_req_cmd->stats_type))) {
@@ -366,16 +360,7 @@ int msm_isp_stats_create_stream(struct vfe_device *vfe_dev,
return rc;
}
- stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops.
- get_stats_idx(stream_req_cmd->stats_type);
-
- if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
- pr_err("%s Invalid stats index %d", __func__, stats_idx);
- return -EINVAL;
- }
-
- stream_info = &stats_data->stream_info[stats_idx];
- if (stream_info->state != STATS_AVALIABLE) {
+ if (stream_info->state != STATS_AVAILABLE) {
pr_err("%s: Stats already requested\n", __func__);
return rc;
}
@@ -389,17 +374,74 @@ int msm_isp_stats_create_stream(struct vfe_device *vfe_dev,
pr_err("%s: Invalid irq subsample pattern\n", __func__);
return rc;
}
+ if (stream_req_cmd->composite_flag >
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask) {
+ pr_err("%s: comp grp %d exceed max %d\n",
+ __func__, stream_req_cmd->composite_flag,
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask);
+ return -EINVAL;
+ }
- stream_info->session_id = stream_req_cmd->session_id;
- stream_info->stream_id = stream_req_cmd->stream_id;
- stream_info->composite_flag = stream_req_cmd->composite_flag;
- stream_info->stats_type = stream_req_cmd->stats_type;
- stream_info->buffer_offset = stream_req_cmd->buffer_offset;
- stream_info->framedrop_pattern = stream_req_cmd->framedrop_pattern;
- stream_info->init_stats_frame_drop = stream_req_cmd->init_frame_drop;
- stream_info->irq_subsample_pattern =
- stream_req_cmd->irq_subsample_pattern;
- stream_info->state = STATS_INACTIVE;
+ if (stream_info->num_isp == 0) {
+ stream_info->session_id = stream_req_cmd->session_id;
+ stream_info->stream_id = stream_req_cmd->stream_id;
+ stream_info->composite_flag = stream_req_cmd->composite_flag;
+ stream_info->stats_type = stream_req_cmd->stats_type;
+ framedrop_pattern = stream_req_cmd->framedrop_pattern;
+ if (framedrop_pattern == SKIP_ALL)
+ framedrop_pattern = 0;
+ else
+ framedrop_pattern = 1;
+ stream_info->framedrop_pattern = framedrop_pattern;
+ stream_info->init_stats_frame_drop =
+ stream_req_cmd->init_frame_drop;
+ stream_info->irq_subsample_pattern =
+ stream_req_cmd->irq_subsample_pattern;
+ framedrop_period = msm_isp_get_framedrop_period(
+ stream_req_cmd->framedrop_pattern);
+ stream_info->framedrop_period = framedrop_period;
+ } else {
+ if (stream_info->vfe_mask & (1 << vfe_dev->pdev->id)) {
+ pr_err("%s: stats %d already requested for vfe %d\n",
+ __func__, stats_idx, vfe_dev->pdev->id);
+ return -EINVAL;
+ }
+ if (stream_info->session_id != stream_req_cmd->session_id)
+ rc = -EINVAL;
+ if (stream_info->session_id != stream_req_cmd->session_id)
+ rc = -EINVAL;
+ if (stream_info->composite_flag !=
+ stream_req_cmd->composite_flag)
+ rc = -EINVAL;
+ if (stream_info->stats_type != stream_req_cmd->stats_type)
+ rc = -EINVAL;
+ framedrop_pattern = stream_req_cmd->framedrop_pattern;
+ if (framedrop_pattern == SKIP_ALL)
+ framedrop_pattern = 0;
+ else
+ framedrop_pattern = 1;
+ if (stream_info->framedrop_pattern != framedrop_pattern)
+ rc = -EINVAL;
+ framedrop_period = msm_isp_get_framedrop_period(
+ stream_req_cmd->framedrop_pattern);
+ if (stream_info->framedrop_period != framedrop_period)
+ rc = -EINVAL;
+ if (rc) {
+ pr_err("%s: Stats stream param mismatch between vfe\n",
+ __func__);
+ return rc;
+ }
+ }
+ stream_info->buffer_offset[stream_info->num_isp] =
+ stream_req_cmd->buffer_offset;
+ stream_info->vfe_dev[stream_info->num_isp] = vfe_dev;
+ stream_info->vfe_mask |= (1 << vfe_dev->pdev->id);
+ stream_info->num_isp++;
+ if (!vfe_dev->is_split || stream_info->num_isp == MAX_VFE) {
+ stream_info->state = STATS_INACTIVE;
+ for (i = 0; i < MSM_ISP_COMP_IRQ_MAX; i++)
+ stream_info->composite_irq[i] = 0;
+ }
if ((vfe_dev->stats_data.stream_handle_cnt << 8) == 0)
vfe_dev->stats_data.stream_handle_cnt++;
@@ -407,7 +449,8 @@ int msm_isp_stats_create_stream(struct vfe_device *vfe_dev,
stream_req_cmd->stream_handle =
(++vfe_dev->stats_data.stream_handle_cnt) << 8 | stats_idx;
- stream_info->stream_handle = stream_req_cmd->stream_handle;
+ stream_info->stream_handle[stream_info->num_isp - 1] =
+ stream_req_cmd->stream_handle;
return 0;
}
@@ -416,42 +459,39 @@ int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg)
int rc = -1;
struct msm_vfe_stats_stream_request_cmd *stream_req_cmd = arg;
struct msm_vfe_stats_stream *stream_info = NULL;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
- uint32_t framedrop_period;
uint32_t stats_idx;
+ unsigned long flags;
- rc = msm_isp_stats_create_stream(vfe_dev, stream_req_cmd);
- if (rc < 0) {
- pr_err("%s: create stream failed\n", __func__);
- return rc;
- }
-
- stats_idx = STATS_IDX(stream_req_cmd->stream_handle);
+ stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_stats_idx(stream_req_cmd->stats_type);
if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
pr_err("%s Invalid stats index %d", __func__, stats_idx);
return -EINVAL;
}
- stream_info = &stats_data->stream_info[stats_idx];
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, stats_idx);
- framedrop_period = msm_isp_get_framedrop_period(
- stream_req_cmd->framedrop_pattern);
+ spin_lock_irqsave(&stream_info->lock, flags);
- if (stream_req_cmd->framedrop_pattern == SKIP_ALL)
- stream_info->framedrop_pattern = 0x0;
- else
- stream_info->framedrop_pattern = 0x1;
- stream_info->framedrop_period = framedrop_period - 1;
+ 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;
+ }
if (stream_info->init_stats_frame_drop == 0)
vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(vfe_dev,
stream_info);
- msm_isp_stats_cfg_stream_scratch(vfe_dev, stream_info,
+ if (stream_info->state == STATS_INACTIVE) {
+ msm_isp_stats_cfg_stream_scratch(stream_info,
VFE_PING_FLAG);
- msm_isp_stats_cfg_stream_scratch(vfe_dev, stream_info,
+ msm_isp_stats_cfg_stream_scratch(stream_info,
VFE_PONG_FLAG);
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
return rc;
}
@@ -460,32 +500,112 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
int rc = -1;
struct msm_vfe_stats_stream_cfg_cmd stream_cfg_cmd;
struct msm_vfe_stats_stream_release_cmd *stream_release_cmd = arg;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
int stats_idx = STATS_IDX(stream_release_cmd->stream_handle);
struct msm_vfe_stats_stream *stream_info = NULL;
+ 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);
return -EINVAL;
}
- stream_info = &stats_data->stream_info[stats_idx];
- if (stream_info->state == STATS_AVALIABLE) {
+ 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 ?
+ stream_info->stream_handle[vfe_idx] : 0);
+ 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;
- } else if (stream_info->state != STATS_INACTIVE) {
+ }
+ vfe_dev->hw_info->vfe_ops.stats_ops.clear_wm_reg(vfe_dev, stream_info);
+
+ if (stream_info->state != STATS_INACTIVE) {
stream_cfg_cmd.enable = 0;
stream_cfg_cmd.num_streams = 1;
stream_cfg_cmd.stream_handle[0] =
stream_release_cmd->stream_handle;
- rc = msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd);
+ spin_lock_irqsave(&stream_info->lock, flags);
}
- vfe_dev->hw_info->vfe_ops.stats_ops.clear_wm_reg(vfe_dev, stream_info);
- memset(stream_info, 0, sizeof(struct msm_vfe_stats_stream));
+ for (i = vfe_idx, k = vfe_idx + 1; k < stream_info->num_isp; k++, i++) {
+ stream_info->vfe_dev[i] = stream_info->vfe_dev[k];
+ stream_info->stream_handle[i] = stream_info->stream_handle[k];
+ stream_info->buffer_offset[i] = stream_info->buffer_offset[k];
+ }
+
+ stream_info->vfe_dev[stream_info->num_isp] = 0;
+ stream_info->stream_handle[stream_info->num_isp] = 0;
+ stream_info->buffer_offset[stream_info->num_isp] = 0;
+ stream_info->num_isp--;
+ stream_info->vfe_mask &= ~(1 << vfe_dev->pdev->id);
+ if (stream_info->num_isp == 0)
+ stream_info->state = STATS_AVAILABLE;
+
+ spin_unlock_irqrestore(&stream_info->lock, flags);
return 0;
}
+void msm_isp_release_all_stats_stream(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_stats_stream_release_cmd
+ stream_release_cmd[MSM_ISP_STATS_MAX];
+ struct msm_vfe_stats_stream_cfg_cmd stream_cfg_cmd;
+ struct msm_vfe_stats_stream *stream_info;
+ int i;
+ int vfe_idx;
+ int num_stream = 0;
+ unsigned long flags;
+
+ stream_cfg_cmd.enable = 0;
+ stream_cfg_cmd.num_streams = 0;
+
+ for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, i);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state == STATS_AVAILABLE) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ vfe_idx = msm_isp_get_vfe_idx_for_stats_stream_user(vfe_dev,
+ stream_info);
+ if (vfe_idx == -ENOTTY) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ stream_release_cmd[num_stream++].stream_handle =
+ stream_info->stream_handle[vfe_idx];
+ if (stream_info->state == STATS_INACTIVE) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ continue;
+ }
+ stream_cfg_cmd.stream_handle[
+ stream_cfg_cmd.num_streams] =
+ stream_info->stream_handle[vfe_idx];
+ stream_cfg_cmd.num_streams++;
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ if (stream_cfg_cmd.num_streams)
+ msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd);
+
+ for (i = 0; i < num_stream; i++)
+ msm_isp_release_stats_stream(vfe_dev, &stream_release_cmd[i]);
+}
+
static int msm_isp_init_stats_ping_pong_reg(
struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info)
@@ -497,108 +617,205 @@ static int msm_isp_init_stats_ping_pong_reg(
stream_info->stream_id);
if (stream_info->bufq_handle == 0) {
pr_err("%s: no buf configured for stream: 0x%x\n",
- __func__, stream_info->stream_handle);
+ __func__, stream_info->stream_handle[0]);
return -EINVAL;
}
- if ((vfe_dev->is_split && vfe_dev->pdev->id == 1) ||
- !vfe_dev->is_split) {
- rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev,
- stream_info, VFE_PING_FLAG);
- if (rc < 0) {
- pr_err("%s: No free buffer for ping\n", __func__);
- return rc;
- }
- rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev,
- stream_info, VFE_PONG_FLAG);
- if (rc < 0) {
- pr_err("%s: No free buffer for pong\n", __func__);
- return rc;
- }
+ rc = msm_isp_stats_cfg_ping_pong_address(
+ stream_info, VFE_PING_FLAG);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for ping\n", __func__);
+ return rc;
+ }
+ rc = msm_isp_stats_cfg_ping_pong_address(
+ stream_info, VFE_PONG_FLAG);
+ if (rc < 0) {
+ pr_err("%s: No free buffer for pong\n", __func__);
+ return rc;
}
return rc;
}
-void msm_isp_update_stats_framedrop_reg(struct vfe_device *vfe_dev)
+void __msm_isp_update_stats_framedrop_reg(
+ struct msm_vfe_stats_stream *stream_info)
{
- int i;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
- struct msm_vfe_stats_stream *stream_info = NULL;
+ int k;
+ struct vfe_device *vfe_dev;
- for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
- stream_info = &stats_data->stream_info[i];
- if (stream_info->state != STATS_ACTIVE)
- continue;
+ if (!stream_info->init_stats_frame_drop)
+ return;
+ stream_info->init_stats_frame_drop--;
+ if (stream_info->init_stats_frame_drop)
+ return;
- if (stream_info->init_stats_frame_drop) {
- stream_info->init_stats_frame_drop--;
- if (stream_info->init_stats_frame_drop == 0) {
- vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(
- vfe_dev, stream_info);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(vfe_dev,
+ stream_info);
+
+ }
+}
+
+static void __msm_isp_stats_stream_update(
+ struct msm_vfe_stats_stream *stream_info)
+{
+ uint32_t enable = 0;
+ uint8_t comp_flag = 0;
+ int k;
+ struct vfe_device *vfe_dev;
+ int index = STATS_IDX(stream_info->stream_handle[0]);
+
+ switch (stream_info->state) {
+ case STATS_INACTIVE:
+ case STATS_ACTIVE:
+ case STATS_AVAILABLE:
+ break;
+ case STATS_START_PENDING:
+ enable = 1;
+ case STATS_STOP_PENDING:
+ stream_info->state =
+ (stream_info->state == STATS_START_PENDING ?
+ STATS_STARTING : STATS_STOPPING);
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
+ vfe_dev, BIT(index), enable);
+ comp_flag = stream_info->composite_flag;
+ if (comp_flag) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_comp_mask(vfe_dev, BIT(index),
+ (comp_flag - 1), enable);
+ } else {
+ if (enable)
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_wm_irq_mask(vfe_dev,
+ stream_info);
+ else
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ clear_wm_irq_mask(vfe_dev,
+ stream_info);
}
}
+ break;
+ case STATS_STARTING:
+ stream_info->state = STATS_ACTIVE;
+ complete_all(&stream_info->active_comp);
+ break;
+ case STATS_STOPPING:
+ stream_info->state = STATS_INACTIVE;
+ complete_all(&stream_info->inactive_comp);
+ break;
}
}
+
void msm_isp_stats_stream_update(struct vfe_device *vfe_dev)
{
int i;
- uint32_t enable = 0;
- uint8_t comp_flag = 0;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
- struct msm_vfe_stats_ops *stats_ops =
- &vfe_dev->hw_info->vfe_ops.stats_ops;
+ struct msm_vfe_stats_stream *stream_info;
+ unsigned long flags;
for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
- if (stats_data->stream_info[i].state == STATS_START_PENDING ||
- stats_data->stream_info[i].state ==
- STATS_STOP_PENDING) {
- enable = stats_data->stream_info[i].state ==
- STATS_START_PENDING ? 1 : 0;
- stats_data->stream_info[i].state =
- stats_data->stream_info[i].state ==
- STATS_START_PENDING ?
- STATS_STARTING : STATS_STOPPING;
- vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
- vfe_dev, BIT(i), enable);
- comp_flag = stats_data->stream_info[i].composite_flag;
- if (comp_flag)
- stats_ops->cfg_comp_mask(vfe_dev, BIT(i),
- (comp_flag - 1), enable);
- } else if (stats_data->stream_info[i].state == STATS_STARTING ||
- stats_data->stream_info[i].state == STATS_STOPPING) {
- stats_data->stream_info[i].state =
- stats_data->stream_info[i].state ==
- STATS_STARTING ? STATS_ACTIVE : STATS_INACTIVE;
- }
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, i);
+ if (stream_info->state == STATS_AVAILABLE ||
+ stream_info->state == STATS_INACTIVE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ __msm_isp_stats_stream_update(stream_info);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
}
- atomic_sub(1, &stats_data->stats_update);
- if (!atomic_read(&stats_data->stats_update))
- complete(&vfe_dev->stats_config_complete);
}
-static int msm_isp_stats_wait_for_cfg_done(struct vfe_device *vfe_dev)
+void msm_isp_process_stats_reg_upd_epoch_irq(struct vfe_device *vfe_dev,
+ enum msm_isp_comp_irq_types irq)
{
+ int i;
+ struct msm_vfe_stats_stream *stream_info;
+ unsigned long flags;
int rc;
- init_completion(&vfe_dev->stats_config_complete);
- atomic_set(&vfe_dev->stats_data.stats_update, 2);
- rc = wait_for_completion_timeout(
- &vfe_dev->stats_config_complete,
+
+ for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) {
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev, i);
+ if (stream_info->state == STATS_AVAILABLE ||
+ stream_info->state == STATS_INACTIVE)
+ continue;
+
+ spin_lock_irqsave(&stream_info->lock, flags);
+
+ rc = msm_isp_composite_stats_irq(vfe_dev, stream_info, irq);
+
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ if (-EFAULT == rc) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return;
+ }
+ continue;
+ }
+
+ if (irq == MSM_ISP_COMP_IRQ_REG_UPD)
+ __msm_isp_stats_stream_update(stream_info);
+ else if (irq == MSM_ISP_COMP_IRQ_EPOCH &&
+ stream_info->state == STATS_ACTIVE)
+ __msm_isp_update_stats_framedrop_reg(stream_info);
+
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+}
+
+static int msm_isp_stats_wait_for_stream_cfg_done(
+ struct msm_vfe_stats_stream *stream_info,
+ int active)
+{
+ int rc = -1;
+
+ if (active && stream_info->state == STATS_ACTIVE)
+ rc = 0;
+ if (!active && stream_info->state == STATS_INACTIVE)
+ rc = 0;
+ if (rc == 0)
+ return rc;
+
+ rc = wait_for_completion_timeout(active ? &stream_info->active_comp :
+ &stream_info->inactive_comp,
msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
- if (rc == 0) {
- pr_err("%s: wait timeout\n", __func__);
- rc = -1;
+ if (rc <= 0) {
+ rc = rc ? rc : -ETIMEDOUT;
+ pr_err("%s: wait for stats stream %x idx %d state %d active %d config failed %d\n",
+ __func__, stream_info->stream_id,
+ STATS_IDX(stream_info->stream_handle[0]),
+ stream_info->state, active, rc);
} else {
rc = 0;
}
return rc;
}
+static int msm_isp_stats_wait_for_streams(
+ struct msm_vfe_stats_stream **streams,
+ int num_stream, int active)
+{
+ int rc = 0;
+ int i;
+ struct msm_vfe_stats_stream *stream_info;
+
+ for (i = 0; i < num_stream; i++) {
+ stream_info = streams[i];
+ rc |= msm_isp_stats_wait_for_stream_cfg_done(stream_info,
+ active);
+ }
+ return rc;
+}
+
static int msm_isp_stats_update_cgc_override(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
{
int i;
uint32_t stats_mask = 0, idx;
+ struct vfe_device *update_vfes[MAX_VFE] = {NULL, NULL};
+ struct msm_vfe_stats_stream *stream_info;
+ int k;
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
@@ -607,12 +824,33 @@ static int msm_isp_stats_update_cgc_override(struct vfe_device *vfe_dev,
pr_err("%s Invalid stats index %d", __func__, idx);
return -EINVAL;
}
- stats_mask |= 1 << idx;
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev,
+ idx);
+ if (stream_info->state == STATS_AVAILABLE)
+ continue;
+
+ /*
+ * we update cgc after making streams inactive or before
+ * starting streams, so stream should be in inactive state
+ */
+ if (stream_info->state == STATS_INACTIVE)
+ stats_mask |= 1 << idx;
+ for (k = 0; k < stream_info->num_isp; k++) {
+ if (update_vfes[stream_info->vfe_dev[k]->pdev->id])
+ continue;
+ update_vfes[stream_info->vfe_dev[k]->pdev->id] =
+ stream_info->vfe_dev[k];
+ }
}
- if (vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override) {
- vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override(
- vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ for (k = 0; k < MAX_VFE; k++) {
+ if (!update_vfes[k])
+ continue;
+ vfe_dev = update_vfes[k];
+ if (vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override) {
+ vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override(
+ vfe_dev, stats_mask, stream_cfg_cmd->enable);
+ }
}
return 0;
}
@@ -621,61 +859,108 @@ int msm_isp_stats_reset(struct vfe_device *vfe_dev)
{
int i = 0, rc = 0;
struct msm_vfe_stats_stream *stream_info = NULL;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
struct msm_isp_timestamp timestamp;
+ struct vfe_device *update_vfes[MAX_VFE] = {NULL, NULL};
+ unsigned long flags;
+ int k;
msm_isp_get_timestamp(&timestamp);
- for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
- stream_info = &stats_data->stream_info[i];
- if (stream_info->state != STATS_ACTIVE)
+ if (vfe_dev->is_split) {
+ for (i = 0; i < MAX_VFE; i++)
+ update_vfes[i] = vfe_dev->common_data->dual_vfe_res->
+ vfe_dev[i];
+ } else {
+ update_vfes[vfe_dev->pdev->id] = vfe_dev;
+ }
+
+ for (k = 0; k < MAX_VFE; k++) {
+ vfe_dev = update_vfes[k];
+ if (!vfe_dev)
continue;
- rc = vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
- vfe_dev->pdev->id, stream_info->bufq_handle,
- MSM_ISP_BUFFER_FLUSH_ALL, &timestamp.buf_time,
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
- if (rc == -EFAULT) {
- msm_isp_halt_send_error(vfe_dev,
- ISP_EVENT_BUF_FATAL_ERROR);
- return rc;
+ for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+ stream_info = msm_isp_get_stats_stream_common_data(
+ vfe_dev, i);
+ if (stream_info->state == STATS_AVAILABLE ||
+ stream_info->state == STATS_INACTIVE)
+ continue;
+
+ if (stream_info->num_isp > 1 &&
+ vfe_dev->pdev->id == ISP_VFE0)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ msm_isp_stats_cfg_stream_scratch(stream_info,
+ VFE_PING_FLAG);
+ msm_isp_stats_cfg_stream_scratch(stream_info,
+ VFE_PONG_FLAG);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ rc = vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
+ stream_info->bufq_handle,
+ MSM_ISP_BUFFER_FLUSH_ALL, &timestamp.buf_time,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ if (rc == -EFAULT) {
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ return rc;
+ }
}
}
return rc;
}
-int msm_isp_stats_restart(struct vfe_device *vfe_dev)
+int msm_isp_stats_restart(struct vfe_device *vfe_dev_ioctl)
{
int i = 0;
struct msm_vfe_stats_stream *stream_info = NULL;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ unsigned long flags;
+ struct vfe_device *update_vfes[MAX_VFE] = {NULL, NULL};
+ struct vfe_device *vfe_dev;
+ int k;
+ int j;
+
+ if (vfe_dev_ioctl->is_split) {
+ for (i = 0; i < MAX_VFE; i++)
+ update_vfes[i] = vfe_dev_ioctl->common_data->
+ dual_vfe_res->vfe_dev[i];
+ } else {
+ update_vfes[vfe_dev_ioctl->pdev->id] = vfe_dev_ioctl;
+ }
- for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
- stream_info = &stats_data->stream_info[i];
- if (stream_info->state < STATS_ACTIVE)
+ for (k = 0; k < MAX_VFE; k++) {
+ vfe_dev = update_vfes[k];
+ if (!vfe_dev)
+ continue;
+ for (i = 0; i < MSM_ISP_STATS_MAX; i++) {
+ stream_info = msm_isp_get_stats_stream_common_data(
+ vfe_dev, i);
+ if (stream_info->state == STATS_AVAILABLE ||
+ stream_info->state == STATS_INACTIVE)
+ continue;
+ if (stream_info->num_isp > 1 &&
+ vfe_dev->pdev->id == ISP_VFE0)
continue;
- msm_isp_init_stats_ping_pong_reg(vfe_dev, stream_info);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ for (j = 0; j < MSM_ISP_COMP_IRQ_MAX; j++)
+ stream_info->composite_irq[j] = 0;
+ msm_isp_init_stats_ping_pong_reg(vfe_dev_ioctl,
+ stream_info);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
}
return 0;
}
-static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev,
+static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
{
- int i, rc = 0;
- uint32_t stats_mask = 0, idx;
- uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0};
- uint32_t num_stats_comp_mask = 0;
+ int i;
struct msm_vfe_stats_stream *stream_info;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
- num_stats_comp_mask =
- vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
- rc = vfe_dev->hw_info->vfe_ops.stats_ops.check_streams(
- stats_data->stream_info);
- if (rc < 0)
- return rc;
+ uint32_t idx;
+ int vfe_idx;
+
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
@@ -683,63 +968,99 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev,
pr_err("%s Invalid stats index %d", __func__, idx);
return -EINVAL;
}
-
- stream_info = &stats_data->stream_info[idx];
- if (stream_info->stream_handle !=
- stream_cfg_cmd->stream_handle[i]) {
- pr_err("%s: Invalid stream handle: 0x%x received\n",
- __func__, stream_cfg_cmd->stream_handle[i]);
- continue;
+ stream_info = msm_isp_get_stats_stream_common_data(
+ vfe_dev, idx);
+ 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_cfg_cmd->stream_handle[i]) {
+ pr_err("%s: Invalid stream handle: 0x%x received expected %x\n",
+ __func__, stream_cfg_cmd->stream_handle[i],
+ vfe_idx == -ENOTTY ? 0 :
+ stream_info->stream_handle[vfe_idx]);
+ return -EINVAL;
}
+ }
+ return 0;
+}
- if (stream_info->composite_flag > num_stats_comp_mask) {
- pr_err("%s: comp grp %d exceed max %d\n",
- __func__, stream_info->composite_flag,
- num_stats_comp_mask);
- return -EINVAL;
+static void __msm_isp_stop_stats_streams(
+ struct msm_vfe_stats_stream **streams,
+ int num_streams,
+ struct msm_isp_timestamp timestamp)
+{
+ int i;
+ int k;
+ struct msm_vfe_stats_stream *stream_info;
+ struct vfe_device *vfe_dev;
+ struct msm_vfe_stats_shared_data *stats_data;
+ unsigned long flags;
+
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ spin_lock_irqsave(&stream_info->lock, flags);
+ init_completion(&stream_info->inactive_comp);
+ stream_info->state = STATS_STOP_PENDING;
+ if (stream_info->vfe_dev[0]->
+ axi_data.src_info[VFE_PIX_0].active == 0) {
+ while (stream_info->state != STATS_INACTIVE)
+ __msm_isp_stats_stream_update(stream_info);
}
- rc = msm_isp_init_stats_ping_pong_reg(vfe_dev, stream_info);
- if (rc < 0) {
- pr_err("%s: No buffer for stream%d\n", __func__, idx);
- return rc;
+ for (k = 0; k < stream_info->num_isp; k++) {
+ stats_data = &stream_info->vfe_dev[k]->stats_data;
+ stats_data->num_active_stream--;
}
- if (!stream_info->composite_flag)
- vfe_dev->hw_info->vfe_ops.stats_ops.
- cfg_wm_irq_mask(vfe_dev, stream_info);
-
- if (vfe_dev->axi_data.src_info[VFE_PIX_0].active)
- stream_info->state = STATS_START_PENDING;
- else
- stream_info->state = STATS_ACTIVE;
-
- stats_data->num_active_stream++;
- stats_mask |= 1 << idx;
-
- if (stream_info->composite_flag > 0)
- comp_stats_mask[stream_info->composite_flag-1] |=
- 1 << idx;
-
- ISP_DBG("%s: stats_mask %x %x active streams %d\n",
- __func__, comp_stats_mask[0],
- comp_stats_mask[1],
- stats_data->num_active_stream);
+ msm_isp_stats_cfg_stream_scratch(
+ stream_info, VFE_PING_FLAG);
+ msm_isp_stats_cfg_stream_scratch(
+ stream_info, VFE_PONG_FLAG);
+ vfe_dev = stream_info->vfe_dev[0];
+ if (vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
+ stream_info->bufq_handle,
+ MSM_ISP_BUFFER_FLUSH_ALL, &timestamp.buf_time,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id ==
+ -EFAULT))
+ msm_isp_halt_send_error(vfe_dev,
+ ISP_EVENT_BUF_FATAL_ERROR);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
}
- if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
- rc = msm_isp_stats_wait_for_cfg_done(vfe_dev);
- } else {
- vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
- vfe_dev, stats_mask, stream_cfg_cmd->enable);
- for (i = 0; i < num_stats_comp_mask; i++) {
- vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
- vfe_dev, comp_stats_mask[i], i, 1);
+ if (msm_isp_stats_wait_for_streams(streams, num_streams, 0)) {
+ for (i = 0; i < num_streams; i++) {
+ stream_info = streams[i];
+ if (stream_info->state == STATS_INACTIVE)
+ continue;
+ spin_lock_irqsave(&stream_info->lock, flags);
+ while (stream_info->state != STATS_INACTIVE)
+ __msm_isp_stats_stream_update(stream_info);
+ spin_unlock_irqrestore(&stream_info->lock, flags);
}
}
- return rc;
}
-static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev,
+static int msm_isp_check_stats_stream_state(
+ struct msm_vfe_stats_stream *stream_info,
+ int cmd)
+{
+ switch (stream_info->state) {
+ case STATS_AVAILABLE:
+ return -EINVAL;
+ case STATS_INACTIVE:
+ if (cmd == 0)
+ return -EALREADY;
+ break;
+ case STATS_ACTIVE:
+ if (cmd)
+ return -EALREADY;
+ break;
+ default:
+ WARN(1, "Invalid stats state %d\n", stream_info->state);
+ }
+ return 0;
+}
+
+static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl,
struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
{
int i, rc = 0;
@@ -747,95 +1068,125 @@ static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev,
uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0};
uint32_t num_stats_comp_mask = 0;
struct msm_vfe_stats_stream *stream_info;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ struct msm_vfe_stats_shared_data *stats_data;
+ int num_stream = 0;
+ struct msm_vfe_stats_stream *streams[MSM_ISP_STATS_MAX];
struct msm_isp_timestamp timestamp;
+ unsigned long flags;
+ int k;
+ struct vfe_device *update_vfes[MAX_VFE] = {NULL, NULL};
+ uint32_t num_active_streams[MAX_VFE] = {0, 0};
+ struct vfe_device *vfe_dev;
msm_isp_get_timestamp(&timestamp);
num_stats_comp_mask =
- vfe_dev->hw_info->stats_hw_info->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++) {
-
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
-
- if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
- pr_err("%s Invalid stats index %d", __func__, idx);
- return -EINVAL;
- }
-
- stream_info = &stats_data->stream_info[idx];
- if (stream_info->stream_handle !=
- stream_cfg_cmd->stream_handle[i]) {
- pr_err("%s: Invalid stream handle: 0x%x received\n",
- __func__, stream_cfg_cmd->stream_handle[i]);
+ stream_info = msm_isp_get_stats_stream_common_data(
+ vfe_dev_ioctl, idx);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ rc = msm_isp_check_stats_stream_state(stream_info, 1);
+ if (rc == -EALREADY) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ rc = 0;
continue;
}
-
- if (stream_info->composite_flag > num_stats_comp_mask) {
- pr_err("%s: comp grp %d exceed max %d\n",
- __func__, stream_info->composite_flag,
- num_stats_comp_mask);
- return -EINVAL;
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ goto error;
}
+ rc = msm_isp_init_stats_ping_pong_reg(vfe_dev_ioctl,
+ stream_info);
+ if (rc < 0) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ pr_err("%s: No buffer for stream%d\n", __func__, idx);
+ return rc;
+ }
+ init_completion(&stream_info->active_comp);
+ stream_info->state = STATS_START_PENDING;
+ if (vfe_dev_ioctl->axi_data.src_info[VFE_PIX_0].active == 0) {
+ while (stream_info->state != STATS_ACTIVE)
+ __msm_isp_stats_stream_update(stream_info);
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
- if (!stream_info->composite_flag)
- vfe_dev->hw_info->vfe_ops.stats_ops.
- clear_wm_irq_mask(vfe_dev, stream_info);
-
- if (vfe_dev->axi_data.src_info[VFE_PIX_0].active)
- stream_info->state = STATS_STOP_PENDING;
- else
- stream_info->state = STATS_INACTIVE;
-
- stats_data->num_active_stream--;
stats_mask |= 1 << idx;
+ for (k = 0; k < stream_info->num_isp; k++) {
+ vfe_dev = stream_info->vfe_dev[k];
+ if (update_vfes[vfe_dev->pdev->id])
+ continue;
+ update_vfes[vfe_dev->pdev->id] = vfe_dev;
+ stats_data = &vfe_dev->stats_data;
+ num_active_streams[vfe_dev->pdev->id] =
+ stats_data->num_active_stream;
+ stats_data->num_active_stream++;
+ }
- if (stream_info->composite_flag > 0)
+ if (stream_info->composite_flag)
comp_stats_mask[stream_info->composite_flag-1] |=
1 << idx;
- msm_isp_stats_cfg_stream_scratch(vfe_dev, stream_info,
- VFE_PING_FLAG);
- msm_isp_stats_cfg_stream_scratch(vfe_dev, stream_info,
- VFE_PONG_FLAG);
-
ISP_DBG("%s: stats_mask %x %x active streams %d\n",
__func__, comp_stats_mask[0],
comp_stats_mask[1],
stats_data->num_active_stream);
+ streams[num_stream++] = stream_info;
}
- if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) {
- rc = msm_isp_stats_wait_for_cfg_done(vfe_dev);
- } else {
- vfe_dev->hw_info->vfe_ops.stats_ops.enable_module(
- vfe_dev, stats_mask, stream_cfg_cmd->enable);
- for (i = 0; i < num_stats_comp_mask; i++) {
- vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(
- vfe_dev, comp_stats_mask[i], i, 0);
- }
+ for (k = 0; k < MAX_VFE; k++) {
+ if (!update_vfes[k] || num_active_streams[k])
+ continue;
+ vfe_dev = update_vfes[k];
+ vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev);
}
+ rc = msm_isp_stats_wait_for_streams(streams, num_stream, 1);
+ if (rc)
+ goto error;
+ return 0;
+error:
+ __msm_isp_stop_stats_streams(streams, num_stream, timestamp);
+ return rc;
+}
+
+static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i, rc = 0;
+ uint32_t idx;
+ uint32_t num_stats_comp_mask = 0;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_isp_timestamp timestamp;
+ int num_stream = 0;
+ struct msm_vfe_stats_stream *streams[MSM_ISP_STATS_MAX];
+ unsigned long flags;
+
+ msm_isp_get_timestamp(&timestamp);
+
+ num_stats_comp_mask =
+ vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask;
+
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
- idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
- if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) {
- pr_err("%s Invalid stats index %d", __func__, idx);
- return -EINVAL;
- }
+ idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
- stream_info = &stats_data->stream_info[idx];
- rc = vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr,
- vfe_dev->pdev->id, stream_info->bufq_handle,
- MSM_ISP_BUFFER_FLUSH_ALL, &timestamp.buf_time,
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
- if (rc == -EFAULT) {
- msm_isp_halt_send_error(vfe_dev,
- ISP_EVENT_BUF_FATAL_ERROR);
- return rc;
+ stream_info = msm_isp_get_stats_stream_common_data(
+ vfe_dev, idx);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ rc = msm_isp_check_stats_stream_state(stream_info, 0);
+ if (rc) {
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ rc = 0;
+ continue;
}
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ streams[num_stream++] = stream_info;
}
+
+ __msm_isp_stop_stats_streams(streams, num_stream, timestamp);
+
return rc;
}
@@ -843,8 +1194,10 @@ int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0;
struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd = arg;
- if (vfe_dev->stats_data.num_active_stream == 0)
- vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev);
+
+ rc = msm_isp_check_stream_cfg_cmd(vfe_dev, stream_cfg_cmd);
+ if (rc)
+ return rc;
if (stream_cfg_cmd->enable) {
msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd);
@@ -863,31 +1216,37 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0, i;
struct msm_vfe_stats_stream *stream_info;
- struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
struct msm_vfe_axi_stream_update_cmd *update_cmd = arg;
struct msm_vfe_axi_stream_cfg_update_info *update_info = NULL;
struct msm_isp_sw_framskip *sw_skip_info = NULL;
+ int vfe_idx;
+ int k;
/*validate request*/
for (i = 0; i < update_cmd->num_streams; i++) {
- update_info = &update_cmd->update_info[i];
+ update_info = (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
/*check array reference bounds*/
if (STATS_IDX(update_info->stream_handle)
> vfe_dev->hw_info->stats_hw_info->num_stats_type) {
pr_err("%s: stats idx %d out of bound!", __func__,
- STATS_IDX(update_info->stream_handle));
+ STATS_IDX(update_info->stream_handle));
return -EINVAL;
}
}
for (i = 0; i < update_cmd->num_streams; i++) {
- update_info = &update_cmd->update_info[i];
- stream_info = &stats_data->stream_info[
- STATS_IDX(update_info->stream_handle)];
- if (stream_info->stream_handle !=
+ update_info = (struct msm_vfe_axi_stream_cfg_update_info *)
+ &update_cmd->update_info[i];
+ stream_info = msm_isp_get_stats_stream_common_data(vfe_dev,
+ STATS_IDX(update_info->stream_handle));
+ 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] !=
update_info->stream_handle) {
pr_err("%s: stats stream handle %x %x mismatch!\n",
- __func__, stream_info->stream_handle,
+ __func__, vfe_idx != -ENOTTY ?
+ stream_info->stream_handle[vfe_idx] : 0,
update_info->stream_handle);
continue;
}
@@ -897,18 +1256,22 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
uint32_t framedrop_period =
msm_isp_get_framedrop_period(
update_info->skip_pattern);
- if (update_info->skip_pattern == SKIP_ALL)
+ if (update_info->skip_pattern ==
+ SKIP_ALL)
stream_info->framedrop_pattern = 0x0;
else
stream_info->framedrop_pattern = 0x1;
stream_info->framedrop_period = framedrop_period - 1;
if (stream_info->init_stats_frame_drop == 0)
- vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(
- vfe_dev, stream_info);
+ for (k = 0; k < stream_info->num_isp; k++)
+ stream_info->vfe_dev[k]->hw_info->
+ vfe_ops.stats_ops.cfg_wm_reg(
+ vfe_dev, stream_info);
break;
}
case UPDATE_STREAM_SW_FRAME_DROP: {
- sw_skip_info = &update_info->sw_skip_info;
+ sw_skip_info =
+ &update_info->sw_skip_info;
if (!stream_info->sw_skip.stream_src_mask)
stream_info->sw_skip = *sw_skip_info;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h
index 01120b65be92..e9728f33fae1 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.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
@@ -23,8 +23,58 @@ int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg);
int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg);
-void msm_isp_update_stats_framedrop_reg(struct vfe_device *vfe_dev);
void msm_isp_stats_disable(struct vfe_device *vfe_dev);
int msm_isp_stats_reset(struct vfe_device *vfe_dev);
int msm_isp_stats_restart(struct vfe_device *vfe_dev);
+void msm_isp_release_all_stats_stream(struct vfe_device *vfe_dev);
+void msm_isp_process_stats_reg_upd_epoch_irq(struct vfe_device *vfe_dev,
+ enum msm_isp_comp_irq_types irq);
+
+static inline int msm_isp_get_vfe_idx_for_stats_stream_user(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx;
+
+ for (vfe_idx = 0; vfe_idx < stream_info->num_isp; vfe_idx++)
+ if (stream_info->vfe_dev[vfe_idx] == vfe_dev)
+ return vfe_idx;
+ return -ENOTTY;
+}
+
+static inline int msm_isp_get_vfe_idx_for_stats_stream(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream_user(vfe_dev,
+ stream_info);
+
+ if (vfe_idx < 0) {
+ WARN(1, "%s vfe index missing for stream %d vfe %d\n",
+ __func__, stream_info->stats_type, vfe_dev->pdev->id);
+ vfe_idx = 0;
+ }
+ return vfe_idx;
+}
+
+static inline struct msm_vfe_stats_stream *
+ msm_isp_get_stats_stream_common_data(
+ struct vfe_device *vfe_dev,
+ enum msm_isp_stats_type idx)
+{
+ if (vfe_dev->is_split)
+ return &vfe_dev->common_data->stats_streams[idx];
+ else
+ return &vfe_dev->common_data->stats_streams[idx +
+ MSM_ISP_STATS_MAX * vfe_dev->pdev->id];
+}
+
+static inline struct msm_vfe_stats_stream *
+ msm_isp_get_stats_stream(struct dual_vfe_resource *dual_vfe_res,
+ int vfe_id,
+ enum msm_isp_stats_type idx)
+{
+ return msm_isp_get_stats_stream_common_data(
+ dual_vfe_res->vfe_dev[vfe_id], idx);
+}
#endif /* __MSM_ISP_STATS_UTIL_H__ */
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 e47a8de30aa9..a4eb80f31984 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
@@ -25,6 +25,22 @@
static DEFINE_MUTEX(bandwidth_mgr_mutex);
static struct msm_isp_bandwidth_mgr isp_bandwidth_mgr;
+#define MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev) { \
+ if (vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE0) { \
+ struct vfe_device *vfe1_dev = vfe_dev->common_data-> \
+ dual_vfe_res->vfe_dev[ISP_VFE1]; \
+ mutex_lock(&vfe1_dev->core_mutex); \
+ } \
+}
+
+#define MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev) { \
+ if (vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE0) { \
+ struct vfe_device *vfe1_dev = vfe_dev->common_data-> \
+ dual_vfe_res->vfe_dev[ISP_VFE1]; \
+ mutex_unlock(&vfe1_dev->core_mutex); \
+ } \
+}
+
static uint64_t msm_isp_cpp_clk_rate;
#define VFE40_8974V2_VERSION 0x1001001A
@@ -354,9 +370,6 @@ void msm_isp_fetch_engine_done_notify(struct vfe_device *vfe_dev,
struct msm_vfe_fetch_engine_info *fetch_engine_info)
{
struct msm_isp_event_data fe_rd_done_event;
- if (!fetch_engine_info->is_busy)
- return;
-
memset(&fe_rd_done_event, 0, sizeof(struct msm_isp_event_data));
fe_rd_done_event.frame_id =
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
@@ -762,26 +775,39 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
}
case VIDIOC_MSM_ISP_REQUEST_STREAM:
mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
rc = msm_isp_request_axi_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_RELEASE_STREAM:
mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
rc = msm_isp_release_axi_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_CFG_STREAM:
mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
rc = msm_isp_cfg_axi_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_AXI_HALT:
mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
rc = msm_isp_axi_halt(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_AXI_RESET:
mutex_lock(&vfe_dev->core_mutex);
+ /* For dual vfe reset both on vfe1 call */
+ if (vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE0) {
+ mutex_unlock(&vfe_dev->core_mutex);
+ return 0;
+ }
if (atomic_read(&vfe_dev->error_info.overflow_state)
!= HALT_ENFORCED) {
rc = msm_isp_stats_reset(vfe_dev);
@@ -796,6 +822,11 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
break;
case VIDIOC_MSM_ISP_AXI_RESTART:
mutex_lock(&vfe_dev->core_mutex);
+ /* For dual vfe restart both on vfe1 call */
+ if (vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE0) {
+ mutex_unlock(&vfe_dev->core_mutex);
+ return 0;
+ }
if (atomic_read(&vfe_dev->error_info.overflow_state)
!= HALT_ENFORCED) {
rc = msm_isp_stats_restart(vfe_dev);
@@ -848,27 +879,37 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
break;
case VIDIOC_MSM_ISP_REQUEST_STATS_STREAM:
mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
rc = msm_isp_request_stats_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_RELEASE_STATS_STREAM:
mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
rc = msm_isp_release_stats_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_CFG_STATS_STREAM:
mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
rc = msm_isp_cfg_stats_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_UPDATE_STATS_STREAM:
mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
rc = msm_isp_update_stats_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_UPDATE_STREAM:
mutex_lock(&vfe_dev->core_mutex);
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
rc = msm_isp_update_axi_stream(vfe_dev, arg);
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
mutex_unlock(&vfe_dev->core_mutex);
break;
case VIDIOC_MSM_ISP_SMMU_ATTACH:
@@ -883,10 +924,7 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
vfe_dev->isp_raw2_debug = 0;
break;
case MSM_SD_UNNOTIFY_FREEZE:
- break;
case MSM_SD_SHUTDOWN:
- while (vfe_dev->vfe_open_cnt != 0)
- msm_isp_close_node(sd, NULL);
break;
default:
@@ -1631,8 +1669,8 @@ static int msm_isp_process_iommu_page_fault(struct vfe_device *vfe_dev)
{
int rc = vfe_dev->buf_mgr->pagefault_debug_disable;
- pr_err("%s:%d] VFE%d Handle Page fault! vfe_dev %pK\n", __func__,
- __LINE__, vfe_dev->pdev->id, vfe_dev);
+ pr_err("%s:%d] VFE%d Handle Page fault!\n", __func__,
+ __LINE__, vfe_dev->pdev->id);
msm_isp_halt_send_error(vfe_dev, ISP_EVENT_IOMMU_P_FAULT);
@@ -1899,6 +1937,7 @@ static void msm_vfe_iommu_fault_handler(struct iommu_domain *domain,
if (vfe_dev->vfe_open_cnt > 0) {
atomic_set(&vfe_dev->error_info.overflow_state,
HALT_ENFORCED);
+ pr_err("%s: fault address is %lx\n", __func__, iova);
msm_isp_process_iommu_page_fault(vfe_dev);
} else {
pr_err("%s: no handling, vfe open cnt = %d\n",
@@ -1928,9 +1967,6 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
return -EINVAL;
}
- if (vfe_dev->pdev->id == ISP_VFE0)
- vfe_dev->common_data->dual_vfe_res->epoch_sync_mask = 0;
-
mutex_lock(&vfe_dev->realtime_mutex);
mutex_lock(&vfe_dev->core_mutex);
@@ -2032,6 +2068,10 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
mutex_unlock(&vfe_dev->realtime_mutex);
return 0;
}
+ MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev);
+ msm_isp_release_all_axi_stream(vfe_dev);
+ msm_isp_release_all_stats_stream(vfe_dev);
+
/* Unregister page fault handler */
cam_smmu_reg_client_page_fault_handler(
vfe_dev->buf_mgr->iommu_hdl,
@@ -2059,6 +2099,7 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
msm_isp_end_avtimer();
vfe_dev->vt_enable = 0;
}
+ MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev);
vfe_dev->is_split = 0;
mutex_unlock(&vfe_dev->core_mutex);
@@ -2088,25 +2129,3 @@ void msm_isp_flush_tasklet(struct vfe_device *vfe_dev)
return;
}
-void msm_isp_save_framedrop_values(struct vfe_device *vfe_dev,
- enum msm_vfe_input_src frame_src)
-{
- struct msm_vfe_axi_stream *stream_info = NULL;
- uint32_t j = 0;
- unsigned long flags;
-
- for (j = 0; j < VFE_AXI_SRC_MAX; j++) {
- stream_info = &vfe_dev->axi_data.stream_info[j];
- if (stream_info->state != ACTIVE)
- continue;
- if (frame_src != SRC_TO_INTF(stream_info->stream_src))
- continue;
-
- stream_info =
- &vfe_dev->axi_data.stream_info[j];
- spin_lock_irqsave(&stream_info->lock, flags);
- stream_info->activated_framedrop_period =
- stream_info->requested_framedrop_period;
- spin_unlock_irqrestore(&stream_info->lock, flags);
- }
-}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h
index 9df60c0d7383..16e3198f35b7 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.h
@@ -70,7 +70,5 @@ void msm_isp_fetch_engine_done_notify(struct vfe_device *vfe_dev,
struct msm_vfe_fetch_engine_info *fetch_engine_info);
void msm_isp_print_fourcc_error(const char *origin, uint32_t fourcc_format);
void msm_isp_flush_tasklet(struct vfe_device *vfe_dev);
-void msm_isp_save_framedrop_values(struct vfe_device *vfe_dev,
- enum msm_vfe_input_src frame_src);
void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp);
#endif /* __MSM_ISP_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
index 2af70b2ee1bf..3b38882c4c45 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
@@ -1390,7 +1390,7 @@ int msm_jpegdma_hw_get_max_downscale(struct msm_jpegdma_device *dma)
*/
int msm_jpegdma_hw_get_qos(struct msm_jpegdma_device *dma)
{
- int i;
+ int i, j;
int ret;
unsigned int cnt;
const void *property;
@@ -1401,32 +1401,37 @@ int msm_jpegdma_hw_get_qos(struct msm_jpegdma_device *dma)
dev_dbg(dma->dev, "Missing qos settings\n");
return 0;
}
+
cnt /= 4;
+ if (cnt % 2)
+ return -EINVAL;
+
+ dma->qos_regs_num = cnt / 2;
- dma->qos_regs = kzalloc((sizeof(*dma->qos_regs) * cnt), GFP_KERNEL);
+ dma->qos_regs = kzalloc((sizeof(struct jpegdma_reg_cfg) *
+ dma->qos_regs_num), GFP_KERNEL);
if (!dma->qos_regs)
return -ENOMEM;
- for (i = 0; i < cnt; i = i + 2) {
+ for (i = 0, j = 0; i < cnt; i += 2, j++) {
ret = of_property_read_u32_index(dma->dev->of_node,
"qcom,qos-reg-settings", i,
- &dma->qos_regs[i].reg);
+ &dma->qos_regs[j].reg);
if (ret < 0) {
- dev_err(dma->dev, "can not read qos reg %d\n", i);
+ dev_err(dma->dev, "can not read qos reg %d\n", j);
goto error;
}
ret = of_property_read_u32_index(dma->dev->of_node,
"qcom,qos-reg-settings", i + 1,
- &dma->qos_regs[i].val);
+ &dma->qos_regs[j].val);
if (ret < 0) {
- dev_err(dma->dev, "can not read qos setting %d\n", i);
+ dev_err(dma->dev, "can not read qos setting %d\n", j);
goto error;
}
- dev_dbg(dma->dev, "Qos idx %d, reg %x val %x\n", i,
- dma->qos_regs[i].reg, dma->qos_regs[i].val);
+ dev_dbg(dma->dev, "Qos idx %d, reg %x val %x\n", j,
+ dma->qos_regs[j].reg, dma->qos_regs[j].val);
}
- dma->qos_regs_num = cnt;
return 0;
error:
@@ -1452,7 +1457,7 @@ void msm_jpegdma_hw_put_qos(struct msm_jpegdma_device *dma)
*/
int msm_jpegdma_hw_get_vbif(struct msm_jpegdma_device *dma)
{
- int i;
+ int i, j;
int ret;
unsigned int cnt;
const void *property;
@@ -1463,33 +1468,38 @@ int msm_jpegdma_hw_get_vbif(struct msm_jpegdma_device *dma)
dev_dbg(dma->dev, "Missing vbif settings\n");
return 0;
}
+
cnt /= 4;
+ if (cnt % 2)
+ return -EINVAL;
- dma->vbif_regs = kzalloc((sizeof(*dma->vbif_regs) * cnt), GFP_KERNEL);
+ dma->vbif_regs_num = cnt / 2;
+
+ dma->vbif_regs = kzalloc((sizeof(struct jpegdma_reg_cfg) *
+ dma->vbif_regs_num), GFP_KERNEL);
if (!dma->vbif_regs)
return -ENOMEM;
- for (i = 0; i < cnt; i = i + 2) {
+ for (i = 0, j = 0; i < cnt; i += 2, j++) {
ret = of_property_read_u32_index(dma->dev->of_node,
"qcom,vbif-reg-settings", i,
- &dma->vbif_regs[i].reg);
+ &dma->vbif_regs[j].reg);
if (ret < 0) {
- dev_err(dma->dev, "can not read vbif reg %d\n", i);
+ dev_err(dma->dev, "can not read vbif reg %d\n", j);
goto error;
}
ret = of_property_read_u32_index(dma->dev->of_node,
"qcom,vbif-reg-settings", i + 1,
- &dma->vbif_regs[i].val);
+ &dma->vbif_regs[j].val);
if (ret < 0) {
- dev_err(dma->dev, "can not read vbif setting %d\n", i);
+ dev_err(dma->dev, "can not read vbif setting %d\n", j);
goto error;
}
- dev_dbg(dma->dev, "Vbif idx %d, reg %x val %x\n", i,
- dma->vbif_regs[i].reg, dma->vbif_regs[i].val);
+ dev_dbg(dma->dev, "Vbif idx %d, reg %x val %x\n", j,
+ dma->vbif_regs[j].reg, dma->vbif_regs[j].val);
}
- dma->vbif_regs_num = cnt;
return 0;
error:
@@ -1515,7 +1525,7 @@ void msm_jpegdma_hw_put_vbif(struct msm_jpegdma_device *dma)
*/
int msm_jpegdma_hw_get_prefetch(struct msm_jpegdma_device *dma)
{
- int i;
+ int i, j;
int ret;
unsigned int cnt;
const void *property;
@@ -1526,35 +1536,39 @@ int msm_jpegdma_hw_get_prefetch(struct msm_jpegdma_device *dma)
dev_dbg(dma->dev, "Missing prefetch settings\n");
return 0;
}
+
cnt /= 4;
+ if (cnt % 2)
+ return -EINVAL;
+
+ dma->prefetch_regs_num = cnt / 2;
- dma->prefetch_regs = kcalloc(cnt, sizeof(*dma->prefetch_regs),
- GFP_KERNEL);
+ dma->prefetch_regs = kzalloc((sizeof(struct jpegdma_reg_cfg) *
+ dma->prefetch_regs_num), GFP_KERNEL);
if (!dma->prefetch_regs)
return -ENOMEM;
- for (i = 0; i < cnt; i = i + 2) {
+ for (i = 0, j = 0; i < cnt; i += 2, j++) {
ret = of_property_read_u32_index(dma->dev->of_node,
"qcom,prefetch-reg-settings", i,
- &dma->prefetch_regs[i].reg);
+ &dma->prefetch_regs[j].reg);
if (ret < 0) {
- dev_err(dma->dev, "can not read prefetch reg %d\n", i);
+ dev_err(dma->dev, "can not read prefetch reg %d\n", j);
goto error;
}
ret = of_property_read_u32_index(dma->dev->of_node,
"qcom,prefetch-reg-settings", i + 1,
- &dma->prefetch_regs[i].val);
+ &dma->prefetch_regs[j].val);
if (ret < 0) {
dev_err(dma->dev, "can not read prefetch setting %d\n",
- i);
+ j);
goto error;
}
- dev_dbg(dma->dev, "Prefetch idx %d, reg %x val %x\n", i,
- dma->prefetch_regs[i].reg, dma->prefetch_regs[i].val);
+ dev_dbg(dma->dev, "Prefetch idx %d, reg %x val %x\n", j,
+ dma->prefetch_regs[j].reg, dma->prefetch_regs[j].val);
}
- dma->prefetch_regs_num = cnt;
return 0;
error:
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h
new file mode 100644
index 000000000000..79d7d94582c5
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h
@@ -0,0 +1,160 @@
+/* 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_CSIPHY_5_0_1_HWREG_H
+#define MSM_CSIPHY_5_0_1_HWREG_H
+
+#define ULPM_WAKE_UP_TIMER_MODE 2
+
+#include <sensor/csiphy/msm_csiphy.h>
+
+struct csiphy_reg_parms_t csiphy_v5_0_1 = {
+ .mipi_csiphy_interrupt_status0_addr = 0x8B0,
+ .mipi_csiphy_interrupt_clear0_addr = 0x858,
+ .mipi_csiphy_glbl_irq_cmd_addr = 0x828,
+ .combo_clk_mask = 0x10,
+};
+
+struct csiphy_reg_3ph_parms_t csiphy_v5_0_1_3ph = {
+ /*MIPI CSI PHY registers*/
+ {0x814, 0xD5},
+ {0x818, 0x1},
+ {0x188, 0x7F},
+ {0x18C, 0x7F},
+ {0x190, 0x0},
+ {0x104, 0x6},
+ {0x108, 0x1},
+ {0x10c, 0x12},
+ {0x114, 0x20},
+ {0x118, 0x3E},
+ {0x11c, 0x41},
+ {0x120, 0x41},
+ {0x124, 0x7F},
+ {0x128, 0x0},
+ {0x12c, 0x0},
+ {0x130, 0x1},
+ {0x134, 0x0},
+ {0x138, 0x0},
+ {0x13C, 0x10},
+ {0x140, 0x1},
+ {0x144, 0x12},
+ {0x148, 0xFE},
+ {0x14C, 0x1},
+ {0x154, 0x0},
+ {0x15C, 0x23},
+ {0x160, ULPM_WAKE_UP_TIMER_MODE},
+ {0x164, 0x00},
+ {0x168, 0xA0},
+ {0x16C, 0x25},
+ {0x170, 0x41},
+ {0x174, 0x41},
+ {0x178, 0x3E},
+ {0x17C, 0x0},
+ {0x180, 0x0},
+ {0x184, 0x7F},
+ {0x1cc, 0x41},
+ {0x81c, 0x2},
+ {0x82c, 0xFF},
+ {0x830, 0xFF},
+ {0x834, 0xFB},
+ {0x838, 0xFF},
+ {0x83c, 0x7F},
+ {0x840, 0xFF},
+ {0x844, 0xFF},
+ {0x848, 0xEF},
+ {0x84c, 0xFF},
+ {0x850, 0xFF},
+ {0x854, 0xFF},
+ {0x28, 0x0},
+ {0x800, 0x0},
+ {0x4, 0xC},
+ {0x8, 0x14},
+ {0x8, 0x14},
+ {0x10, 0x52},
+ {0x14, 0x60},
+ {0x14, 0x60},
+ {0x1C, 0xa},
+ {0x1c, 0xa},
+ {0x38, 0x1},
+ {0x3C, 0xB8},
+ {0x3C, 0xB8},
+ {0x14, 0x0},
+ {0x14, 0x0},
+ {0x700, 0xC0},
+ {0x150, 0},
+ {0x1dc, 0x51},
+ {0x2C, 0x1},
+ {0x34, 0xf},
+ {0x728, 0x4},
+ {0x0, 0x91},
+ {0x70C, 0xA5},
+ {0x38, 0xFE},
+ {0x81c, 0x6},
+};
+
+struct csiphy_settings_t csiphy_combo_mode_v5_0_1 = {
+ {
+ {0x818, 0x1},
+ {0x81c, 0x2},
+ {0x004, 0x08},
+ {0x704, 0x08},
+ {0x204, 0x08},
+ {0x404, 0x08},
+ {0x604, 0x08},
+ {0x02c, 0x1},
+ {0x22c, 0x1},
+ {0x42c, 0x1},
+ {0x62c, 0x1},
+ {0x72c, 0x1},
+ {0x034, 0x0f},
+ {0x234, 0x0f},
+ {0x434, 0x0f},
+ {0x634, 0x0f},
+ {0x734, 0x0f},
+ {0x01c, 0x0a},
+ {0x21c, 0x0a},
+ {0x41c, 0x0a},
+ {0x61c, 0x0a},
+ {0x71c, 0x0a},
+ {0x014, 0x60},
+ {0x214, 0x60},
+ {0x414, 0x60},
+ {0x614, 0x60},
+ {0x714, 0x60},
+ {0x728, 0x4},
+ {0x428, 0x0a},
+ {0x628, 0x0e},
+ {0x03c, 0xb8},
+ {0x73c, 0xb8},
+ {0x23c, 0xb8},
+ {0x43c, 0xb8},
+ {0x63c, 0xb8},
+ {0x000, 0x91},
+ {0x700, 0x80},
+ {0x200, 0x91},
+ {0x400, 0x91},
+ {0x600, 0x80},
+ {0x70c, 0xA5},
+ {0x60c, 0xA5},
+ {0x010, 0x52},
+ {0x710, 0x52},
+ {0x210, 0x52},
+ {0x410, 0x52},
+ {0x610, 0x52},
+ {0x038, 0xfe},
+ {0x738, 0x1f},
+ {0x238, 0xfe},
+ {0x438, 0xfe},
+ {0x638, 0x1f},
+ }
+};
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h
index c2b928d2b5d3..198d130b24fc 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h
@@ -101,4 +101,61 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_3ph = {
{0x38, 0xFE},
{0x81c, 0x6},
};
+
+struct csiphy_settings_t csiphy_combo_mode_v5_0 = {
+ {
+ {0x818, 0x1},
+ {0x81c, 0x2},
+ {0x004, 0x08},
+ {0x704, 0x08},
+ {0x204, 0x08},
+ {0x404, 0x08},
+ {0x604, 0x08},
+ {0x02c, 0x1},
+ {0x22c, 0x1},
+ {0x42c, 0x1},
+ {0x62c, 0x1},
+ {0x72c, 0x1},
+ {0x034, 0x0f},
+ {0x234, 0x0f},
+ {0x434, 0x0f},
+ {0x634, 0x0f},
+ {0x734, 0x0f},
+ {0x01c, 0x0a},
+ {0x21c, 0x0a},
+ {0x41c, 0x0a},
+ {0x61c, 0x0a},
+ {0x71c, 0x0a},
+ {0x014, 0x60},
+ {0x214, 0x60},
+ {0x414, 0x60},
+ {0x614, 0x60},
+ {0x714, 0x60},
+ {0x728, 0x4},
+ {0x428, 0x0a},
+ {0x628, 0x0e},
+ {0x03c, 0xb8},
+ {0x73c, 0xb8},
+ {0x23c, 0xb8},
+ {0x43c, 0xb8},
+ {0x63c, 0xb8},
+ {0x000, 0x91},
+ {0x700, 0x80},
+ {0x200, 0x91},
+ {0x400, 0x91},
+ {0x600, 0x80},
+ {0x70c, 0xA5},
+ {0x60c, 0xA5},
+ {0x010, 0x52},
+ {0x710, 0x52},
+ {0x210, 0x52},
+ {0x410, 0x52},
+ {0x610, 0x52},
+ {0x038, 0xfe},
+ {0x738, 0x1f},
+ {0x238, 0xfe},
+ {0x438, 0xfe},
+ {0x638, 0x1f},
+ }
+};
#endif
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 30af06dc64f5..6a1b385c3d8b 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
@@ -26,6 +26,7 @@
#include "include/msm_csiphy_3_4_2_hwreg.h"
#include "include/msm_csiphy_3_5_hwreg.h"
#include "include/msm_csiphy_5_0_hwreg.h"
+#include "include/msm_csiphy_5_0_1_hwreg.h"
#include "cam_hw_ops.h"
#define DBG_CSIPHY 0
@@ -40,13 +41,14 @@
#define CSIPHY_VERSION_V32 0x32
#define CSIPHY_VERSION_V342 0x342
#define CSIPHY_VERSION_V35 0x35
-#define CSIPHY_VERSION_V50 0x50
+#define CSIPHY_VERSION_V50 0x500
+#define CSIPHY_VERSION_V501 0x501
#define MSM_CSIPHY_DRV_NAME "msm_csiphy"
#define CLK_LANE_OFFSET 1
#define NUM_LANES_OFFSET 4
#define CSI_3PHASE_HW 1
-#define MAX_LANES 4
+#define MAX_DPHY_DATA_LN 4
#define CLOCK_OFFSET 0x700
#define CSIPHY_SOF_DEBUG_COUNT 2
@@ -55,6 +57,22 @@
static struct v4l2_file_operations msm_csiphy_v4l2_subdev_fops;
+static void msm_csiphy_write_settings(
+ struct csiphy_device *csiphy_dev,
+ struct csiphy_settings_t csiphy_settings)
+{
+ int i = 0;
+
+ for (i = 0; i < MAX_CSIPHY_SETTINGS; i++) {
+ if (csiphy_settings.settings[i].addr == 0 &&
+ csiphy_settings.settings[i].data == 0)
+ break;
+
+ msm_camera_io_w(csiphy_settings.settings[i].data,
+ csiphy_dev->base + csiphy_settings.settings[i].addr);
+ }
+}
+
static void msm_csiphy_cphy_irq_config(
struct csiphy_device *csiphy_dev,
struct msm_camera_csiphy_params *csiphy_params)
@@ -418,7 +436,7 @@ static int msm_csiphy_2phase_lane_config(
csiphybase = csiphy_dev->base;
lane_mask = csiphy_params->lane_mask & 0x1f;
- for (i = 0; i < MAX_LANES; i++) {
+ for (i = 0; i < MAX_DPHY_DATA_LN; i++) {
if (mask == 0x2) {
if (lane_mask & mask)
lane_enable |= 0x80;
@@ -437,7 +455,7 @@ static int msm_csiphy_2phase_lane_config(
csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
mipi_csiphy_3ph_cmn_ctrl6.addr);
- for (i = 0, mask = 0x1; i < MAX_LANES; i++) {
+ for (i = 0, mask = 0x1; i < MAX_DPHY_DATA_LN; i++) {
if (!(lane_mask & mask)) {
if (mask == 0x2)
i--;
@@ -562,112 +580,134 @@ static int msm_csiphy_2phase_lane_config_v50(
struct csiphy_device *csiphy_dev,
struct msm_camera_csiphy_params *csiphy_params)
{
- uint32_t val = 0, lane_enable = 0, clk_lane, mask = 1;
+ uint32_t lane_enable = 0, mask = 1;
uint16_t lane_mask = 0, i = 0, offset;
void __iomem *csiphybase;
csiphybase = csiphy_dev->base;
lane_mask = csiphy_params->lane_mask & 0x1f;
- for (i = 0; i < MAX_LANES; i++) {
+
+ lane_enable = msm_camera_io_r(csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_3ph_cmn_ctrl5.addr);
+
+ /* write settle count and lane_enable */
+ for (i = 0; i < MAX_DPHY_DATA_LN; i++) {
if (mask == 0x2) {
if (lane_mask & mask)
lane_enable |= 0x80;
i--;
- } else if (lane_mask & mask)
+ offset = CLOCK_OFFSET;
+ } else if (lane_mask & mask) {
lane_enable |= 0x1 << (i<<1);
+ offset = 0x200*i;
+ }
+
+ if (lane_mask & mask)
+ msm_camera_io_w((csiphy_params->settle_cnt & 0xFF),
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg2.addr + offset);
mask <<= 1;
}
- CDBG("%s:%d lane_enable: %d\n", __func__, __LINE__, lane_enable);
+ CDBG("%s:%d lane_enable: 0x%x\n", __func__, __LINE__, lane_enable);
msm_camera_io_w(lane_enable,
csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
mipi_csiphy_3ph_cmn_ctrl5.addr);
- msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_3ph_cmn_ctrl6.data,
- csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_3ph_cmn_ctrl6.addr);
- msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_3ph_cmn_ctrl7.data,
- csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_3ph_cmn_ctrl7.addr);
- for (i = 0, mask = 0x1; i < MAX_LANES; i++) {
- if (!(lane_mask & mask)) {
- if (mask == 0x2)
- i--;
- mask <<= 0x1;
- continue;
- }
- if (mask == 0x2) {
- val = 4;
- offset = CLOCK_OFFSET;
- clk_lane = 1;
- i--;
- } else {
- offset = 0x200*i;
- val = 0;
- clk_lane = 0;
- }
-
- if (csiphy_params->combo_mode == 1) {
- val |= 0xA;
- if (mask == csiphy_dev->ctrl_reg->
- csiphy_reg.combo_clk_mask) {
- val |= 0x4;
- clk_lane = 1;
- }
- }
- msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl11.data,
- csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl11.addr + offset);
- msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl13.data,
- csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl13.addr + offset);
- msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_cfg7.data,
- csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_cfg7.addr + offset);
+ /* write mode specific settings */
+ if (csiphy_params->combo_mode == 1)
+ msm_csiphy_write_settings(csiphy_dev,
+ csiphy_dev->ctrl_reg->csiphy_combo_mode_settings);
+ else {
msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_cfg5.data,
+ mipi_csiphy_3ph_cmn_ctrl6.data,
csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_cfg5.addr + offset);
- if (clk_lane == 1)
- msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnck_ctrl10.data,
- csiphybase +
- csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnck_ctrl10.addr);
- msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl15.data,
- csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl15.addr + offset);
- msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl0.data,
- csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl0.addr + offset);
+ mipi_csiphy_3ph_cmn_ctrl6.addr);
msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_cfg1.data,
- csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_cfg1.addr + offset);
- msm_camera_io_w((csiphy_params->settle_cnt & 0xFF),
+ mipi_csiphy_3ph_cmn_ctrl7.data,
csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_cfg2.addr + offset);
- if (clk_lane == 1)
- msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnck_ctrl3.data, csiphybase +
- csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnck_ctrl3.addr);
+ mipi_csiphy_3ph_cmn_ctrl7.addr);
msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_cfg4.data, csiphybase +
+ mipi_csiphy_2ph_lnck_ctrl10.data,
+ csiphybase +
csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_cfg4.addr + offset);
+ mipi_csiphy_2ph_lnck_ctrl10.addr);
msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl14.data,
- csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl14.addr + offset);
- mask <<= 1;
+ mipi_csiphy_2ph_lnck_ctrl3.data, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl3.addr);
+
+ for (i = 0, mask = 0x1; i < MAX_DPHY_DATA_LN; i++) {
+ if (!(lane_mask & mask)) {
+ if (mask == 0x2)
+ i--;
+ mask <<= 0x1;
+ continue;
+ }
+ if (mask == 0x2) {
+ offset = CLOCK_OFFSET;
+ i--;
+ } else {
+ offset = 0x200*i;
+ }
+
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl11.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl11.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl13.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl13.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg7.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg7.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg5.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg5.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl15.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl15.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl0.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl0.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg1.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg1.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg4.data, csiphybase +
+ csiphy_dev->ctrl_reg->csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_cfg4.addr + offset);
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl14.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl14.addr + offset);
+ mask <<= 1;
+ }
}
msm_csiphy_cphy_irq_config(csiphy_dev, csiphy_params);
return 0;
@@ -728,7 +768,7 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev,
if (csiphy_dev->hw_version >= CSIPHY_VERSION_V30 &&
csiphy_dev->clk_mux_base != NULL &&
- csiphy_dev->hw_version != CSIPHY_VERSION_V50) {
+ csiphy_dev->hw_version < CSIPHY_VERSION_V50) {
val = msm_camera_io_r(csiphy_dev->clk_mux_base);
if (csiphy_params->combo_mode &&
(csiphy_params->lane_mask & 0x18) == 0x18) {
@@ -751,7 +791,7 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev,
rc = msm_camera_clk_enable(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_3p_clk_info,
csiphy_dev->csiphy_3p_clk, 2, true);
- if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V50)
+ if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50)
rc = msm_csiphy_3phase_lane_config_v50(
csiphy_dev, csiphy_params);
else
@@ -759,7 +799,7 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev,
csiphy_params);
csiphy_dev->num_irq_registers = 20;
} else {
- if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V50)
+ if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50)
rc = msm_csiphy_2phase_lane_config_v50(
csiphy_dev, csiphy_params);
else
@@ -986,19 +1026,20 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
}
CDBG("%s:%d called\n", __func__, __LINE__);
+ if (csiphy_dev->ref_count++) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return rc;
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
if (csiphy_dev->csiphy_state == CSIPHY_POWER_UP) {
pr_err("%s: csiphy invalid state %d\n", __func__,
csiphy_dev->csiphy_state);
rc = -EINVAL;
return rc;
}
- CDBG("%s:%d called\n", __func__, __LINE__);
- if (csiphy_dev->ref_count++) {
- CDBG("%s csiphy refcount = %d\n", __func__,
- csiphy_dev->ref_count);
- return rc;
- }
CDBG("%s:%d called\n", __func__, __LINE__);
rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
@@ -1063,6 +1104,14 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
return rc;
}
csiphy_dev->csiphy_sof_debug_count = 0;
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (csiphy_dev->ref_count++) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return rc;
+ }
+
CDBG("%s:%d called\n", __func__, __LINE__);
if (csiphy_dev->csiphy_state == CSIPHY_POWER_UP) {
pr_err("%s: csiphy invalid state %d\n", __func__,
@@ -1070,13 +1119,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
rc = -EINVAL;
return rc;
}
- CDBG("%s:%d called\n", __func__, __LINE__);
- if (csiphy_dev->ref_count++) {
- CDBG("%s csiphy refcount = %d\n", __func__,
- csiphy_dev->ref_count);
- return rc;
- }
CDBG("%s:%d called\n", __func__, __LINE__);
rc = cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
CAM_AHB_SVS_VOTE);
@@ -1160,7 +1203,7 @@ static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
msm_camera_io_w(0x0,
csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
mipi_csiphy_3ph_cmn_ctrl6.addr);
- if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V50)
+ if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50)
msm_camera_io_w(0x0,
csiphy_dev->base +
csiphy_dev->ctrl_reg->csiphy_3ph_reg.
@@ -1271,7 +1314,7 @@ static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
msm_camera_io_w(0x0,
csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_3ph_reg.
mipi_csiphy_3ph_cmn_ctrl6.addr);
- if (csiphy_dev->hw_dts_version == CSIPHY_VERSION_V50)
+ if (csiphy_dev->hw_dts_version >= CSIPHY_VERSION_V50)
msm_camera_io_w(0x0,
csiphy_dev->base +
csiphy_dev->ctrl_reg->csiphy_3ph_reg.
@@ -1651,6 +1694,16 @@ static int csiphy_probe(struct platform_device *pdev)
new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v5_0;
new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V50;
new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW;
+ new_csiphy_dev->ctrl_reg->csiphy_combo_mode_settings =
+ csiphy_combo_mode_v5_0;
+ } else if (of_device_is_compatible(new_csiphy_dev->pdev->dev.of_node,
+ "qcom,csiphy-v5.01")) {
+ new_csiphy_dev->ctrl_reg->csiphy_3ph_reg = csiphy_v5_0_1_3ph;
+ new_csiphy_dev->ctrl_reg->csiphy_reg = csiphy_v5_0_1;
+ new_csiphy_dev->hw_dts_version = CSIPHY_VERSION_V501;
+ new_csiphy_dev->csiphy_3phase = CSI_3PHASE_HW;
+ new_csiphy_dev->ctrl_reg->csiphy_combo_mode_settings =
+ csiphy_combo_mode_v5_0_1;
} else {
pr_err("%s:%d, invalid hw version : 0x%x\n", __func__, __LINE__,
new_csiphy_dev->hw_dts_version);
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 4944ac606bc8..aba88da1157e 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
@@ -24,12 +24,17 @@
#define MAX_CSIPHY 3
#define CSIPHY_NUM_CLK_MAX 16
+#define MAX_CSIPHY_SETTINGS 120
struct csiphy_reg_t {
uint32_t addr;
uint32_t data;
};
+struct csiphy_settings_t {
+ struct csiphy_reg_t settings[MAX_CSIPHY_SETTINGS];
+};
+
struct csiphy_reg_parms_t {
/*MIPI CSI PHY registers*/
uint32_t mipi_csiphy_lnn_cfg1_addr;
@@ -140,6 +145,7 @@ struct csiphy_reg_3ph_parms_t {
struct csiphy_ctrl_t {
struct csiphy_reg_parms_t csiphy_reg;
struct csiphy_reg_3ph_parms_t csiphy_3ph_reg;
+ struct csiphy_settings_t csiphy_combo_mode_settings;
};
enum msm_csiphy_state_t {
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
index d0ce1de1162a..5f749bd46273 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/of_gpio.h>
+#include <linux/leds-qpnp-flash.h>
#include "msm_flash.h"
#include "msm_camera_dt_util.h"
#include "msm_cci.h"
@@ -491,6 +492,45 @@ static int32_t msm_flash_init(
return 0;
}
+static int32_t msm_flash_prepare(
+ struct msm_flash_ctrl_t *flash_ctrl)
+{
+ int32_t ret = 0;
+
+ CDBG("%s:%d: State : %d\n",
+ __func__, __LINE__, flash_ctrl->flash_state);
+
+ if (flash_ctrl->switch_trigger == NULL) {
+ pr_err("%s:%d Invalid argument\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT &&
+ flash_ctrl->is_regulator_enabled == 0) {
+ ret = qpnp_flash_led_prepare(flash_ctrl->switch_trigger,
+ ENABLE_REGULATOR, NULL);
+ if (ret < 0) {
+ pr_err("%s:%d regulator enable failed ret = %d\n",
+ __func__, __LINE__, ret);
+ return ret;
+ }
+ flash_ctrl->is_regulator_enabled = 1;
+ } else if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_RELEASE &&
+ flash_ctrl->is_regulator_enabled) {
+ ret = qpnp_flash_led_prepare(flash_ctrl->switch_trigger,
+ DISABLE_REGULATOR, NULL);
+ if (ret < 0) {
+ pr_err("%s:%d regulator disable failed ret = %d\n",
+ __func__, __LINE__, ret);
+ return ret;
+ }
+ flash_ctrl->is_regulator_enabled = 0;
+ }
+ CDBG("%s:%d:Exit\n", __func__, __LINE__);
+ return ret;
+}
+
static int32_t msm_flash_low(
struct msm_flash_ctrl_t *flash_ctrl,
struct msm_flash_cfg_data_t *flash_data)
@@ -564,15 +604,33 @@ static int32_t msm_flash_high(
return 0;
}
+static int32_t msm_flash_query_current(
+ struct msm_flash_ctrl_t *flash_ctrl,
+ struct msm_flash_query_data_t *flash_query_data)
+{
+ int32_t ret = -EINVAL;
+ int32_t max_current = -EINVAL;
+
+ if (flash_ctrl->switch_trigger) {
+ ret = qpnp_flash_led_prepare(flash_ctrl->switch_trigger,
+ QUERY_MAX_CURRENT, &max_current);
+ if (ret < 0) {
+ pr_err("%s:%d Query max_avail_curr failed ret = %d\n",
+ __func__, __LINE__, ret);
+ return ret;
+ }
+ }
+
+ flash_query_data->max_avail_curr = max_current;
+ CDBG("%s: %d: max_avail_curr : %d\n", __func__, __LINE__,
+ flash_query_data->max_avail_curr);
+ return 0;
+}
+
static int32_t msm_flash_release(
struct msm_flash_ctrl_t *flash_ctrl)
{
int32_t rc = 0;
- if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_RELEASE) {
- pr_err("%s:%d Invalid flash state = %d",
- __func__, __LINE__, flash_ctrl->flash_state);
- return 0;
- }
rc = flash_ctrl->func_tbl->camera_flash_off(flash_ctrl, NULL);
if (rc < 0) {
@@ -600,24 +658,49 @@ static int32_t msm_flash_config(struct msm_flash_ctrl_t *flash_ctrl,
rc = msm_flash_init(flash_ctrl, flash_data);
break;
case CFG_FLASH_RELEASE:
- if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT)
+ if (flash_ctrl->flash_state != MSM_CAMERA_FLASH_RELEASE) {
rc = flash_ctrl->func_tbl->camera_flash_release(
flash_ctrl);
+ } else {
+ CDBG(pr_fmt("Invalid state : %d\n"),
+ flash_ctrl->flash_state);
+ }
break;
case CFG_FLASH_OFF:
- if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT)
+ if ((flash_ctrl->flash_state != MSM_CAMERA_FLASH_RELEASE) &&
+ (flash_ctrl->flash_state != MSM_CAMERA_FLASH_OFF)) {
rc = flash_ctrl->func_tbl->camera_flash_off(
flash_ctrl, flash_data);
+ if (!rc)
+ flash_ctrl->flash_state = MSM_CAMERA_FLASH_OFF;
+ } else {
+ CDBG(pr_fmt("Invalid state : %d\n"),
+ flash_ctrl->flash_state);
+ }
break;
case CFG_FLASH_LOW:
- if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT)
+ if ((flash_ctrl->flash_state == MSM_CAMERA_FLASH_OFF) ||
+ (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT)) {
rc = flash_ctrl->func_tbl->camera_flash_low(
flash_ctrl, flash_data);
+ if (!rc)
+ flash_ctrl->flash_state = MSM_CAMERA_FLASH_LOW;
+ } else {
+ CDBG(pr_fmt("Invalid state : %d\n"),
+ flash_ctrl->flash_state);
+ }
break;
case CFG_FLASH_HIGH:
- if (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT)
+ if ((flash_ctrl->flash_state == MSM_CAMERA_FLASH_OFF) ||
+ (flash_ctrl->flash_state == MSM_CAMERA_FLASH_INIT)) {
rc = flash_ctrl->func_tbl->camera_flash_high(
flash_ctrl, flash_data);
+ if (!rc)
+ flash_ctrl->flash_state = MSM_CAMERA_FLASH_HIGH;
+ } else {
+ CDBG(pr_fmt("Invalid state : %d\n"),
+ flash_ctrl->flash_state);
+ }
break;
default:
rc = -EFAULT;
@@ -626,11 +709,55 @@ static int32_t msm_flash_config(struct msm_flash_ctrl_t *flash_ctrl,
mutex_unlock(flash_ctrl->flash_mutex);
+ rc = msm_flash_prepare(flash_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d Enable/Disable Regulator failed ret = %d",
+ __func__, __LINE__, rc);
+ return rc;
+ }
+
CDBG("Exit %s type %d\n", __func__, flash_data->cfg_type);
return rc;
}
+static int32_t msm_flash_query_data(struct msm_flash_ctrl_t *flash_ctrl,
+ void __user *argp)
+{
+ int32_t rc = -EINVAL, i = 0;
+ struct msm_flash_query_data_t *flash_query =
+ (struct msm_flash_query_data_t *) argp;
+
+ CDBG("Enter %s type %d\n", __func__, flash_query->query_type);
+
+ switch (flash_query->query_type) {
+ case FLASH_QUERY_CURRENT:
+ if (flash_ctrl->func_tbl && flash_ctrl->func_tbl->
+ camera_flash_query_current != NULL)
+ rc = flash_ctrl->func_tbl->
+ camera_flash_query_current(
+ flash_ctrl, flash_query);
+ else {
+ flash_query->max_avail_curr = 0;
+ for (i = 0; i < flash_ctrl->flash_num_sources; i++) {
+ flash_query->max_avail_curr +=
+ flash_ctrl->flash_op_current[i];
+ }
+ rc = 0;
+ CDBG("%s: max_avail_curr: %d\n", __func__,
+ flash_query->max_avail_curr);
+ }
+ break;
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ CDBG("Exit %s type %d\n", __func__, flash_query->query_type);
+
+ return rc;
+}
+
static long msm_flash_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
@@ -662,8 +789,11 @@ static long msm_flash_subdev_ioctl(struct v4l2_subdev *sd,
pr_err("fctrl->func_tbl NULL\n");
return -EINVAL;
} else {
- return fctrl->func_tbl->camera_flash_release(fctrl);
+ fctrl->func_tbl->camera_flash_release(fctrl);
+ return msm_flash_prepare(fctrl);
}
+ case VIDIOC_MSM_FLASH_QUERY_DATA:
+ return msm_flash_query_data(fctrl, argp);
default:
pr_err_ratelimited("invalid cmd %d\n", cmd);
return -ENOIOCTLCMD;
@@ -1140,6 +1270,7 @@ static struct msm_flash_table msm_pmic_flash_table = {
.camera_flash_off = msm_flash_off,
.camera_flash_low = msm_flash_low,
.camera_flash_high = msm_flash_high,
+ .camera_flash_query_current = msm_flash_query_current,
},
};
@@ -1151,6 +1282,7 @@ static struct msm_flash_table msm_gpio_flash_table = {
.camera_flash_off = msm_flash_off,
.camera_flash_low = msm_flash_low,
.camera_flash_high = msm_flash_high,
+ .camera_flash_query_current = NULL,
},
};
@@ -1162,6 +1294,7 @@ static struct msm_flash_table msm_i2c_flash_table = {
.camera_flash_off = msm_flash_i2c_write_setting_array,
.camera_flash_low = msm_flash_i2c_write_setting_array,
.camera_flash_high = msm_flash_i2c_write_setting_array,
+ .camera_flash_query_current = NULL,
},
};
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.h b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.h
index c82e48cddcaf..ad619fa4eb63 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-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
@@ -27,6 +27,9 @@
enum msm_camera_flash_state_t {
MSM_CAMERA_FLASH_INIT,
+ MSM_CAMERA_FLASH_OFF,
+ MSM_CAMERA_FLASH_LOW,
+ MSM_CAMERA_FLASH_HIGH,
MSM_CAMERA_FLASH_RELEASE,
};
@@ -42,6 +45,9 @@ struct msm_flash_func_t {
struct msm_flash_cfg_data_t *);
int32_t (*camera_flash_high)(struct msm_flash_ctrl_t *,
struct msm_flash_cfg_data_t *);
+ int32_t (*camera_flash_query_current)(struct msm_flash_ctrl_t *,
+ struct msm_flash_query_data_t *);
+
};
struct msm_flash_table {
@@ -67,6 +73,7 @@ struct msm_flash_ctrl_t {
/* Switch node to trigger led */
const char *switch_trigger_name;
struct led_trigger *switch_trigger;
+ uint32_t is_regulator_enabled;
/* Flash */
uint32_t flash_num_sources;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/Makefile b/drivers/media/platform/msm/camera_v2/sensor/io/Makefile
index ec958697ae13..549c35a806f7 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/Makefile
@@ -2,4 +2,5 @@ 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
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
-obj-$(CONFIG_MSMB_CAMERA) += msm_camera_cci_i2c.o msm_camera_qup_i2c.o msm_camera_spi.o msm_camera_dt_util.o
+ccflags-y += -Idrivers/misc/
+obj-$(CONFIG_MSMB_CAMERA) += msm_camera_cci_i2c.o msm_camera_qup_i2c.o msm_camera_spi.o msm_camera_dt_util.o msm_camera_tz_i2c.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
index 0fbe35713d8e..785dd54d65e1 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_i2c.h
@@ -152,4 +152,60 @@ int32_t msm_camera_qup_i2c_poll(struct msm_camera_i2c_client *client,
uint32_t addr, uint16_t data,
enum msm_camera_i2c_data_type data_type, uint32_t delay_ms);
+int32_t msm_camera_tz_i2c_register_sensor(void *s_ctrl_p);
+
+int32_t msm_camera_tz_i2c_power_up(struct msm_camera_i2c_client *client);
+
+int32_t msm_camera_tz_i2c_power_down(struct msm_camera_i2c_client *client);
+
+int32_t msm_camera_tz_i2c_read(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_tz_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
+
+int32_t msm_camera_tz_i2c_write(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_tz_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte);
+
+int32_t msm_camera_tz_i2c_write_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_table_async(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_table_sync(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_table_sync_block(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_seq_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_tz_i2c_write_conf_tbl(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_sensor_tz_i2c_util(struct msm_camera_i2c_client *client,
+ uint16_t cci_cmd);
+
+int32_t msm_camera_tz_i2c_poll(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type);
+
#endif
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
new file mode 100644
index 000000000000..25c152be2b71
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c
@@ -0,0 +1,1093 @@
+/* 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/ktime.h>
+#include <linux/mutex.h>
+#include <soc/qcom/camera2.h>
+#include "qseecom_kernel.h"
+#include "msm_camera_i2c.h"
+#include "msm_camera_io_util.h"
+#include "msm_cci.h"
+#include "msm_sensor.h"
+
+#define QSEECOM_SBUFF_SIZE SZ_128K
+#define MAX_TA_NAME 32
+#define EMPTY_QSEECOM_HANDLE NULL
+
+#ifndef CONFIG_MSM_SEC_CCI_TA_NAME
+ #define CONFIG_MSM_SEC_CCI_TA_NAME "seccamdemo64"
+#endif /* CONFIG_MSM_SEC_CCI_TA_NAME */
+
+/* Update version major number in case the HLOS-TA interface is changed*/
+#define TA_IF_VERSION_MAJ 0
+#define TA_IF_VERSION_MIN 1
+
+#undef CDBG
+#ifdef CONFIG_MSM_SEC_CCI_DEBUG
+
+#define CDBG(fmt, args...) pr_info(CONFIG_MSM_SEC_CCI_TA_NAME "::%s:%d - " fmt,\
+ __func__, __LINE__, ##args)
+#define TZ_I2C_FN_RETURN(ret, i2c_fn, ...) \
+ ((ret < 0) ? i2c_fn(__VA_ARGS__):ret)
+
+#else /* CONFIG_MSM_SEC_CCI_DEBUG */
+
+#define CDBG(fmt, args...) pr_info("%s:%d - " fmt, __func__, __LINE__, ##args)
+#define TZ_I2C_FN_RETURN(ret, i2c_fn, ...) \
+ ((ret < 0) ? -EFAULT:ret)
+
+#endif /* CONFIG_MSM_SEC_CCI_DEBUG */
+
+#pragma pack(push, msm_camera_tz_i2c, 1)
+
+enum msm_camera_tz_i2c_cmd_id_t {
+ TZ_I2C_CMD_GET_NONE,
+ TZ_I2C_CMD_GET_IF_VERSION,
+ TZ_I2C_CMD_POWER_UP,
+ TZ_I2C_CMD_POWER_DOWN,
+ TZ_I2C_CMD_CCI_GENERIC,
+ TZ_I2C_CMD_CCI_READ,
+ TZ_I2C_CMD_CCI_READ_SEQ,
+ TZ_I2C_CMD_CCI_WRITE,
+ TZ_I2C_CMD_CCI_WRITE_SEQ,
+ TZ_I2C_CMD_CCI_WRITE_TABLE_ASYNC,
+ TZ_I2C_CMD_CCI_WRITE_TABLE_SYNC,
+ TZ_I2C_CMD_CCI_WRITE_TABLE_SYNC_BLOCK,
+ TZ_I2C_CMD_CCI_WRITE_TABLE,
+ TZ_I2C_CMD_CCI_WRITE_SEQ_TABLE,
+ TZ_I2C_CMD_CCI_WRITE_TABLE_W_MICRODELAY,
+ TZ_I2C_CMD_CCI_POLL,
+ TZ_I2C_CMD_CCI_WRITE_CONF_TBL,
+ TZ_I2C_CMD_CCI_UTIL,
+};
+
+enum msm_camera_tz_i2c_status_t {
+ TZ_I2C_STATUS_SUCCESS = 0,
+ TZ_I2C_STATUS_GENERAL_FAILURE = -1,
+ TZ_I2C_STATUS_INVALID_INPUT_PARAMS = -2,
+ TZ_I2C_STATUS_INVALID_SENSOR_ID = -3,
+ TZ_I2C_STATUS_BYPASS = -4,
+ TZ_I2C_STATUS_ERR_SIZE = 0x7FFFFFFF
+};
+
+struct msm_camera_tz_i2c_generic_req_t {
+ enum msm_camera_tz_i2c_cmd_id_t cmd_id;
+};
+
+struct msm_camera_tz_i2c_generic_rsp_t {
+ enum msm_camera_tz_i2c_status_t rc;
+};
+
+#define msm_camera_tz_i2c_get_if_version_req_t msm_camera_tz_i2c_generic_req_t
+
+struct msm_camera_tz_i2c_get_if_version_rsp_t {
+ enum msm_camera_tz_i2c_status_t rc;
+ uint32_t if_version_maj;
+ uint32_t if_version_min;
+};
+
+struct msm_camera_tz_i2c_power_up_req_t {
+ enum msm_camera_tz_i2c_cmd_id_t cmd_id;
+ int32_t sensor_id;
+};
+
+#define msm_camera_tz_i2c_power_up_rsp_t msm_camera_tz_i2c_generic_rsp_t
+
+struct msm_camera_tz_i2c_power_down_req_t {
+ enum msm_camera_tz_i2c_cmd_id_t cmd_id;
+ int32_t sensor_id;
+};
+
+#define msm_camera_tz_i2c_power_down_rsp_t msm_camera_tz_i2c_generic_rsp_t
+
+struct msm_camera_tz_i2c_cci_generic_req_t {
+ enum msm_camera_tz_i2c_cmd_id_t cmd_id;
+ int32_t sensor_id;
+ enum msm_camera_tz_i2c_cmd_id_t cci_cmd_id;
+ uint32_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+};
+
+#define msm_camera_tz_i2c_cci_generic_rsp_t msm_camera_tz_i2c_generic_rsp_t
+
+struct msm_camera_tz_i2c_cci_read_req_t {
+ enum msm_camera_tz_i2c_cmd_id_t cmd_id;
+ int32_t sensor_id;
+ uint32_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+ uint32_t addr;
+ uint32_t data_type;
+};
+
+struct msm_camera_tz_i2c_cci_read_rsp_t {
+ enum msm_camera_tz_i2c_status_t rc;
+ uint16_t data;
+};
+
+struct msm_camera_tz_i2c_cci_write_req_t {
+ enum msm_camera_tz_i2c_cmd_id_t cmd_id;
+ int32_t sensor_id;
+ uint32_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+ uint32_t addr;
+ uint16_t data;
+ uint32_t data_type;
+};
+
+#define msm_camera_tz_i2c_cci_write_rsp_t msm_camera_tz_i2c_generic_rsp_t
+
+struct msm_camera_tz_i2c_cci_util_req_t {
+ enum msm_camera_tz_i2c_cmd_id_t cmd_id;
+ int32_t sensor_id;
+ uint32_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+ uint16_t cci_cmd;
+};
+
+#define msm_camera_tz_i2c_cci_util_rsp_t msm_camera_tz_i2c_generic_rsp_t
+
+#pragma pack(pop, msm_camera_tz_i2c)
+
+struct msm_camera_tz_i2c_sensor_info_t {
+ struct msm_sensor_ctrl_t *s_ctrl;
+ struct msm_camera_i2c_fn_t *saved_sensor_i2c_fn;
+ uint32_t secure;
+ uint32_t ta_enabled;
+ struct qseecom_handle *ta_qseecom_handle;
+ const char *ta_name;
+};
+
+struct msm_camera_tz_i2c_ctrl_t {
+ struct mutex lock;
+ uint32_t lock_ready;
+ uint32_t secure_mode;
+};
+
+static struct msm_camera_tz_i2c_ctrl_t msm_camera_tz_i2c_ctrl;
+
+static struct msm_camera_tz_i2c_sensor_info_t sensor_info[MAX_CAMERAS] = {
+ {NULL, NULL, 0, 0, NULL, CONFIG_MSM_SEC_CCI_TA_NAME},
+ {NULL, NULL, 0, 0, NULL, CONFIG_MSM_SEC_CCI_TA_NAME},
+ {NULL, NULL, 0, 0, NULL, CONFIG_MSM_SEC_CCI_TA_NAME},
+ {NULL, NULL, 0, 0, NULL, CONFIG_MSM_SEC_CCI_TA_NAME},
+};
+
+static int32_t msm_camera_tz_i2c_is_sensor_secure(
+ struct msm_camera_i2c_client *client)
+{
+ uint32_t index;
+
+ if (client == NULL) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ CDBG("Enter\n");
+ for (index = 0; index < MAX_CAMERAS; index++) {
+ if ((sensor_info[index].s_ctrl != NULL) &&
+ sensor_info[index].secure &&
+ (sensor_info[index].s_ctrl->sensor_i2c_client ==
+ client)) {
+ CDBG("Found secure sensor ID = %d\n",
+ sensor_info[index].s_ctrl->id);
+ return sensor_info[index].s_ctrl->id;
+ }
+ }
+ return -EINVAL;
+}
+
+static int32_t get_cmd_rsp_buffers(
+ struct qseecom_handle *ta_qseecom_handle,
+ void **cmd, int *cmd_len,
+ void **rsp, int *rsp_len)
+{
+
+ CDBG("Enter\n");
+ if ((ta_qseecom_handle == NULL) ||
+ (cmd == NULL) || (cmd_len == NULL) ||
+ (rsp == NULL) || (rsp_len == NULL)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ 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:%d - Shared buffer too small to hold cmd=%d and rsp=%d\n",
+ __func__, __LINE__,
+ *cmd_len, *rsp_len);
+ return -ENOMEM;
+ }
+
+ *cmd = ta_qseecom_handle->sbuf;
+ *rsp = ta_qseecom_handle->sbuf + *cmd_len;
+ return 0;
+}
+
+static int32_t msm_camera_tz_i2c_ta_get_if_version(
+ struct qseecom_handle *ta_qseecom_handle,
+ uint32_t *if_version_maj,
+ uint32_t *if_version_min)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_get_if_version_req_t *cmd;
+ struct msm_camera_tz_i2c_get_if_version_rsp_t *rsp;
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+ if ((ta_qseecom_handle == NULL) ||
+ (if_version_maj == NULL) || (if_version_min == NULL)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ cmd_len = sizeof(struct msm_camera_tz_i2c_get_if_version_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_get_if_version_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = TZ_I2C_CMD_GET_IF_VERSION;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Unable to get if version info, rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+
+ if (rsp->rc < 0) {
+ CDBG("TZ I2C App error, rc=%d\n", rsp->rc);
+ rc = -EFAULT;
+ } else {
+ *if_version_maj = rsp->if_version_maj;
+ *if_version_min = rsp->if_version_min;
+ CDBG("TZ I2C If version %d.%d\n", *if_version_maj,
+ *if_version_min);
+ }
+ }
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_power_up(
+ struct qseecom_handle *ta_qseecom_handle,
+ int32_t sensor_id,
+ uint32_t *sensor_secure)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_power_up_req_t *cmd;
+ struct msm_camera_tz_i2c_power_up_rsp_t *rsp;
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+
+ if (sensor_secure == NULL)
+ return -EINVAL;
+
+ *sensor_secure = 0;
+ if ((ta_qseecom_handle == NULL) ||
+ (sensor_secure == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ cmd_len = sizeof(struct msm_camera_tz_i2c_power_up_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_power_up_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = TZ_I2C_CMD_POWER_UP;
+ cmd->sensor_id = sensor_id;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Unable to get sensor secure status, rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+
+ if (rsp->rc == TZ_I2C_STATUS_SUCCESS)
+ *sensor_secure = 1;
+ CDBG("Sensor %d is %s\n", sensor_id,
+ (*sensor_secure)?"SECURE":"NON-SECURE");
+ }
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_power_down(
+ struct qseecom_handle *ta_qseecom_handle,
+ int32_t sensor_id)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_power_down_req_t *cmd;
+ struct msm_camera_tz_i2c_power_down_rsp_t *rsp;
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+
+ if ((ta_qseecom_handle == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ cmd_len = sizeof(struct msm_camera_tz_i2c_power_down_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_power_down_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = TZ_I2C_CMD_POWER_DOWN;
+ cmd->sensor_id = sensor_id;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed: rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_cci_generic(
+ struct msm_camera_i2c_client *client,
+ enum msm_camera_tz_i2c_cmd_id_t cci_cmd_id)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_cci_generic_req_t *cmd;
+ struct msm_camera_tz_i2c_cci_generic_rsp_t *rsp;
+ int32_t rc = 0;
+ struct qseecom_handle *ta_qseecom_handle;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+
+ if ((client == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, cci_cmd_id=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ cci_cmd_id);
+
+ ta_qseecom_handle = sensor_info[sensor_id].ta_qseecom_handle;
+ cmd_len = sizeof(struct msm_camera_tz_i2c_cci_generic_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_cci_generic_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = TZ_I2C_CMD_CCI_GENERIC;
+ cmd->sensor_id = sensor_id;
+ cmd->cci_cmd_id = cci_cmd_id;
+ cmd->cci_i2c_master = client->cci_client->cci_i2c_master;
+ cmd->sid = client->cci_client->sid;
+ cmd->cid = client->cci_client->cid;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed: rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+ rc = rsp->rc;
+ CDBG("Done: rc=%d, cci_cmd_id=%d\n", rc, cci_cmd_id);
+ }
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_cci_read(
+ struct msm_camera_i2c_client *client,
+ uint32_t addr,
+ uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_cci_read_req_t *cmd;
+ struct msm_camera_tz_i2c_cci_read_rsp_t *rsp;
+ int32_t rc = 0;
+ struct qseecom_handle *ta_qseecom_handle;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+
+ if ((client == NULL) ||
+ (data == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, Addr=0x%X, Type=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ addr,
+ data_type);
+
+ ta_qseecom_handle = sensor_info[sensor_id].ta_qseecom_handle;
+ cmd_len = sizeof(struct msm_camera_tz_i2c_cci_read_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_cci_read_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = TZ_I2C_CMD_CCI_READ;
+ cmd->sensor_id = sensor_id;
+ cmd->cci_i2c_master = client->cci_client->cci_i2c_master;
+ cmd->sid = client->cci_client->sid;
+ cmd->cid = client->cci_client->cid;
+ cmd->addr = addr;
+ cmd->data_type = data_type;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed: rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+ rc = rsp->rc;
+ *data = rsp->data;
+
+ CDBG("Done: rc=%d, addr=0x%X, data=0x%X\n", rc,
+ addr, *data);
+ }
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_cci_write(
+ struct msm_camera_i2c_client *client,
+ uint32_t addr,
+ uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_cci_write_req_t *cmd;
+ struct msm_camera_tz_i2c_cci_write_rsp_t *rsp;
+ int32_t rc = 0;
+ struct qseecom_handle *ta_qseecom_handle;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+
+ if ((client == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, Addr=0x%X, Data=0x%X Type=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ addr,
+ data,
+ data_type);
+
+ ta_qseecom_handle = sensor_info[sensor_id].ta_qseecom_handle;
+ cmd_len = sizeof(struct msm_camera_tz_i2c_cci_write_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_cci_write_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = TZ_I2C_CMD_CCI_WRITE;
+ cmd->sensor_id = sensor_id;
+ cmd->cci_i2c_master = client->cci_client->cci_i2c_master;
+ cmd->sid = client->cci_client->sid;
+ cmd->cid = client->cci_client->cid;
+ cmd->addr = addr;
+ cmd->data = data;
+ cmd->data_type = data_type;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed:, rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+ rc = rsp->rc;
+
+ CDBG("Done: rc=%d, addr=0x%X, data=0x%X\n", rc,
+ addr, data);
+ }
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_cci_util(
+ struct msm_camera_i2c_client *client,
+ uint16_t cci_cmd)
+{
+ int32_t cmd_len, rsp_len;
+ struct msm_camera_tz_i2c_cci_util_req_t *cmd;
+ struct msm_camera_tz_i2c_cci_util_rsp_t *rsp;
+ int32_t rc = 0;
+ struct qseecom_handle *ta_qseecom_handle;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+
+ if ((client == NULL) ||
+ (sensor_id < 0) ||
+ (sensor_id >= MAX_CAMERAS)) {
+ pr_err("%s:%d - Bad parameters\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, cci_cmd=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ cci_cmd);
+
+ ta_qseecom_handle = sensor_info[sensor_id].ta_qseecom_handle;
+ cmd_len = sizeof(struct msm_camera_tz_i2c_cci_util_req_t);
+ rsp_len = sizeof(struct msm_camera_tz_i2c_cci_util_rsp_t);
+
+ rc = get_cmd_rsp_buffers(ta_qseecom_handle,
+ (void **)&cmd, &cmd_len, (void **)&rsp, &rsp_len);
+ if (!rc) {
+ cmd->cmd_id = TZ_I2C_CMD_CCI_UTIL;
+ cmd->sensor_id = sensor_id;
+ cmd->cci_i2c_master = client->cci_client->cci_i2c_master;
+ cmd->sid = client->cci_client->sid;
+ cmd->cid = client->cci_client->cid;
+ cmd->cci_cmd = cci_cmd;
+
+ rc = qseecom_send_command(ta_qseecom_handle,
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (rc < 0) {
+ pr_err("%s:%d - Failed: rc=%d\n",
+ __func__, __LINE__,
+ rc);
+ return rc;
+ }
+ rc = rsp->rc;
+ CDBG("Done: rc=%d, cci_cmd=%d\n", rc, cci_cmd);
+ }
+ return rc;
+}
+
+static int32_t msm_camera_tz_i2c_ta_probe(
+ struct msm_camera_i2c_client *client)
+{
+ int32_t sensor_id = -1;
+
+ CDBG("Enter\n");
+ sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+ if ((sensor_id >= 0) && sensor_info[sensor_id].ta_enabled
+ && msm_camera_tz_i2c_ctrl.lock_ready) {
+ mutex_lock(&msm_camera_tz_i2c_ctrl.lock);
+ return sensor_id;
+ }
+ return -EINVAL;
+}
+
+static int32_t msm_camera_tz_i2c_ta_done(void)
+{
+ int32_t rc = 0;
+
+ CDBG("Enter\n");
+ if (msm_camera_tz_i2c_ctrl.lock_ready)
+ mutex_unlock(&msm_camera_tz_i2c_ctrl.lock);
+ return rc;
+}
+
+int32_t msm_camera_tz_i2c_power_up(
+ struct msm_camera_i2c_client *client)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+
+ if (!msm_camera_tz_i2c_ctrl.lock_ready) {
+ msm_camera_tz_i2c_ctrl.lock_ready = 1;
+ mutex_init(&msm_camera_tz_i2c_ctrl.lock);
+ }
+
+ CDBG("Enter (sensor_id=%d)\n", sensor_id);
+ if (sensor_id >= 0) {
+ ktime_t startTime;
+
+ mutex_lock(&msm_camera_tz_i2c_ctrl.lock);
+ if (msm_camera_tz_i2c_ctrl.secure_mode) {
+ mutex_unlock(&msm_camera_tz_i2c_ctrl.lock);
+ return rc;
+ }
+ startTime = ktime_get();
+
+ CDBG("Switch to secure mode (secure sensor=%d)\n",
+ sensor_id);
+ /* Start the TA */
+ if ((sensor_info[sensor_id].ta_qseecom_handle == NULL)
+ && (sensor_info[sensor_id].ta_name != NULL) &&
+ ('\0' != sensor_info[sensor_id].ta_name[0])) {
+ uint32_t if_version_maj = 0;
+ uint32_t if_version_min = 0;
+
+ sensor_info[sensor_id].ta_enabled = 0;
+ rc = qseecom_start_app(
+ &sensor_info[sensor_id].ta_qseecom_handle,
+ (char *)sensor_info[sensor_id].ta_name,
+ QSEECOM_SBUFF_SIZE);
+ if (!rc) {
+ rc = msm_camera_tz_i2c_ta_get_if_version(
+ sensor_info[sensor_id].
+ ta_qseecom_handle,
+ &if_version_maj, &if_version_min);
+ }
+
+ if (!rc) {
+ if (if_version_maj != TA_IF_VERSION_MAJ) {
+ CDBG("TA ver mismatch %d.%d != %d.%d\n",
+ if_version_maj, if_version_min,
+ TA_IF_VERSION_MAJ,
+ TA_IF_VERSION_MIN);
+ rc = qseecom_shutdown_app(
+ &sensor_info[sensor_id].
+ ta_qseecom_handle);
+ sensor_info[sensor_id].ta_qseecom_handle
+ = EMPTY_QSEECOM_HANDLE;
+ rc = -EFAULT;
+ } else {
+ uint32_t sensor_secure = 0;
+ /*Notify TA & get sensor secure status*/
+ rc = msm_camera_tz_i2c_ta_power_up(
+ sensor_info[sensor_id].
+ ta_qseecom_handle,
+ sensor_id,
+ &sensor_secure);
+ if (!rc && sensor_secure)
+ /* Sensor validated by TA*/
+ sensor_info[sensor_id].
+ ta_enabled = 1;
+ else {
+ qseecom_shutdown_app(
+ &sensor_info[sensor_id].
+ ta_qseecom_handle);
+ sensor_info[sensor_id].
+ ta_qseecom_handle
+ = EMPTY_QSEECOM_HANDLE;
+ rc = -EFAULT;
+ }
+ }
+ }
+ }
+ CDBG("Init TA %s - %s(%d) - %llu\n",
+ sensor_info[sensor_id].ta_name,
+ (sensor_info[sensor_id].ta_enabled)?"Ok" :
+ "Failed", rc, ktime_us_delta(ktime_get(),
+ startTime));
+ if (!rc)
+ msm_camera_tz_i2c_ctrl.secure_mode++;
+ mutex_unlock(&msm_camera_tz_i2c_ctrl.lock);
+ }
+ return rc;
+}
+
+int32_t msm_camera_tz_i2c_power_down(
+ struct msm_camera_i2c_client *client)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_is_sensor_secure(client);
+
+ if (!msm_camera_tz_i2c_ctrl.lock_ready) {
+ msm_camera_tz_i2c_ctrl.lock_ready = 1;
+ mutex_init(&msm_camera_tz_i2c_ctrl.lock);
+ }
+
+ CDBG("Enter (sensor_id=%d)\n", sensor_id);
+ if ((sensor_id >= 0) && (msm_camera_tz_i2c_ctrl.secure_mode != 0)) {
+ mutex_lock(&msm_camera_tz_i2c_ctrl.lock);
+ if (msm_camera_tz_i2c_ctrl.secure_mode == 1) {
+ ktime_t startTime = ktime_get();
+
+ CDBG("Switch to non-secure mode (secure sensor=%d)\n",
+ sensor_id);
+ /* Shutdown the TA */
+ if (sensor_info[sensor_id].ta_qseecom_handle != NULL) {
+ msm_camera_tz_i2c_ta_power_down(
+ sensor_info[sensor_id].
+ ta_qseecom_handle,
+ sensor_id);
+ rc = qseecom_shutdown_app(&sensor_info[
+ sensor_id].ta_qseecom_handle);
+ sensor_info[sensor_id].ta_qseecom_handle
+ = EMPTY_QSEECOM_HANDLE;
+ }
+ CDBG("Unload TA %s - %s(%d) - %llu\n",
+ sensor_info[sensor_id].ta_name,
+ (!rc)?"Ok":"Failed", rc,
+ ktime_us_delta(ktime_get(), startTime));
+ }
+ msm_camera_tz_i2c_ctrl.secure_mode--;
+ mutex_unlock(&msm_camera_tz_i2c_ctrl.lock);
+ }
+ return rc;
+}
+
+int32_t msm_camera_tz_i2c_register_sensor(
+ void *s_ctrl_p)
+{
+ struct msm_sensor_ctrl_t *s_ctrl = (struct msm_sensor_ctrl_t *)s_ctrl_p;
+
+ if (s_ctrl == NULL) {
+ pr_err("%s:%d - invalid parameter)\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (s_ctrl->id >= MAX_CAMERAS) {
+ pr_err("%s:%d - invalid ID: %d\n",
+ __func__, __LINE__, s_ctrl->id);
+ return -EINVAL;
+ }
+
+ CDBG("id=%d, client=%p\n", s_ctrl->id, s_ctrl);
+ sensor_info[s_ctrl->id].s_ctrl = s_ctrl;
+ sensor_info[s_ctrl->id].secure = s_ctrl->is_secure;
+ return 0;
+}
+
+int32_t msm_camera_tz_i2c_read(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, addr=0x%08X\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ addr);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_read(
+ client, addr, data, data_type);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_read, client, addr, data, data_type);
+}
+
+int32_t msm_camera_tz_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, addr=0x%08X, num=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ addr,
+ num_byte);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, TZ_I2C_CMD_CCI_READ_SEQ);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_read_seq, client, addr, data, num_byte);
+}
+
+int32_t msm_camera_tz_i2c_write(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, addr=0x%08X\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ addr);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_write(
+ client, addr, data, data_type);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write, client, addr, data, data_type);
+}
+
+int32_t msm_camera_tz_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint8_t *data, uint32_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, addr=0x%08X, num=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid,
+ addr,
+ num_byte);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, TZ_I2C_CMD_CCI_WRITE_SEQ);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_seq, client, addr, data, num_byte);
+}
+
+int32_t msm_camera_tz_i2c_write_table_async(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, TZ_I2C_CMD_CCI_WRITE_TABLE_ASYNC);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_table_async, client, write_setting);
+}
+
+int32_t msm_camera_tz_i2c_write_table_sync(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, TZ_I2C_CMD_CCI_WRITE_TABLE_SYNC);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_table_sync, client, write_setting);
+}
+
+int32_t msm_camera_tz_i2c_write_table_sync_block(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, TZ_I2C_CMD_CCI_WRITE_TABLE_SYNC_BLOCK);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_table_sync_block, client,
+ write_setting);
+}
+
+int32_t msm_camera_tz_i2c_write_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, TZ_I2C_CMD_CCI_WRITE_TABLE);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_table, client, write_setting);
+}
+
+int32_t msm_camera_tz_i2c_write_seq_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, TZ_I2C_CMD_CCI_WRITE_SEQ_TABLE);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_seq_table, client, write_setting);
+}
+
+int32_t msm_camera_tz_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, TZ_I2C_CMD_CCI_WRITE_TABLE_W_MICRODELAY);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_table_w_microdelay, client,
+ write_setting);
+}
+
+int32_t msm_camera_tz_i2c_poll(struct msm_camera_i2c_client *client,
+ uint32_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, TZ_I2C_CMD_CCI_POLL);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_poll, client, addr, data, data_type);
+}
+
+int32_t msm_camera_tz_i2c_write_conf_tbl(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_generic(
+ client, TZ_I2C_CMD_CCI_WRITE_CONF_TBL);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_camera_cci_i2c_write_conf_tbl, client, reg_conf_tbl, size,
+ data_type);
+}
+
+int32_t msm_sensor_tz_i2c_util(struct msm_camera_i2c_client *client,
+ uint16_t cci_cmd)
+{
+ int32_t rc = -EFAULT;
+ int32_t sensor_id = msm_camera_tz_i2c_ta_probe(client);
+
+ CDBG("Sensor=%d, MS=%d, SID=%d, CID=%d, cci_cmd=%d\n",
+ sensor_id,
+ client->cci_client->cci_i2c_master,
+ client->cci_client->sid,
+ client->cci_client->cid, cci_cmd);
+
+ if (sensor_id >= 0) {
+ rc = msm_camera_tz_i2c_ta_cci_util(client, cci_cmd);
+ msm_camera_tz_i2c_ta_done();
+ }
+ return TZ_I2C_FN_RETURN(rc,
+ msm_sensor_cci_i2c_util, client, cci_cmd);
+}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
index 22d90a2baf7d..e1143c356721 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -21,6 +21,9 @@
#undef CDBG
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl;
+static struct msm_camera_i2c_fn_t msm_sensor_secure_func_tbl;
+
static void msm_sensor_adjust_mclk(struct msm_camera_power_ctrl_t *ctrl)
{
int idx;
@@ -132,6 +135,11 @@ int msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl)
__func__, __LINE__, power_info, sensor_i2c_client);
return -EINVAL;
}
+
+ /* Power down secure session if it exist*/
+ if (s_ctrl->is_secure)
+ msm_camera_tz_i2c_power_down(sensor_i2c_client);
+
return msm_camera_power_down(power_info, sensor_device_type,
sensor_i2c_client);
}
@@ -170,7 +178,27 @@ int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
if (s_ctrl->set_mclk_23880000)
msm_sensor_adjust_mclk(power_info);
+ CDBG("Sensor %d tagged as %s\n", s_ctrl->id,
+ (s_ctrl->is_secure)?"SECURE":"NON-SECURE");
+
for (retry = 0; retry < 3; retry++) {
+ if (s_ctrl->is_secure) {
+ rc = msm_camera_tz_i2c_power_up(sensor_i2c_client);
+ if (rc < 0) {
+#ifdef CONFIG_MSM_SEC_CCI_DEBUG
+ CDBG("Secure Sensor %d use cci\n", s_ctrl->id);
+ /* session is not secure */
+ s_ctrl->sensor_i2c_client->i2c_func_tbl =
+ &msm_sensor_cci_func_tbl;
+#else /* CONFIG_MSM_SEC_CCI_DEBUG */
+ return rc;
+#endif /* CONFIG_MSM_SEC_CCI_DEBUG */
+ } else {
+ /* session is secure */
+ s_ctrl->sensor_i2c_client->i2c_func_tbl =
+ &msm_sensor_secure_func_tbl;
+ }
+ }
rc = msm_camera_power_up(power_info, s_ctrl->sensor_device_type,
sensor_i2c_client);
if (rc < 0)
@@ -1433,6 +1461,21 @@ static struct msm_camera_i2c_fn_t msm_sensor_qup_func_tbl = {
.i2c_write_table_sync_block = msm_camera_qup_i2c_write_table,
};
+static struct msm_camera_i2c_fn_t msm_sensor_secure_func_tbl = {
+ .i2c_read = msm_camera_tz_i2c_read,
+ .i2c_read_seq = msm_camera_tz_i2c_read_seq,
+ .i2c_write = msm_camera_tz_i2c_write,
+ .i2c_write_table = msm_camera_tz_i2c_write_table,
+ .i2c_write_seq_table = msm_camera_tz_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_tz_i2c_write_table_w_microdelay,
+ .i2c_util = msm_sensor_tz_i2c_util,
+ .i2c_write_conf_tbl = msm_camera_tz_i2c_write_conf_tbl,
+ .i2c_write_table_async = msm_camera_tz_i2c_write_table_async,
+ .i2c_write_table_sync = msm_camera_tz_i2c_write_table_sync,
+ .i2c_write_table_sync_block = msm_camera_tz_i2c_write_table_sync_block,
+};
+
int32_t msm_sensor_init_default_params(struct msm_sensor_ctrl_t *s_ctrl)
{
struct msm_camera_cci_client *cci_client = NULL;
@@ -1466,6 +1509,9 @@ int32_t msm_sensor_init_default_params(struct msm_sensor_ctrl_t *s_ctrl)
/* Get CCI subdev */
cci_client->cci_subdev = msm_cci_get_subdev();
+ if (s_ctrl->is_secure)
+ msm_camera_tz_i2c_register_sensor((void *)s_ctrl);
+
/* Update CCI / I2C function table */
if (!s_ctrl->sensor_i2c_client->i2c_func_tbl)
s_ctrl->sensor_i2c_client->i2c_func_tbl =
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h
index bd12588eada9..5d57ec8c28ff 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+/* 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
@@ -88,6 +88,7 @@ struct msm_sensor_ctrl_t {
enum msm_camera_stream_type_t camera_stream_type;
uint32_t set_mclk_23880000;
uint8_t is_csid_tg_mode;
+ uint32_t is_secure;
};
int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp);
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 02b83c969958..43aadffa2983 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
@@ -992,7 +992,7 @@ CSID_TG:
}
/* Update sensor mount angle and position in media entity flag */
is_yuv = (slave_info->output_format == MSM_SENSOR_YCBCR) ? 1 : 0;
- mount_pos = is_yuv << 25 |
+ mount_pos = ((s_ctrl->is_secure & 0x1) << 26) | is_yuv << 25 |
(s_ctrl->sensordata->sensor_info->position << 16) |
((s_ctrl->sensordata->
sensor_info->sensor_mount_angle / 90) << 8);
@@ -1079,6 +1079,16 @@ static int32_t msm_sensor_driver_get_dt_data(struct msm_sensor_ctrl_t *s_ctrl)
goto FREE_VREG_DATA;
}
+ /* Get custom mode */
+ rc = of_property_read_u32(of_node, "qcom,secure",
+ &s_ctrl->is_secure);
+ CDBG("qcom,secure = %d, rc %d", s_ctrl->is_secure, rc);
+ if (rc < 0) {
+ /* Set default to non-secure mode */
+ s_ctrl->is_secure = 0;
+ rc = 0;
+ }
+
/* Get CCI master */
rc = of_property_read_u32(of_node, "qcom,cci-master",
&s_ctrl->cci_i2c_master);
diff --git a/drivers/media/platform/msm/sde/Kconfig b/drivers/media/platform/msm/sde/Kconfig
index fcd461bb1167..85f5f4257ddb 100644
--- a/drivers/media/platform/msm/sde/Kconfig
+++ b/drivers/media/platform/msm/sde/Kconfig
@@ -5,4 +5,13 @@ config MSM_SDE_ROTATOR
select VIDEOBUF2_CORE
select SW_SYNC if SYNC
---help---
- Enable support of V4L2 rotator driver. \ No newline at end of file
+ Enable support of V4L2 rotator driver.
+
+config MSM_SDE_ROTATOR_EVTLOG_DEBUG
+ depends on MSM_SDE_ROTATOR
+ bool "Enable sde rotator debugging"
+ ---help---
+ The SDE rotator debugging provides support to enable rotator debugging
+ features to: Dump rotator registers during driver errors, panic
+ driver during fatal errors and enable some rotator driver logging
+ into an internal buffer (this avoids logging overhead).
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
index b3e81705bdf4..67c6b11329d8 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
@@ -92,6 +92,12 @@ enum sde_bus_clients {
SDE_MAX_BUS_CLIENTS
};
+enum sde_rot_regdump_access {
+ SDE_ROT_REGDUMP_READ,
+ SDE_ROT_REGDUMP_WRITE,
+ SDE_ROT_REGDUMP_MAX
+};
+
struct reg_bus_client {
char name[MAX_CLIENT_NAME_LEN];
short usecase_ndx;
@@ -107,6 +113,21 @@ struct sde_smmu_client {
bool domain_attached;
};
+struct sde_rot_vbif_debug_bus {
+ u32 disable_bus_addr;
+ u32 block_bus_addr;
+ u32 bit_offset;
+ u32 block_cnt;
+ u32 test_pnt_cnt;
+};
+
+struct sde_rot_regdump {
+ char *name;
+ u32 offset;
+ u32 len;
+ enum sde_rot_regdump_access access;
+};
+
struct sde_rot_data_type {
u32 mdss_version;
@@ -140,6 +161,14 @@ struct sde_rot_data_type {
int iommu_attached;
int iommu_ref_cnt;
+
+ struct sde_rot_vbif_debug_bus *nrt_vbif_dbg_bus;
+ u32 nrt_vbif_dbg_bus_size;
+
+ struct sde_rot_regdump *regdump;
+ u32 regdump_size;
+
+ void *sde_rot_hw;
};
int sde_rotator_base_init(struct sde_rot_data_type **pmdata,
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 e3e71936b8e4..3e5cbdecfba4 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -35,6 +35,7 @@
#include "sde_rotator_r1.h"
#include "sde_rotator_r3.h"
#include "sde_rotator_trace.h"
+#include "sde_rotator_debug.h"
/* waiting for hw time out, 3 vsync for 30fps*/
#define ROT_HW_ACQUIRE_TIMEOUT_IN_MS 100
@@ -127,6 +128,7 @@ static int sde_rotator_bus_scale_set_quota(struct sde_rot_bus_data_type *bus,
bus->curr_bw_uc_idx = new_uc_idx;
bus->curr_quota_val = quota;
+ SDEROT_EVTLOG(new_uc_idx, quota);
SDEROT_DBG("uc_idx=%d quota=%llu\n", new_uc_idx, quota);
ATRACE_BEGIN("msm_bus_scale_req_rot");
ret = msm_bus_scale_client_update_request(bus->bus_hdl,
@@ -274,6 +276,7 @@ static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
return;
}
+ SDEROT_EVTLOG(on);
SDEROT_DBG("%s: rotator regulators", on ? "Enable" : "Disable");
ret = sde_rot_enable_vreg(mgr->module_power.vreg_config,
mgr->module_power.num_vreg, on);
@@ -307,6 +310,7 @@ static int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
}
if (changed) {
+ SDEROT_EVTLOG(enable);
SDEROT_DBG("Rotator clk %s\n", enable ? "enable" : "disable");
for (i = 0; i < mgr->num_rot_clk; i++) {
clk = mgr->rot_clk[i].clk;
@@ -394,6 +398,7 @@ static bool sde_rotator_is_work_pending(struct sde_rot_mgr *mgr,
static void sde_rotator_clear_fence(struct sde_rot_entry *entry)
{
if (entry->input_fence) {
+ SDEROT_EVTLOG(entry->input_fence, 1111);
SDEROT_DBG("sys_fence_put i:%p\n", entry->input_fence);
sde_rotator_put_sync_fence(entry->input_fence);
entry->input_fence = NULL;
@@ -404,6 +409,7 @@ static void sde_rotator_clear_fence(struct sde_rot_entry *entry)
if (entry->fenceq && entry->fenceq->timeline)
sde_rotator_resync_timeline(entry->fenceq->timeline);
+ SDEROT_EVTLOG(entry->output_fence, 2222);
SDEROT_DBG("sys_fence_put o:%p\n", entry->output_fence);
sde_rotator_put_sync_fence(entry->output_fence);
entry->output_fence = NULL;
@@ -565,6 +571,7 @@ static struct sde_rot_perf *sde_rotator_find_session(
static void sde_rotator_release_data(struct sde_rot_entry *entry)
{
+ SDEROT_EVTLOG(entry->src_buf.p[0].addr, entry->dst_buf.p[0].addr);
sde_mdp_data_free(&entry->src_buf, true, DMA_TO_DEVICE);
sde_mdp_data_free(&entry->dst_buf, true, DMA_FROM_DEVICE);
}
@@ -719,6 +726,10 @@ static struct sde_rot_hw_resource *sde_rotator_get_hw_resource(
}
}
atomic_inc(&hw->num_active);
+ SDEROT_EVTLOG(atomic_read(&hw->num_active), hw->pending_count,
+ mgr->rdot_limit, entry->perf->rdot_limit,
+ mgr->wrot_limit, entry->perf->wrot_limit,
+ entry->item.session_id, entry->item.sequence_id);
SDEROT_DBG("active=%d pending=%d rdot=%u/%u wrot=%u/%u s:%d.%d\n",
atomic_read(&hw->num_active), hw->pending_count,
mgr->rdot_limit, entry->perf->rdot_limit,
@@ -766,6 +777,8 @@ static void sde_rotator_put_hw_resource(struct sde_rot_queue *queue,
if (hw_res)
wake_up(&hw_res->wait_queue);
}
+ SDEROT_EVTLOG(atomic_read(&hw->num_active), hw->pending_count,
+ entry->item.session_id, entry->item.sequence_id);
SDEROT_DBG("active=%d pending=%d s:%d.%d\n",
atomic_read(&hw->num_active), hw->pending_count,
entry->item.session_id, entry->item.sequence_id);
@@ -1125,6 +1138,15 @@ static void sde_rotator_commit_handler(struct work_struct *work)
mgr = entry->private->mgr;
+ SDEROT_EVTLOG(
+ entry->item.session_id, entry->item.sequence_id,
+ entry->item.src_rect.x, entry->item.src_rect.y,
+ entry->item.src_rect.w, entry->item.src_rect.h,
+ entry->item.dst_rect.x, entry->item.dst_rect.y,
+ entry->item.dst_rect.w, entry->item.dst_rect.h,
+ entry->item.flags,
+ entry->dnsc_factor_w, entry->dnsc_factor_h);
+
SDEDEV_DBG(mgr->device,
"commit handler s:%d.%u src:(%d,%d,%d,%d) dst:(%d,%d,%d,%d) f:0x%x dnsc:%u/%u\n",
entry->item.session_id, entry->item.sequence_id,
@@ -1233,11 +1255,13 @@ static void sde_rotator_done_handler(struct work_struct *work)
entry->item.flags,
entry->dnsc_factor_w, entry->dnsc_factor_h);
+ SDEROT_EVTLOG(entry->item.session_id, 0);
ret = mgr->ops_wait_for_entry(hw, entry);
if (ret) {
SDEROT_ERR("fail to wait for completion %d\n", ret);
atomic_inc(&request->failed_count);
}
+ SDEROT_EVTLOG(entry->item.session_id, 1);
if (entry->item.ts)
entry->item.ts[SDE_ROTATOR_TS_DONE] = ktime_get();
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 dae1b51bfaa8..c609dbd2036e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -22,6 +22,619 @@
#include "sde_rotator_core.h"
#include "sde_rotator_dev.h"
+#ifdef CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG
+#define SDE_EVTLOG_DEFAULT_ENABLE 1
+#else
+#define SDE_EVTLOG_DEFAULT_ENABLE 0
+#endif
+#define SDE_EVTLOG_DEFAULT_PANIC 1
+#define SDE_EVTLOG_DEFAULT_REGDUMP SDE_ROT_DBG_DUMP_IN_MEM
+#define SDE_EVTLOG_DEFAULT_VBIF_DBGBUSDUMP SDE_ROT_DBG_DUMP_IN_MEM
+
+/*
+ * evtlog will print this number of entries when it is called through
+ * sysfs node or panic. This prevents kernel log from evtlog message
+ * flood.
+ */
+#define SDE_ROT_EVTLOG_PRINT_ENTRY 256
+
+/*
+ * evtlog keeps this number of entries in memory for debug purpose. This
+ * number must be greater than print entry to prevent out of bound evtlog
+ * entry array access.
+ */
+#define SDE_ROT_EVTLOG_ENTRY (SDE_ROT_EVTLOG_PRINT_ENTRY * 4)
+#define SDE_ROT_EVTLOG_MAX_DATA 15
+#define SDE_ROT_EVTLOG_BUF_MAX 512
+#define SDE_ROT_EVTLOG_BUF_ALIGN 32
+#define SDE_ROT_DEBUG_BASE_MAX 10
+
+static DEFINE_SPINLOCK(sde_rot_xlock);
+
+/*
+ * tlog - EVTLOG entry structure
+ * @counter - EVTLOG entriy counter
+ * @time - timestamp of EVTLOG entry
+ * @name - function name of EVTLOG entry
+ * @line - line number of EVTLOG entry
+ * @data - EVTLOG data contents
+ * @data_cnt - number of data contents
+ * @pid - pid of current calling thread
+ */
+struct tlog {
+ u32 counter;
+ s64 time;
+ const char *name;
+ int line;
+ u32 data[SDE_ROT_EVTLOG_MAX_DATA];
+ u32 data_cnt;
+ int pid;
+};
+
+/*
+ * sde_rot_dbg_evtlog - EVTLOG debug data structure
+ * @logs - EVTLOG entries
+ * @first - first entry index in the EVTLOG
+ * @last - last entry index in the EVTLOG
+ * @curr - curr entry index in the EVTLOG
+ * @evtlog - EVTLOG debugfs handle
+ * @evtlog_enable - boolean indicates EVTLOG enable/disable
+ * @panic_on_err - boolean indicates issue panic after EVTLOG dump
+ * @enable_reg_dump - control in-log/memory dump for rotator registers
+ * @enable_vbif_dbgbus_dump - control in-log/memory dump for VBIF debug bus
+ * @evtlog_dump_work - schedule work strucutre for timeout handler
+ * @work_dump_reg - storage for register dump control in schedule work
+ * @work_panic - storage for panic control in schedule work
+ * @work_vbif_dbgbus - storage for VBIF debug bus control in schedule work
+ * @nrt_vbif_dbgbus_dump - memory buffer for VBIF debug bus dumping
+ * @reg_dump_array - memory buffer for rotator registers dumping
+ */
+struct sde_rot_dbg_evtlog {
+ struct tlog logs[SDE_ROT_EVTLOG_ENTRY];
+ u32 first;
+ u32 last;
+ u32 curr;
+ struct dentry *evtlog;
+ u32 evtlog_enable;
+ u32 panic_on_err;
+ u32 enable_reg_dump;
+ u32 enable_vbif_dbgbus_dump;
+ struct work_struct evtlog_dump_work;
+ bool work_dump_reg;
+ bool work_panic;
+ bool work_vbif_dbgbus;
+ u32 *nrt_vbif_dbgbus_dump; /* address for the nrt vbif debug bus dump */
+ u32 *reg_dump_array[SDE_ROT_DEBUG_BASE_MAX];
+} sde_rot_dbg_evtlog;
+
+/*
+ * sde_rot_evtlog_is_enabled - helper function for checking EVTLOG
+ * enable/disable
+ * @flag - EVTLOG option flag
+ */
+static inline bool sde_rot_evtlog_is_enabled(u32 flag)
+{
+ return (flag & sde_rot_dbg_evtlog.evtlog_enable) ||
+ (flag == SDE_ROT_EVTLOG_ALL &&
+ sde_rot_dbg_evtlog.evtlog_enable);
+}
+
+/*
+ * __vbif_debug_bus - helper function for VBIF debug bus dump
+ * @head - VBIF debug bus data structure
+ * @vbif_base - VBIF IO mapped address
+ * @dump_addr - output buffer for memory dump option
+ * @in_log - boolean indicates in-log dump option
+ */
+static void __vbif_debug_bus(struct sde_rot_vbif_debug_bus *head,
+ void __iomem *vbif_base, u32 *dump_addr, bool in_log)
+{
+ int i, j;
+ u32 val;
+
+ if (!dump_addr && !in_log)
+ return;
+
+ for (i = 0; i < head->block_cnt; i++) {
+ writel_relaxed(1 << (i + head->bit_offset),
+ vbif_base + head->block_bus_addr);
+ /* make sure that current bus blcok enable */
+ wmb();
+ for (j = 0; j < head->test_pnt_cnt; j++) {
+ writel_relaxed(j, vbif_base + head->block_bus_addr + 4);
+ /* make sure that test point is enabled */
+ wmb();
+ val = readl_relaxed(vbif_base + MMSS_VBIF_TEST_BUS_OUT);
+ if (dump_addr) {
+ *dump_addr++ = head->block_bus_addr;
+ *dump_addr++ = i;
+ *dump_addr++ = j;
+ *dump_addr++ = val;
+ }
+ if (in_log)
+ pr_err("testpoint:%x arb/xin id=%d index=%d val=0x%x\n",
+ head->block_bus_addr, i, j, val);
+ }
+ }
+}
+
+/*
+ * sde_rot_dump_vbif_debug_bus - VBIF debug bus dump
+ * @bus_dump_flag - dump flag controlling in-log/memory dump option
+ * @dump_mem - output buffer for memory dump location
+ */
+static void sde_rot_dump_vbif_debug_bus(u32 bus_dump_flag,
+ u32 **dump_mem)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ bool in_log, in_mem;
+ u32 *dump_addr = NULL;
+ u32 value;
+ struct sde_rot_vbif_debug_bus *head;
+ phys_addr_t phys = 0;
+ int i, list_size = 0;
+ void __iomem *vbif_base;
+ struct sde_rot_vbif_debug_bus *dbg_bus;
+ u32 bus_size;
+
+ pr_info("======== NRT VBIF Debug bus DUMP =========\n");
+ vbif_base = mdata->vbif_nrt_io.base;
+ dbg_bus = mdata->nrt_vbif_dbg_bus;
+ bus_size = mdata->nrt_vbif_dbg_bus_size;
+
+ if (!vbif_base || !dbg_bus || !bus_size)
+ return;
+
+ /* allocate memory for each test point */
+ for (i = 0; i < bus_size; i++) {
+ head = dbg_bus + i;
+ list_size += (head->block_cnt * head->test_pnt_cnt);
+ }
+
+ /* 4 bytes * 4 entries for each test point*/
+ list_size *= 16;
+
+ in_log = (bus_dump_flag & SDE_ROT_DBG_DUMP_IN_LOG);
+ in_mem = (bus_dump_flag & SDE_ROT_DBG_DUMP_IN_MEM);
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(&mdata->pdev->dev,
+ list_size, &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ pr_info("%s: start_addr:0x%pK end_addr:0x%pK\n",
+ __func__, dump_addr, dump_addr + list_size);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: allocation fails\n");
+ }
+ }
+
+ sde_smmu_ctrl(1);
+
+ value = readl_relaxed(vbif_base + MMSS_VBIF_CLKON);
+ writel_relaxed(value | BIT(1), vbif_base + MMSS_VBIF_CLKON);
+
+ /* make sure that vbif core is on */
+ wmb();
+
+ for (i = 0; i < bus_size; i++) {
+ head = dbg_bus + i;
+
+ writel_relaxed(0, vbif_base + head->disable_bus_addr);
+ writel_relaxed(BIT(0), vbif_base + MMSS_VBIF_TEST_BUS_OUT_CTRL);
+ /* make sure that other bus is off */
+ wmb();
+
+ __vbif_debug_bus(head, vbif_base, dump_addr, in_log);
+ if (dump_addr)
+ dump_addr += (head->block_cnt * head->test_pnt_cnt * 4);
+ }
+
+ sde_smmu_ctrl(0);
+
+ pr_info("========End VBIF Debug bus=========\n");
+}
+
+/*
+ * sde_rot_dump_reg - helper function for dumping rotator register set content
+ * @dump_name - register set name
+ * @reg_dump_flag - dumping flag controlling in-log/memory dump location
+ * @addr - starting address offset for dumping
+ * @len - range of the register set
+ * @dump_mem - output buffer for memory dump location option
+ */
+void sde_rot_dump_reg(const char *dump_name, u32 reg_dump_flag, u32 addr,
+ int len, u32 **dump_mem)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ bool in_log, in_mem;
+ u32 *dump_addr = NULL;
+ phys_addr_t phys = 0;
+ int i;
+
+ in_log = (reg_dump_flag & SDE_ROT_DBG_DUMP_IN_LOG);
+ in_mem = (reg_dump_flag & SDE_ROT_DBG_DUMP_IN_MEM);
+
+ pr_debug("reg_dump_flag=%d in_log=%d in_mem=%d\n",
+ reg_dump_flag, in_log, in_mem);
+
+ if (len % 16)
+ len += 16;
+ len /= 16;
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(&mdata->pdev->dev,
+ len * 16, &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ pr_info("%s: start_addr:0x%p end_addr:0x%p reg_addr=0x%X\n",
+ dump_name, dump_addr, dump_addr + (u32)len * 16,
+ addr);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: kzalloc fails!\n");
+ }
+ }
+
+
+ for (i = 0; i < len; i++) {
+ u32 x0, x4, x8, xc;
+
+ x0 = readl_relaxed(mdata->sde_io.base + addr+0x0);
+ x4 = readl_relaxed(mdata->sde_io.base + addr+0x4);
+ x8 = readl_relaxed(mdata->sde_io.base + addr+0x8);
+ xc = readl_relaxed(mdata->sde_io.base + addr+0xc);
+
+ if (in_log)
+ pr_info("0x%08X : %08x %08x %08x %08x\n",
+ addr, x0, x4, x8, xc);
+
+ if (dump_addr && in_mem) {
+ dump_addr[i*4] = x0;
+ dump_addr[i*4 + 1] = x4;
+ dump_addr[i*4 + 2] = x8;
+ dump_addr[i*4 + 3] = xc;
+ }
+
+ addr += 16;
+ }
+}
+
+/*
+ * sde_rot_dump_reg_all - dumping all SDE rotator registers
+ */
+static void sde_rot_dump_reg_all(void)
+{
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+ struct sde_rot_regdump *head, *regdump;
+ u32 regdump_size;
+ int i;
+
+ regdump = mdata->regdump;
+ regdump_size = mdata->regdump_size;
+
+ if (!regdump || !regdump_size)
+ return;
+
+ /* Enable clock to rotator if not yet enabled */
+ sde_smmu_ctrl(1);
+
+ for (i = 0; (i < regdump_size) && (i < SDE_ROT_DEBUG_BASE_MAX); i++) {
+ head = &regdump[i];
+
+ if (head->access == SDE_ROT_REGDUMP_WRITE) {
+ writel_relaxed(1, mdata->sde_io.base + head->offset);
+ /* Make sure write go through */
+ wmb();
+ } else {
+ sde_rot_dump_reg(head->name,
+ sde_rot_dbg_evtlog.enable_reg_dump,
+ head->offset, head->len,
+ &sde_rot_dbg_evtlog.reg_dump_array[i]);
+ }
+ }
+
+ /* Disable rotator clock */
+ sde_smmu_ctrl(0);
+}
+
+/*
+ * __sde_rot_evtlog_dump_calc_range - calculate dump range for EVTLOG
+ */
+static bool __sde_rot_evtlog_dump_calc_range(void)
+{
+ static u32 next;
+ bool need_dump = true;
+ unsigned long flags;
+ struct sde_rot_dbg_evtlog *evtlog = &sde_rot_dbg_evtlog;
+
+ spin_lock_irqsave(&sde_rot_xlock, flags);
+
+ evtlog->first = next;
+
+ if (evtlog->last == evtlog->first) {
+ need_dump = false;
+ goto dump_exit;
+ }
+
+ if (evtlog->last < evtlog->first) {
+ evtlog->first %= SDE_ROT_EVTLOG_ENTRY;
+ if (evtlog->last < evtlog->first)
+ evtlog->last += SDE_ROT_EVTLOG_ENTRY;
+ }
+
+ if ((evtlog->last - evtlog->first) > SDE_ROT_EVTLOG_PRINT_ENTRY) {
+ pr_warn("evtlog buffer overflow before dump: %d\n",
+ evtlog->last - evtlog->first);
+ evtlog->first = evtlog->last - SDE_ROT_EVTLOG_PRINT_ENTRY;
+ }
+ next = evtlog->first + 1;
+
+dump_exit:
+ spin_unlock_irqrestore(&sde_rot_xlock, flags);
+
+ return need_dump;
+}
+
+/*
+ * sde_rot_evtlog_dump_entry - helper function for EVTLOG content dumping
+ * @evtlog_buf: EVTLOG dump output buffer
+ * @evtlog_buf_size: EVTLOG output buffer size
+ */
+static ssize_t sde_rot_evtlog_dump_entry(char *evtlog_buf,
+ ssize_t evtlog_buf_size)
+{
+ int i;
+ ssize_t off = 0;
+ struct tlog *log, *prev_log;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sde_rot_xlock, flags);
+
+ log = &sde_rot_dbg_evtlog.logs[sde_rot_dbg_evtlog.first %
+ SDE_ROT_EVTLOG_ENTRY];
+
+ prev_log = &sde_rot_dbg_evtlog.logs[(sde_rot_dbg_evtlog.first - 1) %
+ SDE_ROT_EVTLOG_ENTRY];
+
+ off = snprintf((evtlog_buf + off), (evtlog_buf_size - off), "%s:%-4d",
+ log->name, log->line);
+
+ if (off < SDE_ROT_EVTLOG_BUF_ALIGN) {
+ memset((evtlog_buf + off), 0x20,
+ (SDE_ROT_EVTLOG_BUF_ALIGN - off));
+ off = SDE_ROT_EVTLOG_BUF_ALIGN;
+ }
+
+ off += snprintf((evtlog_buf + off), (evtlog_buf_size - off),
+ "=>[%-8d:%-11llu:%9llu][%-4d]:", sde_rot_dbg_evtlog.first,
+ log->time, (log->time - prev_log->time), log->pid);
+
+ for (i = 0; i < log->data_cnt; i++)
+ off += snprintf((evtlog_buf + off), (evtlog_buf_size - off),
+ "%x ", log->data[i]);
+
+ off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), "\n");
+
+ spin_unlock_irqrestore(&sde_rot_xlock, flags);
+
+ return off;
+}
+
+/*
+ * sde_rot_evtlog_dump_all - Dumping all content in EVTLOG buffer
+ */
+static void sde_rot_evtlog_dump_all(void)
+{
+ char evtlog_buf[SDE_ROT_EVTLOG_BUF_MAX];
+
+ while (__sde_rot_evtlog_dump_calc_range()) {
+ sde_rot_evtlog_dump_entry(evtlog_buf, SDE_ROT_EVTLOG_BUF_MAX);
+ pr_info("%s", evtlog_buf);
+ }
+}
+
+/*
+ * sde_rot_evtlog_dump_open - debugfs open handler for evtlog dump
+ * @inode: debugfs inode
+ * @file: file handler
+ */
+static int sde_rot_evtlog_dump_open(struct inode *inode, struct file *file)
+{
+ /* non-seekable */
+ file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/*
+ * sde_rot_evtlog_dump_read - debugfs read handler for evtlog dump
+ * @file: file handler
+ * @buff: user buffer content for debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_rot_evtlog_dump_read(struct file *file, char __user *buff,
+ size_t count, loff_t *ppos)
+{
+ ssize_t len = 0;
+ char evtlog_buf[SDE_ROT_EVTLOG_BUF_MAX];
+
+ if (__sde_rot_evtlog_dump_calc_range()) {
+ len = sde_rot_evtlog_dump_entry(evtlog_buf,
+ SDE_ROT_EVTLOG_BUF_MAX);
+ if (copy_to_user(buff, evtlog_buf, len))
+ return -EFAULT;
+ *ppos += len;
+ }
+
+ return len;
+}
+
+/*
+ * sde_rot_evtlog_dump_write - debugfs write handler for evtlog dump
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_rot_evtlog_dump_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ sde_rot_evtlog_dump_all();
+
+ sde_rot_dump_reg_all();
+
+ if (sde_rot_dbg_evtlog.panic_on_err)
+ panic("evtlog_dump_write");
+
+ return count;
+}
+
+/*
+ * sde_rot_evtlog_dump_helper - helper function for evtlog dump
+ * @dead: boolean indicates panic after dump
+ * @panic_name: Panic signature name show up in log
+ * @dump_rot: boolean indicates rotator register dump
+ * @dump_vbif_debug_bus: boolean indicates VBIF debug bus dump
+ */
+static void sde_rot_evtlog_dump_helper(bool dead, const char *panic_name,
+ bool dump_rot, bool dump_vbif_debug_bus)
+{
+ sde_rot_evtlog_dump_all();
+
+ if (dump_rot)
+ sde_rot_dump_reg_all();
+
+ if (dump_vbif_debug_bus)
+ sde_rot_dump_vbif_debug_bus(
+ sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump,
+ &sde_rot_dbg_evtlog.nrt_vbif_dbgbus_dump);
+
+ if (dead)
+ panic(panic_name);
+}
+
+/*
+ * sde_rot_evtlog_debug_work - schedule work function for evtlog dump
+ * @work: schedule work structure
+ */
+static void sde_rot_evtlog_debug_work(struct work_struct *work)
+{
+ sde_rot_evtlog_dump_helper(
+ sde_rot_dbg_evtlog.work_panic,
+ "evtlog_workitem",
+ sde_rot_dbg_evtlog.work_dump_reg,
+ sde_rot_dbg_evtlog.work_vbif_dbgbus);
+}
+
+/*
+ * sde_rot_dump_panic - Issue evtlog dump and generic panic
+ */
+void sde_rot_dump_panic(void)
+{
+ sde_rot_evtlog_dump_all();
+ sde_rot_dump_reg_all();
+
+ panic("sde_rotator");
+}
+
+/*
+ * sde_rot_evtlog_tout_handler - log dump timeout handler
+ * @queue: boolean indicate putting log dump into queue
+ * @name: function name having timeout
+ */
+void sde_rot_evtlog_tout_handler(bool queue, const char *name, ...)
+{
+ int i;
+ bool dead = false;
+ bool dump_rot = false;
+ bool dump_vbif_dbgbus = false;
+ char *blk_name = NULL;
+ va_list args;
+
+ if (!sde_rot_evtlog_is_enabled(SDE_ROT_EVTLOG_DEFAULT))
+ return;
+
+ if (queue && work_pending(&sde_rot_dbg_evtlog.evtlog_dump_work))
+ return;
+
+ va_start(args, name);
+ for (i = 0; i < SDE_ROT_EVTLOG_MAX_DATA; i++) {
+ blk_name = va_arg(args, char*);
+ if (IS_ERR_OR_NULL(blk_name))
+ break;
+
+ if (!strcmp(blk_name, "rot"))
+ dump_rot = true;
+
+ if (!strcmp(blk_name, "vbif_dbg_bus"))
+ dump_vbif_dbgbus = true;
+
+ if (!strcmp(blk_name, "panic"))
+ dead = true;
+ }
+ va_end(args);
+
+ if (queue) {
+ /* schedule work to dump later */
+ sde_rot_dbg_evtlog.work_panic = dead;
+ sde_rot_dbg_evtlog.work_dump_reg = dump_rot;
+ sde_rot_dbg_evtlog.work_vbif_dbgbus = dump_vbif_dbgbus;
+ schedule_work(&sde_rot_dbg_evtlog.evtlog_dump_work);
+ } else {
+ sde_rot_evtlog_dump_helper(dead, name, dump_rot,
+ dump_vbif_dbgbus);
+ }
+}
+
+/*
+ * sde_rot_evtlog - log contents into memory for dump analysis
+ * @name: Name of function calling evtlog
+ * @line: line number of calling function
+ * @flag: Log control flag
+ */
+void sde_rot_evtlog(const char *name, int line, int flag, ...)
+{
+ unsigned long flags;
+ int i, val = 0;
+ va_list args;
+ struct tlog *log;
+
+ if (!sde_rot_evtlog_is_enabled(flag))
+ return;
+
+ spin_lock_irqsave(&sde_rot_xlock, flags);
+ log = &sde_rot_dbg_evtlog.logs[sde_rot_dbg_evtlog.curr];
+ log->time = ktime_to_us(ktime_get());
+ log->name = name;
+ log->line = line;
+ log->data_cnt = 0;
+ log->pid = current->pid;
+
+ va_start(args, flag);
+ for (i = 0; i < SDE_ROT_EVTLOG_MAX_DATA; i++) {
+
+ val = va_arg(args, int);
+ if (val == SDE_ROT_DATA_LIMITER)
+ break;
+
+ log->data[i] = val;
+ }
+ va_end(args);
+ log->data_cnt = i;
+ sde_rot_dbg_evtlog.curr =
+ (sde_rot_dbg_evtlog.curr + 1) % SDE_ROT_EVTLOG_ENTRY;
+ sde_rot_dbg_evtlog.last++;
+
+ spin_unlock_irqrestore(&sde_rot_xlock, flags);
+}
+
/*
* sde_rotator_stat_show - Show statistics on read to this debugfs file
* @s: Pointer to sequence file structure
@@ -249,6 +862,58 @@ static int sde_rotator_core_create_debugfs(
return 0;
}
+static const struct file_operations sde_rot_evtlog_fops = {
+ .open = sde_rot_evtlog_dump_open,
+ .read = sde_rot_evtlog_dump_read,
+ .write = sde_rot_evtlog_dump_write,
+};
+
+static int sde_rotator_evtlog_create_debugfs(
+ struct sde_rot_mgr *mgr,
+ struct dentry *debugfs_root)
+{
+ int i;
+
+ sde_rot_dbg_evtlog.evtlog = debugfs_create_dir("evtlog", debugfs_root);
+ if (IS_ERR_OR_NULL(sde_rot_dbg_evtlog.evtlog)) {
+ pr_err("debugfs_create_dir fail, error %ld\n",
+ PTR_ERR(sde_rot_dbg_evtlog.evtlog));
+ sde_rot_dbg_evtlog.evtlog = NULL;
+ return -ENODEV;
+ }
+
+ INIT_WORK(&sde_rot_dbg_evtlog.evtlog_dump_work,
+ sde_rot_evtlog_debug_work);
+ sde_rot_dbg_evtlog.work_panic = false;
+
+ for (i = 0; i < SDE_ROT_EVTLOG_ENTRY; i++)
+ sde_rot_dbg_evtlog.logs[i].counter = i;
+
+ debugfs_create_file("dump", 0644, sde_rot_dbg_evtlog.evtlog, NULL,
+ &sde_rot_evtlog_fops);
+ debugfs_create_u32("enable", 0644, sde_rot_dbg_evtlog.evtlog,
+ &sde_rot_dbg_evtlog.evtlog_enable);
+ debugfs_create_u32("panic", 0644, sde_rot_dbg_evtlog.evtlog,
+ &sde_rot_dbg_evtlog.panic_on_err);
+ debugfs_create_u32("reg_dump", 0644, sde_rot_dbg_evtlog.evtlog,
+ &sde_rot_dbg_evtlog.enable_reg_dump);
+ debugfs_create_u32("vbif_dbgbus_dump", 0644, sde_rot_dbg_evtlog.evtlog,
+ &sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump);
+
+ sde_rot_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE;
+ sde_rot_dbg_evtlog.panic_on_err = SDE_EVTLOG_DEFAULT_PANIC;
+ sde_rot_dbg_evtlog.enable_reg_dump = SDE_EVTLOG_DEFAULT_REGDUMP;
+ sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump =
+ SDE_EVTLOG_DEFAULT_VBIF_DBGBUSDUMP;
+
+ pr_info("evtlog_status: enable:%d, panic:%d, dump:%d\n",
+ sde_rot_dbg_evtlog.evtlog_enable,
+ sde_rot_dbg_evtlog.panic_on_err,
+ sde_rot_dbg_evtlog.enable_reg_dump);
+
+ return 0;
+}
+
/*
* struct sde_rotator_stat_ops - processed statistics file operations
*/
@@ -335,6 +1000,12 @@ struct dentry *sde_rotator_create_debugfs(
return NULL;
}
+ if (sde_rotator_evtlog_create_debugfs(rot_dev->mgr, debugfs_root)) {
+ SDEROT_ERR("fail create evtlog debugfs\n");
+ debugfs_remove_recursive(debugfs_root);
+ return NULL;
+ }
+
return debugfs_root;
}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
index 2ed1b759f3e9..dcda54274fad 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
@@ -16,6 +16,32 @@
#include <linux/types.h>
#include <linux/dcache.h>
+#define SDE_ROT_DATA_LIMITER (-1)
+#define SDE_ROT_EVTLOG_TOUT_DATA_LIMITER (NULL)
+
+enum sde_rot_dbg_reg_dump_flag {
+ SDE_ROT_DBG_DUMP_IN_LOG = BIT(0),
+ SDE_ROT_DBG_DUMP_IN_MEM = BIT(1),
+};
+
+enum sde_rot_dbg_evtlog_flag {
+ SDE_ROT_EVTLOG_DEFAULT = BIT(0),
+ SDE_ROT_EVTLOG_IOMMU = BIT(1),
+ SDE_ROT_EVTLOG_DBG = BIT(6),
+ SDE_ROT_EVTLOG_ALL = BIT(7)
+};
+
+#define SDEROT_EVTLOG(...) sde_rot_evtlog(__func__, __LINE__, \
+ SDE_ROT_EVTLOG_DEFAULT, ##__VA_ARGS__, SDE_ROT_DATA_LIMITER)
+
+#define SDEROT_EVTLOG_TOUT_HANDLER(...) \
+ sde_rot_evtlog_tout_handler(false, __func__, ##__VA_ARGS__, \
+ SDE_ROT_EVTLOG_TOUT_DATA_LIMITER)
+
+void sde_rot_evtlog(const char *name, int line, int flag, ...);
+void sde_rot_dump_panic(void);
+void sde_rot_evtlog_tout_handler(bool queue, const char *name, ...);
+
struct sde_rotator_device;
#if defined(CONFIG_DEBUG_FS)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_wb.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_wb.c
index 2cf22ae6a3ed..e0f44be222d6 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_wb.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_wb.c
@@ -316,7 +316,7 @@ static int sde_mdp_wb_wait4comp(struct sde_mdp_ctl *ctl, void *arg)
{
struct sde_mdp_writeback_ctx *ctx;
int rc = 0;
- u64 rot_time;
+ u64 rot_time = 0;
u32 status, mask, isr;
ctx = (struct sde_mdp_writeback_ctx *) ctl->priv_data;
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 9b08c4fe0989..e9d2dd5ec972 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -34,6 +34,7 @@
#include "sde_rotator_r3_hwio.h"
#include "sde_rotator_r3_debug.h"
#include "sde_rotator_trace.h"
+#include "sde_rotator_debug.h"
/* XIN mapping */
#define XIN_SSPP 0
@@ -198,6 +199,29 @@ static u32 sde_hw_rotator_output_pixfmts[] = {
SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC,
};
+static struct sde_rot_vbif_debug_bus nrt_vbif_dbg_bus_r3[] = {
+ {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 */
+};
+
+static struct sde_rot_regdump sde_rot_r3_regdump[] = {
+ { "SDEROT_ROTTOP", SDE_ROT_ROTTOP_OFFSET, 0x100, SDE_ROT_REGDUMP_READ },
+ { "SDEROT_SSPP", SDE_ROT_SSPP_OFFSET, 0x200, SDE_ROT_REGDUMP_READ },
+ { "SDEROT_WB", SDE_ROT_WB_OFFSET, 0x300, SDE_ROT_REGDUMP_READ },
+ { "SDEROT_REGDMA_CSR", SDE_ROT_REGDMA_OFFSET, 0x100,
+ SDE_ROT_REGDUMP_READ },
+ /*
+ * Need to perform a SW reset to REGDMA in order to access the
+ * REGDMA RAM especially if REGDMA is waiting for Rotator IDLE.
+ * REGDMA RAM should be dump at last.
+ */
+ { "SDEROT_REGDMA_RESET", ROTTOP_SW_RESET_OVERRIDE, 1,
+ SDE_ROT_REGDUMP_WRITE },
+ { "SDEROT_REGDMA_RAM", SDE_ROT_REGDMA_RAM_OFFSET, 0x2000,
+ SDE_ROT_REGDUMP_READ },
+};
+
/* Invalid software timestamp value for initialization */
#define SDE_REGDMA_SWTS_INVALID (~0)
@@ -434,6 +458,14 @@ static void sde_hw_rotator_setup_timestamp_packet(
SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x03020100);
SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x80000000);
SDE_REGDMA_BLKWRITE_DATA(wrptr, ctx->timestamp);
+ /*
+ * Must clear secure buffer setting for SW timestamp because
+ * SW timstamp buffer allocation is always non-secure region.
+ */
+ if (ctx->is_secure) {
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_ADDR_SW_STATUS, 0);
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ADDR_SW_STATUS, 0);
+ }
SDE_REGDMA_BLKWRITE_INC(wrptr, ROT_WB_DST_FORMAT, 4);
SDE_REGDMA_BLKWRITE_DATA(wrptr, 0x000037FF);
SDE_REGDMA_BLKWRITE_DATA(wrptr, 0);
@@ -611,6 +643,9 @@ static void sde_hw_rotator_setup_fetchengine(struct sde_hw_rotator_context *ctx,
if (flags & SDE_ROT_FLAG_SECURE_OVERLAY_SESSION) {
SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_ADDR_SW_STATUS, 0xF);
ctx->is_secure = true;
+ } else {
+ SDE_REGDMA_WRITE(wrptr, ROT_SSPP_SRC_ADDR_SW_STATUS, 0);
+ ctx->is_secure = false;
}
/* Update command queue write ptr */
@@ -703,6 +738,11 @@ static void sde_hw_rotator_setup_wbengine(struct sde_hw_rotator_context *ctx,
SDE_REGDMA_WRITE(wrptr, ROT_WB_OUT_XY,
cfg->dst_rect->x | (cfg->dst_rect->y << 16));
+ if (flags & SDE_ROT_FLAG_SECURE_OVERLAY_SESSION)
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ADDR_SW_STATUS, 0x1);
+ else
+ SDE_REGDMA_WRITE(wrptr, ROT_WB_DST_ADDR_SW_STATUS, 0);
+
/*
* setup Downscale factor
*/
@@ -1054,6 +1094,9 @@ static u32 sde_hw_rotator_wait_done_regdma(
sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0;
+ if (status & ROT_ERROR_BIT)
+ SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus", "panic");
+
return sts;
}
@@ -1468,6 +1511,10 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
&entry->dst_buf);
}
+ SDEROT_EVTLOG(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);
+
if (mdata->default_ot_rd_limit) {
struct sde_mdp_set_ot_params ot_params;
@@ -1566,8 +1613,8 @@ static int sde_hw_rotator_kickoff(struct sde_rot_hw_resource *hw,
if (!ctx) {
SDEROT_ERR("Cannot locate rotator ctx from sesison id:%d\n",
entry->item.session_id);
+ return -EINVAL;
}
- WARN_ON(ctx == NULL);
ret = sde_smmu_ctrl(1);
if (IS_ERR_VALUE(ret)) {
@@ -1609,8 +1656,8 @@ static int sde_hw_rotator_wait4done(struct sde_rot_hw_resource *hw,
if (!ctx) {
SDEROT_ERR("Cannot locate rotator ctx from sesison id:%d\n",
entry->item.session_id);
+ return -EINVAL;
}
- WARN_ON(ctx == NULL);
ret = rot->ops.wait_rotator_done(ctx, ctx->q_id, 0);
@@ -1661,6 +1708,13 @@ static int sde_rotator_hw_rev_init(struct sde_hw_rotator *rot)
set_bit(SDE_CAPS_R3_1P5_DOWNSCALE, mdata->sde_caps_map);
}
+ mdata->nrt_vbif_dbg_bus = nrt_vbif_dbg_bus_r3;
+ mdata->nrt_vbif_dbg_bus_size =
+ ARRAY_SIZE(nrt_vbif_dbg_bus_r3);
+
+ mdata->regdump = sde_rot_r3_regdump;
+ mdata->regdump_size = ARRAY_SIZE(sde_rot_r3_regdump);
+
return 0;
}
@@ -1745,8 +1799,10 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
q_id = ROT_QUEUE_LOW_PRIORITY;
ts = (ts >> SDE_REGDMA_SWTS_SHIFT) &
SDE_REGDMA_SWTS_MASK;
+ } else {
+ SDEROT_ERR("unknown ISR status: isr=0x%X\n", isr);
+ goto done_isr_handle;
}
-
ctx = rot->rotCtx[q_id][ts & SDE_HW_ROT_REGDMA_SEG_MASK];
/*
@@ -1766,6 +1822,7 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
[ts & SDE_HW_ROT_REGDMA_SEG_MASK];
};
+done_isr_handle:
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
} else if (isr & REGDMA_INT_ERR_MASK) {
@@ -2183,6 +2240,7 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
clk_set_flags(mgr->rot_clk[mgr->core_clk_idx].clk,
CLKFLAG_NORETAIN_PERIPH);
+ mdata->sde_rot_hw = rot;
return 0;
error_hw_rev_init:
if (rot->irq_num >= 0)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
index 6cc975e22cd4..7bbd8aa53342 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
@@ -33,6 +33,7 @@
#include "sde_rotator_util.h"
#include "sde_rotator_io_util.h"
#include "sde_rotator_smmu.h"
+#include "sde_rotator_debug.h"
#define SMMU_SDE_ROT_SEC "qcom,smmu_sde_rot_sec"
#define SMMU_SDE_ROT_UNSEC "qcom,smmu_sde_rot_unsec"
@@ -332,6 +333,8 @@ int sde_smmu_ctrl(int enable)
int rc = 0;
mutex_lock(&sde_smmu_ref_cnt_lock);
+ SDEROT_EVTLOG(__builtin_return_address(0), enable, mdata->iommu_ref_cnt,
+ mdata->iommu_attached);
SDEROT_DBG("%pS: enable:%d ref_cnt:%d attach:%d\n",
__builtin_return_address(0), enable, mdata->iommu_ref_cnt,
mdata->iommu_attached);
@@ -407,9 +410,10 @@ static int sde_smmu_fault_handler(struct iommu_domain *domain,
sde_smmu = (struct sde_smmu_client *)token;
- /* TODO: trigger rotator panic and dump */
- SDEROT_ERR("TODO: trigger rotator panic and dump, iova=0x%08lx\n",
- iova);
+ /* trigger rotator panic and dump */
+ SDEROT_ERR("trigger rotator panic and dump, iova=0x%08lx\n", iova);
+
+ sde_rot_dump_panic();
return rc;
}
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 44f7af089ee9..eed177ea5bab 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
@@ -586,7 +586,7 @@ void sde_rot_ubwc_data_calc_offset(struct sde_mdp_data *data, u16 x, u16 y,
struct sde_mdp_plane_sizes *ps, struct sde_mdp_format_params *fmt)
{
u16 macro_w, micro_w, micro_h;
- u32 offset;
+ u32 offset = 0;
int ret;
ret = sde_rot_get_ubwc_micro_dim(fmt->format, &micro_w, &micro_h);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 566441e9c546..f0a3875a8f28 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -90,6 +90,7 @@ static void msm_comm_generate_session_error(struct msm_vidc_inst *inst);
static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst);
static void handle_session_error(enum hal_command_response cmd, void *data);
static void msm_vidc_print_running_insts(struct msm_vidc_core *core);
+static void msm_comm_print_debug_info(struct msm_vidc_inst *inst);
bool msm_comm_turbo_session(struct msm_vidc_inst *inst)
{
@@ -905,7 +906,7 @@ static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst,
call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
dprintk(VIDC_ERR,
"sess resp timeout can potentially crash the system\n");
-
+ msm_comm_print_debug_info(inst);
BUG_ON(inst->core->resources.debug_timeout);
rc = -EIO;
} else {
@@ -1601,6 +1602,7 @@ static void handle_sys_error(enum hal_command_response cmd, void *data)
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_core *core = NULL;
struct hfi_device *hdev = NULL;
+ struct msm_vidc_inst *inst = NULL;
int rc = 0;
subsystem_crashed("venus");
@@ -1640,6 +1642,19 @@ static void handle_sys_error(enum hal_command_response cmd, void *data)
dprintk(VIDC_ERR,
"SYS_ERROR can potentially crash the system\n");
+ /*
+ * For SYS_ERROR, there will not be any inst pointer.
+ * Just grab one of the inst from instances list and
+ * use it.
+ */
+
+ mutex_lock(&core->lock);
+ inst = list_first_entry(&core->instances,
+ struct msm_vidc_inst, list);
+ mutex_unlock(&core->lock);
+
+ msm_comm_print_debug_info(inst);
+
BUG_ON(core->resources.debug_timeout);
}
@@ -2450,6 +2465,7 @@ static int msm_comm_session_abort(struct msm_vidc_inst *inst)
call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
dprintk(VIDC_ERR,
"ABORT timeout can potentially crash the system\n");
+ msm_comm_print_debug_info(inst);
BUG_ON(inst->core->resources.debug_timeout);
rc = -EBUSY;
@@ -2522,6 +2538,7 @@ int msm_comm_check_core_init(struct msm_vidc_core *core)
{
int rc = 0;
struct hfi_device *hdev;
+ struct msm_vidc_inst *inst = NULL;
mutex_lock(&core->lock);
if (core->state >= VIDC_CORE_INIT_DONE) {
@@ -2540,6 +2557,17 @@ int msm_comm_check_core_init(struct msm_vidc_core *core)
call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
dprintk(VIDC_ERR,
"SYS_INIT timeout can potentially crash the system\n");
+ /*
+ * For SYS_INIT, there will not be any inst pointer.
+ * Just grab one of the inst from instances list and
+ * use it.
+ */
+ inst = list_first_entry(&core->instances,
+ struct msm_vidc_inst, list);
+
+ mutex_unlock(&core->lock);
+ msm_comm_print_debug_info(inst);
+ mutex_lock(&core->lock);
BUG_ON(core->resources.debug_timeout);
rc = -EIO;
@@ -4017,6 +4045,8 @@ int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype,
call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
dprintk(VIDC_ERR,
"SESS_PROP timeout can potentially crash the system\n");
+ if (inst->core->resources.debug_timeout)
+ msm_comm_print_debug_info(inst);
BUG_ON(inst->core->resources.debug_timeout);
rc = -ETIMEDOUT;
@@ -5232,3 +5262,92 @@ int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a)
exit:
return rc;
}
+
+void msm_comm_print_inst_info(struct msm_vidc_inst *inst)
+{
+ struct buffer_info *temp;
+ struct internal_buf *buf;
+ int i = 0;
+ bool is_decode = false;
+ enum vidc_ports port;
+
+ if (!inst) {
+ dprintk(VIDC_ERR, "%s - invalid param %p\n",
+ __func__, inst);
+ return;
+ }
+
+ is_decode = inst->session_type == MSM_VIDC_DECODER;
+ 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,
+ inst->prop.height[port], inst->prop.width[port],
+ inst->prop.fps, inst->prop.bitrate,
+ !inst->bit_depth ? "8" : "10");
+
+ dprintk(VIDC_ERR,
+ "---Buffer details for inst: %p of type: %d---\n",
+ inst, inst->session_type);
+ mutex_lock(&inst->registeredbufs.lock);
+ dprintk(VIDC_ERR, "registered buffer list:\n");
+ list_for_each_entry(temp, &inst->registeredbufs.list, list)
+ for (i = 0; i < temp->num_planes; i++)
+ dprintk(VIDC_ERR,
+ "type: %d plane: %d addr: %pa size: %d\n",
+ temp->type, i, &temp->device_addr[i],
+ temp->size[i]);
+
+ mutex_unlock(&inst->registeredbufs.lock);
+
+ mutex_lock(&inst->scratchbufs.lock);
+ dprintk(VIDC_ERR, "scratch buffer list:\n");
+ list_for_each_entry(buf, &inst->scratchbufs.list, list)
+ dprintk(VIDC_ERR, "type: %d addr: %pa size: %zu\n",
+ buf->buffer_type, &buf->handle->device_addr,
+ buf->handle->size);
+ mutex_unlock(&inst->scratchbufs.lock);
+
+ mutex_lock(&inst->persistbufs.lock);
+ dprintk(VIDC_ERR, "persist buffer list:\n");
+ list_for_each_entry(buf, &inst->persistbufs.list, list)
+ dprintk(VIDC_ERR, "type: %d addr: %pa size: %zu\n",
+ buf->buffer_type, &buf->handle->device_addr,
+ buf->handle->size);
+ mutex_unlock(&inst->persistbufs.lock);
+
+ mutex_lock(&inst->outputbufs.lock);
+ dprintk(VIDC_ERR, "dpb buffer list:\n");
+ list_for_each_entry(buf, &inst->outputbufs.list, list)
+ dprintk(VIDC_ERR, "type: %d addr: %pa size: %zu\n",
+ buf->buffer_type, &buf->handle->device_addr,
+ buf->handle->size);
+ mutex_unlock(&inst->outputbufs.lock);
+}
+
+static void msm_comm_print_debug_info(struct msm_vidc_inst *inst)
+{
+ struct msm_vidc_core *core = NULL;
+ struct msm_vidc_inst *temp = NULL;
+
+ if (!inst || !inst->core) {
+ dprintk(VIDC_ERR, "%s - invalid param %p %p\n",
+ __func__, inst, core);
+ return;
+ }
+ core = inst->core;
+
+ dprintk(VIDC_ERR, "Venus core frequency = %lu",
+ msm_comm_get_clock_rate(core));
+ dprintk(VIDC_ERR, "Printing instance info that caused Error\n");
+ msm_comm_print_inst_info(inst);
+ dprintk(VIDC_ERR, "Printing remaining instances info\n");
+ mutex_lock(&core->lock);
+ list_for_each_entry(temp, &core->instances, list) {
+ /* inst already printed above. Hence don't repeat.*/
+ if (temp == inst)
+ continue;
+ msm_comm_print_inst_info(temp);
+ }
+ mutex_unlock(&core->lock);
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index 337760508eb1..eac7f658eb31 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -98,4 +98,5 @@ int msm_comm_ctrl_deinit(struct msm_vidc_inst *inst);
void msm_comm_cleanup_internal_buffers(struct msm_vidc_inst *inst);
int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a);
bool msm_comm_turbo_session(struct msm_vidc_inst *inst);
+void msm_comm_print_inst_info(struct msm_vidc_inst *inst);
#endif
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index 1bdc5bf2c93d..25fccab99fb3 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -1238,11 +1238,6 @@ int msm_vidc_smmu_fault_handler(struct iommu_domain *domain,
{
struct msm_vidc_core *core = token;
struct msm_vidc_inst *inst;
- struct buffer_info *temp;
- struct internal_buf *buf;
- int i = 0;
- bool is_decode = false;
- enum vidc_ports port;
if (!domain || !core) {
dprintk(VIDC_ERR, "%s - invalid param %pK %pK\n",
@@ -1257,52 +1252,7 @@ int msm_vidc_smmu_fault_handler(struct iommu_domain *domain,
mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
- is_decode = inst->session_type == MSM_VIDC_DECODER;
- 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,
- inst->prop.height[port], inst->prop.width[port],
- inst->prop.fps, inst->prop.bitrate,
- !inst->bit_depth ? "8" : "10");
-
- dprintk(VIDC_ERR,
- "---Buffer details for inst: %pK of type: %d---\n",
- inst, inst->session_type);
- mutex_lock(&inst->registeredbufs.lock);
- dprintk(VIDC_ERR, "registered buffer list:\n");
- list_for_each_entry(temp, &inst->registeredbufs.list, list)
- for (i = 0; i < temp->num_planes; i++)
- dprintk(VIDC_ERR,
- "type: %d plane: %d addr: %pa size: %d\n",
- temp->type, i, &temp->device_addr[i],
- temp->size[i]);
-
- mutex_unlock(&inst->registeredbufs.lock);
-
- mutex_lock(&inst->scratchbufs.lock);
- dprintk(VIDC_ERR, "scratch buffer list:\n");
- list_for_each_entry(buf, &inst->scratchbufs.list, list)
- dprintk(VIDC_ERR, "type: %d addr: %pa size: %zu\n",
- buf->buffer_type, &buf->handle->device_addr,
- buf->handle->size);
- mutex_unlock(&inst->scratchbufs.lock);
-
- mutex_lock(&inst->persistbufs.lock);
- dprintk(VIDC_ERR, "persist buffer list:\n");
- list_for_each_entry(buf, &inst->persistbufs.list, list)
- dprintk(VIDC_ERR, "type: %d addr: %pa size: %zu\n",
- buf->buffer_type, &buf->handle->device_addr,
- buf->handle->size);
- mutex_unlock(&inst->persistbufs.lock);
-
- mutex_lock(&inst->outputbufs.lock);
- dprintk(VIDC_ERR, "dpb buffer list:\n");
- list_for_each_entry(buf, &inst->outputbufs.list, list)
- dprintk(VIDC_ERR, "type: %d addr: %pa size: %zu\n",
- buf->buffer_type, &buf->handle->device_addr,
- buf->handle->size);
- mutex_unlock(&inst->outputbufs.lock);
+ msm_comm_print_inst_info(inst);
}
core->smmu_fault_handled = true;
mutex_unlock(&core->lock);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h
index 23d84e9f7f7a..f6120dc7d1d5 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h
@@ -16,6 +16,7 @@
#define DT_PARSE
#include <linux/of.h>
#include "msm_vidc_resources.h"
+#include "msm_vidc_common.h"
void msm_vidc_free_platform_resources(
struct msm_vidc_platform_resources *res);
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 5a51fbcf9318..e0fb31de38ff 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -119,7 +119,7 @@ static inline bool __core_in_valid_state(struct venus_hfi_device *device)
return device->state != VENUS_STATE_DEINIT;
}
-static void __dump_packet(u8 *packet)
+static void __dump_packet(u8 *packet, enum vidc_msg_prio log_level)
{
u32 c = 0, packet_size = *(u32 *)packet;
const int row_size = 32;
@@ -132,7 +132,7 @@ static void __dump_packet(u8 *packet)
packet_size % row_size : row_size;
hex_dump_to_buffer(packet + c * row_size, bytes_to_read,
row_size, 4, row, sizeof(row), false);
- dprintk(VIDC_PKT, "%s\n", row);
+ dprintk(log_level, "%s\n", row);
}
}
@@ -342,7 +342,7 @@ static int __write_queue(struct vidc_iface_q_info *qinfo, u8 *packet,
if (msm_vidc_debug & VIDC_PKT) {
dprintk(VIDC_PKT, "%s: %pK\n", __func__, qinfo);
- __dump_packet(packet);
+ __dump_packet(packet, VIDC_PKT);
}
packet_size_in_words = (*(u32 *)packet) >> 2;
@@ -548,7 +548,7 @@ static int __read_queue(struct vidc_iface_q_info *qinfo, u8 *packet,
if (msm_vidc_debug & VIDC_PKT) {
dprintk(VIDC_PKT, "%s: %pK\n", __func__, qinfo);
- __dump_packet(packet);
+ __dump_packet(packet, VIDC_PKT);
}
return rc;
@@ -2517,7 +2517,6 @@ static int venus_hfi_session_clean(void *session)
mutex_lock(&device->lock);
__session_clean(sess_close);
- __flush_debug_queue(device, NULL);
mutex_unlock(&device->lock);
return 0;
@@ -3337,6 +3336,7 @@ static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet)
{
bool local_packet = false;
enum vidc_msg_prio log_level = VIDC_FW;
+ unsigned int pending_packet_count = 0;
if (!device) {
dprintk(VIDC_ERR, "%s: Invalid params\n", __func__);
@@ -3361,6 +3361,23 @@ static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet)
log_level = VIDC_ERR;
}
+ /*
+ * In FATAL situation, print all the pending messages in msg
+ * queue. This is useful for debugging. At this time, message
+ * queues may be corrupted. Hence don't trust them and just print
+ * first max_packets packets.
+ */
+
+ if (local_packet) {
+ dprintk(VIDC_ERR,
+ "Printing all pending messages in message Queue\n");
+ while (!__iface_msgq_read(device, packet) &&
+ pending_packet_count < max_packets) {
+ __dump_packet(packet, log_level);
+ pending_packet_count++;
+ }
+ }
+
while (!__iface_dbgq_read(device, packet)) {
struct hfi_msg_sys_coverage_packet *pkt =
(struct hfi_msg_sys_coverage_packet *) packet;
diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c
index b0e3bec683ed..b102264ca8fd 100644
--- a/drivers/mfd/wcd934x-regmap.c
+++ b/drivers/mfd/wcd934x-regmap.c
@@ -1848,6 +1848,20 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)
if (reg_tbl && reg_tbl[reg_offset] == WCD934X_READ)
return true;
+ /* IIR Coeff registers are not cacheable */
+ if ((reg >= WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) &&
+ (reg <= WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL))
+ return true;
+
+ if ((reg >= WCD934X_CDC_ANC0_IIR_COEFF_1_CTL) &&
+ (reg <= WCD934X_CDC_ANC0_FB_GAIN_CTL))
+ return true;
+
+ if ((reg >= WCD934X_CDC_ANC1_IIR_COEFF_1_CTL) &&
+ (reg <= WCD934X_CDC_ANC1_FB_GAIN_CTL))
+ return true;
+
+
/*
* Need to mark volatile for registers that are writable but
* only few bits are read-only
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 0c5754341991..b6a476cd882d 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -450,10 +450,22 @@ int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res)
{
int i, ret;
u8 irq_level[wcd9xxx_res->num_irq_regs];
+ struct irq_domain *domain;
+ struct device_node *pnode;
mutex_init(&wcd9xxx_res->irq_lock);
mutex_init(&wcd9xxx_res->nested_irq_lock);
+ pnode = of_irq_find_parent(wcd9xxx_res->dev->of_node);
+ if (unlikely(!pnode))
+ return -EINVAL;
+
+ domain = irq_find_host(pnode);
+ if (unlikely(!domain))
+ return -EINVAL;
+
+ wcd9xxx_res->domain = domain;
+
wcd9xxx_res->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx_res);
if (!wcd9xxx_res->irq) {
pr_warn("%s: irq driver is not yet initialized\n", __func__);
@@ -553,7 +565,6 @@ void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res)
if (wcd9xxx_res->irq) {
disable_irq_wake(wcd9xxx_res->irq);
free_irq(wcd9xxx_res->irq, wcd9xxx_res);
- /* Release parent's of node */
wcd9xxx_res->irq = 0;
wcd9xxx_irq_put_upstream_irq(wcd9xxx_res);
}
@@ -626,19 +637,14 @@ wcd9xxx_irq_add_domain(struct device_node *node,
static struct wcd9xxx_irq_drv_data *
wcd9xxx_get_irq_drv_d(const struct wcd9xxx_core_resource *wcd9xxx_res)
{
- struct device_node *pnode;
struct irq_domain *domain;
- pnode = of_irq_find_parent(wcd9xxx_res->dev->of_node);
- /* Shouldn't happen */
- if (unlikely(!pnode))
- return NULL;
+ domain = wcd9xxx_res->domain;
- domain = irq_find_host(pnode);
- if (unlikely(!domain))
+ if (domain)
+ return domain->host_data;
+ else
return NULL;
-
- return (struct wcd9xxx_irq_drv_data *)domain->host_data;
}
static int phyirq_to_virq(struct wcd9xxx_core_resource *wcd9xxx_res, int offset)
@@ -669,10 +675,6 @@ static unsigned int wcd9xxx_irq_get_upstream_irq(
{
struct wcd9xxx_irq_drv_data *data;
- /* Hold parent's of node */
- if (!of_node_get(of_irq_find_parent(wcd9xxx_res->dev->of_node)))
- return -EINVAL;
-
data = wcd9xxx_get_irq_drv_d(wcd9xxx_res);
if (!data) {
pr_err("%s: interrupt controller is not registerd\n", __func__);
@@ -686,8 +688,7 @@ static unsigned int wcd9xxx_irq_get_upstream_irq(
static void wcd9xxx_irq_put_upstream_irq(
struct wcd9xxx_core_resource *wcd9xxx_res)
{
- /* Hold parent's of node */
- of_node_put(of_irq_find_parent(wcd9xxx_res->dev->of_node));
+ wcd9xxx_res->domain = NULL;
}
static int wcd9xxx_map_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
diff --git a/drivers/misc/qcom/Kconfig b/drivers/misc/qcom/Kconfig
index a62297e913d2..9c73960f01ff 100644
--- a/drivers/misc/qcom/Kconfig
+++ b/drivers/misc/qcom/Kconfig
@@ -1,6 +1,7 @@
config MSM_QDSP6V2_CODECS
bool "Audio QDSP6V2 APR support"
depends on MSM_SMD
+ select SND_SOC_QDSP6V2
help
Enable Audio codecs with APR IPC protocol support between
application processor and QDSP6 for B-family. APR is
@@ -9,6 +10,7 @@ config MSM_QDSP6V2_CODECS
config MSM_ULTRASOUND
bool "QDSP6V2 HW Ultrasound support"
+ select SND_SOC_QDSP6V2
help
Enable HW Ultrasound support in QDSP6V2.
QDSP6V2 can support HW encoder & decoder and
diff --git a/drivers/misc/qcom/qdsp6v2/aac_in.c b/drivers/misc/qcom/qdsp6v2/aac_in.c
index c9d5dbb0b313..7176c114f85b 100644
--- a/drivers/misc/qcom/qdsp6v2/aac_in.c
+++ b/drivers/misc/qcom/qdsp6v2/aac_in.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-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
@@ -421,6 +421,8 @@ static long aac_in_compat_ioctl(struct file *file, unsigned int cmd,
struct msm_audio_aac_enc_config cfg;
struct msm_audio_aac_enc_config32 cfg_32;
+ memset(&cfg_32, 0, sizeof(cfg_32));
+
cmd = AUDIO_GET_AAC_ENC_CONFIG;
rc = aac_in_ioctl_shared(file, cmd, &cfg);
if (rc) {
diff --git a/drivers/misc/qcom/qdsp6v2/amrnb_in.c b/drivers/misc/qcom/qdsp6v2/amrnb_in.c
index eb92137f0671..1bb441bd2ff4 100644
--- a/drivers/misc/qcom/qdsp6v2/amrnb_in.c
+++ b/drivers/misc/qcom/qdsp6v2/amrnb_in.c
@@ -1,4 +1,5 @@
-/* Copyright (c) 2010-2012, 2014 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-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
@@ -221,6 +222,8 @@ static long amrnb_in_compat_ioctl(struct file *file,
struct msm_audio_amrnb_enc_config_v2 *amrnb_config;
struct msm_audio_amrnb_enc_config_v2_32 amrnb_config_32;
+ memset(&amrnb_config_32, 0, sizeof(amrnb_config_32));
+
amrnb_config =
(struct msm_audio_amrnb_enc_config_v2 *)audio->enc_cfg;
amrnb_config_32.band_mode = amrnb_config->band_mode;
diff --git a/drivers/misc/qcom/qdsp6v2/amrwb_in.c b/drivers/misc/qcom/qdsp6v2/amrwb_in.c
index 4cea3dc63389..4f94ed2673e6 100644
--- a/drivers/misc/qcom/qdsp6v2/amrwb_in.c
+++ b/drivers/misc/qcom/qdsp6v2/amrwb_in.c
@@ -1,4 +1,5 @@
-/* Copyright (c) 2011-2012, 2014 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-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
@@ -216,6 +217,8 @@ static long amrwb_in_compat_ioctl(struct file *file,
struct msm_audio_amrwb_enc_config *amrwb_config;
struct msm_audio_amrwb_enc_config_32 amrwb_config_32;
+ memset(&amrwb_config_32, 0, sizeof(amrwb_config_32));
+
amrwb_config =
(struct msm_audio_amrwb_enc_config *)audio->enc_cfg;
amrwb_config_32.band_mode = amrwb_config->band_mode;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_alac.c b/drivers/misc/qcom/qdsp6v2/audio_alac.c
index 9748db30fac3..3de204c1ebc8 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_alac.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_alac.c
@@ -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
@@ -196,6 +196,8 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd,
struct msm_audio_alac_config *alac_config;
struct msm_audio_alac_config_32 alac_config_32;
+ memset(&alac_config_32, 0, sizeof(alac_config_32));
+
alac_config = (struct msm_audio_alac_config *)audio->codec_cfg;
alac_config_32.frameLength = alac_config->frameLength;
alac_config_32.compatVersion =
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
index ee5991177687..bfd730017d41 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-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
@@ -205,6 +205,10 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd,
struct msm_audio_amrwbplus_config_v2 *amrwbplus_config;
struct msm_audio_amrwbplus_config_v2_32
amrwbplus_config_32;
+
+ memset(&amrwbplus_config_32, 0,
+ sizeof(amrwbplus_config_32));
+
amrwbplus_config =
(struct msm_audio_amrwbplus_config_v2 *)
audio->codec_cfg;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_ape.c b/drivers/misc/qcom/qdsp6v2/audio_ape.c
index b4c2ddb947de..670ec555b8c6 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_ape.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_ape.c
@@ -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
@@ -180,6 +180,8 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd,
struct msm_audio_ape_config *ape_config;
struct msm_audio_ape_config_32 ape_config_32;
+ memset(&ape_config_32, 0, sizeof(ape_config_32));
+
ape_config = (struct msm_audio_ape_config *)audio->codec_cfg;
ape_config_32.compatibleVersion = ape_config->compatibleVersion;
ape_config_32.compressionLevel =
diff --git a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
index 3a8834446ea4..3632fc2b961b 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
@@ -630,6 +630,8 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd,
case AUDIO_EFFECTS_GET_BUF_AVAIL32: {
struct msm_hwacc_buf_avail32 buf_avail;
+ memset(&buf_avail, 0, sizeof(buf_avail));
+
buf_avail.input_num_avail = atomic_read(&effects->in_count);
buf_avail.output_num_avail = atomic_read(&effects->out_count);
pr_debug("%s: write buf avail: %d, read buf avail: %d\n",
diff --git a/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c
index a15fd87c7be8..91bbba176dfd 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-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
@@ -302,6 +302,8 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd,
struct msm_audio_aac_config *aac_config;
struct msm_audio_aac_config32 aac_config_32;
+ memset(&aac_config_32, 0, sizeof(aac_config_32));
+
aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
aac_config_32.format = aac_config->format;
aac_config_32.audio_object = aac_config->audio_object;
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.c b/drivers/misc/qcom/qdsp6v2/audio_utils.c
index cad0220a4960..065b426ca6d0 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-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
@@ -24,6 +24,15 @@
#include <asm/ioctls.h>
#include "audio_utils.h"
+/*
+ * Define maximum buffer size. Below values are chosen considering the higher
+ * values used among all native drivers.
+ */
+#define MAX_FRAME_SIZE 1536
+#define MAX_FRAMES 5
+#define META_SIZE (sizeof(struct meta_out_dsp))
+#define MAX_BUFFER_SIZE (1 + ((MAX_FRAME_SIZE + META_SIZE) * MAX_FRAMES))
+
static int audio_in_pause(struct q6audio_in *audio)
{
int rc;
@@ -329,6 +338,10 @@ long audio_in_ioctl(struct file *file,
rc = -EINVAL;
break;
}
+ if (cfg.buffer_size > MAX_BUFFER_SIZE) {
+ rc = -EINVAL;
+ break;
+ }
audio->str_cfg.buffer_size = cfg.buffer_size;
audio->str_cfg.buffer_count = cfg.buffer_count;
if (audio->opened) {
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
index 5c23da7f8857..f7ad8f61f2e7 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
@@ -1936,6 +1936,7 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
case AUDIO_GET_CONFIG_32: {
struct msm_audio_config32 cfg_32;
mutex_lock(&audio->lock);
+ memset(&cfg_32, 0, sizeof(cfg_32));
cfg_32.buffer_size = audio->pcm_cfg.buffer_size;
cfg_32.buffer_count = audio->pcm_cfg.buffer_count;
cfg_32.channel_count = audio->pcm_cfg.channel_count;
@@ -2032,6 +2033,7 @@ static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
audio->buf_cfg.frames_per_buf);
mutex_lock(&audio->lock);
+ memset(&cfg_32, 0, sizeof(cfg_32));
cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable;
cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf;
if (copy_to_user((void *)arg, &cfg_32,
diff --git a/drivers/misc/qcom/qdsp6v2/audio_wmapro.c b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
index 8d96b99d8f84..21ad33b7fd5d 100644
--- a/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
+++ b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
@@ -2,7 +2,7 @@
*
* 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
@@ -217,6 +217,8 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd,
struct msm_audio_wmapro_config *wmapro_config;
struct msm_audio_wmapro_config32 wmapro_config_32;
+ memset(&wmapro_config_32, 0, sizeof(wmapro_config_32));
+
wmapro_config =
(struct msm_audio_wmapro_config *)audio->codec_cfg;
wmapro_config_32.armdatareqthr = wmapro_config->armdatareqthr;
diff --git a/drivers/misc/qcom/qdsp6v2/evrc_in.c b/drivers/misc/qcom/qdsp6v2/evrc_in.c
index 2f931be226c6..aab8e27c0094 100644
--- a/drivers/misc/qcom/qdsp6v2/evrc_in.c
+++ b/drivers/misc/qcom/qdsp6v2/evrc_in.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-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
@@ -224,6 +224,8 @@ static long evrc_in_compat_ioctl(struct file *file,
struct msm_audio_evrc_enc_config32 cfg_32;
struct msm_audio_evrc_enc_config *enc_cfg;
+ memset(&cfg_32, 0, sizeof(cfg_32));
+
enc_cfg = audio->enc_cfg;
cfg_32.cdma_rate = enc_cfg->cdma_rate;
cfg_32.min_bit_rate = enc_cfg->min_bit_rate;
diff --git a/drivers/misc/qcom/qdsp6v2/qcelp_in.c b/drivers/misc/qcom/qdsp6v2/qcelp_in.c
index b5d5ad113722..aabf5d33a507 100644
--- a/drivers/misc/qcom/qdsp6v2/qcelp_in.c
+++ b/drivers/misc/qcom/qdsp6v2/qcelp_in.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-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
@@ -225,6 +225,8 @@ static long qcelp_in_compat_ioctl(struct file *file,
struct msm_audio_qcelp_enc_config32 cfg_32;
struct msm_audio_qcelp_enc_config *enc_cfg;
+ memset(&cfg_32, 0, sizeof(cfg_32));
+
enc_cfg = (struct msm_audio_qcelp_enc_config *)audio->enc_cfg;
cfg_32.cdma_rate = enc_cfg->cdma_rate;
cfg_32.min_bit_rate = enc_cfg->min_bit_rate;
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index a15211fd33aa..644178a0cdfc 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1607,6 +1607,7 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
list_for_each_entry(ptr_svc,
&qseecom.registered_listener_list_head, list) {
if (ptr_svc->svc.listener_id == lstnr) {
+ ptr_svc->listener_in_use = true;
ptr_svc->rcv_req_flag = 1;
wake_up_interruptible(&ptr_svc->rcv_req_wq);
break;
@@ -1689,6 +1690,7 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
(const void *)&send_data_rsp,
sizeof(send_data_rsp), resp,
sizeof(*resp));
+ ptr_svc->listener_in_use = false;
if (ret) {
pr_err("scm_call() failed with err: %d (app_id = %d)\n",
ret, data->client.app_id);
@@ -1712,6 +1714,101 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
return ret;
}
+int __qseecom_process_reentrancy_blocked_on_listener(
+ struct qseecom_command_scm_resp *resp,
+ struct qseecom_registered_app_list *ptr_app,
+ struct qseecom_dev_handle *data)
+{
+ struct qseecom_registered_listener_list *list_ptr;
+ int ret = 0;
+ struct qseecom_continue_blocked_request_ireq ireq;
+ struct qseecom_command_scm_resp continue_resp;
+ sigset_t new_sigset, old_sigset;
+ unsigned long flags;
+ bool found_app = false;
+
+ if (!resp || !data) {
+ pr_err("invalid resp or data pointer\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* find app_id & img_name from list */
+ if (!ptr_app) {
+ spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
+ list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
+ list) {
+ if ((ptr_app->app_id == data->client.app_id) &&
+ (!strcmp(ptr_app->app_name,
+ data->client.app_name))) {
+ found_app = true;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
+ flags);
+ if (!found_app) {
+ pr_err("app_id %d (%s) is not found\n",
+ data->client.app_id,
+ (char *)data->client.app_name);
+ ret = -ENOENT;
+ goto exit;
+ }
+ }
+
+ list_ptr = __qseecom_find_svc(resp->data);
+ if (!list_ptr) {
+ pr_err("Invalid listener ID\n");
+ ret = -ENODATA;
+ goto exit;
+ }
+ pr_debug("lsntr %d in_use = %d\n",
+ resp->data, list_ptr->listener_in_use);
+ ptr_app->blocked_on_listener_id = resp->data;
+ /* sleep until listener is available */
+ do {
+ qseecom.app_block_ref_cnt++;
+ ptr_app->app_blocked = true;
+ sigfillset(&new_sigset);
+ sigprocmask(SIG_SETMASK, &new_sigset, &old_sigset);
+ mutex_unlock(&app_access_lock);
+ do {
+ if (!wait_event_freezable(
+ list_ptr->listener_block_app_wq,
+ !list_ptr->listener_in_use)) {
+ break;
+ }
+ } while (1);
+ mutex_lock(&app_access_lock);
+ sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+ ptr_app->app_blocked = false;
+ qseecom.app_block_ref_cnt--;
+ } while (list_ptr->listener_in_use == true);
+ ptr_app->blocked_on_listener_id = 0;
+ /* notify the blocked app that listener is available */
+ pr_warn("Lsntr %d is available, unblock app(%d) %s in TZ\n",
+ resp->data, data->client.app_id,
+ data->client.app_name);
+ ireq.qsee_cmd_id = QSEOS_CONTINUE_BLOCKED_REQ_COMMAND;
+ ireq.app_id = data->client.app_id;
+ ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1,
+ &ireq, sizeof(ireq),
+ &continue_resp, sizeof(continue_resp));
+ if (ret) {
+ pr_err("scm_call for continue blocked req for app(%d) %s failed, ret %d\n",
+ data->client.app_id,
+ data->client.app_name, ret);
+ goto exit;
+ }
+ /*
+ * After TZ app is unblocked, then continue to next case
+ * for incomplete request processing
+ */
+ resp->result = QSEOS_RESULT_INCOMPLETE;
+exit:
+ return ret;
+}
+
static int __qseecom_reentrancy_process_incomplete_cmd(
struct qseecom_dev_handle *data,
struct qseecom_command_scm_resp *resp)
@@ -1725,7 +1822,7 @@ static int __qseecom_reentrancy_process_incomplete_cmd(
sigset_t new_sigset;
sigset_t old_sigset;
- while (resp->result == QSEOS_RESULT_INCOMPLETE) {
+ while (ret == 0 && rc == 0 && resp->result == QSEOS_RESULT_INCOMPLETE) {
lstnr = resp->data;
/*
* Wake up blocking lsitener service with the lstnr id
@@ -1812,16 +1909,37 @@ static int __qseecom_reentrancy_process_incomplete_cmd(
if (ret) {
pr_err("scm_call() failed with err: %d (app_id = %d)\n",
ret, data->client.app_id);
- if (lstnr == RPMB_SERVICE)
- __qseecom_disable_clk(CLK_QSEE);
- return ret;
+ goto exit;
}
- if ((resp->result != QSEOS_RESULT_SUCCESS) &&
- (resp->result != QSEOS_RESULT_INCOMPLETE)) {
+
+ switch (resp->result) {
+ case QSEOS_RESULT_BLOCKED_ON_LISTENER:
+ pr_warn("send lsr %d rsp, but app %d block on lsr %d\n",
+ lstnr, data->client.app_id, resp->data);
+ if (lstnr == resp->data) {
+ pr_err("lstnr %d should not be blocked!\n",
+ lstnr);
+ ret = -EINVAL;
+ goto exit;
+ }
+ ret = __qseecom_process_reentrancy_blocked_on_listener(
+ resp, NULL, data);
+ if (ret) {
+ pr_err("failed to process App(%d) %s blocked on listener %d\n",
+ data->client.app_id,
+ data->client.app_name, resp->data);
+ goto exit;
+ }
+ case QSEOS_RESULT_SUCCESS:
+ case QSEOS_RESULT_INCOMPLETE:
+ break;
+ default:
pr_err("fail:resp res= %d,app_id = %d,lstr = %d\n",
resp->result, data->client.app_id, lstnr);
ret = -EINVAL;
+ goto exit;
}
+exit:
if (lstnr == RPMB_SERVICE)
__qseecom_disable_clk(CLK_QSEE);
@@ -2669,63 +2787,21 @@ int __qseecom_process_reentrancy(struct qseecom_command_scm_resp *resp,
struct qseecom_registered_app_list *ptr_app,
struct qseecom_dev_handle *data)
{
- struct qseecom_registered_listener_list *list_ptr;
int ret = 0;
- struct qseecom_continue_blocked_request_ireq ireq;
- struct qseecom_command_scm_resp continue_resp;
- sigset_t new_sigset, old_sigset;
switch (resp->result) {
case QSEOS_RESULT_BLOCKED_ON_LISTENER:
- pr_debug("App(%d) %s is blocked on listener %d\n",
+ pr_warn("App(%d) %s is blocked on listener %d\n",
data->client.app_id, data->client.app_name,
resp->data);
- list_ptr = __qseecom_find_svc(resp->data);
- if (!list_ptr) {
- pr_err("Invalid listener ID\n");
- return -ENODATA;
- }
- ptr_app->blocked_on_listener_id = resp->data;
- list_ptr->listener_in_use = true;
- /* sleep until listener is available */
- while (list_ptr->listener_in_use == true) {
- qseecom.app_block_ref_cnt++;
- ptr_app->app_blocked = true;
- sigfillset(&new_sigset);
- sigprocmask(SIG_SETMASK, &new_sigset, &old_sigset);
- mutex_unlock(&app_access_lock);
- do {
- if (!wait_event_freezable(
- list_ptr->listener_block_app_wq,
- !list_ptr->listener_in_use)) {
- break;
- }
- } while (1);
- mutex_lock(&app_access_lock);
- sigprocmask(SIG_SETMASK, &old_sigset, NULL);
- ptr_app->app_blocked = false;
- qseecom.app_block_ref_cnt--;
- }
- /* notify the blocked app that listener is available */
- pr_debug("Lsntr %d is available, unblock app(%d) %s in TZ\n",
- resp->data, data->client.app_id,
- data->client.app_name);
- ireq.qsee_cmd_id = QSEOS_CONTINUE_BLOCKED_REQ_COMMAND;
- ireq.app_id = data->client.app_id;
- ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1,
- &ireq, sizeof(ireq),
- &continue_resp, sizeof(continue_resp));
+ ret = __qseecom_process_reentrancy_blocked_on_listener(
+ resp, ptr_app, data);
if (ret) {
- pr_err("scm_call for continue blocked req for app(%d) %s failed, ret %d\n",
- data->client.app_id,
- data->client.app_name, ret);
+ pr_err("failed to process App(%d) %s is blocked on listener %d\n",
+ data->client.app_id, data->client.app_name, resp->data);
return ret;
}
- /*
- * After TZ app is unblocked, then continue to next case
- * for incomplete request processing
- */
- resp->result = QSEOS_RESULT_INCOMPLETE;
+
case QSEOS_RESULT_INCOMPLETE:
qseecom.app_block_ref_cnt++;
ptr_app->app_blocked = true;
@@ -3249,6 +3325,7 @@ static int __qseecom_update_cmd_buf_64(void *msg, bool cleanup,
}
}
len = QSEECOM_SG_LIST_BUF_HDR_SZ_64BIT;
+ sg = sg_ptr->sgl;
goto cleanup;
}
sg = sg_ptr->sgl;
@@ -4015,12 +4092,21 @@ int qseecom_start_app(struct qseecom_handle **handle,
data->client.user_virt_sb_base = 0;
data->client.ihandle = NULL;
+ /* Allocate sglistinfo buffer for kernel client */
+ data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL);
+ if (!(data->sglistinfo_ptr)) {
+ kfree(data);
+ kfree(*handle);
+ *handle = NULL;
+ return -ENOMEM;
+ }
init_waitqueue_head(&data->abort_wq);
data->client.ihandle = ion_alloc(qseecom.ion_clnt, size, 4096,
ION_HEAP(ION_QSECOM_HEAP_ID), 0);
if (IS_ERR_OR_NULL(data->client.ihandle)) {
pr_err("Ion client could not retrieve the handle\n");
+ kfree(data->sglistinfo_ptr);
kfree(data);
kfree(*handle);
*handle = NULL;
@@ -4118,6 +4204,7 @@ int qseecom_start_app(struct qseecom_handle **handle,
return 0;
err:
+ kfree(data->sglistinfo_ptr);
kfree(data);
kfree(*handle);
*handle = NULL;
@@ -4165,6 +4252,7 @@ int qseecom_shutdown_app(struct qseecom_handle **handle)
mutex_unlock(&app_access_lock);
if (ret == 0) {
+ kzfree(data->sglistinfo_ptr);
kzfree(data);
kzfree(*handle);
kzfree(kclient);
@@ -7058,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;
}
@@ -7923,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/misc/uid_stat.c b/drivers/misc/uid_stat.c
new file mode 100644
index 000000000000..185c69c9738a
--- /dev/null
+++ b/drivers/misc/uid_stat.c
@@ -0,0 +1,153 @@
+/* drivers/misc/uid_stat.c
+ *
+ * Copyright (C) 2008 - 2009 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/atomic.h>
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/uid_stat.h>
+#include <net/activity_stats.h>
+
+static DEFINE_SPINLOCK(uid_lock);
+static LIST_HEAD(uid_list);
+static struct proc_dir_entry *parent;
+
+struct uid_stat {
+ struct list_head link;
+ uid_t uid;
+ atomic_t tcp_rcv;
+ atomic_t tcp_snd;
+};
+
+static struct uid_stat *find_uid_stat(uid_t uid) {
+ struct uid_stat *entry;
+
+ list_for_each_entry(entry, &uid_list, link) {
+ if (entry->uid == uid) {
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+static int uid_stat_atomic_int_show(struct seq_file *m, void *v)
+{
+ unsigned int bytes;
+ atomic_t *counter = m->private;
+
+ bytes = (unsigned int) (atomic_read(counter) + INT_MIN);
+ seq_printf(m, "%u\n", bytes);
+ return seq_has_overflowed(m) ? -ENOSPC : 0;
+}
+
+static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode));
+}
+
+static const struct file_operations uid_stat_read_atomic_int_fops = {
+ .open = uid_stat_read_atomic_int_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/* Create a new entry for tracking the specified uid. */
+static struct uid_stat *create_stat(uid_t uid) {
+ struct uid_stat *new_uid;
+ /* Create the uid stat struct and append it to the list. */
+ new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC);
+ if (!new_uid)
+ return NULL;
+
+ new_uid->uid = uid;
+ /* Counters start at INT_MIN, so we can track 4GB of network traffic. */
+ atomic_set(&new_uid->tcp_rcv, INT_MIN);
+ atomic_set(&new_uid->tcp_snd, INT_MIN);
+
+ list_add_tail(&new_uid->link, &uid_list);
+ return new_uid;
+}
+
+static void create_stat_proc(struct uid_stat *new_uid)
+{
+ char uid_s[32];
+ struct proc_dir_entry *entry;
+ sprintf(uid_s, "%d", new_uid->uid);
+ entry = proc_mkdir(uid_s, parent);
+
+ /* Keep reference to uid_stat so we know what uid to read stats from. */
+ proc_create_data("tcp_snd", S_IRUGO, entry,
+ &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd);
+
+ proc_create_data("tcp_rcv", S_IRUGO, entry,
+ &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv);
+}
+
+static struct uid_stat *find_or_create_uid_stat(uid_t uid)
+{
+ struct uid_stat *entry;
+ unsigned long flags;
+ spin_lock_irqsave(&uid_lock, flags);
+ entry = find_uid_stat(uid);
+ if (entry) {
+ spin_unlock_irqrestore(&uid_lock, flags);
+ return entry;
+ }
+ entry = create_stat(uid);
+ spin_unlock_irqrestore(&uid_lock, flags);
+ if (entry)
+ create_stat_proc(entry);
+ return entry;
+}
+
+int uid_stat_tcp_snd(uid_t uid, int size) {
+ struct uid_stat *entry;
+ activity_stats_update();
+ entry = find_or_create_uid_stat(uid);
+ if (!entry)
+ return -1;
+ atomic_add(size, &entry->tcp_snd);
+ return 0;
+}
+
+int uid_stat_tcp_rcv(uid_t uid, int size) {
+ struct uid_stat *entry;
+ activity_stats_update();
+ entry = find_or_create_uid_stat(uid);
+ if (!entry)
+ return -1;
+ atomic_add(size, &entry->tcp_rcv);
+ return 0;
+}
+
+static int __init uid_stat_init(void)
+{
+ parent = proc_mkdir("uid_stat", NULL);
+ if (!parent) {
+ pr_err("uid_stat: failed to create proc entry\n");
+ return -1;
+ }
+ return 0;
+}
+
+__initcall(uid_stat_init);
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 5562308699bc..6142ec1b9dfb 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -68,3 +68,15 @@ config MMC_TEST
This driver is only of interest to those developing or
testing a host driver. Most people should say N here.
+
+config MMC_SIMULATE_MAX_SPEED
+ bool "Turn on maximum speed control per block device"
+ depends on MMC_BLOCK
+ help
+ Say Y here to enable MMC device speed limiting. Used to test and
+ simulate the behavior of the system when confronted with a slow MMC.
+
+ Enables max_read_speed, max_write_speed and cache_size attributes to
+ control the write or read maximum KB/second speed behaviors.
+
+ If unsure, say N here.
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index b116122c5767..7547463928d6 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -38,7 +38,6 @@
#include <linux/pm_runtime.h>
#include <linux/ioprio.h>
-#define CREATE_TRACE_POINTS
#include <trace/events/mmc.h>
#include <linux/mmc/ioctl.h>
@@ -329,6 +328,249 @@ out:
return ret;
}
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
+
+static int max_read_speed, max_write_speed, cache_size = 4;
+
+module_param(max_read_speed, int, S_IRUSR | S_IRGRP);
+MODULE_PARM_DESC(max_read_speed, "maximum KB/s read speed 0=off");
+module_param(max_write_speed, int, S_IRUSR | S_IRGRP);
+MODULE_PARM_DESC(max_write_speed, "maximum KB/s write speed 0=off");
+module_param(cache_size, int, S_IRUSR | S_IRGRP);
+MODULE_PARM_DESC(cache_size, "MB high speed memory or SLC cache");
+
+/*
+ * helper macros and expectations:
+ * size - unsigned long number of bytes
+ * jiffies - unsigned long HZ timestamp difference
+ * speed - unsigned KB/s transfer rate
+ */
+#define size_and_speed_to_jiffies(size, speed) \
+ ((size) * HZ / (speed) / 1024UL)
+#define jiffies_and_speed_to_size(jiffies, speed) \
+ (((speed) * (jiffies) * 1024UL) / HZ)
+#define jiffies_and_size_to_speed(jiffies, size) \
+ ((size) * HZ / (jiffies) / 1024UL)
+
+/* Limits to report warning */
+/* jiffies_and_size_to_speed(10*HZ, queue_max_hw_sectors(q) * 512UL) ~ 25 */
+#define MIN_SPEED(q) 250 /* 10 times faster than a floppy disk */
+#define MAX_SPEED(q) jiffies_and_size_to_speed(1, queue_max_sectors(q) * 512UL)
+
+#define speed_valid(speed) ((speed) > 0)
+
+static const char off[] = "off\n";
+
+static int max_speed_show(int speed, char *buf)
+{
+ if (speed)
+ return scnprintf(buf, PAGE_SIZE, "%uKB/s\n", speed);
+ else
+ return scnprintf(buf, PAGE_SIZE, off);
+}
+
+static int max_speed_store(const char *buf, struct request_queue *q)
+{
+ unsigned int limit, set = 0;
+
+ if (!strncasecmp(off, buf, sizeof(off) - 2))
+ return set;
+ if (kstrtouint(buf, 0, &set) || (set > INT_MAX))
+ return -EINVAL;
+ if (set == 0)
+ return set;
+ limit = MAX_SPEED(q);
+ if (set > limit)
+ pr_warn("max speed %u ineffective above %u\n", set, limit);
+ limit = MIN_SPEED(q);
+ if (set < limit)
+ pr_warn("max speed %u painful below %u\n", set, limit);
+ return set;
+}
+
+static ssize_t max_write_speed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ int ret = max_speed_show(atomic_read(&md->queue.max_write_speed), buf);
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t max_write_speed_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ int set = max_speed_store(buf, md->queue.queue);
+
+ if (set < 0) {
+ mmc_blk_put(md);
+ return set;
+ }
+
+ atomic_set(&md->queue.max_write_speed, set);
+ mmc_blk_put(md);
+ return count;
+}
+
+static const DEVICE_ATTR(max_write_speed, S_IRUGO | S_IWUSR,
+ max_write_speed_show, max_write_speed_store);
+
+static ssize_t max_read_speed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ int ret = max_speed_show(atomic_read(&md->queue.max_read_speed), buf);
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t max_read_speed_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ int set = max_speed_store(buf, md->queue.queue);
+
+ if (set < 0) {
+ mmc_blk_put(md);
+ return set;
+ }
+
+ atomic_set(&md->queue.max_read_speed, set);
+ mmc_blk_put(md);
+ return count;
+}
+
+static const DEVICE_ATTR(max_read_speed, S_IRUGO | S_IWUSR,
+ max_read_speed_show, max_read_speed_store);
+
+static ssize_t cache_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ struct mmc_queue *mq = &md->queue;
+ int cache_size = atomic_read(&mq->cache_size);
+ int ret;
+
+ if (!cache_size)
+ ret = scnprintf(buf, PAGE_SIZE, off);
+ else {
+ int speed = atomic_read(&mq->max_write_speed);
+
+ if (!speed_valid(speed))
+ ret = scnprintf(buf, PAGE_SIZE, "%uMB\n", cache_size);
+ else { /* We accept race between cache_jiffies and cache_used */
+ unsigned long size = jiffies_and_speed_to_size(
+ jiffies - mq->cache_jiffies, speed);
+ long used = atomic_long_read(&mq->cache_used);
+
+ if (size >= used)
+ size = 0;
+ else
+ size = (used - size) * 100 / cache_size
+ / 1024UL / 1024UL;
+
+ ret = scnprintf(buf, PAGE_SIZE, "%uMB %lu%% used\n",
+ cache_size, size);
+ }
+ }
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t cache_size_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mmc_blk_data *md;
+ unsigned int set = 0;
+
+ if (strncasecmp(off, buf, sizeof(off) - 2)
+ && (kstrtouint(buf, 0, &set) || (set > INT_MAX)))
+ return -EINVAL;
+
+ md = mmc_blk_get(dev_to_disk(dev));
+ atomic_set(&md->queue.cache_size, set);
+ mmc_blk_put(md);
+ return count;
+}
+
+static const DEVICE_ATTR(cache_size, S_IRUGO | S_IWUSR,
+ cache_size_show, cache_size_store);
+
+/* correct for write-back */
+static long mmc_blk_cache_used(struct mmc_queue *mq, unsigned long waitfor)
+{
+ long used = 0;
+ int speed = atomic_read(&mq->max_write_speed);
+
+ if (speed_valid(speed)) {
+ unsigned long size = jiffies_and_speed_to_size(
+ waitfor - mq->cache_jiffies, speed);
+ used = atomic_long_read(&mq->cache_used);
+
+ if (size >= used)
+ used = 0;
+ else
+ used -= size;
+ }
+
+ atomic_long_set(&mq->cache_used, used);
+ mq->cache_jiffies = waitfor;
+
+ return used;
+}
+
+static void mmc_blk_simulate_delay(
+ struct mmc_queue *mq,
+ struct request *req,
+ unsigned long waitfor)
+{
+ int max_speed;
+
+ if (!req)
+ return;
+
+ max_speed = (rq_data_dir(req) == READ)
+ ? atomic_read(&mq->max_read_speed)
+ : atomic_read(&mq->max_write_speed);
+ if (speed_valid(max_speed)) {
+ unsigned long bytes = blk_rq_bytes(req);
+
+ if (rq_data_dir(req) != READ) {
+ int cache_size = atomic_read(&mq->cache_size);
+
+ if (cache_size) {
+ unsigned long size = cache_size * 1024L * 1024L;
+ long used = mmc_blk_cache_used(mq, waitfor);
+
+ used += bytes;
+ atomic_long_set(&mq->cache_used, used);
+ bytes = 0;
+ if (used > size)
+ bytes = used - size;
+ }
+ }
+ waitfor += size_and_speed_to_jiffies(bytes, max_speed);
+ if (time_is_after_jiffies(waitfor)) {
+ long msecs = jiffies_to_msecs(waitfor - jiffies);
+
+ if (likely(msecs > 0))
+ msleep(msecs);
+ }
+ }
+}
+
+#else
+
+#define mmc_blk_simulate_delay(mq, req, waitfor)
+
+#endif
static ssize_t
num_wr_reqs_to_start_packing_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -1870,6 +2112,23 @@ static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
}
end_req:
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
+ else if (atomic_read(&mq->cache_size)) {
+ long used = mmc_blk_cache_used(mq, jiffies);
+
+ if (used) {
+ int speed = atomic_read(&mq->max_write_speed);
+
+ if (speed_valid(speed)) {
+ unsigned long msecs = jiffies_to_msecs(
+ size_and_speed_to_jiffies(
+ used, speed));
+ if (msecs)
+ msleep(msecs);
+ }
+ }
+ }
+#endif
blk_end_request_all(req, ret);
return ret ? 0 : 1;
@@ -3346,6 +3605,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
struct mmc_async_req *areq;
const u8 packed_nr = 2;
u8 reqs = 0;
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
+ unsigned long waitfor = jiffies;
+#endif
if (!rqc && !mq->mqrq_prev->req)
return 0;
@@ -3396,6 +3658,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
*/
mmc_blk_reset_success(md, type);
+ mmc_blk_simulate_delay(mq, rqc, waitfor);
+
if (mmc_packed_cmd(mq_rq->cmd_type)) {
ret = mmc_blk_end_packed_req(mq_rq);
break;
@@ -3968,6 +4232,14 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
card->ext_csd.boot_ro_lockable)
device_remove_file(disk_to_dev(md->disk),
&md->power_ro_lock);
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
+ device_remove_file(disk_to_dev(md->disk),
+ &dev_attr_max_write_speed);
+ device_remove_file(disk_to_dev(md->disk),
+ &dev_attr_max_read_speed);
+ device_remove_file(disk_to_dev(md->disk),
+ &dev_attr_cache_size);
+#endif
del_gendisk(md->disk);
}
@@ -4003,6 +4275,24 @@ static int mmc_add_disk(struct mmc_blk_data *md)
ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);
if (ret)
goto force_ro_fail;
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
+ atomic_set(&md->queue.max_write_speed, max_write_speed);
+ ret = device_create_file(disk_to_dev(md->disk),
+ &dev_attr_max_write_speed);
+ if (ret)
+ goto max_write_speed_fail;
+ atomic_set(&md->queue.max_read_speed, max_read_speed);
+ ret = device_create_file(disk_to_dev(md->disk),
+ &dev_attr_max_read_speed);
+ if (ret)
+ goto max_read_speed_fail;
+ atomic_set(&md->queue.cache_size, cache_size);
+ atomic_long_set(&md->queue.cache_used, 0);
+ md->queue.cache_jiffies = jiffies;
+ ret = device_create_file(disk_to_dev(md->disk), &dev_attr_cache_size);
+ if (ret)
+ goto cache_size_fail;
+#endif
if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
card->ext_csd.boot_ro_lockable) {
@@ -4056,6 +4346,14 @@ no_pack_for_random_fails:
num_wr_reqs_to_start_packing_fail:
device_remove_file(disk_to_dev(md->disk), &md->power_ro_lock);
power_ro_lock_fail:
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
+ device_remove_file(disk_to_dev(md->disk), &dev_attr_cache_size);
+cache_size_fail:
+ device_remove_file(disk_to_dev(md->disk), &dev_attr_max_read_speed);
+max_read_speed_fail:
+ device_remove_file(disk_to_dev(md->disk), &dev_attr_max_write_speed);
+max_write_speed_fail:
+#endif
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
force_ro_fail:
del_gendisk(md->disk);
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index d8a33ac55c91..253979b51c84 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -77,6 +77,14 @@ struct mmc_queue {
int (*err_check_fn) (struct mmc_card *, struct mmc_async_req *);
void (*packed_test_fn) (struct request_queue *, struct mmc_queue_req *);
void (*cmdq_shutdown)(struct mmc_queue *);
+#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
+ atomic_t max_write_speed;
+ atomic_t max_read_speed;
+ atomic_t cache_size;
+ /* i/o tracking */
+ atomic_long_t cache_used;
+ unsigned long cache_jiffies;
+#endif
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index b76bb7a74049..3762f698e1ee 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -33,6 +33,7 @@
#include <linux/pm.h>
#include <linux/jiffies.h>
+#define CREATE_TRACE_POINTS
#include <trace/events/mmc.h>
#include <linux/mmc/card.h>
@@ -51,6 +52,11 @@
#include "sd_ops.h"
#include "sdio_ops.h"
+EXPORT_TRACEPOINT_SYMBOL_GPL(mmc_blk_erase_start);
+EXPORT_TRACEPOINT_SYMBOL_GPL(mmc_blk_erase_end);
+EXPORT_TRACEPOINT_SYMBOL_GPL(mmc_blk_rw_start);
+EXPORT_TRACEPOINT_SYMBOL_GPL(mmc_blk_rw_end);
+
/* If the device is not responding */
#define MMC_CORE_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 895dfbb7fbf5..e7ebc3cb8eda 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -22,6 +22,7 @@
#include "core.h"
#include "bus.h"
+#include "host.h"
#include "sd.h"
#include "sdio_bus.h"
#include "mmc_ops.h"
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 0568769fd94a..be3ccf2536d9 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -3707,6 +3707,19 @@ static void sdhci_msm_init(struct sdhci_host *host)
msm_host->pdata->pm_qos_data.latency);
}
+static unsigned int sdhci_msm_get_current_limit(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct sdhci_msm_slot_reg_data *curr_slot = msm_host->pdata->vreg_data;
+ u32 max_curr = 0;
+
+ if (curr_slot && curr_slot->vdd_data)
+ max_curr = curr_slot->vdd_data->hpm_uA;
+
+ return max_curr;
+}
+
static struct sdhci_ops sdhci_msm_ops = {
.crypto_engine_cfg = sdhci_msm_ice_cfg,
.crypto_cfg_reset = sdhci_msm_ice_cfg_reset,
@@ -3732,6 +3745,7 @@ static struct sdhci_ops sdhci_msm_ops = {
.init = sdhci_msm_init,
.pre_req = sdhci_msm_pre_req,
.post_req = sdhci_msm_post_req,
+ .get_current_limit = sdhci_msm_get_current_limit,
};
static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 14ed8e775e6a..0542ba51445f 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2337,9 +2337,10 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
switch (host->timing) {
/* HS400 tuning is done in HS200 mode */
case MMC_TIMING_MMC_HS400:
- err = -EINVAL;
- goto out_unlock;
-
+ if (!(mmc->caps2 & MMC_CAP2_HS400_POST_TUNING)) {
+ err = -EINVAL;
+ goto out_unlock;
+ }
case MMC_TIMING_MMC_HS200:
/*
* Periodic re-tuning for HS400 is not expected to be needed, so
diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig
index e4cf44b1e815..282aec4860eb 100644
--- a/drivers/net/ppp/Kconfig
+++ b/drivers/net/ppp/Kconfig
@@ -150,7 +150,7 @@ config PPPOL2TP
if TTY
config PPPOLAC
- bool "PPP on L2TP Access Concentrator"
+ tristate "PPP on L2TP Access Concentrator"
depends on PPP && INET
help
L2TP (RFC 2661) is a tunneling protocol widely used in virtual private
@@ -159,7 +159,7 @@ config PPPOLAC
fairly simple and suited for clients.
config PPPOPNS
- bool "PPP on PPTP Network Server"
+ tristate "PPP on PPTP Network Server"
depends on PPP && INET
help
PPTP (RFC 2637) is a tunneling protocol widely used in virtual private
diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c
index 1b8180cc1d4d..0184c96579e9 100644
--- a/drivers/net/ppp/pppolac.c
+++ b/drivers/net/ppp/pppolac.c
@@ -206,11 +206,10 @@ static void pppolac_xmit_core(struct work_struct *delivery_work)
while ((skb = skb_dequeue(&delivery_queue))) {
struct sock *sk_udp = skb->sk;
struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
- struct msghdr msg = {
- .msg_iov = (struct iovec *)&iov,
- .msg_iovlen = 1,
- .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
- };
+ struct msghdr msg = { 0 };
+
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iov, 1,
+ skb->len);
sk_udp->sk_prot->sendmsg(sk_udp, &msg, skb->len);
kfree_skb(skb);
}
diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c
index 568bb45cfeac..d9e06039794e 100644
--- a/drivers/net/ppp/pppopns.c
+++ b/drivers/net/ppp/pppopns.c
@@ -189,11 +189,10 @@ static void pppopns_xmit_core(struct work_struct *delivery_work)
while ((skb = skb_dequeue(&delivery_queue))) {
struct sock *sk_raw = skb->sk;
struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
- struct msghdr msg = {
- .msg_iov = (struct iovec *)&iov,
- .msg_iovlen = 1,
- .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
- };
+ struct msghdr msg = { 0 };
+
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iov, 1,
+ skb->len);
sk_raw->sk_prot->sendmsg(sk_raw, &msg, skb->len);
kfree_skb(skb);
}
diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c
index 801c94859084..5d9329168699 100644
--- a/drivers/net/wireless/cnss/cnss_pci.c
+++ b/drivers/net/wireless/cnss/cnss_pci.c
@@ -2761,14 +2761,8 @@ static void cnss_crash_shutdown(const struct subsys_desc *subsys)
wdrv = penv->driver;
pdev = penv->pdev;
- penv->dump_data.version = CNSS_DUMP_FORMAT_VER;
- strlcpy(penv->dump_data.name, CNSS_DUMP_NAME,
- sizeof(penv->dump_data.name));
-
if (pdev && wdrv && wdrv->crash_shutdown)
wdrv->crash_shutdown(pdev);
-
- penv->dump_data.magic = CNSS_DUMP_MAGIC_VER_V2;
}
void cnss_device_self_recovery(void)
@@ -2829,6 +2823,28 @@ static struct notifier_block mnb = {
.notifier_call = cnss_modem_notifier_nb,
};
+static int cnss_init_dump_entry(void)
+{
+ struct msm_dump_entry dump_entry;
+
+ if (!penv)
+ return -ENODEV;
+
+ if (!penv->ramdump_dynamic)
+ return 0;
+
+ penv->dump_data.addr = penv->ramdump_phys;
+ penv->dump_data.len = penv->ramdump_size;
+ penv->dump_data.version = CNSS_DUMP_FORMAT_VER;
+ penv->dump_data.magic = CNSS_DUMP_MAGIC_VER_V2;
+ strlcpy(penv->dump_data.name, CNSS_DUMP_NAME,
+ sizeof(penv->dump_data.name));
+ dump_entry.id = MSM_DUMP_DATA_CNSS_WLAN;
+ dump_entry.addr = virt_to_phys(&penv->dump_data);
+
+ return msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
+}
+
static int cnss_probe(struct platform_device *pdev)
{
int ret = 0;
@@ -2836,7 +2852,6 @@ static int cnss_probe(struct platform_device *pdev)
const char *client_desc;
struct device *dev = &pdev->dev;
u32 rc_num;
- struct msm_dump_entry dump_entry;
struct resource *res;
u32 ramdump_size = 0;
u32 smmu_iova_address[2];
@@ -2952,18 +2967,10 @@ static int cnss_probe(struct platform_device *pdev)
goto skip_ramdump;
}
- if (penv->ramdump_dynamic) {
- penv->dump_data.addr = penv->ramdump_phys;
- penv->dump_data.len = penv->ramdump_size;
- dump_entry.id = MSM_DUMP_DATA_CNSS_WLAN;
- dump_entry.addr = virt_to_phys(&penv->dump_data);
-
- ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
- if (ret) {
- pr_err("%s: Dump table setup failed: %d\n",
- __func__, ret);
- goto err_ramdump_create;
- }
+ ret = cnss_init_dump_entry();
+ if (ret) {
+ pr_err("%s: Dump table setup failed: %d\n", __func__, ret);
+ goto err_ramdump_create;
}
penv->ramdump_dev = create_ramdump_device(penv->subsysdesc.name,
diff --git a/drivers/net/wireless/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c
index f773c5993d44..01b969ec627f 100644
--- a/drivers/net/wireless/cnss/cnss_sdio.c
+++ b/drivers/net/wireless/cnss/cnss_sdio.c
@@ -21,6 +21,8 @@
#include <linux/slab.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
#include <linux/io.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
@@ -46,7 +48,7 @@
/* Values for Dynamic Ramdump Collection*/
#define CNSS_DUMP_FORMAT_VER 0x11
#define CNSS_DUMP_MAGIC_VER_V2 0x42445953
-#define CNSS_DUMP_NAME "CNSS_WLAN"
+#define CNSS_DUMP_NAME "CNSS_WLAN_SDIO"
#define CNSS_PINCTRL_SLEEP_STATE "sleep"
#define CNSS_PINCTRL_ACTIVE_STATE "active"
@@ -60,7 +62,11 @@ struct cnss_sdio_regulator {
struct cnss_sdio_info {
struct cnss_sdio_wlan_driver *wdrv;
struct sdio_func *func;
+ struct mmc_card *card;
+ struct mmc_host *host;
+ struct device *dev;
const struct sdio_device_id *id;
+ bool skip_wlan_en_toggle;
};
struct cnss_ssr_info {
@@ -212,19 +218,103 @@ void cnss_sdio_remove_pm_qos(void)
}
EXPORT_SYMBOL(cnss_sdio_remove_pm_qos);
+static int cnss_put_hw_resources(struct device *dev)
+{
+ int ret = -EINVAL;
+ struct cnss_sdio_info *info;
+ struct mmc_host *host;
+
+ if (!cnss_pdata)
+ return ret;
+
+ info = &cnss_pdata->cnss_sdio_info;
+
+ if (info->skip_wlan_en_toggle) {
+ pr_debug("%s: HW doesn't support wlan toggling\n", __func__);
+ return 0;
+ }
+
+ host = info->host;
+
+ if (!host) {
+ pr_err("%s: MMC host is invalid\n", __func__);
+ return 0;
+ }
+
+ ret = mmc_power_save_host(host);
+ if (ret) {
+ pr_err("%s: Failed to Power Save Host err:%d\n", __func__,
+ ret);
+ return ret;
+ }
+
+ if (!cnss_pdata->regulator.wlan_vreg) {
+ pr_debug("%s: wlan_vreg regulator is invalid\n", __func__);
+ return ret;
+ }
+
+ regulator_disable(cnss_pdata->regulator.wlan_vreg);
+
+ return ret;
+}
+
+static int cnss_get_hw_resources(struct device *dev)
+{
+ int ret = -EINVAL;
+ struct mmc_host *host;
+ struct cnss_sdio_info *info;
+
+ if (!cnss_pdata)
+ return ret;
+
+ info = &cnss_pdata->cnss_sdio_info;
+
+ if (info->skip_wlan_en_toggle) {
+ pr_debug("%s: HW doesn't support wlan toggling\n", __func__);
+ return 0;
+ }
+
+ host = info->host;
+
+ ret = regulator_enable(cnss_pdata->regulator.wlan_vreg);
+ if (ret) {
+ pr_err("%s: Failed to enable wlan vreg\n", __func__);
+ return ret;
+ }
+
+ ret = mmc_power_restore_host(host);
+ if (ret) {
+ pr_err("%s: Failed to restore host power ret:%d\n", __func__,
+ ret);
+ regulator_disable(cnss_pdata->regulator.wlan_vreg);
+ }
+
+ return ret;
+}
+
static int cnss_sdio_shutdown(const struct subsys_desc *subsys, bool force_stop)
{
struct cnss_sdio_info *cnss_info;
struct cnss_sdio_wlan_driver *wdrv;
+ int ret = 0;
if (!cnss_pdata)
return -ENODEV;
cnss_info = &cnss_pdata->cnss_sdio_info;
wdrv = cnss_info->wdrv;
- if (wdrv && wdrv->shutdown)
- wdrv->shutdown(cnss_info->func);
- return 0;
+ if (!wdrv)
+ return 0;
+ if (!wdrv->shutdown)
+ return 0;
+
+ wdrv->shutdown(cnss_info->func);
+ ret = cnss_put_hw_resources(cnss_info->dev);
+
+ if (ret)
+ pr_err("%s: Failed to put hw resources\n", __func__);
+
+ return ret;
}
static int cnss_sdio_powerup(const struct subsys_desc *subsys)
@@ -238,11 +328,23 @@ static int cnss_sdio_powerup(const struct subsys_desc *subsys)
cnss_info = &cnss_pdata->cnss_sdio_info;
wdrv = cnss_info->wdrv;
- if (wdrv && wdrv->reinit) {
- ret = wdrv->reinit(cnss_info->func, cnss_info->id);
- if (ret)
- pr_err("%s: wlan reinit error=%d\n", __func__, ret);
+
+ if (!wdrv)
+ return 0;
+
+ if (!wdrv->reinit)
+ return 0;
+
+ ret = cnss_get_hw_resources(cnss_info->dev);
+ if (ret) {
+ pr_err("%s: Failed to power up HW\n", __func__);
+ return ret;
}
+
+ ret = wdrv->reinit(cnss_info->func, cnss_info->id);
+ if (ret)
+ pr_err("%s: wlan reinit error=%d\n", __func__, ret);
+
return ret;
}
@@ -551,25 +653,41 @@ int cnss_get_restart_level(void)
}
EXPORT_SYMBOL(cnss_get_restart_level);
-static int cnss_sdio_wlan_inserted(
- struct sdio_func *func,
- const struct sdio_device_id *id)
+static int cnss_sdio_wlan_inserted(struct sdio_func *func,
+ const struct sdio_device_id *id)
{
+ struct cnss_sdio_info *info;
+
if (!cnss_pdata)
return -ENODEV;
- cnss_pdata->cnss_sdio_info.func = func;
- cnss_pdata->cnss_sdio_info.id = id;
+ info = &cnss_pdata->cnss_sdio_info;
+
+ info->func = func;
+ info->card = func->card;
+ info->host = func->card->host;
+ info->id = id;
+ info->dev = &func->dev;
+
+ cnss_put_hw_resources(cnss_pdata->cnss_sdio_info.dev);
+
+ pr_info("%s: SDIO Device is Probed\n", __func__);
return 0;
}
static void cnss_sdio_wlan_removed(struct sdio_func *func)
{
+ struct cnss_sdio_info *info;
+
if (!cnss_pdata)
return;
- cnss_pdata->cnss_sdio_info.func = NULL;
- cnss_pdata->cnss_sdio_info.id = NULL;
+ info = &cnss_pdata->cnss_sdio_info;
+
+ info->host = NULL;
+ info->card = NULL;
+ info->func = NULL;
+ info->id = NULL;
}
#if defined(CONFIG_PM)
@@ -577,6 +695,8 @@ static int cnss_sdio_wlan_suspend(struct device *dev)
{
struct cnss_sdio_wlan_driver *wdrv;
struct cnss_sdio_bus_bandwidth *bus_bandwidth;
+ struct sdio_func *func;
+
int error = 0;
if (!cnss_pdata)
@@ -588,11 +708,13 @@ static int cnss_sdio_wlan_suspend(struct device *dev)
bus_bandwidth->bus_client, CNSS_BUS_WIDTH_NONE);
}
+ func = cnss_pdata->cnss_sdio_info.func;
wdrv = cnss_pdata->cnss_sdio_info.wdrv;
if (!wdrv) {
/* This can happen when no wlan driver loaded (no register to
* platform driver).
*/
+ sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
pr_debug("wlan driver not registered\n");
return 0;
}
@@ -692,29 +814,49 @@ EXPORT_SYMBOL(cnss_sdio_configure_spdt);
int cnss_sdio_wlan_register_driver(struct cnss_sdio_wlan_driver *driver)
{
struct cnss_sdio_info *cnss_info;
- int error = 0;
+ struct device *dev;
+ int error = -EINVAL;
if (!cnss_pdata)
return -ENODEV;
cnss_info = &cnss_pdata->cnss_sdio_info;
+ dev = cnss_info->dev;
+
if (cnss_info->wdrv)
pr_debug("%s:wdrv already exists wdrv(%p)\n", __func__,
cnss_info->wdrv);
+ cnss_info->wdrv = driver;
+
+ if (!driver)
+ return error;
+
+ error = cnss_get_hw_resources(dev);
+ if (error) {
+ pr_err("%s: Failed to restore power err:%d\n", __func__, error);
+ return error;
+ }
+
error = cnss_set_pinctrl_state(cnss_pdata, PINCTRL_ACTIVE);
if (error) {
pr_err("%s: Fail to set pinctrl to active state\n", __func__);
- return -EFAULT;
+ goto put_hw;
}
- cnss_info->wdrv = driver;
- if (driver->probe) {
- error = driver->probe(cnss_info->func, cnss_info->id);
- if (error)
- pr_err("%s: wlan probe failed error=%d\n", __func__,
- error);
+ error = driver->probe ? driver->probe(cnss_info->func,
+ cnss_info->id) : error;
+ if (error) {
+ pr_err("%s: wlan probe failed error=%d\n", __func__, error);
+ goto pinctrl_sleep;
}
+
+ return error;
+
+pinctrl_sleep:
+ cnss_set_pinctrl_state(cnss_pdata, PINCTRL_SLEEP);
+put_hw:
+ cnss_put_hw_resources(dev);
return error;
}
EXPORT_SYMBOL(cnss_sdio_wlan_register_driver);
@@ -746,10 +888,17 @@ cnss_sdio_wlan_unregister_driver(struct cnss_sdio_wlan_driver *driver)
pr_err("%s: driver not registered\n", __func__);
return;
}
- if (cnss_info->wdrv->remove)
- cnss_info->wdrv->remove(cnss_info->func);
+
+ if (!driver)
+ return;
+
+ if (!driver->remove)
+ return;
+
+ driver->remove(cnss_info->func);
cnss_info->wdrv = NULL;
cnss_set_pinctrl_state(cnss_pdata, PINCTRL_SLEEP);
+ cnss_put_hw_resources(cnss_info->dev);
}
EXPORT_SYMBOL(cnss_sdio_wlan_unregister_driver);
@@ -1051,6 +1200,8 @@ static int cnss_sdio_init_bus_bandwidth(void)
static int cnss_sdio_probe(struct platform_device *pdev)
{
int error;
+ struct device *dev = &pdev->dev;
+ struct cnss_sdio_info *info;
if (pdev->dev.of_node) {
cnss_pdata = devm_kzalloc(
@@ -1065,6 +1216,7 @@ static int cnss_sdio_probe(struct platform_device *pdev)
return -EINVAL;
cnss_pdata->pdev = pdev;
+ info = &cnss_pdata->cnss_sdio_info;
error = cnss_sdio_pinctrl_init(cnss_pdata, pdev);
if (error) {
@@ -1103,6 +1255,9 @@ static int cnss_sdio_probe(struct platform_device *pdev)
}
}
+ info->skip_wlan_en_toggle = of_property_read_bool(dev->of_node,
+ "qcom,skip-wlan-en-toggle");
+
error = cnss_sdio_wlan_init();
if (error) {
dev_err(&pdev->dev, "cnss wlan init failed error=%d\n", error);
@@ -1152,15 +1307,20 @@ err_wlan_enable_regulator:
static int cnss_sdio_remove(struct platform_device *pdev)
{
+ struct cnss_sdio_info *info;
+
if (!cnss_pdata)
return -ENODEV;
+ info = &cnss_pdata->cnss_sdio_info;
+
cnss_sdio_deinit_bus_bandwidth();
cnss_sdio_wlan_exit();
cnss_subsys_exit();
cnss_ramdump_cleanup();
+ cnss_put_hw_resources(info->dev);
cnss_sdio_release_resource();
-
+ cnss_pdata = NULL;
return 0;
}
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index e278aab1e530..2909443a62ca 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -501,6 +501,7 @@ struct msm_pcie_clk_info_t {
struct clk *hdl;
char *name;
u32 freq;
+ bool config_mem;
bool required;
};
@@ -710,49 +711,49 @@ static struct msm_pcie_gpio_info_t msm_pcie_gpio_info[MSM_PCIE_MAX_GPIO] = {
static struct msm_pcie_clk_info_t
msm_pcie_clk_info[MAX_RC_NUM][MSM_PCIE_MAX_CLK] = {
{
- {NULL, "pcie_0_ref_clk_src", 0, false},
- {NULL, "pcie_0_aux_clk", 1010000, true},
- {NULL, "pcie_0_cfg_ahb_clk", 0, true},
- {NULL, "pcie_0_mstr_axi_clk", 0, true},
- {NULL, "pcie_0_slv_axi_clk", 0, true},
- {NULL, "pcie_0_ldo", 0, true},
- {NULL, "pcie_0_smmu_clk", 0, false},
- {NULL, "pcie_phy_cfg_ahb_clk", 0, false},
- {NULL, "pcie_phy_aux_clk", 0, false},
- {NULL, "pcie_phy_reset", 0, false},
- {NULL, "pcie_phy_com_reset", 0, false},
- {NULL, "pcie_phy_nocsr_com_phy_reset", 0, false},
- {NULL, "pcie_0_phy_reset", 0, true}
+ {NULL, "pcie_0_ref_clk_src", 0, false, false},
+ {NULL, "pcie_0_aux_clk", 1010000, false, true},
+ {NULL, "pcie_0_cfg_ahb_clk", 0, false, true},
+ {NULL, "pcie_0_mstr_axi_clk", 0, true, true},
+ {NULL, "pcie_0_slv_axi_clk", 0, true, true},
+ {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_1_ref_clk_src", 0, false},
- {NULL, "pcie_1_aux_clk", 1010000, true},
- {NULL, "pcie_1_cfg_ahb_clk", 0, true},
- {NULL, "pcie_1_mstr_axi_clk", 0, true},
- {NULL, "pcie_1_slv_axi_clk", 0, true},
- {NULL, "pcie_1_ldo", 0, true},
- {NULL, "pcie_1_smmu_clk", 0, false},
- {NULL, "pcie_phy_cfg_ahb_clk", 0, false},
- {NULL, "pcie_phy_aux_clk", 0, false},
- {NULL, "pcie_phy_reset", 0, false},
- {NULL, "pcie_phy_com_reset", 0, false},
- {NULL, "pcie_phy_nocsr_com_phy_reset", 0, false},
- {NULL, "pcie_1_phy_reset", 0, true}
+ {NULL, "pcie_1_ref_clk_src", 0, false, false},
+ {NULL, "pcie_1_aux_clk", 1010000, false, true},
+ {NULL, "pcie_1_cfg_ahb_clk", 0, false, true},
+ {NULL, "pcie_1_mstr_axi_clk", 0, true, true},
+ {NULL, "pcie_1_slv_axi_clk", 0, true, true},
+ {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_2_ref_clk_src", 0, false},
- {NULL, "pcie_2_aux_clk", 1010000, true},
- {NULL, "pcie_2_cfg_ahb_clk", 0, true},
- {NULL, "pcie_2_mstr_axi_clk", 0, true},
- {NULL, "pcie_2_slv_axi_clk", 0, true},
- {NULL, "pcie_2_ldo", 0, true},
- {NULL, "pcie_2_smmu_clk", 0, false},
- {NULL, "pcie_phy_cfg_ahb_clk", 0, false},
- {NULL, "pcie_phy_aux_clk", 0, false},
- {NULL, "pcie_phy_reset", 0, false},
- {NULL, "pcie_phy_com_reset", 0, false},
- {NULL, "pcie_phy_nocsr_com_phy_reset", 0, false},
- {NULL, "pcie_2_phy_reset", 0, true}
+ {NULL, "pcie_2_ref_clk_src", 0, false, false},
+ {NULL, "pcie_2_aux_clk", 1010000, false, true},
+ {NULL, "pcie_2_cfg_ahb_clk", 0, false, true},
+ {NULL, "pcie_2_mstr_axi_clk", 0, true, true},
+ {NULL, "pcie_2_slv_axi_clk", 0, true, true},
+ {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}
}
};
@@ -760,13 +761,13 @@ static struct msm_pcie_clk_info_t
static struct msm_pcie_clk_info_t
msm_pcie_pipe_clk_info[MAX_RC_NUM][MSM_PCIE_MAX_PIPE_CLK] = {
{
- {NULL, "pcie_0_pipe_clk", 125000000, true},
+ {NULL, "pcie_0_pipe_clk", 125000000, true, true},
},
{
- {NULL, "pcie_1_pipe_clk", 125000000, true},
+ {NULL, "pcie_1_pipe_clk", 125000000, true, true},
},
{
- {NULL, "pcie_2_pipe_clk", 125000000, true},
+ {NULL, "pcie_2_pipe_clk", 125000000, true, true},
}
};
@@ -861,6 +862,32 @@ static inline void msm_pcie_write_reg_field(void *base, u32 offset,
wmb();
}
+static inline void msm_pcie_config_clock_mem(struct msm_pcie_dev_t *dev,
+ struct msm_pcie_clk_info_t *info)
+{
+ int ret;
+
+ ret = clk_set_flags(info->hdl, CLKFLAG_NORETAIN_MEM);
+ if (ret)
+ PCIE_ERR(dev,
+ "PCIe: RC%d can't configure core memory for clk %s: %d.\n",
+ dev->rc_idx, info->name, ret);
+ else
+ PCIE_DBG2(dev,
+ "PCIe: RC%d configured core memory for clk %s.\n",
+ dev->rc_idx, info->name);
+
+ ret = clk_set_flags(info->hdl, CLKFLAG_NORETAIN_PERIPH);
+ if (ret)
+ PCIE_ERR(dev,
+ "PCIe: RC%d can't configure peripheral memory for clk %s: %d.\n",
+ dev->rc_idx, info->name, ret);
+ else
+ PCIE_DBG2(dev,
+ "PCIe: RC%d configured peripheral memory for clk %s.\n",
+ dev->rc_idx, info->name);
+}
+
#if defined(CONFIG_ARCH_FSM9010)
#define PCIE20_PARF_PHY_STTS 0x3c
#define PCIE2_PHY_RESET_CTRL 0x44
@@ -3255,7 +3282,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);
@@ -3450,6 +3477,9 @@ static int msm_pcie_clk_init(struct msm_pcie_dev_t *dev)
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);
+
if (info->freq) {
rc = clk_set_rate(info->hdl, info->freq);
if (rc) {
@@ -3543,6 +3573,9 @@ static int msm_pcie_pipe_clk_init(struct msm_pcie_dev_t *dev)
clk_reset(info->hdl, CLK_RESET_DEASSERT);
+ if (info->config_mem)
+ msm_pcie_config_clock_mem(dev, info);
+
if (info->freq) {
rc = clk_set_rate(info->hdl, info->freq);
if (rc) {
@@ -3921,8 +3954,8 @@ static int msm_pcie_get_resources(struct msm_pcie_dev_t *dev,
cnt = of_property_count_strings((&pdev->dev)->of_node,
"clock-names");
if (cnt > 0) {
- clkfreq = kzalloc(cnt * sizeof(*clkfreq),
- GFP_KERNEL);
+ clkfreq = kzalloc((MSM_PCIE_MAX_CLK + MSM_PCIE_MAX_PIPE_CLK) *
+ sizeof(*clkfreq), GFP_KERNEL);
if (!clkfreq) {
PCIE_ERR(dev, "PCIe: memory alloc failed for RC%d\n",
dev->rc_idx);
diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h
index 35179c8be471..7acef104d5b7 100644
--- a/drivers/phy/phy-qcom-ufs-i.h
+++ b/drivers/phy/phy-qcom-ufs-i.h
@@ -152,6 +152,7 @@ struct ufs_qcom_phy {
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
* @configure_lpm: pointer to a function that configures the phy
* for low power mode.
+ * @dbg_register_dump: pointer to a function that dumps phy registers for debug.
*/
struct ufs_qcom_phy_specific_ops {
int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B);
@@ -161,6 +162,7 @@ struct ufs_qcom_phy_specific_ops {
void (*ctrl_rx_linecfg)(struct ufs_qcom_phy *phy, bool ctrl);
void (*power_control)(struct ufs_qcom_phy *phy, bool val);
int (*configure_lpm)(struct ufs_qcom_phy *phy, bool enable);
+ void (*dbg_register_dump)(struct ufs_qcom_phy *phy);
};
struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy);
@@ -184,5 +186,6 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
void ufs_qcom_phy_write_tbl(struct ufs_qcom_phy *ufs_qcom_phy,
struct ufs_qcom_phy_calibration *tbl,
int tbl_size);
-
+void ufs_qcom_phy_dump_regs(struct ufs_qcom_phy *phy,
+ int offset, int len, char *prefix);
#endif
diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3.c b/drivers/phy/phy-qcom-ufs-qmp-v3.c
index 57c23f70eb63..a9ad3a6f87cc 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-v3.c
+++ b/drivers/phy/phy-qcom-ufs-qmp-v3.c
@@ -194,6 +194,22 @@ out:
return err;
}
+static void ufs_qcom_phy_qmp_v3_dbg_register_dump(struct ufs_qcom_phy *phy)
+{
+ ufs_qcom_phy_dump_regs(phy, COM_BASE, COM_SIZE,
+ "PHY QSERDES COM Registers ");
+ ufs_qcom_phy_dump_regs(phy, PHY_BASE, PHY_SIZE,
+ "PHY Registers ");
+ ufs_qcom_phy_dump_regs(phy, RX_BASE(0), RX_SIZE,
+ "PHY RX0 Registers ");
+ ufs_qcom_phy_dump_regs(phy, TX_BASE(0), TX_SIZE,
+ "PHY TX0 Registers ");
+ ufs_qcom_phy_dump_regs(phy, RX_BASE(1), RX_SIZE,
+ "PHY RX1 Registers ");
+ ufs_qcom_phy_dump_regs(phy, TX_BASE(1), TX_SIZE,
+ "PHY TX1 Registers ");
+}
+
struct phy_ops ufs_qcom_phy_qmp_v3_phy_ops = {
.init = ufs_qcom_phy_qmp_v3_init,
.exit = ufs_qcom_phy_exit,
@@ -210,6 +226,7 @@ struct ufs_qcom_phy_specific_ops phy_v3_ops = {
.ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg,
.power_control = ufs_qcom_phy_qmp_v3_power_control,
.configure_lpm = ufs_qcom_phy_qmp_v3_configure_lpm,
+ .dbg_register_dump = ufs_qcom_phy_qmp_v3_dbg_register_dump,
};
static int ufs_qcom_phy_qmp_v3_probe(struct platform_device *pdev)
diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3.h b/drivers/phy/phy-qcom-ufs-qmp-v3.h
index cda57855acb5..8b1e03eab639 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-v3.h
+++ b/drivers/phy/phy-qcom-ufs-qmp-v3.h
@@ -18,10 +18,18 @@
#include "phy-qcom-ufs-i.h"
/* QCOM UFS PHY control registers */
-#define COM_OFF(x) (0x000 + x)
-#define PHY_OFF(x) (0xC00 + x)
-#define TX_OFF(n, x) (0x400 + (0x400 * n) + x)
-#define RX_OFF(n, x) (0x600 + (0x400 * n) + x)
+#define COM_BASE 0x000
+#define COM_SIZE 0x18C
+#define PHY_BASE 0xC00
+#define PHY_SIZE 0x1DC
+#define TX_BASE(n) (0x400 + (0x400 * n))
+#define TX_SIZE 0x128
+#define RX_BASE(n) (0x600 + (0x400 * n))
+#define RX_SIZE 0x1FC
+#define COM_OFF(x) (COM_BASE + x)
+#define PHY_OFF(x) (PHY_BASE + x)
+#define TX_OFF(n, x) (TX_BASE(n) + x)
+#define RX_OFF(n, x) (RX_BASE(n) + x)
/* UFS PHY QSERDES COM registers */
#define QSERDES_COM_ATB_SEL1 COM_OFF(0x00)
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
index de32b75f4f57..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);
@@ -788,3 +781,21 @@ int ufs_qcom_phy_configure_lpm(struct phy *generic_phy, bool enable)
return ret;
}
EXPORT_SYMBOL(ufs_qcom_phy_configure_lpm);
+
+void ufs_qcom_phy_dump_regs(struct ufs_qcom_phy *phy, int offset,
+ int len, char *prefix)
+{
+ print_hex_dump(KERN_ERR, prefix,
+ len > 4 ? DUMP_PREFIX_OFFSET : DUMP_PREFIX_NONE,
+ 16, 4, phy->mmio + offset, len, false);
+}
+EXPORT_SYMBOL(ufs_qcom_phy_dump_regs);
+
+void ufs_qcom_phy_dbg_register_dump(struct phy *generic_phy)
+{
+ struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+
+ if (ufs_qcom_phy->phy_spec_ops->dbg_register_dump)
+ ufs_qcom_phy->phy_spec_ops->dbg_register_dump(ufs_qcom_phy);
+}
+EXPORT_SYMBOL(ufs_qcom_phy_dbg_register_dump);
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index 6c42ca14d2fd..9753fbb596cf 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014, 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
@@ -14,6 +14,7 @@
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinmux.h>
@@ -39,6 +40,8 @@
#define PMIC_GPIO_SUBTYPE_GPIOC_4CH 0x5
#define PMIC_GPIO_SUBTYPE_GPIO_8CH 0x9
#define PMIC_GPIO_SUBTYPE_GPIOC_8CH 0xd
+#define PMIC_GPIO_SUBTYPE_GPIO_LV 0x10
+#define PMIC_GPIO_SUBTYPE_GPIO_MV 0x11
#define PMIC_MPP_REG_RT_STS 0x10
#define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1
@@ -47,8 +50,11 @@
#define PMIC_GPIO_REG_MODE_CTL 0x40
#define PMIC_GPIO_REG_DIG_VIN_CTL 0x41
#define PMIC_GPIO_REG_DIG_PULL_CTL 0x42
+#define PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL 0x44
+#define PMIC_GPIO_REG_DIG_IN_CTL 0x43
#define PMIC_GPIO_REG_DIG_OUT_CTL 0x45
#define PMIC_GPIO_REG_EN_CTL 0x46
+#define PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL 0x4A
/* PMIC_GPIO_REG_MODE_CTL */
#define PMIC_GPIO_REG_MODE_VALUE_SHIFT 0x1
@@ -57,6 +63,13 @@
#define PMIC_GPIO_REG_MODE_DIR_SHIFT 4
#define PMIC_GPIO_REG_MODE_DIR_MASK 0x7
+#define PMIC_GPIO_MODE_DIGITAL_INPUT 0
+#define PMIC_GPIO_MODE_DIGITAL_OUTPUT 1
+#define PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT 2
+#define PMIC_GPIO_MODE_ANALOG_PASS_THRU 3
+
+#define PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK 0x3
+
/* PMIC_GPIO_REG_DIG_VIN_CTL */
#define PMIC_GPIO_REG_VIN_SHIFT 0
#define PMIC_GPIO_REG_VIN_MASK 0x7
@@ -68,6 +81,16 @@
#define PMIC_GPIO_PULL_DOWN 4
#define PMIC_GPIO_PULL_DISABLE 5
+/* PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL for LV/MV */
+#define PMIC_GPIO_LV_MV_OUTPUT_INVERT 0x80
+#define PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT 7
+#define PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK 0xF
+
+/* PMIC_GPIO_REG_DIG_IN_CTL */
+#define PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN 0x80
+#define PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK 0x7
+#define PMIC_GPIO_DIG_IN_DTEST_SEL_MASK 0xf
+
/* PMIC_GPIO_REG_DIG_OUT_CTL */
#define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT 0
#define PMIC_GPIO_REG_OUT_STRENGTH_MASK 0x3
@@ -87,9 +110,29 @@
#define PMIC_GPIO_PHYSICAL_OFFSET 1
+/* PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL */
+#define PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK 0x3
+
/* Qualcomm specific pin configurations */
#define PMIC_GPIO_CONF_PULL_UP (PIN_CONFIG_END + 1)
#define PMIC_GPIO_CONF_STRENGTH (PIN_CONFIG_END + 2)
+#define PMIC_GPIO_CONF_ATEST (PIN_CONFIG_END + 3)
+#define PMIC_GPIO_CONF_DTEST_BUFFER (PIN_CONFIG_END + 4)
+
+/* The index of each function in pmic_gpio_functions[] array */
+enum pmic_gpio_func_index {
+ PMIC_GPIO_FUNC_INDEX_NORMAL = 0x00,
+ PMIC_GPIO_FUNC_INDEX_PAIRED = 0x01,
+ PMIC_GPIO_FUNC_INDEX_FUNC1 = 0x02,
+ PMIC_GPIO_FUNC_INDEX_FUNC2 = 0x03,
+ PMIC_GPIO_FUNC_INDEX_FUNC3 = 0x04,
+ PMIC_GPIO_FUNC_INDEX_FUNC4 = 0x05,
+ PMIC_GPIO_FUNC_INDEX_DTEST1 = 0x06,
+ PMIC_GPIO_FUNC_INDEX_DTEST2 = 0x07,
+ PMIC_GPIO_FUNC_INDEX_DTEST3 = 0x08,
+ PMIC_GPIO_FUNC_INDEX_DTEST4 = 0x09,
+ PMIC_GPIO_FUNC_INDEX_ANALOG = 0x10,
+};
/**
* struct pmic_gpio_pad - keep current GPIO settings
@@ -101,12 +144,16 @@
* open-drain or open-source mode.
* @output_enabled: Set to true if GPIO output logic is enabled.
* @input_enabled: Set to true if GPIO input buffer logic is enabled.
+ * @lv_mv_type: Set to true if GPIO subtype is GPIO_LV(0x10) or GPIO_MV(0x11).
* @num_sources: Number of power-sources supported by this GPIO.
* @power_source: Current power-source used.
* @buffer_type: Push-pull, open-drain or open-source.
* @pullup: Constant current which flow trough GPIO output buffer.
* @strength: No, Low, Medium, High
* @function: See pmic_gpio_functions[]
+ * @atest: the ATEST selection for GPIO analog-pass-through mode
+ * @dtest_buffer: the DTEST buffer selection for digital input mode,
+ * the default value is INT_MAX if not used.
*/
struct pmic_gpio_pad {
u16 base;
@@ -116,12 +163,15 @@ struct pmic_gpio_pad {
bool have_buffer;
bool output_enabled;
bool input_enabled;
+ bool lv_mv_type;
unsigned int num_sources;
unsigned int power_source;
unsigned int buffer_type;
unsigned int pullup;
unsigned int strength;
unsigned int function;
+ unsigned int atest;
+ unsigned int dtest_buffer;
};
struct pmic_gpio_state {
@@ -134,12 +184,15 @@ struct pmic_gpio_state {
static const struct pinconf_generic_params pmic_gpio_bindings[] = {
{"qcom,pull-up-strength", PMIC_GPIO_CONF_PULL_UP, 0},
{"qcom,drive-strength", PMIC_GPIO_CONF_STRENGTH, 0},
+ {"qcom,atest", PMIC_GPIO_CONF_ATEST, 0},
+ {"qcom,dtest-buffer", PMIC_GPIO_CONF_DTEST_BUFFER, 0},
};
#ifdef CONFIG_DEBUG_FS
static const struct pin_config_item pmic_conf_items[ARRAY_SIZE(pmic_gpio_bindings)] = {
PCONFDUMP(PMIC_GPIO_CONF_PULL_UP, "pull up strength", NULL, true),
PCONFDUMP(PMIC_GPIO_CONF_STRENGTH, "drive-strength", NULL, true),
+ PCONFDUMP(PMIC_GPIO_CONF_ATEST, "atest", NULL, true),
};
#endif
@@ -151,11 +204,25 @@ static const char *const pmic_gpio_groups[] = {
"gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
};
+/*
+ * Treat LV/MV GPIO analog-pass-through mode as a function, add it
+ * to the end of the function list. Add placeholder for the reserved
+ * functions defined in LV/MV OUTPUT_SOURCE_SEL register.
+ */
static const char *const pmic_gpio_functions[] = {
- PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED,
- PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2,
- PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2,
- PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4,
+ [PMIC_GPIO_FUNC_INDEX_NORMAL] = PMIC_GPIO_FUNC_NORMAL,
+ [PMIC_GPIO_FUNC_INDEX_PAIRED] = PMIC_GPIO_FUNC_PAIRED,
+ [PMIC_GPIO_FUNC_INDEX_FUNC1] = PMIC_GPIO_FUNC_FUNC1,
+ [PMIC_GPIO_FUNC_INDEX_FUNC2] = PMIC_GPIO_FUNC_FUNC2,
+ [PMIC_GPIO_FUNC_INDEX_FUNC3] = PMIC_GPIO_FUNC_FUNC3,
+ [PMIC_GPIO_FUNC_INDEX_FUNC4] = PMIC_GPIO_FUNC_FUNC4,
+ [PMIC_GPIO_FUNC_INDEX_DTEST1] = PMIC_GPIO_FUNC_DTEST1,
+ [PMIC_GPIO_FUNC_INDEX_DTEST2] = PMIC_GPIO_FUNC_DTEST2,
+ [PMIC_GPIO_FUNC_INDEX_DTEST3] = PMIC_GPIO_FUNC_DTEST3,
+ [PMIC_GPIO_FUNC_INDEX_DTEST4] = PMIC_GPIO_FUNC_DTEST4,
+ "reserved-a", "reserved-b", "reserved-c",
+ "reserved-d", "reserved-e", "reserved-f",
+ [PMIC_GPIO_FUNC_INDEX_ANALOG] = PMIC_GPIO_FUNC_ANALOG,
};
static inline struct pmic_gpio_state *to_gpio_state(struct gpio_chip *chip)
@@ -252,21 +319,74 @@ static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function,
pad->function = function;
- val = 0;
+ val = PMIC_GPIO_MODE_DIGITAL_INPUT;
if (pad->output_enabled) {
if (pad->input_enabled)
- val = 2;
+ val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT;
else
- val = 1;
+ val = PMIC_GPIO_MODE_DIGITAL_OUTPUT;
+ }
+
+ if (function > PMIC_GPIO_FUNC_INDEX_DTEST4 &&
+ function < PMIC_GPIO_FUNC_INDEX_ANALOG) {
+ pr_err("reserved function: %s hasn't been enabled\n",
+ pmic_gpio_functions[function]);
+ return -EINVAL;
}
- val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
- val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
- val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
+ if (pad->lv_mv_type) {
+ if (pad->function == PMIC_GPIO_FUNC_INDEX_ANALOG) {
+ val = PMIC_GPIO_MODE_ANALOG_PASS_THRU;
+ ret = pmic_gpio_write(state, pad,
+ PMIC_GPIO_REG_MODE_CTL, val);
+ if (ret < 0)
+ return ret;
- ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
- if (ret < 0)
- return ret;
+ ret = pmic_gpio_write(state, pad,
+ PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL,
+ pad->atest);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = pmic_gpio_write(state, pad,
+ PMIC_GPIO_REG_MODE_CTL, val);
+ if (ret < 0)
+ return ret;
+
+ val = pad->out_value
+ << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT;
+ val |= pad->function
+ & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
+ ret = pmic_gpio_write(state, pad,
+ PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val);
+ if (ret < 0)
+ return ret;
+ }
+ } else {
+ /*
+ * GPIO not of LV/MV subtype doesn't have "func3", "func4"
+ * "analog" functions, and "dtest1" to "dtest4" functions
+ * have register value 2 bits lower than the function index
+ * in pmic_gpio_functions[].
+ */
+ if (function == PMIC_GPIO_FUNC_INDEX_FUNC3
+ || function == PMIC_GPIO_FUNC_INDEX_FUNC4
+ || function == PMIC_GPIO_FUNC_INDEX_ANALOG) {
+ return -EINVAL;
+ } else if (function >= PMIC_GPIO_FUNC_INDEX_DTEST1 &&
+ function <= PMIC_GPIO_FUNC_INDEX_DTEST4) {
+ pad->function -= (PMIC_GPIO_FUNC_INDEX_DTEST1 -
+ PMIC_GPIO_FUNC_INDEX_FUNC3);
+ }
+
+ val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
+ val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
+ val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
+
+ ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
+ if (ret < 0)
+ return ret;
+ }
val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
@@ -326,6 +446,12 @@ static int pmic_gpio_config_get(struct pinctrl_dev *pctldev,
case PMIC_GPIO_CONF_STRENGTH:
arg = pad->strength;
break;
+ case PMIC_GPIO_CONF_ATEST:
+ arg = pad->atest;
+ break;
+ case PMIC_GPIO_CONF_DTEST_BUFFER:
+ arg = pad->dtest_buffer;
+ break;
default:
return -EINVAL;
}
@@ -379,7 +505,7 @@ static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
pad->is_enabled = false;
break;
case PIN_CONFIG_POWER_SOURCE:
- if (arg > pad->num_sources)
+ if (arg >= pad->num_sources)
return -EINVAL;
pad->power_source = arg;
break;
@@ -400,6 +526,18 @@ static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
return -EINVAL;
pad->strength = arg;
break;
+ case PMIC_GPIO_CONF_ATEST:
+ if (arg > PMIC_GPIO_AOUT_ATEST4)
+ return -EINVAL;
+ pad->atest = arg;
+ break;
+ case PMIC_GPIO_CONF_DTEST_BUFFER:
+ if ((pad->lv_mv_type && arg > PMIC_GPIO_DIN_DTEST4)
+ || (!pad->lv_mv_type && arg >
+ PMIC_GPIO_DIG_IN_DTEST_SEL_MASK))
+ return -EINVAL;
+ pad->dtest_buffer = arg;
+ break;
default:
return -EINVAL;
}
@@ -424,19 +562,64 @@ static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
if (ret < 0)
return ret;
- val = 0;
+ val = PMIC_GPIO_MODE_DIGITAL_INPUT;
if (pad->output_enabled) {
if (pad->input_enabled)
- val = 2;
+ val = PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT;
else
- val = 1;
+ val = PMIC_GPIO_MODE_DIGITAL_OUTPUT;
+ }
+
+ if (pad->dtest_buffer != INT_MAX) {
+ val = pad->dtest_buffer;
+ if (pad->lv_mv_type)
+ val |= PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN;
+
+ ret = pmic_gpio_write(state, pad,
+ PMIC_GPIO_REG_DIG_IN_CTL, val);
+ if (ret < 0)
+ return ret;
}
- val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
- val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
- val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
+ if (pad->lv_mv_type) {
+ if (pad->function == PMIC_GPIO_FUNC_INDEX_ANALOG) {
+ val = PMIC_GPIO_MODE_ANALOG_PASS_THRU;
+ ret = pmic_gpio_write(state, pad,
+ PMIC_GPIO_REG_MODE_CTL, val);
+ if (ret < 0)
+ return ret;
+
+ ret = pmic_gpio_write(state, pad,
+ PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL,
+ pad->atest);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = pmic_gpio_write(state, pad,
+ PMIC_GPIO_REG_MODE_CTL, val);
+ if (ret < 0)
+ return ret;
+
+ val = pad->out_value
+ << PMIC_GPIO_LV_MV_OUTPUT_INVERT_SHIFT;
+ val |= pad->function
+ & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
+ ret = pmic_gpio_write(state, pad,
+ PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL, val);
+ if (ret < 0)
+ return ret;
+ }
+ } else {
+ val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
+ val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
+ val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
+
+ ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
+ if (ret < 0)
+ return ret;
+ }
- return pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
+ return ret;
}
static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
@@ -444,7 +627,7 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
{
struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
struct pmic_gpio_pad *pad;
- int ret, val;
+ int ret, val, function;
static const char *const biases[] = {
"pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA",
@@ -475,14 +658,28 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
pad->out_value = ret;
}
+ /*
+ * For GPIO not of LV/MV subtypes, the register value of
+ * the function mapping from "dtest1" to "dtest4" is 2 bits
+ * lower than the function index in pmic_gpio_functions[].
+ */
+ if (!pad->lv_mv_type &&
+ pad->function >= PMIC_GPIO_FUNC_INDEX_FUNC3) {
+ function = pad->function + (PMIC_GPIO_FUNC_INDEX_DTEST1
+ - PMIC_GPIO_FUNC_INDEX_FUNC3);
+ } else {
+ function = pad->function;
+ }
seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in");
- seq_printf(s, " %-7s", pmic_gpio_functions[pad->function]);
+ seq_printf(s, " %-7s", pmic_gpio_functions[function]);
seq_printf(s, " vin-%d", pad->power_source);
seq_printf(s, " %-27s", biases[pad->pullup]);
seq_printf(s, " %-10s", buffer_types[pad->buffer_type]);
seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
seq_printf(s, " %-7s", strengths[pad->strength]);
+ if (pad->dtest_buffer != INT_MAX)
+ seq_printf(s, " dtest buffer %d", pad->dtest_buffer);
}
}
@@ -622,40 +819,72 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state,
case PMIC_GPIO_SUBTYPE_GPIOC_8CH:
pad->num_sources = 8;
break;
+ case PMIC_GPIO_SUBTYPE_GPIO_LV:
+ pad->num_sources = 1;
+ pad->have_buffer = true;
+ pad->lv_mv_type = true;
+ break;
+ case PMIC_GPIO_SUBTYPE_GPIO_MV:
+ pad->num_sources = 2;
+ pad->have_buffer = true;
+ pad->lv_mv_type = true;
+ break;
default:
dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype);
return -ENODEV;
}
- val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
- if (val < 0)
- return val;
+ if (pad->lv_mv_type) {
+ val = pmic_gpio_read(state, pad,
+ PMIC_GPIO_REG_LV_MV_DIG_OUT_SOURCE_CTL);
+ if (val < 0)
+ return val;
+
+ pad->out_value = !!(val & PMIC_GPIO_LV_MV_OUTPUT_INVERT);
+ pad->function = val & PMIC_GPIO_LV_MV_OUTPUT_SOURCE_SEL_MASK;
- pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
+ val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
+ if (val < 0)
+ return val;
+
+ dir = val & PMIC_GPIO_REG_LV_MV_MODE_DIR_MASK;
+ } else {
+ val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
+ if (val < 0)
+ return val;
+
+ pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
+
+ dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT;
+ dir &= PMIC_GPIO_REG_MODE_DIR_MASK;
+ pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
+ pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK;
+ }
- dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT;
- dir &= PMIC_GPIO_REG_MODE_DIR_MASK;
switch (dir) {
- case 0:
+ case PMIC_GPIO_MODE_DIGITAL_INPUT:
pad->input_enabled = true;
pad->output_enabled = false;
break;
- case 1:
+ case PMIC_GPIO_MODE_DIGITAL_OUTPUT:
pad->input_enabled = false;
pad->output_enabled = true;
break;
- case 2:
+ case PMIC_GPIO_MODE_DIGITAL_INPUT_OUTPUT:
pad->input_enabled = true;
pad->output_enabled = true;
break;
+ case PMIC_GPIO_MODE_ANALOG_PASS_THRU:
+ if (pad->lv_mv_type)
+ pad->function = PMIC_GPIO_FUNC_INDEX_ANALOG;
+ else
+ return -ENODEV;
+ break;
default:
dev_err(state->dev, "unknown GPIO direction\n");
return -ENODEV;
}
- pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
- pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK;
-
val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL);
if (val < 0)
return val;
@@ -670,6 +899,17 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state,
pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT;
pad->pullup &= PMIC_GPIO_REG_PULL_MASK;
+ val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_IN_CTL);
+ if (val < 0)
+ return val;
+
+ if (pad->lv_mv_type && (val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_EN))
+ pad->dtest_buffer = val & PMIC_GPIO_LV_MV_DIG_IN_DTEST_SEL_MASK;
+ else if (!pad->lv_mv_type)
+ pad->dtest_buffer = val & PMIC_GPIO_DIG_IN_DTEST_SEL_MASK;
+ else
+ pad->dtest_buffer = INT_MAX;
+
val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL);
if (val < 0)
return val;
@@ -680,6 +920,13 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state,
pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT;
pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK;
+ if (pad->function == PMIC_GPIO_FUNC_INDEX_ANALOG) {
+ val = pmic_gpio_read(state, pad,
+ PMIC_GPIO_REG_LV_MV_ANA_PASS_THRU_SEL);
+ if (val < 0)
+ return val;
+ pad->atest = val & PMIC_GPIO_LV_MV_ANA_MUX_SEL_MASK;
+ }
/* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */
pad->is_enabled = true;
return 0;
@@ -693,18 +940,19 @@ static int pmic_gpio_probe(struct platform_device *pdev)
struct pmic_gpio_pad *pad, *pads;
struct pmic_gpio_state *state;
int ret, npins, i;
- u32 res[2];
+ u32 reg;
- ret = of_property_read_u32_array(dev->of_node, "reg", res, 2);
+ ret = of_property_read_u32(dev->of_node, "reg", &reg);
if (ret < 0) {
- dev_err(dev, "missing base address and/or range");
+ dev_err(dev, "missing base address");
return ret;
}
- npins = res[1] / PMIC_GPIO_ADDRESS_RANGE;
-
+ npins = platform_irq_count(pdev);
if (!npins)
return -EINVAL;
+ if (npins < 0)
+ return npins;
BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups));
@@ -752,7 +1000,7 @@ static int pmic_gpio_probe(struct platform_device *pdev)
if (pad->irq < 0)
return pad->irq;
- pad->base = res[0] + i * PMIC_GPIO_ADDRESS_RANGE;
+ pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;
ret = pmic_gpio_populate(state, pad);
if (ret < 0)
@@ -805,6 +1053,7 @@ static const struct of_device_id pmic_gpio_of_match[] = {
{ .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */
{ .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */
{ .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */
+ { .compatible = "qcom,spmi-gpio" }, /* Generic */
{ },
};
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
index 9ce0e30e33e8..73547abc5cf5 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014, 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
@@ -14,6 +14,7 @@
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinmux.h>
@@ -87,6 +88,10 @@
#define PMIC_MPP_REG_AIN_ROUTE_SHIFT 0
#define PMIC_MPP_REG_AIN_ROUTE_MASK 0x7
+/* PMIC_MPP_REG_SINK_CTL */
+#define PMIC_MPP_REG_CURRENT_SINK_MASK 0x7
+#define MPP_CURRENT_SINK_MA_STEP_SIZE 5
+
#define PMIC_MPP_MODE_DIGITAL_INPUT 0
#define PMIC_MPP_MODE_DIGITAL_OUTPUT 1
#define PMIC_MPP_MODE_DIGITAL_BIDIR 2
@@ -106,6 +111,7 @@
#define PMIC_MPP_CONF_ANALOG_LEVEL (PIN_CONFIG_END + 2)
#define PMIC_MPP_CONF_DTEST_SELECTOR (PIN_CONFIG_END + 3)
#define PMIC_MPP_CONF_PAIRED (PIN_CONFIG_END + 4)
+#define PMIC_MPP_CONF_DTEST_BUFFER (PIN_CONFIG_END + 5)
/**
* struct pmic_mpp_pad - keep current MPP settings
@@ -124,6 +130,7 @@
* @function: See pmic_mpp_functions[].
* @drive_strength: Amount of current in sink mode
* @dtest: DTEST route selector
+ * @dtest_buffer: the DTEST buffer selection for digital input mode
*/
struct pmic_mpp_pad {
u16 base;
@@ -141,6 +148,7 @@ struct pmic_mpp_pad {
unsigned int function;
unsigned int drive_strength;
unsigned int dtest;
+ unsigned int dtest_buffer;
};
struct pmic_mpp_state {
@@ -155,6 +163,7 @@ static const struct pinconf_generic_params pmic_mpp_bindings[] = {
{"qcom,analog-level", PMIC_MPP_CONF_ANALOG_LEVEL, 0},
{"qcom,dtest", PMIC_MPP_CONF_DTEST_SELECTOR, 0},
{"qcom,paired", PMIC_MPP_CONF_PAIRED, 0},
+ {"qcom,dtest-buffer", PMIC_MPP_CONF_DTEST_BUFFER, 0},
};
#ifdef CONFIG_DEBUG_FS
@@ -163,6 +172,7 @@ static const struct pin_config_item pmic_conf_items[] = {
PCONFDUMP(PMIC_MPP_CONF_ANALOG_LEVEL, "analog level", NULL, true),
PCONFDUMP(PMIC_MPP_CONF_DTEST_SELECTOR, "dtest", NULL, true),
PCONFDUMP(PMIC_MPP_CONF_PAIRED, "paired", NULL, false),
+ PCONFDUMP(PMIC_MPP_CONF_DTEST_BUFFER, "dtest buffer", NULL, true),
};
#endif
@@ -392,6 +402,9 @@ static int pmic_mpp_config_get(struct pinctrl_dev *pctldev,
case PMIC_MPP_CONF_ANALOG_LEVEL:
arg = pad->aout_level;
break;
+ case PMIC_MPP_CONF_DTEST_BUFFER:
+ arg = pad->dtest_buffer;
+ break;
default:
return -EINVAL;
}
@@ -457,7 +470,7 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
pad->dtest = arg;
break;
case PIN_CONFIG_DRIVE_STRENGTH:
- arg = pad->drive_strength;
+ pad->drive_strength = arg;
break;
case PMIC_MPP_CONF_AMUX_ROUTE:
if (arg >= PMIC_MPP_AMUX_ROUTE_ABUS4)
@@ -470,6 +483,15 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
case PMIC_MPP_CONF_PAIRED:
pad->paired = !!arg;
break;
+ case PMIC_MPP_CONF_DTEST_BUFFER:
+ /*
+ * 0xf is the max value which selects
+ * 4 dtest rails simultaneously
+ */
+ if (arg > 0xf)
+ return -EINVAL;
+ pad->dtest_buffer = arg;
+ break;
default:
return -EINVAL;
}
@@ -481,6 +503,11 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
if (ret < 0)
return ret;
+ val = pad->dtest_buffer;
+ ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_IN_CTL, val);
+ if (ret < 0)
+ return ret;
+
val = pad->pullup << PMIC_MPP_REG_PULL_SHIFT;
ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_PULL_CTL, val);
@@ -497,6 +524,16 @@ static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
if (ret < 0)
return ret;
+ val = 0;
+ if (pad->drive_strength >= MPP_CURRENT_SINK_MA_STEP_SIZE)
+ val = DIV_ROUND_UP(pad->drive_strength,
+ MPP_CURRENT_SINK_MA_STEP_SIZE) - 1;
+
+ val &= PMIC_MPP_REG_CURRENT_SINK_MASK;
+ ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_SINK_CTL, val);
+ if (ret < 0)
+ return ret;
+
ret = pmic_mpp_write_mode_ctl(state, pad);
if (ret < 0)
return ret;
@@ -544,6 +581,8 @@ static void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev,
seq_printf(s, " dtest%d", pad->dtest);
if (pad->paired)
seq_puts(s, " paired");
+ if (pad->dtest_buffer)
+ seq_printf(s, " dtest buffer %d", pad->dtest_buffer);
}
}
@@ -741,7 +780,7 @@ static int pmic_mpp_populate(struct pmic_mpp_state *state,
sel &= PMIC_MPP_REG_MODE_FUNCTION_MASK;
if (sel >= PMIC_MPP_SELECTOR_DTEST_FIRST)
- pad->dtest = sel + 1;
+ pad->dtest = sel - PMIC_MPP_SELECTOR_DTEST_FIRST + 1;
else if (sel == PMIC_MPP_SELECTOR_PAIRED)
pad->paired = true;
@@ -752,6 +791,12 @@ static int pmic_mpp_populate(struct pmic_mpp_state *state,
pad->power_source = val >> PMIC_MPP_REG_VIN_SHIFT;
pad->power_source &= PMIC_MPP_REG_VIN_MASK;
+ val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_IN_CTL);
+ if (val < 0)
+ return val;
+
+ pad->dtest_buffer = val;
+
val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_PULL_CTL);
if (val < 0)
return val;
@@ -770,7 +815,8 @@ static int pmic_mpp_populate(struct pmic_mpp_state *state,
if (val < 0)
return val;
- pad->drive_strength = val;
+ val &= PMIC_MPP_REG_CURRENT_SINK_MASK;
+ pad->drive_strength = (val + 1) * MPP_CURRENT_SINK_MA_STEP_SIZE;
val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AOUT_CTL);
if (val < 0)
@@ -795,17 +841,19 @@ static int pmic_mpp_probe(struct platform_device *pdev)
struct pmic_mpp_pad *pad, *pads;
struct pmic_mpp_state *state;
int ret, npins, i;
- u32 res[2];
+ u32 reg;
- ret = of_property_read_u32_array(dev->of_node, "reg", res, 2);
+ ret = of_property_read_u32(dev->of_node, "reg", &reg);
if (ret < 0) {
- dev_err(dev, "missing base address and/or range");
+ dev_err(dev, "missing base address");
return ret;
}
- npins = res[1] / PMIC_MPP_ADDRESS_RANGE;
+ npins = platform_irq_count(pdev);
if (!npins)
return -EINVAL;
+ if (npins < 0)
+ return npins;
BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups));
@@ -854,7 +902,7 @@ static int pmic_mpp_probe(struct platform_device *pdev)
if (pad->irq < 0)
return pad->irq;
- pad->base = res[0] + i * PMIC_MPP_ADDRESS_RANGE;
+ pad->base = reg + i * PMIC_MPP_ADDRESS_RANGE;
ret = pmic_mpp_populate(state, pad);
if (ret < 0)
@@ -908,6 +956,7 @@ static const struct of_device_id pmic_mpp_of_match[] = {
{ .compatible = "qcom,pm8916-mpp" }, /* 4 MPP's */
{ .compatible = "qcom,pm8941-mpp" }, /* 8 MPP's */
{ .compatible = "qcom,pma8084-mpp" }, /* 8 MPP's */
+ { .compatible = "qcom,spmi-mpp" }, /* Generic */
{ },
};
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
index 19a3c3bc2f1f..e51176ec83d2 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
@@ -23,6 +23,7 @@
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
@@ -650,11 +651,12 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl,
}
static const struct of_device_id pm8xxx_gpio_of_match[] = {
- { .compatible = "qcom,pm8018-gpio", .data = (void *)6 },
- { .compatible = "qcom,pm8038-gpio", .data = (void *)12 },
- { .compatible = "qcom,pm8058-gpio", .data = (void *)40 },
- { .compatible = "qcom,pm8917-gpio", .data = (void *)38 },
- { .compatible = "qcom,pm8921-gpio", .data = (void *)44 },
+ { .compatible = "qcom,pm8018-gpio" },
+ { .compatible = "qcom,pm8038-gpio" },
+ { .compatible = "qcom,pm8058-gpio" },
+ { .compatible = "qcom,pm8917-gpio" },
+ { .compatible = "qcom,pm8921-gpio" },
+ { .compatible = "qcom,ssbi-gpio" },
{ },
};
MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
@@ -665,14 +667,19 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
struct pinctrl_pin_desc *pins;
struct pm8xxx_gpio *pctrl;
int ret;
- int i;
+ int i, npins;
pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
if (!pctrl)
return -ENOMEM;
pctrl->dev = &pdev->dev;
- pctrl->npins = (unsigned long)of_device_get_match_data(&pdev->dev);
+ npins = platform_irq_count(pdev);
+ if (!npins)
+ return -EINVAL;
+ if (npins < 0)
+ return npins;
+ pctrl->npins = npins;
pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!pctrl->regmap) {
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c b/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c
index b868ef1766a0..e9f01de51e18 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c
@@ -23,6 +23,7 @@
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
#include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
@@ -741,11 +742,12 @@ static int pm8xxx_pin_populate(struct pm8xxx_mpp *pctrl,
}
static const struct of_device_id pm8xxx_mpp_of_match[] = {
- { .compatible = "qcom,pm8018-mpp", .data = (void *)6 },
- { .compatible = "qcom,pm8038-mpp", .data = (void *)6 },
- { .compatible = "qcom,pm8917-mpp", .data = (void *)10 },
- { .compatible = "qcom,pm8821-mpp", .data = (void *)4 },
- { .compatible = "qcom,pm8921-mpp", .data = (void *)12 },
+ { .compatible = "qcom,pm8018-mpp" },
+ { .compatible = "qcom,pm8038-mpp" },
+ { .compatible = "qcom,pm8917-mpp" },
+ { .compatible = "qcom,pm8821-mpp" },
+ { .compatible = "qcom,pm8921-mpp" },
+ { .compatible = "qcom,ssbi-mpp" },
{ },
};
MODULE_DEVICE_TABLE(of, pm8xxx_mpp_of_match);
@@ -756,14 +758,19 @@ static int pm8xxx_mpp_probe(struct platform_device *pdev)
struct pinctrl_pin_desc *pins;
struct pm8xxx_mpp *pctrl;
int ret;
- int i;
+ int i, npins;
pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
if (!pctrl)
return -ENOMEM;
pctrl->dev = &pdev->dev;
- pctrl->npins = (unsigned long)of_device_get_match_data(&pdev->dev);
+ npins = platform_irq_count(pdev);
+ if (!npins)
+ return -EINVAL;
+ if (npins < 0)
+ return npins;
+ pctrl->npins = npins;
pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!pctrl->regmap) {
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
index 6addf14d7126..a02247d3e938 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
@@ -2435,6 +2435,7 @@ int ipa_mhi_init(struct ipa_mhi_init_params *params)
int res;
struct ipa_rm_create_params mhi_prod_params;
struct ipa_rm_create_params mhi_cons_params;
+ struct ipa_rm_perf_profile profile;
IPA_MHI_FUNC_ENTRY();
@@ -2506,6 +2507,14 @@ int ipa_mhi_init(struct ipa_mhi_init_params *params)
goto fail_create_rm_prod;
}
+ memset(&profile, 0, sizeof(profile));
+ profile.max_supported_bandwidth_mbps = 1000;
+ res = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_MHI_PROD, &profile);
+ if (res) {
+ IPA_MHI_ERR("fail to set profile to MHI_PROD\n");
+ goto fail_perf_rm_prod;
+ }
+
/* Create CONS in IPA RM */
memset(&mhi_cons_params, 0, sizeof(mhi_cons_params));
mhi_cons_params.name = IPA_RM_RESOURCE_MHI_CONS;
@@ -2518,6 +2527,14 @@ int ipa_mhi_init(struct ipa_mhi_init_params *params)
goto fail_create_rm_cons;
}
+ memset(&profile, 0, sizeof(profile));
+ profile.max_supported_bandwidth_mbps = 1000;
+ res = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_MHI_CONS, &profile);
+ if (res) {
+ IPA_MHI_ERR("fail to set profile to MHI_CONS\n");
+ goto fail_perf_rm_cons;
+ }
+
/* Initialize uC interface */
ipa_uc_mhi_init(ipa_mhi_uc_ready_cb,
ipa_mhi_uc_wakeup_request_cb);
@@ -2530,7 +2547,10 @@ int ipa_mhi_init(struct ipa_mhi_init_params *params)
IPA_MHI_FUNC_EXIT();
return 0;
+fail_perf_rm_cons:
+ ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_CONS);
fail_create_rm_cons:
+fail_perf_rm_prod:
ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD);
fail_create_rm_prod:
destroy_workqueue(ipa_mhi_client_ctx->wq);
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index 8e583203abda..838b78c1934d 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -2136,11 +2136,11 @@ int ipa_usb_xdci_connect(struct ipa_usb_xdci_chan_params *ul_chan_params,
connect_fail:
ipa3_usb_release_xdci_channel(dl_out_params->clnt_hdl,
- dl_chan_params->teth_prot);
+ IPA3_USB_GET_TTYPE(dl_chan_params->teth_prot));
alloc_dl_chan_fail:
if (connect_params->teth_prot != IPA_USB_DIAG)
ipa3_usb_release_xdci_channel(ul_out_params->clnt_hdl,
- ul_chan_params->teth_prot);
+ IPA3_USB_GET_TTYPE(ul_chan_params->teth_prot));
bad_params:
mutex_unlock(&ipa3_usb_ctx->general_mutex);
return result;
diff --git a/drivers/platform/msm/ipa/ipa_rm.c b/drivers/platform/msm/ipa/ipa_rm.c
index bf6352452283..209264d69b26 100644
--- a/drivers/platform/msm/ipa/ipa_rm.c
+++ b/drivers/platform/msm/ipa/ipa_rm.c
@@ -267,17 +267,18 @@ static int _ipa_rm_add_dependency_sync(enum ipa_rm_resource_name resource_name,
time = wait_for_completion_timeout(
&((struct ipa_rm_resource_cons *)consumer)->
request_consumer_in_progress,
- HZ);
+ HZ * 5);
result = 0;
if (!time) {
IPA_RM_ERR("TIMEOUT waiting for %s GRANT event.",
ipa_rm_resource_str(depends_on_name));
result = -ETIME;
- }
- IPA_RM_DBG("%s waited for %s GRANT %lu time.\n",
+ } else {
+ IPA_RM_DBG("%s waited for %s GRANT %lu time.\n",
ipa_rm_resource_str(resource_name),
ipa_rm_resource_str(depends_on_name),
time);
+ }
}
IPA_RM_DBG("EXIT with %d\n", result);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 66553205b520..9cb0b1f3c379 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -575,6 +575,7 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct ipa_ioc_v4_nat_del nat_del;
struct ipa_ioc_rm_dependency rm_depend;
size_t sz;
+ int pre_entry;
IPADBG("cmd=%x nr=%d\n", cmd, _IOC_NR(cmd));
@@ -623,11 +624,11 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
-
+ pre_entry =
+ ((struct ipa_ioc_nat_dma_cmd *)header)->entries;
pyld_sz =
sizeof(struct ipa_ioc_nat_dma_cmd) +
- ((struct ipa_ioc_nat_dma_cmd *)header)->entries *
- sizeof(struct ipa_ioc_nat_dma_one);
+ pre_entry * sizeof(struct ipa_ioc_nat_dma_one);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -638,7 +639,15 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
-
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_nat_dma_cmd *)param)->entries
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_nat_dma_cmd *)param)->entries,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_nat_dma_cmd((struct ipa_ioc_nat_dma_cmd *)param)) {
retval = -EFAULT;
break;
@@ -663,10 +672,11 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_add_hdr *)header)->num_hdrs;
pyld_sz =
sizeof(struct ipa_ioc_add_hdr) +
- ((struct ipa_ioc_add_hdr *)header)->num_hdrs *
- sizeof(struct ipa_hdr_add);
+ pre_entry * sizeof(struct ipa_hdr_add);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -676,6 +686,15 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_add_hdr *)param)->num_hdrs
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_add_hdr *)param)->num_hdrs,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_add_hdr((struct ipa_ioc_add_hdr *)param)) {
retval = -EFAULT;
break;
@@ -692,10 +711,11 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_del_hdr *)header)->num_hdls;
pyld_sz =
sizeof(struct ipa_ioc_del_hdr) +
- ((struct ipa_ioc_del_hdr *)header)->num_hdls *
- sizeof(struct ipa_hdr_del);
+ pre_entry * sizeof(struct ipa_hdr_del);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -705,6 +725,15 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_del_hdr *)param)->num_hdls
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_del_hdr *)param)->num_hdls,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_del_hdr((struct ipa_ioc_del_hdr *)param)) {
retval = -EFAULT;
break;
@@ -721,10 +750,11 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_add_rt_rule *)header)->num_rules;
pyld_sz =
sizeof(struct ipa_ioc_add_rt_rule) +
- ((struct ipa_ioc_add_rt_rule *)header)->num_rules *
- sizeof(struct ipa_rt_rule_add);
+ pre_entry * sizeof(struct ipa_rt_rule_add);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -734,6 +764,16 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_add_rt_rule *)param)->num_rules
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_add_rt_rule *)param)->
+ num_rules,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_add_rt_rule((struct ipa_ioc_add_rt_rule *)param)) {
retval = -EFAULT;
break;
@@ -750,10 +790,11 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_mdfy_rt_rule *)header)->num_rules;
pyld_sz =
sizeof(struct ipa_ioc_mdfy_rt_rule) +
- ((struct ipa_ioc_mdfy_rt_rule *)header)->num_rules *
- sizeof(struct ipa_rt_rule_mdfy);
+ pre_entry * sizeof(struct ipa_rt_rule_mdfy);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -763,6 +804,16 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_mdfy_rt_rule *)param)->num_rules
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_mdfy_rt_rule *)param)->
+ num_rules,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_mdfy_rt_rule((struct ipa_ioc_mdfy_rt_rule *)param)) {
retval = -EFAULT;
break;
@@ -779,10 +830,11 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_del_rt_rule *)header)->num_hdls;
pyld_sz =
sizeof(struct ipa_ioc_del_rt_rule) +
- ((struct ipa_ioc_del_rt_rule *)header)->num_hdls *
- sizeof(struct ipa_rt_rule_del);
+ pre_entry * sizeof(struct ipa_rt_rule_del);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -792,6 +844,15 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_del_rt_rule *)param)->num_hdls
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_del_rt_rule *)param)->num_hdls,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_del_rt_rule((struct ipa_ioc_del_rt_rule *)param)) {
retval = -EFAULT;
break;
@@ -808,10 +869,11 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_add_flt_rule *)header)->num_rules;
pyld_sz =
sizeof(struct ipa_ioc_add_flt_rule) +
- ((struct ipa_ioc_add_flt_rule *)header)->num_rules *
- sizeof(struct ipa_flt_rule_add);
+ pre_entry * sizeof(struct ipa_flt_rule_add);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -821,6 +883,16 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_add_flt_rule *)param)->num_rules
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_add_flt_rule *)param)->
+ num_rules,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_add_flt_rule((struct ipa_ioc_add_flt_rule *)param)) {
retval = -EFAULT;
break;
@@ -837,10 +909,11 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_del_flt_rule *)header)->num_hdls;
pyld_sz =
sizeof(struct ipa_ioc_del_flt_rule) +
- ((struct ipa_ioc_del_flt_rule *)header)->num_hdls *
- sizeof(struct ipa_flt_rule_del);
+ pre_entry * sizeof(struct ipa_flt_rule_del);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -850,6 +923,16 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_del_flt_rule *)param)->num_hdls
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_del_flt_rule *)param)->
+ num_hdls,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_del_flt_rule((struct ipa_ioc_del_flt_rule *)param)) {
retval = -EFAULT;
break;
@@ -866,10 +949,11 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_mdfy_flt_rule *)header)->num_rules;
pyld_sz =
sizeof(struct ipa_ioc_mdfy_flt_rule) +
- ((struct ipa_ioc_mdfy_flt_rule *)header)->num_rules *
- sizeof(struct ipa_flt_rule_mdfy);
+ pre_entry * sizeof(struct ipa_flt_rule_mdfy);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -879,6 +963,16 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_mdfy_flt_rule *)param)->num_rules
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_mdfy_flt_rule *)param)->
+ num_rules,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_mdfy_flt_rule((struct ipa_ioc_mdfy_flt_rule *)param)) {
retval = -EFAULT;
break;
@@ -992,9 +1086,10 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
-
- pyld_sz = sz + ((struct ipa_ioc_query_intf_tx_props *)
- header)->num_tx_props *
+ pre_entry =
+ ((struct ipa_ioc_query_intf_tx_props *)
+ header)->num_tx_props;
+ pyld_sz = sz + pre_entry *
sizeof(struct ipa_ioc_tx_intf_prop);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
@@ -1005,6 +1100,16 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_query_intf_tx_props *)
+ param)->num_tx_props
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_query_intf_tx_props *)
+ param)->num_tx_props, pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa_query_intf_tx_props(
(struct ipa_ioc_query_intf_tx_props *)param)) {
retval = -1;
@@ -1027,9 +1132,10 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
-
- pyld_sz = sz + ((struct ipa_ioc_query_intf_rx_props *)
- header)->num_rx_props *
+ pre_entry =
+ ((struct ipa_ioc_query_intf_rx_props *)
+ header)->num_rx_props;
+ pyld_sz = sz + pre_entry *
sizeof(struct ipa_ioc_rx_intf_prop);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
@@ -1040,6 +1146,15 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_query_intf_rx_props *)
+ param)->num_rx_props != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_query_intf_rx_props *)
+ param)->num_rx_props, pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa_query_intf_rx_props(
(struct ipa_ioc_query_intf_rx_props *)param)) {
retval = -1;
@@ -1062,9 +1177,10 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
-
- pyld_sz = sz + ((struct ipa_ioc_query_intf_ext_props *)
- header)->num_ext_props *
+ pre_entry =
+ ((struct ipa_ioc_query_intf_ext_props *)
+ header)->num_ext_props;
+ pyld_sz = sz + pre_entry *
sizeof(struct ipa_ioc_ext_intf_prop);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
@@ -1075,6 +1191,15 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_query_intf_ext_props *)
+ param)->num_ext_props != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_query_intf_ext_props *)
+ param)->num_ext_props, pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa_query_intf_ext_props(
(struct ipa_ioc_query_intf_ext_props *)param)) {
retval = -1;
@@ -1091,8 +1216,10 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
- pyld_sz = sizeof(struct ipa_msg_meta) +
+ pre_entry =
((struct ipa_msg_meta *)header)->msg_len;
+ pyld_sz = sizeof(struct ipa_msg_meta) +
+ pre_entry;
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -1102,6 +1229,15 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_msg_meta *)param)->msg_len
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_msg_meta *)param)->msg_len,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa_pull_msg((struct ipa_msg_meta *)param,
(char *)param + sizeof(struct ipa_msg_meta),
((struct ipa_msg_meta *)param)->msg_len) !=
@@ -1218,10 +1354,12 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_add_hdr_proc_ctx *)
+ header)->num_proc_ctxs;
pyld_sz =
sizeof(struct ipa_ioc_add_hdr_proc_ctx) +
- ((struct ipa_ioc_add_hdr_proc_ctx *)header)->num_proc_ctxs *
- sizeof(struct ipa_hdr_proc_ctx_add);
+ pre_entry * sizeof(struct ipa_hdr_proc_ctx_add);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -1231,6 +1369,15 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_add_hdr_proc_ctx *)
+ param)->num_proc_ctxs != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_add_hdr_proc_ctx *)
+ param)->num_proc_ctxs, pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_add_hdr_proc_ctx(
(struct ipa_ioc_add_hdr_proc_ctx *)param)) {
retval = -EFAULT;
@@ -1247,10 +1394,11 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_del_hdr_proc_ctx *)header)->num_hdls;
pyld_sz =
sizeof(struct ipa_ioc_del_hdr_proc_ctx) +
- ((struct ipa_ioc_del_hdr_proc_ctx *)header)->num_hdls *
- sizeof(struct ipa_hdr_proc_ctx_del);
+ pre_entry * sizeof(struct ipa_hdr_proc_ctx_del);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -1260,6 +1408,16 @@ static long ipa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_del_hdr_proc_ctx *)
+ param)->num_hdls != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_del_hdr_proc_ctx *)param)->
+ num_hdls,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa2_del_hdr_proc_ctx(
(struct ipa_ioc_del_hdr_proc_ctx *)param)) {
retval = -EFAULT;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index c0ac544fa271..695c8bc4cbc0 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -2397,7 +2397,7 @@ begin:
if (skb->len < IPA_PKT_STATUS_SIZE) {
WARN_ON(sys->prev_skb != NULL);
IPADBG("status straddles buffer\n");
- sys->prev_skb = skb;
+ sys->prev_skb = skb_copy(skb, GFP_KERNEL);
sys->len_partial = skb->len;
return rc;
}
@@ -2482,7 +2482,7 @@ begin:
!status->exception) {
WARN_ON(sys->prev_skb != NULL);
IPADBG("Ins header in next buffer\n");
- sys->prev_skb = skb;
+ sys->prev_skb = skb_copy(skb, GFP_KERNEL);
sys->len_partial = skb->len;
return rc;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
index a1c927c5ec0f..249de808ec5c 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.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
@@ -348,6 +348,11 @@ int ipa_query_intf_ext_props(struct ipa_ioc_query_intf_ext_props *ext)
return result;
}
+static void ipa2_send_msg_free(void *buff, u32 len, u32 type)
+{
+ kfree(buff);
+}
+
/**
* ipa2_send_msg() - Send "message" from kernel client to IPA driver
* @meta: [in] message meta-data
@@ -367,6 +372,7 @@ int ipa2_send_msg(struct ipa_msg_meta *meta, void *buff,
ipa_msg_free_fn callback)
{
struct ipa_push_msg *msg;
+ void *data = NULL;
if (unlikely(!ipa_ctx)) {
IPAERR("IPA driver was not initialized\n");
@@ -392,8 +398,17 @@ int ipa2_send_msg(struct ipa_msg_meta *meta, void *buff,
}
msg->meta = *meta;
- msg->buff = buff;
- msg->callback = callback;
+ if (meta->msg_len > 0 && buff) {
+ data = kmalloc(meta->msg_len, GFP_KERNEL);
+ if (data == NULL) {
+ IPAERR("fail to alloc data container\n");
+ kfree(msg);
+ return -ENOMEM;
+ }
+ memcpy(data, buff, meta->msg_len);
+ msg->buff = data;
+ msg->callback = ipa2_send_msg_free;
+ }
mutex_lock(&ipa_ctx->msg_lock);
list_add_tail(&msg->link, &ipa_ctx->msg_list);
@@ -401,6 +416,8 @@ int ipa2_send_msg(struct ipa_msg_meta *meta, void *buff,
IPA_STATS_INC_CNT(ipa_ctx->stats.msg_w[meta->msg_type]);
wake_up(&ipa_ctx->msg_waitq);
+ if (buff)
+ callback(buff, meta->msg_len, meta->msg_type);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 2420dd78b4c0..1be9a6745531 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -2394,18 +2394,20 @@ static void rmnet_ipa_get_stats_and_update(bool reset)
}
rc = ipa_qmi_get_data_stats(&req, resp);
+ if (rc) {
+ IPAWANERR("ipa_qmi_get_data_stats failed: %d\n", rc);
+ kfree(resp);
+ return;
+ }
- if (!rc) {
- memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
- msg_meta.msg_type = IPA_TETHERING_STATS_UPDATE_STATS;
- msg_meta.msg_len =
- sizeof(struct ipa_get_data_stats_resp_msg_v01);
- rc = ipa2_send_msg(&msg_meta, resp, rmnet_ipa_free_msg);
- if (rc) {
- IPAWANERR("ipa2_send_msg failed: %d\n", rc);
- kfree(resp);
- return;
- }
+ memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
+ msg_meta.msg_type = IPA_TETHERING_STATS_UPDATE_STATS;
+ msg_meta.msg_len = sizeof(struct ipa_get_data_stats_resp_msg_v01);
+ rc = ipa2_send_msg(&msg_meta, resp, rmnet_ipa_free_msg);
+ if (rc) {
+ IPAWANERR("ipa2_send_msg failed: %d\n", rc);
+ kfree(resp);
+ return;
}
}
@@ -2454,18 +2456,20 @@ static void rmnet_ipa_get_network_stats_and_update(void)
req.mux_id_list[0] = ipa_rmnet_ctx.metered_mux_id;
rc = ipa_qmi_get_network_stats(&req, resp);
+ if (rc) {
+ IPAWANERR("ipa_qmi_get_network_stats failed %d\n", rc);
+ kfree(resp);
+ return;
+ }
- if (!rc) {
- memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
- msg_meta.msg_type = IPA_TETHERING_STATS_UPDATE_NETWORK_STATS;
- msg_meta.msg_len =
- sizeof(struct ipa_get_apn_data_stats_resp_msg_v01);
- rc = ipa2_send_msg(&msg_meta, resp, rmnet_ipa_free_msg);
- if (rc) {
- IPAWANERR("ipa2_send_msg failed: %d\n", rc);
- kfree(resp);
- return;
- }
+ memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
+ msg_meta.msg_type = IPA_TETHERING_STATS_UPDATE_NETWORK_STATS;
+ msg_meta.msg_len = sizeof(struct ipa_get_apn_data_stats_resp_msg_v01);
+ rc = ipa2_send_msg(&msg_meta, resp, rmnet_ipa_free_msg);
+ if (rc) {
+ IPAWANERR("ipa2_send_msg failed: %d\n", rc);
+ kfree(resp);
+ return;
}
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index c9120ce83da8..1df2bc6b902c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -596,6 +596,7 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct ipa_ioc_v4_nat_del nat_del;
struct ipa_ioc_rm_dependency rm_depend;
size_t sz;
+ int pre_entry;
IPADBG("cmd=%x nr=%d\n", cmd, _IOC_NR(cmd));
@@ -649,11 +650,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
-
+ pre_entry =
+ ((struct ipa_ioc_nat_dma_cmd *)header)->entries;
pyld_sz =
sizeof(struct ipa_ioc_nat_dma_cmd) +
- ((struct ipa_ioc_nat_dma_cmd *)header)->entries *
- sizeof(struct ipa_ioc_nat_dma_one);
+ pre_entry * sizeof(struct ipa_ioc_nat_dma_one);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -664,7 +665,15 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
-
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_nat_dma_cmd *)param)->entries
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_nat_dma_cmd *)param)->entries,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_nat_dma_cmd((struct ipa_ioc_nat_dma_cmd *)param)) {
retval = -EFAULT;
break;
@@ -689,10 +698,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_add_hdr *)header)->num_hdrs;
pyld_sz =
sizeof(struct ipa_ioc_add_hdr) +
- ((struct ipa_ioc_add_hdr *)header)->num_hdrs *
- sizeof(struct ipa_hdr_add);
+ pre_entry * sizeof(struct ipa_hdr_add);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -702,6 +712,15 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_add_hdr *)param)->num_hdrs
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_add_hdr *)param)->num_hdrs,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_add_hdr((struct ipa_ioc_add_hdr *)param)) {
retval = -EFAULT;
break;
@@ -718,10 +737,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_del_hdr *)header)->num_hdls;
pyld_sz =
sizeof(struct ipa_ioc_del_hdr) +
- ((struct ipa_ioc_del_hdr *)header)->num_hdls *
- sizeof(struct ipa_hdr_del);
+ pre_entry * sizeof(struct ipa_hdr_del);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -731,6 +751,15 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_del_hdr *)param)->num_hdls
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_del_hdr *)param)->num_hdls,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_del_hdr((struct ipa_ioc_del_hdr *)param)) {
retval = -EFAULT;
break;
@@ -747,10 +776,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_add_rt_rule *)header)->num_rules;
pyld_sz =
sizeof(struct ipa_ioc_add_rt_rule) +
- ((struct ipa_ioc_add_rt_rule *)header)->num_rules *
- sizeof(struct ipa_rt_rule_add);
+ pre_entry * sizeof(struct ipa_rt_rule_add);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -760,6 +790,16 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_add_rt_rule *)param)->num_rules
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_add_rt_rule *)param)->
+ num_rules,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_add_rt_rule((struct ipa_ioc_add_rt_rule *)param)) {
retval = -EFAULT;
break;
@@ -776,10 +816,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_add_rt_rule_after *)header)->num_rules;
pyld_sz =
sizeof(struct ipa_ioc_add_rt_rule_after) +
- ((struct ipa_ioc_add_rt_rule_after *)header)->num_rules *
- sizeof(struct ipa_rt_rule_add);
+ pre_entry * sizeof(struct ipa_rt_rule_add);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -789,6 +830,16 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_add_rt_rule_after *)param)->
+ num_rules != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_add_rt_rule_after *)param)->
+ num_rules,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_add_rt_rule_after(
(struct ipa_ioc_add_rt_rule_after *)param)) {
@@ -807,10 +858,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_mdfy_rt_rule *)header)->num_rules;
pyld_sz =
sizeof(struct ipa_ioc_mdfy_rt_rule) +
- ((struct ipa_ioc_mdfy_rt_rule *)header)->num_rules *
- sizeof(struct ipa_rt_rule_mdfy);
+ pre_entry * sizeof(struct ipa_rt_rule_mdfy);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -820,6 +872,16 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_mdfy_rt_rule *)param)->num_rules
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_mdfy_rt_rule *)param)->
+ num_rules,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_mdfy_rt_rule((struct ipa_ioc_mdfy_rt_rule *)param)) {
retval = -EFAULT;
break;
@@ -836,10 +898,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_del_rt_rule *)header)->num_hdls;
pyld_sz =
sizeof(struct ipa_ioc_del_rt_rule) +
- ((struct ipa_ioc_del_rt_rule *)header)->num_hdls *
- sizeof(struct ipa_rt_rule_del);
+ pre_entry * sizeof(struct ipa_rt_rule_del);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -849,6 +912,15 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_del_rt_rule *)param)->num_hdls
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_del_rt_rule *)param)->num_hdls,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_del_rt_rule((struct ipa_ioc_del_rt_rule *)param)) {
retval = -EFAULT;
break;
@@ -865,10 +937,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_add_flt_rule *)header)->num_rules;
pyld_sz =
sizeof(struct ipa_ioc_add_flt_rule) +
- ((struct ipa_ioc_add_flt_rule *)header)->num_rules *
- sizeof(struct ipa_flt_rule_add);
+ pre_entry * sizeof(struct ipa_flt_rule_add);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -878,6 +951,16 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_add_flt_rule *)param)->num_rules
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_add_flt_rule *)param)->
+ num_rules,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_add_flt_rule((struct ipa_ioc_add_flt_rule *)param)) {
retval = -EFAULT;
break;
@@ -895,10 +978,12 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_add_flt_rule_after *)header)->
+ num_rules;
pyld_sz =
sizeof(struct ipa_ioc_add_flt_rule_after) +
- ((struct ipa_ioc_add_flt_rule_after *)header)->num_rules *
- sizeof(struct ipa_flt_rule_add);
+ pre_entry * sizeof(struct ipa_flt_rule_add);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -908,6 +993,16 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_add_flt_rule_after *)param)->
+ num_rules != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_add_flt_rule_after *)param)->
+ num_rules,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_add_flt_rule_after(
(struct ipa_ioc_add_flt_rule_after *)param)) {
retval = -EFAULT;
@@ -925,10 +1020,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_del_flt_rule *)header)->num_hdls;
pyld_sz =
sizeof(struct ipa_ioc_del_flt_rule) +
- ((struct ipa_ioc_del_flt_rule *)header)->num_hdls *
- sizeof(struct ipa_flt_rule_del);
+ pre_entry * sizeof(struct ipa_flt_rule_del);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -938,6 +1034,16 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_del_flt_rule *)param)->num_hdls
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_del_flt_rule *)param)->
+ num_hdls,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_del_flt_rule((struct ipa_ioc_del_flt_rule *)param)) {
retval = -EFAULT;
break;
@@ -954,10 +1060,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_mdfy_flt_rule *)header)->num_rules;
pyld_sz =
sizeof(struct ipa_ioc_mdfy_flt_rule) +
- ((struct ipa_ioc_mdfy_flt_rule *)header)->num_rules *
- sizeof(struct ipa_flt_rule_mdfy);
+ pre_entry * sizeof(struct ipa_flt_rule_mdfy);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -967,6 +1074,16 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_mdfy_flt_rule *)param)->num_rules
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_mdfy_flt_rule *)param)->
+ num_rules,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_mdfy_flt_rule((struct ipa_ioc_mdfy_flt_rule *)param)) {
retval = -EFAULT;
break;
@@ -1080,9 +1197,10 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
-
- pyld_sz = sz + ((struct ipa_ioc_query_intf_tx_props *)
- header)->num_tx_props *
+ pre_entry =
+ ((struct ipa_ioc_query_intf_tx_props *)
+ header)->num_tx_props;
+ pyld_sz = sz + pre_entry *
sizeof(struct ipa_ioc_tx_intf_prop);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
@@ -1093,6 +1211,16 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_query_intf_tx_props *)
+ param)->num_tx_props
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_query_intf_tx_props *)
+ param)->num_tx_props, pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_query_intf_tx_props(
(struct ipa_ioc_query_intf_tx_props *)param)) {
retval = -1;
@@ -1115,9 +1243,10 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
-
- pyld_sz = sz + ((struct ipa_ioc_query_intf_rx_props *)
- header)->num_rx_props *
+ pre_entry =
+ ((struct ipa_ioc_query_intf_rx_props *)
+ header)->num_rx_props;
+ pyld_sz = sz + pre_entry *
sizeof(struct ipa_ioc_rx_intf_prop);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
@@ -1128,6 +1257,15 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_query_intf_rx_props *)
+ param)->num_rx_props != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_query_intf_rx_props *)
+ param)->num_rx_props, pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_query_intf_rx_props(
(struct ipa_ioc_query_intf_rx_props *)param)) {
retval = -1;
@@ -1150,9 +1288,10 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
-
- pyld_sz = sz + ((struct ipa_ioc_query_intf_ext_props *)
- header)->num_ext_props *
+ pre_entry =
+ ((struct ipa_ioc_query_intf_ext_props *)
+ header)->num_ext_props;
+ pyld_sz = sz + pre_entry *
sizeof(struct ipa_ioc_ext_intf_prop);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
@@ -1163,6 +1302,15 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_query_intf_ext_props *)
+ param)->num_ext_props != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_query_intf_ext_props *)
+ param)->num_ext_props, pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_query_intf_ext_props(
(struct ipa_ioc_query_intf_ext_props *)param)) {
retval = -1;
@@ -1179,8 +1327,10 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
- pyld_sz = sizeof(struct ipa_msg_meta) +
+ pre_entry =
((struct ipa_msg_meta *)header)->msg_len;
+ pyld_sz = sizeof(struct ipa_msg_meta) +
+ pre_entry;
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -1190,6 +1340,15 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_msg_meta *)param)->msg_len
+ != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_msg_meta *)param)->msg_len,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_pull_msg((struct ipa_msg_meta *)param,
(char *)param + sizeof(struct ipa_msg_meta),
((struct ipa_msg_meta *)param)->msg_len) !=
@@ -1306,10 +1465,12 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_add_hdr_proc_ctx *)
+ header)->num_proc_ctxs;
pyld_sz =
sizeof(struct ipa_ioc_add_hdr_proc_ctx) +
- ((struct ipa_ioc_add_hdr_proc_ctx *)header)->num_proc_ctxs *
- sizeof(struct ipa_hdr_proc_ctx_add);
+ pre_entry * sizeof(struct ipa_hdr_proc_ctx_add);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -1319,6 +1480,15 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_add_hdr_proc_ctx *)
+ param)->num_proc_ctxs != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_add_hdr_proc_ctx *)
+ param)->num_proc_ctxs, pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_add_hdr_proc_ctx(
(struct ipa_ioc_add_hdr_proc_ctx *)param)) {
retval = -EFAULT;
@@ -1335,10 +1505,11 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ pre_entry =
+ ((struct ipa_ioc_del_hdr_proc_ctx *)header)->num_hdls;
pyld_sz =
sizeof(struct ipa_ioc_del_hdr_proc_ctx) +
- ((struct ipa_ioc_del_hdr_proc_ctx *)header)->num_hdls *
- sizeof(struct ipa_hdr_proc_ctx_del);
+ pre_entry * sizeof(struct ipa_hdr_proc_ctx_del);
param = kzalloc(pyld_sz, GFP_KERNEL);
if (!param) {
retval = -ENOMEM;
@@ -1348,6 +1519,16 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
retval = -EFAULT;
break;
}
+ /* add check in case user-space module compromised */
+ if (unlikely(((struct ipa_ioc_del_hdr_proc_ctx *)
+ param)->num_hdls != pre_entry)) {
+ IPAERR("current %d pre %d\n",
+ ((struct ipa_ioc_del_hdr_proc_ctx *)param)->
+ num_hdls,
+ pre_entry);
+ retval = -EFAULT;
+ break;
+ }
if (ipa3_del_hdr_proc_ctx(
(struct ipa_ioc_del_hdr_proc_ctx *)param)) {
retval = -EFAULT;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 19eb1ee9c881..9e346f12a108 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -3836,6 +3836,7 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in,
union __packed gsi_channel_scratch ch_scratch;
struct ipa_gsi_ep_config *gsi_ep_info;
dma_addr_t dma_addr;
+ dma_addr_t evt_dma_addr;
int result;
if (!ep) {
@@ -3844,13 +3845,13 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in,
}
ep->gsi_evt_ring_hdl = ~0;
+ memset(&gsi_evt_ring_props, 0, sizeof(gsi_evt_ring_props));
/*
* allocate event ring for all interrupt-policy
* pipes and IPA consumers pipes
*/
if (ep->sys->policy != IPA_POLICY_NOINTR_MODE ||
IPA_CLIENT_IS_CONS(ep->client)) {
- memset(&gsi_evt_ring_props, 0, sizeof(gsi_evt_ring_props));
gsi_evt_ring_props.intf = GSI_EVT_CHTYPE_GPI_EV;
gsi_evt_ring_props.intr = GSI_INTR_IRQ;
gsi_evt_ring_props.re_size =
@@ -3859,8 +3860,13 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in,
gsi_evt_ring_props.ring_len = IPA_GSI_EVT_RING_LEN;
gsi_evt_ring_props.ring_base_vaddr =
dma_alloc_coherent(ipa3_ctx->pdev, IPA_GSI_EVT_RING_LEN,
- &dma_addr, 0);
- gsi_evt_ring_props.ring_base_addr = dma_addr;
+ &evt_dma_addr, GFP_KERNEL);
+ if (!gsi_evt_ring_props.ring_base_vaddr) {
+ IPAERR("fail to dma alloc %u bytes\n",
+ IPA_GSI_EVT_RING_LEN);
+ return -ENOMEM;
+ }
+ gsi_evt_ring_props.ring_base_addr = evt_dma_addr;
/* copy mem info */
ep->gsi_mem_info.evt_ring_len = gsi_evt_ring_props.ring_len;
@@ -3895,7 +3901,7 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in,
if (!gsi_ep_info) {
IPAERR("Invalid ep number\n");
result = -EINVAL;
- goto fail_alloc_evt_ring;
+ goto fail_get_gsi_ep_info;
} else
gsi_channel_props.ch_id = gsi_ep_info->ipa_gsi_chan_num;
@@ -3914,7 +3920,12 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in,
gsi_channel_props.ring_len = 2 * in->desc_fifo_sz;
gsi_channel_props.ring_base_vaddr =
dma_alloc_coherent(ipa3_ctx->pdev, gsi_channel_props.ring_len,
- &dma_addr, 0);
+ &dma_addr, GFP_KERNEL);
+ if (!gsi_channel_props.ring_base_vaddr) {
+ IPAERR("fail to dma alloc %u bytes\n",
+ gsi_channel_props.ring_len);
+ goto fail_alloc_channel_ring;
+ }
gsi_channel_props.ring_base_addr = dma_addr;
/* copy mem info */
@@ -3950,7 +3961,7 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in,
result = gsi_write_channel_scratch(ep->gsi_chan_hdl, ch_scratch);
if (result != GSI_STATUS_SUCCESS) {
IPAERR("failed to write scratch %d\n", result);
- goto fail_start_channel;
+ goto fail_write_channel_scratch;
}
result = gsi_start_channel(ep->gsi_chan_hdl);
@@ -3962,17 +3973,25 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in,
return 0;
fail_start_channel:
+fail_write_channel_scratch:
if (gsi_dealloc_channel(ep->gsi_chan_hdl)
!= GSI_STATUS_SUCCESS) {
IPAERR("Failed to dealloc GSI chan.\n");
BUG();
}
fail_alloc_channel:
+ dma_free_coherent(ipa3_ctx->pdev, gsi_channel_props.ring_len,
+ gsi_channel_props.ring_base_vaddr, dma_addr);
+fail_alloc_channel_ring:
+fail_get_gsi_ep_info:
if (ep->gsi_evt_ring_hdl != ~0) {
gsi_dealloc_evt_ring(ep->gsi_evt_ring_hdl);
ep->gsi_evt_ring_hdl = ~0;
}
fail_alloc_evt_ring:
+ if (gsi_evt_ring_props.ring_base_vaddr)
+ dma_free_coherent(ipa3_ctx->pdev, IPA_GSI_EVT_RING_LEN,
+ gsi_evt_ring_props.ring_base_vaddr, evt_dma_addr);
IPAERR("Return with err: %d\n", result);
return result;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
index 32c5004dda95..22756c1fb168 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
@@ -358,6 +358,11 @@ int ipa3_query_intf_ext_props(struct ipa_ioc_query_intf_ext_props *ext)
return result;
}
+static void ipa3_send_msg_free(void *buff, u32 len, u32 type)
+{
+ kfree(buff);
+}
+
/**
* ipa3_send_msg() - Send "message" from kernel client to IPA driver
* @meta: [in] message meta-data
@@ -377,6 +382,7 @@ int ipa3_send_msg(struct ipa_msg_meta *meta, void *buff,
ipa_msg_free_fn callback)
{
struct ipa3_push_msg *msg;
+ void *data = NULL;
if (meta == NULL || (buff == NULL && callback != NULL) ||
(buff != NULL && callback == NULL)) {
@@ -397,8 +403,17 @@ int ipa3_send_msg(struct ipa_msg_meta *meta, void *buff,
}
msg->meta = *meta;
- msg->buff = buff;
- msg->callback = callback;
+ if (meta->msg_len > 0 && buff) {
+ data = kmalloc(meta->msg_len, GFP_KERNEL);
+ if (data == NULL) {
+ IPAERR("fail to alloc data container\n");
+ kfree(msg);
+ return -ENOMEM;
+ }
+ memcpy(data, buff, meta->msg_len);
+ msg->buff = data;
+ msg->callback = ipa3_send_msg_free;
+ }
mutex_lock(&ipa3_ctx->msg_lock);
list_add_tail(&msg->link, &ipa3_ctx->msg_list);
@@ -406,6 +421,8 @@ int ipa3_send_msg(struct ipa_msg_meta *meta, void *buff,
IPA_STATS_INC_CNT(ipa3_ctx->stats.msg_w[meta->msg_type]);
wake_up(&ipa3_ctx->msg_waitq);
+ if (buff)
+ callback(buff, meta->msg_len, meta->msg_type);
return 0;
}
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/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index aebdaab3ac77..2cd08d77df6e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -2467,18 +2467,20 @@ static void rmnet_ipa_get_stats_and_update(void)
req.ipa_stats_type = QMI_IPA_STATS_TYPE_PIPE_V01;
rc = ipa3_qmi_get_data_stats(&req, resp);
+ if (rc) {
+ IPAWANERR("ipa3_qmi_get_data_stats failed: %d\n", rc);
+ kfree(resp);
+ return;
+ }
- if (!rc) {
- memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
- msg_meta.msg_type = IPA_TETHERING_STATS_UPDATE_STATS;
- msg_meta.msg_len =
- sizeof(struct ipa_get_data_stats_resp_msg_v01);
- rc = ipa_send_msg(&msg_meta, resp, rmnet_ipa_free_msg);
- if (rc) {
- IPAWANERR("ipa_send_msg failed: %d\n", rc);
- kfree(resp);
- return;
- }
+ memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
+ msg_meta.msg_type = IPA_TETHERING_STATS_UPDATE_STATS;
+ msg_meta.msg_len = sizeof(struct ipa_get_data_stats_resp_msg_v01);
+ rc = ipa_send_msg(&msg_meta, resp, rmnet_ipa_free_msg);
+ if (rc) {
+ IPAWANERR("ipa_send_msg failed: %d\n", rc);
+ kfree(resp);
+ return;
}
}
@@ -2527,18 +2529,20 @@ static void rmnet_ipa_get_network_stats_and_update(void)
req.mux_id_list[0] = ipa3_rmnet_ctx.metered_mux_id;
rc = ipa3_qmi_get_network_stats(&req, resp);
+ if (rc) {
+ IPAWANERR("ipa3_qmi_get_network_stats failed: %d\n", rc);
+ kfree(resp);
+ return;
+ }
- if (!rc) {
- memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
- msg_meta.msg_type = IPA_TETHERING_STATS_UPDATE_NETWORK_STATS;
- msg_meta.msg_len =
- sizeof(struct ipa_get_apn_data_stats_resp_msg_v01);
- rc = ipa_send_msg(&msg_meta, resp, rmnet_ipa_free_msg);
- if (rc) {
- IPAWANERR("ipa_send_msg failed: %d\n", rc);
- kfree(resp);
- return;
- }
+ memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
+ msg_meta.msg_type = IPA_TETHERING_STATS_UPDATE_NETWORK_STATS;
+ msg_meta.msg_len = sizeof(struct ipa_get_apn_data_stats_resp_msg_v01);
+ rc = ipa_send_msg(&msg_meta, resp, rmnet_ipa_free_msg);
+ if (rc) {
+ IPAWANERR("ipa_send_msg failed: %d\n", rc);
+ kfree(resp);
+ return;
}
}
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/ipa/test/Makefile b/drivers/platform/msm/ipa/test/Makefile
index e1686e608906..c20fd2b42487 100644
--- a/drivers/platform/msm/ipa/test/Makefile
+++ b/drivers/platform/msm/ipa/test/Makefile
@@ -1,2 +1,2 @@
obj-$(CONFIG_IPA_UT) += ipa_ut_mod.o
-ipa_ut_mod-y := ipa_ut_framework.o ipa_test_example.o ipa_test_mhi.o
+ipa_ut_mod-y := ipa_ut_framework.o ipa_test_example.o ipa_test_mhi.o ipa_test_dma.o
diff --git a/drivers/platform/msm/ipa/test/ipa_test_dma.c b/drivers/platform/msm/ipa/test/ipa_test_dma.c
new file mode 100644
index 000000000000..af1b584b0d8c
--- /dev/null
+++ b/drivers/platform/msm/ipa/test/ipa_test_dma.c
@@ -0,0 +1,931 @@
+/* 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/ipa.h>
+#include "../ipa_v3/ipa_i.h"
+#include "ipa_ut_framework.h"
+
+#define IPA_TEST_DMA_WQ_NAME_BUFF_SZ 64
+#define IPA_TEST_DMA_MT_TEST_NUM_WQ 500
+#define IPA_TEST_DMA_MEMCPY_BUFF_SIZE 16384
+#define IPA_TEST_DMA_MAX_PKT_SIZE 0xFF00
+#define IPA_DMA_TEST_LOOP_NUM 1000
+#define IPA_DMA_TEST_INT_LOOP_NUM 50
+#define IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM 128
+#define IPA_DMA_RUN_TEST_UNIT_IN_LOOP(test_unit, iters, rc, args...) \
+ do { \
+ int __i; \
+ for (__i = 0; __i < iters; __i++) { \
+ IPA_UT_LOG(#test_unit " START iter %d\n", __i); \
+ rc = test_unit(args); \
+ if (!rc) \
+ continue; \
+ IPA_UT_LOG(#test_unit " failed %d\n", rc); \
+ break; \
+ } \
+ } while (0)
+
+/**
+ * struct ipa_test_dma_async_user_data - user_data structure for async memcpy
+ * @src_mem: source memory buffer
+ * @dest_mem: destination memory buffer
+ * @call_serial_number: Id of the caller
+ * @copy_done: Completion object
+ */
+struct ipa_test_dma_async_user_data {
+ struct ipa_mem_buffer src_mem;
+ struct ipa_mem_buffer dest_mem;
+ int call_serial_number;
+ struct completion copy_done;
+};
+
+/**
+ * ipa_test_dma_setup() - Suite setup function
+ */
+static int ipa_test_dma_setup(void **ppriv)
+{
+ int rc;
+
+ IPA_UT_DBG("Start Setup\n");
+
+ if (!ipa3_ctx) {
+ IPA_UT_ERR("No IPA ctx\n");
+ return -EINVAL;
+ }
+
+ rc = ipa_dma_init();
+ if (rc)
+ IPA_UT_ERR("Fail to init ipa_dma - return code %d\n", rc);
+ else
+ IPA_UT_DBG("ipa_dma_init() Completed successfully!\n");
+
+ *ppriv = NULL;
+
+ return rc;
+}
+
+/**
+ * ipa_test_dma_teardown() - Suite teardown function
+ */
+static int ipa_test_dma_teardown(void *priv)
+{
+ IPA_UT_DBG("Start Teardown\n");
+ ipa_dma_destroy();
+ return 0;
+}
+
+static int ipa_test_dma_alloc_buffs(struct ipa_mem_buffer *src,
+ struct ipa_mem_buffer *dest,
+ int size)
+{
+ int i;
+ static int val = 1;
+ int rc;
+
+ val++;
+ src->size = size;
+ src->base = dma_alloc_coherent(ipa3_ctx->pdev, src->size,
+ &src->phys_base, GFP_KERNEL);
+ if (!src->base) {
+ IPA_UT_LOG("fail to alloc dma mem %d bytes\n", size);
+ IPA_UT_TEST_FAIL_REPORT("fail to alloc dma mem");
+ return -ENOMEM;
+ }
+
+ dest->size = size;
+ dest->base = dma_alloc_coherent(ipa3_ctx->pdev, dest->size,
+ &dest->phys_base, GFP_KERNEL);
+ if (!dest->base) {
+ IPA_UT_LOG("fail to alloc dma mem %d bytes\n", size);
+ IPA_UT_TEST_FAIL_REPORT("fail to alloc dma mem");
+ rc = -ENOMEM;
+ goto fail_alloc_dest;
+ }
+
+ memset(dest->base, 0, dest->size);
+ for (i = 0; i < src->size; i++)
+ memset(src->base + i, (val + i) & 0xFF, 1);
+ rc = memcmp(dest->base, src->base, dest->size);
+ if (rc == 0) {
+ IPA_UT_LOG("dest & src buffers are equal\n");
+ IPA_UT_TEST_FAIL_REPORT("dest & src buffers are equal");
+ rc = -EFAULT;
+ goto fail_buf_cmp;
+ }
+
+ return 0;
+
+fail_buf_cmp:
+ dma_free_coherent(ipa3_ctx->pdev, dest->size, dest->base,
+ dest->phys_base);
+fail_alloc_dest:
+ dma_free_coherent(ipa3_ctx->pdev, src->size, src->base,
+ src->phys_base);
+ return rc;
+}
+
+static void ipa_test_dma_destroy_buffs(struct ipa_mem_buffer *src,
+ struct ipa_mem_buffer *dest)
+{
+ dma_free_coherent(ipa3_ctx->pdev, src->size, src->base,
+ src->phys_base);
+ dma_free_coherent(ipa3_ctx->pdev, dest->size, dest->base,
+ dest->phys_base);
+}
+
+/**
+ * ipa_test_dma_memcpy_sync() - memcpy in sync mode
+ *
+ * @size: buffer size
+ * @expect_fail: test expects the memcpy to fail
+ *
+ * To be run during tests
+ * 1. Alloc src and dst buffers
+ * 2. sync memcpy src to dst via dma
+ * 3. compare src and dts if memcpy succeeded as expected
+ */
+static int ipa_test_dma_memcpy_sync(int size, bool expect_fail)
+{
+ int rc = 0;
+ int i;
+ struct ipa_mem_buffer src_mem;
+ struct ipa_mem_buffer dest_mem;
+ u8 *src;
+ u8 *dest;
+
+ rc = ipa_test_dma_alloc_buffs(&src_mem, &dest_mem, size);
+ if (rc) {
+ IPA_UT_LOG("fail to alloc buffers\n");
+ IPA_UT_TEST_FAIL_REPORT("fail to alloc buffers");
+ return rc;
+ }
+
+ rc = ipa_dma_sync_memcpy(dest_mem.phys_base, src_mem.phys_base, size);
+ if (!expect_fail && rc) {
+ IPA_UT_LOG("fail to sync memcpy - rc = %d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("sync memcpy failed");
+ goto free_buffs;
+ }
+ if (expect_fail && !rc) {
+ IPA_UT_LOG("sync memcpy succeeded while expected to fail\n");
+ IPA_UT_TEST_FAIL_REPORT(
+ "sync memcpy succeeded while expected to fail");
+ rc = -EFAULT;
+ goto free_buffs;
+ }
+
+ if (!rc) {
+ /* if memcpy succeeded, compare the buffers */
+ rc = memcmp(dest_mem.base, src_mem.base, size);
+ if (rc) {
+ IPA_UT_LOG("BAD memcpy - buffs are not equals\n");
+ IPA_UT_TEST_FAIL_REPORT(
+ "BAD memcpy - buffs are not equals");
+ src = src_mem.base;
+ dest = dest_mem.base;
+ for (i = 0; i < size; i++) {
+ if (*(src + i) != *(dest + i)) {
+ IPA_UT_LOG("byte: %d 0x%x != 0x%x\n",
+ i, *(src + i), *(dest + i));
+ }
+ }
+ }
+ } else {
+ /* if memcpy failed as expected, update the rc */
+ rc = 0;
+ }
+
+free_buffs:
+ ipa_test_dma_destroy_buffs(&src_mem, &dest_mem);
+ return rc;
+}
+
+static void ipa_test_dma_async_memcpy_cb(void *comp_obj)
+{
+ struct completion *xfer_done;
+
+ if (!comp_obj) {
+ IPA_UT_ERR("Invalid Input\n");
+ return;
+ }
+ xfer_done = (struct completion *)comp_obj;
+ complete(xfer_done);
+}
+
+static void ipa_test_dma_async_memcpy_cb_user_data(void *user_param)
+{
+ int rc;
+ int i;
+ u8 *src;
+ u8 *dest;
+ struct ipa_test_dma_async_user_data *udata =
+ (struct ipa_test_dma_async_user_data *)user_param;
+
+ if (!udata) {
+ IPA_UT_ERR("Invalid user param\n");
+ return;
+ }
+
+ rc = memcmp(udata->dest_mem.base, udata->src_mem.base,
+ udata->src_mem.size);
+ if (rc) {
+ IPA_UT_LOG("BAD memcpy - buffs are not equal sn=%d\n",
+ udata->call_serial_number);
+ IPA_UT_TEST_FAIL_REPORT(
+ "BAD memcpy - buffs are not equal");
+ src = udata->src_mem.base;
+ dest = udata->dest_mem.base;
+ for (i = 0; i < udata->src_mem.size; i++) {
+ if (*(src + i) != *(dest + i)) {
+ IPA_UT_ERR("byte: %d 0x%x != 0x%x\n", i,
+ *(src + i), *(dest + i));
+ }
+ }
+ return;
+ }
+
+ IPA_UT_LOG("Notify on async memcopy sn=%d\n",
+ udata->call_serial_number);
+ complete(&(udata->copy_done));
+}
+
+/**
+ * ipa_test_dma_memcpy_async() - memcpy in async mode
+ *
+ * @size: buffer size
+ * @expect_fail: test expected the memcpy to fail
+ *
+ * To be run during tests
+ * 1. Alloc src and dst buffers
+ * 2. async memcpy src to dst via dma and wait for completion
+ * 3. compare src and dts if memcpy succeeded as expected
+ */
+static int ipa_test_dma_memcpy_async(int size, bool expect_fail)
+{
+ int rc = 0;
+ int i;
+ struct ipa_mem_buffer src_mem;
+ struct ipa_mem_buffer dest_mem;
+ u8 *src;
+ u8 *dest;
+ struct completion xfer_done;
+
+ rc = ipa_test_dma_alloc_buffs(&src_mem, &dest_mem, size);
+ if (rc) {
+ IPA_UT_LOG("fail to alloc buffers\n");
+ IPA_UT_TEST_FAIL_REPORT("fail to alloc buffers");
+ return rc;
+ }
+
+ init_completion(&xfer_done);
+ rc = ipa_dma_async_memcpy(dest_mem.phys_base, src_mem.phys_base, size,
+ ipa_test_dma_async_memcpy_cb, &xfer_done);
+ if (!expect_fail && rc) {
+ IPA_UT_LOG("fail to initiate async memcpy - rc=%d\n",
+ rc);
+ IPA_UT_TEST_FAIL_REPORT("async memcpy initiate failed");
+ goto free_buffs;
+ }
+ if (expect_fail && !rc) {
+ IPA_UT_LOG("async memcpy succeeded while expected to fail\n");
+ IPA_UT_TEST_FAIL_REPORT(
+ "async memcpy succeeded while expected to fail");
+ rc = -EFAULT;
+ goto free_buffs;
+ }
+
+ if (!rc) {
+ /* if memcpy succeeded, compare the buffers */
+ wait_for_completion(&xfer_done);
+ rc = memcmp(dest_mem.base, src_mem.base, size);
+ if (rc) {
+ IPA_UT_LOG("BAD memcpy - buffs are not equals\n");
+ IPA_UT_TEST_FAIL_REPORT(
+ "BAD memcpy - buffs are not equals");
+ src = src_mem.base;
+ dest = dest_mem.base;
+ for (i = 0; i < size; i++) {
+ if (*(src + i) != *(dest + i)) {
+ IPA_UT_LOG("byte: %d 0x%x != 0x%x\n",
+ i, *(src + i), *(dest + i));
+ }
+ }
+ }
+ } else {
+ /* if memcpy failed as expected, update the rc */
+ rc = 0;
+ }
+
+free_buffs:
+ ipa_test_dma_destroy_buffs(&src_mem, &dest_mem);
+ return rc;
+}
+
+/**
+ * ipa_test_dma_sync_async_memcpy() - memcpy in sync and then async mode
+ *
+ * @size: buffer size
+ *
+ * To be run during tests
+ * 1. several sync memcopy in row
+ * 2. several async memcopy -
+ * back-to-back (next async try initiated after prev is completed)
+ */
+static int ipa_test_dma_sync_async_memcpy(int size)
+{
+ int rc;
+
+ IPA_DMA_RUN_TEST_UNIT_IN_LOOP(ipa_test_dma_memcpy_sync,
+ IPA_DMA_TEST_INT_LOOP_NUM, rc, size, false);
+ if (rc) {
+ IPA_UT_LOG("sync memcopy fail rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("sync memcopy fail");
+ return rc;
+ }
+
+ IPA_DMA_RUN_TEST_UNIT_IN_LOOP(ipa_test_dma_memcpy_async,
+ IPA_DMA_TEST_INT_LOOP_NUM, rc, size, false);
+ if (rc) {
+ IPA_UT_LOG("async memcopy fail rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("async memcopy fail");
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TEST: test control API - enable/disable dma
+ * 1. enable dma
+ * 2. disable dma
+ */
+static int ipa_test_dma_control_api(void *priv)
+{
+ int rc;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TEST: memcpy before dma enable
+ *
+ * 1. sync memcpy - should fail
+ * 2. async memcpy - should fail
+ */
+static int ipa_test_dma_memcpy_before_enable(void *priv)
+{
+ int rc;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_test_dma_memcpy_sync(IPA_TEST_DMA_MEMCPY_BUFF_SIZE, true);
+ if (rc) {
+ IPA_UT_LOG("sync memcpy succeeded unexpectedly rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("sync memcpy succeeded unexpectedly");
+ return rc;
+ }
+
+ rc = ipa_test_dma_memcpy_async(IPA_TEST_DMA_MEMCPY_BUFF_SIZE, true);
+ if (rc) {
+ IPA_UT_LOG("async memcpy succeeded unexpectedly rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("sync memcpy succeeded unexpectedly");
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TEST: Sync memory copy
+ *
+ * 1. dma enable
+ * 2. sync memcpy
+ * 3. dma disable
+ */
+static int ipa_test_dma_sync_memcpy(void *priv)
+{
+ int rc;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ rc = ipa_test_dma_memcpy_sync(IPA_TEST_DMA_MEMCPY_BUFF_SIZE, false);
+ if (rc) {
+ IPA_UT_LOG("sync memcpy failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("sync memcpy failed");
+ (void)ipa_dma_disable();
+ return rc;
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TEST: Async memory copy
+ *
+ * 1. dma enable
+ * 2. async memcpy
+ * 3. dma disable
+ */
+static int ipa_test_dma_async_memcpy(void *priv)
+{
+ int rc;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ rc = ipa_test_dma_memcpy_async(IPA_TEST_DMA_MEMCPY_BUFF_SIZE, false);
+ if (rc) {
+ IPA_UT_LOG("async memcpy failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("async memcpy failed");
+ (void)ipa_dma_disable();
+ return rc;
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TEST: Iteration of sync memory copy
+ *
+ * 1. dma enable
+ * 2. sync memcpy in loop - in row
+ * 3. dma disable
+ */
+static int ipa_test_dma_sync_memcpy_in_loop(void *priv)
+{
+ int rc;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ IPA_DMA_RUN_TEST_UNIT_IN_LOOP(ipa_test_dma_memcpy_sync,
+ IPA_DMA_TEST_LOOP_NUM, rc,
+ IPA_TEST_DMA_MEMCPY_BUFF_SIZE, false);
+ if (rc) {
+ IPA_UT_LOG("Iterations of sync memcpy failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("Iterations of sync memcpy failed");
+ (void)ipa_dma_disable();
+ return rc;
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TEST: Iteration of async memory copy
+ *
+ * 1. dma enable
+ * 2. async memcpy in loop - back-to-back
+ * next async copy is initiated once previous one completed
+ * 3. dma disable
+ */
+static int ipa_test_dma_async_memcpy_in_loop(void *priv)
+{
+ int rc;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ IPA_DMA_RUN_TEST_UNIT_IN_LOOP(ipa_test_dma_memcpy_async,
+ IPA_DMA_TEST_LOOP_NUM, rc,
+ IPA_TEST_DMA_MEMCPY_BUFF_SIZE, false);
+ if (rc) {
+ IPA_UT_LOG("Iterations of async memcpy failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("Iterations of async memcpy failed");
+ (void)ipa_dma_disable();
+ return rc;
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * TEST: Iteration of interleaved sync and async memory copy
+ *
+ * 1. dma enable
+ * 2. sync and async memcpy in loop - interleaved
+ * 3. dma disable
+ */
+static int ipa_test_dma_interleaved_sync_async_memcpy_in_loop(void *priv)
+{
+ int rc;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ IPA_DMA_RUN_TEST_UNIT_IN_LOOP(ipa_test_dma_sync_async_memcpy,
+ IPA_DMA_TEST_INT_LOOP_NUM, rc,
+ IPA_TEST_DMA_MEMCPY_BUFF_SIZE);
+ if (rc) {
+ IPA_UT_LOG(
+ "Iterations of interleaved sync async memcpy failed rc=%d\n"
+ , rc);
+ IPA_UT_TEST_FAIL_REPORT(
+ "Iterations of interleaved sync async memcpy failed");
+ (void)ipa_dma_disable();
+ return rc;
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ return 0;
+}
+
+static atomic_t ipa_test_dma_mt_test_pass;
+
+static void ipa_test_dma_wrapper_test_one_sync(struct work_struct *work)
+{
+ int rc;
+
+ rc = ipa_test_dma_memcpy_sync(IPA_TEST_DMA_MEMCPY_BUFF_SIZE, false);
+ if (rc) {
+ IPA_UT_LOG("fail sync memcpy from thread rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail sync memcpy from thread");
+ return;
+ }
+ atomic_inc(&ipa_test_dma_mt_test_pass);
+}
+
+static void ipa_test_dma_wrapper_test_one_async(struct work_struct *work)
+{
+ int rc;
+
+ rc = ipa_test_dma_memcpy_async(IPA_TEST_DMA_MEMCPY_BUFF_SIZE, false);
+ if (rc) {
+ IPA_UT_LOG("fail async memcpy from thread rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail async memcpy from thread");
+ return;
+ }
+ atomic_inc(&ipa_test_dma_mt_test_pass);
+}
+
+/**
+ * TEST: Multiple threads running sync and sync mem copy
+ *
+ * 1. dma enable
+ * 2. In-loop
+ * 2.1 create wq for sync memcpy
+ * 2.2 create wq for async memcpy
+ * 2.3 queue sync memcpy work
+ * 2.4 queue async memcoy work
+ * 3. In-loop
+ * 3.1 flush and destroy wq sync
+ * 3.2 flush and destroy wq async
+ * 3. dma disable
+ */
+static int ipa_test_dma_mt_sync_async(void *priv)
+{
+ int rc;
+ int i;
+ static struct workqueue_struct *wq_sync[IPA_TEST_DMA_MT_TEST_NUM_WQ];
+ static struct workqueue_struct *wq_async[IPA_TEST_DMA_MT_TEST_NUM_WQ];
+ static struct work_struct work_async[IPA_TEST_DMA_MT_TEST_NUM_WQ];
+ static struct work_struct work_sync[IPA_TEST_DMA_MT_TEST_NUM_WQ];
+ char buff[IPA_TEST_DMA_WQ_NAME_BUFF_SZ];
+
+ memset(wq_sync, 0, sizeof(wq_sync));
+ memset(wq_sync, 0, sizeof(wq_async));
+ memset(work_async, 0, sizeof(work_async));
+ memset(work_sync, 0, sizeof(work_sync));
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ atomic_set(&ipa_test_dma_mt_test_pass, 0);
+ for (i = 0; i < IPA_TEST_DMA_MT_TEST_NUM_WQ; i++) {
+ snprintf(buff, sizeof(buff), "ipa_test_dmaSwq%d", i);
+ wq_sync[i] = create_singlethread_workqueue(buff);
+ if (!wq_sync[i]) {
+ IPA_UT_ERR("failed to create sync wq#%d\n", i);
+ rc = -EFAULT;
+ goto fail_create_wq;
+ }
+ snprintf(buff, IPA_RESOURCE_NAME_MAX, "ipa_test_dmaAwq%d", i);
+ wq_async[i] = create_singlethread_workqueue(buff);
+ if (!wq_async[i]) {
+ IPA_UT_ERR("failed to create async wq#%d\n", i);
+ rc = -EFAULT;
+ goto fail_create_wq;
+ }
+
+ INIT_WORK(&work_sync[i], ipa_test_dma_wrapper_test_one_sync);
+ queue_work(wq_sync[i], &work_sync[i]);
+ INIT_WORK(&work_async[i], ipa_test_dma_wrapper_test_one_async);
+ queue_work(wq_async[i], &work_async[i]);
+ }
+
+ for (i = 0; i < IPA_TEST_DMA_MT_TEST_NUM_WQ; i++) {
+ flush_workqueue(wq_sync[i]);
+ destroy_workqueue(wq_sync[i]);
+ flush_workqueue(wq_async[i]);
+ destroy_workqueue(wq_async[i]);
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ if ((2 * IPA_TEST_DMA_MT_TEST_NUM_WQ) !=
+ atomic_read(&ipa_test_dma_mt_test_pass)) {
+ IPA_UT_LOG(
+ "Multi-threaded sync/async memcopy failed passed=%d\n"
+ , atomic_read(&ipa_test_dma_mt_test_pass));
+ IPA_UT_TEST_FAIL_REPORT(
+ "Multi-threaded sync/async memcopy failed");
+ return -EFAULT;
+ }
+
+ return 0;
+
+fail_create_wq:
+ (void)ipa_dma_disable();
+ for (i = 0; i < IPA_TEST_DMA_MT_TEST_NUM_WQ; i++) {
+ if (wq_sync[i])
+ destroy_workqueue(wq_sync[i]);
+ if (wq_async[i])
+ destroy_workqueue(wq_async[i]);
+ }
+
+ return rc;
+}
+
+/**
+ * TEST: Several parallel async memory copy iterations
+ *
+ * 1. create several user_data structures - one per iteration
+ * 2. allocate buffs. Give slice for each iteration
+ * 3. iterations of async mem copy
+ * 4. wait for all to complete
+ * 5. dma disable
+ */
+static int ipa_test_dma_parallel_async_memcpy_in_loop(void *priv)
+{
+ int rc;
+ struct ipa_test_dma_async_user_data *udata;
+ struct ipa_mem_buffer all_src_mem;
+ struct ipa_mem_buffer all_dest_mem;
+ int i;
+ bool is_fail = false;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ udata = kzalloc(IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM *
+ sizeof(struct ipa_test_dma_async_user_data), GFP_KERNEL);
+ if (!udata) {
+ IPA_UT_ERR("fail allocate user_data array\n");
+ (void)ipa_dma_disable();
+ return -ENOMEM;
+ }
+
+ rc = ipa_test_dma_alloc_buffs(&all_src_mem, &all_dest_mem,
+ IPA_TEST_DMA_MEMCPY_BUFF_SIZE);
+ if (rc) {
+ IPA_UT_LOG("fail to alloc buffers\n");
+ IPA_UT_TEST_FAIL_REPORT("fail to alloc buffers");
+ kfree(udata);
+ (void)ipa_dma_disable();
+ return rc;
+ }
+
+ for (i = 0 ; i < IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM ; i++) {
+ udata[i].src_mem.size =
+ IPA_TEST_DMA_MEMCPY_BUFF_SIZE /
+ IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM;
+ udata[i].src_mem.base = all_src_mem.base + i *
+ (IPA_TEST_DMA_MEMCPY_BUFF_SIZE /
+ IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM);
+ udata[i].src_mem.phys_base = all_src_mem.phys_base + i *
+ (IPA_TEST_DMA_MEMCPY_BUFF_SIZE /
+ IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM);
+
+ udata[i].dest_mem.size =
+ (IPA_TEST_DMA_MEMCPY_BUFF_SIZE /
+ IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM);
+ udata[i].dest_mem.base = all_dest_mem.base + i *
+ (IPA_TEST_DMA_MEMCPY_BUFF_SIZE /
+ IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM);
+ udata[i].dest_mem.phys_base = all_dest_mem.phys_base + i *
+ (IPA_TEST_DMA_MEMCPY_BUFF_SIZE /
+ IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM);
+
+ udata[i].call_serial_number = i + 1;
+ init_completion(&(udata[i].copy_done));
+ rc = ipa_dma_async_memcpy(udata[i].dest_mem.phys_base,
+ udata[i].src_mem.phys_base,
+ (IPA_TEST_DMA_MEMCPY_BUFF_SIZE /
+ IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM),
+ ipa_test_dma_async_memcpy_cb_user_data, &udata[i]);
+ if (rc) {
+ IPA_UT_LOG("async memcpy initiation fail i=%d rc=%d\n",
+ i, rc);
+ is_fail = true;
+ }
+ }
+
+ for (i = 0; i < IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM ; i++)
+ wait_for_completion(&udata[i].copy_done);
+
+ ipa_test_dma_destroy_buffs(&all_src_mem, &all_dest_mem);
+ kfree(udata);
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ if (is_fail) {
+ IPA_UT_LOG("async memcopy failed\n");
+ IPA_UT_TEST_FAIL_REPORT("async memcopy failed");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/**
+ * TEST: Sync memory copy
+ *
+ * 1. dma enable
+ * 2. sync memcpy with max packet size
+ * 3. dma disable
+ */
+static int ipa_test_dma_sync_memcpy_max_pkt_size(void *priv)
+{
+ int rc;
+
+ IPA_UT_LOG("Test Start\n");
+
+ rc = ipa_dma_enable();
+ if (rc) {
+ IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+ return rc;
+ }
+
+ rc = ipa_test_dma_memcpy_sync(IPA_TEST_DMA_MAX_PKT_SIZE, false);
+ if (rc) {
+ IPA_UT_LOG("sync memcpy failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("sync memcpy failed");
+ (void)ipa_dma_disable();
+ return rc;
+ }
+
+ rc = ipa_dma_disable();
+ if (rc) {
+ IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+ IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+ return rc;
+ }
+
+ return 0;
+}
+
+/* Suite definition block */
+IPA_UT_DEFINE_SUITE_START(dma, "DMA for GSI",
+ ipa_test_dma_setup, ipa_test_dma_teardown)
+{
+ IPA_UT_ADD_TEST(control_api,
+ "Control API",
+ ipa_test_dma_control_api,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(memcpy_before_enable,
+ "Call memcpy before dma enable and expect it to fail",
+ ipa_test_dma_memcpy_before_enable,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(sync_memcpy,
+ "Sync memory copy",
+ ipa_test_dma_sync_memcpy,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(async_memcpy,
+ "Async memory copy",
+ ipa_test_dma_async_memcpy,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(sync_memcpy_in_loop,
+ "Several sync memory copy iterations",
+ ipa_test_dma_sync_memcpy_in_loop,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(async_memcpy_in_loop,
+ "Several async memory copy iterations",
+ ipa_test_dma_async_memcpy_in_loop,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(interleaved_sync_async_memcpy_in_loop,
+ "Several interleaved sync and async memory copy iterations",
+ ipa_test_dma_interleaved_sync_async_memcpy_in_loop,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(multi_threaded_multiple_sync_async_memcpy,
+ "Several multi-threaded sync and async memory copy iterations",
+ ipa_test_dma_mt_sync_async,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(parallel_async_memcpy_in_loop,
+ "Several parallel async memory copy iterations",
+ ipa_test_dma_parallel_async_memcpy_in_loop,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+ IPA_UT_ADD_TEST(sync_memcpy_max_pkt_size,
+ "Sync memory copy with max packet size",
+ ipa_test_dma_sync_memcpy_max_pkt_size,
+ true, IPA_HW_v3_0, IPA_HW_MAX),
+} IPA_UT_DEFINE_SUITE_END(dma);
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h b/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h
index 944800f8e4be..bbb8b771ba0b 100644
--- a/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h
+++ b/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h
@@ -21,6 +21,7 @@
* No importance for order.
*/
IPA_UT_DECLARE_SUITE(mhi);
+IPA_UT_DECLARE_SUITE(dma);
IPA_UT_DECLARE_SUITE(example);
@@ -31,6 +32,7 @@ IPA_UT_DECLARE_SUITE(example);
IPA_UT_DEFINE_ALL_SUITES_START
{
IPA_UT_REGISTER_SUITE(mhi),
+ IPA_UT_REGISTER_SUITE(dma),
IPA_UT_REGISTER_SUITE(example),
} IPA_UT_DEFINE_ALL_SUITES_END;
diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c
index f34242f29e2b..38a6474dd06f 100644
--- a/drivers/platform/msm/sps/sps_bam.c
+++ b/drivers/platform/msm/sps/sps_bam.c
@@ -1083,6 +1083,7 @@ int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index)
{
struct sps_pipe *pipe;
int result;
+ unsigned long flags;
if (pipe_index >= dev->props.num_pipes) {
SPS_ERR(dev, "sps:Invalid BAM %pa pipe: %d\n", BAM_ID(dev),
@@ -1094,8 +1095,10 @@ int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index)
pipe = dev->pipes[pipe_index];
if (BAM_PIPE_IS_ASSIGNED(pipe)) {
if ((dev->pipe_active_mask & (1UL << pipe_index))) {
+ spin_lock_irqsave(&dev->isr_lock, flags);
list_del(&pipe->list);
dev->pipe_active_mask &= ~(1UL << pipe_index);
+ spin_unlock_irqrestore(&dev->isr_lock, flags);
}
dev->pipe_remote_mask &= ~(1UL << pipe_index);
if (pipe->connect.options & SPS_O_NO_DISABLE)
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index b1c3441b285a..545a1e684b25 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -227,6 +227,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(usb_otg),
POWER_SUPPLY_ATTR(battery_charging_enabled),
POWER_SUPPLY_ATTR(charging_enabled),
+ POWER_SUPPLY_ATTR(step_charging_enabled),
+ POWER_SUPPLY_ATTR(step_charging_step),
POWER_SUPPLY_ATTR(pin_enabled),
POWER_SUPPLY_ATTR(input_suspend),
POWER_SUPPLY_ATTR(input_voltage_regulation),
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/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c
index 90a93064ca84..7caa9548308a 100644
--- a/drivers/power/qcom-charger/qpnp-smb2.c
+++ b/drivers/power/qcom-charger/qpnp-smb2.c
@@ -38,15 +38,15 @@ static struct smb_params v1_params = {
.fv = {
.name = "float voltage",
.reg = FLOAT_VOLTAGE_CFG_REG,
- .min_u = 2500000,
- .max_u = 5000000,
- .step_u = 10000,
+ .min_u = 3487500,
+ .max_u = 4920000,
+ .step_u = 7500,
},
.usb_icl = {
.name = "usb input current limit",
.reg = USBIN_CURRENT_LIMIT_CFG_REG,
.min_u = 0,
- .max_u = 6000000,
+ .max_u = 4800000,
.step_u = 25000,
},
.icl_stat = {
@@ -112,8 +112,90 @@ static struct smb_params v1_params = {
.max_u = 1575000,
.step_u = 25000,
},
+ .step_soc_threshold[0] = {
+ .name = "step charge soc threshold 1",
+ .reg = STEP_CHG_SOC_OR_BATT_V_TH1_REG,
+ .min_u = 0,
+ .max_u = 100,
+ .step_u = 1,
+ },
+ .step_soc_threshold[1] = {
+ .name = "step charge soc threshold 2",
+ .reg = STEP_CHG_SOC_OR_BATT_V_TH2_REG,
+ .min_u = 0,
+ .max_u = 100,
+ .step_u = 1,
+ },
+ .step_soc_threshold[2] = {
+ .name = "step charge soc threshold 3",
+ .reg = STEP_CHG_SOC_OR_BATT_V_TH3_REG,
+ .min_u = 0,
+ .max_u = 100,
+ .step_u = 1,
+ },
+ .step_soc_threshold[3] = {
+ .name = "step charge soc threshold 4",
+ .reg = STEP_CHG_SOC_OR_BATT_V_TH4_REG,
+ .min_u = 0,
+ .max_u = 100,
+ .step_u = 1,
+ },
+ .step_soc = {
+ .name = "step charge soc",
+ .reg = STEP_CHG_SOC_VBATT_V_REG,
+ .min_u = 0,
+ .max_u = 100,
+ .step_u = 1,
+ .set_proc = smblib_mapping_soc_from_field_value,
+ },
+ .step_cc_delta[0] = {
+ .name = "step charge current delta 1",
+ .reg = STEP_CHG_CURRENT_DELTA1_REG,
+ .min_u = 100000,
+ .max_u = 3200000,
+ .step_u = 100000,
+ .get_proc = smblib_mapping_cc_delta_to_field_value,
+ .set_proc = smblib_mapping_cc_delta_from_field_value,
+ },
+ .step_cc_delta[1] = {
+ .name = "step charge current delta 2",
+ .reg = STEP_CHG_CURRENT_DELTA2_REG,
+ .min_u = 100000,
+ .max_u = 3200000,
+ .step_u = 100000,
+ .get_proc = smblib_mapping_cc_delta_to_field_value,
+ .set_proc = smblib_mapping_cc_delta_from_field_value,
+ },
+ .step_cc_delta[2] = {
+ .name = "step charge current delta 3",
+ .reg = STEP_CHG_CURRENT_DELTA3_REG,
+ .min_u = 100000,
+ .max_u = 3200000,
+ .step_u = 100000,
+ .get_proc = smblib_mapping_cc_delta_to_field_value,
+ .set_proc = smblib_mapping_cc_delta_from_field_value,
+ },
+ .step_cc_delta[3] = {
+ .name = "step charge current delta 4",
+ .reg = STEP_CHG_CURRENT_DELTA4_REG,
+ .min_u = 100000,
+ .max_u = 3200000,
+ .step_u = 100000,
+ .get_proc = smblib_mapping_cc_delta_to_field_value,
+ .set_proc = smblib_mapping_cc_delta_from_field_value,
+ },
+ .step_cc_delta[4] = {
+ .name = "step charge current delta 5",
+ .reg = STEP_CHG_CURRENT_DELTA5_REG,
+ .min_u = 100000,
+ .max_u = 3200000,
+ .step_u = 100000,
+ .get_proc = smblib_mapping_cc_delta_to_field_value,
+ .set_proc = smblib_mapping_cc_delta_from_field_value,
+ },
};
+#define STEP_CHARGING_MAX_STEPS 5
struct smb_dt_props {
bool suspend_input;
int fcc_ua;
@@ -121,6 +203,8 @@ struct smb_dt_props {
int dc_icl_ua;
int fv_uv;
int wipower_max_uw;
+ u32 step_soc_threshold[STEP_CHARGING_MAX_STEPS - 1];
+ s32 step_cc_delta[STEP_CHARGING_MAX_STEPS];
};
struct smb2 {
@@ -150,6 +234,28 @@ static int smb2_parse_dt(struct smb2 *chip)
return -EINVAL;
}
+ chg->step_chg_enabled = true;
+
+ if (of_property_count_u32_elems(node, "qcom,step-soc-thresholds")
+ != STEP_CHARGING_MAX_STEPS - 1)
+ chg->step_chg_enabled = false;
+
+ rc = of_property_read_u32_array(node, "qcom,step-soc-thresholds",
+ chip->dt.step_soc_threshold,
+ STEP_CHARGING_MAX_STEPS - 1);
+ if (rc < 0)
+ chg->step_chg_enabled = false;
+
+ if (of_property_count_u32_elems(node, "qcom,step-current-deltas")
+ != STEP_CHARGING_MAX_STEPS)
+ chg->step_chg_enabled = false;
+
+ rc = of_property_read_u32_array(node, "qcom,step-current-deltas",
+ chip->dt.step_cc_delta,
+ STEP_CHARGING_MAX_STEPS);
+ if (rc < 0)
+ chg->step_chg_enabled = false;
+
chip->dt.suspend_input = of_property_read_bool(node,
"qcom,suspend-input");
@@ -173,10 +279,10 @@ static int smb2_parse_dt(struct smb2 *chip)
if (rc < 0)
chip->dt.dc_icl_ua = -EINVAL;
- rc = of_property_read_u32(node,
- "qcom,wipower-max-uw", &chip->dt.wipower_max_uw);
+ rc = of_property_read_u32(node, "qcom,wipower-max-uw",
+ &chip->dt.wipower_max_uw);
if (rc < 0)
- chip->dt.wipower_max_uw = SMB2_DEFAULT_WPWR_UW;
+ chip->dt.wipower_max_uw = -EINVAL;
if (of_find_property(node, "qcom,thermal-mitigation", &byte_len)) {
chg->thermal_mitigation = devm_kzalloc(chg->dev, byte_len,
@@ -218,6 +324,7 @@ static enum power_supply_property smb2_usb_props[] = {
POWER_SUPPLY_PROP_PD_ALLOWED,
POWER_SUPPLY_PROP_PD_ACTIVE,
POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
};
static int smb2_usb_get_prop(struct power_supply *psy,
@@ -277,12 +384,19 @@ static int smb2_usb_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED:
rc = smblib_get_prop_input_current_settled(chg, val);
break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW:
+ rc = smblib_get_prop_usb_current_now(chg, val);
+ break;
default:
pr_err("get prop %d is not supported\n", psp);
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,
@@ -396,8 +510,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,
@@ -476,41 +593,57 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
+ POWER_SUPPLY_PROP_CHARGER_TEMP,
+ POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
};
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);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
- smblib_get_prop_batt_status(chg, val);
+ rc = smblib_get_prop_batt_status(chg, val);
break;
case POWER_SUPPLY_PROP_HEALTH:
- smblib_get_prop_batt_health(chg, val);
+ rc = smblib_get_prop_batt_health(chg, val);
break;
case POWER_SUPPLY_PROP_PRESENT:
- smblib_get_prop_batt_present(chg, val);
+ rc = smblib_get_prop_batt_present(chg, val);
break;
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
- smblib_get_prop_input_suspend(chg, val);
+ rc = smblib_get_prop_input_suspend(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
- smblib_get_prop_batt_charge_type(chg, val);
+ rc = smblib_get_prop_batt_charge_type(chg, val);
break;
case POWER_SUPPLY_PROP_CAPACITY:
- smblib_get_prop_batt_capacity(chg, val);
+ rc = smblib_get_prop_batt_capacity(chg, val);
break;
case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
- smblib_get_prop_system_temp_level(chg, val);
+ rc = smblib_get_prop_system_temp_level(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGER_TEMP:
+ rc = smblib_get_prop_charger_temp(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
+ rc = smblib_get_prop_charger_temp_max(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
+ rc = smblib_get_prop_input_current_limited(chg, val);
break;
default:
pr_err("batt power supply prop %d not supported\n", psp);
return -EINVAL;
}
-
+ if (rc < 0) {
+ pr_debug("Couldn't get prop %d rc = %d\n", psp, rc);
+ return -ENODATA;
+ }
return 0;
}
@@ -669,6 +802,73 @@ static int smb2_init_vconn_regulator(struct smb2 *chip)
/***************************
* HARDWARE INITIALIZATION *
***************************/
+static int smb2_config_step_charging(struct smb2 *chip)
+{
+ struct smb_charger *chg = &chip->chg;
+ int rc = 0;
+ int i;
+
+ if (!chg->step_chg_enabled)
+ return rc;
+
+ for (i = 0; i < STEP_CHARGING_MAX_STEPS - 1; i++) {
+ rc = smblib_set_charge_param(chg,
+ &chg->param.step_soc_threshold[i],
+ chip->dt.step_soc_threshold[i]);
+ if (rc < 0) {
+ pr_err("Couldn't configure soc thresholds rc = %d\n",
+ rc);
+ goto err_out;
+ }
+ }
+
+ for (i = 0; i < STEP_CHARGING_MAX_STEPS; i++) {
+ rc = smblib_set_charge_param(chg, &chg->param.step_cc_delta[i],
+ chip->dt.step_cc_delta[i]);
+ if (rc < 0) {
+ pr_err("Couldn't configure cc delta rc = %d\n",
+ rc);
+ goto err_out;
+ }
+ }
+
+ rc = smblib_write(chg, STEP_CHG_UPDATE_REQUEST_TIMEOUT_CFG_REG,
+ STEP_CHG_UPDATE_REQUEST_TIMEOUT_40S);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't configure soc request timeout reg rc=%d\n",
+ rc);
+ goto err_out;
+ }
+
+ rc = smblib_write(chg, STEP_CHG_UPDATE_FAIL_TIMEOUT_CFG_REG,
+ STEP_CHG_UPDATE_FAIL_TIMEOUT_120S);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't configure soc fail timeout reg rc=%d\n",
+ rc);
+ goto err_out;
+ }
+
+ /*
+ * enable step charging, source soc, standard mode, go to final
+ * state in case of failure.
+ */
+ rc = smblib_write(chg, CHGR_STEP_CHG_MODE_CFG_REG,
+ STEP_CHARGING_ENABLE_BIT |
+ STEP_CHARGING_SOURCE_SELECT_BIT |
+ STEP_CHARGING_SOC_FAIL_OPTION_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't configure charger rc=%d\n", rc);
+ goto err_out;
+ }
+
+ return 0;
+err_out:
+ chg->step_chg_enabled = false;
+ return rc;
+}
+
static int smb2_config_wipower_input_power(struct smb2 *chip, int uw)
{
int rc;
@@ -676,6 +876,9 @@ static int smb2_config_wipower_input_power(struct smb2 *chip, int uw)
struct smb_charger *chg = &chip->chg;
s64 nw = (s64)uw * 1000;
+ if (uw < 0)
+ return 0;
+
ua = div_s64(nw, ZIN_ICL_PT_MAX_MV);
rc = smblib_set_charge_param(chg, &chg->param.dc_icl_pt_lv, ua);
if (rc < 0) {
@@ -833,6 +1036,14 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+ /* configure step charging */
+ rc = smb2_config_step_charging(chip);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't configure step charging rc=%d\n",
+ rc);
+ return rc;
+ }
+
/* configure wipower watts */
rc = smb2_config_wipower_input_power(chip, chip->dt.wipower_max_uw);
if (rc < 0) {
@@ -840,6 +1051,16 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+ /* configure PMI stat output to enable and disable parallel charging */
+ rc = smblib_masked_write(chg, STAT_CFG_REG,
+ STAT_PARALLEL_CFG_BIT | STAT_SW_OVERRIDE_CFG_BIT,
+ STAT_PARALLEL_CFG_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't configure signal for parallel rc=%d\n", rc);
+ return rc;
+ }
+
return rc;
}
@@ -856,6 +1077,8 @@ static int smb2_determine_initial_status(struct smb2 *chip)
smblib_handle_usb_source_change(0, &irq_data);
smblib_handle_chg_state_change(0, &irq_data);
smblib_handle_icl_change(0, &irq_data);
+ smblib_handle_step_chg_state_change(0, &irq_data);
+ smblib_handle_step_chg_soc_update_request(0, &irq_data);
return 0;
}
@@ -875,9 +1098,12 @@ 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_debug },
- { "step-chg-soc-update-fail", smblib_handle_debug },
- { "step-chg-soc-update-request", smblib_handle_debug },
+ { "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 },
/* OTG IRQs */
{ "otg-fail", smblib_handle_debug },
{ "otg-overcurrent", smblib_handle_debug },
@@ -912,7 +1138,7 @@ static struct smb2_irq_info smb2_irqs[] = {
{ "wdog-bark", NULL },
{ "aicl-fail", smblib_handle_debug },
{ "aicl-done", smblib_handle_debug },
- { "high-duty-cycle", 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 },
@@ -1023,7 +1249,11 @@ static int smb2_probe(struct platform_device *pdev)
return -EINVAL;
}
- smblib_init(chg);
+ rc = smblib_init(chg);
+ if (rc < 0) {
+ pr_err("Smblib_init failed rc=%d\n", rc);
+ goto cleanup;
+ }
rc = smb2_parse_dt(chip);
if (rc < 0) {
diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c
index 60534bdddd26..21b330127369 100644
--- a/drivers/power/qcom-charger/smb-lib.c
+++ b/drivers/power/qcom-charger/smb-lib.c
@@ -12,6 +12,7 @@
#include <linux/device.h>
#include <linux/regmap.h>
+#include <linux/iio/consumer.h>
#include <linux/power_supply.h>
#include <linux/regulator/driver.h>
#include <linux/irq.h>
@@ -82,12 +83,40 @@ unlock:
return rc;
}
+static int smblib_get_step_charging_adjustment(struct smb_charger *chg,
+ int *cc_offset)
+{
+ int step_state;
+ int rc;
+ u8 stat;
+
+ if (!chg->step_chg_enabled) {
+ *cc_offset = 0;
+ 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;
+ }
+
+ step_state = (stat & STEP_CHARGING_STATUS_MASK) >> 3;
+ rc = smblib_get_charge_param(chg, &chg->param.step_cc_delta[step_state],
+ cc_offset);
+
+ return rc;
+}
+
static void smblib_fcc_split_ua(struct smb_charger *chg, int total_fcc,
int *master_ua, int *slave_ua)
{
int rc, cc_reduction_ua = 0;
+ int step_cc_delta;
int master_percent = min(max(*chg->pl.master_percent, 0), 100);
union power_supply_propval pval = {0, };
+ int effective_fcc;
/*
* if master_percent is 0, s/w will configure master's fcc to zero and
@@ -111,10 +140,21 @@ static void smblib_fcc_split_ua(struct smb_charger *chg, int total_fcc,
}
}
- total_fcc = max(0, total_fcc - cc_reduction_ua);
- *master_ua = (total_fcc * master_percent) / 100;
- *slave_ua = (total_fcc - *master_ua) * chg->pl.taper_percent / 100;
- *master_ua += cc_reduction_ua;
+ rc = smblib_get_step_charging_adjustment(chg, &step_cc_delta);
+ if (rc < 0)
+ step_cc_delta = 0;
+
+ /*
+ * During JEITA condition and with step_charging enabled, PMI will
+ * pick the lower of the two value: (FCC - JEITA current compensation)
+ * or (FCC + step_charging current delta)
+ */
+
+ effective_fcc = min(max(0, total_fcc - cc_reduction_ua),
+ max(0, total_fcc + step_cc_delta));
+ *master_ua = (effective_fcc * master_percent) / 100;
+ *slave_ua = (effective_fcc - *master_ua) * chg->pl.taper_percent / 100;
+ *master_ua = max(0, *master_ua + total_fcc - effective_fcc);
}
/********************
@@ -246,6 +286,28 @@ int smblib_set_charge_param(struct smb_charger *chg,
return rc;
}
+static int step_charge_soc_update(struct smb_charger *chg, int capacity)
+{
+ int rc = 0;
+
+ rc = smblib_set_charge_param(chg, &chg->param.step_soc, capacity);
+ if (rc < 0) {
+ dev_err(chg->dev, "Error in updating soc, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = smblib_write(chg, STEP_CHG_SOC_VBATT_V_UPDATE_REG,
+ STEP_CHG_SOC_VBATT_V_UPDATE_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev,
+ "Couldn't set STEP_CHG_SOC_VBATT_V_UPDATE_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return rc;
+}
+
int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend)
{
int rc = 0;
@@ -394,6 +456,41 @@ static int smblib_register_notifier(struct smb_charger *chg)
return 0;
}
+int smblib_mapping_soc_from_field_value(struct smb_chg_param *param,
+ int val_u, u8 *val_raw)
+{
+ if (val_u > param->max_u || val_u < param->min_u)
+ return -EINVAL;
+
+ *val_raw = val_u << 1;
+
+ return 0;
+}
+
+int smblib_mapping_cc_delta_to_field_value(struct smb_chg_param *param,
+ u8 val_raw)
+{
+ int val_u = val_raw * param->step_u + param->min_u;
+
+ if (val_u > param->max_u)
+ val_u -= param->max_u * 2;
+
+ return val_u;
+}
+
+int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param,
+ int val_u, u8 *val_raw)
+{
+ if (val_u > param->max_u || val_u < param->min_u - param->max_u)
+ return -EINVAL;
+
+ val_u += param->max_u * 2 - param->min_u;
+ val_u %= param->max_u * 2;
+ *val_raw = val_u / param->step_u;
+
+ return 0;
+}
+
/*********************
* VOTABLE CALLBACKS *
*********************/
@@ -891,6 +988,21 @@ int smblib_get_prop_system_temp_level(struct smb_charger *chg,
return 0;
}
+int smblib_get_prop_input_current_limited(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ u8 stat;
+ int rc;
+
+ rc = smblib_read(chg, AICL_STATUS_REG, &stat);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't read AICL_STATUS rc=%d\n", rc);
+ return rc;
+ }
+ val->intval = (stat & SOFT_ILIMIT_BIT) || chg->is_hdc;
+ return 0;
+}
+
/***********************
* BATTERY PSY SETTERS *
***********************/
@@ -1076,12 +1188,20 @@ int smblib_get_prop_usb_online(struct smb_charger *chg,
int smblib_get_prop_usb_voltage_now(struct smb_charger *chg,
union power_supply_propval *val)
{
- if (chg->vbus_present)
- val->intval = MICRO_5V;
- else
- val->intval = 0;
+ int rc = 0;
- return 0;
+ rc = smblib_get_prop_usb_present(chg, val);
+ if (rc < 0 || !val->intval)
+ return rc;
+
+ if (!chg->iio.usbin_v_chan ||
+ PTR_ERR(chg->iio.usbin_v_chan) == -EPROBE_DEFER)
+ chg->iio.usbin_v_chan = iio_channel_get(chg->dev, "usbin_v");
+
+ if (IS_ERR(chg->iio.usbin_v_chan))
+ return PTR_ERR(chg->iio.usbin_v_chan);
+
+ return iio_read_channel_processed(chg->iio.usbin_v_chan, &val->intval);
}
int smblib_get_prop_usb_current_max(struct smb_charger *chg,
@@ -1091,6 +1211,59 @@ int smblib_get_prop_usb_current_max(struct smb_charger *chg,
return 0;
}
+int smblib_get_prop_usb_current_now(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc = 0;
+
+ rc = smblib_get_prop_usb_present(chg, val);
+ if (rc < 0 || !val->intval)
+ return rc;
+
+ if (!chg->iio.usbin_i_chan ||
+ PTR_ERR(chg->iio.usbin_i_chan) == -EPROBE_DEFER)
+ chg->iio.usbin_i_chan = iio_channel_get(chg->dev, "usbin_i");
+
+ if (IS_ERR(chg->iio.usbin_i_chan))
+ return PTR_ERR(chg->iio.usbin_i_chan);
+
+ return iio_read_channel_processed(chg->iio.usbin_i_chan, &val->intval);
+}
+
+int smblib_get_prop_charger_temp(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+
+ if (!chg->iio.temp_chan ||
+ PTR_ERR(chg->iio.temp_chan) == -EPROBE_DEFER)
+ chg->iio.temp_chan = iio_channel_get(chg->dev, "charger_temp");
+
+ if (IS_ERR(chg->iio.temp_chan))
+ return PTR_ERR(chg->iio.temp_chan);
+
+ rc = iio_read_channel_processed(chg->iio.temp_chan, &val->intval);
+ val->intval /= 100;
+ return rc;
+}
+
+int smblib_get_prop_charger_temp_max(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+
+ if (!chg->iio.temp_max_chan ||
+ PTR_ERR(chg->iio.temp_max_chan) == -EPROBE_DEFER)
+ chg->iio.temp_max_chan = iio_channel_get(chg->dev,
+ "charger_temp_max");
+ if (IS_ERR(chg->iio.temp_max_chan))
+ return PTR_ERR(chg->iio.temp_max_chan);
+
+ rc = iio_read_channel_processed(chg->iio.temp_max_chan, &val->intval);
+ val->intval /= 100;
+ return rc;
+}
+
int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg,
union power_supply_propval *val)
{
@@ -1427,6 +1600,57 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data)
return IRQ_HANDLED;
}
+irqreturn_t smblib_handle_step_chg_state_change(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+
+ if (chg->step_chg_enabled)
+ rerun_election(chg->fcc_votable);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t smblib_handle_step_chg_soc_update_fail(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+
+ if (chg->step_chg_enabled)
+ rerun_election(chg->fcc_votable);
+
+ return IRQ_HANDLED;
+}
+
+#define STEP_SOC_REQ_MS 3000
+irqreturn_t smblib_handle_step_chg_soc_update_request(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+ int rc;
+ union power_supply_propval pval = {0, };
+
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+
+ if (!chg->bms_psy) {
+ schedule_delayed_work(&chg->step_soc_req_work,
+ msecs_to_jiffies(STEP_SOC_REQ_MS));
+ return IRQ_HANDLED;
+ }
+
+ rc = smblib_get_prop_batt_capacity(chg, &pval);
+ if (rc < 0)
+ dev_err(chg->dev, "Couldn't get batt capacity rc=%d\n", rc);
+ else
+ step_charge_soc_update(chg, pval.intval);
+
+ return IRQ_HANDLED;
+}
+
irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
@@ -1756,6 +1980,17 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data)
return IRQ_HANDLED;
}
+irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+
+ chg->is_hdc = true;
+ schedule_delayed_work(&chg->clear_hdc_work, msecs_to_jiffies(60));
+
+ return IRQ_HANDLED;
+}
+
/***************
* Work Queues *
***************/
@@ -1774,13 +2009,29 @@ static void smblib_hvdcp_detect_work(struct work_struct *work)
}
}
-static void smblib_bms_update_work(struct work_struct *work)
+static void bms_update_work(struct work_struct *work)
{
struct smb_charger *chg = container_of(work, struct smb_charger,
bms_update_work);
power_supply_changed(chg->batt_psy);
}
+static void step_soc_req_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ step_soc_req_work.work);
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ rc = smblib_get_prop_batt_capacity(chg, &pval);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't get batt capacity rc=%d\n", rc);
+ return;
+ }
+
+ step_charge_soc_update(chg, pval.intval);
+}
+
static void smblib_pl_detect_work(struct work_struct *work)
{
struct smb_charger *chg = container_of(work, struct smb_charger,
@@ -1829,7 +2080,15 @@ done:
vote(chg->awake_votable, PL_VOTER, false, 0);
}
-int smblib_create_votables(struct smb_charger *chg)
+static void clear_hdc_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ clear_hdc_work.work);
+
+ chg->is_hdc = 0;
+}
+
+static int smblib_create_votables(struct smb_charger *chg)
{
int rc = 0;
@@ -1923,15 +2182,53 @@ int smblib_create_votables(struct smb_charger *chg)
return rc;
}
+static void smblib_destroy_votables(struct smb_charger *chg)
+{
+ if (chg->usb_suspend_votable)
+ destroy_votable(chg->usb_suspend_votable);
+ if (chg->dc_suspend_votable)
+ destroy_votable(chg->dc_suspend_votable);
+ if (chg->fcc_max_votable)
+ destroy_votable(chg->fcc_max_votable);
+ if (chg->fcc_votable)
+ destroy_votable(chg->fcc_votable);
+ if (chg->fv_votable)
+ destroy_votable(chg->fv_votable);
+ if (chg->usb_icl_votable)
+ destroy_votable(chg->usb_icl_votable);
+ if (chg->dc_icl_votable)
+ destroy_votable(chg->dc_icl_votable);
+ if (chg->pd_allowed_votable)
+ destroy_votable(chg->pd_allowed_votable);
+ if (chg->awake_votable)
+ destroy_votable(chg->awake_votable);
+ if (chg->pl_disable_votable)
+ destroy_votable(chg->pl_disable_votable);
+}
+
+static void smblib_iio_deinit(struct smb_charger *chg)
+{
+ if (!IS_ERR_OR_NULL(chg->iio.temp_chan))
+ iio_channel_release(chg->iio.temp_chan);
+ if (!IS_ERR_OR_NULL(chg->iio.temp_max_chan))
+ iio_channel_release(chg->iio.temp_max_chan);
+ if (!IS_ERR_OR_NULL(chg->iio.usbin_i_chan))
+ iio_channel_release(chg->iio.usbin_i_chan);
+ if (!IS_ERR_OR_NULL(chg->iio.usbin_v_chan))
+ iio_channel_release(chg->iio.usbin_v_chan);
+}
+
int smblib_init(struct smb_charger *chg)
{
int rc = 0;
mutex_init(&chg->write_lock);
- INIT_WORK(&chg->bms_update_work, smblib_bms_update_work);
+ INIT_WORK(&chg->bms_update_work, bms_update_work);
INIT_WORK(&chg->pl_detect_work, smblib_pl_detect_work);
INIT_DELAYED_WORK(&chg->hvdcp_detect_work, smblib_hvdcp_detect_work);
INIT_DELAYED_WORK(&chg->pl_taper_work, smblib_pl_taper_work);
+ INIT_DELAYED_WORK(&chg->step_soc_req_work, step_soc_req_work);
+ INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work);
chg->fake_capacity = -EINVAL;
switch (chg->mode) {
@@ -1943,6 +2240,7 @@ 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);
@@ -1965,18 +2263,19 @@ int smblib_init(struct smb_charger *chg)
int smblib_deinit(struct smb_charger *chg)
{
- destroy_votable(chg->usb_suspend_votable);
- destroy_votable(chg->dc_suspend_votable);
- destroy_votable(chg->fcc_max_votable);
- destroy_votable(chg->fcc_votable);
- destroy_votable(chg->fv_votable);
- destroy_votable(chg->usb_icl_votable);
- destroy_votable(chg->dc_icl_votable);
- destroy_votable(chg->pd_allowed_votable);
- destroy_votable(chg->awake_votable);
- destroy_votable(chg->pl_disable_votable);
-
- power_supply_unreg_notifier(&chg->nb);
+ switch (chg->mode) {
+ case PARALLEL_MASTER:
+ power_supply_unreg_notifier(&chg->nb);
+ smblib_destroy_votables(chg);
+ break;
+ case PARALLEL_SLAVE:
+ break;
+ default:
+ dev_err(chg->dev, "Unsupported mode %d\n", chg->mode);
+ return -EINVAL;
+ }
+
+ smblib_iio_deinit(chg);
return 0;
}
diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h
index b56cd24adde1..aeb1eb2c454f 100644
--- a/drivers/power/qcom-charger/smb-lib.h
+++ b/drivers/power/qcom-charger/smb-lib.h
@@ -76,6 +76,9 @@ struct smb_params {
struct smb_chg_param dc_icl_div2_mid_hv;
struct smb_chg_param dc_icl_div2_hv;
struct smb_chg_param jeita_cc_comp;
+ struct smb_chg_param step_soc_threshold[4];
+ struct smb_chg_param step_soc;
+ struct smb_chg_param step_cc_delta[5];
};
struct parallel_params {
@@ -85,10 +88,18 @@ struct parallel_params {
int slave_fcc;
};
+struct smb_iio {
+ struct iio_channel *temp_chan;
+ struct iio_channel *temp_max_chan;
+ struct iio_channel *usbin_i_chan;
+ struct iio_channel *usbin_v_chan;
+};
+
struct smb_charger {
struct device *dev;
struct regmap *regmap;
struct smb_params param;
+ struct smb_iio iio;
int *debug_mask;
enum smb_mode mode;
@@ -133,6 +144,8 @@ struct smb_charger {
struct delayed_work hvdcp_detect_work;
struct delayed_work ps_change_timeout_work;
struct delayed_work pl_taper_work;
+ struct delayed_work step_soc_req_work;
+ struct delayed_work clear_hdc_work;
/* cached status */
int voltage_min_uv;
@@ -145,6 +158,9 @@ struct smb_charger {
int *thermal_mitigation;
int fake_capacity;
+
+ bool step_chg_enabled;
+ bool is_hdc;
};
int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
@@ -161,6 +177,13 @@ int smblib_set_charge_param(struct smb_charger *chg,
int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend);
int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend);
+int smblib_mapping_soc_from_field_value(struct smb_chg_param *param,
+ int val_u, u8 *val_raw);
+int smblib_mapping_cc_delta_to_field_value(struct smb_chg_param *param,
+ u8 val_raw);
+int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param,
+ int val_u, u8 *val_raw);
+
int smblib_vbus_regulator_enable(struct regulator_dev *rdev);
int smblib_vbus_regulator_disable(struct regulator_dev *rdev);
int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev);
@@ -171,6 +194,9 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev);
irqreturn_t smblib_handle_debug(int irq, void *data);
irqreturn_t smblib_handle_chg_state_change(int irq, void *data);
+irqreturn_t smblib_handle_step_chg_state_change(int irq, void *data);
+irqreturn_t smblib_handle_step_chg_soc_update_fail(int irq, void *data);
+irqreturn_t smblib_handle_step_chg_soc_update_request(int irq, void *data);
irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data);
irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data);
irqreturn_t smblib_handle_usb_psy_changed(int irq, void *data);
@@ -178,6 +204,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data);
irqreturn_t smblib_handle_usb_source_change(int irq, void *data);
irqreturn_t smblib_handle_icl_change(int irq, void *data);
irqreturn_t smblib_handle_usb_typec_change(int irq, void *data);
+irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data);
int smblib_get_prop_input_suspend(struct smb_charger *chg,
union power_supply_propval *val);
@@ -193,6 +220,8 @@ int smblib_get_prop_batt_health(struct smb_charger *chg,
union power_supply_propval *val);
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_set_prop_input_suspend(struct smb_charger *chg,
const union power_supply_propval *val);
@@ -220,6 +249,8 @@ int smblib_get_prop_usb_voltage_now(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_usb_current_max(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_usb_current_now(struct smb_charger *chg,
+ union power_supply_propval *val);
int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_typec_mode(struct smb_charger *chg,
@@ -230,6 +261,10 @@ int smblib_get_prop_pd_allowed(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_input_current_settled(struct smb_charger *chg,
union power_supply_propval *val);
+int smblib_get_prop_charger_temp(struct smb_charger *chg,
+ union power_supply_propval *val);
+int smblib_get_prop_charger_temp_max(struct smb_charger *chg,
+ union power_supply_propval *val);
int smblib_set_prop_usb_current_max(struct smb_charger *chg,
const union power_supply_propval *val);
int smblib_set_prop_usb_voltage_min(struct smb_charger *chg,
diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h
index b03e8a7e0403..0d5222ec08f8 100644
--- a/drivers/power/qcom-charger/smb-reg.h
+++ b/drivers/power/qcom-charger/smb-reg.h
@@ -283,6 +283,36 @@ enum {
#define IADC_SYNC_CNV_SEL_BIT BIT(1)
#define VADC_SYNC_CNV_SEL_BIT BIT(0)
+#define CHGR_STEP_CHG_MODE_CFG_REG (CHGR_BASE + 0xB0)
+#define STEP_CHARGING_SOC_FAIL_OPTION_BIT BIT(3)
+#define STEP_CHARGING_MODE_SELECT_BIT BIT(2)
+#define STEP_CHARGING_SOURCE_SELECT_BIT BIT(1)
+#define STEP_CHARGING_ENABLE_BIT BIT(0)
+
+#define STEP_CHG_UPDATE_REQUEST_TIMEOUT_CFG_REG (CHGR_BASE + 0xB1)
+#define STEP_CHG_UPDATE_REQUEST_TIMEOUT_CFG_MASK GENMASK(0, 1)
+#define STEP_CHG_UPDATE_REQUEST_TIMEOUT_5S 0
+#define STEP_CHG_UPDATE_REQUEST_TIMEOUT_10S 1
+#define STEP_CHG_UPDATE_REQUEST_TIMEOUT_20S 2
+#define STEP_CHG_UPDATE_REQUEST_TIMEOUT_40S 3
+
+#define STEP_CHG_UPDATE_FAIL_TIMEOUT_CFG_REG (CHGR_BASE + 0xB2)
+#define STEP_CHG_UPDATE_FAIL_TIMEOUT_CFG_MASK GENMASK(0, 1)
+#define STEP_CHG_UPDATE_FAIL_TIMEOUT_10S 0
+#define STEP_CHG_UPDATE_FAIL_TIMEOUT_30S 1
+#define STEP_CHG_UPDATE_FAIL_TIMEOUT_60S 2
+#define STEP_CHG_UPDATE_FAIL_TIMEOUT_120S 3
+
+#define STEP_CHG_SOC_OR_BATT_V_TH1_REG (CHGR_BASE + 0xB3)
+#define STEP_CHG_SOC_OR_BATT_V_TH2_REG (CHGR_BASE + 0xB4)
+#define STEP_CHG_SOC_OR_BATT_V_TH3_REG (CHGR_BASE + 0xB5)
+#define STEP_CHG_SOC_OR_BATT_V_TH4_REG (CHGR_BASE + 0xB6)
+#define STEP_CHG_CURRENT_DELTA1_REG (CHGR_BASE + 0xB7)
+#define STEP_CHG_CURRENT_DELTA2_REG (CHGR_BASE + 0xB8)
+#define STEP_CHG_CURRENT_DELTA3_REG (CHGR_BASE + 0xB9)
+#define STEP_CHG_CURRENT_DELTA4_REG (CHGR_BASE + 0xBA)
+#define STEP_CHG_CURRENT_DELTA5_REG (CHGR_BASE + 0xBB)
+
/* OTG Peripheral Registers */
#define RID_CC_CONTROL_23_16_REG (OTG_BASE + 0x06)
#define RID_CC_CONTROL_23_BIT BIT(7)
diff --git a/drivers/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c
index 259e9f31e243..5b4e7bcccdce 100644
--- a/drivers/power/qcom-charger/smb138x-charger.c
+++ b/drivers/power/qcom-charger/smb138x-charger.c
@@ -47,8 +47,8 @@ static struct smb_params v1_params = {
.name = "fast charge current",
.reg = FAST_CHARGE_CURRENT_CFG_REG,
.min_u = 0,
- .max_u = 4500000,
- .step_u = 25000,
+ .max_u = 5000000,
+ .step_u = 50000,
},
.fv = {
.name = "float voltage",
@@ -184,8 +184,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,
@@ -267,6 +270,8 @@ static enum power_supply_property smb138x_batt_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CHARGER_TEMP,
+ POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
};
static int smb138x_batt_get_prop(struct power_supply *psy,
@@ -296,13 +301,21 @@ static int smb138x_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CAPACITY:
rc = smblib_get_prop_batt_capacity(chg, val);
break;
+ case POWER_SUPPLY_PROP_CHARGER_TEMP:
+ rc = smblib_get_prop_charger_temp(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
+ 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,
@@ -381,6 +394,8 @@ static enum power_supply_property smb138x_parallel_props[] = {
POWER_SUPPLY_PROP_INPUT_SUSPEND,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CHARGER_TEMP,
+ POWER_SUPPLY_PROP_CHARGER_TEMP_MAX,
};
static int smb138x_parallel_get_prop(struct power_supply *psy,
@@ -415,13 +430,22 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
rc = smblib_get_charge_param(chg, &chg->param.fcc,
&val->intval);
break;
+ case POWER_SUPPLY_PROP_CHARGER_TEMP:
+ rc = smblib_get_prop_charger_temp(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
+ rc = smblib_get_prop_charger_temp_max(chg, val);
+ break;
default:
pr_err("parallel 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_parallel_set_prop(struct power_supply *psy,
diff --git a/drivers/power/qcom/debug_core.c b/drivers/power/qcom/debug_core.c
index d3620bbbeafa..ccef04ae9eb2 100644
--- a/drivers/power/qcom/debug_core.c
+++ b/drivers/power/qcom/debug_core.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
@@ -22,6 +22,8 @@
#include "soc/qcom/msm-core.h"
#define MAX_PSTATES 50
+#define NUM_OF_PENTRY 3 /* number of variables for ptable node */
+#define NUM_OF_EENTRY 2 /* number of variables for enable node */
enum arg_offset {
CPU_OFFSET,
@@ -82,15 +84,28 @@ static struct debugfs_blob_wrapper help_msg = {
};
-static void add_to_ptable(uint64_t *arg)
+static void add_to_ptable(unsigned int *arg)
{
struct core_debug *node;
int i, cpu = arg[CPU_OFFSET];
+ uint32_t freq = arg[FREQ_OFFSET];
+ uint32_t power = arg[POWER_OFFSET];
if (!cpu_possible(cpu))
return;
+ if ((freq == 0) || (power == 0)) {
+ pr_warn("Incorrect power data\n");
+ return;
+ }
+
node = &per_cpu(c_dgfs, cpu);
+
+ if (node->len >= MAX_PSTATES) {
+ pr_warn("Dropped ptable update - no space left.\n");
+ return;
+ }
+
if (!node->head) {
node->head = kzalloc(sizeof(struct cpu_pstate_pwr) *
(MAX_PSTATES + 1),
@@ -98,24 +113,18 @@ static void add_to_ptable(uint64_t *arg)
if (!node->head)
return;
}
- for (i = 0; i < MAX_PSTATES; i++) {
- if (node->head[i].freq == arg[FREQ_OFFSET]) {
- node->head[i].power = arg[POWER_OFFSET];
+
+ for (i = 0; i < node->len; i++) {
+ if (node->head[i].freq == freq) {
+ node->head[i].power = power;
return;
}
- if (node->head[i].freq == 0)
- break;
- }
-
- if (i == MAX_PSTATES) {
- pr_warn("Dropped ptable update - no space left.\n");
- return;
}
/* Insert a new frequency (may need to move things around to
keep in ascending order). */
for (i = MAX_PSTATES - 1; i > 0; i--) {
- if (node->head[i-1].freq > arg[FREQ_OFFSET]) {
+ if (node->head[i-1].freq > freq) {
node->head[i].freq = node->head[i-1].freq;
node->head[i].power = node->head[i-1].power;
} else if (node->head[i-1].freq != 0) {
@@ -123,23 +132,29 @@ static void add_to_ptable(uint64_t *arg)
}
}
- node->head[i].freq = arg[FREQ_OFFSET];
- node->head[i].power = arg[POWER_OFFSET];
- node->len++;
+ if (node->len < MAX_PSTATES) {
+ node->head[i].freq = freq;
+ node->head[i].power = power;
+ node->len++;
+ }
if (node->ptr)
node->ptr->len = node->len;
}
-static int split_ptable_args(char *line, uint64_t *arg)
+static int split_ptable_args(char *line, unsigned int *arg, uint32_t n)
{
char *args;
int i;
int ret = 0;
- for (i = 0; line; i++) {
+ for (i = 0; i < n; i++) {
+ if (!line)
+ break;
args = strsep(&line, " ");
- ret = kstrtoull(args, 10, &arg[i]);
+ ret = kstrtouint(args, 10, &arg[i]);
+ if (ret)
+ return ret;
}
return ret;
}
@@ -149,7 +164,7 @@ static ssize_t msm_core_ptable_write(struct file *file,
{
char *kbuf;
int ret;
- uint64_t arg[3];
+ unsigned int arg[3];
if (len == 0)
return 0;
@@ -163,7 +178,7 @@ static ssize_t msm_core_ptable_write(struct file *file,
goto done;
}
kbuf[len] = '\0';
- ret = split_ptable_args(kbuf, arg);
+ ret = split_ptable_args(kbuf, arg, NUM_OF_PENTRY);
if (!ret) {
add_to_ptable(arg);
ret = len;
@@ -201,7 +216,7 @@ static int msm_core_ptable_read(struct seq_file *m, void *data)
seq_printf(m, "--- CPU%d - Live numbers at %ldC---\n",
cpu, node->ptr->temp);
print_table(m, msm_core_data[cpu].ptable,
- msm_core_data[cpu].len);
+ node->driver_len);
}
}
return 0;
@@ -212,7 +227,7 @@ static ssize_t msm_core_enable_write(struct file *file,
{
char *kbuf;
int ret;
- uint64_t arg[3];
+ unsigned int arg[3];
int cpu;
if (len == 0)
@@ -227,7 +242,7 @@ static ssize_t msm_core_enable_write(struct file *file,
goto done;
}
kbuf[len] = '\0';
- ret = split_ptable_args(kbuf, arg);
+ ret = split_ptable_args(kbuf, arg, NUM_OF_EENTRY);
if (ret)
goto done;
cpu = arg[CPU_OFFSET];
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index d49d8606da15..27a5deb1213e 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -150,7 +150,7 @@ static void regulator_lock_supply(struct regulator_dev *rdev)
{
int i;
- for (i = 0; rdev->supply; rdev = rdev_get_supply(rdev), i++)
+ for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++)
mutex_lock_nested(&rdev->mutex, i);
}
diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c
index fe5dbbeac15e..b0439871c41a 100644
--- a/drivers/regulator/cpr3-mmss-regulator.c
+++ b/drivers/regulator/cpr3-mmss-regulator.c
@@ -242,8 +242,8 @@ static const int msmcobalt_v2_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
#define MSMCOBALT_MMSS_CPR_SENSOR_COUNT 35
-#define MSMCOBALT_MMSS_AGING_SENSOR_ID 17
-#define MSMCOBALT_MMSS_AGING_BYPASS_MASK0 0
+#define MSMCOBALT_MMSS_AGING_SENSOR_ID 29
+#define MSMCOBALT_MMSS_AGING_BYPASS_MASK0 (GENMASK(23, 0))
#define MSMCOBALT_MMSS_MAX_TEMP_POINTS 3
#define MSMCOBALT_MMSS_TEMP_SENSOR_ID_START 12
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index 6e8db03fe16e..cd02debc37aa 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -3362,7 +3362,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;
}
}
@@ -3523,7 +3524,7 @@ cleanup:
if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
rc2 = cpr3_ctrl_clear_cpr4_config(ctrl);
- if (rc) {
+ if (rc2) {
cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
rc2);
rc = rc2;
@@ -3725,6 +3726,17 @@ static int cpr3_regulator_aging_adjust(struct cpr3_controller *ctrl)
return 0;
}
+ /*
+ * Verify that the aging possible register (if specified) has an
+ * acceptable value.
+ */
+ if (ctrl->aging_possible_reg) {
+ reg = readl_relaxed(ctrl->aging_possible_reg);
+ reg &= ctrl->aging_possible_mask;
+ if (reg != ctrl->aging_possible_val)
+ return 0;
+ }
+
restore_current_corner = kcalloc(vreg_count,
sizeof(*restore_current_corner), GFP_KERNEL);
restore_vreg_enabled = kcalloc(vreg_count,
@@ -3798,7 +3810,7 @@ static int cpr3_regulator_aging_adjust(struct cpr3_controller *ctrl)
max_aging_volt = max(max_aging_volt, aging_volt);
} else {
cpr3_err(ctrl, "CPR aging measurement failed after %d tries, rc=%d\n",
- rc, CPR3_AGING_RETRY_COUNT);
+ j, rc);
ctrl->aging_failed = true;
ctrl->aging_required = false;
goto cleanup;
@@ -5992,6 +6004,21 @@ int cpr3_regulator_register(struct platform_device *pdev,
}
ctrl->cpr_ctrl_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (ctrl->aging_possible_mask) {
+ /*
+ * Aging possible register address is required if an aging
+ * possible mask has been specified.
+ */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "aging_allowed");
+ if (!res || !res->start) {
+ cpr3_err(ctrl, "CPR aging allowed address is missing\n");
+ return -ENXIO;
+ }
+ ctrl->aging_possible_reg = devm_ioremap(dev, res->start,
+ resource_size(res));
+ }
+
if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
ctrl->irq = platform_get_irq_byname(pdev, "cpr");
if (ctrl->irq < 0) {
diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h
index 0907518722df..8897def3ef76 100644
--- a/drivers/regulator/cpr3-regulator.h
+++ b/drivers/regulator/cpr3-regulator.h
@@ -532,6 +532,9 @@ struct cpr3_panic_regs_info {
* that this CPR3 controller manages.
* @cpr_ctrl_base: Virtual address of the CPR3 controller base register
* @fuse_base: Virtual address of fuse row 0
+ * @aging_possible_reg: Virtual address of an optional platform-specific
+ * register that must be ready to determine if it is
+ * possible to perform an aging measurement.
* @list: list head used in a global cpr3-regulator list so that
* cpr3-regulator structs can be found easily in RAM dumps
* @thread: Array of CPR3 threads managed by the CPR3 controller
@@ -671,6 +674,11 @@ struct cpr3_panic_regs_info {
* @aging_sensor: Array of CPR3 aging sensors which are used to perform
* aging measurements at a runtime.
* @aging_sensor_count: Number of elements in the aging_sensor array
+ * @aging_possible_mask: Optional bitmask used to mask off the
+ * aging_possible_reg register.
+ * @aging_possible_val: Optional value that the masked aging_possible_reg
+ * register must have in order for a CPR aging measurement
+ * to be possible.
* @step_quot_fixed: Fixed step quotient value used for target quotient
* adjustment if use_dynamic_step_quot is not set.
* This parameter is only relevant for CPR4 controllers
@@ -721,6 +729,7 @@ struct cpr3_controller {
int ctrl_id;
void __iomem *cpr_ctrl_base;
void __iomem *fuse_base;
+ void __iomem *aging_possible_reg;
struct list_head list;
struct cpr3_thread *thread;
int thread_count;
@@ -784,6 +793,8 @@ struct cpr3_controller {
bool aging_failed;
struct cpr3_aging_sensor_info *aging_sensor;
int aging_sensor_count;
+ u32 aging_possible_mask;
+ u32 aging_possible_val;
u32 step_quot_fixed;
u32 initial_temp_band;
diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c
index 9d55e9af2e7c..51179f28fcf5 100644
--- a/drivers/regulator/cpr3-util.c
+++ b/drivers/regulator/cpr3-util.c
@@ -1169,6 +1169,24 @@ int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl)
of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-aging-ref-voltage",
&ctrl->aging_ref_volt);
+ /* Aging possible bitmask is optional */
+ ctrl->aging_possible_mask = 0;
+ of_property_read_u32(ctrl->dev->of_node,
+ "qcom,cpr-aging-allowed-reg-mask",
+ &ctrl->aging_possible_mask);
+
+ if (ctrl->aging_possible_mask) {
+ /*
+ * Aging possible register value required if bitmask is
+ * specified
+ */
+ rc = cpr3_parse_ctrl_u32(ctrl,
+ "qcom,cpr-aging-allowed-reg-value",
+ &ctrl->aging_possible_val, 0, UINT_MAX);
+ if (rc)
+ return rc;
+ }
+
if (of_find_property(ctrl->dev->of_node, "clock-names", NULL)) {
ctrl->core_clk = devm_clk_get(ctrl->dev, "core_clk");
if (IS_ERR(ctrl->core_clk)) {
diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c
index 083459f96ac4..284180b0e72f 100644
--- a/drivers/regulator/cprh-kbss-regulator.c
+++ b/drivers/regulator/cprh-kbss-regulator.c
@@ -69,8 +69,9 @@ struct cprh_msmcobalt_kbss_fuses {
/*
* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0.
+ * Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1.
*/
-#define CPRH_MSMCOBALT_KBSS_FUSE_COMBO_COUNT 8
+#define CPRH_MSMCOBALT_KBSS_FUSE_COMBO_COUNT 16
/*
* Constants which define the name of each fuse corner.
@@ -206,11 +207,19 @@ msmcobalt_v1_kbss_fuse_ref_volt[MSMCOBALT_KBSS_FUSE_CORNERS] = {
* Open loop voltage fuse reference voltages in microvolts for MSMCOBALT v2
*/
static const int
-msmcobalt_v2_kbss_fuse_ref_volt[MSMCOBALT_KBSS_FUSE_CORNERS] = {
- 688000,
- 756000,
- 828000,
- 1056000,
+msmcobalt_v2_kbss_fuse_ref_volt[2][MSMCOBALT_KBSS_FUSE_CORNERS] = {
+ [MSMCOBALT_KBSS_POWER_CLUSTER_ID] = {
+ 688000,
+ 756000,
+ 828000,
+ 1056000,
+ },
+ [MSMCOBALT_KBSS_PERFORMANCE_CLUSTER_ID] = {
+ 756000,
+ 756000,
+ 828000,
+ 1056000,
+ },
};
#define MSMCOBALT_KBSS_FUSE_STEP_VOLT 10000
@@ -391,7 +400,7 @@ static int cprh_msmcobalt_kbss_calculate_open_loop_voltages(
{
struct device_node *node = vreg->of_node;
struct cprh_msmcobalt_kbss_fuses *fuse = vreg->platform_fuses;
- int i, j, soc_revision, rc = 0;
+ int i, j, soc_revision, id, rc = 0;
bool allow_interpolation;
u64 freq_low, volt_low, freq_high, volt_high;
const int *ref_volt;
@@ -407,13 +416,12 @@ static int cprh_msmcobalt_kbss_calculate_open_loop_voltages(
goto done;
}
+ id = vreg->thread->ctrl->ctrl_id;
soc_revision = vreg->thread->ctrl->soc_revision;
if (soc_revision == 1)
ref_volt = msmcobalt_v1_kbss_fuse_ref_volt;
- else if (soc_revision == 2)
- ref_volt = msmcobalt_v2_kbss_fuse_ref_volt;
else
- ref_volt = msmcobalt_v2_kbss_fuse_ref_volt;
+ ref_volt = msmcobalt_v2_kbss_fuse_ref_volt[id];
for (i = 0; i < vreg->fuse_corner_count; i++) {
fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse(
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index bfa82ca64499..ad4b6ffef36e 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -349,6 +349,28 @@ out:
return err;
}
+static void ufs_qcom_force_mem_config(struct ufs_hba *hba)
+{
+ struct ufs_clk_info *clki;
+
+ /*
+ * Configure the behavior of ufs clocks core and peripheral
+ * memory state when they are turned off.
+ * This configuration is required to allow retaining
+ * ICE crypto configuration (including keys) when
+ * core_clk_ice is turned off, and powering down
+ * non-ICE RAMs of host controller.
+ */
+ list_for_each_entry(clki, &hba->clk_list_head, list) {
+ if (!strcmp(clki->name, "core_clk_ice"))
+ clk_set_flags(clki->clk, CLKFLAG_RETAIN_MEM);
+ else
+ clk_set_flags(clki->clk, CLKFLAG_NORETAIN_MEM);
+ clk_set_flags(clki->clk, CLKFLAG_NORETAIN_PERIPH);
+ clk_set_flags(clki->clk, CLKFLAG_PERIPH_OFF_CLEAR);
+ }
+}
+
static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
@@ -357,6 +379,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
switch (status) {
case PRE_CHANGE:
+ ufs_qcom_force_mem_config(hba);
ufs_qcom_power_up_sequence(hba);
/*
* The PHY PLL output is the source of tx/rx lane symbol
@@ -741,21 +764,29 @@ out:
static int ufs_qcom_full_reset(struct ufs_hba *hba)
{
- struct ufs_clk_info *clki;
int ret = -ENOTSUPP;
- list_for_each_entry(clki, &hba->clk_list_head, list) {
- if (!strcmp(clki->name, "core_clk")) {
- ret = clk_reset(clki->clk, CLK_RESET_ASSERT);
- if (ret)
- goto out;
- /* Very small delay, per the documented requirement */
- usleep_range(1, 2);
-
- ret = clk_reset(clki->clk, CLK_RESET_DEASSERT);
- break;
- }
+ if (!hba->core_reset) {
+ dev_err(hba->dev, "%s: failed, err = %d\n", __func__,
+ ret);
+ goto out;
+ }
+
+ ret = reset_control_assert(hba->core_reset);
+ if (ret) {
+ dev_err(hba->dev, "%s: core_reset assert failed, err = %d\n",
+ __func__, ret);
+ goto out;
}
+
+ /* Very small delay, per the documented requirement */
+ usleep_range(1, 2);
+
+ ret = reset_control_deassert(hba->core_reset);
+ if (ret)
+ dev_err(hba->dev, "%s: core_reset deassert failed, err = %d\n",
+ __func__, ret);
+
out:
return ret;
}
@@ -996,7 +1027,7 @@ static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result)
}
}
-static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote)
+static int __ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote)
{
int err = 0;
@@ -1027,7 +1058,7 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host)
vote = ufs_qcom_get_bus_vote(host, mode);
if (vote >= 0)
- err = ufs_qcom_set_bus_vote(host, vote);
+ err = __ufs_qcom_set_bus_vote(host, vote);
else
err = vote;
@@ -1038,6 +1069,35 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host)
return err;
}
+static int ufs_qcom_set_bus_vote(struct ufs_hba *hba, bool on)
+{
+ struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+ int vote, err;
+
+ /*
+ * In case ufs_qcom_init() is not yet done, simply ignore.
+ * This ufs_qcom_set_bus_vote() shall be called from
+ * ufs_qcom_init() after init is done.
+ */
+ if (!host)
+ return 0;
+
+ if (on) {
+ vote = host->bus_vote.saved_vote;
+ if (vote == host->bus_vote.min_bw_vote)
+ ufs_qcom_update_bus_bw_vote(host);
+ } else {
+ vote = host->bus_vote.min_bw_vote;
+ }
+
+ err = __ufs_qcom_set_bus_vote(host, vote);
+ if (err)
+ dev_err(hba->dev, "%s: set bus vote failed %d\n",
+ __func__, err);
+
+ return err;
+}
+
static ssize_t
show_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -1403,7 +1463,6 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
int err;
- int vote = 0;
/*
* In case ufs_qcom_init() is not yet done, simply ignore.
@@ -1428,9 +1487,6 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
/* enable the device ref clock for HS mode*/
if (ufshcd_is_hs_mode(&hba->pwr_info))
ufs_qcom_dev_ref_clk_ctrl(host, true);
- vote = host->bus_vote.saved_vote;
- if (vote == host->bus_vote.min_bw_vote)
- ufs_qcom_update_bus_bw_vote(host);
err = ufs_qcom_ice_resume(host);
if (err)
@@ -1449,14 +1505,8 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
/* disable device ref_clk */
ufs_qcom_dev_ref_clk_ctrl(host, false);
}
- vote = host->bus_vote.min_bw_vote;
}
- err = ufs_qcom_set_bus_vote(host, vote);
- if (err)
- dev_err(hba->dev, "%s: set bus vote failed %d\n",
- __func__, err);
-
out:
return err;
}
@@ -2011,6 +2061,7 @@ static int ufs_qcom_init(struct ufs_hba *hba)
ufs_qcom_set_caps(hba);
ufs_qcom_advertise_quirks(hba);
+ ufs_qcom_set_bus_vote(hba, true);
ufs_qcom_setup_clocks(hba, true, false);
if (hba->dev->id < MAX_UFS_QCOM_HOSTS)
@@ -2362,17 +2413,21 @@ void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, void *priv,
static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host)
{
- if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
+ if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN) {
+ ufshcd_rmwl(host->hba, UFS_REG_TEST_BUS_EN,
+ UFS_REG_TEST_BUS_EN, REG_UFS_CFG1);
ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1);
- else
+ } else {
+ ufshcd_rmwl(host->hba, UFS_REG_TEST_BUS_EN, 0, REG_UFS_CFG1);
ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1);
+ }
}
static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
{
/* provide a legal default configuration */
- host->testbus.select_major = TSTBUS_UAWM;
- host->testbus.select_minor = 1;
+ host->testbus.select_major = TSTBUS_UNIPRO;
+ host->testbus.select_minor = 37;
}
static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
@@ -2389,7 +2444,7 @@ static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
* mappings of select_minor, since there is no harm in
* configuring a non-existent select_minor
*/
- if (host->testbus.select_minor > 0x1F) {
+ if (host->testbus.select_minor > 0xFF) {
dev_err(host->hba->dev,
"%s: 0x%05X is not a legal testbus option\n",
__func__, host->testbus.select_minor);
@@ -2458,7 +2513,8 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
break;
case TSTBUS_UNIPRO:
reg = UFS_UNIPRO_CFG;
- offset = 1;
+ offset = 20;
+ mask = 0xFFF;
break;
/*
* No need for a default case, since
@@ -2477,6 +2533,11 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
(u32)host->testbus.select_minor << offset,
reg);
ufs_qcom_enable_test_bus(host);
+ /*
+ * Make sure the test bus configuration is
+ * committed before returning.
+ */
+ mb();
ufshcd_release(host->hba, false);
pm_runtime_put_sync(host->hba->dev);
@@ -2488,15 +2549,44 @@ static void ufs_qcom_testbus_read(struct ufs_hba *hba)
ufs_qcom_dump_regs(hba, UFS_TEST_BUS, 1, "UFS_TEST_BUS ");
}
+static void ufs_qcom_print_unipro_testbus(struct ufs_hba *hba)
+{
+ struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+ u32 *testbus = NULL;
+ int i, nminor = 256, testbus_len = nminor * sizeof(u32);
+
+ testbus = kmalloc(testbus_len, GFP_KERNEL);
+ if (!testbus)
+ return;
+
+ host->testbus.select_major = TSTBUS_UNIPRO;
+ for (i = 0; i < nminor; i++) {
+ host->testbus.select_minor = i;
+ ufs_qcom_testbus_config(host);
+ testbus[i] = ufshcd_readl(hba, UFS_TEST_BUS);
+ }
+ print_hex_dump(KERN_ERR, "UNIPRO_TEST_BUS ", DUMP_PREFIX_OFFSET,
+ 16, 4, testbus, testbus_len, false);
+ kfree(testbus);
+}
+
static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+ struct phy *phy = host->generic_phy;
ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16,
"HCI Vendor Specific Registers ");
+ /* sleep a bit intermittently as we are dumping too much data */
ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper);
+ usleep_range(1000, 1100);
ufs_qcom_testbus_read(hba);
+ usleep_range(1000, 1100);
+ ufs_qcom_print_unipro_testbus(hba);
+ usleep_range(1000, 1100);
+ ufs_qcom_phy_dbg_register_dump(phy);
+ usleep_range(1000, 1100);
ufs_qcom_ice_print_regs(host);
}
@@ -2521,6 +2611,7 @@ static struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
.full_reset = ufs_qcom_full_reset,
.update_sec_cfg = ufs_qcom_update_sec_cfg,
.get_scale_down_gear = ufs_qcom_get_scale_down_gear,
+ .set_bus_vote = ufs_qcom_set_bus_vote,
.dbg_register_dump = ufs_qcom_dump_dbg_regs,
#ifdef CONFIG_DEBUG_FS
.add_debugfs = ufs_qcom_dbg_add_debugfs,
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index ba36d9883a0f..394de8302fd2 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -100,6 +100,7 @@ enum {
#define QUNIPRO_SEL UFS_BIT(0)
#define TEST_BUS_EN BIT(18)
#define TEST_BUS_SEL GENMASK(22, 19)
+#define UFS_REG_TEST_BUS_EN BIT(30)
/* bit definitions for REG_UFS_CFG2 register */
#define UAWM_HW_CGC_EN (1 << 0)
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 19c378c72083..5a9564326099 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -40,6 +40,22 @@
#include "ufshcd.h"
#include "ufshcd-pltfrm.h"
+static int ufshcd_parse_reset_info(struct ufs_hba *hba)
+{
+ int ret = 0;
+
+ hba->core_reset = devm_reset_control_get(hba->dev,
+ "core_reset");
+ if (IS_ERR(hba->core_reset)) {
+ ret = PTR_ERR(hba->core_reset);
+ dev_err(hba->dev, "core_reset unavailable,err = %d\n",
+ ret);
+ hba->core_reset = NULL;
+ }
+
+ return ret;
+}
+
static int ufshcd_parse_clock_info(struct ufs_hba *hba)
{
int ret = 0;
@@ -338,6 +354,13 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
goto dealloc_host;
}
+ err = ufshcd_parse_reset_info(hba);
+ if (err) {
+ dev_err(&pdev->dev, "%s: reset parse failed %d\n",
+ __func__, err);
+ goto dealloc_host;
+ }
+
ufshcd_parse_pm_levels(hba);
if (!dev->dma_mask)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index ce779d760c69..d478767ad3dd 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3932,8 +3932,12 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)
ret = (status != PWR_OK) ? status : -1;
}
out:
- if (ret)
+ if (ret) {
ufsdbg_set_err_state(hba);
+ ufshcd_print_host_state(hba);
+ ufshcd_print_pwr_info(hba);
+ ufshcd_print_host_regs(hba);
+ }
ufshcd_save_tstamp_of_last_dme_cmd(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
@@ -4644,8 +4648,12 @@ link_startup:
ret = ufshcd_make_hba_operational(hba);
out:
- if (ret)
+ if (ret) {
dev_err(hba->dev, "link startup failed %d\n", ret);
+ ufshcd_print_host_state(hba);
+ ufshcd_print_pwr_info(hba);
+ ufshcd_print_host_regs(hba);
+ }
return ret;
}
@@ -7555,6 +7563,13 @@ static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
if (!head || list_empty(head))
goto out;
+ /* call vendor specific bus vote before enabling the clocks */
+ if (on) {
+ ret = ufshcd_vops_set_bus_vote(hba, on);
+ if (ret)
+ return ret;
+ }
+
/*
* vendor specific setup_clocks ops may depend on clocks managed by
* this standard driver hence call the vendor specific setup_clocks
@@ -7593,11 +7608,24 @@ static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
* this standard driver hence call the vendor specific setup_clocks
* after enabling the clocks managed here.
*/
- if (on)
+ if (on) {
ret = ufshcd_vops_setup_clocks(hba, on, is_gating_context);
+ if (ret)
+ goto out;
+ }
+
+ /*
+ * call vendor specific bus vote to remove the vote after
+ * disabling the clocks.
+ */
+ if (!on)
+ ret = ufshcd_vops_set_bus_vote(hba, on);
out:
if (ret) {
+ if (on)
+ /* Can't do much if this fails */
+ (void) ufshcd_vops_set_bus_vote(hba, false);
list_for_each_entry(clki, head, list) {
if (!IS_ERR_OR_NULL(clki->clk) && clki->enabled)
clk_disable_unprepare(clki->clk);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 552d50081e3f..a6298f614a0b 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -55,6 +55,7 @@
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
#include "unipro.h"
#include <asm/irq.h>
@@ -309,6 +310,7 @@ struct ufs_pwr_mode_info {
* @update_sec_cfg: called to restore host controller secure configuration
* @get_scale_down_gear: called to get the minimum supported gear to
* scale down
+ * @set_bus_vote: called to vote for the required bus bandwidth
* @add_debugfs: used to add debugfs entries
* @remove_debugfs: used to remove debugfs entries
*/
@@ -335,6 +337,7 @@ struct ufs_hba_variant_ops {
void (*dbg_register_dump)(struct ufs_hba *hba);
int (*update_sec_cfg)(struct ufs_hba *hba, bool restore_sec_cfg);
u32 (*get_scale_down_gear)(struct ufs_hba *);
+ int (*set_bus_vote)(struct ufs_hba *, bool);
#ifdef CONFIG_DEBUG_FS
void (*add_debugfs)(struct ufs_hba *hba, struct dentry *root);
void (*remove_debugfs)(struct ufs_hba *hba);
@@ -891,6 +894,7 @@ struct ufs_hba {
struct rw_semaphore clk_scaling_lock;
+ struct reset_control *core_reset;
/* If set, don't gate device ref_clk during clock gating */
bool no_ref_clk_gating;
@@ -1259,6 +1263,13 @@ static inline u32 ufshcd_vops_get_scale_down_gear(struct ufs_hba *hba)
return UFS_HS_G1;
}
+static inline int ufshcd_vops_set_bus_vote(struct ufs_hba *hba, bool on)
+{
+ if (hba->var && hba->var->vops && hba->var->vops->set_bus_vote)
+ return hba->var->vops->set_bus_vote(hba, on);
+ return 0;
+}
+
#ifdef CONFIG_DEBUG_FS
static inline void ufshcd_vops_add_debugfs(struct ufs_hba *hba,
struct dentry *root)
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index ea4dd2ce4e1d..9af9ce323bc3 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -167,6 +167,7 @@ static int ngd_qmi_available(struct notifier_block *n, unsigned long code,
SLIM_INFO(dev, "Slimbus QMI NGD CB received event:%ld\n", code);
switch (code) {
case QMI_SERVER_ARRIVE:
+ atomic_set(&dev->ssr_in_progress, 0);
schedule_work(&dev->dsp.dom_up);
break;
default:
@@ -214,6 +215,8 @@ static int dsp_domr_notify_cb(struct notifier_block *n, unsigned long code,
switch (code) {
case SUBSYS_BEFORE_SHUTDOWN:
case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
+ SLIM_INFO(dev, "SLIM DSP SSR notify cb:%lu\n", code);
+ atomic_set(&dev->ssr_in_progress, 1);
/* wait for current transaction */
mutex_lock(&dev->tx_lock);
/* make sure autosuspend is not called until ADSP comes up*/
@@ -225,6 +228,16 @@ static int dsp_domr_notify_cb(struct notifier_block *n, unsigned long code,
break;
case LOCATOR_UP:
reg = _cmd;
+ if (!reg || reg->total_domains != 1) {
+ SLIM_WARN(dev, "error locating audio-PD\n");
+ if (reg)
+ SLIM_WARN(dev, "audio-PDs matched:%d\n",
+ reg->total_domains);
+
+ /* Fall back to SSR */
+ ngd_reg_ssr(dev);
+ return NOTIFY_DONE;
+ }
dev->dsp.domr = service_notif_register_notifier(
reg->domain_list->name,
reg->domain_list->instance_id,
@@ -866,7 +879,7 @@ static int ngd_bulk_wr(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc,
}
if (dev->bulk.size > dev->bulk.buf_sz) {
void *temp = krealloc(dev->bulk.base, dev->bulk.size,
- GFP_KERNEL);
+ GFP_KERNEL | GFP_DMA);
if (!temp) {
ret = -ENOMEM;
goto retpath;
@@ -1316,8 +1329,10 @@ hw_init_retry:
if (ret) {
SLIM_WARN(dev, "SLIM power req failed:%d, retry:%d\n",
ret, retries);
- msm_slim_qmi_power_request(dev, false);
- if (retries < INIT_MX_RETRIES) {
+ if (!atomic_read(&dev->ssr_in_progress))
+ msm_slim_qmi_power_request(dev, false);
+ if (retries < INIT_MX_RETRIES &&
+ !atomic_read(&dev->ssr_in_progress)) {
retries++;
goto hw_init_retry;
}
@@ -1416,7 +1431,8 @@ capability_retry:
SLIM_WARN(dev,
"slim capability time-out:%d, stat:0x%x,cfg:0x%x\n",
retries, laddr, cfg);
- if (retries < INIT_MX_RETRIES) {
+ if ((retries < INIT_MX_RETRIES) &&
+ !atomic_read(&dev->ssr_in_progress)) {
retries++;
goto capability_retry;
}
@@ -1487,8 +1503,7 @@ static int ngd_slim_rx_msgq_thread(void *data)
int retries = 0;
u8 wbuf[8];
- set_current_state(TASK_INTERRUPTIBLE);
- wait_for_completion(notify);
+ wait_for_completion_interruptible(notify);
txn.dt = SLIM_MSG_DEST_LOGICALADDR;
txn.ec = 0;
@@ -1549,8 +1564,7 @@ static int ngd_notify_slaves(void *data)
}
while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
- wait_for_completion(&dev->qmi.slave_notify);
+ wait_for_completion_interruptible(&dev->qmi.slave_notify);
/* Probe devices for first notification */
if (!i) {
i++;
@@ -1683,7 +1697,7 @@ static int ngd_slim_probe(struct platform_device *pdev)
/* typical txn numbers and size used in bulk operation */
dev->bulk.buf_sz = SLIM_MAX_TXNS * 8;
- dev->bulk.base = kzalloc(dev->bulk.buf_sz, GFP_KERNEL);
+ dev->bulk.base = kzalloc(dev->bulk.buf_sz, GFP_KERNEL | GFP_DMA);
if (!dev->bulk.base) {
ret = -ENOMEM;
goto err_nobulk;
@@ -1780,6 +1794,7 @@ static int ngd_slim_probe(struct platform_device *pdev)
dev->ee = 1;
dev->irq = irq->start;
dev->bam.irq = bam_irq->start;
+ atomic_set(&dev->ssr_in_progress, 0);
if (rxreg_access)
dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
@@ -1938,6 +1953,7 @@ static int ngd_slim_runtime_resume(struct device *device)
struct platform_device *pdev = to_platform_device(device);
struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
int ret = 0;
+
mutex_lock(&dev->tx_lock);
if (dev->state >= MSM_CTRL_ASLEEP)
ret = ngd_slim_power_up(dev, false);
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 7616e714299c..65b9fae8040b 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -314,6 +314,7 @@ struct msm_slim_ctrl {
void (*rx_slim)(struct msm_slim_ctrl *dev, u8 *buf);
u32 current_rx_buf[10];
int current_count;
+ atomic_t ssr_in_progress;
};
struct msm_sat_chan {
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 0e74093eeb2b..9c27344165be 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -127,6 +127,17 @@ config MSM_SPCOM
spcom provides clients/server API, although currently only one client
or server is allowed per logical channel.
+config MSM_SPSS_UTILS
+ depends on MSM_PIL
+ bool "Secure Processor Utilities"
+ help
+ spss-utils driver selects Secure Processor firmware file name.
+ The firmware file name for test or production is selected based
+ on a test fuse.
+ Different file name is used for differnt SPSS HW versions,
+ because the SPSS firmware size is too small to support multiple
+ HW versions.
+
config MSM_SMEM_LOGGING
depends on MSM_SMEM
bool "MSM Shared Memory Logger"
@@ -417,6 +428,15 @@ config ICNSS
control messages to FW over QMI channel. It is also responsible for
handling WLAN PD restart notifications.
+config ICNSS_DEBUG
+ bool "ICNSS debug support"
+ depends on ICNSS
+ ---help---
+ Say 'Y' here to enable ICNSS driver debug support. Debug support
+ primarily consists of logs consisting of information related to
+ hardware register access and enabling BUG_ON for certain cases to aid
+ the debugging.
+
config MSM_SECURE_BUFFER
bool "Helper functions for securing buffers through TZ"
help
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index e9e65ea443dd..d9134a558be6 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_MSM_IPC_ROUTER_HSIC_XPRT) += ipc_router_hsic_xprt.o
obj-$(CONFIG_MSM_IPC_ROUTER_MHI_XPRT) += ipc_router_mhi_xprt.o
obj-$(CONFIG_MSM_IPC_ROUTER_GLINK_XPRT) += ipc_router_glink_xprt.o
obj-$(CONFIG_MSM_SPCOM) += spcom.o
+obj-$(CONFIG_MSM_SPSS_UTILS) += spss_utils.o
obj-y += qdsp6v2/
obj-$(CONFIG_MSM_SYSTEM_HEALTH_MONITOR) += system_health_monitor_v01.o
obj-$(CONFIG_MSM_SYSTEM_HEALTH_MONITOR) += system_health_monitor.o
@@ -70,7 +71,7 @@ obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o
obj-$(CONFIG_QCOM_COMMON_LOG) += common_log.o
obj-$(CONFIG_QCOM_IRQ_HELPER) += irq-helper.o
obj-$(CONFIG_TRACER_PKT) += tracer_pkt.o
-obj-$(CONFIG_ICNSS) += icnss.o wlan_firmware_service_v01.o
+obj-$(CONFIG_ICNSS) += icnss.o wlan_firmware_service_v01.o icnss_utils.o
obj-$(CONFIG_SOC_BUS) += socinfo.o
obj-$(CONFIG_QCOM_BUS_SCALING) += msm_bus/
obj-$(CONFIG_MSM_SERVICE_NOTIFIER) += service-notifier.o
diff --git a/drivers/soc/qcom/common_log.c b/drivers/soc/qcom/common_log.c
index d2d877ef427b..f4c69d624342 100644
--- a/drivers/soc/qcom/common_log.c
+++ b/drivers/soc/qcom/common_log.c
@@ -23,7 +23,6 @@
#define PMIC_DUMP_DATA_LEN 4096
#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/glink.c b/drivers/soc/qcom/glink.c
index f54d9c3f4f3d..5612075ba60c 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -30,6 +30,7 @@
#include "glink_private.h"
#include "glink_xprt_if.h"
+#define GLINK_CTX_CANARY 0x58544324 /* "$CTX" */
/* Number of internal IPC Logging log pages */
#define NUM_LOG_PAGES 10
#define GLINK_PM_QOS_HOLDOFF_MS 10
@@ -38,6 +39,8 @@
#define GLINK_QOS_DEF_MTU 2048
#define GLINK_KTHREAD_PRIO 1
+
+static rwlock_t magic_lock;
/**
* struct glink_qos_priority_bin - Packet Scheduler's priority bucket
* @max_rate_kBps: Maximum rate supported by the priority bucket.
@@ -308,6 +311,7 @@ struct channel_ctx {
unsigned long req_rate_kBps;
uint32_t tx_intent_cnt;
uint32_t tx_cnt;
+ uint32_t magic_number;
};
static struct glink_core_if core_impl;
@@ -436,6 +440,37 @@ static void glink_core_deinit_xprt_qos_cfg(
#define GLINK_GET_CH_TX_STATE(ctx) \
((ctx)->tx_intent_cnt || (ctx)->tx_cnt)
+static int glink_get_ch_ctx(struct channel_ctx *ctx)
+{
+ unsigned long flags;
+
+ if (!ctx)
+ return -EINVAL;
+ read_lock_irqsave(&magic_lock, flags);
+ if (ctx->magic_number != GLINK_CTX_CANARY) {
+ read_unlock_irqrestore(&magic_lock, flags);
+ return -EINVAL;
+ }
+ rwref_get(&ctx->ch_state_lhb2);
+ read_unlock_irqrestore(&magic_lock, flags);
+ return 0;
+}
+
+static int glink_put_ch_ctx(struct channel_ctx *ctx, bool update_magic)
+{
+ unsigned long flags;
+
+ if (!update_magic) {
+ rwref_put(&ctx->ch_state_lhb2);
+ return 0;
+ }
+ write_lock_irqsave(&magic_lock, flags);
+ ctx->magic_number = 0;
+ rwref_put(&ctx->ch_state_lhb2);
+ write_unlock_irqrestore(&magic_lock, flags);
+ return 0;
+}
+
/**
* glink_ssr() - Clean up locally for SSR by simulating remote close
* @subsystem: The name of the subsystem being restarted
@@ -2583,7 +2618,7 @@ void *glink_open(const struct glink_open_config *cfg)
GLINK_INFO_CH(ctx, "%s: Created channel, sent OPEN command. ctx %p\n",
__func__, ctx);
-
+ ctx->magic_number = GLINK_CTX_CANARY;
return ctx;
}
EXPORT_SYMBOL(glink_open);
@@ -2681,15 +2716,19 @@ int glink_close(void *handle)
unsigned long flags;
bool is_empty = false;
- if (!ctx)
- return -EINVAL;
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
GLINK_INFO_CH(ctx, "%s: Closing channel, ctx: %p\n", __func__, ctx);
- if (ctx->local_open_state == GLINK_CHANNEL_CLOSED)
+ if (ctx->local_open_state == GLINK_CHANNEL_CLOSED) {
+ glink_put_ch_ctx(ctx, false);
return 0;
+ }
if (ctx->local_open_state == GLINK_CHANNEL_CLOSING) {
/* close already pending */
+ glink_put_ch_ctx(ctx, false);
return -EBUSY;
}
@@ -2754,6 +2793,7 @@ relock: xprt_ctx = ctx->transport_ptr;
rwref_put(&ctx->ch_state_lhb2);
rwref_read_put(&xprt_ctx->xprt_state_lhb0);
+ glink_put_ch_ctx(ctx, true);
return ret;
}
EXPORT_SYMBOL(glink_close);
@@ -2812,29 +2852,30 @@ static int glink_tx_common(void *handle, void *pkt_priv,
if (!size)
return -EINVAL;
- if (!ctx)
- return -EINVAL;
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
rwref_read_get_atomic(&ctx->ch_state_lhb2, is_atomic);
if (!(vbuf_provider || pbuf_provider)) {
- rwref_read_put(&ctx->ch_state_lhb2);
- return -EINVAL;
+ ret = -EINVAL;
+ goto glink_tx_common_err;
}
if (!ch_is_fully_opened(ctx)) {
- rwref_read_put(&ctx->ch_state_lhb2);
- return -EBUSY;
+ ret = -EBUSY;
+ goto glink_tx_common_err;
}
if (size > GLINK_MAX_PKT_SIZE) {
- rwref_read_put(&ctx->ch_state_lhb2);
- return -EINVAL;
+ ret = -EINVAL;
+ goto glink_tx_common_err;
}
if (unlikely(tx_flags & GLINK_TX_TRACER_PKT)) {
if (!(ctx->transport_ptr->capabilities & GCAP_TRACER_PKT)) {
- rwref_read_put(&ctx->ch_state_lhb2);
- return -EOPNOTSUPP;
+ ret = -EOPNOTSUPP;
+ goto glink_tx_common_err;
}
tracer_pkt_log_event(data, GLINK_CORE_TX);
}
@@ -2846,16 +2887,16 @@ static int glink_tx_common(void *handle, void *pkt_priv,
GLINK_ERR_CH(ctx,
"%s: R[%u]:%zu Intent not present for lcid\n",
__func__, riid, size);
- rwref_read_put(&ctx->ch_state_lhb2);
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto glink_tx_common_err;
}
if (is_atomic && !(ctx->transport_ptr->capabilities &
GCAP_AUTO_QUEUE_RX_INT)) {
GLINK_ERR_CH(ctx,
"%s: Cannot request intent in atomic context\n",
__func__);
- rwref_read_put(&ctx->ch_state_lhb2);
- return -EINVAL;
+ ret = -EINVAL;
+ goto glink_tx_common_err;
}
/* request intent of correct size */
@@ -2865,20 +2906,18 @@ static int glink_tx_common(void *handle, void *pkt_priv,
if (ret) {
GLINK_ERR_CH(ctx, "%s: Request intent failed %d\n",
__func__, ret);
- rwref_read_put(&ctx->ch_state_lhb2);
- return ret;
+ goto glink_tx_common_err;
}
while (ch_pop_remote_rx_intent(ctx, size, &riid,
&intent_size, &cookie)) {
- rwref_get(&ctx->ch_state_lhb2);
rwref_read_put(&ctx->ch_state_lhb2);
if (is_atomic) {
GLINK_ERR_CH(ctx,
"%s Intent of size %zu not ready\n",
__func__, size);
- rwref_put(&ctx->ch_state_lhb2);
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto glink_tx_common_err_2;
}
if (ctx->transport_ptr->local_state == GLINK_XPRT_DOWN
@@ -2886,8 +2925,8 @@ static int glink_tx_common(void *handle, void *pkt_priv,
GLINK_ERR_CH(ctx,
"%s: Channel closed while waiting for intent\n",
__func__);
- rwref_put(&ctx->ch_state_lhb2);
- return -EBUSY;
+ ret = -EBUSY;
+ goto glink_tx_common_err_2;
}
/* wait for the remote intent req ack */
@@ -2897,8 +2936,8 @@ static int glink_tx_common(void *handle, void *pkt_priv,
GLINK_ERR_CH(ctx,
"%s: Intent request ack with size: %zu not granted for lcid\n",
__func__, size);
- rwref_put(&ctx->ch_state_lhb2);
- return -ETIMEDOUT;
+ ret = -ETIMEDOUT;
+ goto glink_tx_common_err_2;
}
if (!ctx->int_req_ack) {
@@ -2906,8 +2945,8 @@ static int glink_tx_common(void *handle, void *pkt_priv,
"%s: Intent Request with size: %zu %s",
__func__, size,
"not granted for lcid\n");
- rwref_put(&ctx->ch_state_lhb2);
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto glink_tx_common_err_2;
}
/* wait for the rx_intent from remote side */
@@ -2917,13 +2956,12 @@ static int glink_tx_common(void *handle, void *pkt_priv,
GLINK_ERR_CH(ctx,
"%s: Intent request with size: %zu not granted for lcid\n",
__func__, size);
- rwref_put(&ctx->ch_state_lhb2);
- return -ETIMEDOUT;
+ ret = -ETIMEDOUT;
+ goto glink_tx_common_err_2;
}
reinit_completion(&ctx->int_req_complete);
rwref_read_get(&ctx->ch_state_lhb2);
- rwref_put(&ctx->ch_state_lhb2);
}
}
@@ -2943,8 +2981,8 @@ static int glink_tx_common(void *handle, void *pkt_priv,
if (!tx_info) {
GLINK_ERR_CH(ctx, "%s: No memory for allocation\n", __func__);
ch_push_remote_rx_intent(ctx, intent_size, riid, cookie);
- rwref_read_put(&ctx->ch_state_lhb2);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto glink_tx_common_err;
}
rwref_lock_init(&tx_info->pkt_ref, glink_tx_pkt_release);
INIT_LIST_HEAD(&tx_info->list_done);
@@ -2970,7 +3008,10 @@ static int glink_tx_common(void *handle, void *pkt_priv,
else
xprt_schedule_tx(ctx->transport_ptr, ctx, tx_info);
+glink_tx_common_err:
rwref_read_put(&ctx->ch_state_lhb2);
+glink_tx_common_err_2:
+ glink_put_ch_ctx(ctx, false);
return ret;
}
@@ -3011,13 +3052,15 @@ int glink_queue_rx_intent(void *handle, const void *pkt_priv, size_t size)
struct glink_core_rx_intent *intent_ptr;
int ret = 0;
- if (!ctx)
- return -EINVAL;
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
if (!ch_is_fully_opened(ctx)) {
/* Can only queue rx intents if channel is fully opened */
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
+ glink_put_ch_ctx(ctx, false);
return -EBUSY;
}
@@ -3026,13 +3069,16 @@ int glink_queue_rx_intent(void *handle, const void *pkt_priv, size_t size)
GLINK_ERR_CH(ctx,
"%s: Intent pointer allocation failed size[%zu]\n",
__func__, size);
+ glink_put_ch_ctx(ctx, false);
return -ENOMEM;
}
GLINK_DBG_CH(ctx, "%s: L[%u]:%zu\n", __func__, intent_ptr->id,
intent_ptr->intent_size);
- if (ctx->transport_ptr->capabilities & GCAP_INTENTLESS)
+ if (ctx->transport_ptr->capabilities & GCAP_INTENTLESS) {
+ glink_put_ch_ctx(ctx, false);
return ret;
+ }
/* notify remote side of rx intent */
ret = ctx->transport_ptr->ops->tx_cmd_local_rx_intent(
@@ -3040,7 +3086,7 @@ int glink_queue_rx_intent(void *handle, const void *pkt_priv, size_t size)
if (ret)
/* unable to transmit, dequeue intent */
ch_remove_local_rx_intent(ctx, intent_ptr->id);
-
+ glink_put_ch_ctx(ctx, false);
return ret;
}
EXPORT_SYMBOL(glink_queue_rx_intent);
@@ -3059,20 +3105,25 @@ bool glink_rx_intent_exists(void *handle, size_t size)
struct channel_ctx *ctx = (struct channel_ctx *)handle;
struct glink_core_rx_intent *intent;
unsigned long flags;
+ int ret;
if (!ctx || !ch_is_fully_opened(ctx))
return false;
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return false;
spin_lock_irqsave(&ctx->local_rx_intent_lst_lock_lhc1, flags);
list_for_each_entry(intent, &ctx->local_rx_intent_list, list) {
if (size <= intent->intent_size) {
spin_unlock_irqrestore(
&ctx->local_rx_intent_lst_lock_lhc1, flags);
+ glink_put_ch_ctx(ctx, false);
return true;
}
}
spin_unlock_irqrestore(&ctx->local_rx_intent_lst_lock_lhc1, flags);
-
+ glink_put_ch_ctx(ctx, false);
return false;
}
EXPORT_SYMBOL(glink_rx_intent_exists);
@@ -3093,11 +3144,15 @@ int glink_rx_done(void *handle, const void *ptr, bool reuse)
uint32_t id;
int ret = 0;
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
liid_ptr = ch_get_local_rx_intent_notified(ctx, ptr);
if (IS_ERR_OR_NULL(liid_ptr)) {
/* invalid pointer */
GLINK_ERR_CH(ctx, "%s: Invalid pointer %p\n", __func__, ptr);
+ glink_put_ch_ctx(ctx, false);
return -EINVAL;
}
@@ -3123,7 +3178,7 @@ int glink_rx_done(void *handle, const void *ptr, bool reuse)
/* send rx done */
ctx->transport_ptr->ops->tx_cmd_local_rx_done(ctx->transport_ptr->ops,
ctx->lcid, id, reuse);
-
+ glink_put_ch_ctx(ctx, false);
return ret;
}
EXPORT_SYMBOL(glink_rx_done);
@@ -3171,12 +3226,13 @@ int glink_sigs_set(void *handle, uint32_t sigs)
struct channel_ctx *ctx = (struct channel_ctx *)handle;
int ret;
- if (!ctx)
- return -EINVAL;
-
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
+ glink_put_ch_ctx(ctx, false);
return -EBUSY;
}
@@ -3186,6 +3242,7 @@ int glink_sigs_set(void *handle, uint32_t sigs)
ctx->lcid, ctx->lsigs);
GLINK_INFO_CH(ctx, "%s: Sent SIGNAL SET command\n", __func__);
+ glink_put_ch_ctx(ctx, false);
return ret;
}
EXPORT_SYMBOL(glink_sigs_set);
@@ -3201,17 +3258,22 @@ EXPORT_SYMBOL(glink_sigs_set);
int glink_sigs_local_get(void *handle, uint32_t *sigs)
{
struct channel_ctx *ctx = (struct channel_ctx *)handle;
+ int ret;
- if (!ctx || !sigs)
+ if (!sigs)
return -EINVAL;
-
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
+ glink_put_ch_ctx(ctx, false);
return -EBUSY;
}
*sigs = ctx->lsigs;
+ glink_put_ch_ctx(ctx, false);
return 0;
}
EXPORT_SYMBOL(glink_sigs_local_get);
@@ -3227,17 +3289,23 @@ EXPORT_SYMBOL(glink_sigs_local_get);
int glink_sigs_remote_get(void *handle, uint32_t *sigs)
{
struct channel_ctx *ctx = (struct channel_ctx *)handle;
+ int ret;
- if (!ctx || !sigs)
+ if (!sigs)
return -EINVAL;
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
+ glink_put_ch_ctx(ctx, false);
return -EBUSY;
}
*sigs = ctx->rsigs;
+ glink_put_ch_ctx(ctx, false);
return 0;
}
EXPORT_SYMBOL(glink_sigs_remote_get);
@@ -3333,12 +3401,16 @@ int glink_qos_latency(void *handle, unsigned long latency_us, size_t pkt_size)
int ret;
unsigned long req_rate_kBps;
- if (!ctx || !latency_us || !pkt_size)
+ if (!latency_us || !pkt_size)
return -EINVAL;
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
+ glink_put_ch_ctx(ctx, false);
return -EBUSY;
}
@@ -3348,7 +3420,7 @@ int glink_qos_latency(void *handle, unsigned long latency_us, size_t pkt_size)
if (ret < 0)
GLINK_ERR_CH(ctx, "%s: QoS %lu:%zu cannot be met\n",
__func__, latency_us, pkt_size);
-
+ glink_put_ch_ctx(ctx, false);
return ret;
}
EXPORT_SYMBOL(glink_qos_latency);
@@ -3366,16 +3438,18 @@ int glink_qos_cancel(void *handle)
struct channel_ctx *ctx = (struct channel_ctx *)handle;
int ret;
- if (!ctx)
- return -EINVAL;
-
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
+ glink_put_ch_ctx(ctx, false);
return -EBUSY;
}
ret = glink_qos_reset_priority(ctx);
+ glink_put_ch_ctx(ctx, false);
return ret;
}
EXPORT_SYMBOL(glink_qos_cancel);
@@ -3396,12 +3470,13 @@ int glink_qos_start(void *handle)
int ret;
unsigned long flags;
- if (!ctx)
- return -EINVAL;
-
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
+ glink_put_ch_ctx(ctx, false);
return -EBUSY;
}
@@ -3410,6 +3485,7 @@ int glink_qos_start(void *handle)
ret = glink_qos_add_ch_tx_intent(ctx);
spin_unlock(&ctx->tx_lists_lock_lhc3);
spin_unlock_irqrestore(&ctx->transport_ptr->tx_ready_lock_lhb3, flags);
+ glink_put_ch_ctx(ctx, false);
return ret;
}
EXPORT_SYMBOL(glink_qos_start);
@@ -3428,16 +3504,20 @@ EXPORT_SYMBOL(glink_qos_start);
unsigned long glink_qos_get_ramp_time(void *handle, size_t pkt_size)
{
struct channel_ctx *ctx = (struct channel_ctx *)handle;
+ int ret;
- if (!ctx)
- return (unsigned long)-EINVAL;
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return (unsigned long)ret;
if (!ch_is_fully_opened(ctx)) {
GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n",
__func__);
+ glink_put_ch_ctx(ctx, false);
return (unsigned long)-EBUSY;
}
+ glink_put_ch_ctx(ctx, false);
return ctx->transport_ptr->ops->get_power_vote_ramp_time(
ctx->transport_ptr->ops,
glink_prio_to_power_state(ctx->transport_ptr,
@@ -3521,12 +3601,16 @@ EXPORT_SYMBOL(glink_rpm_mask_rx_interrupt);
int glink_wait_link_down(void *handle)
{
struct channel_ctx *ctx = (struct channel_ctx *)handle;
+ int ret;
- if (!ctx)
- return -EINVAL;
- if (!ctx->transport_ptr)
+ ret = glink_get_ch_ctx(ctx);
+ if (ret)
+ return ret;
+ if (!ctx->transport_ptr) {
+ glink_put_ch_ctx(ctx, false);
return -EOPNOTSUPP;
-
+ }
+ glink_put_ch_ctx(ctx, false);
return ctx->transport_ptr->ops->wait_link_down(ctx->transport_ptr->ops);
}
EXPORT_SYMBOL(glink_wait_link_down);
@@ -4029,6 +4113,37 @@ static struct glink_core_xprt_ctx *glink_create_dummy_xprt_ctx(
return xprt_ptr;
}
+static struct channel_ctx *get_first_ch_ctx(
+ struct glink_core_xprt_ctx *xprt_ctx)
+{
+ unsigned long flags;
+ struct channel_ctx *ctx;
+
+ spin_lock_irqsave(&xprt_ctx->xprt_ctx_lock_lhb1, flags);
+ if (!list_empty(&xprt_ctx->channels)) {
+ ctx = list_first_entry(&xprt_ctx->channels,
+ struct channel_ctx, port_list_node);
+ rwref_get(&ctx->ch_state_lhb2);
+ } else {
+ ctx = NULL;
+ }
+ spin_unlock_irqrestore(&xprt_ctx->xprt_ctx_lock_lhb1, flags);
+ return ctx;
+}
+
+static void glink_core_move_ch_node(struct glink_core_xprt_ctx *xprt_ptr,
+ struct glink_core_xprt_ctx *dummy_xprt_ctx, struct channel_ctx *ctx)
+{
+ unsigned long flags, d_flags;
+
+ spin_lock_irqsave(&dummy_xprt_ctx->xprt_ctx_lock_lhb1, d_flags);
+ spin_lock_irqsave(&xprt_ptr->xprt_ctx_lock_lhb1, flags);
+ rwref_get(&dummy_xprt_ctx->xprt_state_lhb0);
+ list_move_tail(&ctx->port_list_node, &dummy_xprt_ctx->channels);
+ spin_unlock_irqrestore(&xprt_ptr->xprt_ctx_lock_lhb1, flags);
+ spin_unlock_irqrestore(&dummy_xprt_ctx->xprt_ctx_lock_lhb1, d_flags);
+}
+
/**
* glink_core_channel_cleanup() - cleanup all channels for the transport
*
@@ -4039,7 +4154,7 @@ static struct glink_core_xprt_ctx *glink_create_dummy_xprt_ctx(
static void glink_core_channel_cleanup(struct glink_core_xprt_ctx *xprt_ptr)
{
unsigned long flags, d_flags;
- struct channel_ctx *ctx, *tmp_ctx;
+ struct channel_ctx *ctx;
struct channel_lcid *temp_lcid, *temp_lcid1;
struct glink_core_xprt_ctx *dummy_xprt_ctx;
@@ -4048,29 +4163,18 @@ static void glink_core_channel_cleanup(struct glink_core_xprt_ctx *xprt_ptr)
GLINK_ERR("%s: Dummy Transport creation failed\n", __func__);
return;
}
-
rwref_read_get(&dummy_xprt_ctx->xprt_state_lhb0);
rwref_read_get(&xprt_ptr->xprt_state_lhb0);
- spin_lock_irqsave(&dummy_xprt_ctx->xprt_ctx_lock_lhb1, d_flags);
- spin_lock_irqsave(&xprt_ptr->xprt_ctx_lock_lhb1, flags);
-
- list_for_each_entry_safe(ctx, tmp_ctx, &xprt_ptr->channels,
- port_list_node) {
+ ctx = get_first_ch_ctx(xprt_ptr);
+ while (ctx) {
rwref_write_get_atomic(&ctx->ch_state_lhb2, true);
if (ctx->local_open_state == GLINK_CHANNEL_OPENED ||
ctx->local_open_state == GLINK_CHANNEL_OPENING) {
- rwref_get(&dummy_xprt_ctx->xprt_state_lhb0);
- list_move_tail(&ctx->port_list_node,
- &dummy_xprt_ctx->channels);
ctx->transport_ptr = dummy_xprt_ctx;
rwref_write_put(&ctx->ch_state_lhb2);
+ glink_core_move_ch_node(xprt_ptr, dummy_xprt_ctx, ctx);
} else {
/* local state is in either CLOSED or CLOSING */
- spin_unlock_irqrestore(&xprt_ptr->xprt_ctx_lock_lhb1,
- flags);
- spin_unlock_irqrestore(
- &dummy_xprt_ctx->xprt_ctx_lock_lhb1,
- d_flags);
glink_core_remote_close_common(ctx, true);
if (ctx->local_open_state == GLINK_CHANNEL_CLOSING)
glink_core_ch_close_ack_common(ctx, true);
@@ -4078,22 +4182,21 @@ static void glink_core_channel_cleanup(struct glink_core_xprt_ctx *xprt_ptr)
if (ch_is_fully_closed(ctx))
glink_delete_ch_from_list(ctx, false);
rwref_write_put(&ctx->ch_state_lhb2);
- spin_lock_irqsave(&dummy_xprt_ctx->xprt_ctx_lock_lhb1,
- d_flags);
- spin_lock_irqsave(&xprt_ptr->xprt_ctx_lock_lhb1, flags);
}
+ rwref_put(&ctx->ch_state_lhb2);
+ ctx = get_first_ch_ctx(xprt_ptr);
}
+ spin_lock_irqsave(&xprt_ptr->xprt_ctx_lock_lhb1, flags);
list_for_each_entry_safe(temp_lcid, temp_lcid1,
&xprt_ptr->free_lcid_list, list_node) {
list_del(&temp_lcid->list_node);
kfree(&temp_lcid->list_node);
}
- dummy_xprt_ctx->dummy_in_use = false;
spin_unlock_irqrestore(&xprt_ptr->xprt_ctx_lock_lhb1, flags);
- spin_unlock_irqrestore(&dummy_xprt_ctx->xprt_ctx_lock_lhb1, d_flags);
rwref_read_put(&xprt_ptr->xprt_state_lhb0);
spin_lock_irqsave(&dummy_xprt_ctx->xprt_ctx_lock_lhb1, d_flags);
+ dummy_xprt_ctx->dummy_in_use = false;
while (!list_empty(&dummy_xprt_ctx->channels)) {
ctx = list_first_entry(&dummy_xprt_ctx->channels,
struct channel_ctx, port_list_node);
@@ -5273,7 +5376,7 @@ static int glink_scheduler_tx(struct channel_ctx *ctx,
struct glink_core_xprt_ctx *xprt_ctx)
{
unsigned long flags;
- struct glink_core_tx_pkt *tx_info;
+ struct glink_core_tx_pkt *tx_info, *temp_tx_info;
size_t txd_len = 0;
size_t tx_len = 0;
uint32_t num_pkts = 0;
@@ -5308,6 +5411,20 @@ static int glink_scheduler_tx(struct channel_ctx *ctx,
ctx->lcid, tx_info);
}
spin_lock_irqsave(&ctx->tx_lists_lock_lhc3, flags);
+ if (!list_empty(&ctx->tx_active)) {
+ /*
+ * Verify if same tx_info still exist in tx_active
+ * list and is not removed during tx operation.
+ * It can happen if SSR and tx done both happen
+ * before tx_lists_lock_lhc3 is taken.
+ */
+ temp_tx_info = list_first_entry(&ctx->tx_active,
+ struct glink_core_tx_pkt, list_node);
+ if (temp_tx_info != tx_info)
+ continue;
+ } else {
+ break;
+ }
if (ret == -EAGAIN) {
/*
* transport unable to send at the moment and will call
@@ -5334,6 +5451,7 @@ static int glink_scheduler_tx(struct channel_ctx *ctx,
* Break out of the loop so that the scheduler can
* continue with the next channel.
*/
+ rwref_put(&tx_info->pkt_ref);
break;
} else {
txd_len += tx_len;
@@ -5342,8 +5460,8 @@ static int glink_scheduler_tx(struct channel_ctx *ctx,
if (!tx_info->size_remaining) {
num_pkts++;
list_del_init(&tx_info->list_node);
- rwref_put(&tx_info->pkt_ref);
}
+ rwref_put(&tx_info->pkt_ref);
}
ctx->txd_len += txd_len;
@@ -5392,6 +5510,7 @@ static void tx_func(struct kthread_work *work)
glink_pm_qos_vote(xprt_ptr);
ch_ptr = list_first_entry(&xprt_ptr->prio_bin[prio].tx_ready,
struct channel_ctx, tx_ready_list_node);
+ rwref_get(&ch_ptr->ch_state_lhb2);
spin_unlock_irqrestore(&xprt_ptr->tx_ready_lock_lhb3, flags);
if (tx_ready_head == NULL || tx_ready_head_prio < prio) {
@@ -5403,6 +5522,7 @@ static void tx_func(struct kthread_work *work)
GLINK_ERR_XPRT(xprt_ptr,
"%s: Unable to send data on this transport.\n",
__func__);
+ rwref_put(&ch_ptr->ch_state_lhb2);
break;
}
transmitted_successfully = false;
@@ -5413,6 +5533,7 @@ static void tx_func(struct kthread_work *work)
* transport unable to send at the moment and will call
* tx_resume() when it can send again.
*/
+ rwref_put(&ch_ptr->ch_state_lhb2);
break;
} else if (ret < 0) {
/*
@@ -5425,6 +5546,7 @@ static void tx_func(struct kthread_work *work)
GLINK_ERR_XPRT(xprt_ptr,
"%s: unrecoverable xprt failure %d\n",
__func__, ret);
+ rwref_put(&ch_ptr->ch_state_lhb2);
break;
} else if (!ret) {
/*
@@ -5436,6 +5558,7 @@ static void tx_func(struct kthread_work *work)
list_rotate_left(&xprt_ptr->prio_bin[prio].tx_ready);
spin_unlock_irqrestore(&xprt_ptr->tx_ready_lock_lhb3,
flags);
+ rwref_put(&ch_ptr->ch_state_lhb2);
continue;
}
@@ -5453,6 +5576,7 @@ static void tx_func(struct kthread_work *work)
tx_ready_head = NULL;
transmitted_successfully = true;
+ rwref_put(&ch_ptr->ch_state_lhb2);
}
glink_pm_qos_unvote(xprt_ptr);
GLINK_PERF("%s: worker exiting\n", __func__);
@@ -6019,6 +6143,7 @@ EXPORT_SYMBOL(glink_get_xprt_log_ctx);
static int glink_init(void)
{
log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "glink", 0);
+ rwlock_init(&magic_lock);
if (!log_ctx)
GLINK_ERR("%s: unable to create log context\n", __func__);
glink_debugfs_init();
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index aff9683b394f..232ee28318e7 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -34,6 +34,8 @@
#include <linux/qmi_encdec.h>
#include <linux/ipc_logging.h>
#include <linux/msm-bus.h>
+#include <linux/uaccess.h>
+#include <linux/qpnp/qpnp-adc.h>
#include <soc/qcom/memory_dump.h>
#include <soc/qcom/icnss.h>
#include <soc/qcom/msm_qmi_interface.h>
@@ -44,11 +46,13 @@
#include "wlan_firmware_service_v01.h"
-#define ICNSS_PANIC 1
#define WLFW_TIMEOUT_MS 3000
#define WLFW_SERVICE_INS_ID_V01 0
+#define WLFW_CLIENT_ID 0x4b4e454c
#define MAX_PROP_SIZE 32
-#define NUM_LOG_PAGES 4
+#define NUM_LOG_PAGES 10
+#define NUM_REG_LOG_PAGES 4
+#define ICNSS_MAGIC 0x5abc5abc
/*
* Registers: MPM2_PSHOLD
@@ -57,9 +61,12 @@
#define MPM_WCSSAON_CONFIG_OFFSET 0x18
#define MPM_WCSSAON_CONFIG_ARES_N BIT(0)
#define MPM_WCSSAON_CONFIG_WLAN_DISABLE BIT(1)
+#define MPM_WCSSAON_CONFIG_MSM_CLAMP_EN_OVRD BIT(6)
+#define MPM_WCSSAON_CONFIG_MSM_CLAMP_EN_OVRD_VAL BIT(7)
#define MPM_WCSSAON_CONFIG_FORCE_ACTIVE BIT(14)
#define MPM_WCSSAON_CONFIG_FORCE_XO_ENABLE BIT(19)
#define MPM_WCSSAON_CONFIG_DISCONNECT_CLR BIT(21)
+#define MPM_WCSSAON_CONFIG_M2W_CLAMP_EN BIT(22)
/*
* Registers: WCSS_SR_SHADOW_REGISTERS
@@ -148,14 +155,31 @@
#define ICNSS_HW_REG_RETRY 10
+#define WCSS_HM_A_PMM_HW_VERSION_V10 0x40000000
+#define WCSS_HM_A_PMM_HW_VERSION_V20 0x40010000
+#define WCSS_HM_A_PMM_HW_VERSION_Q10 0x40010001
+
#define ICNSS_SERVICE_LOCATION_CLIENT_NAME "ICNSS-WLAN"
#define ICNSS_WLAN_SERVICE_NAME "wlan/fw"
+#define ICNSS_THRESHOLD_HIGH 3600000
+#define ICNSS_THRESHOLD_LOW 3450000
+#define ICNSS_THRESHOLD_GUARD 20000
+
#define icnss_ipc_log_string(_x...) do { \
if (icnss_ipc_log_context) \
ipc_log_string(icnss_ipc_log_context, _x); \
} while (0)
+#ifdef CONFIG_ICNSS_DEBUG
+#define icnss_ipc_log_long_string(_x...) do { \
+ if (icnss_ipc_log_long_context) \
+ ipc_log_string(icnss_ipc_log_long_context, _x); \
+ } while (0)
+#else
+#define icnss_ipc_log_long_string(_x...)
+#endif
+
#define icnss_pr_err(_fmt, ...) do { \
pr_err(_fmt, ##__VA_ARGS__); \
icnss_ipc_log_string("ERR: " pr_fmt(_fmt), \
@@ -180,7 +204,13 @@
##__VA_ARGS__); \
} while (0)
-#ifdef ICNSS_PANIC
+#define icnss_reg_dbg(_fmt, ...) do { \
+ pr_debug(_fmt, ##__VA_ARGS__); \
+ icnss_ipc_log_long_string("REG: " pr_fmt(_fmt), \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#ifdef CONFIG_ICNSS_DEBUG
#define ICNSS_ASSERT(_condition) do { \
if (!(_condition)) { \
icnss_pr_err("ASSERT at line %d\n", \
@@ -208,15 +238,19 @@ enum icnss_debug_quirks {
PDR_ONLY,
};
-#define ICNSS_QUIRKS_DEFAULT ( \
- BIT(SSR_ONLY) \
- )
+#define ICNSS_QUIRKS_DEFAULT 0
unsigned long quirks = ICNSS_QUIRKS_DEFAULT;
module_param(quirks, ulong, 0600);
void *icnss_ipc_log_context;
+#ifdef CONFIG_ICNSS_DEBUG
+void *icnss_ipc_log_long_context;
+#endif
+
+#define ICNSS_EVENT_PENDING 2989
+
enum icnss_driver_event_type {
ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
ICNSS_DRIVER_EVENT_SERVER_EXIT,
@@ -243,9 +277,12 @@ enum icnss_driver_state {
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,
};
struct ce_irq_list {
@@ -298,6 +335,15 @@ struct icnss_stats {
uint32_t disable;
} ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
+ uint32_t pm_suspend;
+ uint32_t pm_suspend_err;
+ uint32_t pm_resume;
+ uint32_t pm_resume_err;
+ uint32_t pm_suspend_noirq;
+ uint32_t pm_suspend_noirq_err;
+ uint32_t pm_resume_noirq;
+ uint32_t pm_resume_noirq_err;
+
uint32_t ind_register_req;
uint32_t ind_register_resp;
uint32_t ind_register_err;
@@ -321,9 +367,13 @@ struct icnss_stats {
uint32_t ini_req;
uint32_t ini_resp;
uint32_t ini_req_err;
+ uint32_t vbatt_req;
+ uint32_t vbatt_resp;
+ uint32_t vbatt_req_err;
};
static struct icnss_priv {
+ uint32_t magic;
struct platform_device *pdev;
struct icnss_driver_ops *ops;
struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
@@ -370,6 +420,15 @@ 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;
+ uint8_t *diag_reg_read_buf;
+ struct qpnp_adc_tm_btm_param vph_monitor_params;
+ struct qpnp_adc_tm_chip *adc_tm_dev;
+ struct qpnp_vadc_chip *vadc_dev;
+ uint64_t vph_pwr;
} *penv;
static void icnss_hw_write_reg(void *base, u32 offset, u32 val)
@@ -382,7 +441,7 @@ static u32 icnss_hw_read_reg(void *base, u32 offset)
{
u32 rdata = readl_relaxed(base + offset);
- icnss_pr_dbg(" READ: offset: 0x%06x 0x%08x\n", offset, rdata);
+ icnss_reg_dbg(" READ: offset: 0x%06x 0x%08x\n", offset, rdata);
return rdata;
}
@@ -394,7 +453,7 @@ static void icnss_hw_write_reg_field(void *base, u32 offset, u32 mask, u32 val)
val = (rdata & ~mask) | (val << shift);
- icnss_pr_dbg("WRITE: offset: 0x%06x 0x%08x -> 0x%08x\n",
+ icnss_reg_dbg("WRITE: offset: 0x%06x 0x%08x -> 0x%08x\n",
offset, rdata, val);
icnss_hw_write_reg(base, offset, val);
@@ -413,12 +472,12 @@ static int icnss_hw_poll_reg_field(void *base, u32 offset, u32 mask, u32 val,
rdata = readl_relaxed(base + offset);
- icnss_pr_dbg(" POLL: offset: 0x%06x 0x%08x == 0x%08x & 0x%08x\n",
+ icnss_reg_dbg(" POLL: offset: 0x%06x 0x%08x == 0x%08x & 0x%08x\n",
offset, val, rdata, mask);
while ((rdata & mask) != val) {
if (retry != 0 && r >= retry) {
- icnss_pr_err(" POLL FAILED: offset: 0x%06x 0x%08x == 0x%08x & 0x%08x\n",
+ icnss_pr_err("POLL FAILED: offset: 0x%06x 0x%08x == 0x%08x & 0x%08x\n",
offset, val, rdata, mask);
return -EIO;
@@ -429,8 +488,8 @@ static int icnss_hw_poll_reg_field(void *base, u32 offset, u32 mask, u32 val,
rdata = readl_relaxed(base + offset);
if (retry)
- icnss_pr_dbg(" POLL: offset: 0x%06x 0x%08x == 0x%08x & 0x%08x\n",
- offset, val, rdata, mask);
+ icnss_reg_dbg(" POLL: offset: 0x%06x 0x%08x == 0x%08x & 0x%08x\n",
+ offset, val, rdata, mask);
}
@@ -486,6 +545,7 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
event->type = type;
event->data = data;
init_completion(&event->complete);
+ event->ret = ICNSS_EVENT_PENDING;
event->sync = sync;
spin_lock_irqsave(&penv->event_lock, flags);
@@ -494,16 +554,213 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type,
penv->stats.events[type].posted++;
queue_work(penv->event_wq, &penv->event_work);
- if (sync) {
- ret = wait_for_completion_interruptible(&event->complete);
- if (ret == 0)
- ret = event->ret;
- kfree(event);
+
+ if (!sync)
+ return ret;
+
+ 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);
+ 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, flags);
+
+ ret = event->ret;
+ kfree(event);
+
+ return ret;
+}
+
+static int wlfw_vbatt_send_sync_msg(struct icnss_priv *priv,
+ uint64_t voltage_uv)
+{
+ int ret;
+ struct wlfw_vbatt_req_msg_v01 req;
+ struct wlfw_vbatt_resp_msg_v01 resp;
+ struct msg_desc req_desc, resp_desc;
+
+ if (!priv->wlfw_clnt) {
+ ret = -ENODEV;
+ goto out;
}
+ icnss_pr_dbg("Sending Vbatt message, state: 0x%lx\n",
+ penv->state);
+
+ memset(&req, 0, sizeof(req));
+ memset(&resp, 0, sizeof(resp));
+
+ req.voltage_uv = voltage_uv;
+
+ req_desc.max_msg_len = WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN;
+ req_desc.msg_id = QMI_WLFW_VBATT_REQ_V01;
+ req_desc.ei_array = wlfw_vbatt_req_msg_v01_ei;
+
+ resp_desc.max_msg_len = WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN;
+ resp_desc.msg_id = QMI_WLFW_VBATT_RESP_V01;
+ resp_desc.ei_array = wlfw_vbatt_resp_msg_v01_ei;
+
+ priv->stats.vbatt_req++;
+
+ ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
+ &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
+ if (ret < 0) {
+ icnss_pr_err("Send vbatt req failed %d\n", ret);
+ goto out;
+ }
+
+ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_pr_err("QMI vbatt request failed %d %d\n",
+ resp.resp.result, resp.resp.error);
+ ret = resp.resp.result;
+ goto out;
+ }
+ priv->stats.vbatt_resp++;
+
+out:
+ priv->stats.vbatt_req_err++;
return ret;
}
+static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
+{
+ int ret = 0;
+ struct qpnp_vadc_result adc_result;
+
+ if (!priv->vadc_dev) {
+ icnss_pr_err("VADC dev doesn't exists\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = qpnp_vadc_read(penv->vadc_dev, VADC_VPH_PWR, &adc_result);
+ if (ret) {
+ icnss_pr_err("Error reading ADC channel %d, ret = %d\n",
+ VADC_VPH_PWR, ret);
+ goto out;
+ }
+
+ icnss_pr_dbg("Phone power read phy=%lld meas=0x%llx\n",
+ adc_result.physical, adc_result.measurement);
+
+ *result_uv = adc_result.physical;
+out:
+ return ret;
+}
+
+static void icnss_vph_notify(enum qpnp_tm_state state, void *ctx)
+{
+ struct icnss_priv *priv = ctx;
+ uint64_t vph_pwr = 0;
+ uint64_t vph_pwr_prev;
+ int ret = 0;
+ bool update = true;
+
+ if (!priv) {
+ icnss_pr_err("Priv pointer is NULL\n");
+ return;
+ }
+
+ vph_pwr_prev = priv->vph_pwr;
+
+ ret = icnss_get_phone_power(priv, &vph_pwr);
+ if (ret)
+ return;
+
+ if (vph_pwr < ICNSS_THRESHOLD_LOW) {
+ if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
+ update = false;
+ priv->vph_monitor_params.state_request =
+ ADC_TM_HIGH_THR_ENABLE;
+ priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
+ ICNSS_THRESHOLD_GUARD;
+ priv->vph_monitor_params.low_thr = 0;
+ } else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
+ if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
+ update = false;
+ priv->vph_monitor_params.state_request =
+ ADC_TM_LOW_THR_ENABLE;
+ priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
+ ICNSS_THRESHOLD_GUARD;
+ priv->vph_monitor_params.high_thr = 0;
+ } else {
+ if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
+ vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
+ update = false;
+ priv->vph_monitor_params.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
+ priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
+ }
+
+ priv->vph_pwr = vph_pwr;
+
+ if (update)
+ wlfw_vbatt_send_sync_msg(priv, vph_pwr);
+
+ icnss_pr_dbg("set low threshold to %d, high threshold to %d\n",
+ priv->vph_monitor_params.low_thr,
+ priv->vph_monitor_params.high_thr);
+ ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
+ &priv->vph_monitor_params);
+ if (ret)
+ icnss_pr_err("TM channel setup failed %d\n", ret);
+}
+
+static int icnss_setup_vph_monitor(struct icnss_priv *priv)
+{
+ int ret = 0;
+
+ if (!priv->adc_tm_dev) {
+ icnss_pr_err("ADC TM handler is NULL\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
+ priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
+ priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+ priv->vph_monitor_params.channel = VADC_VPH_PWR;
+ priv->vph_monitor_params.btm_ctx = priv;
+ priv->vph_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
+ priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
+ icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
+ priv->vph_monitor_params.low_thr,
+ priv->vph_monitor_params.high_thr);
+
+ ret = qpnp_adc_tm_channel_measure(priv->adc_tm_dev,
+ &priv->vph_monitor_params);
+ if (ret)
+ icnss_pr_err("TM channel setup failed %d\n", ret);
+out:
+ return ret;
+}
+
+static int icnss_init_vph_monitor(struct icnss_priv *priv)
+{
+ int ret = 0;
+
+ ret = icnss_get_phone_power(priv, &priv->vph_pwr);
+ if (ret)
+ goto out;
+
+ wlfw_vbatt_send_sync_msg(priv, priv->vph_pwr);
+
+ ret = icnss_setup_vph_monitor(priv);
+ if (ret)
+ goto out;
+out:
+ return ret;
+}
+
+
static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
{
struct msg_desc ind_desc;
@@ -742,6 +999,32 @@ static void icnss_hw_top_level_reset(struct icnss_priv *priv)
ICNSS_HW_REG_RETRY);
}
+static void icnss_hw_io_reset(struct icnss_priv *priv, bool on)
+{
+ u32 hw_version = priv->soc_info.soc_id;
+
+ if (on && !test_bit(ICNSS_FW_READY, &priv->state))
+ return;
+
+ icnss_pr_dbg("HW io reset: %s, SoC: 0x%x, state: 0x%lx\n",
+ on ? "ON" : "OFF", priv->soc_info.soc_id, priv->state);
+
+ if (hw_version == WCSS_HM_A_PMM_HW_VERSION_V10 ||
+ hw_version == WCSS_HM_A_PMM_HW_VERSION_V20) {
+ icnss_hw_write_reg_field(priv->mpm_config_va,
+ MPM_WCSSAON_CONFIG_OFFSET,
+ MPM_WCSSAON_CONFIG_MSM_CLAMP_EN_OVRD_VAL, 0);
+ icnss_hw_write_reg_field(priv->mpm_config_va,
+ MPM_WCSSAON_CONFIG_OFFSET,
+ MPM_WCSSAON_CONFIG_MSM_CLAMP_EN_OVRD, on);
+ } else if (hw_version == WCSS_HM_A_PMM_HW_VERSION_Q10) {
+ icnss_hw_write_reg_field(priv->mpm_config_va,
+ MPM_WCSSAON_CONFIG_OFFSET,
+ MPM_WCSSAON_CONFIG_M2W_CLAMP_EN,
+ on);
+ }
+}
+
int icnss_hw_reset_wlan_ss_power_down(struct icnss_priv *priv)
{
u32 rdata;
@@ -1039,7 +1322,7 @@ int icnss_hw_reset(struct icnss_priv *priv)
MPM_WCSSAON_CONFIG_FORCE_ACTIVE, 1);
icnss_hw_poll_reg_field(priv->mem_base_va, SR_WCSSAON_SR_LSB_OFFSET,
- SR_WCSSAON_SR_LSB_RETENTION_STATUS, 1, 10,
+ SR_WCSSAON_SR_LSB_RETENTION_STATUS, 1, 200,
ICNSS_HW_REG_RETRY);
for (i = 0; i < ICNSS_HW_REG_RETRY; i++) {
@@ -1067,11 +1350,13 @@ int icnss_hw_reset(struct icnss_priv *priv)
}
if (i >= ICNSS_HW_REG_RETRY)
- ICNSS_ASSERT(false);
+ goto top_level_reset;
icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET,
MPM_WCSSAON_CONFIG_DISCONNECT_CLR, 0x1);
+ usleep_range(200, 300);
+
icnss_hw_reset_wlan_ss_power_down(priv);
icnss_hw_reset_common_ss_power_down(priv);
@@ -1107,7 +1392,7 @@ static int icnss_hw_power_on(struct icnss_priv *priv)
int ret = 0;
unsigned long flags;
- icnss_pr_dbg("Power on: state: 0x%lx\n", priv->state);
+ icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
spin_lock_irqsave(&priv->on_off_lock, flags);
if (test_bit(ICNSS_POWER_ON, &priv->state)) {
@@ -1127,6 +1412,8 @@ static int icnss_hw_power_on(struct icnss_priv *priv)
icnss_hw_top_level_release_reset(priv);
+ icnss_hw_io_reset(penv, 1);
+
return ret;
out:
clear_bit(ICNSS_POWER_ON, &priv->state);
@@ -1141,7 +1428,7 @@ static int icnss_hw_power_off(struct icnss_priv *priv)
if (test_bit(HW_ALWAYS_ON, &quirks))
return 0;
- icnss_pr_dbg("Power off: 0x%lx\n", priv->state);
+ icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
spin_lock_irqsave(&priv->on_off_lock, flags);
if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
@@ -1151,6 +1438,8 @@ static int icnss_hw_power_off(struct icnss_priv *priv)
clear_bit(ICNSS_POWER_ON, &priv->state);
spin_unlock_irqrestore(&priv->on_off_lock, flags);
+ icnss_hw_io_reset(penv, 0);
+
icnss_hw_reset(priv);
icnss_clk_deinit(priv);
@@ -1175,6 +1464,8 @@ int icnss_power_on(struct device *dev)
return -EINVAL;
}
+ icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
+
return icnss_hw_power_on(priv);
}
EXPORT_SYMBOL(icnss_power_on);
@@ -1189,11 +1480,13 @@ int icnss_power_off(struct device *dev)
return -EINVAL;
}
+ icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
+
return icnss_hw_power_off(priv);
}
EXPORT_SYMBOL(icnss_power_off);
-int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index)
+static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index)
{
int ret = 0;
phys_addr_t addr;
@@ -1219,11 +1512,11 @@ int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index)
ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
dest_vmids, dest_perms, dest_nelems);
if (ret) {
- icnss_pr_err("region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n",
+ icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n",
index, &addr, size, ret);
goto out;
}
- icnss_pr_dbg("hypervisor map for region %u: source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n",
+ icnss_pr_dbg("Hypervisor map for region %u: source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n",
index, source_vmlist[0], dest_nelems,
dest_vmids[0], dest_vmids[1], dest_vmids[2]);
out:
@@ -1231,7 +1524,7 @@ out:
}
-int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index)
+static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index)
{
int ret = 0;
phys_addr_t addr;
@@ -1255,7 +1548,7 @@ int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index)
ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
dest_vmids, dest_perms, dest_nelems);
if (ret) {
- icnss_pr_err("region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n",
+ icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n",
index, &addr, size, ret);
goto out;
}
@@ -1269,7 +1562,10 @@ out:
static int icnss_setup_msa_permissions(struct icnss_priv *priv)
{
- int ret = 0;
+ int ret;
+
+ if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
+ return 0;
ret = icnss_map_msa_permissions(priv, 0);
if (ret)
@@ -1279,6 +1575,8 @@ static int icnss_setup_msa_permissions(struct icnss_priv *priv)
if (ret)
goto err_map_msa;
+ set_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
+
return ret;
err_map_msa:
@@ -1288,22 +1586,25 @@ err_map_msa:
static void icnss_remove_msa_permissions(struct icnss_priv *priv)
{
+ if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
+ return;
+
icnss_unmap_msa_permissions(priv, 0);
icnss_unmap_msa_permissions(priv, 1);
+
+ clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
}
static int wlfw_msa_mem_info_send_sync_msg(void)
{
- int ret = 0;
+ int ret;
int i;
struct wlfw_msa_info_req_msg_v01 req;
struct wlfw_msa_info_resp_msg_v01 resp;
struct msg_desc req_desc, resp_desc;
- if (!penv || !penv->wlfw_clnt) {
- ret = -ENODEV;
- goto out;
- }
+ if (!penv || !penv->wlfw_clnt)
+ return -ENODEV;
icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state);
@@ -1326,15 +1627,14 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
if (ret < 0) {
- icnss_pr_err("Send req failed %d\n", ret);
+ icnss_pr_err("Send MSA Mem info req failed %d\n", ret);
goto out;
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI request failed %d %d\n",
+ icnss_pr_err("QMI MSA Mem info request failed %d %d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
- penv->stats.msa_info_err++;
goto out;
}
@@ -1345,7 +1645,6 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
icnss_pr_err("Invalid memory region length received: %d\n",
resp.mem_region_info_len);
ret = -EINVAL;
- penv->stats.msa_info_err++;
goto out;
}
@@ -1363,7 +1662,11 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
penv->icnss_mem_region[i].secure_flag);
}
+ return 0;
+
out:
+ penv->stats.msa_info_err++;
+ ICNSS_ASSERT(false);
return ret;
}
@@ -1374,10 +1677,8 @@ static int wlfw_msa_ready_send_sync_msg(void)
struct wlfw_msa_ready_resp_msg_v01 resp;
struct msg_desc req_desc, resp_desc;
- if (!penv || !penv->wlfw_clnt) {
- ret = -ENODEV;
- goto out;
- }
+ if (!penv || !penv->wlfw_clnt)
+ return -ENODEV;
icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n",
penv->state);
@@ -1397,20 +1698,23 @@ static int wlfw_msa_ready_send_sync_msg(void)
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
if (ret < 0) {
- penv->stats.msa_ready_err++;
- icnss_pr_err("Send req failed %d\n", ret);
+ icnss_pr_err("Send MSA ready req failed %d\n", ret);
goto out;
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI request failed %d %d\n",
+ icnss_pr_err("QMI MSA ready request failed %d %d\n",
resp.resp.result, resp.resp.error);
- penv->stats.msa_ready_err++;
ret = resp.resp.result;
goto out;
}
penv->stats.msa_ready_resp++;
+
+ return 0;
+
out:
+ penv->stats.msa_ready_err++;
+ ICNSS_ASSERT(false);
return ret;
}
@@ -1421,10 +1725,8 @@ static int wlfw_ind_register_send_sync_msg(void)
struct wlfw_ind_register_resp_msg_v01 resp;
struct msg_desc req_desc, resp_desc;
- if (!penv || !penv->wlfw_clnt) {
- ret = -ENODEV;
- goto out;
- }
+ if (!penv || !penv->wlfw_clnt)
+ return -ENODEV;
icnss_pr_dbg("Sending indication register message, state: 0x%lx\n",
penv->state);
@@ -1432,6 +1734,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;
@@ -1452,21 +1756,24 @@ static int wlfw_ind_register_send_sync_msg(void)
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp),
WLFW_TIMEOUT_MS);
- penv->stats.ind_register_resp++;
if (ret < 0) {
- icnss_pr_err("Send req failed %d\n", ret);
- penv->stats.ind_register_err++;
+ icnss_pr_err("Send indication register req failed %d\n", ret);
goto out;
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI request failed %d %d\n",
+ icnss_pr_err("QMI indication register request failed %d %d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
- penv->stats.ind_register_err++;
goto out;
}
+ penv->stats.ind_register_resp++;
+
+ return 0;
+
out:
+ penv->stats.ind_register_err++;
+ ICNSS_ASSERT(false);
return ret;
}
@@ -1477,10 +1784,8 @@ static int wlfw_cap_send_sync_msg(void)
struct wlfw_cap_resp_msg_v01 resp;
struct msg_desc req_desc, resp_desc;
- if (!penv || !penv->wlfw_clnt) {
- ret = -ENODEV;
- goto out;
- }
+ if (!penv || !penv->wlfw_clnt)
+ return -ENODEV;
icnss_pr_dbg("Sending capability message, state: 0x%lx\n", penv->state);
@@ -1499,16 +1804,14 @@ static int wlfw_cap_send_sync_msg(void)
&resp_desc, &resp, sizeof(resp),
WLFW_TIMEOUT_MS);
if (ret < 0) {
- icnss_pr_err("Send req failed %d\n", ret);
- penv->stats.cap_err++;
+ icnss_pr_err("Send capability req failed %d\n", ret);
goto out;
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI request failed %d %d\n",
+ icnss_pr_err("QMI capability request failed %d %d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
- penv->stats.cap_err++;
goto out;
}
@@ -1530,7 +1833,12 @@ static int wlfw_cap_send_sync_msg(void)
penv->board_info.board_id, penv->soc_info.soc_id,
penv->fw_version_info.fw_version,
penv->fw_version_info.fw_build_timestamp);
+
+ return 0;
+
out:
+ penv->stats.cap_err++;
+ ICNSS_ASSERT(false);
return ret;
}
@@ -1541,10 +1849,15 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
struct wlfw_wlan_mode_resp_msg_v01 resp;
struct msg_desc req_desc, resp_desc;
- if (!penv || !penv->wlfw_clnt) {
- ret = -ENODEV;
- goto out;
- }
+ if (!penv || !penv->wlfw_clnt)
+ return -ENODEV;
+
+ /* During recovery do not send mode request for WLAN OFF as
+ * FW not able to process it.
+ */
+ if (test_bit(ICNSS_PD_RESTART, &penv->state) &&
+ mode == QMI_WLFW_OFF_V01)
+ return 0;
icnss_pr_dbg("Sending Mode request, state: 0x%lx, mode: %d\n",
penv->state, mode);
@@ -1569,20 +1882,24 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
&resp_desc, &resp, sizeof(resp),
WLFW_TIMEOUT_MS);
if (ret < 0) {
- icnss_pr_err("Send req failed %d\n", ret);
- penv->stats.mode_req_err++;
+ icnss_pr_err("Send mode req failed, mode: %d ret: %d\n",
+ mode, ret);
goto out;
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI request failed %d %d\n",
- resp.resp.result, resp.resp.error);
+ icnss_pr_err("QMI mode request failed mode: %d, %d %d\n",
+ mode, resp.resp.result, resp.resp.error);
ret = resp.resp.result;
- penv->stats.mode_req_err++;
goto out;
}
penv->stats.mode_resp++;
+
+ return 0;
+
out:
+ penv->stats.mode_req_err++;
+ ICNSS_ASSERT(false);
return ret;
}
@@ -1593,10 +1910,8 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
struct wlfw_wlan_cfg_resp_msg_v01 resp;
struct msg_desc req_desc, resp_desc;
- if (!penv || !penv->wlfw_clnt) {
+ if (!penv || !penv->wlfw_clnt)
return -ENODEV;
- goto out;
- }
icnss_pr_dbg("Sending config request, state: 0x%lx\n", penv->state);
@@ -1618,20 +1933,23 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
&resp_desc, &resp, sizeof(resp),
WLFW_TIMEOUT_MS);
if (ret < 0) {
- icnss_pr_err("Send req failed %d\n", ret);
- penv->stats.cfg_req_err++;
+ icnss_pr_err("Send config req failed %d\n", ret);
goto out;
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI request failed %d %d\n",
+ icnss_pr_err("QMI config request failed %d %d\n",
resp.resp.result, resp.resp.error);
ret = resp.resp.result;
- penv->stats.cfg_req_err++;
goto out;
}
penv->stats.cfg_resp++;
+
+ return 0;
+
out:
+ penv->stats.cfg_req_err++;
+ ICNSS_ASSERT(false);
return ret;
}
@@ -1642,10 +1960,8 @@ static int wlfw_ini_send_sync_msg(bool enable_fw_log)
struct wlfw_ini_resp_msg_v01 resp;
struct msg_desc req_desc, resp_desc;
- if (!penv || !penv->wlfw_clnt) {
- ret = -ENODEV;
- goto out;
- }
+ if (!penv || !penv->wlfw_clnt)
+ return -ENODEV;
icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log: %d\n",
penv->state, enable_fw_log);
@@ -1669,20 +1985,145 @@ static int wlfw_ini_send_sync_msg(bool enable_fw_log)
ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
&resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
if (ret < 0) {
- icnss_pr_err("send req failed %d\n", ret);
- penv->stats.ini_req_err++;
+ icnss_pr_err("Send INI req failed fw_log: %d, ret: %d\n",
+ enable_fw_log, ret);
goto out;
}
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- icnss_pr_err("QMI request failed %d %d\n",
- resp.resp.result, resp.resp.error);
+ icnss_pr_err("QMI INI request failed fw_log: %d, %d %d\n",
+ enable_fw_log, resp.resp.result, resp.resp.error);
ret = resp.resp.result;
- penv->stats.ini_req_err++;
goto out;
}
penv->stats.ini_resp++;
+
+ return 0;
+
+out:
+ penv->stats.ini_req_err++;
+ ICNSS_ASSERT(false);
+ return ret;
+}
+
+static int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
+ uint32_t offset, uint32_t mem_type,
+ uint32_t data_len, uint8_t *data)
+{
+ int ret;
+ struct wlfw_athdiag_read_req_msg_v01 req;
+ struct wlfw_athdiag_read_resp_msg_v01 *resp = NULL;
+ struct msg_desc req_desc, resp_desc;
+
+ if (!priv->wlfw_clnt) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ icnss_pr_dbg("Diag read: state 0x%lx, offset %x, mem_type %x, data_len %u\n",
+ priv->state, offset, mem_type, data_len);
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ memset(&req, 0, sizeof(req));
+
+ req.offset = offset;
+ req.mem_type = mem_type;
+ req.data_len = data_len;
+
+ req_desc.max_msg_len = WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN;
+ req_desc.msg_id = QMI_WLFW_ATHDIAG_READ_REQ_V01;
+ req_desc.ei_array = wlfw_athdiag_read_req_msg_v01_ei;
+
+ resp_desc.max_msg_len = WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN;
+ resp_desc.msg_id = QMI_WLFW_ATHDIAG_READ_RESP_V01;
+ resp_desc.ei_array = wlfw_athdiag_read_resp_msg_v01_ei;
+
+ ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
+ &resp_desc, resp, sizeof(*resp),
+ WLFW_TIMEOUT_MS);
+ if (ret < 0) {
+ icnss_pr_err("send athdiag read req failed %d\n", ret);
+ goto out;
+ }
+
+ if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_pr_err("QMI athdiag read request failed %d %d\n",
+ resp->resp.result, resp->resp.error);
+ ret = resp->resp.result;
+ goto out;
+ }
+
+ if (!resp->data_valid || resp->data_len <= data_len) {
+ icnss_pr_err("Athdiag read data is invalid, data_valid = %u, data_len = %u\n",
+ resp->data_valid, resp->data_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(data, resp->data, resp->data_len);
+
out:
+ kfree(resp);
+ return ret;
+}
+
+static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
+ uint32_t offset, uint32_t mem_type,
+ uint32_t data_len, uint8_t *data)
+{
+ int ret;
+ struct wlfw_athdiag_write_req_msg_v01 *req = NULL;
+ struct wlfw_athdiag_write_resp_msg_v01 resp;
+ struct msg_desc req_desc, resp_desc;
+
+ if (!priv->wlfw_clnt) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ icnss_pr_dbg("Diag write: state 0x%lx, offset %x, mem_type %x, data_len %u, data %p\n",
+ priv->state, offset, mem_type, data_len, data);
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ memset(&resp, 0, sizeof(resp));
+
+ req->offset = offset;
+ req->mem_type = mem_type;
+ req->data_len = data_len;
+ memcpy(req->data, data, data_len);
+
+ req_desc.max_msg_len = WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN;
+ req_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_REQ_V01;
+ req_desc.ei_array = wlfw_athdiag_write_req_msg_v01_ei;
+
+ resp_desc.max_msg_len = WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN;
+ resp_desc.msg_id = QMI_WLFW_ATHDIAG_WRITE_RESP_V01;
+ resp_desc.ei_array = wlfw_athdiag_write_resp_msg_v01_ei;
+
+ ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, req, sizeof(*req),
+ &resp_desc, &resp, sizeof(resp),
+ WLFW_TIMEOUT_MS);
+ if (ret < 0) {
+ icnss_pr_err("send athdiag write req failed %d\n", ret);
+ goto out;
+ }
+
+ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+ icnss_pr_err("QMI athdiag write request failed %d %d\n",
+ resp.resp.result, resp.resp.error);
+ ret = resp.resp.result;
+ goto out;
+ }
+out:
+ kfree(req);
return ret;
}
@@ -1766,12 +2207,11 @@ static int icnss_driver_event_server_arrive(void *data)
goto out;
}
- ret = qmi_connect_to_service(penv->wlfw_clnt,
- WLFW_SERVICE_ID_V01,
- WLFW_SERVICE_VERS_V01,
- WLFW_SERVICE_INS_ID_V01);
+ ret = qmi_connect_to_service(penv->wlfw_clnt, WLFW_SERVICE_ID_V01,
+ WLFW_SERVICE_VERS_V01,
+ WLFW_SERVICE_INS_ID_V01);
if (ret < 0) {
- icnss_pr_err("Server not found : %d\n", ret);
+ icnss_pr_err("QMI WLAN Service not found : %d\n", ret);
goto fail;
}
@@ -1792,10 +2232,8 @@ static int icnss_driver_event_server_arrive(void *data)
goto fail;
ret = wlfw_ind_register_send_sync_msg();
- if (ret < 0) {
- icnss_pr_err("Failed to send indication message: %d\n", ret);
+ if (ret < 0)
goto err_power_on;
- }
if (!penv->msa_va) {
icnss_pr_err("Invalid MSA address\n");
@@ -1804,27 +2242,23 @@ static int icnss_driver_event_server_arrive(void *data)
}
ret = wlfw_msa_mem_info_send_sync_msg();
- if (ret < 0) {
- icnss_pr_err("Failed to send MSA info: %d\n", ret);
+ if (ret < 0)
goto err_power_on;
- }
+
ret = icnss_setup_msa_permissions(penv);
- if (ret < 0) {
- icnss_pr_err("Failed to setup msa permissions: %d\n",
- ret);
+ if (ret < 0)
goto err_power_on;
- }
+
ret = wlfw_msa_ready_send_sync_msg();
- if (ret < 0) {
- icnss_pr_err("Failed to send MSA ready : %d\n", ret);
+ if (ret < 0)
goto err_setup_msa;
- }
ret = wlfw_cap_send_sync_msg();
- if (ret < 0) {
- icnss_pr_err("Failed to get capability: %d\n", ret);
+ if (ret < 0)
goto err_setup_msa;
- }
+
+ icnss_init_vph_monitor(penv);
+
return ret;
err_setup_msa:
@@ -1846,6 +2280,10 @@ 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)
+ qpnp_adc_tm_disable_chan_meas(penv->adc_tm_dev,
+ &penv->vph_monitor_params);
+
qmi_handle_destroy(penv->wlfw_clnt);
clear_bit(ICNSS_WLFW_QMI_CONNECTED, &penv->state);
@@ -1886,6 +2324,9 @@ static int icnss_call_driver_reinit(struct icnss_priv *priv)
if (!priv->ops || !priv->ops->reinit)
goto out;
+ if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
+ goto out;
+
icnss_hw_power_on(priv);
ret = priv->ops->reinit(&priv->pdev->dev);
@@ -1916,6 +2357,8 @@ 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);
@@ -1933,7 +2376,10 @@ 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;
}
@@ -1941,10 +2387,10 @@ static int icnss_driver_event_register_driver(void *data)
{
int ret = 0;
- if (penv->ops) {
- ret = -EEXIST;
- goto out;
- }
+ if (penv->ops)
+ return -EEXIST;
+
+ __pm_stay_awake(&penv->ws);
penv->ops = data;
@@ -1971,16 +2417,21 @@ 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;
@@ -1996,15 +2447,21 @@ static int icnss_driver_event_unregister_driver(void *data)
icnss_hw_power_off(penv);
out:
+ __pm_relax(&penv->ws);
return 0;
}
-static int icnss_qmi_pd_event_service_down(struct icnss_priv *priv, void *data)
+static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
+ void *data)
{
int ret = 0;
- if (test_bit(ICNSS_PD_RESTART, &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;
+ }
set_bit(ICNSS_PD_RESTART, &priv->state);
clear_bit(ICNSS_FW_READY, &priv->state);
@@ -2012,6 +2469,9 @@ static int icnss_qmi_pd_event_service_down(struct icnss_priv *priv, void *data)
if (!priv->ops || !priv->ops->shutdown)
goto out;
+ if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
+ goto out;
+
priv->ops->shutdown(&priv->pdev->dev);
out:
@@ -2019,7 +2479,7 @@ out:
ret = icnss_hw_power_off(priv);
- icnss_pr_dbg("Shutdown completed: %d, state: 0x%lx\n",
+ icnss_pr_dbg("PD down completed: %d, state: 0x%lx\n",
ret, priv->state);
return ret;
@@ -2061,7 +2521,7 @@ 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_qmi_pd_event_service_down(penv, event->data);
+ icnss_driver_event_pd_service_down(penv, event->data);
break;
default:
icnss_pr_err("Invalid Event type: %d", event->type);
@@ -2071,11 +2531,15 @@ static void icnss_driver_event_work(struct work_struct *work)
penv->stats.events[event->type].processed++;
+ spin_lock_irqsave(&penv->event_lock, flags);
if (event->sync) {
event->ret = ret;
complete(&event->complete);
- } else
- kfree(event);
+ continue;
+ }
+ spin_unlock_irqrestore(&penv->event_lock, flags);
+
+ kfree(event);
spin_lock_irqsave(&penv->event_lock, flags);
}
@@ -2372,6 +2836,9 @@ int icnss_register_driver(struct icnss_driver_ops *ops)
ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
true, ops);
+ if (ret == -ERESTARTSYS)
+ ret = 0;
+
out:
return ret;
}
@@ -2571,6 +3038,78 @@ int icnss_set_fw_debug_mode(bool enable_fw_log)
}
EXPORT_SYMBOL(icnss_set_fw_debug_mode);
+int icnss_athdiag_read(struct device *dev, uint32_t offset,
+ uint32_t mem_type, uint32_t data_len,
+ uint8_t *output)
+{
+ int ret = 0;
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for diag read: dev %p, data %p, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ if (!output || data_len == 0
+ || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
+ icnss_pr_err("Invalid parameters for diag read: output %p, data_len %u\n",
+ output, data_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+ !test_bit(ICNSS_POWER_ON, &priv->state)) {
+ icnss_pr_err("Invalid state for diag read: 0x%lx\n",
+ priv->state);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = wlfw_athdiag_read_send_sync_msg(priv, offset, mem_type,
+ data_len, output);
+out:
+ return ret;
+}
+EXPORT_SYMBOL(icnss_athdiag_read);
+
+int icnss_athdiag_write(struct device *dev, uint32_t offset,
+ uint32_t mem_type, uint32_t data_len,
+ uint8_t *input)
+{
+ int ret = 0;
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for diag write: dev %p, data %p, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ if (!input || data_len == 0
+ || data_len > QMI_WLFW_MAX_DATA_SIZE_V01) {
+ icnss_pr_err("Invalid parameters for diag write: input %p, data_len %u\n",
+ input, data_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+ !test_bit(ICNSS_POWER_ON, &priv->state)) {
+ icnss_pr_err("Invalid state for diag write: 0x%lx\n",
+ priv->state);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = wlfw_athdiag_write_send_sync_msg(priv, offset, mem_type,
+ data_len, input);
+out:
+ return ret;
+}
+EXPORT_SYMBOL(icnss_athdiag_write);
+
int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config,
enum icnss_driver_mode mode,
const char *host_version)
@@ -2633,14 +3172,10 @@ int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config,
sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
ret = wlfw_wlan_cfg_send_sync_msg(&req);
- if (ret) {
- icnss_pr_err("Failed to send cfg, ret = %d\n", ret);
+ if (ret)
goto out;
- }
skip:
ret = wlfw_wlan_mode_send_sync_msg(mode);
- if (ret)
- icnss_pr_err("Failed to send mode, ret = %d\n", ret);
out:
if (test_bit(SKIP_QMI, &quirks))
ret = 0;
@@ -2890,7 +3425,7 @@ static int icnss_get_vreg_info(struct device *dev,
reg = devm_regulator_get_optional(dev, vreg_info->name);
- if (IS_ERR(reg) == -EPROBE_DEFER) {
+ if (PTR_ERR(reg) == -EPROBE_DEFER) {
icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
vreg_info->name);
ret = PTR_ERR(reg);
@@ -2910,7 +3445,6 @@ static int icnss_get_vreg_info(struct device *dev,
vreg_info->name, ret);
goto done;
}
-
}
vreg_info->reg = reg;
@@ -3172,7 +3706,7 @@ static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
{
- int i;
+ enum icnss_driver_state i;
int skip = 0;
unsigned long state;
@@ -3201,6 +3735,15 @@ 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;
+ case ICNSS_PM_SUSPEND_NOIRQ:
+ seq_puts(s, "PM SUSPEND NOIRQ");
+ continue;
case ICNSS_SSR_ENABLED:
seq_puts(s, "SSR ENABLED");
continue;
@@ -3210,6 +3753,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
case ICNSS_PD_RESTART:
seq_puts(s, "PD RESTART");
continue;
+ case ICNSS_MSA0_ASSIGNED:
+ seq_puts(s, "MSA0 ASSIGNED");
+ continue;
}
seq_printf(s, "UNKNOWN-%d", i);
@@ -3300,6 +3846,19 @@ static int icnss_stats_show(struct seq_file *s, void *data)
ICNSS_STATS_DUMP(s, priv, ini_req);
ICNSS_STATS_DUMP(s, priv, ini_resp);
ICNSS_STATS_DUMP(s, priv, ini_req_err);
+ ICNSS_STATS_DUMP(s, priv, vbatt_req);
+ ICNSS_STATS_DUMP(s, priv, vbatt_resp);
+ ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
+
+ seq_puts(s, "\n<------------------ PM stats ------------------->\n");
+ ICNSS_STATS_DUMP(s, priv, pm_suspend);
+ ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
+ ICNSS_STATS_DUMP(s, priv, pm_resume);
+ ICNSS_STATS_DUMP(s, priv, pm_resume_err);
+ ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
+ 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_show_irqs(s, priv);
@@ -3327,6 +3886,207 @@ static const struct file_operations icnss_stats_fops = {
.llseek = seq_lseek,
};
+static int icnss_regwrite_show(struct seq_file *s, void *data)
+{
+ struct icnss_priv *priv = s->private;
+
+ seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state))
+ seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
+
+ return 0;
+}
+
+static ssize_t icnss_regwrite_write(struct file *fp,
+ const char __user *user_buf,
+ size_t count, loff_t *off)
+{
+ struct icnss_priv *priv =
+ ((struct seq_file *)fp->private_data)->private;
+ char buf[64];
+ char *sptr, *token;
+ unsigned int len = 0;
+ uint32_t reg_offset, mem_type, reg_val;
+ const char *delim = " ";
+ int ret = 0;
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+ !test_bit(ICNSS_POWER_ON, &priv->state))
+ return -EINVAL;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &mem_type))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &reg_offset))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &reg_val))
+ return -EINVAL;
+
+ ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
+ sizeof(uint32_t),
+ (uint8_t *)&reg_val);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static int icnss_regwrite_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, icnss_regwrite_show, inode->i_private);
+}
+
+static const struct file_operations icnss_regwrite_fops = {
+ .read = seq_read,
+ .write = icnss_regwrite_write,
+ .open = icnss_regwrite_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
+static int icnss_regread_show(struct seq_file *s, void *data)
+{
+ struct icnss_priv *priv = s->private;
+
+ if (!priv->diag_reg_read_buf) {
+ seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state))
+ seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
+
+ return 0;
+ }
+
+ seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
+ priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
+ priv->diag_reg_read_len);
+
+ seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
+ priv->diag_reg_read_len, false);
+
+ priv->diag_reg_read_len = 0;
+ kfree(priv->diag_reg_read_buf);
+ priv->diag_reg_read_buf = NULL;
+
+ return 0;
+}
+
+static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
+ size_t count, loff_t *off)
+{
+ struct icnss_priv *priv =
+ ((struct seq_file *)fp->private_data)->private;
+ char buf[64];
+ char *sptr, *token;
+ unsigned int len = 0;
+ uint32_t reg_offset, mem_type;
+ uint32_t data_len = 0;
+ uint8_t *reg_buf = NULL;
+ const char *delim = " ";
+ int ret = 0;
+
+ if (!test_bit(ICNSS_FW_READY, &priv->state) ||
+ !test_bit(ICNSS_POWER_ON, &priv->state))
+ return -EINVAL;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &mem_type))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (!sptr)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &reg_offset))
+ return -EINVAL;
+
+ token = strsep(&sptr, delim);
+ if (!token)
+ return -EINVAL;
+
+ if (kstrtou32(token, 0, &data_len))
+ return -EINVAL;
+
+ if (data_len == 0 ||
+ data_len > QMI_WLFW_MAX_DATA_SIZE_V01)
+ return -EINVAL;
+
+ reg_buf = kzalloc(data_len, GFP_KERNEL);
+ if (!reg_buf)
+ return -ENOMEM;
+
+ ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset,
+ mem_type, data_len,
+ reg_buf);
+ if (ret) {
+ kfree(reg_buf);
+ return ret;
+ }
+
+ priv->diag_reg_read_addr = reg_offset;
+ priv->diag_reg_read_mem_type = mem_type;
+ priv->diag_reg_read_len = data_len;
+ priv->diag_reg_read_buf = reg_buf;
+
+ return count;
+}
+
+static int icnss_regread_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, icnss_regread_show, inode->i_private);
+}
+
+static const struct file_operations icnss_regread_fops = {
+ .read = seq_read,
+ .write = icnss_regread_write,
+ .open = icnss_regread_open,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+};
+
static int icnss_debugfs_create(struct icnss_priv *priv)
{
int ret = 0;
@@ -3347,6 +4107,10 @@ static int icnss_debugfs_create(struct icnss_priv *priv)
debugfs_create_file("stats", 0644, root_dentry, priv,
&icnss_stats_fops);
+ debugfs_create_file("reg_read", 0600, root_dentry, priv,
+ &icnss_regread_fops);
+ debugfs_create_file("reg_write", 0644, root_dentry, priv,
+ &icnss_regwrite_fops);
out:
return ret;
@@ -3357,6 +4121,44 @@ static void icnss_debugfs_destroy(struct icnss_priv *priv)
debugfs_remove_recursive(priv->root_dentry);
}
+static int icnss_get_vbatt_info(struct icnss_priv *priv)
+{
+ struct qpnp_adc_tm_chip *adc_tm_dev = NULL;
+ struct qpnp_vadc_chip *vadc_dev = NULL;
+ int ret = 0;
+
+ adc_tm_dev = qpnp_get_adc_tm(&priv->pdev->dev, "icnss");
+ if (PTR_ERR(adc_tm_dev) == -EPROBE_DEFER) {
+ icnss_pr_err("adc_tm_dev probe defer\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (IS_ERR(adc_tm_dev)) {
+ ret = PTR_ERR(adc_tm_dev);
+ icnss_pr_err("Not able to get ADC dev, VBATT monitoring is disabled: %d\n",
+ ret);
+ return ret;
+ }
+
+ vadc_dev = qpnp_get_vadc(&priv->pdev->dev, "icnss");
+ if (PTR_ERR(vadc_dev) == -EPROBE_DEFER) {
+ icnss_pr_err("vadc_dev probe defer\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (IS_ERR(vadc_dev)) {
+ ret = PTR_ERR(vadc_dev);
+ icnss_pr_err("Not able to get VADC dev, VBATT monitoring is disabled: %d\n",
+ ret);
+ return ret;
+ }
+
+ priv->adc_tm_dev = adc_tm_dev;
+ priv->vadc_dev = vadc_dev;
+
+ return 0;
+}
+
static int icnss_probe(struct platform_device *pdev)
{
int ret = 0;
@@ -3370,14 +4172,21 @@ static int icnss_probe(struct platform_device *pdev)
return -EEXIST;
}
+ icnss_pr_dbg("Platform driver probe\n");
+
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ priv->magic = ICNSS_MAGIC;
dev_set_drvdata(dev, priv);
priv->pdev = pdev;
+ ret = icnss_get_vbatt_info(priv);
+ if (ret == -EPROBE_DEFER)
+ goto out;
+
memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
@@ -3469,9 +4278,8 @@ static int icnss_probe(struct platform_device *pdev)
} else {
priv->smmu_iova_start = res->start;
priv->smmu_iova_len = resource_size(res);
- icnss_pr_dbg("smmu_iova_start: %pa, smmu_iova_len: %zu\n",
- &priv->smmu_iova_start,
- priv->smmu_iova_len);
+ icnss_pr_dbg("SMMU IOVA start: %pa, len: %zu\n",
+ &priv->smmu_iova_start, priv->smmu_iova_len);
res = platform_get_resource_byname(pdev,
IORESOURCE_MEM,
@@ -3481,7 +4289,7 @@ static int icnss_probe(struct platform_device *pdev)
} else {
priv->smmu_iova_ipa_start = res->start;
priv->smmu_iova_ipa_len = resource_size(res);
- icnss_pr_dbg("smmu_iova_ipa_start: %pa, smmu_iova_ipa_len: %zu\n",
+ icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zu\n",
&priv->smmu_iova_ipa_start,
priv->smmu_iova_ipa_len);
}
@@ -3502,6 +4310,8 @@ 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");
@@ -3563,6 +4373,8 @@ 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);
@@ -3619,6 +4431,131 @@ out:
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static int icnss_pm_suspend(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ icnss_pr_dbg("PM Suspend, state: 0x%lx\n", priv->state);
+
+ if (!priv->ops || !priv->ops->pm_suspend ||
+ !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ goto out;
+
+ ret = priv->ops->pm_suspend(dev);
+
+out:
+ if (ret == 0) {
+ priv->stats.pm_suspend++;
+ set_bit(ICNSS_PM_SUSPEND, &priv->state);
+ } else {
+ priv->stats.pm_suspend_err++;
+ }
+ return ret;
+}
+
+static int icnss_pm_resume(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ icnss_pr_dbg("PM resume, state: 0x%lx\n", priv->state);
+
+ if (!priv->ops || !priv->ops->pm_resume ||
+ !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ goto out;
+
+ ret = priv->ops->pm_resume(dev);
+
+out:
+ if (ret == 0) {
+ priv->stats.pm_resume++;
+ clear_bit(ICNSS_PM_SUSPEND, &priv->state);
+ } else {
+ priv->stats.pm_resume_err++;
+ }
+ return ret;
+}
+
+static int icnss_pm_suspend_noirq(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ icnss_pr_dbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
+
+ if (!priv->ops || !priv->ops->suspend_noirq ||
+ !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ goto out;
+
+ ret = priv->ops->suspend_noirq(dev);
+
+out:
+ if (ret == 0) {
+ priv->stats.pm_suspend_noirq++;
+ set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
+ } else {
+ priv->stats.pm_suspend_noirq_err++;
+ }
+ return ret;
+}
+
+static int icnss_pm_resume_noirq(struct device *dev)
+{
+ struct icnss_priv *priv = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (priv->magic != ICNSS_MAGIC) {
+ icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n",
+ dev, priv, priv->magic);
+ return -EINVAL;
+ }
+
+ icnss_pr_dbg("PM resume_noirq, state: 0x%lx\n", priv->state);
+
+ if (!priv->ops || !priv->ops->resume_noirq ||
+ !test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ goto out;
+
+ ret = priv->ops->resume_noirq(dev);
+
+out:
+ if (ret == 0) {
+ priv->stats.pm_resume_noirq++;
+ clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state);
+ } else {
+ priv->stats.pm_resume_noirq_err++;
+ }
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops icnss_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend,
+ icnss_pm_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq,
+ icnss_pm_resume_noirq)
+};
+
static const struct of_device_id icnss_dt_match[] = {
{.compatible = "qcom,icnss"},
{}
@@ -3633,11 +4570,32 @@ static struct platform_driver icnss_driver = {
.resume = icnss_resume,
.driver = {
.name = "icnss",
+ .pm = &icnss_pm_ops,
.owner = THIS_MODULE,
.of_match_table = icnss_dt_match,
},
};
+#ifdef CONFIG_ICNSS_DEBUG
+static void __init icnss_ipc_log_long_context_init(void)
+{
+ icnss_ipc_log_long_context = ipc_log_context_create(NUM_REG_LOG_PAGES,
+ "icnss_long", 0);
+ if (!icnss_ipc_log_long_context)
+ icnss_pr_err("Unable to create register log context\n");
+}
+
+static void __exit icnss_ipc_log_long_context_destroy(void)
+{
+ ipc_log_context_destroy(icnss_ipc_log_long_context);
+ icnss_ipc_log_long_context = NULL;
+}
+#else
+
+static void __init icnss_ipc_log_long_context_init(void) { }
+static void __exit icnss_ipc_log_long_context_destroy(void) { }
+#endif
+
static int __init icnss_initialize(void)
{
icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
@@ -3645,6 +4603,8 @@ static int __init icnss_initialize(void)
if (!icnss_ipc_log_context)
icnss_pr_err("Unable to create log context\n");
+ icnss_ipc_log_long_context_init();
+
return platform_driver_register(&icnss_driver);
}
@@ -3653,6 +4613,8 @@ static void __exit icnss_exit(void)
platform_driver_unregister(&icnss_driver);
ipc_log_context_destroy(icnss_ipc_log_context);
icnss_ipc_log_context = NULL;
+
+ icnss_ipc_log_long_context_destroy();
}
diff --git a/drivers/soc/qcom/icnss_utils.c b/drivers/soc/qcom/icnss_utils.c
new file mode 100644
index 000000000000..5e187d5df8b3
--- /dev/null
+++ b/drivers/soc/qcom/icnss_utils.c
@@ -0,0 +1,132 @@
+/* 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>
+
+#define ICNSS_MAX_CH_NUM 45
+
+static DEFINE_MUTEX(unsafe_channel_list_lock);
+static DEFINE_MUTEX(dfs_nol_info_lock);
+
+static struct icnss_unsafe_channel_list {
+ u16 unsafe_ch_count;
+ u16 unsafe_ch_list[ICNSS_MAX_CH_NUM];
+} unsafe_channel_list;
+
+static struct icnss_dfs_nol_info {
+ void *dfs_nol_info;
+ u16 dfs_nol_info_len;
+} dfs_nol_info;
+
+int icnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count)
+{
+ mutex_lock(&unsafe_channel_list_lock);
+ if ((!unsafe_ch_list) || (ch_count > ICNSS_MAX_CH_NUM)) {
+ mutex_unlock(&unsafe_channel_list_lock);
+ return -EINVAL;
+ }
+
+ unsafe_channel_list.unsafe_ch_count = ch_count;
+
+ if (ch_count != 0) {
+ memcpy(
+ (char *)unsafe_channel_list.unsafe_ch_list,
+ (char *)unsafe_ch_list, ch_count * sizeof(u16));
+ }
+ mutex_unlock(&unsafe_channel_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(icnss_set_wlan_unsafe_channel);
+
+int icnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list,
+ u16 *ch_count, u16 buf_len)
+{
+ mutex_lock(&unsafe_channel_list_lock);
+ if (!unsafe_ch_list || !ch_count) {
+ mutex_unlock(&unsafe_channel_list_lock);
+ return -EINVAL;
+ }
+
+ if (buf_len < (unsafe_channel_list.unsafe_ch_count * sizeof(u16))) {
+ mutex_unlock(&unsafe_channel_list_lock);
+ return -ENOMEM;
+ }
+
+ *ch_count = unsafe_channel_list.unsafe_ch_count;
+ memcpy(
+ (char *)unsafe_ch_list,
+ (char *)unsafe_channel_list.unsafe_ch_list,
+ unsafe_channel_list.unsafe_ch_count * sizeof(u16));
+ mutex_unlock(&unsafe_channel_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(icnss_get_wlan_unsafe_channel);
+
+int icnss_wlan_set_dfs_nol(const void *info, u16 info_len)
+{
+ void *temp;
+ struct icnss_dfs_nol_info *dfs_info;
+
+ mutex_lock(&dfs_nol_info_lock);
+ if (!info || !info_len) {
+ mutex_unlock(&dfs_nol_info_lock);
+ return -EINVAL;
+ }
+
+ temp = kmalloc(info_len, GFP_KERNEL);
+ if (!temp) {
+ mutex_unlock(&dfs_nol_info_lock);
+ return -ENOMEM;
+ }
+
+ memcpy(temp, info, info_len);
+ dfs_info = &dfs_nol_info;
+ kfree(dfs_info->dfs_nol_info);
+
+ dfs_info->dfs_nol_info = temp;
+ dfs_info->dfs_nol_info_len = info_len;
+ mutex_unlock(&dfs_nol_info_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(icnss_wlan_set_dfs_nol);
+
+int icnss_wlan_get_dfs_nol(void *info, u16 info_len)
+{
+ int len;
+ struct icnss_dfs_nol_info *dfs_info;
+
+ mutex_lock(&dfs_nol_info_lock);
+ if (!info || !info_len) {
+ mutex_unlock(&dfs_nol_info_lock);
+ return -EINVAL;
+ }
+
+ dfs_info = &dfs_nol_info;
+
+ if (dfs_info->dfs_nol_info == NULL ||
+ dfs_info->dfs_nol_info_len == 0) {
+ mutex_unlock(&dfs_nol_info_lock);
+ return -ENOENT;
+ }
+
+ len = min(info_len, dfs_info->dfs_nol_info_len);
+
+ memcpy(info, dfs_info->dfs_nol_info, len);
+ mutex_unlock(&dfs_nol_info_lock);
+
+ return len;
+}
+EXPORT_SYMBOL(icnss_wlan_get_dfs_nol);
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/irq-helper.c b/drivers/soc/qcom/irq-helper.c
index 7bb371f7991e..370801291230 100644
--- a/drivers/soc/qcom/irq-helper.c
+++ b/drivers/soc/qcom/irq-helper.c
@@ -78,10 +78,12 @@ IRQ_HELPER_ATTR(irq_blacklist_on, 0444, show_deploy, NULL);
static struct irq_helper *irq_h;
+/* Do not call this API in an atomic context */
int irq_blacklist_on(void)
{
bool flag = false;
+ might_sleep();
if (!irq_h) {
pr_err("%s: init function is not called", __func__);
return -EPERM;
@@ -103,10 +105,12 @@ int irq_blacklist_on(void)
}
EXPORT_SYMBOL(irq_blacklist_on);
+/* Do not call this API in an atomic context */
int irq_blacklist_off(void)
{
bool flag = false;
+ might_sleep();
if (!irq_h) {
pr_err("%s: init function is not called", __func__);
return -EPERM;
diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c
index f44f6f97ecdd..e1e91f56526d 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.c
+++ b/drivers/soc/qcom/memshare/msm_memshare.c
@@ -203,6 +203,7 @@ static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
int dest_vmids[1] = {VMID_HLOS};
int dest_perms[1] = {PERM_READ|PERM_WRITE};
+ mutex_lock(&memsh_drv->mem_share);
switch (code) {
case SUBSYS_BEFORE_SHUTDOWN:
@@ -264,6 +265,7 @@ static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
break;
}
+ mutex_unlock(&memsh_drv->mem_share);
return NOTIFY_DONE;
}
diff --git a/drivers/soc/qcom/remoteqdss.c b/drivers/soc/qcom/remoteqdss.c
index e66ca587adca..5e2a5babdcc8 100644
--- a/drivers/soc/qcom/remoteqdss.c
+++ b/drivers/soc/qcom/remoteqdss.c
@@ -28,8 +28,8 @@ static struct dentry *remoteqdss_dir;
#define REMOTEQDSS_ERR(fmt, ...) \
pr_debug("%s: " fmt, __func__, ## __VA_ARGS__)
-#define REMOTEQDSS_ERR_CALLER(fmt, ...) \
- pr_debug("%pf: " fmt, __builtin_return_address(1), ## __VA_ARGS__)
+#define REMOTEQDSS_ERR_CALLER(fmt, caller, ...) \
+ pr_debug("%pf: " fmt, caller, ## __VA_ARGS__)
struct qdss_msg_translation {
u64 val;
@@ -97,7 +97,7 @@ struct remoteqdss_query_swentity_fmt {
/* msgs is a null terminated array */
static void remoteqdss_err_translation(struct qdss_msg_translation *msgs,
- u64 err)
+ u64 err, const void *caller)
{
static DEFINE_RATELIMIT_STATE(rl, 5 * HZ, 2);
struct qdss_msg_translation *msg;
@@ -110,12 +110,13 @@ static void remoteqdss_err_translation(struct qdss_msg_translation *msgs,
for (msg = msgs; msg->msg; msg++) {
if (err == msg->val && __ratelimit(&rl)) {
- REMOTEQDSS_ERR_CALLER("0x%llx: %s\n", err, msg->msg);
+ REMOTEQDSS_ERR_CALLER("0x%llx: %s\n", caller, err,
+ msg->msg);
return;
}
}
- REMOTEQDSS_ERR_CALLER("Error 0x%llx\n", err);
+ REMOTEQDSS_ERR_CALLER("Error 0x%llx\n", caller, err);
}
/* Shared across all remoteqdss scm functions */
@@ -160,7 +161,7 @@ static void free_remoteqdss_data(struct remoteqdss_data *data)
}
static int remoteqdss_do_scm_call(struct scm_desc *desc,
- dma_addr_t addr, size_t size)
+ dma_addr_t addr, size_t size, const void *caller)
{
int ret;
@@ -175,7 +176,7 @@ static int remoteqdss_do_scm_call(struct scm_desc *desc,
if (ret)
return ret;
- remoteqdss_err_translation(remoteqdss_scm_msgs, desc->ret[0]);
+ remoteqdss_err_translation(remoteqdss_scm_msgs, desc->ret[0], caller);
ret = desc->ret[0] ? -EINVAL : 0;
return ret;
}
@@ -194,7 +195,8 @@ static int remoteqdss_scm_query_swtrace(void *priv, u64 *val)
fmt->subsys_id = data->id;
fmt->cmd_id = CMD_ID_QUERY_SWTRACE_STATE;
- ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt));
+ ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt),
+ __builtin_return_address(0));
*val = desc.ret[1];
dma_free_coherent(&dma_dev, sizeof(*fmt), fmt, addr);
@@ -216,7 +218,8 @@ static int remoteqdss_scm_filter_swtrace(void *priv, u64 val)
fmt->h.cmd_id = CMD_ID_FILTER_SWTRACE_STATE;
fmt->state = (uint32_t)val;
- ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt));
+ ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt),
+ __builtin_return_address(0));
dma_free_coherent(&dma_dev, sizeof(*fmt), fmt, addr);
return ret;
@@ -241,7 +244,8 @@ static int remoteqdss_scm_query_tag(void *priv, u64 *val)
fmt->subsys_id = data->id;
fmt->cmd_id = CMD_ID_QUERY_SWEVENT_TAG;
- ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt));
+ ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt),
+ __builtin_return_address(0));
*val = desc.ret[1];
dma_free_coherent(&dma_dev, sizeof(*fmt), fmt, addr);
@@ -268,7 +272,8 @@ static int remoteqdss_scm_query_swevent(void *priv, u64 *val)
fmt->h.cmd_id = CMD_ID_QUERY_SWEVENT;
fmt->event_group = data->sw_event_group;
- ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt));
+ ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt),
+ __builtin_return_address(0));
*val = desc.ret[1];
dma_free_coherent(&dma_dev, sizeof(*fmt), fmt, addr);
@@ -291,7 +296,8 @@ static int remoteqdss_scm_filter_swevent(void *priv, u64 val)
fmt->event_group = data->sw_event_group;
fmt->event_mask = (uint32_t)val;
- ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt));
+ ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt),
+ __builtin_return_address(0));
dma_free_coherent(&dma_dev, sizeof(*fmt), fmt, addr);
return ret;
@@ -317,7 +323,8 @@ static int remoteqdss_scm_query_swentity(void *priv, u64 *val)
fmt->h.cmd_id = CMD_ID_QUERY_SWENTITY;
fmt->entity_group = data->sw_entity_group;
- ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt));
+ ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt),
+ __builtin_return_address(0));
*val = desc.ret[1];
dma_free_coherent(&dma_dev, sizeof(*fmt), fmt, addr);
@@ -340,7 +347,8 @@ static int remoteqdss_scm_filter_swentity(void *priv, u64 val)
fmt->entity_group = data->sw_entity_group;
fmt->entity_mask = (uint32_t)val;
- ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt));
+ ret = remoteqdss_do_scm_call(&desc, addr, sizeof(*fmt),
+ __builtin_return_address(0));
dma_free_coherent(&dma_dev, sizeof(*fmt), fmt, addr);
return ret;
diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c
index 5eaf2db32d21..03a1591e5b09 100644
--- a/drivers/soc/qcom/rpm-smd.c
+++ b/drivers/soc/qcom/rpm-smd.c
@@ -795,45 +795,23 @@ static int msm_rpm_read_sleep_ack(void)
{
int ret;
char buf[MAX_ERR_BUFFER_SIZE] = {0};
- uint32_t msg_id;
if (glink_enabled)
ret = msm_rpm_glink_rx_poll(glink_data->glink_handle);
else {
ret = msm_rpm_read_smd_data(buf);
- if (!ret) {
- /*
- * Mimic Glink behavior to ensure that the
- * data is read and the msg is removed from
- * the wait list. We should have gotten here
- * only when there are no drivers waiting on
- * ACKs. msm_rpm_get_entry_from_msg_id()
- * return non-NULL only then.
- */
- msg_id = msm_rpm_get_msg_id_from_ack(buf);
- msm_rpm_process_ack(msg_id, 0);
+ if (!ret)
ret = smd_is_pkt_avail(msm_rpm_data.ch_info);
- }
}
return ret;
}
-static void msm_rpm_flush_noack_messages(void)
-{
- while (!list_empty(&msm_rpm_wait_list)) {
- if (!msm_rpm_read_sleep_ack())
- break;
- }
-}
-
static int msm_rpm_flush_requests(bool print)
{
struct rb_node *t;
int ret;
int count = 0;
- msm_rpm_flush_noack_messages();
-
for (t = rb_first(&tr_root); t; t = rb_next(t)) {
struct slp_buf *s = rb_entry(t, struct slp_buf, node);
@@ -1102,18 +1080,14 @@ static void msm_rpm_notify(void *data, unsigned event)
bool msm_rpm_waiting_for_ack(void)
{
- bool ret = false;
+ bool ret;
unsigned long flags;
- struct msm_rpm_wait_data *elem = NULL;
spin_lock_irqsave(&msm_rpm_list_lock, flags);
- elem = list_first_entry_or_null(&msm_rpm_wait_list,
- struct msm_rpm_wait_data, list);
- if (elem)
- ret = !elem->delete_on_ack;
+ ret = list_empty(&msm_rpm_wait_list);
spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
- return ret;
+ return !ret;
}
static struct msm_rpm_wait_data *msm_rpm_get_entry_from_msg_id(uint32_t msg_id)
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index 81dde8ca1ae8..981f78491ecf 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -386,7 +386,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 +402,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);
@@ -425,7 +426,7 @@ 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 +457,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);
+ 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/socinfo.c b/drivers/soc/qcom/socinfo.c
index 23e32214756a..ea4f557fcd70 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -538,6 +538,9 @@ static struct msm_soc_info cpu_of_id[] = {
/* falcon ID */
[317] = {MSM_CPU_FALCON, "MSMFALCON"},
+ /* triton ID */
+ [318] = {MSM_CPU_TRITON, "MSMTRITON"},
+
/* Uninitialized IDs are not known to run Linux.
MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
considered as unknown CPU. */
@@ -1207,6 +1210,10 @@ static void * __init setup_dummy_socinfo(void)
dummy_socinfo.id = 317;
strlcpy(dummy_socinfo.build_id, "msmfalcon - ",
sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_msmtriton()) {
+ dummy_socinfo.id = 318;
+ strlcpy(dummy_socinfo.build_id, "msmtriton - ",
+ sizeof(dummy_socinfo.build_id));
} else if (early_machine_is_apqcobalt()) {
dummy_socinfo.id = 319;
strlcpy(dummy_socinfo.build_id, "apqcobalt - ",
diff --git a/drivers/soc/qcom/spss_utils.c b/drivers/soc/qcom/spss_utils.c
new file mode 100644
index 000000000000..9f799dfd9003
--- /dev/null
+++ b/drivers/soc/qcom/spss_utils.c
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+
+/*
+ * Secure-Processor-SubSystem (SPSS) utilities.
+ *
+ * This driver provides utilities for the Secure Processor (SP).
+ *
+ * The SP daemon needs to load different SPSS images based on:
+ *
+ * 1. Test/Production key used to sign the SPSS image (read fuse).
+ * 2. SPSS HW version (selected via Device Tree).
+ *
+ */
+
+#define pr_fmt(fmt) "spss_utils [%s]: " fmt, __func__
+
+#include <linux/kernel.h> /* min() */
+#include <linux/module.h> /* MODULE_LICENSE */
+#include <linux/device.h> /* class_create() */
+#include <linux/slab.h> /* kzalloc() */
+#include <linux/fs.h> /* file_operations */
+#include <linux/cdev.h> /* cdev_add() */
+#include <linux/errno.h> /* EINVAL, ETIMEDOUT */
+#include <linux/printk.h> /* pr_err() */
+#include <linux/bitops.h> /* BIT(x) */
+#include <linux/platform_device.h> /* platform_driver_register() */
+#include <linux/of.h> /* of_property_count_strings() */
+#include <linux/io.h> /* ioremap_nocache() */
+
+#include <soc/qcom/subsystem_restart.h>
+
+/* driver name */
+#define DEVICE_NAME "spss-utils"
+
+static bool is_test_fuse_set;
+static const char *test_firmware_name;
+static const char *prod_firmware_name;
+static const char *firmware_name;
+static struct device *spss_dev;
+
+/*==========================================================================*/
+/* Device Sysfs */
+/*==========================================================================*/
+
+static ssize_t firmware_name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+
+ if (!dev || !attr || !buf) {
+ pr_err("invalid param.\n");
+ return -EINVAL;
+ }
+
+ if (firmware_name == NULL)
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", "unknown");
+ else
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", firmware_name);
+
+ return ret;
+}
+
+static ssize_t firmware_name_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ pr_err("set firmware name is not allowed.\n");
+
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(firmware_name, 0444,
+ firmware_name_show, firmware_name_store);
+
+static ssize_t test_fuse_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+
+ if (!dev || !attr || !buf) {
+ pr_err("invalid param.\n");
+ return -EINVAL;
+ }
+
+ if (is_test_fuse_set)
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", "test");
+ else
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", "prod");
+
+ return ret;
+}
+
+static ssize_t test_fuse_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ pr_err("set test fuse state is not allowed.\n");
+
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(test_fuse_state, 0444,
+ test_fuse_state_show, test_fuse_state_store);
+
+static int spss_create_sysfs(struct device *dev)
+{
+ int ret;
+
+ ret = device_create_file(dev, &dev_attr_firmware_name);
+ if (ret < 0) {
+ pr_err("failed to create sysfs file for firmware_name.\n");
+ return ret;
+ }
+
+ ret = device_create_file(dev, &dev_attr_test_fuse_state);
+ if (ret < 0) {
+ pr_err("failed to create sysfs file for test_fuse_state.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/*==========================================================================*/
+/* Device Tree */
+/*==========================================================================*/
+
+/**
+ * spss_parse_dt() - Parse Device Tree info.
+ */
+static int spss_parse_dt(struct device_node *node)
+{
+ int ret;
+ u32 spss_fuse_addr = 0;
+ u32 spss_fuse_bit = 0;
+ u32 spss_fuse_mask = 0;
+ void __iomem *spss_fuse_reg = NULL;
+ u32 val = 0;
+
+ ret = of_property_read_string(node, "qcom,spss-test-firmware-name",
+ &test_firmware_name);
+ if (ret < 0) {
+ pr_err("can't get test fw name.\n");
+ return -EFAULT;
+ }
+
+ ret = of_property_read_string(node, "qcom,spss-prod-firmware-name",
+ &prod_firmware_name);
+ if (ret < 0) {
+ pr_err("can't get prod fw name.\n");
+ return -EFAULT;
+ }
+
+ ret = of_property_read_u32(node, "qcom,spss-fuse-addr",
+ &spss_fuse_addr);
+ if (ret < 0) {
+ pr_err("can't get fuse addr.\n");
+ return -EFAULT;
+ }
+
+ ret = of_property_read_u32(node, "qcom,spss-fuse-bit",
+ &spss_fuse_bit);
+ if (ret < 0) {
+ pr_err("can't get fuse bit.\n");
+ return -EFAULT;
+ }
+
+ spss_fuse_mask = BIT(spss_fuse_bit);
+
+ pr_debug("spss_fuse_addr [0x%x] , spss_fuse_bit [%d] .\n",
+ (int) spss_fuse_addr, (int) spss_fuse_bit);
+
+ spss_fuse_reg = ioremap_nocache(spss_fuse_addr, sizeof(u32));
+
+ if (!spss_fuse_reg) {
+ pr_err("can't map fuse addr.\n");
+ return -EFAULT;
+ }
+
+ val = readl_relaxed(spss_fuse_reg);
+
+ pr_debug("spss fuse register value [0x%x].\n", (int) val);
+
+ if (val & spss_fuse_mask)
+ is_test_fuse_set = true;
+
+ iounmap(spss_fuse_reg);
+
+ return 0;
+}
+
+/**
+ * spss_probe() - initialization sequence
+ */
+static int spss_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device_node *np = NULL;
+ struct device *dev = NULL;
+
+ if (!pdev) {
+ pr_err("invalid pdev.\n");
+ return -ENODEV;
+ }
+
+ np = pdev->dev.of_node;
+ if (!np) {
+ pr_err("invalid DT node.\n");
+ return -EINVAL;
+ }
+
+ dev = &pdev->dev;
+ spss_dev = dev;
+
+ if (dev == NULL) {
+ pr_err("invalid dev.\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ ret = spss_parse_dt(np);
+ if (ret < 0) {
+ pr_err("fail to parse device tree.\n");
+ return -EFAULT;
+ }
+
+ if (is_test_fuse_set)
+ firmware_name = test_firmware_name;
+ else
+ firmware_name = prod_firmware_name;
+
+ ret = subsystem_set_fwname("spss", firmware_name);
+ if (ret < 0) {
+ pr_err("fail to set fw name.\n");
+ return -EFAULT;
+ }
+
+ spss_create_sysfs(dev);
+
+ pr_info("Initialization completed ok, firmware_name [%s].\n",
+ firmware_name);
+
+ return 0;
+}
+
+static const struct of_device_id spss_match_table[] = {
+ { .compatible = "qcom,spss-utils", },
+ { },
+};
+
+static struct platform_driver spss_driver = {
+ .probe = spss_probe,
+ .driver = {
+ .name = DEVICE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(spss_match_table),
+ },
+};
+
+/*==========================================================================*/
+/* Driver Init/Exit */
+/*==========================================================================*/
+static int __init spss_init(void)
+{
+ int ret = 0;
+
+ pr_info("spss-utils driver Ver 1.0 12-Sep-2016.\n");
+
+ ret = platform_driver_register(&spss_driver);
+ if (ret)
+ pr_err("register platform driver failed, ret [%d]\n", ret);
+
+ return 0;
+}
+late_initcall(spss_init); /* start after PIL driver */
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Secure Processor Utilities");
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index 6a1a87ead6e4..ae175e176aa3 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]);
}
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index 0ed8a6533e00..6cfb8f7c836c 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -730,6 +730,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,13 +740,34 @@ 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);
notify_each_subsys_device(&subsys, 1, SUBSYS_AFTER_SHUTDOWN, NULL);
}
+int subsystem_set_fwname(const char *name, const char *fw_name)
+{
+ struct subsys_device *subsys;
+
+ if (!name)
+ return -EINVAL;
+
+ if (!fw_name)
+ return -EINVAL;
+
+ subsys = find_subsys(name);
+ if (!subsys)
+ return -EINVAL;
+
+ pr_debug("Changing subsys [%s] fw_name to [%s]\n", name, fw_name);
+ strlcpy(subsys->desc->fw_name, fw_name,
+ sizeof(subsys->desc->fw_name));
+
+ return 0;
+}
+EXPORT_SYMBOL(subsystem_set_fwname);
+
void *__subsystem_get(const char *name, const char *fw_name)
{
struct subsys_device *subsys;
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 65eda2de9586..aa20705b9adc 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;
};
/*
@@ -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/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/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 670eda466438..9fdb06e08d4b 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -35,6 +35,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -197,13 +198,13 @@ struct msm_port {
static
void msm_write(struct uart_port *port, unsigned int val, unsigned int off)
{
- writel_relaxed(val, port->membase + off);
+ writel_relaxed_no_log(val, port->membase + off);
}
static
unsigned int msm_read(struct uart_port *port, unsigned int off)
{
- return readl_relaxed(port->membase + off);
+ return readl_relaxed_no_log(port->membase + off);
}
/*
@@ -403,8 +404,12 @@ static void msm_stop_tx(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
msm_port->imr &= ~UART_IMR_TXLEV;
msm_write(port, msm_port->imr, UART_IMR);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}
static void msm_start_tx(struct uart_port *port)
@@ -416,8 +421,12 @@ static void msm_start_tx(struct uart_port *port)
if (dma->count)
return;
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
msm_port->imr |= UART_IMR_TXLEV;
msm_write(port, msm_port->imr, UART_IMR);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}
static void msm_reset_dm_count(struct uart_port *port, int count)
@@ -439,6 +448,8 @@ static void msm_complete_tx_dma(void *args)
unsigned int count;
u32 val;
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
spin_lock_irqsave(&port->lock, flags);
/* Already stopped */
@@ -475,6 +486,8 @@ static void msm_complete_tx_dma(void *args)
msm_handle_tx(port);
done:
spin_unlock_irqrestore(&port->lock, flags);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}
static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
@@ -547,6 +560,8 @@ static void msm_complete_rx_dma(void *args)
unsigned long flags;
u32 val;
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
spin_lock_irqsave(&port->lock, flags);
/* Already stopped */
@@ -598,6 +613,8 @@ done:
if (count)
tty_flip_buffer_push(tport);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}
static void msm_start_rx_dma(struct msm_port *msm_port)
@@ -672,19 +689,28 @@ static void msm_stop_rx(struct uart_port *port)
struct msm_port *msm_port = UART_TO_MSM(port);
struct msm_dma *dma = &msm_port->rx_dma;
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE);
msm_write(port, msm_port->imr, UART_IMR);
if (dma->chan)
msm_stop_dma(port, dma);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}
static void msm_enable_ms(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
+
msm_port->imr |= UART_IMR_DELTA_CTS;
msm_write(port, msm_port->imr, UART_IMR);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}
static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr)
@@ -931,6 +957,8 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id)
unsigned int misr;
u32 val;
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return IRQ_NONE;
spin_lock_irqsave(&port->lock, flags);
misr = msm_read(port, UART_MISR);
msm_write(port, 0, UART_IMR); /* disable interrupt */
@@ -964,13 +992,25 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id)
msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */
spin_unlock_irqrestore(&port->lock, flags);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
return IRQ_HANDLED;
}
static unsigned int msm_tx_empty(struct uart_port *port)
{
- return (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0;
+ int ret;
+
+ ret = pm_runtime_get_sync(port->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = msm_read(port, UART_SR) & UART_SR_TX_EMPTY ? TIOCSER_TEMT : 0;
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+
+ return ret;
}
static unsigned int msm_get_mctrl(struct uart_port *port)
@@ -999,6 +1039,8 @@ static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned int mr;
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
mr = msm_read(port, UART_MR1);
if (!(mctrl & TIOCM_RTS)) {
@@ -1009,14 +1051,20 @@ static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
mr |= UART_MR1_RX_RDY_CTL;
msm_write(port, mr, UART_MR1);
}
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}
static void msm_break_ctl(struct uart_port *port, int break_ctl)
{
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
if (break_ctl)
msm_write(port, UART_CR_CMD_START_BREAK, UART_CR);
else
msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}
struct msm_baud_map {
@@ -1157,15 +1205,6 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud,
return baud;
}
-static void msm_init_clock(struct uart_port *port)
-{
- struct msm_port *msm_port = UART_TO_MSM(port);
-
- clk_prepare_enable(msm_port->clk);
- clk_prepare_enable(msm_port->pclk);
- msm_serial_set_mnd_regs(port);
-}
-
static int msm_startup(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
@@ -1180,7 +1219,22 @@ static int msm_startup(struct uart_port *port)
if (unlikely(ret))
return ret;
- msm_init_clock(port);
+ /*
+ * UART clk must be kept enabled to
+ * avoid losing received character
+ */
+ ret = clk_prepare_enable(msm_port->clk);
+ if (ret)
+ return ret;
+ ret = clk_prepare(msm_port->pclk);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(port->dev);
+ if (ret < 0)
+ goto err;
+
+ msm_serial_set_mnd_regs(port);
if (likely(port->fifosize > 12))
rfr_level = port->fifosize - 12;
@@ -1206,19 +1260,34 @@ static int msm_startup(struct uart_port *port)
msm_request_rx_dma(msm_port, msm_port->uart.mapbase);
}
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+
return 0;
+
+err:
+ clk_unprepare(msm_port->pclk);
+ clk_disable_unprepare(msm_port->clk);
+ free_irq(port->irq, port);
+ return ret;
}
static void msm_shutdown(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
+
msm_port->imr = 0;
msm_write(port, 0, UART_IMR); /* disable interrupts */
if (msm_port->is_uartdm)
msm_release_dma(msm_port);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+ clk_unprepare(msm_port->pclk);
clk_disable_unprepare(msm_port->clk);
free_irq(port->irq, port);
@@ -1232,6 +1301,8 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned long flags;
unsigned int baud, mr;
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
spin_lock_irqsave(&port->lock, flags);
if (dma->chan) /* Terminate if any */
@@ -1305,6 +1376,8 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
msm_start_rx_dma(msm_port);
spin_unlock_irqrestore(&port->lock, flags);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}
static const char *msm_type(struct uart_port *port)
@@ -1385,12 +1458,26 @@ static void msm_power(struct uart_port *port, unsigned int state,
switch (state) {
case 0:
- clk_prepare_enable(msm_port->clk);
- clk_prepare_enable(msm_port->pclk);
+ /*
+ * UART clk must be kept enabled to
+ * avoid losing received character
+ */
+ if (clk_prepare_enable(msm_port->clk))
+ return;
+ if (clk_prepare(msm_port->pclk)) {
+ clk_disable_unprepare(msm_port->clk);
+ return;
+ }
+ if (pm_runtime_get_sync(port->dev) < 0) {
+ clk_unprepare(msm_port->pclk);
+ clk_disable_unprepare(msm_port->clk);
+ return;
+ }
break;
case 3:
+ pm_runtime_put(port->dev);
+ clk_unprepare(msm_port->pclk);
clk_disable_unprepare(msm_port->clk);
- clk_disable_unprepare(msm_port->pclk);
break;
default:
pr_err("msm_serial: Unknown PM state %d\n", state);
@@ -1632,7 +1719,11 @@ static void msm_console_write(struct console *co, const char *s,
port = msm_get_port_from_line(co->index);
msm_port = UART_TO_MSM(port);
+ if (pm_runtime_get_sync(port->dev) < 0)
+ return;
__msm_console_write(port, s, count, msm_port->is_uartdm);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}
static int __init msm_console_setup(struct console *co, char *options)
@@ -1651,7 +1742,7 @@ static int __init msm_console_setup(struct console *co, char *options)
if (unlikely(!port->membase))
return -ENXIO;
- msm_init_clock(port);
+ msm_serial_set_mnd_regs(port);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1794,6 +1885,12 @@ static int msm_serial_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, port);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
+ pm_runtime_irq_safe(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+
return uart_add_one_port(&msm_uart_driver, port);
}
@@ -1802,6 +1899,7 @@ static int msm_serial_remove(struct platform_device *pdev)
struct uart_port *port = platform_get_drvdata(pdev);
uart_remove_one_port(&msm_uart_driver, port);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
@@ -1812,12 +1910,67 @@ static const struct of_device_id msm_match_table[] = {
{}
};
+#ifdef CONFIG_PM
+static int msm_serial_runtime_suspend(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+ struct msm_port *msm_port = UART_TO_MSM(port);
+
+ if (msm_port->is_uartdm)
+ clk_disable(msm_port->pclk);
+
+ return 0;
+}
+
+static int msm_serial_runtime_resume(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+ struct msm_port *msm_port = UART_TO_MSM(port);
+ int ret;
+
+ if (msm_port->is_uartdm) {
+ ret = clk_enable(msm_port->pclk);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_serial_suspend(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+
+ uart_suspend_port(&msm_uart_driver, port);
+
+ return 0;
+}
+
+static int msm_serial_resume(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+
+ uart_resume_port(&msm_uart_driver, port);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops msm_serial_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(msm_serial_suspend, msm_serial_resume)
+ SET_RUNTIME_PM_OPS(msm_serial_runtime_suspend,
+ msm_serial_runtime_resume, NULL)
+};
+
static struct platform_driver msm_platform_driver = {
.remove = msm_serial_remove,
.probe = msm_serial_probe,
.driver = {
.name = "msm_serial",
.of_match_table = msm_match_table,
+ .pm = &msm_serial_pm_ops,
},
};
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 38ae877c46e3..3ffb01ff6549 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1203,10 +1203,11 @@ static int proc_getdriver(struct usb_dev_state *ps, void __user *arg)
static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)
{
- struct usbdevfs_connectinfo ci = {
- .devnum = ps->dev->devnum,
- .slow = ps->dev->speed == USB_SPEED_LOW
- };
+ struct usbdevfs_connectinfo ci;
+
+ memset(&ci, 0, sizeof(ci));
+ ci.devnum = ps->dev->devnum;
+ ci.slow = ps->dev->speed == USB_SPEED_LOW;
if (copy_to_user(arg, &ci, sizeof(ci)))
return -EFAULT;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 2c26ae1e3eb7..1fb5ce9caf98 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1006,6 +1006,7 @@ struct dwc3 {
/* IRQ timing statistics */
int irq;
unsigned long irq_cnt;
+ unsigned long ep_cmd_timeout_cnt;
unsigned bh_completion_time[MAX_INTR_STATS];
unsigned bh_handled_evt_cnt[MAX_INTR_STATS];
unsigned bh_dbg_index;
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 37a3c954a1dd..83a265f0211e 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -45,6 +45,7 @@
#include <linux/msm-bus.h>
#include <linux/irq.h>
#include <linux/extcon.h>
+#include <linux/reset.h>
#include "power.h"
#include "core.h"
@@ -159,6 +160,7 @@ struct dwc3_msm {
struct clk *utmi_clk_src;
struct clk *bus_aggr_clk;
struct clk *cfg_ahb_clk;
+ struct reset_control *core_reset;
struct regulator *dwc3_gdsc;
struct usb_phy *hs_phy, *ss_phy;
@@ -1517,19 +1519,19 @@ static int dwc3_msm_link_clk_reset(struct dwc3_msm *mdwc, bool assert)
clk_disable_unprepare(mdwc->sleep_clk);
clk_disable_unprepare(mdwc->core_clk);
clk_disable_unprepare(mdwc->iface_clk);
- ret = clk_reset(mdwc->core_clk, CLK_RESET_ASSERT);
+ ret = reset_control_assert(mdwc->core_reset);
if (ret)
- dev_err(mdwc->dev, "dwc3 core_clk assert failed\n");
+ dev_err(mdwc->dev, "dwc3 core_reset assert failed\n");
} else {
dev_dbg(mdwc->dev, "block_reset DEASSERT\n");
- ret = clk_reset(mdwc->core_clk, CLK_RESET_DEASSERT);
+ ret = reset_control_deassert(mdwc->core_reset);
+ if (ret)
+ dev_err(mdwc->dev, "dwc3 core_reset deassert failed\n");
ndelay(200);
clk_prepare_enable(mdwc->iface_clk);
clk_prepare_enable(mdwc->core_clk);
clk_prepare_enable(mdwc->sleep_clk);
clk_prepare_enable(mdwc->utmi_clk);
- if (ret)
- dev_err(mdwc->dev, "dwc3 core_clk deassert failed\n");
enable_irq(mdwc->pwr_event_irq);
}
@@ -1694,7 +1696,7 @@ static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned event)
break;
case DWC3_CONTROLLER_RESTART_USB_SESSION:
dev_dbg(mdwc->dev, "DWC3_CONTROLLER_RESTART_USB_SESSION received\n");
- dwc3_restart_usb_work(&mdwc->restart_usb_work);
+ schedule_work(&mdwc->restart_usb_work);
break;
default:
dev_dbg(mdwc->dev, "unknown dwc3 event\n");
@@ -2041,11 +2043,16 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
if (mdwc->lpm_flags & MDWC3_POWER_COLLAPSE) {
dev_dbg(mdwc->dev, "%s: exit power collapse\n", __func__);
dwc3_msm_config_gdsc(mdwc, 1);
-
- clk_reset(mdwc->core_clk, CLK_RESET_ASSERT);
+ ret = reset_control_assert(mdwc->core_reset);
+ if (ret)
+ dev_err(mdwc->dev, "%s:core_reset assert failed\n",
+ __func__);
/* HW requires a short delay for reset to take place properly */
usleep_range(1000, 1200);
- clk_reset(mdwc->core_clk, CLK_RESET_DEASSERT);
+ ret = reset_control_deassert(mdwc->core_reset);
+ if (ret)
+ dev_err(mdwc->dev, "%s:core_reset deassert failed\n",
+ __func__);
clk_prepare_enable(mdwc->sleep_clk);
}
@@ -2366,6 +2373,17 @@ static int dwc3_msm_get_clk_gdsc(struct dwc3_msm *mdwc)
mdwc->core_clk_rate = clk_round_rate(mdwc->core_clk, LONG_MAX);
}
+ 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 {
@@ -3191,7 +3209,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on)
static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA)
{
- union power_supply_propval pval = {1000 * mA};
+ union power_supply_propval pval = {0};
int ret;
if (mdwc->charging_disabled)
@@ -3208,9 +3226,14 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA)
}
}
+ power_supply_get_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPE, &pval);
+ if (pval.intval != POWER_SUPPLY_TYPE_USB)
+ return 0;
+
dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);
/* Set max current limit in uA */
+ pval.intval = 1000 * mA;
ret = power_supply_set_property(mdwc->usb_psy,
POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
if (ret) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index acaa99615d33..4ad994972b19 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -340,7 +340,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
{
struct dwc3_ep *dep = dwc->eps[ep];
- u32 timeout = 1500;
+ u32 timeout = 3000;
u32 reg;
trace_dwc3_gadget_ep_cmd(dep, cmd, params);
@@ -380,6 +380,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
"Command Timed Out");
dev_err(dwc->dev, "%s command timeout for %s\n",
dwc3_gadget_ep_cmd_string(cmd), dep->name);
+ if (!(cmd & DWC3_DEPCMD_ENDTRANSFER)) {
+ dwc->ep_cmd_timeout_cnt++;
+ dwc3_notify_event(dwc,
+ DWC3_CONTROLLER_RESTART_USB_SESSION);
+ }
return -ETIMEDOUT;
}
@@ -534,7 +539,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
dep->stream_capable = true;
}
- if (!usb_endpoint_xfer_control(desc))
+ if (usb_endpoint_xfer_isoc(desc))
params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN;
/*
@@ -886,11 +891,19 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
else
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
+
+ if (!req->request.no_interrupt && !chain)
+ trb->ctrl |= DWC3_TRB_CTRL_IOC;
break;
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
trb->ctrl = DWC3_TRBCTL_NORMAL;
+ if (req->request.num_mapped_sgs > 0) {
+ if (!last && !chain &&
+ !req->request.no_interrupt)
+ trb->ctrl |= DWC3_TRB_CTRL_IOC;
+ }
break;
default:
/*
@@ -900,9 +913,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
BUG();
}
- if (!req->request.no_interrupt && !chain)
- trb->ctrl |= DWC3_TRB_CTRL_IOC;
-
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
trb->ctrl |= DWC3_TRB_CTRL_CSP;
@@ -1223,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
@@ -2472,6 +2468,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
return 1;
}
+ if ((event->status & DEPEVT_STATUS_IOC) &&
+ (trb->ctrl & DWC3_TRB_CTRL_IOC))
+ return 0;
return 1;
}
@@ -2516,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,
@@ -2559,6 +2550,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
break;
case DWC3_DEPEVT_XFERINPROGRESS:
dep->dbg_ep_events.xferinprogress++;
+ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n",
+ dep->name);
+ return;
+ }
+
dwc3_endpoint_transfer_complete(dwc, dep, event);
break;
case DWC3_DEPEVT_XFERNOTREADY:
@@ -2566,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/composite.c b/drivers/usb/gadget/composite.c
index 3d17fd93c787..a99405261306 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -27,6 +27,14 @@
#define SSUSB_GADGET_VBUS_DRAW_UNITS 8
#define HSUSB_GADGET_VBUS_DRAW_UNITS 2
+/*
+ * Based on enumerated USB speed, draw power with set_config and resume
+ * HSUSB: 500mA, SSUSB: 900mA
+ */
+#define USB_VBUS_DRAW(speed)\
+ (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");
@@ -749,7 +757,6 @@ static int set_config(struct usb_composite_dev *cdev,
struct usb_gadget *gadget = cdev->gadget;
struct usb_configuration *c = NULL;
int result = -EINVAL;
- unsigned power = gadget_is_otg(gadget) ? 8 : 100;
int tmp;
/*
@@ -863,14 +870,8 @@ static int set_config(struct usb_composite_dev *cdev,
}
}
- /* Allow 900mA to draw with Super-Speed */
- if (gadget->speed == USB_SPEED_SUPER)
- power = SSUSB_GADGET_VBUS_DRAW;
- else
- power = CONFIG_USB_GADGET_VBUS_DRAW;
-
done:
- usb_gadget_vbus_draw(gadget, power);
+ usb_gadget_vbus_draw(gadget, USB_VBUS_DRAW(gadget->speed));
if (result >= 0 && cdev->delayed_status)
result = USB_GADGET_DELAYED_STATUS;
return result;
@@ -2319,7 +2320,6 @@ void composite_resume(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
- u16 maxpower;
int ret;
unsigned long flags;
@@ -2352,10 +2352,7 @@ void composite_resume(struct usb_gadget *gadget)
f->resume(f);
}
- maxpower = cdev->config->MaxPower;
-
- usb_gadget_vbus_draw(gadget, maxpower ?
- maxpower : CONFIG_USB_GADGET_VBUS_DRAW);
+ usb_gadget_vbus_draw(gadget, USB_VBUS_DRAW(gadget->speed));
}
spin_unlock_irqrestore(&cdev->lock, flags);
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index 908cfb33bf99..bc1c34f05fa3 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -448,12 +448,19 @@ static void acc_hid_close(struct hid_device *hid)
{
}
+static int acc_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype, int reqtype)
+{
+ return 0;
+}
+
static struct hid_ll_driver acc_hid_ll_driver = {
.parse = acc_hid_parse,
.start = acc_hid_start,
.stop = acc_hid_stop,
.open = acc_hid_open,
.close = acc_hid_close,
+ .raw_request = acc_hid_raw_request,
};
static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev,
@@ -609,8 +616,7 @@ static ssize_t acc_read(struct file *fp, char __user *buf,
{
struct acc_dev *dev = fp->private_data;
struct usb_request *req;
- ssize_t r = count;
- unsigned xfer;
+ ssize_t r = count, xfer, len;
int ret = 0;
pr_debug("acc_read(%zu)\n", count);
@@ -631,6 +637,8 @@ static ssize_t acc_read(struct file *fp, char __user *buf,
goto done;
}
+ len = ALIGN(count, dev->ep_out->maxpacket);
+
if (dev->rx_done) {
// last req cancelled. try to get it.
req = dev->rx_req[0];
@@ -640,7 +648,7 @@ static ssize_t acc_read(struct file *fp, char __user *buf,
requeue_req:
/* queue a request */
req = dev->rx_req[0];
- req->length = count;
+ req->length = len;
dev->rx_done = 0;
ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
if (ret < 0) {
@@ -936,6 +944,8 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev,
memset(dev->serial, 0, sizeof(dev->serial));
dev->start_requested = 0;
dev->audio_mode = 0;
+ strlcpy(dev->manufacturer, "Android", ACC_STRING_SIZE);
+ strlcpy(dev->model, "Android", ACC_STRING_SIZE);
}
}
@@ -1239,13 +1249,13 @@ static int acc_setup(void)
INIT_DELAYED_WORK(&dev->start_work, acc_start_work);
INIT_WORK(&dev->hid_work, acc_hid_work);
- /* _acc_dev must be set before calling usb_gadget_register_driver */
- _acc_dev = dev;
-
ret = misc_register(&acc_device);
if (ret)
goto err;
+ /* _acc_dev must be set before calling usb_gadget_register_driver */
+ _acc_dev = dev;
+
return 0;
err:
diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c
index 39645be93502..bcd817439dbf 100644
--- a/drivers/usb/gadget/function/f_audio_source.c
+++ b/drivers/usb/gadget/function/f_audio_source.c
@@ -583,6 +583,11 @@ static void audio_disable(struct usb_function *f)
usb_ep_disable(audio->in_ep);
}
+static void audio_free_func(struct usb_function *f)
+{
+ /* no-op */
+}
+
/*-------------------------------------------------------------------------*/
static void audio_build_desc(struct audio_dev *audio)
@@ -827,6 +832,7 @@ static struct audio_dev _audio_dev = {
.set_alt = audio_set_alt,
.setup = audio_setup,
.disable = audio_disable,
+ .free_func = audio_free_func,
},
.lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock),
.idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs),
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index e32348d17b26..c298c95d4ba0 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -36,6 +36,7 @@ MODULE_PARM_DESC(num_out_bufs,
static struct workqueue_struct *ipa_usb_wq;
+static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis);
static void ipa_disconnect_handler(struct gsi_data_port *d_port);
static int gsi_ctrl_send_notification(struct f_gsi *gsi,
enum gsi_ctrl_notify_state);
@@ -553,6 +554,7 @@ static void ipa_work_handler(struct work_struct *w)
struct device *dev;
struct device *gad_dev;
struct f_gsi *gsi;
+ bool block_db;
event = read_event(d_port);
@@ -665,7 +667,27 @@ static void ipa_work_handler(struct work_struct *w)
}
break;
case STATE_CONNECTED:
- if (event == EVT_DISCONNECTED) {
+ if (event == EVT_DISCONNECTED || event == EVT_HOST_NRDY) {
+ if (peek_event(d_port) == EVT_HOST_READY) {
+ read_event(d_port);
+ log_event_dbg("%s: NO_OP NRDY_RDY", __func__);
+ break;
+ }
+
+ if (event == EVT_HOST_NRDY) {
+ log_event_dbg("%s: ST_CON_HOST_NRDY\n",
+ __func__);
+ block_db = true;
+ /* stop USB ringing doorbell to GSI(OUT_EP) */
+ usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
+ GSI_EP_OP_SET_CLR_BLOCK_DBL);
+ gsi_rndis_ipa_reset_trigger(gsi);
+ usb_gsi_ep_op(d_port->in_ep, NULL,
+ GSI_EP_OP_ENDXFER);
+ usb_gsi_ep_op(d_port->out_ep, NULL,
+ GSI_EP_OP_ENDXFER);
+ }
+
ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED;
usb_gadget_autopm_put_async(d_port->gadget);
@@ -1269,7 +1291,7 @@ static void gsi_rndis_open(struct f_gsi *rndis)
rndis_signal_connect(rndis->params);
}
-void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis)
+static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis)
{
unsigned long flags;
@@ -1301,12 +1323,11 @@ void gsi_rndis_flow_ctrl_enable(bool enable, struct rndis_params *param)
d_port = &rndis->d_port;
- if (enable) {
- gsi_rndis_ipa_reset_trigger(rndis);
- usb_gsi_ep_op(d_port->in_ep, NULL, GSI_EP_OP_ENDXFER);
- usb_gsi_ep_op(d_port->out_ep, NULL, GSI_EP_OP_ENDXFER);
- post_event(d_port, EVT_DISCONNECTED);
+ if (enable) {
+ log_event_dbg("%s: posting HOST_NRDY\n", __func__);
+ post_event(d_port, EVT_HOST_NRDY);
} else {
+ log_event_dbg("%s: posting HOST_READY\n", __func__);
post_event(d_port, EVT_HOST_READY);
}
@@ -1937,14 +1958,14 @@ static int gsi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (gsi->prot_id == IPA_USB_ECM)
gsi->d_port.cdc_filter = DEFAULT_FILTER;
- post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS);
/*
* For RNDIS the event is posted from the flow control
* handler which is invoked when the host sends the
* GEN_CURRENT_PACKET_FILTER message.
*/
if (gsi->prot_id != IPA_USB_RNDIS)
- post_event(&gsi->d_port, EVT_HOST_READY);
+ post_event(&gsi->d_port,
+ EVT_CONNECT_IN_PROGRESS);
queue_work(gsi->d_port.ipa_usb_wq,
&gsi->d_port.usb_ipa_w);
}
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index 3b3b1b011407..aa186781ef22 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -158,7 +158,7 @@ static struct usb_interface_descriptor ptp_interface_desc = {
.bInterfaceProtocol = 1,
};
-static struct usb_endpoint_descriptor mtp_superspeed_in_desc = {
+static struct usb_endpoint_descriptor mtp_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
@@ -166,8 +166,8 @@ static struct usb_endpoint_descriptor mtp_superspeed_in_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-static struct usb_ss_ep_comp_descriptor mtp_superspeed_in_comp_desc = {
- .bLength = sizeof(mtp_superspeed_in_comp_desc),
+static struct usb_ss_ep_comp_descriptor mtp_ss_in_comp_desc = {
+ .bLength = sizeof(mtp_ss_in_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
@@ -175,7 +175,8 @@ static struct usb_ss_ep_comp_descriptor mtp_superspeed_in_comp_desc = {
/* .bmAttributes = 0, */
};
-static struct usb_endpoint_descriptor mtp_superspeed_out_desc = {
+
+static struct usb_endpoint_descriptor mtp_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
@@ -183,8 +184,8 @@ static struct usb_endpoint_descriptor mtp_superspeed_out_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-static struct usb_ss_ep_comp_descriptor mtp_superspeed_out_comp_desc = {
- .bLength = sizeof(mtp_superspeed_out_comp_desc),
+static struct usb_ss_ep_comp_descriptor mtp_ss_out_comp_desc = {
+ .bLength = sizeof(mtp_ss_out_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
@@ -231,8 +232,8 @@ static struct usb_endpoint_descriptor mtp_intr_desc = {
.bInterval = 6,
};
-static struct usb_ss_ep_comp_descriptor mtp_superspeed_intr_comp_desc = {
- .bLength = sizeof(mtp_superspeed_intr_comp_desc),
+static struct usb_ss_ep_comp_descriptor mtp_intr_ss_comp_desc = {
+ .bLength = sizeof(mtp_intr_ss_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 3 values can be tweaked if necessary */
@@ -259,12 +260,12 @@ static struct usb_descriptor_header *hs_mtp_descs[] = {
static struct usb_descriptor_header *ss_mtp_descs[] = {
(struct usb_descriptor_header *) &mtp_interface_desc,
- (struct usb_descriptor_header *) &mtp_superspeed_in_desc,
- (struct usb_descriptor_header *) &mtp_superspeed_in_comp_desc,
- (struct usb_descriptor_header *) &mtp_superspeed_out_desc,
- (struct usb_descriptor_header *) &mtp_superspeed_out_comp_desc,
+ (struct usb_descriptor_header *) &mtp_ss_in_desc,
+ (struct usb_descriptor_header *) &mtp_ss_in_comp_desc,
+ (struct usb_descriptor_header *) &mtp_ss_out_desc,
+ (struct usb_descriptor_header *) &mtp_ss_out_comp_desc,
(struct usb_descriptor_header *) &mtp_intr_desc,
- (struct usb_descriptor_header *) &mtp_superspeed_intr_comp_desc,
+ (struct usb_descriptor_header *) &mtp_intr_ss_comp_desc,
NULL,
};
@@ -286,12 +287,12 @@ static struct usb_descriptor_header *hs_ptp_descs[] = {
static struct usb_descriptor_header *ss_ptp_descs[] = {
(struct usb_descriptor_header *) &ptp_interface_desc,
- (struct usb_descriptor_header *) &mtp_superspeed_in_desc,
- (struct usb_descriptor_header *) &mtp_superspeed_in_comp_desc,
- (struct usb_descriptor_header *) &mtp_superspeed_out_desc,
- (struct usb_descriptor_header *) &mtp_superspeed_out_comp_desc,
+ (struct usb_descriptor_header *) &mtp_ss_in_desc,
+ (struct usb_descriptor_header *) &mtp_ss_in_comp_desc,
+ (struct usb_descriptor_header *) &mtp_ss_out_desc,
+ (struct usb_descriptor_header *) &mtp_ss_out_comp_desc,
(struct usb_descriptor_header *) &mtp_intr_desc,
- (struct usb_descriptor_header *) &mtp_superspeed_intr_comp_desc,
+ (struct usb_descriptor_header *) &mtp_intr_ss_comp_desc,
NULL,
};
@@ -352,7 +353,7 @@ struct ext_mtp_desc mtp_ext_config_desc = {
.dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)),
.bcdVersion = __constant_cpu_to_le16(0x0100),
.wIndex = __constant_cpu_to_le16(4),
- .bCount = __constant_cpu_to_le16(1),
+ .bCount = 1,
},
.function = {
.bFirstInterfaceNumber = 0,
@@ -395,6 +396,8 @@ struct mtp_instance {
struct usb_function_instance func_inst;
const char *name;
struct mtp_dev *dev;
+ char mtp_ext_compat_id[16];
+ struct usb_os_desc mtp_os_desc;
};
/* temporary variable used between mtp_open() and mtp_gadget_bind() */
@@ -1389,6 +1392,7 @@ mtp_function_bind(struct usb_configuration *c, struct usb_function *f)
struct mtp_dev *dev = func_to_mtp(f);
int id;
int ret;
+ struct mtp_instance *fi_mtp;
dev->cdev = cdev;
DBG(cdev, "mtp_function_bind dev: %p\n", dev);
@@ -1406,6 +1410,18 @@ mtp_function_bind(struct usb_configuration *c, struct usb_function *f)
mtp_string_defs[INTERFACE_STRING_INDEX].id = ret;
mtp_interface_desc.iInterface = ret;
}
+
+ fi_mtp = container_of(f->fi, struct mtp_instance, func_inst);
+
+ if (cdev->use_os_string) {
+ f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
+ GFP_KERNEL);
+ if (!f->os_desc_table)
+ return -ENOMEM;
+ f->os_desc_n = 1;
+ f->os_desc_table[0].os_desc = &fi_mtp->mtp_os_desc;
+ }
+
/* allocate endpoints */
ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc,
&mtp_fullspeed_out_desc, &mtp_intr_desc);
@@ -1419,18 +1435,32 @@ mtp_function_bind(struct usb_configuration *c, struct usb_function *f)
mtp_highspeed_out_desc.bEndpointAddress =
mtp_fullspeed_out_desc.bEndpointAddress;
}
+ /* support super speed hardware */
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ unsigned max_burst;
+
+ /* Calculate bMaxBurst, we know packet size is 1024 */
+ max_burst = min_t(unsigned, MTP_BULK_BUFFER_SIZE / 1024, 15);
+ mtp_ss_in_desc.bEndpointAddress =
+ mtp_fullspeed_in_desc.bEndpointAddress;
+ mtp_ss_in_comp_desc.bMaxBurst = max_burst;
+ mtp_ss_out_desc.bEndpointAddress =
+ mtp_fullspeed_out_desc.bEndpointAddress;
+ mtp_ss_out_comp_desc.bMaxBurst = max_burst;
+ }
/* support super speed hardware */
if (gadget_is_superspeed(c->cdev->gadget)) {
- mtp_superspeed_in_desc.bEndpointAddress =
+ mtp_ss_in_desc.bEndpointAddress =
mtp_fullspeed_in_desc.bEndpointAddress;
- mtp_superspeed_out_desc.bEndpointAddress =
+ mtp_ss_out_desc.bEndpointAddress =
mtp_fullspeed_out_desc.bEndpointAddress;
}
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
- f->name, dev->ep_in->name, dev->ep_out->name);
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
+ (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full"),
+ f->name, dev->ep_in->name, dev->ep_out->name);
return 0;
}
@@ -1449,6 +1479,8 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
mtp_request_free(req, dev->ep_intr);
dev->state = STATE_OFFLINE;
dev->is_ptp = false;
+ kfree(f->os_desc_table);
+ f->os_desc_n = 0;
}
static int mtp_function_set_alt(struct usb_function *f,
@@ -1748,6 +1780,7 @@ static void mtp_free_inst(struct usb_function_instance *fi)
fi_mtp = to_fi_mtp(fi);
kfree(fi_mtp->name);
mtp_cleanup();
+ kfree(fi_mtp->mtp_os_desc.group.default_groups);
kfree(fi_mtp);
}
@@ -1755,6 +1788,8 @@ struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config)
{
struct mtp_instance *fi_mtp;
int ret = 0;
+ struct usb_os_desc *descs[1];
+ char *names[1];
fi_mtp = kzalloc(sizeof(*fi_mtp), GFP_KERNEL);
if (!fi_mtp)
@@ -1762,6 +1797,13 @@ struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config)
fi_mtp->func_inst.set_inst_name = mtp_set_inst_name;
fi_mtp->func_inst.free_func_inst = mtp_free_inst;
+ fi_mtp->mtp_os_desc.ext_compat_id = fi_mtp->mtp_ext_compat_id;
+ INIT_LIST_HEAD(&fi_mtp->mtp_os_desc.ext_prop);
+ descs[0] = &fi_mtp->mtp_os_desc;
+ names[0] = "MTP";
+ usb_os_desc_prepare_interf_dir(&fi_mtp->func_inst.group, 1,
+ descs, names, THIS_MODULE);
+
if (mtp_config) {
ret = mtp_setup_configfs(fi_mtp);
if (ret) {
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 76b25445c6ad..dd73dfe5dcab 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -1014,6 +1014,7 @@ struct net_device *gether_setup_name_default(const char *netname)
spin_lock_init(&dev->lock);
spin_lock_init(&dev->req_lock);
INIT_WORK(&dev->work, eth_work);
+ INIT_WORK(&dev->rx_work, process_rx_w);
INIT_LIST_HEAD(&dev->tx_reqs);
INIT_LIST_HEAD(&dev->rx_reqs);
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index b011efe189e7..1bb7082be8e6 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -1257,8 +1257,11 @@ static void usbpd_sm(struct work_struct *w)
POWER_SUPPLY_PROP_PD_ACTIVE, &val);
if (pd->current_pr == PR_SRC) {
- regulator_disable(pd->vconn);
regulator_disable(pd->vbus);
+ if (pd->vconn_enabled) {
+ regulator_disable(pd->vconn);
+ pd->vconn_enabled = false;
+ }
}
if (pd->current_dr == DR_UFP)
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index 23a4ac11af36..4ecdc350fbd4 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -27,6 +27,7 @@
#include <linux/regulator/machine.h>
#include <linux/usb/phy.h>
#include <linux/usb/msm_hsusb.h>
+#include <linux/reset.h>
#define QUSB2PHY_PWR_CTRL1 0x210
#define PWR_CTRL1_POWR_DOWN BIT(0)
@@ -34,12 +35,8 @@
#define QUSB2PHY_PLL_COMMON_STATUS_ONE 0x1A0
#define CORE_READY_STATUS BIT(0)
-/* In case Efuse register shows zero, use this value */
-#define TUNE2_DEFAULT_HIGH_NIBBLE 0xB
-#define TUNE2_DEFAULT_LOW_NIBBLE 0x3
-
-/* Get TUNE2's high nibble value read from efuse */
-#define TUNE2_HIGH_NIBBLE_VAL(val, pos, mask) ((val >> pos) & mask)
+/* Get TUNE value from efuse bit-mask */
+#define TUNE_VAL_MASK(val, pos, mask) ((val >> pos) & mask)
#define QUSB2PHY_INTR_CTRL 0x22C
#define DMSE_INTR_HIGH_SEL BIT(4)
@@ -52,7 +49,7 @@
#define DMSE_INTERRUPT BIT(1)
#define DPSE_INTERRUPT BIT(0)
-#define QUSB2PHY_PORT_TUNE2 0x240
+#define QUSB2PHY_PORT_TUNE1 0x23c
#define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */
#define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */
@@ -65,19 +62,22 @@
#define LINESTATE_DP BIT(0)
#define LINESTATE_DM BIT(1)
-unsigned int phy_tune2;
-module_param(phy_tune2, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2");
+#define QUSB2PHY_PLL_ANALOG_CONTROLS_ONE 0x0
+
+unsigned int phy_tune1;
+module_param(phy_tune1, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(phy_tune1, "QUSB PHY v2 TUNE1");
struct qusb_phy {
struct usb_phy phy;
void __iomem *base;
- void __iomem *tune2_efuse_reg;
+ void __iomem *efuse_reg;
+ void __iomem *tcsr_clamp_dig_n;
struct clk *ref_clk_src;
struct clk *ref_clk;
struct clk *cfg_ahb_clk;
- struct clk *phy_reset;
+ struct reset_control *phy_reset;
struct regulator *vdd;
struct regulator *vdda33;
@@ -88,9 +88,9 @@ struct qusb_phy {
int host_init_seq_len;
int *qusb_phy_host_init_seq;
- u32 tune2_val;
- int tune2_efuse_bit_pos;
- int tune2_efuse_num_of_bits;
+ u32 tune_val;
+ int efuse_bit_pos;
+ int efuse_num_of_bits;
bool power_enabled;
bool clocks_enabled;
@@ -324,40 +324,34 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value)
return ret;
}
-static void qusb_phy_get_tune2_param(struct qusb_phy *qphy)
+static void qusb_phy_get_tune1_param(struct qusb_phy *qphy)
{
- u8 num_of_bits;
+ u8 reg;
u32 bit_mask = 1;
pr_debug("%s(): num_of_bits:%d bit_pos:%d\n", __func__,
- qphy->tune2_efuse_num_of_bits,
- qphy->tune2_efuse_bit_pos);
+ qphy->efuse_num_of_bits,
+ qphy->efuse_bit_pos);
/* get bit mask based on number of bits to use with efuse reg */
- if (qphy->tune2_efuse_num_of_bits) {
- num_of_bits = qphy->tune2_efuse_num_of_bits;
- bit_mask = (bit_mask << num_of_bits) - 1;
- }
+ bit_mask = (bit_mask << qphy->efuse_num_of_bits) - 1;
/*
- * Read EFUSE register having TUNE2 parameter's high nibble.
- * If efuse register shows value as 0x0, then use default value
- * as 0xB as high nibble. Otherwise use efuse register based
- * value for this purpose.
+ * if efuse reg is updated (i.e non-zero) then use it to program
+ * tune parameters
*/
- qphy->tune2_val = readl_relaxed(qphy->tune2_efuse_reg);
- pr_debug("%s(): bit_mask:%d efuse based tune2 value:%d\n",
- __func__, bit_mask, qphy->tune2_val);
-
- qphy->tune2_val = TUNE2_HIGH_NIBBLE_VAL(qphy->tune2_val,
- qphy->tune2_efuse_bit_pos, bit_mask);
-
- if (!qphy->tune2_val)
- qphy->tune2_val = TUNE2_DEFAULT_HIGH_NIBBLE;
-
- /* Get TUNE2 byte value using high and low nibble value */
- qphy->tune2_val = ((qphy->tune2_val << 0x4) |
- TUNE2_DEFAULT_LOW_NIBBLE);
+ qphy->tune_val = readl_relaxed(qphy->efuse_reg);
+ pr_debug("%s(): bit_mask:%d efuse based tune1 value:%d\n",
+ __func__, bit_mask, qphy->tune_val);
+
+ qphy->tune_val = TUNE_VAL_MASK(qphy->tune_val,
+ qphy->efuse_bit_pos, bit_mask);
+ reg = readb_relaxed(qphy->base + QUSB2PHY_PORT_TUNE1);
+ if (qphy->tune_val) {
+ reg = reg & 0x0f;
+ reg |= (qphy->tune_val << 4);
+ }
+ qphy->tune_val = reg;
}
static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt,
@@ -377,14 +371,19 @@ static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt,
static void qusb_phy_host_init(struct usb_phy *phy)
{
u8 reg;
+ int ret;
struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
dev_dbg(phy->dev, "%s\n", __func__);
/* Perform phy reset */
- clk_reset(qphy->phy_reset, CLK_RESET_ASSERT);
+ ret = reset_control_assert(qphy->phy_reset);
+ if (ret)
+ dev_err(phy->dev, "%s: phy_reset assert failed\n", __func__);
usleep_range(100, 150);
- clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT);
+ ret = reset_control_deassert(qphy->phy_reset);
+ if (ret)
+ dev_err(phy->dev, "%s: phy_reset deassert failed\n", __func__);
qusb_phy_write_seq(qphy->base, qphy->qusb_phy_host_init_seq,
qphy->host_init_seq_len, 0);
@@ -418,9 +417,13 @@ static int qusb_phy_init(struct usb_phy *phy)
qusb_phy_enable_clocks(qphy, true);
/* Perform phy reset */
- clk_reset(qphy->phy_reset, CLK_RESET_ASSERT);
+ ret = reset_control_assert(qphy->phy_reset);
+ if (ret)
+ dev_err(phy->dev, "%s: phy_reset assert failed\n", __func__);
usleep_range(100, 150);
- clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT);
+ ret = reset_control_deassert(qphy->phy_reset);
+ if (ret)
+ dev_err(phy->dev, "%s: phy_reset deassert failed\n", __func__);
if (qphy->emulation) {
if (qphy->emu_init_seq)
@@ -454,27 +457,22 @@ static int qusb_phy_init(struct usb_phy *phy)
if (qphy->qusb_phy_init_seq)
qusb_phy_write_seq(qphy->base, qphy->qusb_phy_init_seq,
qphy->init_seq_len, 0);
- /*
- * Check for EFUSE value only if tune2_efuse_reg is available
- * and try to read EFUSE value only once i.e. not every USB
- * cable connect case.
- */
- if (qphy->tune2_efuse_reg) {
- if (!qphy->tune2_val)
- qusb_phy_get_tune2_param(qphy);
+ if (qphy->efuse_reg) {
+ if (!qphy->tune_val)
+ qusb_phy_get_tune1_param(qphy);
- pr_debug("%s(): Programming TUNE2 parameter as:%x\n", __func__,
- qphy->tune2_val);
- writel_relaxed(qphy->tune2_val,
- qphy->base + QUSB2PHY_PORT_TUNE2);
+ pr_debug("%s(): Programming TUNE1 parameter as:%x\n", __func__,
+ qphy->tune_val);
+ writel_relaxed(qphy->tune_val,
+ qphy->base + QUSB2PHY_PORT_TUNE1);
}
- /* If phy_tune2 modparam set, override tune2 value */
- if (phy_tune2) {
- pr_debug("%s(): (modparam) TUNE2 val:0x%02x\n",
- __func__, phy_tune2);
- writel_relaxed(phy_tune2,
- qphy->base + QUSB2PHY_PORT_TUNE2);
+ /* If phy_tune1 modparam set, override tune1 value */
+ if (phy_tune1) {
+ pr_debug("%s(): (modparam) TUNE1 val:0x%02x\n",
+ __func__, phy_tune1);
+ writel_relaxed(phy_tune1,
+ qphy->base + QUSB2PHY_PORT_TUNE1);
}
/* ensure above writes are completed before re-enabling PHY */
@@ -543,6 +541,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;
+ int ret;
if (qphy->suspended && suspend) {
dev_dbg(phy->dev, "%s: USB PHY is already suspended\n",
@@ -554,6 +553,11 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
/* Bus suspend case */
if (qphy->cable_connected ||
(qphy->phy.flags & PHY_HOST_MODE)) {
+
+ /* enable clock bypass */
+ writel_relaxed(0x90,
+ qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_ONE);
+
/* Disable all interrupts */
writel_relaxed(0x00,
qphy->base + QUSB2PHY_INTR_CTRL);
@@ -584,15 +588,26 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
wmb();
qusb_phy_enable_clocks(qphy, false);
} else { /* Cable disconnect case */
- /* Disable all interrupts */
- writel_relaxed(0x00,
- qphy->base + QUSB2PHY_INTR_CTRL);
-
- /* Put PHY into non-driving mode */
- writel_relaxed(0x23,
- qphy->base + QUSB2PHY_PWR_CTRL1);
- /* Makes sure that above write goes through */
+ 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__);
+
+ /* enable clock bypass */
+ writel_relaxed(0x90,
+ qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_ONE);
+
+ writel_relaxed(0x0, qphy->tcsr_clamp_dig_n);
+ /*
+ * clamp needs asserted before
+ * power/clocks can be turned off
+ */
wmb();
qusb_phy_enable_clocks(qphy, false);
@@ -604,6 +619,11 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
if (qphy->cable_connected ||
(qphy->phy.flags & PHY_HOST_MODE)) {
qusb_phy_enable_clocks(qphy, true);
+
+ /* disable clock bypass */
+ writel_relaxed(0x80,
+ qphy->base + QUSB2PHY_PLL_ANALOG_CONTROLS_ONE);
+
/* Clear all interrupts on resume */
writel_relaxed(0x00,
qphy->base + QUSB2PHY_INTR_CTRL);
@@ -611,6 +631,19 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
/* Makes sure that above write goes through */
wmb();
} else { /* Cable connect case */
+ writel_relaxed(0x1, qphy->tcsr_clamp_dig_n);
+
+ /*
+ * clamp needs de-asserted before
+ * power/clocks can be turned on
+ */
+ wmb();
+
+ 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);
}
@@ -735,23 +768,33 @@ static int qusb_phy_probe(struct platform_device *pdev)
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "tune2_efuse_addr");
+ "tcsr_clamp_dig_n_1p8");
+ if (res) {
+ qphy->tcsr_clamp_dig_n = devm_ioremap_resource(dev, res);
+ if (IS_ERR(qphy->tcsr_clamp_dig_n)) {
+ dev_dbg(dev, "couldn't ioremap tcsr_clamp_dig_n\n");
+ return PTR_ERR(qphy->tcsr_clamp_dig_n);
+ }
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "efuse_addr");
if (res) {
- qphy->tune2_efuse_reg = devm_ioremap_nocache(dev, res->start,
+ qphy->efuse_reg = devm_ioremap_nocache(dev, res->start,
resource_size(res));
- if (!IS_ERR_OR_NULL(qphy->tune2_efuse_reg)) {
+ if (!IS_ERR_OR_NULL(qphy->efuse_reg)) {
ret = of_property_read_u32(dev->of_node,
- "qcom,tune2-efuse-bit-pos",
- &qphy->tune2_efuse_bit_pos);
+ "qcom,efuse-bit-pos",
+ &qphy->efuse_bit_pos);
if (!ret) {
ret = of_property_read_u32(dev->of_node,
- "qcom,tune2-efuse-num-bits",
- &qphy->tune2_efuse_num_of_bits);
+ "qcom,efuse-num-bits",
+ &qphy->efuse_num_of_bits);
}
if (ret) {
dev_err(dev,
- "DT Value for tune2 efuse is invalid.\n");
+ "DT Value for efuse is invalid.\n");
return -EINVAL;
}
}
@@ -779,7 +822,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
}
}
- qphy->phy_reset = devm_clk_get(dev, "phy_reset");
+ qphy->phy_reset = devm_reset_control_get(dev, "phy_reset");
if (IS_ERR(qphy->phy_reset))
return PTR_ERR(qphy->phy_reset);
diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c
index af7ec03314f5..5456b9e8733b 100644
--- a/drivers/usb/phy/phy-msm-qusb.c
+++ b/drivers/usb/phy/phy-msm-qusb.c
@@ -27,6 +27,7 @@
#include <linux/regulator/machine.h>
#include <linux/usb/phy.h>
#include <linux/usb/msm_hsusb.h>
+#include <linux/reset.h>
#define QUSB2PHY_PLL_STATUS 0x38
#define QUSB2PHY_PLL_LOCK BIT(5)
@@ -112,7 +113,7 @@ struct qusb_phy {
struct clk *ref_clk_src;
struct clk *ref_clk;
struct clk *cfg_ahb_clk;
- struct clk *phy_reset;
+ struct reset_control *phy_reset;
struct regulator *vdd;
struct regulator *vdda33;
@@ -443,9 +444,13 @@ static int qusb_phy_init(struct usb_phy *phy)
}
/* Perform phy reset */
- clk_reset(qphy->phy_reset, CLK_RESET_ASSERT);
+ ret = reset_control_assert(qphy->phy_reset);
+ if (ret)
+ dev_err(phy->dev, "%s: phy_reset assert failed\n", __func__);
usleep_range(100, 150);
- clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT);
+ ret = reset_control_deassert(qphy->phy_reset);
+ if (ret)
+ dev_err(phy->dev, "%s: phy_reset deassert failed\n", __func__);
if (qphy->emulation) {
if (qphy->emu_init_seq)
@@ -858,7 +863,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
if (IS_ERR(qphy->cfg_ahb_clk))
return PTR_ERR(qphy->cfg_ahb_clk);
- qphy->phy_reset = devm_clk_get(dev, "phy_reset");
+ qphy->phy_reset = devm_reset_control_get(dev, "phy_reset");
if (IS_ERR(qphy->phy_reset))
return PTR_ERR(qphy->phy_reset);
@@ -1011,8 +1016,11 @@ static int qusb_phy_probe(struct platform_device *pdev)
* not used, there is leakage current seen with QUSB PHY related voltage
* rail. Hence keep QUSB PHY into reset state explicitly here.
*/
- if (hold_phy_reset)
- clk_reset(qphy->phy_reset, CLK_RESET_ASSERT);
+ if (hold_phy_reset) {
+ ret = reset_control_assert(qphy->phy_reset);
+ if (ret)
+ dev_err(dev, "%s:phy_reset assert failed\n", __func__);
+ }
ret = usb_add_phy_dev(&qphy->phy);
if (ret)
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index 529e4ea76a96..fc61e3172d0b 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -25,6 +25,7 @@
#include <linux/usb/msm_hsusb.h>
#include <linux/clk.h>
#include <linux/clk/msm-clk.h>
+#include <linux/reset.h>
enum core_ldo_levels {
CORE_LEVEL_NONE = 0,
@@ -87,8 +88,10 @@ struct msm_ssphy_qmp {
struct clk *aux_clk;
struct clk *cfg_ahb_clk;
struct clk *pipe_clk;
- struct clk *phy_reset;
- struct clk *phy_phy_reset;
+ bool power_enabled;
+ struct reset_control *phy_reset;
+ struct reset_control *phy_phy_reset;
+
bool clk_enabled;
bool cable_connected;
bool in_suspend;
@@ -159,38 +162,47 @@ static void msm_ssusb_qmp_enable_autonomous(struct msm_ssphy_qmp *phy,
}
}
-
-static int msm_ssusb_qmp_config_vdd(struct msm_ssphy_qmp *phy, int high)
+static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
{
- int min, ret;
+ int min, rc = 0;
- min = high ? 1 : 0; /* low or none? */
- ret = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
- phy->vdd_levels[2]);
- if (ret) {
- dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
- return ret;
- }
+ dev_dbg(phy->phy.dev, "reg (%s)\n", on ? "HPM" : "LPM");
- dev_dbg(phy->phy.dev, "min_vol:%d max_vol:%d\n",
- phy->vdd_levels[min], phy->vdd_levels[2]);
- return ret;
-}
+ if (phy->power_enabled == on) {
+ dev_dbg(phy->phy.dev, "PHYs' regulators status %d\n",
+ phy->power_enabled);
+ return 0;
+ }
-static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
-{
- int rc = 0;
+ phy->power_enabled = on;
- dev_dbg(phy->phy.dev, "reg (%s)\n", on ? "HPM" : "LPM");
+ min = on ? 1 : 0; /* low or none? */
if (!on)
goto disable_regulators;
+ rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
+ phy->vdd_levels[2]);
+ if (rc) {
+ dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
+ return rc;
+ }
+
+ dev_dbg(phy->phy.dev, "min_vol:%d max_vol:%d\n",
+ phy->vdd_levels[min], phy->vdd_levels[2]);
+
+ rc = regulator_enable(phy->vdd);
+ if (rc) {
+ dev_err(phy->phy.dev,
+ "regulator_enable(phy->vdd) failed, ret=%d",
+ rc);
+ goto unconfig_vdd;
+ }
rc = regulator_set_load(phy->core_ldo, USB_SSPHY_HPM_LOAD);
if (rc < 0) {
dev_err(phy->phy.dev, "Unable to set HPM of core_ldo\n");
- return rc;
+ goto disable_vdd;
}
rc = regulator_set_voltage(phy->core_ldo,
@@ -226,6 +238,18 @@ put_core_ldo_lpm:
if (rc < 0)
dev_err(phy->phy.dev, "Unable to set LPM of core_ldo\n");
+disable_vdd:
+ rc = regulator_disable(phy->vdd);
+ if (rc)
+ dev_err(phy->phy.dev, "regulator_disable(phy->vdd) failed, ret=%d",
+ rc);
+
+unconfig_vdd:
+ rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
+ phy->vdd_levels[2]);
+ if (rc)
+ dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
+
return rc < 0 ? rc : 0;
}
@@ -263,6 +287,14 @@ static int msm_ssphy_qmp_init(struct usb_phy *uphy)
if (phy->emulation)
return 0;
+ ret = msm_ssusb_qmp_ldo_enable(phy, 1);
+ if (ret) {
+ dev_err(phy->phy.dev,
+ "msm_ssusb_qmp_ldo_enable(1) failed, ret=%d\n",
+ ret);
+ return ret;
+ }
+
if (!phy->clk_enabled) {
if (phy->ref_clk_src)
clk_prepare_enable(phy->ref_clk_src);
@@ -341,56 +373,39 @@ static int msm_ssphy_qmp_reset(struct usb_phy *uphy)
dev_dbg(uphy->dev, "Resetting QMP phy\n");
/* Assert USB3 PHY reset */
- if (phy->phy_phy_reset) {
- ret = clk_reset(phy->phy_phy_reset, CLK_RESET_ASSERT);
- if (ret) {
- dev_err(uphy->dev, "phy_phy reset assert failed\n");
- goto exit;
- }
- } else {
- ret = clk_reset(phy->pipe_clk, CLK_RESET_ASSERT);
- if (ret) {
- dev_err(uphy->dev, "pipe_clk reset assert failed\n");
- goto exit;
- }
+ ret = reset_control_assert(phy->phy_phy_reset);
+ if (ret) {
+ dev_err(uphy->dev, "phy_phy_reset assert failed\n");
+ goto exit;
}
/* Assert USB3 PHY CSR reset */
- ret = clk_reset(phy->phy_reset, CLK_RESET_ASSERT);
+ ret = reset_control_assert(phy->phy_reset);
if (ret) {
- dev_err(uphy->dev, "phy_reset clk assert failed\n");
+ dev_err(uphy->dev, "phy_reset assert failed\n");
goto deassert_phy_phy_reset;
}
/* Deassert USB3 PHY CSR reset */
- ret = clk_reset(phy->phy_reset, CLK_RESET_DEASSERT);
+ ret = reset_control_deassert(phy->phy_reset);
if (ret) {
- dev_err(uphy->dev, "phy_reset clk deassert failed\n");
+ dev_err(uphy->dev, "phy_reset deassert failed\n");
goto deassert_phy_phy_reset;
}
/* Deassert USB3 PHY reset */
- if (phy->phy_phy_reset) {
- ret = clk_reset(phy->phy_phy_reset, CLK_RESET_DEASSERT);
- if (ret) {
- dev_err(uphy->dev, "phy_phy reset deassert failed\n");
- goto exit;
- }
- } else {
- ret = clk_reset(phy->pipe_clk, CLK_RESET_DEASSERT);
- if (ret) {
- dev_err(uphy->dev, "pipe_clk reset deassert failed\n");
- goto exit;
- }
+ ret = reset_control_deassert(phy->phy_phy_reset);
+ if (ret) {
+ dev_err(uphy->dev, "phy_phy_reset deassert failed\n");
+ goto exit;
}
return 0;
deassert_phy_phy_reset:
- if (phy->phy_phy_reset)
- clk_reset(phy->phy_phy_reset, CLK_RESET_DEASSERT);
- else
- clk_reset(phy->pipe_clk, CLK_RESET_DEASSERT);
+ ret = reset_control_deassert(phy->phy_phy_reset);
+ if (ret)
+ dev_err(uphy->dev, "phy_phy_reset deassert failed\n");
exit:
phy->in_suspend = false;
@@ -408,12 +423,6 @@ static int msm_ssphy_power_enable(struct msm_ssphy_qmp *phy, bool on)
*/
if (!host && !phy->cable_connected) {
if (on) {
- ret = regulator_enable(phy->vdd);
- if (ret)
- dev_err(phy->phy.dev,
- "regulator_enable(phy->vdd) failed, ret=%d",
- ret);
-
ret = msm_ssusb_qmp_ldo_enable(phy, 1);
if (ret)
dev_err(phy->phy.dev,
@@ -425,11 +434,6 @@ static int msm_ssphy_power_enable(struct msm_ssphy_qmp *phy, bool on)
dev_err(phy->phy.dev,
"msm_ssusb_qmp_ldo_enable(0) failed, ret=%d\n",
ret);
-
- ret = regulator_disable(phy->vdd);
- if (ret)
- dev_err(phy->phy.dev, "regulator_disable(phy->vdd) failed, ret=%d",
- ret);
}
}
@@ -575,26 +579,18 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
goto err;
}
- if (of_property_match_string(pdev->dev.of_node,
- "clock-names", "phy_reset") >= 0) {
- phy->phy_reset = clk_get(&pdev->dev, "phy_reset");
- if (IS_ERR(phy->phy_reset)) {
- ret = PTR_ERR(phy->phy_reset);
- phy->phy_reset = NULL;
- dev_dbg(dev, "failed to get phy_reset\n");
- goto err;
- }
+ phy->phy_reset = devm_reset_control_get(dev, "phy_reset");
+ if (IS_ERR(phy->phy_reset)) {
+ ret = PTR_ERR(phy->phy_reset);
+ dev_dbg(dev, "failed to get phy_reset\n");
+ goto err;
}
- if (of_property_match_string(pdev->dev.of_node,
- "clock-names", "phy_phy_reset") >= 0) {
- phy->phy_phy_reset = clk_get(dev, "phy_phy_reset");
- if (IS_ERR(phy->phy_phy_reset)) {
- ret = PTR_ERR(phy->phy_phy_reset);
- phy->phy_phy_reset = NULL;
- dev_dbg(dev, "phy_phy_reset unavailable\n");
- goto err;
- }
+ phy->phy_phy_reset = devm_reset_control_get(dev, "phy_phy_reset");
+ if (IS_ERR(phy->phy_phy_reset)) {
+ ret = PTR_ERR(phy->phy_phy_reset);
+ dev_dbg(dev, "failed to get phy_phy_reset\n");
+ goto err;
}
of_get_property(dev->of_node, "qcom,qmp-phy-reg-offset", &size);
@@ -733,24 +729,6 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
goto err;
}
- ret = msm_ssusb_qmp_config_vdd(phy, 1);
- if (ret) {
- dev_err(dev, "ssusb vdd_dig configuration failed\n");
- goto err;
- }
-
- ret = regulator_enable(phy->vdd);
- if (ret) {
- dev_err(dev, "unable to enable the ssusb vdd_dig\n");
- goto unconfig_ss_vdd;
- }
-
- ret = msm_ssusb_qmp_ldo_enable(phy, 1);
- if (ret) {
- dev_err(dev, "ssusb vreg enable failed\n");
- goto disable_ss_vdd;
- }
-
phy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
if (IS_ERR(phy->ref_clk_src))
phy->ref_clk_src = NULL;
@@ -772,16 +750,7 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
phy->phy.type = USB_PHY_TYPE_USB3;
ret = usb_add_phy_dev(&phy->phy);
- if (ret)
- goto disable_ss_ldo;
- return 0;
-disable_ss_ldo:
- msm_ssusb_qmp_ldo_enable(phy, 0);
-disable_ss_vdd:
- regulator_disable(phy->vdd);
-unconfig_ss_vdd:
- msm_ssusb_qmp_config_vdd(phy, 0);
err:
return ret;
}
@@ -799,8 +768,6 @@ static int msm_ssphy_qmp_remove(struct platform_device *pdev)
if (phy->ref_clk_src)
clk_disable_unprepare(phy->ref_clk_src);
msm_ssusb_qmp_ldo_enable(phy, 0);
- regulator_disable(phy->vdd);
- msm_ssusb_qmp_config_vdd(phy, 0);
clk_disable_unprepare(phy->aux_clk);
clk_disable_unprepare(phy->cfg_ahb_clk);
clk_disable_unprepare(phy->pipe_clk);
diff --git a/drivers/video/adf/Kconfig b/drivers/video/adf/Kconfig
index 33858b73d8bb..2777db48fae0 100644
--- a/drivers/video/adf/Kconfig
+++ b/drivers/video/adf/Kconfig
@@ -11,4 +11,4 @@ menuconfig ADF_FBDEV
menuconfig ADF_MEMBLOCK
depends on ADF
depends on HAVE_MEMBLOCK
- tristate "Helper for using memblocks as buffers in ADF drivers"
+ bool "Helper for using memblocks as buffers in ADF drivers"
diff --git a/drivers/video/adf/Makefile b/drivers/video/adf/Makefile
index 78d0915122f4..cdf34a666dc7 100644
--- a/drivers/video/adf/Makefile
+++ b/drivers/video/adf/Makefile
@@ -2,13 +2,15 @@ ccflags-y := -Idrivers/staging/android
CFLAGS_adf.o := -I$(src)
-obj-$(CONFIG_ADF) += adf.o \
+obj-$(CONFIG_ADF) += adf_core.o
+
+adf_core-y := adf.o \
adf_client.o \
adf_fops.o \
adf_format.o \
adf_sysfs.o
-obj-$(CONFIG_COMPAT) += adf_fops32.o
+adf_core-$(CONFIG_COMPAT) += adf_fops32.o
obj-$(CONFIG_ADF_FBDEV) += adf_fbdev.o
diff --git a/drivers/video/adf/adf_client.c b/drivers/video/adf/adf_client.c
index 8061d8e6b9fb..75b2f0b18522 100644
--- a/drivers/video/adf/adf_client.c
+++ b/drivers/video/adf/adf_client.c
@@ -305,8 +305,10 @@ static int adf_buffer_map(struct adf_device *dev, struct adf_buffer *buf,
}
done:
- if (ret < 0)
+ if (ret < 0) {
adf_buffer_mapping_cleanup(mapping, buf);
+ memset(mapping, 0, sizeof(*mapping));
+ }
return ret;
}
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 4724f4378e23..609a7aed4977 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -191,6 +191,7 @@ enum mdss_qos_settings {
MDSS_QOS_TS_PREFILL,
MDSS_QOS_REMAPPER,
MDSS_QOS_IB_NOCR,
+ MDSS_QOS_WB2_WRITE_GATHER_EN,
MDSS_QOS_MAX,
};
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index 4e366e79b6fb..f8e34fed4730 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 */
@@ -1436,10 +1438,54 @@ static ssize_t mdss_dp_rda_connected(struct device *dev,
return ret;
}
+
+static ssize_t mdss_dp_sysfs_wta_s3d_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret, s3d_mode;
+ struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev);
+
+ if (!dp) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+ ret = kstrtoint(buf, 10, &s3d_mode);
+ if (ret) {
+ DEV_ERR("%s: kstrtoint failed. rc=%d\n", __func__, ret);
+ goto end;
+ }
+
+ dp->s3d_mode = s3d_mode;
+ ret = strnlen(buf, PAGE_SIZE);
+ DEV_DBG("%s: %d\n", __func__, dp->s3d_mode);
+end:
+ return ret;
+}
+
+static ssize_t mdss_dp_sysfs_rda_s3d_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret;
+ struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev);
+
+ if (!dp) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", dp->s3d_mode);
+ DEV_DBG("%s: '%d'\n", __func__, dp->s3d_mode);
+
+ return ret;
+}
+
static DEVICE_ATTR(connected, S_IRUGO, mdss_dp_rda_connected, NULL);
+static DEVICE_ATTR(s3d_mode, S_IRUGO | S_IWUSR, mdss_dp_sysfs_rda_s3d_mode,
+ mdss_dp_sysfs_wta_s3d_mode);
static struct attribute *mdss_dp_fs_attrs[] = {
&dev_attr_connected.attr,
+ &dev_attr_s3d_mode.attr,
NULL,
};
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index 2e5c33dad389..4710cf7a98e2 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -404,6 +404,7 @@ struct mdss_dp_drv_pdata {
struct mutex pd_msg_mutex;
struct mutex hdcp_mutex;
bool cable_connected;
+ u32 s3d_mode;
u32 aux_cmd_busy;
u32 aux_cmd_i2c;
int aux_trans_num;
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..a9aff5373a2a 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)
@@ -235,5 +237,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_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index fe6ce30d0c89..e8d68059581f 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -27,7 +27,6 @@
#include "mdss_dba_utils.h"
#define DT_CMD_HDR 6
-#define MIN_REFRESH_RATE 48
#define DEFAULT_MDP_TRANSFER_TIME 14000
#define VSYNC_DELAY msecs_to_jiffies(17)
@@ -1907,10 +1906,10 @@ static int mdss_dsi_set_refresh_rate_range(struct device_node *pan_node,
__func__, __LINE__);
/*
- * Since min refresh rate is not specified when dynamic
- * fps is enabled, using minimum as 30
+ * If min refresh rate is not specified, set it to the
+ * default panel refresh rate.
*/
- pinfo->min_fps = MIN_REFRESH_RATE;
+ pinfo->min_fps = pinfo->mipi.frame_rate;
rc = 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index e0f1a37ac84e..50c7015c6731 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -4664,6 +4664,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_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index f35156a2cfcb..1b5c1b7d51e1 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -1364,6 +1364,7 @@ static void mdss_mdp_memory_retention_enter(void)
}
}
+ __mdss_mdp_reg_access_clk_enable(mdata, true);
if (mdss_mdp_clk) {
clk_set_flags(mdss_mdp_clk, CLKFLAG_RETAIN_MEM);
clk_set_flags(mdss_mdp_clk, CLKFLAG_PERIPH_OFF_SET);
@@ -1375,6 +1376,7 @@ static void mdss_mdp_memory_retention_enter(void)
clk_set_flags(mdss_mdp_lut_clk, CLKFLAG_PERIPH_OFF_SET);
clk_set_flags(mdss_mdp_lut_clk, CLKFLAG_NORETAIN_PERIPH);
}
+ __mdss_mdp_reg_access_clk_enable(mdata, false);
}
static void mdss_mdp_memory_retention_exit(void)
@@ -1396,7 +1398,7 @@ static void mdss_mdp_memory_retention_exit(void)
}
}
-
+ __mdss_mdp_reg_access_clk_enable(mdata, true);
if (mdss_mdp_clk) {
clk_set_flags(mdss_mdp_clk, CLKFLAG_RETAIN_MEM);
clk_set_flags(mdss_mdp_clk, CLKFLAG_RETAIN_PERIPH);
@@ -1408,6 +1410,7 @@ static void mdss_mdp_memory_retention_exit(void)
clk_set_flags(mdss_mdp_lut_clk, CLKFLAG_RETAIN_PERIPH);
clk_set_flags(mdss_mdp_lut_clk, CLKFLAG_PERIPH_OFF_CLEAR);
}
+ __mdss_mdp_reg_access_clk_enable(mdata, false);
}
/**
@@ -1981,6 +1984,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_QOS_SIMPLIFIED_PREFILL, mdata->mdss_qos_map);
set_bit(MDSS_QOS_TS_PREFILL, mdata->mdss_qos_map);
set_bit(MDSS_QOS_IB_NOCR, mdata->mdss_qos_map);
+ set_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, mdata->mdss_qos_map);
set_bit(MDSS_CAPS_YUV_CONFIG, mdata->mdss_caps_map);
set_bit(MDSS_CAPS_SCM_RESTORE_NOT_REQUIRED,
mdata->mdss_caps_map);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index ed55057e1d7e..abc048866313 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -4315,9 +4315,11 @@ void mdss_mdp_check_ctl_reset_status(struct mdss_mdp_ctl *ctl)
return;
pr_debug("hw ctl reset is set for ctl:%d\n", ctl->num);
- status = mdss_mdp_poll_ctl_reset_status(ctl, 5);
+ /* poll for at least ~1 frame */
+ status = mdss_mdp_poll_ctl_reset_status(ctl, 320);
if (status) {
- pr_err("hw recovery is not complete for ctl:%d\n", ctl->num);
+ pr_err("hw recovery is not complete for ctl:%d status:0x%x\n",
+ ctl->num, status);
MDSS_XLOG_TOUT_HANDLER("mdp", "vbif", "vbif_nrt", "dbg_bus",
"vbif_dbg_bus", "panic");
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
index f54cbb575535..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)
@@ -829,6 +830,7 @@ enum mdss_mdp_pingpong_index {
#define MMSS_VBIF_CLKON 0x4
#define MMSS_VBIF_RD_LIM_CONF 0x0B0
#define MMSS_VBIF_WR_LIM_CONF 0x0C0
+#define MDSS_VBIF_WRITE_GATHER_EN 0x0AC
#define MMSS_VBIF_XIN_HALT_CTRL0 0x200
#define MMSS_VBIF_XIN_HALT_CTRL1 0x204
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index cee168a33f85..b4933bdea351 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -411,22 +411,28 @@ static void mdss_mdp_video_avr_vtotal_setup(struct mdss_mdp_ctl *ctl,
if (test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map)) {
struct mdss_panel_data *pdata = ctl->panel_data;
- u32 hsync_period = p->hsync_pulse_width + p->h_back_porch +
- p->width + p->h_front_porch;
- u32 vsync_period = p->vsync_pulse_width + p->v_back_porch +
- p->height + p->v_front_porch;
- u32 min_fps = pdata->panel_info.min_fps;
- u32 diff_fps = abs(pdata->panel_info.default_fps - min_fps);
- u32 vtotal = mdss_panel_get_vtotal(&pdata->panel_info);
-
- int add_porches = mult_frac(vtotal, diff_fps, min_fps);
-
- u32 vsync_period_slow = vsync_period + add_porches;
- u32 avr_vtotal = vsync_period_slow * hsync_period;
+ struct mdss_panel_info *pinfo = &pdata->panel_info;
+ u32 avr_vtotal = pinfo->saved_avr_vtotal;
+
+ if (!pinfo->saved_avr_vtotal) {
+ u32 hsync_period = p->hsync_pulse_width +
+ p->h_back_porch + p->width + p->h_front_porch;
+ u32 vsync_period = p->vsync_pulse_width +
+ p->v_back_porch + p->height + p->v_front_porch;
+ u32 min_fps = pinfo->min_fps;
+ u32 default_fps = mdss_panel_get_framerate(pinfo);
+ u32 diff_fps = abs(default_fps - min_fps);
+ u32 vtotal = mdss_panel_get_vtotal(pinfo);
+ int add_porches = mult_frac(vtotal, diff_fps, min_fps);
+ u32 vsync_period_slow = vsync_period + add_porches;
+
+ avr_vtotal = vsync_period_slow * hsync_period;
+ pinfo->saved_avr_vtotal = avr_vtotal;
+ }
mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_VTOTAL, avr_vtotal);
- MDSS_XLOG(min_fps, vsync_period, vsync_period_slow, avr_vtotal);
+ MDSS_XLOG(pinfo->min_fps, pinfo->default_fps, avr_vtotal);
}
}
@@ -1845,6 +1851,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;
@@ -1966,6 +1973,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_intf_writeback.c b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
index 9026b99cd87a..40b10e368309 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
@@ -488,6 +488,10 @@ int mdss_mdp_writeback_prepare_cwb(struct mdss_mdp_ctl *ctl,
mdss_mdp_writeback_cwb_overflow, sctl);
}
+ if (test_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, ctl->mdata->mdss_qos_map))
+ MDSS_VBIF_WRITE(ctl->mdata, MDSS_VBIF_WRITE_GATHER_EN,
+ BIT(6), false);
+
if (ctl->mdata->default_ot_wr_limit || ctl->mdata->default_ot_rd_limit)
mdss_mdp_set_ot_limit_wb(ctx, false);
@@ -907,6 +911,10 @@ static int mdss_mdp_writeback_display(struct mdss_mdp_ctl *ctl, void *arg)
return ret;
}
+ if (test_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, ctl->mdata->mdss_qos_map))
+ MDSS_VBIF_WRITE(ctl->mdata, MDSS_VBIF_WRITE_GATHER_EN,
+ BIT(6), false);
+
mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
mdss_mdp_writeback_intr_done, ctl);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index efd09302de45..ee1cd8fd623e 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -2152,7 +2152,7 @@ static void pp_dspp_opmode_config(struct mdss_mdp_ctl *ctl, u32 num,
bool pa_side_enabled = false;
side = pp_num_to_side(ctl, num);
- if (side < 0)
+ if (side < 0 || !pp_sts)
return;
if (pp_driver_ops.pp_opmode_config) {
@@ -2217,7 +2217,7 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
{
u32 ad_flags, flags, dspp_num, opmode = 0, ad_bypass;
struct mdp_pgc_lut_data *pgc_config;
- struct pp_sts_type *pp_sts;
+ struct pp_sts_type *pp_sts = NULL;
char __iomem *base, *addr = NULL;
int ret = 0;
struct mdss_data_type *mdata;
diff --git a/drivers/video/fbdev/msm/mdss_panel.c b/drivers/video/fbdev/msm/mdss_panel.c
index 61911810b2c0..97025b3a9c23 100644
--- a/drivers/video/fbdev/msm/mdss_panel.c
+++ b/drivers/video/fbdev/msm/mdss_panel.c
@@ -644,7 +644,6 @@ void mdss_panel_info_from_timing(struct mdss_panel_timing *pt,
pinfo->dsc_enc_total = pt->dsc_enc_total;
pinfo->fbc = pt->fbc;
pinfo->compression_mode = pt->compression_mode;
- pinfo->default_fps = pinfo->mipi.frame_rate;
pinfo->roi_alignment = pt->roi_alignment;
pinfo->te = pt->te;
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index 1137c4475cab..81b6fa7d35b3 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -722,6 +722,9 @@ struct mdss_panel_info {
/* debugfs structure for the panel */
struct mdss_panel_debugfs_info *debugfs_info;
+
+ /* stores initial adaptive variable refresh vtotal value */
+ u32 saved_avr_vtotal;
};
struct mdss_panel_timing {
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/Kconfig b/fs/Kconfig
index 6ce72d8d1ee1..a5d2dc39ba07 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -199,6 +199,7 @@ if MISC_FILESYSTEMS
source "fs/adfs/Kconfig"
source "fs/affs/Kconfig"
source "fs/ecryptfs/Kconfig"
+source "fs/sdcardfs/Kconfig"
source "fs/hfs/Kconfig"
source "fs/hfsplus/Kconfig"
source "fs/befs/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index 79f522575cba..3b54070cd629 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -3,7 +3,7 @@
#
# 14 Sep 2000, Christoph Hellwig <hch@infradead.org>
# Rewritten to use lists instead of if-statements.
-#
+#
obj-y := open.o read_write.o file_table.o super.o \
char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
@@ -59,7 +59,7 @@ obj-y += devpts/
obj-$(CONFIG_PROFILING) += dcookies.o
obj-$(CONFIG_DLM) += dlm/
-
+
# Do not add any filesystems before this line
obj-$(CONFIG_FSCACHE) += fscache/
obj-$(CONFIG_REISERFS_FS) += reiserfs/
@@ -81,6 +81,7 @@ obj-$(CONFIG_ISO9660_FS) += isofs/
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+
obj-$(CONFIG_HFS_FS) += hfs/
obj-$(CONFIG_ECRYPT_FS) += ecryptfs/
+obj-$(CONFIG_SDCARD_FS) += sdcardfs/
obj-$(CONFIG_VXFS_FS) += freevxfs/
obj-$(CONFIG_NFS_FS) += nfs/
obj-$(CONFIG_EXPORTFS) += exportfs/
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 3a93755e880f..0c52941dd62c 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -651,7 +651,7 @@ static unsigned long randomize_stack_top(unsigned long stack_top)
if ((current->flags & PF_RANDOMIZE) &&
!(current->personality & ADDR_NO_RANDOMIZE)) {
- random_variable = (unsigned long) get_random_int();
+ random_variable = get_random_long();
random_variable &= STACK_RND_MASK;
random_variable <<= PAGE_SHIFT;
}
diff --git a/fs/dcache.c b/fs/dcache.c
index 18effa378f97..240935d77844 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -3020,6 +3020,7 @@ char *d_absolute_path(const struct path *path,
return ERR_PTR(error);
return res;
}
+EXPORT_SYMBOL(d_absolute_path);
/*
* same as __d_path but appends "(deleted)" for unlinked files.
diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index b46e9fc64196..95a49ef2781a 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -117,10 +117,16 @@ config EXT4_ENCRYPTION
decrypted pages in the page cache.
config EXT4_FS_ENCRYPTION
- bool
- default y
+ bool "Ext4 FS Encryption"
+ default n
depends on EXT4_ENCRYPTION
+config EXT4_FS_ICE_ENCRYPTION
+ bool "Ext4 Encryption with ICE support"
+ default n
+ depends on EXT4_FS_ENCRYPTION
+ depends on PFK
+
config EXT4_DEBUG
bool "EXT4 debugging support"
depends on EXT4_FS
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index f52cf54f0cbc..1cabbd9a9229 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -14,3 +14,5 @@ ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o crypto.o \
crypto_key.o crypto_fname.o
+
+ext4-$(CONFIG_EXT4_FS_ICE_ENCRYPTION) += ext4_ice.o
diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
index 1a0835073663..1bb67391225a 100644
--- a/fs/ext4/crypto.c
+++ b/fs/ext4/crypto.c
@@ -93,7 +93,8 @@ void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx)
* Return: An allocated and initialized encryption context on success; error
* value or NULL otherwise.
*/
-struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode)
+struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode,
+ gfp_t gfp_flags)
{
struct ext4_crypto_ctx *ctx = NULL;
int res = 0;
@@ -120,7 +121,7 @@ struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode)
list_del(&ctx->free_list);
spin_unlock_irqrestore(&ext4_crypto_ctx_lock, flags);
if (!ctx) {
- ctx = kmem_cache_zalloc(ext4_crypto_ctx_cachep, GFP_NOFS);
+ ctx = kmem_cache_zalloc(ext4_crypto_ctx_cachep, gfp_flags);
if (!ctx) {
res = -ENOMEM;
goto out;
@@ -257,7 +258,8 @@ static int ext4_page_crypto(struct inode *inode,
ext4_direction_t rw,
pgoff_t index,
struct page *src_page,
- struct page *dest_page)
+ struct page *dest_page,
+ gfp_t gfp_flags)
{
u8 xts_tweak[EXT4_XTS_TWEAK_SIZE];
@@ -309,9 +311,10 @@ static int ext4_page_crypto(struct inode *inode,
return 0;
}
-static struct page *alloc_bounce_page(struct ext4_crypto_ctx *ctx)
+static struct page *alloc_bounce_page(struct ext4_crypto_ctx *ctx,
+ gfp_t gfp_flags)
{
- ctx->w.bounce_page = mempool_alloc(ext4_bounce_page_pool, GFP_NOWAIT);
+ ctx->w.bounce_page = mempool_alloc(ext4_bounce_page_pool, gfp_flags);
if (ctx->w.bounce_page == NULL)
return ERR_PTR(-ENOMEM);
ctx->flags |= EXT4_WRITE_PATH_FL;
@@ -334,7 +337,8 @@ static struct page *alloc_bounce_page(struct ext4_crypto_ctx *ctx)
* error value or NULL.
*/
struct page *ext4_encrypt(struct inode *inode,
- struct page *plaintext_page)
+ struct page *plaintext_page,
+ gfp_t gfp_flags)
{
struct ext4_crypto_ctx *ctx;
struct page *ciphertext_page = NULL;
@@ -342,17 +346,17 @@ struct page *ext4_encrypt(struct inode *inode,
BUG_ON(!PageLocked(plaintext_page));
- ctx = ext4_get_crypto_ctx(inode);
+ ctx = ext4_get_crypto_ctx(inode, gfp_flags);
if (IS_ERR(ctx))
return (struct page *) ctx;
/* The encryption operation will require a bounce page. */
- ciphertext_page = alloc_bounce_page(ctx);
+ ciphertext_page = alloc_bounce_page(ctx, gfp_flags);
if (IS_ERR(ciphertext_page))
goto errout;
ctx->w.control_page = plaintext_page;
err = ext4_page_crypto(inode, EXT4_ENCRYPT, plaintext_page->index,
- plaintext_page, ciphertext_page);
+ plaintext_page, ciphertext_page, gfp_flags);
if (err) {
ciphertext_page = ERR_PTR(err);
errout:
@@ -380,8 +384,8 @@ int ext4_decrypt(struct page *page)
{
BUG_ON(!PageLocked(page));
- return ext4_page_crypto(page->mapping->host,
- EXT4_DECRYPT, page->index, page, page);
+ return ext4_page_crypto(page->mapping->host, EXT4_DECRYPT,
+ page->index, page, page, GFP_NOFS);
}
int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex)
@@ -402,11 +406,11 @@ int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex)
BUG_ON(inode->i_sb->s_blocksize != PAGE_CACHE_SIZE);
- ctx = ext4_get_crypto_ctx(inode);
+ ctx = ext4_get_crypto_ctx(inode, GFP_NOFS);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ciphertext_page = alloc_bounce_page(ctx);
+ ciphertext_page = alloc_bounce_page(ctx, GFP_NOWAIT);
if (IS_ERR(ciphertext_page)) {
err = PTR_ERR(ciphertext_page);
goto errout;
@@ -414,11 +418,12 @@ int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex)
while (len--) {
err = ext4_page_crypto(inode, EXT4_ENCRYPT, lblk,
- ZERO_PAGE(0), ciphertext_page);
+ ZERO_PAGE(0), ciphertext_page,
+ GFP_NOFS);
if (err)
goto errout;
- bio = bio_alloc(GFP_KERNEL, 1);
+ bio = bio_alloc(GFP_NOWAIT, 1);
if (!bio) {
err = -ENOMEM;
goto errout;
@@ -453,7 +458,8 @@ errout:
bool ext4_valid_contents_enc_mode(uint32_t mode)
{
- return (mode == EXT4_ENCRYPTION_MODE_AES_256_XTS);
+ return (mode == EXT4_ENCRYPTION_MODE_AES_256_XTS ||
+ mode == EXT4_ENCRYPTION_MODE_PRIVATE);
}
/**
@@ -469,3 +475,63 @@ uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size)
return size;
return 0;
}
+
+/*
+ * Validate dentries for encrypted directories to make sure we aren't
+ * potentially caching stale data after a key has been added or
+ * removed.
+ */
+static int ext4_d_revalidate(struct dentry *dentry, unsigned int flags)
+{
+ struct dentry *dir;
+ struct ext4_crypt_info *ci;
+ int dir_has_key, cached_with_key;
+
+ dir = dget_parent(dentry);
+ if (!ext4_encrypted_inode(d_inode(dir))) {
+ dput(dir);
+ return 0;
+ }
+ ci = EXT4_I(d_inode(dir))->i_crypt_info;
+ if (ci && ci->ci_keyring_key &&
+ (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED) |
+ (1 << KEY_FLAG_DEAD))))
+ ci = NULL;
+
+ /* this should eventually be an flag in d_flags */
+ cached_with_key = dentry->d_fsdata != NULL;
+ dir_has_key = (ci != NULL);
+ dput(dir);
+
+ /*
+ * If the dentry was cached without the key, and it is a
+ * negative dentry, it might be a valid name. We can't check
+ * if the key has since been made available due to locking
+ * reasons, so we fail the validation so ext4_lookup() can do
+ * this check.
+ *
+ * We also fail the validation if the dentry was created with
+ * the key present, but we no longer have the key, or vice versa.
+ */
+ if ((!cached_with_key && d_is_negative(dentry)) ||
+ (!cached_with_key && dir_has_key) ||
+ (cached_with_key && !dir_has_key)) {
+#if 0 /* Revalidation debug */
+ char buf[80];
+ char *cp = simple_dname(dentry, buf, sizeof(buf));
+
+ if (IS_ERR(cp))
+ cp = (char *) "???";
+ pr_err("revalidate: %s %p %d %d %d\n", cp, dentry->d_fsdata,
+ cached_with_key, d_is_negative(dentry),
+ dir_has_key);
+#endif
+ return 0;
+ }
+ return 1;
+}
+
+const struct dentry_operations ext4_encrypted_d_ops = {
+ .d_revalidate = ext4_d_revalidate,
+};
diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c
index 9a16d1e75a49..15342bfff70d 100644
--- a/fs/ext4/crypto_key.c
+++ b/fs/ext4/crypto_key.c
@@ -15,6 +15,7 @@
#include <uapi/linux/keyctl.h>
#include "ext4.h"
+#include "ext4_ice.h"
#include "xattr.h"
static void derive_crypt_complete(struct crypto_async_request *req, int rc)
@@ -111,6 +112,12 @@ void ext4_free_encryption_info(struct inode *inode,
ext4_free_crypt_info(ci);
}
+static int ext4_default_data_encryption_mode(void)
+{
+ return ext4_is_ice_enabled() ? EXT4_ENCRYPTION_MODE_PRIVATE :
+ EXT4_ENCRYPTION_MODE_AES_256_XTS;
+}
+
int _ext4_get_encryption_info(struct inode *inode)
{
struct ext4_inode_info *ei = EXT4_I(inode);
@@ -124,8 +131,8 @@ int _ext4_get_encryption_info(struct inode *inode)
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
struct crypto_ablkcipher *ctfm;
const char *cipher_str;
- char raw_key[EXT4_MAX_KEY_SIZE];
- char mode;
+ int for_fname = 0;
+ int mode;
int res;
if (!ext4_read_workqueue) {
@@ -150,7 +157,8 @@ retry:
if (res < 0) {
if (!DUMMY_ENCRYPTION_ENABLED(sbi))
return res;
- ctx.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+ ctx.contents_encryption_mode =
+ ext4_default_data_encryption_mode();
ctx.filenames_encryption_mode =
EXT4_ENCRYPTION_MODE_AES_256_CTS;
ctx.flags = 0;
@@ -169,12 +177,12 @@ retry:
crypt_info->ci_keyring_key = NULL;
memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
sizeof(crypt_info->ci_master_key));
- if (S_ISREG(inode->i_mode))
- mode = crypt_info->ci_data_mode;
- else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
- mode = crypt_info->ci_filename_mode;
- else
+ if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
+ for_fname = 1;
+ else if (!S_ISREG(inode->i_mode))
BUG();
+ mode = for_fname ? crypt_info->ci_filename_mode :
+ crypt_info->ci_data_mode;
switch (mode) {
case EXT4_ENCRYPTION_MODE_AES_256_XTS:
cipher_str = "xts(aes)";
@@ -182,6 +190,9 @@ retry:
case EXT4_ENCRYPTION_MODE_AES_256_CTS:
cipher_str = "cts(cbc(aes))";
break;
+ case EXT4_ENCRYPTION_MODE_PRIVATE:
+ cipher_str = "bugon";
+ break;
default:
printk_once(KERN_WARNING
"ext4: unsupported key mode %d (ino %u)\n",
@@ -190,7 +201,7 @@ retry:
goto out;
}
if (DUMMY_ENCRYPTION_ENABLED(sbi)) {
- memset(raw_key, 0x42, EXT4_AES_256_XTS_KEY_SIZE);
+ memset(crypt_info->ci_raw_key, 0x42, EXT4_AES_256_XTS_KEY_SIZE);
goto got_key;
}
memcpy(full_key_descriptor, EXT4_KEY_DESC_PREFIX,
@@ -232,28 +243,36 @@ retry:
goto out;
}
res = ext4_derive_key_aes(ctx.nonce, master_key->raw,
- raw_key);
+ crypt_info->ci_raw_key);
up_read(&keyring_key->sem);
if (res)
goto out;
got_key:
- ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0);
- if (!ctfm || IS_ERR(ctfm)) {
- res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
- printk(KERN_DEBUG
- "%s: error %d (inode %u) allocating crypto tfm\n",
- __func__, res, (unsigned) inode->i_ino);
+ if (for_fname ||
+ (crypt_info->ci_data_mode != EXT4_ENCRYPTION_MODE_PRIVATE)) {
+ ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0);
+ if (!ctfm || IS_ERR(ctfm)) {
+ res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
+ pr_debug("%s: error %d (inode %u) allocating crypto tfm\n",
+ __func__, res, (unsigned) inode->i_ino);
+ goto out;
+ }
+ crypt_info->ci_ctfm = ctfm;
+ crypto_ablkcipher_clear_flags(ctfm, ~0);
+ crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm),
+ CRYPTO_TFM_REQ_WEAK_KEY);
+ res = crypto_ablkcipher_setkey(ctfm, crypt_info->ci_raw_key,
+ ext4_encryption_key_size(mode));
+ if (res)
+ goto out;
+ memzero_explicit(crypt_info->ci_raw_key,
+ sizeof(crypt_info->ci_raw_key));
+ } else if (!ext4_is_ice_enabled()) {
+ pr_warn("%s: ICE support not available\n",
+ __func__);
+ res = -EINVAL;
goto out;
}
- crypt_info->ci_ctfm = ctfm;
- crypto_ablkcipher_clear_flags(ctfm, ~0);
- crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm),
- CRYPTO_TFM_REQ_WEAK_KEY);
- res = crypto_ablkcipher_setkey(ctfm, raw_key,
- ext4_encryption_key_size(mode));
- if (res)
- goto out;
- memzero_explicit(raw_key, sizeof(raw_key));
if (cmpxchg(&ei->i_crypt_info, NULL, crypt_info) != NULL) {
ext4_free_crypt_info(crypt_info);
goto retry;
@@ -263,8 +282,9 @@ got_key:
out:
if (res == -ENOKEY)
res = 0;
+ memzero_explicit(crypt_info->ci_raw_key,
+ sizeof(crypt_info->ci_raw_key));
ext4_free_crypt_info(crypt_info);
- memzero_explicit(raw_key, sizeof(raw_key));
return res;
}
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index 1d1bca74f844..33f5e2a50cf8 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -111,6 +111,12 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
int dir_has_error = 0;
struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
+ if (ext4_encrypted_inode(inode)) {
+ err = ext4_get_encryption_info(inode);
+ if (err && err != -ENOKEY)
+ return err;
+ }
+
if (is_dx_dir(inode)) {
err = ext4_dx_readdir(file, ctx);
if (err != ERR_BAD_DX_DIR) {
@@ -157,8 +163,11 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
index, 1);
file->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT;
bh = ext4_bread(NULL, inode, map.m_lblk, 0);
- if (IS_ERR(bh))
- return PTR_ERR(bh);
+ if (IS_ERR(bh)) {
+ err = PTR_ERR(bh);
+ bh = NULL;
+ goto errout;
+ }
}
if (!bh) {
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 786cb51cab56..f287ddfd17f1 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -588,6 +588,7 @@ enum {
#define EXT4_ENCRYPTION_MODE_AES_256_GCM 2
#define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
+#define EXT4_ENCRYPTION_MODE_PRIVATE 127
#include "ext4_crypto.h"
@@ -2251,13 +2252,16 @@ extern struct kmem_cache *ext4_crypt_info_cachep;
bool ext4_valid_contents_enc_mode(uint32_t mode);
uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size);
extern struct workqueue_struct *ext4_read_workqueue;
-struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode);
+struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode,
+ gfp_t gfp_flags);
void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx);
void ext4_restore_control_page(struct page *data_page);
struct page *ext4_encrypt(struct inode *inode,
- struct page *plaintext_page);
+ struct page *plaintext_page,
+ gfp_t gfp_flags);
int ext4_decrypt(struct page *page);
int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex);
+extern const struct dentry_operations ext4_encrypted_d_ops;
#ifdef CONFIG_EXT4_FS_ENCRYPTION
int ext4_init_crypto(void);
@@ -2325,6 +2329,19 @@ int _ext4_get_encryption_info(struct inode *inode);
#ifdef CONFIG_EXT4_FS_ENCRYPTION
int ext4_has_encryption_key(struct inode *inode);
+static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode)
+{
+ return EXT4_I(inode)->i_crypt_info;
+}
+
+static inline int ext4_using_hardware_encryption(struct inode *inode)
+{
+ struct ext4_crypt_info *ci = ext4_encryption_info(inode);
+
+ return S_ISREG(inode->i_mode) && ci &&
+ ci->ci_data_mode == EXT4_ENCRYPTION_MODE_PRIVATE;
+}
+
static inline int ext4_get_encryption_info(struct inode *inode)
{
struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info;
@@ -2338,11 +2355,6 @@ static inline int ext4_get_encryption_info(struct inode *inode)
return 0;
}
-static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode)
-{
- return EXT4_I(inode)->i_crypt_info;
-}
-
#else
static inline int ext4_has_encryption_key(struct inode *inode)
{
@@ -2356,6 +2368,10 @@ static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode)
{
return NULL;
}
+static inline int ext4_using_hardware_encryption(struct inode *inode)
+{
+ return 0;
+}
#endif
@@ -3010,8 +3026,7 @@ extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
struct page *page);
extern int ext4_try_add_inline_entry(handle_t *handle,
struct ext4_filename *fname,
- struct dentry *dentry,
- struct inode *inode);
+ struct inode *dir, struct inode *inode);
extern int ext4_try_create_inline_dir(handle_t *handle,
struct inode *parent,
struct inode *inode);
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
index ac7d4e813796..95cbc9bc1995 100644
--- a/fs/ext4/ext4_crypto.h
+++ b/fs/ext4/ext4_crypto.h
@@ -12,6 +12,7 @@
#define _EXT4_CRYPTO_H
#include <linux/fs.h>
+#include <linux/pfk.h>
#define EXT4_KEY_DESCRIPTOR_SIZE 8
@@ -61,6 +62,7 @@ struct ext4_encryption_context {
#define EXT4_AES_256_CBC_KEY_SIZE 32
#define EXT4_AES_256_CTS_KEY_SIZE 32
#define EXT4_AES_256_XTS_KEY_SIZE 64
+#define EXT4_PRIVATE_KEY_SIZE 64
#define EXT4_MAX_KEY_SIZE 64
#define EXT4_KEY_DESC_PREFIX "ext4:"
@@ -80,8 +82,11 @@ struct ext4_crypt_info {
struct crypto_ablkcipher *ci_ctfm;
struct key *ci_keyring_key;
char ci_master_key[EXT4_KEY_DESCRIPTOR_SIZE];
+ char ci_raw_key[EXT4_MAX_KEY_SIZE];
};
+
+
#define EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001
#define EXT4_WRITE_PATH_FL 0x00000002
@@ -114,6 +119,7 @@ static inline int ext4_encryption_key_size(int mode)
{
switch (mode) {
case EXT4_ENCRYPTION_MODE_AES_256_XTS:
+ case EXT4_ENCRYPTION_MODE_PRIVATE:
return EXT4_AES_256_XTS_KEY_SIZE;
case EXT4_ENCRYPTION_MODE_AES_256_GCM:
return EXT4_AES_256_GCM_KEY_SIZE;
diff --git a/fs/ext4/ext4_ice.c b/fs/ext4/ext4_ice.c
new file mode 100644
index 000000000000..d85bcb8ea1ba
--- /dev/null
+++ b/fs/ext4/ext4_ice.c
@@ -0,0 +1,109 @@
+/* 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 "ext4_ice.h"
+#include "ext4_crypto.h"
+
+
+/*
+ * Retrieves encryption key from the inode
+ */
+char *ext4_get_ice_encryption_key(const struct inode *inode)
+{
+ struct ext4_crypt_info *ci = NULL;
+
+ if (!inode)
+ return NULL;
+
+ ci = ext4_encryption_info((struct inode *)inode);
+ if (!ci)
+ return NULL;
+
+ return &(ci->ci_raw_key[0]);
+}
+
+/*
+ * Retrieves encryption salt from the inode
+ */
+char *ext4_get_ice_encryption_salt(const struct inode *inode)
+{
+ struct ext4_crypt_info *ci = NULL;
+
+ if (!inode)
+ return NULL;
+
+ ci = ext4_encryption_info((struct inode *)inode);
+ if (!ci)
+ return NULL;
+
+ return &(ci->ci_raw_key[ext4_get_ice_encryption_key_size(inode)]);
+}
+
+/*
+ * returns true if the cipher mode in inode is AES XTS
+ */
+int ext4_is_aes_xts_cipher(const struct inode *inode)
+{
+ struct ext4_crypt_info *ci = NULL;
+
+ ci = ext4_encryption_info((struct inode *)inode);
+ if (!ci)
+ return 0;
+
+ return (ci->ci_data_mode == EXT4_ENCRYPTION_MODE_PRIVATE);
+}
+
+/*
+ * returns true if encryption info in both inodes is equal
+ */
+int ext4_is_ice_encryption_info_equal(const struct inode *inode1,
+ const struct inode *inode2)
+{
+ char *key1 = NULL;
+ char *key2 = NULL;
+ char *salt1 = NULL;
+ char *salt2 = NULL;
+
+ if (!inode1 || !inode2)
+ return 0;
+
+ if (inode1 == inode2)
+ return 1;
+
+ /* both do not belong to ice, so we don't care, they are equal for us */
+ if (!ext4_should_be_processed_by_ice(inode1) &&
+ !ext4_should_be_processed_by_ice(inode2))
+ return 1;
+
+ /* one belongs to ice, the other does not -> not equal */
+ if (ext4_should_be_processed_by_ice(inode1) ^
+ ext4_should_be_processed_by_ice(inode2))
+ return 0;
+
+ key1 = ext4_get_ice_encryption_key(inode1);
+ key2 = ext4_get_ice_encryption_key(inode2);
+ salt1 = ext4_get_ice_encryption_salt(inode1);
+ salt2 = ext4_get_ice_encryption_salt(inode2);
+
+ /* key and salt should not be null by this point */
+ if (!key1 || !key2 || !salt1 || !salt2 ||
+ (ext4_get_ice_encryption_key_size(inode1) !=
+ ext4_get_ice_encryption_key_size(inode2)) ||
+ (ext4_get_ice_encryption_salt_size(inode1) !=
+ ext4_get_ice_encryption_salt_size(inode2)))
+ return 0;
+
+ return ((memcmp(key1, key2,
+ ext4_get_ice_encryption_key_size(inode1)) == 0) &&
+ (memcmp(salt1, salt2,
+ ext4_get_ice_encryption_salt_size(inode1)) == 0));
+}
diff --git a/fs/ext4/ext4_ice.h b/fs/ext4/ext4_ice.h
new file mode 100644
index 000000000000..5257edabd6b2
--- /dev/null
+++ b/fs/ext4/ext4_ice.h
@@ -0,0 +1,104 @@
+/* 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 _EXT4_ICE_H
+#define _EXT4_ICE_H
+
+#include "ext4.h"
+#include "ext4_crypto.h"
+
+#ifdef CONFIG_EXT4_FS_ICE_ENCRYPTION
+static inline int ext4_should_be_processed_by_ice(const struct inode *inode)
+{
+ if (!ext4_encrypted_inode((struct inode *)inode))
+ return 0;
+
+ return ext4_using_hardware_encryption((struct inode *)inode);
+}
+
+static inline int ext4_is_ice_enabled(void)
+{
+ return 1;
+}
+
+int ext4_is_aes_xts_cipher(const struct inode *inode);
+
+char *ext4_get_ice_encryption_key(const struct inode *inode);
+char *ext4_get_ice_encryption_salt(const struct inode *inode);
+
+int ext4_is_ice_encryption_info_equal(const struct inode *inode1,
+ const struct inode *inode2);
+
+static inline size_t ext4_get_ice_encryption_key_size(
+ const struct inode *inode)
+{
+ return EXT4_AES_256_XTS_KEY_SIZE / 2;
+}
+
+static inline size_t ext4_get_ice_encryption_salt_size(
+ const struct inode *inode)
+{
+ return EXT4_AES_256_XTS_KEY_SIZE / 2;
+}
+
+#else
+static inline int ext4_should_be_processed_by_ice(const struct inode *inode)
+{
+ return 0;
+}
+static inline int ext4_is_ice_enabled(void)
+{
+ return 0;
+}
+
+static inline char *ext4_get_ice_encryption_key(const struct inode *inode)
+{
+ return NULL;
+}
+
+static inline char *ext4_get_ice_encryption_salt(const struct inode *inode)
+{
+ return NULL;
+}
+
+static inline size_t ext4_get_ice_encryption_key_size(
+ const struct inode *inode)
+{
+ return 0;
+}
+
+static inline size_t ext4_get_ice_encryption_salt_size(
+ const struct inode *inode)
+{
+ return 0;
+}
+
+static inline int ext4_is_xts_cipher(const struct inode *inode)
+{
+ return 0;
+}
+
+static inline int ext4_is_ice_encryption_info_equal(
+ const struct inode *inode1,
+ const struct inode *inode2)
+{
+ return 0;
+}
+
+static inline int ext4_is_aes_xts_cipher(const struct inode *inode)
+{
+ return 0;
+}
+
+#endif
+
+#endif /* _EXT4_ICE_H */
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index d884989cc83d..dfe3b9bafc0d 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -995,12 +995,11 @@ void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
*/
static int ext4_add_dirent_to_inline(handle_t *handle,
struct ext4_filename *fname,
- struct dentry *dentry,
+ struct inode *dir,
struct inode *inode,
struct ext4_iloc *iloc,
void *inline_start, int inline_size)
{
- struct inode *dir = d_inode(dentry->d_parent);
int err;
struct ext4_dir_entry_2 *de;
@@ -1245,12 +1244,11 @@ out:
* the new created block.
*/
int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
- struct dentry *dentry, struct inode *inode)
+ struct inode *dir, struct inode *inode)
{
int ret, inline_size;
void *inline_start;
struct ext4_iloc iloc;
- struct inode *dir = d_inode(dentry->d_parent);
ret = ext4_get_inode_loc(dir, &iloc);
if (ret)
@@ -1264,7 +1262,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
EXT4_INLINE_DOTDOT_SIZE;
inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE;
- ret = ext4_add_dirent_to_inline(handle, fname, dentry, inode, &iloc,
+ ret = ext4_add_dirent_to_inline(handle, fname, dir, inode, &iloc,
inline_start, inline_size);
if (ret != -ENOSPC)
goto out;
@@ -1285,7 +1283,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
if (inline_size) {
inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
- ret = ext4_add_dirent_to_inline(handle, fname, dentry,
+ ret = ext4_add_dirent_to_inline(handle, fname, dir,
inode, &iloc, inline_start,
inline_size);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 06bda0361e7c..b15e6edb8f2c 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -42,6 +42,7 @@
#include "xattr.h"
#include "acl.h"
#include "truncate.h"
+#include "ext4_ice.h"
#include <trace/events/ext4.h>
@@ -979,7 +980,8 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
ll_rw_block(READ, 1, &bh);
*wait_bh++ = bh;
decrypt = ext4_encrypted_inode(inode) &&
- S_ISREG(inode->i_mode);
+ S_ISREG(inode->i_mode) &&
+ !ext4_is_ice_enabled();
}
}
/*
@@ -3459,7 +3461,8 @@ static int __ext4_block_zero_page_range(handle_t *handle,
if (!buffer_uptodate(bh))
goto unlock;
if (S_ISREG(inode->i_mode) &&
- ext4_encrypted_inode(inode)) {
+ ext4_encrypted_inode(inode) &&
+ !ext4_using_hardware_encryption(inode)) {
/* We expect the key to be set. */
BUG_ON(!ext4_has_encryption_key(inode));
BUG_ON(blocksize != PAGE_CACHE_SIZE);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index a969ab39f302..c9aad3b8951f 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -273,7 +273,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **res_dir);
static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
- struct dentry *dentry, struct inode *inode);
+ struct inode *dir, struct inode *inode);
/* checksumming functions */
void initialize_dirent_tail(struct ext4_dir_entry_tail *t,
@@ -1558,6 +1558,24 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
struct ext4_dir_entry_2 *de;
struct buffer_head *bh;
+ if (ext4_encrypted_inode(dir)) {
+ int res = ext4_get_encryption_info(dir);
+
+ /*
+ * This should be a properly defined flag for
+ * dentry->d_flags when we uplift this to the VFS.
+ * d_fsdata is set to (void *) 1 if if the dentry is
+ * created while the directory was encrypted and we
+ * don't have access to the key.
+ */
+ dentry->d_fsdata = NULL;
+ if (ext4_encryption_info(dir))
+ dentry->d_fsdata = (void *) 1;
+ d_set_d_op(dentry, &ext4_encrypted_d_ops);
+ if (res && res != -ENOKEY)
+ return ERR_PTR(res);
+ }
+
if (dentry->d_name.len > EXT4_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
@@ -1928,10 +1946,9 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
* directory, and adds the dentry to the indexed directory.
*/
static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
- struct dentry *dentry,
+ struct inode *dir,
struct inode *inode, struct buffer_head *bh)
{
- struct inode *dir = d_inode(dentry->d_parent);
struct buffer_head *bh2;
struct dx_root *root;
struct dx_frame frames[2], *frame;
@@ -2086,8 +2103,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
return retval;
if (ext4_has_inline_data(dir)) {
- retval = ext4_try_add_inline_entry(handle, &fname,
- dentry, inode);
+ retval = ext4_try_add_inline_entry(handle, &fname, dir, inode);
if (retval < 0)
goto out;
if (retval == 1) {
@@ -2097,7 +2113,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
}
if (is_dx(dir)) {
- retval = ext4_dx_add_entry(handle, &fname, dentry, inode);
+ retval = ext4_dx_add_entry(handle, &fname, dir, inode);
if (!retval || (retval != ERR_BAD_DX_DIR))
goto out;
ext4_clear_inode_flag(dir, EXT4_INODE_INDEX);
@@ -2119,7 +2135,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
if (blocks == 1 && !dx_fallback &&
ext4_has_feature_dir_index(sb)) {
- retval = make_indexed_dir(handle, &fname, dentry,
+ retval = make_indexed_dir(handle, &fname, dir,
inode, bh);
bh = NULL; /* make_indexed_dir releases bh */
goto out;
@@ -2154,12 +2170,11 @@ out:
* Returns 0 for success, or a negative error value
*/
static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
- struct dentry *dentry, struct inode *inode)
+ struct inode *dir, struct inode *inode)
{
struct dx_frame frames[2], *frame;
struct dx_entry *entries, *at;
struct buffer_head *bh;
- struct inode *dir = d_inode(dentry->d_parent);
struct super_block *sb = dir->i_sb;
struct ext4_dir_entry_2 *de;
int err;
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index 17fbe3882b8e..1a6a8ca4de3a 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -23,10 +23,12 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
+#include <linux/backing-dev.h>
#include "ext4_jbd2.h"
#include "xattr.h"
#include "acl.h"
+#include "ext4_ice.h"
static struct kmem_cache *io_end_cachep;
@@ -485,9 +487,24 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode) &&
nr_to_submit) {
- data_page = ext4_encrypt(inode, page);
+ gfp_t gfp_flags = GFP_NOFS;
+
+ retry_encrypt:
+
+ if (!ext4_using_hardware_encryption(inode))
+ data_page = ext4_encrypt(inode, page, gfp_flags);
+
+
if (IS_ERR(data_page)) {
ret = PTR_ERR(data_page);
+ if (ret == ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
+ if (io->io_bio) {
+ ext4_io_submit(io);
+ congestion_wait(BLK_RW_ASYNC, HZ/50);
+ }
+ gfp_flags |= __GFP_NOFAIL;
+ goto retry_encrypt;
+ }
data_page = NULL;
goto out;
}
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index 5dc5e95063de..dd98270d0b21 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -45,6 +45,7 @@
#include <linux/cleancache.h>
#include "ext4.h"
+#include "ext4_ice.h"
/*
* Call ext4_decrypt on every single page, reusing the encryption
@@ -62,12 +63,17 @@ static void completion_pages(struct work_struct *work)
bio_for_each_segment_all(bv, bio, i) {
struct page *page = bv->bv_page;
- int ret = ext4_decrypt(page);
- if (ret) {
- WARN_ON_ONCE(1);
- SetPageError(page);
- } else
+ if (ext4_is_ice_enabled()) {
SetPageUptodate(page);
+ } else {
+ int ret = ext4_decrypt(page);
+
+ if (ret) {
+ WARN_ON_ONCE(1);
+ SetPageError(page);
+ } else
+ SetPageUptodate(page);
+ }
unlock_page(page);
}
ext4_release_crypto_ctx(ctx);
@@ -279,7 +285,7 @@ int ext4_mpage_readpages(struct address_space *mapping,
if (ext4_encrypted_inode(inode) &&
S_ISREG(inode->i_mode)) {
- ctx = ext4_get_crypto_ctx(inode);
+ ctx = ext4_get_crypto_ctx(inode, GFP_NOFS);
if (IS_ERR(ctx))
goto set_error_page;
}
@@ -324,5 +330,6 @@ int ext4_mpage_readpages(struct address_space *mapping,
BUG_ON(pages && !list_empty(pages));
if (bio)
submit_bio(READ, bio);
+
return 0;
}
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 6b6b3e751f8c..06fd5f7f993d 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -232,6 +232,27 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh)
return error;
}
+static int
+__xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
+ void *end, const char *function, unsigned int line)
+{
+ struct ext4_xattr_entry *entry = IFIRST(header);
+ int error = -EFSCORRUPTED;
+
+ if (((void *) header >= end) ||
+ (header->h_magic != le32_to_cpu(EXT4_XATTR_MAGIC)))
+ goto errout;
+ error = ext4_xattr_check_names(entry, end, entry);
+errout:
+ if (error)
+ __ext4_error_inode(inode, function, line, 0,
+ "corrupted in-inode xattr");
+ return error;
+}
+
+#define xattr_check_inode(inode, header, end) \
+ __xattr_check_inode((inode), (header), (end), __func__, __LINE__)
+
static inline int
ext4_xattr_check_entry(struct ext4_xattr_entry *entry, size_t size)
{
@@ -343,7 +364,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
header = IHDR(inode, raw_inode);
entry = IFIRST(header);
end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
- error = ext4_xattr_check_names(entry, end, entry);
+ error = xattr_check_inode(inode, header, end);
if (error)
goto cleanup;
error = ext4_xattr_find_entry(&entry, name_index, name,
@@ -474,7 +495,7 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
raw_inode = ext4_raw_inode(&iloc);
header = IHDR(inode, raw_inode);
end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
- error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header));
+ error = xattr_check_inode(inode, header, end);
if (error)
goto cleanup;
error = ext4_xattr_list_entries(dentry, IFIRST(header),
@@ -990,8 +1011,7 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
is->s.here = is->s.first;
is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
- error = ext4_xattr_check_names(IFIRST(header), is->s.end,
- IFIRST(header));
+ error = xattr_check_inode(inode, header, is->s.end);
if (error)
return error;
/* Find the named attribute. */
@@ -1288,6 +1308,10 @@ retry:
last = entry;
total_ino = sizeof(struct ext4_xattr_ibody_header);
+ error = xattr_check_inode(inode, header, end);
+ if (error)
+ goto cleanup;
+
free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
if (free >= new_extra_isize) {
entry = IFIRST(header);
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index b8d08d0d0a4d..f72f3b25b3f2 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -702,6 +702,8 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
struct fsnotify_group *group;
struct inode *inode;
struct path path;
+ struct path alteredpath;
+ struct path *canonical_path = &path;
struct fd f;
int ret;
unsigned flags = 0;
@@ -741,13 +743,22 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
if (ret)
goto fput_and_out;
+ /* support stacked filesystems */
+ if(path.dentry && path.dentry->d_op) {
+ if (path.dentry->d_op->d_canonical_path) {
+ path.dentry->d_op->d_canonical_path(path.dentry, &alteredpath);
+ canonical_path = &alteredpath;
+ path_put(&path);
+ }
+ }
+
/* inode held in place by reference to path; group by fget on fd */
- inode = path.dentry->d_inode;
+ inode = canonical_path->dentry->d_inode;
group = f.file->private_data;
/* create/update an inode mark */
ret = inotify_update_watch(group, inode, mask);
- path_put(&path);
+ path_put(canonical_path);
fput_and_out:
fdput(f);
return ret;
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index 2429c804cf78..414041342a99 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -554,7 +554,7 @@ static int ramoops_parse_dt(struct platform_device *pdev,
static int ramoops_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct ramoops_platform_data *pdata = platform_get_drvdata(pdev);
+ struct ramoops_platform_data *pdata = pdev->dev.platform_data;
struct ramoops_context *cxt = &oops_cxt;
size_t dump_mem_sz;
phys_addr_t paddr;
@@ -666,7 +666,6 @@ static int ramoops_probe(struct platform_device *pdev)
cxt->size, (unsigned long long)cxt->phys_addr,
cxt->ecc_info.ecc_size, cxt->ecc_info.block_size);
- platform_set_drvdata(pdev, pdata);
return 0;
fail_buf:
diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig
new file mode 100644
index 000000000000..a1c103316ac7
--- /dev/null
+++ b/fs/sdcardfs/Kconfig
@@ -0,0 +1,13 @@
+config SDCARD_FS
+ tristate "sdcard file system"
+ depends on CONFIGFS_FS
+ default n
+ help
+ Sdcardfs is based on Wrapfs file system.
+
+config SDCARD_FS_FADV_NOACTIVE
+ bool "sdcardfs fadvise noactive support"
+ depends on FADV_NOACTIVE
+ default y
+ help
+ Sdcardfs supports fadvise noactive mode.
diff --git a/fs/sdcardfs/Makefile b/fs/sdcardfs/Makefile
new file mode 100644
index 000000000000..b84fbb2b45a4
--- /dev/null
+++ b/fs/sdcardfs/Makefile
@@ -0,0 +1,7 @@
+SDCARDFS_VERSION="0.1"
+
+EXTRA_CFLAGS += -DSDCARDFS_VERSION=\"$(SDCARDFS_VERSION)\"
+
+obj-$(CONFIG_SDCARD_FS) += sdcardfs.o
+
+sdcardfs-y := dentry.o file.o inode.o main.o super.o lookup.o mmap.o packagelist.o derived_perm.o
diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c
new file mode 100644
index 000000000000..ba165ef11e27
--- /dev/null
+++ b/fs/sdcardfs/dentry.c
@@ -0,0 +1,182 @@
+/*
+ * fs/sdcardfs/dentry.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#include "sdcardfs.h"
+#include "linux/ctype.h"
+
+/*
+ * returns: -ERRNO if error (returned to user)
+ * 0: tell VFS to invalidate dentry
+ * 1: dentry is valid
+ */
+static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags)
+{
+ int err = 1;
+ struct path parent_lower_path, lower_path;
+ struct dentry *parent_dentry = NULL;
+ struct dentry *parent_lower_dentry = NULL;
+ struct dentry *lower_cur_parent_dentry = NULL;
+ struct dentry *lower_dentry = NULL;
+
+ if (flags & LOOKUP_RCU)
+ return -ECHILD;
+
+ spin_lock(&dentry->d_lock);
+ if (IS_ROOT(dentry)) {
+ spin_unlock(&dentry->d_lock);
+ return 1;
+ }
+ spin_unlock(&dentry->d_lock);
+
+ /* check uninitialized obb_dentry and
+ * whether the base obbpath has been changed or not */
+ if (is_obbpath_invalid(dentry)) {
+ d_drop(dentry);
+ return 0;
+ }
+
+ parent_dentry = dget_parent(dentry);
+ sdcardfs_get_lower_path(parent_dentry, &parent_lower_path);
+ sdcardfs_get_real_lower(dentry, &lower_path);
+ parent_lower_dentry = parent_lower_path.dentry;
+ lower_dentry = lower_path.dentry;
+ lower_cur_parent_dentry = dget_parent(lower_dentry);
+
+ spin_lock(&lower_dentry->d_lock);
+ if (d_unhashed(lower_dentry)) {
+ spin_unlock(&lower_dentry->d_lock);
+ d_drop(dentry);
+ err = 0;
+ goto out;
+ }
+ spin_unlock(&lower_dentry->d_lock);
+
+ if (parent_lower_dentry != lower_cur_parent_dentry) {
+ d_drop(dentry);
+ err = 0;
+ goto out;
+ }
+
+ if (dentry < lower_dentry) {
+ spin_lock(&dentry->d_lock);
+ spin_lock(&lower_dentry->d_lock);
+ } else {
+ spin_lock(&lower_dentry->d_lock);
+ spin_lock(&dentry->d_lock);
+ }
+
+ if (dentry->d_name.len != lower_dentry->d_name.len) {
+ __d_drop(dentry);
+ err = 0;
+ } else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name,
+ dentry->d_name.len) != 0) {
+ __d_drop(dentry);
+ err = 0;
+ }
+
+ if (dentry < lower_dentry) {
+ spin_unlock(&lower_dentry->d_lock);
+ spin_unlock(&dentry->d_lock);
+ } else {
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&lower_dentry->d_lock);
+ }
+
+out:
+ dput(parent_dentry);
+ dput(lower_cur_parent_dentry);
+ sdcardfs_put_lower_path(parent_dentry, &parent_lower_path);
+ sdcardfs_put_real_lower(dentry, &lower_path);
+ return err;
+}
+
+static void sdcardfs_d_release(struct dentry *dentry)
+{
+ /* release and reset the lower paths */
+ if(has_graft_path(dentry)) {
+ sdcardfs_put_reset_orig_path(dentry);
+ }
+ sdcardfs_put_reset_lower_path(dentry);
+ free_dentry_private_data(dentry);
+ return;
+}
+
+static int sdcardfs_hash_ci(const struct dentry *dentry,
+ struct qstr *qstr)
+{
+ /*
+ * This function is copy of vfat_hashi.
+ * FIXME Should we support national language?
+ * Refer to vfat_hashi()
+ * struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io;
+ */
+ const unsigned char *name;
+ unsigned int len;
+ unsigned long hash;
+
+ name = qstr->name;
+ //len = vfat_striptail_len(qstr);
+ len = qstr->len;
+
+ hash = init_name_hash();
+ while (len--)
+ //hash = partial_name_hash(nls_tolower(t, *name++), hash);
+ hash = partial_name_hash(tolower(*name++), hash);
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+/*
+ * Case insensitive compare of two vfat names.
+ */
+static int sdcardfs_cmp_ci(const struct dentry *parent,
+ const struct dentry *dentry,
+ unsigned int len, const char *str, const struct qstr *name)
+{
+ /* This function is copy of vfat_cmpi */
+ // FIXME Should we support national language?
+ //struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io;
+ //unsigned int alen, blen;
+
+ /* A filename cannot end in '.' or we treat it like it has none */
+ /*
+ alen = vfat_striptail_len(name);
+ blen = __vfat_striptail_len(len, str);
+ if (alen == blen) {
+ if (nls_strnicmp(t, name->name, str, alen) == 0)
+ return 0;
+ }
+ */
+ if (name->len == len) {
+ if (strncasecmp(name->name, str, len) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+const struct dentry_operations sdcardfs_ci_dops = {
+ .d_revalidate = sdcardfs_d_revalidate,
+ .d_release = sdcardfs_d_release,
+ .d_hash = sdcardfs_hash_ci,
+ .d_compare = sdcardfs_cmp_ci,
+ .d_canonical_path = sdcardfs_get_real_lower,
+};
+
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
new file mode 100644
index 000000000000..128b3e56851f
--- /dev/null
+++ b/fs/sdcardfs/derived_perm.c
@@ -0,0 +1,265 @@
+/*
+ * fs/sdcardfs/derived_perm.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#include "sdcardfs.h"
+
+/* copy derived state from parent inode */
+static void inherit_derived_state(struct inode *parent, struct inode *child)
+{
+ struct sdcardfs_inode_info *pi = SDCARDFS_I(parent);
+ struct sdcardfs_inode_info *ci = SDCARDFS_I(child);
+
+ ci->perm = PERM_INHERIT;
+ ci->userid = pi->userid;
+ ci->d_uid = pi->d_uid;
+ ci->under_android = pi->under_android;
+}
+
+/* helper function for derived state */
+void setup_derived_state(struct inode *inode, perm_t perm,
+ userid_t userid, uid_t uid, bool under_android)
+{
+ struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
+
+ info->perm = perm;
+ info->userid = userid;
+ info->d_uid = uid;
+ info->under_android = under_android;
+}
+
+/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */
+void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry)
+{
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode);
+ struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ appid_t appid;
+
+ /* By default, each inode inherits from its parent.
+ * the properties are maintained on its private fields
+ * because the inode attributes will be modified with that of
+ * its lower inode.
+ * The derived state will be updated on the last
+ * stage of each system call by fix_derived_permission(inode).
+ */
+
+ inherit_derived_state(parent->d_inode, dentry->d_inode);
+
+ /* Derive custom permissions based on parent and current node */
+ switch (parent_info->perm) {
+ case PERM_INHERIT:
+ /* Already inherited above */
+ break;
+ case PERM_PRE_ROOT:
+ /* Legacy internal layout places users at top level */
+ info->perm = PERM_ROOT;
+ info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10);
+ break;
+ case PERM_ROOT:
+ /* Assume masked off by default. */
+ if (!strcasecmp(newdentry->d_name.name, "Android")) {
+ /* App-specific directories inside; let anyone traverse */
+ info->perm = PERM_ANDROID;
+ info->under_android = true;
+ }
+ break;
+ case PERM_ANDROID:
+ if (!strcasecmp(newdentry->d_name.name, "data")) {
+ /* App-specific directories inside; let anyone traverse */
+ info->perm = PERM_ANDROID_DATA;
+ } else if (!strcasecmp(newdentry->d_name.name, "obb")) {
+ /* App-specific directories inside; let anyone traverse */
+ info->perm = PERM_ANDROID_OBB;
+ /* Single OBB directory is always shared */
+ } else if (!strcasecmp(newdentry->d_name.name, "media")) {
+ /* App-specific directories inside; let anyone traverse */
+ info->perm = PERM_ANDROID_MEDIA;
+ }
+ break;
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_OBB:
+ case PERM_ANDROID_MEDIA:
+ appid = get_appid(sbi->pkgl_id, newdentry->d_name.name);
+ if (appid != 0) {
+ info->d_uid = multiuser_get_uid(parent_info->userid, appid);
+ }
+ break;
+ }
+}
+
+void get_derived_permission(struct dentry *parent, struct dentry *dentry)
+{
+ get_derived_permission_new(parent, dentry, dentry);
+}
+
+void get_derive_permissions_recursive(struct dentry *parent) {
+ struct dentry *dentry;
+ list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
+ if (dentry && dentry->d_inode) {
+ mutex_lock(&dentry->d_inode->i_mutex);
+ get_derived_permission(parent, dentry);
+ fix_derived_permission(dentry->d_inode);
+ get_derive_permissions_recursive(dentry);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ }
+ }
+}
+
+/* main function for updating derived permission */
+inline void update_derived_permission_lock(struct dentry *dentry)
+{
+ struct dentry *parent;
+
+ if(!dentry || !dentry->d_inode) {
+ printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
+ return;
+ }
+ /* FIXME:
+ * 1. need to check whether the dentry is updated or not
+ * 2. remove the root dentry update
+ */
+ mutex_lock(&dentry->d_inode->i_mutex);
+ if(IS_ROOT(dentry)) {
+ //setup_default_pre_root_state(dentry->d_inode);
+ } else {
+ parent = dget_parent(dentry);
+ if(parent) {
+ get_derived_permission(parent, dentry);
+ dput(parent);
+ }
+ }
+ fix_derived_permission(dentry->d_inode);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+}
+
+int need_graft_path(struct dentry *dentry)
+{
+ int ret = 0;
+ struct dentry *parent = dget_parent(dentry);
+ struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+
+ if(parent_info->perm == PERM_ANDROID &&
+ !strcasecmp(dentry->d_name.name, "obb")) {
+
+ /* /Android/obb is the base obbpath of DERIVED_UNIFIED */
+ if(!(sbi->options.multiuser == false
+ && parent_info->userid == 0)) {
+ ret = 1;
+ }
+ }
+ dput(parent);
+ return ret;
+}
+
+int is_obbpath_invalid(struct dentry *dent)
+{
+ int ret = 0;
+ struct sdcardfs_dentry_info *di = SDCARDFS_D(dent);
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb);
+ char *path_buf, *obbpath_s;
+
+ /* check the base obbpath has been changed.
+ * this routine can check an uninitialized obb dentry as well.
+ * regarding the uninitialized obb, refer to the sdcardfs_mkdir() */
+ spin_lock(&di->lock);
+ if(di->orig_path.dentry) {
+ if(!di->lower_path.dentry) {
+ ret = 1;
+ } else {
+ path_get(&di->lower_path);
+ //lower_parent = lock_parent(lower_path->dentry);
+
+ path_buf = kmalloc(PATH_MAX, GFP_ATOMIC);
+ if(!path_buf) {
+ ret = 1;
+ printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__);
+ } else {
+ obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX);
+ if (d_unhashed(di->lower_path.dentry) ||
+ strcasecmp(sbi->obbpath_s, obbpath_s)) {
+ ret = 1;
+ }
+ kfree(path_buf);
+ }
+
+ //unlock_dir(lower_parent);
+ path_put(&di->lower_path);
+ }
+ }
+ spin_unlock(&di->lock);
+ return ret;
+}
+
+int is_base_obbpath(struct dentry *dentry)
+{
+ int ret = 0;
+ struct dentry *parent = dget_parent(dentry);
+ struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+
+ spin_lock(&SDCARDFS_D(dentry)->lock);
+ if (sbi->options.multiuser) {
+ if(parent_info->perm == PERM_PRE_ROOT &&
+ !strcasecmp(dentry->d_name.name, "obb")) {
+ ret = 1;
+ }
+ } else if (parent_info->perm == PERM_ANDROID &&
+ !strcasecmp(dentry->d_name.name, "obb")) {
+ ret = 1;
+ }
+ spin_unlock(&SDCARDFS_D(dentry)->lock);
+ return ret;
+}
+
+/* The lower_path will be stored to the dentry's orig_path
+ * and the base obbpath will be copyed to the lower_path variable.
+ * if an error returned, there's no change in the lower_path
+ * returns: -ERRNO if error (0: no error) */
+int setup_obb_dentry(struct dentry *dentry, struct path *lower_path)
+{
+ int err = 0;
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ struct path obbpath;
+
+ /* A local obb dentry must have its own orig_path to support rmdir
+ * and mkdir of itself. Usually, we expect that the sbi->obbpath
+ * is avaiable on this stage. */
+ sdcardfs_set_orig_path(dentry, lower_path);
+
+ err = kern_path(sbi->obbpath_s,
+ LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath);
+
+ if(!err) {
+ /* the obbpath base has been found */
+ printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n");
+ pathcpy(lower_path, &obbpath);
+ } else {
+ /* if the sbi->obbpath is not available, we can optionally
+ * setup the lower_path with its orig_path.
+ * but, the current implementation just returns an error
+ * because the sdcard daemon also regards this case as
+ * a lookup fail. */
+ printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n");
+ }
+ return err;
+}
+
+
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
new file mode 100644
index 000000000000..c249fa982d3c
--- /dev/null
+++ b/fs/sdcardfs/file.c
@@ -0,0 +1,356 @@
+/*
+ * fs/sdcardfs/file.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#include "sdcardfs.h"
+#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
+#include <linux/backing-dev.h>
+#endif
+
+static ssize_t sdcardfs_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int err;
+ struct file *lower_file;
+ struct dentry *dentry = file->f_path.dentry;
+#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
+ struct backing_dev_info *bdi;
+#endif
+
+ lower_file = sdcardfs_lower_file(file);
+
+#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
+ if (file->f_mode & FMODE_NOACTIVE) {
+ if (!(lower_file->f_mode & FMODE_NOACTIVE)) {
+ bdi = lower_file->f_mapping->backing_dev_info;
+ lower_file->f_ra.ra_pages = bdi->ra_pages * 2;
+ spin_lock(&lower_file->f_lock);
+ lower_file->f_mode |= FMODE_NOACTIVE;
+ spin_unlock(&lower_file->f_lock);
+ }
+ }
+#endif
+
+ err = vfs_read(lower_file, buf, count, ppos);
+ /* update our inode atime upon a successful lower read */
+ if (err >= 0)
+ fsstack_copy_attr_atime(d_inode(dentry),
+ file_inode(lower_file));
+
+ return err;
+}
+
+static ssize_t sdcardfs_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int err;
+ struct file *lower_file;
+ struct dentry *dentry = file->f_path.dentry;
+
+ /* check disk space */
+ if (!check_min_free_space(dentry, count, 0)) {
+ printk(KERN_INFO "No minimum free space.\n");
+ return -ENOSPC;
+ }
+
+ lower_file = sdcardfs_lower_file(file);
+ err = vfs_write(lower_file, buf, count, ppos);
+ /* update our inode times+sizes upon a successful lower write */
+ if (err >= 0) {
+ fsstack_copy_inode_size(d_inode(dentry),
+ file_inode(lower_file));
+ fsstack_copy_attr_times(d_inode(dentry),
+ file_inode(lower_file));
+ }
+
+ return err;
+}
+
+static int sdcardfs_readdir(struct file *file, struct dir_context *ctx)
+{
+ int err;
+ struct file *lower_file = NULL;
+ struct dentry *dentry = file->f_path.dentry;
+
+ lower_file = sdcardfs_lower_file(file);
+
+ lower_file->f_pos = file->f_pos;
+ err = iterate_dir(lower_file, ctx);
+ file->f_pos = lower_file->f_pos;
+ if (err >= 0) /* copy the atime */
+ fsstack_copy_attr_atime(d_inode(dentry),
+ file_inode(lower_file));
+ return err;
+}
+
+static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ long err = -ENOTTY;
+ struct file *lower_file;
+
+ lower_file = sdcardfs_lower_file(file);
+
+ /* XXX: use vfs_ioctl if/when VFS exports it */
+ if (!lower_file || !lower_file->f_op)
+ goto out;
+ if (lower_file->f_op->unlocked_ioctl)
+ err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
+
+out:
+ return err;
+}
+
+#ifdef CONFIG_COMPAT
+static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ long err = -ENOTTY;
+ struct file *lower_file;
+
+ lower_file = sdcardfs_lower_file(file);
+
+ /* XXX: use vfs_ioctl if/when VFS exports it */
+ if (!lower_file || !lower_file->f_op)
+ goto out;
+ if (lower_file->f_op->compat_ioctl)
+ err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
+
+out:
+ return err;
+}
+#endif
+
+static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int err = 0;
+ bool willwrite;
+ struct file *lower_file;
+ const struct vm_operations_struct *saved_vm_ops = NULL;
+
+ /* this might be deferred to mmap's writepage */
+ willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);
+
+ /*
+ * File systems which do not implement ->writepage may use
+ * generic_file_readonly_mmap as their ->mmap op. If you call
+ * generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL.
+ * But we cannot call the lower ->mmap op, so we can't tell that
+ * writeable mappings won't work. Therefore, our only choice is to
+ * check if the lower file system supports the ->writepage, and if
+ * not, return EINVAL (the same error that
+ * generic_file_readonly_mmap returns in that case).
+ */
+ lower_file = sdcardfs_lower_file(file);
+ if (willwrite && !lower_file->f_mapping->a_ops->writepage) {
+ err = -EINVAL;
+ printk(KERN_ERR "sdcardfs: lower file system does not "
+ "support writeable mmap\n");
+ goto out;
+ }
+
+ /*
+ * find and save lower vm_ops.
+ *
+ * XXX: the VFS should have a cleaner way of finding the lower vm_ops
+ */
+ if (!SDCARDFS_F(file)->lower_vm_ops) {
+ err = lower_file->f_op->mmap(lower_file, vma);
+ if (err) {
+ printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err);
+ goto out;
+ }
+ saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */
+ err = do_munmap(current->mm, vma->vm_start,
+ vma->vm_end - vma->vm_start);
+ if (err) {
+ printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err);
+ goto out;
+ }
+ }
+
+ /*
+ * Next 3 lines are all I need from generic_file_mmap. I definitely
+ * don't want its test for ->readpage which returns -ENOEXEC.
+ */
+ file_accessed(file);
+ vma->vm_ops = &sdcardfs_vm_ops;
+
+ file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */
+ if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */
+ SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops;
+
+out:
+ return err;
+}
+
+static int sdcardfs_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+ struct file *lower_file = NULL;
+ struct path lower_path;
+ struct dentry *dentry = file->f_path.dentry;
+ struct dentry *parent = dget_parent(dentry);
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ const struct cred *saved_cred = NULL;
+
+ /* don't open unhashed/deleted files */
+ if (d_unhashed(dentry)) {
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
+ printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
+ " dentry: %s, task:%s\n",
+ __func__, dentry->d_name.name, current->comm);
+ err = -EACCES;
+ goto out_err;
+ }
+
+ /* save current_cred and override it */
+ OVERRIDE_CRED(sbi, saved_cred);
+
+ file->private_data =
+ kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
+ if (!SDCARDFS_F(file)) {
+ err = -ENOMEM;
+ goto out_revert_cred;
+ }
+
+ /* open lower object and link sdcardfs's file struct to lower's */
+ sdcardfs_get_lower_path(file->f_path.dentry, &lower_path);
+ lower_file = dentry_open(&lower_path, file->f_flags, current_cred());
+ path_put(&lower_path);
+ if (IS_ERR(lower_file)) {
+ err = PTR_ERR(lower_file);
+ lower_file = sdcardfs_lower_file(file);
+ if (lower_file) {
+ sdcardfs_set_lower_file(file, NULL);
+ fput(lower_file); /* fput calls dput for lower_dentry */
+ }
+ } else {
+ sdcardfs_set_lower_file(file, lower_file);
+ }
+
+ if (err)
+ kfree(SDCARDFS_F(file));
+ else {
+ sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode));
+ }
+
+out_revert_cred:
+ REVERT_CRED(saved_cred);
+out_err:
+ dput(parent);
+ return err;
+}
+
+static int sdcardfs_flush(struct file *file, fl_owner_t id)
+{
+ int err = 0;
+ struct file *lower_file = NULL;
+
+ lower_file = sdcardfs_lower_file(file);
+ if (lower_file && lower_file->f_op && lower_file->f_op->flush) {
+ filemap_write_and_wait(file->f_mapping);
+ err = lower_file->f_op->flush(lower_file, id);
+ }
+
+ return err;
+}
+
+/* release all lower object references & free the file info structure */
+static int sdcardfs_file_release(struct inode *inode, struct file *file)
+{
+ struct file *lower_file;
+
+ lower_file = sdcardfs_lower_file(file);
+ if (lower_file) {
+ sdcardfs_set_lower_file(file, NULL);
+ fput(lower_file);
+ }
+
+ kfree(SDCARDFS_F(file));
+ return 0;
+}
+
+static int sdcardfs_fsync(struct file *file, loff_t start, loff_t end,
+ int datasync)
+{
+ int err;
+ struct file *lower_file;
+ struct path lower_path;
+ struct dentry *dentry = file->f_path.dentry;
+
+ err = __generic_file_fsync(file, start, end, datasync);
+ if (err)
+ goto out;
+
+ lower_file = sdcardfs_lower_file(file);
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ err = vfs_fsync_range(lower_file, start, end, datasync);
+ sdcardfs_put_lower_path(dentry, &lower_path);
+out:
+ return err;
+}
+
+static int sdcardfs_fasync(int fd, struct file *file, int flag)
+{
+ int err = 0;
+ struct file *lower_file = NULL;
+
+ lower_file = sdcardfs_lower_file(file);
+ if (lower_file->f_op && lower_file->f_op->fasync)
+ err = lower_file->f_op->fasync(fd, lower_file, flag);
+
+ return err;
+}
+
+const struct file_operations sdcardfs_main_fops = {
+ .llseek = generic_file_llseek,
+ .read = sdcardfs_read,
+ .write = sdcardfs_write,
+ .unlocked_ioctl = sdcardfs_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = sdcardfs_compat_ioctl,
+#endif
+ .mmap = sdcardfs_mmap,
+ .open = sdcardfs_open,
+ .flush = sdcardfs_flush,
+ .release = sdcardfs_file_release,
+ .fsync = sdcardfs_fsync,
+ .fasync = sdcardfs_fasync,
+};
+
+/* trimmed directory options */
+const struct file_operations sdcardfs_dir_fops = {
+ .llseek = generic_file_llseek,
+ .read = generic_read_dir,
+ .iterate = sdcardfs_readdir,
+ .unlocked_ioctl = sdcardfs_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = sdcardfs_compat_ioctl,
+#endif
+ .open = sdcardfs_open,
+ .release = sdcardfs_file_release,
+ .flush = sdcardfs_flush,
+ .fsync = sdcardfs_fsync,
+ .fasync = sdcardfs_fasync,
+};
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
new file mode 100644
index 000000000000..2528da0d3ae1
--- /dev/null
+++ b/fs/sdcardfs/inode.c
@@ -0,0 +1,802 @@
+/*
+ * fs/sdcardfs/inode.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#include "sdcardfs.h"
+
+/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
+const struct cred * override_fsids(struct sdcardfs_sb_info* sbi)
+{
+ struct cred * cred;
+ const struct cred * old_cred;
+
+ cred = prepare_creds();
+ if (!cred)
+ return NULL;
+
+ cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid);
+ cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
+
+ old_cred = override_creds(cred);
+
+ return old_cred;
+}
+
+/* Do not directly use this function, use REVERT_CRED() instead. */
+void revert_fsids(const struct cred * old_cred)
+{
+ const struct cred * cur_cred;
+
+ cur_cred = current->cred;
+ revert_creds(old_cred);
+ put_cred(cur_cred);
+}
+
+static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode, bool want_excl)
+{
+ int err;
+ struct dentry *lower_dentry;
+ struct dentry *lower_parent_dentry = NULL;
+ struct path lower_path;
+ const struct cred *saved_cred = NULL;
+
+ if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
+ printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
+ " dentry: %s, task:%s\n",
+ __func__, dentry->d_name.name, current->comm);
+ err = -EACCES;
+ goto out_eacces;
+ }
+
+ /* save current_cred and override it */
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ lower_dentry = lower_path.dentry;
+ lower_parent_dentry = lock_parent(lower_dentry);
+
+ /* set last 16bytes of mode field to 0664 */
+ mode = (mode & S_IFMT) | 00664;
+ err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl);
+ if (err)
+ goto out;
+
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid);
+ if (err)
+ goto out;
+ fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
+ fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
+
+out:
+ unlock_dir(lower_parent_dentry);
+ sdcardfs_put_lower_path(dentry, &lower_path);
+ REVERT_CRED(saved_cred);
+out_eacces:
+ return err;
+}
+
+#if 0
+static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ struct dentry *lower_old_dentry;
+ struct dentry *lower_new_dentry;
+ struct dentry *lower_dir_dentry;
+ u64 file_size_save;
+ int err;
+ struct path lower_old_path, lower_new_path;
+
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
+
+ file_size_save = i_size_read(d_inode(old_dentry));
+ sdcardfs_get_lower_path(old_dentry, &lower_old_path);
+ sdcardfs_get_lower_path(new_dentry, &lower_new_path);
+ lower_old_dentry = lower_old_path.dentry;
+ lower_new_dentry = lower_new_path.dentry;
+ lower_dir_dentry = lock_parent(lower_new_dentry);
+
+ err = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry),
+ lower_new_dentry, NULL);
+ if (err || !d_inode(lower_new_dentry))
+ goto out;
+
+ err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path);
+ if (err)
+ goto out;
+ fsstack_copy_attr_times(dir, d_inode(lower_new_dentry));
+ fsstack_copy_inode_size(dir, d_inode(lower_new_dentry));
+ set_nlink(d_inode(old_dentry),
+ sdcardfs_lower_inode(d_inode(old_dentry))->i_nlink);
+ i_size_write(d_inode(new_dentry), file_size_save);
+out:
+ unlock_dir(lower_dir_dentry);
+ sdcardfs_put_lower_path(old_dentry, &lower_old_path);
+ sdcardfs_put_lower_path(new_dentry, &lower_new_path);
+ REVERT_CRED();
+ return err;
+}
+#endif
+
+static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int err;
+ struct dentry *lower_dentry;
+ struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
+ struct dentry *lower_dir_dentry;
+ struct path lower_path;
+ const struct cred *saved_cred = NULL;
+
+ if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
+ printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
+ " dentry: %s, task:%s\n",
+ __func__, dentry->d_name.name, current->comm);
+ err = -EACCES;
+ goto out_eacces;
+ }
+
+ /* save current_cred and override it */
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ lower_dentry = lower_path.dentry;
+ dget(lower_dentry);
+ lower_dir_dentry = lock_parent(lower_dentry);
+
+ err = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
+
+ /*
+ * Note: unlinking on top of NFS can cause silly-renamed files.
+ * Trying to delete such files results in EBUSY from NFS
+ * below. Silly-renamed files will get deleted by NFS later on, so
+ * we just need to detect them here and treat such EBUSY errors as
+ * if the upper file was successfully deleted.
+ */
+ if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED)
+ err = 0;
+ if (err)
+ goto out;
+ fsstack_copy_attr_times(dir, lower_dir_inode);
+ fsstack_copy_inode_size(dir, lower_dir_inode);
+ set_nlink(d_inode(dentry),
+ sdcardfs_lower_inode(d_inode(dentry))->i_nlink);
+ d_inode(dentry)->i_ctime = dir->i_ctime;
+ d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */
+out:
+ unlock_dir(lower_dir_dentry);
+ dput(lower_dentry);
+ sdcardfs_put_lower_path(dentry, &lower_path);
+ REVERT_CRED(saved_cred);
+out_eacces:
+ return err;
+}
+
+#if 0
+static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry,
+ const char *symname)
+{
+ int err;
+ struct dentry *lower_dentry;
+ struct dentry *lower_parent_dentry = NULL;
+ struct path lower_path;
+
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
+
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ lower_dentry = lower_path.dentry;
+ lower_parent_dentry = lock_parent(lower_dentry);
+
+ err = vfs_symlink(d_inode(lower_parent_dentry), lower_dentry, symname);
+ if (err)
+ goto out;
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path);
+ if (err)
+ goto out;
+ fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
+ fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
+
+out:
+ unlock_dir(lower_parent_dentry);
+ sdcardfs_put_lower_path(dentry, &lower_path);
+ REVERT_CRED();
+ return err;
+}
+#endif
+
+static int touch(char *abs_path, mode_t mode) {
+ struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode);
+ if (IS_ERR(filp)) {
+ if (PTR_ERR(filp) == -EEXIST) {
+ return 0;
+ }
+ else {
+ printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n",
+ abs_path, PTR_ERR(filp));
+ return PTR_ERR(filp);
+ }
+ }
+ filp_close(filp, current->files);
+ return 0;
+}
+
+static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ int err;
+ int make_nomedia_in_obb = 0;
+ struct dentry *lower_dentry;
+ struct dentry *lower_parent_dentry = NULL;
+ struct path lower_path;
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ const struct cred *saved_cred = NULL;
+ struct sdcardfs_inode_info *pi = SDCARDFS_I(dir);
+ char *page_buf;
+ char *nomedia_dir_name;
+ char *nomedia_fullpath;
+ int fullpath_namelen;
+ int touch_err = 0;
+
+ if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
+ printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
+ " dentry: %s, task:%s\n",
+ __func__, dentry->d_name.name, current->comm);
+ err = -EACCES;
+ goto out_eacces;
+ }
+
+ /* save current_cred and override it */
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+
+ /* check disk space */
+ if (!check_min_free_space(dentry, 0, 1)) {
+ printk(KERN_INFO "sdcardfs: No minimum free space.\n");
+ err = -ENOSPC;
+ goto out_revert;
+ }
+
+ /* the lower_dentry is negative here */
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ lower_dentry = lower_path.dentry;
+ lower_parent_dentry = lock_parent(lower_dentry);
+
+ /* set last 16bytes of mode field to 0775 */
+ mode = (mode & S_IFMT) | 00775;
+ err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode);
+
+ if (err)
+ goto out;
+
+ /* if it is a local obb dentry, setup it with the base obbpath */
+ if(need_graft_path(dentry)) {
+
+ err = setup_obb_dentry(dentry, &lower_path);
+ if(err) {
+ /* if the sbi->obbpath is not available, the lower_path won't be
+ * changed by setup_obb_dentry() but the lower path is saved to
+ * its orig_path. this dentry will be revalidated later.
+ * but now, the lower_path should be NULL */
+ sdcardfs_put_reset_lower_path(dentry);
+
+ /* the newly created lower path which saved to its orig_path or
+ * the lower_path is the base obbpath.
+ * therefore, an additional path_get is required */
+ path_get(&lower_path);
+ } else
+ make_nomedia_in_obb = 1;
+ }
+
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
+ if (err)
+ goto out;
+
+ fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
+ fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
+ /* update number of links on parent directory */
+ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
+
+ if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb"))
+ && (pi->perm == PERM_ANDROID) && (pi->userid == 0))
+ make_nomedia_in_obb = 1;
+
+ /* When creating /Android/data and /Android/obb, mark them as .nomedia */
+ if (make_nomedia_in_obb ||
+ ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) {
+
+ page_buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!page_buf) {
+ printk(KERN_ERR "sdcardfs: failed to allocate page buf\n");
+ goto out;
+ }
+
+ nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE);
+ if (IS_ERR(nomedia_dir_name)) {
+ free_page((unsigned long)page_buf);
+ printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n");
+ goto out;
+ }
+
+ fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1;
+ fullpath_namelen += strlen("/.nomedia");
+ nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL);
+ if (!nomedia_fullpath) {
+ free_page((unsigned long)page_buf);
+ printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n");
+ goto out;
+ }
+
+ strcpy(nomedia_fullpath, nomedia_dir_name);
+ free_page((unsigned long)page_buf);
+ strcat(nomedia_fullpath, "/.nomedia");
+ touch_err = touch(nomedia_fullpath, 0664);
+ if (touch_err) {
+ printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n",
+ nomedia_fullpath, touch_err);
+ kfree(nomedia_fullpath);
+ goto out;
+ }
+ kfree(nomedia_fullpath);
+ }
+out:
+ unlock_dir(lower_parent_dentry);
+ sdcardfs_put_lower_path(dentry, &lower_path);
+out_revert:
+ REVERT_CRED(saved_cred);
+out_eacces:
+ return err;
+}
+
+static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct dentry *lower_dentry;
+ struct dentry *lower_dir_dentry;
+ int err;
+ struct path lower_path;
+ const struct cred *saved_cred = NULL;
+
+ if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
+ printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
+ " dentry: %s, task:%s\n",
+ __func__, dentry->d_name.name, current->comm);
+ err = -EACCES;
+ goto out_eacces;
+ }
+
+ /* save current_cred and override it */
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+
+ /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
+ * the dentry on the original path should be deleted. */
+ sdcardfs_get_real_lower(dentry, &lower_path);
+
+ lower_dentry = lower_path.dentry;
+ lower_dir_dentry = lock_parent(lower_dentry);
+
+ err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry);
+ if (err)
+ goto out;
+
+ d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */
+ if (d_inode(dentry))
+ clear_nlink(d_inode(dentry));
+ fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry));
+ fsstack_copy_inode_size(dir, d_inode(lower_dir_dentry));
+ set_nlink(dir, d_inode(lower_dir_dentry)->i_nlink);
+
+out:
+ unlock_dir(lower_dir_dentry);
+ sdcardfs_put_real_lower(dentry, &lower_path);
+ REVERT_CRED(saved_cred);
+out_eacces:
+ return err;
+}
+
+#if 0
+static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+ dev_t dev)
+{
+ int err;
+ struct dentry *lower_dentry;
+ struct dentry *lower_parent_dentry = NULL;
+ struct path lower_path;
+
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
+
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ lower_dentry = lower_path.dentry;
+ lower_parent_dentry = lock_parent(lower_dentry);
+
+ err = vfs_mknod(d_inode(lower_parent_dentry), lower_dentry, mode, dev);
+ if (err)
+ goto out;
+
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path);
+ if (err)
+ goto out;
+ fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
+ fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
+
+out:
+ unlock_dir(lower_parent_dentry);
+ sdcardfs_put_lower_path(dentry, &lower_path);
+ REVERT_CRED();
+ return err;
+}
+#endif
+
+/*
+ * The locking rules in sdcardfs_rename are complex. We could use a simpler
+ * superblock-level name-space lock for renames and copy-ups.
+ */
+static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int err = 0;
+ struct dentry *lower_old_dentry = NULL;
+ struct dentry *lower_new_dentry = NULL;
+ struct dentry *lower_old_dir_dentry = NULL;
+ struct dentry *lower_new_dir_dentry = NULL;
+ struct dentry *trap = NULL;
+ struct dentry *new_parent = NULL;
+ struct path lower_old_path, lower_new_path;
+ const struct cred *saved_cred = NULL;
+
+ if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) ||
+ !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) {
+ printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
+ " new_dentry: %s, task:%s\n",
+ __func__, new_dentry->d_name.name, current->comm);
+ err = -EACCES;
+ goto out_eacces;
+ }
+
+ /* save current_cred and override it */
+ OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred);
+
+ sdcardfs_get_real_lower(old_dentry, &lower_old_path);
+ sdcardfs_get_lower_path(new_dentry, &lower_new_path);
+ lower_old_dentry = lower_old_path.dentry;
+ lower_new_dentry = lower_new_path.dentry;
+ lower_old_dir_dentry = dget_parent(lower_old_dentry);
+ lower_new_dir_dentry = dget_parent(lower_new_dentry);
+
+ trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
+ /* source should not be ancestor of target */
+ if (trap == lower_old_dentry) {
+ err = -EINVAL;
+ goto out;
+ }
+ /* target should not be ancestor of source */
+ if (trap == lower_new_dentry) {
+ err = -ENOTEMPTY;
+ goto out;
+ }
+
+ err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry,
+ d_inode(lower_new_dir_dentry), lower_new_dentry,
+ NULL, 0);
+ if (err)
+ goto out;
+
+ /* Copy attrs from lower dir, but i_uid/i_gid */
+ sdcardfs_copy_and_fix_attrs(new_dir, d_inode(lower_new_dir_dentry));
+ fsstack_copy_inode_size(new_dir, d_inode(lower_new_dir_dentry));
+
+ if (new_dir != old_dir) {
+ sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry));
+ fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry));
+
+ /* update the derived permission of the old_dentry
+ * with its new parent
+ */
+ new_parent = dget_parent(new_dentry);
+ if(new_parent) {
+ if(d_inode(old_dentry)) {
+ update_derived_permission_lock(old_dentry);
+ }
+ dput(new_parent);
+ }
+ }
+ /* At this point, not all dentry information has been moved, so
+ * we pass along new_dentry for the name.*/
+ mutex_lock(&d_inode(old_dentry)->i_mutex);
+ get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry);
+ fix_derived_permission(d_inode(old_dentry));
+ get_derive_permissions_recursive(old_dentry);
+ mutex_unlock(&d_inode(old_dentry)->i_mutex);
+out:
+ unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
+ dput(lower_old_dir_dentry);
+ dput(lower_new_dir_dentry);
+ sdcardfs_put_real_lower(old_dentry, &lower_old_path);
+ sdcardfs_put_lower_path(new_dentry, &lower_new_path);
+ REVERT_CRED(saved_cred);
+out_eacces:
+ return err;
+}
+
+#if 0
+static int sdcardfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
+{
+ int err;
+ struct dentry *lower_dentry;
+ struct path lower_path;
+ /* XXX readlink does not requires overriding credential */
+
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ lower_dentry = lower_path.dentry;
+ if (!d_inode(lower_dentry)->i_op ||
+ !d_inode(lower_dentry)->i_op->readlink) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = d_inode(lower_dentry)->i_op->readlink(lower_dentry,
+ buf, bufsiz);
+ if (err < 0)
+ goto out;
+ fsstack_copy_attr_atime(d_inode(dentry), d_inode(lower_dentry));
+
+out:
+ sdcardfs_put_lower_path(dentry, &lower_path);
+ return err;
+}
+#endif
+
+#if 0
+static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie)
+{
+ char *buf;
+ int len = PAGE_SIZE, err;
+ mm_segment_t old_fs;
+
+ /* This is freed by the put_link method assuming a successful call. */
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ buf = ERR_PTR(-ENOMEM);
+ return buf;
+ }
+
+ /* read the symlink, and then we will follow it */
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ err = sdcardfs_readlink(dentry, buf, len);
+ set_fs(old_fs);
+ if (err < 0) {
+ kfree(buf);
+ buf = ERR_PTR(err);
+ } else {
+ buf[err] = '\0';
+ }
+ return *cookie = buf;
+}
+#endif
+
+static int sdcardfs_permission(struct inode *inode, int mask)
+{
+ int err;
+
+ /*
+ * Permission check on sdcardfs inode.
+ * Calling process should have AID_SDCARD_RW permission
+ */
+ err = generic_permission(inode, mask);
+
+ /* XXX
+ * Original sdcardfs code calls inode_permission(lower_inode,.. )
+ * for checking inode permission. But doing such things here seems
+ * duplicated work, because the functions called after this func,
+ * such as vfs_create, vfs_unlink, vfs_rename, and etc,
+ * does exactly same thing, i.e., they calls inode_permission().
+ * So we just let they do the things.
+ * If there are any security hole, just uncomment following if block.
+ */
+#if 0
+ if (!err) {
+ /*
+ * Permission check on lower_inode(=EXT4).
+ * we check it with AID_MEDIA_RW permission
+ */
+ struct inode *lower_inode;
+ OVERRIDE_CRED(SDCARDFS_SB(inode->sb));
+
+ lower_inode = sdcardfs_lower_inode(inode);
+ err = inode_permission(lower_inode, mask);
+
+ REVERT_CRED();
+ }
+#endif
+ return err;
+
+}
+
+static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
+{
+ int err;
+ struct dentry *lower_dentry;
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct path lower_path;
+ struct iattr lower_ia;
+ struct dentry *parent;
+
+ inode = d_inode(dentry);
+
+ /*
+ * Check if user has permission to change inode. We don't check if
+ * this user can change the lower inode: that should happen when
+ * calling notify_change on the lower inode.
+ */
+ err = inode_change_ok(inode, ia);
+
+ /* no vfs_XXX operations required, cred overriding will be skipped. wj*/
+ if (!err) {
+ /* check the Android group ID */
+ parent = dget_parent(dentry);
+ if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
+ printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
+ " dentry: %s, task:%s\n",
+ __func__, dentry->d_name.name, current->comm);
+ err = -EACCES;
+ }
+ dput(parent);
+ }
+
+ if (err)
+ goto out_err;
+
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ lower_dentry = lower_path.dentry;
+ lower_inode = sdcardfs_lower_inode(inode);
+
+ /* prepare our own lower struct iattr (with the lower file) */
+ memcpy(&lower_ia, ia, sizeof(lower_ia));
+ if (ia->ia_valid & ATTR_FILE)
+ lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file);
+
+ lower_ia.ia_valid &= ~(ATTR_UID | ATTR_GID | ATTR_MODE);
+
+ /*
+ * If shrinking, first truncate upper level to cancel writing dirty
+ * pages beyond the new eof; and also if its' maxbytes is more
+ * limiting (fail with -EFBIG before making any change to the lower
+ * level). There is no need to vmtruncate the upper level
+ * afterwards in the other cases: we fsstack_copy_inode_size from
+ * the lower level.
+ */
+ if (current->mm)
+ down_write(&current->mm->mmap_sem);
+ if (ia->ia_valid & ATTR_SIZE) {
+ err = inode_newsize_ok(inode, ia->ia_size);
+ if (err) {
+ if (current->mm)
+ up_write(&current->mm->mmap_sem);
+ goto out;
+ }
+ truncate_setsize(inode, ia->ia_size);
+ }
+
+ /*
+ * mode change is for clearing setuid/setgid bits. Allow lower fs
+ * to interpret this in its own way.
+ */
+ if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
+ lower_ia.ia_valid &= ~ATTR_MODE;
+
+ /* notify the (possibly copied-up) lower inode */
+ /*
+ * Note: we use d_inode(lower_dentry), because lower_inode may be
+ * unlinked (no inode->i_sb and i_ino==0. This happens if someone
+ * tries to open(), unlink(), then ftruncate() a file.
+ */
+ mutex_lock(&d_inode(lower_dentry)->i_mutex);
+ err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */
+ NULL);
+ mutex_unlock(&d_inode(lower_dentry)->i_mutex);
+ if (current->mm)
+ up_write(&current->mm->mmap_sem);
+ if (err)
+ goto out;
+
+ /* get attributes from the lower inode and update derived permissions */
+ sdcardfs_copy_and_fix_attrs(inode, lower_inode);
+
+ /*
+ * Not running fsstack_copy_inode_size(inode, lower_inode), because
+ * VFS should update our inode size, and notify_change on
+ * lower_inode should update its size.
+ */
+
+out:
+ sdcardfs_put_lower_path(dentry, &lower_path);
+out_err:
+ return err;
+}
+
+static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *stat)
+{
+ struct dentry *lower_dentry;
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct path lower_path;
+ struct dentry *parent;
+
+ parent = dget_parent(dentry);
+ if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
+ printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
+ " dentry: %s, task:%s\n",
+ __func__, dentry->d_name.name, current->comm);
+ dput(parent);
+ return -EACCES;
+ }
+ dput(parent);
+
+ inode = d_inode(dentry);
+
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ lower_dentry = lower_path.dentry;
+ lower_inode = sdcardfs_lower_inode(inode);
+
+
+ sdcardfs_copy_and_fix_attrs(inode, lower_inode);
+ fsstack_copy_inode_size(inode, lower_inode);
+
+
+ generic_fillattr(inode, stat);
+ sdcardfs_put_lower_path(dentry, &lower_path);
+ return 0;
+}
+
+const struct inode_operations sdcardfs_symlink_iops = {
+ .permission = sdcardfs_permission,
+ .setattr = sdcardfs_setattr,
+ /* XXX Following operations are implemented,
+ * but FUSE(sdcard) or FAT does not support them
+ * These methods are *NOT* perfectly tested.
+ .readlink = sdcardfs_readlink,
+ .follow_link = sdcardfs_follow_link,
+ .put_link = kfree_put_link,
+ */
+};
+
+const struct inode_operations sdcardfs_dir_iops = {
+ .create = sdcardfs_create,
+ .lookup = sdcardfs_lookup,
+#if 0
+ .permission = sdcardfs_permission,
+#endif
+ .unlink = sdcardfs_unlink,
+ .mkdir = sdcardfs_mkdir,
+ .rmdir = sdcardfs_rmdir,
+ .rename = sdcardfs_rename,
+ .setattr = sdcardfs_setattr,
+ .getattr = sdcardfs_getattr,
+ /* XXX Following operations are implemented,
+ * but FUSE(sdcard) or FAT does not support them
+ * These methods are *NOT* perfectly tested.
+ .symlink = sdcardfs_symlink,
+ .link = sdcardfs_link,
+ .mknod = sdcardfs_mknod,
+ */
+};
+
+const struct inode_operations sdcardfs_main_iops = {
+ .permission = sdcardfs_permission,
+ .setattr = sdcardfs_setattr,
+ .getattr = sdcardfs_getattr,
+};
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
new file mode 100644
index 000000000000..a01b06a514fd
--- /dev/null
+++ b/fs/sdcardfs/lookup.c
@@ -0,0 +1,384 @@
+/*
+ * fs/sdcardfs/lookup.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#include "sdcardfs.h"
+#include "linux/delay.h"
+
+/* The dentry cache is just so we have properly sized dentries */
+static struct kmem_cache *sdcardfs_dentry_cachep;
+
+int sdcardfs_init_dentry_cache(void)
+{
+ sdcardfs_dentry_cachep =
+ kmem_cache_create("sdcardfs_dentry",
+ sizeof(struct sdcardfs_dentry_info),
+ 0, SLAB_RECLAIM_ACCOUNT, NULL);
+
+ return sdcardfs_dentry_cachep ? 0 : -ENOMEM;
+}
+
+void sdcardfs_destroy_dentry_cache(void)
+{
+ if (sdcardfs_dentry_cachep)
+ kmem_cache_destroy(sdcardfs_dentry_cachep);
+}
+
+void free_dentry_private_data(struct dentry *dentry)
+{
+ if (!dentry || !dentry->d_fsdata)
+ return;
+ kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata);
+ dentry->d_fsdata = NULL;
+}
+
+/* allocate new dentry private data */
+int new_dentry_private_data(struct dentry *dentry)
+{
+ struct sdcardfs_dentry_info *info = SDCARDFS_D(dentry);
+
+ /* use zalloc to init dentry_info.lower_path */
+ info = kmem_cache_zalloc(sdcardfs_dentry_cachep, GFP_ATOMIC);
+ if (!info)
+ return -ENOMEM;
+
+ spin_lock_init(&info->lock);
+ dentry->d_fsdata = info;
+
+ return 0;
+}
+
+struct inode_data {
+ struct inode *lower_inode;
+ userid_t id;
+};
+
+static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/)
+{
+ struct inode *current_lower_inode = sdcardfs_lower_inode(inode);
+ userid_t current_userid = SDCARDFS_I(inode)->userid;
+ if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode &&
+ current_userid == ((struct inode_data *)candidate_data)->id)
+ return 1; /* found a match */
+ else
+ return 0; /* no match */
+}
+
+static int sdcardfs_inode_set(struct inode *inode, void *lower_inode)
+{
+ /* we do actual inode initialization in sdcardfs_iget */
+ return 0;
+}
+
+struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, userid_t id)
+{
+ struct sdcardfs_inode_info *info;
+ struct inode_data data;
+ struct inode *inode; /* the new inode to return */
+ int err;
+
+ data.id = id;
+ data.lower_inode = lower_inode;
+ inode = iget5_locked(sb, /* our superblock */
+ /*
+ * hashval: we use inode number, but we can
+ * also use "(unsigned long)lower_inode"
+ * instead.
+ */
+ lower_inode->i_ino, /* hashval */
+ sdcardfs_inode_test, /* inode comparison function */
+ sdcardfs_inode_set, /* inode init function */
+ &data); /* data passed to test+set fxns */
+ if (!inode) {
+ err = -EACCES;
+ iput(lower_inode);
+ return ERR_PTR(err);
+ }
+ /* if found a cached inode, then just return it */
+ if (!(inode->i_state & I_NEW))
+ return inode;
+
+ /* initialize new inode */
+ info = SDCARDFS_I(inode);
+
+ inode->i_ino = lower_inode->i_ino;
+ if (!igrab(lower_inode)) {
+ err = -ESTALE;
+ return ERR_PTR(err);
+ }
+ sdcardfs_set_lower_inode(inode, lower_inode);
+
+ inode->i_version++;
+
+ /* use different set of inode ops for symlinks & directories */
+ if (S_ISDIR(lower_inode->i_mode))
+ inode->i_op = &sdcardfs_dir_iops;
+ else if (S_ISLNK(lower_inode->i_mode))
+ inode->i_op = &sdcardfs_symlink_iops;
+ else
+ inode->i_op = &sdcardfs_main_iops;
+
+ /* use different set of file ops for directories */
+ if (S_ISDIR(lower_inode->i_mode))
+ inode->i_fop = &sdcardfs_dir_fops;
+ else
+ inode->i_fop = &sdcardfs_main_fops;
+
+ inode->i_mapping->a_ops = &sdcardfs_aops;
+
+ inode->i_atime.tv_sec = 0;
+ inode->i_atime.tv_nsec = 0;
+ inode->i_mtime.tv_sec = 0;
+ inode->i_mtime.tv_nsec = 0;
+ inode->i_ctime.tv_sec = 0;
+ inode->i_ctime.tv_nsec = 0;
+
+ /* properly initialize special inodes */
+ if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
+ S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
+ init_special_inode(inode, lower_inode->i_mode,
+ lower_inode->i_rdev);
+
+ /* all well, copy inode attributes */
+ sdcardfs_copy_and_fix_attrs(inode, lower_inode);
+ fsstack_copy_inode_size(inode, lower_inode);
+
+ unlock_new_inode(inode);
+ return inode;
+}
+
+/*
+ * Connect a sdcardfs inode dentry/inode with several lower ones. This is
+ * the classic stackable file system "vnode interposition" action.
+ *
+ * @dentry: sdcardfs's dentry which interposes on lower one
+ * @sb: sdcardfs's super_block
+ * @lower_path: the lower path (caller does path_get/put)
+ */
+int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
+ struct path *lower_path, userid_t id)
+{
+ int err = 0;
+ struct inode *inode;
+ struct inode *lower_inode;
+ struct super_block *lower_sb;
+
+ lower_inode = lower_path->dentry->d_inode;
+ lower_sb = sdcardfs_lower_super(sb);
+
+ /* check that the lower file system didn't cross a mount point */
+ if (lower_inode->i_sb != lower_sb) {
+ err = -EXDEV;
+ goto out;
+ }
+
+ /*
+ * We allocate our new inode below by calling sdcardfs_iget,
+ * which will initialize some of the new inode's fields
+ */
+
+ /* inherit lower inode number for sdcardfs's inode */
+ inode = sdcardfs_iget(sb, lower_inode, id);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out;
+ }
+
+ d_add(dentry, inode);
+ update_derived_permission_lock(dentry);
+out:
+ return err;
+}
+
+/*
+ * Main driver function for sdcardfs's lookup.
+ *
+ * Returns: NULL (ok), ERR_PTR if an error occurred.
+ * Fills in lower_parent_path with <dentry,mnt> on success.
+ */
+static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
+ unsigned int flags, struct path *lower_parent_path, userid_t id)
+{
+ int err = 0;
+ struct vfsmount *lower_dir_mnt;
+ struct dentry *lower_dir_dentry = NULL;
+ struct dentry *lower_dentry;
+ const char *name;
+ struct path lower_path;
+ struct qstr this;
+ struct sdcardfs_sb_info *sbi;
+
+ sbi = SDCARDFS_SB(dentry->d_sb);
+ /* must initialize dentry operations */
+ d_set_d_op(dentry, &sdcardfs_ci_dops);
+
+ if (IS_ROOT(dentry))
+ goto out;
+
+ name = dentry->d_name.name;
+
+ /* now start the actual lookup procedure */
+ lower_dir_dentry = lower_parent_path->dentry;
+ lower_dir_mnt = lower_parent_path->mnt;
+
+ /* Use vfs_path_lookup to check if the dentry exists or not */
+ err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
+ &lower_path);
+
+ /* no error: handle positive dentries */
+ if (!err) {
+ /* check if the dentry is an obb dentry
+ * if true, the lower_inode must be replaced with
+ * the inode of the graft path */
+
+ if(need_graft_path(dentry)) {
+
+ /* setup_obb_dentry()
+ * The lower_path will be stored to the dentry's orig_path
+ * and the base obbpath will be copyed to the lower_path variable.
+ * if an error returned, there's no change in the lower_path
+ * returns: -ERRNO if error (0: no error) */
+ err = setup_obb_dentry(dentry, &lower_path);
+
+ if(err) {
+ /* if the sbi->obbpath is not available, we can optionally
+ * setup the lower_path with its orig_path.
+ * but, the current implementation just returns an error
+ * because the sdcard daemon also regards this case as
+ * a lookup fail. */
+ printk(KERN_INFO "sdcardfs: base obbpath is not available\n");
+ sdcardfs_put_reset_orig_path(dentry);
+ goto out;
+ }
+ }
+
+ sdcardfs_set_lower_path(dentry, &lower_path);
+ err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id);
+ if (err) /* path_put underlying path on error */
+ sdcardfs_put_reset_lower_path(dentry);
+ goto out;
+ }
+
+ /*
+ * We don't consider ENOENT an error, and we want to return a
+ * negative dentry.
+ */
+ if (err && err != -ENOENT)
+ goto out;
+
+ /* instatiate a new negative dentry */
+ this.name = name;
+ this.len = strlen(name);
+ this.hash = full_name_hash(this.name, this.len);
+ lower_dentry = d_lookup(lower_dir_dentry, &this);
+ if (lower_dentry)
+ goto setup_lower;
+
+ lower_dentry = d_alloc(lower_dir_dentry, &this);
+ if (!lower_dentry) {
+ err = -ENOMEM;
+ goto out;
+ }
+ d_add(lower_dentry, NULL); /* instantiate and hash */
+
+setup_lower:
+ lower_path.dentry = lower_dentry;
+ lower_path.mnt = mntget(lower_dir_mnt);
+ sdcardfs_set_lower_path(dentry, &lower_path);
+
+ /*
+ * If the intent is to create a file, then don't return an error, so
+ * the VFS will continue the process of making this negative dentry
+ * into a positive one.
+ */
+ if (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET))
+ err = 0;
+
+out:
+ return ERR_PTR(err);
+}
+
+/*
+ * On success:
+ * fills dentry object appropriate values and returns NULL.
+ * On fail (== error)
+ * returns error ptr
+ *
+ * @dir : Parent inode. It is locked (dir->i_mutex)
+ * @dentry : Target dentry to lookup. we should set each of fields.
+ * (dentry->d_name is initialized already)
+ * @nd : nameidata of parent inode
+ */
+struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags)
+{
+ struct dentry *ret = NULL, *parent;
+ struct path lower_parent_path;
+ int err = 0;
+ const struct cred *saved_cred = NULL;
+
+ parent = dget_parent(dentry);
+
+ if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
+ ret = ERR_PTR(-EACCES);
+ printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
+ " dentry: %s, task:%s\n",
+ __func__, dentry->d_name.name, current->comm);
+ goto out_err;
+ }
+
+ /* save current_cred and override it */
+ OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred);
+
+ sdcardfs_get_lower_path(parent, &lower_parent_path);
+
+ /* allocate dentry private data. We free it in ->d_release */
+ err = new_dentry_private_data(dentry);
+ if (err) {
+ ret = ERR_PTR(err);
+ goto out;
+ }
+
+ ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid);
+ if (IS_ERR(ret))
+ {
+ goto out;
+ }
+ if (ret)
+ dentry = ret;
+ if (dentry->d_inode) {
+ fsstack_copy_attr_times(dentry->d_inode,
+ sdcardfs_lower_inode(dentry->d_inode));
+ /* get drived permission */
+ mutex_lock(&dentry->d_inode->i_mutex);
+ get_derived_permission(parent, dentry);
+ fix_derived_permission(dentry->d_inode);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ }
+ /* update parent directory's atime */
+ fsstack_copy_attr_atime(parent->d_inode,
+ sdcardfs_lower_inode(parent->d_inode));
+
+out:
+ sdcardfs_put_lower_path(parent, &lower_parent_path);
+ REVERT_CRED(saved_cred);
+out_err:
+ dput(parent);
+ return ret;
+}
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
new file mode 100644
index 000000000000..a6522286d731
--- /dev/null
+++ b/fs/sdcardfs/main.c
@@ -0,0 +1,402 @@
+/*
+ * fs/sdcardfs/main.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#include "sdcardfs.h"
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/parser.h>
+
+enum {
+ Opt_fsuid,
+ Opt_fsgid,
+ Opt_gid,
+ Opt_debug,
+ Opt_lower_fs,
+ Opt_mask,
+ Opt_multiuser, // May need?
+ Opt_userid,
+ Opt_reserved_mb,
+ Opt_err,
+};
+
+static const match_table_t sdcardfs_tokens = {
+ {Opt_fsuid, "fsuid=%u"},
+ {Opt_fsgid, "fsgid=%u"},
+ {Opt_gid, "gid=%u"},
+ {Opt_debug, "debug"},
+ {Opt_mask, "mask=%u"},
+ {Opt_userid, "userid=%d"},
+ {Opt_multiuser, "multiuser"},
+ {Opt_reserved_mb, "reserved_mb=%u"},
+ {Opt_err, NULL}
+};
+
+static int parse_options(struct super_block *sb, char *options, int silent,
+ int *debug, struct sdcardfs_mount_options *opts)
+{
+ char *p;
+ substring_t args[MAX_OPT_ARGS];
+ int option;
+
+ /* by default, we use AID_MEDIA_RW as uid, gid */
+ opts->fs_low_uid = AID_MEDIA_RW;
+ opts->fs_low_gid = AID_MEDIA_RW;
+ opts->mask = 0;
+ opts->multiuser = false;
+ opts->fs_user_id = 0;
+ opts->gid = 0;
+ /* by default, 0MB is reserved */
+ opts->reserved_mb = 0;
+
+ *debug = 0;
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ int token;
+ if (!*p)
+ continue;
+
+ token = match_token(p, sdcardfs_tokens, args);
+
+ switch (token) {
+ case Opt_debug:
+ *debug = 1;
+ break;
+ case Opt_fsuid:
+ if (match_int(&args[0], &option))
+ return 0;
+ opts->fs_low_uid = option;
+ break;
+ case Opt_fsgid:
+ if (match_int(&args[0], &option))
+ return 0;
+ opts->fs_low_gid = option;
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ return 0;
+ opts->gid = option;
+ break;
+ case Opt_userid:
+ if (match_int(&args[0], &option))
+ return 0;
+ opts->fs_user_id = option;
+ break;
+ case Opt_mask:
+ if (match_int(&args[0], &option))
+ return 0;
+ opts->mask = option;
+ break;
+ case Opt_multiuser:
+ opts->multiuser = true;
+ break;
+ case Opt_reserved_mb:
+ if (match_int(&args[0], &option))
+ return 0;
+ opts->reserved_mb = option;
+ break;
+ /* unknown option */
+ default:
+ if (!silent) {
+ printk( KERN_ERR "Unrecognized mount option \"%s\" "
+ "or missing value", p);
+ }
+ return -EINVAL;
+ }
+ }
+
+ if (*debug) {
+ printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug);
+ printk( KERN_INFO "sdcardfs : options - uid:%d\n",
+ opts->fs_low_uid);
+ printk( KERN_INFO "sdcardfs : options - gid:%d\n",
+ opts->fs_low_gid);
+ }
+
+ return 0;
+}
+
+#if 0
+/*
+ * our custom d_alloc_root work-alike
+ *
+ * we can't use d_alloc_root if we want to use our own interpose function
+ * unchanged, so we simply call our own "fake" d_alloc_root
+ */
+static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb)
+{
+ struct dentry *ret = NULL;
+
+ if (sb) {
+ static const struct qstr name = {
+ .name = "/",
+ .len = 1
+ };
+
+ ret = d_alloc(NULL, &name);
+ if (ret) {
+ d_set_d_op(ret, &sdcardfs_ci_dops);
+ ret->d_sb = sb;
+ ret->d_parent = ret;
+ }
+ }
+ return ret;
+}
+#endif
+
+DEFINE_MUTEX(sdcardfs_super_list_lock);
+LIST_HEAD(sdcardfs_super_list);
+EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock);
+EXPORT_SYMBOL_GPL(sdcardfs_super_list);
+
+/*
+ * There is no need to lock the sdcardfs_super_info's rwsem as there is no
+ * way anyone can have a reference to the superblock at this point in time.
+ */
+static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
+ void *raw_data, int silent)
+{
+ int err = 0;
+ int debug;
+ struct super_block *lower_sb;
+ struct path lower_path;
+ struct sdcardfs_sb_info *sb_info;
+ struct inode *inode;
+
+ printk(KERN_INFO "sdcardfs version 2.0\n");
+
+ if (!dev_name) {
+ printk(KERN_ERR
+ "sdcardfs: read_super: missing dev_name argument\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name);
+ printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data);
+
+ /* parse lower path */
+ err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
+ &lower_path);
+ if (err) {
+ printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name);
+ goto out;
+ }
+
+ /* allocate superblock private data */
+ sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL);
+ if (!SDCARDFS_SB(sb)) {
+ printk(KERN_CRIT "sdcardfs: read_super: out of memory\n");
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ sb_info = sb->s_fs_info;
+ /* parse options */
+ err = parse_options(sb, raw_data, silent, &debug, &sb_info->options);
+ if (err) {
+ printk(KERN_ERR "sdcardfs: invalid options\n");
+ goto out_freesbi;
+ }
+
+ /* set the lower superblock field of upper superblock */
+ lower_sb = lower_path.dentry->d_sb;
+ atomic_inc(&lower_sb->s_active);
+ sdcardfs_set_lower_super(sb, lower_sb);
+
+ /* inherit maxbytes from lower file system */
+ sb->s_maxbytes = lower_sb->s_maxbytes;
+
+ /*
+ * Our c/m/atime granularity is 1 ns because we may stack on file
+ * systems whose granularity is as good.
+ */
+ sb->s_time_gran = 1;
+
+ sb->s_magic = SDCARDFS_SUPER_MAGIC;
+ sb->s_op = &sdcardfs_sops;
+
+ /* get a new inode and allocate our root dentry */
+ inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out_sput;
+ }
+ sb->s_root = d_make_root(inode);
+ if (!sb->s_root) {
+ err = -ENOMEM;
+ goto out_iput;
+ }
+ d_set_d_op(sb->s_root, &sdcardfs_ci_dops);
+
+ /* link the upper and lower dentries */
+ sb->s_root->d_fsdata = NULL;
+ err = new_dentry_private_data(sb->s_root);
+ if (err)
+ goto out_freeroot;
+
+ /* set the lower dentries for s_root */
+ sdcardfs_set_lower_path(sb->s_root, &lower_path);
+
+ /*
+ * No need to call interpose because we already have a positive
+ * dentry, which was instantiated by d_make_root. Just need to
+ * d_rehash it.
+ */
+ d_rehash(sb->s_root);
+
+ /* setup permission policy */
+ sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
+ mutex_lock(&sdcardfs_super_list_lock);
+ if(sb_info->options.multiuser) {
+ setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false);
+ snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
+ /*err = prepare_dir(sb_info->obbpath_s,
+ sb_info->options.fs_low_uid,
+ sb_info->options.fs_low_gid, 00755);*/
+ } else {
+ setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false);
+ snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
+ }
+ fix_derived_permission(sb->s_root->d_inode);
+ sb_info->sb = sb;
+ list_add(&sb_info->list, &sdcardfs_super_list);
+ mutex_unlock(&sdcardfs_super_list_lock);
+
+ if (!silent)
+ printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n",
+ dev_name, lower_sb->s_type->name);
+ goto out; /* all is well */
+
+ /* no longer needed: free_dentry_private_data(sb->s_root); */
+out_freeroot:
+ dput(sb->s_root);
+out_iput:
+ iput(inode);
+out_sput:
+ /* drop refs we took earlier */
+ atomic_dec(&lower_sb->s_active);
+out_freesbi:
+ kfree(SDCARDFS_SB(sb));
+ sb->s_fs_info = NULL;
+out_free:
+ path_put(&lower_path);
+
+out:
+ return err;
+}
+
+/* A feature which supports mount_nodev() with options */
+static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data,
+ int (*fill_super)(struct super_block *, const char *, void *, int))
+
+{
+ int error;
+ struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
+
+ if (IS_ERR(s))
+ return ERR_CAST(s);
+
+ s->s_flags = flags;
+
+ error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0);
+ if (error) {
+ deactivate_locked_super(s);
+ return ERR_PTR(error);
+ }
+ s->s_flags |= MS_ACTIVE;
+ return dget(s->s_root);
+}
+
+struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *raw_data)
+{
+ /*
+ * dev_name is a lower_path_name,
+ * raw_data is a option string.
+ */
+ return mount_nodev_with_options(fs_type, flags, dev_name,
+ raw_data, sdcardfs_read_super);
+}
+
+void sdcardfs_kill_sb(struct super_block *sb) {
+ struct sdcardfs_sb_info *sbi;
+ if (sb->s_magic == SDCARDFS_SUPER_MAGIC) {
+ sbi = SDCARDFS_SB(sb);
+ mutex_lock(&sdcardfs_super_list_lock);
+ list_del(&sbi->list);
+ mutex_unlock(&sdcardfs_super_list_lock);
+ }
+ generic_shutdown_super(sb);
+}
+
+static struct file_system_type sdcardfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = SDCARDFS_NAME,
+ .mount = sdcardfs_mount,
+ .kill_sb = sdcardfs_kill_sb,
+ .fs_flags = 0,
+};
+
+static int __init init_sdcardfs_fs(void)
+{
+ int err;
+
+ pr_info("Registering sdcardfs " SDCARDFS_VERSION "\n");
+
+ err = sdcardfs_init_inode_cache();
+ if (err)
+ goto out;
+ err = sdcardfs_init_dentry_cache();
+ if (err)
+ goto out;
+ err = packagelist_init();
+ if (err)
+ goto out;
+ err = register_filesystem(&sdcardfs_fs_type);
+out:
+ if (err) {
+ sdcardfs_destroy_inode_cache();
+ sdcardfs_destroy_dentry_cache();
+ packagelist_exit();
+ }
+ return err;
+}
+
+static void __exit exit_sdcardfs_fs(void)
+{
+ sdcardfs_destroy_inode_cache();
+ sdcardfs_destroy_dentry_cache();
+ packagelist_exit();
+ unregister_filesystem(&sdcardfs_fs_type);
+ pr_info("Completed sdcardfs module unload\n");
+}
+
+MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University"
+ " (http://www.fsl.cs.sunysb.edu/)");
+MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION
+ " (http://wrapfs.filesystems.org/)");
+MODULE_LICENSE("GPL");
+
+module_init(init_sdcardfs_fs);
+module_exit(exit_sdcardfs_fs);
diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c
new file mode 100644
index 000000000000..e21f64675a80
--- /dev/null
+++ b/fs/sdcardfs/mmap.c
@@ -0,0 +1,81 @@
+/*
+ * fs/sdcardfs/mmap.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#include "sdcardfs.h"
+
+static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ int err;
+ struct file *file, *lower_file;
+ const struct vm_operations_struct *lower_vm_ops;
+ struct vm_area_struct lower_vma;
+
+ memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
+ file = lower_vma.vm_file;
+ lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
+ BUG_ON(!lower_vm_ops);
+
+ lower_file = sdcardfs_lower_file(file);
+ /*
+ * XXX: vm_ops->fault may be called in parallel. Because we have to
+ * resort to temporarily changing the vma->vm_file to point to the
+ * lower file, a concurrent invocation of sdcardfs_fault could see a
+ * different value. In this workaround, we keep a different copy of
+ * the vma structure in our stack, so we never expose a different
+ * value of the vma->vm_file called to us, even temporarily. A
+ * better fix would be to change the calling semantics of ->fault to
+ * take an explicit file pointer.
+ */
+ lower_vma.vm_file = lower_file;
+ err = lower_vm_ops->fault(&lower_vma, vmf);
+ return err;
+}
+
+static ssize_t sdcardfs_direct_IO(struct kiocb *iocb,
+ struct iov_iter *iter, loff_t pos)
+{
+ /*
+ * This function returns zero on purpose in order to support direct IO.
+ * __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null.
+ *
+ * However, this function won't be called by certain file operations
+ * including generic fs functions. * reads and writes are delivered to
+ * the lower file systems and the direct IOs will be handled by them.
+ *
+ * NOTE: exceptionally, on the recent kernels (since Linux 3.8.x),
+ * swap_writepage invokes this function directly.
+ */
+ printk(KERN_INFO "%s, operation is not supported\n", __func__);
+ return 0;
+}
+
+/*
+ * XXX: the default address_space_ops for sdcardfs is empty. We cannot set
+ * our inode->i_mapping->a_ops to NULL because too many code paths expect
+ * the a_ops vector to be non-NULL.
+ */
+const struct address_space_operations sdcardfs_aops = {
+ /* empty on purpose */
+ .direct_IO = sdcardfs_direct_IO,
+};
+
+const struct vm_operations_struct sdcardfs_vm_ops = {
+ .fault = sdcardfs_fault,
+};
diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h
new file mode 100644
index 000000000000..923ba101dfa9
--- /dev/null
+++ b/fs/sdcardfs/multiuser.h
@@ -0,0 +1,37 @@
+/*
+ * fs/sdcardfs/multiuser.h
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#define MULTIUSER_APP_PER_USER_RANGE 100000
+
+typedef uid_t userid_t;
+typedef uid_t appid_t;
+
+static inline userid_t multiuser_get_user_id(uid_t uid) {
+ return uid / MULTIUSER_APP_PER_USER_RANGE;
+}
+
+static inline appid_t multiuser_get_app_id(uid_t uid) {
+ return uid % MULTIUSER_APP_PER_USER_RANGE;
+}
+
+static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
+ return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
+}
+
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
new file mode 100644
index 000000000000..10f0d6be718b
--- /dev/null
+++ b/fs/sdcardfs/packagelist.c
@@ -0,0 +1,437 @@
+/*
+ * fs/sdcardfs/packagelist.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#include "sdcardfs.h"
+#include <linux/hashtable.h>
+#include <linux/delay.h>
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/configfs.h>
+
+#define STRING_BUF_SIZE (512)
+
+struct hashtable_entry {
+ struct hlist_node hlist;
+ void *key;
+ unsigned int value;
+};
+
+struct sb_list {
+ struct super_block *sb;
+ struct list_head list;
+};
+
+struct packagelist_data {
+ DECLARE_HASHTABLE(package_to_appid,8);
+ struct mutex hashtable_lock;
+
+};
+
+static struct packagelist_data *pkgl_data_all;
+
+static struct kmem_cache *hashtable_entry_cachep;
+
+static unsigned int str_hash(const char *key) {
+ int i;
+ unsigned int h = strlen(key);
+ char *data = (char *)key;
+
+ for (i = 0; i < strlen(key); i++) {
+ h = h * 31 + *data;
+ data++;
+ }
+ return h;
+}
+
+appid_t get_appid(void *pkgl_id, const char *app_name)
+{
+ struct packagelist_data *pkgl_dat = pkgl_data_all;
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = str_hash(app_name);
+ appid_t ret_id;
+
+ mutex_lock(&pkgl_dat->hashtable_lock);
+ hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
+ if (!strcasecmp(app_name, hash_cur->key)) {
+ ret_id = (appid_t)hash_cur->value;
+ mutex_unlock(&pkgl_dat->hashtable_lock);
+ return ret_id;
+ }
+ }
+ mutex_unlock(&pkgl_dat->hashtable_lock);
+ return 0;
+}
+
+/* Kernel has already enforced everything we returned through
+ * derive_permissions_locked(), so this is used to lock down access
+ * even further, such as enforcing that apps hold sdcard_rw. */
+int check_caller_access_to_name(struct inode *parent_node, const char* name) {
+
+ /* Always block security-sensitive files at root */
+ if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) {
+ if (!strcasecmp(name, "autorun.inf")
+ || !strcasecmp(name, ".android_secure")
+ || !strcasecmp(name, "android_secure")) {
+ return 0;
+ }
+ }
+
+ /* Root always has access; access for any other UIDs should always
+ * be controlled through packages.list. */
+ if (from_kuid(&init_user_ns, current_fsuid()) == 0) {
+ return 1;
+ }
+
+ /* No extra permissions to enforce */
+ return 1;
+}
+
+/* This function is used when file opening. The open flags must be
+ * checked before calling check_caller_access_to_name() */
+int open_flags_to_access_mode(int open_flags) {
+ if((open_flags & O_ACCMODE) == O_RDONLY) {
+ return 0; /* R_OK */
+ } else if ((open_flags & O_ACCMODE) == O_WRONLY) {
+ return 1; /* W_OK */
+ } else {
+ /* Probably O_RDRW, but treat as default to be safe */
+ return 1; /* R_OK | W_OK */
+ }
+}
+
+static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key,
+ unsigned int value)
+{
+ struct hashtable_entry *hash_cur;
+ struct hashtable_entry *new_entry;
+ unsigned int hash = str_hash(key);
+
+ hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
+ if (!strcasecmp(key, hash_cur->key)) {
+ hash_cur->value = value;
+ return 0;
+ }
+ }
+ new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL);
+ if (!new_entry)
+ return -ENOMEM;
+ new_entry->key = kstrdup(key, GFP_KERNEL);
+ new_entry->value = value;
+ hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash);
+ return 0;
+}
+
+static void fixup_perms(struct super_block *sb) {
+ if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) {
+ mutex_lock(&sb->s_root->d_inode->i_mutex);
+ get_derive_permissions_recursive(sb->s_root);
+ mutex_unlock(&sb->s_root->d_inode->i_mutex);
+ }
+}
+
+static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key,
+ unsigned int value) {
+ int ret;
+ struct sdcardfs_sb_info *sbinfo;
+ mutex_lock(&sdcardfs_super_list_lock);
+ mutex_lock(&pkgl_dat->hashtable_lock);
+ ret = insert_str_to_int_lock(pkgl_dat, key, value);
+ mutex_unlock(&pkgl_dat->hashtable_lock);
+
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+ if (sbinfo) {
+ fixup_perms(sbinfo->sb);
+ }
+ }
+ mutex_unlock(&sdcardfs_super_list_lock);
+ return ret;
+}
+
+static void remove_str_to_int_lock(struct hashtable_entry *h_entry) {
+ kfree(h_entry->key);
+ hash_del(&h_entry->hlist);
+ kmem_cache_free(hashtable_entry_cachep, h_entry);
+}
+
+static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key)
+{
+ struct sdcardfs_sb_info *sbinfo;
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = str_hash(key);
+ mutex_lock(&sdcardfs_super_list_lock);
+ mutex_lock(&pkgl_dat->hashtable_lock);
+ hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
+ if (!strcasecmp(key, hash_cur->key)) {
+ remove_str_to_int_lock(hash_cur);
+ break;
+ }
+ }
+ mutex_unlock(&pkgl_dat->hashtable_lock);
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+ if (sbinfo) {
+ fixup_perms(sbinfo->sb);
+ }
+ }
+ mutex_unlock(&sdcardfs_super_list_lock);
+ return;
+}
+
+static void remove_all_hashentrys(struct packagelist_data *pkgl_dat)
+{
+ struct hashtable_entry *hash_cur;
+ struct hlist_node *h_t;
+ int i;
+ mutex_lock(&pkgl_dat->hashtable_lock);
+ hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist)
+ remove_str_to_int_lock(hash_cur);
+ mutex_unlock(&pkgl_dat->hashtable_lock);
+ hash_init(pkgl_dat->package_to_appid);
+}
+
+static struct packagelist_data * packagelist_create(void)
+{
+ struct packagelist_data *pkgl_dat;
+
+ pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO);
+ if (!pkgl_dat) {
+ printk(KERN_ERR "sdcardfs: Failed to create hash\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ mutex_init(&pkgl_dat->hashtable_lock);
+ hash_init(pkgl_dat->package_to_appid);
+
+ return pkgl_dat;
+}
+
+static void packagelist_destroy(struct packagelist_data *pkgl_dat)
+{
+ remove_all_hashentrys(pkgl_dat);
+ printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n");
+ kfree(pkgl_dat);
+}
+
+struct package_appid {
+ struct config_item item;
+ int add_pid;
+};
+
+static inline struct package_appid *to_package_appid(struct config_item *item)
+{
+ return item ? container_of(item, struct package_appid, item) : NULL;
+}
+
+static ssize_t package_appid_attr_show(struct config_item *item,
+ char *page)
+{
+ ssize_t count;
+ count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name));
+ return count;
+}
+
+static ssize_t package_appid_attr_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct package_appid *package_appid = to_package_appid(item);
+ unsigned long tmp;
+ char *p = (char *) page;
+ int ret;
+
+ tmp = simple_strtoul(p, &p, 10);
+ if (!p || (*p && (*p != '\n')))
+ return -EINVAL;
+
+ if (tmp > INT_MAX)
+ return -ERANGE;
+ ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp);
+ package_appid->add_pid = tmp;
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static struct configfs_attribute package_appid_attr_add_pid = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "appid",
+ .ca_mode = S_IRUGO | S_IWUGO,
+ .show = package_appid_attr_show,
+ .store = package_appid_attr_store,
+};
+
+static struct configfs_attribute *package_appid_attrs[] = {
+ &package_appid_attr_add_pid,
+ NULL,
+};
+
+static void package_appid_release(struct config_item *item)
+{
+ printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name);
+ /* item->ci_name is freed already, so we rely on the dentry */
+ remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name);
+ kfree(to_package_appid(item));
+}
+
+static struct configfs_item_operations package_appid_item_ops = {
+ .release = package_appid_release,
+};
+
+static struct config_item_type package_appid_type = {
+ .ct_item_ops = &package_appid_item_ops,
+ .ct_attrs = package_appid_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+
+struct sdcardfs_packages {
+ struct config_group group;
+};
+
+static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item)
+{
+ return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL;
+}
+
+static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name)
+{
+ struct package_appid *package_appid;
+
+ package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL);
+ if (!package_appid)
+ return ERR_PTR(-ENOMEM);
+
+ config_item_init_type_name(&package_appid->item, name,
+ &package_appid_type);
+
+ package_appid->add_pid = 0;
+
+ return &package_appid->item;
+}
+
+static ssize_t packages_attr_show(struct config_item *item,
+ char *page)
+{
+ struct hashtable_entry *hash_cur;
+ struct hlist_node *h_t;
+ int i;
+ int count = 0;
+ mutex_lock(&pkgl_data_all->hashtable_lock);
+ hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist)
+ count += snprintf(page + count, PAGE_SIZE - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value);
+ mutex_unlock(&pkgl_data_all->hashtable_lock);
+
+
+ return count;
+}
+
+static struct configfs_attribute sdcardfs_packages_attr_description = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "packages_gid.list",
+ .ca_mode = S_IRUGO,
+ .show = packages_attr_show,
+};
+
+static struct configfs_attribute *sdcardfs_packages_attrs[] = {
+ &sdcardfs_packages_attr_description,
+ NULL,
+};
+
+static void sdcardfs_packages_release(struct config_item *item)
+{
+
+ printk(KERN_INFO "sdcardfs: destroyed something?\n");
+ kfree(to_sdcardfs_packages(item));
+}
+
+static struct configfs_item_operations sdcardfs_packages_item_ops = {
+ .release = sdcardfs_packages_release,
+};
+
+/*
+ * Note that, since no extra work is required on ->drop_item(),
+ * no ->drop_item() is provided.
+ */
+static struct configfs_group_operations sdcardfs_packages_group_ops = {
+ .make_item = sdcardfs_packages_make_item,
+};
+
+static struct config_item_type sdcardfs_packages_type = {
+ .ct_item_ops = &sdcardfs_packages_item_ops,
+ .ct_group_ops = &sdcardfs_packages_group_ops,
+ .ct_attrs = sdcardfs_packages_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem sdcardfs_packages_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "sdcardfs",
+ .ci_type = &sdcardfs_packages_type,
+ },
+ },
+};
+
+static int configfs_sdcardfs_init(void)
+{
+ int ret;
+ struct configfs_subsystem *subsys = &sdcardfs_packages_subsys;
+
+ config_group_init(&subsys->su_group);
+ mutex_init(&subsys->su_mutex);
+ ret = configfs_register_subsystem(subsys);
+ if (ret) {
+ printk(KERN_ERR "Error %d while registering subsystem %s\n",
+ ret,
+ subsys->su_group.cg_item.ci_namebuf);
+ }
+ return ret;
+}
+
+static void configfs_sdcardfs_exit(void)
+{
+ configfs_unregister_subsystem(&sdcardfs_packages_subsys);
+}
+
+int packagelist_init(void)
+{
+ hashtable_entry_cachep =
+ kmem_cache_create("packagelist_hashtable_entry",
+ sizeof(struct hashtable_entry), 0, 0, NULL);
+ if (!hashtable_entry_cachep) {
+ printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n");
+ return -ENOMEM;
+ }
+
+ pkgl_data_all = packagelist_create();
+ configfs_sdcardfs_init();
+ return 0;
+}
+
+void packagelist_exit(void)
+{
+ configfs_sdcardfs_exit();
+ packagelist_destroy(pkgl_data_all);
+ if (hashtable_entry_cachep)
+ kmem_cache_destroy(hashtable_entry_cachep);
+}
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
new file mode 100644
index 000000000000..f111f898b630
--- /dev/null
+++ b/fs/sdcardfs/sdcardfs.h
@@ -0,0 +1,530 @@
+/*
+ * fs/sdcardfs/sdcardfs.h
+ *
+ * The sdcardfs v2.0
+ * This file system replaces the sdcard daemon on Android
+ * On version 2.0, some of the daemon functions have been ported
+ * to support the multi-user concepts of Android 4.4
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#ifndef _SDCARDFS_H_
+#define _SDCARDFS_H_
+
+#include <linux/dcache.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/seq_file.h>
+#include <linux/statfs.h>
+#include <linux/fs_stack.h>
+#include <linux/magic.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/security.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include "multiuser.h"
+
+/* the file system name */
+#define SDCARDFS_NAME "sdcardfs"
+
+/* sdcardfs root inode number */
+#define SDCARDFS_ROOT_INO 1
+
+/* useful for tracking code reachability */
+#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__)
+
+#define SDCARDFS_DIRENT_SIZE 256
+
+/* temporary static uid settings for development */
+#define AID_ROOT 0 /* uid for accessing /mnt/sdcard & extSdcard */
+#define AID_MEDIA_RW 1023 /* internal media storage write access */
+
+#define AID_SDCARD_RW 1015 /* external storage write access */
+#define AID_SDCARD_R 1028 /* external storage read access */
+#define AID_SDCARD_PICS 1033 /* external storage photos access */
+#define AID_SDCARD_AV 1034 /* external storage audio/video access */
+#define AID_SDCARD_ALL 1035 /* access all users external storage */
+
+#define AID_PACKAGE_INFO 1027
+
+#define fix_derived_permission(x) \
+ do { \
+ (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \
+ (x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \
+ (x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\
+ } while (0)
+
+
+/* OVERRIDE_CRED() and REVERT_CRED()
+ * OVERRID_CRED()
+ * backup original task->cred
+ * and modifies task->cred->fsuid/fsgid to specified value.
+ * REVERT_CRED()
+ * restore original task->cred->fsuid/fsgid.
+ * These two macro should be used in pair, and OVERRIDE_CRED() should be
+ * placed at the beginning of a function, right after variable declaration.
+ */
+#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \
+ saved_cred = override_fsids(sdcardfs_sbi); \
+ if (!saved_cred) { return -ENOMEM; }
+
+#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \
+ saved_cred = override_fsids(sdcardfs_sbi); \
+ if (!saved_cred) { return ERR_PTR(-ENOMEM); }
+
+#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
+
+#define DEBUG_CRED() \
+ printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \
+ __FUNCTION__, __LINE__, \
+ (int)current->cred->fsuid, \
+ (int)current->cred->fsgid);
+
+/* Android 5.0 support */
+
+/* Permission mode for a specific node. Controls how file permissions
+ * are derived for children nodes. */
+typedef enum {
+ /* Nothing special; this node should just inherit from its parent. */
+ PERM_INHERIT,
+ /* This node is one level above a normal root; used for legacy layouts
+ * which use the first level to represent user_id. */
+ PERM_PRE_ROOT,
+ /* This node is "/" */
+ PERM_ROOT,
+ /* This node is "/Android" */
+ PERM_ANDROID,
+ /* This node is "/Android/data" */
+ PERM_ANDROID_DATA,
+ /* This node is "/Android/obb" */
+ PERM_ANDROID_OBB,
+ /* This node is "/Android/media" */
+ PERM_ANDROID_MEDIA,
+} perm_t;
+
+struct sdcardfs_sb_info;
+struct sdcardfs_mount_options;
+
+/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
+const struct cred * override_fsids(struct sdcardfs_sb_info* sbi);
+/* Do not directly use this function, use REVERT_CRED() instead. */
+void revert_fsids(const struct cred * old_cred);
+
+/* operations vectors defined in specific files */
+extern const struct file_operations sdcardfs_main_fops;
+extern const struct file_operations sdcardfs_dir_fops;
+extern const struct inode_operations sdcardfs_main_iops;
+extern const struct inode_operations sdcardfs_dir_iops;
+extern const struct inode_operations sdcardfs_symlink_iops;
+extern const struct super_operations sdcardfs_sops;
+extern const struct dentry_operations sdcardfs_ci_dops;
+extern const struct address_space_operations sdcardfs_aops, sdcardfs_dummy_aops;
+extern const struct vm_operations_struct sdcardfs_vm_ops;
+
+extern int sdcardfs_init_inode_cache(void);
+extern void sdcardfs_destroy_inode_cache(void);
+extern int sdcardfs_init_dentry_cache(void);
+extern void sdcardfs_destroy_dentry_cache(void);
+extern int new_dentry_private_data(struct dentry *dentry);
+extern void free_dentry_private_data(struct dentry *dentry);
+extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags);
+extern struct inode *sdcardfs_iget(struct super_block *sb,
+ struct inode *lower_inode, userid_t id);
+extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
+ struct path *lower_path, userid_t id);
+
+/* file private data */
+struct sdcardfs_file_info {
+ struct file *lower_file;
+ const struct vm_operations_struct *lower_vm_ops;
+};
+
+/* sdcardfs inode data in memory */
+struct sdcardfs_inode_info {
+ struct inode *lower_inode;
+ /* state derived based on current position in hierachy */
+ perm_t perm;
+ userid_t userid;
+ uid_t d_uid;
+ bool under_android;
+
+ struct inode vfs_inode;
+};
+
+
+/* sdcardfs dentry data in memory */
+struct sdcardfs_dentry_info {
+ spinlock_t lock; /* protects lower_path */
+ struct path lower_path;
+ struct path orig_path;
+};
+
+struct sdcardfs_mount_options {
+ uid_t fs_low_uid;
+ gid_t fs_low_gid;
+ userid_t fs_user_id;
+ gid_t gid;
+ mode_t mask;
+ bool multiuser;
+ unsigned int reserved_mb;
+};
+
+/* sdcardfs super-block data in memory */
+struct sdcardfs_sb_info {
+ struct super_block *sb;
+ struct super_block *lower_sb;
+ /* derived perm policy : some of options have been added
+ * to sdcardfs_mount_options (Android 4.4 support) */
+ struct sdcardfs_mount_options options;
+ spinlock_t lock; /* protects obbpath */
+ char *obbpath_s;
+ struct path obbpath;
+ void *pkgl_id;
+ struct list_head list;
+};
+
+/*
+ * inode to private data
+ *
+ * Since we use containers and the struct inode is _inside_ the
+ * sdcardfs_inode_info structure, SDCARDFS_I will always (given a non-NULL
+ * inode pointer), return a valid non-NULL pointer.
+ */
+static inline struct sdcardfs_inode_info *SDCARDFS_I(const struct inode *inode)
+{
+ return container_of(inode, struct sdcardfs_inode_info, vfs_inode);
+}
+
+/* dentry to private data */
+#define SDCARDFS_D(dent) ((struct sdcardfs_dentry_info *)(dent)->d_fsdata)
+
+/* superblock to private data */
+#define SDCARDFS_SB(super) ((struct sdcardfs_sb_info *)(super)->s_fs_info)
+
+/* file to private Data */
+#define SDCARDFS_F(file) ((struct sdcardfs_file_info *)((file)->private_data))
+
+/* file to lower file */
+static inline struct file *sdcardfs_lower_file(const struct file *f)
+{
+ return SDCARDFS_F(f)->lower_file;
+}
+
+static inline void sdcardfs_set_lower_file(struct file *f, struct file *val)
+{
+ SDCARDFS_F(f)->lower_file = val;
+}
+
+/* inode to lower inode. */
+static inline struct inode *sdcardfs_lower_inode(const struct inode *i)
+{
+ return SDCARDFS_I(i)->lower_inode;
+}
+
+static inline void sdcardfs_set_lower_inode(struct inode *i, struct inode *val)
+{
+ SDCARDFS_I(i)->lower_inode = val;
+}
+
+/* superblock to lower superblock */
+static inline struct super_block *sdcardfs_lower_super(
+ const struct super_block *sb)
+{
+ return SDCARDFS_SB(sb)->lower_sb;
+}
+
+static inline void sdcardfs_set_lower_super(struct super_block *sb,
+ struct super_block *val)
+{
+ SDCARDFS_SB(sb)->lower_sb = val;
+}
+
+/* path based (dentry/mnt) macros */
+static inline void pathcpy(struct path *dst, const struct path *src)
+{
+ dst->dentry = src->dentry;
+ dst->mnt = src->mnt;
+}
+
+/* sdcardfs_get_pname functions calls path_get()
+ * therefore, the caller must call "proper" path_put functions
+ */
+#define SDCARDFS_DENT_FUNC(pname) \
+static inline void sdcardfs_get_##pname(const struct dentry *dent, \
+ struct path *pname) \
+{ \
+ spin_lock(&SDCARDFS_D(dent)->lock); \
+ pathcpy(pname, &SDCARDFS_D(dent)->pname); \
+ path_get(pname); \
+ spin_unlock(&SDCARDFS_D(dent)->lock); \
+ return; \
+} \
+static inline void sdcardfs_put_##pname(const struct dentry *dent, \
+ struct path *pname) \
+{ \
+ path_put(pname); \
+ return; \
+} \
+static inline void sdcardfs_set_##pname(const struct dentry *dent, \
+ struct path *pname) \
+{ \
+ spin_lock(&SDCARDFS_D(dent)->lock); \
+ pathcpy(&SDCARDFS_D(dent)->pname, pname); \
+ spin_unlock(&SDCARDFS_D(dent)->lock); \
+ return; \
+} \
+static inline void sdcardfs_reset_##pname(const struct dentry *dent) \
+{ \
+ spin_lock(&SDCARDFS_D(dent)->lock); \
+ SDCARDFS_D(dent)->pname.dentry = NULL; \
+ SDCARDFS_D(dent)->pname.mnt = NULL; \
+ spin_unlock(&SDCARDFS_D(dent)->lock); \
+ return; \
+} \
+static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \
+{ \
+ struct path pname; \
+ spin_lock(&SDCARDFS_D(dent)->lock); \
+ if(SDCARDFS_D(dent)->pname.dentry) { \
+ pathcpy(&pname, &SDCARDFS_D(dent)->pname); \
+ SDCARDFS_D(dent)->pname.dentry = NULL; \
+ SDCARDFS_D(dent)->pname.mnt = NULL; \
+ spin_unlock(&SDCARDFS_D(dent)->lock); \
+ path_put(&pname); \
+ } else \
+ spin_unlock(&SDCARDFS_D(dent)->lock); \
+ return; \
+}
+
+SDCARDFS_DENT_FUNC(lower_path)
+SDCARDFS_DENT_FUNC(orig_path)
+
+static inline int get_gid(struct sdcardfs_inode_info *info) {
+ struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
+ if (sb_info->options.gid == AID_SDCARD_RW) {
+ /* As an optimization, certain trusted system components only run
+ * as owner but operate across all users. Since we're now handing
+ * out the sdcard_rw GID only to trusted apps, we're okay relaxing
+ * the user boundary enforcement for the default view. The UIDs
+ * assigned to app directories are still multiuser aware. */
+ return AID_SDCARD_RW;
+ } else {
+ return multiuser_get_uid(info->userid, sb_info->options.gid);
+ }
+}
+static inline int get_mode(struct sdcardfs_inode_info *info) {
+ int owner_mode;
+ int filtered_mode;
+ struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
+ int visible_mode = 0775 & ~sb_info->options.mask;
+
+ if (info->perm == PERM_PRE_ROOT) {
+ /* Top of multi-user view should always be visible to ensure
+ * secondary users can traverse inside. */
+ visible_mode = 0711;
+ } else if (info->under_android) {
+ /* Block "other" access to Android directories, since only apps
+ * belonging to a specific user should be in there; we still
+ * leave +x open for the default view. */
+ if (sb_info->options.gid == AID_SDCARD_RW) {
+ visible_mode = visible_mode & ~0006;
+ } else {
+ visible_mode = visible_mode & ~0007;
+ }
+ }
+ owner_mode = info->lower_inode->i_mode & 0700;
+ filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
+ return filtered_mode;
+}
+
+static inline int has_graft_path(const struct dentry *dent)
+{
+ int ret = 0;
+
+ spin_lock(&SDCARDFS_D(dent)->lock);
+ if (SDCARDFS_D(dent)->orig_path.dentry != NULL)
+ ret = 1;
+ spin_unlock(&SDCARDFS_D(dent)->lock);
+
+ return ret;
+}
+
+static inline void sdcardfs_get_real_lower(const struct dentry *dent,
+ struct path *real_lower)
+{
+ /* in case of a local obb dentry
+ * the orig_path should be returned
+ */
+ if(has_graft_path(dent))
+ sdcardfs_get_orig_path(dent, real_lower);
+ else
+ sdcardfs_get_lower_path(dent, real_lower);
+}
+
+static inline void sdcardfs_put_real_lower(const struct dentry *dent,
+ struct path *real_lower)
+{
+ if(has_graft_path(dent))
+ sdcardfs_put_orig_path(dent, real_lower);
+ else
+ sdcardfs_put_lower_path(dent, real_lower);
+}
+
+extern struct mutex sdcardfs_super_list_lock;
+extern struct list_head sdcardfs_super_list;
+
+/* for packagelist.c */
+extern appid_t get_appid(void *pkgl_id, const char *app_name);
+extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
+extern int open_flags_to_access_mode(int open_flags);
+extern int packagelist_init(void);
+extern void packagelist_exit(void);
+
+/* for derived_perm.c */
+extern void setup_derived_state(struct inode *inode, perm_t perm,
+ userid_t userid, uid_t uid, bool under_android);
+extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
+extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry);
+extern void get_derive_permissions_recursive(struct dentry *parent);
+
+extern void update_derived_permission_lock(struct dentry *dentry);
+extern int need_graft_path(struct dentry *dentry);
+extern int is_base_obbpath(struct dentry *dentry);
+extern int is_obbpath_invalid(struct dentry *dentry);
+extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path);
+
+/* locking helpers */
+static inline struct dentry *lock_parent(struct dentry *dentry)
+{
+ struct dentry *dir = dget_parent(dentry);
+ mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT);
+ return dir;
+}
+
+static inline void unlock_dir(struct dentry *dir)
+{
+ mutex_unlock(&d_inode(dir)->i_mutex);
+ dput(dir);
+}
+
+static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t mode)
+{
+ int err;
+ struct dentry *dent;
+ struct iattr attrs;
+ struct path parent;
+
+ dent = kern_path_locked(path_s, &parent);
+ if (IS_ERR(dent)) {
+ err = PTR_ERR(dent);
+ if (err == -EEXIST)
+ err = 0;
+ goto out_unlock;
+ }
+
+ err = vfs_mkdir(d_inode(parent.dentry), dent, mode);
+ if (err) {
+ if (err == -EEXIST)
+ err = 0;
+ goto out_dput;
+ }
+
+ attrs.ia_uid = make_kuid(&init_user_ns, uid);
+ attrs.ia_gid = make_kgid(&init_user_ns, gid);
+ attrs.ia_valid = ATTR_UID | ATTR_GID;
+ mutex_lock(&d_inode(dent)->i_mutex);
+ notify_change(dent, &attrs, NULL);
+ mutex_unlock(&d_inode(dent)->i_mutex);
+
+out_dput:
+ dput(dent);
+
+out_unlock:
+ /* parent dentry locked by lookup_create */
+ mutex_unlock(&d_inode(parent.dentry)->i_mutex);
+ path_put(&parent);
+ return err;
+}
+
+/*
+ * Return 1, if a disk has enough free space, otherwise 0.
+ * We assume that any files can not be overwritten.
+ */
+static inline int check_min_free_space(struct dentry *dentry, size_t size, int dir)
+{
+ int err;
+ struct path lower_path;
+ struct kstatfs statfs;
+ u64 avail;
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+
+ if (sbi->options.reserved_mb) {
+ /* Get fs stat of lower filesystem. */
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ err = vfs_statfs(&lower_path, &statfs);
+ sdcardfs_put_lower_path(dentry, &lower_path);
+
+ if (unlikely(err))
+ return 0;
+
+ /* Invalid statfs informations. */
+ if (unlikely(statfs.f_bsize == 0))
+ return 0;
+
+ /* if you are checking directory, set size to f_bsize. */
+ if (unlikely(dir))
+ size = statfs.f_bsize;
+
+ /* available size */
+ avail = statfs.f_bavail * statfs.f_bsize;
+
+ /* not enough space */
+ if ((u64)size > avail)
+ return 0;
+
+ /* enough space */
+ if ((avail - size) > (sbi->options.reserved_mb * 1024 * 1024))
+ return 1;
+
+ return 0;
+ } else
+ return 1;
+}
+
+/* Copies attrs and maintains sdcardfs managed attrs */
+static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src)
+{
+ dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest));
+ dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid);
+ dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest)));
+ dest->i_rdev = src->i_rdev;
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+ dest->i_blkbits = src->i_blkbits;
+ dest->i_flags = src->i_flags;
+ set_nlink(dest, src->i_nlink);
+}
+#endif /* not _SDCARDFS_H_ */
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
new file mode 100644
index 000000000000..1d6490128c99
--- /dev/null
+++ b/fs/sdcardfs/super.c
@@ -0,0 +1,222 @@
+/*
+ * fs/sdcardfs/super.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co. Ltd
+ * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
+ * Sunghwan Yun, Sungjong Seo
+ *
+ * This program has been developed as a stackable file system based on
+ * the WrapFS which written by
+ *
+ * Copyright (c) 1998-2011 Erez Zadok
+ * Copyright (c) 2009 Shrikar Archak
+ * Copyright (c) 2003-2011 Stony Brook University
+ * Copyright (c) 2003-2011 The Research Foundation of SUNY
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#include "sdcardfs.h"
+
+/*
+ * The inode cache is used with alloc_inode for both our inode info and the
+ * vfs inode.
+ */
+static struct kmem_cache *sdcardfs_inode_cachep;
+
+/* final actions when unmounting a file system */
+static void sdcardfs_put_super(struct super_block *sb)
+{
+ struct sdcardfs_sb_info *spd;
+ struct super_block *s;
+
+ spd = SDCARDFS_SB(sb);
+ if (!spd)
+ return;
+
+ if(spd->obbpath_s) {
+ kfree(spd->obbpath_s);
+ path_put(&spd->obbpath);
+ }
+
+ /* decrement lower super references */
+ s = sdcardfs_lower_super(sb);
+ sdcardfs_set_lower_super(sb, NULL);
+ atomic_dec(&s->s_active);
+
+ kfree(spd);
+ sb->s_fs_info = NULL;
+}
+
+static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ int err;
+ struct path lower_path;
+ u32 min_blocks;
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ err = vfs_statfs(&lower_path, buf);
+ sdcardfs_put_lower_path(dentry, &lower_path);
+
+ if (sbi->options.reserved_mb) {
+ /* Invalid statfs informations. */
+ if (buf->f_bsize == 0) {
+ printk(KERN_ERR "Returned block size is zero.\n");
+ return -EINVAL;
+ }
+
+ min_blocks = ((sbi->options.reserved_mb * 1024 * 1024)/buf->f_bsize);
+ buf->f_blocks -= min_blocks;
+
+ if (buf->f_bavail > min_blocks)
+ buf->f_bavail -= min_blocks;
+ else
+ buf->f_bavail = 0;
+
+ /* Make reserved blocks invisiable to media storage */
+ buf->f_bfree = buf->f_bavail;
+ }
+
+ /* set return buf to our f/s to avoid confusing user-level utils */
+ buf->f_type = SDCARDFS_SUPER_MAGIC;
+
+ return err;
+}
+
+/*
+ * @flags: numeric mount options
+ * @options: mount options string
+ */
+static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options)
+{
+ int err = 0;
+
+ /*
+ * The VFS will take care of "ro" and "rw" flags among others. We
+ * can safely accept a few flags (RDONLY, MANDLOCK), and honor
+ * SILENT, but anything else left over is an error.
+ */
+ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) {
+ printk(KERN_ERR
+ "sdcardfs: remount flags 0x%x unsupported\n", *flags);
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+/*
+ * Called by iput() when the inode reference count reached zero
+ * and the inode is not hashed anywhere. Used to clear anything
+ * that needs to be, before the inode is completely destroyed and put
+ * on the inode free list.
+ */
+static void sdcardfs_evict_inode(struct inode *inode)
+{
+ struct inode *lower_inode;
+
+ truncate_inode_pages(&inode->i_data, 0);
+ clear_inode(inode);
+ /*
+ * Decrement a reference to a lower_inode, which was incremented
+ * by our read_inode when it was created initially.
+ */
+ lower_inode = sdcardfs_lower_inode(inode);
+ sdcardfs_set_lower_inode(inode, NULL);
+ iput(lower_inode);
+}
+
+static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
+{
+ struct sdcardfs_inode_info *i;
+
+ i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL);
+ if (!i)
+ return NULL;
+
+ /* memset everything up to the inode to 0 */
+ memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode));
+
+ i->vfs_inode.i_version = 1;
+ return &i->vfs_inode;
+}
+
+static void sdcardfs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
+}
+
+/* sdcardfs inode cache constructor */
+static void init_once(void *obj)
+{
+ struct sdcardfs_inode_info *i = obj;
+
+ inode_init_once(&i->vfs_inode);
+}
+
+int sdcardfs_init_inode_cache(void)
+{
+ int err = 0;
+
+ sdcardfs_inode_cachep =
+ kmem_cache_create("sdcardfs_inode_cache",
+ sizeof(struct sdcardfs_inode_info), 0,
+ SLAB_RECLAIM_ACCOUNT, init_once);
+ if (!sdcardfs_inode_cachep)
+ err = -ENOMEM;
+ return err;
+}
+
+/* sdcardfs inode cache destructor */
+void sdcardfs_destroy_inode_cache(void)
+{
+ if (sdcardfs_inode_cachep)
+ kmem_cache_destroy(sdcardfs_inode_cachep);
+}
+
+/*
+ * Used only in nfs, to kill any pending RPC tasks, so that subsequent
+ * code can actually succeed and won't leave tasks that need handling.
+ */
+static void sdcardfs_umount_begin(struct super_block *sb)
+{
+ struct super_block *lower_sb;
+
+ lower_sb = sdcardfs_lower_super(sb);
+ if (lower_sb && lower_sb->s_op && lower_sb->s_op->umount_begin)
+ lower_sb->s_op->umount_begin(lower_sb);
+}
+
+static int sdcardfs_show_options(struct seq_file *m, struct dentry *root)
+{
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
+ struct sdcardfs_mount_options *opts = &sbi->options;
+
+ if (opts->fs_low_uid != 0)
+ seq_printf(m, ",uid=%u", opts->fs_low_uid);
+ if (opts->fs_low_gid != 0)
+ seq_printf(m, ",gid=%u", opts->fs_low_gid);
+
+ if (opts->multiuser)
+ seq_printf(m, ",multiuser");
+
+ if (opts->reserved_mb != 0)
+ seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
+
+ return 0;
+};
+
+const struct super_operations sdcardfs_sops = {
+ .put_super = sdcardfs_put_super,
+ .statfs = sdcardfs_statfs,
+ .remount_fs = sdcardfs_remount_fs,
+ .evict_inode = sdcardfs_evict_inode,
+ .umount_begin = sdcardfs_umount_begin,
+ .show_options = sdcardfs_show_options,
+ .alloc_inode = sdcardfs_alloc_inode,
+ .destroy_inode = sdcardfs_destroy_inode,
+ .drop_inode = generic_delete_inode,
+};
diff --git a/include/dt-bindings/clock/msm-clocks-cobalt.h b/include/dt-bindings/clock/msm-clocks-cobalt.h
index b411a0be4e67..b80ea0c31597 100644
--- a/include/dt-bindings/clock/msm-clocks-cobalt.h
+++ b/include/dt-bindings/clock/msm-clocks-cobalt.h
@@ -157,6 +157,7 @@
#define clk_gcc_usb3_phy_reset 0x03d559f1
#define clk_gcc_usb3phy_phy_reset 0xb1a4f885
#define clk_gcc_aggre1_ufs_axi_clk 0x873459d8
+#define clk_gcc_aggre1_ufs_axi_hw_ctl_clk 0x117a6f39
#define clk_gcc_aggre1_usb3_axi_clk 0xc5c3fbe8
#define clk_gcc_bimc_mss_q6_axi_clk 0x7437988f
#define clk_gcc_blsp1_ahb_clk 0x8caa5b4f
@@ -494,10 +495,30 @@
#define clk_sys_apcsaux_clk_gcc 0xf905e862
#define clk_xo_ao 0x428c856d
#define clk_osm_clk_src 0xaabe68c3
+#define clk_cpu_debug_mux 0x3ae8bcb2
/* Audio External Clocks */
#define clk_audio_ap_clk 0x9b5727cb
#define clk_audio_pmi_clk 0xcbfe416d
#define clk_audio_ap_clk2 0x454d1e91
+/* GCC block resets */
+#define QUSB2PHY_PRIM_BCR 0
+#define QUSB2PHY_SEC_BCR 1
+#define BLSP1_BCR 2
+#define BLSP2_BCR 3
+#define BOOT_ROM_BCR 4
+#define PRNG_BCR 5
+#define UFS_BCR 6
+#define USB_30_BCR 7
+#define USB3_PHY_BCR 8
+#define USB3PHY_PHY_BCR 9
+#define PCIE_0_PHY_BCR 10
+#define PCIE_PHY_BCR 11
+#define PCIE_PHY_COM_BCR 12
+#define PCIE_PHY_NOCSR_COM_PHY_BCR 13
+
+/* MMSS block resets */
+#define CAMSS_MICRO_BCR 0
+
#endif
diff --git a/include/dt-bindings/clock/qcom,gcc-msm8996.h b/include/dt-bindings/clock/qcom,gcc-msm8996.h
index 3819485dead3..f66264a2beb4 100644
--- a/include/dt-bindings/clock/qcom,gcc-msm8996.h
+++ b/include/dt-bindings/clock/qcom,gcc-msm8996.h
@@ -233,6 +233,18 @@
#define GCC_PCIE_CLKREF_CLK 216
#define GCC_RX2_USB2_CLKREF_CLK 217
#define GCC_RX1_USB2_CLKREF_CLK 218
+#define GCC_AGGRE0_NOC_QOSGEN_EXTREF_CLK 219
+#define GCC_HLOS1_VOTE_LPASS_CORE_SMMU_CLK 220
+#define GCC_HLOS1_VOTE_LPASS_ADSP_SMMU_CLK 221
+#define GCC_EDP_CLKREF_CLK 222
+#define GCC_MSS_CFG_AHB_CLK 223
+#define GCC_MSS_Q6_BIMC_AXI_CLK 224
+#define GCC_MSS_SNOC_AXI_CLK 225
+#define GCC_MSS_MNOC_BIMC_AXI_CLK 226
+#define GCC_DCC_AHB_ALK 227
+#define GCC_AGGRE0_NOC_MPU_CFG_AHB_CLK 228
+#define GCC_MMSS_GPLL0_DIV_CLK 229
+#define GPLL0_OUT_MSSCC 230
#define GCC_SYSTEM_NOC_BCR 0
#define GCC_CONFIG_NOC_BCR 1
@@ -340,15 +352,4 @@
#define GCC_PCIE_PHY_NOCSR_COM_PHY_BCR 103
#define GCC_PCIE_PHY_COM_BCR 104
-/* Indexes for GDSCs */
-#define AGGRE0_NOC_GDSC 0
-#define HLOS1_VOTE_AGGRE0_NOC_GDSC 1
-#define HLOS1_VOTE_LPASS_ADSP_GDSC 2
-#define HLOS1_VOTE_LPASS_CORE_GDSC 3
-#define USB30_GDSC 4
-#define PCIE0_GDSC 5
-#define PCIE1_GDSC 6
-#define PCIE2_GDSC 7
-#define UFS_GDSC 8
-
#endif
diff --git a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
index aafa76cb569d..64e2dc7183f3 100644
--- a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
+++ b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
@@ -89,15 +89,30 @@
#define PMA8084_GPIO_S4 2
#define PMA8084_GPIO_L6 3
+/* ATEST MUX selection for analog-pass-through mode */
+#define PMIC_GPIO_AOUT_ATEST1 0
+#define PMIC_GPIO_AOUT_ATEST2 1
+#define PMIC_GPIO_AOUT_ATEST3 2
+#define PMIC_GPIO_AOUT_ATEST4 3
+
+/* DTEST buffer for digital input mode */
+#define PMIC_GPIO_DIN_DTEST1 0
+#define PMIC_GPIO_DIN_DTEST2 1
+#define PMIC_GPIO_DIN_DTEST3 2
+#define PMIC_GPIO_DIN_DTEST4 3
+
/* To be used with "function" */
#define PMIC_GPIO_FUNC_NORMAL "normal"
#define PMIC_GPIO_FUNC_PAIRED "paired"
#define PMIC_GPIO_FUNC_FUNC1 "func1"
#define PMIC_GPIO_FUNC_FUNC2 "func2"
+#define PMIC_GPIO_FUNC_FUNC3 "func3"
+#define PMIC_GPIO_FUNC_FUNC4 "func4"
#define PMIC_GPIO_FUNC_DTEST1 "dtest1"
#define PMIC_GPIO_FUNC_DTEST2 "dtest2"
#define PMIC_GPIO_FUNC_DTEST3 "dtest3"
#define PMIC_GPIO_FUNC_DTEST4 "dtest4"
+#define PMIC_GPIO_FUNC_ANALOG "analog"
#define PM8038_GPIO1_2_LPG_DRV PMIC_GPIO_FUNC_FUNC1
#define PM8038_GPIO3_5V_BOOST_EN PMIC_GPIO_FUNC_FUNC1
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index f513dd855cb2..e4221f7c5b53 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -161,6 +161,7 @@ struct dentry_operations {
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool);
struct inode *(*d_select_inode)(struct dentry *, unsigned);
+ void (*d_canonical_path)(const struct dentry *, struct path *);
struct dentry *(*d_real)(struct dentry *, struct inode *);
} ____cacheline_aligned;
diff --git a/include/linux/ecryptfs.h b/include/linux/ecryptfs.h
index 33d35ea4f937..489b99e37128 100644
--- a/include/linux/ecryptfs.h
+++ b/include/linux/ecryptfs.h
@@ -130,7 +130,7 @@ struct ecryptfs_events {
size_t (*get_salt_key_size_cb)(const void *ecrytpfs_data);
};
-
+#ifdef CONFIG_ECRYPT_FS
int ecryptfs_register_to_events(const struct ecryptfs_events *ops);
int ecryptfs_unregister_from_events(int user_handle);
@@ -151,4 +151,55 @@ bool ecryptfs_is_page_in_metadata(const void *ecrytpfs_data, pgoff_t offset);
bool ecryptfs_is_data_equal(const void *ecrytpfs_data1,
const void *ecrytpfs_data2);
+#else
+static inline int ecryptfs_register_to_events(
+ const struct ecryptfs_events *ops)
+{
+ return 1; /* dummy handle */
+}
+
+static int ecryptfs_unregister_from_events(int user_handle)
+{
+ return 0;
+}
+
+static inline const unsigned char *ecryptfs_get_key(const void *ecrytpfs_data)
+{
+ return NULL;
+}
+
+static inline size_t ecryptfs_get_key_size(const void *ecrytpfs_data)
+{
+ return 0;
+}
+
+static inline const unsigned char *ecryptfs_get_salt(const void *ecrytpfs_data)
+{
+ return NULL;
+}
+
+static inline size_t ecryptfs_get_salt_size(const void *ecrytpfs_data)
+{
+ return 0;
+}
+
+static inline bool ecryptfs_cipher_match(const void *ecrytpfs_data,
+ const unsigned char *cipher, size_t cipher_size)
+{
+ return false;
+}
+
+bool ecryptfs_is_page_in_metadata(const void *ecrytpfs_data, pgoff_t offset)
+{
+ return false;
+}
+
+bool ecryptfs_is_data_equal(const void *ecrytpfs_data1,
+ const void *ecrytpfs_data2)
+{
+ return false;
+}
+
+#endif /* CONFIG_ECRYPT_FS */
+
#endif /* _LINUX_ECRYPTFS_H */
diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h
index bd37af71fe0d..ad95957d9189 100644
--- a/include/linux/input/ft5x06_ts.h
+++ b/include/linux/input/ft5x06_ts.h
@@ -34,11 +34,11 @@ struct fw_upgrade_info {
u16 delay_erase_flash;
};
-struct ft5x06_psensor_platform_data {
- struct input_dev *input_psensor_dev;
- struct sensors_classdev ps_cdev;
- int tp_psensor_opened;
- char tp_psensor_data; /* 0 near, 1 far */
+struct ft5x06_gesture_platform_data {
+ int gesture_enable_to_set; /* enable/disable gesture */
+ int in_pocket; /* whether in pocket mode or not */
+ struct device *dev;
+ struct class *gesture_class;
struct ft5x06_ts_data *data;
};
@@ -68,7 +68,8 @@ struct ft5x06_ts_platform_data {
bool no_force_update;
bool i2c_pull_up;
bool ignore_id_check;
- bool psensor_support;
+ bool gesture_support;
+ bool resume_in_workqueue;
int (*power_init)(bool);
int (*power_on)(bool);
};
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 1919b06f28f4..cf4832db2b29 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1443,7 +1443,6 @@ union security_list_options {
int (*file_receive)(struct file *file);
int (*file_open)(struct file *file, const struct cred *cred);
int (*file_close)(struct file *file);
- bool (*allow_merge_bio)(struct bio *bio1, struct bio *bio2);
int (*task_create)(unsigned long clone_flags);
void (*task_free)(struct task_struct *task);
@@ -1708,7 +1707,6 @@ struct security_hook_heads {
struct list_head file_receive;
struct list_head file_open;
struct list_head file_close;
- struct list_head allow_merge_bio;
struct list_head task_create;
struct list_head task_free;
struct list_head cred_alloc_blank;
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index 75908dfa8d64..11e8d89c337b 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -53,12 +53,52 @@
#define TASHA_VERSION_1_0 0
#define TASHA_VERSION_1_1 1
#define TASHA_VERSION_2_0 2
-#define TASHA_IS_1_0(ver) \
- ((ver == TASHA_VERSION_1_0) ? 1 : 0)
-#define TASHA_IS_1_1(ver) \
- ((ver == TASHA_VERSION_1_1) ? 1 : 0)
-#define TASHA_IS_2_0(ver) \
- ((ver == TASHA_VERSION_2_0) ? 1 : 0)
+
+#define TASHA_IS_1_0(wcd) \
+ ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
+ ((wcd->version == TASHA_VERSION_1_0) ? 1 : 0) : 0)
+
+#define TASHA_IS_1_1(wcd) \
+ ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
+ ((wcd->version == TASHA_VERSION_1_1) ? 1 : 0) : 0)
+
+#define TASHA_IS_2_0(wcd) \
+ ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
+ ((wcd->version == TASHA_VERSION_2_0) ? 1 : 0) : 0)
+
+/*
+ * As fine version info cannot be retrieved before tavil probe.
+ * Define three coarse versions for possible future use before tavil probe.
+ */
+#define TAVIL_VERSION_1_0 0
+#define TAVIL_VERSION_1_1 1
+#define TAVIL_VERSION_WCD9340_1_0 2
+#define TAVIL_VERSION_WCD9341_1_0 3
+#define TAVIL_VERSION_WCD9340_1_1 4
+#define TAVIL_VERSION_WCD9341_1_1 5
+
+#define TAVIL_IS_1_0(wcd) \
+ ((wcd->type == WCD934X) ? \
+ ((wcd->version == TAVIL_VERSION_1_0 || \
+ wcd->version == TAVIL_VERSION_WCD9340_1_0 || \
+ wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0)
+#define TAVIL_IS_1_1(wcd) \
+ ((wcd->type == WCD934X) ? \
+ ((wcd->version == TAVIL_VERSION_1_1 || \
+ wcd->version == TAVIL_VERSION_WCD9340_1_1 || \
+ wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0)
+#define TAVIL_IS_WCD9340_1_0(wcd) \
+ ((wcd->type == WCD934X) ? \
+ ((wcd->version == TAVIL_VERSION_WCD9340_1_0) ? 1 : 0) : 0)
+#define TAVIL_IS_WCD9341_1_0(wcd) \
+ ((wcd->type == WCD934X) ? \
+ ((wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0)
+#define TAVIL_IS_WCD9340_1_1(wcd) \
+ ((wcd->type == WCD934X) ? \
+ ((wcd->version == TAVIL_VERSION_WCD9340_1_1) ? 1 : 0) : 0)
+#define TAVIL_IS_WCD9341_1_1(wcd) \
+ ((wcd->type == WCD934X) ? \
+ ((wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0)
#define IS_CODEC_TYPE(wcd, wcdtype) \
((wcd->type == wcdtype) ? true : false)
@@ -204,6 +244,7 @@ struct wcd9xxx_core_resource {
void *parent;
struct device *dev;
+ struct irq_domain *domain;
};
/*
diff --git a/include/linux/namei.h b/include/linux/namei.h
index d8c6334cd150..d53c25453aca 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -75,6 +75,8 @@ extern struct dentry *user_path_create(int, const char __user *, struct path *,
extern void done_path_create(struct path *, struct dentry *);
extern struct dentry *kern_path_locked(const char *, struct path *);
extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
+extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
+ const char *, unsigned int, struct path *);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
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/pfk.h b/include/linux/pfk.h
index a7e8ecbea8f5..2fc64442b8ee 100644
--- a/include/linux/pfk.h
+++ b/include/linux/pfk.h
@@ -23,7 +23,7 @@ int pfk_load_key_start(const struct bio *bio,
struct ice_crypto_setting *ice_setting, bool *is_pfe, bool);
int pfk_load_key_end(const struct bio *bio, bool *is_pfe);
int pfk_remove_key(const unsigned char *key, size_t key_size);
-bool pfk_allow_merge_bio(struct bio *bio1, struct bio *bio2);
+bool pfk_allow_merge_bio(const struct bio *bio1, const struct bio *bio2);
#else
static inline int pfk_load_key_start(const struct bio *bio,
@@ -48,10 +48,6 @@ static inline bool pfk_allow_merge_bio(const struct bio *bio1,
return true;
}
-static inline void pfk_remove_all_keys(void)
-{
-}
-
#endif /* CONFIG_PFK */
#endif /* PFK_H */
diff --git a/include/linux/pft.h b/include/linux/pft.h
index f2173b89a2a0..818383c73476 100644
--- a/include/linux/pft.h
+++ b/include/linux/pft.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* 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
@@ -55,7 +55,8 @@ static inline int pft_get_key_index(struct bio *bio, u32 *key_index,
bool *is_encrypted, bool *is_inplace)
{ return -ENODEV; }
-static inline bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2)
+static inline bool pft_allow_merge_bio(const struct bio *bio1,
+ const struct bio *bio2)
{ return true; }
static inline int pft_file_permission(struct file *file, int mask)
diff --git a/include/linux/phy/phy-qcom-ufs.h b/include/linux/phy/phy-qcom-ufs.h
index 7945fea14d77..25e7a5f183ec 100644
--- a/include/linux/phy/phy-qcom-ufs.h
+++ b/include/linux/phy/phy-qcom-ufs.h
@@ -58,5 +58,6 @@ void ufs_qcom_phy_save_controller_version(struct phy *phy,
u8 major, u16 minor, u16 step);
const char *ufs_qcom_phy_name(struct phy *phy);
int ufs_qcom_phy_configure_lpm(struct phy *generic_phy, bool enable);
+void ufs_qcom_phy_dbg_register_dump(struct phy *generic_phy);
#endif /* PHY_QCOM_UFS_H_ */
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index dc777be5f2e1..6abd019c76f8 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -51,6 +51,7 @@ extern void arch_setup_pdev_archdata(struct platform_device *);
extern struct resource *platform_get_resource(struct platform_device *,
unsigned int, unsigned int);
extern int platform_get_irq(struct platform_device *, unsigned int);
+extern int platform_irq_count(struct platform_device *);
extern struct resource *platform_get_resource_byname(struct platform_device *,
unsigned int,
const char *);
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 56e78254286e..03853d956b41 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -176,6 +176,8 @@ enum power_supply_property {
POWER_SUPPLY_PROP_USB_OTG,
POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_STEP_CHARGING_STEP,
POWER_SUPPLY_PROP_PIN_ENABLED,
POWER_SUPPLY_PROP_INPUT_SUSPEND,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION,
diff --git a/include/linux/random.h b/include/linux/random.h
index a75840c1aa71..9c29122037f9 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -34,6 +34,7 @@ extern const struct file_operations random_fops, urandom_fops;
#endif
unsigned int get_random_int(void);
+unsigned long get_random_long(void);
unsigned long randomize_range(unsigned long start, unsigned long end, unsigned long len);
u32 prandom_u32(void);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 74b2a11b1d1c..4701e0403167 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -336,8 +336,6 @@ enum migrate_types {
GROUP_TO_GROUP,
};
-extern const char *migrate_type_names[];
-
#include <linux/spinlock.h>
/*
diff --git a/include/linux/security.h b/include/linux/security.h
index 3de0302aecf2..e3b5efc0eb4b 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -293,7 +293,6 @@ int security_file_send_sigiotask(struct task_struct *tsk,
int security_file_receive(struct file *file);
int security_file_open(struct file *file, const struct cred *cred);
int security_file_close(struct file *file);
-bool security_allow_merge_bio(struct bio *bio1, struct bio *bio2);
int security_task_create(unsigned long clone_flags);
void security_task_free(struct task_struct *task);
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp);
@@ -826,11 +825,6 @@ static inline int security_file_close(struct file *file)
return 0;
}
-static inline int security_allow_merge_bio(struct bio *bio1, struct bio *bio2)
-{
- return true;
-}
-
static inline int security_task_create(unsigned long clone_flags)
{
return 0;
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 18a8337c8959..5bf59c8493b7 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -47,10 +47,6 @@ struct linger {
struct msghdr {
void *msg_name; /* ptr to socket address structure */
int msg_namelen; /* size of socket address structure */
-#if defined(CONFIG_PPPOLAC) || defined(CONFIG_PPPOPNS)
- struct iovec *msg_iov; /* scatter/gather array */
- __kernel_size_t msg_iovlen; /* # elements in msg_iov */
-#endif
struct iov_iter msg_iter; /* data */
void *msg_control; /* ancillary data */
__kernel_size_t msg_controllen; /* ancillary data buffer length */
diff --git a/include/linux/wakeup_reason.h b/include/linux/wakeup_reason.h
index ad8b76936c7f..d84d8c301546 100644
--- a/include/linux/wakeup_reason.h
+++ b/include/linux/wakeup_reason.h
@@ -21,7 +21,12 @@
#define MAX_SUSPEND_ABORT_LEN 256
void log_wakeup_reason(int irq);
-void log_suspend_abort_reason(const char *fmt, ...);
int check_wakeup_reason(int irq);
+#ifdef CONFIG_SUSPEND
+void log_suspend_abort_reason(const char *fmt, ...);
+#else
+static inline void log_suspend_abort_reason(const char *fmt, ...) { }
+#endif
+
#endif /* _LINUX_WAKEUP_REASON_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index e0b0d2b12b88..801315d1d405 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -63,6 +63,11 @@
struct wiphy;
+#define CFG80211_SCAN_BSSID 1
+#define CFG80211_CONNECT_PREV_BSSID 1
+#define CFG80211_CONNECT_BSS 1
+#define CFG80211_ABORT_SCAN 1
+
/*
* wireless hardware capability structures
*/
@@ -1455,6 +1460,7 @@ struct cfg80211_ssid {
* @mac_addr_mask: MAC address mask used with randomisation, bits that
* are 0 in the mask should be randomised, bits that are 1 should
* be taken from the @mac_addr
+ * @bssid: BSSID to scan for (most commonly, the wildcard BSSID)
*/
struct cfg80211_scan_request {
struct cfg80211_ssid *ssids;
@@ -1471,6 +1477,7 @@ struct cfg80211_scan_request {
u8 mac_addr[ETH_ALEN] __aligned(2);
u8 mac_addr_mask[ETH_ALEN] __aligned(2);
+ u8 bssid[ETH_ALEN] __aligned(2);
/* internal */
struct wiphy *wiphy;
@@ -1893,6 +1900,7 @@ struct cfg80211_ibss_params {
* @vht_capa_mask: The bits of vht_capa which are to be used.
* @pbss: if set, connect to a PCP instead of AP. Valid for DMG
* networks.
+ * @prev_bssid: previous BSSID, if not %NULL use reassociate frame
*/
struct cfg80211_connect_params {
struct ieee80211_channel *channel;
@@ -1916,6 +1924,7 @@ struct cfg80211_connect_params {
struct ieee80211_vht_cap vht_capa;
struct ieee80211_vht_cap vht_capa_mask;
bool pbss;
+ const u8 *prev_bssid;
};
/**
@@ -2327,6 +2336,8 @@ struct cfg80211_qos_map {
* the driver, and will be valid until passed to cfg80211_scan_done().
* For scan results, call cfg80211_inform_bss(); you can call this outside
* the scan/scan_done bracket too.
+ * @abort_scan: Tell the driver to abort an ongoing scan. The driver shall
+ * indicate the status of the scan through cfg80211_scan_done().
*
* @auth: Request to authenticate with the specified peer
* (invoked with the wireless_dev mutex held)
@@ -2599,6 +2610,7 @@ struct cfg80211_ops {
int (*scan)(struct wiphy *wiphy,
struct cfg80211_scan_request *request);
+ void (*abort_scan)(struct wiphy *wiphy, struct wireless_dev *wdev);
int (*auth)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_auth_request *req);
@@ -4661,6 +4673,32 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
#endif
/**
+ * cfg80211_connect_bss - notify cfg80211 of connection result
+ *
+ * @dev: network device
+ * @bssid: the BSSID of the AP
+ * @bss: entry of bss to which STA got connected to, can be obtained
+ * through cfg80211_get_bss (may be %NULL)
+ * @req_ie: association request IEs (maybe be %NULL)
+ * @req_ie_len: association request IEs length
+ * @resp_ie: association response IEs (may be %NULL)
+ * @resp_ie_len: assoc response IEs length
+ * @status: status code, 0 for successful connection, use
+ * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
+ * the real status code for failures.
+ * @gfp: allocation flags
+ *
+ * It should be called by the underlying driver whenever connect() has
+ * succeeded. This is similar to cfg80211_connect_result(), but with the
+ * option of identifying the exact bss entry for the connection. Only one of
+ * these functions should be called.
+ */
+void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
+ struct cfg80211_bss *bss, const u8 *req_ie,
+ size_t req_ie_len, const u8 *resp_ie,
+ size_t resp_ie_len, u16 status, gfp_t gfp);
+
+/**
* cfg80211_connect_result - notify cfg80211 of connection result
*
* @dev: network device
@@ -4677,10 +4715,15 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
* It should be called by the underlying driver whenever connect() has
* succeeded.
*/
-void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
- const u8 *req_ie, size_t req_ie_len,
- const u8 *resp_ie, size_t resp_ie_len,
- u16 status, gfp_t gfp);
+static inline void
+cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
+ const u8 *req_ie, size_t req_ie_len,
+ const u8 *resp_ie, size_t resp_ie_len,
+ u16 status, gfp_t gfp)
+{
+ cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, resp_ie,
+ resp_ie_len, status, gfp);
+}
/**
* cfg80211_roamed - notify cfg80211 of roaming
diff --git a/include/net/rmnet_config.h b/include/net/rmnet_config.h
new file mode 100644
index 000000000000..0e6282b05483
--- /dev/null
+++ b/include/net/rmnet_config.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ *
+ * RMNET Data config definition
+ *
+ */
+
+#ifndef _RMNET_CONFIG_H_
+#define _RMNET_CONFIG_H_
+
+#include <linux/skbuff.h>
+
+struct rmnet_phys_ep_conf_s {
+ void (*recycle)(struct sk_buff *); /* Destruct function */
+ void *config;
+};
+
+#endif /* _RMNET_CONFIG_H_ */
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index 62f1ff65f273..efc5425cf17d 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -26,6 +26,10 @@ struct icnss_driver_ops {
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);
+ int (*resume_noirq)(struct device *dev);
};
@@ -103,11 +107,22 @@ extern int icnss_ce_request_irq(unsigned int ce_id,
unsigned long flags, const char *name, void *ctx);
extern int icnss_get_ce_id(int irq);
extern int icnss_set_fw_debug_mode(bool enable_fw_log);
+extern int icnss_athdiag_read(struct device *dev, uint32_t offset,
+ uint32_t mem_type, uint32_t data_len,
+ uint8_t *output);
+extern int icnss_athdiag_write(struct device *dev, uint32_t offset,
+ uint32_t mem_type, uint32_t data_len,
+ uint8_t *input);
extern int icnss_get_irq(int ce_id);
extern int icnss_power_on(struct device *dev);
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 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);
#endif /* _ICNSS_WLAN_H_ */
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/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
index 82672bba7c17..76555ce53d97 100644
--- a/include/soc/qcom/socinfo.h
+++ b/include/soc/qcom/socinfo.h
@@ -96,6 +96,8 @@
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmhamster")
#define early_machine_is_msmfalcon() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmfalcon")
+#define early_machine_is_msmtriton() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmtriton")
#else
#define of_board_is_sim() 0
#define of_board_is_rumi() 0
@@ -133,6 +135,7 @@
#define early_machine_is_apqcobalt() 0
#define early_machine_is_msmhamster() 0
#define early_machine_is_msmfalcon() 0
+#define early_machine_is_msmtriton() 0
#endif
#define PLATFORM_SUBTYPE_MDM 1
@@ -192,6 +195,7 @@ enum msm_cpu {
MSM_CPU_COBALT,
MSM_CPU_HAMSTER,
MSM_CPU_FALCON,
+ MSM_CPU_TRITON,
};
struct msm_soc_info {
diff --git a/include/soc/qcom/subsystem_restart.h b/include/soc/qcom/subsystem_restart.h
index 3a5f6e678b4f..780666c332e2 100644
--- a/include/soc/qcom/subsystem_restart.h
+++ b/include/soc/qcom/subsystem_restart.h
@@ -113,6 +113,7 @@ extern int subsystem_crashed(const char *name);
extern void *subsystem_get(const char *name);
extern void *subsystem_get_with_fwname(const char *name, const char *fw_name);
+extern int subsystem_set_fwname(const char *name, const char *fw_name);
extern void subsystem_put(void *subsystem);
extern struct subsys_device *subsys_register(struct subsys_desc *desc);
@@ -157,6 +158,11 @@ static inline void *subsystem_get_with_fwname(const char *name,
return NULL;
}
+static inline int subsystem_set_fwname(const char *name,
+ const char *fw_name) {
+ return 0;
+}
+
static inline void subsystem_put(void *subsystem) { }
static inline
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 695e33f4d1cf..636ae899c304 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -804,6 +804,7 @@ struct adm_cmd_connect_afe_port_v5 {
#define INT_FM_TX 0x3005
#define RT_PROXY_PORT_001_RX 0x2000
#define RT_PROXY_PORT_001_TX 0x2001
+#define DISPLAY_PORT_RX 0x6020
#define AFE_PORT_INVALID 0xFFFF
#define SLIMBUS_INVALID AFE_PORT_INVALID
@@ -953,6 +954,8 @@ struct adm_cmd_connect_afe_port_v5 {
#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_RX 0x4010
/* SLIMbus Tx port on channel 8. */
#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_TX 0x4011
+/* AFE Rx port for audio over Display port */
+#define AFE_PORT_ID_HDMI_OVER_DP_RX 0x6020
/*USB AFE port */
#define AFE_PORT_ID_USB_RX 0x7000
#define AFE_PORT_ID_USB_TX 0x7001
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index e1f4920053ed..2b6e8f8240d9 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -141,7 +141,8 @@ struct snd_pcm_ops {
#define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */
#define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */
#define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */
-#define SNDRV_PCM_RATE_384000 (1<<13) /* 384000Hz */
+#define SNDRV_PCM_RATE_352800 (1<<13) /* 352800Hz */
+#define SNDRV_PCM_RATE_384000 (1<<14) /* 384000Hz */
#define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */
#define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */
@@ -154,6 +155,9 @@ struct snd_pcm_ops {
SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000)
#define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\
SNDRV_PCM_RATE_192000)
+#define SNDRV_PCM_RATE_8000_384000 (SNDRV_PCM_RATE_8000_192000|\
+ SNDRV_PCM_RATE_352800|\
+ SNDRV_PCM_RATE_384000)
#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt)
#define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8)
#define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8)
diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h
index 6be903a4c8d0..b252463b72a2 100644
--- a/include/sound/q6afe-v2.h
+++ b/include/sound/q6afe-v2.h
@@ -173,14 +173,16 @@ enum {
IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6,
IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7,
IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7,
- /* IDX 118->122 */
+ /* IDX 118->121 */
IDX_SLIMBUS_7_RX,
IDX_SLIMBUS_7_TX,
IDX_SLIMBUS_8_RX,
IDX_SLIMBUS_8_TX,
- /* IDX 123-> 124 */
+ /* IDX 122-> 123 */
IDX_AFE_PORT_ID_USB_RX,
IDX_AFE_PORT_ID_USB_TX,
+ /* IDX 124 */
+ IDX_DISPLAY_PORT_RX,
AFE_MAX_PORTS
};
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index dadc2f7a4eae..8525f2e7f738 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -226,7 +226,7 @@ struct audio_client *q6asm_get_audio_client(int session_id);
int q6asm_audio_client_buf_alloc(unsigned int dir/* 1:Out,0:In */,
struct audio_client *ac,
unsigned int bufsz,
- unsigned int bufcnt);
+ uint32_t bufcnt);
int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir
/* 1:Out,0:In */,
struct audio_client *ac,
diff --git a/include/trace/events/power.h b/include/trace/events/power.h
index 44228e7238cc..2df03af6f328 100644
--- a/include/trace/events/power.h
+++ b/include/trace/events/power.h
@@ -135,7 +135,7 @@ TRACE_EVENT(cpu_frequency_limits,
TP_fast_assign(
__entry->min_freq = min_freq;
- __entry->max_freq = min_freq;
+ __entry->max_freq = max_freq;
__entry->cpu_id = cpu_id;
),
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index f35630045c2f..1ef5ec3eaf70 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -9,9 +9,6 @@
#include <linux/binfmts.h>
struct rq;
-struct group_cpu_time;
-struct migration_sum_data;
-extern const char *task_event_names[];
/*
* Tracepoint for calling kthread_stop, performed to end a kthread:
@@ -113,6 +110,11 @@ TRACE_EVENT(sched_enq_deq_task,
#ifdef CONFIG_SCHED_HMP
+struct group_cpu_time;
+struct migration_sum_data;
+extern const char *task_event_names[];
+extern const char *migrate_type_names[];
+
TRACE_EVENT(sched_task_load,
TP_PROTO(struct task_struct *p, bool boost, int reason,
diff --git a/include/trace/events/skb.h b/include/trace/events/skb.h
index 95ca6e841212..b2e3c3b91f72 100644
--- a/include/trace/events/skb.h
+++ b/include/trace/events/skb.h
@@ -52,24 +52,29 @@ TRACE_EVENT(consume_skb,
TRACE_EVENT(print_skb_gso,
- TP_PROTO(struct sk_buff *skb),
+ TP_PROTO(struct sk_buff *skb, __be16 src, __be16 dest),
- TP_ARGS(skb),
+ TP_ARGS(skb, src, dest),
TP_STRUCT__entry(
__field(void *, skbaddr)
__field(int , len)
__field(int , data_len)
+ __field(__be16, src)
+ __field(__be16, dest)
),
TP_fast_assign(
__entry->skbaddr = skb;
__entry->len = skb->len;
__entry->data_len = skb->data_len;
+ __entry->src = src;
+ __entry->dest = dest;
),
- TP_printk("GSO: skbaddr=%p, len=%d, data_len=%d",
- __entry->skbaddr, __entry->len, __entry->data_len)
+ TP_printk("GSO: skbaddr=%pK, len=%d, data_len=%d, src=%u, dest=%u",
+ __entry->skbaddr, __entry->len, __entry->data_len,
+ be16_to_cpu(__entry->src), be16_to_cpu(__entry->dest))
);
TRACE_EVENT(skb_copy_datagram_iovec,
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/magic.h b/include/uapi/linux/magic.h
index accb036bbc9c..cfb5c406f344 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -52,6 +52,8 @@
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
+#define SDCARDFS_SUPER_MAGIC 0xb550ca10
+
#define SMB_SUPER_MAGIC 0x517B
#define CGROUP_SUPER_MAGIC 0x27e0eb
diff --git a/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h
index a9fe10d8cd6e..7902cfbafad8 100644
--- a/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h
+++ b/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -352,4 +352,10 @@
#define WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL (0xB70)
#define WCD9XXX_CDC_RX2_RX_PATH_SEC1 (0xB72)
+/* Class-H registers for codecs from and above WCD934X */
+#define WCD9XXX_HPH_CNP_WG_CTL (0x06cc)
+#define WCD9XXX_FLYBACK_VNEG_CTRL_4 (0x06a8)
+#define WCD9XXX_HPH_NEW_INT_PA_MISC2 (0x0738)
+#define WCD9XXX_RX_BIAS_HPH_LOWPOWER (0x06bf)
+#define WCD9XXX_HPH_PA_CTL1 (0x06d1)
#endif
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index b5323800eeb5..5305e8f4fce1 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -322,7 +322,9 @@
* @NL80211_CMD_GET_SCAN: get scan results
* @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
* %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
- * probe requests at CCK rate or not.
+ * probe requests at CCK rate or not. %NL80211_ATTR_MAC can be used to
+ * specify a BSSID to scan for; if not included, the wildcard BSSID will
+ * be used.
* @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
* NL80211_CMD_GET_SCAN and on the "scan" multicast group)
* @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
@@ -820,6 +822,10 @@
* as an event to indicate changes for devices with wiphy-specific regdom
* management.
*
+ * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is
+ * not running. The driver indicates the status of the scan through
+ * cfg80211_scan_done().
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1006,6 +1012,8 @@ enum nl80211_commands {
NL80211_CMD_WIPHY_REG_CHANGE,
+ NL80211_CMD_ABORT_SCAN,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
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 540a96c57e5b..2c7ada5d02cf 100644
--- a/include/uapi/media/msm_cam_sensor.h
+++ b/include/uapi/media/msm_cam_sensor.h
@@ -34,6 +34,9 @@
#define MAX_NUMBER_OF_STEPS 47
#define MAX_REGULATOR 5
+/*msm_flash_query_data_t query types*/
+#define FLASH_QUERY_CURRENT 1
+
#define MSM_V4L2_PIX_FMT_META v4l2_fourcc('M', 'E', 'T', 'A') /* META */
#define MSM_V4L2_PIX_FMT_META10 v4l2_fourcc('M', 'E', '1', '0') /* META10 */
#define MSM_V4L2_PIX_FMT_SBGGR14 v4l2_fourcc('B', 'G', '1', '4')
@@ -531,6 +534,12 @@ struct msm_flash_cfg_data_t {
} cfg;
};
+struct msm_flash_query_data_t {
+ int32_t flags;
+ int32_t query_type;
+ int32_t max_avail_curr;
+};
+
/* sensor init structures and enums */
enum msm_sensor_init_cfg_type_t {
CFG_SINIT_PROBE,
@@ -586,5 +595,8 @@ struct sensor_init_cfg_data {
#define VIDIOC_MSM_OIS_CFG_DOWNLOAD \
_IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct msm_ois_cfg_download_data)
+#define VIDIOC_MSM_FLASH_QUERY_DATA \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_flash_query_data_t)
+
#endif
diff --git a/include/uapi/media/msmb_isp.h b/include/uapi/media/msmb_isp.h
index 9f933dc7e84f..cdb85170919a 100644
--- a/include/uapi/media/msmb_isp.h
+++ b/include/uapi/media/msmb_isp.h
@@ -18,6 +18,9 @@
#define ISP_META_CHANNEL_BIT (0x10000 << 3)
#define ISP_SCRATCH_BUF_BIT (0x10000 << 4)
#define ISP_OFFLINE_STATS_BIT (0x10000 << 5)
+#define ISP_SVHDR_IN_BIT (0x10000 << 6) /* RDI hw stream for SVHDR */
+#define ISP_SVHDR_OUT_BIT (0x10000 << 7) /* SVHDR output bufq stream*/
+
#define ISP_STATS_STREAM_BIT 0x80000000
struct msm_vfe_cfg_cmd_list;
@@ -324,7 +327,9 @@ enum msm_vfe_axi_stream_update_type {
UPDATE_STREAM_ADD_BUFQ,
UPDATE_STREAM_REMOVE_BUFQ,
UPDATE_STREAM_SW_FRAME_DROP,
+ UPDATE_STREAM_REQUEST_FRAMES_VER2,
};
+#define UPDATE_STREAM_REQUEST_FRAMES_VER2 UPDATE_STREAM_REQUEST_FRAMES_VER2
enum msm_vfe_iommu_type {
IOMMU_ATTACH,
@@ -347,6 +352,13 @@ struct msm_vfe_axi_stream_cfg_update_info {
struct msm_isp_sw_framskip sw_skip_info;
};
+struct msm_vfe_axi_stream_cfg_update_info_req_frm {
+ uint32_t stream_handle;
+ uint32_t user_stream_id;
+ uint32_t frame_id;
+ uint32_t buf_index;
+};
+
struct msm_vfe_axi_halt_cmd {
uint32_t stop_camif;
uint32_t overflow_detected;
@@ -365,8 +377,15 @@ struct msm_vfe_axi_restart_cmd {
struct msm_vfe_axi_stream_update_cmd {
uint32_t num_streams;
enum msm_vfe_axi_stream_update_type update_type;
- struct msm_vfe_axi_stream_cfg_update_info
+ /*
+ * For backward compatibility, ensure 1st member of any struct
+ * in union below is uint32_t stream_handle.
+ */
+ union {
+ struct msm_vfe_axi_stream_cfg_update_info
update_info[MSM_ISP_STATS_MAX];
+ struct msm_vfe_axi_stream_cfg_update_info_req_frm req_frm_ver2;
+ };
};
struct msm_vfe_smmu_attach_cmd {
@@ -808,82 +827,133 @@ struct msm_isp_ahb_clk_cfg {
#define V4L2_PIX_FMT_SGRBG14 v4l2_fourcc('B', 'A', '1', '4') /* 14 GRGR.BGBG.*/
#define V4L2_PIX_FMT_SRGGB14 v4l2_fourcc('R', 'G', '1', '4') /* 14 RGRG.GBGB.*/
+enum msm_isp_ioctl_cmd_code {
+ MSM_VFE_REG_CFG = BASE_VIDIOC_PRIVATE,
+ MSM_ISP_REQUEST_BUF,
+ MSM_ISP_ENQUEUE_BUF,
+ MSM_ISP_RELEASE_BUF,
+ MSM_ISP_REQUEST_STREAM,
+ MSM_ISP_CFG_STREAM,
+ MSM_ISP_RELEASE_STREAM,
+ MSM_ISP_INPUT_CFG,
+ MSM_ISP_SET_SRC_STATE,
+ MSM_ISP_REQUEST_STATS_STREAM,
+ MSM_ISP_CFG_STATS_STREAM,
+ MSM_ISP_RELEASE_STATS_STREAM,
+ MSM_ISP_REG_UPDATE_CMD,
+ MSM_ISP_UPDATE_STREAM,
+ MSM_VFE_REG_LIST_CFG,
+ MSM_ISP_SMMU_ATTACH,
+ MSM_ISP_UPDATE_STATS_STREAM,
+ MSM_ISP_AXI_HALT,
+ MSM_ISP_AXI_RESET,
+ MSM_ISP_AXI_RESTART,
+ MSM_ISP_FETCH_ENG_START,
+ MSM_ISP_DEQUEUE_BUF,
+ MSM_ISP_SET_DUAL_HW_MASTER_SLAVE,
+ MSM_ISP_MAP_BUF_START_FE,
+ MSM_ISP_UNMAP_BUF,
+};
+
#define VIDIOC_MSM_VFE_REG_CFG \
- _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_vfe_cfg_cmd2)
+ _IOWR('V', MSM_VFE_REG_CFG, \
+ struct msm_vfe_cfg_cmd2)
#define VIDIOC_MSM_ISP_REQUEST_BUF \
- _IOWR('V', BASE_VIDIOC_PRIVATE+1, struct msm_isp_buf_request)
+ _IOWR('V', MSM_ISP_REQUEST_BUF, \
+ struct msm_isp_buf_request)
#define VIDIOC_MSM_ISP_ENQUEUE_BUF \
- _IOWR('V', BASE_VIDIOC_PRIVATE+2, struct msm_isp_qbuf_info)
+ _IOWR('V', MSM_ISP_ENQUEUE_BUF, \
+ struct msm_isp_qbuf_info)
#define VIDIOC_MSM_ISP_RELEASE_BUF \
- _IOWR('V', BASE_VIDIOC_PRIVATE+3, struct msm_isp_buf_request)
+ _IOWR('V', MSM_ISP_RELEASE_BUF, \
+ struct msm_isp_buf_request)
#define VIDIOC_MSM_ISP_REQUEST_STREAM \
- _IOWR('V', BASE_VIDIOC_PRIVATE+4, struct msm_vfe_axi_stream_request_cmd)
+ _IOWR('V', MSM_ISP_REQUEST_STREAM, \
+ struct msm_vfe_axi_stream_request_cmd)
#define VIDIOC_MSM_ISP_CFG_STREAM \
- _IOWR('V', BASE_VIDIOC_PRIVATE+5, struct msm_vfe_axi_stream_cfg_cmd)
+ _IOWR('V', MSM_ISP_CFG_STREAM, \
+ struct msm_vfe_axi_stream_cfg_cmd)
#define VIDIOC_MSM_ISP_RELEASE_STREAM \
- _IOWR('V', BASE_VIDIOC_PRIVATE+6, struct msm_vfe_axi_stream_release_cmd)
+ _IOWR('V', MSM_ISP_RELEASE_STREAM, \
+ struct msm_vfe_axi_stream_release_cmd)
#define VIDIOC_MSM_ISP_INPUT_CFG \
- _IOWR('V', BASE_VIDIOC_PRIVATE+7, struct msm_vfe_input_cfg)
+ _IOWR('V', MSM_ISP_INPUT_CFG, \
+ struct msm_vfe_input_cfg)
#define VIDIOC_MSM_ISP_SET_SRC_STATE \
- _IOWR('V', BASE_VIDIOC_PRIVATE+8, struct msm_vfe_axi_src_state)
+ _IOWR('V', MSM_ISP_SET_SRC_STATE, \
+ struct msm_vfe_axi_src_state)
#define VIDIOC_MSM_ISP_REQUEST_STATS_STREAM \
- _IOWR('V', BASE_VIDIOC_PRIVATE+9, \
- struct msm_vfe_stats_stream_request_cmd)
+ _IOWR('V', MSM_ISP_REQUEST_STATS_STREAM, \
+ struct msm_vfe_stats_stream_request_cmd)
#define VIDIOC_MSM_ISP_CFG_STATS_STREAM \
- _IOWR('V', BASE_VIDIOC_PRIVATE+10, struct msm_vfe_stats_stream_cfg_cmd)
+ _IOWR('V', MSM_ISP_CFG_STATS_STREAM, \
+ struct msm_vfe_stats_stream_cfg_cmd)
#define VIDIOC_MSM_ISP_RELEASE_STATS_STREAM \
- _IOWR('V', BASE_VIDIOC_PRIVATE+11, \
- struct msm_vfe_stats_stream_release_cmd)
+ _IOWR('V', MSM_ISP_RELEASE_STATS_STREAM, \
+ struct msm_vfe_stats_stream_release_cmd)
#define VIDIOC_MSM_ISP_REG_UPDATE_CMD \
- _IOWR('V', BASE_VIDIOC_PRIVATE+12, enum msm_vfe_input_src)
+ _IOWR('V', MSM_ISP_REG_UPDATE_CMD, \
+ enum msm_vfe_input_src)
#define VIDIOC_MSM_ISP_UPDATE_STREAM \
- _IOWR('V', BASE_VIDIOC_PRIVATE+13, struct msm_vfe_axi_stream_update_cmd)
+ _IOWR('V', MSM_ISP_UPDATE_STREAM, \
+ struct msm_vfe_axi_stream_update_cmd)
#define VIDIOC_MSM_VFE_REG_LIST_CFG \
- _IOWR('V', BASE_VIDIOC_PRIVATE+14, struct msm_vfe_cfg_cmd_list)
+ _IOWR('V', MSM_VFE_REG_LIST_CFG, \
+ struct msm_vfe_cfg_cmd_list)
#define VIDIOC_MSM_ISP_SMMU_ATTACH \
- _IOWR('V', BASE_VIDIOC_PRIVATE+15, struct msm_vfe_smmu_attach_cmd)
+ _IOWR('V', MSM_ISP_SMMU_ATTACH, \
+ struct msm_vfe_smmu_attach_cmd)
#define VIDIOC_MSM_ISP_UPDATE_STATS_STREAM \
- _IOWR('V', BASE_VIDIOC_PRIVATE+16, struct msm_vfe_axi_stream_update_cmd)
+ _IOWR('V', MSM_ISP_UPDATE_STATS_STREAM, \
+ struct msm_vfe_axi_stream_update_cmd)
#define VIDIOC_MSM_ISP_AXI_HALT \
- _IOWR('V', BASE_VIDIOC_PRIVATE+17, struct msm_vfe_axi_halt_cmd)
+ _IOWR('V', MSM_ISP_AXI_HALT, \
+ struct msm_vfe_axi_halt_cmd)
#define VIDIOC_MSM_ISP_AXI_RESET \
- _IOWR('V', BASE_VIDIOC_PRIVATE+18, struct msm_vfe_axi_reset_cmd)
+ _IOWR('V', MSM_ISP_AXI_RESET, \
+ struct msm_vfe_axi_reset_cmd)
#define VIDIOC_MSM_ISP_AXI_RESTART \
- _IOWR('V', BASE_VIDIOC_PRIVATE+19, struct msm_vfe_axi_restart_cmd)
+ _IOWR('V', MSM_ISP_AXI_RESTART, \
+ struct msm_vfe_axi_restart_cmd)
#define VIDIOC_MSM_ISP_FETCH_ENG_START \
- _IOWR('V', BASE_VIDIOC_PRIVATE+20, struct msm_vfe_fetch_eng_start)
+ _IOWR('V', MSM_ISP_FETCH_ENG_START, \
+ struct msm_vfe_fetch_eng_start)
#define VIDIOC_MSM_ISP_DEQUEUE_BUF \
- _IOWR('V', BASE_VIDIOC_PRIVATE+21, struct msm_isp_qbuf_info)
+ _IOWR('V', MSM_ISP_DEQUEUE_BUF, \
+ struct msm_isp_qbuf_info)
#define VIDIOC_MSM_ISP_SET_DUAL_HW_MASTER_SLAVE \
- _IOWR('V', BASE_VIDIOC_PRIVATE+22, struct msm_isp_set_dual_hw_ms_cmd)
+ _IOWR('V', MSM_ISP_SET_DUAL_HW_MASTER_SLAVE, \
+ struct msm_isp_set_dual_hw_ms_cmd)
#define VIDIOC_MSM_ISP_MAP_BUF_START_FE \
- _IOWR('V', BASE_VIDIOC_PRIVATE+23, struct msm_vfe_fetch_eng_start)
+ _IOWR('V', MSM_ISP_MAP_BUF_START_FE, \
+ struct msm_vfe_fetch_eng_start)
#define VIDIOC_MSM_ISP_UNMAP_BUF \
- _IOWR('V', BASE_VIDIOC_PRIVATE+24, struct msm_isp_unmap_buf_req)
+ _IOWR('V', MSM_ISP_UNMAP_BUF, \
+ struct msm_isp_unmap_buf_req)
#define VIDIOC_MSM_ISP_AHB_CLK_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE+25, struct msm_isp_ahb_clk_cfg)
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..d6ec580584b6 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
@@ -8325,6 +8328,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/locking/mutex.c b/kernel/locking/mutex.c
index 0551c219c40e..fb42418507ae 100644
--- a/kernel/locking/mutex.c
+++ b/kernel/locking/mutex.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/debug_locks.h>
#include <linux/osq_lock.h>
+#include <linux/delay.h>
/*
* In the DEBUG case we are using the "NULL fastpath" for mutexes,
@@ -378,6 +379,17 @@ static bool mutex_optimistic_spin(struct mutex *lock,
* values at the cost of a few extra spins.
*/
cpu_relax_lowlatency();
+
+ /*
+ * On arm systems, we must slow down the waiter's repeated
+ * aquisition of spin_mlock and atomics on the lock count, or
+ * we risk starving out a thread attempting to release the
+ * mutex. The mutex slowpath release must take spin lock
+ * wait_lock. This spin lock can share a monitor with the
+ * other waiter atomics in the mutex data structure, so must
+ * take care to rate limit the waiters.
+ */
+ udelay(1);
}
osq_unlock(&lock->osq);
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 84c480946fb2..6d6f63be1f9b 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -1,6 +1,7 @@
config SUSPEND
bool "Suspend to RAM and standby"
depends on ARCH_SUSPEND_POSSIBLE
+ select RTC_LIB
default y
---help---
Allow the system to enter sleep states in which main memory is
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index ae54c5bfac1d..7474463b9835 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -90,13 +90,6 @@
#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>
-const char *task_event_names[] = {"PUT_PREV_TASK", "PICK_NEXT_TASK",
- "TASK_WAKE", "TASK_MIGRATE", "TASK_UPDATE",
- "IRQ_UPDATE"};
-
-const char *migrate_type_names[] = {"GROUP_TO_RQ", "RQ_TO_GROUP",
- "RQ_TO_RQ", "GROUP_TO_GROUP"};
-
ATOMIC_NOTIFIER_HEAD(load_alert_notifier_head);
DEFINE_MUTEX(sched_domains_mutex);
diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c
index 447f3880f645..5002619961ce 100644
--- a/kernel/sched/hmp.c
+++ b/kernel/sched/hmp.c
@@ -22,6 +22,13 @@
#include <trace/events/sched.h>
+const char *task_event_names[] = {"PUT_PREV_TASK", "PICK_NEXT_TASK",
+ "TASK_WAKE", "TASK_MIGRATE", "TASK_UPDATE",
+ "IRQ_UPDATE"};
+
+const char *migrate_type_names[] = {"GROUP_TO_RQ", "RQ_TO_GROUP",
+ "RQ_TO_RQ", "GROUP_TO_GROUP"};
+
static ktime_t ktime_last;
static bool sched_ktime_suspended;
@@ -117,7 +124,18 @@ u32 __weak get_freq_max_load(int cpu, u32 freq)
return 100;
}
-DEFINE_PER_CPU(struct freq_max_load *, freq_max_load);
+struct freq_max_load_entry {
+ /* The maximum load which has accounted governor's headroom. */
+ u64 hdemand;
+};
+
+struct freq_max_load {
+ struct rcu_head rcu;
+ int length;
+ struct freq_max_load_entry freqs[0];
+};
+
+static DEFINE_PER_CPU(struct freq_max_load *, freq_max_load);
static DEFINE_SPINLOCK(freq_max_load_lock);
struct cpu_pwr_stats __weak *get_cpu_pwr_stats(void)
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index e31334d5f581..ec7721112b05 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -28,19 +28,6 @@ extern atomic_long_t calc_load_tasks;
extern void calc_global_load_tick(struct rq *this_rq);
-struct freq_max_load_entry {
- /* The maximum load which has accounted governor's headroom. */
- u64 hdemand;
-};
-
-struct freq_max_load {
- struct rcu_head rcu;
- int length;
- struct freq_max_load_entry freqs[0];
-};
-
-extern DEFINE_PER_CPU(struct freq_max_load *, freq_max_load);
-
extern long calc_load_fold_active(struct rq *this_rq);
#ifdef CONFIG_SMP
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 9472691c1eb0..029da92fb712 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -121,7 +121,7 @@ static unsigned long soft_lockup_nmi_warn;
unsigned int __read_mostly hardlockup_panic =
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE;
#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI
-static unsigned long hardlockup_allcpu_dumped;
+static unsigned long __maybe_unused hardlockup_allcpu_dumped;
#endif
/*
* We may not want to enable hard lockup detection by default in all cases,
diff --git a/mm/mmap.c b/mm/mmap.c
index 6c561acdca92..a089cca8d79a 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2634,6 +2634,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
return 0;
}
+EXPORT_SYMBOL(do_munmap);
int vm_munmap(unsigned long start, size_t len)
{
diff --git a/net/core/dev.c b/net/core/dev.c
index 95b832edc303..a299c3956daa 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -137,6 +137,8 @@
#include <linux/errqueue.h>
#include <linux/hrtimer.h>
#include <linux/netfilter_ingress.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
#include "net-sysfs.h"
@@ -2773,7 +2775,10 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device
if (netif_needs_gso(skb, features)) {
struct sk_buff *segs;
- trace_print_skb_gso(skb);
+ __be16 src_port = tcp_hdr(skb)->source;
+ __be16 dest_port = tcp_hdr(skb)->dest;
+
+ trace_print_skb_gso(skb, src_port, dest_port);
segs = skb_gso_segment(skb, features);
if (IS_ERR(segs)) {
goto out_kfree_skb;
@@ -4142,6 +4147,7 @@ static void gro_list_prepare(struct napi_struct *napi, struct sk_buff *skb)
unsigned long diffs;
NAPI_GRO_CB(p)->flush = 0;
+ NAPI_GRO_CB(p)->flush_id = 0;
if (hash != skb_get_hash_raw(p)) {
NAPI_GRO_CB(p)->same_flow = 0;
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_core.c b/net/ipc_router/ipc_router_core.c
index fae41583d0e9..008d034fcf8f 100644
--- a/net/ipc_router/ipc_router_core.c
+++ b/net/ipc_router/ipc_router_core.c
@@ -2176,7 +2176,6 @@ static void cleanup_rmt_server(struct msm_ipc_router_xprt_info *xprt_info,
{
union rr_control_msg ctl;
- ipc_router_reset_conn(rport_ptr);
memset(&ctl, 0, sizeof(ctl));
ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
ctl.srv.service = server->name.service;
@@ -2207,6 +2206,7 @@ static void cleanup_rmt_ports(struct msm_ipc_router_xprt_info *xprt_info,
server = rport_ptr->server;
rport_ptr->server = NULL;
mutex_unlock(&rport_ptr->rport_lock_lhb2);
+ ipc_router_reset_conn(rport_ptr);
if (server) {
cleanup_rmt_server(xprt_info, rport_ptr,
server);
@@ -2361,13 +2361,13 @@ static void ipc_router_reset_conn(struct msm_ipc_router_remote_port *rport_ptr)
list_for_each_entry_safe(conn_info, tmp_conn_info,
&rport_ptr->conn_info_list, list) {
port_ptr = ipc_router_get_port_ref(conn_info->port_id);
- if (!port_ptr)
- continue;
- mutex_lock(&port_ptr->port_lock_lhc3);
- port_ptr->conn_status = CONNECTION_RESET;
- mutex_unlock(&port_ptr->port_lock_lhc3);
- wake_up(&port_ptr->port_rx_wait_q);
- kref_put(&port_ptr->ref, ipc_router_release_port);
+ if (port_ptr) {
+ mutex_lock(&port_ptr->port_lock_lhc3);
+ port_ptr->conn_status = CONNECTION_RESET;
+ mutex_unlock(&port_ptr->port_lock_lhc3);
+ wake_up(&port_ptr->port_rx_wait_q);
+ kref_put(&port_ptr->ref, ipc_router_release_port);
+ }
list_del(&conn_info->list);
kfree(conn_info);
@@ -2651,6 +2651,7 @@ static int process_rmv_client_msg(struct msm_ipc_router_xprt_info *xprt_info,
server = rport_ptr->server;
rport_ptr->server = NULL;
mutex_unlock(&rport_ptr->rport_lock_lhb2);
+ ipc_router_reset_conn(rport_ptr);
down_write(&server_list_lock_lha2);
if (server)
cleanup_rmt_server(NULL, rport_ptr, server);
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index a243ef1d7aa0..c600403137b7 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1345,6 +1345,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
for (p = *head; p; p = p->next) {
struct iphdr *iph2;
+ u16 flush_id;
if (!NAPI_GRO_CB(p)->same_flow)
continue;
@@ -1368,14 +1369,24 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
(iph->tos ^ iph2->tos) |
((iph->frag_off ^ iph2->frag_off) & htons(IP_DF));
- /* Save the IP ID check to be included later when we get to
- * the transport layer so only the inner most IP ID is checked.
- * This is because some GSO/TSO implementations do not
- * correctly increment the IP ID for the outer hdrs.
- */
- NAPI_GRO_CB(p)->flush_id =
- ((u16)(ntohs(iph2->id) + NAPI_GRO_CB(p)->count) ^ id);
NAPI_GRO_CB(p)->flush |= flush;
+
+ /* We must save the offset as it is possible to have multiple
+ * flows using the same protocol and address pairs so we
+ * need to wait until we can validate this is part of the
+ * same flow with a 5-tuple or better to avoid unnecessary
+ * collisions between flows. We can support one of two
+ * possible scenarios, either a fixed value with DF bit set
+ * or an incrementing value with DF either set or unset.
+ * In the case of a fixed value we will end up losing the
+ * data that the IP ID was a fixed value, however per RFC
+ * 6864 in such a case the actual value of the IP ID is
+ * meant to be ignored anyway.
+ */
+ flush_id = (u16)(id - ntohs(iph2->id));
+ if (flush_id || !(iph2->frag_off & htons(IP_DF)))
+ NAPI_GRO_CB(p)->flush_id |= flush_id ^
+ NAPI_GRO_CB(p)->count;
}
NAPI_GRO_CB(skb)->flush |= flush;
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index eeca943f12dc..f542438c70ce 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -238,9 +238,6 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
/* flush if Traffic Class fields are different */
NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000));
NAPI_GRO_CB(p)->flush |= flush;
-
- /* Clear flush_id, there's really no concept of ID in IPv6. */
- NAPI_GRO_CB(p)->flush_id = 0;
}
NAPI_GRO_CB(skb)->flush |= flush;
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index e2e7d54f9bb1..dca5cacc51f0 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -1175,6 +1175,38 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only)
spin_unlock_bh(&iface_stat_list_lock);
}
+/* Guarantied to return a net_device that has a name */
+static void get_dev_and_dir(const struct sk_buff *skb,
+ struct xt_action_param *par,
+ enum ifs_tx_rx *direction,
+ const struct net_device **el_dev)
+{
+ BUG_ON(!direction || !el_dev);
+
+ if (par->in) {
+ *el_dev = par->in;
+ *direction = IFS_RX;
+ } else if (par->out) {
+ *el_dev = par->out;
+ *direction = IFS_TX;
+ } else {
+ pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n",
+ par->hooknum, __func__);
+ BUG();
+ }
+ if (unlikely(!(*el_dev)->name)) {
+ pr_err("qtaguid[%d]: %s(): no dev->name?!!\n",
+ par->hooknum, __func__);
+ BUG();
+ }
+ if (skb->dev && *el_dev != skb->dev) {
+ MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs par->%s=%p %s\n",
+ par->hooknum, skb->dev, skb->dev->name,
+ *direction == IFS_RX ? "in" : "out", *el_dev,
+ (*el_dev)->name);
+ }
+}
+
/*
* Update stats for the specified interface from the skb.
* Do nothing if the entry
@@ -1186,50 +1218,27 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb,
{
struct iface_stat *entry;
const struct net_device *el_dev;
- enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX;
+ enum ifs_tx_rx direction;
int bytes = skb->len;
int proto;
- if (!skb->dev) {
- MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
- el_dev = par->in ? : par->out;
- } else {
- const struct net_device *other_dev;
- el_dev = skb->dev;
- other_dev = par->in ? : par->out;
- if (el_dev != other_dev) {
- MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
- "par->(in/out)=%p %s\n",
- par->hooknum, el_dev, el_dev->name, other_dev,
- other_dev->name);
- }
- }
-
- if (unlikely(!el_dev)) {
- pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n",
- par->hooknum, __func__);
- BUG();
- } else if (unlikely(!el_dev->name)) {
- pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n",
- par->hooknum, __func__);
- BUG();
- } else {
- proto = ipx_proto(skb, par);
- MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
- par->hooknum, el_dev->name, el_dev->type,
- par->family, proto);
- }
+ get_dev_and_dir(skb, par, &direction, &el_dev);
+ proto = ipx_proto(skb, par);
+ MT_DEBUG("qtaguid[%d]: iface_stat: %s(%s): "
+ "type=%d fam=%d proto=%d dir=%d\n",
+ par->hooknum, __func__, el_dev->name, el_dev->type,
+ par->family, proto, direction);
spin_lock_bh(&iface_stat_list_lock);
entry = get_iface_entry(el_dev->name);
if (entry == NULL) {
- IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n",
- __func__, el_dev->name);
+ IF_DEBUG("qtaguid[%d]: iface_stat: %s(%s): not tracked\n",
+ par->hooknum, __func__, el_dev->name);
spin_unlock_bh(&iface_stat_list_lock);
return;
}
- IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__,
+ IF_DEBUG("qtaguid[%d]: %s(%s): entry=%p\n", par->hooknum, __func__,
el_dev->name, entry);
data_counters_update(&entry->totals_via_skb, 0, direction, proto,
@@ -1294,14 +1303,14 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
spin_lock_bh(&iface_stat_list_lock);
iface_entry = get_iface_entry(ifname);
if (!iface_entry) {
- pr_err_ratelimited("qtaguid: iface_stat: stat_update() "
+ pr_err_ratelimited("qtaguid: tag_stat: stat_update() "
"%s not found\n", ifname);
spin_unlock_bh(&iface_stat_list_lock);
return;
}
/* It is ok to process data when an iface_entry is inactive */
- MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n",
+ MT_DEBUG("qtaguid: tag_stat: stat_update() dev=%s entry=%p\n",
ifname, iface_entry);
/*
@@ -1318,7 +1327,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
tag = combine_atag_with_uid(acct_tag, uid);
uid_tag = make_tag_from_uid(uid);
}
- MT_DEBUG("qtaguid: iface_stat: stat_update(): "
+ MT_DEBUG("qtaguid: tag_stat: stat_update(): "
" looking for tag=0x%llx (uid=%u) in ife=%p\n",
tag, get_uid_from_tag(tag), iface_entry);
/* Loop over tag list under this interface for {acct_tag,uid_tag} */
@@ -1578,8 +1587,8 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb,
struct sock *sk;
unsigned int hook_mask = (1 << par->hooknum);
- MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb,
- par->hooknum, par->family);
+ MT_DEBUG("qtaguid[%d]: find_sk(skb=%p) family=%d\n",
+ par->hooknum, skb, par->family);
/*
* Let's not abuse the the xt_socket_get*_sk(), or else it will
@@ -1600,8 +1609,8 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb,
}
if (sk) {
- MT_DEBUG("qtaguid: %p->sk_proto=%u "
- "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state);
+ MT_DEBUG("qtaguid[%d]: %p->sk_proto=%u->sk_state=%d\n",
+ par->hooknum, sk, sk->sk_protocol, sk->sk_state);
/*
* When in TCP_TIME_WAIT the sk is not a "struct sock" but
* "struct inet_timewait_sock" which is missing fields.
@@ -1619,37 +1628,19 @@ static void account_for_uid(const struct sk_buff *skb,
struct xt_action_param *par)
{
const struct net_device *el_dev;
+ enum ifs_tx_rx direction;
+ int proto;
- if (!skb->dev) {
- MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
- el_dev = par->in ? : par->out;
- } else {
- const struct net_device *other_dev;
- el_dev = skb->dev;
- other_dev = par->in ? : par->out;
- if (el_dev != other_dev) {
- MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
- "par->(in/out)=%p %s\n",
- par->hooknum, el_dev, el_dev->name, other_dev,
- other_dev->name);
- }
- }
-
- if (unlikely(!el_dev)) {
- pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum);
- } else if (unlikely(!el_dev->name)) {
- pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum);
- } else {
- int proto = ipx_proto(skb, par);
- MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
- par->hooknum, el_dev->name, el_dev->type,
- par->family, proto);
+ get_dev_and_dir(skb, par, &direction, &el_dev);
+ proto = ipx_proto(skb, par);
+ MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d dir=%d\n",
+ par->hooknum, el_dev->name, el_dev->type,
+ par->family, proto, direction);
- if_tag_stat_update(el_dev->name, uid,
- skb->sk ? skb->sk : alternate_sk,
- par->in ? IFS_RX : IFS_TX,
- proto, skb->len);
- }
+ if_tag_stat_update(el_dev->name, uid,
+ skb->sk ? skb->sk : alternate_sk,
+ direction,
+ proto, skb->len);
}
static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
@@ -1661,6 +1652,11 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
kuid_t sock_uid;
bool res;
bool set_sk_callback_lock = false;
+ /*
+ * TODO: unhack how to force just accounting.
+ * For now we only do tag stats when the uid-owner is not requested
+ */
+ bool do_tag_stat = !(info->match & XT_QTAGUID_UID);
if (unlikely(module_passive))
return (info->match ^ info->invert) == 0;
@@ -1734,12 +1730,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
* couldn't find the owner, so for now we just count them
* against the system.
*/
- /*
- * TODO: unhack how to force just accounting.
- * For now we only do iface stats when the uid-owner is not
- * requested.
- */
- if (!(info->match & XT_QTAGUID_UID))
+ if (do_tag_stat)
account_for_uid(skb, sk, 0, par);
MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n",
par->hooknum,
@@ -1754,18 +1745,15 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
filp = sk->sk_socket->file;
if (filp == NULL) {
MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum);
- account_for_uid(skb, sk, 0, par);
+ if (do_tag_stat)
+ account_for_uid(skb, sk, 0, par);
res = ((info->match ^ info->invert) &
(XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0;
atomic64_inc(&qtu_events.match_no_sk_file);
goto put_sock_ret_res;
}
sock_uid = filp->f_cred->fsuid;
- /*
- * TODO: unhack how to force just accounting.
- * For now we only do iface stats when the uid-owner is not requested
- */
- if (!(info->match & XT_QTAGUID_UID))
+ if (do_tag_stat)
account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par);
/*
@@ -1946,7 +1934,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v)
);
f_count = atomic_long_read(
&sock_tag_entry->socket->file->f_count);
- seq_printf(m, "sock=%p tag=0x%llx (uid=%u) pid=%u "
+ seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u "
"f_count=%lu\n",
sock_tag_entry->sk,
sock_tag_entry->tag, uid,
diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c
index 99592ae56d9b..90604d8a8b4f 100644
--- a/net/netfilter/xt_quota2.c
+++ b/net/netfilter/xt_quota2.c
@@ -16,14 +16,15 @@
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
+#include <linux/workqueue.h>
#include <asm/atomic.h>
#include <net/netlink.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_quota2.h>
-#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
-#include <linux/netfilter_ipv4/ipt_ULOG.h>
-#endif
+
+#define QUOTA2_SYSFS_WORK_MAX_SIZE 64
+#define QUOTA2_SYSFS_NUM_ENVP 3
/**
* @lock: lock to protect quota writers from each other
@@ -35,17 +36,16 @@ struct xt_quota_counter {
atomic_t ref;
char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)];
struct proc_dir_entry *procfs_entry;
+ char last_iface[QUOTA2_SYSFS_WORK_MAX_SIZE];
+ char last_prefix[QUOTA2_SYSFS_WORK_MAX_SIZE];
+ struct work_struct work;
};
-#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
-/* Harald's favorite number +1 :D From ipt_ULOG.C */
-static int qlog_nl_event = 112;
-module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(event_num,
- "Event number for NETLINK_NFLOG message. 0 disables log."
- "111 is what ipt_ULOG uses.");
-static struct sock *nflognl;
-#endif
+#define to_quota_counter(x) container_of(x, struct xt_quota_counter, work)
+
+static struct class *quota_class;
+static struct device *quota_device;
+static struct kobject *quota_kobj;
static LIST_HEAD(counter_list);
static DEFINE_SPINLOCK(counter_list_lock);
@@ -56,68 +56,39 @@ static kuid_t quota_list_uid = KUIDT_INIT(0);
static kgid_t quota_list_gid = KGIDT_INIT(0);
module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR);
-#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
-static void quota2_log(unsigned int hooknum,
- const struct sk_buff *skb,
- const struct net_device *in,
+static void quota2_work(struct work_struct *work)
+{
+ char alert_msg[QUOTA2_SYSFS_WORK_MAX_SIZE];
+ char iface_name[QUOTA2_SYSFS_WORK_MAX_SIZE];
+ char *envp[QUOTA2_SYSFS_NUM_ENVP] = {alert_msg, iface_name, NULL};
+ struct xt_quota_counter *counter = to_quota_counter(work);
+
+ snprintf(alert_msg, sizeof(alert_msg), "ALERT_NAME=%s", counter->name);
+ snprintf(iface_name, sizeof(iface_name), "INTERFACE=%s",
+ counter->last_iface);
+
+ kobject_uevent_env(quota_kobj, KOBJ_CHANGE, envp);
+}
+
+static void quota2_log(const struct net_device *in,
const struct net_device *out,
+ struct xt_quota_counter *q,
const char *prefix)
{
- ulog_packet_msg_t *pm;
- struct sk_buff *log_skb;
- size_t size;
- struct nlmsghdr *nlh;
-
- if (!qlog_nl_event)
+ if (!prefix)
return;
- size = NLMSG_SPACE(sizeof(*pm));
- size = max(size, (size_t)NLMSG_GOODSIZE);
- log_skb = alloc_skb(size, GFP_ATOMIC);
- if (!log_skb) {
- pr_err("xt_quota2: cannot alloc skb for logging\n");
- return;
- }
+ strlcpy(q->last_prefix, prefix, QUOTA2_SYSFS_WORK_MAX_SIZE);
- nlh = nlmsg_put(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event,
- sizeof(*pm), 0);
- if (!nlh) {
- pr_err("xt_quota2: nlmsg_put failed\n");
- kfree_skb(log_skb);
- return;
- }
- pm = nlmsg_data(nlh);
- if (skb->tstamp.tv64 == 0)
- __net_timestamp((struct sk_buff *)skb);
- pm->data_len = 0;
- pm->hook = hooknum;
- if (prefix != NULL)
- strlcpy(pm->prefix, prefix, sizeof(pm->prefix));
- else
- *(pm->prefix) = '\0';
if (in)
- strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name));
- else
- pm->indev_name[0] = '\0';
-
- if (out)
- strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name));
+ strlcpy(q->last_iface, in->name, QUOTA2_SYSFS_WORK_MAX_SIZE);
+ else if (out)
+ strlcpy(q->last_iface, out->name, QUOTA2_SYSFS_WORK_MAX_SIZE);
else
- pm->outdev_name[0] = '\0';
+ strlcpy(q->last_iface, "UNKNOWN", QUOTA2_SYSFS_WORK_MAX_SIZE);
- NETLINK_CB(log_skb).dst_group = 1;
- pr_debug("throwing 1 packets to netlink group 1\n");
- netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC);
-}
-#else
-static void quota2_log(unsigned int hooknum,
- const struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- const char *prefix)
-{
+ schedule_work(&q->work);
}
-#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */
static ssize_t quota_proc_read(struct file *file, char __user *buf,
size_t size, loff_t *ppos)
@@ -174,6 +145,9 @@ q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon)
INIT_LIST_HEAD(&e->list);
atomic_set(&e->ref, 1);
strlcpy(e->name, q->name, sizeof(e->name));
+ strlcpy(e->last_prefix, "UNSET", sizeof(e->last_prefix));
+ strlcpy(e->last_iface, "UNSET", sizeof(e->last_iface));
+ INIT_WORK(&e->work, quota2_work);
}
return e;
}
@@ -307,11 +281,7 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par)
} else {
/* We are transitioning, log that fact. */
if (e->quota) {
- quota2_log(par->hooknum,
- skb,
- par->in,
- par->out,
- q->name);
+ quota2_log(par->in, par->out, e, q->name);
}
/* we do not allow even small packets from now on */
e->quota = 0;
@@ -349,11 +319,25 @@ static int __init quota_mt2_init(void)
int ret;
pr_debug("xt_quota2: init()");
-#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
- nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, NULL);
- if (!nflognl)
- return -ENOMEM;
-#endif
+ quota_class = class_create(THIS_MODULE, "xt_quota2");
+ ret = PTR_ERR(quota_class);
+ if (IS_ERR(quota_class)) {
+ pr_err("xt_quota2: couldn't create class");
+ class_destroy(quota_class);
+ return ret;
+ }
+
+ quota_device = device_create(quota_class, NULL, MKDEV(0, 0), NULL,
+ "counters");
+ ret = PTR_ERR(quota_device);
+ if (IS_ERR(quota_device)) {
+ pr_err("xt_quota2: couldn't create device");
+ device_destroy(quota_class, MKDEV(0, 0));
+ class_destroy(quota_class);
+ return ret;
+ }
+
+ quota_kobj = &quota_device->kobj;
proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net);
if (proc_xt_quota == NULL)
@@ -370,6 +354,8 @@ static void __exit quota_mt2_exit(void)
{
xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
remove_proc_entry("xt_quota", init_net.proc_net);
+ device_destroy(quota_class, MKDEV(0, 0));
+ class_destroy(quota_class);
}
module_init(quota_mt2_init);
diff --git a/net/rmnet_data/rmnet_data_config.c b/net/rmnet_data/rmnet_data_config.c
index c49393924e26..ebce455b645d 100644
--- a/net/rmnet_data/rmnet_data_config.c
+++ b/net/rmnet_data/rmnet_data_config.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -21,6 +21,7 @@
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/rmnet_data.h>
+#include <net/rmnet_config.h>
#include "rmnet_data_config.h"
#include "rmnet_data_handlers.h"
#include "rmnet_data_vnd.h"
@@ -141,14 +142,22 @@ static inline int _rmnet_is_physical_endpoint_associated(struct net_device *dev)
* - pointer to configuration if successful
* - 0 (null) if device is not associated
*/
-static inline struct rmnet_phys_ep_conf_s *_rmnet_get_phys_ep_config
- (struct net_device *dev)
+struct rmnet_phys_ep_config *_rmnet_get_phys_ep_config
+ (struct net_device *dev)
{
- if (_rmnet_is_physical_endpoint_associated(dev))
- return (struct rmnet_phys_ep_conf_s *)
- rcu_dereference(dev->rx_handler_data);
- else
+ struct rmnet_phys_ep_conf_s *_rmnet_phys_ep_config;
+
+ if (_rmnet_is_physical_endpoint_associated(dev)) {
+ _rmnet_phys_ep_config = (struct rmnet_phys_ep_conf_s *)
+ rcu_dereference(dev->rx_handler_data);
+ if (_rmnet_phys_ep_config && _rmnet_phys_ep_config->config)
+ return (struct rmnet_phys_ep_config *)
+ _rmnet_phys_ep_config->config;
+ else
+ return 0;
+ } else {
return 0;
+ }
}
/**
@@ -165,7 +174,7 @@ static inline struct rmnet_phys_ep_conf_s *_rmnet_get_phys_ep_config
struct rmnet_logical_ep_conf_s *_rmnet_get_logical_ep(struct net_device *dev,
int config_id)
{
- struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_phys_ep_config *config;
struct rmnet_logical_ep_conf_s *epconfig_l;
if (rmnet_vnd_is_vnd(dev))
@@ -400,7 +409,7 @@ static void _rmnet_netlink_get_link_egress_data_format
struct rmnet_nl_msg_s *resp_rmnet)
{
struct net_device *dev;
- struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_phys_ep_config *config;
_RMNET_NETLINK_NULL_CHECKS();
resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
@@ -432,7 +441,7 @@ static void _rmnet_netlink_get_link_ingress_data_format
struct rmnet_nl_msg_s *resp_rmnet)
{
struct net_device *dev;
- struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_phys_ep_config *config;
_RMNET_NETLINK_NULL_CHECKS();
resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
@@ -725,7 +734,7 @@ int rmnet_set_ingress_data_format(struct net_device *dev,
uint32_t ingress_data_format,
uint8_t tail_spacing)
{
- struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_phys_ep_config *config;
ASSERT_RTNL();
LOGL("(%s,0x%08X);", dev->name, ingress_data_format);
@@ -762,7 +771,7 @@ int rmnet_set_egress_data_format(struct net_device *dev,
uint16_t agg_size,
uint16_t agg_count)
{
- struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_phys_ep_config *config;
ASSERT_RTNL();
LOGL("(%s,0x%08X, %d, %d);",
@@ -800,7 +809,9 @@ int rmnet_set_egress_data_format(struct net_device *dev,
int rmnet_associate_network_device(struct net_device *dev)
{
struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_phys_ep_config *conf;
int rc;
+
ASSERT_RTNL();
LOGL("(%s);\n", dev->name);
@@ -819,19 +830,25 @@ int rmnet_associate_network_device(struct net_device *dev)
}
config = kmalloc(sizeof(*config), GFP_ATOMIC);
+ conf = kmalloc(sizeof(*conf), GFP_ATOMIC);
- if (!config)
+ if (!config || !conf)
return RMNET_CONFIG_NOMEM;
memset(config, 0, sizeof(struct rmnet_phys_ep_conf_s));
- config->dev = dev;
- spin_lock_init(&config->agg_lock);
+ memset(conf, 0, sizeof(struct rmnet_phys_ep_config));
+
+ config->config = conf;
+ conf->dev = dev;
+ spin_lock_init(&conf->agg_lock);
+ config->recycle = kfree_skb;
rc = netdev_rx_handler_register(dev, rmnet_rx_handler, config);
if (rc) {
LOGM("netdev_rx_handler_register returns %d", rc);
kfree(config);
+ kfree(conf);
return RMNET_CONFIG_DEVICE_IN_USE;
}
diff --git a/net/rmnet_data/rmnet_data_config.h b/net/rmnet_data/rmnet_data_config.h
index a76bcef79e6a..78329c38b364 100644
--- a/net/rmnet_data/rmnet_data_config.h
+++ b/net/rmnet_data/rmnet_data_config.h
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/time.h>
#include <linux/spinlock.h>
+#include <net/rmnet_config.h>
#ifndef _RMNET_DATA_CONFIG_H_
#define _RMNET_DATA_CONFIG_H_
@@ -62,7 +63,7 @@ struct rmnet_logical_ep_conf_s {
* @agg_time: Wall clock time when aggregated frame was created
* @agg_last: Last time the aggregation routing was invoked
*/
-struct rmnet_phys_ep_conf_s {
+struct rmnet_phys_ep_config {
struct net_device *dev;
struct rmnet_logical_ep_conf_s local_ep;
struct rmnet_logical_ep_conf_s muxed_ep[RMNET_DATA_MAX_LOGICAL_EP];
@@ -123,4 +124,7 @@ int rmnet_create_vnd(int id);
int rmnet_create_vnd_prefix(int id, const char *name);
int rmnet_free_vnd(int id);
+struct rmnet_phys_ep_config *_rmnet_get_phys_ep_config
+ (struct net_device *dev);
+
#endif /* _RMNET_DATA_CONFIG_H_ */
diff --git a/net/rmnet_data/rmnet_data_handlers.c b/net/rmnet_data/rmnet_data_handlers.c
index 9d04b2f8ddd9..185b609e637f 100644
--- a/net/rmnet_data/rmnet_data_handlers.c
+++ b/net/rmnet_data/rmnet_data_handlers.c
@@ -22,6 +22,7 @@
#include <linux/netdev_features.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
+#include <net/rmnet_config.h>
#include "rmnet_data_private.h"
#include "rmnet_data_config.h"
#include "rmnet_data_vnd.h"
@@ -321,7 +322,7 @@ static rx_handler_result_t __rmnet_deliver_skb(struct sk_buff *skb,
* - RX_HANDLER_PASS if packet should be passed up the stack by caller
*/
static rx_handler_result_t rmnet_ingress_deliver_packet(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config)
+ struct rmnet_phys_ep_config *config)
{
if (!config) {
LOGD("%s", "NULL physical EP provided");
@@ -356,7 +357,7 @@ static rx_handler_result_t rmnet_ingress_deliver_packet(struct sk_buff *skb,
* - result of __rmnet_deliver_skb() for all other cases
*/
static rx_handler_result_t _rmnet_map_ingress_handler(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config)
+ struct rmnet_phys_ep_config *config)
{
struct rmnet_logical_ep_conf_s *ep;
uint8_t mux_id;
@@ -440,7 +441,7 @@ static rx_handler_result_t _rmnet_map_ingress_handler(struct sk_buff *skb,
* - result of _rmnet_map_ingress_handler() for all other cases
*/
static rx_handler_result_t rmnet_map_ingress_handler(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config)
+ struct rmnet_phys_ep_config *config)
{
struct sk_buff *skbn;
int rc, co = 0;
@@ -480,7 +481,7 @@ static rx_handler_result_t rmnet_map_ingress_handler(struct sk_buff *skb,
* - 1 on failure
*/
static int rmnet_map_egress_handler(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config,
+ struct rmnet_phys_ep_config *config,
struct rmnet_logical_ep_conf_s *ep,
struct net_device *orig_dev)
{
@@ -563,7 +564,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb,
*/
rx_handler_result_t rmnet_ingress_handler(struct sk_buff *skb)
{
- struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_phys_ep_config *config;
struct net_device *dev;
int rc;
@@ -574,8 +575,7 @@ rx_handler_result_t rmnet_ingress_handler(struct sk_buff *skb)
trace_rmnet_ingress_handler(skb);
rmnet_print_packet(skb, dev->name, 'r');
- config = (struct rmnet_phys_ep_conf_s *)
- rcu_dereference(skb->dev->rx_handler_data);
+ config = _rmnet_get_phys_ep_config(skb->dev);
if (!config) {
LOGD("%s is not associated with rmnet_data", skb->dev->name);
@@ -654,14 +654,13 @@ rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)
void rmnet_egress_handler(struct sk_buff *skb,
struct rmnet_logical_ep_conf_s *ep)
{
- struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_phys_ep_config *config;
struct net_device *orig_dev;
int rc;
orig_dev = skb->dev;
skb->dev = ep->egress_dev;
- config = (struct rmnet_phys_ep_conf_s *)
- rcu_dereference(skb->dev->rx_handler_data);
+ config = _rmnet_get_phys_ep_config(skb->dev);
if (!config) {
LOGD("%s is not associated with rmnet_data", skb->dev->name);
diff --git a/net/rmnet_data/rmnet_data_stats.c b/net/rmnet_data/rmnet_data_stats.c
index 5fefda55c72c..20f1628242c7 100644
--- a/net/rmnet_data/rmnet_data_stats.c
+++ b/net/rmnet_data/rmnet_data_stats.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -21,6 +21,7 @@
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/netdevice.h>
+#include <net/rmnet_config.h>
#include "rmnet_data_private.h"
#include "rmnet_data_stats.h"
#include "rmnet_data_config.h"
@@ -73,8 +74,16 @@ void rmnet_kfree_skb(struct sk_buff *skb, unsigned int reason)
skb_free[reason]++;
spin_unlock_irqrestore(&rmnet_skb_free_lock, flags);
- if (skb)
- kfree_skb(skb);
+ if (likely(skb)) {
+ struct rmnet_phys_ep_conf_s *config;
+
+ config = (struct rmnet_phys_ep_conf_s *)rcu_dereference
+ (skb->dev->rx_handler_data);
+ if (likely(config))
+ config->recycle(skb);
+ else
+ kfree_skb(skb);
+ }
}
void rmnet_stats_queue_xmit(int rc, unsigned int reason)
diff --git a/net/rmnet_data/rmnet_map.h b/net/rmnet_data/rmnet_map.h
index 0a6ad908b827..71abca122dd6 100644
--- a/net/rmnet_data/rmnet_map.h
+++ b/net/rmnet_data/rmnet_map.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -13,6 +13,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
+#include <net/rmnet_config.h>
#ifndef _RMNET_MAP_H_
#define _RMNET_MAP_H_
@@ -133,14 +134,14 @@ enum rmnet_map_agg_state_e {
uint8_t rmnet_map_demultiplex(struct sk_buff *skb);
struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config);
+ struct rmnet_phys_ep_config *config);
struct rmnet_map_header_s *rmnet_map_add_map_header(struct sk_buff *skb,
int hdrlen, int pad);
rx_handler_result_t rmnet_map_command(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config);
+ struct rmnet_phys_ep_config *config);
void rmnet_map_aggregate(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config);
+ struct rmnet_phys_ep_config *config);
int rmnet_map_checksum_downlink_packet(struct sk_buff *skb);
int rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
diff --git a/net/rmnet_data/rmnet_map_command.c b/net/rmnet_data/rmnet_map_command.c
index 4bcfa10db486..055d5f402957 100644
--- a/net/rmnet_data/rmnet_map_command.c
+++ b/net/rmnet_data/rmnet_map_command.c
@@ -18,6 +18,7 @@
#include <linux/rmnet_data.h>
#include <linux/net_map.h>
#include <net/pkt_sched.h>
+#include <net/rmnet_config.h>
#include "rmnet_data_config.h"
#include "rmnet_map.h"
#include "rmnet_data_private.h"
@@ -44,7 +45,7 @@ MODULE_PARM_DESC(rmnet_map_command_stats, "MAP command statistics");
* - RMNET_MAP_COMMAND_ACK on success
*/
static uint8_t rmnet_map_do_flow_control(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config,
+ struct rmnet_phys_ep_config *config,
int enable)
{
struct rmnet_map_control_command_s *cmd;
@@ -116,7 +117,7 @@ static uint8_t rmnet_map_do_flow_control(struct sk_buff *skb,
*/
static void rmnet_map_send_ack(struct sk_buff *skb,
unsigned char type,
- struct rmnet_phys_ep_conf_s *config)
+ struct rmnet_phys_ep_config *config)
{
struct rmnet_map_control_command_s *cmd;
int xmit_status;
@@ -162,7 +163,7 @@ static void rmnet_map_send_ack(struct sk_buff *skb,
* - RX_HANDLER_CONSUMED. Command frames are always consumed.
*/
rx_handler_result_t rmnet_map_command(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config)
+ struct rmnet_phys_ep_config *config)
{
struct rmnet_map_control_command_s *cmd;
unsigned char command_name;
diff --git a/net/rmnet_data/rmnet_map_data.c b/net/rmnet_data/rmnet_map_data.c
index 68caf44c1f92..beff8332c731 100644
--- a/net/rmnet_data/rmnet_map_data.c
+++ b/net/rmnet_data/rmnet_map_data.c
@@ -31,6 +31,7 @@
#include <net/ip.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
+#include <net/rmnet_config.h>
#include "rmnet_data_config.h"
#include "rmnet_map.h"
#include "rmnet_data_private.h"
@@ -52,7 +53,7 @@ MODULE_PARM_DESC(agg_bypass_time, "Skip agg when apart spaced more than this");
struct agg_work {
struct delayed_work work;
- struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_phys_ep_config *config;
};
#define RMNET_MAP_DEAGGR_SPACING 64
@@ -131,7 +132,7 @@ done:
* - 0 (null) if no more aggregated packets
*/
struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config)
+ struct rmnet_phys_ep_config *config)
{
struct sk_buff *skbn;
struct rmnet_map_header_s *maph;
@@ -185,7 +186,7 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
static void rmnet_map_flush_packet_queue(struct work_struct *work)
{
struct agg_work *real_work;
- struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_phys_ep_config *config;
unsigned long flags;
struct sk_buff *skb;
int rc, agg_count = 0;
@@ -233,7 +234,7 @@ static void rmnet_map_flush_packet_queue(struct work_struct *work)
* the argument SKB and should not be further processed by any other function.
*/
void rmnet_map_aggregate(struct sk_buff *skb,
- struct rmnet_phys_ep_conf_s *config) {
+ struct rmnet_phys_ep_config *config) {
uint8_t *dest_buff;
struct agg_work *work;
unsigned long flags;
diff --git a/net/wireless/core.h b/net/wireless/core.h
index a918fc303d51..05125d092b18 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -213,6 +213,7 @@ struct cfg80211_event {
const u8 *resp_ie;
size_t req_ie_len;
size_t resp_ie_len;
+ struct cfg80211_bss *bss;
u16 status;
} cr;
struct {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 30f54d1fc841..8fae48e01a43 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -6022,6 +6022,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
request->no_cck =
nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
+ if (info->attrs[NL80211_ATTR_MAC])
+ memcpy(request->bssid, nla_data(info->attrs[NL80211_ATTR_MAC]),
+ ETH_ALEN);
+ else
+ eth_broadcast_addr(request->bssid);
+
request->wdev = wdev;
request->wiphy = &rdev->wiphy;
request->scan_start = jiffies;
@@ -6453,6 +6459,24 @@ out_free:
return ERR_PTR(err);
}
+static int nl80211_abort_scan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (!rdev->ops->abort_scan)
+ return -EOPNOTSUPP;
+
+ if (rdev->scan_msg)
+ return 0;
+
+ if (!rdev->scan_req)
+ return -ENOENT;
+
+ rdev_abort_scan(rdev, wdev);
+ return 0;
+}
+
static int nl80211_start_sched_scan(struct sk_buff *skb,
struct genl_info *info)
{
@@ -7932,6 +7956,10 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
connect.mfp = NL80211_MFP_NO;
}
+ if (info->attrs[NL80211_ATTR_PREV_BSSID])
+ connect.prev_bssid =
+ nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
connect.channel = nl80211_get_valid_chan(
wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]);
@@ -11004,6 +11032,14 @@ static const struct genl_ops nl80211_ops[] = {
NL80211_FLAG_NEED_RTNL,
},
{
+ .cmd = NL80211_CMD_ABORT_SCAN,
+ .doit = nl80211_abort_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
.cmd = NL80211_CMD_GET_SCAN,
.policy = nl80211_policy,
.dumpit = nl80211_dump_scan,
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 571aa9424354..966d15247030 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -426,6 +426,14 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline void rdev_abort_scan(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ trace_rdev_abort_scan(&rdev->wiphy, wdev);
+ rdev->ops->abort_scan(&rdev->wiphy, wdev);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
static inline int rdev_auth(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_auth_request *req)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 30f967665e84..16c3424507c3 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1293,6 +1293,8 @@ int cfg80211_wext_siwscan(struct net_device *dev,
if (wiphy->bands[i])
creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
+ eth_broadcast_addr(creq->bssid);
+
rdev->scan_req = creq;
err = rdev_scan(rdev, creq);
if (err) {
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index e9be8c3b177b..37d8ab3a71be 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -142,6 +142,8 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
wdev->conn->params.ssid_len);
request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
+ eth_broadcast_addr(request->bssid);
+
request->wdev = wdev;
request->wiphy = &rdev->wiphy;
request->scan_start = jiffies;
@@ -765,19 +767,32 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
kfree(country_ie);
}
-void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
- const u8 *req_ie, size_t req_ie_len,
- const u8 *resp_ie, size_t resp_ie_len,
- u16 status, gfp_t gfp)
+/* Consumes bss object one way or another */
+void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
+ struct cfg80211_bss *bss, const u8 *req_ie,
+ size_t req_ie_len, const u8 *resp_ie,
+ size_t resp_ie_len, u16 status, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_event *ev;
unsigned long flags;
+ if (bss) {
+ /* Make sure the bss entry provided by the driver is valid. */
+ struct cfg80211_internal_bss *ibss = bss_from_pub(bss);
+
+ if (WARN_ON(list_empty(&ibss->list))) {
+ cfg80211_put_bss(wdev->wiphy, bss);
+ return;
+ }
+ }
+
ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
- if (!ev)
+ if (!ev) {
+ cfg80211_put_bss(wdev->wiphy, bss);
return;
+ }
ev->type = EVENT_CONNECT_RESULT;
if (bssid)
@@ -792,6 +807,9 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
ev->cr.resp_ie_len = resp_ie_len;
memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
}
+ if (bss)
+ cfg80211_hold_bss(bss_from_pub(bss));
+ ev->cr.bss = bss;
ev->cr.status = status;
spin_lock_irqsave(&wdev->event_lock, flags);
@@ -799,7 +817,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
spin_unlock_irqrestore(&wdev->event_lock, flags);
queue_work(cfg80211_wq, &rdev->event_work);
}
-EXPORT_SYMBOL(cfg80211_connect_result);
+EXPORT_SYMBOL(cfg80211_connect_bss);
/* Consumes bss object one way or another */
void __cfg80211_roamed(struct wireless_dev *wdev,
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 43edcf8f6d5e..ca24294fe463 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1221,6 +1221,7 @@ TRACE_EVENT(rdev_connect,
__field(bool, privacy)
__field(u32, wpa_versions)
__field(u32, flags)
+ MAC_ENTRY(prev_bssid)
),
TP_fast_assign(
WIPHY_ASSIGN;
@@ -1232,13 +1233,14 @@ TRACE_EVENT(rdev_connect,
__entry->privacy = sme->privacy;
__entry->wpa_versions = sme->crypto.wpa_versions;
__entry->flags = sme->flags;
+ MAC_ASSIGN(prev_bssid, sme->prev_bssid);
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
", ssid: %s, auth type: %d, privacy: %s, wpa versions: %u, "
- "flags: %u",
+ "flags: %u, previous bssid: " MAC_PR_FMT,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid,
__entry->auth_type, BOOL_TO_STR(__entry->privacy),
- __entry->wpa_versions, __entry->flags)
+ __entry->wpa_versions, __entry->flags, MAC_PR_ARG(prev_bssid))
);
TRACE_EVENT(rdev_set_cqm_rssi_config,
@@ -2803,6 +2805,10 @@ TRACE_EVENT(cfg80211_ft_event,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap))
);
+DEFINE_EVENT(wiphy_wdev_evt, rdev_abort_scan,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
diff --git a/net/wireless/util.c b/net/wireless/util.c
index a2532f46169d..a5b20d75017e 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -877,7 +877,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
ev->cr.resp_ie, ev->cr.resp_ie_len,
ev->cr.status,
ev->cr.status == WLAN_STATUS_SUCCESS,
- NULL);
+ ev->cr.bss);
break;
case EVENT_ROAMED:
__cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie,
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/pfe/Kconfig b/security/pfe/Kconfig
index b951861bfc84..207be713f800 100644
--- a/security/pfe/Kconfig
+++ b/security/pfe/Kconfig
@@ -15,7 +15,6 @@ config PFT
config PFK
bool "Per-File-Key driver"
depends on SECURITY
- depends on ECRYPT_FS
default n
help
This driver is used for storing eCryptfs information
diff --git a/security/pfe/Makefile b/security/pfe/Makefile
index 983eedb86170..f7badf74c73f 100644
--- a/security/pfe/Makefile
+++ b/security/pfe/Makefile
@@ -3,6 +3,7 @@
#
ccflags-y += -Isecurity/selinux -Isecurity/selinux/include -Ifs/ecryptfs
+ccflags-y += -Ifs/ext4
obj-$(CONFIG_PFT) += pft.o
-obj-$(CONFIG_PFK) += pfk.o pfk_kc.o pfk_ice.o
+obj-$(CONFIG_PFK) += pfk.o pfk_kc.o pfk_ice.o pfk_ext4.o pfk_ecryptfs.o
diff --git a/security/pfe/pfk.c b/security/pfe/pfk.c
index 5cfb3758d3cf..7e38c9fbf171 100644
--- a/security/pfe/pfk.c
+++ b/security/pfe/pfk.c
@@ -14,24 +14,27 @@
/*
* Per-File-Key (PFK).
*
- * This driver is used for storing eCryptfs information (mainly file
- * encryption key) in file node as part of eCryptfs hardware enhanced solution
- * provided by Qualcomm Technologies, Inc.
+ * This driver is responsible for overall management of various
+ * Per File Encryption variants that work on top of or as part of different
+ * file systems.
*
- * The information is stored in node when file is first opened (eCryptfs
- * will fire a callback notifying PFK about this event) and will be later
- * accessed by Block Device Driver to actually load the key to encryption hw.
+ * The driver has the following purpose :
+ * 1) Define priorities between PFE's if more than one is enabled
+ * 2) Extract key information from inode
+ * 3) Load and manage various keys in ICE HW engine
+ * 4) It should be invoked from various layers in FS/BLOCK/STORAGE DRIVER
+ * that need to take decision on HW encryption management of the data
+ * Some examples:
+ * BLOCK LAYER: when it takes decision on whether 2 chunks can be united
+ * to one encryption / decryption request sent to the HW
*
- * PFK exposes API's for loading and removing keys from encryption hw
- * and also API to determine whether 2 adjacent blocks can be agregated by
- * Block Layer in one request to encryption hw.
- * PFK is only supposed to be used by eCryptfs, except the below.
+ * UFS DRIVER: when it need to configure ICE HW with a particular key slot
+ * to be used for encryption / decryption
+ *
+ * PFE variants can differ on particular way of storing the cryptographic info
+ * inside inode, actions to be taken upon file operations, etc., but the common
+ * properties are described above
*
- * Please note, the only API that uses EXPORT_SYMBOL() is pfk_remove_key,
- * this is intentionally, as it is the only API that is intended to be used
- * by any kernel module, including dynamically loaded ones. All other API's,
- * as mentioned above are only supposed to be used by eCryptfs which is
- * a static module.
*/
@@ -45,7 +48,6 @@
#include <linux/printk.h>
#include <linux/bio.h>
#include <linux/security.h>
-#include <linux/lsm_hooks.h>
#include <crypto/ice.h>
#include <linux/pfk.h>
@@ -55,87 +57,120 @@
#include "objsec.h"
#include "ecryptfs_kernel.h"
#include "pfk_ice.h"
+#include "pfk_ext4.h"
+#include "pfk_ecryptfs.h"
+#include "pfk_internal.h"
+#include "ext4.h"
-static DEFINE_MUTEX(pfk_lock);
static bool pfk_ready;
-static int g_events_handle;
/* might be replaced by a table when more than one cipher is supported */
-#define PFK_SUPPORTED_CIPHER "aes_xts"
#define PFK_SUPPORTED_KEY_SIZE 32
#define PFK_SUPPORTED_SALT_SIZE 32
+/* Various PFE types and function tables to support each one of them */
+enum pfe_type {ECRYPTFS_PFE, EXT4_CRYPT_PFE, INVALID_PFE};
-/**
- * inode_to_filename() - get the filename from inode pointer.
- * @inode: inode pointer
- *
- * it is used for debug prints.
- *
- * Return: filename string or "unknown".
- */
-static char *inode_to_filename(struct inode *inode)
-{
- struct dentry *dentry = NULL;
- char *filename = NULL;
+typedef int (*pfk_parse_inode_type)(const struct bio *bio,
+ const struct inode *inode,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo,
+ bool *is_pfe);
- if (hlist_empty(&inode->i_dentry))
- return "unknown";
+typedef bool (*pfk_allow_merge_bio_type)(const struct bio *bio1,
+ const struct bio *bio2, const struct inode *inode1,
+ const struct inode *inode2);
- dentry = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias);
- filename = dentry->d_iname;
+static const pfk_parse_inode_type pfk_parse_inode_ftable[] = {
+ /* ECRYPTFS_PFE */ &pfk_ecryptfs_parse_inode,
+ /* EXT4_CRYPT_PFE */ &pfk_ext4_parse_inode,
+};
- return filename;
+static const pfk_allow_merge_bio_type pfk_allow_merge_bio_ftable[] = {
+ /* ECRYPTFS_PFE */ &pfk_ecryptfs_allow_merge_bio,
+ /* EXT4_CRYPT_PFE */ &pfk_ext4_allow_merge_bio,
+};
+
+static void __exit pfk_exit(void)
+{
+ pfk_ready = false;
+ pfk_ext4_deinit();
+ pfk_ecryptfs_deinit();
+ pfk_kc_deinit();
}
-static int pfk_inode_alloc_security(struct inode *inode)
+static int __init pfk_init(void)
{
- struct inode_security_struct *i_sec = NULL;
- if (inode == NULL)
- return -EINVAL;
+ int ret = 0;
- i_sec = kzalloc(sizeof(*i_sec), GFP_KERNEL);
+ ret = pfk_ecryptfs_init();
+ if (ret != 0)
+ goto fail;
+
+ ret = pfk_ext4_init();
+ if (ret != 0) {
+ pfk_ecryptfs_deinit();
+ goto fail;
+ }
- if (i_sec == NULL)
- return -ENOMEM;
+ ret = pfk_kc_init();
+ if (ret != 0) {
+ pr_err("could init pfk key cache, error %d\n", ret);
+ pfk_ext4_deinit();
+ pfk_ecryptfs_deinit();
+ goto fail;
+ }
- inode->i_security = i_sec;
+ pfk_ready = true;
+ pr_info("Driver initialized successfully\n");
return 0;
+
+fail:
+ pr_err("Failed to init driver\n");
+ return -ENODEV;
}
-static void pfk_inode_free_security(struct inode *inode)
+/*
+ * If more than one type is supported simultaneously, this function will also
+ * set the priority between them
+ */
+static enum pfe_type pfk_get_pfe_type(const struct inode *inode)
{
- if (inode == NULL)
- return;
+ if (!inode)
+ return INVALID_PFE;
- kzfree(inode->i_security);
-}
+ if (pfk_is_ecryptfs_type(inode))
+ return ECRYPTFS_PFE;
-static struct security_hook_list pfk_hooks[] = {
- LSM_HOOK_INIT(inode_alloc_security, pfk_inode_alloc_security),
- LSM_HOOK_INIT(inode_free_security, pfk_inode_free_security),
- LSM_HOOK_INIT(allow_merge_bio, pfk_allow_merge_bio),
-};
+ if (pfk_is_ext4_type(inode))
+ return EXT4_CRYPT_PFE;
-static int __init pfk_lsm_init(void)
+ return INVALID_PFE;
+}
+
+/**
+ * inode_to_filename() - get the filename from inode pointer.
+ * @inode: inode pointer
+ *
+ * it is used for debug prints.
+ *
+ * Return: filename string or "unknown".
+ */
+char *inode_to_filename(const struct inode *inode)
{
- /* Check if PFK is the chosen lsm via security_module_enable() */
- if (security_module_enable("pfk")) {
- security_add_hooks(pfk_hooks, ARRAY_SIZE(pfk_hooks));
- pr_debug("pfk is the chosen lsm, registered successfully !\n");
- } else {
- pr_debug("pfk is not the chosen lsm.\n");
- if (!selinux_is_enabled()) {
- pr_err("se linux is not enabled.\n");
- return -ENODEV;
- }
+ struct dentry *dentry = NULL;
+ char *filename = NULL;
- }
+ if (hlist_empty(&inode->i_dentry))
+ return "unknown";
- return 0;
+ dentry = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias);
+ filename = dentry->d_iname;
+
+ return filename;
}
/**
@@ -149,37 +184,6 @@ static inline bool pfk_is_ready(void)
}
/**
- * pfk_get_page_index() - get the inode from a bio.
- * @bio: Pointer to BIO structure.
- *
- * Walk the bio struct links to get the inode.
- * Please note, that in general bio may consist of several pages from
- * several files, but in our case we always assume that all pages come
- * from the same file, since our logic ensures it. That is why we only
- * walk through the first page to look for inode.
- *
- * Return: pointer to the inode struct if successful, or NULL otherwise.
- *
- */
-static int pfk_get_page_index(const struct bio *bio, pgoff_t *page_index)
-{
- if (!bio || !page_index)
- return -EINVAL;
-
- if (!bio_has_data((struct bio *)bio))
- return -EINVAL;
-
- if (!bio->bi_io_vec)
- return -EINVAL;
- if (!bio->bi_io_vec->bv_page)
- return -EINVAL;
-
- *page_index = bio->bi_io_vec->bv_page->index;
-
- return 0;
-}
-
-/**
* pfk_bio_get_inode() - get the inode from a bio.
* @bio: Pointer to BIO structure.
*
@@ -196,10 +200,8 @@ static struct inode *pfk_bio_get_inode(const struct bio *bio)
{
if (!bio)
return NULL;
-
if (!bio_has_data((struct bio *)bio))
return NULL;
-
if (!bio->bi_io_vec)
return NULL;
if (!bio->bi_io_vec->bv_page)
@@ -225,96 +227,13 @@ static struct inode *pfk_bio_get_inode(const struct bio *bio)
}
/**
- * pfk_get_ecryptfs_data() - retrieves ecryptfs data stored inside node
- * @inode: inode
- *
- * Return the data or NULL if there isn't any or in case of error
- * Should be invoked under lock
- */
-static void *pfk_get_ecryptfs_data(const struct inode *inode)
-{
- struct inode_security_struct *isec = NULL;
-
- if (!inode)
- return NULL;
-
- isec = inode->i_security;
-
- if (!isec) {
- pr_debug("i_security is NULL, could be irrelevant file\n");
- return NULL;
- }
-
- return isec->pfk_data;
-}
-
-/**
- * pfk_set_ecryptfs_data() - stores ecryptfs data inside node
- * @inode: inode to update
- * @data: data to put inside the node
- *
- * Returns 0 in case of success, error otherwise
- * Should be invoked under lock
- */
-static int pfk_set_ecryptfs_data(struct inode *inode, void *ecryptfs_data)
-{
- struct inode_security_struct *isec = NULL;
-
- if (!inode)
- return -EINVAL;
-
- isec = inode->i_security;
-
- if (!isec) {
- pr_err("i_security is NULL, not ready yet\n");
- return -EINVAL;
- }
-
- isec->pfk_data = ecryptfs_data;
-
- return 0;
-}
-
-
-/**
- * pfk_parse_cipher() - parse cipher from ecryptfs to enum
- * @ecryptfs_data: ecrypfs data
- * @algo: pointer to store the output enum (can be null)
- *
- * return 0 in case of success, error otherwise (i.e not supported cipher)
- */
-static int pfk_parse_cipher(const void *ecryptfs_data,
- enum ice_cryto_algo_mode *algo)
-{
- /*
- * currently only AES XTS algo is supported
- * in the future, table with supported ciphers might
- * be introduced
- */
-
- if (!ecryptfs_data)
- return -EINVAL;
-
- if (!ecryptfs_cipher_match(ecryptfs_data,
- PFK_SUPPORTED_CIPHER, sizeof(PFK_SUPPORTED_CIPHER))) {
- pr_debug("ecryptfs alghoritm is not supported by pfk\n");
- return -EINVAL;
- }
-
- if (algo)
- *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS;
-
- return 0;
-}
-
-/**
* pfk_key_size_to_key_type() - translate key size to key size enum
* @key_size: key size in bytes
* @key_size_type: pointer to store the output enum (can be null)
*
* return 0 in case of success, error otherwise (i.e not supported key size)
*/
-static int pfk_key_size_to_key_type(size_t key_size,
+int pfk_key_size_to_key_type(size_t key_size,
enum ice_crpto_key_size *key_size_type)
{
/*
@@ -334,100 +253,38 @@ static int pfk_key_size_to_key_type(size_t key_size,
return 0;
}
-static int pfk_bio_to_key(const struct bio *bio, unsigned char const **key,
- size_t *key_size, unsigned char const **salt, size_t *salt_size,
- bool *is_pfe, bool start)
+/*
+ * Retrieves filesystem type from inode's superblock
+ */
+bool pfe_is_inode_filesystem_type(const struct inode *inode,
+ const char *fs_type)
{
- struct inode *inode = NULL;
- int ret = 0;
- void *ecryptfs_data = NULL;
- pgoff_t offset;
- bool is_metadata = false;
-
- /*
- * only a few errors below can indicate that
- * this function was not invoked within PFE context,
- * otherwise we will consider it PFE
- */
- *is_pfe = true;
-
-
- if (!bio)
- return -EINVAL;
-
- if (!key || !salt || !key_size || !salt_size)
- return -EINVAL;
-
- inode = pfk_bio_get_inode(bio);
- if (!inode) {
- *is_pfe = false;
- return -EINVAL;
- }
-
- ecryptfs_data = pfk_get_ecryptfs_data(inode);
- if (!ecryptfs_data) {
- *is_pfe = false;
- return -EPERM;
- }
-
- pr_debug("loading key for file %s, start %d\n",
- inode_to_filename(inode), start);
-
- ret = pfk_get_page_index(bio, &offset);
- if (ret != 0) {
- pr_err("could not get page index from bio, probably bug %d\n",
- ret);
- return -EINVAL;
- }
-
- is_metadata = ecryptfs_is_page_in_metadata(ecryptfs_data, offset);
- if (is_metadata == true) {
- pr_debug("ecryptfs metadata, bypassing ICE\n");
- *is_pfe = false;
- return -EPERM;
- }
-
- *key = ecryptfs_get_key(ecryptfs_data);
- if (!key) {
- pr_err("could not parse key from ecryptfs\n");
- return -EINVAL;
- }
-
- *key_size = ecryptfs_get_key_size(ecryptfs_data);
- if (!(*key_size)) {
- pr_err("could not parse key size from ecryptfs\n");
- return -EINVAL;
- }
+ if (!inode || !fs_type)
+ return false;
- *salt = ecryptfs_get_salt(ecryptfs_data);
- if (!salt) {
- pr_err("could not parse salt from ecryptfs\n");
- return -EINVAL;
- }
+ if (!inode->i_sb)
+ return false;
- *salt_size = ecryptfs_get_salt_size(ecryptfs_data);
- if (!(*salt_size)) {
- pr_err("could not parse salt size from ecryptfs\n");
- return -EINVAL;
- }
+ if (!inode->i_sb->s_type)
+ return false;
- return 0;
+ return (strcmp(inode->i_sb->s_type->name, fs_type) == 0);
}
+
/**
* pfk_load_key_start() - loads PFE encryption key to the ICE
- * Can also be invoked from non
- * PFE context, than it is not
- * relevant and is_pfe flag is
- * set to true
+ * Can also be invoked from non
+ * PFE context, in this case it
+ * is not relevant and is_pfe
+ * flag is set to false
+ *
* @bio: Pointer to the BIO structure
* @ice_setting: Pointer to ice setting structure that will be filled with
* ice configuration values, including the index to which the key was loaded
- * @is_pfe: Pointer to is_pfe flag, which will be true if function was invoked
- * from PFE context
+ * @is_pfe: will be false if inode is not relevant to PFE, in such a case
+ * it should be treated as non PFE by the block layer
*
- * Via bio gets access to ecryptfs key stored in auxiliary structure inside
- * inode and loads it to encryption hw.
* Returns the index where the key is stored in encryption hw and additional
* information that will be used later for configuration of the encryption hw.
*
@@ -439,15 +296,12 @@ int pfk_load_key_start(const struct bio *bio,
bool async)
{
int ret = 0;
- const unsigned char *key = NULL;
- const unsigned char *salt = NULL;
- size_t key_size = 0;
- size_t salt_size = 0;
- enum ice_cryto_algo_mode algo_mode = 0;
+ struct pfk_key_info key_info = {0};
+ enum ice_cryto_algo_mode algo_mode = ICE_CRYPTO_ALGO_MODE_AES_XTS;
enum ice_crpto_key_size key_size_type = 0;
- void *ecryptfs_data = NULL;
u32 key_index = 0;
struct inode *inode = NULL;
+ enum pfe_type which_pfe = INVALID_PFE;
if (!is_pfe) {
pr_err("is_pfe is NULL\n");
@@ -469,35 +323,32 @@ int pfk_load_key_start(const struct bio *bio,
return -EINVAL;
}
- ret = pfk_bio_to_key(bio, &key, &key_size, &salt, &salt_size, is_pfe,
- true);
- if (ret != 0)
- return ret;
-
inode = pfk_bio_get_inode(bio);
if (!inode) {
*is_pfe = false;
return -EINVAL;
}
- ecryptfs_data = pfk_get_ecryptfs_data(inode);
- if (!ecryptfs_data) {
+ which_pfe = pfk_get_pfe_type(inode);
+ if (which_pfe == INVALID_PFE) {
*is_pfe = false;
return -EPERM;
}
- ret = pfk_parse_cipher(ecryptfs_data, &algo_mode);
- if (ret != 0) {
- pr_err("not supported cipher\n");
+ pr_debug("parsing file %s with PFE %d\n",
+ inode_to_filename(inode), which_pfe);
+
+ ret = (*(pfk_parse_inode_ftable[which_pfe]))
+ (bio, inode, &key_info, &algo_mode, is_pfe);
+ if (ret != 0)
return ret;
- }
- ret = pfk_key_size_to_key_type(key_size, &key_size_type);
+ ret = pfk_key_size_to_key_type(key_info.key_size, &key_size_type);
if (ret != 0)
return ret;
- ret = pfk_kc_load_key_start(key, key_size, salt, salt_size, &key_index,
- async);
+ ret = pfk_kc_load_key_start(key_info.key, key_info.key_size,
+ key_info.salt, key_info.salt_size, &key_index, async);
if (ret) {
if (ret != -EBUSY && ret != -EAGAIN)
pr_err("start: could not load key into pfk key cache, error %d\n",
@@ -512,30 +363,29 @@ int pfk_load_key_start(const struct bio *bio,
ice_setting->key_mode = ICE_CRYPTO_USE_LUT_SW_KEY;
ice_setting->key_index = key_index;
+ pr_debug("loaded key for file %s key_index %d\n",
+ inode_to_filename(inode), key_index);
+
return 0;
}
/**
* pfk_load_key_end() - marks the PFE key as no longer used by ICE
- * Can also be invoked from non
- * PFE context, than it is not
- * relevant and is_pfe flag is
- * set to true
+ * Can also be invoked from non
+ * PFE context, in this case it is not
+ * relevant and is_pfe flag is
+ * set to false
+ *
* @bio: Pointer to the BIO structure
* @is_pfe: Pointer to is_pfe flag, which will be true if function was invoked
* from PFE context
- *
- * Via bio gets access to ecryptfs key stored in auxiliary structure inside
- * inode and loads it to encryption hw.
- *
*/
int pfk_load_key_end(const struct bio *bio, bool *is_pfe)
{
int ret = 0;
- const unsigned char *key = NULL;
- const unsigned char *salt = NULL;
- size_t key_size = 0;
- size_t salt_size = 0;
+ struct pfk_key_info key_info = {0};
+ enum pfe_type which_pfe = INVALID_PFE;
+ struct inode *inode = NULL;
if (!is_pfe) {
pr_err("is_pfe is NULL\n");
@@ -551,42 +401,31 @@ int pfk_load_key_end(const struct bio *bio, bool *is_pfe)
if (!pfk_is_ready())
return -ENODEV;
- ret = pfk_bio_to_key(bio, &key, &key_size, &salt, &salt_size, is_pfe,
- false);
- if (ret != 0)
- return ret;
-
- pfk_kc_load_key_end(key, key_size, salt, salt_size);
-
- return 0;
-}
+ inode = pfk_bio_get_inode(bio);
+ if (!inode) {
+ *is_pfe = false;
+ return -EINVAL;
+ }
-/**
- * pfk_remove_key() - removes key from hw
- * @key: pointer to the key
- * @key_size: key size
- *
- * Will be used by external clients to remove a particular key for security
- * reasons.
- * The only API that can be used by dynamically loaded modules,
- * see explanations above at the beginning of this file.
- * The key is removed securely (by memsetting the previous value)
- */
-int pfk_remove_key(const unsigned char *key, size_t key_size)
-{
- int ret = 0;
+ which_pfe = pfk_get_pfe_type(inode);
+ if (which_pfe == INVALID_PFE) {
+ *is_pfe = false;
+ return -EPERM;
+ }
- if (!pfk_is_ready())
- return -ENODEV;
+ ret = (*(pfk_parse_inode_ftable[which_pfe]))
+ (bio, inode, &key_info, NULL, is_pfe);
+ if (ret != 0)
+ return ret;
- if (!key)
- return -EINVAL;
+ pfk_kc_load_key_end(key_info.key, key_info.key_size,
+ key_info.salt, key_info.salt_size);
- ret = pfk_kc_remove_key(key, key_size);
+ pr_debug("finished using key for file %s\n",
+ inode_to_filename(inode));
- return ret;
+ return 0;
}
-EXPORT_SYMBOL(pfk_remove_key);
/**
* pfk_allow_merge_bio() - Check if 2 BIOs can be merged.
@@ -603,252 +442,39 @@ EXPORT_SYMBOL(pfk_remove_key);
* Return: true if the BIOs allowed to be merged, false
* otherwise.
*/
-bool pfk_allow_merge_bio(struct bio *bio1, struct bio *bio2)
+bool pfk_allow_merge_bio(const struct bio *bio1, const struct bio *bio2)
{
- int ret;
- void *ecryptfs_data1 = NULL;
- void *ecryptfs_data2 = NULL;
- pgoff_t offset1, offset2;
- bool res = false;
+ struct inode *inode1 = NULL;
+ struct inode *inode2 = NULL;
+ enum pfe_type which_pfe1 = INVALID_PFE;
+ enum pfe_type which_pfe2 = INVALID_PFE;
- /* if there is no pfk, don't disallow merging blocks */
if (!pfk_is_ready())
- return true;
+ return false;
if (!bio1 || !bio2)
return false;
- ecryptfs_data1 = pfk_get_ecryptfs_data(pfk_bio_get_inode(bio1));
- ecryptfs_data2 = pfk_get_ecryptfs_data(pfk_bio_get_inode(bio2));
-
- /*
- * if we have 2 different encrypted files or 1 encrypted and 1 regular,
- * merge is forbidden
- */
- if (!ecryptfs_is_data_equal(ecryptfs_data1, ecryptfs_data2)) {
- res = false;
- goto end;
- }
-
- /*
- * if both are equall in their NULLINNESS, we have 2 unencrypted files,
- * allow merge
- */
- if (!ecryptfs_data1) {
- res = true;
- goto end;
- }
-
- /*
- * at this point both bio's are in the same file which is probably
- * encrypted, last thing to check is header vs data
- * We are assuming that we are not working in O_DIRECT mode,
- * since it is not currently supported by eCryptfs
- */
- ret = pfk_get_page_index(bio1, &offset1);
- if (ret != 0) {
- pr_err("could not get page index from bio1, probably bug %d\n",
- ret);
- res = false;
- goto end;
- }
-
- ret = pfk_get_page_index(bio2, &offset2);
- if (ret != 0) {
- pr_err("could not get page index from bio2, bug %d\n", ret);
- res = false;
- goto end;
- }
-
- res = (ecryptfs_is_page_in_metadata(ecryptfs_data1, offset1) ==
- ecryptfs_is_page_in_metadata(ecryptfs_data2, offset2));
-
- /* fall through */
-
-end:
-
- return res;
-}
-
-/**
- * pfk_open_cb() - callback function for file open event
- * @inode: file inode
- * @data: data provided by eCryptfs
- *
- * Will be invoked from eCryptfs in case of file open event
- */
-static void pfk_open_cb(struct inode *inode, void *ecryptfs_data)
-{
- size_t key_size;
-
- if (!pfk_is_ready())
- return;
-
- if (!inode) {
- pr_err("inode is null\n");
- return;
- }
-
- key_size = ecryptfs_get_key_size(ecryptfs_data);
- if (!(key_size)) {
- pr_err("could not parse key size from ecryptfs\n");
- return;
- }
-
- if (0 != pfk_parse_cipher(ecryptfs_data, NULL)) {
- pr_debug("open_cb: not supported cipher\n");
- return;
- }
-
-
- if (0 != pfk_key_size_to_key_type(key_size, NULL))
- return;
-
- mutex_lock(&pfk_lock);
- pfk_set_ecryptfs_data(inode, ecryptfs_data);
- mutex_unlock(&pfk_lock);
-}
-
-/**
- * pfk_release_cb() - callback function for file release event
- * @inode: file inode
- *
- * Will be invoked from eCryptfs in case of file release event
- */
-static void pfk_release_cb(struct inode *inode)
-{
- const unsigned char *key = NULL;
- const unsigned char *salt = NULL;
- size_t key_size = 0;
- size_t salt_size = 0;
- void *data = NULL;
-
- if (!pfk_is_ready())
- return;
-
- if (!inode) {
- pr_err("inode is null\n");
- return;
- }
-
- data = pfk_get_ecryptfs_data(inode);
- if (!data) {
- pr_debug("could not get ecryptfs data from inode\n");
- return;
- }
-
- key = ecryptfs_get_key(data);
- if (!key) {
- pr_err("could not parse key from ecryptfs\n");
- return;
- }
-
- key_size = ecryptfs_get_key_size(data);
- if (!(key_size)) {
- pr_err("could not parse key size from ecryptfs\n");
- return;
- }
-
- salt = ecryptfs_get_salt(data);
- if (!salt) {
- pr_err("could not parse salt from ecryptfs\n");
- return;
- }
-
- salt_size = ecryptfs_get_salt_size(data);
- if (!salt_size) {
- pr_err("could not parse salt size from ecryptfs\n");
- return;
- }
+ if (bio1 == bio2)
+ return true;
- pfk_kc_remove_key_with_salt(key, key_size, salt, salt_size);
+ inode1 = pfk_bio_get_inode(bio1);
+ inode2 = pfk_bio_get_inode(bio2);
- mutex_lock(&pfk_lock);
- pfk_set_ecryptfs_data(inode, NULL);
- mutex_unlock(&pfk_lock);
-}
+ which_pfe1 = pfk_get_pfe_type(inode1);
+ which_pfe2 = pfk_get_pfe_type(inode2);
-static bool pfk_is_cipher_supported_cb(const void *ecryptfs_data)
-{
- if (!pfk_is_ready())
+ /* nodes with different encryption, do not merge */
+ if (which_pfe1 != which_pfe2)
return false;
- if (!ecryptfs_data)
- return false;
-
- return (pfk_parse_cipher(ecryptfs_data, NULL)) == 0;
-}
-
-static bool pfk_is_hw_crypt_cb(void)
-{
- if (!pfk_is_ready())
- return false;
-
- return true;
-}
-
-static size_t pfk_get_salt_key_size_cb(const void *ecryptfs_data)
-{
- if (!pfk_is_ready())
- return 0;
-
- if (!pfk_is_cipher_supported_cb(ecryptfs_data))
- return 0;
-
- return PFK_SUPPORTED_SALT_SIZE;
-}
-
-
-static void __exit pfk_exit(void)
-{
- pfk_ready = false;
- ecryptfs_unregister_from_events(g_events_handle);
- pfk_kc_deinit();
-}
-
-static int __init pfk_init(void)
-{
-
- int ret = 0;
- struct ecryptfs_events events = {0};
-
- events.open_cb = pfk_open_cb;
- events.release_cb = pfk_release_cb;
- events.is_cipher_supported_cb = pfk_is_cipher_supported_cb;
- events.is_hw_crypt_cb = pfk_is_hw_crypt_cb;
- events.get_salt_key_size_cb = pfk_get_salt_key_size_cb;
-
- g_events_handle = ecryptfs_register_to_events(&events);
- if (0 == g_events_handle) {
- pr_err("could not register with eCryptfs, error %d\n", ret);
- goto fail;
- }
-
- ret = pfk_kc_init();
- if (ret != 0) {
- pr_err("could init pfk key cache, error %d\n", ret);
- ecryptfs_unregister_from_events(g_events_handle);
- goto fail;
- }
-
- ret = pfk_lsm_init();
- if (ret != 0) {
- pr_debug("neither pfk nor se-linux sec modules are enabled\n");
- pr_debug("not an error, just don't enable pfk\n");
- pfk_kc_deinit();
- ecryptfs_unregister_from_events(g_events_handle);
- return 0;
- }
-
- pfk_ready = true;
- pr_info("Driver initialized successfully\n");
-
- return 0;
+ /* both nodes do not have encryption, allow merge */
+ if (which_pfe1 == INVALID_PFE)
+ return true;
-fail:
- pr_err("Failed to init driver\n");
- return -ENODEV;
+ return (*(pfk_allow_merge_bio_ftable[which_pfe1]))(bio1, bio2,
+ inode1, inode2);
}
module_init(pfk_init);
diff --git a/security/pfe/pfk_ecryptfs.c b/security/pfe/pfk_ecryptfs.c
new file mode 100644
index 000000000000..1d6a2eeaf6fc
--- /dev/null
+++ b/security/pfe/pfk_ecryptfs.c
@@ -0,0 +1,630 @@
+/*
+ * 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
+ * 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.
+ */
+
+/*
+ * Per-File-Key (PFK) - eCryptfs.
+ *
+ * This driver is used for storing eCryptfs information (mainly file
+ * encryption key) in file node as part of eCryptfs hardware enhanced solution
+ * provided by Qualcomm Technologies, Inc.
+ *
+ * The information is stored in node when file is first opened (eCryptfs
+ * will fire a callback notifying PFK about this event) and will be later
+ * accessed by Block Device Driver to actually load the key to encryption hw.
+ *
+ * PFK exposes API's for loading and removing keys from encryption hw
+ * and also API to determine whether 2 adjacent blocks can be agregated by
+ * Block Layer in one request to encryption hw.
+ * PFK is only supposed to be used by eCryptfs, except the below.
+ *
+ */
+
+
+/* Uncomment the line below to enable debug messages */
+/* #define DEBUG 1 */
+#define pr_fmt(fmt) "pfk_ecryptfs [%s]: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/printk.h>
+#include <linux/bio.h>
+#include <linux/security.h>
+#include <linux/lsm_hooks.h>
+#include <crypto/ice.h>
+
+#include <linux/pfk.h>
+#include <linux/ecryptfs.h>
+
+#include "pfk_ecryptfs.h"
+#include "pfk_kc.h"
+#include "objsec.h"
+#include "ecryptfs_kernel.h"
+#include "pfk_ice.h"
+
+static DEFINE_MUTEX(pfk_ecryptfs_lock);
+static bool pfk_ecryptfs_ready;
+static int g_events_handle;
+
+
+/* might be replaced by a table when more than one cipher is supported */
+#define PFK_SUPPORTED_CIPHER "aes_xts"
+#define PFK_SUPPORTED_SALT_SIZE 32
+
+static void *pfk_ecryptfs_get_data(const struct inode *inode);
+static void pfk_ecryptfs_open_cb(struct inode *inode, void *ecryptfs_data);
+static void pfk_ecryptfs_release_cb(struct inode *inode);
+static bool pfk_ecryptfs_is_cipher_supported_cb(const void *ecryptfs_data);
+static size_t pfk_ecryptfs_get_salt_key_size_cb(const void *ecryptfs_data);
+static bool pfk_ecryptfs_is_hw_crypt_cb(void);
+
+
+/**
+ * pfk_is_ecryptfs_type() - return true if inode belongs to ICE ecryptfs PFE
+ * @inode: inode pointer
+ */
+bool pfk_is_ecryptfs_type(const struct inode *inode)
+{
+ void *ecryptfs_data = NULL;
+
+ /*
+ * the actual filesystem of an inode is still ext4, eCryptfs never
+ * reaches bio
+ */
+ if (!pfe_is_inode_filesystem_type(inode, "ext4"))
+ return false;
+
+ ecryptfs_data = pfk_ecryptfs_get_data(inode);
+
+ if (!ecryptfs_data)
+ return false;
+
+ return true;
+}
+
+static int pfk_ecryptfs_inode_alloc_security(struct inode *inode)
+{
+ struct inode_security_struct *i_sec = NULL;
+
+ if (inode == NULL)
+ return -EINVAL;
+
+ i_sec = kzalloc(sizeof(*i_sec), GFP_KERNEL);
+
+ if (i_sec == NULL)
+ return -ENOMEM;
+
+ inode->i_security = i_sec;
+
+ return 0;
+}
+
+static void pfk_ecryptfs_inode_free_security(struct inode *inode)
+{
+ if (inode == NULL)
+ return;
+
+ kzfree(inode->i_security);
+}
+
+static struct security_hook_list pfk_ecryptfs_hooks[] = {
+ LSM_HOOK_INIT(inode_alloc_security, pfk_ecryptfs_inode_alloc_security),
+ LSM_HOOK_INIT(inode_free_security, pfk_ecryptfs_inode_free_security),
+};
+
+/*
+ * pfk_ecryptfs_lsm_init() - makes sure either se-linux or pfk_ecryptfs are
+ * registered as security module.
+ *
+ * This is required because ecryptfs uses a field inside security struct in
+ * inode to store its info
+ */
+static int __init pfk_ecryptfs_lsm_init(void)
+{
+ /* Check if PFK is the chosen lsm via security_module_enable() */
+ if (security_module_enable("pfk_ecryptfs")) {
+ security_add_hooks(pfk_ecryptfs_hooks,
+ ARRAY_SIZE(pfk_ecryptfs_hooks));
+ pr_debug("pfk_ecryptfs is the chosen lsm, registered successfully !\n");
+ } else {
+ pr_debug("pfk_ecryptfs is not the chosen lsm.\n");
+ if (!selinux_is_enabled()) {
+ pr_err("se linux is not enabled.\n");
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * pfk_ecryptfs_deinit() - Deinit function, should be invoked by upper PFK layer
+ */
+void __exit pfk_ecryptfs_deinit(void)
+{
+ pfk_ecryptfs_ready = false;
+ ecryptfs_unregister_from_events(g_events_handle);
+}
+
+/*
+ * pfk_ecryptfs_init() - Init function, should be invoked by upper PFK layer
+ */
+int __init pfk_ecryptfs_init(void)
+{
+ int ret = 0;
+ struct ecryptfs_events events = {0};
+
+ events.open_cb = pfk_ecryptfs_open_cb;
+ events.release_cb = pfk_ecryptfs_release_cb;
+ events.is_cipher_supported_cb = pfk_ecryptfs_is_cipher_supported_cb;
+ events.is_hw_crypt_cb = pfk_ecryptfs_is_hw_crypt_cb;
+ events.get_salt_key_size_cb = pfk_ecryptfs_get_salt_key_size_cb;
+
+ g_events_handle = ecryptfs_register_to_events(&events);
+ if (g_events_handle == 0) {
+ pr_err("could not register with eCryptfs, error %d\n", ret);
+ goto fail;
+ }
+
+ ret = pfk_ecryptfs_lsm_init();
+ if (ret != 0) {
+ pr_debug("neither pfk nor se-linux sec modules are enabled\n");
+ pr_debug("not an error, just don't enable PFK ecryptfs\n");
+ ecryptfs_unregister_from_events(g_events_handle);
+ return 0;
+ }
+
+ pfk_ecryptfs_ready = true;
+ pr_info("PFK ecryptfs inited successfully\n");
+
+ return 0;
+
+fail:
+ pr_err("Failed to init PFK ecryptfs\n");
+ return -ENODEV;
+}
+
+/**
+ * pfk_ecryptfs_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the driver is ready.
+ */
+static inline bool pfk_ecryptfs_is_ready(void)
+{
+ return pfk_ecryptfs_ready;
+}
+
+/**
+ * pfk_ecryptfs_get_page_index() - get the inode from a bio.
+ * @bio: Pointer to BIO structure.
+ *
+ * Walk the bio struct links to get the inode.
+ * Please note, that in general bio may consist of several pages from
+ * several files, but in our case we always assume that all pages come
+ * from the same file, since our logic ensures it. That is why we only
+ * walk through the first page to look for inode.
+ *
+ * Return: pointer to the inode struct if successful, or NULL otherwise.
+ *
+ */
+static int pfk_ecryptfs_get_page_index(const struct bio *bio,
+ pgoff_t *page_index)
+{
+ if (!bio || !page_index)
+ return -EINVAL;
+ if (!bio_has_data((struct bio *)bio))
+ return -EINVAL;
+ if (!bio->bi_io_vec)
+ return -EINVAL;
+ if (!bio->bi_io_vec->bv_page)
+ return -EINVAL;
+
+ *page_index = bio->bi_io_vec->bv_page->index;
+
+ return 0;
+}
+
+/**
+ * pfk_ecryptfs_get_data() - retrieves ecryptfs data stored inside node
+ * @inode: inode
+ *
+ * Return the data or NULL if there isn't any or in case of error
+ * Should be invoked under lock
+ */
+static void *pfk_ecryptfs_get_data(const struct inode *inode)
+{
+ struct inode_security_struct *isec = NULL;
+
+ if (!inode)
+ return NULL;
+
+ isec = inode->i_security;
+
+ if (!isec) {
+ pr_debug("i_security is NULL, could be irrelevant file\n");
+ return NULL;
+ }
+
+ return isec->pfk_data;
+}
+
+/**
+ * pfk_ecryptfs_set_data() - stores ecryptfs data inside node
+ * @inode: inode to update
+ * @data: data to put inside the node
+ *
+ * Returns 0 in case of success, error otherwise
+ * Should be invoked under lock
+ */
+static int pfk_ecryptfs_set_data(struct inode *inode, void *ecryptfs_data)
+{
+ struct inode_security_struct *isec = NULL;
+
+ if (!inode)
+ return -EINVAL;
+
+ isec = inode->i_security;
+
+ if (!isec) {
+ pr_err("i_security is NULL, not ready yet\n");
+ return -EINVAL;
+ }
+
+ isec->pfk_data = ecryptfs_data;
+
+ return 0;
+}
+
+
+/**
+ * pfk_ecryptfs_parse_cipher() - parse cipher from ecryptfs to enum
+ * @ecryptfs_data: ecrypfs data
+ * @algo: pointer to store the output enum (can be null)
+ *
+ * return 0 in case of success, error otherwise (i.e not supported cipher)
+ */
+static int pfk_ecryptfs_parse_cipher(const void *ecryptfs_data,
+ enum ice_cryto_algo_mode *algo)
+{
+ /*
+ * currently only AES XTS algo is supported
+ * in the future, table with supported ciphers might
+ * be introduced
+ */
+
+ if (!ecryptfs_data)
+ return -EINVAL;
+
+ if (!ecryptfs_cipher_match(ecryptfs_data,
+ PFK_SUPPORTED_CIPHER, sizeof(PFK_SUPPORTED_CIPHER))) {
+ pr_debug("ecryptfs alghoritm is not supported by pfk\n");
+ return -EINVAL;
+ }
+
+ if (algo)
+ *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS;
+
+ return 0;
+}
+
+/*
+ * pfk_ecryptfs_parse_inode() - parses key and algo information from inode
+ *
+ * Should be invoked by upper pfk layer
+ * @bio: bio
+ * @inode: inode to be parsed
+ * @key_info: out, key and salt information to be stored
+ * @algo: out, algorithm to be stored (can be null)
+ * @is_pfe: out, will be false if inode is not relevant to PFE, in such a case
+ * it should be treated as non PFE by the block layer
+ */
+int pfk_ecryptfs_parse_inode(const struct bio *bio,
+ const struct inode *inode,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo,
+ bool *is_pfe)
+{
+ int ret = 0;
+ void *ecryptfs_data = NULL;
+ pgoff_t offset;
+ bool is_metadata = false;
+
+ if (!is_pfe)
+ return -EINVAL;
+
+ /*
+ * only a few errors below can indicate that
+ * this function was not invoked within PFE context,
+ * otherwise we will consider it PFE
+ */
+ *is_pfe = true;
+
+ if (!pfk_ecryptfs_is_ready())
+ return -ENODEV;
+
+ if (!inode)
+ return -EINVAL;
+
+ if (!key_info)
+ return -EINVAL;
+
+ ecryptfs_data = pfk_ecryptfs_get_data(inode);
+ if (!ecryptfs_data) {
+ pr_err("internal error, no ecryptfs data\n");
+ return -EINVAL;
+ }
+
+ ret = pfk_ecryptfs_get_page_index(bio, &offset);
+ if (ret != 0) {
+ pr_err("could not get page index from bio, probably bug %d\n",
+ ret);
+ return -EINVAL;
+ }
+
+ is_metadata = ecryptfs_is_page_in_metadata(ecryptfs_data, offset);
+ if (is_metadata == true) {
+ pr_debug("ecryptfs metadata, bypassing ICE\n");
+ *is_pfe = false;
+ return -EPERM;
+ }
+
+ key_info->key = ecryptfs_get_key(ecryptfs_data);
+ if (!key_info->key) {
+ pr_err("could not parse key from ecryptfs\n");
+ return -EINVAL;
+ }
+
+ key_info->key_size = ecryptfs_get_key_size(ecryptfs_data);
+ if (!key_info->key_size) {
+ pr_err("could not parse key size from ecryptfs\n");
+ return -EINVAL;
+ }
+
+ key_info->salt = ecryptfs_get_salt(ecryptfs_data);
+ if (!key_info->salt) {
+ pr_err("could not parse salt from ecryptfs\n");
+ return -EINVAL;
+ }
+
+ key_info->salt_size = ecryptfs_get_salt_size(ecryptfs_data);
+ if (!key_info->salt_size) {
+ pr_err("could not parse salt size from ecryptfs\n");
+ return -EINVAL;
+ }
+
+ ret = pfk_ecryptfs_parse_cipher(ecryptfs_data, algo);
+ if (ret != 0) {
+ pr_err("not supported cipher\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * pfk_ecryptfs_allow_merge_bio() - Check if 2 bios can be merged.
+ *
+ * Should be invoked by upper pfk layer
+ *
+ * @bio1: Pointer to first BIO structure.
+ * @bio2: Pointer to second BIO structure.
+ * @inode1: Pointer to inode from first bio
+ * @inode2: Pointer to inode from second bio
+ *
+ * Prevent merging of BIOs from encrypted and non-encrypted
+ * files, or files encrypted with different key.
+ * Also prevent non encrypted and encrypted data from the same file
+ * to be merged (ecryptfs header if stored inside file should be non
+ * encrypted)
+ *
+ * Return: true if the BIOs allowed to be merged, false
+ * otherwise.
+ */
+bool pfk_ecryptfs_allow_merge_bio(const struct bio *bio1,
+ const struct bio *bio2, const struct inode *inode1,
+ const struct inode *inode2)
+{
+ int ret;
+ void *ecryptfs_data1 = NULL;
+ void *ecryptfs_data2 = NULL;
+ pgoff_t offset1, offset2;
+
+ /* if there is no ecryptfs pfk, don't disallow merging blocks */
+ if (!pfk_ecryptfs_is_ready())
+ return true;
+
+ if (!inode1 || !inode2)
+ return false;
+
+ ecryptfs_data1 = pfk_ecryptfs_get_data(inode1);
+ ecryptfs_data2 = pfk_ecryptfs_get_data(inode2);
+
+ if (!ecryptfs_data1 || !ecryptfs_data2) {
+ pr_err("internal error, ecryptfs data should not be null");
+ return false;
+ }
+
+ /*
+ * if we have 2 different encrypted files merge is not allowed
+ */
+ if (!ecryptfs_is_data_equal(ecryptfs_data1, ecryptfs_data2))
+ return false;
+
+ /*
+ * at this point both bio's are in the same file which is probably
+ * encrypted, last thing to check is header vs data
+ * We are assuming that we are not working in O_DIRECT mode,
+ * since it is not currently supported by eCryptfs
+ */
+ ret = pfk_ecryptfs_get_page_index(bio1, &offset1);
+ if (ret != 0) {
+ pr_err("could not get page index from bio1, probably bug %d\n",
+ ret);
+ return false;
+ }
+
+ ret = pfk_ecryptfs_get_page_index(bio2, &offset2);
+ if (ret != 0) {
+ pr_err("could not get page index from bio2, bug %d\n", ret);
+ return false;
+ }
+
+ return (ecryptfs_is_page_in_metadata(ecryptfs_data1, offset1) ==
+ ecryptfs_is_page_in_metadata(ecryptfs_data2, offset2));
+}
+
+/**
+ * pfk_ecryptfs_open_cb() - callback function for file open event
+ * @inode: file inode
+ * @data: data provided by eCryptfs
+ *
+ * Will be invoked from eCryptfs in case of file open event
+ */
+static void pfk_ecryptfs_open_cb(struct inode *inode, void *ecryptfs_data)
+{
+ size_t key_size;
+
+ if (!pfk_ecryptfs_is_ready())
+ return;
+
+ if (!inode) {
+ pr_err("inode is null\n");
+ return;
+ }
+
+ key_size = ecryptfs_get_key_size(ecryptfs_data);
+ if (!(key_size)) {
+ pr_err("could not parse key size from ecryptfs\n");
+ return;
+ }
+
+ if (pfk_ecryptfs_parse_cipher(ecryptfs_data, NULL) != 0) {
+ pr_debug("open_cb: not supported cipher\n");
+ return;
+ }
+
+ if (pfk_key_size_to_key_type(key_size, NULL) != 0)
+ return;
+
+ mutex_lock(&pfk_ecryptfs_lock);
+ pfk_ecryptfs_set_data(inode, ecryptfs_data);
+ mutex_unlock(&pfk_ecryptfs_lock);
+}
+
+/**
+ * pfk_ecryptfs_release_cb() - callback function for file release event
+ * @inode: file inode
+ *
+ * Will be invoked from eCryptfs in case of file release event
+ */
+static void pfk_ecryptfs_release_cb(struct inode *inode)
+{
+ const unsigned char *key = NULL;
+ const unsigned char *salt = NULL;
+ size_t key_size = 0;
+ size_t salt_size = 0;
+ void *data = NULL;
+
+ if (!pfk_ecryptfs_is_ready())
+ return;
+
+ if (!inode) {
+ pr_err("inode is null\n");
+ return;
+ }
+
+ data = pfk_ecryptfs_get_data(inode);
+ if (!data) {
+ pr_debug("could not get ecryptfs data from inode\n");
+ return;
+ }
+
+ key = ecryptfs_get_key(data);
+ if (!key) {
+ pr_err("could not parse key from ecryptfs\n");
+ return;
+ }
+
+ key_size = ecryptfs_get_key_size(data);
+ if (!(key_size)) {
+ pr_err("could not parse key size from ecryptfs\n");
+ return;
+ }
+
+ salt = ecryptfs_get_salt(data);
+ if (!salt) {
+ pr_err("could not parse salt from ecryptfs\n");
+ return;
+ }
+
+ salt_size = ecryptfs_get_salt_size(data);
+ if (!salt_size) {
+ pr_err("could not parse salt size from ecryptfs\n");
+ return;
+ }
+
+ pfk_kc_remove_key_with_salt(key, key_size, salt, salt_size);
+
+ mutex_lock(&pfk_ecryptfs_lock);
+ pfk_ecryptfs_set_data(inode, NULL);
+ mutex_unlock(&pfk_ecryptfs_lock);
+}
+
+/*
+ * pfk_ecryptfs_is_cipher_supported_cb() - callback function to determine
+ * whether a particular cipher (stored in ecryptfs_data) is cupported by pfk
+ *
+ * Ecryptfs should invoke this callback whenever it needs to determine whether
+ * pfk supports the particular cipher mode
+ *
+ * @ecryptfs_data: ecryptfs data
+ */
+static bool pfk_ecryptfs_is_cipher_supported_cb(const void *ecryptfs_data)
+{
+ if (!pfk_ecryptfs_is_ready())
+ return false;
+
+ if (!ecryptfs_data)
+ return false;
+
+ return (pfk_ecryptfs_parse_cipher(ecryptfs_data, NULL)) == 0;
+}
+
+/*
+ * pfk_ecryptfs_is_hw_crypt_cb() - callback function that implements a query
+ * by ecryptfs whether PFK supports HW encryption
+ */
+static bool pfk_ecryptfs_is_hw_crypt_cb(void)
+{
+ if (!pfk_ecryptfs_is_ready())
+ return false;
+
+ return true;
+}
+
+/*
+ * pfk_ecryptfs_get_salt_key_size_cb() - callback function to determine
+ * what is the salt size supported by PFK
+ *
+ * @ecryptfs_data: ecryptfs data
+ */
+static size_t pfk_ecryptfs_get_salt_key_size_cb(const void *ecryptfs_data)
+{
+ if (!pfk_ecryptfs_is_ready())
+ return 0;
+
+ if (!pfk_ecryptfs_is_cipher_supported_cb(ecryptfs_data))
+ return 0;
+
+ return PFK_SUPPORTED_SALT_SIZE;
+}
diff --git a/security/pfe/pfk_ecryptfs.h b/security/pfe/pfk_ecryptfs.h
new file mode 100644
index 000000000000..1d9942c4c09b
--- /dev/null
+++ b/security/pfe/pfk_ecryptfs.h
@@ -0,0 +1,39 @@
+/* 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
+ * 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 _PFK_ECRYPTFS_H_
+#define _PFK_ECRYPTFS_H_
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <crypto/ice.h>
+#include "pfk_internal.h"
+
+
+bool pfk_is_ecryptfs_type(const struct inode *inode);
+
+int pfk_ecryptfs_parse_inode(const struct bio *bio,
+ const struct inode *inode,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo,
+ bool *is_pfe);
+
+bool pfk_ecryptfs_allow_merge_bio(const struct bio *bio1,
+ const struct bio *bio2, const struct inode *inode1,
+ const struct inode *inode2);
+
+int __init pfk_ecryptfs_init(void);
+
+void __exit pfk_ecryptfs_deinit(void);
+
+#endif /* _PFK_ECRYPTFS_H_ */
diff --git a/security/pfe/pfk_ext4.c b/security/pfe/pfk_ext4.c
new file mode 100644
index 000000000000..07e2c7ab829c
--- /dev/null
+++ b/security/pfe/pfk_ext4.c
@@ -0,0 +1,212 @@
+/*
+ * 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
+ * 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.
+ */
+
+/*
+ * Per-File-Key (PFK) - EXT4
+ *
+ * This driver is used for working with EXT4 crypt extension
+ *
+ * The key information is stored in node by EXT4 when file is first opened
+ * and will be later accessed by Block Device Driver to actually load the key
+ * to encryption hw.
+ *
+ * PFK exposes API's for loading and removing keys from encryption hw
+ * and also API to determine whether 2 adjacent blocks can be agregated by
+ * Block Layer in one request to encryption hw.
+ *
+ */
+
+
+/* Uncomment the line below to enable debug messages */
+/* #define DEBUG 1 */
+#define pr_fmt(fmt) "pfk_ext4 [%s]: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/printk.h>
+
+#include "ext4_ice.h"
+#include "pfk_ext4.h"
+
+static bool pfk_ext4_ready;
+
+/*
+ * pfk_ext4_deinit() - Deinit function, should be invoked by upper PFK layer
+ */
+void __exit pfk_ext4_deinit(void)
+{
+ pfk_ext4_ready = false;
+}
+
+/*
+ * pfk_ecryptfs_init() - Init function, should be invoked by upper PFK layer
+ */
+int __init pfk_ext4_init(void)
+{
+ pfk_ext4_ready = true;
+ pr_info("PFK EXT4 inited successfully\n");
+
+ return 0;
+}
+
+/**
+ * pfk_ecryptfs_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the driver is ready.
+ */
+static inline bool pfk_ext4_is_ready(void)
+{
+ return pfk_ext4_ready;
+}
+
+/**
+ * pfk_ext4_dump_inode() - dumps all interesting info about inode to the screen
+ *
+ *
+ */
+/*
+ * static void pfk_ext4_dump_inode(const struct inode* inode)
+ * {
+ * struct ext4_crypt_info *ci = ext4_encryption_info((struct inode*)inode);
+ *
+ * pr_debug("dumping inode with address 0x%p\n", inode);
+ * pr_debug("S_ISREG is %d\n", S_ISREG(inode->i_mode));
+ * pr_debug("EXT4_INODE_ENCRYPT flag is %d\n",
+ * ext4_test_inode_flag((struct inode*)inode, EXT4_INODE_ENCRYPT));
+ * if (ci) {
+ * pr_debug("crypt_info address 0x%p\n", ci);
+ * pr_debug("ci->ci_data_mode %d\n", ci->ci_data_mode);
+ * } else {
+ * pr_debug("crypt_info is NULL\n");
+ * }
+ * }
+*/
+
+/**
+ * pfk_is_ext4_type() - return true if inode belongs to ICE EXT4 PFE
+ * @inode: inode pointer
+ */
+bool pfk_is_ext4_type(const struct inode *inode)
+{
+ if (!pfe_is_inode_filesystem_type(inode, "ext4"))
+ return false;
+
+ return ext4_should_be_processed_by_ice(inode);
+}
+
+/**
+ * pfk_ext4_parse_cipher() - parse cipher from inode to enum
+ * @inode: inode
+ * @algo: pointer to store the output enum (can be null)
+ *
+ * return 0 in case of success, error otherwise (i.e not supported cipher)
+ */
+static int pfk_ext4_parse_cipher(const struct inode *inode,
+ enum ice_cryto_algo_mode *algo)
+{
+ /*
+ * currently only AES XTS algo is supported
+ * in the future, table with supported ciphers might
+ * be introduced
+ */
+
+ if (!inode)
+ return -EINVAL;
+
+ if (!ext4_is_aes_xts_cipher(inode)) {
+ pr_err("ext4 alghoritm is not supported by pfk\n");
+ return -EINVAL;
+ }
+
+ if (algo)
+ *algo = ICE_CRYPTO_ALGO_MODE_AES_XTS;
+
+ return 0;
+}
+
+
+int pfk_ext4_parse_inode(const struct bio *bio,
+ const struct inode *inode,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo,
+ bool *is_pfe)
+{
+ int ret = 0;
+
+ if (!is_pfe)
+ return -EINVAL;
+
+ /*
+ * only a few errors below can indicate that
+ * this function was not invoked within PFE context,
+ * otherwise we will consider it PFE
+ */
+ *is_pfe = true;
+
+ if (!pfk_ext4_is_ready())
+ return -ENODEV;
+
+ if (!inode)
+ return -EINVAL;
+
+ if (!key_info)
+ return -EINVAL;
+
+ key_info->key = ext4_get_ice_encryption_key(inode);
+ if (!key_info->key) {
+ pr_err("could not parse key from ext4\n");
+ return -EINVAL;
+ }
+
+ key_info->key_size = ext4_get_ice_encryption_key_size(inode);
+ if (!key_info->key_size) {
+ pr_err("could not parse key size from ext4\n");
+ return -EINVAL;
+ }
+
+ key_info->salt = ext4_get_ice_encryption_salt(inode);
+ if (!key_info->salt) {
+ pr_err("could not parse salt from ext4\n");
+ return -EINVAL;
+ }
+
+ key_info->salt_size = ext4_get_ice_encryption_salt_size(inode);
+ if (!key_info->salt_size) {
+ pr_err("could not parse salt size from ext4\n");
+ return -EINVAL;
+ }
+
+ ret = pfk_ext4_parse_cipher(inode, algo);
+ if (ret != 0) {
+ pr_err("not supported cipher\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+bool pfk_ext4_allow_merge_bio(const struct bio *bio1,
+ const struct bio *bio2, const struct inode *inode1,
+ const struct inode *inode2)
+{
+ /* if there is no ext4 pfk, don't disallow merging blocks */
+ if (!pfk_ext4_is_ready())
+ return true;
+
+ if (!inode1 || !inode2)
+ return false;
+
+ return ext4_is_ice_encryption_info_equal(inode1, inode2);
+}
+
diff --git a/security/pfe/pfk_ext4.h b/security/pfe/pfk_ext4.h
new file mode 100644
index 000000000000..6b367b428e50
--- /dev/null
+++ b/security/pfe/pfk_ext4.h
@@ -0,0 +1,37 @@
+/* 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
+ * 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 _PFK_EXT4_H_
+#define _PFK_EXT4_H_
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <crypto/ice.h>
+#include "pfk_internal.h"
+
+bool pfk_is_ext4_type(const struct inode *inode);
+
+int pfk_ext4_parse_inode(const struct bio *bio,
+ const struct inode *inode,
+ struct pfk_key_info *key_info,
+ enum ice_cryto_algo_mode *algo,
+ bool *is_pfe);
+
+bool pfk_ext4_allow_merge_bio(const struct bio *bio1,
+ const struct bio *bio2, const struct inode *inode1,
+ const struct inode *inode2);
+
+int __init pfk_ext4_init(void);
+
+void __exit pfk_ext4_deinit(void);
+
+#endif /* _PFK_EXT4_H_ */
diff --git a/security/pfe/pfk_ice.h b/security/pfe/pfk_ice.h
index 1d6339a575be..fe3f2ad3950c 100644
--- a/security/pfe/pfk_ice.h
+++ b/security/pfe/pfk_ice.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* 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
@@ -22,7 +22,6 @@
#include <linux/types.h>
-
int pfk_ice_init(void);
int pfk_ice_deinit(void);
diff --git a/security/pfe/pfk_internal.h b/security/pfe/pfk_internal.h
new file mode 100644
index 000000000000..abdd0b325b39
--- /dev/null
+++ b/security/pfe/pfk_internal.h
@@ -0,0 +1,34 @@
+/* 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
+ * 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 _PFK_INTERNAL_H_
+#define _PFK_INTERNAL_H_
+
+#include <linux/types.h>
+#include <crypto/ice.h>
+
+struct pfk_key_info {
+ const unsigned char *key;
+ const unsigned char *salt;
+ size_t key_size;
+ size_t salt_size;
+};
+
+int pfk_key_size_to_key_type(size_t key_size,
+ enum ice_crpto_key_size *key_size_type);
+
+bool pfe_is_inode_filesystem_type(const struct inode *inode,
+ const char *fs_type);
+
+char *inode_to_filename(const struct inode *inode);
+
+#endif /* _PFK_INTERNAL_H_ */
diff --git a/security/pfe/pft.c b/security/pfe/pft.c
index e4bbba7b6295..74433c4fe0ff 100644
--- a/security/pfe/pft.c
+++ b/security/pfe/pft.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ * 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
@@ -746,7 +746,7 @@ EXPORT_SYMBOL(pft_get_key_index);
* Return: true if the BIOs allowed to be merged, false
* otherwise.
*/
-bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2)
+bool pft_allow_merge_bio(const struct bio *bio1, const struct bio *bio2)
{
u32 key_index1 = 0, key_index2 = 0;
bool is_encrypted1 = false, is_encrypted2 = false;
diff --git a/security/security.c b/security/security.c
index 81a555c14a35..7d1de1f6299f 100644
--- a/security/security.c
+++ b/security/security.c
@@ -857,11 +857,6 @@ int security_file_close(struct file *file)
return call_int_hook(file_close, 0, file);
}
-bool security_allow_merge_bio(struct bio *bio1, struct bio *bio2)
-{
- return call_int_hook(allow_merge_bio, 1, bio1, bio2);
-}
-
int security_task_create(unsigned long clone_flags)
{
return call_int_hook(task_create, 0, clone_flags);
@@ -1693,7 +1688,6 @@ struct security_hook_heads security_hook_heads = {
.file_receive = LIST_HEAD_INIT(security_hook_heads.file_receive),
.file_open = LIST_HEAD_INIT(security_hook_heads.file_open),
.file_close = LIST_HEAD_INIT(security_hook_heads.file_close),
- .allow_merge_bio = LIST_HEAD_INIT(security_hook_heads.allow_merge_bio),
.task_create = LIST_HEAD_INIT(security_hook_heads.task_create),
.task_free = LIST_HEAD_INIT(security_hook_heads.task_free),
.cred_alloc_blank =
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 33d1262704ec..89fea87feafb 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3585,12 +3585,6 @@ static int selinux_file_close(struct file *file)
return pft_file_close(file);
}
-static bool selinux_allow_merge_bio(struct bio *bio1, struct bio *bio2)
-{
- return pft_allow_merge_bio(bio1, bio2) &&
- pfk_allow_merge_bio(bio1, bio2);
-}
-
/* task security operations */
static int selinux_task_create(unsigned long clone_flags)
@@ -6000,7 +5994,6 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(file_open, selinux_file_open),
LSM_HOOK_INIT(file_close, selinux_file_close),
- LSM_HOOK_INIT(allow_merge_bio, selinux_allow_merge_bio),
LSM_HOOK_INIT(task_create, selinux_task_create),
LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),
diff --git a/sound/core/timer.c b/sound/core/timer.c
index b982d1b089bd..f420cd8583da 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1121,7 +1121,11 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
mutex_lock(&register_mutex);
list_for_each_entry(timer, &snd_timer_list, device_list) {
- if (timer->card && timer->card->shutdown)
+ if (timer->card == NULL) {
+ pr_debug("%s: timer->card is NULL\n", __func__);
+ continue;
+ }
+ if (timer->card->shutdown)
continue;
switch (timer->tmr_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
@@ -1247,6 +1251,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
tu->tstamp = *tstamp;
if ((tu->filter & (1 << event)) == 0 || !tu->tread)
return;
+ memset(&r1, 0, sizeof(r1));
r1.event = event;
r1.tstamp = *tstamp;
r1.val = resolution;
@@ -1281,6 +1286,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
}
if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
tu->last_resolution != resolution) {
+ memset(&r1, 0, sizeof(r1));
r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
r1.tstamp = tstamp;
r1.val = resolution;
@@ -1746,6 +1752,7 @@ static int snd_timer_user_params(struct file *file,
if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
if (tu->tread) {
struct snd_timer_tread tread;
+ memset(&tread, 0, sizeof(tread));
tread.event = SNDRV_TIMER_EVENT_EARLY;
tread.tstamp.tv_sec = 0;
tread.tstamp.tv_nsec = 0;
diff --git a/sound/soc/codecs/audio-ext-clk.c b/sound/soc/codecs/audio-ext-clk.c
index c422267dbf2c..34b1d9481457 100755..100644
--- a/sound/soc/codecs/audio-ext-clk.c
+++ b/sound/soc/codecs/audio-ext-clk.c
@@ -193,7 +193,13 @@ static int audio_get_pinctrl(struct platform_device *pdev)
int ret;
pnctrl_info = &audio_ap_clk2.pnctrl_info;
- pnctrl_info->pinctrl = NULL;
+
+ if (pnctrl_info->pinctrl) {
+ dev_dbg(&pdev->dev, "%s: already requested before\n",
+ __func__);
+ return -EINVAL;
+ }
+
pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR_OR_NULL(pinctrl)) {
dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n",
diff --git a/sound/soc/codecs/msm_hdmi_codec_rx.c b/sound/soc/codecs/msm_hdmi_codec_rx.c
index 241410c60b97..dee66f231ceb 100755..100644
--- a/sound/soc/codecs/msm_hdmi_codec_rx.c
+++ b/sound/soc/codecs/msm_hdmi_codec_rx.c
@@ -19,115 +19,208 @@
#include <sound/soc.h>
#include <linux/msm_ext_display.h>
-#define MSM_HDMI_PCM_RATES SNDRV_PCM_RATE_48000
+#define MSM_EXT_DISP_PCM_RATES SNDRV_PCM_RATE_48000
-static int msm_hdmi_audio_codec_return_value;
+static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"};
-struct msm_hdmi_audio_codec_rx_data {
- struct platform_device *hdmi_core_pdev;
- struct msm_ext_disp_audio_codec_ops hdmi_ops;
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_type, ext_disp_audio_type_text);
+
+struct msm_ext_disp_audio_codec_rx_data {
+ struct platform_device *ext_disp_core_pdev;
+ struct msm_ext_disp_audio_codec_ops ext_disp_ops;
+ int cable_status;
};
-static int msm_hdmi_edid_ctl_info(struct snd_kcontrol *kcontrol,
+static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct msm_hdmi_audio_codec_rx_data *codec_data;
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
struct msm_ext_disp_audio_edid_blk edid_blk;
int rc;
codec_data = snd_soc_codec_get_drvdata(codec);
- if (!codec_data->hdmi_ops.get_audio_edid_blk) {
- pr_debug("%s: get_audio_edid_blk() is NULL\n", __func__);
+ if (!codec_data) {
+ dev_err(codec->dev, "%s: codec_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!codec_data->ext_disp_ops.get_audio_edid_blk) {
+ dev_dbg(codec->dev, "%s: get_audio_edid_blk() is NULL\n",
+ __func__);
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = 0;
return 0;
}
- rc = codec_data->hdmi_ops.get_audio_edid_blk(
- codec_data->hdmi_core_pdev,
- &edid_blk);
+ rc = codec_data->ext_disp_ops.get_audio_edid_blk(
+ codec_data->ext_disp_core_pdev, &edid_blk);
+
if (!IS_ERR_VALUE(rc)) {
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = edid_blk.audio_data_blk_size +
edid_blk.spk_alloc_data_blk_size;
}
+ dev_dbg(codec->dev, "%s: count: %d\n", __func__, uinfo->count);
+
return rc;
}
-static int msm_hdmi_edid_get(struct snd_kcontrol *kcontrol,
+static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct msm_hdmi_audio_codec_rx_data *codec_data;
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
struct msm_ext_disp_audio_edid_blk edid_blk;
int rc;
codec_data = snd_soc_codec_get_drvdata(codec);
-
- if (!codec_data->hdmi_ops.get_audio_edid_blk)
+ if (!codec_data || !codec_data->ext_disp_ops.get_audio_edid_blk) {
+ dev_err(codec->dev, "%s: codec_data or get_audio_edid_blk() is NULL\n",
+ __func__);
return -EINVAL;
+ }
- rc = codec_data->hdmi_ops.get_audio_edid_blk(
- codec_data->hdmi_core_pdev, &edid_blk);
-
+ rc = codec_data->ext_disp_ops.get_audio_edid_blk(
+ codec_data->ext_disp_core_pdev, &edid_blk);
if (!IS_ERR_VALUE(rc)) {
memcpy(ucontrol->value.bytes.data,
- edid_blk.audio_data_blk,
- edid_blk.audio_data_blk_size);
+ edid_blk.audio_data_blk,
+ edid_blk.audio_data_blk_size);
memcpy((ucontrol->value.bytes.data +
- edid_blk.audio_data_blk_size),
- edid_blk.spk_alloc_data_blk,
- edid_blk.spk_alloc_data_blk_size);
+ edid_blk.audio_data_blk_size),
+ edid_blk.spk_alloc_data_blk,
+ edid_blk.spk_alloc_data_blk_size);
+
+ dev_dbg(codec->dev, "%s: data_blk_size:%d, spk_alloc_data_blk_size:%d\n",
+ __func__, edid_blk.audio_data_blk_size,
+ edid_blk.spk_alloc_data_blk_size);
}
return rc;
}
-static const struct snd_kcontrol_new msm_hdmi_codec_rx_controls[] = {
+static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
+ enum msm_ext_disp_cable_state cable_state;
+ enum msm_ext_disp_type disp_type;
+ int rc;
+
+ codec_data = snd_soc_codec_get_drvdata(codec);
+ if (!codec_data ||
+ !codec_data->ext_disp_ops.get_audio_edid_blk ||
+ !codec_data->ext_disp_ops.get_intf_id) {
+ dev_err(codec->dev, "%s: codec_data, get_audio_edid_blk() or get_intf_id is NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ cable_state = codec_data->ext_disp_ops.cable_status(
+ codec_data->ext_disp_core_pdev, 1);
+ if (IS_ERR_VALUE(cable_state)) {
+ dev_err(codec->dev, "%s: Error retrieving cable state from ext_disp, err:%d\n",
+ __func__, cable_state);
+ rc = cable_state;
+ goto done;
+ }
+
+ codec_data->cable_status = cable_state;
+ if (cable_state == EXT_DISPLAY_CABLE_DISCONNECT) {
+ dev_err(codec->dev, "%s: Display cable disconnected\n",
+ __func__);
+ ucontrol->value.integer.value[0] = 0;
+ rc = 0;
+ goto done;
+ }
+
+ disp_type = codec_data->ext_disp_ops.get_intf_id(
+ codec_data->ext_disp_core_pdev);
+ if (!IS_ERR_VALUE(disp_type)) {
+ switch (disp_type) {
+ case EXT_DISPLAY_TYPE_DP:
+ ucontrol->value.integer.value[0] = 2;
+ rc = 0;
+ break;
+ case EXT_DISPLAY_TYPE_HDMI:
+ ucontrol->value.integer.value[0] = 1;
+ rc = 0;
+ break;
+ default:
+ rc = -EINVAL;
+ dev_err(codec->dev, "%s: Invalid disp_type:%d\n",
+ __func__, disp_type);
+ goto done;
+ }
+ dev_dbg(codec->dev, "%s: Display type: %d\n",
+ __func__, disp_type);
+ } else {
+ dev_err(codec->dev, "%s: Error retrieving disp_type from ext_disp, err:%d\n",
+ __func__, disp_type);
+ rc = disp_type;
+ }
+
+done:
+ return rc;
+}
+
+static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "HDMI EDID",
+ .info = msm_ext_disp_edid_ctl_info,
+ .get = msm_ext_disp_edid_get,
+ },
{
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
- .iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "HDMI EDID",
- .info = msm_hdmi_edid_ctl_info,
- .get = msm_hdmi_edid_get,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Display Port EDID",
+ .info = msm_ext_disp_edid_ctl_info,
+ .get = msm_ext_disp_edid_get,
},
+ SOC_ENUM_EXT("External Display Type", ext_disp_audio_type,
+ msm_ext_disp_audio_type_get, NULL),
};
-static int msm_hdmi_audio_codec_rx_dai_startup(
+static int msm_ext_disp_audio_codec_rx_dai_startup(
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret = 0;
- struct msm_hdmi_audio_codec_rx_data *codec_data =
+ struct msm_ext_disp_audio_codec_rx_data *codec_data =
dev_get_drvdata(dai->codec->dev);
- if (!codec_data->hdmi_ops.cable_status) {
- dev_err(dai->dev, "%s() cable_status is null\n", __func__);
+ if (!codec_data || !codec_data->ext_disp_ops.cable_status) {
+ dev_err(dai->dev, "%s() codec_data or cable_status is null\n",
+ __func__);
return -EINVAL;
}
- msm_hdmi_audio_codec_return_value =
- codec_data->hdmi_ops.cable_status(
- codec_data->hdmi_core_pdev, 1);
- if (IS_ERR_VALUE(msm_hdmi_audio_codec_return_value)) {
+ codec_data->cable_status =
+ codec_data->ext_disp_ops.cable_status(
+ codec_data->ext_disp_core_pdev, 1);
+ if (IS_ERR_VALUE(codec_data->cable_status)) {
dev_err(dai->dev,
- "%s() HDMI core is not ready (ret val = %d)\n",
- __func__, msm_hdmi_audio_codec_return_value);
- ret = msm_hdmi_audio_codec_return_value;
- } else if (!msm_hdmi_audio_codec_return_value) {
+ "%s() ext disp core is not ready (ret val = %d)\n",
+ __func__, codec_data->cable_status);
+ ret = codec_data->cable_status;
+ } else if (!codec_data->cable_status) {
dev_err(dai->dev,
- "%s() HDMI cable is not connected (ret val = %d)\n",
- __func__, msm_hdmi_audio_codec_return_value);
+ "%s() ext disp cable is not connected (ret val = %d)\n",
+ __func__, codec_data->cable_status);
ret = -ENODEV;
}
return ret;
}
-static int msm_hdmi_audio_codec_rx_dai_hw_params(
+static int msm_ext_disp_audio_codec_rx_dai_hw_params(
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -139,23 +232,24 @@ static int msm_hdmi_audio_codec_rx_dai_hw_params(
int rc = 0;
struct msm_ext_disp_audio_setup_params audio_setup_params = {0};
- struct msm_hdmi_audio_codec_rx_data *codec_data =
+ struct msm_ext_disp_audio_codec_rx_data *codec_data =
dev_get_drvdata(dai->codec->dev);
- if (!codec_data->hdmi_ops.audio_info_setup) {
- dev_err(dai->dev, "%s() audio_info_setup is null\n", __func__);
+ if (!codec_data || !codec_data->ext_disp_ops.audio_info_setup) {
+ dev_err(dai->dev, "%s: codec_data or audio_info_setup is null\n",
+ __func__);
return -EINVAL;
}
- if (IS_ERR_VALUE(msm_hdmi_audio_codec_return_value)) {
+ if (IS_ERR_VALUE(codec_data->cable_status)) {
dev_err_ratelimited(dai->dev,
- "%s() HDMI core is not ready (ret val = %d)\n",
- __func__, msm_hdmi_audio_codec_return_value);
- return msm_hdmi_audio_codec_return_value;
- } else if (!msm_hdmi_audio_codec_return_value) {
+ "%s() ext disp core is not ready (ret val = %d)\n",
+ __func__, codec_data->cable_status);
+ return codec_data->cable_status;
+ } else if (!codec_data->cable_status) {
dev_err_ratelimited(dai->dev,
- "%s() HDMI cable is not connected (ret val = %d)\n",
- __func__, msm_hdmi_audio_codec_return_value);
+ "%s() ext disp cable is not connected (ret val = %d)\n",
+ __func__, codec_data->cable_status);
return -ENODEV;
}
@@ -204,54 +298,49 @@ static int msm_hdmi_audio_codec_rx_dai_hw_params(
audio_setup_params.level_shift = level_shift;
audio_setup_params.down_mix = down_mix;
- rc = codec_data->hdmi_ops.audio_info_setup(
- codec_data->hdmi_core_pdev, &audio_setup_params);
+ rc = codec_data->ext_disp_ops.audio_info_setup(
+ codec_data->ext_disp_core_pdev, &audio_setup_params);
if (IS_ERR_VALUE(rc)) {
dev_err_ratelimited(dai->dev,
- "%s() HDMI core is not ready, rc: %d\n",
+ "%s() ext disp core is not ready, rc: %d\n",
__func__, rc);
}
return rc;
}
-static void msm_hdmi_audio_codec_rx_dai_shutdown(
+static void msm_ext_disp_audio_codec_rx_dai_shutdown(
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int rc;
- struct msm_hdmi_audio_codec_rx_data *codec_data =
+ struct msm_ext_disp_audio_codec_rx_data *codec_data =
dev_get_drvdata(dai->codec->dev);
- if (!codec_data->hdmi_ops.cable_status) {
- dev_err(dai->dev, "%s() cable_status is null\n", __func__);
+ if (!codec_data || !codec_data->ext_disp_ops.cable_status) {
+ dev_err(dai->dev, "%s: codec data or cable_status is null\n",
+ __func__);
return;
}
- rc = codec_data->hdmi_ops.cable_status(
- codec_data->hdmi_core_pdev, 0);
+ rc = codec_data->ext_disp_ops.cable_status(
+ codec_data->ext_disp_core_pdev, 0);
if (IS_ERR_VALUE(rc)) {
dev_err(dai->dev,
- "%s() HDMI core had problems releasing HDMI audio flag\n",
+ "%s: ext disp core had problems releasing audio flag\n",
__func__);
}
return;
}
-static struct snd_soc_dai_ops msm_hdmi_audio_codec_rx_dai_ops = {
- .startup = msm_hdmi_audio_codec_rx_dai_startup,
- .hw_params = msm_hdmi_audio_codec_rx_dai_hw_params,
- .shutdown = msm_hdmi_audio_codec_rx_dai_shutdown
-};
-
-static int msm_hdmi_audio_codec_rx_probe(struct snd_soc_codec *codec)
+static int msm_ext_disp_audio_codec_rx_probe(struct snd_soc_codec *codec)
{
- struct msm_hdmi_audio_codec_rx_data *codec_data;
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
struct device_node *of_node_parent = NULL;
- codec_data = kzalloc(sizeof(struct msm_hdmi_audio_codec_rx_data),
+ codec_data = kzalloc(sizeof(struct msm_ext_disp_audio_codec_rx_data),
GFP_KERNEL);
if (!codec_data) {
@@ -268,16 +357,16 @@ static int msm_hdmi_audio_codec_rx_probe(struct snd_soc_codec *codec)
return -ENODEV;
}
- codec_data->hdmi_core_pdev = of_find_device_by_node(of_node_parent);
- if (!codec_data->hdmi_core_pdev) {
+ codec_data->ext_disp_core_pdev = of_find_device_by_node(of_node_parent);
+ if (!codec_data->ext_disp_core_pdev) {
dev_err(codec->dev, "%s(): can't get parent pdev\n", __func__);
kfree(codec_data);
return -ENODEV;
}
- if (msm_hdmi_register_audio_codec(codec_data->hdmi_core_pdev,
- &codec_data->hdmi_ops)) {
- dev_err(codec->dev, "%s(): can't register with hdmi core",
+ if (msm_ext_disp_register_audio_codec(codec_data->ext_disp_core_pdev,
+ &codec_data->ext_disp_ops)) {
+ dev_err(codec->dev, "%s(): can't register with ext disp core",
__func__);
kfree(codec_data);
return -ENODEV;
@@ -285,15 +374,15 @@ static int msm_hdmi_audio_codec_rx_probe(struct snd_soc_codec *codec)
dev_set_drvdata(codec->dev, codec_data);
- dev_dbg(codec->dev, "%s(): registerd %s with HDMI core\n",
+ dev_dbg(codec->dev, "%s(): registered %s with ext disp core\n",
__func__, codec->component.name);
return 0;
}
-static int msm_hdmi_audio_codec_rx_remove(struct snd_soc_codec *codec)
+static int msm_ext_disp_audio_codec_rx_remove(struct snd_soc_codec *codec)
{
- struct msm_hdmi_audio_codec_rx_data *codec_data;
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
codec_data = dev_get_drvdata(codec->dev);
kfree(codec_data);
@@ -301,7 +390,13 @@ static int msm_hdmi_audio_codec_rx_remove(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_dai_driver msm_hdmi_audio_codec_rx_dais[] = {
+static struct snd_soc_dai_ops msm_ext_disp_audio_codec_rx_dai_ops = {
+ .startup = msm_ext_disp_audio_codec_rx_dai_startup,
+ .hw_params = msm_ext_disp_audio_codec_rx_dai_hw_params,
+ .shutdown = msm_ext_disp_audio_codec_rx_dai_shutdown
+};
+
+static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = {
{
.name = "msm_hdmi_audio_codec_rx_dai",
.playback = {
@@ -310,66 +405,89 @@ static struct snd_soc_dai_driver msm_hdmi_audio_codec_rx_dais[] = {
.channels_max = 8,
.rate_min = 48000,
.rate_max = 48000,
- .rates = MSM_HDMI_PCM_RATES,
+ .rates = MSM_EXT_DISP_PCM_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = &msm_hdmi_audio_codec_rx_dai_ops,
+ .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
+ },
+ {
+ .name = "msm_dp_audio_codec_rx_dai",
+ .playback = {
+ .stream_name = "Display Port Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 48000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
},
};
-static struct snd_soc_codec_driver msm_hdmi_audio_codec_rx_soc_driver = {
- .probe = msm_hdmi_audio_codec_rx_probe,
- .remove = msm_hdmi_audio_codec_rx_remove,
- .controls = msm_hdmi_codec_rx_controls,
- .num_controls = ARRAY_SIZE(msm_hdmi_codec_rx_controls),
+static struct snd_soc_codec_driver msm_ext_disp_audio_codec_rx_soc_driver = {
+ .probe = msm_ext_disp_audio_codec_rx_probe,
+ .remove = msm_ext_disp_audio_codec_rx_remove,
+ .controls = msm_ext_disp_codec_rx_controls,
+ .num_controls = ARRAY_SIZE(msm_ext_disp_codec_rx_controls),
};
-static int msm_hdmi_audio_codec_rx_plat_probe(
+static int msm_ext_disp_audio_codec_rx_plat_probe(
struct platform_device *pdev)
{
dev_dbg(&pdev->dev, "%s(): dev name %s\n", __func__,
dev_name(&pdev->dev));
return snd_soc_register_codec(&pdev->dev,
- &msm_hdmi_audio_codec_rx_soc_driver,
- msm_hdmi_audio_codec_rx_dais,
- ARRAY_SIZE(msm_hdmi_audio_codec_rx_dais));
+ &msm_ext_disp_audio_codec_rx_soc_driver,
+ msm_ext_disp_audio_codec_rx_dais,
+ ARRAY_SIZE(msm_ext_disp_audio_codec_rx_dais));
}
-static int msm_hdmi_audio_codec_rx_plat_remove(
+static int msm_ext_disp_audio_codec_rx_plat_remove(
struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
-static const struct of_device_id msm_hdmi_audio_codec_rx_dt_match[] = {
- { .compatible = "qcom,msm-hdmi-audio-codec-rx", },
+static const struct of_device_id msm_ext_disp_audio_codec_rx_dt_match[] = {
+ { .compatible = "qcom,msm-ext-disp-audio-codec-rx", },
{}
};
-MODULE_DEVICE_TABLE(of, msm_hdmi_codec_dt_match);
+MODULE_DEVICE_TABLE(of, msm_ext_disp_audio_codec_rx_dt_match);
-static struct platform_driver msm_hdmi_audio_codec_rx_driver = {
+static struct platform_driver msm_ext_disp_audio_codec_rx_driver = {
.driver = {
- .name = "msm-hdmi-audio-codec-rx",
+ .name = "msm-ext-disp-audio-codec-rx",
.owner = THIS_MODULE,
- .of_match_table = msm_hdmi_audio_codec_rx_dt_match,
+ .of_match_table = msm_ext_disp_audio_codec_rx_dt_match,
},
- .probe = msm_hdmi_audio_codec_rx_plat_probe,
- .remove = msm_hdmi_audio_codec_rx_plat_remove,
+ .probe = msm_ext_disp_audio_codec_rx_plat_probe,
+ .remove = msm_ext_disp_audio_codec_rx_plat_remove,
};
-static int __init msm_hdmi_audio_codec_rx_init(void)
+static int __init msm_ext_disp_audio_codec_rx_init(void)
{
- return platform_driver_register(&msm_hdmi_audio_codec_rx_driver);
+ int rc;
+
+ rc = platform_driver_register(&msm_ext_disp_audio_codec_rx_driver);
+ if (rc) {
+ pr_err("%s: failed to register ext disp codec driver err:%d\n",
+ __func__, rc);
+ }
+
+ return rc;
}
-module_init(msm_hdmi_audio_codec_rx_init);
+module_init(msm_ext_disp_audio_codec_rx_init);
-static void __exit msm_hdmi_audio_codec_rx_exit(void)
+static void __exit msm_ext_disp_audio_codec_rx_exit(void)
{
- platform_driver_unregister(&msm_hdmi_audio_codec_rx_driver);
+ platform_driver_unregister(&msm_ext_disp_audio_codec_rx_driver);
}
-module_exit(msm_hdmi_audio_codec_rx_exit);
+module_exit(msm_ext_disp_audio_codec_rx_exit);
-MODULE_DESCRIPTION("MSM HDMI CODEC driver");
+MODULE_DESCRIPTION("MSM External Display Audio CODEC Driver");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 60a7ad1b01c2..5b6af14e1d94 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -1545,6 +1545,13 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
mbhc->btn_press_intr = false;
if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM,
+ false);
+ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+ false);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+ 1);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
} else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 158b9ae08642..9acab619ec8e 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -793,7 +793,7 @@ struct tasha_priv {
struct wcd_swr_ctrl_platform_data swr_plat_data;
/* Port values for Rx and Tx codec_dai */
- unsigned int rx_port_value;
+ unsigned int rx_port_value[TASHA_RX_MAX];
unsigned int tx_port_value;
unsigned int vi_feed_value;
@@ -1114,7 +1114,7 @@ static void tasha_cdc_sido_ccl_enable(struct tasha_priv *tasha, bool ccl_flag)
if (!codec)
return;
- if (!TASHA_IS_2_0(tasha->wcd9xxx->version)) {
+ if (!TASHA_IS_2_0(tasha->wcd9xxx)) {
dev_dbg(codec->dev, "%s: tasha version < 2p0, return\n",
__func__);
return;
@@ -1139,7 +1139,7 @@ static void tasha_cdc_sido_ccl_enable(struct tasha_priv *tasha, bool ccl_flag)
static bool tasha_cdc_is_svs_enabled(struct tasha_priv *tasha)
{
- if (TASHA_IS_2_0(tasha->wcd9xxx->version) &&
+ if (TASHA_IS_2_0(tasha->wcd9xxx) &&
svs_scaling_enabled)
return true;
@@ -1269,7 +1269,7 @@ int tasha_enable_efuse_sensing(struct snd_soc_codec *codec)
tasha_cdc_mclk_enable(codec, true, false);
- if (!TASHA_IS_2_0(priv->wcd9xxx->version))
+ if (!TASHA_IS_2_0(priv->wcd9xxx))
snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL,
0x1E, 0x02);
snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL,
@@ -1282,7 +1282,7 @@ int tasha_enable_efuse_sensing(struct snd_soc_codec *codec)
if (!(snd_soc_read(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS) & 0x01))
WARN(1, "%s: Efuse sense is not complete\n", __func__);
- if (TASHA_IS_2_0(priv->wcd9xxx->version)) {
+ if (TASHA_IS_2_0(priv->wcd9xxx)) {
if (!(snd_soc_read(codec,
WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0) & 0x40))
snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST,
@@ -1502,7 +1502,7 @@ static void tasha_mbhc_hph_l_pull_up_control(struct snd_soc_codec *codec,
dev_dbg(codec->dev, "%s: HS pull up current:%d\n",
__func__, pull_up_cur);
- if (TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL,
0xC0, pull_up_cur << 6);
else
@@ -1980,7 +1980,7 @@ static void tasha_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
};
s16 *d1 = NULL;
- if (!TASHA_IS_2_0(wcd9xxx->version)) {
+ if (!TASHA_IS_2_0(wcd9xxx)) {
dev_dbg(codec->dev, "%s: Z-det is not supported for this codec version\n",
__func__);
*zl = 0;
@@ -2168,13 +2168,13 @@ static void tasha_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec,
if (enable) {
snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
0x40, 0x40);
- if (TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
0x10, 0x10);
} else {
snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
0x40, 0x00);
- if (TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
0x10, 0x00);
}
@@ -2276,6 +2276,7 @@ static int tasha_put_anc_func(struct snd_kcontrol *kcontrol,
snd_soc_dapm_enable_pin(dapm, "ANC HPHL");
snd_soc_dapm_enable_pin(dapm, "ANC EAR PA");
snd_soc_dapm_enable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA");
snd_soc_dapm_disable_pin(dapm, "LINEOUT2");
snd_soc_dapm_disable_pin(dapm, "LINEOUT2 PA");
snd_soc_dapm_disable_pin(dapm, "LINEOUT1");
@@ -2297,6 +2298,7 @@ static int tasha_put_anc_func(struct snd_kcontrol *kcontrol,
snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA");
snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA");
snd_soc_dapm_enable_pin(dapm, "LINEOUT2");
snd_soc_dapm_enable_pin(dapm, "LINEOUT2 PA");
snd_soc_dapm_enable_pin(dapm, "LINEOUT1");
@@ -2597,7 +2599,8 @@ static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = tasha_p->rx_port_value;
+ ucontrol->value.enumerated.item[0] =
+ tasha_p->rx_port_value[widget->shift];
return 0;
}
@@ -2616,25 +2619,27 @@ static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct snd_soc_dapm_update *update = NULL;
+ unsigned int rx_port_value;
u32 port_id = widget->shift;
+ tasha_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0];
+ rx_port_value = tasha_p->rx_port_value[port_id];
+
pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
- widget->name, ucontrol->id.name, tasha_p->rx_port_value,
+ widget->name, ucontrol->id.name, rx_port_value,
widget->shift, ucontrol->value.integer.value[0]);
- tasha_p->rx_port_value = ucontrol->value.enumerated.item[0];
-
mutex_lock(&tasha_p->codec_mutex);
if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
- if (tasha_p->rx_port_value > 2) {
+ if (rx_port_value > 2) {
dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
__func__);
goto err;
}
}
/* value need to match the Virtual port and AIF number */
- switch (tasha_p->rx_port_value) {
+ switch (rx_port_value) {
case 0:
list_del_init(&core->rx_chs[port_id].list);
break;
@@ -2694,13 +2699,13 @@ static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
&tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list);
break;
default:
- pr_err("Unknown AIF %d\n", tasha_p->rx_port_value);
+ pr_err("Unknown AIF %d\n", rx_port_value);
goto err;
}
rtn:
mutex_unlock(&tasha_p->codec_mutex);
snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
- tasha_p->rx_port_value, e, update);
+ rx_port_value, e, update);
return 0;
err:
@@ -2835,23 +2840,19 @@ static const struct snd_kcontrol_new aif4_mad_mixer[] = {
};
static const struct snd_kcontrol_new rx_int1_spline_mix_switch[] = {
- SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0),
- SOC_DAPM_SINGLE("HPHL Native Switch", SND_SOC_NOPM, 0, 1, 0)
+ SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0)
};
static const struct snd_kcontrol_new rx_int2_spline_mix_switch[] = {
- SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0),
- SOC_DAPM_SINGLE("HPHR Native Switch", SND_SOC_NOPM, 0, 1, 0)
+ SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0)
};
static const struct snd_kcontrol_new rx_int3_spline_mix_switch[] = {
- SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0),
- SOC_DAPM_SINGLE("LO1 Native Switch", SND_SOC_NOPM, 0, 1, 0)
+ SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0)
};
static const struct snd_kcontrol_new rx_int4_spline_mix_switch[] = {
- SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0),
- SOC_DAPM_SINGLE("LO2 Native Switch", SND_SOC_NOPM, 0, 1, 0)
+ SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0)
};
static const struct snd_kcontrol_new rx_int5_spline_mix_switch[] = {
@@ -3133,8 +3134,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",
@@ -3148,7 +3154,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;
@@ -3664,7 +3669,7 @@ static int tasha_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_PRE_PMU:
tasha->rx_bias_count++;
if (tasha->rx_bias_count == 1) {
- if (TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
tasha_codec_init_flyback(codec);
snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES,
0x01, 0x01);
@@ -3823,7 +3828,8 @@ static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w,
i = 0;
anc_cal_size = anc_writes_size;
- if (!strcmp(w->name, "RX INT0 DAC"))
+ if (!strcmp(w->name, "RX INT0 DAC") ||
+ !strcmp(w->name, "ANC SPK1 PA"))
tasha_realign_anc_coeff(codec,
WCD9335_CDC_ANC0_IIR_COEFF_1_CTL,
WCD9335_CDC_ANC0_IIR_COEFF_2_CTL);
@@ -3872,8 +3878,9 @@ static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_POST_PMD:
if (!strcmp(w->name, "ANC HPHL PA") ||
- !strcmp(w->name, "ANC EAR PA") ||
- !strcmp(w->name, "ANC LINEOUT1 PA")) {
+ !strcmp(w->name, "ANC EAR PA") ||
+ !strcmp(w->name, "ANC SPK1 PA") ||
+ !strcmp(w->name, "ANC LINEOUT1 PA")) {
snd_soc_update_bits(codec,
WCD9335_CDC_ANC0_MODE_1_CTL, 0x30, 0x00);
msleep(50);
@@ -3886,7 +3893,7 @@ static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec,
WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x00);
} else if (!strcmp(w->name, "ANC HPHR PA") ||
- !strcmp(w->name, "ANC LINEOUT2 PA")) {
+ !strcmp(w->name, "ANC LINEOUT2 PA")) {
snd_soc_update_bits(codec,
WCD9335_CDC_ANC1_MODE_1_CTL, 0x30, 0x00);
msleep(50);
@@ -3930,7 +3937,7 @@ static void tasha_codec_hph_post_pa_config(struct tasha_priv *tasha,
{
u8 scale_val = 0;
- if (!TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (!TASHA_IS_2_0(tasha->wcd9xxx))
return;
switch (event) {
@@ -4264,6 +4271,35 @@ static int tasha_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w,
return ret;
}
+static int tasha_codec_enable_spk_anc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s %s %d %d\n", __func__, w->name, event,
+ tasha->anc_func);
+
+ if (!tasha->anc_func)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0,
+ 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0,
+ 0x10, 0x00);
+ ret = tasha_codec_enable_anc(w, kcontrol, event);
+ break;
+ }
+ return ret;
+}
+
static int tasha_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
@@ -4415,7 +4451,7 @@ static void tasha_codec_hph_mode_config(struct snd_soc_codec *codec,
{
struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
- if (!TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (!TASHA_IS_2_0(tasha->wcd9xxx))
return;
switch (mode) {
@@ -4479,14 +4515,14 @@ static int tasha_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
/* 1000us required as per HW requirement */
usleep_range(1000, 1100);
if ((hph_mode == CLS_H_LP) &&
- (TASHA_IS_1_1(wcd9xxx->version))) {
+ (TASHA_IS_1_1(wcd9xxx))) {
snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
0x03, 0x03);
}
break;
case SND_SOC_DAPM_PRE_PMD:
if ((hph_mode == CLS_H_LP) &&
- (TASHA_IS_1_1(wcd9xxx->version))) {
+ (TASHA_IS_1_1(wcd9xxx))) {
snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
0x03, 0x00);
}
@@ -4569,14 +4605,14 @@ static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
/* 1000us required as per HW requirement */
usleep_range(1000, 1100);
if ((hph_mode == CLS_H_LP) &&
- (TASHA_IS_1_1(wcd9xxx->version))) {
+ (TASHA_IS_1_1(wcd9xxx))) {
snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
0x03, 0x03);
}
break;
case SND_SOC_DAPM_PRE_PMD:
if ((hph_mode == CLS_H_LP) &&
- (TASHA_IS_1_1(wcd9xxx->version))) {
+ (TASHA_IS_1_1(wcd9xxx))) {
snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
0x03, 0x00);
}
@@ -4801,7 +4837,7 @@ static void tasha_codec_hd2_control(struct snd_soc_codec *codec,
u16 hd2_scale_reg;
u16 hd2_enable_reg = 0;
- if (!TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (!TASHA_IS_2_0(tasha->wcd9xxx))
return;
if (prim_int_reg == WCD9335_CDC_RX1_RX_PATH_CTL) {
@@ -6819,7 +6855,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"SPL SRC0 MUX", "SRC_IN_HPHL", "RX INT1_1 MIX1"},
{"RX INT1 SPLINE MIX", NULL, "RX INT1_1 MIX1"},
{"RX INT1 SPLINE MIX", "HPHL Switch", "SPL SRC0 MUX"},
- {"RX INT1 SPLINE MIX", "HPHL Native Switch", "RX INT1 NATIVE SUPPLY"},
+ {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"},
+ {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 NATIVE MUX"},
+ {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"},
{"RX INT1 SEC MIX", NULL, "RX INT1 SPLINE MIX"},
{"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"},
{"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"},
@@ -6833,7 +6871,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"SPL SRC1 MUX", "SRC_IN_HPHR", "RX INT2_1 MIX1"},
{"RX INT2 SPLINE MIX", NULL, "RX INT2_1 MIX1"},
{"RX INT2 SPLINE MIX", "HPHR Switch", "SPL SRC1 MUX"},
- {"RX INT2 SPLINE MIX", "HPHR Native Switch", "RX INT2 NATIVE SUPPLY"},
+ {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"},
+ {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 NATIVE MUX"},
+ {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"},
{"RX INT2 SEC MIX", NULL, "RX INT2 SPLINE MIX"},
{"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"},
{"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"},
@@ -6847,7 +6887,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"SPL SRC0 MUX", "SRC_IN_LO1", "RX INT3_1 MIX1"},
{"RX INT3 SPLINE MIX", NULL, "RX INT3_1 MIX1"},
{"RX INT3 SPLINE MIX", "LO1 Switch", "SPL SRC0 MUX"},
- {"RX INT3 SPLINE MIX", "LO1 Native Switch", "RX INT3 NATIVE SUPPLY"},
+ {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"},
+ {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 NATIVE MUX"},
+ {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"},
{"RX INT3 SEC MIX", NULL, "RX INT3 SPLINE MIX"},
{"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"},
{"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"},
@@ -6860,7 +6902,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"SPL SRC1 MUX", "SRC_IN_LO2", "RX INT4_1 MIX1"},
{"RX INT4 SPLINE MIX", NULL, "RX INT4_1 MIX1"},
{"RX INT4 SPLINE MIX", "LO2 Switch", "SPL SRC1 MUX"},
- {"RX INT4 SPLINE MIX", "LO2 Native Switch", "RX INT4 NATIVE SUPPLY"},
+ {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"},
+ {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 NATIVE MUX"},
+ {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"},
{"RX INT4 SEC MIX", NULL, "RX INT4 SPLINE MIX"},
{"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"},
{"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"},
@@ -6916,6 +6960,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RX INT7 CHAIN", NULL, "RX_BIAS"},
{"SPK1 OUT", NULL, "RX INT7 CHAIN"},
+ {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"},
+ {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"},
+ {"SPK1 OUT", NULL, "ANC SPK1 PA"},
+
{"SPL SRC3 MUX", "SRC_IN_SPKRR", "RX INT8_1 MIX1"},
{"RX INT8 SPLINE MIX", NULL, "RX INT8_1 MIX1"},
{"RX INT8 SPLINE MIX", "SPKRR Switch", "SPL SRC3 MUX"},
@@ -6948,6 +6996,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"ANC EAR Enable", "Switch", "ADC MUX11"},
{"RX INT0 MIX2", NULL, "ANC EAR Enable"},
+ {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"},
+ {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"},
+ {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"},
+
{"ANC LINEOUT1 Enable", "Switch", "ADC MUX10"},
{"ANC LINEOUT1 Enable", "Switch", "ADC MUX11"},
{"RX INT3 MIX2", NULL, "ANC LINEOUT1 Enable"},
@@ -8000,7 +8052,7 @@ static void wcd_vbat_adc_out_config(struct wcd_vbat *vbat,
if (!vbat->adc_config) {
tasha_cdc_mclk_enable(codec, true, false);
- if (TASHA_IS_2_0(wcd9xxx->version))
+ if (TASHA_IS_2_0(wcd9xxx))
wcd_vbat_adc_out_config_2_0(vbat, codec);
else
wcd_vbat_adc_out_config_1_x(vbat, codec);
@@ -9056,6 +9108,10 @@ static const char * const anc1_fb_mux_text[] = {
"ZERO", "ANC_IN_HPHR", "ANC_IN_LO2"
};
+static const char * const native_mux_text[] = {
+ "OFF", "ON",
+};
+
static const struct soc_enum spl_src0_mux_chain_enum =
SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 0, 3,
spl_src0_mux_text);
@@ -9108,6 +9164,22 @@ static const struct soc_enum rx_int8_2_mux_chain_enum =
SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9,
rx_int_mix_mux_text);
+static const struct soc_enum int1_1_native_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+ native_mux_text);
+
+static const struct soc_enum int2_1_native_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+ native_mux_text);
+
+static const struct soc_enum int3_1_native_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+ native_mux_text);
+
+static const struct soc_enum int4_1_native_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+ native_mux_text);
+
static const struct soc_enum rx_int0_1_mix_inp0_chain_enum =
SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13,
rx_prim_mix_text);
@@ -9640,6 +9712,18 @@ static const struct snd_kcontrol_new rx_int7_2_mux =
static const struct snd_kcontrol_new rx_int8_2_mux =
SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum);
+static const struct snd_kcontrol_new int1_1_native_mux =
+ SOC_DAPM_ENUM("RX INT1_1 NATIVE MUX Mux", int1_1_native_enum);
+
+static const struct snd_kcontrol_new int2_1_native_mux =
+ SOC_DAPM_ENUM("RX INT2_1 NATIVE MUX Mux", int2_1_native_enum);
+
+static const struct snd_kcontrol_new int3_1_native_mux =
+ SOC_DAPM_ENUM("RX INT3_1 NATIVE MUX Mux", int3_1_native_enum);
+
+static const struct snd_kcontrol_new int4_1_native_mux =
+ SOC_DAPM_ENUM("RX INT4_1 NATIVE MUX Mux", int4_1_native_enum);
+
static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux =
SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum);
@@ -10020,12 +10104,18 @@ static const struct snd_kcontrol_new anc_hphr_switch =
static const struct snd_kcontrol_new anc_ear_switch =
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+static const struct snd_kcontrol_new anc_ear_spkr_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
static const struct snd_kcontrol_new anc_lineout1_switch =
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
static const struct snd_kcontrol_new anc_lineout2_switch =
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+static const struct snd_kcontrol_new anc_spkr_pa_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);
@@ -10103,20 +10193,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]),
@@ -10645,6 +10740,14 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
tasha_codec_configure_cpe_input,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("RX INT1_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+ &int1_1_native_mux),
+ SND_SOC_DAPM_MUX("RX INT2_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+ &int2_1_native_mux),
+ SND_SOC_DAPM_MUX("RX INT3_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+ &int3_1_native_mux),
+ SND_SOC_DAPM_MUX("RX INT4_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+ &int4_1_native_mux),
SND_SOC_DAPM_MUX("RX MIX TX0 MUX", SND_SOC_NOPM, 0, 0,
&rx_mix_tx0_mux),
SND_SOC_DAPM_MUX("RX MIX TX1 MUX", SND_SOC_NOPM, 0, 0,
@@ -10791,6 +10894,9 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
tasha_codec_enable_lineout_pa,
SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tasha_codec_enable_spk_anc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_OUTPUT("HPHL"),
SND_SOC_DAPM_OUTPUT("HPHR"),
@@ -10852,10 +10958,14 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
&anc_hphr_switch),
SND_SOC_DAPM_SWITCH("ANC EAR Enable", SND_SOC_NOPM, 0, 0,
&anc_ear_switch),
+ SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0,
+ &anc_ear_spkr_switch),
SND_SOC_DAPM_SWITCH("ANC LINEOUT1 Enable", SND_SOC_NOPM, 0, 0,
&anc_lineout1_switch),
SND_SOC_DAPM_SWITCH("ANC LINEOUT2 Enable", SND_SOC_NOPM, 0, 0,
&anc_lineout2_switch),
+ SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0,
+ &anc_spkr_pa_switch),
};
static int tasha_get_channel_map(struct snd_soc_dai *dai,
@@ -10945,7 +11055,7 @@ static int tasha_set_channel_map(struct snd_soc_dai *dai,
/* Reserve TX12/TX13 for MAD data channel */
dai_data = &tasha->dai[AIF4_MAD_TX];
if (dai_data) {
- if (TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
list_add_tail(&core->tx_chs[TASHA_TX13].list,
&dai_data->wcd9xxx_ch_list);
else
@@ -10968,8 +11078,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,
@@ -11896,9 +12017,9 @@ static ssize_t tasha_codec_version_read(struct snd_info_entry *entry,
wcd9xxx = tasha->wcd9xxx;
if (wcd9xxx->codec_type->id_major == TASHA_MAJOR) {
- if (TASHA_IS_1_0(wcd9xxx->version))
+ if (TASHA_IS_1_0(wcd9xxx))
len = snprintf(buffer, sizeof(buffer), "WCD9335_1_0\n");
- else if (TASHA_IS_1_1(wcd9xxx->version))
+ else if (TASHA_IS_1_1(wcd9xxx))
len = snprintf(buffer, sizeof(buffer), "WCD9335_1_1\n");
else
snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
@@ -12240,7 +12361,7 @@ static void tasha_update_reg_reset_values(struct snd_soc_codec *codec)
u32 i;
struct wcd9xxx *tasha_core = dev_get_drvdata(codec->dev->parent);
- if (TASHA_IS_1_1(tasha_core->version)) {
+ if (TASHA_IS_1_1(tasha_core)) {
for (i = 0; i < ARRAY_SIZE(tasha_reg_update_reset_val_1_1);
i++)
snd_soc_write(codec,
@@ -12260,27 +12381,27 @@ static void tasha_codec_init_reg(struct snd_soc_codec *codec)
tasha_codec_reg_init_common_val[i].mask,
tasha_codec_reg_init_common_val[i].val);
- if (TASHA_IS_1_1(wcd9xxx->version) ||
- TASHA_IS_1_0(wcd9xxx->version))
+ if (TASHA_IS_1_1(wcd9xxx) ||
+ TASHA_IS_1_0(wcd9xxx))
for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_1_x_val); i++)
snd_soc_update_bits(codec,
tasha_codec_reg_init_1_x_val[i].reg,
tasha_codec_reg_init_1_x_val[i].mask,
tasha_codec_reg_init_1_x_val[i].val);
- if (TASHA_IS_1_1(wcd9xxx->version)) {
+ if (TASHA_IS_1_1(wcd9xxx)) {
for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_1); i++)
snd_soc_update_bits(codec,
tasha_codec_reg_init_val_1_1[i].reg,
tasha_codec_reg_init_val_1_1[i].mask,
tasha_codec_reg_init_val_1_1[i].val);
- } else if (TASHA_IS_1_0(wcd9xxx->version)) {
+ } else if (TASHA_IS_1_0(wcd9xxx)) {
for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_0); i++)
snd_soc_update_bits(codec,
tasha_codec_reg_init_val_1_0[i].reg,
tasha_codec_reg_init_val_1_0[i].mask,
tasha_codec_reg_init_val_1_0[i].val);
- } else if (TASHA_IS_2_0(wcd9xxx->version)) {
+ } else if (TASHA_IS_2_0(wcd9xxx)) {
for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_2_0); i++)
snd_soc_update_bits(codec,
tasha_codec_reg_init_val_2_0[i].reg,
@@ -12767,7 +12888,7 @@ static int tasha_codec_cpe_fll_enable(struct snd_soc_codec *codec,
}
}
- if (TASHA_IS_1_0(wcd9xxx->version)) {
+ if (TASHA_IS_1_0(wcd9xxx)) {
tasha_cdc_mclk_enable(codec, true, false);
clk_sel_reg_val = 0x02;
}
@@ -12806,7 +12927,7 @@ static int tasha_codec_cpe_fll_enable(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0,
0x01, 0x00);
- if (TASHA_IS_1_0(wcd9xxx->version))
+ if (TASHA_IS_1_0(wcd9xxx))
tasha_cdc_mclk_enable(codec, false, false);
/*
@@ -12935,7 +13056,7 @@ static int tasha_cpe_err_irq_control(struct snd_soc_codec *codec,
struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
u8 irq_bits;
- if (TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
irq_bits = 0xFF;
else
irq_bits = 0x3F;
@@ -13248,7 +13369,7 @@ static int tasha_codec_probe(struct snd_soc_codec *codec)
}
/* Initialize MBHC module */
- if (TASHA_IS_2_0(tasha->wcd9xxx->version)) {
+ if (TASHA_IS_2_0(tasha->wcd9xxx)) {
wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].reg =
WCD9335_MBHC_FSM_STATUS;
wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].mask = 0x01;
@@ -13362,6 +13483,7 @@ static int tasha_codec_probe(struct snd_soc_codec *codec)
snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA");
snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA");
mutex_unlock(&tasha->codec_mutex);
snd_soc_dapm_sync(dapm);
@@ -13633,7 +13755,7 @@ static int tasha_swrm_clock(void *handle, bool enable)
if (enable) {
tasha->swr_clk_users++;
if (tasha->swr_clk_users == 1) {
- if (TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
regmap_update_bits(
tasha->wcd9xxx->regmap,
WCD9335_TEST_DEBUG_NPL_DLY_TEST_1,
@@ -13650,7 +13772,7 @@ static int tasha_swrm_clock(void *handle, bool enable)
WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL,
0x01, 0x00);
__tasha_cdc_mclk_enable(tasha, false);
- if (TASHA_IS_2_0(tasha->wcd9xxx->version))
+ if (TASHA_IS_2_0(tasha->wcd9xxx))
regmap_update_bits(
tasha->wcd9xxx->regmap,
WCD9335_TEST_DEBUG_NPL_DLY_TEST_1,
diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h
index 8b20805de6f9..ac3031ffe615 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-routing.h
+++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h
@@ -312,6 +312,43 @@ const struct snd_soc_dapm_route tavil_audio_map[] = {
{"ADC MUX13", "DMIC", "DMIC MUX13"},
{"ADC MUX13", "AMIC", "AMIC MUX13"},
+ {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"},
+ {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"},
+ {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"},
+ {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"},
+ {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"},
+
{"DMIC MUX0", "DMIC0", "DMIC0"},
{"DMIC MUX0", "DMIC1", "DMIC1"},
{"DMIC MUX0", "DMIC2", "DMIC2"},
@@ -860,6 +897,29 @@ const struct snd_soc_dapm_route tavil_audio_map[] = {
{"RX INT8 CHAIN", NULL, "RX_BIAS"},
{"SPK2 OUT", NULL, "RX INT8 CHAIN"},
+ /* ANC Routing */
+ {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"},
+ {"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"},
+ {"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"},
+ {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"},
+ {"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"},
+ {"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"},
+
+ {"ANC OUT EAR Enable", "Switch", "ADC MUX10"},
+ {"ANC OUT EAR Enable", "Switch", "ADC MUX11"},
+ {"RX INT0 MIX2", NULL, "ANC OUT EAR Enable"},
+
+ {"ANC EAR PA", NULL, "RX INT0 DAC"},
+ {"ANC EAR", NULL, "ANC EAR PA"},
+
+ {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"},
+ {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"},
+ {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"},
+
+ {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"},
+ {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"},
+ {"SPK1 OUT", NULL, "ANC SPK1 PA"},
+
/*
* SRC0, SRC1 inputs to Sidetone RX Mixer
* on RX0, RX1, RX2, RX3, RX4 and RX7 chains
diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c
index 50a023013fa8..28914ed3f937 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.c
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -66,6 +66,15 @@
#define WCD934X_FORMATS_S16_LE (SNDRV_PCM_FMTBIT_S16_LE)
+/* Macros for packing register writes into a U32 */
+#define WCD934X_PACKED_REG_SIZE sizeof(u32)
+#define WCD934X_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \
+ do { \
+ ((reg) = ((packed >> 16) & (0xffff))); \
+ ((mask) = ((packed >> 8) & (0xff))); \
+ ((val) = ((packed) & (0xff))); \
+ } while (0)
+
#define STRING(name) #name
#define WCD_DAPM_ENUM(name, reg, offset, text) \
static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \
@@ -91,6 +100,7 @@ static const struct snd_kcontrol_new name##_mux = \
#define WCD934X_MCLK_CLK_12P288MHZ 12288000
#define WCD934X_MCLK_CLK_9P6MHZ 9600000
+#define WCD934X_INTERP_MUX_NUM_INPUTS 3
#define WCD934X_NUM_INTERPOLATORS 9
#define WCD934X_NUM_DECIMATORS 9
#define WCD934X_RX_PATH_CTL_OFFSET 20
@@ -129,6 +139,8 @@ static const struct snd_kcontrol_new name##_mux = \
#define WCD934X_MAD_AUDIO_FIRMWARE_PATH "wcd934x/wcd934x_mad_audio.bin"
+#define TAVIL_VERSION_ENTRY_SIZE 17
+
enum {
VI_SENSE_1,
VI_SENSE_2,
@@ -177,6 +189,16 @@ enum {
INTn_2_INP_SEL_PROXIMITY,
};
+enum {
+ INTERP_MAIN_PATH,
+ INTERP_MIX_PATH,
+};
+
+struct tavil_idle_detect_config {
+ u8 hph_idle_thr;
+ u8 hph_idle_detect_en;
+};
+
static const struct intr_data wcd934x_intr_table[] = {
{WCD9XXX_IRQ_SLIMBUS, false},
{WCD934X_IRQ_MBHC_SW_DET, true},
@@ -378,6 +400,24 @@ static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = {
(WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE),
SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1
},
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ WCD934X_CDC_ANC0_IIR_ADAPT_CTL),
+ AANC_FF_GAIN_ADAPTIVE, 0x4, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ WCD934X_CDC_ANC0_IIR_ADAPT_CTL),
+ AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD934X_REG_BITS, 0
+ },
+ {
+ 1,
+ (WCD934X_REGISTER_START_OFFSET +
+ WCD934X_CDC_ANC0_FF_A_GAIN_CTL),
+ AANC_GAIN_CONTROL, 0xFF, WCD934X_REG_BITS, 0
+ },
};
static struct afe_param_cdc_reg_cfg_data tavil_audio_reg_cfg = {
@@ -385,6 +425,11 @@ static struct afe_param_cdc_reg_cfg_data tavil_audio_reg_cfg = {
.reg_data = audio_reg_cfg,
};
+static struct afe_param_id_cdc_aanc_version tavil_cdc_aanc_version = {
+ .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION,
+ .aanc_hw_version = AANC_HW_BLOCK_VERSION_2,
+};
+
static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
@@ -451,8 +496,14 @@ struct tavil_priv {
s32 micb_ref[TAVIL_MAX_MICBIAS];
s32 pullup_ref[TAVIL_MAX_MICBIAS];
+ /* ANC related */
+ u32 anc_slot;
+ bool anc_func;
+
/* compander */
int comp_enabled[COMPANDER_MAX];
+ int ear_spkr_gain;
+
/* class h specific data */
struct wcd_clsh_cdc_data clsh_d;
/* Tavil Interpolator Mode Select for EAR, HPH_L and HPH_R */
@@ -461,6 +512,9 @@ struct tavil_priv {
/* Mad switch reference count */
int mad_switch_cnt;
+ /* track tavil interface type */
+ u8 intf_type;
+
/* to track the status */
unsigned long status_mask;
@@ -494,6 +548,10 @@ struct tavil_priv {
/* cal info for codec */
struct fw_info *fw_data;
+ /* Entry for version info */
+ struct snd_info_entry *entry;
+ struct snd_info_entry *version_entry;
+
/* SVS voting related */
struct mutex svs_mutex;
int svs_ref_cnt;
@@ -504,6 +562,7 @@ struct tavil_priv {
/* Main path clock users count */
int main_clk_users[WCD934X_NUM_INTERPOLATORS];
struct tavil_dsd_config *dsd_config;
+ struct tavil_idle_detect_config idle_det_cfg;
};
static const struct tavil_reg_mask_val tavil_spkr_default[] = {
@@ -537,7 +596,7 @@ int wcd934x_get_codec_info(struct wcd9xxx *wcd9xxx,
{
u16 id_minor, id_major;
struct regmap *wcd_regmap;
- int rc, version = 0;
+ int rc, version = -1;
if (!wcd9xxx || !wcd_type)
return -EINVAL;
@@ -561,8 +620,22 @@ int wcd934x_get_codec_info(struct wcd9xxx *wcd9xxx,
dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n",
__func__, id_major, id_minor);
- /* Version detection */
- version = 1.0;
+ if (id_major != TAVIL_MAJOR)
+ goto version_unknown;
+
+ /*
+ * As fine version info cannot be retrieved before tavil probe.
+ * Assign coarse versions for possible future use before tavil probe.
+ */
+ if (id_minor == cpu_to_le16(0))
+ version = TAVIL_VERSION_1_0;
+ else if (id_minor == cpu_to_le16(0x01))
+ version = TAVIL_VERSION_1_1;
+
+version_unknown:
+ if (version < 0)
+ dev_err(wcd9xxx->dev, "%s: wcd934x version unknown\n",
+ __func__);
/* Fill codec type info */
wcd_type->id_major = id_major;
@@ -720,7 +793,7 @@ void *tavil_get_afe_config(struct snd_soc_codec *codec,
case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
return &tavil_slimbus_slave_port_cfg;
case AFE_AANC_VERSION:
- return NULL;
+ return &tavil_cdc_aanc_version;
case AFE_CDC_REGISTER_PAGE_CONFIG:
return &tavil_cdc_reg_page_cfg;
default:
@@ -731,6 +804,37 @@ void *tavil_get_afe_config(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL(tavil_get_afe_config);
+static bool is_tavil_playback_dai(int dai_id)
+{
+ if ((dai_id == AIF1_PB) || (dai_id == AIF2_PB) ||
+ (dai_id == AIF3_PB) || (dai_id == AIF4_PB))
+ return true;
+
+ return false;
+}
+
+static int tavil_find_playback_dai_id_for_port(int port_id,
+ struct tavil_priv *tavil)
+{
+ struct wcd9xxx_codec_dai_data *dai;
+ struct wcd9xxx_ch *ch;
+ int i, slv_port_id;
+
+ for (i = AIF1_PB; i < NUM_CODEC_DAIS; i++) {
+ if (!is_tavil_playback_dai(i))
+ continue;
+
+ dai = &tavil->dai[i];
+ list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+ slv_port_id = wcd9xxx_get_slave_port(ch->ch_num);
+ if ((slv_port_id > 0) && (slv_port_id == port_id))
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
static void tavil_vote_svs(struct tavil_priv *tavil, bool vote)
{
struct wcd9xxx *wcd9xxx;
@@ -761,6 +865,212 @@ done:
mutex_unlock(&tavil->svs_mutex);
}
+static int tavil_get_anc_slot(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tavil->anc_slot;
+ return 0;
+}
+
+static int tavil_put_anc_slot(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ tavil->anc_slot = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int tavil_get_anc_func(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = (tavil->anc_func == true ? 1 : 0);
+ return 0;
+}
+
+static int tavil_put_anc_func(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ mutex_lock(&tavil->codec_mutex);
+ tavil->anc_func = (!ucontrol->value.integer.value[0] ? false : true);
+ dev_dbg(codec->dev, "%s: anc_func %x", __func__, tavil->anc_func);
+
+ if (tavil->anc_func == true) {
+ snd_soc_dapm_enable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_enable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA");
+ snd_soc_dapm_disable_pin(dapm, "EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "EAR");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA");
+ snd_soc_dapm_enable_pin(dapm, "EAR PA");
+ snd_soc_dapm_enable_pin(dapm, "EAR");
+ }
+ mutex_unlock(&tavil->codec_mutex);
+
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static int tavil_codec_enable_anc(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);
+ const char *filename;
+ const struct firmware *fw;
+ int i;
+ int ret = 0;
+ int num_anc_slots;
+ struct wcd9xxx_anc_header *anc_head;
+ struct firmware_cal *hwdep_cal = NULL;
+ u32 anc_writes_size = 0;
+ u32 anc_cal_size = 0;
+ int anc_size_remaining;
+ u32 *anc_ptr;
+ u16 reg;
+ u8 mask, val;
+ size_t cal_size;
+ const void *data;
+
+ if (!tavil->anc_func)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_ANC_CAL);
+ if (hwdep_cal) {
+ data = hwdep_cal->data;
+ cal_size = hwdep_cal->size;
+ dev_dbg(codec->dev, "%s: using hwdep calibration, cal_size %zd",
+ __func__, cal_size);
+ } else {
+ filename = "WCD934X/WCD934X_anc.bin";
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(codec->dev, "%s: Failed to acquire ANC data: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ if (!fw) {
+ dev_err(codec->dev, "%s: Failed to get anc fw\n",
+ __func__);
+ return -ENODEV;
+ }
+ data = fw->data;
+ cal_size = fw->size;
+ dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+ __func__);
+ }
+ if (cal_size < sizeof(struct wcd9xxx_anc_header)) {
+ dev_err(codec->dev, "%s: Invalid cal_size %zd\n",
+ __func__, cal_size);
+ ret = -EINVAL;
+ goto err;
+ }
+ /* First number is the number of register writes */
+ anc_head = (struct wcd9xxx_anc_header *)(data);
+ anc_ptr = (u32 *)(data + sizeof(struct wcd9xxx_anc_header));
+ anc_size_remaining = cal_size -
+ sizeof(struct wcd9xxx_anc_header);
+ num_anc_slots = anc_head->num_anc_slots;
+
+ if (tavil->anc_slot >= num_anc_slots) {
+ dev_err(codec->dev, "%s: Invalid ANC slot selected\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ for (i = 0; i < num_anc_slots; i++) {
+ if (anc_size_remaining < WCD934X_PACKED_REG_SIZE) {
+ dev_err(codec->dev, "%s: Invalid register format\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ anc_writes_size = (u32)(*anc_ptr);
+ anc_size_remaining -= sizeof(u32);
+ anc_ptr += 1;
+
+ if ((anc_writes_size * WCD934X_PACKED_REG_SIZE) >
+ anc_size_remaining) {
+ dev_err(codec->dev, "%s: Invalid register format\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (tavil->anc_slot == i)
+ break;
+
+ anc_size_remaining -= (anc_writes_size *
+ WCD934X_PACKED_REG_SIZE);
+ anc_ptr += anc_writes_size;
+ }
+ if (i == num_anc_slots) {
+ dev_err(codec->dev, "%s: Selected ANC slot not present\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ anc_cal_size = anc_writes_size;
+ for (i = 0; i < anc_writes_size; i++) {
+ WCD934X_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val);
+ snd_soc_write(codec, reg, (val & mask));
+ }
+
+ 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:
+ if (!strcmp(w->name, "ANC EAR PA") ||
+ !strcmp(w->name, "ANC SPK1 PA")) {
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL,
+ 0x30, 0x00);
+ msleep(50);
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC0_CLK_RESET_CTL,
+ 0x38, 0x38);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC0_CLK_RESET_CTL,
+ 0x07, 0x00);
+ snd_soc_update_bits(codec,
+ WCD934X_CDC_ANC0_CLK_RESET_CTL,
+ 0x38, 0x00);
+ }
+ break;
+ }
+
+ return 0;
+err:
+ if (!hwdep_cal)
+ release_firmware(fw);
+ return ret;
+}
+
static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1353,10 +1663,40 @@ static int tavil_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
return 0;
}
+static int tavil_codec_enable_spkr_anc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ if (!tavil->anc_func)
+ return 0;
+
+ dev_dbg(codec->dev, "%s: w: %s event: %d anc: %d\n", __func__,
+ w->name, event, tavil->anc_func);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0,
+ 0x10, 0x10);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0,
+ 0x10, 0x00);
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ break;
+ }
+ return ret;
+}
+
static int tavil_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
+ int ret = 0;
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
@@ -1377,11 +1717,43 @@ static int tavil_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
WCD934X_CDC_RX0_RX_PATH_MIX_CTL,
0x10, 0x00);
break;
- default:
+ case SND_SOC_DAPM_POST_PMD:
+ /*
+ * 5ms sleep is required after PA is disabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5500);
+
+ if (!(strcmp(w->name, "ANC EAR PA"))) {
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+ snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0,
+ 0x10, 0x00);
+ }
break;
};
- return 0;
+ return ret;
+}
+
+static void tavil_codec_override(struct snd_soc_codec *codec, int mode,
+ int event)
+{
+ if (mode == CLS_AB || mode == CLS_AB_HIFI) {
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!(snd_soc_read(codec,
+ WCD934X_CDC_RX2_RX_PATH_CTL) & 0x10) &&
+ (!(snd_soc_read(codec,
+ WCD934X_CDC_RX1_RX_PATH_CTL) & 0x10)))
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00);
+ break;
+ }
+ }
}
static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
@@ -1396,6 +1768,8 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, (0x03 << 1));
set_bit(HPH_PA_DELAY, &tavil->status_mask);
break;
case SND_SOC_DAPM_POST_PMU:
@@ -1410,6 +1784,9 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
/* Remove mute */
snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL,
0x10, 0x00);
+ /* Enable GM3 boost */
+ snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL,
+ 0x80, 0x80);
/* Enable AutoChop timer at the end of power up */
snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
0x02, 0x02);
@@ -1427,6 +1804,7 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
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:
/* Enable DSD Mute before PA disable */
@@ -1436,6 +1814,9 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
0x04, 0x04);
break;
case SND_SOC_DAPM_POST_PMD:
+ tavil_codec_override(codec, tavil->hph_mode, event);
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, 0x0);
/* 5ms sleep is required after PA disable */
usleep_range(5000, 5100);
break;
@@ -1456,6 +1837,8 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, (0x03 << 1));
set_bit(HPH_PA_DELAY, &tavil->status_mask);
break;
case SND_SOC_DAPM_POST_PMU:
@@ -1470,6 +1853,12 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
/* Remove Mute on primary path */
snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL,
0x10, 0x00);
+ /* Enable GM3 boost */
+ snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL,
+ 0x80, 0x80);
+ /* Enable AutoChop timer at the end of power up */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ 0x02, 0x02);
/* Remove mix path mute if it is enabled */
if ((snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_MIX_CTL)) &
0x10)
@@ -1484,6 +1873,7 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
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:
/* Enable DSD Mute before PA disable */
@@ -1493,6 +1883,9 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
0x04, 0x04);
break;
case SND_SOC_DAPM_POST_PMD:
+ tavil_codec_override(codec, tavil->hph_mode, event);
+ snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+ 0x06, 0x0);
/* 5ms sleep is required after PA disable */
usleep_range(5000, 5100);
break;
@@ -1550,6 +1943,7 @@ static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
+ int ret = 0;
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
@@ -1560,10 +1954,17 @@ static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
/* Disable AutoChop timer during power up */
snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
0x02, 0x00);
+
+ if (tavil->anc_func)
+ ret = tavil_codec_enable_anc(w, kcontrol, event);
+
wcd_clsh_fsm(codec, &tavil->clsh_d,
WCD_CLSH_EVENT_PRE_DAC,
WCD_CLSH_STATE_EAR,
CLS_H_NORMAL);
+ if (tavil->anc_func)
+ snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0,
+ 0x10, 0x10);
break;
case SND_SOC_DAPM_POST_PMD:
wcd_clsh_fsm(codec, &tavil->clsh_d,
@@ -1575,7 +1976,7 @@ static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
break;
};
- return 0;
+ return ret;
}
static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
@@ -1602,9 +2003,17 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
__func__, hph_mode);
return -EINVAL;
}
+ if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+ /* Ripple freq control enable */
+ snd_soc_update_bits(codec,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ 0x01, 0x01);
/* Disable AutoChop timer during power up */
snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
- 0x02, 0x00);
+ 0x02, 0x00);
+ /* Set RDAC gain */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x40);
if (dsd_conf &&
(snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
@@ -1613,8 +2022,7 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
wcd_clsh_fsm(codec, &tavil->clsh_d,
WCD_CLSH_EVENT_PRE_DAC,
WCD_CLSH_STATE_HPHR,
- ((hph_mode == CLS_H_LOHIFI) ?
- CLS_H_HIFI : hph_mode));
+ hph_mode);
break;
case SND_SOC_DAPM_POST_PMD:
/* 1000us required as per HW requirement */
@@ -1622,8 +2030,15 @@ static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
wcd_clsh_fsm(codec, &tavil->clsh_d,
WCD_CLSH_EVENT_POST_PA,
WCD_CLSH_STATE_HPHR,
- ((hph_mode == CLS_H_LOHIFI) ?
- CLS_H_HIFI : hph_mode));
+ hph_mode);
+ if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+ /* Ripple freq control disable */
+ snd_soc_update_bits(codec,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ 0x01, 0x0);
+ /* Re-set RDAC gain */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x0);
break;
default:
break;
@@ -1657,6 +2072,17 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
__func__, hph_mode);
return -EINVAL;
}
+ if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+ /* Ripple freq control enable */
+ snd_soc_update_bits(codec,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ 0x01, 0x01);
+ /* Disable AutoChop timer during power up */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+ 0x02, 0x00);
+ /* Set RDAC gain */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x40);
if (dsd_conf &&
(snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
hph_mode = CLS_H_HIFI;
@@ -1664,8 +2090,7 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
wcd_clsh_fsm(codec, &tavil->clsh_d,
WCD_CLSH_EVENT_PRE_DAC,
WCD_CLSH_STATE_HPHL,
- ((hph_mode == CLS_H_LOHIFI) ?
- CLS_H_HIFI : hph_mode));
+ hph_mode);
break;
case SND_SOC_DAPM_POST_PMD:
/* 1000us required as per HW requirement */
@@ -1673,8 +2098,15 @@ static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
wcd_clsh_fsm(codec, &tavil->clsh_d,
WCD_CLSH_EVENT_POST_PA,
WCD_CLSH_STATE_HPHL,
- ((hph_mode == CLS_H_LOHIFI) ?
- CLS_H_HIFI : hph_mode));
+ hph_mode);
+ if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+ /* Ripple freq control disable */
+ snd_soc_update_bits(codec,
+ WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+ 0x01, 0x0);
+ /* Re-set RDAC gain */
+ snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+ 0xF0, 0x0);
break;
default:
break;
@@ -2207,6 +2639,46 @@ static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w,
return 0;
}
+static void tavil_codec_hphdelay_lutbypass(struct snd_soc_codec *codec,
+ u16 interp_idx, int event)
+{
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ u8 hph_dly_mask;
+ u16 hph_lut_bypass_reg = 0;
+ u16 hph_comp_ctrl7 = 0;
+
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hph_dly_mask = 1;
+ hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT;
+ hph_comp_ctrl7 = WCD934X_CDC_COMPANDER1_CTL7;
+ break;
+ case INTERP_HPHR:
+ hph_dly_mask = 2;
+ hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT;
+ hph_comp_ctrl7 = WCD934X_CDC_COMPANDER2_CTL7;
+ break;
+ default:
+ break;
+ }
+
+ if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0,
+ hph_dly_mask, 0x0);
+ snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x80);
+ if (tavil->hph_mode == CLS_H_ULP)
+ snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x20);
+ }
+
+ if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0,
+ hph_dly_mask, hph_dly_mask);
+ snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00);
+ snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x0);
+ }
+}
+
static void tavil_codec_hd2_control(struct snd_soc_codec *codec,
u16 interp_idx, int event)
{
@@ -2225,18 +2697,68 @@ static void tavil_codec_hd2_control(struct snd_soc_codec *codec,
}
if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
- snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10);
- snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01);
+ snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x14);
snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04);
}
if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00);
- snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00);
snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00);
}
}
+static int tavil_codec_config_ear_spkr_gain(struct snd_soc_codec *codec,
+ int event, int gain_reg)
+{
+ int comp_gain_offset, val;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ switch (tavil->swr.spkr_mode) {
+ /* Compander gain in SPKR_MODE1 case is 12 dB */
+ case WCD934X_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 (tavil->comp_enabled[COMPANDER_7] &&
+ (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) &&
+ (tavil->ear_spkr_gain != 0)) {
+ /* For example, val is -8(-12+5-1) for 4dB of gain */
+ val = comp_gain_offset + tavil->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 (tavil->comp_enabled[COMPANDER_7] &&
+ (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL ||
+ gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) &&
+ (tavil->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 tavil_config_compander(struct snd_soc_codec *codec, int interp_n,
int event)
{
@@ -2267,7 +2789,9 @@ static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n,
}
if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00);
snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04);
+ snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00);
snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00);
@@ -2276,6 +2800,36 @@ static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n,
return 0;
}
+static void tavil_codec_idle_detect_control(struct snd_soc_codec *codec,
+ int interp, int event)
+{
+ int reg = 0, mask, val;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ if (!tavil->idle_det_cfg.hph_idle_detect_en)
+ return;
+
+ if (interp == INTERP_HPHL) {
+ reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL;
+ mask = 0x01;
+ val = 0x01;
+ }
+ if (interp == INTERP_HPHR) {
+ reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL;
+ mask = 0x02;
+ val = 0x02;
+ }
+
+ if (reg && SND_SOC_DAPM_EVENT_ON(event))
+ snd_soc_update_bits(codec, reg, mask, val);
+
+ if (reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_update_bits(codec, reg, mask, 0x00);
+ tavil->idle_det_cfg.hph_idle_thr = 0;
+ snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, 0x0);
+ }
+}
+
/**
* tavil_codec_enable_interp_clk - Enable main path Interpolator
* clock.
@@ -2305,7 +2859,11 @@ int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, main_reg, 0x10, 0x10);
/* Clk enable */
snd_soc_update_bits(codec, main_reg, 0x20, 0x20);
+ tavil_codec_idle_detect_control(codec, interp_idx,
+ event);
tavil_codec_hd2_control(codec, interp_idx, event);
+ tavil_codec_hphdelay_lutbypass(codec, interp_idx,
+ event);
tavil_config_compander(codec, interp_idx, event);
}
tavil->main_clk_users[interp_idx]++;
@@ -2316,7 +2874,11 @@ int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
if (tavil->main_clk_users[interp_idx] <= 0) {
tavil->main_clk_users[interp_idx] = 0;
tavil_config_compander(codec, interp_idx, event);
+ tavil_codec_hphdelay_lutbypass(codec, interp_idx,
+ event);
tavil_codec_hd2_control(codec, interp_idx, event);
+ tavil_codec_idle_detect_control(codec, interp_idx,
+ event);
/* Clk Disable */
snd_soc_update_bits(codec, main_reg, 0x20, 0x00);
/* Reset enable and disable */
@@ -2334,6 +2896,110 @@ int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL(tavil_codec_enable_interp_clk);
+static int tavil_codec_set_idle_detect_thr(struct snd_soc_codec *codec,
+ int interp, int path_type)
+{
+ int port_id[4] = { 0, 0, 0, 0 };
+ int *port_ptr, num_ports;
+ int bit_width = 0, i;
+ int mux_reg, mux_reg_val;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int dai_id, idle_thr;
+
+ if ((interp != INTERP_HPHL) && (interp != INTERP_HPHR))
+ return 0;
+
+ if (!tavil->idle_det_cfg.hph_idle_detect_en)
+ return 0;
+
+ port_ptr = &port_id[0];
+ num_ports = 0;
+
+ /*
+ * Read interpolator MUX input registers and find
+ * which slimbus port is connected and store the port
+ * numbers in port_id array.
+ */
+ if (path_type == INTERP_MIX_PATH) {
+ mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1 +
+ 2 * (interp - 1);
+ mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f;
+
+ if ((mux_reg_val >= INTn_2_INP_SEL_RX0) &&
+ (mux_reg_val < INTn_2_INP_SEL_PROXIMITY)) {
+ *port_ptr++ = mux_reg_val +
+ WCD934X_RX_PORT_START_NUMBER - 1;
+ num_ports++;
+ }
+ }
+
+ if (path_type == INTERP_MAIN_PATH) {
+ mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0 +
+ 2 * (interp - 1);
+ mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f;
+ i = WCD934X_INTERP_MUX_NUM_INPUTS;
+
+ while (i) {
+ if ((mux_reg_val >= INTn_1_INP_SEL_RX0) &&
+ (mux_reg_val <= INTn_1_INP_SEL_RX7)) {
+ *port_ptr++ = mux_reg_val +
+ WCD934X_RX_PORT_START_NUMBER -
+ INTn_1_INP_SEL_RX0;
+ num_ports++;
+ }
+ mux_reg_val = (snd_soc_read(codec, mux_reg) &
+ 0xf0) >> 4;
+ mux_reg += 1;
+ i--;
+ }
+ }
+
+ dev_dbg(codec->dev, "%s: num_ports: %d, ports[%d %d %d %d]\n",
+ __func__, num_ports, port_id[0], port_id[1],
+ port_id[2], port_id[3]);
+
+ i = 0;
+ while (num_ports) {
+ dai_id = tavil_find_playback_dai_id_for_port(port_id[i++],
+ tavil);
+
+ if ((dai_id >= 0) && (dai_id < NUM_CODEC_DAIS)) {
+ dev_dbg(codec->dev, "%s: dai_id: %d bit_width: %d\n",
+ __func__, dai_id,
+ tavil->dai[dai_id].bit_width);
+
+ if (tavil->dai[dai_id].bit_width > bit_width)
+ bit_width = tavil->dai[dai_id].bit_width;
+ }
+
+ num_ports--;
+ }
+
+ switch (bit_width) {
+ case 16:
+ idle_thr = 0xff; /* F16 */
+ break;
+ case 24:
+ case 32:
+ idle_thr = 0x03; /* F22 */
+ break;
+ default:
+ idle_thr = 0x00;
+ break;
+ }
+
+ dev_dbg(codec->dev, "%s: (new) idle_thr: %d, (cur) idle_thr: %d\n",
+ __func__, idle_thr, tavil->idle_det_cfg.hph_idle_thr);
+
+ if ((tavil->idle_det_cfg.hph_idle_thr == 0) ||
+ (idle_thr < tavil->idle_det_cfg.hph_idle_thr)) {
+ snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, idle_thr);
+ tavil->idle_det_cfg.hph_idle_thr = idle_thr;
+ }
+
+ return 0;
+}
+
static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
@@ -2361,6 +3027,8 @@ static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ tavil_codec_set_idle_detect_thr(codec, w->shift,
+ INTERP_MIX_PATH);
tavil_codec_enable_interp_clk(codec, event, w->shift);
/* Clk enable */
snd_soc_update_bits(codec, mix_reg, 0x20, 0x20);
@@ -2387,6 +3055,7 @@ static int tavil_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);
+ tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
break;
case SND_SOC_DAPM_POST_PMD:
/* Clk Disable */
@@ -2417,6 +3086,7 @@ static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
val += offset_val;
snd_soc_write(codec, gain_reg, val);
}
+ tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
break;
};
dev_dbg(codec->dev, "%s event %d name %s\n", __func__, event, w->name);
@@ -2474,6 +3144,8 @@ static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ tavil_codec_set_idle_detect_thr(codec, w->shift,
+ INTERP_MAIN_PATH);
tavil_codec_enable_interp_clk(codec, event, w->shift);
break;
case SND_SOC_DAPM_POST_PMU:
@@ -2499,6 +3171,7 @@ static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w,
val = snd_soc_read(codec, gain_reg);
val += offset_val;
snd_soc_write(codec, gain_reg, val);
+ tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
break;
case SND_SOC_DAPM_POST_PMD:
tavil_codec_enable_interp_clk(codec, event, w->shift);
@@ -2524,6 +3197,7 @@ static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w,
val += offset_val;
snd_soc_write(codec, gain_reg, val);
}
+ tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
break;
};
@@ -2898,6 +3572,9 @@ static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_POST_PMD:
snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00);
+ snd_soc_update_bits(codec, dec_cfg_reg,
+ WCD934X_DEC_PWR_LVL_MASK,
+ WCD934X_DEC_PWR_LVL_DF);
break;
};
out:
@@ -2905,6 +3582,56 @@ out:
return ret;
}
+static u32 tavil_get_dmic_sample_rate(struct snd_soc_codec *codec,
+ unsigned int dmic,
+ struct wcd9xxx_pdata *pdata)
+{
+ u8 tx_stream_fs;
+ u8 adc_mux_index = 0, adc_mux_sel = 0;
+ bool dec_found = false;
+ u16 adc_mux_ctl_reg, tx_fs_reg;
+ u32 dmic_fs;
+
+ while (dec_found == 0 && adc_mux_index < WCD934X_MAX_VALID_ADC_MUX) {
+ if (adc_mux_index < 4) {
+ adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+ (adc_mux_index * 2);
+ } else if (adc_mux_index < WCD934X_INVALID_ADC_MUX) {
+ adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+ adc_mux_index - 4;
+ } else if (adc_mux_index == WCD934X_INVALID_ADC_MUX) {
+ ++adc_mux_index;
+ continue;
+ }
+ adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) &
+ 0xF8) >> 3) - 1;
+
+ if (adc_mux_sel == dmic) {
+ dec_found = true;
+ break;
+ }
+
+ ++adc_mux_index;
+ }
+
+ if (dec_found && adc_mux_index <= 8) {
+ tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index);
+ tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F;
+ if (tx_stream_fs <= 4) {
+ if (pdata->dmic_sample_rate <=
+ WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ)
+ dmic_fs = pdata->dmic_sample_rate;
+ else
+ dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ;
+ } else
+ dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+ } else {
+ dmic_fs = pdata->dmic_sample_rate;
+ }
+
+ return dmic_fs;
+}
+
static u8 tavil_get_dmic_clk_val(struct snd_soc_codec *codec,
u32 mclk_rate, u32 dmic_clk_rate)
{
@@ -2988,6 +3715,7 @@ static int tavil_codec_enable_dmic(struct snd_soc_dapm_widget *w,
s32 *dmic_clk_cnt;
u8 dmic_rate_val, dmic_rate_shift = 1;
unsigned int dmic;
+ u32 dmic_sample_rate;
int ret;
char *wname;
@@ -3030,10 +3758,12 @@ static int tavil_codec_enable_dmic(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ dmic_sample_rate = tavil_get_dmic_sample_rate(codec, dmic,
+ pdata);
dmic_rate_val =
tavil_get_dmic_clk_val(codec,
pdata->mclk_rate,
- pdata->dmic_sample_rate);
+ dmic_sample_rate);
(*dmic_clk_cnt)++;
if (*dmic_clk_cnt == 1) {
@@ -3296,6 +4026,58 @@ static int __tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w,
return 0;
}
+/*
+ * tavil_codec_enable_standalone_micbias - enable micbias standalone
+ * @codec: pointer to codec instance
+ * @micb_num: number of micbias to be enabled
+ * @enable: true to enable micbias or false to disable
+ *
+ * This function is used to enable micbias (1, 2, 3 or 4) during
+ * standalone independent of whether TX use-case is running or not
+ *
+ * Return: error code in case of failure or 0 for success
+ */
+int tavil_codec_enable_standalone_micbias(struct snd_soc_codec *codec,
+ int micb_num,
+ bool enable)
+{
+ const char * const micb_names[] = {
+ DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE,
+ DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE
+ };
+ int micb_index = micb_num - 1;
+ int rc;
+
+ if (!codec) {
+ pr_err("%s: Codec memory is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) {
+ dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+ __func__, micb_index);
+ return -EINVAL;
+ }
+
+ if (enable)
+ rc = snd_soc_dapm_force_enable_pin_unlocked(
+ 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]);
+
+ if (!rc)
+ snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+ else
+ dev_err(codec->dev, "%s: micbias%d force %s pin failed\n",
+ __func__, micb_num, (enable ? "enable" : "disable"));
+
+ return rc;
+}
+EXPORT_SYMBOL(tavil_codec_enable_standalone_micbias);
+
static int tavil_codec_force_enable_micbias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
@@ -3577,37 +4359,49 @@ static int tavil_compander_put(struct snd_kcontrol *kcontrol,
return 0;
}
+static int tavil_hph_idle_detect_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ int val = 0;
+
+ if (tavil)
+ val = tavil->idle_det_cfg.hph_idle_detect_en;
+
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+
+static int tavil_hph_idle_detect_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ if (tavil)
+ tavil->idle_det_cfg.hph_idle_detect_en =
+ ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
static int tavil_dmic_pin_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- u16 ctl_reg;
+ u16 dmic_pin;
u8 reg_val, pinctl_position;
pinctl_position = ((struct soc_multi_mixer_control *)
kcontrol->private_value)->shift;
- switch (pinctl_position >> 3) {
- case 0:
- ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_0;
- break;
- case 1:
- ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_1;
- break;
- case 2:
- ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_2;
- break;
- case 3:
- ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_3;
- break;
- default:
- dev_err(codec->dev, "%s: Invalid pinctl position = %d\n",
- __func__, pinctl_position);
- return -EINVAL;
- }
- reg_val = snd_soc_read(codec, ctl_reg);
- reg_val = (reg_val >> (pinctl_position & 0x07)) & 0x1;
- ucontrol->value.integer.value[0] = reg_val;
+ dmic_pin = pinctl_position & 0x07;
+ reg_val = snd_soc_read(codec,
+ WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1);
+
+ ucontrol->value.integer.value[0] = !!reg_val;
return 0;
}
@@ -3616,10 +4410,11 @@ static int tavil_dmic_pin_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- u16 ctl_reg, cfg_reg;
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ u16 ctl_reg, cfg_reg, dmic_pin;
u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask;
- /* 1- high or low; 0- high Z */
+ /* 0- high or low; 1- high Z */
pinctl_mode = ucontrol->value.integer.value[0];
pinctl_position = ((struct soc_multi_mixer_control *)
kcontrol->private_value)->shift;
@@ -3643,16 +4438,20 @@ static int tavil_dmic_pin_mode_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
- ctl_val = pinctl_mode << (pinctl_position & 0x07);
+ ctl_val = ~(pinctl_mode << (pinctl_position & 0x07));
mask = 1 << (pinctl_position & 0x07);
snd_soc_update_bits(codec, ctl_reg, mask, ctl_val);
- cfg_reg = WCD934X_TLMM_BIST_MODE_PINCFG + pinctl_position;
- if (!pinctl_mode)
- cfg_val = 0x4;
- else
+ dmic_pin = pinctl_position & 0x07;
+ cfg_reg = WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1;
+ if (pinctl_mode) {
+ if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ cfg_val = 0x6;
+ else
+ cfg_val = 0xD;
+ } else
cfg_val = 0;
- snd_soc_update_bits(codec, cfg_reg, 0x07, cfg_val);
+ snd_soc_update_bits(codec, cfg_reg, 0x1F, cfg_val);
dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n",
__func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val);
@@ -3866,6 +4665,76 @@ static int tavil_ear_pa_gain_put(struct snd_kcontrol *kcontrol,
return 0;
}
+static int tavil_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 tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tavil->ear_spkr_gain;
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int tavil_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 tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ tavil->ear_spkr_gain = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: gain = %d\n", __func__, tavil->ear_spkr_gain);
+
+ return 0;
+}
+
+static int tavil_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = tavil->hph_mode;
+ return 0;
+}
+
+static int tavil_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+ u32 mode_val;
+
+ mode_val = ucontrol->value.enumerated.item[0];
+
+ dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val);
+
+ if (mode_val == 0) {
+ dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H LOHiFi\n",
+ __func__);
+ mode_val = CLS_H_LOHIFI;
+ }
+ tavil->hph_mode = mode_val;
+ return 0;
+}
+
+static const char * const rx_hph_mode_mux_text[] = {
+ "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI",
+ "CLS_H_ULP", "CLS_AB_HIFI",
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+ rx_hph_mode_mux_text);
+
+static const char *const tavil_anc_func_text[] = {"OFF", "ON"};
+static const struct soc_enum tavil_anc_func_enum =
+ SOC_ENUM_SINGLE_EXT(2, tavil_anc_func_text);
+
/* Cutoff frequency for high pass filter */
static const char * const cf_text[] = {
"CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ"
@@ -3880,13 +4749,25 @@ static const char * const amic_pwr_lvl_text[] = {
"LOW_PWR", "DEFAULT", "HIGH_PERF"
};
+static const char * const hph_idle_detect_text[] = {
+ "OFF", "ON"
+};
+
static const char * const tavil_ear_pa_gain_text[] = {
"G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB",
"G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB"
};
+static const char * const tavil_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 SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_pa_gain_enum, tavil_ear_pa_gain_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_spkr_pa_gain_enum,
+ tavil_ear_spkr_pa_gain_text);
static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text);
+static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text);
static SOC_ENUM_SINGLE_DECL(cf_dec0_enum, WCD934X_CDC_TX0_TX_PATH_CFG0, 5,
cf_text);
static SOC_ENUM_SINGLE_DECL(cf_dec1_enum, WCD934X_CDC_TX1_TX_PATH_CFG0, 5,
@@ -3937,6 +4818,8 @@ static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 2,
static const struct snd_kcontrol_new tavil_snd_controls[] = {
SOC_ENUM_EXT("EAR PA Gain", tavil_ear_pa_gain_enum,
tavil_ear_pa_gain_get, tavil_ear_pa_gain_put),
+ SOC_ENUM_EXT("EAR SPKR PA Gain", tavil_ear_spkr_pa_gain_enum,
+ tavil_ear_spkr_pa_gain_get, tavil_ear_spkr_pa_gain_put),
SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 20, 1, line_gain),
SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 20, 1, line_gain),
SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER,
@@ -4017,6 +4900,11 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = {
WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, 40,
digital_gain),
+ SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tavil_get_anc_slot,
+ tavil_put_anc_slot),
+ SOC_ENUM_EXT("ANC Function", tavil_anc_func_enum, tavil_get_anc_func,
+ tavil_put_anc_func),
+
SOC_ENUM("TX0 HPF cut off", cf_dec0_enum),
SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
@@ -4042,6 +4930,9 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = {
SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum),
SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum),
+ SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+ tavil_rx_hph_mode_get, tavil_rx_hph_mode_put),
+
SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0,
tavil_iir_enable_audio_mixer_get,
tavil_iir_enable_audio_mixer_put),
@@ -4107,6 +4998,9 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = {
SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0,
tavil_compander_get, tavil_compander_put),
+ SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum,
+ tavil_hph_idle_detect_get, tavil_hph_idle_detect_put),
+
SOC_ENUM_EXT("MAD Input", tavil_conn_mad_enum,
tavil_mad_input_get, tavil_mad_input_put),
@@ -4382,6 +5276,15 @@ static const char * const amic4_5_sel_text[] = {
"AMIC4", "AMIC5"
};
+static const char * const anc0_fb_mux_text[] = {
+ "ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR",
+ "ANC_IN_LO1"
+};
+
+static const char * const anc1_fb_mux_text[] = {
+ "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2"
+};
+
static const char * const rx_echo_mux_text[] = {
"ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4",
"RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8"
@@ -4833,6 +5736,18 @@ WCD_DAPM_ENUM(int4_2_native, SND_SOC_NOPM, 0, native_mux_text);
WCD_DAPM_ENUM(int7_2_native, SND_SOC_NOPM, 0, native_mux_text);
WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(anc0_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text);
+WCD_DAPM_ENUM(anc1_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text);
+
+static const struct snd_kcontrol_new anc_ear_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_ear_spkr_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_spkr_pa_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
static const struct snd_kcontrol_new mad_cpe1_switch =
SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
@@ -5203,6 +6118,9 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
WCD_DAPM_MUX("AMIC4_5 SEL", 0, tx_amic4_5),
+ WCD_DAPM_MUX("ANC0 FB MUX", 0, anc0_fb),
+ WCD_DAPM_MUX("ANC1 FB MUX", 0, anc1_fb),
+
SND_SOC_DAPM_INPUT("AMIC1"),
SND_SOC_DAPM_INPUT("AMIC2"),
SND_SOC_DAPM_INPUT("AMIC3"),
@@ -5434,6 +6352,12 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0,
tavil_codec_enable_lineout_pa,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0,
+ tavil_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tavil_codec_enable_spkr_anc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_OUTPUT("EAR"),
SND_SOC_DAPM_OUTPUT("HPHL"),
@@ -5442,6 +6366,14 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("LINEOUT2"),
SND_SOC_DAPM_OUTPUT("SPK1 OUT"),
SND_SOC_DAPM_OUTPUT("SPK2 OUT"),
+ SND_SOC_DAPM_OUTPUT("ANC EAR"),
+
+ SND_SOC_DAPM_SWITCH("ANC OUT EAR Enable", SND_SOC_NOPM, 0, 0,
+ &anc_ear_switch),
+ SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0,
+ &anc_ear_spkr_switch),
+ SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0,
+ &anc_spkr_pa_switch),
SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0,
tavil_codec_enable_rx_bias,
@@ -6169,6 +7101,104 @@ static int __tavil_cdc_mclk_enable(struct tavil_priv *tavil,
return ret;
}
+static ssize_t tavil_codec_version_read(struct snd_info_entry *entry,
+ void *file_private_data,
+ struct file *file,
+ char __user *buf, size_t count,
+ loff_t pos)
+{
+ struct tavil_priv *tavil;
+ struct wcd9xxx *wcd9xxx;
+ char buffer[TAVIL_VERSION_ENTRY_SIZE];
+ int len = 0;
+
+ tavil = (struct tavil_priv *) entry->private_data;
+ if (!tavil) {
+ pr_err("%s: tavil priv is null\n", __func__);
+ return -EINVAL;
+ }
+
+ wcd9xxx = tavil->wcd9xxx;
+
+ switch (wcd9xxx->version) {
+ case TAVIL_VERSION_WCD9340_1_0:
+ len = snprintf(buffer, sizeof(buffer), "WCD9340_1_0\n");
+ break;
+ case TAVIL_VERSION_WCD9341_1_0:
+ len = snprintf(buffer, sizeof(buffer), "WCD9341_1_0\n");
+ break;
+ case TAVIL_VERSION_WCD9340_1_1:
+ len = snprintf(buffer, sizeof(buffer), "WCD9340_1_1\n");
+ break;
+ case TAVIL_VERSION_WCD9341_1_1:
+ len = snprintf(buffer, sizeof(buffer), "WCD9341_1_1\n");
+ break;
+ default:
+ len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
+ }
+
+ return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static struct snd_info_entry_ops tavil_codec_info_ops = {
+ .read = tavil_codec_version_read,
+};
+
+/*
+ * tavil_codec_info_create_codec_entry - creates wcd934x module
+ * @codec_root: The parent directory
+ * @codec: Codec instance
+ *
+ * Creates wcd934x module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int tavil_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+ struct snd_soc_codec *codec)
+{
+ struct snd_info_entry *version_entry;
+ struct tavil_priv *tavil;
+ struct snd_soc_card *card;
+
+ if (!codec_root || !codec)
+ return -EINVAL;
+
+ tavil = snd_soc_codec_get_drvdata(codec);
+ card = codec->component.card;
+ tavil->entry = snd_register_module_info(codec_root->module,
+ "tavil",
+ codec_root);
+ if (!tavil->entry) {
+ dev_dbg(codec->dev, "%s: failed to create wcd934x entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry = snd_info_create_card_entry(card->snd_card,
+ "version",
+ tavil->entry);
+ if (!version_entry) {
+ dev_dbg(codec->dev, "%s: failed to create wcd934x version entry\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ version_entry->private_data = tavil;
+ version_entry->size = TAVIL_VERSION_ENTRY_SIZE;
+ version_entry->content = SNDRV_INFO_CONTENT_DATA;
+ version_entry->c.ops = &tavil_codec_info_ops;
+
+ if (snd_info_register(version_entry) < 0) {
+ snd_info_free_entry(version_entry);
+ return -ENOMEM;
+ }
+ tavil->version_entry = version_entry;
+
+ return 0;
+}
+EXPORT_SYMBOL(tavil_codec_info_create_codec_entry);
+
/**
* tavil_cdc_mclk_enable - Enable/disable codec mclk
*
@@ -6274,7 +7304,6 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = {
{WCD934X_BIAS_VBG_FINE_ADJ, 0xFF, 0x75},
{WCD934X_CODEC_CPR_SVS_CX_VDD, 0xFF, 0x7C}, /* value in svs mode */
{WCD934X_CODEC_CPR_SVS2_CX_VDD, 0xFF, 0x58}, /* value in svs2 mode */
- {WCD934X_SIDO_NEW_VOUT_D_FREQ2, 0x01, 0x01},
{WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
{WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
{WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
@@ -6289,7 +7318,16 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = {
{WCD934X_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08},
{WCD934X_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12},
{WCD934X_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08},
- {WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x1F, 0x03},
+ {WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x1F, 0x09},
+ {WCD934X_CDC_TX0_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX1_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX2_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX3_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX4_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX5_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00},
+ {WCD934X_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00},
};
static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {
@@ -6306,13 +7344,6 @@ static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {
{WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
{WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
{WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
- {WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 0x01, 0x01},
- {WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 0x01, 0x01},
- {WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 0x01, 0x01},
- {WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 0x01, 0x01},
- {WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 0x01, 0x01},
- {WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 0x01, 0x01},
- {WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 0x01, 0x01},
{WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0x01, 0x01},
{WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x01, 0x01},
{WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80},
@@ -6562,6 +7593,7 @@ static int tavil_handle_pdata(struct tavil_priv *tavil,
{
struct snd_soc_codec *codec = tavil->codec;
u8 mad_dmic_ctl_val;
+ u8 anc_ctl_value;
u32 def_dmic_rate, dmic_clk_drv;
int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4;
int rc = 0;
@@ -6667,6 +7699,20 @@ static int tavil_handle_pdata(struct tavil_priv *tavil,
snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC2_CTL,
0x0E, mad_dmic_ctl_val << 1);
+ if (dmic_clk_drv == WCD934X_DMIC_CLK_DIV_2)
+ anc_ctl_value = WCD934X_ANC_DMIC_X2_FULL_RATE;
+ else
+ anc_ctl_value = WCD934X_ANC_DMIC_X2_HALF_RATE;
+
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL,
+ 0x40, anc_ctl_value << 6);
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL,
+ 0x20, anc_ctl_value << 5);
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL,
+ 0x40, anc_ctl_value << 6);
+ snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL,
+ 0x20, anc_ctl_value << 5);
+
done:
return rc;
}
@@ -6775,6 +7821,7 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
dev_info(codec->dev, "%s()\n", __func__);
tavil = snd_soc_codec_get_drvdata(codec);
+ tavil->intf_type = wcd9xxx_get_intf_type();
/* Resource Manager post Init */
ret = wcd_resmgr_post_init(tavil->resmgr, NULL, codec);
@@ -6785,8 +7832,8 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
}
/* Class-H Init */
wcd_clsh_init(&tavil->clsh_d);
- /* Default HPH Mode to Class-H HiFi */
- tavil->hph_mode = CLS_H_HIFI;
+ /* Default HPH Mode to Class-H Low HiFi */
+ tavil->hph_mode = CLS_H_LOHIFI;
tavil->fw_data = devm_kzalloc(codec->dev, sizeof(*(tavil->fw_data)),
GFP_KERNEL);
@@ -6816,14 +7863,6 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
for (i = 0; i < COMPANDER_MAX; i++)
tavil->comp_enabled[i] = 0;
- dev_dbg(codec->dev, "%s: MCLK Rate = %x\n", __func__,
- control->mclk_rate);
- if (control->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ)
- snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
- 0x03, 0x00);
- else if (control->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ)
- snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
- 0x03, 0x01);
tavil_codec_init_reg(codec);
tavil_enable_sido_buck(codec);
@@ -6888,6 +7927,12 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
if (IS_ERR_OR_NULL(tavil->dsd_config))
dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__);
+ mutex_lock(&tavil->codec_mutex);
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+ snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+ snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA");
+ mutex_unlock(&tavil->codec_mutex);
+
snd_soc_dapm_sync(dapm);
tavil_wdsp_initialize(codec);
@@ -7395,6 +8440,45 @@ static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil)
return rc;
}
+static void ___tavil_get_codec_fine_version(struct tavil_priv *tavil)
+{
+ int val1, val2, version;
+ struct regmap *regmap;
+ u16 id_minor;
+ u32 version_mask = 0;
+
+ regmap = tavil->wcd9xxx->regmap;
+ version = tavil->wcd9xxx->version;
+ id_minor = tavil->wcd9xxx->codec_type->id_minor;
+
+ regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1);
+ regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2);
+
+ dev_dbg(tavil->dev, "%s: chip version :0x%x 0x:%x\n",
+ __func__, val1, val2);
+
+ version_mask |= (!!((u8)val1 & 0x80)) << DSD_DISABLED_MASK;
+ version_mask |= (!!((u8)val2 & 0x01)) << SLNQ_DISABLED_MASK;
+
+ switch (version_mask) {
+ case DSD_DISABLED | SLNQ_DISABLED:
+ if (id_minor == cpu_to_le16(0))
+ version = TAVIL_VERSION_WCD9340_1_0;
+ else if (id_minor == cpu_to_le16(0x01))
+ version = TAVIL_VERSION_WCD9340_1_1;
+ break;
+ case SLNQ_DISABLED:
+ if (id_minor == cpu_to_le16(0))
+ version = TAVIL_VERSION_WCD9341_1_0;
+ else if (id_minor == cpu_to_le16(0x01))
+ version = TAVIL_VERSION_WCD9341_1_1;
+ break;
+ }
+
+ tavil->wcd9xxx->version = version;
+ tavil->wcd9xxx->codec_type->version = version;
+}
+
/*
* tavil_get_wcd_dsp_cntl: Get the reference to wcd_dsp_cntl
* @dev: Device pointer for codec device
@@ -7424,7 +8508,6 @@ static int tavil_probe(struct platform_device *pdev)
struct tavil_priv *tavil;
struct clk *wcd_ext_clk;
struct wcd9xxx_resmgr_v2 *resmgr;
- int val1, val2, val3, val4;
tavil = devm_kzalloc(&pdev->dev, sizeof(struct tavil_priv),
GFP_KERNEL);
@@ -7469,6 +8552,7 @@ static int tavil_probe(struct platform_device *pdev)
tavil->swr.plat_data.bulk_write = tavil_swrm_bulk_write;
tavil->swr.plat_data.clk = tavil_swrm_clock;
tavil->swr.plat_data.handle_irq = tavil_swrm_handle_irq;
+ tavil->swr.spkr_gain_offset = WCD934X_RX_GAIN_OFFSET_0_DB;
/* Register for Clock */
wcd_ext_clk = clk_get(tavil->wcd9xxx->dev, "wcd_clk");
@@ -7480,19 +8564,19 @@ static int tavil_probe(struct platform_device *pdev)
tavil->wcd_ext_clk = wcd_ext_clk;
set_bit(AUDIO_NOMINAL, &tavil->status_mask);
/* Update codec register default values */
+ dev_dbg(&pdev->dev, "%s: MCLK Rate = %x\n", __func__,
+ tavil->wcd9xxx->mclk_rate);
+ if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ)
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+ 0x03, 0x00);
+ else if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ)
+ regmap_update_bits(tavil->wcd9xxx->regmap,
+ WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+ 0x03, 0x01);
tavil_update_reg_defaults(tavil);
__tavil_enable_efuse_sensing(tavil);
-
- regmap_read(tavil->wcd9xxx->regmap,
- WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val1);
- regmap_read(tavil->wcd9xxx->regmap,
- WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val2);
- regmap_read(tavil->wcd9xxx->regmap,
- WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val3);
- regmap_read(tavil->wcd9xxx->regmap,
- WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9, &val4);
- dev_dbg(&pdev->dev, "%s: chip version :0x%x 0x:%x 0x:%x 0x:%x\n",
- __func__, val1, val2, val3, val4);
+ ___tavil_get_codec_fine_version(tavil);
/* Register with soc framework */
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil,
diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/sound/soc/codecs/wcd934x/wcd934x.h
index 9cffa168c298..1f5fda88c4f9 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.h
+++ b/sound/soc/codecs/wcd934x/wcd934x.h
@@ -32,6 +32,9 @@
#define WCD934X_DMIC_CLK_DIV_16 0x5
#define WCD934X_DMIC_CLK_DRIVE_DEFAULT 0x02
+#define WCD934X_ANC_DMIC_X2_FULL_RATE 1
+#define WCD934X_ANC_DMIC_X2_HALF_RATE 0
+
#define TAVIL_MAX_MICBIAS 4
#define TAVIL_NUM_INTERPOLATORS 9
#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64
@@ -39,6 +42,13 @@
/* Convert from vout ctl to micbias voltage in mV */
#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50)
+/* Feature masks to distinguish codec version */
+#define DSD_DISABLED_MASK 0
+#define SLNQ_DISABLED_MASK 1
+
+#define DSD_DISABLED (1 << DSD_DISABLED_MASK)
+#define SLNQ_DISABLED (1 << SLNQ_DISABLED_MASK)
+
/* Number of input and output Slimbus port */
enum {
WCD934X_RX0 = 0,
@@ -180,4 +190,6 @@ extern struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec);
extern int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
int event, int intp_idx);
extern struct tavil_dsd_config *tavil_get_dsd_config(struct snd_soc_codec *);
+extern int tavil_codec_info_create_codec_entry(struct snd_info_entry *,
+ struct snd_soc_codec *);
#endif
diff --git a/sound/soc/codecs/wcd9xxx-common-v2.c b/sound/soc/codecs/wcd9xxx-common-v2.c
index 615922274690..63872bbf540c 100644
--- a/sound/soc/codecs/wcd9xxx-common-v2.c
+++ b/sound/soc/codecs/wcd9xxx-common-v2.c
@@ -198,6 +198,11 @@ void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped, bool reset)
pr_debug("%s, impedance not in range = %d\n", __func__, imped);
return;
}
+ if (index >= ARRAY_SIZE(imped_table)) {
+ pr_debug("%s, impedance index not in range = %d\n", __func__,
+ index);
+ return;
+ }
for (i = 0; i < MAX_IMPED_PARAMS; i++)
snd_soc_update_bits(codec, imped_table[index][i].reg,
imped_table[index][i].mask,
@@ -232,10 +237,16 @@ static const char *mode_to_str(int mode)
return "CLS_H_NORMAL";
case CLS_H_HIFI:
return "CLS_H_HIFI";
+ case CLS_H_LOHIFI:
+ return "CLS_H_LOHIFI";
case CLS_H_LP:
return "CLS_H_LP";
+ case CLS_H_ULP:
+ return "CLS_H_ULP";
case CLS_AB:
return "CLS_AB";
+ case CLS_AB_HIFI:
+ return "CLS_AB_HIFI";
default:
return "CLS_H_INVALID";
};
@@ -327,7 +338,8 @@ static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_data *clsh_d,
static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec,
int mode)
{
- if (mode == CLS_H_HIFI)
+ if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+ mode == CLS_AB_HIFI || mode == CLS_AB)
snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
0x08, 0x08); /* set to HIFI */
else
@@ -338,7 +350,8 @@ static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec,
static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec,
int mode)
{
- if (mode == CLS_H_HIFI)
+ if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+ mode == CLS_AB_HIFI || mode == CLS_AB)
snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
0x04, 0x04); /* set to HIFI */
else
@@ -346,6 +359,55 @@ static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec,
0x04, 0x00); /* set to Default */
}
+static inline void wcd_clsh_gm3_boost_disable(struct snd_soc_codec *codec,
+ int mode)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (!IS_CODEC_TYPE(wcd9xxx, WCD934X))
+ return;
+
+ if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+ mode == CLS_AB_HIFI || mode == CLS_AB) {
+ snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL,
+ 0x80, 0x0); /* disable GM3 Boost */
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
+ 0xF0, 0x80);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL,
+ 0x80, 0x80); /* set to Default */
+ snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
+ 0xF0, 0x70);
+ }
+}
+
+
+static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec,
+ int mode)
+{
+ struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+ if (!IS_CODEC_TYPE(wcd9xxx, WCD934X))
+ return;
+
+ if (mode == CLS_H_LOHIFI || mode == CLS_AB) {
+ snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
+ 0x20, 0x20);
+ snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+ 0xF0, 0xC0);
+ snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
+ 0x0E, 0x02);
+ } else {
+
+ snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
+ 0x20, 0x0);
+ snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+ 0xF0, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
+ 0x0E, 0x06);
+ }
+}
+
static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec,
struct wcd_clsh_cdc_data *clsh_d,
int mode,
@@ -381,7 +443,7 @@ static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec,
(1 << 6), (enable << 6));
/* 100usec delay is needed as per HW requirement */
usleep_range(100, 110);
- if (enable && (TASHA_IS_1_1(wcd9xxx->version))) {
+ if (enable && (TASHA_IS_1_1(wcd9xxx))) {
wcd_clsh_set_flyback_mode(codec, CLS_H_HIFI);
snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN,
0x60, 0x40);
@@ -422,7 +484,7 @@ static void wcd_clsh_set_gain_path(struct snd_soc_codec *codec,
u8 val = 0;
struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
- if (!TASHA_IS_2_0(wcd9xxx->version))
+ if (!TASHA_IS_2_0(wcd9xxx))
return;
switch (mode) {
@@ -465,11 +527,26 @@ static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec,
gain = DAC_GAIN_0DB;
ipeak = DELTA_I_50MA;
break;
+ case CLS_AB_HIFI:
+ val = 0x08;
+ break;
case CLS_H_HIFI:
val = 0x08;
gain = DAC_GAIN_M0P2DB;
ipeak = DELTA_I_50MA;
break;
+ case CLS_H_LOHIFI:
+ val = 0x00;
+ if ((IS_CODEC_TYPE(wcd9xxx, WCD9335)) ||
+ (IS_CODEC_TYPE(wcd9xxx, WCD9326))) {
+ val = 0x08;
+ gain = DAC_GAIN_M0P2DB;
+ ipeak = DELTA_I_50MA;
+ }
+ break;
+ case CLS_H_ULP:
+ val = 0x0C;
+ break;
case CLS_H_LP:
val = 0x04;
ipeak = DELTA_I_30MA;
@@ -478,8 +555,16 @@ static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec,
return;
};
+ /*
+ * For tavil set mode to Lower_power for
+ * CLS_H_LOHIFI and CLS_AB
+ */
+ if ((IS_CODEC_TYPE(wcd9xxx, WCD934X)) &&
+ (mode == CLS_H_LOHIFI || mode == CLS_AB))
+ val = 0x04;
+
snd_soc_update_bits(codec, WCD9XXX_A_ANA_HPH, 0x0C, val);
- if (TASHA_IS_2_0(wcd9xxx->version)) {
+ if (TASHA_IS_2_0(wcd9xxx)) {
snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_VCL_2,
0x30, (res_val << 4));
if (mode != CLS_H_LP)
@@ -510,7 +595,7 @@ static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode)
{
struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
- if (!TASHA_IS_2_0(wcd9xxx->version))
+ if (!TASHA_IS_2_0(wcd9xxx))
return;
snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A);
@@ -533,7 +618,7 @@ static void wcd_clsh_state_lo(struct snd_soc_codec *codec,
dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
is_enable ? "enable" : "disable");
- if (mode != CLS_AB) {
+ if (mode != CLS_AB && mode != CLS_AB_HIFI) {
dev_err(codec->dev, "%s: LO cannot be in this mode: %d\n",
__func__, mode);
return;
@@ -581,7 +666,8 @@ static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec,
WCD_CLSH_STATE_HPHR);
else
return;
- if (hph_mode != CLS_AB && !is_native_44_1_active(codec))
+ if (hph_mode != CLS_AB && hph_mode != CLS_AB_HIFI
+ && !is_native_44_1_active(codec))
snd_soc_update_bits(codec,
WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
0x40, 0x40);
@@ -790,6 +876,7 @@ static void wcd_clsh_state_hph_lo(struct snd_soc_codec *codec,
hph_mode);
if ((hph_mode == CLS_AB) ||
+ (hph_mode == CLS_AB_HIFI) ||
(hph_mode == CLS_NONE))
goto end;
@@ -838,7 +925,7 @@ static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec,
dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
is_enable ? "enable" : "disable");
- if (mode == CLS_AB)
+ if (mode == CLS_AB || mode == CLS_AB_HIFI)
return;
if (is_enable) {
@@ -876,7 +963,7 @@ static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec,
}
if (is_enable) {
- if (mode != CLS_AB) {
+ if (mode != CLS_AB && mode != CLS_AB_HIFI) {
wcd_enable_clsh_block(codec, clsh_d, true);
/*
* These K1 values depend on the Headphone Impedance
@@ -892,6 +979,8 @@ static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec,
}
wcd_clsh_set_buck_regulator_mode(codec, mode);
wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_gm3_boost_disable(codec, mode);
+ wcd_clsh_force_iq_ctl(codec, mode);
wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
wcd_clsh_set_flyback_current(codec, mode);
wcd_clsh_set_buck_mode(codec, mode);
@@ -901,7 +990,7 @@ static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec,
} else {
wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
- if (mode != CLS_AB) {
+ if (mode != CLS_AB && mode != CLS_AB_HIFI) {
snd_soc_update_bits(codec,
WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
0x40, 0x00);
@@ -910,6 +999,8 @@ static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec,
/* buck and flyback set to default mode and disable */
wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL);
+ wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL);
wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL);
@@ -930,7 +1021,7 @@ static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec,
}
if (is_enable) {
- if (mode != CLS_AB) {
+ if (mode != CLS_AB && mode != CLS_AB_HIFI) {
wcd_enable_clsh_block(codec, clsh_d, true);
/*
* These K1 values depend on the Headphone Impedance
@@ -946,6 +1037,8 @@ static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec,
}
wcd_clsh_set_buck_regulator_mode(codec, mode);
wcd_clsh_set_flyback_mode(codec, mode);
+ wcd_clsh_gm3_boost_disable(codec, mode);
+ wcd_clsh_force_iq_ctl(codec, mode);
wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
wcd_clsh_set_flyback_current(codec, mode);
wcd_clsh_set_buck_mode(codec, mode);
@@ -955,7 +1048,7 @@ static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec,
} else {
wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
- if (mode != CLS_AB) {
+ if (mode != CLS_AB && mode != CLS_AB_HIFI) {
snd_soc_update_bits(codec,
WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
0x40, 0x00);
@@ -964,6 +1057,8 @@ static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec,
/* set buck and flyback to Default Mode */
wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+ wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL);
+ wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL);
wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL);
diff --git a/sound/soc/codecs/wcd9xxx-common-v2.h b/sound/soc/codecs/wcd9xxx-common-v2.h
index d3c52e73a7da..ee7e587b3f24 100644
--- a/sound/soc/codecs/wcd9xxx-common-v2.h
+++ b/sound/soc/codecs/wcd9xxx-common-v2.h
@@ -61,8 +61,10 @@ enum {
CLS_H_NORMAL = 0, /* Class-H Default */
CLS_H_HIFI, /* Class-H HiFi */
CLS_H_LP, /* Class-H Low Power */
- CLS_AB, /* Class-AB */
+ CLS_AB, /* Class-AB Low HIFI*/
CLS_H_LOHIFI, /* LoHIFI */
+ CLS_H_ULP, /* Ultra Low power */
+ CLS_AB_HIFI, /* Class-AB */
CLS_NONE, /* None of the above modes */
};
diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c
index 0b4bd3c15127..e9f167fa643b 100644
--- a/sound/soc/codecs/wcd_cpe_core.c
+++ b/sound/soc/codecs/wcd_cpe_core.c
@@ -2051,6 +2051,7 @@ struct wcd_cpe_core *wcd_cpe_init(const char *img_fname,
goto schedule_dload_work;
}
+ arch_setup_dma_ops(core->dev, 0, 0, NULL, 0);
core->cpe_dump_v_addr = dma_alloc_coherent(core->dev,
core->hw_info.dram_size,
&core->cpe_dump_addr,
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 0171810803a9..b97b73dc4191 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -90,6 +90,7 @@ struct wsa881x_priv {
bool comp_enable;
bool boost_enable;
bool visense_enable;
+ u8 pa_gain;
struct swr_port port[WSA881X_MAX_SWR_PORTS];
int pd_gpio;
struct wsa881x_tz_priv tz_pdata;
@@ -134,6 +135,47 @@ static unsigned int devnum;
static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec,
bool enable);
+static const char * const wsa_pa_gain_text[] = {
+ "G_18_DB", "G_16P5_DB", "G_15_DB", "G_13P5_DB", "G_12_DB", "G_10P5_DB",
+ "G_9_DB", "G_7P5_DB", "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB",
+ "G_0_DB"
+};
+
+static const struct soc_enum wsa_pa_gain_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa_pa_gain_text), wsa_pa_gain_text);
+
+static int wsa_pa_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wsa881x->pa_gain;
+
+ dev_dbg(codec->dev, "%s: PA gain = 0x%x\n", __func__, wsa881x->pa_gain);
+
+ return 0;
+}
+
+static int wsa_pa_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
+ wsa881x->pa_gain = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wsa_analog_gain_controls[] = {
+ SOC_ENUM_EXT("WSA PA Gain", wsa_pa_gain_enum,
+ wsa_pa_gain_get, wsa_pa_gain_put),
+};
+
static int codec_debug_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
@@ -775,6 +817,7 @@ static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int min_gain, max_gain;
dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event);
switch (event) {
@@ -786,15 +829,32 @@ static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w,
swr_slvdev_datapath_control(wsa881x->swr_slave,
wsa881x->swr_slave->dev_num,
true);
+ /* Set register mode if compander is not enabled */
+ if (!wsa881x->comp_enable)
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN,
+ 0x08, 0x08);
+ else
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN,
+ 0x08, 0x00);
+
break;
case SND_SOC_DAPM_POST_PMU:
if (!wsa881x->comp_enable) {
+ max_gain = wsa881x->pa_gain;
+ /*
+ * Gain has to set incrementally in 4 steps
+ * as per HW sequence
+ */
+ if (max_gain > G_4P5DB)
+ min_gain = G_0DB;
+ else
+ min_gain = max_gain + 3;
/*
* 1ms delay is needed before change in gain
* as per HW requirement.
*/
usleep_range(1000, 1010);
- wsa881x_ramp_pa_gain(codec, G_13P5DB, G_18DB, 1000);
+ wsa881x_ramp_pa_gain(codec, min_gain, max_gain, 1000);
}
if (wsa881x->visense_enable) {
wsa881x_visense_txfe_ctrl(codec, ENABLE,
@@ -987,6 +1047,8 @@ static int wsa881x_probe(struct snd_soc_codec *codec)
wsa881x->tz_pdata.codec = codec;
wsa881x->tz_pdata.wsa_temp_reg_read = wsa881x_temp_reg_read;
wsa881x_init_thermal(&wsa881x->tz_pdata);
+ snd_soc_add_codec_controls(codec, wsa_analog_gain_controls,
+ ARRAY_SIZE(wsa_analog_gain_controls));
INIT_DELAYED_WORK(&wsa881x->ocp_ctl_work, wsa881x_ocp_ctl_work);
return 0;
}
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
index 759716199224..e740a24704b7 100644
--- a/sound/soc/msm/Kconfig
+++ b/sound/soc/msm/Kconfig
@@ -15,6 +15,7 @@ config SND_SOC_MSM_QDSP6V2_INTF
config SND_SOC_QDSP6V2
tristate "SoC ALSA audio driver for QDSP6V2"
select SND_SOC_MSM_QDSP6V2_INTF
+ select SND_SOC_COMPRESS
help
To add support for MSM QDSP6V2 Soc Audio.
This will enable sound soc platform specific
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 85fa45ff400f..4cb62a6b3e7d 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -25,7 +25,7 @@ static struct snd_soc_dai_ops msm_fe_dai_ops = {};
/* 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, 352800, 384000
};
static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
@@ -92,7 +92,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia1 Playback",
.aif_name = "MM_DL1",
- .rates = (SNDRV_PCM_RATE_8000_192000|
+ .rates = (SNDRV_PCM_RATE_8000_384000|
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -100,12 +100,12 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "MultiMedia1 Capture",
.aif_name = "MM_UL1",
- .rates = (SNDRV_PCM_RATE_8000_192000|
+ .rates = (SNDRV_PCM_RATE_8000_384000|
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE|
@@ -123,7 +123,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia2 Playback",
.aif_name = "MM_DL2",
- .rates = (SNDRV_PCM_RATE_8000_192000|
+ .rates = (SNDRV_PCM_RATE_8000_384000|
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -131,12 +131,12 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "MultiMedia2 Capture",
.aif_name = "MM_UL2",
- .rates = (SNDRV_PCM_RATE_8000_192000|
+ .rates = (SNDRV_PCM_RATE_8000_384000|
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -206,7 +206,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia3 Playback",
.aif_name = "MM_DL3",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -214,12 +214,12 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 6,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "MultiMedia3 Capture",
.aif_name = "MM_UL3",
- .rates = (SNDRV_PCM_RATE_8000_192000|
+ .rates = (SNDRV_PCM_RATE_8000_384000|
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE),
@@ -236,7 +236,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia4 Playback",
.aif_name = "MM_DL4",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -244,7 +244,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_Multimedia_dai_ops,
.compress_new = snd_soc_new_compress,
@@ -255,7 +255,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia5 Playback",
.aif_name = "MM_DL5",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -263,7 +263,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "MultiMedia5 Capture",
@@ -286,7 +286,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia6 Playback",
.aif_name = "MM_DL6",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -294,7 +294,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "MultiMedia6 Capture",
@@ -317,7 +317,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia7 Playback",
.aif_name = "MM_DL7",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -325,7 +325,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_Multimedia_dai_ops,
.compress_new = snd_soc_new_compress,
@@ -336,7 +336,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia8 Playback",
.aif_name = "MM_DL8",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -344,7 +344,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "MultiMedia8 Capture",
@@ -368,13 +368,13 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "SLIMBUS0_HOSTLESS Playback",
.aif_name = "SLIM0_DL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "SLIMBUS0_HOSTLESS Capture",
@@ -386,7 +386,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_dai_ops,
.name = "SLIMBUS0_HOSTLESS",
@@ -396,13 +396,13 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "SLIMBUS1_HOSTLESS Playback",
.aif_name = "SLIM1_DL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "SLIMBUS1_HOSTLESS Capture",
@@ -423,13 +423,13 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "SLIMBUS3_HOSTLESS Playback",
.aif_name = "SLIM3_DL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "SLIMBUS3_HOSTLESS Capture",
@@ -450,13 +450,13 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "SLIMBUS4_HOSTLESS Playback",
.aif_name = "SLIM4_DL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "SLIMBUS4_HOSTLESS Capture",
@@ -477,13 +477,13 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "SLIMBUS6_HOSTLESS Playback",
.aif_name = "SLIM6_DL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_dai_ops,
.name = "SLIMBUS6_HOSTLESS",
@@ -493,24 +493,24 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "SLIMBUS8_HOSTLESS Playback",
.aif_name = "SLIM8_DL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "SLIMBUS8_HOSTLESS Capture",
.aif_name = "SLIM8_UL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_dai_ops,
.name = "SLIMBUS8_HOSTLESS",
@@ -747,13 +747,13 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "Primary MI2S_RX Hostless Playback",
.aif_name = "PRI_MI2S_DL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE),
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_dai_ops,
.name = "PRI_MI2S_RX_HOSTLESS",
@@ -779,13 +779,13 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "Secondary MI2S_RX Hostless Playback",
.aif_name = "SEC_MI2S_DL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_dai_ops,
.name = "SEC_MI2S_RX_HOSTLESS",
@@ -811,13 +811,13 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "Tertiary MI2S_RX Hostless Playback",
.aif_name = "TERT_MI2S_DL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_dai_ops,
.name = "TERT_MI2S_RX_HOSTLESS",
@@ -843,13 +843,13 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "Quaternary MI2S_RX Hostless Playback",
.aif_name = "QUAT_MI2S_DL_HL",
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SNDRV_PCM_RATE_8000_384000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_dai_ops,
.name = "QUAT_MI2S_RX_HOSTLESS",
@@ -2016,7 +2016,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia9 Playback",
.aif_name = "MM_DL9",
- .rates = (SNDRV_PCM_RATE_8000_192000|
+ .rates = (SNDRV_PCM_RATE_8000_384000|
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -2024,7 +2024,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.capture = {
.stream_name = "MultiMedia9 Capture",
@@ -2216,7 +2216,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia10 Playback",
.aif_name = "MM_DL10",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -2224,7 +2224,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_Multimedia_dai_ops,
.compress_new = snd_soc_new_compress,
@@ -2235,7 +2235,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia11 Playback",
.aif_name = "MM_DL11",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -2243,7 +2243,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_Multimedia_dai_ops,
.compress_new = snd_soc_new_compress,
@@ -2254,7 +2254,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia12 Playback",
.aif_name = "MM_DL12",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -2262,7 +2262,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_Multimedia_dai_ops,
.compress_new = snd_soc_new_compress,
@@ -2273,7 +2273,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia13 Playback",
.aif_name = "MM_DL13",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -2281,7 +2281,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_Multimedia_dai_ops,
.compress_new = snd_soc_new_compress,
@@ -2292,7 +2292,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia14 Playback",
.aif_name = "MM_DL14",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -2300,7 +2300,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_Multimedia_dai_ops,
.compress_new = snd_soc_new_compress,
@@ -2311,7 +2311,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia15 Playback",
.aif_name = "MM_DL15",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -2319,7 +2319,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_Multimedia_dai_ops,
.compress_new = snd_soc_new_compress,
@@ -2330,7 +2330,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.playback = {
.stream_name = "MultiMedia16 Playback",
.aif_name = "MM_DL16",
- .rates = (SNDRV_PCM_RATE_8000_192000 |
+ .rates = (SNDRV_PCM_RATE_8000_384000 |
SNDRV_PCM_RATE_KNOT),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
@@ -2338,7 +2338,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = {
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_fe_Multimedia_dai_ops,
.compress_new = snd_soc_new_compress,
diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c
index b0948acf8a71..63367ce07f5a 100644
--- a/sound/soc/msm/msmcobalt.c
+++ b/sound/soc/msm/msmcobalt.c
@@ -101,6 +101,12 @@ struct dev_config {
u32 channels;
};
+enum {
+ HDMI_RX_IDX = 0,
+ DP_RX_IDX,
+ EXT_DISP_RX_IDX_MAX,
+};
+
struct msm_wsa881x_dev_info {
struct device_node *of_node;
u32 index;
@@ -147,6 +153,13 @@ static struct dev_config slim_tx_cfg[] = {
[SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
};
+
+/* Default configuration of external display BE */
+static struct dev_config ext_disp_rx_cfg[] = {
+ [HDMI_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+ [DP_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
static struct dev_config usb_rx_cfg = {
.sample_rate = SAMPLING_RATE_48KHZ,
.bit_format = SNDRV_PCM_FORMAT_S16_LE,
@@ -159,12 +172,6 @@ static struct dev_config usb_tx_cfg = {
.channels = 1,
};
-static struct dev_config hdmi_rx_cfg = {
- .sample_rate = SAMPLING_RATE_48KHZ,
- .bit_format = SNDRV_PCM_FORMAT_S16_LE,
- .channels = 2,
-};
-
static struct dev_config proxy_rx_cfg = {
.sample_rate = SAMPLING_RATE_48KHZ,
.bit_format = SNDRV_PCM_FORMAT_S16_LE,
@@ -178,20 +185,23 @@ static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four",
"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 *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",
"KHZ_88P2", "KHZ_96", "KHZ_176P4",
"KHZ_192", "KHZ_352P8", "KHZ_384"};
static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
-static const char *const usb_ch_text[] = {"One", "Two"};
+static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven",
+ "Eight"};
static char const *ch_text[] = {"Two", "Three", "Four", "Five",
"Six", "Seven", "Eight"};
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"};
-static char const *hdmi_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
- "KHZ_192"};
+static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192"};
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);
@@ -202,7 +212,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text);
-static SOC_ENUM_SINGLE_EXT_DECL(hdmi_rx_chs, ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text);
static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text);
@@ -210,7 +220,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text);
static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text);
static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text);
static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text);
-static SOC_ENUM_SINGLE_EXT_DECL(hdmi_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text);
static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text);
@@ -219,7 +229,8 @@ static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text);
static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text);
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(hdmi_rx_sample_rate, hdmi_rx_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate,
+ ext_disp_sample_rate_text);
static struct platform_device *spdev;
@@ -999,15 +1010,33 @@ static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol,
return rc;
}
-static int hdmi_rx_format_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol)
{
+ int idx;
- switch (hdmi_rx_cfg.bit_format) {
- case SNDRV_PCM_FORMAT_S24_3LE:
- ucontrol->value.integer.value[0] = 2;
- break;
+ if (strnstr(kcontrol->id.name, "HDMI_RX", sizeof("HDMI_RX")))
+ idx = HDMI_RX_IDX;
+ else if (strnstr(kcontrol->id.name, "Display Port RX",
+ sizeof("Display Port RX")))
+ idx = DP_RX_IDX;
+ else {
+ pr_err("%s: unsupported BE: %s",
+ __func__, kcontrol->id.name);
+ idx = -EINVAL;
+ }
+
+ return idx;
+}
+static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ switch (ext_disp_rx_cfg[idx].bit_format) {
case SNDRV_PCM_FORMAT_S24_LE:
ucontrol->value.integer.value[0] = 1;
break;
@@ -1018,66 +1047,79 @@ static int hdmi_rx_format_get(struct snd_kcontrol *kcontrol,
break;
}
- pr_debug("%s: hdmi_rx_cfg = %d, ucontrol value = %ld\n",
- __func__, hdmi_rx_cfg.bit_format,
+ pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n",
+ __func__, idx, ext_disp_rx_cfg[idx].bit_format,
ucontrol->value.integer.value[0]);
-
return 0;
}
-static int hdmi_rx_format_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
switch (ucontrol->value.integer.value[0]) {
- case 2:
- hdmi_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
- break;
case 1:
- hdmi_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE;
break;
case 0:
default:
- hdmi_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE;
break;
}
- pr_debug("%s: hdmi_rx_cfg.bit_format = %d, ucontrol value = %ld\n",
- __func__, hdmi_rx_cfg.bit_format,
+ pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n",
+ __func__, idx, ext_disp_rx_cfg[idx].bit_format,
ucontrol->value.integer.value[0]);
return 0;
}
-static int hdmi_rx_ch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- pr_debug("%s: hdmi_rx_cfg.channels = %d\n", __func__,
- hdmi_rx_cfg.channels);
- ucontrol->value.integer.value[0] = hdmi_rx_cfg.channels - 2;
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.integer.value[0] =
+ ext_disp_rx_cfg[idx].channels - 2;
+
+ pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__,
+ idx, ext_disp_rx_cfg[idx].channels);
return 0;
}
-static int hdmi_rx_ch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- hdmi_rx_cfg.channels = ucontrol->value.integer.value[0] + 2;
- if (hdmi_rx_cfg.channels > 8) {
- pr_err("%s: channels %d exceeded 8.Limiting to max chs-8\n",
- __func__, hdmi_rx_cfg.channels);
- hdmi_rx_cfg.channels = 8;
- }
- pr_debug("%s: hdmi_rx_cfg.channels = %d\n", __func__,
- hdmi_rx_cfg.channels);
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+ ext_disp_rx_cfg[idx].channels =
+ ucontrol->value.integer.value[0] + 2;
+
+ pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__,
+ idx, ext_disp_rx_cfg[idx].channels);
return 1;
}
-static int hdmi_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- int sample_rate_val = 0;
+ int sample_rate_val;
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
- switch (hdmi_rx_cfg.sample_rate) {
+ switch (ext_disp_rx_cfg[idx].sample_rate) {
case SAMPLING_RATE_192KHZ:
sample_rate_val = 2;
break;
@@ -1093,33 +1135,36 @@ static int hdmi_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
}
ucontrol->value.integer.value[0] = sample_rate_val;
- pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
- hdmi_rx_cfg.sample_rate);
+ pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__,
+ idx, ext_disp_rx_cfg[idx].sample_rate);
return 0;
}
-static int hdmi_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- pr_debug("%s: ucontrol value = %ld\n", __func__,
- ucontrol->value.integer.value[0]);
+ int idx = ext_disp_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
switch (ucontrol->value.integer.value[0]) {
case 2:
- hdmi_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ;
break;
case 1:
- hdmi_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ;
break;
case 0:
default:
- hdmi_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+ ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ;
+ break;
}
- pr_debug("%s: hdmi_rx_cfg.sample_rate = %d\n", __func__,
- hdmi_rx_cfg.sample_rate);
-
+ pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n",
+ __func__, ucontrol->value.integer.value[0], idx,
+ ext_disp_rx_cfg[idx].sample_rate);
return 0;
}
@@ -1162,8 +1207,10 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
usb_audio_rx_ch_get, usb_audio_rx_ch_put),
SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs,
usb_audio_tx_ch_get, usb_audio_tx_ch_put),
- SOC_ENUM_EXT("HDMI_RX Channels", hdmi_rx_chs,
- hdmi_rx_ch_get, hdmi_rx_ch_put),
+ SOC_ENUM_EXT("HDMI_RX Channels", ext_disp_rx_chs,
+ ext_disp_rx_ch_get, ext_disp_rx_ch_put),
+ SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs,
+ ext_disp_rx_ch_get, ext_disp_rx_ch_put),
SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs,
proxy_rx_ch_get, proxy_rx_ch_put),
SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format,
@@ -1178,8 +1225,10 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
usb_audio_rx_format_get, usb_audio_rx_format_put),
SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format,
usb_audio_tx_format_get, usb_audio_tx_format_put),
- SOC_ENUM_EXT("HDMI_RX Bit Format", hdmi_rx_format,
- hdmi_rx_format_get, hdmi_rx_format_put),
+ SOC_ENUM_EXT("HDMI_RX Bit Format", ext_disp_rx_format,
+ ext_disp_rx_format_get, ext_disp_rx_format_put),
+ SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format,
+ ext_disp_rx_format_get, ext_disp_rx_format_put),
SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate,
slim_rx_sample_rate_get, slim_rx_sample_rate_put),
SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate,
@@ -1199,9 +1248,12 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate,
usb_audio_tx_sample_rate_get,
usb_audio_tx_sample_rate_put),
- SOC_ENUM_EXT("HDMI_RX SampleRate", hdmi_rx_sample_rate,
- hdmi_rx_sample_rate_get,
- hdmi_rx_sample_rate_put),
+ SOC_ENUM_EXT("HDMI_RX SampleRate", ext_disp_rx_sample_rate,
+ ext_disp_rx_sample_rate_get,
+ ext_disp_rx_sample_rate_put),
+ SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate,
+ ext_disp_rx_sample_rate_get,
+ ext_disp_rx_sample_rate_put),
};
static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
@@ -1324,6 +1376,26 @@ static int msm_slim_get_ch_from_beid(int32_t be_id)
return ch_id;
}
+static int msm_ext_disp_get_idx_from_beid(int32_t be_id)
+{
+ int idx;
+
+ switch (be_id) {
+ case MSM_BACKEND_DAI_HDMI_RX:
+ idx = HDMI_RX_IDX;
+ break;
+ case MSM_BACKEND_DAI_DISPLAY_PORT_RX:
+ idx = DP_RX_IDX;
+ break;
+ default:
+ pr_err("%s: Incorrect ext_disp be_id %d\n", __func__, be_id);
+ idx = -EINVAL;
+ break;
+ }
+
+ return idx;
+}
+
static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -1333,9 +1405,9 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
int rc = 0;
+ int idx;
void *config = NULL;
struct snd_soc_codec *codec = NULL;
- int ch_num;
pr_debug("%s: format = %d, rate = %d\n",
__func__, params_format(params), params_rate(params));
@@ -1347,20 +1419,20 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
case MSM_BACKEND_DAI_SLIMBUS_3_RX:
case MSM_BACKEND_DAI_SLIMBUS_4_RX:
case MSM_BACKEND_DAI_SLIMBUS_6_RX:
- ch_num = msm_slim_get_ch_from_beid(dai_link->be_id);
+ idx = msm_slim_get_ch_from_beid(dai_link->be_id);
param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
- slim_rx_cfg[ch_num].bit_format);
- rate->min = rate->max = slim_rx_cfg[ch_num].sample_rate;
- channels->min = channels->max = slim_rx_cfg[ch_num].channels;
+ slim_rx_cfg[idx].bit_format);
+ rate->min = rate->max = slim_rx_cfg[idx].sample_rate;
+ channels->min = channels->max = slim_rx_cfg[idx].channels;
break;
case MSM_BACKEND_DAI_SLIMBUS_0_TX:
case MSM_BACKEND_DAI_SLIMBUS_3_TX:
- ch_num = msm_slim_get_ch_from_beid(dai_link->be_id);
+ idx = msm_slim_get_ch_from_beid(dai_link->be_id);
param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
- slim_tx_cfg[ch_num].bit_format);
- rate->min = rate->max = slim_tx_cfg[ch_num].sample_rate;
- channels->min = channels->max = slim_tx_cfg[ch_num].channels;
+ slim_tx_cfg[idx].bit_format);
+ rate->min = rate->max = slim_tx_cfg[idx].sample_rate;
+ channels->min = channels->max = slim_tx_cfg[idx].channels;
break;
case MSM_BACKEND_DAI_SLIMBUS_1_TX:
@@ -1435,10 +1507,19 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
break;
case MSM_BACKEND_DAI_HDMI_RX:
+ case MSM_BACKEND_DAI_DISPLAY_PORT_RX:
+ idx = msm_ext_disp_get_idx_from_beid(dai_link->be_id);
+ if (IS_ERR_VALUE(idx)) {
+ pr_err("%s: Incorrect ext disp idx %d\n",
+ __func__, idx);
+ rc = idx;
+ goto done;
+ }
+
param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
- hdmi_rx_cfg.bit_format);
- rate->min = rate->max = hdmi_rx_cfg.sample_rate;
- channels->min = channels->max = hdmi_rx_cfg.channels;
+ ext_disp_rx_cfg[idx].bit_format);
+ rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate;
+ channels->min = channels->max = ext_disp_rx_cfg[idx].channels;
break;
case MSM_BACKEND_DAI_AFE_PCM_RX:
@@ -1450,7 +1531,9 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
rate->min = rate->max = SAMPLING_RATE_48KHZ;
break;
}
- return 0;
+
+done:
+ return rc;
}
static bool msm_swap_gnd_mic(struct snd_soc_codec *codec)
@@ -1785,6 +1868,17 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
tavil_set_spkr_gain_offset(rtd->codec,
RX_GAIN_OFFSET_M1P5_DB);
}
+ card = rtd->card->snd_card;
+ entry = snd_register_module_info(card->module, "codecs",
+ card->proc_root);
+ if (!entry) {
+ pr_debug("%s: Cannot create codecs module entry\n",
+ __func__);
+ pdata->codec_root = NULL;
+ goto done;
+ }
+ pdata->codec_root = entry;
+ tavil_codec_info_create_codec_entry(pdata->codec_root, codec);
} else {
if (rtd_aux && rtd_aux->component)
if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
@@ -1805,6 +1899,7 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
pdata->codec_root = entry;
tasha_codec_info_create_codec_entry(pdata->codec_root, codec);
}
+done:
codec_reg_done = true;
return 0;
@@ -3293,14 +3388,14 @@ static struct snd_soc_dai_link msm_wcn_be_dai_links[] = {
},
};
-static struct snd_soc_dai_link hdmi_be_dai_link[] = {
+static struct snd_soc_dai_link ext_disp_be_dai_link[] = {
/* HDMI BACK END DAI Link */
{
.name = LPASS_BE_HDMI,
.stream_name = "HDMI Playback",
.cpu_dai_name = "msm-dai-q6-hdmi.8",
.platform_name = "msm-pcm-routing",
- .codec_name = "msm-hdmi-audio-codec-rx",
+ .codec_name = "msm-ext-disp-audio-codec-rx",
.codec_dai_name = "msm_hdmi_audio_codec_rx_dai",
.no_pcm = 1,
.dpcm_playback = 1,
@@ -3309,6 +3404,21 @@ static struct snd_soc_dai_link hdmi_be_dai_link[] = {
.ignore_pmdown_time = 1,
.ignore_suspend = 1,
},
+ /* DISP PORT BACK END DAI Link */
+ {
+ .name = LPASS_BE_DISPLAY_PORT,
+ .stream_name = "Display Port Playback",
+ .cpu_dai_name = "msm-dai-q6-dp.24608",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-ext-disp-audio-codec-rx",
+ .codec_dai_name = "msm_dp_audio_codec_rx_dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
};
static struct snd_soc_dai_link msm_tasha_dai_links[
@@ -3317,7 +3427,7 @@ static struct snd_soc_dai_link msm_tasha_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(hdmi_be_dai_link)];
+ ARRAY_SIZE(ext_disp_be_dai_link)];
static struct snd_soc_dai_link msm_tavil_dai_links[
ARRAY_SIZE(msm_common_dai_links) +
@@ -3325,7 +3435,7 @@ 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(hdmi_be_dai_link)];
+ ARRAY_SIZE(ext_disp_be_dai_link)];
static int msm_snd_card_late_probe(struct snd_soc_card *card)
{
@@ -3725,14 +3835,15 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
total_links += ARRAY_SIZE(msm_wcn_be_dai_links);
}
- if (of_property_read_bool(dev->of_node, "qcom,hdmi-audio-rx")) {
- dev_dbg(dev, "%s(): HDMI support present\n", __func__);
+ if (of_property_read_bool(dev->of_node,
+ "qcom,ext-disp-audio-rx")) {
+ dev_dbg(dev, "%s(): External display audio support present\n",
+ __func__);
memcpy(msm_tasha_dai_links + total_links,
- hdmi_be_dai_link,
- sizeof(hdmi_be_dai_link));
- total_links += ARRAY_SIZE(hdmi_be_dai_link);
+ ext_disp_be_dai_link,
+ sizeof(ext_disp_be_dai_link));
+ total_links += ARRAY_SIZE(ext_disp_be_dai_link);
}
-
dailink = msm_tasha_dai_links;
} else if (!strcmp(match->data, "tavil_codec")) {
card = &snd_soc_card_tavil_msm;
@@ -3762,14 +3873,15 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
total_links += ARRAY_SIZE(msm_wcn_be_dai_links);
}
- if (of_property_read_bool(dev->of_node, "qcom,hdmi-audio-rx")) {
- dev_dbg(dev, "%s(): HDMI support present\n", __func__);
+ if (of_property_read_bool(dev->of_node,
+ "qcom,ext-disp-audio-rx")) {
+ dev_dbg(dev, "%s(): ext disp audio support present\n",
+ __func__);
memcpy(msm_tavil_dai_links + total_links,
- hdmi_be_dai_link,
- sizeof(hdmi_be_dai_link));
- total_links += ARRAY_SIZE(hdmi_be_dai_link);
+ ext_disp_be_dai_link,
+ sizeof(ext_disp_be_dai_link));
+ total_links += ARRAY_SIZE(ext_disp_be_dai_link);
}
-
dailink = msm_tavil_dai_links;
} else if (!strcmp(match->data, "stub_codec")) {
card = &snd_soc_card_stub_msm;
@@ -3786,7 +3898,6 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
dailink = msm_stub_dai_links;
total_links = len_2;
}
- dev_dbg(dev, "%s(): No hdmi audio support\n", __func__);
if (card) {
card->dai_link = dailink;
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 7e0f790b30e9..841bb5bce13f 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -1762,7 +1762,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;
/*
@@ -2005,20 +2010,25 @@ static int msm_compr_pointer(struct snd_compr_stream *cstream,
gapless_transition = prtd->gapless_state.gapless_transition;
spin_unlock_irqrestore(&prtd->lock, flags);
+ if (gapless_transition)
+ pr_debug("%s session time in gapless transition",
+ __func__);
+
/*
- Query timestamp from DSP if some data is with it.
- This prevents timeouts.
+ - Do not query if no buffer has been given.
+ - Do not query on a gapless transition.
+ Playback for the 2nd stream can start (thus returning time
+ starting from 0) before the driver knows about EOS of first stream.
*/
- if (!first_buffer || gapless_transition) {
- if (gapless_transition)
- pr_debug("%s session time in gapless transition",
- __func__);
+
+ if (!first_buffer && !gapless_transition) {
if (pdata->use_legacy_api)
rc = q6asm_get_session_time_legacy(prtd->audio_client,
- &timestamp);
+ &prtd->marker_timestamp);
else
rc = q6asm_get_session_time(prtd->audio_client,
- &timestamp);
+ &prtd->marker_timestamp);
+
if (rc < 0) {
pr_err("%s: Get Session Time return value =%lld\n",
__func__, timestamp);
@@ -2027,9 +2037,8 @@ static int msm_compr_pointer(struct snd_compr_stream *cstream,
else
return -EAGAIN;
}
- } else {
- timestamp = prtd->marker_timestamp;
}
+ timestamp = prtd->marker_timestamp;
/* DSP returns timestamp in usec */
pr_debug("%s: timestamp = %lld usec\n", __func__, timestamp);
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
index 9c57adb863d4..69a9e14c47de 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.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
@@ -32,53 +32,82 @@ enum {
STATUS_MAX
};
-struct msm_hdmi_ca {
+struct msm_ext_disp_ca {
bool set_ca;
u32 ca;
};
-static struct msm_hdmi_ca hdmi_ca = { false, 0x0 };
-
struct msm_dai_q6_hdmi_dai_data {
DECLARE_BITMAP(status_mask, STATUS_MAX);
u32 rate;
u32 channels;
+ struct msm_ext_disp_ca ca;
union afe_port_config port_config;
};
-static int msm_dai_q6_hdmi_format_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int msm_dai_q6_ext_disp_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
-
struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
int value = ucontrol->value.integer.value[0];
+
+ if (!dai_data) {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
dai_data->port_config.hdmi_multi_ch.datatype = value;
pr_debug("%s: value = %d\n", __func__, value);
+
return 0;
}
-static int msm_dai_q6_hdmi_format_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int msm_dai_q6_ext_disp_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
-
struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+
+ if (!dai_data) {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
ucontrol->value.integer.value[0] =
dai_data->port_config.hdmi_multi_ch.datatype;
+ pr_debug("%s: value = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+
return 0;
}
-static int msm_dai_q6_hdmi_ca_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int msm_dai_q6_ext_disp_ca_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- hdmi_ca.ca = ucontrol->value.integer.value[0];
- hdmi_ca.set_ca = true;
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+
+ if (!dai_data) {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dai_data->ca.ca = ucontrol->value.integer.value[0];
+ dai_data->ca.set_ca = true;
+ pr_debug("%s: ca = %d\n", __func__, dai_data->ca.ca);
return 0;
}
-static int msm_dai_q6_hdmi_ca_get(struct snd_kcontrol *kcontrol,
+static int msm_dai_q6_ext_disp_ca_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- ucontrol->value.integer.value[0] = hdmi_ca.ca;
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+
+ if (!dai_data) {
+ pr_err("%s: dai_data is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = dai_data->ca.ca;
+ pr_debug("%s: ca = %d\n", __func__, dai_data->ca.ca);
return 0;
}
@@ -97,12 +126,22 @@ static const struct soc_enum hdmi_config_enum[] = {
static const struct snd_kcontrol_new hdmi_config_controls[] = {
SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
- msm_dai_q6_hdmi_format_get,
- msm_dai_q6_hdmi_format_put),
+ msm_dai_q6_ext_disp_format_get,
+ msm_dai_q6_ext_disp_format_put),
SOC_SINGLE_MULTI_EXT("HDMI RX CA", SND_SOC_NOPM, 0,
HDMI_RX_CA_MAX, 0, 1,
- msm_dai_q6_hdmi_ca_get,
- msm_dai_q6_hdmi_ca_put),
+ msm_dai_q6_ext_disp_ca_get,
+ msm_dai_q6_ext_disp_ca_put),
+};
+
+static const struct snd_kcontrol_new display_port_config_controls[] = {
+ SOC_ENUM_EXT("Display Port RX Format", hdmi_config_enum[0],
+ msm_dai_q6_ext_disp_format_get,
+ msm_dai_q6_ext_disp_format_put),
+ SOC_SINGLE_MULTI_EXT("Display Port RX CA", SND_SOC_NOPM, 0,
+ HDMI_RX_CA_MAX, 0, 1,
+ msm_dai_q6_ext_disp_ca_get,
+ msm_dai_q6_ext_disp_ca_put),
};
/* Current implementation assumes hw_param is called once
@@ -200,9 +239,9 @@ static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream,
struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
int rc = 0;
- if (hdmi_ca.set_ca)
+ if (dai_data->ca.set_ca)
dai_data->port_config.hdmi_multi_ch.channel_allocation =
- hdmi_ca.ca;
+ dai_data->ca.ca;
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
rc = afe_port_start(dai->id, &dai_data->port_config,
@@ -236,8 +275,8 @@ static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
struct snd_soc_dapm_route intercon;
struct snd_soc_dapm_context *dapm;
- if (!dai) {
- pr_err("%s: dai not found\n", __func__);
+ if (!dai || !dai->driver) {
+ pr_err("%s: dai or dai->driver is NULL\n", __func__);
return -EINVAL;
}
dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data),
@@ -252,19 +291,33 @@ static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
msm_dai_q6_hdmi_set_dai_id(dai);
- kcontrol = &hdmi_config_controls[0];
-
- rc = snd_ctl_add(dai->component->card->snd_card,
- snd_ctl_new1(kcontrol, dai_data));
-
- kcontrol = &hdmi_config_controls[1];
-
- rc = snd_ctl_add(dai->component->card->snd_card,
- snd_ctl_new1(kcontrol, dai_data));
+ if (dai->driver->id == HDMI_RX) {
+ kcontrol = &hdmi_config_controls[0];
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+
+ kcontrol = &hdmi_config_controls[1];
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+ } else if (dai->driver->id == DISPLAY_PORT_RX) {
+ kcontrol = &display_port_config_controls[0];
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+
+ kcontrol = &display_port_config_controls[1];
+ rc = snd_ctl_add(dai->component->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+ } else {
+ dev_err(dai->dev, "%s: Invalid id:%d\n",
+ __func__, dai->driver->id);
+ kfree(dai_data);
+ dev_set_drvdata(dai->dev, NULL);
+ return -EINVAL;
+ }
dapm = snd_soc_component_get_dapm(dai->component);
memset(&intercon, 0 , sizeof(intercon));
- if (!rc && dai && dai->driver) {
+ if (!rc) {
if (dai->driver->playback.stream_name &&
dai->driver->playback.aif_name) {
dev_dbg(dai->dev, "%s add route for widget %s",
@@ -325,8 +378,8 @@ static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 2,
.channels_max = 8,
- .rate_max = 192000,
- .rate_min = 48000,
+ .rate_max = 192000,
+ .rate_min = 48000,
},
.ops = &msm_dai_q6_hdmi_ops,
.id = HDMI_RX,
@@ -334,6 +387,27 @@ static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
.remove = msm_dai_q6_hdmi_dai_remove,
};
+static struct snd_soc_dai_driver msm_dai_q6_display_port_rx_dai[] = {
+ {
+ .playback = {
+ .stream_name = "Display Port Playback",
+ .aif_name = "DISPLAY_PORT",
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_max = 192000,
+ .rate_min = 48000,
+ },
+ .ops = &msm_dai_q6_hdmi_ops,
+ .id = DISPLAY_PORT_RX,
+ .probe = msm_dai_q6_hdmi_dai_probe,
+ .remove = msm_dai_q6_hdmi_dai_remove,
+ },
+};
+
static const struct snd_soc_component_driver msm_dai_hdmi_q6_component = {
.name = "msm-dai-q6-hdmi",
};
@@ -362,6 +436,12 @@ static int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev)
&msm_dai_hdmi_q6_component,
&msm_dai_q6_hdmi_hdmi_rx_dai, 1);
break;
+ case DISPLAY_PORT_RX:
+ rc = snd_soc_register_component(&pdev->dev,
+ &msm_dai_hdmi_q6_component,
+ &msm_dai_q6_display_port_rx_dai[0],
+ ARRAY_SIZE(msm_dai_q6_display_port_rx_dai));
+ break;
default:
dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id);
rc = -ENODEV;
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 51ebd039d96b..5fa3b6fb0885 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -37,6 +37,10 @@
#define CHANNEL_STATUS_MASK 0x4
#define AFE_API_VERSION_CLOCK_SET 1
+#define DAI_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
enum {
ENC_FMT_NONE,
ENC_FMT_SBC = ASM_MEDIA_FMT_SBC,
@@ -2498,7 +2502,7 @@ static struct snd_soc_dai_driver msm_dai_q6_usb_rx_dai = {
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rate_max = 192000,
.rate_min = 8000,
},
@@ -2519,7 +2523,7 @@ static struct snd_soc_dai_driver msm_dai_q6_usb_tx_dai = {
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rate_max = 192000,
.rate_min = 8000,
},
@@ -2808,15 +2812,12 @@ static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
.playback = {
.stream_name = "Slimbus Playback",
.aif_name = "SLIMBUS_0_RX",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_dai_q6_ops,
.id = SLIMBUS_0_RX,
@@ -2827,15 +2828,12 @@ static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
.playback = {
.stream_name = "Slimbus1 Playback",
.aif_name = "SLIMBUS_1_RX",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_dai_q6_ops,
.id = SLIMBUS_1_RX,
@@ -2846,15 +2844,12 @@ static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
.playback = {
.stream_name = "Slimbus2 Playback",
.aif_name = "SLIMBUS_2_RX",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_dai_q6_ops,
.id = SLIMBUS_2_RX,
@@ -2865,15 +2860,12 @@ static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
.playback = {
.stream_name = "Slimbus3 Playback",
.aif_name = "SLIMBUS_3_RX",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_dai_q6_ops,
.id = SLIMBUS_3_RX,
@@ -2884,15 +2876,12 @@ static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
.playback = {
.stream_name = "Slimbus4 Playback",
.aif_name = "SLIMBUS_4_RX",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_dai_q6_ops,
.id = SLIMBUS_4_RX,
@@ -2903,15 +2892,12 @@ static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
.playback = {
.stream_name = "Slimbus6 Playback",
.aif_name = "SLIMBUS_6_RX",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_dai_q6_ops,
.id = SLIMBUS_6_RX,
@@ -2922,15 +2908,12 @@ static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
.playback = {
.stream_name = "Slimbus5 Playback",
.aif_name = "SLIMBUS_5_RX",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_44100,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_dai_q6_ops,
.id = SLIMBUS_5_RX,
@@ -2941,15 +2924,12 @@ static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
.playback = {
.stream_name = "Slimbus7 Playback",
.aif_name = "SLIMBUS_7_RX",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_44100,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_dai_q6_ops,
.id = SLIMBUS_7_RX,
@@ -2960,15 +2940,12 @@ static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
.playback = {
.stream_name = "Slimbus8 Playback",
.aif_name = "SLIMBUS_8_RX",
- .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_44100,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
+ .rates = SNDRV_PCM_RATE_8000_384000,
+ .formats = DAI_FORMATS_S16_S24_S32_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 384000,
},
.ops = &msm_dai_q6_ops,
.id = SLIMBUS_8_RX,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
index 6e5b4e871fe6..972cacb50f47 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
@@ -375,8 +375,12 @@ static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
q6asm_cmd(prtd->audio_client, CMD_PAUSE);
q6asm_cmd(prtd->audio_client, CMD_FLUSH);
buf = q6asm_shared_io_buf(prtd->audio_client, dir);
- if (buf)
- memset(buf->data, 0, buf->actual_size);
+ if (buf == NULL) {
+ pr_err("%s: shared IO buffer is null\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+ memset(buf->data, 0, buf->actual_size);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 695f57b30322..aea60f1fa044 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -99,7 +99,8 @@ struct msm_pcm_route_bdai_pp_params {
static struct msm_pcm_route_bdai_pp_params
msm_bedais_pp_params[MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX] = {
- {HDMI_RX, 0, 0, 0}
+ {HDMI_RX, 0, 0, 0},
+ {DISPLAY_PORT_RX, 0, 0, 0},
};
static int msm_routing_send_device_pp_params(int port_id, int copp_idx);
@@ -448,6 +449,7 @@ struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
{ SLIMBUS_8_TX, 0, 0, 0, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_8_TX},
{ 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},
};
/* Track ASM playback & capture sessions of DAI */
@@ -2932,6 +2934,58 @@ static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
};
+
+static const struct snd_kcontrol_new display_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
/* incall music delivery mixer */
static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
@@ -5583,6 +5637,12 @@ static const struct snd_kcontrol_new hdmi_rx_port_mixer_controls[] = {
msm_routing_put_port_mixer),
};
+static const struct snd_kcontrol_new display_port_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+ MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
static const struct snd_kcontrol_new sec_i2s_rx_port_mixer_controls[] = {
SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SEC_I2S_RX,
MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
@@ -7435,6 +7495,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
SND_SOC_DAPM_AIF_OUT("SLIMBUS_2_RX", "Slimbus2 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT", "Display Port Playback",
+ 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback",
0, 0, 0, 0),
@@ -7709,6 +7771,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
slimbus_7_rx_mixer_controls, ARRAY_SIZE(slimbus_7_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DISPLAY_PORT Mixer", SND_SOC_NOPM, 0, 0,
+ display_port_mixer_controls, ARRAY_SIZE(display_port_mixer_controls)),
SND_SOC_DAPM_MIXER("SPDIF_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
spdif_rx_mixer_controls, ARRAY_SIZE(spdif_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
@@ -7923,6 +7987,9 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
SND_SOC_DAPM_MIXER("HDMI_RX Port Mixer",
SND_SOC_NOPM, 0, 0, hdmi_rx_port_mixer_controls,
ARRAY_SIZE(hdmi_rx_port_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, display_port_rx_port_mixer_controls,
+ ARRAY_SIZE(display_port_rx_port_mixer_controls)),
SND_SOC_DAPM_MIXER("SEC_I2S_RX Port Mixer",
SND_SOC_NOPM, 0, 0, sec_i2s_rx_port_mixer_controls,
ARRAY_SIZE(sec_i2s_rx_port_mixer_controls)),
@@ -8114,6 +8181,24 @@ static const struct snd_soc_dapm_route intercon[] = {
{"HDMI Mixer", "MultiMedia16", "MM_DL16"},
{"HDMI", NULL, "HDMI Mixer"},
+ {"DISPLAY_PORT Mixer", "MultiMedia1", "MM_DL1"},
+ {"DISPLAY_PORT Mixer", "MultiMedia2", "MM_DL2"},
+ {"DISPLAY_PORT Mixer", "MultiMedia3", "MM_DL3"},
+ {"DISPLAY_PORT Mixer", "MultiMedia4", "MM_DL4"},
+ {"DISPLAY_PORT Mixer", "MultiMedia5", "MM_DL5"},
+ {"DISPLAY_PORT Mixer", "MultiMedia6", "MM_DL6"},
+ {"DISPLAY_PORT Mixer", "MultiMedia7", "MM_DL7"},
+ {"DISPLAY_PORT Mixer", "MultiMedia8", "MM_DL8"},
+ {"DISPLAY_PORT Mixer", "MultiMedia9", "MM_DL9"},
+ {"DISPLAY_PORT Mixer", "MultiMedia10", "MM_DL10"},
+ {"DISPLAY_PORT Mixer", "MultiMedia11", "MM_DL11"},
+ {"DISPLAY_PORT Mixer", "MultiMedia12", "MM_DL12"},
+ {"DISPLAY_PORT Mixer", "MultiMedia13", "MM_DL13"},
+ {"DISPLAY_PORT Mixer", "MultiMedia14", "MM_DL14"},
+ {"DISPLAY_PORT Mixer", "MultiMedia15", "MM_DL15"},
+ {"DISPLAY_PORT Mixer", "MultiMedia16", "MM_DL16"},
+ {"DISPLAY_PORT", NULL, "DISPLAY_PORT Mixer"},
+
{"SPDIF_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"SPDIF_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
{"SPDIF_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
@@ -9517,6 +9602,9 @@ static const struct snd_soc_dapm_route intercon[] = {
{"HDMI_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
{"HDMI", NULL, "HDMI_RX Port Mixer"},
+ {"DISPLAY_PORT_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+ {"DISPLAY_PORT", NULL, "DISPLAY_PORT_RX Port Mixer"},
+
{"SEC_I2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
{"SEC_I2S_RX", NULL, "SEC_I2S_RX Port Mixer"},
@@ -9571,6 +9659,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"BE_OUT", NULL, "SLIMBUS_8_RX"},
{"BE_OUT", NULL, "USB_AUDIO_RX"},
{"BE_OUT", NULL, "HDMI"},
+ {"BE_OUT", NULL, "DISPLAY_PORT"},
{"BE_OUT", NULL, "SPDIF_RX"},
{"BE_OUT", NULL, "MI2S_RX"},
{"BE_OUT", NULL, "QUAT_MI2S_RX"},
@@ -9886,7 +9975,7 @@ static int msm_routing_send_device_pp_params(int port_id, int copp_idx)
pr_debug("%s: port_id %d, copp_idx %d\n", __func__, port_id, copp_idx);
- if (port_id != HDMI_RX) {
+ if (port_id != HDMI_RX && port_id != DISPLAY_PORT_RX) {
pr_err("%s: Device pp params on invalid port %d\n",
__func__, port_id);
return -EINVAL;
@@ -9959,7 +10048,7 @@ static int msm_routing_put_device_pp_params_mixer(struct snd_kcontrol *kcontrol,
for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) {
port_id = msm_bedais[be_idx].port_id;
- if (port_id == HDMI_RX)
+ if (port_id == HDMI_RX || port_id == DISPLAY_PORT_RX)
break;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index 009eebede28a..d8cec53c0d9e 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -18,6 +18,7 @@
#define LPASS_BE_SLIMBUS_0_RX "SLIMBUS_0_RX"
#define LPASS_BE_SLIMBUS_0_TX "SLIMBUS_0_TX"
#define LPASS_BE_HDMI "HDMI"
+#define LPASS_BE_DISPLAY_PORT "DISPLAY_PORT"
#define LPASS_BE_INT_BT_SCO_RX "INT_BT_SCO_RX"
#define LPASS_BE_INT_BT_SCO_TX "INT_BT_SCO_TX"
#define LPASS_BE_INT_BT_A2DP_RX "INT_BT_A2DP_RX"
@@ -311,6 +312,7 @@ enum {
MSM_BACKEND_DAI_SLIMBUS_8_TX,
MSM_BACKEND_DAI_USB_RX,
MSM_BACKEND_DAI_USB_TX,
+ MSM_BACKEND_DAI_DISPLAY_PORT_RX,
MSM_BACKEND_DAI_MAX,
};
@@ -339,7 +341,7 @@ enum {
#define RELEASE_LOCK 0
#define ACQUIRE_LOCK 1
-#define MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX 1
+#define MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX 2
#define HDMI_RX_ID 0x8001
#define ADM_PP_PARAM_MUTE_ID 0
#define ADM_PP_PARAM_MUTE_BIT 1
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
index 65c24973036e..6570819c2b31 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
@@ -670,7 +670,7 @@ static void voip_process_dl_pkt(uint8_t *voc_pkt, void *private_data)
} else {
*((uint32_t *)voc_pkt) = 0;
spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
- pr_err("DL data not available\n");
+ pr_err_ratelimited("DL data not available\n");
}
wake_up(&prtd->in_wait);
}
@@ -910,7 +910,7 @@ static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
} else if (ret == 0) {
- pr_err("%s: No UL data available\n", __func__);
+ pr_err_ratelimited("%s: No UL data available\n", __func__);
ret = -ETIMEDOUT;
} else {
pr_err("%s: Read was interrupted\n", __func__);
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 16c7ef4c0bf5..f6a687611f8a 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -423,6 +423,7 @@ int afe_get_port_type(u16 port_id)
case SECONDARY_I2S_RX:
case MI2S_RX:
case HDMI_RX:
+ case DISPLAY_PORT_RX:
case AFE_PORT_ID_SPDIF_RX:
case SLIMBUS_0_RX:
case SLIMBUS_1_RX:
@@ -574,6 +575,7 @@ int afe_sizeof_cfg_cmd(u16 port_id)
ret_size = SIZEOF_CFG_CMD(afe_param_id_i2s_cfg);
break;
case HDMI_RX:
+ case DISPLAY_PORT_RX:
ret_size =
SIZEOF_CFG_CMD(afe_param_id_hdmi_multi_chan_audio_cfg);
break;
@@ -2897,6 +2899,7 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config,
cfg_type = AFE_PARAM_ID_I2S_CONFIG;
break;
case HDMI_RX:
+ case DISPLAY_PORT_RX:
cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
break;
case VOICE_PLAYBACK_TX:
@@ -3073,6 +3076,7 @@ int afe_get_port_index(u16 port_id)
case MI2S_RX: return IDX_MI2S_RX;
case MI2S_TX: return IDX_MI2S_TX;
case HDMI_RX: return IDX_HDMI_RX;
+ case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX;
case AFE_PORT_ID_SPDIF_RX: return IDX_SPDIF_RX;
case RSVD_2: return IDX_RSVD_2;
case RSVD_3: return IDX_RSVD_3;
@@ -3352,6 +3356,7 @@ int afe_open(u16 port_id,
cfg_type = AFE_PARAM_ID_I2S_CONFIG;
break;
case HDMI_RX:
+ case DISPLAY_PORT_RX:
cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
break;
case SLIMBUS_0_RX:
@@ -4809,6 +4814,7 @@ int afe_validate_port(u16 port_id)
case MI2S_RX:
case MI2S_TX:
case HDMI_RX:
+ case DISPLAY_PORT_RX:
case AFE_PORT_ID_SPDIF_RX:
case RSVD_2:
case RSVD_3:
@@ -5591,6 +5597,12 @@ int afe_get_sp_th_vi_ftm_data(struct afe_sp_th_vi_get_param *th_vi)
goto done;
}
index = q6audio_get_port_index(port);
+ if (index < 0) {
+ pr_err("%s: invalid port 0x%x, index %d\n",
+ __func__, port, index);
+ ret = -EINVAL;
+ goto done;
+ }
th_vi->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
th_vi->hdr.pkt_size = sizeof(*th_vi);
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 20d3f5212323..206fbec249fa 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -1195,7 +1195,7 @@ err:
int q6asm_audio_client_buf_alloc(unsigned int dir,
struct audio_client *ac,
unsigned int bufsz,
- unsigned int bufcnt)
+ uint32_t bufcnt)
{
int cnt = 0;
int rc = 0;
@@ -1222,7 +1222,7 @@ int q6asm_audio_client_buf_alloc(unsigned int dir,
return 0;
}
mutex_lock(&ac->cmd_lock);
- if (bufcnt > (LONG_MAX/sizeof(struct audio_buffer))) {
+ if (bufcnt > (U32_MAX/sizeof(struct audio_buffer))) {
pr_err("%s: Buffer size overflows", __func__);
mutex_unlock(&ac->cmd_lock);
goto fail;
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
index 56dc6b58a646..a737a27cc327 100644
--- a/sound/soc/msm/qdsp6v2/q6audio-v2.c
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c
@@ -37,6 +37,7 @@ int q6audio_get_port_index(u16 port_id)
case MI2S_RX: return IDX_MI2S_RX;
case MI2S_TX: return IDX_MI2S_TX;
case HDMI_RX: return IDX_HDMI_RX;
+ case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX;
case AFE_PORT_ID_SPDIF_RX: return IDX_SPDIF_RX;
case RSVD_2: return IDX_RSVD_2;
case RSVD_3: return IDX_RSVD_3;
@@ -246,6 +247,8 @@ int q6audio_get_port_id(u16 port_id)
case MI2S_RX: return AFE_PORT_ID_PRIMARY_MI2S_RX;
case MI2S_TX: return AFE_PORT_ID_PRIMARY_MI2S_TX;
case HDMI_RX: return AFE_PORT_ID_MULTICHAN_HDMI_RX;
+ case DISPLAY_PORT_RX:
+ return AFE_PORT_ID_HDMI_OVER_DP_RX;
case AFE_PORT_ID_SPDIF_RX: return AFE_PORT_ID_SPDIF_RX;
case RSVD_2: return IDX_RSVD_2;
case RSVD_3: return IDX_RSVD_3;
@@ -573,6 +576,7 @@ int q6audio_validate_port(u16 port_id)
case MI2S_RX:
case MI2S_TX:
case HDMI_RX:
+ case DISPLAY_PORT_RX:
case RSVD_2:
case RSVD_3:
case DIGI_MIC_TX:
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 084f7df9d243..b242a8fe6017 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -48,6 +48,7 @@ struct cvd_version_table cvd_version_table_mapping[CVD_INT_VERSION_MAX] = {
{CVD_VERSION_0_0, CVD_INT_VERSION_0_0},
{CVD_VERSION_2_1, CVD_INT_VERSION_2_1},
{CVD_VERSION_2_2, CVD_INT_VERSION_2_2},
+ {CVD_VERSION_2_3, CVD_INT_VERSION_2_3},
};
static struct common_data common;
diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h
index d230bf097eb9..3b3e728f88f2 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.h
+++ b/sound/soc/msm/qdsp6v2/q6voice.h
@@ -41,12 +41,14 @@
#define CVD_VERSION_0_0 "0.0"
#define CVD_VERSION_2_1 "2.1"
#define CVD_VERSION_2_2 "2.2"
+#define CVD_VERSION_2_3 "2.3"
#define CVD_INT_VERSION_DEFAULT 0
#define CVD_INT_VERSION_0_0 1
#define CVD_INT_VERSION_2_1 2
#define CVD_INT_VERSION_2_2 3
-#define CVD_INT_VERSION_LAST CVD_INT_VERSION_2_2
+#define CVD_INT_VERSION_2_3 4
+#define CVD_INT_VERSION_LAST CVD_INT_VERSION_2_3
#define CVD_INT_VERSION_MAX (CVD_INT_VERSION_LAST + 1)
struct cvd_version_table {
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index b2dd69055927..c48d8c8d9766 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -201,7 +201,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
uinfo->value.integer.min = 0;
- if (mc->min < 0 && (uinfo->type == SNDRV_CTL_ELEM_TYPE_INTEGER))
+ if (uinfo->type == SNDRV_CTL_ELEM_TYPE_INTEGER)
uinfo->value.integer.max = platform_max - mc->min;
else
uinfo->value.integer.max = platform_max;
@@ -224,14 +224,12 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
int snd_soc_info_volsw_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
-
snd_soc_info_volsw(kcontrol, uinfo);
/* Max represents the number of levels in an SX control not the
- * maximum value, so add the minimum value back on
+ * maximum value.
+ * uinfo->value.integer.max is set to number of levels
+ * in snd_soc_info_volsw_sx. No further adjustment is necessary.
*/
- uinfo->value.integer.max += mc->min;
return 0;
}
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 53dd085d3ee2..a6884fd1e4b3 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -139,6 +139,9 @@ static int snd_soc_dummy_probe(struct platform_device *pdev)
{
int ret;
+ memset(&dummy_codec, 0,
+ sizeof(struct snd_soc_codec_driver));
+
ret = snd_soc_register_codec(&pdev->dev, &dummy_codec, &dummy_dai, 1);
if (ret < 0)
return ret;
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 524688e4c144..1f6c247f773a 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -673,6 +673,9 @@ static void usb_audio_disconnect(struct usb_interface *intf)
card = chip->card;
+ if (chip->disconnect_cb)
+ chip->disconnect_cb(chip);
+
mutex_lock(&register_mutex);
if (atomic_inc_return(&chip->shutdown) == 1) {
struct snd_usb_stream *as;
@@ -707,8 +710,6 @@ static void usb_audio_disconnect(struct usb_interface *intf)
if (chip->num_interfaces <= 0) {
usb_chip[chip->index] = NULL;
mutex_unlock(&register_mutex);
- if (chip->disconnect_cb)
- chip->disconnect_cb(chip);
snd_card_free_when_closed(card);
} else {
mutex_unlock(&register_mutex);
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index fa0206cc14c6..8337d11bad12 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -134,6 +134,12 @@ static struct msg_desc uaudio_stream_resp_desc = {
.ei_array = qmi_uaudio_stream_resp_msg_v01_ei,
};
+static struct msg_desc uaudio_stream_ind_desc = {
+ .max_msg_len = QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN,
+ .msg_id = QMI_UADUIO_STREAM_IND_V01,
+ .ei_array = qmi_uaudio_stream_ind_msg_v01_ei,
+};
+
enum mem_type {
MEM_EVENT_RING,
MEM_DCBA,
@@ -643,29 +649,47 @@ void uaudio_disconnect_cb(struct snd_usb_audio *chip)
int ret, if_idx;
struct uaudio_dev *dev;
int card_num = chip->card_num;
+ struct uaudio_qmi_svc *svc = uaudio_svc;
+ struct qmi_uaudio_stream_ind_msg_v01 disconnect_ind = {0};
pr_debug("%s: for card# %d\n", __func__, card_num);
- mutex_lock(&chip->dev_lock);
if (card_num >= SNDRV_CARDS) {
pr_err("%s: invalid card number\n", __func__);
- goto done;
+ return;
}
+ mutex_lock(&chip->dev_lock);
dev = &uadev[card_num];
+
+ /* clean up */
+ if (!dev->udev) {
+ pr_debug("%s: no clean up required\n", __func__);
+ goto done;
+ }
+
if (atomic_read(&dev->in_use)) {
+ mutex_unlock(&chip->dev_lock);
+
+ pr_debug("%s: sending qmi indication disconnect\n", __func__);
+ disconnect_ind.dev_event = USB_AUDIO_DEV_DISCONNECT_V01;
+ disconnect_ind.slot_id = dev->udev->slot_id;
+ ret = qmi_send_ind(svc->uaudio_svc_hdl, svc->curr_conn,
+ &uaudio_stream_ind_desc, &disconnect_ind,
+ sizeof(disconnect_ind));
+ if (ret < 0) {
+ pr_err("%s: qmi send failed wiht err: %d\n",
+ __func__, ret);
+ return;
+ }
+
ret = wait_event_interruptible(dev->disconnect_wq,
!atomic_read(&dev->in_use));
if (ret < 0) {
pr_debug("%s: failed with ret %d\n", __func__, ret);
- goto done;
+ return;
}
- }
-
- /* clean up */
- if (!dev->udev) {
- pr_debug("%s: no clean up required\n", __func__);
- goto done;
+ mutex_lock(&chip->dev_lock);
}
/* free xfer buffer and unmap xfer ring and buf per interface */
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.",